Subversion Repositories Kolibri OS

Rev

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

Rev Author Line No. Line
3555 Serge 1
; Functions for USB pipe manipulation: opening/closing, sending data etc.
2
;
3
USB_STDCALL_VERIFY = 1
4
macro stdcall_verify [arg]
5
{
6
common
7
if USB_STDCALL_VERIFY
8
        pushad
9
        stdcall arg
10
        call    verify_regs
11
        popad
12
else
13
        stdcall arg
14
end if
15
}
16
 
17
; Initialization of usb_static_ep structure,
18
; called from controller-specific initialization; edi -> usb_static_ep
19
proc usb_init_static_endpoint
20
        mov     [edi+usb_static_ep.NextVirt], edi
21
        mov     [edi+usb_static_ep.PrevVirt], edi
22
        ret
23
endp
24
 
25
; Part of API for drivers, see documentation for USBOpenPipe.
26
proc usb_open_pipe stdcall uses ebx esi edi,\
27
 config_pipe:dword, endpoint:dword, maxpacket:dword, type:dword, interval:dword
28
locals
4423 Serge 29
tt_vars         rd      24      ; should be enough for ehci_select_tt_interrupt_list
3555 Serge 30
targetsmask     dd      ?       ; S-Mask for USB2
31
bandwidth       dd      ?
32
target          dd      ?
33
endl
34
; 1. Verify type of pipe: it must be one of *_PIPE constants.
35
; Isochronous pipes are not supported yet.
36
        mov     eax, [type]
37
        cmp     eax, INTERRUPT_PIPE
38
        ja      .badtype
39
        cmp     al, ISOCHRONOUS_PIPE
40
        jnz     .goodtype
41
.badtype:
42
        dbgstr 'unsupported type of USB pipe'
43
        jmp     .return0
44
.goodtype:
45
; 2. Allocate memory for pipe and transfer queue.
46
; Empty transfer queue consists of one inactive TD.
47
        mov     ebx, [config_pipe]
48
        mov     esi, [ebx+usb_pipe.Controller]
49
        mov     edx, [esi+usb_controller.HardwareFunc]
50
        call    [edx+usb_hardware_func.AllocPipe]
51
        test    eax, eax
52
        jz      .nothing
53
        mov     edi, eax
54
        mov     edx, [esi+usb_controller.HardwareFunc]
55
        call    [edx+usb_hardware_func.AllocTD]
56
        test    eax, eax
57
        jz      .free_and_return0
58
; 3. Initialize transfer queue: pointer to transfer descriptor,
59
; pointers in transfer descriptor, queue lock.
60
        mov     [edi+usb_pipe.LastTD], eax
61
        mov     [eax+usb_gtd.NextVirt], eax
62
        mov     [eax+usb_gtd.PrevVirt], eax
63
        mov     [eax+usb_gtd.Pipe], edi
64
        lea     ecx, [edi+usb_pipe.Lock]
65
        call    mutex_init
66
; 4. Initialize software part of pipe structure, except device-related fields.
67
        mov     al, byte [type]
68
        mov     [edi+usb_pipe.Type], al
69
        xor     eax, eax
70
        mov     [edi+usb_pipe.Flags], al
71
        mov     [edi+usb_pipe.DeviceData], eax
72
        mov     [edi+usb_pipe.Controller], esi
73
        or      [edi+usb_pipe.NextWait], -1
74
; 5. Initialize device-related fields:
75
; for zero endpoint, set .NextSibling = .PrevSibling = this;
76
; for other endpoins, copy device data, take the lock guarding pipe list
77
; for the device and verify that disconnect processing has not yet started
78
; for the device. (Since disconnect processing also takes that lock,
79
; either it has completed or it will not start until we release the lock.)
80
; Note: usb_device_disconnected should not see the new pipe until
81
; initialization is complete, so that lock will be held during next steps
82
; (disconnect processing should either not see it at all, or see fully
83
; initialized pipe).
84
        cmp     [endpoint], eax
85
        jz      .zero_endpoint
86
        mov     ecx, [ebx+usb_pipe.DeviceData]
87
        mov     [edi+usb_pipe.DeviceData], ecx
88
        call    mutex_lock
89
        test    [ebx+usb_pipe.Flags], USB_FLAG_CLOSED
90
        jz      .common
91
.fail:
92
; If disconnect processing has completed, unlock the mutex, free memory
93
; allocated in step 2 and return zero.
94
        call    mutex_unlock
