Subversion Repositories Kolibri OS

Rev

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

Rev Author Line No. Line
3520 clevermous 1
; Functions for USB pipe manipulation: opening/closing, sending data etc.
2
;
3
; =============================================================================
4
; ================================= Constants =================================
5
; =============================================================================
6
; USB pipe types
7
CONTROL_PIPE = 0
8
ISOCHRONOUS_PIPE = 1
9
BULK_PIPE = 2
10
INTERRUPT_PIPE = 3
11
 
12
; Status codes for transfer callbacks.
13
; Taken from OHCI as most verbose controller in this sense.
14
USB_STATUS_OK           = 0     ; no error
15
USB_STATUS_CRC          = 1     ; CRC error
16
USB_STATUS_BITSTUFF     = 2     ; bit stuffing violation
17
USB_STATUS_TOGGLE       = 3     ; data toggle mismatch
18
USB_STATUS_STALL        = 4     ; device returned STALL
19
USB_STATUS_NORESPONSE   = 5     ; device not responding
20
USB_STATUS_PIDCHECK     = 6     ; invalid PID check bits
21
USB_STATUS_WRONGPID     = 7     ; unexpected PID value
22
USB_STATUS_OVERRUN      = 8     ; too many data from endpoint
23
USB_STATUS_UNDERRUN     = 9     ; too few data from endpoint
24
USB_STATUS_BUFOVERRUN   = 12    ; overflow of internal controller buffer
25
USB_STATUS_BUFUNDERRUN  = 13    ; underflow of internal controller buffer
26
USB_STATUS_CLOSED       = 16    ; pipe closed
27
                                ; either explicitly with USBClosePipe
28
                                ; or implicitly due to device disconnect
29
 
30
; flags for usb_pipe.Flags
31
USB_FLAG_CLOSED     = 1         ; pipe is closed, no new transfers
32
; pipe is closed, return error instead of submitting any new transfer
33
USB_FLAG_CAN_FREE   = 2
34
; pipe is closed via explicit call to USBClosePipe, so it can be freed without
35
; any driver notification; if this flag is not set, then the pipe is closed due
36
; to device disconnect, so it must remain valid until return from disconnect
37
; callback provided by the driver
38
USB_FLAG_EXTRA_WAIT = 4
39
; The pipe was in wait list, while another event occured;
40
; when the first wait will be done, reinsert the pipe to wait list
41
USB_FLAG_CLOSED_BIT = 0 ; USB_FLAG_CLOSED = 1 shl USB_FLAG_CLOSED_BIT
42
 
43
; =============================================================================
44
; ================================ Structures =================================
45
; =============================================================================
46
 
