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