95
        mov     edx, [esi+usb_controller.HardwareFunc]
96
        stdcall [edx+usb_hardware_func.FreeTD], [edi+usb_pipe.LastTD]
97
.free_and_return0:
98
        mov     edx, [esi+usb_controller.HardwareFunc]
99
        stdcall [edx+usb_hardware_func.FreePipe], edi
100
.return0:
101
        xor     eax, eax
102
        jmp     .nothing
103
.zero_endpoint:
104
        mov     [edi+usb_pipe.NextSibling], edi
105
        mov     [edi+usb_pipe.PrevSibling], edi
106
.common:
107
; 6. Initialize hardware part of pipe structure.
108
; 6a. Acquire the corresponding mutex.
109
        lea     ecx, [esi+usb_controller.ControlLock]
110
        cmp     [type], BULK_PIPE
111
        jb      @f      ; control pipe
112
        lea     ecx, [esi+usb_controller.BulkLock]
113
        jz      @f      ; bulk pipe
114
        lea     ecx, [esi+usb_controller.PeriodicLock]
115
@@:
116
        call    mutex_lock
117
; 6b. Let the controller-specific code do its job.
118
        push    ecx
119
        mov     edx, [esi+usb_controller.HardwareFunc]
120
        mov     eax, [edi+usb_pipe.LastTD]
121
        mov     ecx, [config_pipe]
122
        call    [edx+usb_hardware_func.InitPipe]
123
        pop     ecx
124
; 6c. Release the mutex.
125
        push    eax
126
        call    mutex_unlock
127
        pop     eax
128
; 6d. If controller-specific code indicates failure,
129
; release the lock taken in step 5, free memory allocated in step 2
130
; and return zero.
131
        test    eax, eax
132
        jz      .fail
133
; 7. The pipe is initialized. If this is not the first pipe for the device,
134
; insert it to the tail of pipe list for the device,
135
; increment number of pipes,
136
; release the lock taken at step 5.
137
        mov     ecx, [edi+usb_pipe.DeviceData]
138
        test    ecx, ecx
139
        jz      @f
140
        mov     eax, [ebx+usb_pipe.PrevSibling]
141
        mov     [edi+usb_pipe.NextSibling], ebx
142
        mov     [edi+usb_pipe.PrevSibling], eax
143
        mov     [ebx+usb_pipe.PrevSibling], edi
144
        mov     [eax+usb_pipe.NextSibling], edi
145
        inc     [ecx+usb_device_data.NumPipes]
146
        call    mutex_unlock
147
@@:
148
; 8. Return pointer to usb_pipe.
149
        mov     eax, edi
150
.nothing:
151
        ret
152
endp
153
 
154
; This procedure is called several times during initial device configuration,
155
; when usb_device_data structure is reallocated.
156
; It (re)initializes all pointers in usb_device_data.
157
; ebx -> usb_pipe
158
proc usb_reinit_pipe_list
159
        push    eax
160
; 1. (Re)initialize the lock guarding pipe list.
161
        mov     ecx, [ebx+usb_pipe.DeviceData]
162
        call    mutex_init
163
; 2. Initialize list of opened pipes: two entries, the head and ebx.
164
        add     ecx, usb_device_data.OpenedPipeList - usb_pipe.NextSibling
165
        mov     [ecx+usb_pipe.NextSibling], ebx
166
        mov     [ecx+usb_pipe.PrevSibling], ebx
167
        mov     [ebx+usb_pipe.NextSibling], ecx
168
        mov     [ebx+usb_pipe.PrevSibling], ecx
169
; 3. Initialize list of closed pipes: empty list, only the head is present.
170
        add     ecx, usb_device_data.ClosedPipeList - usb_device_data.OpenedPipeList
171
        mov     [ecx+usb_pipe.NextSibling], ecx
172
        mov     [ecx+usb_pipe.PrevSibling], ecx
173
        pop     eax
174
        ret
175
endp
176
 
177
; Part of API for drivers, see documentation for USBClosePipe.
178
proc usb_close_pipe
179
        push    ebx esi ; save used registers to be stdcall
180
virtual at esp
181
        rd      2       ; saved registers
182
        dd      ?       ; return address
183
.pipe   dd      ?
184
end virtual
185
; 1. Lock the pipe list for the device.
186
        mov     ebx, [.pipe]