47
; Pipe descriptor.
48
; * An USB pipe is described by two structures, for hardware and for software.
49
; * This is the software part. The hardware part is defined in a driver
50
;   of the corresponding controller.
51
; * The hardware part is located immediately before usb_pipe,
52
;   both are allocated at once by controller-specific code
53
;   (it knows the total length, which depends on the hardware part).
54
struct usb_pipe
55
Controller      dd      ?
56
; Pointer to usb_controller structure corresponding to this pipe.
57
; Must be the first dword after hardware part, see *hci_new_device.
58
;
59
; Every endpoint is included into one of processing lists:
60
; * Bulk list contains all Bulk endpoints.
61
; * Control list contains all Control endpoints.
62
; * Several Periodic lists serve Interrupt endpoints with different interval.
63
;   - There are N=2^n "leaf" periodic lists for N ms interval, one is processed
64
;     in the frames 0,N,2N,..., another is processed in the frames
65
;     1,1+N,1+2N,... and so on. The hardware starts processing of periodic
66
;     endpoints in every frame from the list identified by lower n bits of the
67
;     frame number; the addresses of these N lists are written to the
68
;     controller data area during the initialization.
69
;   - We assume that n=5, N=32 to simplify the code and compact the data.
70
;     OHCI works in this way. UHCI and EHCI actually have n=10, N=1024,
71
;     but this is an overkill for interrupt endpoints; the large value of N is
72
;     useful only for isochronous transfers in UHCI and EHCI. UHCI/EHCI code
73
;     initializes "leaf" lists k,k+32,k+64,...,k+(1024-32) to the same value,
74
;     giving essentially N=32.
75
;     This restriction means that the actual maximum interval of polling any
76
;     interrupt endpoint is 32ms, which seems to be a reasonable value.
77
;   - Similarly, there are 16 lists for 16-ms interval, 8 lists for 8-ms
78
;     interval and so on. Finally, there is one list for 1ms interval. Their
79
;     addresses are not directly known to the controller.
80
;   - The hardware serves endpoints following a physical link from the hardware
81
;     part.
82
;   - The hardware links are organized as follows. If the list item is not the
83
;     last, it's hardware link points to the next item. The hardware link of
84
;     the last item points to the first item of the "next" list.
85
;   - The "next" list for k-th and (k+M)-th periodic lists for interval 2M ms
86
;     is the k-th periodic list for interval M ms, M >= 1. In this scheme,
87
;     if two "previous" lists are served in the frames k,k+2M,k+4M,...
88
;     and k+M,k+3M,k+5M,... correspondingly, the "next" list is served in
89
;     the frames k,k+M,k+2M,k+3M,k+4M,k+5M,..., which is exactly what we want.
90
;   - The links between Periodic, Control, Bulk lists and the processing of
91
;     Isochronous endpoints are controller-specific.
92
; * The head of every processing list is a static entry which does not
93
;   correspond to any real pipe. It is described by usb_static_ep
94
;   structure, not usb_pipe. For OHCI and UHCI, sizeof.usb_static_ep plus
95
;   sizeof hardware part is 20h, the total number of lists is
96
;   32+16+8+4+2+1+1+1 = 65, so all these structures fit in one page,
97
;   leaving space for other data. This is another reason for 32ms limit.
98
; * Static endpoint descriptors are kept in *hci_controller structure.
99
; * All items in every processing list, including the static head, are
100
;   organized in a double-linked list using .NextVirt and .PrevVirt fields.
101
; * [[item.NextVirt].PrevVirt] = [[item.PrevVirt].NextVirt] for all items.
102
NextVirt        dd      ?
103
; Next endpoint in the processing list.
104
; See also PrevVirt field and the description before NextVirt field.
105
PrevVirt        dd      ?
106
; Previous endpoint in the processing list.
107
; See also NextVirt field and the description before NextVirt field.
108
;
109
; Every pipe has the associated transfer queue, that is, the double-linked
110
; list of Transfer Descriptors aka TD. For Control, Bulk and Interrupt
111
; endpoints this list consists of usb_gtd structures
112
; (GTD = General Transfer Descriptors), for Isochronous endpoints
113
; this list consists of usb_itd structures, which are not developed yet.
114
; The pipe needs to know only the last TD; the first TD can be
115
; obtained as [[pipe.LastTD].NextVirt].
116
LastTD          dd      ?
117
; Last TD in the transfer queue.
118
;
119
; All opened pipes corresponding to the same physical device are organized in
120
; the double-linked list using .NextSibling and .PrevSibling fields.
121
; The head of this list is kept in usb_device_data structure (OpenedPipeList).
122
; This list is used when the device is disconnected and all pipes for the
123
; device should be closed.
124
; Also, all pipes closed due to disconnect must remain valid at least until
125
; driver-provided disconnect function returns; all should-be-freed-but-not-now
126
; pipes for one device are organized in another double-linked list with
127
; the head in usb_device_data.ClosedPipeList; this list uses the same link
128
; fields, one pipe can never be in both lists.
129
NextSibling     dd      ?
130
; Next pipe for the physical device.
131
PrevSibling     dd      ?
132
; Previous pipe for the physical device.
133
;
134
; When hardware part of pipe is changed, some time is needed before further
135
; actions so that hardware reacts on this change. During that time,
136
; all changed pipes are organized in single-linked list with the head
137
; usb_controller.WaitPipeList* and link field NextWait.
138
; Currently there are two possible reasons to change:
139
; change of address/packet size in initial configuration,
140
; close of the pipe. They are distinguished by USB_FLAG_CLOSED.
141
NextWait        dd      ?
142
Lock            MUTEX
143
; Mutex that guards operations with transfer queue for this pipe.
144
Type            db      ?
145
; Type of pipe, one of {CONTROL,ISOCHRONOUS,BULK,INTERRUPT}_PIPE.
146
Flags           db      ?
147
; Combination of flags, USB_FLAG_*.
148
                rb      2       ; dword alignment
149
DeviceData      dd      ?
150
; Pointer to usb_device_data, common for all pipes for one device.
151
ends
152
 
153
; This structure describes the static head of every list of pipes.
154
struct usb_static_ep
155
; software fields
156
Bandwidth       dd      ?
157
; valid only for interrupt/isochronous USB1 lists
158
; The offsets of the following two fields must be the same in this structure
159
; and in usb_pipe.
160
NextVirt        dd      ?
161
PrevVirt        dd      ?
162
ends
163
 
164
; This structure represents one transfer descriptor
165
; ('g' stands for "general" as opposed to isochronous usb_itd).
166
; Note that one transfer can have several descriptors:
167
; a control transfer has three stages.
168
; Additionally, every controller has a limit on transfer length with
169
; one descriptor (packet size for UHCI, 1K for OHCI, 4K for EHCI),
170
; large transfers must be split into individual packets according to that limit.
171
struct usb_gtd
172
Callback        dd      ?
173
; Zero for intermediate descriptors, pointer to callback function
174
; for final descriptor. See the docs for description of the callback.
175
UserData        dd      ?
176
; Dword which is passed to Callback as is, not used by USB code itself.
177
; Two following fields organize all descriptors for one pipe in
178
; the linked list.
179
NextVirt        dd      ?
180
PrevVirt        dd      ?
181
Pipe            dd      ?
182
; Pointer to the parent usb_pipe.
183
Buffer          dd      ?
184
; Pointer to data for this descriptor.
185
Length          dd      ?
186
; Length of data for this descriptor.
187
ends
188
 
