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
);
154 else if (Request
->GetTransferType() == USB_ENDPOINT_TYPE_INTERRUPT
)
157 // use full speed queue
159 m_Hardware
->GetQueueHead(UHCI_INTERRUPT_QUEUE
, &QueueHead
);
163 // FIXME support isochronous
173 // now link the new queue head
175 LinkQueueHead(QueueHead
, NewQueueHead
);
176 return STATUS_SUCCESS
;
181 CUSBQueue::AddUSBRequest(
184 PUHCI_QUEUE_HEAD NewQueueHead
;
186 PUHCIREQUEST Request
;
189 Request
= (PUHCIREQUEST
)Req
;
194 Status
= Request
->GetEndpointDescriptor(&NewQueueHead
);
195 if (!NT_SUCCESS(Status
))
198 // failed to create queue head
200 DPRINT1("[USBUHCI] Failed to create queue head %x\n", Status
);
207 ASSERT(PVOID(Request
) == NewQueueHead
->Request
);
212 DPRINT("AddUSBRequest Request %p\n", Request
);
213 DPRINT("NewQueueHead %p\n", NewQueueHead
);
214 return AddQueueHead(NewQueueHead
);
218 CUSBQueue::LinkQueueHead(
219 IN PUHCI_QUEUE_HEAD QueueHead
,
220 IN PUHCI_QUEUE_HEAD NextQueueHead
)
222 NextQueueHead
->LinkPhysical
= QueueHead
->LinkPhysical
;
223 NextQueueHead
->NextLogicalDescriptor
= QueueHead
->NextLogicalDescriptor
;
225 QueueHead
->LinkPhysical
= NextQueueHead
->PhysicalAddress
| QH_NEXT_IS_QH
;
226 QueueHead
->NextLogicalDescriptor
= (PVOID
)NextQueueHead
;
231 CUSBQueue::UnLinkQueueHead(
232 PUHCI_QUEUE_HEAD QueueHeadToRemove
,
233 PUHCI_QUEUE_HEAD PreviousQueueHead
)
235 PreviousQueueHead
->LinkPhysical
= QueueHeadToRemove
->LinkPhysical
;
236 PreviousQueueHead
->NextLogicalDescriptor
= QueueHeadToRemove
->NextLogicalDescriptor
;
240 CUSBQueue::AbortDevicePipe(
241 IN UCHAR DeviceAddress
,
242 IN PUSB_ENDPOINT_DESCRIPTOR EndDescriptor
)
245 PUHCI_TRANSFER_DESCRIPTOR Descriptor
;
246 PUHCI_QUEUE_HEAD QueueHead
, PreviousQueueHead
= NULL
;
247 UCHAR EndpointAddress
, EndpointDeviceAddress
;
248 PUSB_ENDPOINT EndpointDescriptor
;
251 EndpointDescriptor
= (PUSB_ENDPOINT
)EndDescriptor
;
254 KeAcquireSpinLock(&m_Lock
, &OldLevel
);
257 m_Hardware
->GetQueueHead(UHCI_INTERRUPT_QUEUE
, &QueueHead
);
262 Descriptor
= (PUHCI_TRANSFER_DESCRIPTOR
)QueueHead
->NextElementDescriptor
;
266 // extract endpoint address
267 EndpointAddress
= (Descriptor
->Token
>> TD_TOKEN_ENDPTADDR_SHIFT
) & 0x0F;
269 // extract device address
270 EndpointDeviceAddress
= (Descriptor
->Token
>> TD_TOKEN_DEVADDR_SHIFT
) & 0x7F;
272 // check if they match
273 if (EndpointAddress
== (EndpointDescriptor
->EndPointDescriptor
.bEndpointAddress
& 0x0F) &&
274 DeviceAddress
== EndpointDeviceAddress
)
276 // cleanup queue head
277 QueueHeadCleanup(QueueHead
, PreviousQueueHead
, &QueueHead
);
282 // move to next queue head
283 PreviousQueueHead
= QueueHead
;
284 QueueHead
= (PUHCI_QUEUE_HEAD
)QueueHead
->NextLogicalDescriptor
;
288 KeReleaseSpinLock(&m_Lock
, OldLevel
);
289 return STATUS_SUCCESS
;
293 CUSBQueue::CreateUSBRequest(
294 IUSBRequest
**OutRequest
)
296 PUSBREQUEST UsbRequest
;
300 Status
= InternalCreateUSBRequest(&UsbRequest
);
302 if (NT_SUCCESS(Status
))
304 *OutRequest
= UsbRequest
;
311 CUSBQueue::IsQueueHeadComplete(
312 IN PUHCI_QUEUE_HEAD QueueHead
)
314 PUHCI_TRANSFER_DESCRIPTOR Descriptor
;
317 if (QueueHead
->NextElementDescriptor
== NULL
)
322 DPRINT("QueueHead %p empty element physical\n", QueueHead
);
327 // check all descriptors
329 Descriptor
= (PUHCI_TRANSFER_DESCRIPTOR
)QueueHead
->NextElementDescriptor
;
332 if (Descriptor
->Status
& TD_STATUS_ACTIVE
)
335 // descriptor is still active
337 DPRINT("Descriptor %p is active Status %x BufferSize %lu\n", Descriptor
, Descriptor
->Status
, Descriptor
->BufferSize
);
341 if (Descriptor
->Status
& TD_ERROR_MASK
)
346 DPRINT1("[USBUHCI] Error detected at descriptor %p Physical %x\n", Descriptor
, Descriptor
->PhysicalAddress
);
351 ErrorCount
= (Descriptor
->Status
>> TD_ERROR_COUNT_SHIFT
) & TD_ERROR_COUNT_MASK
;
355 // error retry count elapsed
357 DPRINT1("[USBUHCI] ErrorBuffer %x TimeOut %x Nak %x BitStuff %x\n",
358 Descriptor
->Status
& TD_STATUS_ERROR_BUFFER
,
359 Descriptor
->Status
& TD_STATUS_ERROR_TIMEOUT
,
360 Descriptor
->Status
& TD_STATUS_ERROR_NAK
,
361 Descriptor
->Status
& TD_STATUS_ERROR_BITSTUFF
);
364 else if (Descriptor
->Status
& TD_STATUS_ERROR_BABBLE
)
369 DPRINT1("[USBUHCI] Babble detected\n");
377 DPRINT1("[USBUHCI] Stall detected\n");
382 // move to next descriptor
384 Descriptor
= (PUHCI_TRANSFER_DESCRIPTOR
)Descriptor
->NextLogicalDescriptor
;
388 // request is complete
394 CUSBQueue::QueueHeadCleanup(
395 IN PUHCI_QUEUE_HEAD QueueHead
,
396 IN PUHCI_QUEUE_HEAD PreviousQueueHead
,
397 OUT PUHCI_QUEUE_HEAD
*NextQueueHead
)
399 PUHCIREQUEST Request
;
400 PUHCI_QUEUE_HEAD NewQueueHead
;
406 UnLinkQueueHead(QueueHead
, PreviousQueueHead
);
409 // get next queue head
411 *NextQueueHead
= (PUHCI_QUEUE_HEAD
)PreviousQueueHead
->NextLogicalDescriptor
;
412 ASSERT(*NextQueueHead
!= QueueHead
);
415 // the queue head is complete, is the transfer now completed?
417 Request
= (PUHCIREQUEST
)QueueHead
->Request
;
423 DPRINT("Request %p\n", Request
);
424 Request
->FreeEndpointDescriptor(QueueHead
);
427 // check if transfer is complete
429 if (Request
->IsRequestComplete())
432 // the transfer is complete
434 Request
->CompletionCallback();
440 // grab new queue head
442 Status
= Request
->GetEndpointDescriptor(&NewQueueHead
);
443 if (!NT_SUCCESS(Status
))
446 // failed to get new queue head
448 DPRINT1("[USBUHCI] Failed to get new queue head with %x\n", Status
);
449 Request
->CompletionCallback();
457 Status
= AddQueueHead(NewQueueHead
);
458 if (!NT_SUCCESS(Status
))
461 // failed to get new queue head
463 DPRINT1("[USBUHCI] Failed to add queue head with %x\n", Status
);
464 Request
->CompletionCallback();
472 CUSBQueue::TransferInterrupt(
473 UCHAR ErrorInterrupt
)
476 PUHCI_QUEUE_HEAD QueueHead
, PreviousQueueHead
= NULL
;
482 KeAcquireSpinLock(&m_Lock
, &OldLevel
);
487 m_Hardware
->GetQueueHead(UHCI_INTERRUPT_QUEUE
, &QueueHead
);
492 // is queue head complete
494 DPRINT("QueueHead %p\n", QueueHead
);
495 IsComplete
= IsQueueHeadComplete(QueueHead
);
499 // cleanup queue head
501 QueueHeadCleanup(QueueHead
, PreviousQueueHead
, &QueueHead
);
506 // backup previous queue head
508 PreviousQueueHead
= QueueHead
;
511 // get next queue head
513 QueueHead
= (PUHCI_QUEUE_HEAD
)QueueHead
->NextLogicalDescriptor
;
519 KeReleaseSpinLock(&m_Lock
, OldLevel
);
525 PUSBQUEUE
*OutUsbQueue
)
530 // allocate controller
532 This
= new(NonPagedPool
, TAG_USBUHCI
) CUSBQueue(0);
536 // failed to allocate
538 return STATUS_INSUFFICIENT_RESOURCES
;
542 // add reference count
549 *OutUsbQueue
= (PUSBQUEUE
)This
;
554 return STATUS_SUCCESS
;