187
        mov     esi, [ebx+usb_pipe.Controller]
188
        mov     ecx, [ebx+usb_pipe.DeviceData]
189
        call    mutex_lock
190
; 2. Set the flag "the driver has abandoned this pipe, free it at any time".
191
        lea     ecx, [ebx+usb_pipe.Lock]
192
        call    mutex_lock
193
        or      [ebx+usb_pipe.Flags], USB_FLAG_CAN_FREE
194
        call    mutex_unlock
195
; 3. Call the worker function.
196
        call    usb_close_pipe_nolock
197
; 4. Unlock the pipe list for the device.
198
        mov     ecx, [ebx+usb_pipe.DeviceData]
199
        call    mutex_unlock
200
; 5. Wakeup the USB thread so that it can proceed with releasing that pipe.
201
        push    edi
202
        call    usb_wakeup
203
        pop     edi
204
; 6. Return.
205
        pop     esi ebx ; restore used registers to be stdcall
206
        retn    4
207
endp
208
 
209
; Worker function for pipe closing. Called by usb_close_pipe API and
210
; from disconnect processing.
211
; The lock guarding pipe list for the device should be held by the caller.
212
; ebx -> usb_pipe, esi -> usb_controller
213
proc usb_close_pipe_nolock
214
; 1. Set the flag "pipe is closed, ignore new transfers".
215
; If it was already set, do nothing.
216
        lea     ecx, [ebx+usb_pipe.Lock]
217
        call    mutex_lock
218
        bts     dword [ebx+usb_pipe.Flags], USB_FLAG_CLOSED_BIT
219
        jc      .closed
220
        call    mutex_unlock
221
; 2. Remove the pipe from the list of opened pipes.
222
        mov     eax, [ebx+usb_pipe.NextSibling]
223
        mov     edx, [ebx+usb_pipe.PrevSibling]
224
        mov     [eax+usb_pipe.PrevSibling], edx
225
        mov     [edx+usb_pipe.NextSibling], eax
226
; 3. Unlink the pipe from hardware structures.
227
; 3a. Acquire the corresponding lock.
228
        lea     edx, [esi+usb_controller.WaitPipeListAsync]
229
        lea     ecx, [esi+usb_controller.ControlLock]
230
        cmp     [ebx+usb_pipe.Type], BULK_PIPE
231
        jb      @f      ; control pipe
232
        lea     ecx, [esi+usb_controller.BulkLock]
233
        jz      @f      ; bulk pipe
234
        add     edx, usb_controller.WaitPipeListPeriodic - usb_controller.WaitPipeListAsync
235
        lea     ecx, [esi+usb_controller.PeriodicLock]
236
@@:
237
        push    edx
238
        call    mutex_lock
239
        push    ecx
240
; 3b. Let the controller-specific code do its job.
241
        mov     eax, [esi+usb_controller.HardwareFunc]
242
        call    [eax+usb_hardware_func.UnlinkPipe]
243
; 3c. Release the corresponding lock.
244
        pop     ecx
245
        call    mutex_unlock
246
; 4. Put the pipe into wait queue.
247
        pop     edx
248
        cmp     [ebx+usb_pipe.NextWait], -1
249
        jz      .insert_new
250
        or      [ebx+usb_pipe.Flags], USB_FLAG_EXTRA_WAIT
251
        jmp     .inserted
252
.insert_new:
253
        mov     eax, [edx]
254
        mov     [ebx+usb_pipe.NextWait], eax
255
        mov     [edx], ebx
256
.inserted:
257
; 5. Return.
258
        ret
259
.closed:
260
        call    mutex_unlock
261
        xor     eax, eax
262
        ret
263
endp
264
 
265
; This procedure is called when a pipe with USB_FLAG_CLOSED is removed from the
266
; corresponding wait list. It means that the hardware has fully forgot about it.
267
; ebx -> usb_pipe, esi -> usb_controller
268
proc usb_pipe_closed
269
        push    edi
270
        mov     edi, [esi+usb_controller.HardwareFunc]
271
; 1. Loop over all transfers, calling the driver with USB_STATUS_CLOSED
272
; and freeing all descriptors.
273
        mov     edx, [ebx+usb_pipe.LastTD]
274
        test    edx, edx
275
        jz      .no_transfer
276
        mov     edx, [edx+usb_gtd.NextVirt]
277
.transfer_loop:
278
        cmp     edx, [ebx+usb_pipe.LastTD]