189
; =============================================================================
190
; =================================== Code ====================================
191
; =============================================================================
192
 
193
USB_STDCALL_VERIFY = 1
194
macro stdcall_verify [arg]
195
{
196
common
197
if USB_STDCALL_VERIFY
198
        pushad
199
        stdcall arg
200
        call    verify_regs
201
        popad
202
else
203
        stdcall arg
204
end if
205
}
206
 
207
; Initialization of usb_static_ep structure,
208
; called from controller-specific initialization; edi -> usb_static_ep
209
proc usb_init_static_endpoint
210
        mov     [edi+usb_static_ep.NextVirt], edi
211
        mov     [edi+usb_static_ep.PrevVirt], edi
212
        ret
213
endp
214
 
215
; Part of API for drivers, see documentation for USBOpenPipe.
216
proc usb_open_pipe stdcall uses ebx esi edi,\
217
 config_pipe:dword, endpoint:dword, maxpacket:dword, type:dword, interval:dword
218
locals
3826 clevermous 219
tt_vars         rd      (ehci_select_tt_interrupt_list.local_vars_size + 3) / 4
3520 clevermous 220
targetsmask     dd      ?       ; S-Mask for USB2
221
bandwidth       dd      ?
222
target          dd      ?
223
endl
224
; 1. Verify type of pipe: it must be one of *_PIPE constants.
225
; Isochronous pipes are not supported yet.
226
        mov     eax, [type]
227
        cmp     eax, INTERRUPT_PIPE
228
        ja      .badtype
229
        cmp     al, ISOCHRONOUS_PIPE
230
        jnz     .goodtype
231
.badtype:
232
        dbgstr 'unsupported type of USB pipe'
233
        jmp     .return0
234
.goodtype:
235
; 2. Allocate memory for pipe and transfer queue.
236
; Empty transfer queue consists of one inactive TD.
237
        mov     ebx, [config_pipe]
238
        mov     esi, [ebx+usb_pipe.Controller]
239
        mov     edx, [esi+usb_controller.HardwareFunc]
240
        call    [edx+usb_hardware_func.AllocPipe]
241
        test    eax, eax
242
        jz      .nothing
243
        mov     edi, eax
244
        mov     edx, [esi+usb_controller.HardwareFunc]
245
        call    [edx+usb_hardware_func.AllocTD]
246
        test    eax, eax
247
        jz      .free_and_return0
248
; 3. Initialize transfer queue: pointer to transfer descriptor,
249
; pointers in transfer descriptor, queue lock.
250
        mov     [edi+usb_pipe.LastTD], eax
251
        mov     [eax+usb_gtd.NextVirt], eax
252
        mov     [eax+usb_gtd.PrevVirt], eax
253
        mov     [eax+usb_gtd.Pipe], edi
254
        lea     ecx, [edi+usb_pipe.Lock]
255
        call    mutex_init
256
; 4. Initialize software part of pipe structure, except device-related fields.
257
        mov     al, byte [type]
258
        mov     [edi+usb_pipe.Type], al
259
        xor     eax, eax
260
        mov     [edi+usb_pipe.Flags], al
261
        mov     [edi+usb_pipe.DeviceData], eax
262
        mov     [edi+usb_pipe.Controller], esi
263
        or      [edi+usb_pipe.NextWait], -1
264
; 5. Initialize device-related fields:
265
; for zero endpoint, set .NextSibling = .PrevSibling = this;
266
; for other endpoins, copy device data, take the lock guarding pipe list
267
; for the device and verify that disconnect processing has not yet started
268
; for the device. (Since disconnect processing also takes that lock,
269
; either it has completed or it will not start until we release the lock.)
270
; Note: usb_device_disconnected should not see the new pipe until
271
; initialization is complete, so that lock will be held during next steps
272
; (disconnect processing should either not see it at all, or see fully
273
; initialized pipe).
274
        cmp     [endpoint], eax
275
        jz      .zero_endpoint
276
        mov     ecx, [ebx+usb_pipe.DeviceData]
277
        mov     [edi+usb_pipe.DeviceData], ecx
278
        call    mutex_lock
279
        test    [ebx+usb_pipe.Flags], USB_FLAG_CLOSED
280
        jz      .common
281
.fail:
282
; If disconnect processing has completed, unlock the mutex, free memory
283
; allocated in step 2 and return zero.
284
        call    mutex_unlock
285
        mov     edx, [esi+usb_controller.HardwareFunc]
286
        stdcall [edx+usb_hardware_func.FreeTD], [edi+usb_pipe.LastTD]
287
.free_and_return0:
288
        mov     edx, [esi+usb_controller.HardwareFunc]
289
        stdcall [edx+usb_hardware_func.FreePipe], edi
290
.return0:
291
        xor     eax, eax
