Subversion Repositories Kolibri OS

Rev

Rev 5201 | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

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