Subversion Repositories Kolibri OS

Rev

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

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