292
        jmp     .nothing
293
.zero_endpoint:
294
        mov     [edi+usb_pipe.NextSibling], edi
295
        mov     [edi+usb_pipe.PrevSibling], edi
296
.common:
297
; 6. Initialize hardware part of pipe structure.
298
; 6a. Acquire the corresponding mutex.
299
        lea     ecx, [esi+usb_controller.ControlLock]
300
        cmp     [type], BULK_PIPE
301
        jb      @f      ; control pipe
302
        lea     ecx, [esi+usb_controller.BulkLock]
303
        jz      @f      ; bulk pipe
304
        lea     ecx, [esi+usb_controller.PeriodicLock]
305
@@:
306
        call    mutex_lock
307
; 6b. Let the controller-specific code do its job.
308
        push    ecx
309
        mov     edx, [esi+usb_controller.HardwareFunc]
310
        mov     eax, [edi+usb_pipe.LastTD]
311
        mov     ecx, [config_pipe]
312
        call    [edx+usb_hardware_func.InitPipe]
313
        pop     ecx
314
; 6c. Release the mutex.
315
        push    eax
316
        call    mutex_unlock
317
        pop     eax
318
; 6d. If controller-specific code indicates failure,
319
; release the lock taken in step 5, free memory allocated in step 2
320
; and return zero.
321
        test    eax, eax
322
        jz      .fail
323
; 7. The pipe is initialized. If this is not the first pipe for the device,
324
; insert it to the tail of pipe list for the device,
325
; increment number of pipes,
326
; release the lock taken at step 5.
327
        mov     ecx, [edi+usb_pipe.DeviceData]
328
        test    ecx, ecx
329
        jz      @f
330
        mov     eax, [ebx+usb_pipe.PrevSibling]
331
        mov     [edi+usb_pipe.NextSibling], ebx
332
        mov     [edi+usb_pipe.PrevSibling], eax
333
        mov     [ebx+usb_pipe.PrevSibling], edi
334
        mov     [eax+usb_pipe.NextSibling], edi
335
        inc     [ecx+usb_device_data.NumPipes]
336
        call    mutex_unlock
337
@@:
338
; 8. Return pointer to usb_pipe.
339
        mov     eax, edi
340
.nothing:
341
        ret
342
endp
343
 
344
; This procedure is called several times during initial device configuration,
345
; when usb_device_data structure is reallocated.
346
; It (re)initializes all pointers in usb_device_data.
347
; ebx -> usb_pipe
348
proc usb_reinit_pipe_list
349
        push    eax
350
; 1. (Re)initialize the lock guarding pipe list.
351
        mov     ecx, [ebx+usb_pipe.DeviceData]
352
        call    mutex_init
353
; 2. Initialize list of opened pipes: two entries, the head and ebx.
354
        add     ecx, usb_device_data.OpenedPipeList - usb_pipe.NextSibling
355
        mov     [ecx+usb_pipe.NextSibling], ebx
356
        mov     [ecx+usb_pipe.PrevSibling], ebx
357
        mov     [ebx+usb_pipe.NextSibling], ecx
358
        mov     [ebx+usb_pipe.PrevSibling], ecx
359
; 3. Initialize list of closed pipes: empty list, only the head is present.
360
        add     ecx, usb_device_data.ClosedPipeList - usb_device_data.OpenedPipeList
361
        mov     [ecx+usb_pipe.NextSibling], ecx
362
        mov     [ecx+usb_pipe.PrevSibling], ecx
363
        pop     eax
364
        ret
365
endp
366
 
367
; Part of API for drivers, see documentation for USBClosePipe.
368
proc usb_close_pipe
369
        push    ebx esi ; save used registers to be stdcall
370
virtual at esp
371
        rd      2       ; saved registers
372
        dd      ?       ; return address
373
.pipe   dd      ?
374
end virtual
375
; 1. Lock the pipe list for the device.
376
        mov     ebx, [.pipe]
377
        mov     esi, [ebx+usb_pipe.Controller]
378
        mov     ecx, [ebx+usb_pipe.DeviceData]
379
        call    mutex_lock
380
; 2. Set the flag "the driver has abandoned this pipe, free it at any time".
381
        lea     ecx, [ebx+usb_pipe.Lock]
382
        call    mutex_lock
383
        or      [ebx+usb_pipe.Flags], USB_FLAG_CAN_FREE
384
        call    mutex_unlock
385
; 3. Call the worker function.
386
        call    usb_close_pipe_nolock
387
; 4. Unlock the pipe list for the device.
388
        mov     ecx, [ebx+usb_pipe.DeviceData]
389
        call    mutex_unlock
390
; 5. Wakeup the USB thread so that it can proceed with releasing that pipe.
391
        push    edi
392
        call    usb_wakeup
393
        pop     edi
394
; 6. Return.
395
        pop     esi ebx ; restore used registers to be stdcall
396
        retn    4
397
endp
398
 
