From 241079c114f1b2054a169cb993527847336586b4 Mon Sep 17 00:00:00 2001 From: CleverMouse Date: Wed, 29 Jan 2014 12:14:28 +0000 Subject: [PATCH] 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 --- drivers/usb/ehci.asm | 61 ++++++-- drivers/usb/ehci_scheduler.inc | 4 +- drivers/usb/ohci.asm | 43 +++++- drivers/usb/uhci.asm | 51 ++++-- drivers/usb/usb1_scheduler.inc | 7 +- kernel/trunk/bus/usb/common.inc | 18 ++- kernel/trunk/bus/usb/hccommon.inc | 20 ++- kernel/trunk/bus/usb/pipe.inc | 221 +++++++++++++++++++++++--- kernel/trunk/bus/usb/protocol.inc | 126 +++++++++++---- kernel/trunk/docs/usbapi.txt | 6 + kernel/trunk/docs/usbapi_ru.txt | 249 ------------------------------ 11 files changed, 467 insertions(+), 339 deletions(-) delete mode 100644 kernel/trunk/docs/usbapi_ru.txt diff --git a/drivers/usb/ehci.asm b/drivers/usb/ehci.asm index 81a111ab7f..a5b856ed4a 100644 --- a/drivers/usb/ehci.asm +++ b/drivers/usb/ehci.asm @@ -156,8 +156,6 @@ Overlay ehci_hardware_td ? ; Working area for the current TD, if there is any. ; When TD is retired, it is written to that TD and Overlay is loaded ; from the new TD, if any. -BaseList dd ? -; Pointer to head of the corresponding pipe list. ends ; This structure describes the static head of every list of pipes. @@ -293,6 +291,8 @@ ehci_hardware_func: dd ehci_alloc_transfer dd ehci_insert_transfer dd ehci_new_device + dd ehci_disable_pipe + dd ehci_enable_pipe ehci_name db 'EHCI',0 endg @@ -998,7 +998,7 @@ end virtual jz .return0 mov word [edi+ehci_pipe.Flags-sizeof.ehci_pipe], ax .insert: - mov [edi+ehci_pipe.BaseList-sizeof.ehci_pipe], edx + mov [edi+usb_pipe.BaseList], edx ; Insert to the head of the corresponding list. ; Note: inserting to the head guarantees that the list traverse in ; 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 .interrupt_common: @@: - mov edx, [ebx+usb_pipe.NextVirt] - mov eax, [ebx+usb_pipe.PrevVirt] - mov [edx+usb_pipe.PrevVirt], eax - mov [eax+usb_pipe.NextVirt], edx + ret +endp + +; 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 - sub edx, eax + sub edx, ecx cmp edx, sizeof.ehci_controller - mov edx, [ebx+ehci_pipe.NextQH-sizeof.ehci_pipe] jb .prev_is_static - mov [eax+ehci_pipe.NextQH-sizeof.ehci_pipe], edx + mov [ecx+ehci_pipe.NextQH-sizeof.ehci_pipe], eax ret .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 endp diff --git a/drivers/usb/ehci_scheduler.inc b/drivers/usb/ehci_scheduler.inc index c12c59e107..14f290a5d2 100644 --- a/drivers/usb/ehci_scheduler.inc +++ b/drivers/usb/ehci_scheduler.inc @@ -298,7 +298,7 @@ proc ehci_hs_interrupt_list_unlink imul eax, ecx movzx ecx, byte [ebx+ehci_pipe.Flags-sizeof.ehci_pipe] ; get target list - mov edx, [ebx+ehci_pipe.BaseList-sizeof.ehci_pipe] + mov edx, [ebx+usb_pipe.BaseList] ; update bandwidth .dec_bandwidth: shr ecx, 1 @@ -732,7 +732,7 @@ proc ehci_fs_interrupt_list_unlink mov edi, esp call tt_fill_split_info ; get target list - mov edx, [ebx+ehci_pipe.BaseList-sizeof.ehci_pipe] + mov edx, [ebx+usb_pipe.BaseList] ; update bandwidth for Start-Split mov eax, [edi+usb_split_info.ssplit_bandwidth] xor ecx, ecx diff --git a/drivers/usb/ohci.asm b/drivers/usb/ohci.asm index 27d8f080f5..f771fc036d 100644 --- a/drivers/usb/ohci.asm +++ b/drivers/usb/ohci.asm @@ -64,7 +64,7 @@ Flags dd ? ; 2. Next 4 bits (bits 7-10) are EndpointNumber. This is the USB address of ; the endpoint within the function. ; 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. ; For CONTROL endpoints, the transfer direction is different ; for different transfers, so the value of this field is 0 @@ -322,6 +322,8 @@ ohci_hardware_func: dd ohci_alloc_transfer dd ohci_insert_transfer dd ohci_new_device + dd ohci_disable_pipe + dd ohci_enable_pipe ohci_name db 'OHCI',0 endg @@ -1014,6 +1016,7 @@ end virtual ; Inserting to tail would work as well, ; but let's be consistent with other controllers. .insert: + mov [edi+usb_pipe.BaseList], edx mov ecx, [edx+usb_pipe.NextVirt] mov [edi+usb_pipe.NextVirt], ecx mov [edi+usb_pipe.PrevVirt], edx @@ -1614,17 +1617,41 @@ proc ohci_unlink_pipe mov eax, [ebx+ohci_pipe.Flags-sizeof.ohci_pipe] bt eax, 13 setc cl - bt eax, 11 + bt eax, 12 setc ch shr eax, 16 stdcall usb1_interrupt_list_unlink, eax, ecx @@: - mov edx, [ebx+usb_pipe.NextVirt] - mov eax, [ebx+usb_pipe.PrevVirt] - mov [edx+usb_pipe.PrevVirt], eax - mov [eax+usb_pipe.NextVirt], edx - mov edx, [ebx+ohci_pipe.NextED-sizeof.ohci_pipe] - mov [eax+ohci_pipe.NextED-sizeof.ohci_pipe], edx + ret +endp + +; This procedure temporarily removes the given pipe from hardware queue, +; keeping it in software lists. +; 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 endp diff --git a/drivers/usb/uhci.asm b/drivers/usb/uhci.asm index c2e9777aec..fdd0220732 100644 --- a/drivers/usb/uhci.asm +++ b/drivers/usb/uhci.asm @@ -274,6 +274,8 @@ uhci_hardware_func: dd uhci_alloc_transfer dd uhci_insert_transfer dd uhci_new_device + dd uhci_disable_pipe + dd uhci_enable_pipe uhci_name db 'UHCI',0 endg @@ -1133,7 +1135,6 @@ proc uhci_fix_toggle jnz .loop ; 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) - or dword [ecx+uhci_pipe.Token-sizeof.uhci_pipe-usb_pipe.Lock], eax ; 6. Unlock the transfer queue. invoke MutexUnlock .nothing: @@ -1461,6 +1462,7 @@ end virtual test edx, edx jz .return0 .insert: + mov [edi+usb_pipe.BaseList], edx ; Insert to the head of the corresponding list. ; Note: inserting to the head guarantees that the list traverse in ; uhci_process_updated_schedule, once started, will not interact with new pipes. @@ -1505,17 +1507,44 @@ proc uhci_unlink_pipe shr eax, 21 stdcall usb1_interrupt_list_unlink, eax, ecx @@: -; Note: we need to ensure that NextVirt field of the pipe is not modified; -; this procedure can be called while uhci_process_updated_schedule processes -; the same pipe, and it needs a correct NextVirt field to continue. - mov edx, [ebx+usb_pipe.NextVirt] - mov eax, [ebx+usb_pipe.PrevVirt] - mov [edx+usb_pipe.PrevVirt], eax - mov [eax+usb_pipe.NextVirt], edx -; Note: eax could be either usb_pipe or usb_static_ep; + ret +endp + +; This procedure temporarily removes the given pipe from hardware queue, +; keeping it in software lists. +; esi -> usb_controller, ebx -> usb_pipe +proc uhci_disable_pipe + 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. - mov edx, [ebx+uhci_pipe.NextQH-sizeof.uhci_pipe] - mov [eax+uhci_pipe.NextQH-sizeof.uhci_pipe], edx + mov [edx+uhci_pipe.NextQH-sizeof.uhci_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 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 endp diff --git a/drivers/usb/usb1_scheduler.inc b/drivers/usb/usb1_scheduler.inc index 939736feef..b4d4d8f196 100644 --- a/drivers/usb/usb1_scheduler.inc +++ b/drivers/usb/usb1_scheduler.inc @@ -167,12 +167,7 @@ end virtual mov eax, [.maxpacket] mov ecx, dword [.lowspeed] call calc_usb1_bandwidth -; find list header - mov edx, ebx -@@: - mov edx, [edx+usb_pipe.NextVirt] - cmp [edx+usb_pipe.Controller], esi - jz @b + mov edx, [ebx+usb_pipe.BaseList] ; subtract pipe bandwidth sub [edx+usb_static_ep.Bandwidth], eax ret 8 diff --git a/kernel/trunk/bus/usb/common.inc b/kernel/trunk/bus/usb/common.inc index 5ba1e1409a..cf59e30cb5 100644 --- a/kernel/trunk/bus/usb/common.inc +++ b/kernel/trunk/bus/usb/common.inc @@ -6,7 +6,7 @@ ; ============================================================================= ; Version of all structures related to host controllers. ; 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 ; 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 ; either explicitly with USBClosePipe ; or implicitly due to device disconnect +USB_STATUS_CANCELLED = 17 ; transfer cancelled with USBAbortPipe ; Possible speeds of USB devices USB_SPEED_FS = 0 ; full-speed @@ -63,6 +64,9 @@ USB_FLAG_CAN_FREE = 2 USB_FLAG_EXTRA_WAIT = 4 ; The pipe was in wait list, while another event occured; ; 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 ; ============================================================================= @@ -136,6 +140,14 @@ NewDevice dd ? ; Initiate configuration of a new device (create pseudo-pipe describing that ; device and call usb_new_device). ; 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 ; pointers to kernel API functions that are called from *HCI-drivers @@ -307,6 +319,8 @@ NextVirt dd ? PrevVirt dd ? ; Previous endpoint in the processing list. ; 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 ; list of Transfer Descriptors aka TD. For Control, Bulk and Interrupt @@ -427,6 +441,8 @@ DeviceDescrSize db ? ; Size of device descriptor. Speed db ? ; Device speed, one of USB_SPEED_*. +Timer dd ? +; Handle of timer that handles request timeout. NumInterfaces dd ? ; Number of interfaces. ConfigDataSize dd ? diff --git a/kernel/trunk/bus/usb/hccommon.inc b/kernel/trunk/bus/usb/hccommon.inc index 724000d088..4f0a2baae4 100644 --- a/kernel/trunk/bus/usb/hccommon.inc +++ b/kernel/trunk/bus/usb/hccommon.inc @@ -114,7 +114,7 @@ proc get_phys_addr ret 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 ; needs to be synchronized. When it will be known that the cache is updated, ; usb_subscription_done procedure will be called. @@ -128,6 +128,17 @@ proc usb_subscribe_control ret 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. ; Continues process of device enumeration based on when it was delayed ; due to call to usb_subscribe_control. @@ -254,13 +265,18 @@ proc usb_process_one_wait_list mov [esi+usb_controller.WaitPipeListAsync+edx], ebx jmp .continue .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 test [ebx+usb_pipe.Flags], USB_FLAG_CLOSED jz .nodisconnect call usb_pipe_closed jmp .continue .nodisconnect: + test [ebx+usb_pipe.Flags], USB_FLAG_DISABLED + jz .nodisabled + call usb_pipe_disabled + jmp .continue +.nodisabled: call usb_subscription_done .continue: ; 8. Restore edx and next pipe saved in step 5 and continue the loop. diff --git a/kernel/trunk/bus/usb/pipe.inc b/kernel/trunk/bus/usb/pipe.inc index 00be955062..0b25c682c6 100644 --- a/kernel/trunk/bus/usb/pipe.inc +++ b/kernel/trunk/bus/usb/pipe.inc @@ -13,6 +13,11 @@ else stdcall arg end if } +if USB_STDCALL_VERIFY +STDCALL_VERIFY_EXTRA = 20h +else +STDCALL_VERIFY_EXTRA = 0 +end if ; Initialization of usb_static_ep structure, ; called from controller-specific initialization; edi -> usb_static_ep @@ -238,8 +243,17 @@ proc usb_close_pipe_nolock call mutex_lock push ecx ; 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] 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. pop ecx call mutex_unlock @@ -262,36 +276,66 @@ proc usb_close_pipe_nolock ret 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 ; corresponding wait list. It means that the hardware has fully forgot about it. ; ebx -> usb_pipe, esi -> usb_controller proc usb_pipe_closed push edi mov edi, [esi+usb_controller.HardwareFunc] -; 1. Loop over all transfers, calling the driver with USB_STATUS_CLOSED -; and freeing all descriptors. +; 1. Notify all registered callbacks with status USB_STATUS_CLOSED, if any, +; and free all transfer descriptors, including the last one. + lea ecx, [ebx+usb_pipe.Lock] + call mutex_lock mov edx, [ebx+usb_pipe.LastTD] test edx, edx jz .no_transfer - mov edx, [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 + mov eax, [edx+usb_gtd.NextVirt] push edx - stdcall_verify ecx, ebx, USB_STATUS_CLOSED, \ - [edx+usb_gtd.Buffer], 0, [edx+usb_gtd.UserData] - pop edx -.no_callback: - push [edx+usb_gtd.NextVirt] - stdcall [edi+usb_hardware_func.FreeTD], edx - pop edx - jmp .transfer_loop -.transfer_done: - stdcall [edi+usb_hardware_func.FreeTD], edx + push eax + call mutex_unlock + push USB_STATUS_CLOSED + call usb_pipe_aborted +; It is safe to free LastTD here: +; usb_*_transfer_async do not enqueue new transfers if USB_FLAG_CLOSED is set. + stdcall [edi+usb_hardware_func.FreeTD], [ebx+usb_pipe.LastTD] + jmp @f .no_transfer: + call mutex_unlock +@@: ; 2. Decrement number of pipes for the device. ; If this pipe is the last pipe, go to 5. mov ecx, [ebx+usb_pipe.DeviceData] @@ -342,14 +386,23 @@ proc usb_pipe_closed dec eax jnz .notify_loop .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] test eax, eax jz @f bts [esi+usb_controller.ExistingAddresses], eax @@: 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. ; Free the remaining pipes. mov eax, [ebx+usb_pipe.DeviceData] @@ -366,15 +419,74 @@ proc usb_pipe_closed .free_done: stdcall [edi+usb_hardware_func.FreePipe], ebx 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 call free -; 9. Return. +; 10. Return. .nothing: pop edi ret 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. proc usb_normal_transfer_async stdcall uses ebx edi,\ pipe:dword, buffer:dword, size:dword, callback:dword, calldata:dword, flags:dword @@ -508,6 +620,69 @@ endl ret 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. proc usb_get_param virtual at esp diff --git a/kernel/trunk/bus/usb/protocol.inc b/kernel/trunk/bus/usb/protocol.inc index 8f62b37f92..7361ccfd19 100644 --- a/kernel/trunk/bus/usb/protocol.inc +++ b/kernel/trunk/bus/usb/protocol.inc @@ -33,6 +33,19 @@ USB_INTERFACE_POWER_DESCR = 8 ; read to the debug board. 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 ================================= ; ============================================================================= @@ -179,21 +192,36 @@ ends ; out: eax = 0 <=> failed, the caller should disable the port. proc usb_new_device 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 ; - memory for device data ; - pipe for zero endpoint ; 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. ; The order for other two allocations is quite arbitrary. -; 1a. Allocate a bus address. +; 2a. Allocate a bus address. push ecx call usb_set_address_request pop ecx -; 1b. If failed, just return zero. +; 2b. If failed, just return zero. test eax, eax 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 ; input and output, see usb_after_set_address. Later we will reallocate it ; to actual size needed for descriptors. @@ -201,10 +229,10 @@ proc usb_new_device push ecx call malloc 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 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 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. @@ -227,12 +255,14 @@ proc usb_new_device ; Put pointer to pipe into ebx. "xchg eax,reg" is one byte, mov is two bytes. xchg eax, ebx 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 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 -; 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.TTPort], 0 mov [eax+usb_device_data.NumInterfaces], edi @@ -268,7 +298,7 @@ proc usb_new_device mov [eax+usb_device_data.Port], cl mov edx, [esi+usb_controller.ResettingHub] 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. ; Root hubs use the array inside usb_controller 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 @@: call usb_reinit_pipe_list -; 5. Issue SET_ADDRESS control request, using buffer filled in step 1a. -; Use the return value from usb_control_async as our return value; -; if it is zero, then something has failed. +; 6. Issue SET_ADDRESS control request, using buffer filled in step 2a. +; 6a. Configure timer to force reset after timeout. +; 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] 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: -; 6. Return. +; 7. Return. pop edi ebx ; restore used registers to be stdcall ret -; Handlers of failures in steps 1b, 1d, 1f. +; Handlers of failures in steps 2b, 2d, 2f. .freememory: call free jmp .freeaddr @@ -349,16 +392,23 @@ endp ; 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 push ebx ; save ebx to be stdcall -; Load data to registers for further references. 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 eax, [esi+usb_controller.HardwareFunc] -; 1. Check whether the device has accepted new address. If so, proceed to 2. -; Otherwise, go to 3. +; 2. Check whether the device has accepted new address. If so, proceed 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 jnz .error -; 2. Address accepted. -; 2a. The controller-specific structure for the control pipe still uses +; 3. Address accepted. +; 3a. The controller-specific structure for the control pipe still uses ; zero address. Call the controller-specific function to change it to ; the actual address. ; 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. ; dbgstr 'address set in device' call [eax+usb_hardware_func.SetDeviceAddress] -; 2b. If the port is in non-root hub, clear 'reset in progress' flag. -; In any case, proceed to 4. +; 3b. If the port is in non-root hub, clear 'reset in progress' flag. +; In any case, proceed to 6. mov eax, [esi+usb_controller.ResettingHub] test eax, eax jz .return and [eax+usb_hub.Actions], not HUB_RESET_IN_PROGRESS .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 usb_test_pending_port +.wakeup: + push esi edi + call usb_wakeup + pop edi esi .nothing: pop ebx ; restore ebx to be stdcall 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: -; 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] -; 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 -; 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 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). diff --git a/kernel/trunk/docs/usbapi.txt b/kernel/trunk/docs/usbapi.txt index f3fe0f0be1..9997afcb13 100644 --- a/kernel/trunk/docs/usbapi.txt +++ b/kernel/trunk/docs/usbapi.txt @@ -186,6 +186,7 @@ USB_STATUS_BUFOVERRUN = 12 ; overflow of internal controller buffer USB_STATUS_BUFUNDERRUN = 13 ; underflow of internal controller buffer USB_STATUS_CLOSED = 16 ; pipe closed, either explicitly with USBClosePipe ; 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 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 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); Returns miscellaneous parameters of the device. pipe0 is the pointer to the config pipe. diff --git a/kernel/trunk/docs/usbapi_ru.txt b/kernel/trunk/docs/usbapi_ru.txt deleted file mode 100644 index cf89079d4e..0000000000 --- a/kernel/trunk/docs/usbapi_ru.txt +++ /dev/null @@ -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 \ No newline at end of file