Subversion Repositories Kolibri OS

Rev

Rev 4423 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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