399
; Worker function for pipe closing. Called by usb_close_pipe API and
400
; from disconnect processing.
401
; The lock guarding pipe list for the device should be held by the caller.
402
; ebx -> usb_pipe, esi -> usb_controller
403
proc usb_close_pipe_nolock
404
; 1. Set the flag "pipe is closed, ignore new transfers".
405
; If it was already set, do nothing.
406
        lea     ecx, [ebx+usb_pipe.Lock]
407
        call    mutex_lock
408
        bts     dword [ebx+usb_pipe.Flags], USB_FLAG_CLOSED_BIT
409
        jc      .closed
410
        call    mutex_unlock
411
; 2. Remove the pipe from the list of opened pipes.
412
        mov     eax, [ebx+usb_pipe.NextSibling]
413
        mov     edx, [ebx+usb_pipe.PrevSibling]
414
        mov     [eax+usb_pipe.PrevSibling], edx
415
        mov     [edx+usb_pipe.NextSibling], eax
416
; 3. Unlink the pipe from hardware structures.
417
; 3a. Acquire the corresponding lock.
418
        lea     edx, [esi+usb_controller.WaitPipeListAsync]
419
        lea     ecx, [esi+usb_controller.ControlLock]
420
        cmp     [ebx+usb_pipe.Type], BULK_PIPE
421
        jb      @f      ; control pipe
422
        lea     ecx, [esi+usb_controller.BulkLock]
423
        jz      @f      ; bulk pipe
424
        add     edx, usb_controller.WaitPipeListPeriodic - usb_controller.WaitPipeListAsync
425
        lea     ecx, [esi+usb_controller.PeriodicLock]
426
@@:
427
        push    edx
428
        call    mutex_lock
429
        push    ecx
430
; 3b. Let the controller-specific code do its job.
431
        mov     eax, [esi+usb_controller.HardwareFunc]
432
        call    [eax+usb_hardware_func.UnlinkPipe]
433
; 3c. Release the corresponding lock.
434
        pop     ecx
435
        call    mutex_unlock
436
; 4. Put the pipe into wait queue.
437
        pop     edx
438
        cmp     [ebx+usb_pipe.NextWait], -1
439
        jz      .insert_new
440
        or      [ebx+usb_pipe.Flags], USB_FLAG_EXTRA_WAIT
441
        jmp     .inserted
442
.insert_new:
443
        mov     eax, [edx]
444
        mov     [ebx+usb_pipe.NextWait], eax
445
        mov     [edx], ebx
446
.inserted:
447
; 5. Return.
448
        ret
449
.closed:
450
        call    mutex_unlock
451
        xor     eax, eax
452
        ret
453
endp
454
 
455
; This procedure is called when a pipe with USB_FLAG_CLOSED is removed from the
456
; corresponding wait list. It means that the hardware has fully forgot about it.
457
; ebx -> usb_pipe, esi -> usb_controller
458
proc usb_pipe_closed
459
        push    edi
460
        mov     edi, [esi+usb_controller.HardwareFunc]
461
; 1. Loop over all transfers, calling the driver with USB_STATUS_CLOSED
462
; and freeing all descriptors.
463
        mov     edx, [ebx+usb_pipe.LastTD]
464
        test    edx, edx
465
        jz      .no_transfer
466
        mov     edx, [edx+usb_gtd.NextVirt]
467
.transfer_loop:
468
        cmp     edx, [ebx+usb_pipe.LastTD]
469
        jz      .transfer_done
470
        mov     ecx, [edx+usb_gtd.Callback]
471
        test    ecx, ecx
472
        jz      .no_callback
473
        push    edx
474
        stdcall_verify ecx, ebx, USB_STATUS_CLOSED, \
475
                [edx+usb_gtd.Buffer], 0, [edx+usb_gtd.UserData]
476
        pop     edx
477
.no_callback:
478
        push    [edx+usb_gtd.NextVirt]
479
        stdcall [edi+usb_hardware_func.FreeTD], edx
480
        pop     edx
481
        jmp     .transfer_loop
482
.transfer_done:
483
        stdcall [edi+usb_hardware_func.FreeTD], edx
484
.no_transfer:
485
; 2. Decrement number of pipes for the device.
486
; If this pipe is the last pipe, go to 5.
487
        mov     ecx, [ebx+usb_pipe.DeviceData]
488
        call    mutex_lock
489
        dec     [ecx+usb_device_data.NumPipes]
490
        jz      .last_pipe
491
        call    mutex_unlock
492
; 3. If the flag "the driver has abandoned this pipe" is set,
493
; free memory and return.
494
        test    [ebx+usb_pipe.Flags], USB_FLAG_CAN_FREE
495
        jz      .nofree
496
        stdcall [edi+usb_hardware_func.FreePipe], ebx
497
        pop     edi
498
        ret
499
; 4. Otherwise, add it to the list of closed pipes and return.
500
.nofree:
501
        add     ecx, usb_device_data.ClosedPipeList - usb_pipe.NextSibling
502
        mov     edx, [ecx+usb_pipe.PrevSibling]
