/* * PROJECT: ReactOS Universal Serial Bus Host Controller Interface * LICENSE: GPL - See COPYING in the top level directory * FILE: drivers/usb/usbuhci/usb_request.cpp * PURPOSE: USB UHCI device driver. * PROGRAMMERS: * Michael Martin (michael.martin@reactos.org) * Johannes Anderwald (johannes.anderwald@reactos.org) */ #include "usbuhci.h" #define NDEBUG #include class CUSBRequest : public IUHCIRequest { public: STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface); STDMETHODIMP_(ULONG) AddRef() { InterlockedIncrement(&m_Ref); return m_Ref; } STDMETHODIMP_(ULONG) Release() { InterlockedDecrement(&m_Ref); if (!m_Ref) { delete this; return 0; } return m_Ref; } // com IMP_IUSBREQUEST IMP_IUHCIREQUEST // local functions ULONG InternalGetTransferType(); UCHAR InternalGetPidDirection(); UCHAR GetDeviceAddress(); NTSTATUS BuildSetupPacket(); NTSTATUS BuildSetupPacketFromURB(); UCHAR GetEndpointAddress(); USHORT GetMaxPacketSize(); NTSTATUS CreateDescriptor(PUHCI_TRANSFER_DESCRIPTOR *OutDescriptor, IN UCHAR PidCode, ULONG BufferLength); NTSTATUS BuildControlTransferDescriptor(IN PUHCI_QUEUE_HEAD * OutQueueHead); NTSTATUS BuildBulkInterruptTransferDescriptor(IN PUHCI_QUEUE_HEAD * OutQueueHead); NTSTATUS BuildQueueHead(OUT PUHCI_QUEUE_HEAD *OutQueueHead); VOID FreeDescriptor(IN PUHCI_TRANSFER_DESCRIPTOR Descriptor); NTSTATUS BuildTransferDescriptorChain(IN PVOID TransferBuffer, IN ULONG TransferBufferLength, IN UCHAR PidCode, IN UCHAR InitialDataToggle, OUT PUHCI_TRANSFER_DESCRIPTOR * OutFirstDescriptor, OUT PUHCI_TRANSFER_DESCRIPTOR * OutLastDescriptor, OUT PULONG OutTransferBufferOffset, OUT PUCHAR OutDataToggle); // constructor / destructor CUSBRequest(IUnknown *OuterUnknown){} virtual ~CUSBRequest(){} protected: LONG m_Ref; // // memory manager for allocating setup packet / queue head / transfer descriptors // PDMAMEMORYMANAGER m_DmaManager; // // caller provided irp packet containing URB request // PIRP m_Irp; // // transfer buffer length // ULONG m_TransferBufferLength; // // current transfer length // ULONG m_TransferBufferLengthCompleted; // // Total Transfer Length // ULONG m_TotalBytesTransferred; // // transfer buffer MDL // PMDL m_TransferBufferMDL; // // caller provided setup packet // PUSB_DEFAULT_PIPE_SETUP_PACKET m_SetupPacket; // // completion event for callers who initialized request with setup packet // PKEVENT m_CompletionEvent; // // device address for callers who initialized it with device address // UCHAR m_DeviceAddress; // // store end point address // PUSB_ENDPOINT m_EndpointDescriptor; // // allocated setup packet from the DMA pool // PUSB_DEFAULT_PIPE_SETUP_PACKET m_DescriptorPacket; PHYSICAL_ADDRESS m_DescriptorSetupPacket; // // stores the result of the operation // NTSTATUS m_NtStatusCode; ULONG m_UrbStatusCode; // // store device speed // USB_DEVICE_SPEED m_DeviceSpeed; // base PVOID m_Base; }; //---------------------------------------------------------------------------------------- NTSTATUS STDMETHODCALLTYPE CUSBRequest::QueryInterface( IN REFIID refiid, OUT PVOID* Output) { return STATUS_UNSUCCESSFUL; } //---------------------------------------------------------------------------------------- NTSTATUS CUSBRequest::InitializeWithSetupPacket( IN PDMAMEMORYMANAGER DmaManager, IN PUSB_DEFAULT_PIPE_SETUP_PACKET SetupPacket, IN PUSBDEVICE Device, IN OPTIONAL struct _USB_ENDPOINT * EndpointDescriptor, IN OUT ULONG TransferBufferLength, IN OUT PMDL TransferBuffer) { // // sanity checks // PC_ASSERT(DmaManager); PC_ASSERT(SetupPacket); // // initialize packet // m_DmaManager = DmaManager; m_SetupPacket = SetupPacket; m_TransferBufferLength = TransferBufferLength; m_TransferBufferMDL = TransferBuffer; m_DeviceAddress = Device->GetDeviceAddress(); m_EndpointDescriptor = EndpointDescriptor; m_TotalBytesTransferred = 0; m_DeviceSpeed = Device->GetSpeed(); // // Set Length Completed to 0 // m_TransferBufferLengthCompleted = 0; // // allocate completion event // m_CompletionEvent = (PKEVENT)ExAllocatePoolWithTag(NonPagedPool, sizeof(KEVENT), TAG_USBUHCI); if (!m_CompletionEvent) { // // failed to allocate completion event // return STATUS_INSUFFICIENT_RESOURCES; } // // initialize completion event // KeInitializeEvent(m_CompletionEvent, NotificationEvent, FALSE); // // done // return STATUS_SUCCESS; } //---------------------------------------------------------------------------------------- NTSTATUS CUSBRequest::InitializeWithIrp( IN PDMAMEMORYMANAGER DmaManager, IN PUSBDEVICE Device, IN OUT PIRP Irp) { PIO_STACK_LOCATION IoStack; PURB Urb; // // sanity checks // PC_ASSERT(DmaManager); PC_ASSERT(Irp); m_DmaManager = DmaManager; m_TotalBytesTransferred = 0; m_DeviceSpeed = Device->GetSpeed(); // // get current irp stack location // IoStack = IoGetCurrentIrpStackLocation(Irp); // // sanity check // PC_ASSERT(IoStack->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL); PC_ASSERT(IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_USB_SUBMIT_URB); PC_ASSERT(IoStack->Parameters.Others.Argument1 != 0); // // get urb // Urb = (PURB)IoStack->Parameters.Others.Argument1; // // store irp // m_Irp = Irp; // // check function type // switch (Urb->UrbHeader.Function) { case URB_FUNCTION_ISOCH_TRANSFER: { // // there must be at least one packet // ASSERT(Urb->UrbIsochronousTransfer.NumberOfPackets); // // is there data to be transferred // if (Urb->UrbIsochronousTransfer.TransferBufferLength) { // // Check if there is a MDL // if (!Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL) { // // sanity check // PC_ASSERT(Urb->UrbBulkOrInterruptTransfer.TransferBuffer); // // Create one using TransferBuffer // DPRINT("Creating Mdl from Urb Buffer %p Length %lu\n", Urb->UrbBulkOrInterruptTransfer.TransferBuffer, Urb->UrbBulkOrInterruptTransfer.TransferBufferLength); m_TransferBufferMDL = IoAllocateMdl(Urb->UrbBulkOrInterruptTransfer.TransferBuffer, Urb->UrbBulkOrInterruptTransfer.TransferBufferLength, FALSE, FALSE, NULL); if (!m_TransferBufferMDL) { // // failed to allocate mdl // return STATUS_INSUFFICIENT_RESOURCES; } // // build mdl for non paged pool // FIXME: Does hub driver already do this when passing MDL? // MmBuildMdlForNonPagedPool(m_TransferBufferMDL); } else { // // use provided mdl // m_TransferBufferMDL = Urb->UrbIsochronousTransfer.TransferBufferMDL; } } // // save buffer length // m_TransferBufferLength = Urb->UrbIsochronousTransfer.TransferBufferLength; // // Set Length Completed to 0 // m_TransferBufferLengthCompleted = 0; // // get endpoint descriptor // m_EndpointDescriptor = (PUSB_ENDPOINT)Urb->UrbIsochronousTransfer.PipeHandle; // // completed initialization // break; } // // luckily those request have the same structure layout // case URB_FUNCTION_CLASS_INTERFACE: case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE: case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: { // // bulk interrupt transfer // if (Urb->UrbBulkOrInterruptTransfer.TransferBufferLength) { // // Check if there is a MDL // if (!Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL) { // // sanity check // PC_ASSERT(Urb->UrbBulkOrInterruptTransfer.TransferBuffer); // // Create one using TransferBuffer // DPRINT("Creating Mdl from Urb Buffer %p Length %lu\n", Urb->UrbBulkOrInterruptTransfer.TransferBuffer, Urb->UrbBulkOrInterruptTransfer.TransferBufferLength); m_TransferBufferMDL = IoAllocateMdl(Urb->UrbBulkOrInterruptTransfer.TransferBuffer, Urb->UrbBulkOrInterruptTransfer.TransferBufferLength, FALSE, FALSE, NULL); if (!m_TransferBufferMDL) { // // failed to allocate mdl // return STATUS_INSUFFICIENT_RESOURCES; } // // build mdl for non paged pool // FIXME: Does hub driver already do this when passing MDL? // MmBuildMdlForNonPagedPool(m_TransferBufferMDL); // // Keep that ehci created the MDL and needs to free it. // } else { m_TransferBufferMDL = Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL; } // // save buffer length // m_TransferBufferLength = Urb->UrbBulkOrInterruptTransfer.TransferBufferLength; // // Set Length Completed to 0 // m_TransferBufferLengthCompleted = 0; // // get endpoint descriptor // m_EndpointDescriptor = (PUSB_ENDPOINT)Urb->UrbBulkOrInterruptTransfer.PipeHandle; } break; } default: DPRINT1("URB Function: not supported %x\n", Urb->UrbHeader.Function); PC_ASSERT(FALSE); } // // done // return STATUS_SUCCESS; } //---------------------------------------------------------------------------------------- BOOLEAN CUSBRequest::IsRequestComplete() { // // FIXME: check if request was split // // // Check if the transfer was completed, only valid for Bulk Transfers // if ((m_TransferBufferLengthCompleted < m_TransferBufferLength) && (GetTransferType() == USB_ENDPOINT_TYPE_BULK)) { // // Transfer not completed // return FALSE; } return TRUE; } //---------------------------------------------------------------------------------------- ULONG CUSBRequest::GetTransferType() { // // call internal implementation // return InternalGetTransferType(); } USHORT CUSBRequest::GetMaxPacketSize() { if (!m_EndpointDescriptor) { // // control request // return 0; } ASSERT(m_Irp); ASSERT(m_EndpointDescriptor); // // return max packet size // return m_EndpointDescriptor->EndPointDescriptor.wMaxPacketSize; } UCHAR CUSBRequest::GetInterval() { ASSERT(m_EndpointDescriptor); ASSERT((m_EndpointDescriptor->EndPointDescriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_INTERRUPT); // // return interrupt interval // return m_EndpointDescriptor->EndPointDescriptor.bInterval; } UCHAR CUSBRequest::GetEndpointAddress() { if (!m_EndpointDescriptor) { // // control request // return 0; } ASSERT(m_Irp); ASSERT(m_EndpointDescriptor); // // endpoint number is between 1-15 // return (m_EndpointDescriptor->EndPointDescriptor.bEndpointAddress & 0xF); } //---------------------------------------------------------------------------------------- ULONG CUSBRequest::InternalGetTransferType() { ULONG TransferType; // // check if an irp is provided // if (m_Irp) { ASSERT(m_EndpointDescriptor); // // end point is defined in the low byte of bmAttributes // TransferType = (m_EndpointDescriptor->EndPointDescriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK); } else { // // initialized with setup packet, must be a control transfer // TransferType = USB_ENDPOINT_TYPE_CONTROL; } // // done // return TransferType; } UCHAR CUSBRequest::InternalGetPidDirection() { if (m_EndpointDescriptor) { // // end point direction is highest bit in bEndpointAddress // return (m_EndpointDescriptor->EndPointDescriptor.bEndpointAddress & USB_ENDPOINT_DIRECTION_MASK) >> 7; } else { // // request arrives on the control pipe, extract direction from setup packet // ASSERT(m_SetupPacket); return (m_SetupPacket->bmRequestType.B >> 7); } } //---------------------------------------------------------------------------------------- UCHAR CUSBRequest::GetDeviceAddress() { PIO_STACK_LOCATION IoStack; PURB Urb; PUSBDEVICE UsbDevice; // // check if there is an irp provided // if (!m_Irp) { // // used provided address // return m_DeviceAddress; } // // get current stack location // IoStack = IoGetCurrentIrpStackLocation(m_Irp); // // get contained urb // Urb = (PURB)IoStack->Parameters.Others.Argument1; // // check if there is a pipe handle provided // if (Urb->UrbHeader.UsbdDeviceHandle) { // // there is a device handle provided // UsbDevice = (PUSBDEVICE)Urb->UrbHeader.UsbdDeviceHandle; // // return device address // return UsbDevice->GetDeviceAddress(); } // // no device handle provided, it is the host root bus // return 0; } //---------------------------------------------------------------------------------------- NTSTATUS CUSBRequest::GetEndpointDescriptor( struct _UHCI_QUEUE_HEAD ** OutQueueHead) { NTSTATUS Status = STATUS_UNSUCCESSFUL; ULONG TransferType; // get transfer type TransferType = InternalGetTransferType(); if (TransferType == USB_ENDPOINT_TYPE_CONTROL) { // // build queue head // Status = BuildControlTransferDescriptor(OutQueueHead); } else if (TransferType == USB_ENDPOINT_TYPE_INTERRUPT || TransferType == USB_ENDPOINT_TYPE_BULK) { // // build queue head // Status = BuildBulkInterruptTransferDescriptor(OutQueueHead); } if (!NT_SUCCESS(Status)) { // // failed // return Status; } // // store result // (*OutQueueHead)->Request = PVOID(this); // // done // return STATUS_SUCCESS; } //---------------------------------------------------------------------------------------- VOID CUSBRequest::GetResultStatus( OUT OPTIONAL NTSTATUS * NtStatusCode, OUT OPTIONAL PULONG UrbStatusCode) { // // sanity check // PC_ASSERT(m_CompletionEvent); // // wait for the operation to complete // KeWaitForSingleObject(m_CompletionEvent, Executive, KernelMode, FALSE, NULL); // // copy status // if (NtStatusCode) { *NtStatusCode = m_NtStatusCode; } // // copy urb status // if (UrbStatusCode) { *UrbStatusCode = m_UrbStatusCode; } } //----------------------------------------------------------------------------------------- NTSTATUS CUSBRequest::CreateDescriptor( OUT PUHCI_TRANSFER_DESCRIPTOR *OutDescriptor, IN UCHAR PidCode, ULONG BufferLength) { PUHCI_TRANSFER_DESCRIPTOR Descriptor; PHYSICAL_ADDRESS Address; NTSTATUS Status; // // allocate descriptor // Status = m_DmaManager->Allocate(sizeof(UHCI_TRANSFER_DESCRIPTOR), (PVOID*)&Descriptor, &Address); if (!NT_SUCCESS(Status)) { DPRINT1("[USBUHCI] Failed to allocate descriptor\n"); return Status; } // // init descriptor // Descriptor->PhysicalAddress = Address.LowPart; Descriptor->Status = TD_STATUS_ACTIVE; if (InternalGetTransferType() == USB_ENDPOINT_TYPE_ISOCHRONOUS) { // // isochronous transfer descriptor // Descriptor->Status |= TD_CONTROL_ISOCHRONOUS; } else { // // error count // Descriptor->Status |= TD_CONTROL_3_ERRORS; if (PidCode == TD_TOKEN_IN && (InternalGetTransferType() != USB_ENDPOINT_TYPE_CONTROL)) { // // enable short packet detect for bulk & interrupt // Descriptor->Status |= TD_CONTROL_SPD; } } // // is it low speed device // if (m_DeviceSpeed == UsbLowSpeed) { // // low speed device // Descriptor->Status |= TD_CONTROL_LOWSPEED; } // // store buffer size // Descriptor->BufferSize = BufferLength; // // is there a buffer // if(BufferLength) { // // store buffer length // Descriptor->Token = (BufferLength - 1) << TD_TOKEN_MAXLEN_SHIFT; } else { // // no buffer magic constant // Descriptor->Token = TD_TOKEN_NULL_DATA; } // // store address & endpoint number // Descriptor->Token |= GetEndpointAddress() << TD_TOKEN_ENDPTADDR_SHIFT; Descriptor->Token |= GetDeviceAddress() << TD_TOKEN_DEVADDR_SHIFT | PidCode; if (BufferLength) { // // allocate buffer for descriptor // Status = m_DmaManager->Allocate(BufferLength, (PVOID*)&Descriptor->BufferLogical, &Address); if (!NT_SUCCESS(Status)) { DPRINT1("[USBUHCI] Failed to allocate descriptor buffer length %lu\n", BufferLength); m_DmaManager->Release(Descriptor, sizeof(UHCI_TRANSFER_DESCRIPTOR)); return Status; } // // store address // Descriptor->BufferPhysical = Address.LowPart; } // // done // *OutDescriptor = Descriptor; return STATUS_SUCCESS; } NTSTATUS CUSBRequest::BuildTransferDescriptorChain( IN PVOID TransferBuffer, IN ULONG TransferBufferLength, IN UCHAR PidCode, IN UCHAR InitialDataToggle, OUT PUHCI_TRANSFER_DESCRIPTOR * OutFirstDescriptor, OUT PUHCI_TRANSFER_DESCRIPTOR * OutLastDescriptor, OUT PULONG OutTransferBufferOffset, OUT PUCHAR OutDataToggle) { PUHCI_TRANSFER_DESCRIPTOR FirstDescriptor = NULL, CurrentDescriptor, LastDescriptor = NULL; ULONG TransferBufferOffset = 0; NTSTATUS Status; ULONG MaxPacketSize, CurrentBufferSize; // // FIXME FIXME FIXME FIXME FIXME // if (GetDeviceSpeed() == UsbLowSpeed) { // // low speed use max 8 bytes // MaxPacketSize = 8; } else { if (m_EndpointDescriptor) { // // use endpoint size // MaxPacketSize = m_EndpointDescriptor->EndPointDescriptor.wMaxPacketSize; } else { // // use max 64 bytes // MaxPacketSize = 64; } } do { // // determine current packet size // CurrentBufferSize = min(MaxPacketSize, TransferBufferLength - TransferBufferOffset); // // allocate descriptor // Status = CreateDescriptor(&CurrentDescriptor, PidCode, CurrentBufferSize); if (!NT_SUCCESS(Status)) { // // failed to allocate queue head // DPRINT1("[UHCI] Failed to create descriptor\n"); ASSERT(FALSE); return Status; } if (PidCode == TD_TOKEN_OUT) { // // copy buffer // RtlCopyMemory(CurrentDescriptor->BufferLogical, (PVOID)((ULONG_PTR)TransferBuffer + TransferBufferOffset), CurrentBufferSize); } else { // // store user buffer // CurrentDescriptor->UserBuffer = (PVOID)((ULONG_PTR)TransferBuffer + TransferBufferOffset); } if (!FirstDescriptor) { // // first descriptor // FirstDescriptor = CurrentDescriptor; } else { // // link descriptor // LastDescriptor->LinkPhysical = CurrentDescriptor->PhysicalAddress | TD_DEPTH_FIRST; LastDescriptor->NextLogicalDescriptor = (PVOID)CurrentDescriptor; } if (InitialDataToggle) { // // apply data toggle // CurrentDescriptor->Token |= TD_TOKEN_DATA1; } // // re-run // LastDescriptor = CurrentDescriptor; TransferBufferOffset += CurrentBufferSize; InitialDataToggle = !InitialDataToggle; }while(TransferBufferOffset < TransferBufferLength); if (OutTransferBufferOffset) { // // store transfer buffer length // *OutTransferBufferOffset = TransferBufferOffset; } if (OutFirstDescriptor) { // // store first descriptor // *OutFirstDescriptor = FirstDescriptor; } if (OutLastDescriptor) { // // store last descriptor // *OutLastDescriptor = CurrentDescriptor; } if (OutDataToggle) { // // store data toggle // *OutDataToggle = InitialDataToggle; } // // done // return STATUS_SUCCESS; } NTSTATUS CUSBRequest::BuildQueueHead( OUT PUHCI_QUEUE_HEAD *OutQueueHead) { PUHCI_QUEUE_HEAD QueueHead; NTSTATUS Status; PHYSICAL_ADDRESS Address; // // allocate queue head // Status = m_DmaManager->Allocate(sizeof(UHCI_QUEUE_HEAD), (PVOID*)&QueueHead, &Address); if (!NT_SUCCESS(Status)) { // // failed to allocate queue head // DPRINT1("[UHCI] Failed to create queue head\n"); return Status; } // // store address // QueueHead->PhysicalAddress = Address.LowPart; QueueHead->ElementPhysical = Address.LowPart; // // store result // *OutQueueHead = QueueHead; return STATUS_SUCCESS; } VOID CUSBRequest::FreeDescriptor( IN PUHCI_TRANSFER_DESCRIPTOR Descriptor) { if (Descriptor->BufferLogical) { // // free buffer // m_DmaManager->Release(Descriptor->BufferLogical, Descriptor->BufferSize); } // // free descriptors // m_DmaManager->Release(Descriptor, sizeof(UHCI_TRANSFER_DESCRIPTOR)); } NTSTATUS CUSBRequest::BuildBulkInterruptTransferDescriptor( IN PUHCI_QUEUE_HEAD * OutQueueHead) { NTSTATUS Status; PUHCI_QUEUE_HEAD QueueHead; PUHCI_TRANSFER_DESCRIPTOR FirstDescriptor, LastDescriptor; ULONG ChainDescriptorLength; BOOLEAN Direction; PVOID Buffer; ULONG BufferSize; // create queue head Status = BuildQueueHead(&QueueHead); if (!NT_SUCCESS(Status)) { // failed to allocate descriptor DPRINT1("[UHCI] Failed to create queue head\n"); return Status; } // get direction Direction = InternalGetPidDirection(); if (!m_Base) { // get buffer base m_Base = MmGetMdlVirtualAddress(m_TransferBufferMDL); // sanity check ASSERT(m_Base != NULL); } // get new buffer offset Buffer = (PVOID)((ULONG_PTR)m_Base + m_TransferBufferLengthCompleted); // FIXME determine buffer limit BufferSize = min(m_TransferBufferLength - m_TransferBufferLengthCompleted, PAGE_SIZE); // create descriptor chain Status = BuildTransferDescriptorChain(Buffer, BufferSize, Direction ? TD_TOKEN_IN : TD_TOKEN_OUT, m_EndpointDescriptor->DataToggle, &FirstDescriptor, &LastDescriptor, &ChainDescriptorLength, NULL); // adjust buffer offset m_TransferBufferLengthCompleted += ChainDescriptorLength; // fire interrupt when the last descriptor is complete LastDescriptor->Status |= TD_CONTROL_IOC; LastDescriptor->LinkPhysical = TD_TERMINATE; LastDescriptor->NextLogicalDescriptor = NULL; // link queue head with first data descriptor descriptor QueueHead->NextElementDescriptor = (PVOID)FirstDescriptor; QueueHead->ElementPhysical = FirstDescriptor->PhysicalAddress; // store result *OutQueueHead = QueueHead; return STATUS_SUCCESS; } NTSTATUS CUSBRequest::BuildControlTransferDescriptor( IN PUHCI_QUEUE_HEAD * OutQueueHead) { PUHCI_TRANSFER_DESCRIPTOR SetupDescriptor, StatusDescriptor, FirstDescriptor, LastDescriptor; PUHCI_QUEUE_HEAD QueueHead; BOOLEAN Direction; NTSTATUS Status; ULONG ChainDescriptorLength; // // create queue head // Status = BuildQueueHead(&QueueHead); if (!NT_SUCCESS(Status)) { // // failed to allocate descriptor // DPRINT1("[UHCI] Failed to create queue head\n"); return Status; } // // get direction // Direction = InternalGetPidDirection(); // // build setup descriptor // Status = CreateDescriptor(&SetupDescriptor, TD_TOKEN_SETUP, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET)); if (!NT_SUCCESS(Status)) { // // failed to allocate descriptor // DPRINT1("[UHCI] Failed to create setup descriptor\n"); m_DmaManager->Release(QueueHead, sizeof(UHCI_QUEUE_HEAD)); return Status; } // // build status descriptor // Status = CreateDescriptor(&StatusDescriptor, Direction ? TD_TOKEN_OUT : TD_TOKEN_IN, 0); if (!NT_SUCCESS(Status)) { // // failed to allocate descriptor // DPRINT1("[UHCI] Failed to create status descriptor\n"); FreeDescriptor(SetupDescriptor); m_DmaManager->Release(QueueHead, sizeof(UHCI_QUEUE_HEAD)); return Status; } if (m_SetupPacket) { // // copy setup packet // RtlCopyMemory(SetupDescriptor->BufferLogical, m_SetupPacket, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET)); } else { // // generate setup packet from urb // ASSERT(FALSE); } // // init status descriptor // StatusDescriptor->Status |= TD_CONTROL_IOC; StatusDescriptor->Token |= TD_TOKEN_DATA1; StatusDescriptor->LinkPhysical = TD_TERMINATE; StatusDescriptor->NextLogicalDescriptor = NULL; if (m_TransferBufferLength) { // // create descriptor chain // Status = BuildTransferDescriptorChain(MmGetMdlVirtualAddress(m_TransferBufferMDL), m_TransferBufferLength, Direction ? TD_TOKEN_IN : TD_TOKEN_OUT, TRUE, &FirstDescriptor, &LastDescriptor, &ChainDescriptorLength, NULL); if (!NT_SUCCESS(Status)) { // // failed to allocate descriptor // DPRINT1("[UHCI] Failed to create descriptor chain\n"); FreeDescriptor(SetupDescriptor); FreeDescriptor(StatusDescriptor); m_DmaManager->Release(QueueHead, sizeof(UHCI_QUEUE_HEAD)); return Status; } // // link setup descriptor to first data descriptor // SetupDescriptor->LinkPhysical = FirstDescriptor->PhysicalAddress | TD_DEPTH_FIRST; SetupDescriptor->NextLogicalDescriptor = (PVOID)FirstDescriptor; // // link last data descriptor to status descriptor // LastDescriptor->LinkPhysical = StatusDescriptor->PhysicalAddress | TD_DEPTH_FIRST; LastDescriptor->NextLogicalDescriptor = (PVOID)StatusDescriptor; } else { // // directly link setup to status descriptor // SetupDescriptor->LinkPhysical = StatusDescriptor->PhysicalAddress | TD_DEPTH_FIRST; SetupDescriptor->NextLogicalDescriptor = (PVOID)StatusDescriptor; } // // link queue head with setup descriptor // QueueHead->NextElementDescriptor = (PVOID)SetupDescriptor; QueueHead->ElementPhysical = SetupDescriptor->PhysicalAddress; // // store result // *OutQueueHead = QueueHead; return STATUS_SUCCESS; } USB_DEVICE_SPEED CUSBRequest::GetDeviceSpeed() { return m_DeviceSpeed; } VOID CUSBRequest::FreeEndpointDescriptor( struct _UHCI_QUEUE_HEAD * OutDescriptor) { PUHCI_TRANSFER_DESCRIPTOR Descriptor, NextDescriptor; ULONG ErrorCount; UCHAR DataToggle = 0; ULONG Index = 0; // // grab first transfer descriptor // Descriptor = (PUHCI_TRANSFER_DESCRIPTOR)OutDescriptor->NextElementDescriptor; while(Descriptor) { // get data toggle DataToggle = (Descriptor->Token >> TD_TOKEN_DATA_TOGGLE_SHIFT) & 0x01; if (Descriptor->Status & TD_ERROR_MASK) { // // error happened // DPRINT1("[USBUHCI] Error detected at descriptor %p Physical %x\n", Descriptor, Descriptor->PhysicalAddress); // // get error count // ErrorCount = (Descriptor->Status >> TD_ERROR_COUNT_SHIFT) & TD_ERROR_COUNT_MASK; if (ErrorCount == 0) { // // error retry count elapsed // m_NtStatusCode = STATUS_UNSUCCESSFUL; if (Descriptor->Status & TD_STATUS_ERROR_BUFFER) { DPRINT1("[USBUHCI] Buffer Error detected in descriptor %p Index %lu\n", Descriptor, Index); m_UrbStatusCode = USBD_STATUS_DATA_BUFFER_ERROR; } else if (Descriptor->Status & TD_STATUS_ERROR_TIMEOUT) { DPRINT1("[USBUHCI] Timeout detected in descriptor %p Index %lu\n", Descriptor, Index); m_UrbStatusCode = USBD_STATUS_TIMEOUT; } else if (Descriptor->Status & TD_STATUS_ERROR_NAK) { DPRINT1("[USBUHCI] Unexpected pid detected in descriptor %p Index %lu\n", Descriptor, Index); m_UrbStatusCode = USBD_STATUS_UNEXPECTED_PID; } else if (Descriptor->Status & TD_STATUS_ERROR_BITSTUFF) { DPRINT1("[USBUHCI] BitStuff detected in descriptor %p Index %lu\n", Descriptor, Index); m_UrbStatusCode = USBD_STATUS_BTSTUFF; } } else if (Descriptor->Status & TD_STATUS_ERROR_BABBLE) { // // babble error // DPRINT1("[USBUHCI] Babble detected in descriptor %p Index %lu\n", Descriptor, Index); m_UrbStatusCode = USBD_STATUS_BABBLE_DETECTED; } else { // // stall detected // DPRINT1("[USBUHCI] Stall detected Descriptor %p Index %lu\n", Descriptor, Index); m_UrbStatusCode = USBD_STATUS_STALL_PID; } } else { // // FIXME detect actual length // if (Descriptor->UserBuffer) { // // copy contents back // RtlCopyMemory(Descriptor->UserBuffer, Descriptor->BufferLogical, Descriptor->BufferSize); } } // // move to next descriptor // NextDescriptor = (PUHCI_TRANSFER_DESCRIPTOR)Descriptor->NextLogicalDescriptor; // // free endpoint descriptor // FreeDescriptor(Descriptor); // // move to next // Descriptor = NextDescriptor; Index++; } // // now free queue head // m_DmaManager->Release(OutDescriptor, sizeof(UHCI_QUEUE_HEAD)); // is there an endpoint descriptor if (m_EndpointDescriptor) { // invert last data toggle m_EndpointDescriptor->DataToggle = (DataToggle == 0); } } VOID CUSBRequest::CompletionCallback() { PIO_STACK_LOCATION IoStack; PURB Urb; DPRINT("CUSBRequest::CompletionCallback\n"); if (m_Irp) { // // set irp completion status // m_Irp->IoStatus.Status = m_NtStatusCode; // // get current irp stack location // IoStack = IoGetCurrentIrpStackLocation(m_Irp); // // get urb // Urb = (PURB)IoStack->Parameters.Others.Argument1; // // store urb status // Urb->UrbHeader.Status = m_UrbStatusCode; // // Check if the MDL was created // if (!Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL) { // // Free Mdl // IoFreeMdl(m_TransferBufferMDL); } // // FIXME calculate length // // // complete request // IoCompleteRequest(m_Irp, IO_NO_INCREMENT); } else { // // signal completion event // PC_ASSERT(m_CompletionEvent); KeSetEvent(m_CompletionEvent, 0, FALSE); } } //----------------------------------------------------------------------------------------- NTSTATUS NTAPI InternalCreateUSBRequest( PUSBREQUEST *OutRequest) { PUSBREQUEST This; // // allocate requests // This = new(NonPagedPool, TAG_USBUHCI) CUSBRequest(0); if (!This) { // // failed to allocate // return STATUS_INSUFFICIENT_RESOURCES; } // // add reference count // This->AddRef(); // // return result // *OutRequest = (PUSBREQUEST)This; // // done // return STATUS_SUCCESS; }