Subversion Repositories Kolibri OS

Rev

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

  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.  
  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. }
  26. if USB_STDCALL_VERIFY
  27. STDCALL_VERIFY_EXTRA = 20h
  28. else
  29. STDCALL_VERIFY_EXTRA = 0
  30. end if
  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
  44. tt_vars         rd      24      ; should be enough for ehci_select_tt_interrupt_list
  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.
  256.         test    [ebx+usb_pipe.Flags], USB_FLAG_DISABLED
  257.         jnz     @f
  258.         mov     eax, [esi+usb_controller.HardwareFunc]
  259.         call    [eax+usb_hardware_func.DisablePipe]
  260. @@:
  261.         mov     eax, [esi+usb_controller.HardwareFunc]
  262.         call    [eax+usb_hardware_func.UnlinkPipe]
  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
  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.  
  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.  
  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]
  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
  333.         mov     edx, [ebx+usb_pipe.LastTD]
  334.         test    edx, edx
  335.         jz      .no_transfer
  336.         mov     eax, [edx+usb_gtd.NextVirt]
  337.         push    edx
  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
  346. .no_transfer:
  347.         call    mutex_unlock
  348. @@:
  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
  377.         mov     eax, [ecx+usb_device_data.NumInterfaces]
  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:
  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.
  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'
  415. ; 8. All drivers have returned from disconnect callback,
  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
  432. ; 9. Free the usb_device_data structure.
  433.         sub     eax, usb_device_data.ClosedPipeList - usb_pipe.NextSibling
  434.         call    free
  435. ; 10. Return.
  436. .nothing:
  437.         pop     edi
  438.         ret
  439. endp
  440.  
  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.  
  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.  
  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.  
  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.  
  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.  
  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.  
  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
  861.