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/usbehci/usb_queue.cpp
5 * PURPOSE: USB EHCI 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
);
36 NTSTATUS
Initialize(IN PUSBHARDWAREDEVICE Hardware
, PDMA_ADAPTER AdapterObject
, IN OPTIONAL PKSPIN_LOCK Lock
);
37 ULONG
GetPendingRequestCount();
38 NTSTATUS
AddUSBRequest(PURB Urb
);
39 NTSTATUS
AddUSBRequest(IUSBRequest
* Request
);
40 NTSTATUS
CancelRequests();
41 NTSTATUS
CreateUSBRequest(IUSBRequest
**OutRequest
);
43 // constructor / destructor
44 CUSBQueue(IUnknown
*OuterUnknown
){}
45 virtual ~CUSBQueue(){}
50 PDMA_ADAPTER m_Adapter
;
52 PHYSICAL_ADDRESS PhysicalAddress
;
53 PQUEUE_HEAD AsyncQueueHead
;
54 PQUEUE_HEAD PendingQueueHead
;
55 IDMAMemoryManager
*m_MemoryManager
;
57 PQUEUE_HEAD
CreateQueueHead();
58 PQUEUE_TRANSFER_DESCRIPTOR
CreateDescriptor(UCHAR PIDCode
, ULONG TotalBytesToTransfer
);
59 VOID
LinkQueueHead(PQUEUE_HEAD HeadQueueHead
, PQUEUE_HEAD NewQueueHead
);
60 VOID
UnlinkQueueHead(PQUEUE_HEAD QueueHead
);
61 VOID
LinkQueueHeadChain(PQUEUE_HEAD HeadQueueHead
, PQUEUE_HEAD NewQueueHead
);
62 PQUEUE_HEAD
UnlinkQueueHeadChain(PQUEUE_HEAD HeadQueueHead
, ULONG Count
);
65 //=================================================================================================
70 CUSBQueue::QueryInterface(
74 if (IsEqualGUIDAligned(refiid
, IID_IUnknown
))
76 *Output
= PVOID(PUNKNOWN(this));
77 PUNKNOWN(*Output
)->AddRef();
78 return STATUS_SUCCESS
;
81 return STATUS_UNSUCCESSFUL
;
85 CUSBQueue::Initialize(
86 IN PUSBHARDWAREDEVICE Hardware
,
87 PDMA_ADAPTER AdapterObject
,
88 IN OPTIONAL PKSPIN_LOCK Lock
)
92 DPRINT1("CUSBQueue::Initialize()\n");
95 ASSERT(AdapterObject
);
98 // Create Common Buffer
100 VirtualBase
= AdapterObject
->DmaOperations
->AllocateCommonBuffer(AdapterObject
,
106 DPRINT1("Failed to allocate a common buffer\n");
107 return STATUS_INSUFFICIENT_RESOURCES
;
111 // Create DMAMemoryManager for use with QueueHeads and Transfer Descriptors.
113 Status
= CreateDMAMemoryManager(&m_MemoryManager
);
114 if (!NT_SUCCESS(Status
))
116 DPRINT1("Failed to create DMAMemoryManager Object\n");
121 // initialize device lock
123 KeInitializeSpinLock(&m_Lock
);
126 // Initialize the DMAMemoryManager
128 Status
= m_MemoryManager
->Initialize(Hardware
, &m_Lock
, PAGE_SIZE
* 4, VirtualBase
, PhysicalAddress
, 32);
129 if (!NT_SUCCESS(Status
))
131 DPRINT1("Failed to initialize the DMAMemoryManager\n");
136 // Create a QueueHead for use in Async Register
138 AsyncQueueHead
= CreateQueueHead();
139 AsyncQueueHead
->HorizontalLinkPointer
= AsyncQueueHead
->PhysicalAddr
| QH_TYPE_QH
;
140 AsyncQueueHead
->EndPointCharacteristics
.QEDTDataToggleControl
= FALSE
;
141 AsyncQueueHead
->Token
.Bits
.InterruptOnComplete
= FALSE
;
142 AsyncQueueHead
->EndPointCharacteristics
.HeadOfReclamation
= TRUE
;
143 AsyncQueueHead
->Token
.Bits
.Halted
= TRUE
;
145 Hardware
->SetAsyncListRegister(AsyncQueueHead
->PhysicalAddr
);
148 // Create a Unused QueueHead to hold pending QueueHeads
150 PendingQueueHead
= CreateQueueHead();
151 PendingQueueHead
->Token
.Bits
.Halted
= TRUE
;
154 // Initialize ListHead in QueueHeads
156 InitializeListHead(&AsyncQueueHead
->LinkedQueueHeads
);
157 InitializeListHead(&PendingQueueHead
->LinkedQueueHeads
);
159 return STATUS_SUCCESS
;
163 CUSBQueue::GetPendingRequestCount()
170 CUSBQueue::AddUSBRequest(
171 IUSBRequest
* Request
)
174 return STATUS_NOT_IMPLEMENTED
;
178 CUSBQueue::AddUSBRequest(
182 return STATUS_NOT_IMPLEMENTED
;
186 CUSBQueue::CancelRequests()
189 return STATUS_NOT_IMPLEMENTED
;
193 CUSBQueue::CreateUSBRequest(
194 IUSBRequest
**OutRequest
)
197 return STATUS_NOT_IMPLEMENTED
;
201 CUSBQueue::CreateQueueHead()
203 PQUEUE_HEAD QueueHead
;
204 PHYSICAL_ADDRESS PhysicalAddress
;
207 // Create the QueueHead from Common Buffer
209 Status
= m_MemoryManager
->Allocate(sizeof(QUEUE_HEAD
),
212 if (!NT_SUCCESS(Status
))
216 // Initialize default values
219 QueueHead
->PhysicalAddr
= PhysicalAddress
.LowPart
;
220 QueueHead
->HorizontalLinkPointer
= TERMINATE_POINTER
;
221 QueueHead
->AlternateNextPointer
= TERMINATE_POINTER
;
222 QueueHead
->NextPointer
= TERMINATE_POINTER
;
224 // 1 for non high speed, 0 for high speed device
225 QueueHead
->EndPointCharacteristics
.ControlEndPointFlag
= 0;
226 QueueHead
->EndPointCharacteristics
.HeadOfReclamation
= FALSE
;
227 QueueHead
->EndPointCharacteristics
.MaximumPacketLength
= 64;
229 // Set NakCountReload to max value possible
230 QueueHead
->EndPointCharacteristics
.NakCountReload
= 0xF;
232 // Get the Initial Data Toggle from the Queue Element Desriptor
233 QueueHead
->EndPointCharacteristics
.QEDTDataToggleControl
= FALSE
;
235 QueueHead
->EndPointCharacteristics
.EndPointSpeed
= QH_ENDPOINT_HIGHSPEED
;
237 QueueHead
->EndPointCapabilities
.NumberOfTransactionPerFrame
= 0x03;
239 // Interrupt when QueueHead is processed
240 QueueHead
->Token
.Bits
.InterruptOnComplete
= FALSE
;
245 PQUEUE_TRANSFER_DESCRIPTOR
246 CUSBQueue::CreateDescriptor(
248 ULONG TotalBytesToTransfer
)
250 PQUEUE_TRANSFER_DESCRIPTOR Descriptor
;
251 PHYSICAL_ADDRESS PhysicalAddress
;
255 // Create the Descriptor from Common Buffer
257 Status
= m_MemoryManager
->Allocate(sizeof(QUEUE_TRANSFER_DESCRIPTOR
),
260 if (!NT_SUCCESS(Status
))
264 // Set default values
266 Descriptor
->NextPointer
= TERMINATE_POINTER
;
267 Descriptor
->AlternateNextPointer
= TERMINATE_POINTER
;
268 Descriptor
->Token
.Bits
.DataToggle
= TRUE
;
269 Descriptor
->Token
.Bits
.ErrorCounter
= 0x03;
270 Descriptor
->Token
.Bits
.Active
= TRUE
;
271 Descriptor
->Token
.Bits
.PIDCode
= PIDCode
;
272 Descriptor
->Token
.Bits
.TotalBytesToTransfer
= TotalBytesToTransfer
;
273 Descriptor
->PhysicalAddr
= PhysicalAddress
.LowPart
;
279 // LinkQueueHead - Links one QueueHead to the end of HeadQueueHead list, updating HorizontalLinkPointer.
282 CUSBQueue::LinkQueueHead(
283 PQUEUE_HEAD HeadQueueHead
,
284 PQUEUE_HEAD NewQueueHead
)
286 PQUEUE_HEAD LastQueueHead
, NextQueueHead
;
288 ASSERT(HeadQueueHead
);
289 ASSERT(NewQueueHead
);
292 // Link the LIST_ENTRYs
294 InsertTailList(&HeadQueueHead
->LinkedQueueHeads
, &NewQueueHead
->LinkedQueueHeads
);
297 // Update HLP for Previous QueueHead, which should be the last in list.
299 Entry
= NewQueueHead
->LinkedQueueHeads
.Blink
;
300 LastQueueHead
= CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
301 LastQueueHead
->HorizontalLinkPointer
= (NewQueueHead
->PhysicalAddr
| QH_TYPE_QH
);
304 // Update HLP for NewQueueHead to point to next, which should be the HeadQueueHead
306 Entry
= NewQueueHead
->LinkedQueueHeads
.Flink
;
307 NextQueueHead
= CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
308 ASSERT(NextQueueHead
== HeadQueueHead
);
309 NewQueueHead
->HorizontalLinkPointer
= NextQueueHead
->PhysicalAddr
;
313 // UnlinkQueueHead - Unlinks one QueueHead, updating HorizontalLinkPointer.
316 CUSBQueue::UnlinkQueueHead(
317 PQUEUE_HEAD QueueHead
)
319 PQUEUE_HEAD PreviousQH
, NextQH
;
322 Entry
= QueueHead
->LinkedQueueHeads
.Blink
;
323 PreviousQH
= CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
324 Entry
= QueueHead
->LinkedQueueHeads
.Flink
;
325 NextQH
= CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
326 ASSERT(QueueHead
->HorizontalLinkPointer
== (NextQH
->PhysicalAddr
| QH_TYPE_QH
));
327 PreviousQH
->HorizontalLinkPointer
= NextQH
->PhysicalAddr
| QH_TYPE_QH
;
329 RemoveEntryList(&QueueHead
->LinkedQueueHeads
);
333 // LinkQueueHeadChain - Links a list of QueueHeads to the HeadQueueHead list, updating HorizontalLinkPointer.
336 CUSBQueue::LinkQueueHeadChain(
337 PQUEUE_HEAD HeadQueueHead
,
338 PQUEUE_HEAD NewQueueHead
)
340 PQUEUE_HEAD LastQueueHead
;
342 ASSERT(HeadQueueHead
);
343 ASSERT(NewQueueHead
);
346 // Find the last QueueHead in NewQueueHead
348 Entry
= NewQueueHead
->LinkedQueueHeads
.Blink
;
349 ASSERT(Entry
!= NewQueueHead
->LinkedQueueHeads
.Flink
);
350 LastQueueHead
= CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
353 // Set the LinkPointer and Flink
355 LastQueueHead
->HorizontalLinkPointer
= HeadQueueHead
->PhysicalAddr
| QH_TYPE_QH
;
356 LastQueueHead
->LinkedQueueHeads
.Flink
= &HeadQueueHead
->LinkedQueueHeads
;
359 // Fine the last QueueHead in HeadQueueHead
361 Entry
= HeadQueueHead
->LinkedQueueHeads
.Blink
;
362 HeadQueueHead
->LinkedQueueHeads
.Blink
= &LastQueueHead
->LinkedQueueHeads
;
363 LastQueueHead
= CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
364 LastQueueHead
->LinkedQueueHeads
.Flink
= &NewQueueHead
->LinkedQueueHeads
;
365 LastQueueHead
->HorizontalLinkPointer
= NewQueueHead
->PhysicalAddr
| QH_TYPE_QH
;
369 // UnlinkQueueHeadChain - Unlinks a list number of QueueHeads from HeadQueueHead list, updating HorizontalLinkPointer.
370 // returns the chain of QueueHeads removed from HeadQueueHead.
373 CUSBQueue::UnlinkQueueHeadChain(
374 PQUEUE_HEAD HeadQueueHead
,
377 PQUEUE_HEAD LastQueueHead
, FirstQueueHead
;
382 // Find the last QueueHead in NewQueueHead
384 Entry
= &HeadQueueHead
->LinkedQueueHeads
;
385 FirstQueueHead
= CONTAINING_RECORD(Entry
->Flink
, QUEUE_HEAD
, LinkedQueueHeads
);
387 for (Index
= 0; Index
< Count
; Index
++)
389 Entry
= Entry
->Flink
;
391 if (Entry
== &HeadQueueHead
->LinkedQueueHeads
)
393 DPRINT1("Warnnig; Only %d QueueHeads in HeadQueueHead\n", Index
);
399 LastQueueHead
= CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
400 HeadQueueHead
->LinkedQueueHeads
.Flink
= LastQueueHead
->LinkedQueueHeads
.Flink
;
401 if (Count
+ 1 == Index
)
403 HeadQueueHead
->LinkedQueueHeads
.Blink
= &HeadQueueHead
->LinkedQueueHeads
;
406 HeadQueueHead
->LinkedQueueHeads
.Blink
= LastQueueHead
->LinkedQueueHeads
.Flink
;
408 FirstQueueHead
->LinkedQueueHeads
.Blink
= &LastQueueHead
->LinkedQueueHeads
;
409 LastQueueHead
->LinkedQueueHeads
.Flink
= &FirstQueueHead
->LinkedQueueHeads
;
410 LastQueueHead
->HorizontalLinkPointer
= TERMINATE_POINTER
;
411 return FirstQueueHead
;
416 PUSBQUEUE
*OutUsbQueue
)
421 // allocate controller
423 This
= new(NonPagedPool
, TAG_USBEHCI
) CUSBQueue(0);
427 // failed to allocate
429 return STATUS_INSUFFICIENT_RESOURCES
;
433 // add reference count
440 *OutUsbQueue
= (PUSBQUEUE
)This
;
445 return STATUS_SUCCESS
;