2 * PROJECT: ReactOS Universal Serial Bus Host Controller Interface
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: drivers/usb/usbuhci/usb_queue.cpp
5 * PURPOSE: USB UHCI device driver.
7 * Michael Martin (michael.martin@reactos.org)
8 * Johannes Anderwald (johannes.anderwald@reactos.org)
16 class CUSBQueue
: public IUHCIQueue
19 STDMETHODIMP
QueryInterface( REFIID InterfaceId
, PVOID
* Interface
);
21 STDMETHODIMP_(ULONG
) AddRef()
23 InterlockedIncrement(&m_Ref
);
26 STDMETHODIMP_(ULONG
) Release()
28 InterlockedDecrement(&m_Ref
);
43 VOID
LinkQueueHead(PUHCI_QUEUE_HEAD QueueHead
, PUHCI_QUEUE_HEAD NextQueueHead
);
44 VOID
UnLinkQueueHead(PUHCI_QUEUE_HEAD PreviousQueueHead
, PUHCI_QUEUE_HEAD NextQueueHead
);
45 BOOLEAN
IsQueueHeadComplete(PUHCI_QUEUE_HEAD QueueHead
);
46 NTSTATUS
AddQueueHead(PUHCI_QUEUE_HEAD NewQueueHead
);
47 VOID
QueueHeadCleanup(IN PUHCI_QUEUE_HEAD QueueHead
, IN PUHCI_QUEUE_HEAD PreviousQueueHead
, OUT PUHCI_QUEUE_HEAD
*NextQueueHead
);
50 // constructor / destructor
51 CUSBQueue(IUnknown
*OuterUnknown
){}
52 virtual ~CUSBQueue(){}
55 LONG m_Ref
; // reference count
56 KSPIN_LOCK m_Lock
; // list lock
57 PUHCIHARDWAREDEVICE m_Hardware
; // hardware
61 //=================================================================================================
66 CUSBQueue::QueryInterface(
70 if (IsEqualGUIDAligned(refiid
, IID_IUnknown
))
72 *Output
= PVOID(PUNKNOWN(this));
73 PUNKNOWN(*Output
)->AddRef();
74 return STATUS_SUCCESS
;
77 return STATUS_UNSUCCESSFUL
;
81 CUSBQueue::Initialize(
82 IN PUSBHARDWAREDEVICE Hardware
,
83 IN PDMA_ADAPTER AdapterObject
,
84 IN PDMAMEMORYMANAGER MemManager
,
85 IN OPTIONAL PKSPIN_LOCK Lock
)
91 m_Hardware
= PUHCIHARDWAREDEVICE(Hardware
);
94 // initialize spinlock
96 KeInitializeSpinLock(&m_Lock
);
97 return STATUS_SUCCESS
;
101 CUSBQueue::AddQueueHead(
102 PUHCI_QUEUE_HEAD NewQueueHead
)
104 PUHCIREQUEST Request
;
105 PUHCI_QUEUE_HEAD QueueHead
= NULL
;
111 Request
= (PUHCIREQUEST
)NewQueueHead
->Request
;
117 return STATUS_INVALID_PARAMETER
;
120 if (Request
->GetTransferType() == USB_ENDPOINT_TYPE_CONTROL
)
125 if (Request
->GetDeviceSpeed() == UsbLowSpeed
)
128 // use low speed queue
130 m_Hardware
->GetQueueHead(UHCI_LOW_SPEED_CONTROL_QUEUE
, &QueueHead
);
135 // use full speed queue
137 m_Hardware
->GetQueueHead(UHCI_FULL_SPEED_CONTROL_QUEUE
, &QueueHead
);
140 else if (Request
->GetTransferType() == USB_ENDPOINT_TYPE_BULK
)
143 // use full speed queue
145 m_Hardware
->GetQueueHead(UHCI_BULK_QUEUE
, &QueueHead
);
147 else if (Request
->GetTransferType() == USB_ENDPOINT_TYPE_INTERRUPT
)
150 // use full speed queue
152 m_Hardware
->GetQueueHead(UHCI_INTERRUPT_QUEUE
, &QueueHead
);
156 // FIXME support isochronous
166 // now link the new queue head
168 LinkQueueHead(QueueHead
, NewQueueHead
);
169 return STATUS_SUCCESS
;
174 CUSBQueue::AddUSBRequest(
177 PUHCI_QUEUE_HEAD NewQueueHead
;
179 PUHCIREQUEST Request
;
182 Request
= (PUHCIREQUEST
)Req
;
187 Status
= Request
->GetEndpointDescriptor(&NewQueueHead
);
188 if (!NT_SUCCESS(Status
))
191 // failed to create queue head
193 DPRINT1("[USBUHCI] Failed to create queue head %x\n", Status
);
200 ASSERT(PVOID(Request
) == NewQueueHead
->Request
);
205 DPRINT("AddUSBRequest Request %p\n", Request
);
206 DPRINT("NewQueueHead %p\n", NewQueueHead
);
207 return AddQueueHead(NewQueueHead
);
211 CUSBQueue::LinkQueueHead(
212 IN PUHCI_QUEUE_HEAD QueueHead
,
213 IN PUHCI_QUEUE_HEAD NextQueueHead
)
215 NextQueueHead
->LinkPhysical
= QueueHead
->LinkPhysical
;
216 NextQueueHead
->NextLogicalDescriptor
= QueueHead
->NextLogicalDescriptor
;
218 QueueHead
->LinkPhysical
= NextQueueHead
->PhysicalAddress
| QH_NEXT_IS_QH
;
219 QueueHead
->NextLogicalDescriptor
= (PVOID
)NextQueueHead
;
224 CUSBQueue::UnLinkQueueHead(
225 PUHCI_QUEUE_HEAD QueueHeadToRemove
,
226 PUHCI_QUEUE_HEAD PreviousQueueHead
)
228 PreviousQueueHead
->LinkPhysical
= QueueHeadToRemove
->LinkPhysical
;
229 PreviousQueueHead
->NextLogicalDescriptor
= QueueHeadToRemove
->NextLogicalDescriptor
;
233 CUSBQueue::AbortDevicePipe(
234 IN UCHAR DeviceAddress
,
235 IN PUSB_ENDPOINT_DESCRIPTOR EndDescriptor
)
238 PUHCI_TRANSFER_DESCRIPTOR Descriptor
;
239 PUHCI_QUEUE_HEAD QueueHead
, PreviousQueueHead
= NULL
;
240 UCHAR EndpointAddress
, EndpointDeviceAddress
;
241 PUSB_ENDPOINT EndpointDescriptor
;
244 EndpointDescriptor
= (PUSB_ENDPOINT
)EndDescriptor
;
247 KeAcquireSpinLock(&m_Lock
, &OldLevel
);
250 m_Hardware
->GetQueueHead(UHCI_INTERRUPT_QUEUE
, &QueueHead
);
255 Descriptor
= (PUHCI_TRANSFER_DESCRIPTOR
)QueueHead
->NextElementDescriptor
;
259 // extract endpoint address
260 EndpointAddress
= (Descriptor
->Token
>> TD_TOKEN_ENDPTADDR_SHIFT
) & 0x0F;
262 // extract device address
263 EndpointDeviceAddress
= (Descriptor
->Token
>> TD_TOKEN_DEVADDR_SHIFT
) & 0x7F;
265 // check if they match
266 if (EndpointAddress
== (EndpointDescriptor
->EndPointDescriptor
.bEndpointAddress
& 0x0F) &&
267 DeviceAddress
== EndpointDeviceAddress
)
269 // cleanup queue head
270 QueueHeadCleanup(QueueHead
, PreviousQueueHead
, &QueueHead
);
275 // move to next queue head
276 PreviousQueueHead
= QueueHead
;
277 QueueHead
= (PUHCI_QUEUE_HEAD
)QueueHead
->NextLogicalDescriptor
;
281 KeReleaseSpinLock(&m_Lock
, OldLevel
);
282 return STATUS_SUCCESS
;
286 CUSBQueue::CreateUSBRequest(
287 IUSBRequest
**OutRequest
)
289 PUSBREQUEST UsbRequest
;
293 Status
= InternalCreateUSBRequest(&UsbRequest
);
295 if (NT_SUCCESS(Status
))
297 *OutRequest
= UsbRequest
;
304 CUSBQueue::IsQueueHeadComplete(
305 IN PUHCI_QUEUE_HEAD QueueHead
)
307 PUHCI_TRANSFER_DESCRIPTOR Descriptor
;
310 if (QueueHead
->NextElementDescriptor
== NULL
)
315 DPRINT("QueueHead %p empty element physical\n", QueueHead
);
320 // check all descriptors
322 Descriptor
= (PUHCI_TRANSFER_DESCRIPTOR
)QueueHead
->NextElementDescriptor
;
325 if (Descriptor
->Status
& TD_STATUS_ACTIVE
)
328 // descriptor is still active
330 DPRINT("Descriptor %p is active Status %x BufferSize %lu\n", Descriptor
, Descriptor
->Status
, Descriptor
->BufferSize
);
334 if (Descriptor
->Status
& TD_ERROR_MASK
)
339 DPRINT1("[USBUHCI] Error detected at descriptor %p Physical %x\n", Descriptor
, Descriptor
->PhysicalAddress
);
344 ErrorCount
= (Descriptor
->Status
>> TD_ERROR_COUNT_SHIFT
) & TD_ERROR_COUNT_MASK
;
348 // error retry count elapsed
350 DPRINT1("[USBUHCI] ErrorBuffer %x TimeOut %x Nak %x BitStuff %x\n",
351 Descriptor
->Status
& TD_STATUS_ERROR_BUFFER
,
352 Descriptor
->Status
& TD_STATUS_ERROR_TIMEOUT
,
353 Descriptor
->Status
& TD_STATUS_ERROR_NAK
,
354 Descriptor
->Status
& TD_STATUS_ERROR_BITSTUFF
);
357 else if (Descriptor
->Status
& TD_STATUS_ERROR_BABBLE
)
362 DPRINT1("[USBUHCI] Babble detected\n");
370 DPRINT1("[USBUHCI] Stall detected\n");
375 // move to next descriptor
377 Descriptor
= (PUHCI_TRANSFER_DESCRIPTOR
)Descriptor
->NextLogicalDescriptor
;
381 // request is complete
387 CUSBQueue::QueueHeadCleanup(
388 IN PUHCI_QUEUE_HEAD QueueHead
,
389 IN PUHCI_QUEUE_HEAD PreviousQueueHead
,
390 OUT PUHCI_QUEUE_HEAD
*NextQueueHead
)
392 PUHCIREQUEST Request
;
393 PUHCI_QUEUE_HEAD NewQueueHead
;
399 UnLinkQueueHead(QueueHead
, PreviousQueueHead
);
402 // get next queue head
404 *NextQueueHead
= (PUHCI_QUEUE_HEAD
)PreviousQueueHead
->NextLogicalDescriptor
;
405 ASSERT(*NextQueueHead
!= QueueHead
);
408 // the queue head is complete, is the transfer now completed?
410 Request
= (PUHCIREQUEST
)QueueHead
->Request
;
416 DPRINT("Request %p\n", Request
);
417 Request
->FreeEndpointDescriptor(QueueHead
);
420 // check if transfer is complete
422 if (Request
->IsRequestComplete())
425 // the transfer is complete
427 Request
->CompletionCallback();
433 // grab new queue head
435 Status
= Request
->GetEndpointDescriptor(&NewQueueHead
);
436 if (!NT_SUCCESS(Status
))
439 // failed to get new queue head
441 DPRINT1("[USBUHCI] Failed to get new queue head with %x\n", Status
);
442 Request
->CompletionCallback();
450 Status
= AddQueueHead(NewQueueHead
);
451 if (!NT_SUCCESS(Status
))
454 // failed to get new queue head
456 DPRINT1("[USBUHCI] Failed to add queue head with %x\n", Status
);
457 Request
->CompletionCallback();
465 CUSBQueue::TransferInterrupt(
466 UCHAR ErrorInterrupt
)
469 PUHCI_QUEUE_HEAD QueueHead
, PreviousQueueHead
= NULL
;
475 KeAcquireSpinLock(&m_Lock
, &OldLevel
);
480 m_Hardware
->GetQueueHead(UHCI_INTERRUPT_QUEUE
, &QueueHead
);
485 // is queue head complete
487 DPRINT("QueueHead %p\n", QueueHead
);
488 IsComplete
= IsQueueHeadComplete(QueueHead
);
492 // cleanup queue head
494 QueueHeadCleanup(QueueHead
, PreviousQueueHead
, &QueueHead
);
499 // backup previous queue head
501 PreviousQueueHead
= QueueHead
;
504 // get next queue head
506 QueueHead
= (PUHCI_QUEUE_HEAD
)QueueHead
->NextLogicalDescriptor
;
512 KeReleaseSpinLock(&m_Lock
, OldLevel
);
518 PUSBQUEUE
*OutUsbQueue
)
523 // allocate controller
525 This
= new(NonPagedPool
, TAG_USBUHCI
) CUSBQueue(0);
529 // failed to allocate
531 return STATUS_INSUFFICIENT_RESOURCES
;
535 // add reference count
542 *OutUsbQueue
= (PUSBQUEUE
)This
;
547 return STATUS_SUCCESS
;