279
        jz      .transfer_done
280
        mov     ecx, [edx+usb_gtd.Callback]
281
        test    ecx, ecx
282
        jz      .no_callback
283
        push    edx
284
        stdcall_verify ecx, ebx, USB_STATUS_CLOSED, \
285
                [edx+usb_gtd.Buffer], 0, [edx+usb_gtd.UserData]
286
        pop     edx
287
.no_callback:
288
        push    [edx+usb_gtd.NextVirt]
289
        stdcall [edi+usb_hardware_func.FreeTD], edx
290
        pop     edx
291
        jmp     .transfer_loop
292
.transfer_done:
293
        stdcall [edi+usb_hardware_func.FreeTD], edx
294
.no_transfer:
295
; 2. Decrement number of pipes for the device.
296
; If this pipe is the last pipe, go to 5.
297
        mov     ecx, [ebx+usb_pipe.DeviceData]
298
        call    mutex_lock
299
        dec     [ecx+usb_device_data.NumPipes]
300
        jz      .last_pipe
301
        call    mutex_unlock
302
; 3. If the flag "the driver has abandoned this pipe" is set,
303
; free memory and return.
304
        test    [ebx+usb_pipe.Flags], USB_FLAG_CAN_FREE
305
        jz      .nofree
306
        stdcall [edi+usb_hardware_func.FreePipe], ebx
307
        pop     edi
308
        ret
309
; 4. Otherwise, add it to the list of closed pipes and return.
310
.nofree:
311
        add     ecx, usb_device_data.ClosedPipeList - usb_pipe.NextSibling
312
        mov     edx, [ecx+usb_pipe.PrevSibling]
313
        mov     [ebx+usb_pipe.NextSibling], ecx
314
        mov     [ebx+usb_pipe.PrevSibling], edx
315
        mov     [ecx+usb_pipe.PrevSibling], ebx
316
        mov     [edx+usb_pipe.NextSibling], ebx
317
        pop     edi
318
        ret
319
.last_pipe:
320
; That was the last pipe for the device.
321
; 5. Notify device driver(s) about disconnect.
322
        call    mutex_unlock
3908 Serge 323
        mov     eax, [ecx+usb_device_data.NumInterfaces]
3555 Serge 324
        test    eax, eax
325
        jz      .notify_done
326
        add     ecx, [ecx+usb_device_data.Interfaces]
327
.notify_loop:
328
        mov     edx, [ecx+usb_interface_data.DriverFunc]
329
        test    edx, edx
330
        jz      @f
331
        mov     edx, [edx+USBSRV.usb_func]
332
        cmp     [edx+USBFUNC.strucsize], USBFUNC.device_disconnect + 4
333
        jb      @f
334
        mov     edx, [edx+USBFUNC.device_disconnect]
335
        test    edx, edx
336
        jz      @f
337
        push    eax ecx
338
        stdcall_verify edx, [ecx+usb_interface_data.DriverData]
339
        pop     ecx eax
340
@@:
341
        add     ecx, sizeof.usb_interface_data
342
        dec     eax
343
        jnz     .notify_loop
344
.notify_done:
345
; 6. Bus address, if assigned, can now be reused.
346
        call    [edi+usb_hardware_func.GetDeviceAddress]
347
        test    eax, eax
348
        jz      @f
349
        bts     [esi+usb_controller.ExistingAddresses], eax
350
@@:
351
        dbgstr 'USB device disconnected'
352
; 7. All drivers have returned from disconnect callback,
353
; so all drivers should not use any device-related pipes.
354
; Free the remaining pipes.
355
        mov     eax, [ebx+usb_pipe.DeviceData]
356
        add     eax, usb_device_data.ClosedPipeList - usb_pipe.NextSibling
357
        push    eax
358
        mov     eax, [eax+usb_pipe.NextSibling]
359
.free_loop:
360
        cmp     eax, [esp]
361
        jz      .free_done
362
        push    [eax+usb_pipe.NextSibling]
363
        stdcall [edi+usb_hardware_func.FreePipe], eax
364
        pop     eax
365
        jmp     .free_loop
366
.free_done:
367
        stdcall [edi+usb_hardware_func.FreePipe], ebx
368
        pop     eax
369
; 8. Free the usb_device_data structure.
370
        sub     eax, usb_device_data.ClosedPipeList - usb_pipe.NextSibling
371
        call    free
