Subversion Repositories Kolibri OS

Rev

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

  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. }
  16. if USB_STDCALL_VERIFY
  17. STDCALL_VERIFY_EXTRA = 20h
  18. else
  19. STDCALL_VERIFY_EXTRA = 0
  20. end if
  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.
  246.         test    [ebx+usb_pipe.Flags], USB_FLAG_DISABLED
  247.         jnz     @f
  248.         mov     eax, [esi+usb_controller.HardwareFunc]
  249.         call    [eax+usb_hardware_func.DisablePipe]
  250. @@:
  251.         mov     eax, [esi+usb_controller.HardwareFunc]
  252.         call    [eax+usb_hardware_func.UnlinkPipe]
  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
  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.  
  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.  
  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]
  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
  323.         mov     edx, [ebx+usb_pipe.LastTD]
  324.         test    edx, edx
  325.         jz      .no_transfer
  326.         mov     eax, [edx+usb_gtd.NextVirt]
  327.         push    edx
  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
  336. .no_transfer:
  337.         call    mutex_unlock
  338. @@:
  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:
  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.
  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'
  405. ; 8. All drivers have returned from disconnect callback,
  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
  422. ; 9. Free the usb_device_data structure.
  423.         sub     eax, usb_device_data.ClosedPipeList - usb_pipe.NextSibling
  424.         call    free
  425. ; 10. Return.
  426. .nothing:
  427.         pop     edi
  428.         ret
  429. endp
  430.  
  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.  
  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.  
  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.  
  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
  851.