Subversion Repositories Kolibri OS

Rev

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

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