Subversion Repositories Kolibri OS

Rev

Rev 4429 | Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  2. ;;                                                              ;;
  3. ;; Copyright (C) KolibriOS team 2013-2014. All rights reserved. ;;
  4. ;; Distributed under terms of the GNU General Public License    ;;
  5. ;;                                                              ;;
  6. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  7.  
  8. $Revision: 4850 $
  9.  
  10.  
  11. ; Support for USB (non-root) hubs:
  12. ; powering up/resetting/disabling ports,
  13. ; watching for adding/removing devices.
  14.  
  15. ; =============================================================================
  16. ; ================================= Constants =================================
  17. ; =============================================================================
  18. ; Hub constants
  19. ; USB hub descriptor type
  20. USB_HUB_DESCRIPTOR = 29h
  21.  
  22. ; Features for CLEAR_FEATURE commands to the hub.
  23. C_HUB_LOCAL_POWER   = 0
  24. C_HUB_OVER_CURRENT  = 1
  25.  
  26. ; Bits in result of GET_STATUS command for a port.
  27. ; Also suitable for CLEAR_FEATURE/SET_FEATURE commands, where applicable,
  28. ; except TEST/INDICATOR.
  29. PORT_CONNECTION     = 0
  30. PORT_ENABLE         = 1
  31. PORT_SUSPEND        = 2
  32. PORT_OVER_CURRENT   = 3
  33. PORT_RESET          = 4
  34. PORT_POWER          = 8
  35. PORT_LOW_SPEED      = 9
  36. PORT_HIGH_SPEED     = 10
  37. PORT_TEST_BIT       = 11
  38. PORT_INDICATOR_BIT  = 12
  39. C_PORT_CONNECTION   = 16
  40. C_PORT_ENABLE       = 17
  41. C_PORT_SUSPEND      = 18
  42. C_PORT_OVER_CURRENT = 19
  43. C_PORT_RESET        = 20
  44. PORT_TEST_FEATURE   = 21
  45. PORT_INDICATOR_FEATURE = 22
  46.  
  47. ; Internal constants
  48. ; Bits in usb_hub.Actions
  49. HUB_WAIT_POWERED      = 1
  50. ; ports were powered, wait until power is stable
  51. HUB_WAIT_CONNECT      = 2
  52. ; some device was connected, wait initial debounce interval
  53. HUB_RESET_IN_PROGRESS = 4
  54. ; reset in progress, so buffer for config requests is owned
  55. ; by reset process; this includes all stages from initial disconnect test
  56. ; to end of setting address (fail on any stage should lead to disabling port,
  57. ; which requires a config request)
  58. HUB_RESET_WAITING     = 8
  59. ; the port is ready for reset, but another device somewhere on the bus
  60. ; is resetting. Implies HUB_RESET_IN_PROGRESS
  61. HUB_RESET_SIGNAL      = 10h
  62. ; reset signalling is active for some port in the hub
  63. ; Implies HUB_RESET_IN_PROGRESS
  64. HUB_RESET_RECOVERY    = 20h
  65. ; reset recovery is active for some port in the hub
  66. ; Implies HUB_RESET_IN_PROGRESS
  67.  
  68. ; Well, I think that those 5 flags WAIT_CONNECT and RESET_* require additional
  69. ; comments. So that is the overview of what happens with a new device assuming
  70. ; no errors.
  71. ; * device is connected;
  72. ; * hub notifies us about connect event; after some processing
  73. ;   usb_hub_port_change finally processes that event, setting the flag
  74. ;   HUB_WAIT_CONNECT and storing time when the device was connected;
  75. ; * 100 ms delay;
  76. ; * usb_hub_process_deferred clears HUB_WAIT_CONNECT,
  77. ;   sets HUB_RESET_IN_PROGRESS, stores the port index in ConfigBuffer and asks
  78. ;   the hub whether there was a disconnect event for that port during those
  79. ;   100 ms (on the hardware level notifications are obtained using polling
  80. ;   with some intervals, so it is possible that the corresponding notification
  81. ;   has not arrived yet);
  82. ; * usb_hub_connect_port_status checks that there was no disconnect event
  83. ;   and sets HUB_RESET_WAITING flag (HUB_RESET_IN_PROGRESS is still set,
  84. ;   ConfigBuffer still contains the port index);
  85. ; * usb_hub_process_deferred checks whether there is another device currently
  86. ;   resetting. If so, it waits until reset is done
  87. ;   (with HUB_RESET_WAITING and HUB_RESET_IN_PROGRESS bits set);
  88. ; * usb_hub_process_deferred clears HUB_RESET_WAITING, sets HUB_RESET_SIGNAL
  89. ;   and initiates reset signalling on the port;
  90. ; * usb_hub_process_deferred checks the status every tick;
  91. ;   when reset signalling is stopped by the hub, usb_hub_resetting_port_status
  92. ;   callback clears HUB_RESET_SIGNAL and sets HUB_RESET_RECOVERY;
  93. ; * 10 ms (at least) delay;
  94. ; * usb_hub_process_deferred clears HUB_RESET_RECOVERY and notifies other code
  95. ;   that the new device is ready to be configured;
  96. ; * when it is possible to reset another device, the protocol layer
  97. ;   clears HUB_RESET_IN_PROGRESS bit.
  98.  
  99. ; =============================================================================
  100. ; ================================ Structures =================================
  101. ; =============================================================================
  102. ; This structure contains all used data for one hub.
  103. struct usb_hub
  104. ; All configured hubs are organized in the global usb_hub_list.
  105. ; Two following fields give next/prev items in that list.
  106. ; While the hub is unconfigured, they point to usb_hub itself.
  107. Next                    dd      ?
  108. Prev                    dd      ?
  109. Controller              dd      ?
  110. ; Pointer to usb_controller for the bus.
  111. ;
  112. ; Handles of two pipes: configuration control pipe for zero endpoint opened by
  113. ; the common code and status interrupt pipe opened by us.
  114. ConfigPipe              dd      ?
  115. StatusPipe              dd      ?
  116. NumPorts                dd      ?
  117. ; Number of downstream ports; from 1 to 255.
  118. MaxPacketSize           dd      ?
  119. ; Maximum packet size for interrupt endpoint.
  120. ; Usually equals ceil((1+NumPorts)/8), but some hubs give additional bytes.
  121. Actions                 dd      ?
  122. ; Bitfield with HUB_* constants.
  123. PoweredOnTime           dd      ?
  124. ; Time (in ticks) when all downstream ports were powered up.
  125. ResetTime               dd      ?
  126. ; Time (in ticks) when the current port was reset;
  127. ; when a port is resetting, contains the last tick of status check;
  128. ; when reset recovery for a port is active, contains the time when
  129. ; reset was completed.
  130. ;
  131. ; There are two possible reasons for configuration requests:
  132. ; synchronous, when certain time is passed after something,
  133. ; and asynchronous, when the hub is notifying about some change and
  134. ; config request needs to be issued in order to query details.
  135. ; Use two different buffers to avoid unnecessary dependencies.
  136. ConfigBuffer            rb      8
  137. ; Buffer for configuration requests for synchronous events.
  138. ChangeConfigBuffer      rb      8
  139. ; Buffer for configuration requests for status changes.
  140. AccStatusChange         db      ?
  141. ; Accumulated status change. See 11.12.3 of USB2 spec or comments in code.
  142. HubCharacteristics      dw      ?
  143. ; Copy of usb_hub_descr.wHubCharacteristics.
  144. PowerOnInterval         db      ?
  145. ; Copy of usb_hub_descr.bPwrOn2PwrGood.
  146. ;
  147. ; Two following fields are written at once by GET_STATUS request
  148. ; and must remain in this order.
  149. StatusData              dw      ?
  150. ; Bitfield with 1 shl PORT_* indicating status of the current port.
  151. StatusChange            dw      ?
  152. ; Bitfield with 1 shl PORT_* indicating change in status of the current port.
  153. ; Two following fields are written at once by GET_STATUS request
  154. ; and must remain in this order.
  155. ; The meaning is the same as of StatusData/StatusChange; two following fields
  156. ; are used by the synchronous requests to avoid unnecessary interactions with
  157. ; the asynchronous handler.
  158. ResetStatusData         dw      ?
  159. ResetStatusChange       dw      ?
  160. StatusChangePtr         dd      ?
  161. ; Pointer to StatusChangeBuf.
  162. ConnectedDevicesPtr     dd      ?
  163. ; Pointer to ConnectedDevices.
  164. ConnectedTimePtr        dd      ?
  165. ; Pointer to ConnectedTime.
  166. ;
  167. ; Variable-length parts:
  168. ; DeviceRemovable rb (NumPorts+8)/8
  169. ;  Bit i+1 = device at port i (zero-based) is non-removable.
  170. ; StatusChangeBuf rb (NumPorts+8)/8
  171. ;  Buffer for status interrupt pipe. Bit 0 = hub status change,
  172. ;  other bits = status change of the corresponding ports.
  173. ; ConnectedDevices rd NumPorts
  174. ;  Pointers to config pipes for connected devices or zero if no device connected.
  175. ; ConnectedTime rd NumPorts
  176. ;  For initial debounce interval:
  177. ;   time (in ticks) when a device was connected at that port.
  178. ;  Normally: -1
  179. ends
  180.  
  181. ; Hub descriptor.
  182. struct usb_hub_descr usb_descr
  183. bNbrPorts               db      ?
  184. ; Number of downstream ports.
  185. wHubCharacteristics     dw      ?
  186. ; Bit 0: 0 = all ports are powered at once, 1 = individual port power switching
  187. ; Bit 1: reserved, must be zero
  188. ; Bit 2: 1 = the hub is part of a compound device
  189. ; Bits 3-4: 00 = global overcurrent protection,
  190. ;           01 = individual port overcurrent protection,
  191. ;           1x = no overcurrent protection
  192. ; Bits 5-6: Transaction Translator Think Time, 8*(value+1) full-speed bit times
  193. ; Bit 7: 1 = port indicators supported
  194. ; Other bits are reserved.
  195. bPwrOn2PwrGood          db      ?
  196. ; Time in 2ms intervals between powering up a port and a port becoming ready.
  197. bHubContrCurrent        db      ?
  198. ; Maximum current requirements of the Hub Controller electronics in mA.
  199. ; DeviceRemovable - variable length
  200. ;  Bit 0 is reserved, bit i+1 = device at port i is non-removable.
  201. ; PortPwrCtrlMask - variable length
  202. ;  Obsolete, exists for compatibility. We ignore it.
  203. ends
  204.  
  205. iglobal
  206. align 4
  207. ; Implementation of struct USBFUNC for hubs.
  208. usb_hub_callbacks:
  209.         dd      usb_hub_callbacks_end - usb_hub_callbacks
  210.         dd      usb_hub_init
  211.         dd      usb_hub_disconnect
  212. usb_hub_callbacks_end:
  213. usb_hub_pseudosrv       dd      usb_hub_callbacks
  214. endg
  215.  
  216. ; This procedure is called when new hub is detected.
  217. ; It initializes the device.
  218. ; Technically, initialization implies sending several USB queries,
  219. ; so it is split in several procedures. The first is usb_hub_init,
  220. ; other are callbacks which will be called at some time in the future,
  221. ; when the device will respond.
  222. ; edx = usb_interface_descr, ecx = length rest
  223. proc usb_hub_init
  224.         push    ebx esi         ; save used registers to be stdcall
  225. virtual at esp
  226.                 rd      2       ; saved registers
  227.                 dd      ?       ; return address
  228. .pipe           dd      ?       ; handle of the config pipe
  229. .config         dd      ?       ; pointer to usb_config_descr
  230. .interface      dd      ?       ; pointer to usb_interface_descr
  231. end virtual
  232. ; 1. Check that the maximal nesting is not exceeded:
  233. ; 5 non-root hubs is the maximum according to the spec.
  234.         mov     ebx, [.pipe]
  235.         push    5
  236.         mov     eax, ebx
  237. .count_parents:
  238.         mov     eax, [eax+usb_pipe.DeviceData]
  239.         mov     eax, [eax+usb_device_data.Hub]
  240.         test    eax, eax
  241.         jz      .depth_ok
  242.         mov     eax, [eax+usb_hub.ConfigPipe]
  243.         dec     dword [esp]
  244.         jnz     .count_parents
  245.         pop     eax
  246.         dbgstr 'Hub chain is too long'
  247.         jmp     .return0
  248. .depth_ok:
  249.         pop     eax
  250. ; Hubs use one IN interrupt endpoint for polling the device
  251. ; 2. Locate the descriptor of the interrupt endpoint.
  252. ; Loop over all descriptors owned by this interface.
  253. .lookep:
  254. ; 2a. Skip the current descriptor.
  255.         movzx   eax, [edx+usb_descr.bLength]
  256.         add     edx, eax
  257.         sub     ecx, eax
  258.         jb      .errorep
  259. ; 2b. Length of data left must be at least sizeof.usb_endpoint_descr.
  260.         cmp     ecx, sizeof.usb_endpoint_descr
  261.         jb      .errorep
  262. ; 2c. If we have found another interface descriptor but not found our endpoint,
  263. ; this is an error: all subsequent descriptors belong to that interface
  264. ; (or further interfaces).
  265.         cmp     [edx+usb_endpoint_descr.bDescriptorType], USB_INTERFACE_DESCR
  266.         jz      .errorep
  267. ; 2d. Ignore all interface-related descriptors except endpoint descriptor.
  268.         cmp     [edx+usb_endpoint_descr.bDescriptorType], USB_ENDPOINT_DESCR
  269.         jnz     .lookep
  270. ; 2e. Length of endpoint descriptor must be at least sizeof.usb_endpoint_descr.
  271.         cmp     [edx+usb_endpoint_descr.bLength], sizeof.usb_endpoint_descr
  272.         jb      .errorep
  273. ; 2f. Ignore all endpoints except for INTERRUPT IN.
  274.         cmp     [edx+usb_endpoint_descr.bEndpointAddress], 0
  275.         jge     .lookep
  276.         mov     al, [edx+usb_endpoint_descr.bmAttributes]
  277.         and     al, 3
  278.         cmp     al, INTERRUPT_PIPE
  279.         jnz     .lookep
  280. ; We have located the descriptor for INTERRUPT IN endpoint,
  281. ; the pointer is in edx.
  282. ; 3. Allocate memory for the hub descriptor.
  283. ; Maximum length (assuming 255 downstream ports) is 40 bytes.
  284. ; Allocate 4 extra bytes to keep wMaxPacketSize.
  285. ; 3a. Save registers.
  286.         push    edx
  287. ; 3b. Call the allocator.
  288.         movi    eax, 44
  289.         call    malloc
  290. ; 3c. Restore registers.
  291.         pop     ecx
  292. ; 3d. If failed, say something to the debug board and return error.
  293.         test    eax, eax
  294.         jz      .nomemory
  295. ; 3e. Store the pointer in esi. xchg eax,r32 is one byte shorter than mov.
  296.         xchg    esi, eax
  297. ; 4. Open a pipe for the status endpoint with descriptor found in step 1.
  298.         movzx   eax, [ecx+usb_endpoint_descr.bEndpointAddress]
  299.         movzx   edx, [ecx+usb_endpoint_descr.bInterval]
  300.         movzx   ecx, [ecx+usb_endpoint_descr.wMaxPacketSize]
  301.         test    ecx, (1 shl 11) - 1
  302.         jz      .free
  303.         push    ecx
  304.         stdcall usb_open_pipe, ebx, eax, ecx, INTERRUPT_PIPE, edx
  305.         pop     ecx
  306. ; If failed, free the memory allocated in step 3,
  307. ; say something to the debug board and return error.
  308.         test    eax, eax
  309.         jz      .free
  310. ; 5. Send control query for the hub descriptor,
  311. ; pass status pipe as a callback parameter,
  312. ; allow short packets.
  313.         and     ecx, (1 shl 11) - 1
  314.         mov     [esi+40], ecx
  315.         mov     dword [esi], 0xA0 + \   ; class-specific request
  316.                 (USB_GET_DESCRIPTOR shl 8) + \
  317.                 (0 shl 16) + \          ; descriptor index 0
  318.                 (USB_HUB_DESCRIPTOR shl 24)
  319.         mov     dword [esi+4], 40 shl 16
  320.         stdcall usb_control_async, ebx, esi, esi, 40, usb_hub_got_config, eax, 1
  321. ; 6. If failed, free the memory allocated in step 3,
  322. ; say something to the debug board and return error.
  323.         test    eax, eax
  324.         jz      .free
  325. ; Otherwise, return 1. usb_hub_got_config will overwrite it later.
  326.         xor     eax, eax
  327.         inc     eax
  328.         jmp     .nothing
  329. .free:
  330.         xchg    eax, esi
  331.         call    free
  332.         jmp     .return0
  333. .errorep:
  334.         dbgstr 'Invalid config descriptor for a hub'
  335.         jmp     .return0
  336. .nomemory:
  337.         dbgstr 'No memory for USB hub data'
  338. .return0:
  339.         xor     eax, eax
  340. .nothing:
  341.         pop     esi ebx         ; restore used registers to be stdcall
  342.         retn    12
  343. endp
  344.  
  345. ; This procedure is called when the request for the hub descriptor initiated
  346. ; by usb_hub_init is finished, either successfully or unsuccessfully.
  347. proc usb_hub_got_config stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword
  348.         push    ebx             ; save used registers to be stdcall
  349. ; 1. If failed, say something to the debug board, free the buffer
  350. ; and stop the initialization.
  351.         cmp     [status], 0
  352.         jnz     .invalid
  353. ; 2. The length must be at least sizeof.usb_hub_descr.
  354. ; Note that [length] includes 8 bytes of setup packet.
  355.         cmp     [length], 8 + sizeof.usb_hub_descr
  356.         jb      .invalid
  357. ; 3. Sanity checks for the hub descriptor.
  358.         mov     eax, [buffer]
  359. if USB_DUMP_DESCRIPTORS
  360.         mov     ecx, [length]
  361.         sub     ecx, 8
  362.         DEBUGF 1,'K : hub config:'
  363.         push    eax
  364. @@:
  365.         DEBUGF 1,' %x',[eax]:2
  366.         inc     eax
  367.         dec     ecx
  368.         jnz     @b
  369.         DEBUGF 1,'\n'
  370.         pop     eax
  371. end if
  372.         cmp     [eax+usb_hub_descr.bLength], sizeof.usb_hub_descr
  373.         jb      .invalid
  374.         cmp     [eax+usb_hub_descr.bDescriptorType], USB_HUB_DESCRIPTOR
  375.         jnz     .invalid
  376.         movzx   ecx, [eax+usb_hub_descr.bNbrPorts]
  377.         test    ecx, ecx
  378.         jz      .invalid
  379. ; 4. We use sizeof.usb_hub_descr bytes plus DeviceRemovable info;
  380. ; size of DeviceRemovable is (NumPorts+1) bits, this gives
  381. ; floor(NumPorts/8)+1 bytes. Check that all data are present in the
  382. ; descriptor and were successfully read.
  383.         mov     edx, ecx
  384.         shr     edx, 3
  385.         add     edx, sizeof.usb_hub_descr + 1
  386.         cmp     [eax+usb_hub_descr.bLength], dl
  387.         jb      .invalid
  388.         sub     [length], 8
  389.         cmp     [length], edx
  390.         jb      .invalid
  391. ; 5. Allocate the memory for usb_hub structure.
  392. ; Total size of variable-length data is ALIGN_UP(floor(NumPorts/8)+1+MaxPacketSize,4)+8*NumPorts.
  393.         add     edx, [eax+40]
  394.         add     edx, sizeof.usb_hub - sizeof.usb_hub_descr + 3
  395.         and     edx, not 3
  396.         lea     eax, [edx+ecx*8]
  397.         push    ecx edx
  398.         call    malloc
  399.         pop     edx ecx
  400.         test    eax, eax
  401.         jz      .nomemory
  402.         xchg    eax, ebx
  403. ; 6. Fill usb_hub structure.
  404.         mov     [ebx+usb_hub.NumPorts], ecx
  405.         add     edx, ebx
  406.         mov     [ebx+usb_hub.ConnectedDevicesPtr], edx
  407.         mov     eax, [pipe]
  408.         mov     [ebx+usb_hub.ConfigPipe], eax
  409.         mov     edx, [eax+usb_pipe.Controller]
  410.         mov     [ebx+usb_hub.Controller], edx
  411.         mov     eax, [calldata]
  412.         mov     [ebx+usb_hub.StatusPipe], eax
  413.         push    esi edi
  414.         mov     esi, [buffer]
  415.         mov     eax, [esi+40]
  416.         mov     [ebx+usb_hub.MaxPacketSize], eax
  417. ; The following commands load bNbrPorts, wHubCharacteristics, bPwrOn2PwrGood.
  418.         mov     edx, dword [esi+usb_hub_descr.bNbrPorts]
  419.         mov     dl, 0
  420. ; The following command zeroes AccStatusChange and stores
  421. ; HubCharacteristics and PowerOnInterval.
  422.         mov     dword [ebx+usb_hub.AccStatusChange], edx
  423.         xor     eax, eax
  424.         mov     [ebx+usb_hub.Actions], eax
  425. ; Copy DeviceRemovable data.
  426.         lea     edi, [ebx+sizeof.usb_hub]
  427.         add     esi, sizeof.usb_hub_descr
  428.         mov     edx, ecx
  429.         shr     ecx, 3
  430.         inc     ecx
  431.         rep movsb
  432.         mov     [ebx+usb_hub.StatusChangePtr], edi
  433. ; Zero ConnectedDevices.
  434.         mov     edi, [ebx+usb_hub.ConnectedDevicesPtr]
  435.         mov     ecx, edx
  436.         rep stosd
  437.         mov     [ebx+usb_hub.ConnectedTimePtr], edi
  438. ; Set ConnectedTime to -1.
  439.         dec     eax
  440.         mov     ecx, edx
  441.         rep stosd
  442.         pop     edi esi
  443. ; 7. Replace value of 1 returned from usb_hub_init to the real value.
  444. ; Note: hubs are part of the core USB code, so this code can work with
  445. ; internals of other parts. Another way, the only possible one for external
  446. ; drivers, is to use two memory allocations: one (returned from AddDevice and
  447. ; fixed after that) for pointer, another for real data. That would work also,
  448. ; but wastes one allocation.
  449.         mov     eax, [pipe]
  450.         mov     eax, [eax+usb_pipe.DeviceData]
  451.         add     eax, [eax+usb_device_data.Interfaces]
  452. .scan:
  453.         cmp     [eax+usb_interface_data.DriverData], 1
  454.         jnz     @f
  455.         cmp     [eax+usb_interface_data.DriverFunc], usb_hub_pseudosrv - USBSRV.usb_func
  456.         jz      .scan_found
  457. @@:
  458.         add     eax, sizeof.usb_interface_data
  459.         jmp     .scan
  460. .scan_found:
  461.         mov     [eax+usb_interface_data.DriverData], ebx
  462. ; 8. Insert the hub structure to the tail of the overall list of all hubs.
  463.         mov     ecx, usb_hubs_list
  464.         mov     edx, [ecx+usb_hub.Prev]
  465.         mov     [ecx+usb_hub.Prev], ebx
  466.         mov     [edx+usb_hub.Next], ebx
  467.         mov     [ebx+usb_hub.Prev], edx
  468.         mov     [ebx+usb_hub.Next], ecx
  469. ; 9. Start powering up all ports.
  470.         DEBUGF 1,'K : found hub with %d ports\n',[ebx+usb_hub.NumPorts]
  471.         lea     eax, [ebx+usb_hub.ConfigBuffer]
  472.         xor     ecx, ecx
  473.         mov     dword [eax], 23h + \    ; class-specific request to hub port
  474.                 (USB_SET_FEATURE shl 8) + \
  475.                 (PORT_POWER shl 16)
  476.         mov     edx, [ebx+usb_hub.NumPorts]
  477.         mov     dword [eax+4], edx
  478.         stdcall usb_control_async, [ebx+usb_hub.ConfigPipe], eax, ecx, ecx, usb_hub_port_powered, ebx, ecx
  479. .freebuf:
  480. ; 10. Free the buffer for hub descriptor and return.
  481.         mov     eax, [buffer]
  482.         call    free
  483.         pop     ebx             ; restore used registers to be stdcall
  484.         ret
  485. .nomemory:
  486.         dbgstr 'No memory for USB hub data'
  487.         jmp     .freebuf
  488. .invalid:
  489.         dbgstr 'Invalid hub descriptor'
  490.         jmp     .freebuf
  491. endp
  492.  
  493. ; This procedure is called when the request to power up some port is completed,
  494. ; either successfully or unsuccessfully.
  495. proc usb_hub_port_powered stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword
  496. ; 1. Check whether the operation was successful.
  497. ; If not, say something to the debug board and ssstop the initialization.
  498.         cmp     [status], 0
  499.         jnz     .invalid
  500. ; 2. Check whether all ports were powered.
  501. ; If so, go to 4. Otherwise, proceed to 3.
  502.         mov     eax, [calldata]
  503.         dec     dword [eax+usb_hub.ConfigBuffer+4]
  504.         jz      .done
  505. ; 3. Power up the next port and return.
  506.         lea     edx, [eax+usb_hub.ConfigBuffer]
  507.         xor     ecx, ecx
  508.         stdcall usb_control_async, [eax+usb_hub.ConfigPipe], edx, ecx, ecx, usb_hub_port_powered, eax, ecx
  509. .nothing:
  510.         ret
  511. .done:
  512. ; 4. All ports were powered.
  513. ; The hub requires some delay until power will be stable, the delay value
  514. ; is provided in the hub descriptor; we have copied that value to
  515. ; usb_hub.PowerOnInterval. Note the time and set the corresponding flag
  516. ; for usb_hub_process_deferred.
  517.         mov     ecx, [timer_ticks]
  518.         mov     [eax+usb_hub.PoweredOnTime], ecx
  519.         or      [eax+usb_hub.Actions], HUB_WAIT_POWERED
  520.         jmp     .nothing
  521. .invalid:
  522.         dbgstr 'Error while powering hub ports'
  523.         jmp     .nothing
  524. endp
  525.  
  526. ; Requests notification about any changes in hub/ports configuration.
  527. ; Called when initial configuration is done and when a previous notification
  528. ; has been processed.
  529. proc usb_hub_wait_change
  530.         stdcall usb_normal_transfer_async, [eax+usb_hub.StatusPipe], \
  531.                 [eax+usb_hub.StatusChangePtr], [eax+usb_hub.MaxPacketSize], usb_hub_changed, eax, 1
  532.         ret
  533. endp
  534.  
  535. ; This procedure is called when something has changed on the hub.
  536. proc usb_hub_changed stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword
  537. ;       DEBUGF 1,'K : [%d] int pipe for hub %x\n',[timer_ticks],[calldata]
  538. ; 1. Check whether our request has failed.
  539. ; If so, say something to the debug board and stop processing notifications.
  540.         xor     ecx, ecx
  541.         cmp     [status], ecx
  542.         jnz     .failed
  543. ; 2. If no data were retrieved, restart waiting.
  544.         mov     eax, [calldata]
  545.         cmp     [length], ecx
  546.         jz      .continue
  547. ; 3. If size of data retrieved is less than maximal, pad with zeroes;
  548. ; this corresponds to 'state of other ports was not changed'
  549.         mov     ecx, [eax+usb_hub.NumPorts]
  550.         shr     ecx, 3
  551.         inc     ecx
  552.         sub     ecx, [length]
  553.         jbe     .restart
  554.         push    eax edi
  555.         mov     edi, [buffer]
  556.         add     edi, [length]
  557.         xor     eax, eax
  558.         rep stosb
  559.         pop     edi eax
  560. .restart:
  561. ; State of some elements of the hub was changed.
  562. ; Find the first element that was changed,
  563. ; ask the hub about nature of the change,
  564. ; clear the corresponding change,
  565. ; reask the hub about status+change (it is possible that another change
  566. ; occurs between the first ask and clearing the change; we won't see that
  567. ; change, so we need to query the status after clearing the change),
  568. ; continue two previous steps until nothing changes,
  569. ; process all changes which were registered.
  570. ; When all changes for one element will be processed, return to here and look
  571. ; for other changed elements.
  572.         mov     edx, [eax+usb_hub.StatusChangePtr]
  573. ; We keep all observed changes in the special var usb_hub.AccStatusChange;
  574. ; it will be logical OR of all observed StatusChange's.
  575. ; 4. No observed changes yet, zero usb_hub.AccStatusChange.
  576.         xor     ecx, ecx
  577.         mov     [eax+usb_hub.AccStatusChange], cl
  578. ; 5. Test whether there was a change in the hub itself.
  579. ; If so, query hub state.
  580.         btr     dword [edx], ecx
  581.         jnc     .no_hub_change
  582. .next_hub_change:
  583. ;       DEBUGF 1,'K : [%d] querying status of hub %x\n',[timer_ticks],eax
  584.         lea     edx, [eax+usb_hub.ChangeConfigBuffer]
  585.         lea     ecx, [eax+usb_hub.StatusData]
  586.         mov     dword [edx], 0A0h + \   ; class-specific request from hub itself
  587.                 (USB_GET_STATUS shl 8)
  588.         mov     dword [edx+4], 4 shl 16 ; get 4 bytes
  589.         stdcall usb_control_async, [eax+usb_hub.ConfigPipe], edx, ecx, 4, usb_hub_status, eax, 0
  590.         jmp     .nothing
  591. .no_hub_change:
  592. ; 6. Find the first port with changed state and clear the corresponding bit
  593. ; (so next scan after .restart will not consider this port again).
  594. ; If found, go to 8. Otherwise, advance to 7.
  595.         inc     ecx
  596. .test_port_change:
  597.         btr     [edx], ecx
  598.         jc      .found_port_change
  599.         inc     ecx
  600.         cmp     ecx, [eax+usb_hub.NumPorts]
  601.         jbe     .test_port_change
  602. .continue:
  603. ; 7. All changes have been processed. Wait for next notification.
  604.         call    usb_hub_wait_change
  605. .nothing:
  606.         ret
  607. .found_port_change:
  608.         mov     dword [eax+usb_hub.ChangeConfigBuffer+4], ecx
  609. .next_port_change:
  610. ; 8. Query port state. Continue work in usb_hub_port_status callback.
  611. ;       movzx   ecx, [eax+usb_hub.ChangeConfigBuffer+4]
  612. ;       dec     ecx
  613. ;       DEBUGF 1,'K : [%d] querying status of hub %x port %d\n',[timer_ticks],eax,ecx
  614.         lea     edx, [eax+usb_hub.ChangeConfigBuffer]
  615.         mov     dword [edx], 0A3h + \   ; class-specific request from hub port
  616.                 (USB_GET_STATUS shl 8)
  617.         mov     byte [edx+6], 4         ; data length = 4 bytes
  618.         lea     ecx, [eax+usb_hub.StatusData]
  619.         stdcall usb_control_async, [eax+usb_hub.ConfigPipe], edx, ecx, 4, usb_hub_port_status, eax, 0
  620.         jmp     .nothing
  621. .failed:
  622.         cmp     [status], USB_STATUS_CLOSED
  623.         jz      .nothing
  624.         dbgstr 'Querying hub notification failed'
  625.         jmp     .nothing
  626. endp
  627.  
  628. ; This procedure is called when the request of hub status is completed,
  629. ; either successfully or unsuccessfully.
  630. proc usb_hub_status stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword
  631. ; 1. Check whether our request has failed.
  632. ; If so, say something to the debug board and stop processing notifications.
  633.         cmp     [status], 0
  634.         jnz     .failed
  635. ; 2. Accumulate observed changes.
  636.         mov     eax, [calldata]
  637.         mov     dl, byte [eax+usb_hub.StatusChange]
  638.         or      [eax+usb_hub.AccStatusChange], dl
  639. .next_change:
  640. ; 3. Find the first change. If found, advance to 4. Otherwise, go to 5.
  641.         mov     cl, C_HUB_OVER_CURRENT
  642.         btr     dword [eax+usb_hub.StatusChange], 1
  643.         jc      .clear_hub_change
  644.         mov     cl, C_HUB_LOCAL_POWER
  645.         btr     dword [eax+usb_hub.StatusChange], 0
  646.         jnc     .final
  647. .clear_hub_change:
  648. ; 4. Clear the change and continue in usb_hub_change_cleared callback.
  649.         lea     edx, [eax+usb_hub.ChangeConfigBuffer]
  650.         mov     dword [edx], 20h + \    ; class-specific request to hub itself
  651.                 (USB_CLEAR_FEATURE shl 8)
  652.         mov     [edx+2], cl     ; feature selector
  653.         and     dword [edx+4], 0
  654.         stdcall usb_control_async, [eax+usb_hub.ConfigPipe], edx, 0, 0, usb_hub_change_cleared, eax, 0
  655. .nothing:
  656.         ret
  657. .final:
  658. ; 5. All changes cleared and accumulated, now process them.
  659. ; Note: that needs work.
  660.         DEBUGF 1,'K : hub status %x\n',[eax+usb_hub.AccStatusChange]:2
  661.         test    [eax+usb_hub.AccStatusChange], 1
  662.         jz      .no_local_power
  663.         test    [eax+usb_hub.StatusData], 1
  664.         jz      .local_power_lost
  665.         dbgstr 'Hub local power is now good'
  666.         jmp     .no_local_power
  667. .local_power_lost:
  668.         dbgstr 'Hub local power is now lost'
  669. .no_local_power:
  670.         test    [eax+usb_hub.AccStatusChange], 2
  671.         jz      .no_overcurrent
  672.         test    [eax+usb_hub.StatusData], 2
  673.         jz      .no_overcurrent
  674.         dbgstr 'Hub global overcurrent'
  675. .no_overcurrent:
  676. ; 6. Process possible changes for other ports.
  677.         jmp     usb_hub_changed.restart
  678. .failed:
  679.         dbgstr 'Querying hub status failed'
  680.         jmp     .nothing
  681. endp
  682.  
  683. ; This procedure is called when the request to clear hub change is completed,
  684. ; either successfully or unsuccessfully.
  685. proc usb_hub_change_cleared stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword
  686. ; 1. Check whether our request has failed.
  687. ; If so, say something to the debug board and stop processing notifications.
  688.         cmp     [status], 0
  689.         jnz     .failed
  690. ; 2. If there is a change which was observed, but not yet cleared,
  691. ; go to the code which clears it.
  692.         mov     eax, [calldata]
  693.         cmp     [eax+usb_hub.StatusChange], 0
  694.         jnz     usb_hub_status.next_change
  695. ; 3. Otherwise, go to the code which queries the status.
  696.         jmp     usb_hub_changed.next_hub_change
  697. .failed:
  698.         dbgstr 'Clearing hub change failed'
  699.         ret
  700. endp
  701.  
  702. ; This procedure is called when the request of port status is completed,
  703. ; either successfully or unsuccessfully.
  704. proc usb_hub_port_status stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword
  705. ; 1. Check whether our request has failed.
  706. ; If so, say something to the debug board and stop processing notifications.
  707.         cmp     [status], 0
  708.         jnz     .failed
  709. ; 2. Accumulate observed changes.
  710.         mov     eax, [calldata]
  711. ;       movzx   ecx, [eax+usb_hub.ChangeConfigBuffer+4]
  712. ;       dec     ecx
  713. ;       DEBUGF 1,'K : [%d] hub %x port %d status %x change %x\n',[timer_ticks],eax,ecx,[eax+usb_hub.StatusData]:4,[eax+usb_hub.StatusChange]:4
  714.         mov     dl, byte [eax+usb_hub.StatusChange]
  715.         or      [eax+usb_hub.AccStatusChange], dl
  716. .next_change:
  717. ; 3. Find the first change. If found, advance to 4. Otherwise, go to 5.
  718. ; Ignore change in reset status; it is cleared by synchronous code
  719. ; (usb_hub_process_deferred), so avoid unnecessary interference.
  720. ;       mov     cl, C_PORT_RESET
  721.         btr     dword [eax+usb_hub.StatusChange], PORT_RESET
  722. ;       jc      .clear_port_change
  723.         mov     cl, C_PORT_OVER_CURRENT
  724.         btr     dword [eax+usb_hub.StatusChange], PORT_OVER_CURRENT
  725.         jc      .clear_port_change
  726.         mov     cl, C_PORT_SUSPEND
  727.         btr     dword [eax+usb_hub.StatusChange], PORT_SUSPEND
  728.         jc      .clear_port_change
  729.         mov     cl, C_PORT_ENABLE
  730.         btr     dword [eax+usb_hub.StatusChange], PORT_ENABLE
  731.         jc      .clear_port_change
  732.         mov     cl, C_PORT_CONNECTION
  733.         btr     dword [eax+usb_hub.StatusChange], PORT_CONNECTION
  734.         jnc     .final
  735. .clear_port_change:
  736. ; 4. Clear the change and continue in usb_hub_port_changed callback.
  737.         call    usb_hub_clear_port_change
  738.         jmp     .nothing
  739. .final:
  740. ; All changes cleared and accumulated, now process them.
  741.         movzx   ecx, byte [eax+usb_hub.ChangeConfigBuffer+4]
  742.         dec     ecx
  743.         DEBUGF 1,'K : final: hub %x port %d status %x change %x\n',eax,ecx,[eax+usb_hub.StatusData]:4,[eax+usb_hub.AccStatusChange]:2
  744. ; 5. Process connect/disconnect events.
  745. ; 5a. Test whether there is such event.
  746.         test    byte [eax+usb_hub.AccStatusChange], 1 shl PORT_CONNECTION
  747.         jz      .nodisconnect
  748. ; 5b. If there was a connected device, notify the main code about disconnect.
  749.         push    ebx
  750.         mov     edx, [eax+usb_hub.ConnectedDevicesPtr]
  751.         xor     ebx, ebx
  752.         xchg    ebx, [edx+ecx*4]
  753.         test    ebx, ebx
  754.         jz      @f
  755.         push    eax ecx
  756.         call    usb_device_disconnected
  757.         pop     ecx eax
  758. @@:
  759.         pop     ebx
  760. ; 5c. If the disconnect event corresponds to the port which is currently
  761. ; resetting, then another request from synchronous code could be in the fly,
  762. ; so aborting reset immediately would lead to problems with those requests.
  763. ; Thus, just set the corresponding status and let the synchronous code process.
  764.         test    byte [eax+usb_hub.Actions], (HUB_RESET_SIGNAL or HUB_RESET_RECOVERY)
  765.         jz      @f
  766.         mov     edx, [eax+usb_hub.Controller]
  767.         cmp     [edx+usb_controller.ResettingPort], cl
  768.         jnz     @f
  769.         mov     [edx+usb_controller.ResettingStatus], -1
  770. @@:
  771. ; 5d. If the current status is 'connected', store the current time as connect
  772. ; time and set the corresponding bit for usb_hub_process_deferred.
  773. ; Otherwise, set connect time to -1.
  774. ; If current time is -1, pretend that the event occured one tick later and
  775. ; store zero.
  776.         mov     edx, [eax+usb_hub.ConnectedTimePtr]
  777.         test    byte [eax+usb_hub.StatusData], 1 shl PORT_CONNECTION
  778.         jz      .disconnected
  779.         or      [eax+usb_hub.Actions], HUB_WAIT_CONNECT
  780.         push    eax
  781.         call    usb_hub_store_connected_time
  782.         pop     eax
  783.         jmp     @f
  784. .disconnected:
  785.         or      dword [edx+ecx*4], -1
  786. @@:
  787. .nodisconnect:
  788. ; 6. Process port disabling.
  789.         test    [eax+usb_hub.AccStatusChange], 1 shl PORT_ENABLE
  790.         jz      .nodisable
  791.         test    byte [eax+usb_hub.StatusData], 1 shl PORT_ENABLE
  792.         jnz     .nodisable
  793. ; Note: that needs work.
  794.         dbgstr 'Port disabled'
  795. .nodisable:
  796. ; 7. Process port overcurrent.
  797.         test    [eax+usb_hub.AccStatusChange], 1 shl PORT_OVER_CURRENT
  798.         jz      .noovercurrent
  799.         test    byte [eax+usb_hub.StatusData], 1 shl PORT_OVER_CURRENT
  800.         jz      .noovercurrent
  801. ; Note: that needs work.
  802.         dbgstr 'Port over-current'
  803. .noovercurrent:
  804. ; 8. Process possible changes for other ports.
  805.         jmp     usb_hub_changed.restart
  806. .failed:
  807.         dbgstr 'Querying port status failed'
  808. .nothing:
  809.         ret
  810. endp
  811.  
  812. ; Helper procedure to store current time in ConnectedTime,
  813. ; advancing -1 to zero if needed.
  814. proc usb_hub_store_connected_time
  815.         mov     eax, [timer_ticks]
  816. ; transform -1 to 0, leave other values as is
  817.         cmp     eax, -1
  818.         sbb     eax, -1
  819.         mov     [edx+ecx*4], eax
  820.         ret
  821. endp
  822.  
  823. ; Helper procedure for several parts of hub code.
  824. ; Sends a request to clear the given feature of the port.
  825. ; eax -> usb_hub, cl = feature;
  826. ; as is should be called from async code, sync code should set
  827. ; edx to ConfigBuffer and call usb_hub_clear_port_change.buffer;
  828. ; port number (1-based) should be filled in [edx+4] by previous requests.
  829. proc usb_hub_clear_port_change
  830.         lea     edx, [eax+usb_hub.ChangeConfigBuffer]
  831. .buffer:
  832. ;       push    edx
  833. ;       movzx   edx, byte [edx+4]
  834. ;       dec     edx
  835. ;       DEBUGF 1,'K : [%d] hub %x port %d clear feature %d\n',[timer_ticks],eax,edx,cl
  836. ;       pop     edx
  837.         mov     dword [edx], 23h + \    ; class-specific request to hub port
  838.                 (USB_CLEAR_FEATURE shl 8)
  839.         mov     byte [edx+2], cl
  840.         and     dword [edx+4], 0xFF
  841.         stdcall usb_control_async, [eax+usb_hub.ConfigPipe], edx, edx, 0, usb_hub_port_changed, eax, 0
  842.         ret
  843. endp
  844.  
  845. ; This procedure is called when the request to clear port change is completed,
  846. ; either successfully or unsuccessfully.
  847. proc usb_hub_port_changed stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword
  848. ; 1. Check whether our request has failed.
  849. ; If so, say something to the debug board and stop processing notifications.
  850.         cmp     [status], 0
  851.         jnz     .failed
  852. ; 2. If the request was originated by synchronous code, no further processing
  853. ; is required.
  854.         mov     eax, [calldata]
  855.         lea     edx, [eax+usb_hub.ConfigBuffer]
  856.         cmp     [buffer], edx
  857.         jz      .nothing
  858. ; 3. If there is a change which was observed, but not yet cleared,
  859. ; go to the code which clears it.
  860.         cmp     [eax+usb_hub.StatusChange], 0
  861.         jnz     usb_hub_port_status.next_change
  862. ; 4. Otherwise, go to the code which queries the status.
  863.         jmp     usb_hub_changed.next_port_change
  864. .failed:
  865.         dbgstr 'Clearing port change failed'
  866. .nothing:
  867.         ret
  868. endp
  869.  
  870. ; This procedure is called in the USB thread from usb_thread_proc,
  871. ; contains synchronous code which should be activated at certain time
  872. ; (e.g. reset a recently connected device after debounce interval 100ms).
  873. ; Returns the number of ticks when it should be called next time.
  874. proc usb_hub_process_deferred
  875. ; 1. Top-of-stack will contain return value; initialize to infinite timeout.
  876.         push    -1
  877. ; 2. If wait for stable power is active, then
  878. ; either reschedule wakeup (if time is not over)
  879. ; or start processing notifications.
  880.         test    byte [esi+usb_hub.Actions], HUB_WAIT_POWERED
  881.         jz      .no_powered
  882.         movzx   eax, [esi+usb_hub.PowerOnInterval]
  883. ; three following instructions are equivalent to edx = ceil(eax / 5) + 1
  884. ; 1 extra tick is added to make sure that the interval is at least as needed
  885. ; (it is possible that PoweredOnTime was set just before timer interrupt, and
  886. ; this test goes on just after timer interrupt)
  887.         add     eax, 9
  888. ; two following instructions are equivalent to edx = floor(eax / 5)
  889. ; for any 0 <= eax < 40000000h
  890.         mov     ecx, 33333334h
  891.         mul     ecx
  892.         mov     eax, [timer_ticks]
  893.         sub     eax, [esi+usb_hub.PoweredOnTime]
  894.         sub     eax, edx
  895.         jge     .powered_on
  896.         neg     eax
  897.         pop     ecx
  898.         push    eax
  899.         jmp     .no_powered
  900. .powered_on:
  901.         and     [esi+usb_hub.Actions], not HUB_WAIT_POWERED
  902.         mov     eax, esi
  903.         call    usb_hub_wait_change
  904. .no_powered:
  905. ; 3. If reset is pending, check whether we can start it and start it, if so.
  906.         test    byte [esi+usb_hub.Actions], HUB_RESET_WAITING
  907.         jz      .no_wait_reset
  908.         mov     eax, [esi+usb_hub.Controller]
  909.         cmp     [eax+usb_controller.ResettingPort], -1
  910.         jnz     .no_wait_reset
  911.         call    usb_hub_initiate_reset
  912. .no_wait_reset:
  913. ; 4. If reset signalling is active, wait for end of reset signalling
  914. ; and schedule wakeup in 1 tick.
  915.         test    byte [esi+usb_hub.Actions], HUB_RESET_SIGNAL
  916.         jz      .no_resetting_port
  917. ; It has no sense to query status several times per tick.
  918.         mov     eax, [timer_ticks]
  919.         cmp     eax, [esi+usb_hub.ResetTime]
  920.         jz      @f
  921.         mov     [esi+usb_hub.ResetTime], eax
  922.         movzx   ecx, byte [esi+usb_hub.ConfigBuffer+4]
  923.         mov     eax, usb_hub_resetting_port_status
  924.         call    usb_hub_query_port_status
  925. @@:
  926.         pop     eax
  927.         push    1
  928. .no_resetting_port:
  929. ; 5. If reset recovery is active and time is not over, reschedule wakeup.
  930.         test    byte [esi+usb_hub.Actions], HUB_RESET_RECOVERY
  931.         jz      .no_reset_recovery
  932.         mov     eax, [timer_ticks]
  933.         sub     eax, [esi+usb_hub.ResetTime]
  934.         sub     eax, USB_RESET_RECOVERY_TIME
  935.         jge     .reset_done
  936.         neg     eax
  937.         cmp     [esp], eax
  938.         jb      @f
  939.         mov     [esp], eax
  940. @@:
  941.         jmp     .no_reset_recovery
  942. .reset_done:
  943. ; 6. If reset recovery is active and time is over, clear 'reset recovery' flag,
  944. ; notify other code about a new device and let it do further steps.
  945. ; If that fails, stop reset process for this port and disable that port.
  946.         and     [esi+usb_hub.Actions], not HUB_RESET_RECOVERY
  947. ; Bits 9-10 of port status encode port speed.
  948. ; If PORT_LOW_SPEED is set, the device is low-speed. Otherwise,
  949. ; PORT_HIGH_SPEED bit distinguishes full-speed and high-speed devices.
  950. ; This corresponds to values of USB_SPEED_FS=0, USB_SPEED_LS=1, USB_SPEED_HS=2.
  951.         mov     eax, dword [esi+usb_hub.ResetStatusData]
  952.         shr     eax, PORT_LOW_SPEED
  953.         and     eax, 3
  954.         test    al, 1
  955.         jz      @f
  956.         mov     al, 1
  957. @@:
  958. ;       movzx   ecx, [esi+usb_hub.ConfigBuffer+4]
  959. ;       dec     ecx
  960. ;       DEBUGF 1,'K : [%d] hub %x port %d speed %d\n',[timer_ticks],esi,ecx,eax
  961.         push    esi
  962.         mov     esi, [esi+usb_hub.Controller]
  963.         cmp     [esi+usb_controller.ResettingStatus], -1
  964.         jz      .disconnected_while_reset
  965.         mov     edx, [esi+usb_controller.HardwareFunc]
  966.         call    [edx+usb_hardware_func.NewDevice]
  967.         pop     esi
  968.         test    eax, eax
  969.         jnz     .no_reset_recovery
  970.         mov     eax, esi
  971.         call    usb_hub_disable_resetting_port
  972.         jmp     .no_reset_recovery
  973. .disconnected_while_reset:
  974.         pop     esi
  975.         mov     eax, esi
  976.         call    usb_hub_reset_aborted
  977. .no_reset_recovery:
  978. ; 7. Handle recent connection events.
  979. ; Note: that should be done after step 6, because step 6 can clear
  980. ; HUB_RESET_IN_PROGRESS flag.
  981. ; 7a. Test whether there is such an event pending. If no, skip this step.
  982.         test    byte [esi+usb_hub.Actions], HUB_WAIT_CONNECT
  983.         jz      .no_wait_connect
  984. ; 7b. If we have started reset process for another port in the same hub,
  985. ; skip this step: the buffer for config requests can be used for that port.
  986.         test    byte [esi+usb_hub.Actions], HUB_RESET_IN_PROGRESS
  987.         jnz     .no_wait_connect
  988. ; 7c. Clear flag 'there are connection events which should be processed'.
  989. ; If there are another connection events, this flag will be set again.
  990.         and     [esi+usb_hub.Actions], not HUB_WAIT_CONNECT
  991. ; 7d. Prepare for loop over all ports.
  992.         xor     ecx, ecx
  993. .test_wait_connect:
  994. ; 7e. For every port test for recent connection event.
  995. ; If none, continue the loop for the next port.
  996.         mov     edx, [esi+usb_hub.ConnectedTimePtr]
  997.         mov     eax, [edx+ecx*4]
  998.         cmp     eax, -1
  999.         jz      .next_wait_connect
  1000.         or      [esi+usb_hub.Actions], HUB_WAIT_CONNECT
  1001. ; 7f. Test whether initial delay is over.
  1002.         sub     eax, [timer_ticks]
  1003.         neg     eax
  1004.         sub     eax, USB_CONNECT_DELAY
  1005.         jge     .connect_delay_over
  1006. ; 7g. The initial delay is not over;
  1007. ; set the corresponding flag again, reschedule wakeup and continue the loop.
  1008.         neg     eax
  1009.         cmp     [esp], eax
  1010.         jb      @f
  1011.         mov     [esp], eax
  1012. @@:
  1013.         jmp     .next_wait_connect
  1014. .connect_delay_over:
  1015. ; The initial delay is over.
  1016. ; It is possible that there was disconnect event during that delay, probably
  1017. ; with connect event after that. If so, we should restart the waiting. However,
  1018. ; on the hardware level connect/disconnect events from hubs are implemented
  1019. ; using polling with interval selected by the hub, so it is possible that
  1020. ; we have not yet observed that disconnect event.
  1021. ; Thus, we query port status+change data before all further processing.
  1022. ; 7h. Send the request for status+change data.
  1023.         push    ecx
  1024. ; Hub requests expect 1-based port number, not zero-based we operate with.
  1025.         inc     ecx
  1026.         mov     eax, usb_hub_connect_port_status
  1027.         call    usb_hub_query_port_status
  1028.         pop     ecx
  1029. ; 3i. If request has been submitted successfully, set the flag
  1030. ; 'reset in progress, config buffer is owned by reset process' and break
  1031. ; from the loop.
  1032.         test    eax, eax
  1033.         jz      .next_wait_connect
  1034.         or      [esi+usb_hub.Actions], HUB_RESET_IN_PROGRESS
  1035.         jmp     .no_wait_connect
  1036. .next_wait_connect:
  1037. ; 7j. Continue the loop for next port.
  1038.         inc     ecx
  1039.         cmp     ecx, [esi+usb_hub.NumPorts]
  1040.         jb      .test_wait_connect
  1041. .no_wait_connect:
  1042. ; 8. Pop return value from top-of-stack and return.
  1043.         pop     eax
  1044.         ret
  1045. endp
  1046.  
  1047. ; Helper procedure for other code. Called when reset process is aborted.
  1048. proc usb_hub_reset_aborted
  1049. ; Clear 'reset in progress' flag and test for other devices which could be
  1050. ; waiting for reset.
  1051.         and     [eax+usb_hub.Actions], not HUB_RESET_IN_PROGRESS
  1052.         push    esi
  1053.         mov     esi, [eax+usb_hub.Controller]
  1054.         call    usb_test_pending_port
  1055.         pop     esi
  1056.         ret
  1057. endp
  1058.  
  1059. ; Helper procedure for usb_hub_process_deferred.
  1060. ; Sends a request to query port status.
  1061. ; esi -> usb_hub, eax = callback, ecx = 1-based port.
  1062. proc usb_hub_query_port_status
  1063. ;       dec     ecx
  1064. ;       DEBUGF 1,'K : [%d] [main] hub %x port %d query status\n',[timer_ticks],esi,ecx
  1065. ;       inc     ecx
  1066.         add     ecx, 4 shl 16           ; data length = 4
  1067.         lea     edx, [esi+usb_hub.ConfigBuffer]
  1068.         mov     dword [edx], 0A3h + \   ; class-specific request from hub port
  1069.                 (USB_GET_STATUS shl 8)
  1070.         mov     dword [edx+4], ecx
  1071.         lea     ecx, [esi+usb_hub.ResetStatusData]
  1072.         stdcall usb_control_async, [esi+usb_hub.ConfigPipe], edx, ecx, 4, eax, esi, 0
  1073.         ret
  1074. endp
  1075.  
  1076. ; This procedure is called when the request to query port status
  1077. ; initiated by usb_hub_process_deferred for testing connection is completed,
  1078. ; either successfully or unsuccessfully.
  1079. proc usb_hub_connect_port_status stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword
  1080.         push    esi     ; save used register to be stdcall
  1081.         mov     eax, [calldata]
  1082.         mov     esi, [pipe]
  1083. ;       movzx   ecx, [eax+usb_hub.ConfigBuffer+4]
  1084. ;       dec     ecx
  1085. ;       DEBUGF 1,'K : [%d] [connect test] hub %x port %d status %x change %x\n',[timer_ticks],eax,ecx,[eax+usb_hub.ResetStatusData]:4,[eax+usb_hub.ResetStatusChange]:4
  1086. ; 1. In any case, clear 'reset in progress' flag.
  1087. ; If everything is ok, it would be set again.
  1088.         and     [eax+usb_hub.Actions], not HUB_RESET_IN_PROGRESS
  1089. ; 2. If the request has failed, stop reset process.
  1090.         cmp     [status], 0
  1091.         jnz     .nothing
  1092.         mov     edx, [eax+usb_hub.ConnectedTimePtr]
  1093.         movzx   ecx, byte [eax+usb_hub.ConfigBuffer+4]
  1094.         dec     ecx
  1095. ; 3. Test whether there was a disconnect event.
  1096.         test    byte [eax+usb_hub.ResetStatusChange], 1 shl PORT_CONNECTION
  1097.         jz      .reset
  1098. ; 4. There was a disconnect event.
  1099. ; There is another handler of connect/disconnect events, usb_hub_port_status.
  1100. ; However, we do not know whether it has already processed this event
  1101. ; or it will process it sometime later.
  1102. ; If ConnectedTime is -1, then another handler has already run,
  1103. ; there was no connection event, so just leave the value as -1.
  1104. ; Otherwise, there are two possibilities: either another handler has not yet
  1105. ; run (which is quite likely), or there was a connection event and the other
  1106. ; handler has run exactly while our request was processed (otherwise our
  1107. ; request would not been submitted; this is quite unlikely due to timing
  1108. ; requirements, but not impossible). In this case, set ConnectedTime to the
  1109. ; current time: in the likely case it prevents usb_hub_process_deferred from immediate
  1110. ; issuing of another requests (which would be just waste of time);
  1111. ; in the unlikely case it is still correct (although slightly increases
  1112. ; the debounce interval).
  1113.         cmp     dword [edx+ecx*4], -1
  1114.         jz      .nothing
  1115.         call    usb_hub_store_connected_time
  1116.         jmp     .nothing
  1117. .reset:
  1118. ; 5. The device remained connected for the entire debounce interval;
  1119. ; we can proceed with initialization.
  1120. ; Clear connected time for this port and notify usb_hub_process_deferred that
  1121. ; the new port is waiting for reset.
  1122.         or      dword [edx+ecx*4], -1
  1123.         or      [eax+usb_hub.Actions], HUB_RESET_IN_PROGRESS + HUB_RESET_WAITING
  1124. .nothing:
  1125.         pop     esi     ; restore used register to be stdcall
  1126.         ret
  1127. endp
  1128.  
  1129. ; This procedure is called when the request to query port status
  1130. ; initiated by usb_hub_process_deferred for testing reset status is completed,
  1131. ; either successfully or unsuccessfully.
  1132. proc usb_hub_resetting_port_status stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword
  1133. ; 1. If the request has failed, do nothing.
  1134.         cmp     [status], 0
  1135.         jnz     .nothing
  1136. ; 2. If reset signalling is still active, do nothing.
  1137.         mov     eax, [calldata]
  1138. ;       movzx   ecx, [eax+usb_hub.ConfigBuffer+4]
  1139. ;       dec     ecx
  1140. ;       DEBUGF 1,'K : hub %x port %d ResetStatusData = %x change = %x\n',eax,ecx,[eax+usb_hub.ResetStatusData]:4,[eax+usb_hub.ResetStatusChange]:4
  1141.         test    byte [eax+usb_hub.ResetStatusData], 1 shl PORT_RESET
  1142.         jnz     .nothing
  1143. ; 3. Store the current time to start reset recovery interval
  1144. ; and clear 'reset signalling active' flag.
  1145.         mov     edx, [timer_ticks]
  1146.         mov     [eax+usb_hub.ResetTime], edx
  1147.         and     [eax+usb_hub.Actions], not HUB_RESET_SIGNAL
  1148. ; 4. If the device has not been disconnected, set 'reset recovery active' bit.
  1149. ; Otherwise, terminate reset process.
  1150.         test    byte [eax+usb_hub.ResetStatusChange], 1 shl PORT_CONNECTION
  1151.         jnz     .disconnected
  1152.         or      [eax+usb_hub.Actions], HUB_RESET_RECOVERY
  1153. .common:
  1154. ; In any case, clear change of resetting status.
  1155.         lea     edx, [eax+usb_hub.ConfigBuffer]
  1156.         mov     cl, C_PORT_RESET
  1157.         call    usb_hub_clear_port_change.buffer
  1158. .nothing:
  1159.         ret
  1160. .disconnected:
  1161.         call    usb_hub_reset_aborted
  1162.         jmp     .common
  1163. endp
  1164.  
  1165. ; Helper procedure for usb_hub_process_deferred. Initiates reset signalling
  1166. ; on the current port (given by 1-based value [ConfigBuffer+4]).
  1167. ; esi -> usb_hub, eax -> usb_controller
  1168. proc usb_hub_initiate_reset
  1169. ; 1. Store hub+port data in the controller structure.
  1170.         movzx   ecx, [esi+usb_hub.ConfigBuffer+4]
  1171.         dec     ecx
  1172.         mov     [eax+usb_controller.ResettingPort], cl
  1173.         mov     [eax+usb_controller.ResettingHub], esi
  1174. ; 2. Store the current time and set 'reset signalling active' flag.
  1175.         mov     eax, [timer_ticks]
  1176.         mov     [esi+usb_hub.ResetTime], eax
  1177.         and     [esi+usb_hub.Actions], not HUB_RESET_WAITING
  1178.         or      [esi+usb_hub.Actions], HUB_RESET_SIGNAL
  1179. ; 3. Send request to the hub to initiate request signalling.
  1180.         lea     edx, [esi+usb_hub.ConfigBuffer]
  1181. ;       DEBUGF 1,'K : [%d] hub %x port %d initiate reset\n',[timer_ticks],esi,ecx
  1182.         mov     dword [edx], 23h + \
  1183.                 (USB_SET_FEATURE shl 8) + \
  1184.                 (PORT_RESET shl 16)
  1185.         and     dword [edx+4], 0xFF
  1186.         stdcall usb_control_async, [esi+usb_hub.ConfigPipe], edx, 0, 0, usb_hub_reset_started, esi, 0
  1187.         test    eax, eax
  1188.         jnz     @f
  1189.         mov     eax, esi
  1190.         call    usb_hub_reset_aborted
  1191. @@:
  1192.         ret
  1193. endp
  1194.  
  1195. ; This procedure is called when the request to start reset signalling initiated
  1196. ; by usb_hub_initiate_reset is completed, either successfully or unsuccessfully.
  1197. proc usb_hub_reset_started stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword
  1198. ; If the request is successful, do nothing.
  1199. ; Otherwise, clear 'reset signalling' flag and abort reset process.
  1200.         mov     eax, [calldata]
  1201. ;       movzx   ecx, [eax+usb_hub.ConfigBuffer+4]
  1202. ;       dec     ecx
  1203. ;       DEBUGF 1,'K : [%d] hub %x port %d reset started\n',[timer_ticks],eax,ecx
  1204.         cmp     [status], 0
  1205.         jz      .nothing
  1206.         and     [eax+usb_hub.Actions], not HUB_RESET_SIGNAL
  1207.         dbgstr 'Failed to reset hub port'
  1208.         call    usb_hub_reset_aborted
  1209. .nothing:
  1210.         ret
  1211. endp
  1212.  
  1213. ; This procedure is called by the protocol layer if something has failed during
  1214. ; initial stages of the configuration process, so the device should be disabled
  1215. ; at hub level.
  1216. proc usb_hub_disable_resetting_port
  1217.         and     [eax+usb_hub.Actions], not HUB_RESET_IN_PROGRESS
  1218. ;       movzx   ecx, [eax+usb_hub.ConfigBuffer+4]
  1219. ;       dec     ecx
  1220. ;       DEBUGF 1,'K : [%d] hub %x port %d disable\n',[timer_ticks],eax,ecx
  1221.         lea     edx, [eax+usb_hub.ConfigBuffer]
  1222.         mov     cl, PORT_ENABLE
  1223.         jmp     usb_hub_clear_port_change.buffer
  1224. endp
  1225.  
  1226. ; This procedure is called when the hub is disconnected.
  1227. proc usb_hub_disconnect
  1228. virtual at esp
  1229.                 dd      ?       ; return address
  1230. .hubdata        dd      ?
  1231. end virtual
  1232. ; 1. If the hub is disconnected during initial configuration,
  1233. ; 1 is stored as hub data and there is nothing to do.
  1234.         mov     eax, [.hubdata]
  1235.         cmp     eax, 1
  1236.         jz      .nothing
  1237. ; 2. Remove the hub from the overall list.
  1238.         mov     ecx, [eax+usb_hub.Next]
  1239.         mov     edx, [eax+usb_hub.Prev]
  1240.         mov     [ecx+usb_hub.Prev], edx
  1241.         mov     [edx+usb_hub.Next], ecx
  1242. ; 3. If some child is in reset process, abort reset.
  1243.         push    esi
  1244.         mov     esi, [eax+usb_hub.Controller]
  1245.         cmp     [esi+usb_controller.ResettingHub], eax
  1246.         jnz     @f
  1247.         cmp     [esi+usb_controller.ResettingPort], -1
  1248.         jz      @f
  1249.         push    eax
  1250.         call    usb_test_pending_port
  1251.         pop     eax
  1252. @@:
  1253.         pop     esi
  1254. ; 4. Loop over all children and notify other code that they were disconnected.
  1255.         push    ebx
  1256.         xor     ecx, ecx
  1257. .disconnect_children:
  1258.         mov     ebx, [eax+usb_hub.ConnectedDevicesPtr]
  1259.         mov     ebx, [ebx+ecx*4]
  1260.         test    ebx, ebx
  1261.         jz      @f
  1262.         push    eax ecx
  1263.         call    usb_device_disconnected
  1264.         pop     ecx eax
  1265. @@:
  1266.         inc     ecx
  1267.         cmp     ecx, [eax+usb_hub.NumPorts]
  1268.         jb      .disconnect_children
  1269. ; 4. Free memory allocated for the hub data.
  1270.         call    free
  1271.         pop     ebx
  1272. .nothing:
  1273.         retn    4
  1274. endp
  1275.  
  1276. ; Helper function for USB2 scheduler.
  1277. ; in: eax -> usb_hub
  1278. ; out: ecx = TT think time for the hub in FS-bytes
  1279. proc usb_get_tt_think_time
  1280.         movzx   ecx, [eax+usb_hub.HubCharacteristics]
  1281.         shr     ecx, 5
  1282.         and     ecx, 3
  1283.         inc     ecx
  1284.         ret
  1285. endp
  1286.