[USBEHCI_NEW]
[reactos.git] / drivers / usb / usbehci_new / usb_queue.cpp
1 /*
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.
6 * PROGRAMMERS:
7 * Michael Martin (michael.martin@reactos.org)
8 * Johannes Anderwald (johannes.anderwald@reactos.org)
9 */
10
11 #include "usbehci.h"
12 #include "hardware.h"
13
14 class CUSBQueue : public IUSBQueue
15 {
16 public:
17 STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);
18
19 STDMETHODIMP_(ULONG) AddRef()
20 {
21 InterlockedIncrement(&m_Ref);
22 return m_Ref;
23 }
24 STDMETHODIMP_(ULONG) Release()
25 {
26 InterlockedDecrement(&m_Ref);
27
28 if (!m_Ref)
29 {
30 delete this;
31 return 0;
32 }
33 return m_Ref;
34 }
35
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);
42
43 // constructor / destructor
44 CUSBQueue(IUnknown *OuterUnknown){}
45 virtual ~CUSBQueue(){}
46
47 protected:
48 LONG m_Ref;
49 KSPIN_LOCK m_Lock;
50 PDMA_ADAPTER m_Adapter;
51 PVOID VirtualBase;
52 PHYSICAL_ADDRESS PhysicalAddress;
53 PQUEUE_HEAD AsyncQueueHead;
54 PQUEUE_HEAD PendingQueueHead;
55 IDMAMemoryManager *m_MemoryManager;
56
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);
63 };
64
65 //=================================================================================================
66 // COM
67 //
68 NTSTATUS
69 STDMETHODCALLTYPE
70 CUSBQueue::QueryInterface(
71 IN REFIID refiid,
72 OUT PVOID* Output)
73 {
74 if (IsEqualGUIDAligned(refiid, IID_IUnknown))
75 {
76 *Output = PVOID(PUNKNOWN(this));
77 PUNKNOWN(*Output)->AddRef();
78 return STATUS_SUCCESS;
79 }
80
81 return STATUS_UNSUCCESSFUL;
82 }
83
84 NTSTATUS
85 CUSBQueue::Initialize(
86 IN PUSBHARDWAREDEVICE Hardware,
87 PDMA_ADAPTER AdapterObject,
88 IN OPTIONAL PKSPIN_LOCK Lock)
89 {
90 NTSTATUS Status;
91
92 DPRINT1("CUSBQueue::Initialize()\n");
93
94 ASSERT(Hardware);
95 ASSERT(AdapterObject);
96
97 //
98 // Create Common Buffer
99 //
100 VirtualBase = AdapterObject->DmaOperations->AllocateCommonBuffer(AdapterObject,
101 PAGE_SIZE * 4,
102 &PhysicalAddress,
103 FALSE);
104 if (!VirtualBase)
105 {
106 DPRINT1("Failed to allocate a common buffer\n");
107 return STATUS_INSUFFICIENT_RESOURCES;
108 }
109
110 //
111 // Create DMAMemoryManager for use with QueueHeads and Transfer Descriptors.
112 //
113 Status = CreateDMAMemoryManager(&m_MemoryManager);
114 if (!NT_SUCCESS(Status))
115 {
116 DPRINT1("Failed to create DMAMemoryManager Object\n");
117 return Status;
118 }
119
120 //
121 // initialize device lock
122 //
123 KeInitializeSpinLock(&m_Lock);
124
125 //
126 // Initialize the DMAMemoryManager
127 //
128 Status = m_MemoryManager->Initialize(Hardware, &m_Lock, PAGE_SIZE * 4, VirtualBase, PhysicalAddress, 32);
129 if (!NT_SUCCESS(Status))
130 {
131 DPRINT1("Failed to initialize the DMAMemoryManager\n");
132 return Status;
133 }
134
135 //
136 // Create a QueueHead for use in Async Register
137 //
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;
144
145 Hardware->SetAsyncListRegister(AsyncQueueHead->PhysicalAddr);
146
147 //
148 // Create a Unused QueueHead to hold pending QueueHeads
149 //
150 PendingQueueHead = CreateQueueHead();
151 PendingQueueHead->Token.Bits.Halted = TRUE;
152
153 //
154 // Initialize ListHead in QueueHeads
155 //
156 InitializeListHead(&AsyncQueueHead->LinkedQueueHeads);
157 InitializeListHead(&PendingQueueHead->LinkedQueueHeads);
158
159 return STATUS_SUCCESS;
160 }
161
162 ULONG
163 CUSBQueue::GetPendingRequestCount()
164 {
165 UNIMPLEMENTED
166 return 0;
167 }
168
169 NTSTATUS
170 CUSBQueue::AddUSBRequest(
171 IUSBRequest * Request)
172 {
173 UNIMPLEMENTED
174 return STATUS_NOT_IMPLEMENTED;
175 }
176
177 NTSTATUS
178 CUSBQueue::AddUSBRequest(
179 PURB Urb)
180 {
181 UNIMPLEMENTED
182 return STATUS_NOT_IMPLEMENTED;
183 }
184
185 NTSTATUS
186 CUSBQueue::CancelRequests()
187 {
188 UNIMPLEMENTED
189 return STATUS_NOT_IMPLEMENTED;
190 }
191
192 NTSTATUS
193 CUSBQueue::CreateUSBRequest(
194 IUSBRequest **OutRequest)
195 {
196 UNIMPLEMENTED
197 return STATUS_NOT_IMPLEMENTED;
198 }
199
200 PQUEUE_HEAD
201 CUSBQueue::CreateQueueHead()
202 {
203 PQUEUE_HEAD QueueHead;
204 PHYSICAL_ADDRESS PhysicalAddress;
205 NTSTATUS Status;
206 //
207 // Create the QueueHead from Common Buffer
208 //
209 Status = m_MemoryManager->Allocate(sizeof(QUEUE_HEAD),
210 (PVOID*)&QueueHead,
211 &PhysicalAddress);
212 if (!NT_SUCCESS(Status))
213 return NULL;
214
215 //
216 // Initialize default values
217 //
218
219 QueueHead->PhysicalAddr = PhysicalAddress.LowPart;
220 QueueHead->HorizontalLinkPointer = TERMINATE_POINTER;
221 QueueHead->AlternateNextPointer = TERMINATE_POINTER;
222 QueueHead->NextPointer = TERMINATE_POINTER;
223
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;
228
229 // Set NakCountReload to max value possible
230 QueueHead->EndPointCharacteristics.NakCountReload = 0xF;
231
232 // Get the Initial Data Toggle from the Queue Element Desriptor
233 QueueHead->EndPointCharacteristics.QEDTDataToggleControl = FALSE;
234
235 QueueHead->EndPointCharacteristics.EndPointSpeed = QH_ENDPOINT_HIGHSPEED;
236
237 QueueHead->EndPointCapabilities.NumberOfTransactionPerFrame = 0x03;
238
239 // Interrupt when QueueHead is processed
240 QueueHead->Token.Bits.InterruptOnComplete = FALSE;
241
242 return QueueHead;
243 }
244
245 PQUEUE_TRANSFER_DESCRIPTOR
246 CUSBQueue::CreateDescriptor(
247 UCHAR PIDCode,
248 ULONG TotalBytesToTransfer)
249 {
250 PQUEUE_TRANSFER_DESCRIPTOR Descriptor;
251 PHYSICAL_ADDRESS PhysicalAddress;
252 NTSTATUS Status;
253
254 //
255 // Create the Descriptor from Common Buffer
256 //
257 Status = m_MemoryManager->Allocate(sizeof(QUEUE_TRANSFER_DESCRIPTOR),
258 (PVOID*)&Descriptor,
259 &PhysicalAddress);
260 if (!NT_SUCCESS(Status))
261 return NULL;
262
263 //
264 // Set default values
265 //
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;
274
275 return Descriptor;
276 }
277
278 //
279 // LinkQueueHead - Links one QueueHead to the end of HeadQueueHead list, updating HorizontalLinkPointer.
280 //
281 VOID
282 CUSBQueue::LinkQueueHead(
283 PQUEUE_HEAD HeadQueueHead,
284 PQUEUE_HEAD NewQueueHead)
285 {
286 PQUEUE_HEAD LastQueueHead, NextQueueHead;
287 PLIST_ENTRY Entry;
288 ASSERT(HeadQueueHead);
289 ASSERT(NewQueueHead);
290
291 //
292 // Link the LIST_ENTRYs
293 //
294 InsertTailList(&HeadQueueHead->LinkedQueueHeads, &NewQueueHead->LinkedQueueHeads);
295
296 //
297 // Update HLP for Previous QueueHead, which should be the last in list.
298 //
299 Entry = NewQueueHead->LinkedQueueHeads.Blink;
300 LastQueueHead = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
301 LastQueueHead->HorizontalLinkPointer = (NewQueueHead->PhysicalAddr | QH_TYPE_QH);
302
303 //
304 // Update HLP for NewQueueHead to point to next, which should be the HeadQueueHead
305 //
306 Entry = NewQueueHead->LinkedQueueHeads.Flink;
307 NextQueueHead = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
308 ASSERT(NextQueueHead == HeadQueueHead);
309 NewQueueHead->HorizontalLinkPointer = NextQueueHead->PhysicalAddr;
310 }
311
312 //
313 // UnlinkQueueHead - Unlinks one QueueHead, updating HorizontalLinkPointer.
314 //
315 VOID
316 CUSBQueue::UnlinkQueueHead(
317 PQUEUE_HEAD QueueHead)
318 {
319 PQUEUE_HEAD PreviousQH, NextQH;
320 PLIST_ENTRY Entry;
321
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;
328
329 RemoveEntryList(&QueueHead->LinkedQueueHeads);
330 }
331
332 //
333 // LinkQueueHeadChain - Links a list of QueueHeads to the HeadQueueHead list, updating HorizontalLinkPointer.
334 //
335 VOID
336 CUSBQueue::LinkQueueHeadChain(
337 PQUEUE_HEAD HeadQueueHead,
338 PQUEUE_HEAD NewQueueHead)
339 {
340 PQUEUE_HEAD LastQueueHead;
341 PLIST_ENTRY Entry;
342 ASSERT(HeadQueueHead);
343 ASSERT(NewQueueHead);
344
345 //
346 // Find the last QueueHead in NewQueueHead
347 //
348 Entry = NewQueueHead->LinkedQueueHeads.Blink;
349 ASSERT(Entry != NewQueueHead->LinkedQueueHeads.Flink);
350 LastQueueHead = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
351
352 //
353 // Set the LinkPointer and Flink
354 //
355 LastQueueHead->HorizontalLinkPointer = HeadQueueHead->PhysicalAddr | QH_TYPE_QH;
356 LastQueueHead->LinkedQueueHeads.Flink = &HeadQueueHead->LinkedQueueHeads;
357
358 //
359 // Fine the last QueueHead in HeadQueueHead
360 //
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;
366 }
367
368 //
369 // UnlinkQueueHeadChain - Unlinks a list number of QueueHeads from HeadQueueHead list, updating HorizontalLinkPointer.
370 // returns the chain of QueueHeads removed from HeadQueueHead.
371 //
372 PQUEUE_HEAD
373 CUSBQueue::UnlinkQueueHeadChain(
374 PQUEUE_HEAD HeadQueueHead,
375 ULONG Count)
376 {
377 PQUEUE_HEAD LastQueueHead, FirstQueueHead;
378 PLIST_ENTRY Entry;
379 ULONG Index;
380
381 //
382 // Find the last QueueHead in NewQueueHead
383 //
384 Entry = &HeadQueueHead->LinkedQueueHeads;
385 FirstQueueHead = CONTAINING_RECORD(Entry->Flink, QUEUE_HEAD, LinkedQueueHeads);
386
387 for (Index = 0; Index < Count; Index++)
388 {
389 Entry = Entry->Flink;
390
391 if (Entry == &HeadQueueHead->LinkedQueueHeads)
392 {
393 DPRINT1("Warnnig; Only %d QueueHeads in HeadQueueHead\n", Index);
394 Count = Index + 1;
395 break;
396 }
397 }
398
399 LastQueueHead = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
400 HeadQueueHead->LinkedQueueHeads.Flink = LastQueueHead->LinkedQueueHeads.Flink;
401 if (Count + 1 == Index)
402 {
403 HeadQueueHead->LinkedQueueHeads.Blink = &HeadQueueHead->LinkedQueueHeads;
404 }
405 else
406 HeadQueueHead->LinkedQueueHeads.Blink = LastQueueHead->LinkedQueueHeads.Flink;
407
408 FirstQueueHead->LinkedQueueHeads.Blink = &LastQueueHead->LinkedQueueHeads;
409 LastQueueHead->LinkedQueueHeads.Flink = &FirstQueueHead->LinkedQueueHeads;
410 LastQueueHead->HorizontalLinkPointer = TERMINATE_POINTER;
411 return FirstQueueHead;
412 }
413
414 NTSTATUS
415 CreateUSBQueue(
416 PUSBQUEUE *OutUsbQueue)
417 {
418 PUSBQUEUE This;
419
420 //
421 // allocate controller
422 //
423 This = new(NonPagedPool, TAG_USBEHCI) CUSBQueue(0);
424 if (!This)
425 {
426 //
427 // failed to allocate
428 //
429 return STATUS_INSUFFICIENT_RESOURCES;
430 }
431
432 //
433 // add reference count
434 //
435 This->AddRef();
436
437 //
438 // return result
439 //
440 *OutUsbQueue = (PUSBQUEUE)This;
441
442 //
443 // done
444 //
445 return STATUS_SUCCESS;
446 }