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
)
172 POHCI_ENDPOINT_DESCRIPTOR HeadDescriptor
;
173 POHCI_ENDPOINT_DESCRIPTOR Descriptor
;
174 POHCI_ISO_TD CurrentDescriptor
;
178 DPRINT("CUSBQueue::AddUSBRequest\n");
183 ASSERT(Request
!= NULL
);
188 Type
= Request
->GetTransferType();
191 // add extra reference which is released when the request is completed
196 // get transfer descriptors
198 Status
= Request
->GetEndpointDescriptor(&Descriptor
);
199 if (!NT_SUCCESS(Status
))
202 // failed to get transfer descriptor
204 DPRINT1("CUSBQueue::AddUSBRequest GetEndpointDescriptor failed with %x\n", Status
);
216 if (Type
== USB_ENDPOINT_TYPE_BULK
)
219 // get head descriptor
221 HeadDescriptor
= m_BulkHeadEndpointDescriptor
;
223 else if (Type
== USB_ENDPOINT_TYPE_CONTROL
)
226 // get head descriptor
228 HeadDescriptor
= m_ControlHeadEndpointDescriptor
;
230 else if (Type
== USB_ENDPOINT_TYPE_INTERRUPT
)
233 // get head descriptor
235 HeadDescriptor
= FindInterruptEndpointDescriptor(Request
->GetInterval());
236 ASSERT(HeadDescriptor
);
238 else if (Type
== USB_ENDPOINT_TYPE_ISOCHRONOUS
)
241 // get head descriptor
243 HeadDescriptor
= m_IsoHeadEndpointDescriptor
;
246 // get current frame number
248 m_Hardware
->GetCurrentFrameNumber(&FrameNumber
);
251 // FIXME: increment frame number
256 // apply frame number to iso transfer descriptors
258 CurrentDescriptor
= (POHCI_ISO_TD
)Descriptor
->HeadLogicalDescriptor
;
260 DPRINT1("ISO: NextFrameNumber %x\n", FrameNumber
);
261 Frame
= (FrameNumber
& 0xFFFF);
263 while(CurrentDescriptor
)
266 // set current frame number
268 CurrentDescriptor
->Flags
|= OHCI_ITD_SET_STARTING_FRAME(Frame
);
271 // move to next frame number
273 Frame
+= OHCI_ITD_GET_FRAME_COUNT(CurrentDescriptor
->Flags
);
276 // move to next descriptor
278 CurrentDescriptor
= CurrentDescriptor
->NextLogicalDescriptor
;
282 // get current frame number
284 m_Hardware
->GetCurrentFrameNumber(&FrameNumber
);
286 DPRINT1("Hardware 1ms %p Iso %p\n",m_InterruptEndpoints
[0], m_IsoHeadEndpointDescriptor
);
287 ASSERT(m_InterruptEndpoints
[0]->NextPhysicalEndpoint
== m_IsoHeadEndpointDescriptor
->PhysicalAddress
.LowPart
);
289 PrintEndpointList(m_IsoHeadEndpointDescriptor
);
297 return STATUS_INVALID_PARAMETER
;
301 // set descriptor active
303 Descriptor
->Flags
&= ~OHCI_ENDPOINT_SKIP
;
306 // insert endpoint at end
308 LinkEndpoint(HeadDescriptor
, Descriptor
);
310 if (Type
== USB_ENDPOINT_TYPE_CONTROL
|| Type
== USB_ENDPOINT_TYPE_BULK
)
313 // notify hardware of our request
315 m_Hardware
->HeadEndpointDescriptorModified(Type
);
318 return STATUS_SUCCESS
;
322 CUSBQueue::CancelRequests()
325 return STATUS_NOT_IMPLEMENTED
;
329 CUSBQueue::CreateUSBRequest(
330 IUSBRequest
**OutRequest
)
332 PUSBREQUEST UsbRequest
;
336 Status
= InternalCreateUSBRequest(&UsbRequest
);
338 if (NT_SUCCESS(Status
))
340 *OutRequest
= UsbRequest
;
347 CUSBQueue::FindTransferDescriptorInEndpoint(
348 IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
,
349 IN ULONG TransferDescriptorLogicalAddress
,
350 OUT POHCI_ENDPOINT_DESCRIPTOR
*OutEndpointDescriptor
,
351 OUT POHCI_ENDPOINT_DESCRIPTOR
*OutPreviousEndpointDescriptor
)
353 POHCI_ENDPOINT_DESCRIPTOR LastDescriptor
= EndpointDescriptor
;
357 // skip first endpoint head
359 EndpointDescriptor
= (POHCI_ENDPOINT_DESCRIPTOR
)EndpointDescriptor
->NextDescriptor
;
361 while(EndpointDescriptor
)
364 // check if the transfer descriptor is inside the list
366 if (IsTransferDescriptorInEndpoint(EndpointDescriptor
, TransferDescriptorLogicalAddress
))
371 *OutEndpointDescriptor
= EndpointDescriptor
;
372 *OutPreviousEndpointDescriptor
= LastDescriptor
;
377 return STATUS_SUCCESS
;
381 // store last endpoint
383 LastDescriptor
= EndpointDescriptor
;
388 EndpointDescriptor
= (POHCI_ENDPOINT_DESCRIPTOR
)EndpointDescriptor
->NextDescriptor
;
392 // failed to endpoint
394 return STATUS_NOT_FOUND
;
398 CUSBQueue::FindTransferDescriptorInInterruptHeadEndpoints(IN ULONG TransferDescriptorLogicalAddress
, OUT POHCI_ENDPOINT_DESCRIPTOR
*OutEndpointDescriptor
, OUT POHCI_ENDPOINT_DESCRIPTOR
*OutPreviousEndpointDescriptor
)
404 // search descriptor in endpoint list
406 for(Index
= 0; Index
< OHCI_STATIC_ENDPOINT_COUNT
; Index
++)
409 // is it in current endpoint
411 Status
= FindTransferDescriptorInEndpoint(m_InterruptEndpoints
[Index
], TransferDescriptorLogicalAddress
, OutEndpointDescriptor
, OutPreviousEndpointDescriptor
);
412 if (NT_SUCCESS(Status
))
415 // found transfer descriptor
417 return STATUS_SUCCESS
;
424 return STATUS_NOT_FOUND
;
428 CUSBQueue::FindTransferDescriptorInIsochronousHeadEndpoints(
429 IN ULONG TransferDescriptorLogicalAddress
,
430 OUT POHCI_ENDPOINT_DESCRIPTOR
*OutEndpointDescriptor
,
431 OUT POHCI_ENDPOINT_DESCRIPTOR
*OutPreviousEndpointDescriptor
)
433 POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
;
434 POHCI_ENDPOINT_DESCRIPTOR LastDescriptor
= m_IsoHeadEndpointDescriptor
;
438 // skip first endpoint head
440 EndpointDescriptor
= (POHCI_ENDPOINT_DESCRIPTOR
)m_IsoHeadEndpointDescriptor
->NextDescriptor
;
442 while(EndpointDescriptor
)
445 // check if the transfer descriptor is inside the list
447 if (IsTransferDescriptorInIsoEndpoint(EndpointDescriptor
, TransferDescriptorLogicalAddress
))
452 *OutEndpointDescriptor
= EndpointDescriptor
;
453 *OutPreviousEndpointDescriptor
= LastDescriptor
;
458 return STATUS_SUCCESS
;
462 // store last endpoint
464 LastDescriptor
= EndpointDescriptor
;
469 EndpointDescriptor
= (POHCI_ENDPOINT_DESCRIPTOR
)EndpointDescriptor
->NextDescriptor
;
473 // failed to endpoint
475 return STATUS_NOT_FOUND
;
479 CUSBQueue::IsTransferDescriptorInIsoEndpoint(
480 IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
,
481 IN ULONG TransferDescriptorLogicalAddress
)
483 POHCI_ISO_TD Descriptor
;
486 // get first general transfer descriptor
488 Descriptor
= (POHCI_ISO_TD
)EndpointDescriptor
->HeadLogicalDescriptor
;
497 if (Descriptor
->PhysicalAddress
.LowPart
== TransferDescriptorLogicalAddress
)
508 Descriptor
= (POHCI_ISO_TD
)Descriptor
->NextLogicalDescriptor
;
512 // no descriptor found
519 CUSBQueue::IsTransferDescriptorInEndpoint(
520 IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
,
521 IN ULONG TransferDescriptorLogicalAddress
)
523 POHCI_GENERAL_TD Descriptor
;
526 // get first general transfer descriptor
528 Descriptor
= (POHCI_GENERAL_TD
)EndpointDescriptor
->HeadLogicalDescriptor
;
537 if (Descriptor
->PhysicalAddress
.LowPart
== TransferDescriptorLogicalAddress
)
548 Descriptor
= (POHCI_GENERAL_TD
)Descriptor
->NextLogicalDescriptor
;
553 // no descriptor found
559 CUSBQueue::CleanupEndpointDescriptor(
560 POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
,
561 POHCI_ENDPOINT_DESCRIPTOR PreviousEndpointDescriptor
)
566 // FIXME: verify unlinking process
568 PreviousEndpointDescriptor
->NextDescriptor
= EndpointDescriptor
->NextDescriptor
;
569 PreviousEndpointDescriptor
->NextPhysicalEndpoint
= EndpointDescriptor
->NextPhysicalEndpoint
;
572 // get corresponding request
574 Request
= PUSBREQUEST(EndpointDescriptor
->Request
);
578 // notify of completion
580 Request
->CompletionCallback(EndpointDescriptor
);
583 // free endpoint descriptor
585 Request
->FreeEndpointDescriptor(EndpointDescriptor
);
588 // FIXME: check if complete
590 //ASSERT(Request->IsRequestComplete());
592 Request
->FreeEndpointDescriptor(EndpointDescriptor
);
601 CUSBQueue::PrintEndpointList(
602 POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
)
604 DPRINT1("CUSBQueue::PrintEndpointList HeadEndpoint %p Logical %x\n", EndpointDescriptor
, EndpointDescriptor
->PhysicalAddress
.LowPart
);
607 // get first general transfer descriptor
609 EndpointDescriptor
= (POHCI_ENDPOINT_DESCRIPTOR
)EndpointDescriptor
->NextDescriptor
;
611 while(EndpointDescriptor
)
613 DPRINT1(" CUSBQueue::PrintEndpointList Endpoint %p Logical %x\n", EndpointDescriptor
, EndpointDescriptor
->PhysicalAddress
.LowPart
);
618 EndpointDescriptor
= (POHCI_ENDPOINT_DESCRIPTOR
)EndpointDescriptor
->NextDescriptor
;
623 CUSBQueue::TransferDescriptorCompletionCallback(
624 ULONG TransferDescriptorLogicalAddress
)
626 POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
, PreviousEndpointDescriptor
;
629 DPRINT("CUSBQueue::TransferDescriptorCompletionCallback transfer descriptor %x\n", TransferDescriptorLogicalAddress
);
632 // find transfer descriptor in control list
634 Status
= FindTransferDescriptorInEndpoint(m_ControlHeadEndpointDescriptor
, TransferDescriptorLogicalAddress
, &EndpointDescriptor
, &PreviousEndpointDescriptor
);
635 if (NT_SUCCESS(Status
))
640 CleanupEndpointDescriptor(EndpointDescriptor
, PreviousEndpointDescriptor
);
649 // find transfer descriptor in bulk list
651 Status
= FindTransferDescriptorInEndpoint(m_BulkHeadEndpointDescriptor
, TransferDescriptorLogicalAddress
, &EndpointDescriptor
, &PreviousEndpointDescriptor
);
652 if (NT_SUCCESS(Status
))
657 CleanupEndpointDescriptor(EndpointDescriptor
, PreviousEndpointDescriptor
);
666 // find transfer descriptor in interrupt list
668 Status
= FindTransferDescriptorInInterruptHeadEndpoints(TransferDescriptorLogicalAddress
, &EndpointDescriptor
, &PreviousEndpointDescriptor
);
669 if (NT_SUCCESS(Status
))
674 CleanupEndpointDescriptor(EndpointDescriptor
, PreviousEndpointDescriptor
);
683 // last try: find the descriptor in isochronous list
685 Status
= FindTransferDescriptorInIsochronousHeadEndpoints(TransferDescriptorLogicalAddress
, &EndpointDescriptor
, &PreviousEndpointDescriptor
);
686 if (NT_SUCCESS(Status
))
691 DPRINT1("ISO endpoint complete\n");
693 CleanupEndpointDescriptor(EndpointDescriptor
, PreviousEndpointDescriptor
);
702 // hardware reported dead endpoint completed
704 DPRINT1("CUSBQueue::TransferDescriptorCompletionCallback invalid transfer descriptor %x\n", TransferDescriptorLogicalAddress
);
708 POHCI_ENDPOINT_DESCRIPTOR
709 CUSBQueue::FindInterruptEndpointDescriptor(
710 UCHAR InterruptInterval
)
718 ASSERT(InterruptInterval
<= OHCI_BIGGEST_INTERVAL
);
721 // find interrupt index
723 while (Power
<= OHCI_BIGGEST_INTERVAL
/ 2)
726 // is current interval greater
728 if (Power
* 2 > InterruptInterval
)
737 // move to next interrupt
742 DPRINT("InterruptInterval %lu Selected InterruptIndex %lu Choosen Interval %lu\n", InterruptInterval
, Index
, Power
);
747 return m_InterruptEndpoints
[Index
];
752 PUSBQUEUE
*OutUsbQueue
)
757 // allocate controller
759 This
= new(NonPagedPool
, TAG_USBOHCI
) CUSBQueue(0);
763 // failed to allocate
765 return STATUS_INSUFFICIENT_RESOURCES
;
769 // add reference count
776 *OutUsbQueue
= (PUSBQUEUE
)This
;
781 return STATUS_SUCCESS
;