Sync with trunk r58151 to bring the latest changes from Amine and Timo.
[reactos.git] / drivers / usb / usbuhci / usb_queue.cpp
1 /*
2 * PROJECT: ReactOS Universal Serial Bus Host Controller Interface
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: drivers/usb/usbuhci/usb_queue.cpp
5 * PURPOSE: USB UHCI device driver.
6 * PROGRAMMERS:
7 * Michael Martin (michael.martin@reactos.org)
8 * Johannes Anderwald (johannes.anderwald@reactos.org)
9 */
10
11 #include "usbuhci.h"
12 #include "hardware.h"
13
14 class CUSBQueue : public IUHCIQueue
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 // com
37 IMP_IUSBQUEUE
38 IMP_IUHCIQUEUE
39
40 // local
41 VOID LinkQueueHead(PUHCI_QUEUE_HEAD QueueHead, PUHCI_QUEUE_HEAD NextQueueHead);
42 VOID UnLinkQueueHead(PUHCI_QUEUE_HEAD PreviousQueueHead, PUHCI_QUEUE_HEAD NextQueueHead);
43 BOOLEAN IsQueueHeadComplete(PUHCI_QUEUE_HEAD QueueHead);
44 NTSTATUS AddQueueHead(PUHCI_QUEUE_HEAD NewQueueHead);
45 VOID QueueHeadCleanup(IN PUHCI_QUEUE_HEAD QueueHead, IN PUHCI_QUEUE_HEAD PreviousQueueHead, OUT PUHCI_QUEUE_HEAD *NextQueueHead);
46
47
48 // constructor / destructor
49 CUSBQueue(IUnknown *OuterUnknown){}
50 virtual ~CUSBQueue(){}
51
52 protected:
53 LONG m_Ref; // reference count
54 KSPIN_LOCK m_Lock; // list lock
55 PUHCIHARDWAREDEVICE m_Hardware; // hardware
56
57 };
58
59 //=================================================================================================
60 // COM
61 //
62 NTSTATUS
63 STDMETHODCALLTYPE
64 CUSBQueue::QueryInterface(
65 IN REFIID refiid,
66 OUT PVOID* Output)
67 {
68 if (IsEqualGUIDAligned(refiid, IID_IUnknown))
69 {
70 *Output = PVOID(PUNKNOWN(this));
71 PUNKNOWN(*Output)->AddRef();
72 return STATUS_SUCCESS;
73 }
74
75 return STATUS_UNSUCCESSFUL;
76 }
77
78 NTSTATUS
79 CUSBQueue::Initialize(
80 IN PUSBHARDWAREDEVICE Hardware,
81 IN PDMA_ADAPTER AdapterObject,
82 IN PDMAMEMORYMANAGER MemManager,
83 IN OPTIONAL PKSPIN_LOCK Lock)
84 {
85
86 //
87 // store hardware
88 //
89 m_Hardware = PUHCIHARDWAREDEVICE(Hardware);
90
91 //
92 // initialize spinlock
93 //
94 KeInitializeSpinLock(&m_Lock);
95 return STATUS_SUCCESS;
96 }
97
98 NTSTATUS
99 CUSBQueue::AddQueueHead(
100 PUHCI_QUEUE_HEAD NewQueueHead)
101 {
102 PUHCIREQUEST Request;
103 PUHCI_QUEUE_HEAD QueueHead = NULL;
104
105
106 //
107 // get request
108 //
109 Request = (PUHCIREQUEST)NewQueueHead->Request;
110 if (!Request)
111 {
112 //
113 // no request
114 //
115 return STATUS_INVALID_PARAMETER;
116 }
117
118 if (Request->GetTransferType() == USB_ENDPOINT_TYPE_CONTROL)
119 {
120 //
121 // get device speed
122 //
123 if (Request->GetDeviceSpeed() == UsbLowSpeed)
124 {
125 //
126 // use low speed queue
127 //
128 m_Hardware->GetQueueHead(UHCI_LOW_SPEED_CONTROL_QUEUE, &QueueHead);
129 }
130 else
131 {
132 //
133 // use full speed queue
134 //
135 m_Hardware->GetQueueHead(UHCI_FULL_SPEED_CONTROL_QUEUE, &QueueHead);
136 }
137 }
138 else if (Request->GetTransferType() == USB_ENDPOINT_TYPE_BULK)
139 {
140 //
141 // use full speed queue
142 //
143 m_Hardware->GetQueueHead(UHCI_BULK_QUEUE, &QueueHead);
144 }
145 else if (Request->GetTransferType() == USB_ENDPOINT_TYPE_INTERRUPT)
146 {
147 //
148 // use full speed queue
149 //
150 m_Hardware->GetQueueHead(UHCI_INTERRUPT_QUEUE, &QueueHead);
151 }
152 else if (Request->GetTransferType() == USB_ENDPOINT_TYPE_INTERRUPT)
153 {
154 //
155 // use full speed queue
156 //
157 m_Hardware->GetQueueHead(UHCI_INTERRUPT_QUEUE, &QueueHead);
158 }
159
160 //
161 // FIXME support isochronous
162 //
163 ASSERT(QueueHead);
164
165 //
166 // add reference
167 //
168 Request->AddRef();
169
170 //
171 // now link the new queue head
172 //
173 LinkQueueHead(QueueHead, NewQueueHead);
174 return STATUS_SUCCESS;
175
176 }
177
178 NTSTATUS
179 CUSBQueue::AddUSBRequest(
180 IUSBRequest * Req)
181 {
182 PUHCI_QUEUE_HEAD NewQueueHead;
183 NTSTATUS Status;
184 PUHCIREQUEST Request;
185
186 // get request
187 Request = (PUHCIREQUEST)Req;
188
189 //
190 // get queue head
191 //
192 Status = Request->GetEndpointDescriptor(&NewQueueHead);
193 if (!NT_SUCCESS(Status))
194 {
195 //
196 // failed to create queue head
197 //
198 DPRINT1("[USBUHCI] Failed to create queue head %x\n", Status);
199 return Status;
200 }
201
202 //
203 // sanity check
204 //
205 ASSERT(PVOID(Request) == NewQueueHead->Request);
206
207 //
208 // add queue head
209 //
210 DPRINT("AddUSBRequest Request %p\n", Request);
211 DPRINT("NewQueueHead %p\n", NewQueueHead);
212 return AddQueueHead(NewQueueHead);
213 }
214
215 VOID
216 CUSBQueue::LinkQueueHead(
217 IN PUHCI_QUEUE_HEAD QueueHead,
218 IN PUHCI_QUEUE_HEAD NextQueueHead)
219 {
220 NextQueueHead->LinkPhysical = QueueHead->LinkPhysical;
221 NextQueueHead->NextLogicalDescriptor = QueueHead->NextLogicalDescriptor;
222
223 QueueHead->LinkPhysical = NextQueueHead->PhysicalAddress | QH_NEXT_IS_QH;
224 QueueHead->NextLogicalDescriptor = (PVOID)NextQueueHead;
225 }
226
227
228 VOID
229 CUSBQueue::UnLinkQueueHead(
230 PUHCI_QUEUE_HEAD QueueHeadToRemove,
231 PUHCI_QUEUE_HEAD PreviousQueueHead)
232 {
233 PreviousQueueHead->LinkPhysical = QueueHeadToRemove->LinkPhysical;
234 PreviousQueueHead->NextLogicalDescriptor = QueueHeadToRemove->NextLogicalDescriptor;
235 }
236
237 NTSTATUS
238 CUSBQueue::AbortDevicePipe(
239 IN UCHAR DeviceAddress,
240 IN PUSB_ENDPOINT_DESCRIPTOR EndDescriptor)
241 {
242 KIRQL OldLevel;
243 PUHCI_TRANSFER_DESCRIPTOR Descriptor;
244 PUHCI_QUEUE_HEAD QueueHead, PreviousQueueHead = NULL;
245 UCHAR EndpointAddress, EndpointDeviceAddress;
246 PUSB_ENDPOINT EndpointDescriptor;
247
248 // get descriptor
249 EndpointDescriptor = (PUSB_ENDPOINT)EndDescriptor;
250
251 // acquire lock
252 KeAcquireSpinLock(&m_Lock, &OldLevel);
253
254 // get queue head
255 m_Hardware->GetQueueHead(UHCI_INTERRUPT_QUEUE, &QueueHead);
256
257 while(QueueHead)
258 {
259 // get descriptor
260 Descriptor = (PUHCI_TRANSFER_DESCRIPTOR)QueueHead->NextElementDescriptor;
261
262 if (Descriptor)
263 {
264 // extract endpoint address
265 EndpointAddress = (Descriptor->Token >> TD_TOKEN_ENDPTADDR_SHIFT) & 0x0F;
266
267 // extract device address
268 EndpointDeviceAddress = (Descriptor->Token >> TD_TOKEN_DEVADDR_SHIFT) & 0x7F;
269
270 // check if they match
271 if (EndpointAddress == (EndpointDescriptor->EndPointDescriptor.bEndpointAddress & 0x0F) &&
272 DeviceAddress == EndpointDeviceAddress)
273 {
274 // cleanup queue head
275 QueueHeadCleanup(QueueHead, PreviousQueueHead, &QueueHead);
276 continue;
277 }
278 }
279
280 // move to next queue head
281 PreviousQueueHead = QueueHead;
282 QueueHead = (PUHCI_QUEUE_HEAD)QueueHead->NextLogicalDescriptor;
283 }
284
285 // release lock
286 KeReleaseSpinLock(&m_Lock, OldLevel);
287 return STATUS_SUCCESS;
288 }
289
290 NTSTATUS
291 CUSBQueue::CreateUSBRequest(
292 IUSBRequest **OutRequest)
293 {
294 PUSBREQUEST UsbRequest;
295 NTSTATUS Status;
296
297 *OutRequest = NULL;
298 Status = InternalCreateUSBRequest(&UsbRequest);
299
300 if (NT_SUCCESS(Status))
301 {
302 *OutRequest = UsbRequest;
303 }
304
305 return Status;
306 }
307
308 BOOLEAN
309 CUSBQueue::IsQueueHeadComplete(
310 IN PUHCI_QUEUE_HEAD QueueHead)
311 {
312 PUHCI_TRANSFER_DESCRIPTOR Descriptor;
313 ULONG ErrorCount;
314
315 if (QueueHead->NextElementDescriptor == NULL)
316 {
317 //
318 // empty queue head
319 //
320 DPRINT("QueueHead %p empty element physical\n", QueueHead);
321 return FALSE;
322 }
323
324 //
325 // check all descriptors
326 //
327 Descriptor = (PUHCI_TRANSFER_DESCRIPTOR)QueueHead->NextElementDescriptor;
328 while(Descriptor)
329 {
330 if (Descriptor->Status & TD_STATUS_ACTIVE)
331 {
332 //
333 // descriptor is still active
334 //
335 DPRINT("Descriptor %p is active Status %x BufferSize %lu\n", Descriptor, Descriptor->Status, Descriptor->BufferSize);
336 return FALSE;
337 }
338
339 if (Descriptor->Status & TD_ERROR_MASK)
340 {
341 //
342 // error happened
343 //
344 DPRINT1("[USBUHCI] Error detected at descriptor %p Physical %x\n", Descriptor, Descriptor->PhysicalAddress);
345
346 //
347 // get error count
348 //
349 ErrorCount = (Descriptor->Status >> TD_ERROR_COUNT_SHIFT) & TD_ERROR_COUNT_MASK;
350 if (ErrorCount == 0)
351 {
352 //
353 // error retry count elapsed
354 //
355 DPRINT1("[USBUHCI] ErrorBuffer %x TimeOut %x Nak %x BitStuff %x\n",
356 Descriptor->Status & TD_STATUS_ERROR_BUFFER,
357 Descriptor->Status & TD_STATUS_ERROR_TIMEOUT,
358 Descriptor->Status & TD_STATUS_ERROR_NAK,
359 Descriptor->Status & TD_STATUS_ERROR_BITSTUFF);
360 return TRUE;
361 }
362 else if (Descriptor->Status & TD_STATUS_ERROR_BABBLE)
363 {
364 //
365 // babble error
366 //
367 DPRINT1("[USBUHCI] Babble detected\n");
368 return TRUE;
369 }
370 else
371 {
372 //
373 // stall detected
374 //
375 DPRINT1("[USBUHCI] Stall detected\n");
376 }
377 }
378
379 //
380 // move to next descriptor
381 //
382 Descriptor = (PUHCI_TRANSFER_DESCRIPTOR)Descriptor->NextLogicalDescriptor;
383 }
384
385 //
386 // request is complete
387 //
388 return TRUE;
389 }
390
391 VOID
392 CUSBQueue::QueueHeadCleanup(
393 IN PUHCI_QUEUE_HEAD QueueHead,
394 IN PUHCI_QUEUE_HEAD PreviousQueueHead,
395 OUT PUHCI_QUEUE_HEAD *NextQueueHead)
396 {
397 PUHCIREQUEST Request;
398 PUHCI_QUEUE_HEAD NewQueueHead;
399 NTSTATUS Status;
400
401 //
402 // unlink queue head
403 //
404 UnLinkQueueHead(QueueHead, PreviousQueueHead);
405
406 //
407 // get next queue head
408 //
409 *NextQueueHead = (PUHCI_QUEUE_HEAD)PreviousQueueHead->NextLogicalDescriptor;
410 ASSERT(*NextQueueHead != QueueHead);
411
412 //
413 // the queue head is complete, is the transfer now completed?
414 //
415 Request = (PUHCIREQUEST)QueueHead->Request;
416 ASSERT(Request);
417
418 //
419 // free queue head
420 //
421 DPRINT("Request %p\n", Request);
422 Request->FreeEndpointDescriptor(QueueHead);
423
424 //
425 // check if transfer is complete
426 //
427 if (Request->IsRequestComplete())
428 {
429 //
430 // the transfer is complete
431 //
432 Request->CompletionCallback();
433 Request->Release();
434 return;
435 }
436
437 //
438 // grab new queue head
439 //
440 Status = Request->GetEndpointDescriptor(&NewQueueHead);
441 if (!NT_SUCCESS(Status))
442 {
443 //
444 // failed to get new queue head
445 //
446 DPRINT1("[USBUHCI] Failed to get new queue head with %x\n", Status);
447 Request->CompletionCallback();
448 Request->Release();
449 return;
450 }
451
452 //
453 // Link queue head
454 //
455 Status = AddQueueHead(NewQueueHead);
456 if (!NT_SUCCESS(Status))
457 {
458 //
459 // failed to get new queue head
460 //
461 DPRINT1("[USBUHCI] Failed to add queue head with %x\n", Status);
462 Request->CompletionCallback();
463 Request->Release();
464 return;
465 }
466
467 }
468
469 VOID
470 CUSBQueue::TransferInterrupt(
471 UCHAR ErrorInterrupt)
472 {
473 KIRQL OldLevel;
474 PUHCI_QUEUE_HEAD QueueHead, PreviousQueueHead = NULL;
475 BOOLEAN IsComplete;
476
477 //
478 // acquire lock
479 //
480 KeAcquireSpinLock(&m_Lock, &OldLevel);
481
482 //
483 // get queue head
484 //
485 m_Hardware->GetQueueHead(UHCI_INTERRUPT_QUEUE, &QueueHead);
486
487 while(QueueHead)
488 {
489 //
490 // is queue head complete
491 //
492 DPRINT("QueueHead %p\n", QueueHead);
493 IsComplete = IsQueueHeadComplete(QueueHead);
494 if (IsComplete)
495 {
496 //
497 // cleanup queue head
498 //
499 QueueHeadCleanup(QueueHead, PreviousQueueHead, &QueueHead);
500 continue;
501 }
502
503 //
504 // backup previous queue head
505 //
506 PreviousQueueHead = QueueHead;
507
508 //
509 // get next queue head
510 //
511 QueueHead = (PUHCI_QUEUE_HEAD)QueueHead->NextLogicalDescriptor;
512 }
513
514 //
515 // release lock
516 //
517 KeReleaseSpinLock(&m_Lock, OldLevel);
518 }
519
520 NTSTATUS
521 NTAPI
522 CreateUSBQueue(
523 PUSBQUEUE *OutUsbQueue)
524 {
525 PUSBQUEUE This;
526
527 //
528 // allocate controller
529 //
530 This = new(NonPagedPool, TAG_USBUHCI) CUSBQueue(0);
531 if (!This)
532 {
533 //
534 // failed to allocate
535 //
536 return STATUS_INSUFFICIENT_RESOURCES;
537 }
538
539 //
540 // add reference count
541 //
542 This->AddRef();
543
544 //
545 // return result
546 //
547 *OutUsbQueue = (PUSBQUEUE)This;
548
549 //
550 // done
551 //
552 return STATUS_SUCCESS;
553 }