Subversion Repositories Kolibri OS

Rev

Rev 5363 | Details | Compare with Previous | Last modification | View Log | RSS feed

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