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/usbohci/usb_queue.cpp
5 * PURPOSE: USB OHCI device driver.
7 * Michael Martin (michael.martin@reactos.org)
8 * Johannes Anderwald (johannes.anderwald@reactos.org)
14 class CUSBQueue
: public IUSBQueue
17 STDMETHODIMP
QueryInterface( REFIID InterfaceId
, PVOID
* Interface
);
19 STDMETHODIMP_(ULONG
) AddRef()
21 InterlockedIncrement(&m_Ref
);
24 STDMETHODIMP_(ULONG
) Release()
26 InterlockedDecrement(&m_Ref
);
37 virtual NTSTATUS
Initialize(IN PUSBHARDWAREDEVICE Hardware
, PDMA_ADAPTER AdapterObject
, IN PDMAMEMORYMANAGER MemManager
, IN OPTIONAL PKSPIN_LOCK Lock
);
38 virtual ULONG
GetPendingRequestCount();
39 virtual NTSTATUS
AddUSBRequest(IUSBRequest
* Request
);
40 virtual NTSTATUS
CancelRequests();
41 virtual NTSTATUS
CreateUSBRequest(IUSBRequest
**OutRequest
);
42 virtual VOID
TransferDescriptorCompletionCallback(ULONG TransferDescriptorLogicalAddress
);
43 virtual NTSTATUS
AbortDevicePipe(UCHAR DeviceAddress
, IN PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor
);
46 BOOLEAN
IsTransferDescriptorInEndpoint(IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
, IN ULONG TransferDescriptorLogicalAddress
);
47 BOOLEAN
IsTransferDescriptorInIsoEndpoint(IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
, IN ULONG TransferDescriptorLogicalAddress
);
48 NTSTATUS
FindTransferDescriptorInEndpoint(IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
, IN ULONG TransferDescriptorLogicalAddress
, OUT POHCI_ENDPOINT_DESCRIPTOR
*OutEndpointDescriptor
, OUT POHCI_ENDPOINT_DESCRIPTOR
*OutPreviousEndpointDescriptor
);
49 NTSTATUS
FindTransferDescriptorInInterruptHeadEndpoints(IN ULONG TransferDescriptorLogicalAddress
, OUT POHCI_ENDPOINT_DESCRIPTOR
*OutEndpointDescriptor
, OUT POHCI_ENDPOINT_DESCRIPTOR
*OutPreviousEndpointDescriptor
);
50 NTSTATUS
FindTransferDescriptorInIsochronousHeadEndpoints(IN ULONG TransferDescriptorLogicalAddress
, OUT POHCI_ENDPOINT_DESCRIPTOR
*OutEndpointDescriptor
, OUT POHCI_ENDPOINT_DESCRIPTOR
*OutPreviousEndpointDescriptor
);
52 VOID
CleanupEndpointDescriptor(POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
, POHCI_ENDPOINT_DESCRIPTOR PreviousEndpointDescriptor
);
53 POHCI_ENDPOINT_DESCRIPTOR
FindInterruptEndpointDescriptor(UCHAR InterruptInterval
);
54 VOID
PrintEndpointList(POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
);
55 VOID
LinkEndpoint(POHCI_ENDPOINT_DESCRIPTOR HeadEndpointDescriptor
, POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
);
57 // constructor / destructor
58 CUSBQueue(IUnknown
*OuterUnknown
){}
59 virtual ~CUSBQueue(){}
62 LONG m_Ref
; // reference count
63 KSPIN_LOCK m_Lock
; // list lock
64 PUSBHARDWAREDEVICE m_Hardware
; // hardware
65 POHCI_ENDPOINT_DESCRIPTOR m_BulkHeadEndpointDescriptor
; // bulk head descriptor
66 POHCI_ENDPOINT_DESCRIPTOR m_ControlHeadEndpointDescriptor
; // control head descriptor
67 POHCI_ENDPOINT_DESCRIPTOR m_IsoHeadEndpointDescriptor
; // isochronous head descriptor
68 POHCI_ENDPOINT_DESCRIPTOR
* m_InterruptEndpoints
;
71 //=================================================================================================
76 CUSBQueue::QueryInterface(
80 if (IsEqualGUIDAligned(refiid
, IID_IUnknown
))
82 *Output
= PVOID(PUNKNOWN(this));
83 PUNKNOWN(*Output
)->AddRef();
84 return STATUS_SUCCESS
;
87 return STATUS_UNSUCCESSFUL
;
91 CUSBQueue::Initialize(
92 IN PUSBHARDWAREDEVICE Hardware
,
93 IN PDMA_ADAPTER AdapterObject
,
94 IN PDMAMEMORYMANAGER MemManager
,
95 IN OPTIONAL PKSPIN_LOCK Lock
)
98 // get bulk endpoint descriptor
100 Hardware
->GetBulkHeadEndpointDescriptor(&m_BulkHeadEndpointDescriptor
);
103 // get control endpoint descriptor
105 Hardware
->GetControlHeadEndpointDescriptor(&m_ControlHeadEndpointDescriptor
);
108 // get isochronous endpoint
110 Hardware
->GetIsochronousHeadEndpointDescriptor(&m_IsoHeadEndpointDescriptor
);
113 // get interrupt endpoints
115 Hardware
->GetInterruptEndpointDescriptors(&m_InterruptEndpoints
);
118 // initialize spinlock
120 KeInitializeSpinLock(&m_Lock
);
125 m_Hardware
= Hardware
;
127 return STATUS_SUCCESS
;
131 CUSBQueue::GetPendingRequestCount()
134 // Loop through the pending list and iterrate one for each QueueHead that
135 // has a IRP to complete.
142 CUSBQueue::LinkEndpoint(
143 POHCI_ENDPOINT_DESCRIPTOR HeadEndpointDescriptor
,
144 POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
)
146 POHCI_ENDPOINT_DESCRIPTOR CurrentEndpointDescriptor
= HeadEndpointDescriptor
;
149 // get last descriptor in queue
151 while(CurrentEndpointDescriptor
->NextDescriptor
)
154 // move to last descriptor
156 CurrentEndpointDescriptor
= (POHCI_ENDPOINT_DESCRIPTOR
)CurrentEndpointDescriptor
->NextDescriptor
;
162 CurrentEndpointDescriptor
->NextPhysicalEndpoint
= EndpointDescriptor
->PhysicalAddress
.LowPart
;
163 CurrentEndpointDescriptor
->NextDescriptor
= EndpointDescriptor
;
168 CUSBQueue::AddUSBRequest(
169 IUSBRequest
* Request
)
173 POHCI_ENDPOINT_DESCRIPTOR HeadDescriptor
;
174 POHCI_ENDPOINT_DESCRIPTOR Descriptor
;
175 POHCI_ISO_TD CurrentDescriptor
;
179 DPRINT("CUSBQueue::AddUSBRequest\n");
184 ASSERT(Request
!= NULL
);
189 Type
= Request
->GetTransferType();
192 // add extra reference which is released when the request is completed
197 // get transfer descriptors
199 Status
= Request
->GetEndpointDescriptor(&Descriptor
);
200 if (!NT_SUCCESS(Status
))
203 // failed to get transfer descriptor
205 DPRINT1("CUSBQueue::AddUSBRequest GetEndpointDescriptor failed with %x\n", Status
);
217 if (Type
== USB_ENDPOINT_TYPE_BULK
)
220 // get head descriptor
222 HeadDescriptor
= m_BulkHeadEndpointDescriptor
;
224 else if (Type
== USB_ENDPOINT_TYPE_CONTROL
)
227 // get head descriptor
229 HeadDescriptor
= m_ControlHeadEndpointDescriptor
;
231 else if (Type
== USB_ENDPOINT_TYPE_INTERRUPT
)
234 // get head descriptor
236 HeadDescriptor
= FindInterruptEndpointDescriptor(Request
->GetInterval());
237 ASSERT(HeadDescriptor
);
239 else if (Type
== USB_ENDPOINT_TYPE_ISOCHRONOUS
)
242 // get head descriptor
244 HeadDescriptor
= m_IsoHeadEndpointDescriptor
;
247 // get current frame number
249 m_Hardware
->GetCurrentFrameNumber(&FrameNumber
);
252 // FIXME: increment frame number
257 // apply frame number to iso transfer descriptors
259 CurrentDescriptor
= (POHCI_ISO_TD
)Descriptor
->HeadLogicalDescriptor
;
261 DPRINT("ISO: NextFrameNumber %x\n", FrameNumber
);
262 Frame
= (FrameNumber
& 0xFFFF);
264 while(CurrentDescriptor
)
267 // set current frame number
269 CurrentDescriptor
->Flags
|= OHCI_ITD_SET_STARTING_FRAME(Frame
);
272 // move to next frame number
274 Frame
+= OHCI_ITD_GET_FRAME_COUNT(CurrentDescriptor
->Flags
);
277 // move to next descriptor
279 CurrentDescriptor
= CurrentDescriptor
->NextLogicalDescriptor
;
283 // get current frame number
285 m_Hardware
->GetCurrentFrameNumber(&FrameNumber
);
287 DPRINT("Hardware 1ms %p Iso %p\n",m_InterruptEndpoints
[0], m_IsoHeadEndpointDescriptor
);
288 ASSERT(m_InterruptEndpoints
[0]->NextPhysicalEndpoint
== m_IsoHeadEndpointDescriptor
->PhysicalAddress
.LowPart
);
290 PrintEndpointList(m_IsoHeadEndpointDescriptor
);
298 return STATUS_INVALID_PARAMETER
;
302 // set descriptor active
304 Descriptor
->Flags
&= ~OHCI_ENDPOINT_SKIP
;
307 // insert endpoint at end
309 LinkEndpoint(HeadDescriptor
, Descriptor
);
311 if (Type
== USB_ENDPOINT_TYPE_CONTROL
|| Type
== USB_ENDPOINT_TYPE_BULK
)
314 // notify hardware of our request
316 m_Hardware
->HeadEndpointDescriptorModified(Type
);
319 return STATUS_SUCCESS
;
323 CUSBQueue::CancelRequests()
326 return STATUS_NOT_IMPLEMENTED
;
330 CUSBQueue::CreateUSBRequest(
331 IUSBRequest
**OutRequest
)
333 PUSBREQUEST UsbRequest
;
337 Status
= InternalCreateUSBRequest(&UsbRequest
);
339 if (NT_SUCCESS(Status
))
341 *OutRequest
= UsbRequest
;
348 CUSBQueue::FindTransferDescriptorInEndpoint(
349 IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
,
350 IN ULONG TransferDescriptorLogicalAddress
,
351 OUT POHCI_ENDPOINT_DESCRIPTOR
*OutEndpointDescriptor
,
352 OUT POHCI_ENDPOINT_DESCRIPTOR
*OutPreviousEndpointDescriptor
)
354 POHCI_ENDPOINT_DESCRIPTOR LastDescriptor
= EndpointDescriptor
;
358 // skip first endpoint head
360 EndpointDescriptor
= (POHCI_ENDPOINT_DESCRIPTOR
)EndpointDescriptor
->NextDescriptor
;
362 while(EndpointDescriptor
)
365 // check if the transfer descriptor is inside the list
367 if ((EndpointDescriptor
->HeadPhysicalDescriptor
& OHCI_ENDPOINT_HEAD_MASK
) == EndpointDescriptor
->TailPhysicalDescriptor
|| (EndpointDescriptor
->HeadPhysicalDescriptor
& OHCI_ENDPOINT_HALTED
))
372 *OutEndpointDescriptor
= EndpointDescriptor
;
373 *OutPreviousEndpointDescriptor
= LastDescriptor
;
378 return STATUS_SUCCESS
;
382 // store last endpoint
384 LastDescriptor
= EndpointDescriptor
;
389 EndpointDescriptor
= (POHCI_ENDPOINT_DESCRIPTOR
)EndpointDescriptor
->NextDescriptor
;
393 // failed to endpoint
395 return STATUS_NOT_FOUND
;
399 CUSBQueue::FindTransferDescriptorInInterruptHeadEndpoints(IN ULONG TransferDescriptorLogicalAddress
, OUT POHCI_ENDPOINT_DESCRIPTOR
*OutEndpointDescriptor
, OUT POHCI_ENDPOINT_DESCRIPTOR
*OutPreviousEndpointDescriptor
)
405 // search descriptor in endpoint list
407 for(Index
= 0; Index
< OHCI_STATIC_ENDPOINT_COUNT
; Index
++)
410 // is it in current endpoint
412 Status
= FindTransferDescriptorInEndpoint(m_InterruptEndpoints
[Index
], TransferDescriptorLogicalAddress
, OutEndpointDescriptor
, OutPreviousEndpointDescriptor
);
413 if (NT_SUCCESS(Status
))
416 // found transfer descriptor
418 return STATUS_SUCCESS
;
425 return STATUS_NOT_FOUND
;
429 CUSBQueue::FindTransferDescriptorInIsochronousHeadEndpoints(
430 IN ULONG TransferDescriptorLogicalAddress
,
431 OUT POHCI_ENDPOINT_DESCRIPTOR
*OutEndpointDescriptor
,
432 OUT POHCI_ENDPOINT_DESCRIPTOR
*OutPreviousEndpointDescriptor
)
434 POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
;
435 POHCI_ENDPOINT_DESCRIPTOR LastDescriptor
= m_IsoHeadEndpointDescriptor
;
439 // skip first endpoint head
441 EndpointDescriptor
= (POHCI_ENDPOINT_DESCRIPTOR
)m_IsoHeadEndpointDescriptor
->NextDescriptor
;
443 while(EndpointDescriptor
)
446 // check if the transfer descriptor is inside the list
448 if (IsTransferDescriptorInIsoEndpoint(EndpointDescriptor
, TransferDescriptorLogicalAddress
))
453 *OutEndpointDescriptor
= EndpointDescriptor
;
454 *OutPreviousEndpointDescriptor
= LastDescriptor
;
459 return STATUS_SUCCESS
;
463 // store last endpoint
465 LastDescriptor
= EndpointDescriptor
;
470 EndpointDescriptor
= (POHCI_ENDPOINT_DESCRIPTOR
)EndpointDescriptor
->NextDescriptor
;
474 // failed to endpoint
476 return STATUS_NOT_FOUND
;
480 CUSBQueue::IsTransferDescriptorInIsoEndpoint(
481 IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
,
482 IN ULONG TransferDescriptorLogicalAddress
)
484 POHCI_ISO_TD Descriptor
;
487 // get first general transfer descriptor
489 Descriptor
= (POHCI_ISO_TD
)EndpointDescriptor
->HeadLogicalDescriptor
;
498 if (Descriptor
->PhysicalAddress
.LowPart
== TransferDescriptorLogicalAddress
)
509 Descriptor
= (POHCI_ISO_TD
)Descriptor
->NextLogicalDescriptor
;
513 // no descriptor found
520 CUSBQueue::IsTransferDescriptorInEndpoint(
521 IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
,
522 IN ULONG TransferDescriptorLogicalAddress
)
524 POHCI_GENERAL_TD Descriptor
;
527 // get first general transfer descriptor
529 Descriptor
= (POHCI_GENERAL_TD
)EndpointDescriptor
->HeadLogicalDescriptor
;
538 if (Descriptor
->PhysicalAddress
.LowPart
== TransferDescriptorLogicalAddress
)
549 Descriptor
= (POHCI_GENERAL_TD
)Descriptor
->NextLogicalDescriptor
;
554 // no descriptor found
560 CUSBQueue::CleanupEndpointDescriptor(
561 POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
,
562 POHCI_ENDPOINT_DESCRIPTOR PreviousEndpointDescriptor
)
567 // FIXME: verify unlinking process
569 PreviousEndpointDescriptor
->NextDescriptor
= EndpointDescriptor
->NextDescriptor
;
570 PreviousEndpointDescriptor
->NextPhysicalEndpoint
= EndpointDescriptor
->NextPhysicalEndpoint
;
573 // get corresponding request
575 Request
= PUSBREQUEST(EndpointDescriptor
->Request
);
579 // notify of completion
581 Request
->CompletionCallback(EndpointDescriptor
);
584 // free endpoint descriptor
586 Request
->FreeEndpointDescriptor(EndpointDescriptor
);
595 CUSBQueue::PrintEndpointList(
596 POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
)
598 DPRINT1("CUSBQueue::PrintEndpointList HeadEndpoint %p Logical %x\n", EndpointDescriptor
, EndpointDescriptor
->PhysicalAddress
.LowPart
);
601 // get first general transfer descriptor
603 EndpointDescriptor
= (POHCI_ENDPOINT_DESCRIPTOR
)EndpointDescriptor
->NextDescriptor
;
605 while(EndpointDescriptor
)
607 DPRINT1(" CUSBQueue::PrintEndpointList Endpoint %p Logical %x\n", EndpointDescriptor
, EndpointDescriptor
->PhysicalAddress
.LowPart
);
612 EndpointDescriptor
= (POHCI_ENDPOINT_DESCRIPTOR
)EndpointDescriptor
->NextDescriptor
;
617 CUSBQueue::TransferDescriptorCompletionCallback(
618 ULONG TransferDescriptorLogicalAddress
)
620 POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor
, PreviousEndpointDescriptor
;
623 DPRINT("CUSBQueue::TransferDescriptorCompletionCallback transfer descriptor %x\n", TransferDescriptorLogicalAddress
);
628 // find transfer descriptor in control list
630 Status
= FindTransferDescriptorInEndpoint(m_ControlHeadEndpointDescriptor
, TransferDescriptorLogicalAddress
, &EndpointDescriptor
, &PreviousEndpointDescriptor
);
631 if (NT_SUCCESS(Status
))
636 CleanupEndpointDescriptor(EndpointDescriptor
, PreviousEndpointDescriptor
);
645 // find transfer descriptor in bulk list
647 Status
= FindTransferDescriptorInEndpoint(m_BulkHeadEndpointDescriptor
, TransferDescriptorLogicalAddress
, &EndpointDescriptor
, &PreviousEndpointDescriptor
);
648 if (NT_SUCCESS(Status
))
653 CleanupEndpointDescriptor(EndpointDescriptor
, PreviousEndpointDescriptor
);
662 // find transfer descriptor in interrupt list
664 Status
= FindTransferDescriptorInInterruptHeadEndpoints(TransferDescriptorLogicalAddress
, &EndpointDescriptor
, &PreviousEndpointDescriptor
);
665 if (NT_SUCCESS(Status
))
670 CleanupEndpointDescriptor(EndpointDescriptor
, PreviousEndpointDescriptor
);
679 // last try: find the descriptor in isochronous list
681 Status
= FindTransferDescriptorInIsochronousHeadEndpoints(TransferDescriptorLogicalAddress
, &EndpointDescriptor
, &PreviousEndpointDescriptor
);
682 if (NT_SUCCESS(Status
))
687 DPRINT("ISO endpoint complete\n");
689 CleanupEndpointDescriptor(EndpointDescriptor
, PreviousEndpointDescriptor
);
698 // no more completed descriptors found
705 // hardware reported dead endpoint completed
707 DPRINT1("CUSBQueue::TransferDescriptorCompletionCallback invalid transfer descriptor %x\n", TransferDescriptorLogicalAddress
);
711 POHCI_ENDPOINT_DESCRIPTOR
712 CUSBQueue::FindInterruptEndpointDescriptor(
713 UCHAR InterruptInterval
)
721 ASSERT(InterruptInterval
<= OHCI_BIGGEST_INTERVAL
);
724 // find interrupt index
726 while (Power
<= OHCI_BIGGEST_INTERVAL
/ 2)
729 // is current interval greater
731 if (Power
* 2 > InterruptInterval
)
740 // move to next interrupt
745 DPRINT("InterruptInterval %lu Selected InterruptIndex %lu Choosen Interval %lu\n", InterruptInterval
, Index
, Power
);
750 return m_InterruptEndpoints
[Index
];
754 CUSBQueue::AbortDevicePipe(
755 IN UCHAR DeviceAddress
,
756 IN PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor
)
758 POHCI_ENDPOINT_DESCRIPTOR HeadDescriptor
, CurrentDescriptor
, PreviousDescriptor
, TempDescriptor
;
760 POHCI_GENERAL_TD TransferDescriptor
;
765 Type
= (EndpointDescriptor
->bmAttributes
& USB_ENDPOINT_TYPE_MASK
);
770 if (Type
== USB_ENDPOINT_TYPE_BULK
)
773 // get head descriptor
775 HeadDescriptor
= m_BulkHeadEndpointDescriptor
;
777 else if (Type
== USB_ENDPOINT_TYPE_CONTROL
)
780 // get head descriptor
782 HeadDescriptor
= m_ControlHeadEndpointDescriptor
;
784 else if (Type
== USB_ENDPOINT_TYPE_INTERRUPT
)
787 // get head descriptor
789 HeadDescriptor
= FindInterruptEndpointDescriptor(EndpointDescriptor
->bInterval
);
790 ASSERT(HeadDescriptor
);
792 else if (Type
== USB_ENDPOINT_TYPE_ISOCHRONOUS
)
795 return STATUS_NOT_IMPLEMENTED
;
799 // FIXME should disable list processing
803 // now remove all endpoints
805 ASSERT(HeadDescriptor
);
806 CurrentDescriptor
= (POHCI_ENDPOINT_DESCRIPTOR
)HeadDescriptor
->NextDescriptor
;
807 PreviousDescriptor
= (POHCI_ENDPOINT_DESCRIPTOR
)HeadDescriptor
;
809 while(CurrentDescriptor
)
811 if ((CurrentDescriptor
->HeadPhysicalDescriptor
& OHCI_ENDPOINT_HEAD_MASK
) == CurrentDescriptor
->TailPhysicalDescriptor
|| (CurrentDescriptor
->HeadPhysicalDescriptor
& OHCI_ENDPOINT_HALTED
))
816 TempDescriptor
= (POHCI_ENDPOINT_DESCRIPTOR
)CurrentDescriptor
->NextDescriptor
;
817 CleanupEndpointDescriptor(CurrentDescriptor
, PreviousDescriptor
);
820 // use next descriptor
822 CurrentDescriptor
= TempDescriptor
;
825 if (!CurrentDescriptor
)
828 if (CurrentDescriptor
->HeadPhysicalDescriptor
)
830 TransferDescriptor
= (POHCI_GENERAL_TD
)CurrentDescriptor
->HeadLogicalDescriptor
;
831 ASSERT(TransferDescriptor
);
833 if ((OHCI_ENDPOINT_GET_ENDPOINT_NUMBER(TransferDescriptor
->Flags
) == (EndpointDescriptor
->bEndpointAddress
& 0xF)) &&
834 (OHCI_ENDPOINT_GET_DEVICE_ADDRESS(TransferDescriptor
->Flags
) == DeviceAddress
))
839 TempDescriptor
= (POHCI_ENDPOINT_DESCRIPTOR
)CurrentDescriptor
->NextDescriptor
;
840 CleanupEndpointDescriptor(CurrentDescriptor
, PreviousDescriptor
);
842 // use next descriptor
844 CurrentDescriptor
= TempDescriptor
;
848 if (!CurrentDescriptor
)
851 PreviousDescriptor
= CurrentDescriptor
;
852 CurrentDescriptor
= (POHCI_ENDPOINT_DESCRIPTOR
)CurrentDescriptor
->NextDescriptor
;
858 return STATUS_SUCCESS
;
864 PUSBQUEUE
*OutUsbQueue
)
869 // allocate controller
871 This
= new(NonPagedPool
, TAG_USBOHCI
) CUSBQueue(0);
875 // failed to allocate
877 return STATUS_INSUFFICIENT_RESOURCES
;
881 // add reference count
888 *OutUsbQueue
= (PUSBQUEUE
)This
;
893 return STATUS_SUCCESS
;