Subversion Repositories Kolibri OS

Rev

Rev 5363 | Details | Compare with Previous | Last modification | View Log | RSS feed

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