372
; 9. Return.
373
.nothing:
374
        pop     edi
375
        ret
376
endp
377
 
378
; Part of API for drivers, see documentation for USBNormalTransferAsync.
379
proc usb_normal_transfer_async stdcall uses ebx edi,\
380
 pipe:dword, buffer:dword, size:dword, callback:dword, calldata:dword, flags:dword
381
; 1. Sanity check: callback must be nonzero.
382
; (It is important for other parts of code.)
383
        xor     eax, eax
384
        cmp     [callback], eax
385
        jz      .nothing
386
; 2. Lock the transfer queue.
387
        mov     ebx, [pipe]
388
        lea     ecx, [ebx+usb_pipe.Lock]
389
        call    mutex_lock
390
; 3. If the pipe has already been closed (presumably due to device disconnect),
391
; release the lock taken in step 2 and return zero.
392
        xor     eax, eax
393
        test    [ebx+usb_pipe.Flags], USB_FLAG_CLOSED
394
        jnz     .unlock
395
; 4. Allocate and initialize TDs for the transfer.
396
        mov     edx, [ebx+usb_pipe.Controller]
397
        mov     edi, [edx+usb_controller.HardwareFunc]
398
        stdcall [edi+usb_hardware_func.AllocTransfer], [buffer], [size], [flags], [ebx+usb_pipe.LastTD], 0
399
; If failed, release the lock taken in step 2 and return zero.
400
        test    eax, eax
401
        jz      .unlock
402
; 5. Store callback and its parameters in the last descriptor for this transfer.
403
        mov     ecx, [eax+usb_gtd.PrevVirt]
404
        mov     edx, [callback]
405
        mov     [ecx+usb_gtd.Callback], edx
406
        mov     edx, [calldata]
407
        mov     [ecx+usb_gtd.UserData], edx
408
        mov     edx, [buffer]
409
        mov     [ecx+usb_gtd.Buffer], edx
410
; 6. Advance LastTD pointer and activate transfer.
411
        push    [ebx+usb_pipe.LastTD]
412
        mov     [ebx+usb_pipe.LastTD], eax
413
        call    [edi+usb_hardware_func.InsertTransfer]
414
        pop     eax
415
; 7. Release the lock taken in step 2 and
416
; return pointer to the first descriptor for the new transfer.
417
.unlock:
418
        push    eax
419
        lea     ecx, [ebx+usb_pipe.Lock]
420
        call    mutex_unlock
421
        pop     eax
422
.nothing:
423
        ret
424
endp
425
 
426
; Part of API for drivers, see documentation for USBControlTransferAsync.
427
proc usb_control_async stdcall uses ebx edi,\
428
 pipe:dword, config:dword, buffer:dword, size:dword, callback:dword, calldata:dword, flags:dword
429
locals
430
last_td         dd      ?
431
endl
432
; 1. Sanity check: callback must be nonzero.
433
; (It is important for other parts of code.)
434
        cmp     [callback], 0
435
        jz      .return0
436
; 2. Lock the transfer queue.
437
        mov     ebx, [pipe]
438
        lea     ecx, [ebx+usb_pipe.Lock]
439
        call    mutex_lock
440
; 3. If the pipe has already been closed (presumably due to device disconnect),
441
; release the lock taken in step 2 and return zero.
442
        test    [ebx+usb_pipe.Flags], USB_FLAG_CLOSED
443
        jnz     .unlock_return0
444
; A control transfer contains two or three stages:
445
; Setup stage, optional Data stage, Status stage.
446
; 4. Allocate and initialize TDs for the Setup stage.
447
; Payload is 8 bytes from [config].
448
        mov     edx, [ebx+usb_pipe.Controller]
449
        mov     edi, [edx+usb_controller.HardwareFunc]
450
        stdcall [edi+usb_hardware_func.AllocTransfer], [config], 8, 0, [ebx+usb_pipe.LastTD], (2 shl 2) + 0
451
                ; short transfer is an error, direction is DATA0, token is SETUP
452
        mov     [last_td], eax
453
        test    eax, eax
454
        jz      .fail
455
; 5. Allocate and initialize TDs for the Data stage, if [size] is nonzero.
456
; Payload is [size] bytes from [buffer].
457
        mov     edx, [config]
458
        mov     ecx, (3 shl 2) + 1      ; DATA1, token is OUT
459
        cmp     byte [edx], 0
460
        jns     @f