503
        mov     [ebx+usb_pipe.NextSibling], ecx
504
        mov     [ebx+usb_pipe.PrevSibling], edx
505
        mov     [ecx+usb_pipe.PrevSibling], ebx
506
        mov     [edx+usb_pipe.NextSibling], ebx
507
        pop     edi
508
        ret
509
.last_pipe:
510
; That was the last pipe for the device.
511
; 5. Notify device driver(s) about disconnect.
512
        call    mutex_unlock
3826 clevermous 513
        mov     eax, [ecx+usb_device_data.NumInterfaces]
3520 clevermous 514
        test    eax, eax
515
        jz      .notify_done
516
        add     ecx, [ecx+usb_device_data.Interfaces]
517
.notify_loop:
518
        mov     edx, [ecx+usb_interface_data.DriverFunc]
519
        test    edx, edx
520
        jz      @f
521
        mov     edx, [edx+USBSRV.usb_func]
522
        cmp     [edx+USBFUNC.strucsize], USBFUNC.device_disconnect + 4
523
        jb      @f
524
        mov     edx, [edx+USBFUNC.device_disconnect]
525
        test    edx, edx
526
        jz      @f
527
        push    eax ecx
528
        stdcall_verify edx, [ecx+usb_interface_data.DriverData]
529
        pop     ecx eax
530
@@:
531
        add     ecx, sizeof.usb_interface_data
532
        dec     eax
533
        jnz     .notify_loop
534
.notify_done:
535
; 6. Bus address, if assigned, can now be reused.
536
        call    [edi+usb_hardware_func.GetDeviceAddress]
537
        test    eax, eax
538
        jz      @f
539
        bts     [esi+usb_controller.ExistingAddresses], eax
540
@@:
541
        dbgstr 'USB device disconnected'
542
; 7. All drivers have returned from disconnect callback,
543
; so all drivers should not use any device-related pipes.
544
; Free the remaining pipes.
545
        mov     eax, [ebx+usb_pipe.DeviceData]
546
        add     eax, usb_device_data.ClosedPipeList - usb_pipe.NextSibling
547
        push    eax
548
        mov     eax, [eax+usb_pipe.NextSibling]
549
.free_loop:
550
        cmp     eax, [esp]
551
        jz      .free_done
552
        push    [eax+usb_pipe.NextSibling]
553
        stdcall [edi+usb_hardware_func.FreePipe], eax
554
        pop     eax
555
        jmp     .free_loop
556
.free_done:
557
        stdcall [edi+usb_hardware_func.FreePipe], ebx
558
        pop     eax
559
; 8. Free the usb_device_data structure.
560
        sub     eax, usb_device_data.ClosedPipeList - usb_pipe.NextSibling
561
        call    free
562
; 9. Return.
563
.nothing:
564
        pop     edi
565
        ret
566
endp
567
 
568
; Part of API for drivers, see documentation for USBNormalTransferAsync.
569
proc usb_normal_transfer_async stdcall uses ebx edi,\
570
 pipe:dword, buffer:dword, size:dword, callback:dword, calldata:dword, flags:dword
571
; 1. Sanity check: callback must be nonzero.
572
; (It is important for other parts of code.)
573
        xor     eax, eax
574
        cmp     [callback], eax
575
        jz      .nothing
576
; 2. Lock the transfer queue.
577
        mov     ebx, [pipe]
578
        lea     ecx, [ebx+usb_pipe.Lock]
579
        call    mutex_lock
580
; 3. If the pipe has already been closed (presumably due to device disconnect),
581
; release the lock taken in step 2 and return zero.
582
        xor     eax, eax
583
        test    [ebx+usb_pipe.Flags], USB_FLAG_CLOSED
584
        jnz     .unlock
585
; 4. Allocate and initialize TDs for the transfer.
586
        mov     edx, [ebx+usb_pipe.Controller]
587
        mov     edi, [edx+usb_controller.HardwareFunc]
588
        stdcall [edi+usb_hardware_func.AllocTransfer], [buffer], [size], [flags], [ebx+usb_pipe.LastTD], 0
589
; If failed, release the lock taken in step 2 and return zero.
590
        test    eax, eax
591
        jz      .unlock
592
; 5. Store callback and its parameters in the last descriptor for this transfer.
593
        mov     ecx, [eax+usb_gtd.PrevVirt]
594
        mov     edx, [callback]
595
        mov     [ecx+usb_gtd.Callback], edx
596
        mov     edx, [calldata]
597
        mov     [ecx+usb_gtd.UserData], edx
598
        mov     edx, [buffer]
599
        mov     [ecx+usb_gtd.Buffer], edx
600
; 6. Advance LastTD pointer and activate transfer.
601
        push    [ebx+usb_pipe.LastTD]
602
        mov     [ebx+usb_pipe.LastTD], eax
603
        call    [edi+usb_hardware_func.InsertTransfer]
604
        pop     eax
