Subversion Repositories Kolibri OS

Rev

Rev 3520 | Rev 4850 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. ; Memory management for USB structures.
  2. ; Protocol layer uses the common kernel heap malloc/free.
  3. ; Hardware layer has special requirements:
  4. ; * memory blocks should be properly aligned
  5. ; * memory blocks should not cross page boundary
  6. ; Hardware layer allocates fixed-size blocks.
  7. ; Thus, the specific allocator is quite easy to write:
  8. ; allocate one page, split into blocks, maintain the single-linked
  9. ; list of all free blocks in each page.
  10.  
  11. ; Note: size must be a multiple of required alignment.
  12.  
  13. ; Data for one pool: dd pointer to the first page, MUTEX lock.
  14.  
  15. uglobal
  16. ; Structures in UHCI and OHCI have equal sizes.
  17. ; Thus, functions and data for allocating/freeing can be shared;
  18. ; we keep them here rather than in controller-specific files.
  19. align 4
  20. ; Data for UHCI and OHCI endpoints pool.
  21. usb1_ep_first_page      dd      ?
  22. usb1_ep_mutex           MUTEX
  23. ; Data for UHCI and OHCI general transfer descriptors pool.
  24. usb_gtd_first_page      dd      ?
  25. usb_gtd_mutex           MUTEX
  26. endg
  27.  
  28. ; sanity check: structures in UHCI and OHCI should be the same for allocation
  29. if (sizeof.ohci_pipe = sizeof.uhci_pipe)
  30.  
  31. ; Allocates one endpoint structure for UHCI/OHCI.
  32. ; Returns pointer to software part (usb_pipe) in eax.
  33. proc usb1_allocate_endpoint
  34.         push    ebx
  35.         mov     ebx, usb1_ep_mutex
  36.         stdcall usb_allocate_common, (sizeof.ohci_pipe + sizeof.usb_pipe + 0Fh) and not 0Fh
  37.         test    eax, eax
  38.         jz      @f
  39.         add     eax, sizeof.ohci_pipe
  40. @@:
  41.         pop     ebx
  42.         ret
  43. endp
  44.  
  45. ; Free one endpoint structure for UHCI/OHCI.
  46. ; Stdcall with one argument, pointer to software part (usb_pipe).
  47. proc usb1_free_endpoint
  48.         sub     dword [esp+4], sizeof.ohci_pipe
  49.         jmp     usb_free_common
  50. endp
  51.  
  52. else
  53. ; sanity check continued
  54. .err allocate_endpoint/free_endpoint must be different for OHCI and UHCI
  55. end if
  56.  
  57. ; sanity check: structures in UHCI and OHCI should be the same for allocation
  58. if (sizeof.ohci_gtd = sizeof.uhci_gtd)
  59.  
  60. ; Allocates one general transfer descriptor structure for UHCI/OHCI.
  61. ; Returns pointer to software part (usb_gtd) in eax.
  62. proc usb1_allocate_general_td
  63.         push    ebx
  64.         mov     ebx, usb_gtd_mutex
  65.         stdcall usb_allocate_common, (sizeof.ohci_gtd + sizeof.usb_gtd + 0Fh) and not 0Fh
  66.         test    eax, eax
  67.         jz      @f
  68.         add     eax, sizeof.ohci_gtd
  69. @@:
  70.         pop     ebx
  71.         ret
  72. endp
  73.  
  74. ; Free one general transfer descriptor structure for UHCI/OHCI.
  75. ; Stdcall with one argument, pointer to software part (usb_gtd).
  76. proc usb1_free_general_td
  77.         sub     dword [esp+4], sizeof.ohci_gtd
  78.         jmp     usb_free_common
  79. endp
  80.  
  81. else
  82. ; sanity check continued
  83. .err allocate_general_td/free_general_td must be different for OHCI and UHCI
  84. end if
  85.  
  86. ; Allocator for fixed-size blocks: allocate a block.
  87. ; [ebx-4] = pointer to the first page, ebx = pointer to MUTEX structure.
  88. proc usb_allocate_common
  89.         push    edi     ; save used register to be stdcall
  90. virtual at esp
  91.         dd      ?       ; saved edi
  92.         dd      ?       ; return address
  93. .size   dd      ?
  94. end virtual
  95. ; 1. Take the lock.
  96.         mov     ecx, ebx
  97.         call    mutex_lock
  98. ; 2. Find the first allocated page with a free block, if any.
  99. ; 2a. Initialize for the loop.
  100.         mov     edx, ebx
  101. .pageloop:
  102. ; 2b. Get the next page, keeping the current in eax.
  103.         mov     eax, edx
  104.         mov     edx, [edx-4]
  105. ; 2c. If there is no next page, we're out of luck; go to 4.
  106.         test    edx, edx
  107.         jz      .newpage
  108.         add     edx, 0x1000
  109. @@:
  110. ; 2d. Get the pointer to the first free block on this page.
  111. ; If there is no free block, continue to 2b.
  112.         mov     eax, [edx-8]
  113.         test    eax, eax
  114.         jz      .pageloop
  115. ; 2e. Get the pointer to the next free block.
  116.         mov     ecx, [eax]
  117. ; 2f. Update the pointer to the first free block from eax to ecx.
  118. ; Normally [edx-8] still contains eax, if so, atomically set it to ecx
  119. ; and proceed to 3.
  120. ; However, the price of simplicity of usb_free_common (in particular, it
  121. ; doesn't take the lock) is that [edx-8] could (rarely) be changed while
  122. ; we processed steps 2d+2e. If so, return to 2d and retry.
  123.         lock cmpxchg [edx-8], ecx
  124.         jnz     @b
  125. .return:
  126. ; 3. Release the lock taken in step 1 and return.
  127.         push    eax
  128.         mov     ecx, ebx
  129.         call    mutex_unlock
  130.         pop     eax
  131.         pop     edi     ; restore used register to be stdcall
  132.         ret     4
  133. .newpage:
  134. ; 4. Allocate a new page.
  135.         push    eax
  136.         stdcall kernel_alloc, 0x1000
  137.         pop     edx
  138. ; If failed, say something to the debug board and return zero.
  139.         test    eax, eax
  140.         jz      .nomemory
  141. ; 5. Add the new page to the tail of list of allocated pages.
  142.         mov     [edx-4], eax
  143. ; 6. Initialize two service dwords in the end of page:
  144. ; first free block is (start of page) + (block size)
  145. ; (we will return first block at (start of page), so consider it allocated),
  146. ; no next page.
  147.         mov     edx, eax
  148.         lea     edi, [eax+0x1000-8]
  149.         add     edx, [.size]
  150.         mov     [edi], edx
  151.         and     dword [edi+4], 0
  152. ; 7. All blocks starting from edx are free; join them in a single-linked list.
  153. @@:
  154.         mov     ecx, edx
  155.         add     edx, [.size]
  156.         mov     [ecx], edx
  157.         cmp     edx, edi
  158.         jbe     @b
  159.         sub     ecx, [.size]
  160.         and     dword [ecx], 0
  161. ; 8. Return (start of page).
  162.         jmp     .return
  163. .nomemory:
  164.         dbgstr 'no memory for USB descriptor'
  165.         xor     eax, eax
  166.         jmp     .return
  167. endp
  168.  
  169. ; Allocator for fixed-size blocks: free a block.
  170. proc usb_free_common
  171.         push    ecx edx
  172. virtual at esp
  173.         rd      2       ; saved registers
  174.         dd      ?       ; return address
  175. .block  dd      ?
  176. end virtual
  177. ; Insert the given block to the head of free blocks in this page.
  178.         mov     ecx, [.block]
  179.         mov     edx, ecx
  180.         or      edx, 0xFFF
  181. @@:
  182.         mov     eax, [edx+1-8]
  183.         mov     [ecx], eax
  184.         lock cmpxchg [edx+1-8], ecx
  185.         jnz     @b
  186.         pop     edx ecx
  187.         ret     4
  188. endp
  189.  
  190. ; Helper procedure for OHCI: translate physical address in ecx
  191. ; of some transfer descriptor to linear address.
  192. proc usb_td_to_virt
  193. ; Traverse all pages used for transfer descriptors, looking for the one
  194. ; with physical address as in ecx.
  195.         mov     eax, [usb_gtd_first_page]
  196. @@:
  197.         test    eax, eax
  198.         jz      .zero
  199.         push    eax
  200.         call    get_pg_addr
  201.         sub     eax, ecx
  202.         jz      .found
  203.         cmp     eax, -0x1000
  204.         ja      .found
  205.         pop     eax
  206.         mov     eax, [eax+0x1000-4]
  207.         jmp     @b
  208. .found:
  209. ; When found, combine page address from eax with page offset from ecx.
  210.         pop     eax
  211.         and     ecx, 0xFFF
  212.         add     eax, ecx
  213. .zero:
  214.         ret
  215. endp
  216.