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)
14 class CUSBQueue
: public IUHCIQueue
17 STDMETHODIMP
QueryInterface( REFIID InterfaceId
, PVOID
* Interface
);
19 STDMETHODIMP_(ULONG
) AddRef()
21 InterlockedIncrement(&m_Ref
);
24 STDMETHODIMP_(ULONG
) Release()
26 InterlockedDecrement(&m_Ref
);
41 VOID
LinkQueueHead(PUHCI_QUEUE_HEAD QueueHead
, PUHCI_QUEUE_HEAD NextQueueHead
);
42 VOID
UnLinkQueueHead(PUHCI_QUEUE_HEAD PreviousQueueHead
, PUHCI_QUEUE_HEAD NextQueueHead
);
43 BOOLEAN
IsQueueHeadComplete(PUHCI_QUEUE_HEAD QueueHead
);
44 NTSTATUS
AddQueueHead(PUHCI_QUEUE_HEAD NewQueueHead
);
45 VOID
QueueHeadCleanup(IN PUHCI_QUEUE_HEAD QueueHead
, IN PUHCI_QUEUE_HEAD PreviousQueueHead
, OUT PUHCI_QUEUE_HEAD
*NextQueueHead
);
48 // constructor / destructor
49 CUSBQueue(IUnknown
*OuterUnknown
){}
50 virtual ~CUSBQueue(){}
53 LONG m_Ref
; // reference count
54 KSPIN_LOCK m_Lock
; // list lock
55 PUHCIHARDWAREDEVICE m_Hardware
; // hardware
59 //=================================================================================================
64 CUSBQueue::QueryInterface(
68 if (IsEqualGUIDAligned(refiid
, IID_IUnknown
))
70 *Output
= PVOID(PUNKNOWN(this));
71 PUNKNOWN(*Output
)->AddRef();
72 return STATUS_SUCCESS
;
75 return STATUS_UNSUCCESSFUL
;
79 CUSBQueue::Initialize(
80 IN PUSBHARDWAREDEVICE Hardware
,
81 IN PDMA_ADAPTER AdapterObject
,
82 IN PDMAMEMORYMANAGER MemManager
,
83 IN OPTIONAL PKSPIN_LOCK Lock
)
89 m_Hardware
= PUHCIHARDWAREDEVICE(Hardware
);
92 // initialize spinlock
94 KeInitializeSpinLock(&m_Lock
);
95 return STATUS_SUCCESS
;
99 CUSBQueue::AddQueueHead(
100 PUHCI_QUEUE_HEAD NewQueueHead
)
102 PUHCIREQUEST Request
;
103 PUHCI_QUEUE_HEAD QueueHead
= NULL
;
109 Request
= (PUHCIREQUEST
)NewQueueHead
->Request
;
115 return STATUS_INVALID_PARAMETER
;
118 if (Request
->GetTransferType() == USB_ENDPOINT_TYPE_CONTROL
)
123 if (Request
->GetDeviceSpeed() == UsbLowSpeed
)
126 // use low speed queue
128 m_Hardware
->GetQueueHead(UHCI_LOW_SPEED_CONTROL_QUEUE
, &QueueHead
);
133 // use full speed queue
135 m_Hardware
->GetQueueHead(UHCI_FULL_SPEED_CONTROL_QUEUE
, &QueueHead
);
138 else if (Request
->GetTransferType() == USB_ENDPOINT_TYPE_BULK
)
141 // use full speed queue
143 m_Hardware
->GetQueueHead(UHCI_BULK_QUEUE
, &QueueHead
);
145 else if (Request
->GetTransferType() == USB_ENDPOINT_TYPE_INTERRUPT
)
148 // use full speed queue
150 m_Hardware
->GetQueueHead(UHCI_INTERRUPT_QUEUE
, &QueueHead
);
152 else if (Request
->GetTransferType() == USB_ENDPOINT_TYPE_INTERRUPT
)
155 // use full speed queue
157 m_Hardware
->GetQueueHead(UHCI_INTERRUPT_QUEUE
, &QueueHead
);
161 // FIXME support isochronous
171 // now link the new queue head
173 LinkQueueHead(QueueHead
, NewQueueHead
);
174 return STATUS_SUCCESS
;
179 CUSBQueue::AddUSBRequest(
182 PUHCI_QUEUE_HEAD NewQueueHead
;
184 PUHCIREQUEST Request
;
187 Request
= (PUHCIREQUEST
)Req
;
192 Status
= Request
->GetEndpointDescriptor(&NewQueueHead
);
193 if (!NT_SUCCESS(Status
))
196 // failed to create queue head
198 DPRINT1("[USBUHCI] Failed to create queue head %x\n", Status
);
205 ASSERT(PVOID(Request
) == NewQueueHead
->Request
);
210 DPRINT("AddUSBRequest Request %p\n", Request
);
211 DPRINT("NewQueueHead %p\n", NewQueueHead
);
212 return AddQueueHead(NewQueueHead
);
216 CUSBQueue::LinkQueueHead(
217 IN PUHCI_QUEUE_HEAD QueueHead
,
218 IN PUHCI_QUEUE_HEAD NextQueueHead
)
220 NextQueueHead
->LinkPhysical
= QueueHead
->LinkPhysical
;
221 NextQueueHead
->NextLogicalDescriptor
= QueueHead
->NextLogicalDescriptor
;
223 QueueHead
->LinkPhysical
= NextQueueHead
->PhysicalAddress
| QH_NEXT_IS_QH
;
224 QueueHead
->NextLogicalDescriptor
= (PVOID
)NextQueueHead
;
229 CUSBQueue::UnLinkQueueHead(
230 PUHCI_QUEUE_HEAD QueueHeadToRemove
,
231 PUHCI_QUEUE_HEAD PreviousQueueHead
)
233 PreviousQueueHead
->LinkPhysical
= QueueHeadToRemove
->LinkPhysical
;
234 PreviousQueueHead
->NextLogicalDescriptor
= QueueHeadToRemove
->NextLogicalDescriptor
;
238 CUSBQueue::AbortDevicePipe(
239 IN UCHAR DeviceAddress
,
240 IN PUSB_ENDPOINT_DESCRIPTOR EndDescriptor
)
243 PUHCI_TRANSFER_DESCRIPTOR Descriptor
;
244 PUHCI_QUEUE_HEAD QueueHead
, PreviousQueueHead
= NULL
;
245 UCHAR EndpointAddress
, EndpointDeviceAddress
;
246 PUSB_ENDPOINT EndpointDescriptor
;
249 EndpointDescriptor
= (PUSB_ENDPOINT
)EndDescriptor
;
252 KeAcquireSpinLock(&m_Lock
, &OldLevel
);
255 m_Hardware
->GetQueueHead(UHCI_INTERRUPT_QUEUE
, &QueueHead
);
260 Descriptor
= (PUHCI_TRANSFER_DESCRIPTOR
)QueueHead
->NextElementDescriptor
;
264 // extract endpoint address
265 EndpointAddress
= (Descriptor
->Token
>> TD_TOKEN_ENDPTADDR_SHIFT
) & 0x0F;
267 // extract device address
268 EndpointDeviceAddress
= (Descriptor
->Token
>> TD_TOKEN_DEVADDR_SHIFT
) & 0x7F;
270 // check if they match
271 if (EndpointAddress
== (EndpointDescriptor
->EndPointDescriptor
.bEndpointAddress
& 0x0F) &&
272 DeviceAddress
== EndpointDeviceAddress
)
274 // cleanup queue head
275 QueueHeadCleanup(QueueHead
, PreviousQueueHead
, &QueueHead
);
280 // move to next queue head
281 PreviousQueueHead
= QueueHead
;
282 QueueHead
= (PUHCI_QUEUE_HEAD
)QueueHead
->NextLogicalDescriptor
;
286 KeReleaseSpinLock(&m_Lock
, OldLevel
);
287 return STATUS_SUCCESS
;
291 CUSBQueue::CreateUSBRequest(
292 IUSBRequest
**OutRequest
)
294 PUSBREQUEST UsbRequest
;
298 Status
= InternalCreateUSBRequest(&UsbRequest
);
300 if (NT_SUCCESS(Status
))
302 *OutRequest
= UsbRequest
;
309 CUSBQueue::IsQueueHeadComplete(
310 IN PUHCI_QUEUE_HEAD QueueHead
)
312 PUHCI_TRANSFER_DESCRIPTOR Descriptor
;
315 if (QueueHead
->NextElementDescriptor
== NULL
)
320 DPRINT("QueueHead %p empty element physical\n", QueueHead
);
325 // check all descriptors
327 Descriptor
= (PUHCI_TRANSFER_DESCRIPTOR
)QueueHead
->NextElementDescriptor
;
330 if (Descriptor
->Status
& TD_STATUS_ACTIVE
)
333 // descriptor is still active
335 DPRINT("Descriptor %p is active Status %x BufferSize %lu\n", Descriptor
, Descriptor
->Status
, Descriptor
->BufferSize
);
339 if (Descriptor
->Status
& TD_ERROR_MASK
)
344 DPRINT1("[USBUHCI] Error detected at descriptor %p Physical %x\n", Descriptor
, Descriptor
->PhysicalAddress
);
349 ErrorCount
= (Descriptor
->Status
>> TD_ERROR_COUNT_SHIFT
) & TD_ERROR_COUNT_MASK
;
353 // error retry count elapsed
355 DPRINT1("[USBUHCI] ErrorBuffer %x TimeOut %x Nak %x BitStuff %x\n",
356 Descriptor
->Status
& TD_STATUS_ERROR_BUFFER
,
357 Descriptor
->Status
& TD_STATUS_ERROR_TIMEOUT
,
358 Descriptor
->Status
& TD_STATUS_ERROR_NAK
,
359 Descriptor
->Status
& TD_STATUS_ERROR_BITSTUFF
);
362 else if (Descriptor
->Status
& TD_STATUS_ERROR_BABBLE
)
367 DPRINT1("[USBUHCI] Babble detected\n");
375 DPRINT1("[USBUHCI] Stall detected\n");
380 // move to next descriptor
382 Descriptor
= (PUHCI_TRANSFER_DESCRIPTOR
)Descriptor
->NextLogicalDescriptor
;
386 // request is complete
392 CUSBQueue::QueueHeadCleanup(
393 IN PUHCI_QUEUE_HEAD QueueHead
,
394 IN PUHCI_QUEUE_HEAD PreviousQueueHead
,
395 OUT PUHCI_QUEUE_HEAD
*NextQueueHead
)
397 PUHCIREQUEST Request
;
398 PUHCI_QUEUE_HEAD NewQueueHead
;
404 UnLinkQueueHead(QueueHead
, PreviousQueueHead
);
407 // get next queue head
409 *NextQueueHead
= (PUHCI_QUEUE_HEAD
)PreviousQueueHead
->NextLogicalDescriptor
;
410 ASSERT(*NextQueueHead
!= QueueHead
);
413 // the queue head is complete, is the transfer now completed?
415 Request
= (PUHCIREQUEST
)QueueHead
->Request
;
421 DPRINT("Request %p\n", Request
);
422 Request
->FreeEndpointDescriptor(QueueHead
);
425 // check if transfer is complete
427 if (Request
->IsRequestComplete())
430 // the transfer is complete
432 Request
->CompletionCallback();
438 // grab new queue head
440 Status
= Request
->GetEndpointDescriptor(&NewQueueHead
);
441 if (!NT_SUCCESS(Status
))
444 // failed to get new queue head
446 DPRINT1("[USBUHCI] Failed to get new queue head with %x\n", Status
);
447 Request
->CompletionCallback();
455 Status
= AddQueueHead(NewQueueHead
);
456 if (!NT_SUCCESS(Status
))
459 // failed to get new queue head
461 DPRINT1("[USBUHCI] Failed to add queue head with %x\n", Status
);
462 Request
->CompletionCallback();
470 CUSBQueue::TransferInterrupt(
471 UCHAR ErrorInterrupt
)
474 PUHCI_QUEUE_HEAD QueueHead
, PreviousQueueHead
= NULL
;
480 KeAcquireSpinLock(&m_Lock
, &OldLevel
);
485 m_Hardware
->GetQueueHead(UHCI_INTERRUPT_QUEUE
, &QueueHead
);
490 // is queue head complete
492 DPRINT("QueueHead %p\n", QueueHead
);
493 IsComplete
= IsQueueHeadComplete(QueueHead
);
497 // cleanup queue head
499 QueueHeadCleanup(QueueHead
, PreviousQueueHead
, &QueueHead
);
504 // backup previous queue head
506 PreviousQueueHead
= QueueHead
;
509 // get next queue head
511 QueueHead
= (PUHCI_QUEUE_HEAD
)QueueHead
->NextLogicalDescriptor
;
517 KeReleaseSpinLock(&m_Lock
, OldLevel
);
523 PUSBQUEUE
*OutUsbQueue
)
528 // allocate controller
530 This
= new(NonPagedPool
, TAG_USBUHCI
) CUSBQueue(0);
534 // failed to allocate
536 return STATUS_INSUFFICIENT_RESOURCES
;
540 // add reference count
547 *OutUsbQueue
= (PUSBQUEUE
)This
;
552 return STATUS_SUCCESS
;