605
; 7. Release the lock taken in step 2 and
606
; return pointer to the first descriptor for the new transfer.
607
.unlock:
608
        push    eax
609
        lea     ecx, [ebx+usb_pipe.Lock]
610
        call    mutex_unlock
611
        pop     eax
612
.nothing:
613
        ret
614
endp
615
 
616
; Part of API for drivers, see documentation for USBControlTransferAsync.
617
proc usb_control_async stdcall uses ebx edi,\
618
 pipe:dword, config:dword, buffer:dword, size:dword, callback:dword, calldata:dword, flags:dword
619
locals
620
last_td         dd      ?
621
endl
622
; 1. Sanity check: callback must be nonzero.
623
; (It is important for other parts of code.)
624
        cmp     [callback], 0
625
        jz      .return0
626
; 2. Lock the transfer queue.
627
        mov     ebx, [pipe]
628
        lea     ecx, [ebx+usb_pipe.Lock]
629
        call    mutex_lock
630
; 3. If the pipe has already been closed (presumably due to device disconnect),
631
; release the lock taken in step 2 and return zero.
632
        test    [ebx+usb_pipe.Flags], USB_FLAG_CLOSED
633
        jnz     .unlock_return0
634
; A control transfer contains two or three stages:
635
; Setup stage, optional Data stage, Status stage.
636
; 4. Allocate and initialize TDs for the Setup stage.
637
; Payload is 8 bytes from [config].
638
        mov     edx, [ebx+usb_pipe.Controller]
639
        mov     edi, [edx+usb_controller.HardwareFunc]
640
        stdcall [edi+usb_hardware_func.AllocTransfer], [config], 8, 0, [ebx+usb_pipe.LastTD], (2 shl 2) + 0
641
                ; short transfer is an error, direction is DATA0, token is SETUP
642
        mov     [last_td], eax
643
        test    eax, eax
644
        jz      .fail
645
; 5. Allocate and initialize TDs for the Data stage, if [size] is nonzero.
646
; Payload is [size] bytes from [buffer].
647
        mov     edx, [config]
648
        mov     ecx, (3 shl 2) + 1      ; DATA1, token is OUT
649
        cmp     byte [edx], 0
650
        jns     @f
651
        cmp     [size], 0
652
        jz      @f
653
        inc     ecx     ; token is IN
654
@@:
655
        cmp     [size], 0
656
        jz      .nodata
657
        push    ecx
658
        stdcall [edi+usb_hardware_func.AllocTransfer], [buffer], [size], [flags], eax, ecx
659
        pop     ecx
660
        test    eax, eax
661
        jz      .fail
662
        mov     [last_td], eax
663
.nodata:
664
; 6. Allocate and initialize TDs for the Status stage.
665
; No payload.
666
        xor     ecx, 3  ; IN becomes OUT, OUT becomes IN
667
        stdcall [edi+usb_hardware_func.AllocTransfer], 0, 0, 0, eax, ecx
668
        test    eax, eax
669
        jz      .fail
670
; 7. Store callback and its parameters in the last descriptor for this transfer.
671
        mov     ecx, [eax+usb_gtd.PrevVirt]
672
        mov     edx, [callback]
673
        mov     [ecx+usb_gtd.Callback], edx
674
        mov     edx, [calldata]
675
        mov     [ecx+usb_gtd.UserData], edx
676
        mov     edx, [buffer]
677
        mov     [ecx+usb_gtd.Buffer], edx
678
; 8. Advance LastTD pointer and activate transfer.
679
        push    [ebx+usb_pipe.LastTD]
680
        mov     [ebx+usb_pipe.LastTD], eax
681
        call    [edi+usb_hardware_func.InsertTransfer]
682
; 9. Release the lock taken in step 2 and
683
; return pointer to the first descriptor for the new transfer.
684
        lea     ecx, [ebx+usb_pipe.Lock]
685
        call    mutex_unlock
686
        pop     eax
687
        ret
688
.fail:
689
        mov     eax, [last_td]
690
        test    eax, eax
691
        jz      .unlock_return0
692
        stdcall usb_undo_tds, [ebx+usb_pipe.LastTD]
693
.unlock_return0:
694
        lea     ecx, [ebx+usb_pipe.Lock]
695
        call    mutex_unlock
696
.return0:
697
        xor     eax, eax
698
        ret
699
endp
700
 
3745 clevermous 701
; Part of API for drivers, see documentation for USBGetParam.
702
proc usb_get_param
703
virtual at esp
704
                dd      ?       ; return address
705
.pipe           dd      ?
706
.param          dd      ?
707
end virtual
708
        mov     edx, [.param]
709
        mov     ecx, [.pipe]
710
        mov     eax, [ecx+usb_pipe.DeviceData]
711
        test    edx, edx
712
        jz      .get_device_descriptor
713
        dec     edx
714
        jz      .get_config_descriptor
715
        dec     edx
716
        jz      .get_speed
717
        or      eax, -1
718
        ret     8
