Subversion Repositories Kolibri OS

Rev

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