461
        cmp     [size], 0
462
        jz      @f
463
        inc     ecx     ; token is IN
464
@@:
465
        cmp     [size], 0
466
        jz      .nodata
467
        push    ecx
468
        stdcall [edi+usb_hardware_func.AllocTransfer], [buffer], [size], [flags], eax, ecx
469
        pop     ecx
470
        test    eax, eax
471
        jz      .fail
472
        mov     [last_td], eax
473
.nodata:
474
; 6. Allocate and initialize TDs for the Status stage.
475
; No payload.
476
        xor     ecx, 3  ; IN becomes OUT, OUT becomes IN
477
        stdcall [edi+usb_hardware_func.AllocTransfer], 0, 0, 0, eax, ecx
478
        test    eax, eax
479
        jz      .fail
480
; 7. Store callback and its parameters in the last descriptor for this transfer.
481
        mov     ecx, [eax+usb_gtd.PrevVirt]
482
        mov     edx, [callback]
483
        mov     [ecx+usb_gtd.Callback], edx
484
        mov     edx, [calldata]
485
        mov     [ecx+usb_gtd.UserData], edx
486
        mov     edx, [buffer]
487
        mov     [ecx+usb_gtd.Buffer], edx
488
; 8. Advance LastTD pointer and activate transfer.
489
        push    [ebx+usb_pipe.LastTD]
490
        mov     [ebx+usb_pipe.LastTD], eax
491
        call    [edi+usb_hardware_func.InsertTransfer]
492
; 9. Release the lock taken in step 2 and
493
; return pointer to the first descriptor for the new transfer.
494
        lea     ecx, [ebx+usb_pipe.Lock]
495
        call    mutex_unlock
496
        pop     eax
497
        ret
498
.fail:
499
        mov     eax, [last_td]
500
        test    eax, eax
501
        jz      .unlock_return0
502
        stdcall usb_undo_tds, [ebx+usb_pipe.LastTD]
503
.unlock_return0:
504
        lea     ecx, [ebx+usb_pipe.Lock]
505
        call    mutex_unlock
506
.return0:
507
        xor     eax, eax
508
        ret
509
endp
510
 
3908 Serge 511
; Part of API for drivers, see documentation for USBGetParam.
512
proc usb_get_param
513
virtual at esp
514
                dd      ?       ; return address
515
.pipe           dd      ?
516
.param          dd      ?
517
end virtual
518
        mov     edx, [.param]
519
        mov     ecx, [.pipe]
520
        mov     eax, [ecx+usb_pipe.DeviceData]
521
        test    edx, edx
522
        jz      .get_device_descriptor
523
        dec     edx
524
        jz      .get_config_descriptor
525
        dec     edx
526
        jz      .get_speed
527
        or      eax, -1
528
        ret     8
529
.get_device_descriptor:
530
        add     eax, usb_device_data.DeviceDescriptor
531
        ret     8
532
.get_config_descriptor:
533
        movzx   ecx, [eax+usb_device_data.DeviceDescrSize]
534
        lea     eax, [eax+ecx+usb_device_data.DeviceDescriptor]
535
        ret     8
536
.get_speed:
537
        movzx   eax, [eax+usb_device_data.Speed]
538
        ret     8
539
endp
540
 
3555 Serge 541
; Initialize software part of usb_gtd. Called from controller-specific code
542
; somewhere in AllocTransfer with eax -> next (inactive) usb_gtd,
543
; ebx -> usb_pipe, ebp frame from call to AllocTransfer with [.td] ->
544
; current (initializing) usb_gtd.
545
; Returns ecx = [.td].
546
proc usb_init_transfer
547
virtual at ebp-4
548
.Size   dd      ?
549
        rd      2
550
.Buffer dd      ?
551
        dd      ?
552
.Flags  dd      ?
553
.td     dd      ?
554
end virtual
555
        mov     [eax+usb_gtd.Pipe], ebx
556
        mov     ecx, [.td]
557
        mov     [eax+usb_gtd.PrevVirt], ecx
558
        mov     edx, [ecx+usb_gtd.NextVirt]
559
        mov     [ecx+usb_gtd.NextVirt], eax
560
        mov     [eax+usb_gtd.NextVirt], edx
561
        mov     [edx+usb_gtd.PrevVirt], eax
562
        mov     edx, [.Size]
563
        mov     [ecx+usb_gtd.Length], edx
564
        xor     edx, edx