719
.get_device_descriptor:
720
        add     eax, usb_device_data.DeviceDescriptor
721
        ret     8
722
.get_config_descriptor:
723
        movzx   ecx, [eax+usb_device_data.DeviceDescrSize]
724
        lea     eax, [eax+ecx+usb_device_data.DeviceDescriptor]
725
        ret     8
726
.get_speed:
727
        movzx   eax, [eax+usb_device_data.Speed]
728
        ret     8
729
endp
730
 
3520 clevermous 731
; Initialize software part of usb_gtd. Called from controller-specific code
732
; somewhere in AllocTransfer with eax -> next (inactive) usb_gtd,
733
; ebx -> usb_pipe, ebp frame from call to AllocTransfer with [.td] ->
734
; current (initializing) usb_gtd.
735
; Returns ecx = [.td].
736
proc usb_init_transfer
737
virtual at ebp-4
738
.Size   dd      ?
739
        rd      2
740
.Buffer dd      ?
741
        dd      ?
742
.Flags  dd      ?
743
.td     dd      ?
744
end virtual
745
        mov     [eax+usb_gtd.Pipe], ebx
746
        mov     ecx, [.td]
747
        mov     [eax+usb_gtd.PrevVirt], ecx
748
        mov     edx, [ecx+usb_gtd.NextVirt]
749
        mov     [ecx+usb_gtd.NextVirt], eax
750
        mov     [eax+usb_gtd.NextVirt], edx
751
        mov     [edx+usb_gtd.PrevVirt], eax
752
        mov     edx, [.Size]
753
        mov     [ecx+usb_gtd.Length], edx
754
        xor     edx, edx
755
        mov     [ecx+usb_gtd.Callback], edx
756
        mov     [ecx+usb_gtd.UserData], edx
757
        ret
758
endp
759
 
760
; Free all TDs for the current transfer if something has failed
761
; during initialization (e.g. no memory for the next TD).
762
; Stdcall with one stack argument = first TD for the transfer
763
; and eax = last initialized TD for the transfer.
764
proc usb_undo_tds
765
        push    [eax+usb_gtd.NextVirt]
766
@@:
767
        cmp     eax, [esp+8]
768
        jz      @f
769
        push    [eax+usb_gtd.PrevVirt]
770
        stdcall [edi+usb_hardware_func.FreeTD], eax
771
        pop     eax
772
        jmp     @b
773
@@:
774
        pop     ecx
775
        mov     [eax+usb_gtd.NextVirt], ecx
776
        mov     [ecx+usb_gtd.PrevVirt], eax
777
        ret     4
778
endp
779
 
780
; Helper procedure for handling short packets in controller-specific code.
781
; Returns with CF cleared if this is the final packet in some stage:
782
; for control transfers that means one of Data and Status stages,
783
; for other transfers - the final packet in the only stage.
784
proc usb_is_final_packet
785
        cmp     [ebx+usb_gtd.Callback], 0
786
        jnz     .nothing
787
        mov     eax, [ebx+usb_gtd.NextVirt]
788
        cmp     [eax+usb_gtd.Callback], 0
789
        jz      .stc
790
        mov     eax, [ebx+usb_gtd.Pipe]
791
        cmp     [eax+usb_pipe.Type], CONTROL_PIPE
792
        jz      .nothing
793
.stc:
794
        stc
795
.nothing:
796
        ret
797
endp
798
 
799
; Helper procedure for controller-specific code:
800
; removes one TD from the transfer queue, ebx -> usb_gtd to remove.
801
proc usb_unlink_td
802
        mov     ecx, [ebx+usb_gtd.Pipe]
803
        add     ecx, usb_pipe.Lock
804
        call    mutex_lock
805
        mov     eax, [ebx+usb_gtd.PrevVirt]
806
        mov     edx, [ebx+usb_gtd.NextVirt]
807
        mov     [edx+usb_gtd.PrevVirt], eax
808
        mov     [eax+usb_gtd.NextVirt], edx
809
        call    mutex_unlock
810
        ret
811
endp
812
 
813
if USB_STDCALL_VERIFY
814
proc verify_regs
815
virtual at esp
816
        dd      ?       ; return address
817
.edi    dd      ?
818
.esi    dd      ?
819
.ebp    dd      ?
820
.esp    dd      ?
821
.ebx    dd      ?
822
.edx    dd      ?
823
.ecx    dd      ?
824
.eax    dd      ?
825
end virtual
826
        cmp     ebx, [.ebx]
827
        jz      @f
828
        dbgstr 'ERROR!!! ebx changed'
829
@@:
830
        cmp     esi, [.esi]
831
        jz      @f
832
        dbgstr 'ERROR!!! esi changed'
833
@@:
834
        cmp     edi, [.edi]
835
        jz      @f
836
        dbgstr 'ERROR!!! edi changed'
837
@@:
838
        cmp     ebp, [.ebp]
839
        jz      @f
840
        dbgstr 'ERROR!!! ebp changed'
841
@@:
842
        ret
843
endp
844
end if