/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_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_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 |
; 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 |
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 |
; 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 ? |
/kernel/trunk/bus/usb/hccommon.inc |
---|
114,7 → 114,7 |
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 |
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,7 → 265,7 |
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 |
261,6 → 272,11 |
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. |
/kernel/trunk/bus/usb/pipe.inc |
---|
13,6 → 13,11 |
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 |
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,6 → 276,40 |
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 |
268,30 → 316,26 |
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,7 → 386,16 |
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 |
349,7 → 402,7 |
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 |
.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 |
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 |
/kernel/trunk/bus/usb/protocol.inc |
---|
33,6 → 33,19 |
; 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,7 → 192,22 |
; 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 |
186,14 → 214,14 |
; 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 |
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 |
; 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 |
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 |
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. |
; 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. |
lea eax, [esi+usb_controller.SetAddressBuffer] |
stdcall usb_control_async, ebx, eax, edi, edi, usb_set_address_callback, edi, edi |
.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 |
; 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 |
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 ebx, [pipe] |
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 |
; 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). |