/* * PROJECT: ReactOS Universal Serial Bus Bulk Driver Library * LICENSE: GPL - See COPYING in the top level directory * FILE: lib/drivers/libusb/usb_device.cpp * PURPOSE: USB Common Driver Library. * PROGRAMMERS: * Michael Martin (michael.martin@reactos.org) * Johannes Anderwald (johannes.anderwald@reactos.org) */ #include "libusb.h" #define NDEBUG #include class CUSBDevice : public IUSBDevice { 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; } // IUSBDevice interface functions virtual NTSTATUS Initialize(IN PHUBCONTROLLER HubController, IN PUSBHARDWAREDEVICE Device, IN PVOID Parent, IN ULONG Port, IN ULONG PortStatus); virtual BOOLEAN IsHub(); virtual NTSTATUS GetParent(PVOID * Parent); virtual UCHAR GetDeviceAddress(); virtual ULONG GetPort(); virtual USB_DEVICE_SPEED GetSpeed(); virtual USB_DEVICE_TYPE GetType(); virtual ULONG GetState(); virtual void SetDeviceHandleData(PVOID Data); virtual NTSTATUS SetDeviceAddress(UCHAR DeviceAddress); virtual void GetDeviceDescriptor(PUSB_DEVICE_DESCRIPTOR DeviceDescriptor); virtual UCHAR GetConfigurationValue(); virtual NTSTATUS SubmitIrp(PIRP Irp); virtual VOID GetConfigurationDescriptors(IN PUSB_CONFIGURATION_DESCRIPTOR ConfigDescriptorBuffer, IN ULONG BufferLength, OUT PULONG OutBufferLength); virtual ULONG GetConfigurationDescriptorsLength(); virtual NTSTATUS SubmitSetupPacket(IN PUSB_DEFAULT_PIPE_SETUP_PACKET SetupPacket, OUT ULONG BufferLength, OUT PVOID Buffer); virtual NTSTATUS SelectConfiguration(IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor, IN PUSBD_INTERFACE_INFORMATION Interface, OUT USBD_CONFIGURATION_HANDLE *ConfigurationHandle); virtual NTSTATUS SelectInterface(IN USBD_CONFIGURATION_HANDLE ConfigurationHandle, IN OUT PUSBD_INTERFACE_INFORMATION Interface); virtual NTSTATUS AbortPipe(IN PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor); virtual UCHAR GetMaxPacketSize(); // local function virtual NTSTATUS CommitIrp(PIRP Irp); virtual NTSTATUS CommitSetupPacket(PUSB_DEFAULT_PIPE_SETUP_PACKET Packet, IN OPTIONAL PUSB_ENDPOINT EndpointDescriptor, IN ULONG BufferLength, IN OUT PMDL Mdl); virtual NTSTATUS CreateConfigurationDescriptor(UCHAR ConfigurationIndex); virtual NTSTATUS CreateDeviceDescriptor(); virtual VOID DumpDeviceDescriptor(PUSB_DEVICE_DESCRIPTOR DeviceDescriptor); virtual VOID DumpConfigurationDescriptor(PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor); virtual NTSTATUS GetConfigurationDescriptor(UCHAR ConfigurationIndex, USHORT BufferSize, PVOID Buffer); virtual NTSTATUS BuildInterfaceDescriptor(IN ULONG ConfigurationIndex, IN PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor, OUT PUSBD_INTERFACE_INFORMATION InterfaceInfo, OUT PUSB_INTERFACE *OutUsbInterface); // constructor / destructor CUSBDevice(IUnknown *OuterUnknown){} virtual ~CUSBDevice(){} protected: LONG m_Ref; PHUBCONTROLLER m_HubController; PUSBHARDWAREDEVICE m_Device; PVOID m_Parent; ULONG m_Port; UCHAR m_DeviceAddress; PVOID m_Data; UCHAR m_ConfigurationIndex; KSPIN_LOCK m_Lock; USB_DEVICE_DESCRIPTOR m_DeviceDescriptor; ULONG m_PortStatus; PUSBQUEUE m_Queue; PDMAMEMORYMANAGER m_DmaManager; LPCSTR m_USBType; PUSB_CONFIGURATION m_ConfigurationDescriptors; }; //---------------------------------------------------------------------------------------- NTSTATUS STDMETHODCALLTYPE CUSBDevice::QueryInterface( IN REFIID refiid, OUT PVOID* Output) { return STATUS_UNSUCCESSFUL; } //---------------------------------------------------------------------------------------- NTSTATUS CUSBDevice::Initialize( IN PHUBCONTROLLER HubController, IN PUSBHARDWAREDEVICE Device, IN PVOID Parent, IN ULONG Port, IN ULONG PortStatus) { NTSTATUS Status; // // initialize members // m_HubController = HubController; m_Device = Device; m_Parent = Parent; m_Port = Port; m_PortStatus = PortStatus; m_USBType = m_Device->GetUSBType(); // // initialize device lock // KeInitializeSpinLock(&m_Lock); // // no device address has been set yet // m_DeviceAddress = 0; // // get usb request queue // Status = m_Device->GetUSBQueue(&m_Queue); if (!NT_SUCCESS(Status)) { // // failed to get usb queue // DPRINT1("[%s] GetUsbQueue failed with %x\n", m_USBType, Status); return Status; } // // get dma manager // Status = m_Device->GetDMA(&m_DmaManager); if (!NT_SUCCESS(Status)) { // // failed to get dma manager // DPRINT1("[%s] GetDMA failed with %x\n", m_USBType, Status); return Status; } // // sanity check // PC_ASSERT(m_DmaManager); // // get device descriptor // Status = CreateDeviceDescriptor(); if (!NT_SUCCESS(Status)) { // // failed to get device descriptor // DPRINT1("[%s] Failed to get device descriptor with %x\n", m_USBType, Status); return Status; } // // done // return Status; } //---------------------------------------------------------------------------------------- BOOLEAN CUSBDevice::IsHub() { // // USB Standard Device Class see http://www.usb.org/developers/defined_class/#BaseClass09h // for details // return (m_DeviceDescriptor.bDeviceClass == 0x09 && m_DeviceDescriptor.bDeviceSubClass == 0x00); } //---------------------------------------------------------------------------------------- NTSTATUS CUSBDevice::GetParent( PVOID * Parent) { // // returns parent // *Parent = m_Parent; // // done // return STATUS_SUCCESS; } //---------------------------------------------------------------------------------------- UCHAR CUSBDevice::GetDeviceAddress() { // // get device address // return m_DeviceAddress; } //---------------------------------------------------------------------------------------- ULONG CUSBDevice::GetPort() { // // get port to which this device is connected to // return m_Port; } //---------------------------------------------------------------------------------------- USB_DEVICE_SPEED CUSBDevice::GetSpeed() { if (m_PortStatus & USB_PORT_STATUS_LOW_SPEED) { // // low speed device // return UsbLowSpeed; } else if (m_PortStatus & USB_PORT_STATUS_HIGH_SPEED) { // // high speed device // return UsbHighSpeed; } // // default to full speed // return UsbFullSpeed; } //---------------------------------------------------------------------------------------- USB_DEVICE_TYPE CUSBDevice::GetType() { // // device is encoded into bcdUSB // if (m_DeviceDescriptor.bcdUSB == 0x110) { // // USB 1.1 device // return Usb11Device; } else if (m_DeviceDescriptor.bcdUSB == 0x200) { // // USB 2.0 device // return Usb20Device; } DPRINT1("[%s] GetType Unknown bcdUSB Type %x\n", m_USBType, m_DeviceDescriptor.bcdUSB); //PC_ASSERT(FALSE); return Usb11Device; } //---------------------------------------------------------------------------------------- ULONG CUSBDevice::GetState() { UNIMPLEMENTED; return FALSE; } //---------------------------------------------------------------------------------------- void CUSBDevice::SetDeviceHandleData( PVOID Data) { // // set device data, for debugging issues // m_Data = Data; } //---------------------------------------------------------------------------------------- NTSTATUS CUSBDevice::SetDeviceAddress( UCHAR DeviceAddress) { PUSB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; NTSTATUS Status; UCHAR Index; DPRINT1("[%s] SetDeviceAddress> Address %x\n", m_USBType, DeviceAddress); CtrlSetup = (PUSB_DEFAULT_PIPE_SETUP_PACKET)ExAllocatePoolWithTag(NonPagedPool, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET), TAG_USBLIB); if (!CtrlSetup) return STATUS_INSUFFICIENT_RESOURCES; // zero request RtlZeroMemory(CtrlSetup, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET)); // initialize request CtrlSetup->bRequest = USB_REQUEST_SET_ADDRESS; CtrlSetup->wValue.W = DeviceAddress; // set device address Status = CommitSetupPacket(CtrlSetup, NULL, 0, NULL); // free setup packet ExFreePoolWithTag(CtrlSetup, TAG_USBLIB); // check for success if (!NT_SUCCESS(Status)) { // failed to set device address DPRINT1("[%s] SetDeviceAddress> failed to set device address with %lx Address %x\n", m_USBType, Status, DeviceAddress); return Status; } // lets have a short nap KeStallExecutionProcessor(300); // store new device address m_DeviceAddress = DeviceAddress; // fetch device descriptor Status = CreateDeviceDescriptor(); if (!NT_SUCCESS(Status)) { DPRINT1("[%s] SetDeviceAddress failed to retrieve device descriptor with device address set Error %lx\n", m_USBType, Status); // return error status return Status; } // check for invalid device descriptor if (m_DeviceDescriptor.bLength != sizeof(USB_DEVICE_DESCRIPTOR) || m_DeviceDescriptor.bDescriptorType != USB_DEVICE_DESCRIPTOR_TYPE || m_DeviceDescriptor.bNumConfigurations == 0) { // failed to retrieve device descriptor DPRINT1("[%s] SetDeviceAddress> device returned bogus device descriptor\n", m_USBType); DumpDeviceDescriptor(&m_DeviceDescriptor); // return error status return STATUS_UNSUCCESSFUL; } // dump device descriptor DumpDeviceDescriptor(&m_DeviceDescriptor); // sanity checks PC_ASSERT(m_DeviceDescriptor.bNumConfigurations); // allocate configuration descriptor m_ConfigurationDescriptors = (PUSB_CONFIGURATION) ExAllocatePoolWithTag(NonPagedPool, sizeof(USB_CONFIGURATION) * m_DeviceDescriptor.bNumConfigurations, TAG_USBLIB); // zero configuration descriptor RtlZeroMemory(m_ConfigurationDescriptors, sizeof(USB_CONFIGURATION) * m_DeviceDescriptor.bNumConfigurations); // retrieve the configuration descriptors for (Index = 0; Index < m_DeviceDescriptor.bNumConfigurations; Index++) { // retrieve configuration descriptors from device Status = CreateConfigurationDescriptor(Index); if (!NT_SUCCESS(Status)) { DPRINT1("[%s] SetDeviceAddress> failed to retrieve configuration %lu\n", m_USBType, Index); break; } } // // done // return Status; } //---------------------------------------------------------------------------------------- void CUSBDevice::GetDeviceDescriptor( PUSB_DEVICE_DESCRIPTOR DeviceDescriptor) { RtlMoveMemory(DeviceDescriptor, &m_DeviceDescriptor, sizeof(USB_DEVICE_DESCRIPTOR)); } //---------------------------------------------------------------------------------------- UCHAR CUSBDevice::GetConfigurationValue() { // // return configuration index // return m_ConfigurationIndex; } //---------------------------------------------------------------------------------------- NTSTATUS CUSBDevice::CommitIrp( PIRP Irp) { NTSTATUS Status; PUSBREQUEST Request; if (!m_Queue || !m_DmaManager) { // // no queue, wtf? // DPRINT1("[%s] CommitIrp> no queue / dma !!!\n", m_USBType); return STATUS_UNSUCCESSFUL; } // // build usb request // Status = m_Queue->CreateUSBRequest(&Request); if (!NT_SUCCESS(Status)) { // // failed to build request // DPRINT1("[%s] CommitIrp> CreateUSBRequest failed with %lx\n", m_USBType, Status); return Status; } // // initialize request // Status = Request->InitializeWithIrp(m_DmaManager, PUSBDEVICE(this), Irp); // // mark irp as pending // IoMarkIrpPending(Irp); // // now add the request // Status = m_Queue->AddUSBRequest(Request); if (!NT_SUCCESS(Status)) { // // failed to add request // DPRINT1("[%s] failed add request to queue with %lx\n", m_USBType, Status); Request->Release(); return Status; } // // done // return STATUS_PENDING; } //---------------------------------------------------------------------------------------- NTSTATUS CUSBDevice::SubmitIrp( PIRP Irp) { KIRQL OldLevel; NTSTATUS Status; // // acquire device lock // KeAcquireSpinLock(&m_Lock, &OldLevel); // // commit urb // Status = CommitIrp(Irp); // // release lock // KeReleaseSpinLock(&m_Lock, OldLevel); return Status; } //---------------------------------------------------------------------------------------- NTSTATUS CUSBDevice::CommitSetupPacket( IN PUSB_DEFAULT_PIPE_SETUP_PACKET Packet, IN OPTIONAL PUSB_ENDPOINT EndpointDescriptor, IN ULONG BufferLength, IN OUT PMDL Mdl) { NTSTATUS Status; PUSBREQUEST Request; if (!m_Queue) { // // no queue, wtf? // DPRINT1("[%s] CommitSetupPacket> no queue!!!\n", m_USBType); return STATUS_UNSUCCESSFUL; } // // build usb request // Status = m_Queue->CreateUSBRequest(&Request); if (!NT_SUCCESS(Status)) { // // failed to build request // DPRINT1("[%s] CommitSetupPacket> CreateUSBRequest failed with %x\n", m_USBType, Status); return Status; } // // initialize request // Status = Request->InitializeWithSetupPacket(m_DmaManager, Packet, PUSBDEVICE(this), EndpointDescriptor, BufferLength, Mdl); if (!NT_SUCCESS(Status)) { // // failed to initialize request // DPRINT1("[%s] CommitSetupPacket failed to initialize usb request with %x\n", m_USBType, Status); Request->Release(); return Status; } // // now add the request // Status = m_Queue->AddUSBRequest(Request); if (!NT_SUCCESS(Status)) { // // failed to add request // DPRINT1("[%s] CommitSetupPacket> failed add request to queue with %x\n", m_USBType, Status); Request->Release(); return Status; } // // get the result code when the operation has been finished // Request->GetResultStatus(&Status, NULL); // // release request // Request->Release(); // // done // return Status; } //---------------------------------------------------------------------------------------- NTSTATUS CUSBDevice::CreateDeviceDescriptor() { USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; PMDL Mdl; NTSTATUS Status; PVOID Buffer; // // zero descriptor // RtlZeroMemory(&m_DeviceDescriptor, sizeof(USB_DEVICE_DESCRIPTOR)); RtlZeroMemory(&CtrlSetup, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET)); // // setup request // CtrlSetup.bRequest = USB_REQUEST_GET_DESCRIPTOR; CtrlSetup.wValue.HiByte = USB_DEVICE_DESCRIPTOR_TYPE; CtrlSetup.wLength = sizeof(USB_DEVICE_DESCRIPTOR); CtrlSetup.bmRequestType.B = 0x80; // // allocate buffer // Buffer = ExAllocatePool(NonPagedPool, PAGE_SIZE); if (!Buffer) { // // failed to allocate // return STATUS_INSUFFICIENT_RESOURCES; } // // zero buffer // RtlZeroMemory(Buffer, PAGE_SIZE); // // allocate mdl describing the device descriptor // Mdl = IoAllocateMdl(Buffer, sizeof(USB_DEVICE_DESCRIPTOR), FALSE, FALSE, 0); if (!Mdl) { // // failed to allocate mdl // return STATUS_INSUFFICIENT_RESOURCES; } // // build mdl for non paged pool // MmBuildMdlForNonPagedPool(Mdl); // // commit setup packet // Status = CommitSetupPacket(&CtrlSetup, NULL, sizeof(USB_DEVICE_DESCRIPTOR), Mdl); // // now free the mdl // IoFreeMdl(Mdl); if (NT_SUCCESS(Status)) { // // copy device descriptor // RtlCopyMemory(&m_DeviceDescriptor, Buffer, sizeof(USB_DEVICE_DESCRIPTOR)); } // // free buffer // ExFreePool(Buffer); // // done // return Status; } //---------------------------------------------------------------------------------------- NTSTATUS CUSBDevice::GetConfigurationDescriptor( IN UCHAR ConfigurationIndex, IN USHORT BufferSize, IN PVOID Buffer) { USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; NTSTATUS Status; PMDL Mdl; // // now build MDL describing the buffer // Mdl = IoAllocateMdl(Buffer, BufferSize, FALSE, FALSE, 0); if (!Mdl) { // // failed to allocate mdl // return STATUS_INSUFFICIENT_RESOURCES; } // // build mdl for non paged pool // MmBuildMdlForNonPagedPool(Mdl); // // build setup packet // CtrlSetup.bmRequestType.Recipient = BMREQUEST_TO_DEVICE; CtrlSetup.bmRequestType.Type = BMREQUEST_STANDARD; CtrlSetup.bmRequestType.Reserved = 0; CtrlSetup.bmRequestType.Dir = BMREQUEST_DEVICE_TO_HOST; CtrlSetup.bRequest = USB_REQUEST_GET_DESCRIPTOR; CtrlSetup.wValue.LowByte = ConfigurationIndex; CtrlSetup.wValue.HiByte = USB_CONFIGURATION_DESCRIPTOR_TYPE; CtrlSetup.wIndex.W = 0; CtrlSetup.wLength = BufferSize; // // commit packet // Status = CommitSetupPacket(&CtrlSetup, NULL, BufferSize, Mdl); // // free mdl // IoFreeMdl(Mdl); // // done // return Status; } //---------------------------------------------------------------------------------------- NTSTATUS CUSBDevice::CreateConfigurationDescriptor( UCHAR Index) { NTSTATUS Status; PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor; // // sanity checks // PC_ASSERT(m_ConfigurationDescriptors); // // first allocate a buffer which should be enough to store all different interfaces and endpoints // ConfigurationDescriptor = (PUSB_CONFIGURATION_DESCRIPTOR)ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, TAG_USBLIB); if (!ConfigurationDescriptor) { // // failed to allocate buffer // return STATUS_INSUFFICIENT_RESOURCES; } // // get partial configuration descriptor // Status = GetConfigurationDescriptor(Index, sizeof(USB_CONFIGURATION_DESCRIPTOR), ConfigurationDescriptor); if (!NT_SUCCESS(Status)) { // // failed to get partial configuration descriptor // DPRINT1("[%s] Failed to get partial configuration descriptor Status %x Index %x\n", m_USBType, Status, Index); ExFreePoolWithTag(ConfigurationDescriptor, TAG_USBLIB); return Status; } // // now get full descriptor // Status = GetConfigurationDescriptor(Index, ConfigurationDescriptor->wTotalLength, ConfigurationDescriptor); if (!NT_SUCCESS(Status)) { // // failed to get full configuration descriptor // DPRINT1("[%s] Failed to get full configuration descriptor Status %x Index %x\n", m_USBType, Status, Index); ExFreePoolWithTag(ConfigurationDescriptor, TAG_USBLIB); return Status; } // // informal debug print // DumpConfigurationDescriptor(ConfigurationDescriptor); // // sanity check // PC_ASSERT(ConfigurationDescriptor->bLength == sizeof(USB_CONFIGURATION_DESCRIPTOR)); PC_ASSERT(ConfigurationDescriptor->wTotalLength <= PAGE_SIZE); PC_ASSERT(ConfigurationDescriptor->bNumInterfaces); // // request is complete, initialize configuration descriptor // m_ConfigurationDescriptors[Index].ConfigurationDescriptor = ConfigurationDescriptor; InitializeListHead(&m_ConfigurationDescriptors[Index].InterfaceList); // // done // return Status; } //---------------------------------------------------------------------------------------- VOID CUSBDevice::GetConfigurationDescriptors( IN PUSB_CONFIGURATION_DESCRIPTOR ConfigDescriptorBuffer, IN ULONG BufferLength, OUT PULONG OutBufferLength) { ULONG Length; // sanity check ASSERT(BufferLength >= sizeof(USB_CONFIGURATION_DESCRIPTOR)); ASSERT(ConfigDescriptorBuffer); ASSERT(OutBufferLength); // reset copied length *OutBufferLength = 0; // FIXME: support multiple configurations PC_ASSERT(m_DeviceDescriptor.bNumConfigurations == 1); // copy configuration descriptor Length = min(m_ConfigurationDescriptors[0].ConfigurationDescriptor->wTotalLength, BufferLength); RtlCopyMemory(ConfigDescriptorBuffer, m_ConfigurationDescriptors[0].ConfigurationDescriptor, Length); *OutBufferLength = Length; } //---------------------------------------------------------------------------------------- ULONG CUSBDevice::GetConfigurationDescriptorsLength() { // // FIXME: support multiple configurations // PC_ASSERT(m_DeviceDescriptor.bNumConfigurations == 1); return m_ConfigurationDescriptors[0].ConfigurationDescriptor->wTotalLength; } //---------------------------------------------------------------------------------------- VOID CUSBDevice::DumpDeviceDescriptor(PUSB_DEVICE_DESCRIPTOR DeviceDescriptor) { DPRINT1("Dumping Device Descriptor %p\n", DeviceDescriptor); DPRINT1("bLength %x\n", DeviceDescriptor->bLength); DPRINT1("bDescriptorType %x\n", DeviceDescriptor->bDescriptorType); DPRINT1("bcdUSB %x\n", DeviceDescriptor->bcdUSB); DPRINT1("bDeviceClass %x\n", DeviceDescriptor->bDeviceClass); DPRINT1("bDeviceSubClass %x\n", DeviceDescriptor->bDeviceSubClass); DPRINT1("bDeviceProtocol %x\n", DeviceDescriptor->bDeviceProtocol); DPRINT1("bMaxPacketSize0 %x\n", DeviceDescriptor->bMaxPacketSize0); DPRINT1("idVendor %x\n", DeviceDescriptor->idVendor); DPRINT1("idProduct %x\n", DeviceDescriptor->idProduct); DPRINT1("bcdDevice %x\n", DeviceDescriptor->bcdDevice); DPRINT1("iManufacturer %x\n", DeviceDescriptor->iManufacturer); DPRINT1("iProduct %x\n", DeviceDescriptor->iProduct); DPRINT1("iSerialNumber %x\n", DeviceDescriptor->iSerialNumber); DPRINT1("bNumConfigurations %x\n", DeviceDescriptor->bNumConfigurations); } //---------------------------------------------------------------------------------------- VOID CUSBDevice::DumpConfigurationDescriptor(PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor) { DPRINT1("Dumping ConfigurationDescriptor %p\n", ConfigurationDescriptor); DPRINT1("bLength %x\n", ConfigurationDescriptor->bLength); DPRINT1("bDescriptorType %x\n", ConfigurationDescriptor->bDescriptorType); DPRINT1("wTotalLength %x\n", ConfigurationDescriptor->wTotalLength); DPRINT1("bNumInterfaces %x\n", ConfigurationDescriptor->bNumInterfaces); DPRINT1("bConfigurationValue %x\n", ConfigurationDescriptor->bConfigurationValue); DPRINT1("iConfiguration %x\n", ConfigurationDescriptor->iConfiguration); DPRINT1("bmAttributes %x\n", ConfigurationDescriptor->bmAttributes); DPRINT1("MaxPower %x\n", ConfigurationDescriptor->MaxPower); } //---------------------------------------------------------------------------------------- NTSTATUS CUSBDevice::SubmitSetupPacket( IN PUSB_DEFAULT_PIPE_SETUP_PACKET SetupPacket, IN OUT ULONG BufferLength, OUT PVOID Buffer) { NTSTATUS Status; PMDL Mdl = NULL; if (BufferLength) { // // allocate mdl // Mdl = IoAllocateMdl(Buffer, BufferLength, FALSE, FALSE, 0); if (!Mdl) { // // no memory // return STATUS_INSUFFICIENT_RESOURCES; } // // HACK HACK HACK: assume the buffer is build from non paged pool // MmBuildMdlForNonPagedPool(Mdl); } // // commit setup packet // Status = CommitSetupPacket(SetupPacket, NULL, BufferLength, Mdl); if (Mdl != NULL) { // // free mdl // IoFreeMdl(Mdl); } // // done // return Status; } //---------------------------------------------------------------------------------------- NTSTATUS CUSBDevice::BuildInterfaceDescriptor( IN ULONG ConfigurationIndex, IN PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor, OUT PUSBD_INTERFACE_INFORMATION InterfaceInfo, OUT PUSB_INTERFACE *OutUsbInterface) { PUSB_INTERFACE UsbInterface; PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor; ULONG PipeIndex; // allocate interface handle UsbInterface = (PUSB_INTERFACE)ExAllocatePool(NonPagedPool, sizeof(USB_INTERFACE) + (InterfaceDescriptor->bNumEndpoints - 1) * sizeof(USB_ENDPOINT)); if (!UsbInterface) { // failed to allocate memory return STATUS_INSUFFICIENT_RESOURCES; } // zero descriptor RtlZeroMemory(UsbInterface, sizeof(USB_INTERFACE) + (InterfaceDescriptor->bNumEndpoints - 1) * sizeof(USB_ENDPOINT)); // store handle InterfaceInfo->InterfaceHandle = (USBD_INTERFACE_HANDLE)UsbInterface; InterfaceInfo->Class = InterfaceDescriptor->bInterfaceClass; InterfaceInfo->SubClass = InterfaceDescriptor->bInterfaceSubClass; InterfaceInfo->Protocol = InterfaceDescriptor->bInterfaceProtocol; InterfaceInfo->Reserved = 0; // init interface handle UsbInterface->InterfaceDescriptor = InterfaceDescriptor; InsertTailList(&m_ConfigurationDescriptors[ConfigurationIndex].InterfaceList, &UsbInterface->ListEntry); // grab first endpoint descriptor EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR) (InterfaceDescriptor + 1); // now copy all endpoint information for(PipeIndex = 0; PipeIndex < InterfaceDescriptor->bNumEndpoints; PipeIndex++) { while(EndpointDescriptor->bDescriptorType != USB_ENDPOINT_DESCRIPTOR_TYPE) { // skip intermediate descriptors if (EndpointDescriptor->bLength == 0 || EndpointDescriptor->bDescriptorType == USB_INTERFACE_DESCRIPTOR_TYPE) { // bogus configuration descriptor DPRINT1("[%s] Bogus descriptor found in InterfaceNumber %x Alternate %x EndpointIndex %x bLength %x bDescriptorType %x\n", m_USBType, InterfaceDescriptor->bInterfaceNumber, InterfaceDescriptor->bAlternateSetting, PipeIndex, EndpointDescriptor->bLength, EndpointDescriptor->bDescriptorType); // failed return STATUS_UNSUCCESSFUL; } // move to next descriptor EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR)((ULONG_PTR)EndpointDescriptor + EndpointDescriptor->bLength); } // store in interface info RtlCopyMemory(&UsbInterface->EndPoints[PipeIndex].EndPointDescriptor, EndpointDescriptor, sizeof(USB_ENDPOINT_DESCRIPTOR)); DPRINT("Configuration Descriptor %p Length %lu\n", m_ConfigurationDescriptors[ConfigurationIndex].ConfigurationDescriptor, m_ConfigurationDescriptors[ConfigurationIndex].ConfigurationDescriptor->wTotalLength); DPRINT("EndpointDescriptor %p DescriptorType %x bLength %x\n", EndpointDescriptor, EndpointDescriptor->bDescriptorType, EndpointDescriptor->bLength); DPRINT("EndpointDescriptorHandle %p bAddress %x bmAttributes %x\n",&UsbInterface->EndPoints[PipeIndex], UsbInterface->EndPoints[PipeIndex].EndPointDescriptor.bEndpointAddress, UsbInterface->EndPoints[PipeIndex].EndPointDescriptor.bmAttributes); // copy pipe info InterfaceInfo->Pipes[PipeIndex].MaximumPacketSize = UsbInterface->EndPoints[PipeIndex].EndPointDescriptor.wMaxPacketSize; InterfaceInfo->Pipes[PipeIndex].EndpointAddress = UsbInterface->EndPoints[PipeIndex].EndPointDescriptor.bEndpointAddress; InterfaceInfo->Pipes[PipeIndex].Interval = UsbInterface->EndPoints[PipeIndex].EndPointDescriptor.bInterval; InterfaceInfo->Pipes[PipeIndex].PipeType = (USBD_PIPE_TYPE)UsbInterface->EndPoints[PipeIndex].EndPointDescriptor.bmAttributes; InterfaceInfo->Pipes[PipeIndex].PipeHandle = (PVOID)&UsbInterface->EndPoints[PipeIndex]; // move to next descriptor EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR)((ULONG_PTR)EndpointDescriptor + EndpointDescriptor->bLength); } if (OutUsbInterface) { // output result *OutUsbInterface = UsbInterface; } return STATUS_SUCCESS; } //---------------------------------------------------------------------------------------- NTSTATUS CUSBDevice::SelectConfiguration( IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor, IN PUSBD_INTERFACE_INFORMATION InterfaceInfo, OUT USBD_CONFIGURATION_HANDLE *ConfigurationHandle) { ULONG InterfaceIndex; USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; NTSTATUS Status; UCHAR bConfigurationValue = 0; ULONG ConfigurationIndex = 0, Index; UCHAR Found = FALSE; PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor; PUSB_INTERFACE UsbInterface; PLIST_ENTRY Entry; if (ConfigurationDescriptor) { // find configuration index for (Index = 0; Index < m_DeviceDescriptor.bNumConfigurations; Index++) { if (m_ConfigurationDescriptors[Index].ConfigurationDescriptor->bConfigurationValue == ConfigurationDescriptor->bConfigurationValue) { // found configuration index ConfigurationIndex = Index; Found = TRUE; } } if (!Found) { DPRINT1("[%s] invalid configuration value %u\n", m_USBType, ConfigurationDescriptor->bConfigurationValue); return STATUS_INVALID_PARAMETER; } // sanity check ASSERT(ConfigurationDescriptor->bNumInterfaces <= m_ConfigurationDescriptors[ConfigurationIndex].ConfigurationDescriptor->bNumInterfaces); // get configuration value bConfigurationValue = ConfigurationDescriptor->bConfigurationValue; } // now build setup packet RtlZeroMemory(&CtrlSetup, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET)); CtrlSetup.bRequest = USB_REQUEST_SET_CONFIGURATION; CtrlSetup.wValue.W = bConfigurationValue; // select configuration Status = CommitSetupPacket(&CtrlSetup, NULL, 0, NULL); if (!ConfigurationDescriptor) { // unconfigure request DPRINT1("[%s] SelectConfiguration Unconfigure Request Status %lx\n", m_USBType, Status); m_ConfigurationIndex = 0; return Status; } // informal debug print DPRINT("[%s] SelectConfiguration New Configuration %x Old Configuration %x Result %lx\n", m_USBType, ConfigurationIndex, m_ConfigurationIndex, Status); if (!NT_SUCCESS(Status)) { // // failed // return Status; } // destroy old interface info while (!IsListEmpty(&m_ConfigurationDescriptors[m_ConfigurationIndex].InterfaceList)) { // remove entry Entry = RemoveHeadList(&m_ConfigurationDescriptors[m_ConfigurationIndex].InterfaceList); // get interface info UsbInterface = (PUSB_INTERFACE)CONTAINING_RECORD(Entry, USB_INTERFACE, ListEntry); // free interface info ExFreePool(UsbInterface); } // sanity check ASSERT(IsListEmpty(&m_ConfigurationDescriptors[ConfigurationIndex].InterfaceList)); // store new configuration device index m_ConfigurationIndex = ConfigurationIndex; // store configuration handle *ConfigurationHandle = &m_ConfigurationDescriptors[ConfigurationIndex]; // copy interface info and pipe info for(InterfaceIndex = 0; InterfaceIndex < ConfigurationDescriptor->bNumInterfaces; InterfaceIndex++) { // interface info checks ASSERT(InterfaceInfo->Length != 0); #ifdef _MSC_VER PC_ASSERT(InterfaceInfo->Length == FIELD_OFFSET(USBD_INTERFACE_INFORMATION, Pipes[InterfaceInfo->NumberOfPipes])); #endif // find interface descriptor InterfaceDescriptor = USBD_ParseConfigurationDescriptor(m_ConfigurationDescriptors[ConfigurationIndex].ConfigurationDescriptor, InterfaceInfo->InterfaceNumber, InterfaceInfo->AlternateSetting); // sanity checks ASSERT(InterfaceDescriptor != NULL); // check if the number of pipes have been properly set ASSERT(InterfaceInfo->NumberOfPipes == InterfaceDescriptor->bNumEndpoints); // copy interface info Status = BuildInterfaceDescriptor(ConfigurationIndex, InterfaceDescriptor, InterfaceInfo, NULL); if (!NT_SUCCESS(Status)) { // failed DPRINT1("[%s] Failed to copy interface descriptor Index %lu InterfaceDescriptor %p InterfaceInfo %p\n", m_USBType, ConfigurationIndex, InterfaceDescriptor, InterfaceInfo); break; } // move offset InterfaceInfo = (PUSBD_INTERFACE_INFORMATION)((PUCHAR)InterfaceInfo + InterfaceInfo->Length); } // // done // return Status; } //---------------------------------------------------------------------------------------- NTSTATUS CUSBDevice::SelectInterface( IN USBD_CONFIGURATION_HANDLE ConfigurationHandle, IN OUT PUSBD_INTERFACE_INFORMATION InterfaceInfo) { ULONG PipeIndex; USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; NTSTATUS Status; ULONG Index, ConfigurationIndex = 0, Found = FALSE; PUSB_INTERFACE UsbInterface; PLIST_ENTRY Entry; PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor; // check if handle is valid for(Index = 0; Index < m_DeviceDescriptor.bNumConfigurations; Index++) { if (&m_ConfigurationDescriptors[Index] == ConfigurationHandle) { // found configuration index ConfigurationIndex = Index; Found = TRUE; } } if (!Found) { // invalid handle passed DPRINT1("[%s] Invalid configuration handle passed %p\n", m_USBType, ConfigurationHandle); return STATUS_INVALID_PARAMETER; } // initialize setup packet RtlZeroMemory(&CtrlSetup, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET)); CtrlSetup.bRequest = USB_REQUEST_SET_INTERFACE; CtrlSetup.wValue.W = InterfaceInfo->AlternateSetting; CtrlSetup.wIndex.W = InterfaceInfo->InterfaceNumber; CtrlSetup.bmRequestType.B = 0x01; // issue request Status = CommitSetupPacket(&CtrlSetup, NULL, 0, NULL); // informal debug print DPRINT1("[%s] SelectInterface AlternateSetting %x InterfaceNumber %x Status %lx\n", m_USBType, InterfaceInfo->AlternateSetting, InterfaceInfo->InterfaceNumber, Status); #if 0 if (!NT_SUCCESS(Status)) { // failed to select interface return Status; } #endif Status = STATUS_SUCCESS; // find interface Found = FALSE; Entry = m_ConfigurationDescriptors[ConfigurationIndex].InterfaceList.Flink; while (Entry != &m_ConfigurationDescriptors[ConfigurationIndex].InterfaceList) { // grab interface descriptor UsbInterface = (PUSB_INTERFACE)CONTAINING_RECORD(Entry, USB_INTERFACE, ListEntry); if (UsbInterface->InterfaceDescriptor->bAlternateSetting == InterfaceInfo->AlternateSetting && UsbInterface->InterfaceDescriptor->bInterfaceNumber == InterfaceInfo->InterfaceNumber) { // found interface Found = TRUE; break; } // next entry Entry = Entry->Flink; } if (!Found) { // find interface descriptor InterfaceDescriptor = USBD_ParseConfigurationDescriptor(m_ConfigurationDescriptors[ConfigurationIndex].ConfigurationDescriptor, InterfaceInfo->InterfaceNumber, InterfaceInfo->AlternateSetting); if (!InterfaceDescriptor) { DPRINT1("[%s] No such interface Alternate %x InterfaceNumber %x\n", m_USBType, InterfaceInfo->AlternateSetting, InterfaceInfo->InterfaceNumber); return STATUS_UNSUCCESSFUL; } // build interface descriptor Status = BuildInterfaceDescriptor(ConfigurationIndex, InterfaceDescriptor, InterfaceInfo, &UsbInterface); if (!NT_SUCCESS(Status)) { // failed DPRINT1("[%s] Failed to build interface descriptor Status %x\n", m_USBType, Status); return Status; } } // assert on pipe length mismatch DPRINT1("NumberOfPipes %lu Endpoints %lu Length %lu\n", InterfaceInfo->NumberOfPipes, UsbInterface->InterfaceDescriptor->bNumEndpoints, InterfaceInfo->Length); // sanity check ASSERT(GET_USBD_INTERFACE_SIZE(UsbInterface->InterfaceDescriptor->bNumEndpoints) == InterfaceInfo->Length); // store number of pipes InterfaceInfo->NumberOfPipes = UsbInterface->InterfaceDescriptor->bNumEndpoints; // copy pipe handles for (PipeIndex = 0; PipeIndex < UsbInterface->InterfaceDescriptor->bNumEndpoints; PipeIndex++) { // copy pipe handle DPRINT1("PipeIndex %lu\n", PipeIndex); DPRINT1("EndpointAddress %x\n", InterfaceInfo->Pipes[PipeIndex].EndpointAddress); DPRINT1("Interval %c\n", InterfaceInfo->Pipes[PipeIndex].Interval); DPRINT1("MaximumPacketSize %hu\n", InterfaceInfo->Pipes[PipeIndex].MaximumPacketSize); DPRINT1("MaximumTransferSize %lu\n", InterfaceInfo->Pipes[PipeIndex].MaximumTransferSize); DPRINT1("PipeFlags %lu\n", InterfaceInfo->Pipes[PipeIndex].PipeFlags); DPRINT1("PipeType %d\n", InterfaceInfo->Pipes[PipeIndex].PipeType); DPRINT1("UsbEndPoint %x\n", InterfaceInfo->Pipes[PipeIndex].EndpointAddress); // sanity checks ASSERT(InterfaceInfo->Pipes[PipeIndex].EndpointAddress == UsbInterface->EndPoints[PipeIndex].EndPointDescriptor.bEndpointAddress); ASSERT(InterfaceInfo->Pipes[PipeIndex].Interval == UsbInterface->EndPoints[PipeIndex].EndPointDescriptor.bInterval); // store pipe handle InterfaceInfo->Pipes[PipeIndex].PipeHandle = &UsbInterface->EndPoints[PipeIndex]; // data toggle is reset on select interface requests UsbInterface->EndPoints[PipeIndex].DataToggle = FALSE; } // // done // return Status; } NTSTATUS CUSBDevice::AbortPipe( IN PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor) { // // let it handle usb queue // ASSERT(m_Queue); ASSERT(m_DeviceAddress); // // done // return m_Queue->AbortDevicePipe(m_DeviceAddress, EndpointDescriptor); } UCHAR CUSBDevice::GetMaxPacketSize() { return m_DeviceDescriptor.bMaxPacketSize0; } //---------------------------------------------------------------------------------------- NTSTATUS NTAPI CreateUSBDevice( PUSBDEVICE *OutDevice) { CUSBDevice * This; // // allocate controller // This = new(NonPagedPool, TAG_USBLIB) CUSBDevice(0); if (!This) { // // failed to allocate // return STATUS_INSUFFICIENT_RESOURCES; } // // add reference count // This->AddRef(); // // return result // *OutDevice = (PUSBDEVICE)This; // // done // return STATUS_SUCCESS; }