Subversion Repositories Kolibri OS

Rev

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

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