Subversion Repositories Kolibri OS

Rev

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

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