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 NTSTATUS
FindTransferDescriptorInEndpoint(IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
, IN ULONG TransferDescriptorLogicalAddress
, OUT POHCI_ENDPOINT_DESCRIPTOR
*OutEndpointDescriptor
, OUT POHCI_ENDPOINT_DESCRIPTOR
*OutPreviousEndpointDescriptor
);
47 NTSTATUS
FindTransferDescriptorInInterruptHeadEndpoints(IN ULONG TransferDescriptorLogicalAddress
, OUT POHCI_ENDPOINT_DESCRIPTOR
*OutEndpointDescriptor
, OUT POHCI_ENDPOINT_DESCRIPTOR
*OutPreviousEndpointDescriptor
);
49 VOID
CleanupEndpointDescriptor(POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
, POHCI_ENDPOINT_DESCRIPTOR PreviousEndpointDescriptor
);
50 POHCI_ENDPOINT_DESCRIPTOR
FindInterruptEndpointDescriptor(UCHAR InterruptInterval
);
51 VOID
PrintEndpointList(POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
);
52 VOID
LinkEndpoint(POHCI_ENDPOINT_DESCRIPTOR HeadEndpointDescriptor
, POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
);
54 // constructor / destructor
55 CUSBQueue(IUnknown
*OuterUnknown
){}
56 virtual ~CUSBQueue(){}
59 LONG m_Ref
; // reference count
60 KSPIN_LOCK m_Lock
; // list lock
61 PUSBHARDWAREDEVICE m_Hardware
; // hardware
62 POHCI_ENDPOINT_DESCRIPTOR m_BulkHeadEndpointDescriptor
; // bulk head descriptor
63 POHCI_ENDPOINT_DESCRIPTOR m_ControlHeadEndpointDescriptor
; // control head descriptor
64 POHCI_ENDPOINT_DESCRIPTOR m_IsoHeadEndpointDescriptor
; // isochronous head descriptor
65 POHCI_ENDPOINT_DESCRIPTOR
* m_InterruptEndpoints
;
68 //=================================================================================================
73 CUSBQueue::QueryInterface(
77 if (IsEqualGUIDAligned(refiid
, IID_IUnknown
))
79 *Output
= PVOID(PUNKNOWN(this));
80 PUNKNOWN(*Output
)->AddRef();
81 return STATUS_SUCCESS
;
84 return STATUS_UNSUCCESSFUL
;
88 CUSBQueue::Initialize(
89 IN PUSBHARDWAREDEVICE Hardware
,
90 IN PDMA_ADAPTER AdapterObject
,
91 IN PDMAMEMORYMANAGER MemManager
,
92 IN OPTIONAL PKSPIN_LOCK Lock
)
95 // get bulk endpoint descriptor
97 Hardware
->GetBulkHeadEndpointDescriptor(&m_BulkHeadEndpointDescriptor
);
100 // get control endpoint descriptor
102 Hardware
->GetControlHeadEndpointDescriptor(&m_ControlHeadEndpointDescriptor
);
105 Hardware
->GetIsochronousHeadEndpointDescriptor(&m_IsoHeadEndpointDescriptor
);
108 // get interrupt endpoints
110 Hardware
->GetInterruptEndpointDescriptors(&m_InterruptEndpoints
);
113 // initialize spinlock
115 KeInitializeSpinLock(&m_Lock
);
120 m_Hardware
= Hardware
;
122 return STATUS_SUCCESS
;
126 CUSBQueue::GetPendingRequestCount()
129 // Loop through the pending list and iterrate one for each QueueHead that
130 // has a IRP to complete.
137 CUSBQueue::LinkEndpoint(
138 POHCI_ENDPOINT_DESCRIPTOR HeadEndpointDescriptor
,
139 POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
)
141 POHCI_ENDPOINT_DESCRIPTOR CurrentEndpointDescriptor
= HeadEndpointDescriptor
;
144 // get last descriptor in queue
146 while(CurrentEndpointDescriptor
->NextDescriptor
)
149 // move to last descriptor
151 CurrentEndpointDescriptor
= (POHCI_ENDPOINT_DESCRIPTOR
)CurrentEndpointDescriptor
->NextDescriptor
;
157 CurrentEndpointDescriptor
->NextPhysicalEndpoint
= EndpointDescriptor
->PhysicalAddress
.LowPart
;
158 CurrentEndpointDescriptor
->NextDescriptor
= EndpointDescriptor
;
163 CUSBQueue::AddUSBRequest(
164 IUSBRequest
* Request
)
169 POHCI_ENDPOINT_DESCRIPTOR HeadDescriptor
;
170 POHCI_ENDPOINT_DESCRIPTOR Descriptor
;
171 POHCI_ISO_TD CurrentDescriptor
;
174 DPRINT("CUSBQueue::AddUSBRequest\n");
179 ASSERT(Request
!= NULL
);
184 Type
= Request
->GetTransferType();
187 // add extra reference which is released when the request is completed
192 // get transfer descriptors
194 Status
= Request
->GetEndpointDescriptor(&Descriptor
);
195 if (!NT_SUCCESS(Status
))
198 // failed to get transfer descriptor
200 DPRINT1("CUSBQueue::AddUSBRequest GetEndpointDescriptor failed with %x\n", Status
);
212 if (Type
== USB_ENDPOINT_TYPE_BULK
)
215 // get head descriptor
217 HeadDescriptor
= m_BulkHeadEndpointDescriptor
;
219 else if (Type
== USB_ENDPOINT_TYPE_CONTROL
)
222 // get head descriptor
224 HeadDescriptor
= m_ControlHeadEndpointDescriptor
;
226 else if (Type
== USB_ENDPOINT_TYPE_INTERRUPT
)
229 // get head descriptor
231 HeadDescriptor
= FindInterruptEndpointDescriptor(Request
->GetInterval());
232 ASSERT(HeadDescriptor
);
234 else if (Type
== USB_ENDPOINT_TYPE_ISOCHRONOUS
)
237 // get head descriptor
239 HeadDescriptor
= m_IsoHeadEndpointDescriptor
;
242 // get current frame number
244 m_Hardware
->GetCurrentFrameNumber(&FrameNumber
);
247 // increment frame number
252 // apply frame number to iso transfer descriptors
254 CurrentDescriptor
= (POHCI_ISO_TD
)Descriptor
->HeadLogicalDescriptor
;
256 DPRINT1("ISO: NextFrameNumber %x\n", FrameNumber
);
258 while(CurrentDescriptor
)
261 // set current frame number
263 CurrentDescriptor
->Flags
|= OHCI_ITD_SET_STARTING_FRAME(FrameNumber
);
266 // move to next frame number
271 // move to next descriptor
273 CurrentDescriptor
= CurrentDescriptor
->NextLogicalDescriptor
;
278 // insert endpoint at end
280 LinkEndpoint(HeadDescriptor
, Descriptor
);
283 // set descriptor active
285 Descriptor
->Flags
&= ~OHCI_ENDPOINT_SKIP
;
287 if (Type
== USB_ENDPOINT_TYPE_CONTROL
|| Type
== USB_ENDPOINT_TYPE_BULK
)
290 // notify hardware of our request
292 m_Hardware
->HeadEndpointDescriptorModified(Type
);
296 return STATUS_SUCCESS
;
300 CUSBQueue::CancelRequests()
303 return STATUS_NOT_IMPLEMENTED
;
307 CUSBQueue::CreateUSBRequest(
308 IUSBRequest
**OutRequest
)
310 PUSBREQUEST UsbRequest
;
314 Status
= InternalCreateUSBRequest(&UsbRequest
);
316 if (NT_SUCCESS(Status
))
318 *OutRequest
= UsbRequest
;
325 CUSBQueue::FindTransferDescriptorInEndpoint(
326 IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
,
327 IN ULONG TransferDescriptorLogicalAddress
,
328 OUT POHCI_ENDPOINT_DESCRIPTOR
*OutEndpointDescriptor
,
329 OUT POHCI_ENDPOINT_DESCRIPTOR
*OutPreviousEndpointDescriptor
)
331 POHCI_ENDPOINT_DESCRIPTOR LastDescriptor
= EndpointDescriptor
;
335 // skip first endpoint head
337 EndpointDescriptor
= (POHCI_ENDPOINT_DESCRIPTOR
)EndpointDescriptor
->NextDescriptor
;
339 while(EndpointDescriptor
)
342 // check if the transfer descriptor is inside the list
344 if (IsTransferDescriptorInEndpoint(EndpointDescriptor
, TransferDescriptorLogicalAddress
))
349 *OutEndpointDescriptor
= EndpointDescriptor
;
350 *OutPreviousEndpointDescriptor
= LastDescriptor
;
355 return STATUS_SUCCESS
;
359 // store last endpoint
361 LastDescriptor
= EndpointDescriptor
;
366 EndpointDescriptor
= (POHCI_ENDPOINT_DESCRIPTOR
)EndpointDescriptor
->NextDescriptor
;
370 // failed to endpoint
372 return STATUS_NOT_FOUND
;
376 CUSBQueue::FindTransferDescriptorInInterruptHeadEndpoints(IN ULONG TransferDescriptorLogicalAddress
, OUT POHCI_ENDPOINT_DESCRIPTOR
*OutEndpointDescriptor
, OUT POHCI_ENDPOINT_DESCRIPTOR
*OutPreviousEndpointDescriptor
)
382 // search descriptor in endpoint list
384 for(Index
= 0; Index
< OHCI_STATIC_ENDPOINT_COUNT
; Index
++)
387 // is it in current endpoint
389 Status
= FindTransferDescriptorInEndpoint(m_InterruptEndpoints
[Index
], TransferDescriptorLogicalAddress
, OutEndpointDescriptor
, OutPreviousEndpointDescriptor
);
390 if (NT_SUCCESS(Status
))
393 // found transfer descriptor
395 return STATUS_SUCCESS
;
402 return STATUS_NOT_FOUND
;
406 CUSBQueue::IsTransferDescriptorInEndpoint(
407 IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
,
408 IN ULONG TransferDescriptorLogicalAddress
)
410 POHCI_GENERAL_TD Descriptor
;
413 // get first general transfer descriptor
415 Descriptor
= (POHCI_GENERAL_TD
)EndpointDescriptor
->HeadLogicalDescriptor
;
424 if (Descriptor
->PhysicalAddress
.LowPart
== TransferDescriptorLogicalAddress
)
435 Descriptor
= (POHCI_GENERAL_TD
)Descriptor
->NextLogicalDescriptor
;
440 // no descriptor found
446 CUSBQueue::CleanupEndpointDescriptor(
447 POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
,
448 POHCI_ENDPOINT_DESCRIPTOR PreviousEndpointDescriptor
)
453 // FIXME: verify unlinking process
455 PreviousEndpointDescriptor
->NextDescriptor
= EndpointDescriptor
->NextDescriptor
;
456 PreviousEndpointDescriptor
->NextPhysicalEndpoint
= EndpointDescriptor
->NextPhysicalEndpoint
;
459 // get corresponding request
461 Request
= PUSBREQUEST(EndpointDescriptor
->Request
);
465 // notify of completion
467 Request
->CompletionCallback(EndpointDescriptor
);
470 // free endpoint descriptor
472 Request
->FreeEndpointDescriptor(EndpointDescriptor
);
475 // FIXME: check if complete
477 //ASSERT(Request->IsRequestComplete());
486 CUSBQueue::PrintEndpointList(
487 POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
)
489 DPRINT1("CUSBQueue::PrintEndpointList HeadEndpoint %p Logical %x\n", EndpointDescriptor
, EndpointDescriptor
->PhysicalAddress
.LowPart
);
492 // get first general transfer descriptor
494 EndpointDescriptor
= (POHCI_ENDPOINT_DESCRIPTOR
)EndpointDescriptor
->NextDescriptor
;
496 while(EndpointDescriptor
)
498 DPRINT1(" CUSBQueue::PrintEndpointList Endpoint %p Logical %x\n", EndpointDescriptor
, EndpointDescriptor
->PhysicalAddress
.LowPart
);
503 EndpointDescriptor
= (POHCI_ENDPOINT_DESCRIPTOR
)EndpointDescriptor
->NextDescriptor
;
508 CUSBQueue::TransferDescriptorCompletionCallback(
509 ULONG TransferDescriptorLogicalAddress
)
511 POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
, PreviousEndpointDescriptor
;
514 DPRINT("CUSBQueue::TransferDescriptorCompletionCallback transfer descriptor %x\n", TransferDescriptorLogicalAddress
);
517 // find transfer descriptor in control list
519 Status
= FindTransferDescriptorInEndpoint(m_ControlHeadEndpointDescriptor
, TransferDescriptorLogicalAddress
, &EndpointDescriptor
, &PreviousEndpointDescriptor
);
520 if (NT_SUCCESS(Status
))
525 CleanupEndpointDescriptor(EndpointDescriptor
, PreviousEndpointDescriptor
);
534 // find transfer descriptor in bulk list
536 Status
= FindTransferDescriptorInEndpoint(m_BulkHeadEndpointDescriptor
, TransferDescriptorLogicalAddress
, &EndpointDescriptor
, &PreviousEndpointDescriptor
);
537 if (NT_SUCCESS(Status
))
542 CleanupEndpointDescriptor(EndpointDescriptor
, PreviousEndpointDescriptor
);
551 // find transfer descriptor in interrupt list
553 Status
= FindTransferDescriptorInInterruptHeadEndpoints(TransferDescriptorLogicalAddress
, &EndpointDescriptor
, &PreviousEndpointDescriptor
);
554 if (NT_SUCCESS(Status
))
559 CleanupEndpointDescriptor(EndpointDescriptor
, PreviousEndpointDescriptor
);
569 // hardware reported dead endpoint completed
571 DPRINT("CUSBQueue::TransferDescriptorCompletionCallback invalid transfer descriptor %x\n", TransferDescriptorLogicalAddress
);
576 POHCI_ENDPOINT_DESCRIPTOR
577 CUSBQueue::FindInterruptEndpointDescriptor(
578 UCHAR InterruptInterval
)
586 ASSERT(InterruptInterval
< OHCI_BIGGEST_INTERVAL
);
589 // find interrupt index
591 while (Power
<= OHCI_BIGGEST_INTERVAL
/ 2)
594 // is current interval greater
596 if (Power
* 2 > InterruptInterval
)
605 // move to next interrupt
610 DPRINT("InterruptInterval %lu Selected InterruptIndex %lu Choosen Interval %lu\n", InterruptInterval
, Index
, Power
);
615 return m_InterruptEndpoints
[Index
];
620 PUSBQUEUE
*OutUsbQueue
)
625 // allocate controller
627 This
= new(NonPagedPool
, TAG_USBOHCI
) CUSBQueue(0);
631 // failed to allocate
633 return STATUS_INSUFFICIENT_RESOURCES
;
637 // add reference count
644 *OutUsbQueue
= (PUSBQUEUE
)This
;
649 return STATUS_SUCCESS
;