diff --git a/reactos/lib/drivers/CMakeLists.txt b/reactos/lib/drivers/CMakeLists.txt index c23d0636528..538efc6f972 100644 --- a/reactos/lib/drivers/CMakeLists.txt +++ b/reactos/lib/drivers/CMakeLists.txt @@ -3,5 +3,6 @@ add_subdirectory(chew) add_subdirectory(csq) add_subdirectory(hidparser) add_subdirectory(ip) +add_subdirectory(libusb) add_subdirectory(lwip) add_subdirectory(sound) diff --git a/reactos/lib/drivers/libusb/CMakeLists.txt b/reactos/lib/drivers/libusb/CMakeLists.txt new file mode 100644 index 00000000000..61925dc1b77 --- /dev/null +++ b/reactos/lib/drivers/libusb/CMakeLists.txt @@ -0,0 +1,21 @@ +set_cpp() + +remove_definitions(-D_WIN32_WINNT=0x502) +add_definitions(-D_WIN32_WINNT=0x600) + + +add_definitions( + -DUNICODE -D_UNICODE) + +list(APPEND SOURCE + hcd_controller.cpp + hub_controller.cpp + memory_manager.cpp + misc.cpp + usb_device.cpp + purecall.cpp + libusb.cpp) + +add_library(libusb ${SOURCE}) +add_dependencies(libusb bugcodes) + diff --git a/reactos/lib/drivers/libusb/common_interfaces.h b/reactos/lib/drivers/libusb/common_interfaces.h new file mode 100644 index 00000000000..307cc02f9fe --- /dev/null +++ b/reactos/lib/drivers/libusb/common_interfaces.h @@ -0,0 +1,667 @@ + +#ifndef COMMON_INTERFACES_HPP +#define COMMON_INTERFACES_HPP + +typedef struct _USB_ENDPOINT +{ + USB_ENDPOINT_DESCRIPTOR EndPointDescriptor; + UCHAR HubAddress; + UCHAR HubPort; + UCHAR DataToggle; +} USB_ENDPOINT, *PUSB_ENDPOINT; + +typedef struct _USB_INTERFACE +{ + LIST_ENTRY ListEntry; + PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor; + USB_ENDPOINT EndPoints[1]; +} USB_INTERFACE, *PUSB_INTERFACE; + +typedef struct +{ + PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor; + LIST_ENTRY InterfaceList; +}USB_CONFIGURATION, *PUSB_CONFIGURATION; + +//--------------------------------------------------------------------------- +// +// Object Hierachy +// -------------------------------------------------------------------- +// | IRootHCDController | +// | IHCDController Intel USB Universal Host Controller - 3A37 | +// | IHCDController - Intel USB Universal HostController - 3A38 | +// | IHCDController - Intel USB Universal HostController - 3A38 | +// |------------------------------------------------------------------| +// +// +// IHCDController Intel USB Universal Host Controller - 3A37 +// IHubController +// IUSBHardwareDevice +// IDMAMemoryManager +// IUSBQueue <- interacts with -> IUSBRequest +// +// +// Each IHCDController creates an IUSBHardwareDevice class upon initialization. The +// IUSBHardwardeDevice class is used to abstract usb controller specifics. The IHubController +// manages all attached devices and handles hub control ioctl requests. +// +// Each IUSBHardwareDevice has one IDMAMemoryManager and one IUSBQueue. The IDMAMemoryManager +// is used to handle dma memory allocations. The IUSBQueue manages requests which are send to the +// usb hardware. See IUSBRequest class for details. +// + + +//========================================================================================= +// +// class IRootHCDController +// +// Description: This class serves as the root host controller. The host controller mantains +// a list of registered controllers and provides support functions for the host controllers + +struct IHCDController; + +DECLARE_INTERFACE_(IRootHCDController, IUnknown) +{ + DEFINE_ABSTRACT_UNKNOWN() + +//----------------------------------------------------------------------------------------- +// +// Initialize +// +// Description: This function initializes the root host controller. It allocates the resources +// required to manage the registered controllers + + virtual NTSTATUS Initialize() = 0; + +//----------------------------------------------------------------------------------------- +// +// RegisterHCD +// +// Description: this function registers a host controller with the root host controller + + virtual NTSTATUS RegisterHCD(struct IHCDController * Controller) = 0; + +//----------------------------------------------------------------------------------------- +// +// UnregisterHCD +// +// Description: this function unregistes a host controller + + virtual NTSTATUS UnregisterHCD(struct IHCDController * Controller) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetControllerCount +// +// Description: returns the number of host controllers registered + + virtual ULONG GetControllerCount() = 0; + +}; + +typedef IRootHCDController *PROOTHDCCONTROLLER; + +//========================================================================================= +// +// class IHCDController +// +// Description: This class is used to manage a single USB host controller +// + +DECLARE_INTERFACE_(IHCDController, IUnknown) +{ + DEFINE_ABSTRACT_UNKNOWN() + +//----------------------------------------------------------------------------------------- +// +// Initialize +// +// Description: This function initializes the IHCDController implementation. +// It creates an IUSBHardwareDevice object and initializes it. It also registeres itself with +// the IRootHCDController +// + virtual NTSTATUS Initialize(IN PROOTHDCCONTROLLER RootHCDController, + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT PhysicalDeviceObject) = 0; + +}; + +typedef IHCDController *PHCDCONTROLLER; + + +//========================================================================================= +// +// class IUSBHardwareDevice +// +// Description: This class provides access to the usb hardware controller +// + +struct IDMAMemoryManager; +struct IUSBQueue; + +#define DEFINE_ABSTRACT_USBHARDWAREDEVICE() \ + STDMETHOD_(NTSTATUS, Initialize)( THIS_ \ + IN PDRIVER_OBJECT DriverObject, \ + IN PDEVICE_OBJECT FunctionalDeviceObject, \ + IN PDEVICE_OBJECT PhysicalDeviceObject, \ + IN PDEVICE_OBJECT LowerDeviceObject) PURE; \ + \ + STDMETHOD_(NTSTATUS, PnpStart)( THIS_ \ + IN PCM_RESOURCE_LIST RawResources, \ + IN PCM_RESOURCE_LIST TranslatedResources) PURE; \ + \ + STDMETHOD_(NTSTATUS, PnpStop)( THIS) PURE; \ + \ + STDMETHOD_(NTSTATUS, GetDeviceDetails)( THIS_ \ + OUT OPTIONAL PUSHORT VendorId, \ + OUT OPTIONAL PUSHORT DeviceId, \ + OUT OPTIONAL PULONG NumberOfPorts, \ + OUT OPTIONAL PULONG Speed) PURE; \ + \ + STDMETHOD_(NTSTATUS, GetUSBQueue)( THIS_ \ + OUT struct IUSBQueue **OutUsbQueue) PURE; \ + \ + STDMETHOD_(NTSTATUS, GetDMA)( THIS_ \ + OUT struct IDMAMemoryManager **OutDMA) PURE; \ + \ + STDMETHOD_(NTSTATUS, ResetPort)( THIS_ \ + IN ULONG PortNumber) PURE; \ + \ + STDMETHOD_(NTSTATUS, GetPortStatus)( THIS_ \ + IN ULONG PortId, \ + OUT USHORT *PortStatus, \ + OUT USHORT *PortChange) PURE; \ + \ + STDMETHOD_(NTSTATUS, ClearPortStatus)( THIS_ \ + IN ULONG PortId, \ + IN ULONG Status) PURE; \ + \ + STDMETHOD_(NTSTATUS, SetPortFeature)( THIS_ \ + IN ULONG PortId, \ + IN ULONG Feature) PURE; \ + \ + STDMETHOD_(VOID, SetStatusChangeEndpointCallBack)( THIS_ \ + IN PVOID CallBack, \ + IN PVOID Context) PURE; + + +#define IMP_IUSBHARDWAREDEVICE \ + STDMETHODIMP_(NTSTATUS) Initialize( \ + IN PDRIVER_OBJECT DriverObject, \ + IN PDEVICE_OBJECT FunctionalDeviceObject, \ + IN PDEVICE_OBJECT PhysicalDeviceObject, \ + IN PDEVICE_OBJECT LowerDeviceObject); \ + \ + STDMETHODIMP_(NTSTATUS) PnpStart( \ + IN PCM_RESOURCE_LIST RawResources, \ + IN PCM_RESOURCE_LIST TranslatedResources); \ + \ + STDMETHODIMP_(NTSTATUS) PnpStop(VOID); \ + \ + STDMETHODIMP_(NTSTATUS) GetDeviceDetails( \ + OUT OPTIONAL PUSHORT VendorId, \ + OUT OPTIONAL PUSHORT DeviceId, \ + OUT OPTIONAL PULONG NumberOfPorts, \ + OUT OPTIONAL PULONG Speed); \ + \ + STDMETHODIMP_(NTSTATUS) GetUSBQueue( \ + OUT struct IUSBQueue **OutUsbQueue); \ + \ + STDMETHODIMP_(NTSTATUS) GetDMA( \ + OUT struct IDMAMemoryManager **OutDMA); \ + \ + STDMETHODIMP_(NTSTATUS) ResetPort( \ + IN ULONG PortNumber); \ + \ + STDMETHODIMP_(NTSTATUS) GetPortStatus( \ + IN ULONG PortId, \ + OUT USHORT *PortStatus, \ + OUT USHORT *PortChange); \ + \ + STDMETHODIMP_(NTSTATUS) ClearPortStatus( \ + IN ULONG PortId, \ + IN ULONG Status); \ + \ + STDMETHODIMP_(NTSTATUS) SetPortFeature( \ + IN ULONG PortId, \ + IN ULONG Feature); \ + \ + STDMETHODIMP_(VOID) SetStatusChangeEndpointCallBack( \ + IN PVOID CallBack, \ + IN PVOID Context); + +DECLARE_INTERFACE_(IUSBHardwareDevice, IUnknown) +{ + DEFINE_ABSTRACT_UNKNOWN() + DEFINE_ABSTRACT_USBHARDWAREDEVICE() +}; + +typedef IUSBHardwareDevice *PUSBHARDWAREDEVICE; + + +//========================================================================================= +// +// class IDMAMemoryManager +// +// Description: This class provides access to the dma buffer. It provides methods to +// allocate and free from the dma buffer +// + +DECLARE_INTERFACE_(IDMAMemoryManager, IUnknown) +{ + DEFINE_ABSTRACT_UNKNOWN() + +//----------------------------------------------------------------------------------------- +// +// Initialize +// +// Description: initializes the memory manager + + virtual NTSTATUS Initialize(IN PUSBHARDWAREDEVICE Device, + IN PKSPIN_LOCK Lock, + IN ULONG DmaBufferSize, + IN PVOID VirtualBase, + IN PHYSICAL_ADDRESS PhysicalAddress, + IN ULONG DefaultBlockSize) = 0; + +//----------------------------------------------------------------------------------------- +// +// Allocate +// +// Description: allocates block of memory from allocator + + virtual NTSTATUS Allocate(IN ULONG Size, + OUT PVOID *OutVirtualBase, + OUT PPHYSICAL_ADDRESS OutPhysicalAddress) = 0; + + +//----------------------------------------------------------------------------------------- +// +// Free +// +// Description: releases memory block + + virtual NTSTATUS Release(IN PVOID VirtualBase, + IN ULONG Size) = 0; + +}; + +typedef IDMAMemoryManager *PDMAMEMORYMANAGER; + + +//========================================================================================= +// +// class IUSBRequest +// +// Description: This class is used to issue request to usb controller. The class is +// initialized using InitializeXXX methods. You also need to call SetEndpoint to define the endpoint +// In addition you can call SetCompletionDetails if you need to wait for the end of +// the request or want to complete an irp. You call AddUSBRequest to add the request to the queue. +// Once the request is completed the CompletionCallback is invoked. The CompletionCallback +// will take care of any completion details which have been set. If the request is cancelled, the +// CancelCallback routine is invoked. +// + +#define DEFINE_ABSTRACT_USBREQUEST() \ + STDMETHOD_(NTSTATUS, InitializeWithSetupPacket)( THIS_ \ + IN PDMAMEMORYMANAGER DmaManager, \ + IN PUSB_DEFAULT_PIPE_SETUP_PACKET SetupPacket, \ + IN UCHAR DeviceAddress, \ + IN OPTIONAL struct _USB_ENDPOINT *EndpointDescriptor, \ + IN OUT ULONG TransferBufferLength, \ + IN OUT PMDL TransferBuffer) PURE; \ + \ + STDMETHOD_(NTSTATUS, InitializeWithIrp)( THIS_ \ + IN PDMAMEMORYMANAGER DmaManager, \ + IN OUT PIRP Irp) PURE; \ + \ + STDMETHOD_(BOOLEAN, IsRequestComplete)( THIS) PURE; \ + \ + STDMETHOD_(ULONG, GetTransferType)( THIS) PURE; \ + \ + STDMETHOD_(VOID, GetResultStatus)( THIS_ \ + OUT OPTIONAL NTSTATUS * NtStatusCode, \ + OUT OPTIONAL PULONG UrbStatusCode) PURE; + +#define IMP_IUSBREQUEST \ + STDMETHODIMP_(NTSTATUS) InitializeWithSetupPacket( \ + IN PDMAMEMORYMANAGER DmaManager, \ + IN PUSB_DEFAULT_PIPE_SETUP_PACKET SetupPacket, \ + IN UCHAR DeviceAddress, \ + IN OPTIONAL struct _USB_ENDPOINT *EndpointDescriptor, \ + IN OUT ULONG TransferBufferLength, \ + IN OUT PMDL TransferBuffer); \ + \ + STDMETHODIMP_(NTSTATUS) InitializeWithIrp( \ + IN PDMAMEMORYMANAGER DmaManager, \ + IN OUT PIRP Irp); \ + \ + STDMETHODIMP_(BOOLEAN) IsRequestComplete(VOID); \ + \ + STDMETHODIMP_(ULONG) GetTransferType(VOID); \ + \ + STDMETHODIMP_(VOID) GetResultStatus( \ + OUT OPTIONAL NTSTATUS * NtStatusCode, \ + OUT OPTIONAL PULONG UrbStatusCode); + +DECLARE_INTERFACE_(IUSBRequest, IUnknown) +{ + DEFINE_ABSTRACT_UNKNOWN() + DEFINE_ABSTRACT_USBREQUEST() +}; + + +typedef IUSBRequest *PUSBREQUEST; + +//========================================================================================= +// +// class IUSBQueue +// +// Description: This class manages pending requests +// + +#define DEFINE_ABSTRACT_USBQUEUE() \ + STDMETHOD_(NTSTATUS, Initialize)( THIS_ \ + IN PUSBHARDWAREDEVICE Hardware, \ + IN PDMA_ADAPTER AdapterObject, \ + IN PDMAMEMORYMANAGER MemManager, \ + IN OPTIONAL PKSPIN_LOCK Lock) PURE; \ + \ + STDMETHOD_(NTSTATUS, AddUSBRequest)( THIS_ \ + IN IUSBRequest * Request) PURE; \ + \ + STDMETHOD_(NTSTATUS, CreateUSBRequest)( THIS_ \ + IN IUSBRequest **OutRequest) PURE; \ + \ + STDMETHOD_(NTSTATUS, AbortDevicePipe)( THIS_ \ + IN UCHAR DeviceAddress, \ + IN PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor) PURE; + +#define IMP_IUSBQUEUE \ + STDMETHODIMP_(NTSTATUS) Initialize( \ + IN PUSBHARDWAREDEVICE Hardware, \ + IN PDMA_ADAPTER AdapterObject, \ + IN PDMAMEMORYMANAGER MemManager, \ + IN OPTIONAL PKSPIN_LOCK Lock); \ + \ + STDMETHODIMP_(NTSTATUS) AddUSBRequest( \ + IN IUSBRequest * Request); \ + \ + STDMETHODIMP_(NTSTATUS) CreateUSBRequest( \ + OUT IUSBRequest **OutRequest); \ + \ + STDMETHODIMP_(NTSTATUS) AbortDevicePipe( \ + IN UCHAR DeviceAddress, \ + IN PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor); + +DECLARE_INTERFACE_(IUSBQueue, IUnknown) +{ + DEFINE_ABSTRACT_UNKNOWN() + DEFINE_ABSTRACT_USBQUEUE() +}; + +typedef IUSBQueue *PUSBQUEUE; + +//========================================================================================= +// +// class IHubController +// +// Description: This class implements a hub controller +// + +DECLARE_INTERFACE_(IHubController, IUnknown) +{ + DEFINE_ABSTRACT_UNKNOWN() + +//---------------------------------------------------------------------------------------- +// +// Initialize +// +// Description: Initializes the hub controller + + virtual NTSTATUS Initialize(IN PDRIVER_OBJECT DriverObject, + IN PHCDCONTROLLER Controller, + IN PUSBHARDWAREDEVICE Device, + IN BOOLEAN IsRootHubDevice, + IN ULONG DeviceAddress) = 0; + +//---------------------------------------------------------------------------------------- +// +// GetHubControllerDeviceObject +// +// Description: Returns the hub controller device object + + virtual NTSTATUS GetHubControllerDeviceObject(PDEVICE_OBJECT * HubDeviceObject) = 0; + +//---------------------------------------------------------------------------------------- +// +// GetHubControllerSymbolicLink +// +// Description: Returns the symbolic link of the root hub + + virtual NTSTATUS GetHubControllerSymbolicLink(ULONG BufferLength, PVOID Buffer, PULONG RequiredLength) = 0; + + +}; + +typedef IHubController *PHUBCONTROLLER; + +//========================================================================================= +// +// class IDispatchIrp +// +// Description: This class is used to handle irp dispatch requests +// + +DECLARE_INTERFACE_(IDispatchIrp, IUnknown) +{ + DEFINE_ABSTRACT_UNKNOWN() + +//----------------------------------------------------------------------------------------- +// +// HandlePnp +// +// Description: This function handles all pnp requests + + virtual NTSTATUS HandlePnp(IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp) = 0; + +//----------------------------------------------------------------------------------------- +// +// HandlePower +// +// Description: This function handles all power pnp requests +// + virtual NTSTATUS HandlePower(IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp) = 0; + +//----------------------------------------------------------------------------------------- +// +// HandleDeviceControl +// +// Description: handles device io control requests + + virtual NTSTATUS HandleDeviceControl(IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp) = 0; +}; + +typedef IDispatchIrp *PDISPATCHIRP; + +//========================================================================================= +// +// class IUSBDevice +// +// Description: This class is used to abstract details of a usb device +// + +DECLARE_INTERFACE_(IUSBDevice, IUnknown) +{ + DEFINE_ABSTRACT_UNKNOWN() + +//---------------------------------------------------------------------------------------- +// +// Initialize +// +// Description: Initializes the usb device + + virtual NTSTATUS Initialize(IN PHUBCONTROLLER HubController, + IN PUSBHARDWAREDEVICE Device, + IN PVOID Parent, + IN ULONG Port, + IN ULONG PortStatus) = 0; + +//----------------------------------------------------------------------------------------- +// +// IsHub +// +// Description: returns true when device is a hub + + virtual BOOLEAN IsHub() = 0; + +//----------------------------------------------------------------------------------------- +// +// GetParent +// +// Description: gets the parent device of the this device + + virtual NTSTATUS GetParent(PVOID * Parent) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetDeviceAddress +// +// Description: gets the device address of the this device + + virtual UCHAR GetDeviceAddress() = 0; + + +//----------------------------------------------------------------------------------------- +// +// GetPort +// +// Description: gets the port to which this device is connected + + virtual ULONG GetPort() = 0; + +//----------------------------------------------------------------------------------------- +// +// GetSpeed +// +// Description: gets the speed of the device + + virtual USB_DEVICE_SPEED GetSpeed() = 0; + +//----------------------------------------------------------------------------------------- +// +// GetType +// +// Description: gets the type of the device, either 1.1 or 2.0 device + + virtual USB_DEVICE_TYPE GetType() = 0; + +//----------------------------------------------------------------------------------------- +// +// GetState +// +// Description: gets the device state + + virtual ULONG GetState() = 0; + +//----------------------------------------------------------------------------------------- +// +// SetDeviceHandleData +// +// Description: sets device handle data + + virtual void SetDeviceHandleData(PVOID Data) = 0; + +//----------------------------------------------------------------------------------------- +// +// SetDeviceAddress +// +// Description: sets device handle data + + virtual NTSTATUS SetDeviceAddress(UCHAR DeviceAddress) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetDeviceDescriptor +// +// Description: sets device handle data + + virtual void GetDeviceDescriptor(PUSB_DEVICE_DESCRIPTOR DeviceDescriptor) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetConfigurationValue +// +// Description: gets current selected configuration index + + virtual UCHAR GetConfigurationValue() = 0; + +//----------------------------------------------------------------------------------------- +// +// SubmitIrp +// +// Description: submits an irp containing an urb + + virtual NTSTATUS SubmitIrp(PIRP Irp) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetConfigurationDescriptors +// +// Description: returns one or more configuration descriptors + + virtual VOID GetConfigurationDescriptors(IN PUSB_CONFIGURATION_DESCRIPTOR ConfigDescriptorBuffer, + IN ULONG BufferLength, + OUT PULONG OutBufferLength) = 0; + +//----------------------------------------------------------------------------------------- +// +// Description: returns length of configuration descriptors +// + virtual ULONG GetConfigurationDescriptorsLength() = 0; + +//----------------------------------------------------------------------------------------- +// +// SubmitSetupPacket +// +// Description: submits an setup packet. The usb device will then create an usb request from it and submit it to the queue + + virtual NTSTATUS SubmitSetupPacket(IN PUSB_DEFAULT_PIPE_SETUP_PACKET SetupPacket, + IN OUT ULONG BufferLength, + OUT PVOID Buffer) = 0; + +//----------------------------------------------------------------------------------------- +// +// SelectConfiguration +// +// Description: selects a configuration + + virtual NTSTATUS SelectConfiguration(IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor, + IN PUSBD_INTERFACE_INFORMATION Interface, + OUT USBD_CONFIGURATION_HANDLE *ConfigurationHandle) = 0; + +//----------------------------------------------------------------------------------------- +// +// SelectConfiguration +// +// Description: selects a interface of an configuration + + virtual NTSTATUS SelectInterface(IN USBD_CONFIGURATION_HANDLE ConfigurationHandle, + IN OUT PUSBD_INTERFACE_INFORMATION Interface) = 0; + +//----------------------------------------------------------------------------------------- +// +// AbortPipe +// +// Description: aborts all pending requsts + + virtual NTSTATUS AbortPipe(IN PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor) = 0; + +}; + +typedef IUSBDevice *PUSBDEVICE; + +#endif diff --git a/reactos/lib/drivers/libusb/hcd_controller.cpp b/reactos/lib/drivers/libusb/hcd_controller.cpp new file mode 100644 index 00000000000..540aecb13e1 --- /dev/null +++ b/reactos/lib/drivers/libusb/hcd_controller.cpp @@ -0,0 +1,781 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Driver Library + * LICENSE: GPL - See COPYING in the top level directory + * FILE: lib/drivers/libusb/hcd_controller.cpp + * PURPOSE: USB Common Driver Library. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#define INITGUID +#include "libusb.h" + +class CHCDController : public IHCDController, + public IDispatchIrp +{ +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; + } + + // IHCDController interface functions + NTSTATUS Initialize(IN PROOTHDCCONTROLLER RootHCDController, IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject); + + // IDispatchIrp interface functions + NTSTATUS HandlePnp(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp); + NTSTATUS HandlePower(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp); + NTSTATUS HandleDeviceControl(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp); + + // local functions + NTSTATUS CreateFDO(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT * OutDeviceObject); + NTSTATUS SetSymbolicLink(BOOLEAN Enable); + + // constructor / destructor + CHCDController(IUnknown *OuterUnknown){} + virtual ~CHCDController(){} + +protected: + LONG m_Ref; + PROOTHDCCONTROLLER m_RootController; + PDRIVER_OBJECT m_DriverObject; + PDEVICE_OBJECT m_PhysicalDeviceObject; + PDEVICE_OBJECT m_FunctionalDeviceObject; + PDEVICE_OBJECT m_NextDeviceObject; + PUSBHARDWAREDEVICE m_Hardware; + PHUBCONTROLLER m_HubController; + ULONG m_FDODeviceNumber; +}; + +//================================================================================================= +// COM +// +NTSTATUS +STDMETHODCALLTYPE +CHCDController::QueryInterface( + IN REFIID refiid, + OUT PVOID* Output) +{ + return STATUS_UNSUCCESSFUL; +} + +//------------------------------------------------------------------------------------------------- +NTSTATUS +CHCDController::Initialize( + IN PROOTHDCCONTROLLER RootHCDController, + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT PhysicalDeviceObject) +{ + NTSTATUS Status; + PCOMMON_DEVICE_EXTENSION DeviceExtension; + + // + // create usb hardware + // + Status = CreateUSBHardware(&m_Hardware); + if (!NT_SUCCESS(Status)) + { + // + // failed to create hardware object + // + DPRINT1("[USBLIB] Failed to create hardware object\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // initialize members + // + m_DriverObject = DriverObject; + m_PhysicalDeviceObject = PhysicalDeviceObject; + m_RootController = RootHCDController; + + // + // create FDO + // + Status = CreateFDO(m_DriverObject, &m_FunctionalDeviceObject); + if (!NT_SUCCESS(Status)) + { + // + // failed to create PDO + // + return Status; + } + + // + // now attach to device stack + // + m_NextDeviceObject = IoAttachDeviceToDeviceStack(m_FunctionalDeviceObject, m_PhysicalDeviceObject); + if (!m_NextDeviceObject) + { + // + // failed to attach to device stack + // + IoDeleteDevice(m_FunctionalDeviceObject); + m_FunctionalDeviceObject = 0; + + return STATUS_NO_SUCH_DEVICE; + } + + // + // initialize hardware object + // + Status = m_Hardware->Initialize(m_DriverObject, m_FunctionalDeviceObject, m_PhysicalDeviceObject, m_NextDeviceObject); + if (!NT_SUCCESS(Status)) + { + DPRINT1("[USBLIB] Failed to initialize hardware object %x\n", Status); + + // + // failed to initialize hardware object, detach from device stack + // + IoDetachDevice(m_NextDeviceObject); + + // + // now delete the device + // + IoDeleteDevice(m_FunctionalDeviceObject); + + // + // nullify pointers :) + // + m_FunctionalDeviceObject = 0; + m_NextDeviceObject = 0; + + return Status; + } + + + // + // set device flags + // + m_FunctionalDeviceObject->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE; + + + // + // get device extension + // + DeviceExtension = (PCOMMON_DEVICE_EXTENSION)m_FunctionalDeviceObject->DeviceExtension; + PC_ASSERT(DeviceExtension); + + // + // initialize device extension + // + DeviceExtension->IsFDO = TRUE; + DeviceExtension->IsHub = FALSE; + DeviceExtension->Dispatcher = PDISPATCHIRP(this); + + // + // device is initialized + // + m_FunctionalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + + + // + // is there a root controller + // + if (m_RootController) + { + // + // add reference + // + m_RootController->AddRef(); + + // + // register with controller + // + m_RootController->RegisterHCD(this); + } + + + // + // done + // + return STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------------------------- +NTSTATUS +CHCDController::HandleDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PCOMMON_DEVICE_EXTENSION DeviceExtension; + NTSTATUS Status = STATUS_NOT_IMPLEMENTED; + PUSB_HCD_DRIVERKEY_NAME DriverKey; + ULONG ResultLength; + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // get device extension + // + DeviceExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // sanity check + // + PC_ASSERT(DeviceExtension->IsFDO); + + DPRINT1("[USBLIB] HandleDeviceControl>Type: IoCtl %x InputBufferLength %lu OutputBufferLength %lu\n", + IoStack->Parameters.DeviceIoControl.IoControlCode, + IoStack->Parameters.DeviceIoControl.InputBufferLength, + IoStack->Parameters.DeviceIoControl.OutputBufferLength); + + // + // perform ioctl for FDO + // + if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_GET_HCD_DRIVERKEY_NAME) + { + // + // check if sizee is at least >= USB_HCD_DRIVERKEY_NAME + // + if(IoStack->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(USB_HCD_DRIVERKEY_NAME)) + { + // + // get device property size + // + Status = IoGetDeviceProperty(m_PhysicalDeviceObject, DevicePropertyDriverKeyName, 0, NULL, &ResultLength); + + // + // get input buffer + // + DriverKey = (PUSB_HCD_DRIVERKEY_NAME)Irp->AssociatedIrp.SystemBuffer; + + // + // check result + // + if (Status == STATUS_BUFFER_TOO_SMALL) + { + // + // does the caller provide enough buffer space + // + if (IoStack->Parameters.DeviceIoControl.OutputBufferLength >= ResultLength) + { + // + // it does + // + Status = IoGetDeviceProperty(m_PhysicalDeviceObject, DevicePropertyDriverKeyName, IoStack->Parameters.DeviceIoControl.OutputBufferLength - sizeof(ULONG), DriverKey->DriverKeyName, &ResultLength); + + if (NT_SUCCESS(Status)) + { + // + // informal debug print + // + DPRINT1("[USBLIB] Result %S\n", DriverKey->DriverKeyName); + } + } + + // + // store result + // + DriverKey->ActualLength = ResultLength + FIELD_OFFSET(USB_HCD_DRIVERKEY_NAME, DriverKeyName) + sizeof(WCHAR); + Irp->IoStatus.Information = IoStack->Parameters.DeviceIoControl.OutputBufferLength; + Status = STATUS_SUCCESS; + } + } + else + { + // + // buffer is certainly too small + // + Status = STATUS_BUFFER_OVERFLOW; + Irp->IoStatus.Information = sizeof(USB_HCD_DRIVERKEY_NAME); + } + } + else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_USB_GET_ROOT_HUB_NAME) + { + // + // check if sizee is at least >= USB_HCD_DRIVERKEY_NAME + // + if(IoStack->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(USB_HCD_DRIVERKEY_NAME)) + { + // + // sanity check + // + PC_ASSERT(m_HubController); + + // + // get input buffer + // + DriverKey = (PUSB_HCD_DRIVERKEY_NAME)Irp->AssociatedIrp.SystemBuffer; + + // + // get symbolic link + // + Status = m_HubController->GetHubControllerSymbolicLink(IoStack->Parameters.DeviceIoControl.OutputBufferLength - sizeof(ULONG), DriverKey->DriverKeyName, &ResultLength); + + + if (NT_SUCCESS(Status)) + { + // + // null terminate it + // + PC_ASSERT(IoStack->Parameters.DeviceIoControl.OutputBufferLength - sizeof(ULONG) - sizeof(WCHAR) >= ResultLength); + + DriverKey->DriverKeyName[ResultLength / sizeof(WCHAR)] = L'\0'; + DPRINT1("[USBLIB] Result %S\n", DriverKey->DriverKeyName); + } + + // + // store result + // + DriverKey->ActualLength = ResultLength + FIELD_OFFSET(USB_HCD_DRIVERKEY_NAME, DriverKeyName) + sizeof(WCHAR); + Irp->IoStatus.Information = IoStack->Parameters.DeviceIoControl.OutputBufferLength; + Status = STATUS_SUCCESS; + } + else + { + // + // buffer is certainly too small + // + Status = STATUS_BUFFER_OVERFLOW; + Irp->IoStatus.Information = sizeof(USB_HCD_DRIVERKEY_NAME); + } + } + + // + // complete the request + // + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + // + // done + // + return Status; +} + +NTSTATUS +CHCDController::HandlePnp( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PCOMMON_DEVICE_EXTENSION DeviceExtension; + PCM_RESOURCE_LIST RawResourceList; + PCM_RESOURCE_LIST TranslatedResourceList; + PDEVICE_RELATIONS DeviceRelations; + NTSTATUS Status; + + // + // get device extension + // + DeviceExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // sanity check + // + PC_ASSERT(DeviceExtension->IsFDO); + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + switch(IoStack->MinorFunction) + { + case IRP_MN_START_DEVICE: + { + DPRINT("[USBLIB] HandlePnp IRP_MN_START FDO\n"); + + // + // first start lower device object + // + Status = SyncForwardIrp(m_NextDeviceObject, Irp); + + if (NT_SUCCESS(Status)) + { + // + // operation succeeded, lets start the device + // + RawResourceList = IoStack->Parameters.StartDevice.AllocatedResources; + TranslatedResourceList = IoStack->Parameters.StartDevice.AllocatedResourcesTranslated; + + if (m_Hardware) + { + // + // start the hardware + // + Status = m_Hardware->PnpStart(RawResourceList, TranslatedResourceList); + } + + // + // enable symbolic link + // + Status = SetSymbolicLink(TRUE); + } + + DPRINT("[USBLIB] HandlePnp IRP_MN_START FDO: Status %x\n", Status); + break; + } + case IRP_MN_QUERY_DEVICE_RELATIONS: + { + DPRINT("[USBLIB] HandlePnp IRP_MN_QUERY_DEVICE_RELATIONS Type %lx\n", IoStack->Parameters.QueryDeviceRelations.Type); + + if (m_HubController == NULL) + { + // + // create hub controller + // + Status = CreateHubController(&m_HubController); + if (!NT_SUCCESS(Status)) + { + // + // failed to create hub controller + // + break; + } + + // + // initialize hub controller + // + Status = m_HubController->Initialize(m_DriverObject, PHCDCONTROLLER(this), m_Hardware, TRUE, 0 /* FIXME*/); + if (!NT_SUCCESS(Status)) + { + // + // failed to initialize hub controller + // + break; + } + + // + // add reference to prevent it from getting deleting while hub driver adds / removes references + // + m_HubController->AddRef(); + } + + if (IoStack->Parameters.QueryDeviceRelations.Type == BusRelations) + { + // + // allocate device relations + // + DeviceRelations = (PDEVICE_RELATIONS)ExAllocatePool(PagedPool, sizeof(DEVICE_RELATIONS)); + + if (!DeviceRelations) + { + // + // no memory + // + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // + // init device relations + // + DeviceRelations->Count = 1; + Status = m_HubController->GetHubControllerDeviceObject(&DeviceRelations->Objects [0]); + + // + // sanity check + // + PC_ASSERT(Status == STATUS_SUCCESS); + + ObReferenceObject(DeviceRelations->Objects [0]); + + // + // store result + // + Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations; + Status = STATUS_SUCCESS; + } + else + { + // + // not supported + // + Status = STATUS_NOT_SUPPORTED; + } + break; + } + case IRP_MN_STOP_DEVICE: + { + DPRINT("[USBLIB] HandlePnp IRP_MN_STOP_DEVICE\n"); + + if (m_Hardware) + { + // + // stop the hardware + // + Status = m_Hardware->PnpStop(); + } + else + { + // + // fake success + // + Status = STATUS_SUCCESS; + } + + if (NT_SUCCESS(Status)) + { + // + // stop lower device + // + Status = SyncForwardIrp(m_NextDeviceObject, Irp); + } + break; + } + case IRP_MN_QUERY_REMOVE_DEVICE: + case IRP_MN_QUERY_STOP_DEVICE: + { +#if 0 + // + // sure + // + Irp->IoStatus.Status = STATUS_SUCCESS; + + // + // forward irp to next device object + // + IoSkipCurrentIrpStackLocation(Irp); + return IoCallDriver(m_NextDeviceObject, Irp); +#else + DPRINT1("[USBLIB] Denying controller removal due to reinitialization bugs\n"); + Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_UNSUCCESSFUL; +#endif + } + case IRP_MN_REMOVE_DEVICE: + { + DPRINT("[USBLIB] HandlePnp IRP_MN_REMOVE_DEVICE FDO\n"); + + // + // delete the symbolic link + // + SetSymbolicLink(FALSE); + + // + // forward irp to next device object + // + IoSkipCurrentIrpStackLocation(Irp); + IoCallDriver(m_NextDeviceObject, Irp); + + // + // detach device from device stack + // + IoDetachDevice(m_NextDeviceObject); + + // + // delete device + // + IoDeleteDevice(m_FunctionalDeviceObject); + + return STATUS_SUCCESS; + } + default: + { + // + // forward irp to next device object + // + IoSkipCurrentIrpStackLocation(Irp); + return IoCallDriver(m_NextDeviceObject, Irp); + } + } + + // + // store result and complete request + // + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return Status; +} + +NTSTATUS +CHCDController::HandlePower( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + UNIMPLEMENTED + + Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +CHCDController::CreateFDO( + PDRIVER_OBJECT DriverObject, + PDEVICE_OBJECT * OutDeviceObject) +{ + WCHAR CharDeviceName[64]; + NTSTATUS Status; + ULONG UsbDeviceNumber = 0; + UNICODE_STRING DeviceName; + + while (TRUE) + { + // + // construct device name + // + swprintf(CharDeviceName, L"\\Device\\USBFDO-%d", UsbDeviceNumber); + + // + // initialize device name + // + RtlInitUnicodeString(&DeviceName, CharDeviceName); + + // + // create device + // + Status = IoCreateDevice(DriverObject, + sizeof(COMMON_DEVICE_EXTENSION), + &DeviceName, + FILE_DEVICE_CONTROLLER, + 0, + FALSE, + OutDeviceObject); + + // + // check for success + // + if (NT_SUCCESS(Status)) + break; + + // + // is there a device object with that same name + // + if ((Status == STATUS_OBJECT_NAME_EXISTS) || (Status == STATUS_OBJECT_NAME_COLLISION)) + { + // + // Try the next name + // + UsbDeviceNumber++; + continue; + } + + // + // bail out on other errors + // + if (!NT_SUCCESS(Status)) + { + DPRINT1("[USBLIB] CreateFDO: Failed to create %wZ, Status %x\n", &DeviceName, Status); + return Status; + } + } + + // + // store FDO number + // + m_FDODeviceNumber = UsbDeviceNumber; + + DPRINT("[USBLIB] CreateFDO: DeviceName %wZ\n", &DeviceName); + + /* done */ + return Status; +} + +NTSTATUS +CHCDController::SetSymbolicLink( + BOOLEAN Enable) +{ + NTSTATUS Status; + WCHAR LinkName[32]; + WCHAR FDOName[32]; + UNICODE_STRING Link, FDO; + + if (Enable) + { + // + // create legacy link + // + swprintf(LinkName, L"\\DosDevices\\HCD%d", m_FDODeviceNumber); + swprintf(FDOName, L"\\Device\\USBFDO-%d", m_FDODeviceNumber); + RtlInitUnicodeString(&Link, LinkName); + RtlInitUnicodeString(&FDO, FDOName); + + // + // create symbolic link + // + Status = IoCreateSymbolicLink(&Link, &FDO); + + if (!NT_SUCCESS(Status)) + { + // + // FIXME: handle me + // + ASSERT(0); + } + } + else + { + // + // create legacy link + // + swprintf(LinkName, L"\\DosDevices\\HCD%d", m_FDODeviceNumber); + RtlInitUnicodeString(&Link, LinkName); + + // + // now delete the symbolic link + // + Status = IoDeleteSymbolicLink(&Link); + + if (!NT_SUCCESS(Status)) + { + // + // FIXME: handle me + // + ASSERT(0); + } + } + + // + // done + // + return Status; +} + +NTSTATUS +CreateHCDController( + PHCDCONTROLLER *OutHcdController) +{ + PHCDCONTROLLER This; + + // + // allocate controller + // + This = new(NonPagedPool, TAG_USBLIB) CHCDController(0); + if (!This) + { + // + // failed to allocate + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // add reference count + // + This->AddRef(); + + // + // return result + // + *OutHcdController = (PHCDCONTROLLER)This; + + // + // done + // + return STATUS_SUCCESS; +} diff --git a/reactos/lib/drivers/libusb/hub_controller.cpp b/reactos/lib/drivers/libusb/hub_controller.cpp new file mode 100644 index 00000000000..f39723ecc49 --- /dev/null +++ b/reactos/lib/drivers/libusb/hub_controller.cpp @@ -0,0 +1,3776 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Driver Library + * LICENSE: GPL - See COPYING in the top level directory + * FILE: lib/drivers/libusb/hub_controller.cpp + * PURPOSE: USB Common Driver Library. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + + +#define INITGUID +#include "libusb.h" + +VOID StatusChangeEndpointCallBack( + PVOID Context); + +class CHubController : public IHubController, + public IDispatchIrp +{ +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; + } + + // IHubController interface functions + virtual NTSTATUS Initialize(IN PDRIVER_OBJECT DriverObject, IN PHCDCONTROLLER Controller, IN PUSBHARDWAREDEVICE Device, IN BOOLEAN IsRootHubDevice, IN ULONG DeviceAddress); + virtual NTSTATUS GetHubControllerDeviceObject(PDEVICE_OBJECT * HubDeviceObject); + virtual NTSTATUS GetHubControllerSymbolicLink(ULONG BufferLength, PVOID Buffer, PULONG RequiredLength); + + // IDispatchIrp interface functions + virtual NTSTATUS HandlePnp(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp); + virtual NTSTATUS HandlePower(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp); + virtual NTSTATUS HandleDeviceControl(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp); + + // local functions + NTSTATUS HandleQueryInterface(PIO_STACK_LOCATION IoStack); + NTSTATUS SetDeviceInterface(BOOLEAN bEnable); + NTSTATUS CreatePDO(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT * OutDeviceObject); + PUSBHARDWAREDEVICE GetUsbHardware(); + ULONG AcquireDeviceAddress(); + VOID ReleaseDeviceAddress(ULONG DeviceAddress); + BOOLEAN ValidateUsbDevice(PUSBDEVICE UsbDevice); + NTSTATUS AddUsbDevice(PUSBDEVICE UsbDevice); + NTSTATUS RemoveUsbDevice(PUSBDEVICE UsbDevice); + VOID SetNotification(PVOID CallbackContext, PRH_INIT_CALLBACK CallbackRoutine); + // internal ioctl routines + NTSTATUS HandleGetDescriptor(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleGetDescriptorFromInterface(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleClassDevice(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleGetStatusFromDevice(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleSelectConfiguration(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleSelectInterface(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleClassOther(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleClassInterface(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleClassEndpoint(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleBulkOrInterruptTransfer(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleIsochronousTransfer(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleClearStall(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleSyncResetAndClearStall(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleAbortPipe(IN OUT PIRP Irp, PURB Urb); + + friend VOID StatusChangeEndpointCallBack(PVOID Context); + + // constructor / destructor + CHubController(IUnknown *OuterUnknown){} + virtual ~CHubController(){} + +protected: + LONG m_Ref; + PHCDCONTROLLER m_Controller; + PUSBHARDWAREDEVICE m_Hardware; + BOOLEAN m_IsRootHubDevice; + ULONG m_DeviceAddress; + + BOOLEAN m_InterfaceEnabled; + UNICODE_STRING m_HubDeviceInterfaceString; + + PDEVICE_OBJECT m_HubControllerDeviceObject; + PDRIVER_OBJECT m_DriverObject; + + PVOID m_HubCallbackContext; + PRH_INIT_CALLBACK m_HubCallbackRoutine; + + USB_DEVICE_DESCRIPTOR m_DeviceDescriptor; + + KSPIN_LOCK m_Lock; + RTL_BITMAP m_DeviceAddressBitmap; + PULONG m_DeviceAddressBitmapBuffer; + LIST_ENTRY m_UsbDeviceList; + PIRP m_PendingSCEIrp; + + //Internal Functions + BOOLEAN QueryStatusChageEndpoint(PIRP Irp); +}; + +typedef struct +{ + LIST_ENTRY Entry; + PUSBDEVICE Device; +}USBDEVICE_ENTRY, *PUSBDEVICE_ENTRY; + +/* Lifted from Linux with slight changes */ +const UCHAR ROOTHUB2_DEVICE_DESCRIPTOR [] = +{ + 0x12, /* bLength; */ + USB_DEVICE_DESCRIPTOR_TYPE, /* bDescriptorType; Device */ + 0x00, 0x20, /* bcdUSB; v1.1 */ + USB_DEVICE_CLASS_HUB, /* bDeviceClass; HUB_CLASSCODE */ + 0x01, /* bDeviceSubClass; */ + 0x00, /* bDeviceProtocol; [ low/full speeds only ] */ + 0x08, /* bMaxPacketSize0; 8 Bytes */ + /* Fill Vendor and Product in when init root hub */ + 0x00, 0x00, /* idVendor; */ + 0x00, 0x00, /* idProduct; */ + 0x00, 0x00, /* bcdDevice */ + 0x00, /* iManufacturer; */ + 0x00, /* iProduct; */ + 0x00, /* iSerialNumber; */ + 0x01 /* bNumConfigurations; */ + +}; + +const USB_CONFIGURATION_DESCRIPTOR ROOTHUB2_CONFIGURATION_DESCRIPTOR = +{ + sizeof(USB_CONFIGURATION_DESCRIPTOR), + USB_CONFIGURATION_DESCRIPTOR_TYPE, + sizeof(USB_CONFIGURATION_DESCRIPTOR) + sizeof(USB_INTERFACE_DESCRIPTOR) + sizeof(USB_ENDPOINT_DESCRIPTOR), + 1, + 1, + 0, + 0x40, /* self powered */ + 0x0 +}; + +const USB_INTERFACE_DESCRIPTOR ROOTHUB2_INTERFACE_DESCRIPTOR = +{ + sizeof(USB_INTERFACE_DESCRIPTOR), /* bLength */ + USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType; Interface */ + 0, /* bInterfaceNumber; */ + 0, /* bAlternateSetting; */ + 0x1, /* bNumEndpoints; */ + 0x09, /* bInterfaceClass; HUB_CLASSCODE */ + 0x01, /* bInterfaceSubClass; */ + 0x00, /* bInterfaceProtocol: */ + 0x00, /* iInterface; */ +}; + +const USB_ENDPOINT_DESCRIPTOR ROOTHUB2_ENDPOINT_DESCRIPTOR = +{ + sizeof(USB_ENDPOINT_DESCRIPTOR), /* bLength */ + USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ + 0x81, /* bEndPointAddress */ + USB_ENDPOINT_TYPE_INTERRUPT, /* bmAttributes */ + 0x01, /* wMaxPacketSize */ + 0xC /* bInterval */ +}; + +//---------------------------------------------------------------------------------------- +NTSTATUS +STDMETHODCALLTYPE +CHubController::QueryInterface( + IN REFIID refiid, + OUT PVOID* Output) +{ + return STATUS_UNSUCCESSFUL; +} +//---------------------------------------------------------------------------------------- +NTSTATUS +CHubController::Initialize( + IN PDRIVER_OBJECT DriverObject, + IN PHCDCONTROLLER Controller, + IN PUSBHARDWAREDEVICE Device, + IN BOOLEAN IsRootHubDevice, + IN ULONG DeviceAddress) +{ + NTSTATUS Status; + PCOMMON_DEVICE_EXTENSION DeviceExtension; + USHORT VendorID, DeviceID; + ULONG Dummy1; + + DPRINT1("CHubController::Initialize\n"); + + // + // initialize members + // + m_Controller = Controller; + m_Hardware = Device; + m_IsRootHubDevice = IsRootHubDevice; + m_DeviceAddress = DeviceAddress; + m_DriverObject = DriverObject; + KeInitializeSpinLock(&m_Lock); + InitializeListHead(&m_UsbDeviceList); + + // + // allocate device address bitmap buffer + // + m_DeviceAddressBitmapBuffer = (PULONG)ExAllocatePoolWithTag(NonPagedPool, 16, TAG_USBLIB); + if (!m_DeviceAddressBitmapBuffer) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // initialize device address bitmap + // + RtlInitializeBitMap(&m_DeviceAddressBitmap, m_DeviceAddressBitmapBuffer, 128); + RtlClearAllBits(&m_DeviceAddressBitmap); + + + // + // create PDO + // + Status = CreatePDO(m_DriverObject, &m_HubControllerDeviceObject); + if (!NT_SUCCESS(Status)) + { + // + // failed to create hub device object + // + return Status; + } + + // + // get device extension + // + DeviceExtension = (PCOMMON_DEVICE_EXTENSION)m_HubControllerDeviceObject->DeviceExtension; + + // + // initialize device extension + // + DeviceExtension->IsFDO = FALSE; + DeviceExtension->IsHub = TRUE; //FIXME + DeviceExtension->Dispatcher = PDISPATCHIRP(this); + + // + // intialize device descriptor + // + C_ASSERT(sizeof(USB_DEVICE_DESCRIPTOR) == sizeof(ROOTHUB2_DEVICE_DESCRIPTOR)); + RtlMoveMemory(&m_DeviceDescriptor, ROOTHUB2_DEVICE_DESCRIPTOR, sizeof(USB_DEVICE_DESCRIPTOR)); + + if (NT_SUCCESS(m_Hardware->GetDeviceDetails(&VendorID, &DeviceID, &Dummy1, &Dummy1))) + { + // + // update device descriptor + // + m_DeviceDescriptor.idVendor = VendorID; + m_DeviceDescriptor.idProduct = DeviceID; + m_DeviceDescriptor.bcdUSB = 0x200; //FIXME + } + + // + // Set the SCE Callback that the Hardware Device will call on port status change + // + Device->SetStatusChangeEndpointCallBack((PVOID)StatusChangeEndpointCallBack, this); + + // + // clear init flag + // + m_HubControllerDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + + return STATUS_SUCCESS; +} + +// +// Queries the ports to see if there has been a device connected or removed. +// +BOOLEAN +CHubController::QueryStatusChageEndpoint( + PIRP Irp) +{ + ULONG PortCount, PortId; + PIO_STACK_LOCATION IoStack; + USHORT PortStatus, PortChange; + PURB Urb; + PUCHAR TransferBuffer; + UCHAR Changed = FALSE; + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + ASSERT(IoStack); + + // + // Get the Urb + // + Urb = (PURB)IoStack->Parameters.Others.Argument1; + ASSERT(Urb); + + // + // Get the number of ports and check each one for device connected + // + m_Hardware->GetDeviceDetails(NULL, NULL, &PortCount, NULL); + DPRINT1("[USBLIB] SCE Request %p TransferBufferLength %lu Flags %x MDL %p\n", Urb->UrbBulkOrInterruptTransfer.TransferBuffer, Urb->UrbBulkOrInterruptTransfer.TransferBufferLength, Urb->UrbBulkOrInterruptTransfer.TransferFlags, Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL); + + TransferBuffer = (PUCHAR)Urb->UrbBulkOrInterruptTransfer.TransferBuffer; + + // + // Loop the ports + // + for (PortId = 0; PortId < PortCount; PortId++) + { + m_Hardware->GetPortStatus(PortId, &PortStatus, &PortChange); + + DPRINT1("[USBLIB] Port %d: Status %x, Change %x\n", PortId, PortStatus, PortChange); + + + // + // If theres a flag in PortChange return TRUE so the SCE Irp will be completed + // + if (PortChange != 0) + { + DPRINT1("[USBLIB] Change state on port %d\n", PortId); + // Set the value for the port number + *TransferBuffer = 1 << ((PortId + 1) & 7); + Changed = TRUE; + } + } + + return Changed; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::GetHubControllerDeviceObject(PDEVICE_OBJECT * HubDeviceObject) +{ + // + // store controller object + // + *HubDeviceObject = m_HubControllerDeviceObject; + + return STATUS_SUCCESS; +} +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::GetHubControllerSymbolicLink( + ULONG BufferLength, + PVOID Buffer, + PULONG RequiredLength) +{ + if (!m_InterfaceEnabled) + { + // + // device interface not yet enabled + // + return STATUS_UNSUCCESSFUL; + } + + if (BufferLength < (ULONG)m_HubDeviceInterfaceString.Length - 8) + { + // + // buffer too small + // length is without '\??\' + // + *RequiredLength = m_HubDeviceInterfaceString.Length- 8; + + // + // done + // + return STATUS_BUFFER_OVERFLOW; + } + + // + // copy symbolic link + // + RtlCopyMemory(Buffer, &m_HubDeviceInterfaceString.Buffer[4], m_HubDeviceInterfaceString.Length - 8); + + // + // store length, length is without '\??\' + // + *RequiredLength = m_HubDeviceInterfaceString.Length - 8; + + // + // done + // + return STATUS_SUCCESS; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandlePnp( + IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PCOMMON_DEVICE_EXTENSION DeviceExtension; + PDEVICE_CAPABILITIES DeviceCapabilities; + PPNP_BUS_INFORMATION BusInformation; + PDEVICE_RELATIONS DeviceRelations; + NTSTATUS Status; + ULONG Index = 0, Length; + USHORT VendorID, DeviceID; + ULONG HiSpeed, NumPorts; + WCHAR Buffer[300]; + LPWSTR DeviceName; + + // + // get device extension + // + DeviceExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // sanity check + // + ASSERT(DeviceExtension->IsFDO == FALSE); + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + switch(IoStack->MinorFunction) + { + case IRP_MN_START_DEVICE: + { + DPRINT("[USBLIB] HandlePnp IRP_MN_START_DEVICE\n"); + // + // register device interface + // + Status = SetDeviceInterface(TRUE); + break; + } + case IRP_MN_QUERY_STOP_DEVICE: + case IRP_MN_QUERY_REMOVE_DEVICE: + { + // + // sure + // + Status = STATUS_SUCCESS; + break; + } + case IRP_MN_QUERY_ID: + { + DPRINT("[USBLIB] HandlePnp IRP_MN_QUERY_ID Type %x\n", IoStack->Parameters.QueryId.IdType); + + if (IoStack->Parameters.QueryId.IdType == BusQueryDeviceID) + { + if (m_Hardware) + { + // + // query device id + // + Status = m_Hardware->GetDeviceDetails(&VendorID, &DeviceID, &NumPorts, &HiSpeed); + + if (HiSpeed == 0x200) + { + // + // USB 2.0 hub + // + swprintf(Buffer, L"USB\\ROOT_HUB20"); + } + else + { + // + // USB 1.1 hub + // + swprintf(Buffer, L"USB\\ROOT_HUB"); + } + + DPRINT("Name %S\n", Buffer); + + // + // calculate length + // + Length = (wcslen(Buffer) + 1); + + // + // allocate buffer + // + DeviceName = (LPWSTR)ExAllocatePoolWithTag(PagedPool, Length * sizeof(WCHAR), TAG_USBLIB); + + if (!DeviceName) + { + // + // no memory + // + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // + // copy device name + // + wcscpy(DeviceName, Buffer); + + // + // store result + // + Irp->IoStatus.Information = (ULONG_PTR)DeviceName; + Status = STATUS_SUCCESS; + break; + } + Status = STATUS_UNSUCCESSFUL; + PC_ASSERT(0); + break; + } + + if (IoStack->Parameters.QueryId.IdType == BusQueryHardwareIDs) + { + if (m_Hardware) + { + // + // query device id + // + Status = m_Hardware->GetDeviceDetails(&VendorID, &DeviceID, &NumPorts, &HiSpeed); + + if (!NT_SUCCESS(Status)) + { + DPRINT1("[USBLIB] HandlePnp> failed to get hardware id %x\n", Status); + VendorID = 0x8086; + DeviceID = 0x3A37; + } + + if (HiSpeed == 0x200) + { + // + // USB 2.0 hub + // + Index += swprintf(&Buffer[Index], L"USB\\ROOT_HUB20&VID%04x&PID%04x&REV0000", VendorID, DeviceID) + 1; + Index += swprintf(&Buffer[Index], L"USB\\ROOT_HUB20&VID%04x&PID%04x", VendorID, DeviceID) + 1; + Index += swprintf(&Buffer[Index], L"USB\\ROOT_HUB20") + 1; + } + else + { + // + // USB 1.1 hub + // + Index += swprintf(&Buffer[Index], L"USB\\ROOT_HUB&VID%04x&PID%04x&REV0000", VendorID, DeviceID) + 1; + Index += swprintf(&Buffer[Index], L"USB\\ROOT_HUB&VID%04x&PID%04x", VendorID, DeviceID) + 1; + Index += swprintf(&Buffer[Index], L"USB\\ROOT_HUB") + 1; + } + + Buffer[Index] = UNICODE_NULL; + Index++; + + + DPRINT1("[USBLIB] Name %S\n", Buffer); + + // + // allocate buffer + // + DeviceName = (LPWSTR)ExAllocatePoolWithTag(PagedPool, Index * sizeof(WCHAR), TAG_USBLIB); + + if (!DeviceName) + { + // + // no memory + // + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // + // copy device name + // + RtlMoveMemory(DeviceName, Buffer, Index * sizeof(WCHAR)); + + // + // store result + // + Irp->IoStatus.Information = (ULONG_PTR)DeviceName; + Status = STATUS_SUCCESS; + break; + } + } + Status = STATUS_SUCCESS; + break; + } + case IRP_MN_QUERY_CAPABILITIES: + { + DPRINT("[USBLIB] HandlePnp IRP_MN_QUERY_CAPABILITIES\n"); + + DeviceCapabilities = (PDEVICE_CAPABILITIES)IoStack->Parameters.DeviceCapabilities.Capabilities; + + DeviceCapabilities->LockSupported = FALSE; + DeviceCapabilities->EjectSupported = FALSE; + DeviceCapabilities->Removable = FALSE; + DeviceCapabilities->DockDevice = FALSE; + DeviceCapabilities->UniqueID = FALSE; + DeviceCapabilities->SilentInstall = FALSE; + DeviceCapabilities->RawDeviceOK = FALSE; + DeviceCapabilities->SurpriseRemovalOK = FALSE; + DeviceCapabilities->Address = 0; + DeviceCapabilities->UINumber = 0; + DeviceCapabilities->DeviceD2 = 1; + + /* FIXME */ + DeviceCapabilities->HardwareDisabled = FALSE; + DeviceCapabilities->NoDisplayInUI = FALSE; + DeviceCapabilities->DeviceState[0] = PowerDeviceD0; + for (Index = 1; Index < PowerSystemMaximum; Index++) + DeviceCapabilities->DeviceState[Index] = PowerDeviceD3; + DeviceCapabilities->DeviceWake = PowerDeviceUnspecified; + DeviceCapabilities->D1Latency = 0; + DeviceCapabilities->D2Latency = 0; + DeviceCapabilities->D3Latency = 0; + + Status = STATUS_SUCCESS; + break; + } + case IRP_MN_QUERY_INTERFACE: + { + DPRINT("[USBLIB] HandlePnp IRP_MN_QUERY_INTERFACE\n"); + + // + // handle device interface requests + // + Status = HandleQueryInterface(IoStack); + break; + } + case IRP_MN_REMOVE_DEVICE: + { + DPRINT("[USBLIB] HandlePnp IRP_MN_REMOVE_DEVICE\n"); + + // + // deactivate device interface for BUS PDO + // + SetDeviceInterface(FALSE); + + // + // complete the request first + // + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + // + // now delete device + // + IoDeleteDevice(m_HubControllerDeviceObject); + + // + // nullify pointer + // + m_HubControllerDeviceObject = 0; + + // + // done + // + return STATUS_SUCCESS; + } + case IRP_MN_QUERY_DEVICE_RELATIONS: + { + DPRINT("[USBLIB] HandlePnp IRP_MN_QUERY_DEVICE_RELATIONS Type %x\n", IoStack->Parameters.QueryDeviceRelations.Type); + + if (IoStack->Parameters.QueryDeviceRelations.Type == TargetDeviceRelation) + { + // + // allocate device relations + // + DeviceRelations = (PDEVICE_RELATIONS)ExAllocatePoolWithTag(PagedPool, sizeof(DEVICE_RELATIONS), TAG_USBLIB); + if (!DeviceRelations) + { + // + // no memory + // + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // + // initialize device relations + // + DeviceRelations->Count = 1; + DeviceRelations->Objects[0] = DeviceObject; + ObReferenceObject(DeviceObject); + + // + // done + // + Status = STATUS_SUCCESS; + Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations; + } + else + { + // + // not handled + // + Status = Irp->IoStatus.Status; + } + break; + } + case IRP_MN_QUERY_BUS_INFORMATION: + { + DPRINT("[USBLIB] HandlePnp IRP_MN_QUERY_BUS_INFORMATION\n"); + + // + // allocate buffer for bus information + // + BusInformation = (PPNP_BUS_INFORMATION)ExAllocatePool(PagedPool, sizeof(PNP_BUS_INFORMATION)); + if (BusInformation) + { + // + // copy BUS guid + // + RtlMoveMemory(&BusInformation->BusTypeGuid, &GUID_BUS_TYPE_USB, sizeof(GUID)); + + // + // set bus type + // + BusInformation->LegacyBusType = PNPBus; + BusInformation->BusNumber = 0; + + Status = STATUS_SUCCESS; + Irp->IoStatus.Information = (ULONG_PTR)BusInformation; + } + else + { + // + // no memory + // + Status = STATUS_INSUFFICIENT_RESOURCES; + } + break; + } + case IRP_MN_STOP_DEVICE: + { + DPRINT("[USBLIB] HandlePnp IRP_MN_STOP_DEVICE\n"); + // + // stop device + // + Status = STATUS_SUCCESS; + break; + } + default: + { + // + // ignore request with default status + // + Status = Irp->IoStatus.Status; + break; + } + } + + // + // complete request + // + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + // + // done + // + return Status; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandlePower( + IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp) +{ + UNIMPLEMENTED + Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_NOT_IMPLEMENTED; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleIsochronousTransfer( + IN OUT PIRP Irp, + PURB Urb) +{ + PUSBDEVICE UsbDevice; + PUSB_ENDPOINT_DESCRIPTOR EndPointDesc = NULL; + + // + // Check PipeHandle to determine if this is a Bulk or Interrupt Transfer Request + // + EndPointDesc = (PUSB_ENDPOINT_DESCRIPTOR)Urb->UrbIsochronousTransfer.PipeHandle; + + if (!EndPointDesc) + { + DPRINT1("[USBLIB] Error No EndpointDesc\n"); + Urb->UrbIsochronousTransfer.Hdr.Status = USBD_STATUS_INVALID_PIPE_HANDLE; + return STATUS_INVALID_PARAMETER; + } + + // + // sanity checks + // + ASSERT(EndPointDesc); + ASSERT((EndPointDesc->bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_ISOCHRONOUS); + + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("[USBLIB] HandleIsochronousTransfer invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + return UsbDevice->SubmitIrp(Irp); +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleBulkOrInterruptTransfer( + IN OUT PIRP Irp, + PURB Urb) +{ + PUSBDEVICE UsbDevice; + PUSB_ENDPOINT EndPointDesc = NULL; + // + // First check if the request is for the Status Change Endpoint + // + + // + // Is the Request for the root hub + // + if (Urb->UrbHeader.UsbdDeviceHandle == 0) + { + ASSERT(m_PendingSCEIrp == NULL); + if (QueryStatusChageEndpoint(Irp)) + { + StatusChangeEndpointCallBack(this); + return STATUS_SUCCESS; + } + + // + // Else pend the IRP, to be completed when a device connects or disconnects. + // + DPRINT("[USBLIB] Pending SCE Irp\n"); + m_PendingSCEIrp = Irp; + IoMarkIrpPending(Irp); + return STATUS_PENDING; + } + + // + // Check PipeHandle to determine if this is a Bulk or Interrupt Transfer Request + // + EndPointDesc = (PUSB_ENDPOINT)Urb->UrbBulkOrInterruptTransfer.PipeHandle; + + // + // sanity checks + // + ASSERT(EndPointDesc); + ASSERT((EndPointDesc->EndPointDescriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_BULK || (EndPointDesc->EndPointDescriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_INTERRUPT); + + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("[USBLIB] HandleBulkOrInterruptTransfer invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + return UsbDevice->SubmitIrp(Irp); +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleClassOther( + IN OUT PIRP Irp, + PURB Urb) +{ + NTSTATUS Status = STATUS_NOT_IMPLEMENTED; + USHORT PortStatus = 0, PortChange = 0; + PUSHORT Buffer; + ULONG NumPort; + ULONG PortId; + + DPRINT("[USBLIB] HandleClassOther> Request %x Value %x\n", Urb->UrbControlVendorClassRequest.Request, Urb->UrbControlVendorClassRequest.Value); + + // + // get number of ports available + // + Status = m_Hardware->GetDeviceDetails(NULL, NULL, &NumPort, NULL); + PC_ASSERT(Status == STATUS_SUCCESS); + + // + // sanity check + // + PC_ASSERT(Urb->UrbControlVendorClassRequest.Index - 1 < (USHORT)NumPort); + + // + // port range reported start from 1 -n + // convert back port id so it matches the hardware + // + PortId = Urb->UrbControlVendorClassRequest.Index - 1; + + // + // check request code + // + switch(Urb->UrbControlVendorClassRequest.Request) + { + case USB_REQUEST_GET_STATUS: + { + // + // sanity check + // + PC_ASSERT(Urb->UrbControlVendorClassRequest.TransferBufferLength == sizeof(USHORT) * 2); + PC_ASSERT(Urb->UrbControlVendorClassRequest.TransferBuffer); + + // + // get port status + // + Status = m_Hardware->GetPortStatus(PortId, &PortStatus, &PortChange); + + if (NT_SUCCESS(Status)) + { + // + // request contains buffer of 2 ushort which are used from submitting port status and port change status + // + DPRINT("[USBLIB] PortId %x PortStatus %x PortChange %x\n", PortId, PortStatus, PortChange); + Buffer = (PUSHORT)Urb->UrbControlVendorClassRequest.TransferBuffer; + + // + // store status, then port change + // + *Buffer = PortStatus; + Buffer++; + *Buffer = PortChange; + } + + // + // done + // + break; + } + case USB_REQUEST_CLEAR_FEATURE: + { + switch (Urb->UrbControlVendorClassRequest.Value) + { + case C_PORT_CONNECTION: + Status = m_Hardware->ClearPortStatus(PortId, C_PORT_CONNECTION); + break; + case C_PORT_RESET: + Status= m_Hardware->ClearPortStatus(PortId, C_PORT_RESET); + break; + default: + DPRINT("[USBLIB] Unknown Value for Clear Feature %x \n", Urb->UrbControlVendorClassRequest.Value); + break; + } + + break; + } + case USB_REQUEST_SET_FEATURE: + { + // + // request set feature + // + switch(Urb->UrbControlVendorClassRequest.Value) + { + case PORT_ENABLE: + { + // + // port enable is a no-op for EHCI + // + Status = STATUS_SUCCESS; + break; + } + + case PORT_SUSPEND: + { + // + // set suspend port feature + // + Status = m_Hardware->SetPortFeature(PortId, PORT_SUSPEND); + break; + } + case PORT_POWER: + { + // + // set power feature on port + // + Status = m_Hardware->SetPortFeature(PortId, PORT_POWER); + break; + } + + case PORT_RESET: + { + // + // reset port feature + // + Status = m_Hardware->SetPortFeature(PortId, PORT_RESET); + PC_ASSERT(Status == STATUS_SUCCESS); + break; + } + default: + DPRINT1("[USBLIB] Unsupported request id %x\n", Urb->UrbControlVendorClassRequest.Value); + PC_ASSERT(FALSE); + } + break; + } + default: + DPRINT1("[USBLIB] HandleClassOther Unknown request code %x\n", Urb->UrbControlVendorClassRequest.Request); + PC_ASSERT(0); + Status = STATUS_INVALID_DEVICE_REQUEST; + } + return Status; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleSelectConfiguration( + IN OUT PIRP Irp, + PURB Urb) +{ + PUSBDEVICE UsbDevice; + PUSBD_INTERFACE_INFORMATION InterfaceInfo; + + // + // is the request for the Root Hub + // + if (Urb->UrbHeader.UsbdDeviceHandle == NULL) + { + // + // FIXME: support setting device to unconfigured state + // + PC_ASSERT(Urb->UrbSelectConfiguration.ConfigurationDescriptor); + + // + // set device handle + // + Urb->UrbSelectConfiguration.ConfigurationHandle = (PVOID)&ROOTHUB2_CONFIGURATION_DESCRIPTOR; + + // + // copy interface info + // + InterfaceInfo = &Urb->UrbSelectConfiguration.Interface; + + InterfaceInfo->InterfaceHandle = (USBD_INTERFACE_HANDLE)&ROOTHUB2_INTERFACE_DESCRIPTOR; + InterfaceInfo->Class = ROOTHUB2_INTERFACE_DESCRIPTOR.bInterfaceClass; + InterfaceInfo->SubClass = ROOTHUB2_INTERFACE_DESCRIPTOR.bInterfaceSubClass; + InterfaceInfo->Protocol = ROOTHUB2_INTERFACE_DESCRIPTOR.bInterfaceProtocol; + InterfaceInfo->Reserved = 0; + + // + // sanity check + // + PC_ASSERT(InterfaceInfo->NumberOfPipes == 1); + + // + // copy pipe info + // + InterfaceInfo->Pipes[0].MaximumPacketSize = ROOTHUB2_ENDPOINT_DESCRIPTOR.wMaxPacketSize; + InterfaceInfo->Pipes[0].EndpointAddress = ROOTHUB2_ENDPOINT_DESCRIPTOR.bEndpointAddress; + InterfaceInfo->Pipes[0].Interval = ROOTHUB2_ENDPOINT_DESCRIPTOR.bInterval; + InterfaceInfo->Pipes[0].PipeType = (USBD_PIPE_TYPE)(ROOTHUB2_ENDPOINT_DESCRIPTOR.bmAttributes & USB_ENDPOINT_TYPE_MASK); + InterfaceInfo->Pipes[0].PipeHandle = (PVOID)&ROOTHUB2_ENDPOINT_DESCRIPTOR; + + return STATUS_SUCCESS; + } + else + { + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("[USBLIB] HandleSelectConfiguration invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + // + // select configuration + // + return UsbDevice->SelectConfiguration(Urb->UrbSelectConfiguration.ConfigurationDescriptor, &Urb->UrbSelectConfiguration.Interface, &Urb->UrbSelectConfiguration.ConfigurationHandle); + } +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleSelectInterface( + IN OUT PIRP Irp, + PURB Urb) +{ + PUSBDEVICE UsbDevice; + + // + // sanity check + // + PC_ASSERT(Urb->UrbSelectInterface.ConfigurationHandle); + + // + // is the request for the Root Hub + // + if (Urb->UrbHeader.UsbdDeviceHandle == NULL) + { + // + // no op for root hub + // + return STATUS_SUCCESS; + } + else + { + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("[USBLIB] HandleSelectInterface invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + // + // select interface + // + return UsbDevice->SelectInterface(Urb->UrbSelectInterface.ConfigurationHandle, &Urb->UrbSelectInterface.Interface); + } +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleGetStatusFromDevice( + IN OUT PIRP Irp, + PURB Urb) +{ + PUSHORT DeviceStatus; + USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + NTSTATUS Status; + PUSBDEVICE UsbDevice; + + // + // sanity checks + // + PC_ASSERT(Urb->UrbControlGetStatusRequest.TransferBufferLength >= sizeof(USHORT)); + PC_ASSERT(Urb->UrbControlGetStatusRequest.TransferBuffer); + + // + // get status buffer + // + DeviceStatus = (PUSHORT)Urb->UrbControlGetStatusRequest.TransferBuffer; + + + if (Urb->UrbHeader.UsbdDeviceHandle == NULL) + { + // + // FIXME need more flags ? + // + *DeviceStatus = USB_PORT_STATUS_CONNECT; + return STATUS_SUCCESS; + } + + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("[USBLIB] HandleGetStatusFromDevice invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + + // + // generate setup packet + // + CtrlSetup.bRequest = USB_REQUEST_GET_STATUS; + CtrlSetup.wValue.LowByte = 0; + CtrlSetup.wValue.HiByte = 0; + CtrlSetup.wIndex.W = Urb->UrbControlGetStatusRequest.Index; + CtrlSetup.wLength = (USHORT)Urb->UrbControlGetStatusRequest.TransferBufferLength; + CtrlSetup.bmRequestType.B = 0x80; + + + if (Urb->UrbHeader.Function == URB_FUNCTION_GET_STATUS_FROM_INTERFACE) + { + // + // add interface type + // + CtrlSetup.bmRequestType.B |= 0x01; + } + else if (Urb->UrbHeader.Function == URB_FUNCTION_GET_STATUS_FROM_ENDPOINT) + { + // + // add interface type + // + CtrlSetup.bmRequestType.B |= 0x02; + } + + // + // submit setup packet + // + Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, Urb->UrbControlDescriptorRequest.TransferBufferLength, Urb->UrbControlDescriptorRequest.TransferBuffer); + ASSERT(Status == STATUS_SUCCESS); + DPRINT1("[USBLIB] HandleGetStatusFromDevice Status %x Length %lu DeviceStatus %x\n", Status, Urb->UrbControlDescriptorRequest.TransferBufferLength, *DeviceStatus); + + // + // done + // + return Status; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleClassDevice( + IN OUT PIRP Irp, + IN OUT PURB Urb) +{ + NTSTATUS Status = STATUS_NOT_IMPLEMENTED; + PUSB_HUB_DESCRIPTOR UsbHubDescriptor; + ULONG PortCount, Dummy2; + USHORT Dummy1; + PUSBDEVICE UsbDevice; + USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + + DPRINT("CHubController::HandleClassDevice Request %x Class %x\n", Urb->UrbControlVendorClassRequest.Request, Urb->UrbControlVendorClassRequest.Value >> 8); + + // + // check class request type + // + switch(Urb->UrbControlVendorClassRequest.Request) + { + case USB_REQUEST_GET_STATUS: + { + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("HandleClassDevice invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + + // + // generate setup packet + // + CtrlSetup.bRequest = USB_REQUEST_GET_STATUS; + CtrlSetup.wValue.LowByte = Urb->UrbControlVendorClassRequest.Index; + CtrlSetup.wValue.W = Urb->UrbControlVendorClassRequest.Value; + CtrlSetup.wIndex.W = Urb->UrbControlVendorClassRequest.Index; + CtrlSetup.wLength = (USHORT)Urb->UrbControlGetStatusRequest.TransferBufferLength; + CtrlSetup.bmRequestType.B = 0xA0; + + // + // submit setup packet + // + Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, Urb->UrbControlDescriptorRequest.TransferBufferLength, Urb->UrbControlDescriptorRequest.TransferBuffer); + ASSERT(Status == STATUS_SUCCESS); + break; + } + case USB_REQUEST_GET_DESCRIPTOR: + { + switch (Urb->UrbControlVendorClassRequest.Value >> 8) + { + case USB_DEVICE_CLASS_RESERVED: // FALL THROUGH + case USB_DEVICE_CLASS_HUB: + { + // + // sanity checks + // + PC_ASSERT(Urb->UrbControlVendorClassRequest.TransferBuffer); + PC_ASSERT(Urb->UrbControlVendorClassRequest.TransferBufferLength >= sizeof(USB_HUB_DESCRIPTOR)); + + // + // get hub descriptor + // + UsbHubDescriptor = (PUSB_HUB_DESCRIPTOR)Urb->UrbControlVendorClassRequest.TransferBuffer; + + // + // one hub is handled + // + UsbHubDescriptor->bDescriptorLength = sizeof(USB_HUB_DESCRIPTOR); + Urb->UrbControlVendorClassRequest.TransferBufferLength = sizeof(USB_HUB_DESCRIPTOR); + + // + // type should 0x29 according to msdn + // + UsbHubDescriptor->bDescriptorType = 0x29; + + // + // get port count + // + Status = m_Hardware->GetDeviceDetails(&Dummy1, &Dummy1, &PortCount, &Dummy2); + PC_ASSERT(Status == STATUS_SUCCESS); + + // + // FIXME: retrieve values + // + UsbHubDescriptor->bNumberOfPorts = (UCHAR)PortCount; + UsbHubDescriptor->wHubCharacteristics = 0x00; + UsbHubDescriptor->bPowerOnToPowerGood = 0x01; + UsbHubDescriptor->bHubControlCurrent = 0x00; + + // + // done + // + Status = STATUS_SUCCESS; + break; + } + default: + DPRINT1("[USBLIB] HandleClassDevice Class %x not implemented\n", Urb->UrbControlVendorClassRequest.Value >> 8); + break; + } + break; + } + default: + DPRINT1("[USBLIB] HandleClassDevice Type %x not implemented\n", Urb->UrbControlVendorClassRequest.Request); + } + + return Status; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleGetDescriptorFromInterface( + IN OUT PIRP Irp, + IN OUT PURB Urb) +{ + PUSBDEVICE UsbDevice; + USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + NTSTATUS Status; + + // + // sanity check + // + ASSERT(Urb->UrbControlDescriptorRequest.TransferBufferLength); + ASSERT(Urb->UrbControlDescriptorRequest.TransferBuffer); + + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("[USBLIB] HandleGetDescriptorFromInterface invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + // + // generate setup packet + // + CtrlSetup.bRequest = USB_REQUEST_GET_DESCRIPTOR; + CtrlSetup.wValue.LowByte = Urb->UrbControlDescriptorRequest.Index; + CtrlSetup.wValue.HiByte = Urb->UrbControlDescriptorRequest.DescriptorType; + CtrlSetup.wIndex.W = Urb->UrbControlDescriptorRequest.LanguageId; + CtrlSetup.wLength = (USHORT)Urb->UrbControlDescriptorRequest.TransferBufferLength; + CtrlSetup.bmRequestType.B = 0x81; + + // + // submit setup packet + // + Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, Urb->UrbControlDescriptorRequest.TransferBufferLength, Urb->UrbControlDescriptorRequest.TransferBuffer); + ASSERT(Status == STATUS_SUCCESS); + + // + // done + // + return Status; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleGetDescriptor( + IN OUT PIRP Irp, + IN OUT PURB Urb) +{ + NTSTATUS Status = STATUS_NOT_IMPLEMENTED; + PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor; + USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + PUCHAR Buffer; + PUSBDEVICE UsbDevice; + ULONG Length; + + DPRINT("[USBLIB] HandleGetDescriptor\n"); + + // + // check descriptor type + // + switch(Urb->UrbControlDescriptorRequest.DescriptorType) + { + case USB_DEVICE_DESCRIPTOR_TYPE: + { + // + // sanity check + // + PC_ASSERT(Urb->UrbControlDescriptorRequest.TransferBufferLength >= sizeof(USB_DEVICE_DESCRIPTOR)); + PC_ASSERT(Urb->UrbControlDescriptorRequest.TransferBuffer); + + if (Urb->UrbHeader.UsbdDeviceHandle == NULL) + { + // + // copy root hub device descriptor + // + RtlCopyMemory((PUCHAR)Urb->UrbControlDescriptorRequest.TransferBuffer, &m_DeviceDescriptor, sizeof(USB_DEVICE_DESCRIPTOR)); + Status = STATUS_SUCCESS; + } + else + { + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("[USBLIB] HandleGetDescriptor invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + // + // retrieve device descriptor from device + // + UsbDevice->GetDeviceDescriptor((PUSB_DEVICE_DESCRIPTOR)Urb->UrbControlDescriptorRequest.TransferBuffer); + Status = STATUS_SUCCESS; + } + break; + } + case USB_CONFIGURATION_DESCRIPTOR_TYPE: + { + // + // sanity checks + // + PC_ASSERT(Urb->UrbControlDescriptorRequest.TransferBuffer); + PC_ASSERT(Urb->UrbControlDescriptorRequest.TransferBufferLength >= sizeof(USB_CONFIGURATION_DESCRIPTOR)); + + if (Urb->UrbHeader.UsbdDeviceHandle == NULL) + { + // + // request is for the root bus controller + // + RtlCopyMemory(Urb->UrbControlDescriptorRequest.TransferBuffer, &ROOTHUB2_CONFIGURATION_DESCRIPTOR, sizeof(USB_CONFIGURATION_DESCRIPTOR)); + + // + // get configuration descriptor, very retarded! + // + ConfigurationDescriptor = (PUSB_CONFIGURATION_DESCRIPTOR)Urb->UrbControlDescriptorRequest.TransferBuffer; + + // + // check if buffer can hold interface and endpoint descriptor + // + if (ConfigurationDescriptor->wTotalLength > Urb->UrbControlDescriptorRequest.TransferBufferLength) + { + // + // buffer too small + // + Status = STATUS_SUCCESS; + ASSERT(FALSE); + break; + } + + // + // copy interface descriptor template + // + Buffer = (PUCHAR)(ConfigurationDescriptor + 1); + RtlCopyMemory(Buffer, &ROOTHUB2_INTERFACE_DESCRIPTOR, sizeof(USB_INTERFACE_DESCRIPTOR)); + + // + // copy end point descriptor template + // + Buffer += sizeof(USB_INTERFACE_DESCRIPTOR); + RtlCopyMemory(Buffer, &ROOTHUB2_ENDPOINT_DESCRIPTOR, sizeof(USB_ENDPOINT_DESCRIPTOR)); + + // + // done + // + Status = STATUS_SUCCESS; + + } + else + { + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("[USBLIB] USB_CONFIGURATION_DESCRIPTOR_TYPE invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + if (sizeof(USB_CONFIGURATION_DESCRIPTOR) > Urb->UrbControlDescriptorRequest.TransferBufferLength) + { + // + // buffer too small + // + Urb->UrbControlDescriptorRequest.TransferBufferLength = UsbDevice->GetConfigurationDescriptorsLength(); + + // + // bail out + // + Status = STATUS_SUCCESS; + break; + } + + // + // perform work in IUSBDevice + // + UsbDevice->GetConfigurationDescriptors((PUSB_CONFIGURATION_DESCRIPTOR)Urb->UrbControlDescriptorRequest.TransferBuffer, Urb->UrbControlDescriptorRequest.TransferBufferLength, &Length); + + // + // store result size + // + Urb->UrbControlDescriptorRequest.TransferBufferLength = Length; + Status = STATUS_SUCCESS; + } + break; + } + case USB_STRING_DESCRIPTOR_TYPE: + { + // + // sanity check + // + PC_ASSERT(Urb->UrbControlDescriptorRequest.TransferBuffer); + PC_ASSERT(Urb->UrbControlDescriptorRequest.TransferBufferLength); + + + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("[USBLIB] USB_STRING_DESCRIPTOR_TYPE invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + // + // generate setup packet + // + CtrlSetup.bRequest = USB_REQUEST_GET_DESCRIPTOR; + CtrlSetup.wValue.LowByte = Urb->UrbControlDescriptorRequest.Index; + CtrlSetup.wValue.HiByte = Urb->UrbControlDescriptorRequest.DescriptorType; + CtrlSetup.wIndex.W = Urb->UrbControlDescriptorRequest.LanguageId; + CtrlSetup.wLength = (USHORT)Urb->UrbControlDescriptorRequest.TransferBufferLength; + CtrlSetup.bmRequestType.B = 0x80; + + // + // submit setup packet + // + Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, Urb->UrbControlDescriptorRequest.TransferBufferLength, Urb->UrbControlDescriptorRequest.TransferBuffer); + break; + } + default: + DPRINT1("[USBLIB] CHubController::HandleGetDescriptor DescriptorType %x unimplemented\n", Urb->UrbControlDescriptorRequest.DescriptorType); + break; + } + + // + // done + // + return Status; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleClassEndpoint( + IN OUT PIRP Irp, + IN OUT PURB Urb) +{ + USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + NTSTATUS Status; + PUSBDEVICE UsbDevice; + + // + // sanity check + // + PC_ASSERT(Urb->UrbControlVendorClassRequest.TransferBuffer); + PC_ASSERT(Urb->UrbControlVendorClassRequest.TransferBufferLength); + PC_ASSERT(Urb->UrbHeader.UsbdDeviceHandle); + + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("[USBLIB] HandleClassEndpoint invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + + DPRINT1("URB_FUNCTION_CLASS_ENDPOINT\n"); + DPRINT1("TransferFlags %x\n", Urb->UrbControlVendorClassRequest.TransferFlags); + DPRINT1("TransferBufferLength %x\n", Urb->UrbControlVendorClassRequest.TransferBufferLength); + DPRINT1("TransferBuffer %x\n", Urb->UrbControlVendorClassRequest.TransferBuffer); + DPRINT1("TransferBufferMDL %x\n", Urb->UrbControlVendorClassRequest.TransferBufferMDL); + DPRINT1("RequestTypeReservedBits %x\n", Urb->UrbControlVendorClassRequest.RequestTypeReservedBits); + DPRINT1("Request %x\n", Urb->UrbControlVendorClassRequest.Request); + DPRINT1("Value %x\n", Urb->UrbControlVendorClassRequest.Value); + DPRINT1("Index %x\n", Urb->UrbControlVendorClassRequest.Index); + + // + // initialize setup packet + // + CtrlSetup.bmRequestType.B = 0x22; //FIXME: Const. + CtrlSetup.bRequest = Urb->UrbControlVendorClassRequest.Request; + CtrlSetup.wValue.W = Urb->UrbControlVendorClassRequest.Value; + CtrlSetup.wIndex.W = Urb->UrbControlVendorClassRequest.Index; + CtrlSetup.wLength = Urb->UrbControlVendorClassRequest.TransferBufferLength; + + if (Urb->UrbControlVendorClassRequest.TransferFlags & USBD_TRANSFER_DIRECTION_IN) + { + // + // data direction is device to host + // + CtrlSetup.bmRequestType.B |= 0x80; + } + + + // + // issue request + // + Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, Urb->UrbControlVendorClassRequest.TransferBufferLength, Urb->UrbControlVendorClassRequest.TransferBuffer); + + // + // assert on failure + // + PC_ASSERT(NT_SUCCESS(Status)); + + + // + // done + // + return Status; +} + +NTSTATUS +CHubController::HandleSyncResetAndClearStall( + IN OUT PIRP Irp, + IN OUT PURB Urb) +{ + NTSTATUS Status = STATUS_SUCCESS; + PUSB_ENDPOINT EndpointDescriptor; + ULONG Type; + + // + // sanity check + // + PC_ASSERT(Urb->UrbHeader.UsbdDeviceHandle); + PC_ASSERT(Urb->UrbHeader.Length == sizeof(struct _URB_PIPE_REQUEST)); + PC_ASSERT(Urb->UrbPipeRequest.PipeHandle); + + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("[USBLIB] HandleSyncResetAndClearStall invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // abort pipe + // + Status = HandleAbortPipe(Irp, Urb); + if (!NT_SUCCESS(Status)) + { + // + // failed + // + DPRINT1("[USBLIB] failed to reset pipe %x\n", Status); + } + + + // + // get endpoint descriptor + // + EndpointDescriptor = (PUSB_ENDPOINT)Urb->UrbPipeRequest.PipeHandle; + + // + // get type + // + Type = (EndpointDescriptor->EndPointDescriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK); + if (Type != USB_ENDPOINT_TYPE_ISOCHRONOUS) + { + // + // clear stall + // + Status = HandleClearStall(Irp, Urb); + } + DPRINT1("[USBLIB] URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL Status %x\n", Status); + + // + // reset data toggle + // + ASSERT(NT_SUCCESS(Status)); + EndpointDescriptor->DataToggle = 0x0; + + // + // done + // + return Status; +} + +NTSTATUS +CHubController::HandleAbortPipe( + IN OUT PIRP Irp, + IN OUT PURB Urb) +{ + NTSTATUS Status; + PUSBDEVICE UsbDevice; + PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor; + + // + // sanity check + // + PC_ASSERT(Urb->UrbHeader.UsbdDeviceHandle); + PC_ASSERT(Urb->UrbHeader.Length == sizeof(struct _URB_PIPE_REQUEST)); + PC_ASSERT(Urb->UrbPipeRequest.PipeHandle); + + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("[USBLIB] HandleAbortPipe invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get endpoint descriptor + // + EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR)Urb->UrbPipeRequest.PipeHandle; + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + + // + // issue request + // + Status = UsbDevice->AbortPipe(EndpointDescriptor); + DPRINT1("[USBLIB] URB_FUNCTION_ABORT_PIPE Status %x\n", Status); + + // + // done + // + return Status; +} + + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleClearStall( + IN OUT PIRP Irp, + IN OUT PURB Urb) +{ + USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + NTSTATUS Status; + PUSBDEVICE UsbDevice; + PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor; + + + // + // sanity check + // + PC_ASSERT(Urb->UrbHeader.UsbdDeviceHandle); + PC_ASSERT(Urb->UrbHeader.Length == sizeof(struct _URB_PIPE_REQUEST)); + PC_ASSERT(Urb->UrbPipeRequest.PipeHandle); + + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("[USBLIB] HandleClearStall invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get endpoint descriptor + // + EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR)Urb->UrbPipeRequest.PipeHandle; + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + DPRINT1("[USBLIB] URB_FUNCTION_SYNC_CLEAR_STALL\n"); + + // + // initialize setup packet + // + CtrlSetup.bmRequestType.B = 0x02; + CtrlSetup.bRequest = USB_REQUEST_CLEAR_FEATURE; + CtrlSetup.wValue.W = USB_FEATURE_ENDPOINT_STALL; + CtrlSetup.wIndex.W = EndpointDescriptor->bEndpointAddress; + CtrlSetup.wLength = 0; + CtrlSetup.wValue.W = 0; + + // + // issue request + // + Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, 0, 0); + + DPRINT1("[USBLIB] URB_FUNCTION_CLEAR_STALL Status %x\n", Status); + + // + // done + // + return Status; +} + + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleClassInterface( + IN OUT PIRP Irp, + IN OUT PURB Urb) +{ + USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + NTSTATUS Status; + PUSBDEVICE UsbDevice; + + // + // sanity check + // + //ASSERT(Urb->UrbControlVendorClassRequest.TransferBuffer || Urb->UrbControlVendorClassRequest.TransferBufferMDL); + //ASSERT(Urb->UrbControlVendorClassRequest.TransferBufferLength); + PC_ASSERT(Urb->UrbHeader.UsbdDeviceHandle); + + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("[USBLIB] HandleClassInterface invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + + DPRINT1("URB_FUNCTION_CLASS_INTERFACE\n"); + DPRINT1("TransferFlags %x\n", Urb->UrbControlVendorClassRequest.TransferFlags); + DPRINT1("TransferBufferLength %x\n", Urb->UrbControlVendorClassRequest.TransferBufferLength); + DPRINT1("TransferBuffer %x\n", Urb->UrbControlVendorClassRequest.TransferBuffer); + DPRINT1("TransferBufferMDL %x\n", Urb->UrbControlVendorClassRequest.TransferBufferMDL); + DPRINT1("RequestTypeReservedBits %x\n", Urb->UrbControlVendorClassRequest.RequestTypeReservedBits); + DPRINT1("Request %x\n", Urb->UrbControlVendorClassRequest.Request); + DPRINT1("Value %x\n", Urb->UrbControlVendorClassRequest.Value); + DPRINT1("Index %x\n", Urb->UrbControlVendorClassRequest.Index); + + // + // initialize setup packet + // + CtrlSetup.bmRequestType.B = 0x21; + CtrlSetup.bRequest = Urb->UrbControlVendorClassRequest.Request; + CtrlSetup.wValue.W = Urb->UrbControlVendorClassRequest.Value; + CtrlSetup.wIndex.W = Urb->UrbControlVendorClassRequest.Index; + CtrlSetup.wLength = Urb->UrbControlVendorClassRequest.TransferBufferLength; + + if (Urb->UrbControlVendorClassRequest.TransferFlags & USBD_TRANSFER_DIRECTION_IN) + { + // + // data direction is device to host + // + CtrlSetup.bmRequestType.B |= 0x80; + } + + // + // issue request + // + Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, Urb->UrbControlVendorClassRequest.TransferBufferLength, Urb->UrbControlVendorClassRequest.TransferBuffer); + + // + // assert on failure + // + if (!NT_SUCCESS(Status)) + { + // + // display error + // + DPRINT1("URB_FUNCTION_CLASS_INTERFACE failed with Urb Status %x\n", Urb->UrbHeader.Status); + } + + // + // done + // + return Status; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PCOMMON_DEVICE_EXTENSION DeviceExtension; + PURB Urb; + NTSTATUS Status = STATUS_NOT_IMPLEMENTED; + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // get device extension + // + DeviceExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // determine which request should be performed + // + switch(IoStack->Parameters.DeviceIoControl.IoControlCode) + { + case IOCTL_INTERNAL_USB_SUBMIT_URB: + { + // + // get urb + // + Urb = (PURB)IoStack->Parameters.Others.Argument1; + PC_ASSERT(Urb); + + switch (Urb->UrbHeader.Function) + { + case URB_FUNCTION_SYNC_RESET_PIPE: + case URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL: + Status = HandleSyncResetAndClearStall(Irp, Urb); + break; + case URB_FUNCTION_ABORT_PIPE: + Status = HandleAbortPipe(Irp, Urb); + break; + case URB_FUNCTION_SYNC_CLEAR_STALL: + Status = HandleClearStall(Irp, Urb); + break; + case URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE: + Status = HandleGetDescriptorFromInterface(Irp, Urb); + break; + case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE: + Status = HandleGetDescriptor(Irp, Urb); + break; + case URB_FUNCTION_CLASS_DEVICE: + Status = HandleClassDevice(Irp, Urb); + break; + case URB_FUNCTION_GET_STATUS_FROM_DEVICE: + case URB_FUNCTION_GET_STATUS_FROM_INTERFACE: + case URB_FUNCTION_GET_STATUS_FROM_ENDPOINT: + Status = HandleGetStatusFromDevice(Irp, Urb); + break; + case URB_FUNCTION_SELECT_CONFIGURATION: + Status = HandleSelectConfiguration(Irp, Urb); + break; + case URB_FUNCTION_SELECT_INTERFACE: + Status = HandleSelectInterface(Irp, Urb); + break; + case URB_FUNCTION_CLASS_OTHER: + Status = HandleClassOther(Irp, Urb); + break; + case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: + Status = HandleBulkOrInterruptTransfer(Irp, Urb); + break; + case URB_FUNCTION_ISOCH_TRANSFER: + Status = HandleIsochronousTransfer(Irp, Urb); + break; + case URB_FUNCTION_CLASS_INTERFACE: + Status = HandleClassInterface(Irp, Urb); + break; + case URB_FUNCTION_CLASS_ENDPOINT: + Status = HandleClassEndpoint(Irp, Urb); + break; + default: + DPRINT1("[USBLIB] IOCTL_INTERNAL_USB_SUBMIT_URB Function %x NOT IMPLEMENTED\n", Urb->UrbHeader.Function); + break; + } + // + // request completed + // + break; + } + case IOCTL_INTERNAL_USB_GET_DEVICE_HANDLE: + { + DPRINT("[USBLIB] IOCTL_INTERNAL_USB_GET_DEVICE_HANDLE %p\n", this); + + if (IoStack->Parameters.Others.Argument1) + { + // + // store object as device handle + // + *(PVOID *)IoStack->Parameters.Others.Argument1 = (PVOID)this; + Status = STATUS_SUCCESS; + } + else + { + // + // mis-behaving hub driver + // + Status = STATUS_INVALID_DEVICE_REQUEST; + } + + // + // request completed + // + break; + } + case IOCTL_INTERNAL_USB_GET_ROOTHUB_PDO: + { + DPRINT("[USBLIB] IOCTL_INTERNAL_USB_GET_ROOTHUB_PDO\n"); + + // + // this is the first request send, it delivers the PDO to the caller + // + if (IoStack->Parameters.Others.Argument1) + { + // + // store root hub pdo object + // + *(PVOID *)IoStack->Parameters.Others.Argument1 = DeviceObject; + } + + if (IoStack->Parameters.Others.Argument2) + { + // + // documentation claims to deliver the hcd controller object, although it is wrong + // + *(PVOID *)IoStack->Parameters.Others.Argument2 = DeviceObject; + } + + // + // request completed + // + Status = STATUS_SUCCESS; + break; + } + case IOCTL_INTERNAL_USB_GET_HUB_COUNT: + { + DPRINT("[USBLIB] IOCTL_INTERNAL_USB_GET_HUB_COUNT\n"); + + // + // after IOCTL_INTERNAL_USB_GET_ROOTHUB_PDO is delivered, the usbhub driver + // requests this ioctl to deliver the number of presents. + + if (IoStack->Parameters.Others.Argument1) + { + // + // FIXME / verify: there is only one hub + // + *(PULONG)IoStack->Parameters.Others.Argument1 = 1; + } + + // + // request completed + // + Status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof(ULONG); + break; + } + case IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION: + { + DPRINT1("[USBLIB] IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION UNIMPLEMENTED\n"); + Status = STATUS_SUCCESS; + break; + } + default: + { + DPRINT1("[USBLIB] HandleDeviceControl>Type: IoCtl %x InputBufferLength %lu OutputBufferLength %lu NOT IMPLEMENTED\n", + IoStack->Parameters.DeviceIoControl.IoControlCode, + IoStack->Parameters.DeviceIoControl.InputBufferLength, + IoStack->Parameters.DeviceIoControl.OutputBufferLength); + break; + } + } + if (Status != STATUS_PENDING) + { + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + } + + return Status; +} + +//----------------------------------------------------------------------------------------- +PUSBHARDWAREDEVICE +CHubController::GetUsbHardware() +{ + return m_Hardware; +} + +//----------------------------------------------------------------------------------------- +ULONG +CHubController::AcquireDeviceAddress() +{ + KIRQL OldLevel; + ULONG DeviceAddress; + + // + // acquire device lock + // + KeAcquireSpinLock(&m_Lock, &OldLevel); + + // + // find address + // + DeviceAddress = RtlFindClearBits(&m_DeviceAddressBitmap, 1, 0); + if (DeviceAddress != MAXULONG) + { + // + // reserve address + // + RtlSetBits(&m_DeviceAddressBitmap, DeviceAddress, 1); + + // + // device addresses start from 0x1 - 0xFF + // + DeviceAddress++; + } + + // + // release spin lock + // + KeReleaseSpinLock(&m_Lock, OldLevel); + + // + // return device address + // + return DeviceAddress; +} +//----------------------------------------------------------------------------------------- +VOID +CHubController::ReleaseDeviceAddress( + ULONG DeviceAddress) +{ + KIRQL OldLevel; + + // + // acquire device lock + // + KeAcquireSpinLock(&m_Lock, &OldLevel); + + // + // sanity check + // + PC_ASSERT(DeviceAddress != 0); + + // + // convert back to bit number + // + DeviceAddress--; + + // + // clear bit + // + RtlClearBits(&m_DeviceAddressBitmap, DeviceAddress, 1); + + // + // release lock + // + KeReleaseSpinLock(&m_Lock, OldLevel); +} +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::RemoveUsbDevice( + PUSBDEVICE UsbDevice) +{ + PUSBDEVICE_ENTRY DeviceEntry; + PLIST_ENTRY Entry; + NTSTATUS Status = STATUS_UNSUCCESSFUL; + KIRQL OldLevel; + + // + // acquire lock + // + KeAcquireSpinLock(&m_Lock, &OldLevel); + + // + // point to first entry + // + Entry = m_UsbDeviceList.Flink; + + // + // find matching entry + // + while(Entry != &m_UsbDeviceList) + { + // + // get entry + // + DeviceEntry = (PUSBDEVICE_ENTRY)CONTAINING_RECORD(Entry, USBDEVICE_ENTRY, Entry); + + // + // is it current entry + // + if (DeviceEntry->Device == UsbDevice) + { + // + // remove entry + // + RemoveEntryList(Entry); + + // + // free entry + // + ExFreePoolWithTag(DeviceEntry, TAG_USBLIB); + + // + // done + // + Status = STATUS_SUCCESS; + break; + } + + // + // goto next device + // + Entry = Entry->Flink; + } + + // + // release lock + // + KeReleaseSpinLock(&m_Lock, OldLevel); + + // + // return result + // + return Status; +} +//----------------------------------------------------------------------------------------- +BOOLEAN +CHubController::ValidateUsbDevice(PUSBDEVICE UsbDevice) +{ + PUSBDEVICE_ENTRY DeviceEntry; + PLIST_ENTRY Entry; + KIRQL OldLevel; + BOOLEAN Result = FALSE; + + // + // acquire lock + // + KeAcquireSpinLock(&m_Lock, &OldLevel); + + // + // point to first entry + // + Entry = m_UsbDeviceList.Flink; + + // + // find matching entry + // + while(Entry != &m_UsbDeviceList) + { + // + // get entry + // + DeviceEntry = (PUSBDEVICE_ENTRY)CONTAINING_RECORD(Entry, USBDEVICE_ENTRY, Entry); + + // + // is it current entry + // + if (DeviceEntry->Device == UsbDevice) + { + // + // device is valid + // + Result = TRUE; + break; + } + + // + // goto next device + // + Entry = Entry->Flink; + } + + // + // release lock + // + KeReleaseSpinLock(&m_Lock, OldLevel); + + // + // return result + // + return Result; + +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::AddUsbDevice( + PUSBDEVICE UsbDevice) +{ + PUSBDEVICE_ENTRY DeviceEntry; + KIRQL OldLevel; + + // + // allocate device entry + // + DeviceEntry = (PUSBDEVICE_ENTRY)ExAllocatePoolWithTag(NonPagedPool, sizeof(USBDEVICE_ENTRY), TAG_USBLIB); + if (!DeviceEntry) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // initialize entry + // + DeviceEntry->Device = UsbDevice; + + // + // acquire lock + // + KeAcquireSpinLock(&m_Lock, &OldLevel); + + // + // insert entry + // + InsertTailList(&m_UsbDeviceList, &DeviceEntry->Entry); + + // + // release spin lock + // + KeReleaseSpinLock(&m_Lock, OldLevel); + + // + // done + // + return STATUS_SUCCESS; +} + +//----------------------------------------------------------------------------------------- +VOID +CHubController::SetNotification( + PVOID CallbackContext, + PRH_INIT_CALLBACK CallbackRoutine) +{ + KIRQL OldLevel; + + // + // acquire hub controller lock + // + KeAcquireSpinLock(&m_Lock, &OldLevel); + + // + // now set the callback routine and context of the hub + // + m_HubCallbackContext = CallbackContext; + m_HubCallbackRoutine = CallbackRoutine; + + // + // release hub controller lock + // + KeReleaseSpinLock(&m_Lock, OldLevel); +} + +//================================================================================================= +// +// Generic Interface functions +// +VOID +USB_BUSIFFN +USBI_InterfaceReference( + PVOID BusContext) +{ + CHubController * Controller = (CHubController*)BusContext; + + DPRINT1("USBH_InterfaceReference\n"); + + // + // add reference + // + Controller->AddRef(); +} + +VOID +USB_BUSIFFN +USBI_InterfaceDereference( + PVOID BusContext) +{ + CHubController * Controller = (CHubController*)BusContext; + + DPRINT1("USBH_InterfaceDereference\n"); + + // + // release + // + Controller->Release(); +} +//================================================================================================= +// +// USB Hub Interface functions +// +NTSTATUS +USB_BUSIFFN +USBHI_CreateUsbDevice( + PVOID BusContext, + PUSB_DEVICE_HANDLE *NewDevice, + PUSB_DEVICE_HANDLE HubDeviceHandle, + USHORT PortStatus, + USHORT PortNumber) +{ + PUSBDEVICE NewUsbDevice; + CHubController * Controller; + NTSTATUS Status; + + DPRINT1("USBHI_CreateUsbDevice\n"); + + // + // first get hub controller + // + Controller = (CHubController *)BusContext; + + // + // sanity check + // + PC_ASSERT(Controller); + PC_ASSERT(BusContext == HubDeviceHandle); + + // + // now allocate usb device + // + Status = CreateUSBDevice(&NewUsbDevice); + + // + // check for success + // + if (!NT_SUCCESS(Status)) + { + // + // release controller + // + Controller->Release(); + DPRINT1("USBHI_CreateUsbDevice: failed to create usb device %x\n", Status); + return Status; + } + + // + // now initialize device + // + Status = NewUsbDevice->Initialize(PHUBCONTROLLER(Controller), Controller->GetUsbHardware(), HubDeviceHandle, PortNumber, PortStatus); + + // + // check for success + // + if (!NT_SUCCESS(Status)) + { + // + // release usb device + // + NewUsbDevice->Release(); + DPRINT1("USBHI_CreateUsbDevice: failed to initialize usb device %x\n", Status); + return Status; + } + + // + // insert into list + // + Status = Controller->AddUsbDevice(NewUsbDevice); + // + // check for success + // + if (!NT_SUCCESS(Status)) + { + // + // release usb device + // + NewUsbDevice->Release(); + + DPRINT1("USBHI_CreateUsbDevice: failed to add usb device %x\n", Status); + return Status; + } + + // + // store the handle + // + *NewDevice = NewUsbDevice; + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +USB_BUSIFFN +USBHI_InitializeUsbDevice( + PVOID BusContext, + PUSB_DEVICE_HANDLE DeviceHandle) +{ + PUSBDEVICE UsbDevice; + CHubController * Controller; + ULONG DeviceAddress; + NTSTATUS Status; + ULONG Index = 0; + + DPRINT1("USBHI_InitializeUsbDevice\n"); + + // + // first get controller + // + Controller = (CHubController *)BusContext; + PC_ASSERT(Controller); + + // + // get device object + // + UsbDevice = (PUSBDEVICE)DeviceHandle; + PC_ASSERT(UsbDevice); + + // + // validate device handle + // + if (!Controller->ValidateUsbDevice(UsbDevice)) + { + DPRINT1("USBHI_InitializeUsbDevice invalid device handle %p\n", DeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // now reserve an address + // + DeviceAddress = Controller->AcquireDeviceAddress(); + + // + // is the device address valid + // + if (DeviceAddress == MAXULONG) + { + // + // failed to get an device address from the device address pool + // + DPRINT1("USBHI_InitializeUsbDevice failed to get device address\n"); + return STATUS_DEVICE_DATA_ERROR; + } + + do + { + // + // now set the device address + // + Status = UsbDevice->SetDeviceAddress((UCHAR)DeviceAddress); + + if (NT_SUCCESS(Status)) + break; + + }while(Index++ < 3 ); + + // + // check for failure + // + if (!NT_SUCCESS(Status)) + { + // + // failed to set device address + // + DPRINT1("USBHI_InitializeUsbDevice failed to set address with %x\n", Status); + + // + // release address + // + Controller->ReleaseDeviceAddress(DeviceAddress); + + // + // return error + // + return STATUS_DEVICE_DATA_ERROR; + } + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +USB_BUSIFFN +USBHI_GetUsbDescriptors( + PVOID BusContext, + PUSB_DEVICE_HANDLE DeviceHandle, + PUCHAR DeviceDescriptorBuffer, + PULONG DeviceDescriptorBufferLength, + PUCHAR ConfigDescriptorBuffer, + PULONG ConfigDescriptorBufferLength) +{ + PUSBDEVICE UsbDevice; + CHubController * Controller; + + DPRINT1("USBHI_GetUsbDescriptors\n"); + + // + // sanity check + // + PC_ASSERT(DeviceDescriptorBuffer); + PC_ASSERT(DeviceDescriptorBufferLength); + PC_ASSERT(*DeviceDescriptorBufferLength >= sizeof(USB_DEVICE_DESCRIPTOR)); + PC_ASSERT(ConfigDescriptorBufferLength); + PC_ASSERT(*ConfigDescriptorBufferLength >= sizeof(USB_CONFIGURATION_DESCRIPTOR)); + + // + // first get controller + // + Controller = (CHubController *)BusContext; + PC_ASSERT(Controller); + + + // + // get device object + // + UsbDevice = (PUSBDEVICE)DeviceHandle; + PC_ASSERT(UsbDevice); + + // + // validate device handle + // + if (!Controller->ValidateUsbDevice(UsbDevice)) + { + DPRINT1("USBHI_GetUsbDescriptors invalid device handle %p\n", DeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device descriptor + // + UsbDevice->GetDeviceDescriptor((PUSB_DEVICE_DESCRIPTOR)DeviceDescriptorBuffer); + + // + // store result length + // + *DeviceDescriptorBufferLength = sizeof(USB_DEVICE_DESCRIPTOR); + + // + // get configuration descriptor + // + UsbDevice->GetConfigurationDescriptors((PUSB_CONFIGURATION_DESCRIPTOR)ConfigDescriptorBuffer, *ConfigDescriptorBufferLength, ConfigDescriptorBufferLength); + + // + // complete the request + // + return STATUS_SUCCESS; +} + +NTSTATUS +USB_BUSIFFN +USBHI_RemoveUsbDevice( + PVOID BusContext, + PUSB_DEVICE_HANDLE DeviceHandle, + ULONG Flags) +{ + PUSBDEVICE UsbDevice; + CHubController * Controller; + NTSTATUS Status; + + DPRINT1("USBHI_RemoveUsbDevice\n"); + + // + // first get controller + // + Controller = (CHubController *)BusContext; + PC_ASSERT(Controller); + + // + // get device object + // + UsbDevice = (PUSBDEVICE)DeviceHandle; + PC_ASSERT(UsbDevice); + + // + // validate device handle + // + if (!Controller->ValidateUsbDevice(UsbDevice)) + { + DPRINT1("USBHI_RemoveUsbDevice invalid device handle %p\n", DeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // check if there were flags passed + // + if (Flags & USBD_KEEP_DEVICE_DATA || Flags & USBD_MARK_DEVICE_BUSY) + { + // + // ignore flags for now + // + return STATUS_SUCCESS; + } + + // + // remove device + // + Status = Controller->RemoveUsbDevice(UsbDevice); + if (!NT_SUCCESS(Status)) + { + // + // invalid device handle + // + DPRINT1("USBHI_RemoveUsbDevice Invalid device handle %p\n", UsbDevice); + PC_ASSERT(0); + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // release usb device + // + UsbDevice->Release(); + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +USB_BUSIFFN +USBHI_RestoreUsbDevice( + PVOID BusContext, + PUSB_DEVICE_HANDLE OldDeviceHandle, + PUSB_DEVICE_HANDLE NewDeviceHandle) +{ + PUSBDEVICE OldUsbDevice, NewUsbDevice; + CHubController * Controller; + + DPRINT1("USBHI_RestoreUsbDevice\n"); + + // + // first get controller + // + Controller = (CHubController *)BusContext; + PC_ASSERT(Controller); + + // + // get device object + // + OldUsbDevice = (PUSBDEVICE)OldDeviceHandle; + NewUsbDevice = (PUSBDEVICE)NewDeviceHandle; + PC_ASSERT(OldUsbDevice); + PC_ASSERT(NewDeviceHandle); + + // + // validate device handle + // + PC_ASSERT(Controller->ValidateUsbDevice(NewUsbDevice)); + PC_ASSERT(Controller->ValidateUsbDevice(OldUsbDevice)); + + DPRINT1("NewUsbDevice: DeviceAddress %x\n", NewUsbDevice->GetDeviceAddress()); + DPRINT1("OldUsbDevice: DeviceAddress %x\n", OldUsbDevice->GetDeviceAddress()); + + // + // remove old device handle + // + USBHI_RemoveUsbDevice(BusContext, OldDeviceHandle, 0); + + return STATUS_SUCCESS; +} + +NTSTATUS +USB_BUSIFFN +USBHI_QueryDeviceInformation( + PVOID BusContext, + PUSB_DEVICE_HANDLE DeviceHandle, + PVOID DeviceInformationBuffer, + ULONG DeviceInformationBufferLength, + PULONG LengthReturned) +{ + PUSB_DEVICE_INFORMATION_0 DeviceInfo; + PUSBDEVICE UsbDevice; + CHubController * Controller; + + DPRINT1("USBHI_QueryDeviceInformation %p\n", BusContext); + + // + // sanity check + // + PC_ASSERT(DeviceInformationBufferLength >= sizeof(USB_DEVICE_INFORMATION_0)); + PC_ASSERT(DeviceInformationBuffer); + PC_ASSERT(LengthReturned); + + // + // get controller object + // + Controller = (CHubController*)BusContext; + PC_ASSERT(Controller); + + // + // get device object + // + UsbDevice = (PUSBDEVICE)DeviceHandle; + PC_ASSERT(UsbDevice); + + if (BusContext != DeviceHandle) + { + // + // validate device handle + // + if (!Controller->ValidateUsbDevice(UsbDevice)) + { + DPRINT1("USBHI_QueryDeviceInformation invalid device handle %p\n", DeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // access information buffer + // + DeviceInfo = (PUSB_DEVICE_INFORMATION_0)DeviceInformationBuffer; + + // + // initialize with default values + // + DeviceInfo->InformationLevel = 0; + DeviceInfo->ActualLength = sizeof(USB_DEVICE_INFORMATION_0); + DeviceInfo->PortNumber = UsbDevice->GetPort(); + DeviceInfo->CurrentConfigurationValue = UsbDevice->GetConfigurationValue(); + DeviceInfo->DeviceAddress = UsbDevice->GetDeviceAddress(); + DeviceInfo->HubAddress = 0; //FIXME + DeviceInfo->DeviceSpeed = UsbDevice->GetSpeed(); + DeviceInfo->DeviceType = UsbDevice->GetType(); + DeviceInfo->NumberOfOpenPipes = 0; //FIXME + + // + // get device descriptor + // + UsbDevice->GetDeviceDescriptor(&DeviceInfo->DeviceDescriptor); + + // + // FIXME return pipe information + // + + // + // store result length + // + *LengthReturned = sizeof(USB_DEVICE_INFORMATION_0); + + return STATUS_SUCCESS; + } + + // + // access information buffer + // + DeviceInfo = (PUSB_DEVICE_INFORMATION_0)DeviceInformationBuffer; + + // + // initialize with default values + // + DeviceInfo->InformationLevel = 0; + DeviceInfo->ActualLength = sizeof(USB_DEVICE_INFORMATION_0); + DeviceInfo->PortNumber = 0; + DeviceInfo->CurrentConfigurationValue = 0; //FIXME; + DeviceInfo->DeviceAddress = 0; + DeviceInfo->HubAddress = 0; //FIXME + DeviceInfo->DeviceSpeed = UsbHighSpeed; //FIXME + DeviceInfo->DeviceType = Usb20Device; //FIXME + DeviceInfo->NumberOfOpenPipes = 0; //FIXME + + // + // get device descriptor + // + RtlMoveMemory(&DeviceInfo->DeviceDescriptor, ROOTHUB2_DEVICE_DESCRIPTOR, sizeof(USB_DEVICE_DESCRIPTOR)); + + // + // FIXME return pipe information + // + + // + // store result length + // +#ifdef _MSC_VER + *LengthReturned = FIELD_OFFSET(USB_DEVICE_INFORMATION_0, PipeList[DeviceInfo->NumberOfOpenPipes]); +#else + *LengthReturned = sizeof(USB_DEVICE_INFORMATION_0) + (DeviceInfo->NumberOfOpenPipes > 1 ? (DeviceInfo->NumberOfOpenPipes - 1) * sizeof(USB_PIPE_INFORMATION_0) : 0); +#endif + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +USB_BUSIFFN +USBHI_GetControllerInformation( + PVOID BusContext, + PVOID ControllerInformationBuffer, + ULONG ControllerInformationBufferLength, + PULONG LengthReturned) +{ + PUSB_CONTROLLER_INFORMATION_0 ControllerInfo; + + DPRINT1("USBHI_GetControllerInformation\n"); + + // + // sanity checks + // + PC_ASSERT(ControllerInformationBuffer); + PC_ASSERT(ControllerInformationBufferLength >= sizeof(USB_CONTROLLER_INFORMATION_0)); + + // + // get controller info buffer + // + ControllerInfo = (PUSB_CONTROLLER_INFORMATION_0)ControllerInformationBuffer; + + // + // FIXME only version 0 is supported for now + // + PC_ASSERT(ControllerInfo->InformationLevel == 0); + + // + // fill in information + // + ControllerInfo->ActualLength = sizeof(USB_CONTROLLER_INFORMATION_0); + ControllerInfo->SelectiveSuspendEnabled = FALSE; //FIXME + ControllerInfo->IsHighSpeedController = TRUE; + + // + // set length returned + // + *LengthReturned = ControllerInfo->ActualLength; + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +USB_BUSIFFN +USBHI_ControllerSelectiveSuspend( + PVOID BusContext, + BOOLEAN Enable) +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +USB_BUSIFFN +USBHI_GetExtendedHubInformation( + PVOID BusContext, + PDEVICE_OBJECT HubPhysicalDeviceObject, + PVOID HubInformationBuffer, + ULONG HubInformationBufferLength, + PULONG LengthReturned) +{ + PUSB_EXTHUB_INFORMATION_0 HubInfo; + CHubController * Controller; + PUSBHARDWAREDEVICE Hardware; + ULONG Index; + ULONG NumPort, Dummy2; + USHORT Dummy1; + NTSTATUS Status; + + DPRINT1("USBHI_GetExtendedHubInformation\n"); + + // + // sanity checks + // + PC_ASSERT(HubInformationBuffer); + PC_ASSERT(HubInformationBufferLength == sizeof(USB_EXTHUB_INFORMATION_0)); + PC_ASSERT(LengthReturned); + + // + // get hub controller + // + Controller = (CHubController *)BusContext; + PC_ASSERT(Controller); + + // + // get usb hardware device + // + Hardware = Controller->GetUsbHardware(); + + // + // retrieve number of ports + // + Status = Hardware->GetDeviceDetails(&Dummy1, &Dummy1, &NumPort, &Dummy2); + if (!NT_SUCCESS(Status)) + { + // + // failed to get hardware details, ouch ;) + // + DPRINT1("USBHI_GetExtendedHubInformation failed to get hardware details with %x\n", Status); + return Status; + } + + // + // get hub information buffer + // + HubInfo = (PUSB_EXTHUB_INFORMATION_0)HubInformationBuffer; + + // + // initialize hub information + // + HubInfo->InformationLevel = 0; + + // + // store port count + // + HubInfo->NumberOfPorts = NumPort; + + // + // initialize port information + // + for(Index = 0; Index < NumPort; Index++) + { + HubInfo->Port[Index].PhysicalPortNumber = Index + 1; + HubInfo->Port[Index].PortLabelNumber = Index + 1; + HubInfo->Port[Index].VidOverride = 0; + HubInfo->Port[Index].PidOverride = 0; + HubInfo->Port[Index].PortAttributes = USB_PORTATTR_SHARED_USB2; //FIXME + } + + // + // store result length + // +#ifdef _MSC_VER + *LengthReturned = FIELD_OFFSET(USB_EXTHUB_INFORMATION_0, Port[HubInfo->NumberOfPorts]); +#else + *LengthReturned = FIELD_OFFSET(USB_EXTHUB_INFORMATION_0, Port) + sizeof(USB_EXTPORT_INFORMATION_0) * HubInfo->NumberOfPorts; +#endif + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +USB_BUSIFFN +USBHI_GetRootHubSymbolicName( + PVOID BusContext, + PVOID HubSymNameBuffer, + ULONG HubSymNameBufferLength, + PULONG HubSymNameActualLength) +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +PVOID +USB_BUSIFFN +USBHI_GetDeviceBusContext( + PVOID HubBusContext, + PVOID DeviceHandle) +{ + UNIMPLEMENTED + return NULL; +} + +NTSTATUS +USB_BUSIFFN +USBHI_Initialize20Hub( + PVOID BusContext, + PUSB_DEVICE_HANDLE HubDeviceHandle, + ULONG TtCount) +{ + DPRINT("USBHI_Initialize20Hub HubDeviceHandle %p UNIMPLEMENTED TtCount %lu\n", HubDeviceHandle, TtCount); + return STATUS_SUCCESS; +} + +NTSTATUS +USB_BUSIFFN +USBHI_RootHubInitNotification( + PVOID BusContext, + PVOID CallbackContext, + PRH_INIT_CALLBACK CallbackRoutine) +{ + CHubController * Controller; + + DPRINT("USBHI_RootHubInitNotification %p \n", CallbackContext); + + // + // get controller object + // + Controller = (CHubController*)BusContext; + PC_ASSERT(Controller); + + // + // set notification routine + // + Controller->SetNotification(CallbackContext, CallbackRoutine); + + // + // FIXME: determine when to perform callback + // + CallbackRoutine(CallbackContext); + + // + // done + // + return STATUS_SUCCESS; +} + +VOID +USB_BUSIFFN +USBHI_FlushTransfers( + PVOID BusContext, + PVOID DeviceHandle) +{ + UNIMPLEMENTED +} + +VOID +USB_BUSIFFN +USBHI_SetDeviceHandleData( + PVOID BusContext, + PVOID DeviceHandle, + PDEVICE_OBJECT UsbDevicePdo) +{ + PUSBDEVICE UsbDevice; + CHubController * Controller; + + // + // get controller + // + Controller = (CHubController *)BusContext; + PC_ASSERT(Controller); + + // + // get device handle + // + UsbDevice = (PUSBDEVICE)DeviceHandle; + + // + // validate device handle + // + if (!Controller->ValidateUsbDevice(UsbDevice)) + { + DPRINT1("USBHI_SetDeviceHandleData DeviceHandle %p is invalid\n", DeviceHandle); + + // + // invalid handle + // + return; + } + else + { + // + // usbhub sends this request as a part of the Pnp startup sequence + // looks like we need apply a dragon voodoo to fixup the device stack + // otherwise usbhub will cause a bugcheck + // + DPRINT1("USBHI_SetDeviceHandleData %p\n", UsbDevicePdo); + + // + // sanity check + // + PC_ASSERT(UsbDevicePdo->AttachedDevice); + + // + // should be usbstor + // fixup device stack voodoo part #2 + // + UsbDevicePdo->AttachedDevice->StackSize++; + + // + // set device handle data + // + UsbDevice->SetDeviceHandleData(UsbDevicePdo); + } +} + +//================================================================================================= +// +// USB Device Interface functions +// + +VOID +USB_BUSIFFN +USBDI_GetUSBDIVersion( + PVOID BusContext, + PUSBD_VERSION_INFORMATION VersionInformation, + PULONG HcdCapabilites) +{ + CHubController * Controller; + PUSBHARDWAREDEVICE Device; + ULONG Speed, Dummy2; + USHORT Dummy1; + + DPRINT1("USBDI_GetUSBDIVersion\n"); + + // + // get controller + // + Controller = (CHubController*)BusContext; + + // + // get usb hardware + // + Device = Controller->GetUsbHardware(); + PC_ASSERT(Device); + + if (VersionInformation) + { + // + // windows xp supported + // + VersionInformation->USBDI_Version = 0x00000500; + + // + // get device speed + // + Device->GetDeviceDetails(&Dummy1, &Dummy1, &Dummy2, &Speed); + + // + // store speed details + // + VersionInformation->Supported_USB_Version = Speed; + } + + // + // no flags supported + // + *HcdCapabilites = 0; +} + +NTSTATUS +USB_BUSIFFN +USBDI_QueryBusTime( + PVOID BusContext, + PULONG CurrentFrame) +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +USB_BUSIFFN +USBDI_SubmitIsoOutUrb( + PVOID BusContext, + PURB Urb) +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +USB_BUSIFFN +USBDI_QueryBusInformation( + PVOID BusContext, + ULONG Level, + PVOID BusInformationBuffer, + PULONG BusInformationBufferLength, + PULONG BusInformationActualLength) +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +BOOLEAN +USB_BUSIFFN +USBDI_IsDeviceHighSpeed( + PVOID BusContext) +{ + CHubController * Controller; + PUSBHARDWAREDEVICE Device; + ULONG Speed, Dummy2; + USHORT Dummy1; + + DPRINT1("USBDI_IsDeviceHighSpeed\n"); + + // + // get controller + // + Controller = (CHubController*)BusContext; + + // + // get usb hardware + // + Device = Controller->GetUsbHardware(); + PC_ASSERT(Device); + + // + // get device speed + // + Device->GetDeviceDetails(&Dummy1, &Dummy1, &Dummy2, &Speed); + + // + // USB 2.0 equals 0x200 + // + return (Speed == 0x200); +} + +NTSTATUS +USB_BUSIFFN +USBDI_EnumLogEntry( + PVOID BusContext, + ULONG DriverTag, + ULONG EnumTag, + ULONG P1, + ULONG P2) +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +CHubController::HandleQueryInterface( + PIO_STACK_LOCATION IoStack) +{ + PUSB_BUS_INTERFACE_HUB_V5 InterfaceHub; + PUSB_BUS_INTERFACE_USBDI_V2 InterfaceDI; + UNICODE_STRING GuidBuffer; + NTSTATUS Status; + + if (IsEqualGUIDAligned(*IoStack->Parameters.QueryInterface.InterfaceType, USB_BUS_INTERFACE_HUB_GUID)) + { + // + // get request parameters + // + InterfaceHub = (PUSB_BUS_INTERFACE_HUB_V5)IoStack->Parameters.QueryInterface.Interface; + InterfaceHub->Version = IoStack->Parameters.QueryInterface.Version; + + // + // check version + // + if (IoStack->Parameters.QueryInterface.Version >= 6) + { + DPRINT1("USB_BUS_INTERFACE_HUB_GUID version %x not supported!\n", IoStack->Parameters.QueryInterface.Version); + + // + // version not supported + // + return STATUS_NOT_SUPPORTED; + } + + // + // Interface version 0 + // + if (IoStack->Parameters.QueryInterface.Version >= 0) + { + InterfaceHub->Size = IoStack->Parameters.QueryInterface.Size; + InterfaceHub->BusContext = PVOID(this); + InterfaceHub->InterfaceReference = USBI_InterfaceReference; + InterfaceHub->InterfaceDereference = USBI_InterfaceDereference; + } + + // + // Interface version 1 + // + if (IoStack->Parameters.QueryInterface.Version >= 1) + { + InterfaceHub->CreateUsbDevice = USBHI_CreateUsbDevice; + InterfaceHub->InitializeUsbDevice = USBHI_InitializeUsbDevice; + InterfaceHub->GetUsbDescriptors = USBHI_GetUsbDescriptors; + InterfaceHub->RemoveUsbDevice = USBHI_RemoveUsbDevice; + InterfaceHub->RestoreUsbDevice = USBHI_RestoreUsbDevice; + InterfaceHub->QueryDeviceInformation = USBHI_QueryDeviceInformation; + } + + // + // Interface version 2 + // + if (IoStack->Parameters.QueryInterface.Version >= 2) + { + InterfaceHub->GetControllerInformation = USBHI_GetControllerInformation; + InterfaceHub->ControllerSelectiveSuspend = USBHI_ControllerSelectiveSuspend; + InterfaceHub->GetExtendedHubInformation = USBHI_GetExtendedHubInformation; + InterfaceHub->GetRootHubSymbolicName = USBHI_GetRootHubSymbolicName; + InterfaceHub->GetDeviceBusContext = USBHI_GetDeviceBusContext; + InterfaceHub->Initialize20Hub = USBHI_Initialize20Hub; + + } + + // + // Interface version 3 + // + if (IoStack->Parameters.QueryInterface.Version >= 3) + { + InterfaceHub->RootHubInitNotification = USBHI_RootHubInitNotification; + } + + // + // Interface version 4 + // + if (IoStack->Parameters.QueryInterface.Version >= 4) + { + InterfaceHub->FlushTransfers = USBHI_FlushTransfers; + } + + // + // Interface version 5 + // + if (IoStack->Parameters.QueryInterface.Version >= 5) + { + InterfaceHub->SetDeviceHandleData = USBHI_SetDeviceHandleData; + } + + // + // request completed + // + return STATUS_SUCCESS; + } + else if (IsEqualGUIDAligned(*IoStack->Parameters.QueryInterface.InterfaceType, USB_BUS_INTERFACE_USBDI_GUID)) + { + // + // get request parameters + // + InterfaceDI = (PUSB_BUS_INTERFACE_USBDI_V2) IoStack->Parameters.QueryInterface.Interface; + InterfaceDI->Version = IoStack->Parameters.QueryInterface.Version; + + // + // check version + // + if (IoStack->Parameters.QueryInterface.Version >= 3) + { + DPRINT1("USB_BUS_INTERFACE_USBDI_GUID version %x not supported!\n", IoStack->Parameters.QueryInterface.Version); + + // + // version not supported + // + return STATUS_NOT_SUPPORTED; + } + + // + // interface version 0 + // + if (IoStack->Parameters.QueryInterface.Version >= 0) + { + InterfaceDI->Size = IoStack->Parameters.QueryInterface.Size; + InterfaceDI->BusContext = PVOID(this); + InterfaceDI->InterfaceReference = USBI_InterfaceReference; + InterfaceDI->InterfaceDereference = USBI_InterfaceDereference; + InterfaceDI->GetUSBDIVersion = USBDI_GetUSBDIVersion; + InterfaceDI->QueryBusTime = USBDI_QueryBusTime; + InterfaceDI->SubmitIsoOutUrb = USBDI_SubmitIsoOutUrb; + InterfaceDI->QueryBusInformation = USBDI_QueryBusInformation; + } + + // + // interface version 1 + // + if (IoStack->Parameters.QueryInterface.Version >= 1) + { + InterfaceDI->IsDeviceHighSpeed = USBDI_IsDeviceHighSpeed; + } + + // + // interface version 2 + // + if (IoStack->Parameters.QueryInterface.Version >= 2) + { + InterfaceDI->EnumLogEntry = USBDI_EnumLogEntry; + } + + // + // request completed + // + return STATUS_SUCCESS; + } + else + { + // + // convert guid to string + // + Status = RtlStringFromGUID(*IoStack->Parameters.QueryInterface.InterfaceType, &GuidBuffer); + if (NT_SUCCESS(Status)) + { + // + // print interface + // + DPRINT1("HandleQueryInterface UNKNOWN INTERFACE GUID: %wZ Version %x\n", &GuidBuffer, IoStack->Parameters.QueryInterface.Version); + + // + // free guid buffer + // + RtlFreeUnicodeString(&GuidBuffer); + } + } + return STATUS_NOT_SUPPORTED; +} + +NTSTATUS +CHubController::SetDeviceInterface( + BOOLEAN Enable) +{ + NTSTATUS Status = STATUS_SUCCESS; + + if (Enable) + { + // + // register device interface + // + Status = IoRegisterDeviceInterface(m_HubControllerDeviceObject, &GUID_DEVINTERFACE_USB_HUB, 0, &m_HubDeviceInterfaceString); + + if (NT_SUCCESS(Status)) + { + // + // now enable the device interface + // + Status = IoSetDeviceInterfaceState(&m_HubDeviceInterfaceString, TRUE); + + // + // enable interface + // + m_InterfaceEnabled = TRUE; + } + } + else if (m_InterfaceEnabled) + { + // + // disable device interface + // + Status = IoSetDeviceInterfaceState(&m_HubDeviceInterfaceString, FALSE); + + if (NT_SUCCESS(Status)) + { + // + // now delete interface string + // + RtlFreeUnicodeString(&m_HubDeviceInterfaceString); + } + + // + // disable interface + // + m_InterfaceEnabled = FALSE; + } + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +CHubController::CreatePDO( + PDRIVER_OBJECT DriverObject, + PDEVICE_OBJECT * OutDeviceObject) +{ + WCHAR CharDeviceName[64]; + NTSTATUS Status; + ULONG UsbDeviceNumber = 0; + UNICODE_STRING DeviceName; + + while (TRUE) + { + // + // construct device name + // + swprintf(CharDeviceName, L"\\Device\\USBPDO-%d", UsbDeviceNumber); + + // + // initialize device name + // + RtlInitUnicodeString(&DeviceName, CharDeviceName); + + // + // create device + // + Status = IoCreateDevice(DriverObject, + sizeof(COMMON_DEVICE_EXTENSION), + &DeviceName, + FILE_DEVICE_CONTROLLER, + 0, + FALSE, + OutDeviceObject); + + /* check for success */ + if (NT_SUCCESS(Status)) + break; + + // + // is there a device object with that same name + // + if ((Status == STATUS_OBJECT_NAME_EXISTS) || (Status == STATUS_OBJECT_NAME_COLLISION)) + { + // + // Try the next name + // + UsbDeviceNumber++; + continue; + } + + // + // bail out on other errors + // + if (!NT_SUCCESS(Status)) + { + DPRINT1("CreatePDO: Failed to create %wZ, Status %x\n", &DeviceName, Status); + return Status; + } + } + + DPRINT1("CHubController::CreatePDO: DeviceName %wZ\n", &DeviceName); + + // + // fixup device stack voodoo part #1 + // + (*OutDeviceObject)->StackSize++; + + /* done */ + return Status; +} + + + +NTSTATUS +CreateHubController( + PHUBCONTROLLER *OutHcdController) +{ + PHUBCONTROLLER This; + + // + // allocate controller + // + This = new(NonPagedPool, TAG_USBLIB) CHubController(0); + if (!This) + { + // + // failed to allocate + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // add reference count + // + This->AddRef(); + + // + // return result + // + *OutHcdController = (PHUBCONTROLLER)This; + + // + // done + // + return STATUS_SUCCESS; +} + +VOID StatusChangeEndpointCallBack(PVOID Context) +{ + CHubController* This; + PIRP Irp; + This = (CHubController*)Context; + + ASSERT(This); + + Irp = This->m_PendingSCEIrp; + if (!Irp) + { + DPRINT1("There was no pending IRP for SCE. Did the usb hub 2.0 driver (usbhub2) load?\n"); + return; + } + + This->m_PendingSCEIrp = NULL; + This->QueryStatusChageEndpoint(Irp); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + IoCompleteRequest(Irp, IO_NO_INCREMENT); +} diff --git a/reactos/lib/drivers/libusb/libusb.cpp b/reactos/lib/drivers/libusb/libusb.cpp new file mode 100644 index 00000000000..2014b69c586 --- /dev/null +++ b/reactos/lib/drivers/libusb/libusb.cpp @@ -0,0 +1,123 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Driver Library + * LICENSE: GPL - See COPYING in the top level directory + * FILE: lib/drivers/libusb/libusb.cpp + * PURPOSE: USB Common Driver Library. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + + +#include "libusb.h" + +// +// driver verifier +// +DRIVER_ADD_DEVICE USBLIB_AddDevice; + +NTSTATUS +NTAPI +USBLIB_AddDevice( + PDRIVER_OBJECT DriverObject, + PDEVICE_OBJECT PhysicalDeviceObject) +{ + NTSTATUS Status; + PHCDCONTROLLER HcdController; + + DPRINT1("EHCI_AddDevice\n"); + + /* first create the controller object */ + Status = CreateHCDController(&HcdController); + if (!NT_SUCCESS(Status)) + { + /* failed to create hcd */ + DPRINT1("AddDevice: Failed to create hcd with %x\n", Status); + return Status; + } + + /* initialize the hcd */ + Status = HcdController->Initialize(NULL, // FIXME + DriverObject, + PhysicalDeviceObject); + + /* check for success */ + if (!NT_SUCCESS(Status)) + { + /* failed to initialize device */ + DPRINT1("AddDevice: failed to initialize\n"); + + /* release object */ + HcdController->Release(); + } + + return Status; + +} + +NTSTATUS +NTAPI +USBLIB_Dispatch( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) +{ + PCOMMON_DEVICE_EXTENSION DeviceExtension; + PIO_STACK_LOCATION IoStack; + NTSTATUS Status; + + // + // get common device extension + // + DeviceExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // sanity checks + // + PC_ASSERT(DeviceExtension->Dispatcher); + + switch(IoStack->MajorFunction) + { + case IRP_MJ_PNP: + { + // + // dispatch pnp + // + return DeviceExtension->Dispatcher->HandlePnp(DeviceObject, Irp); + } + + case IRP_MJ_POWER: + { + // + // dispatch pnp + // + return DeviceExtension->Dispatcher->HandlePower(DeviceObject, Irp); + } + case IRP_MJ_INTERNAL_DEVICE_CONTROL: + case IRP_MJ_DEVICE_CONTROL: + { + // + // dispatch pnp + // + return DeviceExtension->Dispatcher->HandleDeviceControl(DeviceObject, Irp); + } + default: + { + DPRINT1("EHCI_Dispatch> Major %lu Minor %lu unhandeled\n", IoStack->MajorFunction, IoStack->MinorFunction); + Status = STATUS_SUCCESS; + } + } + + // + // complete request + // + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return Status; +} diff --git a/reactos/lib/drivers/libusb/libusb.h b/reactos/lib/drivers/libusb/libusb.h new file mode 100644 index 00000000000..620a1066e6b --- /dev/null +++ b/reactos/lib/drivers/libusb/libusb.h @@ -0,0 +1,100 @@ +#ifndef LIBUSB_H__ +#define LIBUSB_H__ + +#include +#define NDEBUG +#include +#include +#include +#include + +extern "C" +{ + #include +} + + +// +// FIXME: +// #include +// +#include +#include +#include + +// +// FIXME: +// the following includes are required to get kcom to compile +// +#include +#include +#include + +#include "common_interfaces.h" + +// +// flags for handling USB_REQUEST_SET_FEATURE / USB_REQUEST_GET_FEATURE +// +#define PORT_ENABLE 1 +#define PORT_SUSPEND 2 +#define PORT_OVER_CURRENT 3 +#define PORT_RESET 4 +#define PORT_POWER 8 +#define C_PORT_CONNECTION 16 +#define C_PORT_ENABLE 17 +#define C_PORT_SUSPEND 18 +#define C_PORT_OVER_CURRENT 19 +#define C_PORT_RESET 20 + +typedef struct +{ + BOOLEAN IsFDO; // is device a FDO or PDO + BOOLEAN IsHub; // is device a hub / child - not yet used + PDISPATCHIRP Dispatcher; // dispatches the code +}COMMON_DEVICE_EXTENSION, *PCOMMON_DEVICE_EXTENSION; + +// +// tag for allocations +// +#define TAG_USBLIB 'LBSU' + +// +// assert for c++ - taken from portcls +// +#define PC_ASSERT(exp) \ + (VOID)((!(exp)) ? \ + RtlAssert((PVOID) #exp, (PVOID)__FILE__, __LINE__, NULL ), FALSE : TRUE) + +// hcd_controller.cpp +NTSTATUS CreateHCDController(PHCDCONTROLLER *HcdController); + +// hardware.cpp +NTSTATUS CreateUSBHardware(PUSBHARDWAREDEVICE *OutHardware); + +// misc.cpp +NTSTATUS NTAPI SyncForwardIrp(PDEVICE_OBJECT DeviceObject, PIRP Irp); +NTSTATUS NTAPI GetBusInterface(PDEVICE_OBJECT DeviceObject, PBUS_INTERFACE_STANDARD busInterface); + +// root_hub_controller.cpp +NTSTATUS CreateHubController(PHUBCONTROLLER * OutHubController); + +// memory_manager.cpp +NTSTATUS CreateDMAMemoryManager(PDMAMEMORYMANAGER *OutMemoryManager); + + +// usb_device.cpp +NTSTATUS CreateUSBDevice(PUSBDEVICE *OutDevice); + +// usb_queue.cpp +NTSTATUS CreateUSBQueue(PUSBQUEUE *OutUsbQueue); + +// usb_request.cpp +NTSTATUS InternalCreateUSBRequest(PUSBREQUEST *OutRequest); + +// libusb.cpp +NTSTATUS NTAPI USBLIB_AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT PhysicalDeviceObject); +NTSTATUS NTAPI USBLIB_Dispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp); + + + +#endif diff --git a/reactos/lib/drivers/libusb/memory_manager.cpp b/reactos/lib/drivers/libusb/memory_manager.cpp new file mode 100644 index 00000000000..d7007972783 --- /dev/null +++ b/reactos/lib/drivers/libusb/memory_manager.cpp @@ -0,0 +1,370 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Driver Library + * LICENSE: GPL - See COPYING in the top level directory + * FILE: lib/drivers/libusb/memory_manager.cpp + * PURPOSE: USB Common Driver Library. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + + +#include "libusb.h" + +class CDMAMemoryManager : public IDMAMemoryManager +{ +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; + } + + // IDMAMemoryManager interface functions + virtual NTSTATUS Initialize(IN PUSBHARDWAREDEVICE Device, IN PKSPIN_LOCK Lock, IN ULONG DmaBufferSize, IN PVOID VirtualBase, IN PHYSICAL_ADDRESS PhysicalAddress, IN ULONG DefaultBlockSize); + virtual NTSTATUS Allocate(IN ULONG Size, OUT PVOID *OutVirtualBase, OUT PPHYSICAL_ADDRESS OutPhysicalAddress); + virtual NTSTATUS Release(IN PVOID VirtualBase, IN ULONG Size); + + // constructor / destructor + CDMAMemoryManager(IUnknown *OuterUnknown){} + virtual ~CDMAMemoryManager(){} + +protected: + LONG m_Ref; + PUSBHARDWAREDEVICE m_Device; + PKSPIN_LOCK m_Lock; + LONG m_DmaBufferSize; + PVOID m_VirtualBase; + PHYSICAL_ADDRESS m_PhysicalAddress; + ULONG m_BlockSize; + + PULONG m_BitmapBuffer; + RTL_BITMAP m_Bitmap; +}; + +//---------------------------------------------------------------------------------------- +NTSTATUS +STDMETHODCALLTYPE +CDMAMemoryManager::QueryInterface( + IN REFIID refiid, + OUT PVOID* Output) +{ + return STATUS_UNSUCCESSFUL; +} + +NTSTATUS +CDMAMemoryManager::Initialize( + IN PUSBHARDWAREDEVICE Device, + IN PKSPIN_LOCK Lock, + IN ULONG DmaBufferSize, + IN PVOID VirtualBase, + IN PHYSICAL_ADDRESS PhysicalAddress, + IN ULONG DefaultBlockSize) +{ + ULONG BitmapLength; + + // + // sanity checks + // + PC_ASSERT(DmaBufferSize >= PAGE_SIZE); + PC_ASSERT(DmaBufferSize % PAGE_SIZE == 0); + PC_ASSERT(DefaultBlockSize == 32 || DefaultBlockSize == 64 || DefaultBlockSize == 128); + + // + // calculate bitmap length + // + BitmapLength = (DmaBufferSize / DefaultBlockSize) / 8; + + // + // allocate bitmap buffer + // + m_BitmapBuffer = (PULONG)ExAllocatePoolWithTag(NonPagedPool, BitmapLength, TAG_USBLIB); + if (!m_BitmapBuffer) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // initialize bitmap + // + RtlInitializeBitMap(&m_Bitmap, m_BitmapBuffer, BitmapLength * 8); + + // + // clear all bits + // + RtlClearAllBits(&m_Bitmap); + + // + // initialize rest of memory allocator + // + m_PhysicalAddress = PhysicalAddress; + m_VirtualBase = VirtualBase; + m_DmaBufferSize = DmaBufferSize; + m_BitmapBuffer = m_BitmapBuffer; + m_Lock = Lock; + m_BlockSize = DefaultBlockSize; + + /* done */ + return STATUS_SUCCESS; +} + +NTSTATUS +CDMAMemoryManager::Allocate( + IN ULONG Size, + OUT PVOID *OutVirtualAddress, + OUT PPHYSICAL_ADDRESS OutPhysicalAddress) +{ + ULONG Length, BlockCount, FreeIndex, StartPage, EndPage; + KIRQL OldLevel; + ULONG BlocksPerPage; + + // + // sanity checks + // + ASSERT(Size <= PAGE_SIZE); + //ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); + + // + // align request + // + Length = (Size + m_BlockSize -1) & ~(m_BlockSize -1); + + // + // sanity check + // + ASSERT(Length); + + // + // convert to block count + // + BlockCount = Length / m_BlockSize; + + // + // acquire lock + // + KeAcquireSpinLock(m_Lock, &OldLevel); + + // + // helper variable + // + BlocksPerPage = PAGE_SIZE / m_BlockSize; + + // + // start search + // + FreeIndex = 0; + do + { + // + // search for an free index + // + FreeIndex = RtlFindClearBits(&m_Bitmap, BlockCount, FreeIndex); + + // + // check if there was a block found + // + if (FreeIndex == MAXULONG) + { + // + // no free block found + // + break; + } + + // + // check that the allocation does not spawn over page boundaries + // + StartPage = (FreeIndex * m_BlockSize); + StartPage = (StartPage != 0 ? StartPage / PAGE_SIZE : 0); + EndPage = ((FreeIndex + BlockCount) * m_BlockSize) / PAGE_SIZE; + + // + // does the request start and end on the same page + // + if (StartPage == EndPage) + { + // + // reserve block + // + RtlSetBits(&m_Bitmap, FreeIndex, BlockCount); + + // + // reserve block + // + break; + } + else if ((BlockCount == BlocksPerPage) && (FreeIndex % BlocksPerPage == 0)) + { + // + // the request equals PAGE_SIZE and is aligned at page boundary + // reserve block + // + RtlSetBits(&m_Bitmap, FreeIndex, BlockCount); + + // + // reserve block + // + break; + } + else + { + // + // request spawned over page boundary + // restart search on next page + // + FreeIndex = (EndPage * PAGE_SIZE) / m_BlockSize; + } + } + while(TRUE); + + // + // release lock + // + KeReleaseSpinLock(m_Lock, OldLevel); + + // + // did allocation succeed + // + if (FreeIndex == MAXULONG) + { + // + // failed to allocate block, requestor must retry + // + return STATUS_UNSUCCESSFUL; + } + + // + // return result + // + *OutVirtualAddress = (PVOID)((ULONG_PTR)m_VirtualBase + FreeIndex * m_BlockSize); + OutPhysicalAddress->QuadPart = m_PhysicalAddress.QuadPart + FreeIndex * m_BlockSize; + + // + // clear block + // + RtlZeroMemory(*OutVirtualAddress, Length); + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +CDMAMemoryManager::Release( + IN PVOID VirtualAddress, + IN ULONG Size) +{ + KIRQL OldLevel; + ULONG BlockOffset = 0, BlockLength, BlockCount; + + // + // sanity checks + // + PC_ASSERT(VirtualAddress); + PC_ASSERT((ULONG_PTR)VirtualAddress >= (ULONG_PTR)m_VirtualBase); + PC_ASSERT((ULONG_PTR)m_VirtualBase + m_DmaBufferSize > (ULONG_PTR)m_VirtualBase); + + // + // calculate block length + // + BlockLength = ((ULONG_PTR)VirtualAddress - (ULONG_PTR)m_VirtualBase); + + // + // check if its the first block + // + if (BlockLength) + { + // + // divide by base block size + // + BlockOffset = BlockLength / m_BlockSize; + } + + // + // align length to block size + // + Size = (Size + m_BlockSize - 1) & ~(m_BlockSize - 1); + + // + // convert to blocks + // + BlockCount = Size / m_BlockSize; + ASSERT(BlockCount); + + // + // acquire lock + // + KeAcquireSpinLock(m_Lock, &OldLevel); + + // + // sanity check + // + ASSERT(RtlAreBitsSet(&m_Bitmap, BlockOffset, BlockCount)); + + // + // release buffer + // + RtlClearBits(&m_Bitmap, BlockOffset, BlockCount); + + // + // release lock + // + KeReleaseSpinLock(m_Lock, OldLevel); + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +CreateDMAMemoryManager( + PDMAMEMORYMANAGER *OutMemoryManager) +{ + CDMAMemoryManager* This; + + // + // allocate controller + // + This = new(NonPagedPool, TAG_USBLIB) CDMAMemoryManager(0); + if (!This) + { + // + // failed to allocate + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // add reference count + // + This->AddRef(); + + // + // return result + // + *OutMemoryManager = (PDMAMEMORYMANAGER)This; + + // + // done + // + return STATUS_SUCCESS; +} + diff --git a/reactos/lib/drivers/libusb/misc.cpp b/reactos/lib/drivers/libusb/misc.cpp new file mode 100644 index 00000000000..fe36cf447cc --- /dev/null +++ b/reactos/lib/drivers/libusb/misc.cpp @@ -0,0 +1,135 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Driver Library + * LICENSE: GPL - See COPYING in the top level directory + * FILE: lib/drivers/libusb/misc.cpp + * PURPOSE: USB Common Driver Library. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + + +#include "libusb.h" + +// +// driver verifier +// +IO_COMPLETION_ROUTINE SyncForwardIrpCompletionRoutine; + +NTSTATUS +NTAPI +SyncForwardIrpCompletionRoutine( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Context) +{ + if (Irp->PendingReturned) + { + KeSetEvent((PKEVENT)Context, IO_NO_INCREMENT, FALSE); + } + return STATUS_MORE_PROCESSING_REQUIRED; +} + +NTSTATUS +NTAPI +SyncForwardIrp(PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + KEVENT Event; + NTSTATUS Status; + + // + // initialize event + // + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + // + // copy irp stack location + // + IoCopyCurrentIrpStackLocationToNext(Irp); + + // + // set completion routine + // + IoSetCompletionRoutine(Irp, SyncForwardIrpCompletionRoutine, &Event, TRUE, TRUE, TRUE); + + + // + // call driver + // + Status = IoCallDriver(DeviceObject, Irp); + + + // + // check if pending + // + if (Status == STATUS_PENDING) + { + // + // wait for the request to finish + // + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + + // + // copy status code + // + Status = Irp->IoStatus.Status; + } + + // + // done + // + return Status; +} + +NTSTATUS +NTAPI +GetBusInterface( + PDEVICE_OBJECT DeviceObject, + PBUS_INTERFACE_STANDARD busInterface) +{ + KEVENT Event; + NTSTATUS Status; + PIRP Irp; + IO_STATUS_BLOCK IoStatus; + PIO_STACK_LOCATION Stack; + + if ((!DeviceObject) || (!busInterface)) + return STATUS_UNSUCCESSFUL; + + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, + DeviceObject, + NULL, + 0, + NULL, + &Event, + &IoStatus); + + if (Irp == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + Stack=IoGetNextIrpStackLocation(Irp); + Stack->MajorFunction = IRP_MJ_PNP; + Stack->MinorFunction = IRP_MN_QUERY_INTERFACE; + Stack->Parameters.QueryInterface.Size = sizeof(BUS_INTERFACE_STANDARD); + Stack->Parameters.QueryInterface.InterfaceType = (LPGUID)&GUID_BUS_INTERFACE_STANDARD; + Stack->Parameters.QueryInterface.Version = 1; + Stack->Parameters.QueryInterface.Interface = (PINTERFACE)busInterface; + Stack->Parameters.QueryInterface.InterfaceSpecificData = NULL; + Irp->IoStatus.Status=STATUS_NOT_SUPPORTED ; + + Status=IoCallDriver(DeviceObject, Irp); + + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + + Status=IoStatus.Status; + } + + return Status; +} + diff --git a/reactos/lib/drivers/libusb/purecall.cpp b/reactos/lib/drivers/libusb/purecall.cpp new file mode 100644 index 00000000000..f7d49f9d001 --- /dev/null +++ b/reactos/lib/drivers/libusb/purecall.cpp @@ -0,0 +1,26 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Driver Library + * LICENSE: GPL - See COPYING in the top level directory + * FILE: lib/drivers/libusb/purecall.cpp + * PURPOSE: USB Common Driver Library. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + + + +#include "libusb.h" + + +extern "C" { + void + __cxa_pure_virtual() + { + // put error handling here + + DbgBreakPoint(); + + } +} + diff --git a/reactos/lib/drivers/libusb/usb_device.cpp b/reactos/lib/drivers/libusb/usb_device.cpp new file mode 100644 index 00000000000..14f3554aa3d --- /dev/null +++ b/reactos/lib/drivers/libusb/usb_device.cpp @@ -0,0 +1,1218 @@ +/* + * 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) + */ + + +#define INITGUID +#include "libusb.h" + +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); + + + // 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); + + // 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; + + 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; + + // + // 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("CUSBDevice::Initialize GetUsbQueue failed with %x\n", Status); + return Status; + } + + // + // get dma manager + // + Status = m_Device->GetDMA(&m_DmaManager); + if (!NT_SUCCESS(Status)) + { + // + // failed to get dma manager + // + DPRINT1("CUSBDevice::Initialize GetDMA failed with %x\n", Status); + return Status; + } + + // + // sanity check + // + PC_ASSERT(m_DmaManager); + + // + // get device descriptor + // + Status = CreateDeviceDescriptor(); + if (!NT_SUCCESS(Status)) + { + // + // failed to get device descriptor + // + DPRINT1("CUSBDevice::Initialize Failed to get device descriptor with %x\n", 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("CUSBDevice::GetType Unknown bcdUSB Type %x\n", 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 OldAddress; + UCHAR Index; + + DPRINT1("CUSBDevice::SetDeviceAddress Address %d\n", 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, 0, 0, 0); + + // free setup packet + ExFreePoolWithTag(CtrlSetup, TAG_USBLIB); + + // check for success + if (!NT_SUCCESS(Status)) + { + // failed to set device address + DPRINT1("CUSBDevice::SetDeviceAddress> failed to set device address with %x Address %x\n", Status, DeviceAddress); + return Status; + } + + // lets have a short nap + KeStallExecutionProcessor(300); + + // back up old address + OldAddress = m_DeviceAddress; + + // store new device address + m_DeviceAddress = DeviceAddress; + + // check that setting device address succeeded by retrieving the device descriptor + Status = CreateDeviceDescriptor(); + if (!NT_SUCCESS(Status)) + { + // failed to retrieve device descriptor + DPRINT1("CUSBbDevice::SetDeviceAddress> failed to retrieve device descriptor with device address set Error %x\n", Status); + m_DeviceAddress = OldAddress; + + // return error status + return Status; + } + + // 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("CUSBDevice::SetDeviceAddress> failed to retrieve configuration %lu\n", 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("CUSBDevice::CommitUrb> no queue / dma !!!\n"); + return STATUS_UNSUCCESSFUL; + } + + // + // build usb request + // + Status = m_Queue->CreateUSBRequest(&Request); + if (!NT_SUCCESS(Status)) + { + // + // failed to build request + // + DPRINT1("CUSBDevice::CommitSetupPacket> CreateUSBRequest failed with %x\n", Status); + return Status; + } + + // + // initialize request + // + Status = Request->InitializeWithIrp(m_DmaManager, 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("CUSBDevice::CommitSetupPacket> failed add request to queue with %x\n", 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("CUSBDevice::CommitSetupPacket> no queue!!!\n"); + return STATUS_UNSUCCESSFUL; + } + + // + // build usb request + // + Status = m_Queue->CreateUSBRequest(&Request); + if (!NT_SUCCESS(Status)) + { + // + // failed to build request + // + DPRINT1("CUSBDevice::CommitSetupPacket> CreateUSBRequest failed with %x\n", Status); + return Status; + } + + // + // initialize request + // + Status = Request->InitializeWithSetupPacket(m_DmaManager, Packet, m_DeviceAddress, EndpointDescriptor, BufferLength, Mdl); + if (!NT_SUCCESS(Status)) + { + // + // failed to initialize request + // + DPRINT1("CUSBDevice::CommitSetupPacket> failed to initialize usb request with %x\n", Status); + Request->Release(); + return Status; + } + + // + // now add the request + // + Status = m_Queue->AddUSBRequest(Request); + if (!NT_SUCCESS(Status)) + { + // + // failed to add request + // + DPRINT1("CUSBDevice::CommitSetupPacket> failed add request to queue with %x\n", 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, 0, sizeof(USB_DEVICE_DESCRIPTOR), Mdl); + + // + // now free the mdl + // + IoFreeMdl(Mdl); + + if (NT_SUCCESS(Status)) + { + // + // informal dbg print + // + RtlCopyMemory(&m_DeviceDescriptor, Buffer, sizeof(USB_DEVICE_DESCRIPTOR)); + DumpDeviceDescriptor(&m_DeviceDescriptor); + } + + // + // free buffer + // + ExFreePool(Buffer); + + // + // done + // + return Status; + +} + +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBDevice::CreateConfigurationDescriptor( + UCHAR Index) +{ + PVOID Buffer; + USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + NTSTATUS Status; + PMDL Mdl; + 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 + // + Buffer = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, TAG_USBLIB); + if (!Buffer) + { + // + // failed to allocate buffer + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // build setup packet + // + CtrlSetup.bmRequestType._BM.Recipient = BMREQUEST_TO_DEVICE; + CtrlSetup.bmRequestType._BM.Type = BMREQUEST_STANDARD; + CtrlSetup.bmRequestType._BM.Reserved = 0; + CtrlSetup.bmRequestType._BM.Dir = BMREQUEST_DEVICE_TO_HOST; + CtrlSetup.bRequest = USB_REQUEST_GET_DESCRIPTOR; + CtrlSetup.wValue.LowByte = Index; + CtrlSetup.wValue.HiByte = USB_CONFIGURATION_DESCRIPTOR_TYPE; + CtrlSetup.wIndex.W = 0; + CtrlSetup.wLength = PAGE_SIZE; + + // + // now build MDL describing the buffer + // + Mdl = IoAllocateMdl(Buffer, PAGE_SIZE, FALSE, FALSE, 0); + if (!Mdl) + { + // + // failed to allocate mdl + // + ExFreePoolWithTag(Buffer, TAG_USBLIB); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // build mdl for non paged pool + // + MmBuildMdlForNonPagedPool(Mdl); + + // + // commit packet + // + Status = CommitSetupPacket(&CtrlSetup, 0, PAGE_SIZE, Mdl); + if (!NT_SUCCESS(Status)) + { + // + // failed to issue request, cleanup + // + IoFreeMdl(Mdl); + ExFreePool(Buffer); + return Status; + } + + // + // now free the mdl + // + IoFreeMdl(Mdl); + + // + // get configuration descriptor + // + ConfigurationDescriptor = (PUSB_CONFIGURATION_DESCRIPTOR)Buffer; + + // + // 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) +{ + // 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 + RtlCopyMemory(ConfigDescriptorBuffer, m_ConfigurationDescriptors[0].ConfigurationDescriptor, min(m_ConfigurationDescriptors[0].ConfigurationDescriptor->wTotalLength, BufferLength)); + *OutBufferLength = m_ConfigurationDescriptors[0].ConfigurationDescriptor->wTotalLength; +} + +//---------------------------------------------------------------------------------------- +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 %x\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 %x\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, 0, BufferLength, Mdl); + + if (Mdl != NULL) + { + // + // free mdl + // + IoFreeMdl(Mdl); + } + + // + // done + // + return Status; +} + +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBDevice::SelectConfiguration( + IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor, + IN PUSBD_INTERFACE_INFORMATION InterfaceInfo, + OUT USBD_CONFIGURATION_HANDLE *ConfigurationHandle) +{ + ULONG InterfaceIndex, PipeIndex; + 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; + PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor; + 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("[USBUHCI] invalid configuration value %lu\n", 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, 0, 0, 0); + + if (!ConfigurationDescriptor) + { + // unconfigure request + DPRINT1("CUsbDevice::SelectConfiguration Unconfigure Request Status %x\n", Status); + m_ConfigurationIndex = 0; + return Status; + } + + // informal debug print + DPRINT1("CUsbDevice::SelectConfiguration New Configuration %x Old Configuration %x Result %x\n", 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 + InterfaceInfo->Class = InterfaceDescriptor->bInterfaceClass; + InterfaceInfo->SubClass = InterfaceDescriptor->bInterfaceSubClass; + InterfaceInfo->Protocol = InterfaceDescriptor->bInterfaceProtocol; + InterfaceInfo->Reserved = 0; + + // allocate interface handle + UsbInterface = (PUSB_INTERFACE)ExAllocatePool(NonPagedPool, sizeof(USB_INTERFACE) + (InterfaceInfo->NumberOfPipes - 1) * sizeof(USB_ENDPOINT)); + if (!UsbInterface) + { + // failed to allocate memory + return STATUS_INSUFFICIENT_RESOURCES; + } + + // store handle + InterfaceInfo->InterfaceHandle = (USBD_INTERFACE_HANDLE)UsbInterface; + + // 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 < InterfaceInfo->NumberOfPipes; PipeIndex++) + { + while(EndpointDescriptor->bDescriptorType != USB_ENDPOINT_DESCRIPTOR_TYPE && EndpointDescriptor->bLength != sizeof(USB_ENDPOINT_DESCRIPTOR)) + { + // skip intermediate descriptors + if (EndpointDescriptor->bLength == 0 || EndpointDescriptor->bDescriptorType == USB_INTERFACE_DESCRIPTOR_TYPE) + { + // bogus configuration descriptor + DPRINT1("[USBEHCI] Bogus descriptor found in InterfaceNumber %x Alternate %x EndpointIndex %x bLength %x bDescriptorType %x\n", 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)); + + // 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); + } + + // move offset + InterfaceInfo = (PUSBD_INTERFACE_INFORMATION)((ULONG_PTR)PtrToUlong(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; + + // 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("[USBEHCI] Invalid configuration handle passed %p\n", 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, 0, 0, 0); + + // informal debug print + DPRINT1("CUSBDevice::SelectInterface AlternateSetting %x InterfaceNumber %x Status %x\n", 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; + } + } + + if (!Found) + { + // selected interface but interface not found + // something is really wrong + DPRINT1("[USBEHCI] Error: interface not found!!!\n"); + return STATUS_UNSUCCESSFUL; + } + + // assert on pipe length mismatch + ASSERT(InterfaceInfo->NumberOfPipes == UsbInterface->InterfaceDescriptor->bNumEndpoints); + + // copy pipe handles + for(PipeIndex = 0; PipeIndex < min(InterfaceInfo->NumberOfPipes, UsbInterface->InterfaceDescriptor->bNumEndpoints); PipeIndex++) + { + // copy pipe handle + DPRINT1("PipeIndex %lu\n", PipeIndex); + DPRINT1("EndpointAddress %x\n", InterfaceInfo->Pipes[PipeIndex].EndpointAddress); + DPRINT1("Interval %d\n", InterfaceInfo->Pipes[PipeIndex].Interval); + DPRINT1("MaximumPacketSize %d\n", InterfaceInfo->Pipes[PipeIndex].MaximumPacketSize); + DPRINT1("MaximumTransferSize %d\n", InterfaceInfo->Pipes[PipeIndex].MaximumTransferSize); + DPRINT1("PipeFlags %d\n", InterfaceInfo->Pipes[PipeIndex].PipeFlags); + DPRINT1("PipeType %dd\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); +} + + +//---------------------------------------------------------------------------------------- +NTSTATUS +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; +} +