Subversion Repositories Kolibri OS

Rev

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

  1. ; USB Host Controller support code: hardware-independent part,
  2. ; common for all controller types.
  3.  
  4. ; =============================================================================
  5. ; ================================= Constants =================================
  6. ; =============================================================================
  7. ; USB device must have at least 100ms of stable power before initializing can
  8. ; proceed; one timer tick is 10ms, so enforce delay in 10 ticks
  9. USB_CONNECT_DELAY = 10
  10. ; USB requires at least 10 ms for reset signalling. Normally, this is one timer
  11. ; tick. However, it is possible that we start reset signalling in the end of
  12. ; interval between timer ticks and then we test time in the start of the next
  13. ; interval; in this case, the delta between [timer_ticks] is 1, but the real
  14. ; time passed is significantly less than 10 ms. To avoid this, we add an extra
  15. ; tick; this guarantees that at least 10 ms have passed.
  16. USB_RESET_TIME = 2
  17. ; USB requires at least 10 ms of reset recovery, a delay between reset
  18. ; signalling and any commands to device. Add an extra tick for the same reasons
  19. ; as with the previous constant.
  20. USB_RESET_RECOVERY_TIME = 2
  21.  
  22. ; =============================================================================
  23. ; ================================ Structures =================================
  24. ; =============================================================================
  25. ; Controller descriptor.
  26. ; This structure represents the common (controller-independent) part
  27. ; of a controller for the USB code. The corresponding controller-dependent
  28. ; part *hci_controller is located immediately before usb_controller.
  29. struct usb_controller
  30. ; Two following fields organize all controllers in the global linked list.
  31. Next            dd      ?
  32. Prev            dd      ?
  33. HardwareFunc    dd      ?
  34. ; Pointer to usb_hardware_func structure with controller-specific functions.
  35. NumPorts        dd      ?
  36. ; Number of ports in the root hub.
  37. SetAddressBuffer        rb      8
  38. ; Buffer for USB control command SET_ADDRESS.
  39. ExistingAddresses       rd      128/32
  40. ; Bitmask for 128 bits; bit i is cleared <=> address i is free for allocating
  41. ; for new devices. Bit 0 is always set.
  42. ;
  43. ; The hardware is allowed to cache some data from hardware structures.
  44. ; Regular operations are designed considering this,
  45. ; but sometimes it is required to wait for synchronization of hardware cache
  46. ; with modified structures in memory.
  47. ; The code keeps two queues of pipes waiting for synchronization,
  48. ; one for asynchronous (bulk/control) pipes, one for periodic pipes, hardware
  49. ; cache is invalidated under different conditions for those types.
  50. ; Both queues are organized in the same way, as single-linked lists.
  51. ; There are three special positions: the head of list (new pipes are added
  52. ; here), the first pipe to be synchronized at the current iteration,
  53. ; the tail of list (all pipes starting from here are synchronized).
  54. WaitPipeListAsync       dd      ?
  55. WaitPipeListPeriodic    dd      ?
  56. ; List heads.
  57. WaitPipeRequestAsync    dd      ?
  58. WaitPipeRequestPeriodic dd      ?
  59. ; Pending request to hardware to refresh cache for items from WaitPipeList*.
  60. ; (Pointers to some items in WaitPipeList* or NULLs).
  61. ReadyPipeHeadAsync      dd      ?
  62. ReadyPipeHeadPeriodic   dd      ?
  63. ; Items of RemovingList* which were released by hardware and are ready
  64. ; for further processing.
  65. ; (Pointers to some items in WaitPipeList* or NULLs).
  66. NewConnected    dd      ?
  67. ; bit mask of recently connected ports of the root hub,
  68. ; bit set = a device was recently connected to the corresponding port;
  69. ; after USB_CONNECT_DELAY ticks of stable status these ports are moved to
  70. ; PendingPorts
  71. NewDisconnected dd      ?
  72. ; bit mask of disconnected ports of the root hub,
  73. ; bit set = a device in the corresponding port was disconnected,
  74. ; disconnect processing is required.
  75. PendingPorts    dd      ?
  76. ; bit mask of ports which are ready to be initialized
  77. ControlLock     MUTEX   ?
  78. ; mutex which guards all operations with control queue
  79. BulkLock        MUTEX   ?
  80. ; mutex which guards all operations with bulk queue
  81. PeriodicLock    MUTEX   ?
  82. ; mutex which guards all operations with periodic queues
  83. WaitSpinlock:
  84. ; spinlock guarding WaitPipeRequest/ReadyPipeHead (but not WaitPipeList)
  85. StartWaitFrame  dd      ?
  86. ; USB frame number when WaitPipeRequest* was registered.
  87. ResettingHub    dd      ?
  88. ; Pointer to usb_hub responsible for the currently resetting port, if any.
  89. ; NULL for the root hub.
  90. ResettingPort   db      ?
  91. ; Port that is currently resetting, 0-based.
  92. ResettingSpeed  db      ?
  93. ; Speed of currently resetting device.
  94. ResettingStatus db      ?
  95. ; Status of port reset. 0 = no port is resetting, -1 = reset failed,
  96. ; 1 = reset in progress, 2 = reset recovery in progress.
  97.                 rb      1       ; alignment
  98. ResetTime       dd      ?
  99. ; Time when reset signalling or reset recovery has been started.
  100. ConnectedTime   rd      16
  101. ; Time, in timer ticks, when the port i has signalled the connect event.
  102. ; Valid only if bit i in NewConnected is set.
  103. DevicesByPort   rd      16
  104. ; Pointer to usb_pipe for zero endpoint (which serves as device handle)
  105. ; for each port.
  106. ends
  107.  
  108. ; Interface-specific data. Several interfaces of one device can operate
  109. ; independently, each is controlled by some driver and is identified by
  110. ; some driver-specific data passed as is to the driver.
  111. struct usb_interface_data
  112. DriverData      dd      ?
  113. ; Passed as is to the driver.
  114. DriverFunc      dd      ?
  115. ; Pointer to USBSRV structure for the driver.
  116. ends
  117.  
  118. ; Device-specific data.
  119. struct usb_device_data
  120. PipeListLock    MUTEX
  121. ; Lock guarding OpenedPipeList. Must be the first item of the structure,
  122. ; the code passes pointer to usb_device_data as is to mutex_lock/unlock.
  123. OpenedPipeList  rd      2
  124. ; List of all opened pipes for the device.
  125. ; Used when the device is disconnected, so all pipes should be closed.
  126. ClosedPipeList  rd      2
  127. ; List of all closed, but still valid pipes for the device.
  128. ; A pipe closed with USBClosePipe is just deallocated,
  129. ; but a pipe closed due to disconnect must remain valid until driver-provided
  130. ; disconnect handler returns; this list links all such pipes to deallocate them
  131. ; after disconnect processing.
  132. NumPipes        dd      ?
  133. ; Number of not-yet-closed pipes.
  134. Hub             dd      ?
  135. ; NULL if connected to the root hub, pointer to usb_hub otherwise.
  136. TTHub           dd      ?
  137. ; Pointer to usb_hub for (the) hub with Transaction Translator for the device,
  138. ; NULL if the device operates in the same speed as the controller.
  139. Port            db      ?
  140. ; Port on the hub, zero-based.
  141. TTPort          db      ?
  142. ; Port on the TTHub, zero-based.
  143. DeviceDescrSize db      ?
  144. ; Size of device descriptor.
  145. Speed           db      ?
  146. ; Device speed, one of USB_SPEED_*.
  147. NumInterfaces   dd      ?
  148. ; Number of interfaces.
  149. ConfigDataSize  dd      ?
  150. ; Total size of data associated with the configuration descriptor
  151. ; (including the configuration descriptor itself).
  152. Interfaces      dd      ?
  153. ; Offset from the beginning of this structure to Interfaces field.
  154. ; Variable-length fields:
  155. ; DeviceDescriptor:
  156. ;  device descriptor starts here
  157. ; ConfigDescriptor = DeviceDescriptor + DeviceDescrSize
  158. ;  configuration descriptor with all associated data
  159. ; Interfaces = ALIGN_UP(ConfigDescriptor + ConfigDataSize, 4)
  160. ;  array of NumInterfaces elements of type usb_interface_data
  161. ends
  162.  
  163. usb_device_data.DeviceDescriptor = sizeof.usb_device_data
  164.  
  165. ; Description of controller-specific data and functions.
  166. struct usb_hardware_func
  167. ID              dd      ?       ; '*HCI'
  168. DataSize        dd      ?       ; sizeof(*hci_controller)
  169. Init            dd      ?
  170. ; Initialize controller-specific part of controller data.
  171. ; in: eax -> *hci_controller to initialize, [ebp-4] = (bus shl 8) + devfn
  172. ; out: eax = 0 <=> failed, otherwise eax -> usb_controller
  173. ProcessDeferred dd      ?
  174. ; Called regularly from the main loop of USB thread
  175. ; (either due to timeout from a previous call, or due to explicit wakeup).
  176. ; in: esi -> usb_controller
  177. ; out: eax = maximum timeout for next call (-1 = infinity)
  178. SetDeviceAddress        dd      ?
  179. ; in: esi -> usb_controller, ebx -> usb_pipe, cl = address
  180. GetDeviceAddress        dd      ?
  181. ; in: esi -> usb_controller, ebx -> usb_pipe
  182. ; out: eax = address
  183. PortDisable             dd      ?
  184. ; Disable the given port in the root hub.
  185. ; in: esi -> usb_controller, ecx = port (zero-based)
  186. InitiateReset           dd      ?
  187. ; Start reset signalling on the given port.
  188. ; in: esi -> usb_controller, ecx = port (zero-based)
  189. SetEndpointPacketSize   dd      ?
  190. ; in: esi -> usb_controller, ebx -> usb_pipe, ecx = packet size
  191. AllocPipe               dd      ?
  192. ; out: eax = pointer to allocated usb_pipe
  193. FreePipe                dd      ?
  194. ; void stdcall with one argument = pointer to previously allocated usb_pipe
  195. InitPipe                dd      ?
  196. ; in: edi -> usb_pipe for target, ecx -> usb_pipe for config pipe,
  197. ; esi -> usb_controller, eax -> usb_gtd for the first TD,
  198. ; [ebp+12] = endpoint, [ebp+16] = maxpacket, [ebp+20] = type
  199. UnlinkPipe              dd      ?
  200. ; esi -> usb_controller, ebx -> usb_pipe
  201. AllocTD                 dd      ?
  202. ; out: eax = pointer to allocated usb_gtd
  203. FreeTD                  dd      ?
  204. ; void stdcall with one argument = pointer to previously allocated usb_gtd
  205. AllocTransfer           dd      ?
  206. ; Allocate and initialize one stage of a transfer.
  207. ; ebx -> usb_pipe, other parameters are passed through the stack:
  208. ; buffer,size = data to transfer
  209. ; flags = same as in usb_open_pipe:
  210. ;   bit 0 = allow short transfer, other bits reserved
  211. ; td = pointer to the current end-of-queue descriptor
  212. ; direction =
  213. ;   0000b for normal transfers,
  214. ;   1000b for control SETUP transfer,
  215. ;   1101b for control OUT transfer,
  216. ;   1110b for control IN transfer
  217. ; returns eax = pointer to the new end-of-queue descriptor
  218. ; (not included in the queue itself) or 0 on error
  219. InsertTransfer          dd      ?
  220. ; Activate previously initialized transfer (maybe with multiple stages).
  221. ; esi -> usb_controller, ebx -> usb_pipe,
  222. ; [esp+4] -> first usb_gtd for the transfer,
  223. ; ecx -> last descriptor for the transfer
  224. NewDevice               dd      ?
  225. ; Initiate configuration of a new device (create pseudo-pipe describing that
  226. ; device and call usb_new_device).
  227. ; esi -> usb_controller, eax = speed (one of USB_SPEED_* constants).
  228. ends
  229.  
  230. ; =============================================================================
  231. ; =================================== Code ====================================
  232. ; =============================================================================
  233.  
  234. ; Initializes one controller, called by usb_init for every controller.
  235. ; edi -> usb_hardware_func, eax -> PCIDEV structure for the device.
  236. proc usb_init_controller
  237.         push    ebp
  238.         mov     ebp, esp
  239. ; 1. Store in the stack PCI coordinates and save pointer to PCIDEV:
  240. ; make [ebp-4] = (bus shl 8) + devfn, used by controller-specific Init funcs.
  241.         push    dword [eax+PCIDEV.devfn]
  242.         push    eax
  243. ; 2. Allocate *hci_controller + usb_controller.
  244.         mov     ebx, [edi+usb_hardware_func.DataSize]
  245.         add     ebx, sizeof.usb_controller
  246.         stdcall kernel_alloc, ebx
  247.         test    eax, eax
  248.         jz      .nothing
  249. ; 3. Zero-initialize both structures.
  250.         push    edi eax
  251.         mov     ecx, ebx
  252.         shr     ecx, 2
  253.         xchg    edi, eax
  254.         xor     eax, eax
  255.         rep stosd
  256. ; 4. Initialize usb_controller structure,
  257. ; except data known only to controller-specific code (like NumPorts)
  258. ; and link fields
  259. ; (this structure will be inserted to the overall list at step 6).
  260.         dec     eax
  261.         mov     [edi+usb_controller.ExistingAddresses+4-sizeof.usb_controller], eax
  262.         mov     [edi+usb_controller.ExistingAddresses+8-sizeof.usb_controller], eax
  263.         mov     [edi+usb_controller.ExistingAddresses+12-sizeof.usb_controller], eax
  264.         mov     [edi+usb_controller.ResettingPort-sizeof.usb_controller], al    ; no resetting port
  265.         dec     eax     ; don't allocate zero address
  266.         mov     [edi+usb_controller.ExistingAddresses-sizeof.usb_controller], eax
  267.         lea     ecx, [edi+usb_controller.PeriodicLock-sizeof.usb_controller]
  268.         call    mutex_init
  269.         add     ecx, usb_controller.ControlLock - usb_controller.PeriodicLock
  270.         call    mutex_init
  271.         add     ecx, usb_controller.BulkLock - usb_controller.ControlLock
  272.         call    mutex_init
  273.         pop     eax edi
  274.         mov     [eax+ebx-sizeof.usb_controller+usb_controller.HardwareFunc], edi
  275.         push    eax
  276. ; 5. Call controller-specific initialization.
  277. ; If failed, free memory allocated in step 2 and return.
  278.         call    [edi+usb_hardware_func.Init]
  279.         test    eax, eax
  280.         jz      .fail
  281.         pop     ecx
  282. ; 6. Insert the controller to the global list.
  283.         xchg    eax, ebx
  284.         mov     ecx, usb_controllers_list_mutex
  285.         call    mutex_lock
  286.         mov     edx, usb_controllers_list
  287.         mov     eax, [edx+usb_controller.Prev]
  288.         mov     [ebx+usb_controller.Next], edx
  289.         mov     [ebx+usb_controller.Prev], eax
  290.         mov     [edx+usb_controller.Prev], ebx
  291.         mov     [eax+usb_controller.Next], ebx
  292.         call    mutex_unlock
  293. ; 7. Wakeup USB thread to call ProcessDeferred.
  294.         call    usb_wakeup
  295. .nothing:
  296. ; 8. Restore pointer to PCIDEV saved in step 1 and return.
  297.         pop     eax
  298.         leave
  299.         ret
  300. .fail:
  301.         call    kernel_free
  302.         jmp     .nothing
  303. endp
  304.  
  305. ; Helper function, calculates physical address including offset in page.
  306. proc get_phys_addr
  307.         push    ecx
  308.         mov     ecx, eax
  309.         and     ecx, 0xFFF
  310.         call    get_pg_addr
  311.         add     eax, ecx
  312.         pop     ecx
  313.         ret
  314. endp
  315.  
  316. ; Put the given control pipe in the wait list;
  317. ; called when the pipe structure is changed and a possible hardware cache
  318. ; needs to be synchronized. When it will be known that the cache is updated,
  319. ; usb_subscription_done procedure will be called.
  320. proc usb_subscribe_control
  321.         cmp     [ebx+usb_pipe.NextWait], -1
  322.         jnz     @f
  323.         mov     eax, [esi+usb_controller.WaitPipeListAsync]
  324.         mov     [ebx+usb_pipe.NextWait], eax
  325.         mov     [esi+usb_controller.WaitPipeListAsync], ebx
  326. @@:
  327.         ret
  328. endp
  329.  
  330. ; Called after synchronization of hardware cache with software changes.
  331. ; Continues process of device enumeration based on when it was delayed
  332. ; due to call to usb_subscribe_control.
  333. proc usb_subscription_done
  334.         mov     eax, [ebx+usb_pipe.DeviceData]
  335.         cmp     [eax+usb_device_data.DeviceDescrSize], 0
  336.         jz      usb_after_set_address
  337.         jmp     usb_after_set_endpoint_size
  338. endp
  339.  
  340. ; This function is called when a new device has either passed
  341. ; or failed first stages of configuration, so the next device
  342. ; can enter configuration process.
  343. proc usb_test_pending_port
  344.         mov     [esi+usb_controller.ResettingPort], -1
  345.         cmp     [esi+usb_controller.PendingPorts], 0
  346.         jz      .nothing
  347.         bsf     ecx, [esi+usb_controller.PendingPorts]
  348.         btr     [esi+usb_controller.PendingPorts], ecx
  349.         mov     eax, [esi+usb_controller.HardwareFunc]
  350.         jmp     [eax+usb_hardware_func.InitiateReset]
  351. .nothing:
  352.         ret
  353. endp
  354.  
  355. ; This procedure is regularly called from controller-specific ProcessDeferred,
  356. ; it checks whether there are disconnected events and if so, process them.
  357. proc usb_disconnect_stage2
  358.         bsf     ecx, [esi+usb_controller.NewDisconnected]
  359.         jz      .nothing
  360.         lock btr [esi+usb_controller.NewDisconnected], ecx
  361.         btr     [esi+usb_controller.PendingPorts], ecx
  362.         xor     ebx, ebx
  363.         xchg    ebx, [esi+usb_controller.DevicesByPort+ecx*4]
  364.         test    ebx, ebx
  365.         jz      usb_disconnect_stage2
  366.         call    usb_device_disconnected
  367.         jmp     usb_disconnect_stage2
  368. .nothing:
  369.         ret
  370. endp
  371.  
  372. ; Initial stage of disconnect processing: called when device is disconnected.
  373. proc usb_device_disconnected
  374. ; Loop over all pipes, close everything, wait until hardware reacts.
  375. ; The final handling is done in usb_pipe_closed.
  376.         push    ebx
  377.         mov     ecx, [ebx+usb_pipe.DeviceData]
  378.         call    mutex_lock
  379.         lea     eax, [ecx+usb_device_data.OpenedPipeList-usb_pipe.NextSibling]
  380.         push    eax
  381.         mov     ebx, [eax+usb_pipe.NextSibling]
  382. .pipe_loop:
  383.         call    usb_close_pipe_nolock
  384.         mov     ebx, [ebx+usb_pipe.NextSibling]
  385.         cmp     ebx, [esp]
  386.         jnz     .pipe_loop
  387.         pop     eax
  388.         pop     ebx
  389.         mov     ecx, [ebx+usb_pipe.DeviceData]
  390.         call    mutex_unlock
  391.         ret
  392. endp
  393.  
  394. ; Called from controller-specific ProcessDeferred,
  395. ; processes wait-pipe-done notifications,
  396. ; returns whether there are more items in wait queues.
  397. ; in: esi -> usb_controller
  398. ; out: eax = bitmask of pipe types with non-empty wait queue
  399. proc usb_process_wait_lists
  400.         xor     edx, edx
  401.         push    edx
  402.         call    usb_process_one_wait_list
  403.         jnc     @f
  404.         or      byte [esp], 1 shl CONTROL_PIPE
  405. @@:
  406.         movi    edx, 4
  407.         call    usb_process_one_wait_list
  408.         jnc     @f
  409.         or      byte [esp], 1 shl INTERRUPT_PIPE
  410. @@:
  411.         xor     edx, edx
  412.         call    usb_process_one_wait_list
  413.         jnc     @f
  414.         or      byte [esp], 1 shl CONTROL_PIPE
  415. @@:
  416.         pop     eax
  417.         ret
  418. endp
  419.  
  420. ; Helper procedure for usb_process_wait_lists;
  421. ; does the same for one wait queue.
  422. ; in: esi -> usb_controller,
  423. ; edx=0 for *Async, edx=4 for *Periodic list
  424. ; out: CF = issue new request
  425. proc usb_process_one_wait_list
  426. ; 1. Check whether there is a pending request. If so, do nothing.
  427.         mov     ebx, [esi+usb_controller.WaitPipeRequestAsync+edx]
  428.         cmp     ebx, [esi+usb_controller.ReadyPipeHeadAsync+edx]
  429.         clc
  430.         jnz     .nothing
  431. ; 2. Check whether there are new data. If so, issue a new request.
  432.         cmp     ebx, [esi+usb_controller.WaitPipeListAsync+edx]
  433.         stc
  434.         jnz     .nothing
  435.         test    ebx, ebx
  436.         jz      .nothing
  437. ; 3. Clear all lists.
  438.         xor     ecx, ecx
  439.         mov     [esi+usb_controller.WaitPipeListAsync+edx], ecx
  440.         mov     [esi+usb_controller.WaitPipeRequestAsync+edx], ecx
  441.         mov     [esi+usb_controller.ReadyPipeHeadAsync+edx], ecx
  442. ; 4. Loop over all pipes from the wait list.
  443. .pipe_loop:
  444. ; For every pipe:
  445. ; 5. Save edx and next pipe in the list.
  446.         push    edx
  447.         push    [ebx+usb_pipe.NextWait]
  448. ; 6. If USB_FLAG_EXTRA_WAIT is set, reinsert the pipe to the list and continue.
  449.         test    [ebx+usb_pipe.Flags], USB_FLAG_EXTRA_WAIT
  450.         jz      .process
  451.         mov     eax, [esi+usb_controller.WaitPipeListAsync+edx]
  452.         mov     [ebx+usb_pipe.NextWait], eax
  453.         mov     [esi+usb_controller.WaitPipeListAsync+edx], ebx
  454.         jmp     .continue
  455. .process:
  456. ; 7. Call the handler depending on USB_FLAG_CLOSED.
  457.         or      [ebx+usb_pipe.NextWait], -1
  458.         test    [ebx+usb_pipe.Flags], USB_FLAG_CLOSED
  459.         jz      .nodisconnect
  460.         call    usb_pipe_closed
  461.         jmp     .continue
  462. .nodisconnect:
  463.         call    usb_subscription_done
  464. .continue:
  465. ; 8. Restore edx and next pipe saved in step 5 and continue the loop.
  466.         pop     ebx
  467.         pop     edx
  468.         test    ebx, ebx
  469.         jnz     .pipe_loop
  470. .check_new_work:
  471. ; 9. Set CF depending on whether WaitPipeList* is nonzero.
  472.         cmp     [esi+usb_controller.WaitPipeListAsync+edx], 1
  473.         cmc
  474. .nothing:
  475.         ret
  476. endp
  477.