565
        mov     [ecx+usb_gtd.Callback], edx
566
        mov     [ecx+usb_gtd.UserData], edx
567
        ret
568
endp
569
 
570
; Free all TDs for the current transfer if something has failed
571
; during initialization (e.g. no memory for the next TD).
572
; Stdcall with one stack argument = first TD for the transfer
573
; and eax = last initialized TD for the transfer.
574
proc usb_undo_tds
575
        push    [eax+usb_gtd.NextVirt]
576
@@:
577
        cmp     eax, [esp+8]
578
        jz      @f
579
        push    [eax+usb_gtd.PrevVirt]
580
        stdcall [edi+usb_hardware_func.FreeTD], eax
581
        pop     eax
582
        jmp     @b
583
@@:
584
        pop     ecx
585
        mov     [eax+usb_gtd.NextVirt], ecx
586
        mov     [ecx+usb_gtd.PrevVirt], eax
587
        ret     4
588
endp
589
 
590
; Helper procedure for handling short packets in controller-specific code.
591
; Returns with CF cleared if this is the final packet in some stage:
592
; for control transfers that means one of Data and Status stages,
593
; for other transfers - the final packet in the only stage.
594
proc usb_is_final_packet
595
        cmp     [ebx+usb_gtd.Callback], 0
596
        jnz     .nothing
597
        mov     eax, [ebx+usb_gtd.NextVirt]
598
        cmp     [eax+usb_gtd.Callback], 0
599
        jz      .stc
600
        mov     eax, [ebx+usb_gtd.Pipe]
601
        cmp     [eax+usb_pipe.Type], CONTROL_PIPE
602
        jz      .nothing
603
.stc:
604
        stc
605
.nothing:
606
        ret
607
endp
608
 
609
; Helper procedure for controller-specific code:
610
; removes one TD from the transfer queue, ebx -> usb_gtd to remove.
611
proc usb_unlink_td
612
        mov     ecx, [ebx+usb_gtd.Pipe]
613
        add     ecx, usb_pipe.Lock
614
        call    mutex_lock
615
        mov     eax, [ebx+usb_gtd.PrevVirt]
616
        mov     edx, [ebx+usb_gtd.NextVirt]
617
        mov     [edx+usb_gtd.PrevVirt], eax
618
        mov     [eax+usb_gtd.NextVirt], edx
619
        call    mutex_unlock
620
        ret
621
endp
622
 
4423 Serge 623
; One part of transfer is completed, run the associated callback
624
; or update total length in the next part of transfer.
625
; in: ebx -> usb_gtd, ecx = status, edx = length
626
proc usb_process_gtd
627
; 1. Test whether it is the last descriptor in the transfer
628
; <=> it has an associated callback.
629
        mov     eax, [ebx+usb_gtd.Callback]
630
        test    eax, eax
631
        jz      .nocallback
632
; 2. It has an associated callback; call it with corresponding parameters.
633
        stdcall_verify eax, [ebx+usb_gtd.Pipe], ecx, \
634
                [ebx+usb_gtd.Buffer], edx, [ebx+usb_gtd.UserData]
635
        ret
636
.nocallback:
637
; 3. It is an intermediate descriptor. Add its length to the length
638
; in the following descriptor.
639
        mov     eax, [ebx+usb_gtd.NextVirt]
640
        add     [eax+usb_gtd.Length], edx
641
        ret
642
endp
643
 
3555 Serge 644
if USB_STDCALL_VERIFY
645
proc verify_regs
646
virtual at esp
647
        dd      ?       ; return address
648
.edi    dd      ?
649
.esi    dd      ?
650
.ebp    dd      ?
651
.esp    dd      ?
652
.ebx    dd      ?
653
.edx    dd      ?
654
.ecx    dd      ?
655
.eax    dd      ?
656
end virtual
657
        cmp     ebx, [.ebx]
658
        jz      @f
659
        dbgstr 'ERROR!!! ebx changed'
660
@@:
661
        cmp     esi, [.esi]
662
        jz      @f
663
        dbgstr 'ERROR!!! esi changed'
664
@@:
665
        cmp     edi, [.edi]
666
        jz      @f
667
        dbgstr 'ERROR!!! edi changed'
668
@@:
669
        cmp     ebp, [.ebp]
670
        jz      @f
671
        dbgstr 'ERROR!!! ebp changed'
672
@@:
673
        ret
674
endp
675
end if