ReactOS  0.4.12-dev-685-gf36cbf7
usb_queue.cpp
Go to the documentation of this file.
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:
20 
22  {
24  return m_Ref;
25  }
27  {
29 
30  if (!m_Ref)
31  {
32  delete this;
33  return 0;
34  }
35  return m_Ref;
36  }
37 
38  // com
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);
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
58 
59 };
60 
61 //=================================================================================================
62 // COM
63 //
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 
82  IN PUSBHARDWAREDEVICE Hardware,
83  IN PDMA_ADAPTER AdapterObject,
84  IN PDMAMEMORYMANAGER MemManager,
86 {
87 
88  //
89  // store hardware
90  //
91  m_Hardware = PUHCIHARDWAREDEVICE(Hardware);
92 
93  //
94  // initialize spinlock
95  //
97  return STATUS_SUCCESS;
98 }
99 
100 NTSTATUS
102  PUHCI_QUEUE_HEAD NewQueueHead)
103 {
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  //
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;
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
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
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(
235  IN PUSB_ENDPOINT_DESCRIPTOR EndDescriptor)
236 {
237  KIRQL OldLevel;
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
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;
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
305  IN PUHCI_QUEUE_HEAD QueueHead)
306 {
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",
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
388  IN PUHCI_QUEUE_HEAD QueueHead,
389  IN PUHCI_QUEUE_HEAD PreviousQueueHead,
390  OUT PUHCI_QUEUE_HEAD *NextQueueHead)
391 {
393  PUHCI_QUEUE_HEAD NewQueueHead;
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
518  PUSBQUEUE *OutUsbQueue)
519 {
520  PUSBQUEUE This;
521 
522  //
523  // allocate controller
524  //
526  if (!This)
527  {
528  //
529  // failed to allocate
530  //
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 }
#define TD_STATUS_ERROR_NAK
Definition: hardware.h:115
#define IN
Definition: typedefs.h:38
PKSPIN_LOCK m_Lock
Definition: usb_queue.cpp:50
#define REFIID
Definition: guiddef.h:113
#define TRUE
Definition: types.h:120
#define STATUS_INSUFFICIENT_RESOURCES
Definition: udferr_usr.h:158
IUSBHardwareDevice * PUSBHARDWAREDEVICE
_In_ BOOLEAN Release
Definition: classpnp.h:929
STDMETHODIMP_(ULONG) Release()
Definition: usb_queue.cpp:26
#define STATUS_INVALID_PARAMETER
Definition: udferr_usr.h:135
IUnknown * PUNKNOWN
Definition: com_apitest.h:45
#define UHCI_FULL_SPEED_CONTROL_QUEUE
Definition: hardware.h:187
PUHCIHARDWAREDEVICE m_Hardware
Definition: usb_queue.cpp:57
USB_ENDPOINT_DESCRIPTOR EndPointDescriptor
LONG NTSTATUS
Definition: precomp.h:26
#define TD_STATUS_ACTIVE
Definition: hardware.h:111
#define UHCI_BULK_QUEUE
Definition: hardware.h:188
IUHCIRequest * PUHCIREQUEST
Definition: interfaces.h:109
KSPIN_LOCK * PKSPIN_LOCK
Definition: env_spec_w32.h:73
#define USB_ENDPOINT_TYPE_CONTROL
Definition: usb100.h:62
#define TD_ERROR_COUNT_MASK
Definition: hardware.h:140
VOID UnLinkQueueHead(PUHCI_QUEUE_HEAD PreviousQueueHead, PUHCI_QUEUE_HEAD NextQueueHead)
Definition: usb_queue.cpp:224
BOOLEAN IsQueueHeadComplete(PUHCI_QUEUE_HEAD QueueHead)
Definition: usb_queue.cpp:304
#define TD_STATUS_ERROR_BITSTUFF
Definition: hardware.h:118
virtual ~CUSBQueue()
Definition: usb_queue.cpp:52
#define UHCI_INTERRUPT_QUEUE
Definition: hardware.h:185
#define IsEqualGUIDAligned(guid1, guid2)
Definition: wdm.template.h:233
struct _UHCI_TRANSFER_DESCRIPTOR * PUHCI_TRANSFER_DESCRIPTOR
_In_ NDIS_HANDLE _In_ PNDIS_REQUEST Request
Definition: ndis.h:5155
UCHAR KIRQL
Definition: env_spec_w32.h:591
#define UHCI_LOW_SPEED_CONTROL_QUEUE
Definition: hardware.h:186
NTSTATUS(* NTAPI)(IN PFILE_FULL_EA_INFORMATION EaBuffer, IN ULONG EaLength, OUT PULONG ErrorOffset)
Definition: IoEaTest.cpp:117
#define TD_ERROR_COUNT_SHIFT
Definition: hardware.h:139
_In_ PUSB_DEVICE_HANDLE _Out_ PUSHORT DeviceAddress
Definition: hubbusif.h:359
long LONG
Definition: pedump.c:60
PEHCIHARDWAREDEVICE m_Hardware
Definition: usb_queue.cpp:52
#define STDMETHODIMP
Definition: basetyps.h:43
FORCEINLINE VOID KeInitializeSpinLock(_Out_ PKSPIN_LOCK SpinLock)
Definition: kefuncs.h:251
unsigned char BOOLEAN
smooth NULL
Definition: ftsmooth.c:416
#define TD_STATUS_ERROR_BUFFER
Definition: hardware.h:113
void DPRINT(...)
Definition: polytest.cpp:61
PVOID NextElementDescriptor
Definition: hardware.h:177
void * PVOID
Definition: retypes.h:9
NTSTATUS NTAPI CreateUSBQueue(PUSBQUEUE *OutUsbQueue)
Definition: usb_queue.cpp:1204
struct _UHCI_QUEUE_HEAD * PUHCI_QUEUE_HEAD
STDMETHODIMP QueryInterface(REFIID InterfaceId, PVOID *Interface)
STDMETHODIMP_(ULONG) AddRef()
Definition: usb_queue.cpp:21
IN PVOID IN PVOID IN USHORT IN USHORT IN PINTERFACE Interface
Definition: pci.h:359
VOID QueueHeadCleanup(PQUEUE_HEAD QueueHead)
Definition: usb_queue.cpp:893
#define IMP_IUHCIQUEUE
Definition: interfaces.h:125
struct _USB_ENDPOINT * PUSB_ENDPOINT
if(!(yy_init))
Definition: macro.lex.yy.c:714
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:24
#define TD_ERROR_MASK
Definition: hardware.h:138
const GUID IID_IUnknown
#define KeAcquireSpinLock(sl, irql)
Definition: env_spec_w32.h:609
#define STDMETHODCALLTYPE
Definition: bdasup.h:9
LONG m_Ref
Definition: usb_queue.cpp:49
nsrefcnt Release()
ULONG AddRef()
CUSBQueue(IUnknown *OuterUnknown)
Definition: usb_queue.cpp:51
ASSERT((InvokeOnSuccess||InvokeOnError||InvokeOnCancel) ?(CompletionRoutine !=NULL) :TRUE)
#define STATUS_UNSUCCESSFUL
Definition: udferr_usr.h:132
unsigned char UCHAR
Definition: xmlstorage.h:181
#define TD_STATUS_ERROR_TIMEOUT
Definition: hardware.h:117
#define TAG_USBUHCI
Definition: usbuhci.h:26
#define InterlockedDecrement
Definition: armddk.h:52
Definition: arc.h:85
IN OUT PLONG IN OUT PLONG Addend IN OUT PLONG IN LONG IN OUT PLONG IN LONG Increment IN PNDIS_RW_LOCK Lock
Definition: CrNtStubs.h:75
#define USB_ENDPOINT_TYPE_BULK
Definition: usb100.h:64
Status
Definition: gdiplustypes.h:24
#define InterlockedIncrement
Definition: armddk.h:53
IUSBQueue * PUSBQUEUE
#define QH_NEXT_IS_QH
Definition: hardware.h:181
IUSBRequest * PUSBREQUEST
#define TD_STATUS_ERROR_BABBLE
Definition: hardware.h:114
ULONG KSPIN_LOCK
Definition: env_spec_w32.h:72
BOOL Initialize(HINSTANCE hInstance)
Definition: msconfig.c:341
IDMAMemoryManager * PDMAMEMORYMANAGER
#define KeReleaseSpinLock(sl, irql)
Definition: env_spec_w32.h:627
static ULONG WINAPI AddRef(IStream *iface)
Definition: clist.c:90
NTSTATUS AddQueueHead(PUHCI_QUEUE_HEAD NewQueueHead)
Definition: usb_queue.cpp:101
#define TD_TOKEN_DEVADDR_SHIFT
Definition: hardware.h:134
IUHCIHardwareDevice * PUHCIHARDWAREDEVICE
Definition: interfaces.h:58
#define DPRINT1
Definition: precomp.h:8
PVOID NextLogicalDescriptor
Definition: hardware.h:175
NTSTATUS NTAPI InternalCreateUSBRequest(PUSBREQUEST *OutRequest)
#define OUT
Definition: typedefs.h:39
unsigned int ULONG
Definition: retypes.h:1
#define IMP_IUSBQUEUE
return STATUS_SUCCESS
Definition: btrfs.c:2725
#define TD_TOKEN_ENDPTADDR_SHIFT
Definition: hardware.h:133
nsrefcnt AddRef()
VOID LinkQueueHead(PQUEUE_HEAD HeadQueueHead, PQUEUE_HEAD NewQueueHead)
Definition: usb_queue.cpp:466
ULONG LinkPhysical
Definition: hardware.h:170
#define USB_ENDPOINT_TYPE_INTERRUPT
Definition: usb100.h:65
_In_ PSTORAGE_PROPERTY_ID _Outptr_ PSTORAGE_DESCRIPTOR_HEADER * Descriptor
Definition: classpnp.h:966
PULONG MinorVersion OPTIONAL
Definition: CrossNt.h:68