Subversion Repositories Kolibri OS

Rev

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

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