forked from KolibriOS/kolibrios
API to cancel all queued transfers on USB pipe; add timeout for USB device early initialization
git-svn-id: svn://kolibrios.org@4547 a494cfbc-eb01-0410-851d-a64ba20cac60
This commit is contained in:
parent
49a6c736a9
commit
241079c114
@ -156,8 +156,6 @@ Overlay ehci_hardware_td ?
|
|||||||
; Working area for the current TD, if there is any.
|
; Working area for the current TD, if there is any.
|
||||||
; When TD is retired, it is written to that TD and Overlay is loaded
|
; When TD is retired, it is written to that TD and Overlay is loaded
|
||||||
; from the new TD, if any.
|
; from the new TD, if any.
|
||||||
BaseList dd ?
|
|
||||||
; Pointer to head of the corresponding pipe list.
|
|
||||||
ends
|
ends
|
||||||
|
|
||||||
; This structure describes the static head of every list of pipes.
|
; This structure describes the static head of every list of pipes.
|
||||||
@ -293,6 +291,8 @@ ehci_hardware_func:
|
|||||||
dd ehci_alloc_transfer
|
dd ehci_alloc_transfer
|
||||||
dd ehci_insert_transfer
|
dd ehci_insert_transfer
|
||||||
dd ehci_new_device
|
dd ehci_new_device
|
||||||
|
dd ehci_disable_pipe
|
||||||
|
dd ehci_enable_pipe
|
||||||
ehci_name db 'EHCI',0
|
ehci_name db 'EHCI',0
|
||||||
endg
|
endg
|
||||||
|
|
||||||
@ -998,7 +998,7 @@ end virtual
|
|||||||
jz .return0
|
jz .return0
|
||||||
mov word [edi+ehci_pipe.Flags-sizeof.ehci_pipe], ax
|
mov word [edi+ehci_pipe.Flags-sizeof.ehci_pipe], ax
|
||||||
.insert:
|
.insert:
|
||||||
mov [edi+ehci_pipe.BaseList-sizeof.ehci_pipe], edx
|
mov [edi+usb_pipe.BaseList], edx
|
||||||
; Insert to the head of the corresponding list.
|
; Insert to the head of the corresponding list.
|
||||||
; Note: inserting to the head guarantees that the list traverse in
|
; Note: inserting to the head guarantees that the list traverse in
|
||||||
; ehci_process_updated_schedule, once started, will not interact with new pipes.
|
; ehci_process_updated_schedule, once started, will not interact with new pipes.
|
||||||
@ -1912,19 +1912,58 @@ proc ehci_unlink_pipe
|
|||||||
call ehci_fs_interrupt_list_unlink
|
call ehci_fs_interrupt_list_unlink
|
||||||
.interrupt_common:
|
.interrupt_common:
|
||||||
@@:
|
@@:
|
||||||
mov edx, [ebx+usb_pipe.NextVirt]
|
ret
|
||||||
mov eax, [ebx+usb_pipe.PrevVirt]
|
endp
|
||||||
mov [edx+usb_pipe.PrevVirt], eax
|
|
||||||
mov [eax+usb_pipe.NextVirt], edx
|
; This procedure temporarily removes the given pipe from hardware queue.
|
||||||
|
; esi -> usb_controller, ebx -> usb_pipe
|
||||||
|
proc ehci_disable_pipe
|
||||||
|
mov eax, [ebx+ehci_pipe.NextQH-sizeof.ehci_pipe]
|
||||||
|
mov ecx, [ebx+usb_pipe.PrevVirt]
|
||||||
mov edx, esi
|
mov edx, esi
|
||||||
sub edx, eax
|
sub edx, ecx
|
||||||
cmp edx, sizeof.ehci_controller
|
cmp edx, sizeof.ehci_controller
|
||||||
mov edx, [ebx+ehci_pipe.NextQH-sizeof.ehci_pipe]
|
|
||||||
jb .prev_is_static
|
jb .prev_is_static
|
||||||
mov [eax+ehci_pipe.NextQH-sizeof.ehci_pipe], edx
|
mov [ecx+ehci_pipe.NextQH-sizeof.ehci_pipe], eax
|
||||||
ret
|
ret
|
||||||
.prev_is_static:
|
.prev_is_static:
|
||||||
mov [eax+ehci_static_ep.NextQH-ehci_static_ep.SoftwarePart], edx
|
mov [ecx+ehci_static_ep.NextQH-ehci_static_ep.SoftwarePart], eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; This procedure reinserts the given pipe to hardware queue
|
||||||
|
; after ehci_disable_pipe, with clearing transfer queue.
|
||||||
|
; esi -> usb_controller, ebx -> usb_pipe
|
||||||
|
; edx -> current descriptor, eax -> new last descriptor
|
||||||
|
proc ehci_enable_pipe
|
||||||
|
; 1. Clear transfer queue.
|
||||||
|
; 1a. Clear status bits so that the controller will try to advance the queue
|
||||||
|
; without doing anything, keep DataToggle and PID bits.
|
||||||
|
and [ebx+ehci_pipe.Overlay.Token-sizeof.ehci_pipe], 80000000h
|
||||||
|
; 1b. Set [Alternate]NextTD to physical address of the new last descriptor.
|
||||||
|
sub eax, sizeof.ehci_gtd
|
||||||
|
invoke GetPhysAddr
|
||||||
|
mov [ebx+ehci_pipe.HeadTD-sizeof.ehci_pipe], eax
|
||||||
|
mov [ebx+ehci_pipe.Overlay.NextTD-sizeof.ehci_pipe], eax
|
||||||
|
mov [ebx+ehci_pipe.Overlay.AlternateNextTD-sizeof.ehci_pipe], eax
|
||||||
|
; 2. Reinsert the pipe to hardware queue.
|
||||||
|
lea eax, [ebx-sizeof.ehci_pipe]
|
||||||
|
invoke GetPhysAddr
|
||||||
|
inc eax
|
||||||
|
inc eax
|
||||||
|
mov ecx, [ebx+usb_pipe.PrevVirt]
|
||||||
|
mov edx, esi
|
||||||
|
sub edx, ecx
|
||||||
|
cmp edx, sizeof.ehci_controller
|
||||||
|
jb .prev_is_static
|
||||||
|
mov edx, [ecx+ehci_pipe.NextQH-sizeof.ehci_pipe]
|
||||||
|
mov [ebx+ehci_pipe.NextQH-sizeof.ehci_pipe], edx
|
||||||
|
mov [ecx+ehci_pipe.NextQH-sizeof.ehci_pipe], eax
|
||||||
|
ret
|
||||||
|
.prev_is_static:
|
||||||
|
mov edx, [ecx+ehci_static_ep.NextQH-ehci_static_ep.SoftwarePart]
|
||||||
|
mov [ebx+ehci_pipe.NextQH-sizeof.ehci_pipe], edx
|
||||||
|
mov [ecx+ehci_static_ep.NextQH-ehci_static_ep.SoftwarePart], eax
|
||||||
ret
|
ret
|
||||||
endp
|
endp
|
||||||
|
|
||||||
|
@ -298,7 +298,7 @@ proc ehci_hs_interrupt_list_unlink
|
|||||||
imul eax, ecx
|
imul eax, ecx
|
||||||
movzx ecx, byte [ebx+ehci_pipe.Flags-sizeof.ehci_pipe]
|
movzx ecx, byte [ebx+ehci_pipe.Flags-sizeof.ehci_pipe]
|
||||||
; get target list
|
; get target list
|
||||||
mov edx, [ebx+ehci_pipe.BaseList-sizeof.ehci_pipe]
|
mov edx, [ebx+usb_pipe.BaseList]
|
||||||
; update bandwidth
|
; update bandwidth
|
||||||
.dec_bandwidth:
|
.dec_bandwidth:
|
||||||
shr ecx, 1
|
shr ecx, 1
|
||||||
@ -732,7 +732,7 @@ proc ehci_fs_interrupt_list_unlink
|
|||||||
mov edi, esp
|
mov edi, esp
|
||||||
call tt_fill_split_info
|
call tt_fill_split_info
|
||||||
; get target list
|
; get target list
|
||||||
mov edx, [ebx+ehci_pipe.BaseList-sizeof.ehci_pipe]
|
mov edx, [ebx+usb_pipe.BaseList]
|
||||||
; update bandwidth for Start-Split
|
; update bandwidth for Start-Split
|
||||||
mov eax, [edi+usb_split_info.ssplit_bandwidth]
|
mov eax, [edi+usb_split_info.ssplit_bandwidth]
|
||||||
xor ecx, ecx
|
xor ecx, ecx
|
||||||
|
@ -64,7 +64,7 @@ Flags dd ?
|
|||||||
; 2. Next 4 bits (bits 7-10) are EndpointNumber. This is the USB address of
|
; 2. Next 4 bits (bits 7-10) are EndpointNumber. This is the USB address of
|
||||||
; the endpoint within the function.
|
; the endpoint within the function.
|
||||||
; 3. Next 2 bits (bits 11-12) are Direction. This 2-bit field indicates the
|
; 3. Next 2 bits (bits 11-12) are Direction. This 2-bit field indicates the
|
||||||
; direction of data flow: 1 = IN, 2 = OUT. If neither IN nor OUT is
|
; direction of data flow: 1 = OUT, 2 = IN. If neither IN nor OUT is
|
||||||
; specified, then the direction is determined from the PID field of the TD.
|
; specified, then the direction is determined from the PID field of the TD.
|
||||||
; For CONTROL endpoints, the transfer direction is different
|
; For CONTROL endpoints, the transfer direction is different
|
||||||
; for different transfers, so the value of this field is 0
|
; for different transfers, so the value of this field is 0
|
||||||
@ -322,6 +322,8 @@ ohci_hardware_func:
|
|||||||
dd ohci_alloc_transfer
|
dd ohci_alloc_transfer
|
||||||
dd ohci_insert_transfer
|
dd ohci_insert_transfer
|
||||||
dd ohci_new_device
|
dd ohci_new_device
|
||||||
|
dd ohci_disable_pipe
|
||||||
|
dd ohci_enable_pipe
|
||||||
ohci_name db 'OHCI',0
|
ohci_name db 'OHCI',0
|
||||||
endg
|
endg
|
||||||
|
|
||||||
@ -1014,6 +1016,7 @@ end virtual
|
|||||||
; Inserting to tail would work as well,
|
; Inserting to tail would work as well,
|
||||||
; but let's be consistent with other controllers.
|
; but let's be consistent with other controllers.
|
||||||
.insert:
|
.insert:
|
||||||
|
mov [edi+usb_pipe.BaseList], edx
|
||||||
mov ecx, [edx+usb_pipe.NextVirt]
|
mov ecx, [edx+usb_pipe.NextVirt]
|
||||||
mov [edi+usb_pipe.NextVirt], ecx
|
mov [edi+usb_pipe.NextVirt], ecx
|
||||||
mov [edi+usb_pipe.PrevVirt], edx
|
mov [edi+usb_pipe.PrevVirt], edx
|
||||||
@ -1614,17 +1617,41 @@ proc ohci_unlink_pipe
|
|||||||
mov eax, [ebx+ohci_pipe.Flags-sizeof.ohci_pipe]
|
mov eax, [ebx+ohci_pipe.Flags-sizeof.ohci_pipe]
|
||||||
bt eax, 13
|
bt eax, 13
|
||||||
setc cl
|
setc cl
|
||||||
bt eax, 11
|
bt eax, 12
|
||||||
setc ch
|
setc ch
|
||||||
shr eax, 16
|
shr eax, 16
|
||||||
stdcall usb1_interrupt_list_unlink, eax, ecx
|
stdcall usb1_interrupt_list_unlink, eax, ecx
|
||||||
@@:
|
@@:
|
||||||
mov edx, [ebx+usb_pipe.NextVirt]
|
ret
|
||||||
mov eax, [ebx+usb_pipe.PrevVirt]
|
endp
|
||||||
mov [edx+usb_pipe.PrevVirt], eax
|
|
||||||
mov [eax+usb_pipe.NextVirt], edx
|
; This procedure temporarily removes the given pipe from hardware queue,
|
||||||
mov edx, [ebx+ohci_pipe.NextED-sizeof.ohci_pipe]
|
; keeping it in software lists.
|
||||||
mov [eax+ohci_pipe.NextED-sizeof.ohci_pipe], edx
|
; esi -> usb_controller, ebx -> usb_pipe
|
||||||
|
proc ohci_disable_pipe
|
||||||
|
mov eax, [ebx+ohci_pipe.NextED-sizeof.ohci_pipe]
|
||||||
|
mov edx, [ebx+usb_pipe.PrevVirt]
|
||||||
|
mov [edx+ohci_pipe.NextED-sizeof.ohci_pipe], eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; This procedure reinserts the given pipe from hardware queue
|
||||||
|
; after ehci_disable_pipe, with clearing transfer queue.
|
||||||
|
; esi -> usb_controller, ebx -> usb_pipe
|
||||||
|
; edx -> current descriptor, eax -> new last descriptor
|
||||||
|
proc ohci_enable_pipe
|
||||||
|
sub eax, sizeof.ohci_gtd
|
||||||
|
invoke GetPhysAddr
|
||||||
|
mov edx, [ebx+ohci_pipe.HeadP-sizeof.ohci_pipe]
|
||||||
|
and edx, 2
|
||||||
|
or eax, edx
|
||||||
|
mov [ebx+ohci_pipe.HeadP-sizeof.ohci_pipe], eax
|
||||||
|
lea eax, [ebx-sizeof.ohci_pipe]
|
||||||
|
invoke GetPhysAddr
|
||||||
|
mov edx, [ebx+usb_pipe.PrevVirt]
|
||||||
|
mov ecx, [edx+ohci_pipe.NextED-sizeof.ohci_pipe]
|
||||||
|
mov [ebx+ohci_pipe.NextED-sizeof.ohci_pipe], ecx
|
||||||
|
mov [edx+ohci_pipe.NextED-sizeof.ohci_pipe], eax
|
||||||
ret
|
ret
|
||||||
endp
|
endp
|
||||||
|
|
||||||
|
@ -274,6 +274,8 @@ uhci_hardware_func:
|
|||||||
dd uhci_alloc_transfer
|
dd uhci_alloc_transfer
|
||||||
dd uhci_insert_transfer
|
dd uhci_insert_transfer
|
||||||
dd uhci_new_device
|
dd uhci_new_device
|
||||||
|
dd uhci_disable_pipe
|
||||||
|
dd uhci_enable_pipe
|
||||||
uhci_name db 'UHCI',0
|
uhci_name db 'UHCI',0
|
||||||
endg
|
endg
|
||||||
|
|
||||||
@ -1133,7 +1135,6 @@ proc uhci_fix_toggle
|
|||||||
jnz .loop
|
jnz .loop
|
||||||
; 5. Flip the toggle bit in uhci_pipe structure.
|
; 5. Flip the toggle bit in uhci_pipe structure.
|
||||||
xor byte [ecx+uhci_pipe.Token-sizeof.uhci_pipe-usb_pipe.Lock+2], 1 shl (19-16)
|
xor byte [ecx+uhci_pipe.Token-sizeof.uhci_pipe-usb_pipe.Lock+2], 1 shl (19-16)
|
||||||
or dword [ecx+uhci_pipe.Token-sizeof.uhci_pipe-usb_pipe.Lock], eax
|
|
||||||
; 6. Unlock the transfer queue.
|
; 6. Unlock the transfer queue.
|
||||||
invoke MutexUnlock
|
invoke MutexUnlock
|
||||||
.nothing:
|
.nothing:
|
||||||
@ -1461,6 +1462,7 @@ end virtual
|
|||||||
test edx, edx
|
test edx, edx
|
||||||
jz .return0
|
jz .return0
|
||||||
.insert:
|
.insert:
|
||||||
|
mov [edi+usb_pipe.BaseList], edx
|
||||||
; Insert to the head of the corresponding list.
|
; Insert to the head of the corresponding list.
|
||||||
; Note: inserting to the head guarantees that the list traverse in
|
; Note: inserting to the head guarantees that the list traverse in
|
||||||
; uhci_process_updated_schedule, once started, will not interact with new pipes.
|
; uhci_process_updated_schedule, once started, will not interact with new pipes.
|
||||||
@ -1505,17 +1507,44 @@ proc uhci_unlink_pipe
|
|||||||
shr eax, 21
|
shr eax, 21
|
||||||
stdcall usb1_interrupt_list_unlink, eax, ecx
|
stdcall usb1_interrupt_list_unlink, eax, ecx
|
||||||
@@:
|
@@:
|
||||||
; Note: we need to ensure that NextVirt field of the pipe is not modified;
|
ret
|
||||||
; this procedure can be called while uhci_process_updated_schedule processes
|
endp
|
||||||
; the same pipe, and it needs a correct NextVirt field to continue.
|
|
||||||
mov edx, [ebx+usb_pipe.NextVirt]
|
; This procedure temporarily removes the given pipe from hardware queue,
|
||||||
mov eax, [ebx+usb_pipe.PrevVirt]
|
; keeping it in software lists.
|
||||||
mov [edx+usb_pipe.PrevVirt], eax
|
; esi -> usb_controller, ebx -> usb_pipe
|
||||||
mov [eax+usb_pipe.NextVirt], edx
|
proc uhci_disable_pipe
|
||||||
; Note: eax could be either usb_pipe or usb_static_ep;
|
mov eax, [ebx+uhci_pipe.NextQH-sizeof.uhci_pipe]
|
||||||
|
mov edx, [ebx+usb_pipe.PrevVirt]
|
||||||
|
; Note: edx could be either usb_pipe or usb_static_ep;
|
||||||
; fortunately, NextQH and SoftwarePart have same offsets in both.
|
; fortunately, NextQH and SoftwarePart have same offsets in both.
|
||||||
mov edx, [ebx+uhci_pipe.NextQH-sizeof.uhci_pipe]
|
mov [edx+uhci_pipe.NextQH-sizeof.uhci_pipe], eax
|
||||||
mov [eax+uhci_pipe.NextQH-sizeof.uhci_pipe], edx
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; This procedure reinserts the given pipe from hardware queue
|
||||||
|
; after ehci_disable_pipe, with clearing transfer queue.
|
||||||
|
; esi -> usb_controller, ebx -> usb_pipe
|
||||||
|
; edx -> current descriptor, eax -> new last descriptor
|
||||||
|
proc uhci_enable_pipe
|
||||||
|
; 1. Copy DataToggle bit from edx to pipe.
|
||||||
|
mov ecx, [edx+uhci_gtd.Token-sizeof.uhci_gtd]
|
||||||
|
xor ecx, [ebx+uhci_pipe.Token-sizeof.uhci_pipe]
|
||||||
|
and ecx, 1 shl 19
|
||||||
|
xor [ebx+uhci_pipe.Token-sizeof.uhci_pipe], ecx
|
||||||
|
; 2. Store new last descriptor as the current HeadTD.
|
||||||
|
sub eax, sizeof.uhci_gtd
|
||||||
|
invoke GetPhysAddr
|
||||||
|
mov [ebx+uhci_pipe.HeadTD-sizeof.uhci_pipe], eax
|
||||||
|
; 3. Reinsert the pipe to hardware queue.
|
||||||
|
lea eax, [ebx-sizeof.uhci_pipe]
|
||||||
|
invoke GetPhysAddr
|
||||||
|
inc eax
|
||||||
|
inc eax
|
||||||
|
mov edx, [ebx+usb_pipe.PrevVirt]
|
||||||
|
mov ecx, [edx+uhci_pipe.NextQH-sizeof.uhci_pipe]
|
||||||
|
mov [ebx+uhci_pipe.NextQH-sizeof.uhci_pipe], ecx
|
||||||
|
mov [edx+uhci_pipe.NextQH-sizeof.uhci_pipe], eax
|
||||||
ret
|
ret
|
||||||
endp
|
endp
|
||||||
|
|
||||||
|
@ -167,12 +167,7 @@ end virtual
|
|||||||
mov eax, [.maxpacket]
|
mov eax, [.maxpacket]
|
||||||
mov ecx, dword [.lowspeed]
|
mov ecx, dword [.lowspeed]
|
||||||
call calc_usb1_bandwidth
|
call calc_usb1_bandwidth
|
||||||
; find list header
|
mov edx, [ebx+usb_pipe.BaseList]
|
||||||
mov edx, ebx
|
|
||||||
@@:
|
|
||||||
mov edx, [edx+usb_pipe.NextVirt]
|
|
||||||
cmp [edx+usb_pipe.Controller], esi
|
|
||||||
jz @b
|
|
||||||
; subtract pipe bandwidth
|
; subtract pipe bandwidth
|
||||||
sub [edx+usb_static_ep.Bandwidth], eax
|
sub [edx+usb_static_ep.Bandwidth], eax
|
||||||
ret 8
|
ret 8
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
; =============================================================================
|
; =============================================================================
|
||||||
; Version of all structures related to host controllers.
|
; Version of all structures related to host controllers.
|
||||||
; Must be the same in kernel and *hci-drivers.
|
; Must be the same in kernel and *hci-drivers.
|
||||||
USBHC_VERSION = 1
|
USBHC_VERSION = 2
|
||||||
|
|
||||||
; USB device must have at least 100ms of stable power before initializing can
|
; USB device must have at least 100ms of stable power before initializing can
|
||||||
; proceed; one timer tick is 10ms, so enforce delay in 10 ticks
|
; proceed; one timer tick is 10ms, so enforce delay in 10 ticks
|
||||||
@ -46,6 +46,7 @@ USB_STATUS_BUFUNDERRUN = 13 ; underflow of internal controller buffer
|
|||||||
USB_STATUS_CLOSED = 16 ; pipe closed
|
USB_STATUS_CLOSED = 16 ; pipe closed
|
||||||
; either explicitly with USBClosePipe
|
; either explicitly with USBClosePipe
|
||||||
; or implicitly due to device disconnect
|
; or implicitly due to device disconnect
|
||||||
|
USB_STATUS_CANCELLED = 17 ; transfer cancelled with USBAbortPipe
|
||||||
|
|
||||||
; Possible speeds of USB devices
|
; Possible speeds of USB devices
|
||||||
USB_SPEED_FS = 0 ; full-speed
|
USB_SPEED_FS = 0 ; full-speed
|
||||||
@ -63,6 +64,9 @@ USB_FLAG_CAN_FREE = 2
|
|||||||
USB_FLAG_EXTRA_WAIT = 4
|
USB_FLAG_EXTRA_WAIT = 4
|
||||||
; The pipe was in wait list, while another event occured;
|
; The pipe was in wait list, while another event occured;
|
||||||
; when the first wait will be done, reinsert the pipe to wait list
|
; when the first wait will be done, reinsert the pipe to wait list
|
||||||
|
USB_FLAG_DISABLED = 8
|
||||||
|
; The pipe is temporarily disabled so that it is not visible to hardware
|
||||||
|
; but still remains in software list. Used for usb_abort_pipe.
|
||||||
USB_FLAG_CLOSED_BIT = 0 ; USB_FLAG_CLOSED = 1 shl USB_FLAG_CLOSED_BIT
|
USB_FLAG_CLOSED_BIT = 0 ; USB_FLAG_CLOSED = 1 shl USB_FLAG_CLOSED_BIT
|
||||||
|
|
||||||
; =============================================================================
|
; =============================================================================
|
||||||
@ -136,6 +140,14 @@ NewDevice dd ?
|
|||||||
; Initiate configuration of a new device (create pseudo-pipe describing that
|
; Initiate configuration of a new device (create pseudo-pipe describing that
|
||||||
; device and call usb_new_device).
|
; device and call usb_new_device).
|
||||||
; esi -> usb_controller, eax = speed (one of USB_SPEED_* constants).
|
; esi -> usb_controller, eax = speed (one of USB_SPEED_* constants).
|
||||||
|
DisablePipe dd ?
|
||||||
|
; This procedure temporarily removes the given pipe from hardware queue.
|
||||||
|
; esi -> usb_controller, ebx -> usb_pipe
|
||||||
|
EnablePipe dd ?
|
||||||
|
; This procedure reinserts the given pipe to hardware queue
|
||||||
|
; after DisablePipe, with clearing transfer queue.
|
||||||
|
; esi -> usb_controller, ebx -> usb_pipe
|
||||||
|
; edx -> current descriptor, eax -> new last descriptor
|
||||||
ends
|
ends
|
||||||
|
|
||||||
; pointers to kernel API functions that are called from *HCI-drivers
|
; pointers to kernel API functions that are called from *HCI-drivers
|
||||||
@ -307,6 +319,8 @@ NextVirt dd ?
|
|||||||
PrevVirt dd ?
|
PrevVirt dd ?
|
||||||
; Previous endpoint in the processing list.
|
; Previous endpoint in the processing list.
|
||||||
; See also NextVirt field and the description before NextVirt field.
|
; See also NextVirt field and the description before NextVirt field.
|
||||||
|
BaseList dd ?
|
||||||
|
; Pointer to head of the processing list.
|
||||||
;
|
;
|
||||||
; Every pipe has the associated transfer queue, that is, the double-linked
|
; Every pipe has the associated transfer queue, that is, the double-linked
|
||||||
; list of Transfer Descriptors aka TD. For Control, Bulk and Interrupt
|
; list of Transfer Descriptors aka TD. For Control, Bulk and Interrupt
|
||||||
@ -427,6 +441,8 @@ DeviceDescrSize db ?
|
|||||||
; Size of device descriptor.
|
; Size of device descriptor.
|
||||||
Speed db ?
|
Speed db ?
|
||||||
; Device speed, one of USB_SPEED_*.
|
; Device speed, one of USB_SPEED_*.
|
||||||
|
Timer dd ?
|
||||||
|
; Handle of timer that handles request timeout.
|
||||||
NumInterfaces dd ?
|
NumInterfaces dd ?
|
||||||
; Number of interfaces.
|
; Number of interfaces.
|
||||||
ConfigDataSize dd ?
|
ConfigDataSize dd ?
|
||||||
|
@ -114,7 +114,7 @@ proc get_phys_addr
|
|||||||
ret
|
ret
|
||||||
endp
|
endp
|
||||||
|
|
||||||
; Put the given control pipe in the wait list;
|
; Put the given control/bulk pipe in the wait list;
|
||||||
; called when the pipe structure is changed and a possible hardware cache
|
; called when the pipe structure is changed and a possible hardware cache
|
||||||
; needs to be synchronized. When it will be known that the cache is updated,
|
; needs to be synchronized. When it will be known that the cache is updated,
|
||||||
; usb_subscription_done procedure will be called.
|
; usb_subscription_done procedure will be called.
|
||||||
@ -128,6 +128,17 @@ proc usb_subscribe_control
|
|||||||
ret
|
ret
|
||||||
endp
|
endp
|
||||||
|
|
||||||
|
; Same as usb_subscribe_control, but for interrupt/isochronous pipe.
|
||||||
|
proc usb_subscribe_periodic
|
||||||
|
cmp [ebx+usb_pipe.NextWait], -1
|
||||||
|
jnz @f
|
||||||
|
mov eax, [esi+usb_controller.WaitPipeListPeriodic]
|
||||||
|
mov [ebx+usb_pipe.NextWait], eax
|
||||||
|
mov [esi+usb_controller.WaitPipeListPeriodic], ebx
|
||||||
|
@@:
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
; Called after synchronization of hardware cache with software changes.
|
; Called after synchronization of hardware cache with software changes.
|
||||||
; Continues process of device enumeration based on when it was delayed
|
; Continues process of device enumeration based on when it was delayed
|
||||||
; due to call to usb_subscribe_control.
|
; due to call to usb_subscribe_control.
|
||||||
@ -254,13 +265,18 @@ proc usb_process_one_wait_list
|
|||||||
mov [esi+usb_controller.WaitPipeListAsync+edx], ebx
|
mov [esi+usb_controller.WaitPipeListAsync+edx], ebx
|
||||||
jmp .continue
|
jmp .continue
|
||||||
.process:
|
.process:
|
||||||
; 7. Call the handler depending on USB_FLAG_CLOSED.
|
; 7. Call the handler depending on USB_FLAG_CLOSED and USB_FLAG_DISABLED.
|
||||||
or [ebx+usb_pipe.NextWait], -1
|
or [ebx+usb_pipe.NextWait], -1
|
||||||
test [ebx+usb_pipe.Flags], USB_FLAG_CLOSED
|
test [ebx+usb_pipe.Flags], USB_FLAG_CLOSED
|
||||||
jz .nodisconnect
|
jz .nodisconnect
|
||||||
call usb_pipe_closed
|
call usb_pipe_closed
|
||||||
jmp .continue
|
jmp .continue
|
||||||
.nodisconnect:
|
.nodisconnect:
|
||||||
|
test [ebx+usb_pipe.Flags], USB_FLAG_DISABLED
|
||||||
|
jz .nodisabled
|
||||||
|
call usb_pipe_disabled
|
||||||
|
jmp .continue
|
||||||
|
.nodisabled:
|
||||||
call usb_subscription_done
|
call usb_subscription_done
|
||||||
.continue:
|
.continue:
|
||||||
; 8. Restore edx and next pipe saved in step 5 and continue the loop.
|
; 8. Restore edx and next pipe saved in step 5 and continue the loop.
|
||||||
|
@ -13,6 +13,11 @@ else
|
|||||||
stdcall arg
|
stdcall arg
|
||||||
end if
|
end if
|
||||||
}
|
}
|
||||||
|
if USB_STDCALL_VERIFY
|
||||||
|
STDCALL_VERIFY_EXTRA = 20h
|
||||||
|
else
|
||||||
|
STDCALL_VERIFY_EXTRA = 0
|
||||||
|
end if
|
||||||
|
|
||||||
; Initialization of usb_static_ep structure,
|
; Initialization of usb_static_ep structure,
|
||||||
; called from controller-specific initialization; edi -> usb_static_ep
|
; called from controller-specific initialization; edi -> usb_static_ep
|
||||||
@ -238,8 +243,17 @@ proc usb_close_pipe_nolock
|
|||||||
call mutex_lock
|
call mutex_lock
|
||||||
push ecx
|
push ecx
|
||||||
; 3b. Let the controller-specific code do its job.
|
; 3b. Let the controller-specific code do its job.
|
||||||
|
test [ebx+usb_pipe.Flags], USB_FLAG_DISABLED
|
||||||
|
jnz @f
|
||||||
|
mov eax, [esi+usb_controller.HardwareFunc]
|
||||||
|
call [eax+usb_hardware_func.DisablePipe]
|
||||||
|
@@:
|
||||||
mov eax, [esi+usb_controller.HardwareFunc]
|
mov eax, [esi+usb_controller.HardwareFunc]
|
||||||
call [eax+usb_hardware_func.UnlinkPipe]
|
call [eax+usb_hardware_func.UnlinkPipe]
|
||||||
|
mov edx, [ebx+usb_pipe.NextVirt]
|
||||||
|
mov eax, [ebx+usb_pipe.PrevVirt]
|
||||||
|
mov [edx+usb_pipe.PrevVirt], eax
|
||||||
|
mov [eax+usb_pipe.NextVirt], edx
|
||||||
; 3c. Release the corresponding lock.
|
; 3c. Release the corresponding lock.
|
||||||
pop ecx
|
pop ecx
|
||||||
call mutex_unlock
|
call mutex_unlock
|
||||||
@ -262,36 +276,66 @@ proc usb_close_pipe_nolock
|
|||||||
ret
|
ret
|
||||||
endp
|
endp
|
||||||
|
|
||||||
|
; This procedure is called when all transfers are aborted
|
||||||
|
; either due to call to usb_abort_pipe or due to pipe closing.
|
||||||
|
; It notifies all callbacks and frees all transfer descriptors.
|
||||||
|
; ebx -> usb_pipe, esi -> usb_controller, edi -> usb_hardware_func
|
||||||
|
; three stack parameters: status code for callback functions
|
||||||
|
; and descriptors where to start and stop.
|
||||||
|
proc usb_pipe_aborted
|
||||||
|
virtual at esp
|
||||||
|
dd ? ; return address
|
||||||
|
.status dd ? ; USB_STATUS_CLOSED or USB_STATUS_CANCELLED
|
||||||
|
.first_td dd ?
|
||||||
|
.last_td dd ?
|
||||||
|
end virtual
|
||||||
|
; Loop over all transfers, calling the driver with the given status
|
||||||
|
; and freeing all descriptors except the last one.
|
||||||
|
.loop:
|
||||||
|
mov edx, [.first_td]
|
||||||
|
cmp edx, [.last_td]
|
||||||
|
jz .done
|
||||||
|
mov ecx, [edx+usb_gtd.Callback]
|
||||||
|
test ecx, ecx
|
||||||
|
jz .no_callback
|
||||||
|
stdcall_verify ecx, ebx, [.status+12+STDCALL_VERIFY_EXTRA], \
|
||||||
|
[edx+usb_gtd.Buffer], 0, [edx+usb_gtd.UserData]
|
||||||
|
mov edx, [.first_td]
|
||||||
|
.no_callback:
|
||||||
|
mov eax, [edx+usb_gtd.NextVirt]
|
||||||
|
mov [.first_td], eax
|
||||||
|
stdcall [edi+usb_hardware_func.FreeTD], edx
|
||||||
|
jmp .loop
|
||||||
|
.done:
|
||||||
|
ret 12
|
||||||
|
endp
|
||||||
|
|
||||||
; This procedure is called when a pipe with USB_FLAG_CLOSED is removed from the
|
; This procedure is called when a pipe with USB_FLAG_CLOSED is removed from the
|
||||||
; corresponding wait list. It means that the hardware has fully forgot about it.
|
; corresponding wait list. It means that the hardware has fully forgot about it.
|
||||||
; ebx -> usb_pipe, esi -> usb_controller
|
; ebx -> usb_pipe, esi -> usb_controller
|
||||||
proc usb_pipe_closed
|
proc usb_pipe_closed
|
||||||
push edi
|
push edi
|
||||||
mov edi, [esi+usb_controller.HardwareFunc]
|
mov edi, [esi+usb_controller.HardwareFunc]
|
||||||
; 1. Loop over all transfers, calling the driver with USB_STATUS_CLOSED
|
; 1. Notify all registered callbacks with status USB_STATUS_CLOSED, if any,
|
||||||
; and freeing all descriptors.
|
; and free all transfer descriptors, including the last one.
|
||||||
|
lea ecx, [ebx+usb_pipe.Lock]
|
||||||
|
call mutex_lock
|
||||||
mov edx, [ebx+usb_pipe.LastTD]
|
mov edx, [ebx+usb_pipe.LastTD]
|
||||||
test edx, edx
|
test edx, edx
|
||||||
jz .no_transfer
|
jz .no_transfer
|
||||||
mov edx, [edx+usb_gtd.NextVirt]
|
mov eax, [edx+usb_gtd.NextVirt]
|
||||||
.transfer_loop:
|
|
||||||
cmp edx, [ebx+usb_pipe.LastTD]
|
|
||||||
jz .transfer_done
|
|
||||||
mov ecx, [edx+usb_gtd.Callback]
|
|
||||||
test ecx, ecx
|
|
||||||
jz .no_callback
|
|
||||||
push edx
|
push edx
|
||||||
stdcall_verify ecx, ebx, USB_STATUS_CLOSED, \
|
push eax
|
||||||
[edx+usb_gtd.Buffer], 0, [edx+usb_gtd.UserData]
|
call mutex_unlock
|
||||||
pop edx
|
push USB_STATUS_CLOSED
|
||||||
.no_callback:
|
call usb_pipe_aborted
|
||||||
push [edx+usb_gtd.NextVirt]
|
; It is safe to free LastTD here:
|
||||||
stdcall [edi+usb_hardware_func.FreeTD], edx
|
; usb_*_transfer_async do not enqueue new transfers if USB_FLAG_CLOSED is set.
|
||||||
pop edx
|
stdcall [edi+usb_hardware_func.FreeTD], [ebx+usb_pipe.LastTD]
|
||||||
jmp .transfer_loop
|
jmp @f
|
||||||
.transfer_done:
|
|
||||||
stdcall [edi+usb_hardware_func.FreeTD], edx
|
|
||||||
.no_transfer:
|
.no_transfer:
|
||||||
|
call mutex_unlock
|
||||||
|
@@:
|
||||||
; 2. Decrement number of pipes for the device.
|
; 2. Decrement number of pipes for the device.
|
||||||
; If this pipe is the last pipe, go to 5.
|
; If this pipe is the last pipe, go to 5.
|
||||||
mov ecx, [ebx+usb_pipe.DeviceData]
|
mov ecx, [ebx+usb_pipe.DeviceData]
|
||||||
@ -342,14 +386,23 @@ proc usb_pipe_closed
|
|||||||
dec eax
|
dec eax
|
||||||
jnz .notify_loop
|
jnz .notify_loop
|
||||||
.notify_done:
|
.notify_done:
|
||||||
; 6. Bus address, if assigned, can now be reused.
|
; 6. Kill the timer, if active.
|
||||||
|
; (Usually not; possible if device is disconnected
|
||||||
|
; while processing SET_ADDRESS request).
|
||||||
|
mov eax, [ebx+usb_pipe.DeviceData]
|
||||||
|
cmp [eax+usb_device_data.Timer], 0
|
||||||
|
jz @f
|
||||||
|
stdcall cancel_timer_hs, [eax+usb_device_data.Timer]
|
||||||
|
mov [eax+usb_device_data.Timer], 0
|
||||||
|
@@:
|
||||||
|
; 7. Bus address, if assigned, can now be reused.
|
||||||
call [edi+usb_hardware_func.GetDeviceAddress]
|
call [edi+usb_hardware_func.GetDeviceAddress]
|
||||||
test eax, eax
|
test eax, eax
|
||||||
jz @f
|
jz @f
|
||||||
bts [esi+usb_controller.ExistingAddresses], eax
|
bts [esi+usb_controller.ExistingAddresses], eax
|
||||||
@@:
|
@@:
|
||||||
dbgstr 'USB device disconnected'
|
dbgstr 'USB device disconnected'
|
||||||
; 7. All drivers have returned from disconnect callback,
|
; 8. All drivers have returned from disconnect callback,
|
||||||
; so all drivers should not use any device-related pipes.
|
; so all drivers should not use any device-related pipes.
|
||||||
; Free the remaining pipes.
|
; Free the remaining pipes.
|
||||||
mov eax, [ebx+usb_pipe.DeviceData]
|
mov eax, [ebx+usb_pipe.DeviceData]
|
||||||
@ -366,15 +419,74 @@ proc usb_pipe_closed
|
|||||||
.free_done:
|
.free_done:
|
||||||
stdcall [edi+usb_hardware_func.FreePipe], ebx
|
stdcall [edi+usb_hardware_func.FreePipe], ebx
|
||||||
pop eax
|
pop eax
|
||||||
; 8. Free the usb_device_data structure.
|
; 9. Free the usb_device_data structure.
|
||||||
sub eax, usb_device_data.ClosedPipeList - usb_pipe.NextSibling
|
sub eax, usb_device_data.ClosedPipeList - usb_pipe.NextSibling
|
||||||
call free
|
call free
|
||||||
; 9. Return.
|
; 10. Return.
|
||||||
.nothing:
|
.nothing:
|
||||||
pop edi
|
pop edi
|
||||||
ret
|
ret
|
||||||
endp
|
endp
|
||||||
|
|
||||||
|
; This procedure is called when a pipe with USB_FLAG_DISABLED is removed from the
|
||||||
|
; corresponding wait list. It means that the hardware has fully forgot about it.
|
||||||
|
; ebx -> usb_pipe, esi -> usb_controller
|
||||||
|
proc usb_pipe_disabled
|
||||||
|
push edi
|
||||||
|
mov edi, [esi+usb_controller.HardwareFunc]
|
||||||
|
; 1. Acquire pipe lock.
|
||||||
|
lea ecx, [ebx+usb_pipe.Lock]
|
||||||
|
call mutex_lock
|
||||||
|
; 2. Clear USB_FLAG_DISABLED in pipe state.
|
||||||
|
and [ebx+usb_pipe.Flags], not USB_FLAG_DISABLED
|
||||||
|
; 3. Sanity check: ignore uninitialized pipes.
|
||||||
|
cmp [ebx+usb_pipe.LastTD], 0
|
||||||
|
jz .no_transfer
|
||||||
|
; 4. Acquire the first and last to-be-cancelled transfer descriptor,
|
||||||
|
; save them in stack for the step 6,
|
||||||
|
; ask the controller driver to enable the pipe for hardware,
|
||||||
|
; removing transfers between first and last to-be-cancelled descriptors.
|
||||||
|
lea ecx, [esi+usb_controller.ControlLock]
|
||||||
|
cmp [ebx+usb_pipe.Type], BULK_PIPE
|
||||||
|
jb @f ; control pipe
|
||||||
|
lea ecx, [esi+usb_controller.BulkLock]
|
||||||
|
jz @f ; bulk pipe
|
||||||
|
lea ecx, [esi+usb_controller.PeriodicLock]
|
||||||
|
@@:
|
||||||
|
call mutex_lock
|
||||||
|
mov eax, [ebx+usb_pipe.BaseList]
|
||||||
|
mov edx, [eax+usb_pipe.NextVirt]
|
||||||
|
mov [ebx+usb_pipe.NextVirt], edx
|
||||||
|
mov [ebx+usb_pipe.PrevVirt], eax
|
||||||
|
mov [edx+usb_pipe.PrevVirt], ebx
|
||||||
|
mov [eax+usb_pipe.NextVirt], ebx
|
||||||
|
mov eax, [ebx+usb_pipe.LastTD]
|
||||||
|
mov edx, [eax+usb_gtd.NextVirt]
|
||||||
|
mov [eax+usb_gtd.NextVirt], eax
|
||||||
|
mov [eax+usb_gtd.PrevVirt], eax
|
||||||
|
push eax
|
||||||
|
push edx
|
||||||
|
push ecx
|
||||||
|
call [edi+usb_hardware_func.EnablePipe]
|
||||||
|
pop ecx
|
||||||
|
call mutex_unlock
|
||||||
|
; 5. Release pipe lock acquired at step 1.
|
||||||
|
; Callbacks called at step 6 can insert new transfers,
|
||||||
|
; so we cannot call usb_pipe_aborted while holding pipe lock.
|
||||||
|
lea ecx, [ebx+usb_pipe.Lock]
|
||||||
|
call mutex_unlock
|
||||||
|
; 6. Notify all registered callbacks with status USB_STATUS_CANCELLED, if any.
|
||||||
|
; Two arguments describing transfers range were pushed at step 4.
|
||||||
|
push USB_STATUS_CANCELLED
|
||||||
|
call usb_pipe_aborted
|
||||||
|
pop edi
|
||||||
|
ret
|
||||||
|
.no_transfer:
|
||||||
|
call mutex_unlock
|
||||||
|
pop edi
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
; Part of API for drivers, see documentation for USBNormalTransferAsync.
|
; Part of API for drivers, see documentation for USBNormalTransferAsync.
|
||||||
proc usb_normal_transfer_async stdcall uses ebx edi,\
|
proc usb_normal_transfer_async stdcall uses ebx edi,\
|
||||||
pipe:dword, buffer:dword, size:dword, callback:dword, calldata:dword, flags:dword
|
pipe:dword, buffer:dword, size:dword, callback:dword, calldata:dword, flags:dword
|
||||||
@ -508,6 +620,69 @@ endl
|
|||||||
ret
|
ret
|
||||||
endp
|
endp
|
||||||
|
|
||||||
|
; Part of API for drivers, see documentation for USBAbortPipe.
|
||||||
|
proc usb_abort_pipe
|
||||||
|
push ebx esi ; save used registers to be stdcall
|
||||||
|
virtual at esp
|
||||||
|
rd 2 ; saved registers
|
||||||
|
dd ? ; return address
|
||||||
|
.pipe dd ?
|
||||||
|
end virtual
|
||||||
|
mov ebx, [.pipe]
|
||||||
|
; 1. Acquire pipe lock.
|
||||||
|
lea ecx, [ebx+usb_pipe.Lock]
|
||||||
|
call mutex_lock
|
||||||
|
; 2. If the pipe is already closed or abort is in progress,
|
||||||
|
; just release pipe lock and return.
|
||||||
|
test [ebx+usb_pipe.Flags], USB_FLAG_CLOSED + USB_FLAG_DISABLED
|
||||||
|
jnz .nothing
|
||||||
|
; 3. Mark the pipe as aborting.
|
||||||
|
or [ebx+usb_pipe.Flags], USB_FLAG_DISABLED
|
||||||
|
; 4. We cannot do anything except adding new transfers concurrently with hardware.
|
||||||
|
; Ask the controller driver to (temporarily) remove the pipe from hardware queue.
|
||||||
|
mov esi, [ebx+usb_pipe.Controller]
|
||||||
|
; 4a. Acquire queue lock.
|
||||||
|
lea ecx, [esi+usb_controller.ControlLock]
|
||||||
|
cmp [ebx+usb_pipe.Type], BULK_PIPE
|
||||||
|
jb @f ; control pipe
|
||||||
|
lea ecx, [esi+usb_controller.BulkLock]
|
||||||
|
jz @f ; bulk pipe
|
||||||
|
lea ecx, [esi+usb_controller.PeriodicLock]
|
||||||
|
@@:
|
||||||
|
call mutex_lock
|
||||||
|
push ecx
|
||||||
|
; 4b. Call the driver.
|
||||||
|
mov eax, [esi+usb_controller.HardwareFunc]
|
||||||
|
call [eax+usb_hardware_func.DisablePipe]
|
||||||
|
; 4c. Remove the pipe from software list.
|
||||||
|
mov eax, [ebx+usb_pipe.NextVirt]
|
||||||
|
mov edx, [ebx+usb_pipe.PrevVirt]
|
||||||
|
mov [eax+usb_pipe.PrevVirt], edx
|
||||||
|
mov [edx+usb_pipe.NextVirt], eax
|
||||||
|
; 4c. Register the pipe in corresponding wait list.
|
||||||
|
test [ebx+usb_pipe.Type], 1
|
||||||
|
jz .control_bulk
|
||||||
|
call usb_subscribe_periodic
|
||||||
|
jmp @f
|
||||||
|
.control_bulk:
|
||||||
|
call usb_subscribe_control
|
||||||
|
@@:
|
||||||
|
; 4d. Release queue lock.
|
||||||
|
pop ecx
|
||||||
|
call mutex_unlock
|
||||||
|
; 4e. Notify the USB thread about new work.
|
||||||
|
push ebx esi edi
|
||||||
|
call usb_wakeup
|
||||||
|
pop edi esi ebx
|
||||||
|
; That's all for now. To be continued in usb_pipe_disabled.
|
||||||
|
; 5. Release pipe lock acquired at step 1 and return.
|
||||||
|
.nothing:
|
||||||
|
lea ecx, [ebx+usb_pipe.Lock]
|
||||||
|
call mutex_unlock
|
||||||
|
pop esi ebx
|
||||||
|
ret 4
|
||||||
|
endp
|
||||||
|
|
||||||
; Part of API for drivers, see documentation for USBGetParam.
|
; Part of API for drivers, see documentation for USBGetParam.
|
||||||
proc usb_get_param
|
proc usb_get_param
|
||||||
virtual at esp
|
virtual at esp
|
||||||
|
@ -33,6 +33,19 @@ USB_INTERFACE_POWER_DESCR = 8
|
|||||||
; read to the debug board.
|
; read to the debug board.
|
||||||
USB_DUMP_DESCRIPTORS = 1
|
USB_DUMP_DESCRIPTORS = 1
|
||||||
|
|
||||||
|
; According to the USB specification (9.2.6.3),
|
||||||
|
; any device must response to SET_ADDRESS in 50 ms, or 5 timer ticks.
|
||||||
|
; Of course, our world is far from ideal.
|
||||||
|
; I have seen devices that just NAK everything when being reset from working
|
||||||
|
; state, but start to work after second reset.
|
||||||
|
; Our strategy is as follows: give 2 seconds for the first attempt,
|
||||||
|
; this should be enough for normal devices and not too long to detect buggy ones.
|
||||||
|
; If the device continues NAKing, reset it and retry several times,
|
||||||
|
; doubling the interval: 2s -> 4s -> 8s -> 16s. Give up after that.
|
||||||
|
; Numbers are quite arbitrary.
|
||||||
|
TIMEOUT_SET_ADDRESS_INITIAL = 200
|
||||||
|
TIMEOUT_SET_ADDRESS_LAST = 1600
|
||||||
|
|
||||||
; =============================================================================
|
; =============================================================================
|
||||||
; ================================ Structures =================================
|
; ================================ Structures =================================
|
||||||
; =============================================================================
|
; =============================================================================
|
||||||
@ -179,21 +192,36 @@ ends
|
|||||||
; out: eax = 0 <=> failed, the caller should disable the port.
|
; out: eax = 0 <=> failed, the caller should disable the port.
|
||||||
proc usb_new_device
|
proc usb_new_device
|
||||||
push ebx edi ; save used registers to be stdcall
|
push ebx edi ; save used registers to be stdcall
|
||||||
; 1. Allocate resources. Any device uses the following resources:
|
; 1. Check whether we're here because we were trying to reset
|
||||||
|
; already-registered device in hope to fix something serious.
|
||||||
|
; If so, skip allocation and go to 6.
|
||||||
|
movzx eax, [esi+usb_controller.ResettingPort]
|
||||||
|
mov edx, [esi+usb_controller.ResettingHub]
|
||||||
|
test edx, edx
|
||||||
|
jz .test_roothub
|
||||||
|
mov edx, [edx+usb_hub.ConnectedDevicesPtr]
|
||||||
|
mov ebx, [edx+eax*4]
|
||||||
|
jmp @f
|
||||||
|
.test_roothub:
|
||||||
|
mov ebx, [esi+usb_controller.DevicesByPort+eax*4]
|
||||||
|
@@:
|
||||||
|
test ebx, ebx
|
||||||
|
jnz .try_set_address
|
||||||
|
; 2. Allocate resources. Any device uses the following resources:
|
||||||
; - device address in the bus
|
; - device address in the bus
|
||||||
; - memory for device data
|
; - memory for device data
|
||||||
; - pipe for zero endpoint
|
; - pipe for zero endpoint
|
||||||
; If some allocation fails, we must undo our actions. Closing the pipe
|
; If some allocation fails, we must undo our actions. Closing the pipe
|
||||||
; is a hard task, so we avoid it and open the pipe as the last resource.
|
; is a hard task, so we avoid it and open the pipe as the last resource.
|
||||||
; The order for other two allocations is quite arbitrary.
|
; The order for other two allocations is quite arbitrary.
|
||||||
; 1a. Allocate a bus address.
|
; 2a. Allocate a bus address.
|
||||||
push ecx
|
push ecx
|
||||||
call usb_set_address_request
|
call usb_set_address_request
|
||||||
pop ecx
|
pop ecx
|
||||||
; 1b. If failed, just return zero.
|
; 2b. If failed, just return zero.
|
||||||
test eax, eax
|
test eax, eax
|
||||||
jz .nothing
|
jz .nothing
|
||||||
; 1c. Allocate memory for device data.
|
; 2c. Allocate memory for device data.
|
||||||
; For now, we need sizeof.usb_device_data and extra 8 bytes for GET_DESCRIPTOR
|
; For now, we need sizeof.usb_device_data and extra 8 bytes for GET_DESCRIPTOR
|
||||||
; input and output, see usb_after_set_address. Later we will reallocate it
|
; input and output, see usb_after_set_address. Later we will reallocate it
|
||||||
; to actual size needed for descriptors.
|
; to actual size needed for descriptors.
|
||||||
@ -201,10 +229,10 @@ proc usb_new_device
|
|||||||
push ecx
|
push ecx
|
||||||
call malloc
|
call malloc
|
||||||
pop ecx
|
pop ecx
|
||||||
; 1d. If failed, free the bus address and return zero.
|
; 2d. If failed, free the bus address and return zero.
|
||||||
test eax, eax
|
test eax, eax
|
||||||
jz .nomemory
|
jz .nomemory
|
||||||
; 1e. Open pipe for endpoint zero.
|
; 2e. Open pipe for endpoint zero.
|
||||||
; For now, we do not know the actual maximum packet size;
|
; For now, we do not know the actual maximum packet size;
|
||||||
; for full-speed devices it can be any of 8, 16, 32, 64 bytes,
|
; for full-speed devices it can be any of 8, 16, 32, 64 bytes,
|
||||||
; low-speed devices must have 8 bytes, high-speed devices must have 64 bytes.
|
; low-speed devices must have 8 bytes, high-speed devices must have 64 bytes.
|
||||||
@ -227,12 +255,14 @@ proc usb_new_device
|
|||||||
; Put pointer to pipe into ebx. "xchg eax,reg" is one byte, mov is two bytes.
|
; Put pointer to pipe into ebx. "xchg eax,reg" is one byte, mov is two bytes.
|
||||||
xchg eax, ebx
|
xchg eax, ebx
|
||||||
pop eax
|
pop eax
|
||||||
; 1f. If failed, free the memory, the bus address and return zero.
|
; 2f. If failed, free the memory, the bus address and return zero.
|
||||||
test ebx, ebx
|
test ebx, ebx
|
||||||
jz .freememory
|
jz .freememory
|
||||||
; 2. Store pointer to device data in the pipe structure.
|
; 3. Store pointer to device data in the pipe structure.
|
||||||
mov [ebx+usb_pipe.DeviceData], eax
|
mov [ebx+usb_pipe.DeviceData], eax
|
||||||
; 3. Init device data, using usb_controller.Resetting* variables.
|
; 4. Init device data, using usb_controller.Resetting* variables.
|
||||||
|
mov [eax+usb_device_data.Timer], edi
|
||||||
|
mov dword [eax+usb_device_data.DeviceDescriptor], TIMEOUT_SET_ADDRESS_INITIAL
|
||||||
mov [eax+usb_device_data.TTHub], edi
|
mov [eax+usb_device_data.TTHub], edi
|
||||||
mov [eax+usb_device_data.TTPort], 0
|
mov [eax+usb_device_data.TTPort], 0
|
||||||
mov [eax+usb_device_data.NumInterfaces], edi
|
mov [eax+usb_device_data.NumInterfaces], edi
|
||||||
@ -268,7 +298,7 @@ proc usb_new_device
|
|||||||
mov [eax+usb_device_data.Port], cl
|
mov [eax+usb_device_data.Port], cl
|
||||||
mov edx, [esi+usb_controller.ResettingHub]
|
mov edx, [esi+usb_controller.ResettingHub]
|
||||||
mov [eax+usb_device_data.Hub], edx
|
mov [eax+usb_device_data.Hub], edx
|
||||||
; 4. Store pointer to the config pipe in the hub data.
|
; 5. Store pointer to the config pipe in the hub data.
|
||||||
; Config pipe serves as device identifier.
|
; Config pipe serves as device identifier.
|
||||||
; Root hubs use the array inside usb_controller structure,
|
; Root hubs use the array inside usb_controller structure,
|
||||||
; non-root hubs use the array immediately after usb_hub structure.
|
; non-root hubs use the array immediately after usb_hub structure.
|
||||||
@ -281,16 +311,29 @@ proc usb_new_device
|
|||||||
mov [esi+usb_controller.DevicesByPort+ecx*4], ebx
|
mov [esi+usb_controller.DevicesByPort+ecx*4], ebx
|
||||||
@@:
|
@@:
|
||||||
call usb_reinit_pipe_list
|
call usb_reinit_pipe_list
|
||||||
; 5. Issue SET_ADDRESS control request, using buffer filled in step 1a.
|
; 6. Issue SET_ADDRESS control request, using buffer filled in step 2a.
|
||||||
; Use the return value from usb_control_async as our return value;
|
; 6a. Configure timer to force reset after timeout.
|
||||||
; if it is zero, then something has failed.
|
; Note: we can't use self-destructing timer, because we need to be able to cancel it,
|
||||||
|
; and for self-destructing timer we could have race condition in cancelling/destructing.
|
||||||
|
; DEBUGF 1,'K : pipe %x\n',ebx
|
||||||
|
.try_set_address:
|
||||||
|
xor edi, edi
|
||||||
|
mov edx, [ebx+usb_pipe.DeviceData]
|
||||||
|
stdcall timer_hs, [edx+usb_device_data.DeviceDescriptor], 7FFFFFFFh, usb_abort_pipe, ebx
|
||||||
|
test eax, eax
|
||||||
|
jz .nothing
|
||||||
|
mov edx, [ebx+usb_pipe.DeviceData]
|
||||||
|
mov [edx+usb_device_data.Timer], eax
|
||||||
|
; 6b. If it succeeded, setup timer to configure wait timeout.
|
||||||
lea eax, [esi+usb_controller.SetAddressBuffer]
|
lea eax, [esi+usb_controller.SetAddressBuffer]
|
||||||
stdcall usb_control_async, ebx, eax, edi, edi, usb_set_address_callback, edi, edi
|
stdcall usb_control_async, ebx, eax, edi, edi, usb_set_address_callback, edi, edi
|
||||||
|
; Use the return value from usb_control_async as our return value;
|
||||||
|
; if it is zero, then something has failed.
|
||||||
.nothing:
|
.nothing:
|
||||||
; 6. Return.
|
; 7. Return.
|
||||||
pop edi ebx ; restore used registers to be stdcall
|
pop edi ebx ; restore used registers to be stdcall
|
||||||
ret
|
ret
|
||||||
; Handlers of failures in steps 1b, 1d, 1f.
|
; Handlers of failures in steps 2b, 2d, 2f.
|
||||||
.freememory:
|
.freememory:
|
||||||
call free
|
call free
|
||||||
jmp .freeaddr
|
jmp .freeaddr
|
||||||
@ -349,16 +392,23 @@ endp
|
|||||||
; Note that USB stack uses esi = pointer to usb_controller.
|
; Note that USB stack uses esi = pointer to usb_controller.
|
||||||
proc usb_set_address_callback stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword
|
proc usb_set_address_callback stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword
|
||||||
push ebx ; save ebx to be stdcall
|
push ebx ; save ebx to be stdcall
|
||||||
; Load data to registers for further references.
|
|
||||||
mov ebx, [pipe]
|
mov ebx, [pipe]
|
||||||
|
; 1. In any case, cancel the timer.
|
||||||
|
mov eax, [ebx+usb_pipe.DeviceData]
|
||||||
|
stdcall cancel_timer_hs, [eax+usb_device_data.Timer]
|
||||||
|
mov eax, [ebx+usb_pipe.DeviceData]
|
||||||
|
mov [eax+usb_device_data.Timer], 0
|
||||||
|
; Load data to registers for further references.
|
||||||
mov ecx, dword [esi+usb_controller.SetAddressBuffer+2]
|
mov ecx, dword [esi+usb_controller.SetAddressBuffer+2]
|
||||||
mov eax, [esi+usb_controller.HardwareFunc]
|
mov eax, [esi+usb_controller.HardwareFunc]
|
||||||
; 1. Check whether the device has accepted new address. If so, proceed to 2.
|
; 2. Check whether the device has accepted new address. If so, proceed to 3.
|
||||||
; Otherwise, go to 3.
|
; Otherwise, go to 4 if killed by usb_set_address_timeout or to 5 otherwise.
|
||||||
|
cmp [status], USB_STATUS_CANCELLED
|
||||||
|
jz .timeout
|
||||||
cmp [status], 0
|
cmp [status], 0
|
||||||
jnz .error
|
jnz .error
|
||||||
; 2. Address accepted.
|
; 3. Address accepted.
|
||||||
; 2a. The controller-specific structure for the control pipe still uses
|
; 3a. The controller-specific structure for the control pipe still uses
|
||||||
; zero address. Call the controller-specific function to change it to
|
; zero address. Call the controller-specific function to change it to
|
||||||
; the actual address.
|
; the actual address.
|
||||||
; Note that the hardware could cache the controller-specific structure,
|
; Note that the hardware could cache the controller-specific structure,
|
||||||
@ -367,25 +417,49 @@ proc usb_set_address_callback stdcall, pipe:dword, status:dword, buffer:dword, l
|
|||||||
; be safe to continue.
|
; be safe to continue.
|
||||||
; dbgstr 'address set in device'
|
; dbgstr 'address set in device'
|
||||||
call [eax+usb_hardware_func.SetDeviceAddress]
|
call [eax+usb_hardware_func.SetDeviceAddress]
|
||||||
; 2b. If the port is in non-root hub, clear 'reset in progress' flag.
|
; 3b. If the port is in non-root hub, clear 'reset in progress' flag.
|
||||||
; In any case, proceed to 4.
|
; In any case, proceed to 6.
|
||||||
mov eax, [esi+usb_controller.ResettingHub]
|
mov eax, [esi+usb_controller.ResettingHub]
|
||||||
test eax, eax
|
test eax, eax
|
||||||
jz .return
|
jz .return
|
||||||
and [eax+usb_hub.Actions], not HUB_RESET_IN_PROGRESS
|
and [eax+usb_hub.Actions], not HUB_RESET_IN_PROGRESS
|
||||||
.return:
|
.return:
|
||||||
; 4. Address configuration done, we can proceed with other ports.
|
; 6. Address configuration done, we can proceed with other ports.
|
||||||
; Call the worker function for that.
|
; Call the worker function for that.
|
||||||
call usb_test_pending_port
|
call usb_test_pending_port
|
||||||
|
.wakeup:
|
||||||
|
push esi edi
|
||||||
|
call usb_wakeup
|
||||||
|
pop edi esi
|
||||||
.nothing:
|
.nothing:
|
||||||
pop ebx ; restore ebx to be stdcall
|
pop ebx ; restore ebx to be stdcall
|
||||||
ret
|
ret
|
||||||
|
.timeout:
|
||||||
|
; 4. Device continues to NAK the request. Reset it and retry.
|
||||||
|
mov edx, [ebx+usb_pipe.DeviceData]
|
||||||
|
mov ecx, [edx+usb_device_data.DeviceDescriptor]
|
||||||
|
add ecx, ecx
|
||||||
|
cmp ecx, TIMEOUT_SET_ADDRESS_LAST
|
||||||
|
ja .error
|
||||||
|
mov [edx+usb_device_data.DeviceDescriptor], ecx
|
||||||
|
dbgstr 'Timeout in USB device initialization, trying to reset...'
|
||||||
|
cmp [esi+usb_controller.ResettingHub], 0
|
||||||
|
jz .reset_roothub
|
||||||
|
push esi
|
||||||
|
mov esi, [esi+usb_controller.ResettingHub]
|
||||||
|
call usb_hub_initiate_reset
|
||||||
|
pop esi
|
||||||
|
jmp .nothing
|
||||||
|
.reset_roothub:
|
||||||
|
movzx ecx, [esi+usb_controller.ResettingPort]
|
||||||
|
call [eax+usb_hardware_func.InitiateReset]
|
||||||
|
jmp .wakeup
|
||||||
.error:
|
.error:
|
||||||
; 3. Device error: device not responding, disconnect etc.
|
; 5. Device error: device not responding, disconnect etc.
|
||||||
DEBUGF 1,'K : error %d in SET_ADDRESS, USB device disabled\n',[status]
|
DEBUGF 1,'K : error %d in SET_ADDRESS, USB device disabled\n',[status]
|
||||||
; 3a. The address has not been accepted. Mark it as free.
|
; 5a. The address has not been accepted. Mark it as free.
|
||||||
bts dword [esi+usb_controller.ExistingAddresses], ecx
|
bts dword [esi+usb_controller.ExistingAddresses], ecx
|
||||||
; 3b. Disable the port with bad device.
|
; 5b. Disable the port with bad device.
|
||||||
; For the root hub, call the controller-specific function and go to 6.
|
; For the root hub, call the controller-specific function and go to 6.
|
||||||
; For non-root hubs, let the hub code do its work and return (the request
|
; For non-root hubs, let the hub code do its work and return (the request
|
||||||
; could take some time, the hub code is responsible for proceeding).
|
; could take some time, the hub code is responsible for proceeding).
|
||||||
|
@ -186,6 +186,7 @@ USB_STATUS_BUFOVERRUN = 12 ; overflow of internal controller buffer
|
|||||||
USB_STATUS_BUFUNDERRUN = 13 ; underflow of internal controller buffer
|
USB_STATUS_BUFUNDERRUN = 13 ; underflow of internal controller buffer
|
||||||
USB_STATUS_CLOSED = 16 ; pipe closed, either explicitly with USBClosePipe
|
USB_STATUS_CLOSED = 16 ; pipe closed, either explicitly with USBClosePipe
|
||||||
; or due to device disconnect
|
; or due to device disconnect
|
||||||
|
USB_STATUS_CANCELLED = 17 ; transfer cancelled with USBAbortPipe
|
||||||
|
|
||||||
If several transfers are queued for the same pipe, their callback functions
|
If several transfers are queued for the same pipe, their callback functions
|
||||||
are called in the same order as they were queued.
|
are called in the same order as they were queued.
|
||||||
@ -194,6 +195,11 @@ implicitly due to device disconnect, all callback functions are called
|
|||||||
with USB_STATUS_CLOSED. The call to DeviceDisconnected() occurs after
|
with USB_STATUS_CLOSED. The call to DeviceDisconnected() occurs after
|
||||||
all callbacks.
|
all callbacks.
|
||||||
|
|
||||||
|
void __stdcall USBAbortPipe(void* pipe);
|
||||||
|
Initiates cancellation of all active transfers for the given pipe. Asynchronous.
|
||||||
|
When a transfer will be cancelled, the associated callback function
|
||||||
|
will be called with USB_STATUS_CANCELLED.
|
||||||
|
|
||||||
void* __stdcall USBGetParam(void* pipe0, int param);
|
void* __stdcall USBGetParam(void* pipe0, int param);
|
||||||
Returns miscellaneous parameters of the device.
|
Returns miscellaneous parameters of the device.
|
||||||
pipe0 is the pointer to the config pipe.
|
pipe0 is the pointer to the config pipe.
|
||||||
|
@ -1,249 +0,0 @@
|
|||||||
Когда ядро обнаруживает подключенное устройство USB, оно настраивает его
|
|
||||||
согласно USB-протокола - SET_ADDRESS + SET_CONFIGURATION. Всегда
|
|
||||||
устанавливается первая конфигурация. Ядро также читает дескриптор
|
|
||||||
устройства, чтобы показать некоторую информацию, читает и анализирует
|
|
||||||
дескриптор конфигурации. Для каждого интерфейса ядро будет искать класс этого
|
|
||||||
интерфейса и попытается загрузить соответствующий драйвер COFF. В настоящее
|
|
||||||
время соответствие кодов классов и имен драйверов жестко прописано в коде ядра
|
|
||||||
и выглядит следующим образом:
|
|
||||||
3 = usbhid.obj,
|
|
||||||
7 = usbprint.obj,
|
|
||||||
8 = usbstor.obj,
|
|
||||||
9 = поддерживаются самим ядром,
|
|
||||||
другие = usbother.obj.
|
|
||||||
|
|
||||||
Драйвер должен быть стандартным драйвером в формате COFF, экспортирующим
|
|
||||||
процедуру под названием "START" и переменную "version". Загрузчик вызывает
|
|
||||||
процедуру "START" как STDCALL с одним параметром DRV_ENTRY = 1. При завершении
|
|
||||||
работы системы, если инициализация драйвера была успешна, "START" процедуру
|
|
||||||
также вызывает код остановки системы с одним параметром DRV_EXIT = -1.
|
|
||||||
|
|
||||||
Драйвер должен зарегистрировать себя в качестве драйвера USB в процедуре
|
|
||||||
"START". Это делается путем вызова экспортируемой ядром функции RegUSBDriver и
|
|
||||||
возврата её результата в качестве результата "START" процедуры.
|
|
||||||
|
|
||||||
void* __stdcall RegUSBDriver(
|
|
||||||
const char* name,
|
|
||||||
void* handler,
|
|
||||||
const USBFUNC* usbfunc
|
|
||||||
);
|
|
||||||
|
|
||||||
Параметр 'name' должен совпадать с именем драйвера, например "usbhid" для
|
|
||||||
usbhid.obj.
|
|
||||||
|
|
||||||
Параметр 'handler' является необязательным. Если он не NULL, то он должен
|
|
||||||
указывать на стандартный обработчик IOCTL интерфейса, как в обычном (не-USB)
|
|
||||||
драйвере.
|
|
||||||
|
|
||||||
Параметр "Usbfunc" представляет собой указатель на следующую структуру:
|
|
||||||
|
|
||||||
struc USBFUNC
|
|
||||||
{
|
|
||||||
.strucsize dd ? ; размер структуры, включая это поле
|
|
||||||
.add_device dd ? ; указатель на AddDevice процедуру в драйвере
|
|
||||||
; (необходимо)
|
|
||||||
.device_disconnect dd ? ; указатель на DeviceDisconnected процедуру в драйвере
|
|
||||||
; опционально, может быть NULL
|
|
||||||
; В будущем могут быть добавлены другие функции
|
|
||||||
}
|
|
||||||
|
|
||||||
Драйвер ДОЛЖЕН реализовать функцию:
|
|
||||||
|
|
||||||
void* __stdcall AddDevice(
|
|
||||||
void* pipe0,
|
|
||||||
void* configdescr,
|
|
||||||
void* interfacedescr
|
|
||||||
);
|
|
||||||
|
|
||||||
Параметр "Pipe0" - хэндл контрольного канала для нулевой конечной точки
|
|
||||||
устройства. Он может быть использован в качестве аргумента для
|
|
||||||
USBControlTransferAsync (см. далее).
|
|
||||||
|
|
||||||
Параметр 'configdescr' указывает на дескриптор конфигурации и все связанные с
|
|
||||||
ним данные, представленные так, как их возвращает запрос GET_DESCRIPTOR.
|
|
||||||
Полный размер данных содержится в поле Length самого дескриптора.
|
|
||||||
(см. USB2.0 spec.)
|
|
||||||
|
|
||||||
Параметр 'interfacedescr' указывает на дескриптор интерфейса инициализируемого
|
|
||||||
в данный момент. Это указатель на данные находящиеся внутри структуры
|
|
||||||
"configdescr". (Помним, что структура INTERFACE_DESCRIPTOR, находится внутри
|
|
||||||
структуры CONFIGURATION_DESCRIPTOR. См. USB2.0 Spec.) Обратите внимание, что
|
|
||||||
одно устройство может реализовывать много интерфейсов и AddDevice может быть
|
|
||||||
вызвана несколько раз с одним "configdescr" но разными "interfacedescr".
|
|
||||||
|
|
||||||
Возвращенное значение NULL показывает, что инициализация не была успешной.
|
|
||||||
Любое другое значение означает инициализацию устройства. Ядро не делает попыток
|
|
||||||
как-то интерпретировать это значение. Это может быть, например, указатель на
|
|
||||||
внутренние данные драйвера в памяти, выделенной с помощью Kmalloc или индексом
|
|
||||||
в какой-то своей таблице. (Помните, что Kmalloc() НЕ stdcall-функция! Она
|
|
||||||
портит регистр ebx!)
|
|
||||||
|
|
||||||
Драйвер МОЖЕТ реализовать функцию:
|
|
||||||
|
|
||||||
void __stdcall DeviceDisconnected(
|
|
||||||
void* devicedata
|
|
||||||
);
|
|
||||||
|
|
||||||
Если данная функция реализована, то ядро вызывает её, когда устройство
|
|
||||||
отключено, посылая ей в качестве параметра "devicedata" то, что было возвращено
|
|
||||||
ему функцией "AddDevice" при старте драйвера.
|
|
||||||
|
|
||||||
Драйвер может использовать следующие функции экспортируемые ядром:
|
|
||||||
|
|
||||||
void* __stdcall USBOpenPipe(
|
|
||||||
void* pipe0,
|
|
||||||
int endpoint,
|
|
||||||
int maxpacketsize,
|
|
||||||
int type,
|
|
||||||
int interval
|
|
||||||
);
|
|
||||||
|
|
||||||
Параметр "Pipe0" - хэндл контрольного канала для нулевой конечной точки
|
|
||||||
устройства. Используется для идентификации устройства.
|
|
||||||
|
|
||||||
Параметр "endpoint" номер конечной точки USB. Младшие 4 бита, собственно, номер
|
|
||||||
точки, а бит 7 имеет следующее значение: 0 - для OUT точки, 1 - для IN точки.
|
|
||||||
Остальные биты должны быть равны нулю.
|
|
||||||
|
|
||||||
Параметр "maxpacketsize" устанавливает максимальный размер пакета для канала.
|
|
||||||
|
|
||||||
Параметр "type" устанавливает тип передачи для конечной точки, как это прописано
|
|
||||||
в USB спецификации:
|
|
||||||
|
|
||||||
0 = control,
|
|
||||||
1 = isochronous (сейчас не поддерживается),
|
|
||||||
2 = bulk,
|
|
||||||
3 = interrupt.
|
|
||||||
|
|
||||||
Параметр "interval" игнорируется для control и bulk передач. Для конечных точек
|
|
||||||
по прерываниям устанавливает периодичность опроса в миллисекундах.
|
|
||||||
|
|
||||||
Функция возвращает хэндл канала при успешном его открытии либо NULL при ошибке.
|
|
||||||
Хэндл канала обращается в NULL когда:
|
|
||||||
а) канал будет явно закрыт функцией USBClosePipe (см. ниже);
|
|
||||||
б) была выполнена предоставленная драйвером функция "DeviceDisconnected".
|
|
||||||
|
|
||||||
void __stdcall USBClosePipe(
|
|
||||||
void* pipe
|
|
||||||
);
|
|
||||||
|
|
||||||
Освобождает все ресурсы, связанные с выбранным каналом. Единственный параметр -
|
|
||||||
указатель на хэндл, который был возвращен функцией USBOpenPipe при открытии
|
|
||||||
канала. Когда устройство отключается, все связанные с ним каналы закрываются
|
|
||||||
ядром; нет необходимости в самостоятельном вызове этой функции.
|
|
||||||
|
|
||||||
void* __stdcall USBNormalTransferAsync(
|
|
||||||
void* pipe,
|
|
||||||
void* buffer,
|
|
||||||
int size,
|
|
||||||
void* callback,
|
|
||||||
void* calldata,
|
|
||||||
int flags
|
|
||||||
);
|
|
||||||
void* __stdcall USBControlTransferAsync(
|
|
||||||
void* pipe,
|
|
||||||
void* setup,
|
|
||||||
void* buffer,
|
|
||||||
int size,
|
|
||||||
void* callback,
|
|
||||||
void* calldata,
|
|
||||||
int flags
|
|
||||||
);
|
|
||||||
|
|
||||||
Первая функция ставит в очередь bulk или interrupt передачу для выбранного
|
|
||||||
канала. Тип и направление передачи фиксированы для bulk и interrupt типов
|
|
||||||
конечных точек, как это было выбрано функцией USBOpenPipe.
|
|
||||||
Вторая функция ставит в очередь control передачу для выбранного канала.
|
|
||||||
Направление этой передачи определяется битом 7 байта 0 пакета "setup"
|
|
||||||
(0 - для OUT, 1 - для IN передачи). Эта функция возвращает управление немедленно.
|
|
||||||
По окончании передачи вызывается функция "callback" заданная как аргумент
|
|
||||||
USB______TransferAsync.
|
|
||||||
|
|
||||||
Параметр "pipe" - хэндл, возвращенный функцией USBOpenPipe.
|
|
||||||
|
|
||||||
Параметр 'setup' функции USBControlTransferAsync указывает на 8-байтный
|
|
||||||
конфигурационный пакет (см. USB2.0 Spec).
|
|
||||||
|
|
||||||
Параметр "buffer" - это указатель на буфер. Для IN передач он будет заполнен
|
|
||||||
принятыми данными. Для OUT передач он должен быть заполнен данными, которые мы
|
|
||||||
хотим передать. Указатель может быть NULL для пустых передач, либо для передач
|
|
||||||
control, если дополнительных данных не требуется.
|
|
||||||
|
|
||||||
Параметр "size" - это размер данных для передачи. Он может быть равен 0 для
|
|
||||||
пустых передач, либо для передач control, если дополнительных данных не требуется.
|
|
||||||
|
|
||||||
Параметр "callback" - это указатель на функцию, которая будет вызвана по
|
|
||||||
окончании передачи.
|
|
||||||
|
|
||||||
Параметр "calldata" будет передан функции "callback" вызываемой по окончании
|
|
||||||
передачи. Например, он может быть NULL или указывать на данные устройства или
|
|
||||||
указывать на данные используемые как дополнительные параметры, передаваемые от
|
|
||||||
вызывающей USB_____TransferAsync функции в callback функцию.
|
|
||||||
|
|
||||||
Другие данные, связанные с передачей, могут быть помещены до буфера (по смещению)
|
|
||||||
или после него. Они могут быть использованы из callback-функции, при необходимости.
|
|
||||||
|
|
||||||
Параметр "flags" - это битовое поле. Бит 0 игнорируется для OUT передач. Для IN
|
|
||||||
передач он означает, может ли устройство передать меньше данных (бит=1), чем
|
|
||||||
определено в "size" или нет (бит=0). Остальные биты не используются и должны
|
|
||||||
быть равны 0.
|
|
||||||
|
|
||||||
Возвращаемое функциями значение равно NULL в случае ошибки и не NULL если
|
|
||||||
передача успешно поставлена в очередь. Если происходит ошибка при передаче, то
|
|
||||||
callback функция будет об этом оповещена.
|
|
||||||
|
|
||||||
void __stdcall CallbackFunction(
|
|
||||||
void* pipe,
|
|
||||||
int status,
|
|
||||||
void* buffer,
|
|
||||||
int length,
|
|
||||||
void* calldata
|
|
||||||
);
|
|
||||||
|
|
||||||
Параметры 'pipe', 'buffer', 'calldata' значат то же, что и для
|
|
||||||
USB_____TransferAsync.
|
|
||||||
|
|
||||||
Параметр "length" это счетчик переданных байт. Для control передач он отражает
|
|
||||||
дополнительные 8 байт этапа SETUP. Т.е. 0 означает ошибку на этапе SETUP, а
|
|
||||||
"size"+8 успешную передачу.
|
|
||||||
|
|
||||||
Параметр "status" не равен 0 в случае ошибки:
|
|
||||||
USB_STATUS_OK = 0 ; без ошибок
|
|
||||||
USB_STATUS_CRC = 1 ; ошибка контрольной суммы
|
|
||||||
USB_STATUS_BITSTUFF = 2 ; ошибка инверсии битов (bitstuffing)
|
|
||||||
USB_STATUS_TOGGLE = 3 ; data toggle mismatch
|
|
||||||
; (Нарушение последовательности DAT0/DAT1)
|
|
||||||
USB_STATUS_STALL = 4 ; устройство возвратило STALL статус (остановлено)
|
|
||||||
USB_STATUS_NORESPONSE = 5 ; устройство не отвечает
|
|
||||||
USB_STATUS_PIDCHECK = 6 ; ошибка в поле PacketID (PID)
|
|
||||||
USB_STATUS_WRONGPID = 7 ; неожидаемое PacketID (PID) значение
|
|
||||||
USB_STATUS_OVERRUN = 8 ; слишком много данных от конечной точки
|
|
||||||
USB_STATUS_UNDERRUN = 9 ; слишком мало данных от конечной точки
|
|
||||||
USB_STATUS_BUFOVERRUN = 12 ; переполнение внутреннего буфера контроллера
|
|
||||||
; возможна только для изохронных передач
|
|
||||||
USB_STATUS_BUFUNDERRUN = 13 ; опустошение внутреннего буфера контроллера
|
|
||||||
; возможна только для изохронных передач
|
|
||||||
USB_STATUS_CLOSED = 16 ; канал закрыт либо через ClosePipe, либо в
|
|
||||||
; результате отключения устройства
|
|
||||||
|
|
||||||
Если несколько передач были поставлены в очередь для одного канала, то callback
|
|
||||||
функции для них будут вызываться в порядке постановки передач в очередь.
|
|
||||||
Если канал был закрыт ввиду USBClosePipe или отключения устройства, то callback
|
|
||||||
функции (если очередь передач не пуста) получат USB_STATUS_CLOSED.
|
|
||||||
Вызов DeviceDisconnected() последует после отработки всех оставшихся в очереди
|
|
||||||
callback функций.
|
|
||||||
|
|
||||||
void* __stdcall USBGetParam(void* pipe0, int param);
|
|
||||||
|
|
||||||
Возвращает указатель на некоторые параметры устройства запомненные ядром при
|
|
||||||
инициализации первой конфигурации. Не передает ничего устройству по шине.
|
|
||||||
|
|
||||||
pipe0 - хэндл контрольного канала для нулевой конечной точки устройства.
|
|
||||||
|
|
||||||
param - выбор возвращаемого параметра:
|
|
||||||
0 - возвратить указатель на дескриптор устройства;
|
|
||||||
1 - возвратить указатель на дескриптор конфигурации;
|
|
||||||
2 - возвратить режим шины устройства:
|
|
||||||
USB_SPEED_FS = 0 ; full-speed
|
|
||||||
USB_SPEED_LS = 1 ; low-speed
|
|
||||||
USB_SPEED_HS = 2 ; high-speed
|
|
Loading…
Reference in New Issue
Block a user