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