2 * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: drivers/usb/usbohci/usb_queue.cpp
5 * PURPOSE: USB OHCI device driver.
7 * Michael Martin (michael.martin@reactos.org)
8 * Johannes Anderwald (johannes.anderwald@reactos.org)
14 class CUSBQueue
: public IUSBQueue
17 STDMETHODIMP
QueryInterface( REFIID InterfaceId
, PVOID
* Interface
);
19 STDMETHODIMP_(ULONG
) AddRef()
21 InterlockedIncrement(&m_Ref
);
24 STDMETHODIMP_(ULONG
) Release()
26 InterlockedDecrement(&m_Ref
);
37 virtual NTSTATUS
Initialize(IN PUSBHARDWAREDEVICE Hardware
, PDMA_ADAPTER AdapterObject
, IN PDMAMEMORYMANAGER MemManager
, IN OPTIONAL PKSPIN_LOCK Lock
);
38 virtual ULONG
GetPendingRequestCount();
39 virtual NTSTATUS
AddUSBRequest(IUSBRequest
* Request
);
40 virtual NTSTATUS
CancelRequests();
41 virtual NTSTATUS
CreateUSBRequest(IUSBRequest
**OutRequest
);
42 virtual VOID
TransferDescriptorCompletionCallback(ULONG TransferDescriptorLogicalAddress
);
45 BOOLEAN
IsTransferDescriptorInEndpoint(IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
, IN ULONG TransferDescriptorLogicalAddress
);
46 BOOLEAN
IsTransferDescriptorInIsoEndpoint(IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
, IN ULONG TransferDescriptorLogicalAddress
);
47 NTSTATUS
FindTransferDescriptorInEndpoint(IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
, IN ULONG TransferDescriptorLogicalAddress
, OUT POHCI_ENDPOINT_DESCRIPTOR
*OutEndpointDescriptor
, OUT POHCI_ENDPOINT_DESCRIPTOR
*OutPreviousEndpointDescriptor
);
48 NTSTATUS
FindTransferDescriptorInInterruptHeadEndpoints(IN ULONG TransferDescriptorLogicalAddress
, OUT POHCI_ENDPOINT_DESCRIPTOR
*OutEndpointDescriptor
, OUT POHCI_ENDPOINT_DESCRIPTOR
*OutPreviousEndpointDescriptor
);
49 NTSTATUS
FindTransferDescriptorInIsochronousHeadEndpoints(IN ULONG TransferDescriptorLogicalAddress
, OUT POHCI_ENDPOINT_DESCRIPTOR
*OutEndpointDescriptor
, OUT POHCI_ENDPOINT_DESCRIPTOR
*OutPreviousEndpointDescriptor
);
51 VOID
CleanupEndpointDescriptor(POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
, POHCI_ENDPOINT_DESCRIPTOR PreviousEndpointDescriptor
);
52 POHCI_ENDPOINT_DESCRIPTOR
FindInterruptEndpointDescriptor(UCHAR InterruptInterval
);
53 VOID
PrintEndpointList(POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
);
54 VOID
LinkEndpoint(POHCI_ENDPOINT_DESCRIPTOR HeadEndpointDescriptor
, POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
);
56 // constructor / destructor
57 CUSBQueue(IUnknown
*OuterUnknown
){}
58 virtual ~CUSBQueue(){}
61 LONG m_Ref
; // reference count
62 KSPIN_LOCK m_Lock
; // list lock
63 PUSBHARDWAREDEVICE m_Hardware
; // hardware
64 POHCI_ENDPOINT_DESCRIPTOR m_BulkHeadEndpointDescriptor
; // bulk head descriptor
65 POHCI_ENDPOINT_DESCRIPTOR m_ControlHeadEndpointDescriptor
; // control head descriptor
66 POHCI_ENDPOINT_DESCRIPTOR m_IsoHeadEndpointDescriptor
; // isochronous head descriptor
67 POHCI_ENDPOINT_DESCRIPTOR
* m_InterruptEndpoints
;
70 //=================================================================================================
75 CUSBQueue::QueryInterface(
79 if (IsEqualGUIDAligned(refiid
, IID_IUnknown
))
81 *Output
= PVOID(PUNKNOWN(this));
82 PUNKNOWN(*Output
)->AddRef();
83 return STATUS_SUCCESS
;
86 return STATUS_UNSUCCESSFUL
;
90 CUSBQueue::Initialize(
91 IN PUSBHARDWAREDEVICE Hardware
,
92 IN PDMA_ADAPTER AdapterObject
,
93 IN PDMAMEMORYMANAGER MemManager
,
94 IN OPTIONAL PKSPIN_LOCK Lock
)
97 // get bulk endpoint descriptor
99 Hardware
->GetBulkHeadEndpointDescriptor(&m_BulkHeadEndpointDescriptor
);
102 // get control endpoint descriptor
104 Hardware
->GetControlHeadEndpointDescriptor(&m_ControlHeadEndpointDescriptor
);
107 // get isochronous endpoint
109 Hardware
->GetIsochronousHeadEndpointDescriptor(&m_IsoHeadEndpointDescriptor
);
112 // get interrupt endpoints
114 Hardware
->GetInterruptEndpointDescriptors(&m_InterruptEndpoints
);
117 // initialize spinlock
119 KeInitializeSpinLock(&m_Lock
);
124 m_Hardware
= Hardware
;
126 return STATUS_SUCCESS
;
130 CUSBQueue::GetPendingRequestCount()
133 // Loop through the pending list and iterrate one for each QueueHead that
134 // has a IRP to complete.
141 CUSBQueue::LinkEndpoint(
142 POHCI_ENDPOINT_DESCRIPTOR HeadEndpointDescriptor
,
143 POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
)
145 POHCI_ENDPOINT_DESCRIPTOR CurrentEndpointDescriptor
= HeadEndpointDescriptor
;
148 // get last descriptor in queue
150 while(CurrentEndpointDescriptor
->NextDescriptor
)
153 // move to last descriptor
155 CurrentEndpointDescriptor
= (POHCI_ENDPOINT_DESCRIPTOR
)CurrentEndpointDescriptor
->NextDescriptor
;
161 CurrentEndpointDescriptor
->NextPhysicalEndpoint
= EndpointDescriptor
->PhysicalAddress
.LowPart
;
162 CurrentEndpointDescriptor
->NextDescriptor
= EndpointDescriptor
;
167 CUSBQueue::AddUSBRequest(
168 IUSBRequest
* Request
)
173 POHCI_ENDPOINT_DESCRIPTOR HeadDescriptor
;
174 POHCI_ENDPOINT_DESCRIPTOR Descriptor
;
175 POHCI_ISO_TD CurrentDescriptor
;
179 DPRINT("CUSBQueue::AddUSBRequest\n");
184 ASSERT(Request
!= NULL
);
189 Type
= Request
->GetTransferType();
192 // add extra reference which is released when the request is completed
197 // get transfer descriptors
199 Status
= Request
->GetEndpointDescriptor(&Descriptor
);
200 if (!NT_SUCCESS(Status
))
203 // failed to get transfer descriptor
205 DPRINT1("CUSBQueue::AddUSBRequest GetEndpointDescriptor failed with %x\n", Status
);
217 if (Type
== USB_ENDPOINT_TYPE_BULK
)
220 // get head descriptor
222 HeadDescriptor
= m_BulkHeadEndpointDescriptor
;
224 else if (Type
== USB_ENDPOINT_TYPE_CONTROL
)
227 // get head descriptor
229 HeadDescriptor
= m_ControlHeadEndpointDescriptor
;
231 else if (Type
== USB_ENDPOINT_TYPE_INTERRUPT
)
234 // get head descriptor
236 HeadDescriptor
= FindInterruptEndpointDescriptor(Request
->GetInterval());
237 ASSERT(HeadDescriptor
);
239 else if (Type
== USB_ENDPOINT_TYPE_ISOCHRONOUS
)
242 // get head descriptor
244 HeadDescriptor
= m_IsoHeadEndpointDescriptor
;
247 // get current frame number
249 m_Hardware
->GetCurrentFrameNumber(&FrameNumber
);
252 // FIXME: increment frame number
257 // apply frame number to iso transfer descriptors
259 CurrentDescriptor
= (POHCI_ISO_TD
)Descriptor
->HeadLogicalDescriptor
;
261 DPRINT1("ISO: NextFrameNumber %x\n", FrameNumber
);
262 Frame
= (FrameNumber
& 0xFFFF);
264 while(CurrentDescriptor
)
267 // set current frame number
269 CurrentDescriptor
->Flags
|= OHCI_ITD_SET_STARTING_FRAME(Frame
);
272 // move to next frame number
274 Frame
+= OHCI_ITD_GET_FRAME_COUNT(CurrentDescriptor
->Flags
);
277 // move to next descriptor
279 CurrentDescriptor
= CurrentDescriptor
->NextLogicalDescriptor
;
283 // get current frame number
285 m_Hardware
->GetCurrentFrameNumber(&FrameNumber
);
287 DPRINT1("Hardware 1ms %p Iso %p\n",m_InterruptEndpoints
[0], m_IsoHeadEndpointDescriptor
);
288 ASSERT(m_InterruptEndpoints
[0]->NextPhysicalEndpoint
== m_IsoHeadEndpointDescriptor
->PhysicalAddress
.LowPart
);
290 PrintEndpointList(m_IsoHeadEndpointDescriptor
);
294 // set descriptor active
296 Descriptor
->Flags
&= ~OHCI_ENDPOINT_SKIP
;
299 // insert endpoint at end
301 LinkEndpoint(HeadDescriptor
, Descriptor
);
303 if (Type
== USB_ENDPOINT_TYPE_CONTROL
|| Type
== USB_ENDPOINT_TYPE_BULK
)
306 // notify hardware of our request
308 m_Hardware
->HeadEndpointDescriptorModified(Type
);
312 return STATUS_SUCCESS
;
316 CUSBQueue::CancelRequests()
319 return STATUS_NOT_IMPLEMENTED
;
323 CUSBQueue::CreateUSBRequest(
324 IUSBRequest
**OutRequest
)
326 PUSBREQUEST UsbRequest
;
330 Status
= InternalCreateUSBRequest(&UsbRequest
);
332 if (NT_SUCCESS(Status
))
334 *OutRequest
= UsbRequest
;
341 CUSBQueue::FindTransferDescriptorInEndpoint(
342 IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
,
343 IN ULONG TransferDescriptorLogicalAddress
,
344 OUT POHCI_ENDPOINT_DESCRIPTOR
*OutEndpointDescriptor
,
345 OUT POHCI_ENDPOINT_DESCRIPTOR
*OutPreviousEndpointDescriptor
)
347 POHCI_ENDPOINT_DESCRIPTOR LastDescriptor
= EndpointDescriptor
;
351 // skip first endpoint head
353 EndpointDescriptor
= (POHCI_ENDPOINT_DESCRIPTOR
)EndpointDescriptor
->NextDescriptor
;
355 while(EndpointDescriptor
)
358 // check if the transfer descriptor is inside the list
360 if (IsTransferDescriptorInEndpoint(EndpointDescriptor
, TransferDescriptorLogicalAddress
))
365 *OutEndpointDescriptor
= EndpointDescriptor
;
366 *OutPreviousEndpointDescriptor
= LastDescriptor
;
371 return STATUS_SUCCESS
;
375 // store last endpoint
377 LastDescriptor
= EndpointDescriptor
;
382 EndpointDescriptor
= (POHCI_ENDPOINT_DESCRIPTOR
)EndpointDescriptor
->NextDescriptor
;
386 // failed to endpoint
388 return STATUS_NOT_FOUND
;
392 CUSBQueue::FindTransferDescriptorInInterruptHeadEndpoints(IN ULONG TransferDescriptorLogicalAddress
, OUT POHCI_ENDPOINT_DESCRIPTOR
*OutEndpointDescriptor
, OUT POHCI_ENDPOINT_DESCRIPTOR
*OutPreviousEndpointDescriptor
)
398 // search descriptor in endpoint list
400 for(Index
= 0; Index
< OHCI_STATIC_ENDPOINT_COUNT
; Index
++)
403 // is it in current endpoint
405 Status
= FindTransferDescriptorInEndpoint(m_InterruptEndpoints
[Index
], TransferDescriptorLogicalAddress
, OutEndpointDescriptor
, OutPreviousEndpointDescriptor
);
406 if (NT_SUCCESS(Status
))
409 // found transfer descriptor
411 return STATUS_SUCCESS
;
418 return STATUS_NOT_FOUND
;
422 CUSBQueue::FindTransferDescriptorInIsochronousHeadEndpoints(
423 IN ULONG TransferDescriptorLogicalAddress
,
424 OUT POHCI_ENDPOINT_DESCRIPTOR
*OutEndpointDescriptor
,
425 OUT POHCI_ENDPOINT_DESCRIPTOR
*OutPreviousEndpointDescriptor
)
427 POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
;
428 POHCI_ENDPOINT_DESCRIPTOR LastDescriptor
= m_IsoHeadEndpointDescriptor
;
432 // skip first endpoint head
434 EndpointDescriptor
= (POHCI_ENDPOINT_DESCRIPTOR
)m_IsoHeadEndpointDescriptor
->NextDescriptor
;
436 while(EndpointDescriptor
)
439 // check if the transfer descriptor is inside the list
441 if (IsTransferDescriptorInIsoEndpoint(EndpointDescriptor
, TransferDescriptorLogicalAddress
))
446 *OutEndpointDescriptor
= EndpointDescriptor
;
447 *OutPreviousEndpointDescriptor
= LastDescriptor
;
452 return STATUS_SUCCESS
;
456 // store last endpoint
458 LastDescriptor
= EndpointDescriptor
;
463 EndpointDescriptor
= (POHCI_ENDPOINT_DESCRIPTOR
)EndpointDescriptor
->NextDescriptor
;
467 // failed to endpoint
469 return STATUS_NOT_FOUND
;
473 CUSBQueue::IsTransferDescriptorInIsoEndpoint(
474 IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
,
475 IN ULONG TransferDescriptorLogicalAddress
)
477 POHCI_ISO_TD Descriptor
;
480 // get first general transfer descriptor
482 Descriptor
= (POHCI_ISO_TD
)EndpointDescriptor
->HeadLogicalDescriptor
;
491 if (Descriptor
->PhysicalAddress
.LowPart
== TransferDescriptorLogicalAddress
)
502 Descriptor
= (POHCI_ISO_TD
)Descriptor
->NextLogicalDescriptor
;
506 // no descriptor found
513 CUSBQueue::IsTransferDescriptorInEndpoint(
514 IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
,
515 IN ULONG TransferDescriptorLogicalAddress
)
517 POHCI_GENERAL_TD Descriptor
;
520 // get first general transfer descriptor
522 Descriptor
= (POHCI_GENERAL_TD
)EndpointDescriptor
->HeadLogicalDescriptor
;
531 if (Descriptor
->PhysicalAddress
.LowPart
== TransferDescriptorLogicalAddress
)
542 Descriptor
= (POHCI_GENERAL_TD
)Descriptor
->NextLogicalDescriptor
;
547 // no descriptor found
553 CUSBQueue::CleanupEndpointDescriptor(
554 POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
,
555 POHCI_ENDPOINT_DESCRIPTOR PreviousEndpointDescriptor
)
560 // FIXME: verify unlinking process
562 PreviousEndpointDescriptor
->NextDescriptor
= EndpointDescriptor
->NextDescriptor
;
563 PreviousEndpointDescriptor
->NextPhysicalEndpoint
= EndpointDescriptor
->NextPhysicalEndpoint
;
566 // get corresponding request
568 Request
= PUSBREQUEST(EndpointDescriptor
->Request
);
572 // notify of completion
574 Request
->CompletionCallback(EndpointDescriptor
);
577 // free endpoint descriptor
579 Request
->FreeEndpointDescriptor(EndpointDescriptor
);
582 // FIXME: check if complete
584 //ASSERT(Request->IsRequestComplete());
586 Request
->FreeEndpointDescriptor(EndpointDescriptor
);
595 CUSBQueue::PrintEndpointList(
596 POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
)
598 DPRINT1("CUSBQueue::PrintEndpointList HeadEndpoint %p Logical %x\n", EndpointDescriptor
, EndpointDescriptor
->PhysicalAddress
.LowPart
);
601 // get first general transfer descriptor
603 EndpointDescriptor
= (POHCI_ENDPOINT_DESCRIPTOR
)EndpointDescriptor
->NextDescriptor
;
605 while(EndpointDescriptor
)
607 DPRINT1(" CUSBQueue::PrintEndpointList Endpoint %p Logical %x\n", EndpointDescriptor
, EndpointDescriptor
->PhysicalAddress
.LowPart
);
612 EndpointDescriptor
= (POHCI_ENDPOINT_DESCRIPTOR
)EndpointDescriptor
->NextDescriptor
;
617 CUSBQueue::TransferDescriptorCompletionCallback(
618 ULONG TransferDescriptorLogicalAddress
)
620 POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
, PreviousEndpointDescriptor
;
623 DPRINT("CUSBQueue::TransferDescriptorCompletionCallback transfer descriptor %x\n", TransferDescriptorLogicalAddress
);
626 // find transfer descriptor in control list
628 Status
= FindTransferDescriptorInEndpoint(m_ControlHeadEndpointDescriptor
, TransferDescriptorLogicalAddress
, &EndpointDescriptor
, &PreviousEndpointDescriptor
);
629 if (NT_SUCCESS(Status
))
634 CleanupEndpointDescriptor(EndpointDescriptor
, PreviousEndpointDescriptor
);
643 // find transfer descriptor in bulk list
645 Status
= FindTransferDescriptorInEndpoint(m_BulkHeadEndpointDescriptor
, TransferDescriptorLogicalAddress
, &EndpointDescriptor
, &PreviousEndpointDescriptor
);
646 if (NT_SUCCESS(Status
))
651 CleanupEndpointDescriptor(EndpointDescriptor
, PreviousEndpointDescriptor
);
660 // find transfer descriptor in interrupt list
662 Status
= FindTransferDescriptorInInterruptHeadEndpoints(TransferDescriptorLogicalAddress
, &EndpointDescriptor
, &PreviousEndpointDescriptor
);
663 if (NT_SUCCESS(Status
))
668 CleanupEndpointDescriptor(EndpointDescriptor
, PreviousEndpointDescriptor
);
677 // last try: find the descriptor in isochronous list
679 Status
= FindTransferDescriptorInIsochronousHeadEndpoints(TransferDescriptorLogicalAddress
, &EndpointDescriptor
, &PreviousEndpointDescriptor
);
680 if (NT_SUCCESS(Status
))
685 DPRINT1("ISO endpoint complete\n");
687 CleanupEndpointDescriptor(EndpointDescriptor
, PreviousEndpointDescriptor
);
696 // hardware reported dead endpoint completed
698 DPRINT1("CUSBQueue::TransferDescriptorCompletionCallback invalid transfer descriptor %x\n", TransferDescriptorLogicalAddress
);
702 POHCI_ENDPOINT_DESCRIPTOR
703 CUSBQueue::FindInterruptEndpointDescriptor(
704 UCHAR InterruptInterval
)
712 ASSERT(InterruptInterval
<= OHCI_BIGGEST_INTERVAL
);
715 // find interrupt index
717 while (Power
<= OHCI_BIGGEST_INTERVAL
/ 2)
720 // is current interval greater
722 if (Power
* 2 > InterruptInterval
)
731 // move to next interrupt
736 DPRINT("InterruptInterval %lu Selected InterruptIndex %lu Choosen Interval %lu\n", InterruptInterval
, Index
, Power
);
741 return m_InterruptEndpoints
[Index
];
746 PUSBQUEUE
*OutUsbQueue
)
751 // allocate controller
753 This
= new(NonPagedPool
, TAG_USBOHCI
) CUSBQueue(0);
757 // failed to allocate
759 return STATUS_INSUFFICIENT_RESOURCES
;
763 // add reference count
770 *OutUsbQueue
= (PUSBQUEUE
)This
;
775 return STATUS_SUCCESS
;