Subversion Repositories Kolibri OS

Rev

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