Subversion Repositories Kolibri OS

Rev

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

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