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