Subversion Repositories Kolibri OS

Rev

Rev 4418 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

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