Subversion Repositories Kolibri OS

Rev

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

  1. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  2. ;;                                                              ;;
  3. ;; Copyright (C) KolibriOS team 2013-2024. All rights reserved. ;;
  4. ;; Distributed under terms of the GNU General Public License    ;;
  5. ;;                                                              ;;
  6. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  7.  
  8.  
  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. }
  24. if USB_STDCALL_VERIFY
  25. STDCALL_VERIFY_EXTRA = 20h
  26. else
  27. STDCALL_VERIFY_EXTRA = 0
  28. end if
  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
  42. tt_vars         rd      24      ; should be enough for ehci_select_tt_interrupt_list
  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.
  254.         test    [ebx+usb_pipe.Flags], USB_FLAG_DISABLED
  255.         jnz     @f
  256.         mov     eax, [esi+usb_controller.HardwareFunc]
  257.         call    [eax+usb_hardware_func.DisablePipe]
  258. @@:
  259.         mov     eax, [esi+usb_controller.HardwareFunc]
  260.         call    [eax+usb_hardware_func.UnlinkPipe]
  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
  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.  
  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.  
  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]
  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
  331.         mov     edx, [ebx+usb_pipe.LastTD]
  332.         test    edx, edx
  333.         jz      .no_transfer
  334.         mov     eax, [edx+usb_gtd.NextVirt]
  335.         push    edx
  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
  344. .no_transfer:
  345.         call    mutex_unlock
  346. @@:
  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
  375.         mov     eax, [ecx+usb_device_data.NumInterfaces]
  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:
  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.
  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'
  413. ; 8. All drivers have returned from disconnect callback,
  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
  430. ; 9. Free the usb_device_data structure.
  431.         sub     eax, usb_device_data.ClosedPipeList - usb_pipe.NextSibling
  432.         call    free
  433. ; 10. Return.
  434. .nothing:
  435.         pop     edi
  436.         ret
  437. endp
  438.  
  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.  
  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.  
  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.  
  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.  
  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.  
  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.  
  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
  859.