[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 PQUEUE_HEAD AsyncListQueueHead;
52 PQUEUE_HEAD PendingListQueueHead;
53
54 VOID LinkQueueHead(PQUEUE_HEAD HeadQueueHead, PQUEUE_HEAD NewQueueHead);
55 VOID UnlinkQueueHead(PQUEUE_HEAD QueueHead);
56 VOID LinkQueueHeadChain(PQUEUE_HEAD HeadQueueHead, PQUEUE_HEAD NewQueueHead);
57 PQUEUE_HEAD UnlinkQueueHeadChain(PQUEUE_HEAD HeadQueueHead, ULONG Count);
58 };
59
60 //=================================================================================================
61 // COM
62 //
63 NTSTATUS
64 STDMETHODCALLTYPE
65 CUSBQueue::QueryInterface(
66 IN REFIID refiid,
67 OUT PVOID* Output)
68 {
69 if (IsEqualGUIDAligned(refiid, IID_IUnknown))
70 {
71 *Output = PVOID(PUNKNOWN(this));
72 PUNKNOWN(*Output)->AddRef();
73 return STATUS_SUCCESS;
74 }
75
76 return STATUS_UNSUCCESSFUL;
77 }
78
79 NTSTATUS
80 CUSBQueue::Initialize(
81 IN PUSBHARDWAREDEVICE Hardware,
82 PDMA_ADAPTER AdapterObject,
83 IN OPTIONAL PKSPIN_LOCK Lock)
84 {
85 NTSTATUS Status = STATUS_SUCCESS;
86
87 DPRINT1("CUSBQueue::Initialize()\n");
88
89 ASSERT(Hardware);
90
91 //
92 // initialize device lock
93 //
94 KeInitializeSpinLock(&m_Lock);
95
96 //
97 // Get the AsyncQueueHead
98 //
99 AsyncListQueueHead = (PQUEUE_HEAD)Hardware->GetAsyncListRegister();
100
101 //
102 // Create the PendingListQueueHead from NONPAGEDPOOL. It will never be linked into the Asynclist Schedule
103 //
104 PendingListQueueHead = (PQUEUE_HEAD)ExAllocatePoolWithTag(NonPagedPool, sizeof(QUEUE_HEAD), TAG_USBEHCI);
105 if (!PendingListQueueHead)
106 {
107 DPRINT1("Pool Allocation failed!\n");
108 return STATUS_INSUFFICIENT_RESOURCES;
109 }
110
111 //
112 // Initialize the List Head
113 //
114 InitializeListHead(&PendingListQueueHead->LinkedQueueHeads);
115
116 return Status;
117 }
118
119 ULONG
120 CUSBQueue::GetPendingRequestCount()
121 {
122 //
123 // Loop through the pending list and iterrate one for each QueueHead that
124 // has a IRP to complete.
125 //
126
127
128 return 0;
129 }
130
131 NTSTATUS
132 CUSBQueue::AddUSBRequest(
133 IUSBRequest * Request)
134 {
135 PQUEUE_HEAD QueueHead;
136 ASSERT(Request != NULL);
137
138 Request->GetQueueHead(&QueueHead);
139
140 //
141 // Add it to the pending list
142 //
143 LinkQueueHead(PendingListQueueHead, QueueHead);
144
145 return STATUS_SUCCESS;
146 }
147
148 NTSTATUS
149 CUSBQueue::AddUSBRequest(
150 PURB Urb)
151 {
152 UNIMPLEMENTED
153 return STATUS_NOT_IMPLEMENTED;
154 }
155
156 NTSTATUS
157 CUSBQueue::CancelRequests()
158 {
159 UNIMPLEMENTED
160 return STATUS_NOT_IMPLEMENTED;
161 }
162
163 NTSTATUS
164 CUSBQueue::CreateUSBRequest(
165 IUSBRequest **OutRequest)
166 {
167 PUSBREQUEST UsbRequest;
168 NTSTATUS Status;
169
170 *OutRequest = NULL;
171 Status = InternalCreateUSBRequest(&UsbRequest);
172
173 if (NT_SUCCESS(Status))
174 {
175 *OutRequest = UsbRequest;
176 }
177
178 return Status;
179 }
180
181 //
182 // LinkQueueHead - Links one QueueHead to the end of HeadQueueHead list, updating HorizontalLinkPointer.
183 //
184 VOID
185 CUSBQueue::LinkQueueHead(
186 PQUEUE_HEAD HeadQueueHead,
187 PQUEUE_HEAD NewQueueHead)
188 {
189 PQUEUE_HEAD LastQueueHead, NextQueueHead;
190 PLIST_ENTRY Entry;
191 ASSERT(HeadQueueHead);
192 ASSERT(NewQueueHead);
193
194 //
195 // Link the LIST_ENTRYs
196 //
197 InsertTailList(&HeadQueueHead->LinkedQueueHeads, &NewQueueHead->LinkedQueueHeads);
198
199 //
200 // Update HLP for Previous QueueHead, which should be the last in list.
201 //
202 Entry = NewQueueHead->LinkedQueueHeads.Blink;
203 LastQueueHead = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
204 LastQueueHead->HorizontalLinkPointer = (NewQueueHead->PhysicalAddr | QH_TYPE_QH);
205
206 //
207 // Update HLP for NewQueueHead to point to next, which should be the HeadQueueHead
208 //
209 Entry = NewQueueHead->LinkedQueueHeads.Flink;
210 NextQueueHead = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
211 ASSERT(NextQueueHead == HeadQueueHead);
212 NewQueueHead->HorizontalLinkPointer = NextQueueHead->PhysicalAddr;
213 }
214
215 //
216 // UnlinkQueueHead - Unlinks one QueueHead, updating HorizontalLinkPointer.
217 //
218 VOID
219 CUSBQueue::UnlinkQueueHead(
220 PQUEUE_HEAD QueueHead)
221 {
222 PQUEUE_HEAD PreviousQH, NextQH;
223 PLIST_ENTRY Entry;
224
225 Entry = QueueHead->LinkedQueueHeads.Blink;
226 PreviousQH = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
227 Entry = QueueHead->LinkedQueueHeads.Flink;
228 NextQH = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
229 ASSERT(QueueHead->HorizontalLinkPointer == (NextQH->PhysicalAddr | QH_TYPE_QH));
230 PreviousQH->HorizontalLinkPointer = NextQH->PhysicalAddr | QH_TYPE_QH;
231
232 RemoveEntryList(&QueueHead->LinkedQueueHeads);
233 }
234
235 //
236 // LinkQueueHeadChain - Links a list of QueueHeads to the HeadQueueHead list, updating HorizontalLinkPointer.
237 //
238 VOID
239 CUSBQueue::LinkQueueHeadChain(
240 PQUEUE_HEAD HeadQueueHead,
241 PQUEUE_HEAD NewQueueHead)
242 {
243 PQUEUE_HEAD LastQueueHead;
244 PLIST_ENTRY Entry;
245 ASSERT(HeadQueueHead);
246 ASSERT(NewQueueHead);
247
248 //
249 // Find the last QueueHead in NewQueueHead
250 //
251 Entry = NewQueueHead->LinkedQueueHeads.Blink;
252 ASSERT(Entry != NewQueueHead->LinkedQueueHeads.Flink);
253 LastQueueHead = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
254
255 //
256 // Set the LinkPointer and Flink
257 //
258 LastQueueHead->HorizontalLinkPointer = HeadQueueHead->PhysicalAddr | QH_TYPE_QH;
259 LastQueueHead->LinkedQueueHeads.Flink = &HeadQueueHead->LinkedQueueHeads;
260
261 //
262 // Fine the last QueueHead in HeadQueueHead
263 //
264 Entry = HeadQueueHead->LinkedQueueHeads.Blink;
265 HeadQueueHead->LinkedQueueHeads.Blink = &LastQueueHead->LinkedQueueHeads;
266 LastQueueHead = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
267 LastQueueHead->LinkedQueueHeads.Flink = &NewQueueHead->LinkedQueueHeads;
268 LastQueueHead->HorizontalLinkPointer = NewQueueHead->PhysicalAddr | QH_TYPE_QH;
269 }
270
271 //
272 // UnlinkQueueHeadChain - Unlinks a list number of QueueHeads from HeadQueueHead list, updating HorizontalLinkPointer.
273 // returns the chain of QueueHeads removed from HeadQueueHead.
274 //
275 PQUEUE_HEAD
276 CUSBQueue::UnlinkQueueHeadChain(
277 PQUEUE_HEAD HeadQueueHead,
278 ULONG Count)
279 {
280 PQUEUE_HEAD LastQueueHead, FirstQueueHead;
281 PLIST_ENTRY Entry;
282 ULONG Index;
283
284 //
285 // Find the last QueueHead in NewQueueHead
286 //
287 Entry = &HeadQueueHead->LinkedQueueHeads;
288 FirstQueueHead = CONTAINING_RECORD(Entry->Flink, QUEUE_HEAD, LinkedQueueHeads);
289
290 for (Index = 0; Index < Count; Index++)
291 {
292 Entry = Entry->Flink;
293
294 if (Entry == &HeadQueueHead->LinkedQueueHeads)
295 {
296 DPRINT1("Warnnig; Only %d QueueHeads in HeadQueueHead\n", Index);
297 Count = Index + 1;
298 break;
299 }
300 }
301
302 LastQueueHead = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
303 HeadQueueHead->LinkedQueueHeads.Flink = LastQueueHead->LinkedQueueHeads.Flink;
304 if (Count + 1 == Index)
305 {
306 HeadQueueHead->LinkedQueueHeads.Blink = &HeadQueueHead->LinkedQueueHeads;
307 }
308 else
309 HeadQueueHead->LinkedQueueHeads.Blink = LastQueueHead->LinkedQueueHeads.Flink;
310
311 FirstQueueHead->LinkedQueueHeads.Blink = &LastQueueHead->LinkedQueueHeads;
312 LastQueueHead->LinkedQueueHeads.Flink = &FirstQueueHead->LinkedQueueHeads;
313 LastQueueHead->HorizontalLinkPointer = TERMINATE_POINTER;
314 return FirstQueueHead;
315 }
316
317 NTSTATUS
318 CreateUSBQueue(
319 PUSBQUEUE *OutUsbQueue)
320 {
321 PUSBQUEUE This;
322
323 //
324 // allocate controller
325 //
326 This = new(NonPagedPool, TAG_USBEHCI) CUSBQueue(0);
327 if (!This)
328 {
329 //
330 // failed to allocate
331 //
332 return STATUS_INSUFFICIENT_RESOURCES;
333 }
334
335 //
336 // add reference count
337 //
338 This->AddRef();
339
340 //
341 // return result
342 //
343 *OutUsbQueue = (PUSBQUEUE)This;
344
345 //
346 // done
347 //
348 return STATUS_SUCCESS;
349 }