/* * PROJECT: ReactOS Universal Serial Bus Hub Driver * LICENSE: GPL - See COPYING in the top level directory * FILE: drivers/usb/usbhub/fdo.c * PURPOSE: Handle FDO * PROGRAMMERS: * Michael Martin (michael.martin@reactos.org) * Johannes Anderwald (johannes.anderwald@reactos.org) */ #include "usbhub.h" #include #define NDEBUG #include NTSTATUS QueryStatusChangeEndpoint( IN PDEVICE_OBJECT DeviceObject); NTSTATUS CreateUsbChildDeviceObject( IN PDEVICE_OBJECT UsbHubDeviceObject, IN LONG PortId, OUT PDEVICE_OBJECT *UsbChildDeviceObject, IN ULONG PortStatus); NTSTATUS DestroyUsbChildDeviceObject( IN PDEVICE_OBJECT UsbHubDeviceObject, IN LONG PortId); NTSTATUS GetPortStatusAndChange( IN PDEVICE_OBJECT RootHubDeviceObject, IN ULONG PortId, OUT PPORT_STATUS_CHANGE StatusChange) { NTSTATUS Status; PURB Urb; // // Allocate URB // Urb = ExAllocatePoolWithTag(NonPagedPool, sizeof(URB), USB_HUB_TAG); if (!Urb) { DPRINT1("Failed to allocate memory for URB!\n"); return STATUS_INSUFFICIENT_RESOURCES; } // // Zero it // RtlZeroMemory(Urb, sizeof(URB)); // // Initialize URB for getting Port Status // UsbBuildVendorRequest(Urb, URB_FUNCTION_CLASS_OTHER, sizeof(Urb->UrbControlVendorClassRequest), USBD_TRANSFER_DIRECTION_OUT, 0, USB_REQUEST_GET_STATUS, 0, PortId, StatusChange, 0, sizeof(PORT_STATUS_CHANGE), 0); // FIXME: support usb hubs Urb->UrbHeader.UsbdDeviceHandle = NULL; // // Query the Root Hub // Status = SubmitRequestToRootHub(RootHubDeviceObject, IOCTL_INTERNAL_USB_SUBMIT_URB, Urb, NULL); // // Free URB // ExFreePool(Urb); return Status; } NTSTATUS SetPortFeature( IN PDEVICE_OBJECT RootHubDeviceObject, IN ULONG PortId, IN ULONG Feature) { NTSTATUS Status; PURB Urb; // // Allocate URB // Urb = ExAllocatePoolWithTag(NonPagedPool, sizeof(URB), USB_HUB_TAG); if (!Urb) { DPRINT1("Failed to allocate memory for URB!\n"); return STATUS_INSUFFICIENT_RESOURCES; } // // Zero it // RtlZeroMemory(Urb, sizeof(URB)); // // Initialize URB for Clearing Port Reset // UsbBuildVendorRequest(Urb, URB_FUNCTION_CLASS_OTHER, sizeof(Urb->UrbControlVendorClassRequest), USBD_TRANSFER_DIRECTION_IN, 0, USB_REQUEST_SET_FEATURE, Feature, PortId, NULL, 0, 0, 0); // FIXME support usbhubs Urb->UrbHeader.UsbdDeviceHandle = NULL; // // Query the Root Hub // Status = SubmitRequestToRootHub(RootHubDeviceObject, IOCTL_INTERNAL_USB_SUBMIT_URB, Urb, NULL); // // Free URB // ExFreePool(Urb); return Status; } NTSTATUS ClearPortFeature( IN PDEVICE_OBJECT RootHubDeviceObject, IN ULONG PortId, IN ULONG Feature) { NTSTATUS Status; PURB Urb; // // Allocate a URB // Urb = ExAllocatePoolWithTag(NonPagedPool, sizeof(URB), USB_HUB_TAG); if (!Urb) { DPRINT1("Failed to allocate memory for URB!\n"); return STATUS_INSUFFICIENT_RESOURCES; } // // Zero it // RtlZeroMemory(Urb, sizeof(URB)); // // Initialize URB for Clearing Port Reset // UsbBuildVendorRequest(Urb, URB_FUNCTION_CLASS_OTHER, sizeof(Urb->UrbControlVendorClassRequest), USBD_TRANSFER_DIRECTION_IN, 0, USB_REQUEST_CLEAR_FEATURE, Feature, PortId, NULL, 0, 0, 0); // FIXME: support usb hubs Urb->UrbHeader.UsbdDeviceHandle = NULL; // // Query the Root Hub // Status = SubmitRequestToRootHub(RootHubDeviceObject, IOCTL_INTERNAL_USB_SUBMIT_URB, Urb, NULL); // // Free URB // ExFreePool(Urb); return Status; } VOID NTAPI DeviceStatusChangeThread( IN PVOID Context) { NTSTATUS Status; PDEVICE_OBJECT DeviceObject, RootHubDeviceObject; PHUB_DEVICE_EXTENSION HubDeviceExtension; PWORK_ITEM_DATA WorkItemData; PORT_STATUS_CHANGE PortStatus; LONG PortId; BOOLEAN SignalResetComplete = FALSE; DPRINT("Entered DeviceStatusChangeThread, Context %x\n", Context); WorkItemData = (PWORK_ITEM_DATA)Context; DeviceObject = (PDEVICE_OBJECT)WorkItemData->Context; HubDeviceExtension = (PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension; RootHubDeviceObject = HubDeviceExtension->RootHubPhysicalDeviceObject; // // Loop all ports // for (PortId = 1; PortId <= HubDeviceExtension->UsbExtHubInfo.NumberOfPorts; PortId++) { // // Get Port Status // Status = GetPortStatusAndChange(RootHubDeviceObject, PortId, &PortStatus); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to get port status for port %d, Status %x\n", PortId, Status); // FIXME: Do we really want to halt further SCE requests? return; } DPRINT("Port %d Status %x\n", PortId, PortStatus.Status); DPRINT("Port %d Change %x\n", PortId, PortStatus.Change); // // Check for new device connection // if (PortStatus.Change & USB_PORT_STATUS_CONNECT) { // // Clear Port Connect // Status = ClearPortFeature(RootHubDeviceObject, PortId, C_PORT_CONNECTION); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to clear connection change for port %d\n", PortId); continue; } // // Is this a connect or disconnect? // if (!(PortStatus.Status & USB_PORT_STATUS_CONNECT)) { DPRINT1("Device disconnected from port %d\n", PortId); Status = DestroyUsbChildDeviceObject(DeviceObject, PortId); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to delete child device object after disconnect\n"); continue; } } else { DPRINT1("Device connected from port %d\n", PortId); // No SCE completion done for clearing C_PORT_CONNECT // // Reset Port // Status = SetPortFeature(RootHubDeviceObject, PortId, PORT_RESET); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to reset port %d\n", PortId); SignalResetComplete = TRUE; continue; } } } else if (PortStatus.Change & USB_PORT_STATUS_ENABLE) { // // Clear Enable // Status = ClearPortFeature(RootHubDeviceObject, PortId, C_PORT_ENABLE); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to clear enable change on port %d\n", PortId); continue; } } else if (PortStatus.Change & USB_PORT_STATUS_RESET) { // // Request event signalling later // SignalResetComplete = TRUE; // // Clear Reset // Status = ClearPortFeature(RootHubDeviceObject, PortId, C_PORT_RESET); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to clear reset change on port %d\n", PortId); continue; } // // Get Port Status // Status = GetPortStatusAndChange(RootHubDeviceObject, PortId, &PortStatus); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to get port status for port %d, Status %x\n", PortId, Status); // FIXME: Do we really want to halt further SCE requests? return; } DPRINT("Port %d Status %x\n", PortId, PortStatus.Status); DPRINT("Port %d Change %x\n", PortId, PortStatus.Change); // // Check that reset was cleared // if(PortStatus.Change & USB_PORT_STATUS_RESET) { DPRINT1("Port did not clear reset! Possible Hardware problem!\n"); continue; } // // Check if the device is still connected // if (!(PortStatus.Status & USB_PORT_STATUS_CONNECT)) { DPRINT1("Device has been disconnected\n"); continue; } // // Make sure its Connected and Enabled // if (!(PortStatus.Status & (USB_PORT_STATUS_CONNECT | USB_PORT_STATUS_ENABLE))) { DPRINT1("Usb Device is not connected and enabled!\n"); // // Attempt another reset // Status = SetPortFeature(RootHubDeviceObject, PortId, PORT_RESET); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to reset port %d\n", PortId); } continue; } // // This is a new device // Status = CreateUsbChildDeviceObject(DeviceObject, PortId, NULL, PortStatus.Status); } } ExFreePool(WorkItemData); // // Send another SCE Request // DPRINT("Sending another SCE!\n"); QueryStatusChangeEndpoint(DeviceObject); // // Check if a reset event was satisfied // if (SignalResetComplete) { // // Signal anyone waiting on it // KeSetEvent(&HubDeviceExtension->ResetComplete, IO_NO_INCREMENT, FALSE); } } NTSTATUS NTAPI StatusChangeEndpointCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) { PDEVICE_OBJECT RealDeviceObject; PHUB_DEVICE_EXTENSION HubDeviceExtension; PWORK_ITEM_DATA WorkItemData; RealDeviceObject = (PDEVICE_OBJECT)Context; HubDeviceExtension = (PHUB_DEVICE_EXTENSION)RealDeviceObject->DeviceExtension; // // NOTE: USBPORT frees this IRP // DPRINT("Received Irp %x, HubDeviceExtension->PendingSCEIrp %x\n", Irp, HubDeviceExtension->PendingSCEIrp); //IoFreeIrp(Irp); // // Create and initialize work item data // WorkItemData = ExAllocatePoolWithTag(NonPagedPool, sizeof(WORK_ITEM_DATA), USB_HUB_TAG); if (!WorkItemData) { DPRINT1("Failed to allocate memory!n"); return STATUS_INSUFFICIENT_RESOURCES; } WorkItemData->Context = RealDeviceObject; DPRINT("Queuing work item\n"); // // Queue the work item to handle initializing the device // ExInitializeWorkItem(&WorkItemData->WorkItem, DeviceStatusChangeThread, (PVOID)WorkItemData); ExQueueWorkItem(&WorkItemData->WorkItem, DelayedWorkQueue); // // Return more processing required so the IO Manger doesn’t try to mess with IRP just freed // return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS QueryStatusChangeEndpoint( IN PDEVICE_OBJECT DeviceObject) { PDEVICE_OBJECT RootHubDeviceObject; PIO_STACK_LOCATION Stack; PHUB_DEVICE_EXTENSION HubDeviceExtension; PURB PendingSCEUrb; HubDeviceExtension = (PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension; RootHubDeviceObject = HubDeviceExtension->RootHubPhysicalDeviceObject; // // Allocate a URB // PendingSCEUrb = ExAllocatePoolWithTag(NonPagedPool, sizeof(URB), USB_HUB_TAG); // // Initialize URB for Status Change Endpoint request // UsbBuildInterruptOrBulkTransferRequest(PendingSCEUrb, sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER), HubDeviceExtension->PipeHandle, HubDeviceExtension->PortStatusChange, NULL, sizeof(USHORT) * 2 * HubDeviceExtension->UsbExtHubInfo.NumberOfPorts, USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK, NULL); // Set the device handle PendingSCEUrb->UrbHeader.UsbdDeviceHandle = HubDeviceExtension->RootHubHandle; // // Allocate an Irp // HubDeviceExtension->PendingSCEIrp = ExAllocatePoolWithTag(NonPagedPool, IoSizeOfIrp(RootHubDeviceObject->StackSize), USB_HUB_TAG); /* HubDeviceExtension->PendingSCEIrp = IoAllocateIrp(RootHubDeviceObject->StackSize, FALSE); */ DPRINT("Allocated IRP %x\n", HubDeviceExtension->PendingSCEIrp); if (!HubDeviceExtension->PendingSCEIrp) { DPRINT1("USBHUB: Failed to allocate IRP for SCE request!\n"); return STATUS_INSUFFICIENT_RESOURCES; } // // Initialize the IRP // IoInitializeIrp(HubDeviceExtension->PendingSCEIrp, IoSizeOfIrp(RootHubDeviceObject->StackSize), RootHubDeviceObject->StackSize); HubDeviceExtension->PendingSCEIrp->IoStatus.Status = STATUS_NOT_SUPPORTED; HubDeviceExtension->PendingSCEIrp->IoStatus.Information = 0; HubDeviceExtension->PendingSCEIrp->Flags = 0; HubDeviceExtension->PendingSCEIrp->UserBuffer = NULL; // // Get the Next Stack Location and Initialize it // Stack = IoGetNextIrpStackLocation(HubDeviceExtension->PendingSCEIrp); Stack->DeviceObject = DeviceObject; Stack->Parameters.Others.Argument1 = PendingSCEUrb; Stack->Parameters.Others.Argument2 = NULL; Stack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; Stack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB; // // Set the completion routine for when device is connected to root hub // IoSetCompletionRoutine(HubDeviceExtension->PendingSCEIrp, StatusChangeEndpointCompletion, DeviceObject, TRUE, TRUE, TRUE); // // Send to RootHub // DPRINT("DeviceObject is %x\n", DeviceObject); DPRINT("Iocalldriver %x with irp %x\n", RootHubDeviceObject, HubDeviceExtension->PendingSCEIrp); IoCallDriver(RootHubDeviceObject, HubDeviceExtension->PendingSCEIrp); return STATUS_PENDING; } NTSTATUS QueryInterface( IN PDEVICE_OBJECT DeviceObject, IN CONST GUID InterfaceType, IN LONG Size, IN LONG Version, OUT PVOID Interface) { KEVENT Event; PIRP Irp; IO_STATUS_BLOCK IoStatus; NTSTATUS Status; PIO_STACK_LOCATION Stack = NULL; // // Initialize the Event used to wait for Irp completion // KeInitializeEvent(&Event, NotificationEvent, FALSE); // // Build Control Request // Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, DeviceObject, NULL, 0, NULL, &Event, &IoStatus); // // Get Next Stack Location and Initialize it. // Stack = IoGetNextIrpStackLocation(Irp); Stack->MinorFunction = IRP_MN_QUERY_INTERFACE; Stack->Parameters.QueryInterface.InterfaceType= &InterfaceType;//USB_BUS_INTERFACE_HUB_GUID; Stack->Parameters.QueryInterface.Size = Size; Stack->Parameters.QueryInterface.Version = Version; Stack->Parameters.QueryInterface.Interface = Interface; Stack->Parameters.QueryInterface.InterfaceSpecificData = NULL; Status = IoCallDriver(DeviceObject, Irp); if (Status == STATUS_PENDING) { DPRINT("Operation pending\n"); KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL); Status = IoStatus.Status; } return Status; } NTSTATUS GetUsbDeviceDescriptor( IN PDEVICE_OBJECT ChildDeviceObject, IN UCHAR DescriptorType, IN UCHAR Index, IN USHORT LangId, OUT PVOID TransferBuffer, IN ULONG TransferBufferLength) { NTSTATUS Status; PDEVICE_OBJECT RootHubDeviceObject; PURB Urb; PHUB_DEVICE_EXTENSION HubDeviceExtension; PHUB_CHILDDEVICE_EXTENSION ChildDeviceExtension; // // Get the Hubs Device Extension // ChildDeviceExtension = (PHUB_CHILDDEVICE_EXTENSION)ChildDeviceObject->DeviceExtension; HubDeviceExtension = (PHUB_DEVICE_EXTENSION) ChildDeviceExtension->ParentDeviceObject->DeviceExtension; RootHubDeviceObject = HubDeviceExtension->RootHubPhysicalDeviceObject; // // Allocate a URB // Urb = ExAllocatePoolWithTag(NonPagedPool, sizeof(URB), USB_HUB_TAG); if (!Urb) { DPRINT1("Failed to allocate memory for URB!\n"); return STATUS_INSUFFICIENT_RESOURCES; } // // Zero it // RtlZeroMemory(Urb, sizeof(URB)); // // Initialize URB for getting device descriptor // UsbBuildGetDescriptorRequest(Urb, sizeof(Urb->UrbControlDescriptorRequest), DescriptorType, Index, LangId, TransferBuffer, NULL, TransferBufferLength, NULL); // // Set the device handle // Urb->UrbHeader.UsbdDeviceHandle = (PVOID)ChildDeviceExtension->UsbDeviceHandle; // // Query the Root Hub // Status = SubmitRequestToRootHub(RootHubDeviceObject, IOCTL_INTERNAL_USB_SUBMIT_URB, Urb, NULL); return Status; } NTSTATUS GetUsbStringDescriptor( IN PDEVICE_OBJECT ChildDeviceObject, IN UCHAR Index, IN USHORT LangId, OUT PVOID *TransferBuffer, OUT USHORT *Size) { NTSTATUS Status; PUSB_STRING_DESCRIPTOR StringDesc = NULL; ULONG SizeNeeded; LPWSTR Buffer; StringDesc = ExAllocatePoolWithTag(NonPagedPool, sizeof(USB_STRING_DESCRIPTOR), USB_HUB_TAG); if (!StringDesc) { DPRINT1("Failed to allocate buffer for string!\n"); return STATUS_INSUFFICIENT_RESOURCES; } // // Get the index string descriptor length // FIXME: Implement LangIds // Status = GetUsbDeviceDescriptor(ChildDeviceObject, USB_STRING_DESCRIPTOR_TYPE, Index, 0x0409, StringDesc, sizeof(USB_STRING_DESCRIPTOR)); if (!NT_SUCCESS(Status)) { DPRINT1("GetUsbDeviceDescriptor failed with status %x\n", Status); ExFreePool(StringDesc); return Status; } DPRINT1("StringDesc->bLength %d\n", StringDesc->bLength); // // Did we get something more than the length of the first two fields of structure? // if (StringDesc->bLength == 2) { DPRINT1("USB Device Error!\n"); ExFreePool(StringDesc); return STATUS_DEVICE_DATA_ERROR; } SizeNeeded = StringDesc->bLength + sizeof(WCHAR); // // Free String // ExFreePool(StringDesc); // // Recreate with appropriate size // StringDesc = ExAllocatePoolWithTag(NonPagedPool, SizeNeeded, USB_HUB_TAG); if (!StringDesc) { DPRINT1("Failed to allocate buffer for string!\n"); return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(StringDesc, SizeNeeded); // // Get the string // Status = GetUsbDeviceDescriptor(ChildDeviceObject, USB_STRING_DESCRIPTOR_TYPE, Index, 0x0409, StringDesc, SizeNeeded); if (!NT_SUCCESS(Status)) { DPRINT1("GetUsbDeviceDescriptor failed with status %x\n", Status); ExFreePool(StringDesc); return Status; } // // Allocate Buffer to return // Buffer = ExAllocatePoolWithTag(NonPagedPool, SizeNeeded, USB_HUB_TAG); if (!Buffer) { DPRINT1("Failed to allocate buffer for string!\n"); ExFreePool(StringDesc); return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(Buffer, SizeNeeded); // // Copy the string to destination // RtlCopyMemory(Buffer, StringDesc->bString, SizeNeeded - FIELD_OFFSET(USB_STRING_DESCRIPTOR, bString)); *Size = SizeNeeded; *TransferBuffer = Buffer; ExFreePool(StringDesc); return STATUS_SUCCESS; } ULONG IsCompositeDevice( IN PUSB_DEVICE_DESCRIPTOR DeviceDescriptor, IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor) { if (DeviceDescriptor->bNumConfigurations != 1) { // // composite device must have only one configuration // DPRINT1("IsCompositeDevice bNumConfigurations %x\n", DeviceDescriptor->bNumConfigurations); return FALSE; } if (ConfigurationDescriptor->bNumInterfaces < 2) { // // composite device must have multiple interfaces // DPRINT1("IsCompositeDevice bNumInterfaces %x\n", ConfigurationDescriptor->bNumInterfaces); return FALSE; } if (DeviceDescriptor->bDeviceClass == 0) { // // composite device // ASSERT(DeviceDescriptor->bDeviceSubClass == 0); ASSERT(DeviceDescriptor->bDeviceProtocol == 0); DPRINT1("IsCompositeDevice: TRUE\n"); return TRUE; } if (DeviceDescriptor->bDeviceClass == 0xEF && DeviceDescriptor->bDeviceSubClass == 0x02 && DeviceDescriptor->bDeviceProtocol == 0x01) { // // USB-IF association descriptor // DPRINT1("IsCompositeDevice: TRUE\n"); return TRUE; } DPRINT1("DeviceDescriptor bDeviceClass %x bDeviceSubClass %x bDeviceProtocol %x\n", DeviceDescriptor->bDeviceClass, DeviceDescriptor->bDeviceSubClass, DeviceDescriptor->bDeviceProtocol); // // not a composite device // return FALSE; } NTSTATUS CreateDeviceIds( PDEVICE_OBJECT UsbChildDeviceObject) { NTSTATUS Status = STATUS_SUCCESS; ULONG Index = 0; LPWSTR DeviceString; WCHAR Buffer[200]; PHUB_CHILDDEVICE_EXTENSION UsbChildExtension; PHUB_DEVICE_EXTENSION HubDeviceExtension; PUSB_DEVICE_DESCRIPTOR DeviceDescriptor; PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor; PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor; // // get child device extension // UsbChildExtension = (PHUB_CHILDDEVICE_EXTENSION)UsbChildDeviceObject->DeviceExtension; // get hub device extension HubDeviceExtension = (PHUB_DEVICE_EXTENSION) UsbChildExtension->ParentDeviceObject->DeviceExtension; // // get device descriptor // DeviceDescriptor = &UsbChildExtension->DeviceDesc; // // get configuration descriptor // ConfigurationDescriptor = UsbChildExtension->FullConfigDesc; // // use first interface descriptor available // InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(ConfigurationDescriptor, ConfigurationDescriptor, 0, -1, -1, -1, -1); if (InterfaceDescriptor == NULL) { DPRINT1("Error USBD_ParseConfigurationDescriptorEx failed to parse interface descriptor\n"); return STATUS_INVALID_PARAMETER; } ASSERT(InterfaceDescriptor); // // Construct the CompatibleIds // if (IsCompositeDevice(DeviceDescriptor, ConfigurationDescriptor)) { // // sanity checks // ASSERT(DeviceDescriptor->bNumConfigurations == 1); ASSERT(ConfigurationDescriptor->bNumInterfaces > 1); Index += swprintf(&Buffer[Index], L"USB\\DevClass_%02x&SubClass_%02x&Prot_%02x", DeviceDescriptor->bDeviceClass, DeviceDescriptor->bDeviceSubClass, DeviceDescriptor->bDeviceProtocol) + 1; Index += swprintf(&Buffer[Index], L"USB\\DevClass_%02x&SubClass_%02x", DeviceDescriptor->bDeviceClass, DeviceDescriptor->bDeviceSubClass) + 1; Index += swprintf(&Buffer[Index], L"USB\\DevClass_%02x", DeviceDescriptor->bDeviceClass) + 1; Index += swprintf(&Buffer[Index], L"USB\\COMPOSITE") + 1; } else { // // FIXME: support multiple configurations // ASSERT(DeviceDescriptor->bNumConfigurations == 1); if (DeviceDescriptor->bDeviceClass == 0) { Index += swprintf(&Buffer[Index], L"USB\\Class_%02x&SubClass_%02x&Prot_%02x", InterfaceDescriptor->bInterfaceClass, InterfaceDescriptor->bInterfaceSubClass, InterfaceDescriptor->bInterfaceProtocol) + 1; Index += swprintf(&Buffer[Index], L"USB\\Class_%02x&SubClass_%02x", InterfaceDescriptor->bInterfaceClass, InterfaceDescriptor->bInterfaceSubClass) + 1; Index += swprintf(&Buffer[Index], L"USB\\Class_%02x", InterfaceDescriptor->bInterfaceClass) + 1; } else { Index += swprintf(&Buffer[Index], L"USB\\Class_%02x&SubClass_%02x&Prot_%02x", DeviceDescriptor->bDeviceClass, DeviceDescriptor->bDeviceSubClass, DeviceDescriptor->bDeviceProtocol) + 1; Index += swprintf(&Buffer[Index], L"USB\\Class_%02x&SubClass_%02x", DeviceDescriptor->bDeviceClass, DeviceDescriptor->bDeviceSubClass) + 1; Index += swprintf(&Buffer[Index], L"USB\\Class_%02x", DeviceDescriptor->bDeviceClass) + 1; } } // // now allocate the buffer // DeviceString = ExAllocatePool(NonPagedPool, (Index + 1) * sizeof(WCHAR)); if (!DeviceString) { // // no memory // return STATUS_INSUFFICIENT_RESOURCES; } // // copy buffer // RtlCopyMemory(DeviceString, Buffer, Index * sizeof(WCHAR)); DeviceString[Index] = UNICODE_NULL; UsbChildExtension->usCompatibleIds.Buffer = DeviceString; UsbChildExtension->usCompatibleIds.Length = Index * sizeof(WCHAR); UsbChildExtension->usCompatibleIds.MaximumLength = (Index + 1) * sizeof(WCHAR); DPRINT("usCompatibleIds %wZ\n", &UsbChildExtension->usCompatibleIds); // // Construct DeviceId string // Index = swprintf(Buffer, L"USB\\Vid_%04x&Pid_%04x", UsbChildExtension->DeviceDesc.idVendor, UsbChildExtension->DeviceDesc.idProduct) + 1; // // now allocate the buffer // DeviceString = ExAllocatePool(NonPagedPool, Index * sizeof(WCHAR)); if (!DeviceString) { // // no memory // return STATUS_INSUFFICIENT_RESOURCES; } // // copy buffer // RtlCopyMemory(DeviceString, Buffer, Index * sizeof(WCHAR)); UsbChildExtension->usDeviceId.Buffer = DeviceString; UsbChildExtension->usDeviceId.Length = (Index-1) * sizeof(WCHAR); UsbChildExtension->usDeviceId.MaximumLength = Index * sizeof(WCHAR); DPRINT("usDeviceId %wZ\n", &UsbChildExtension->usDeviceId); // // Construct HardwareIds // Index = 0; Index += swprintf(&Buffer[Index], L"USB\\Vid_%04x&Pid_%04x&Rev_%04x", UsbChildExtension->DeviceDesc.idVendor, UsbChildExtension->DeviceDesc.idProduct, UsbChildExtension->DeviceDesc.bcdDevice) + 1; Index += swprintf(&Buffer[Index], L"USB\\Vid_%04x&Pid_%04x", UsbChildExtension->DeviceDesc.idVendor, UsbChildExtension->DeviceDesc.idProduct) + 1; // // now allocate the buffer // DeviceString = ExAllocatePool(NonPagedPool, (Index + 1) * sizeof(WCHAR)); if (!DeviceString) { // // no memory // return STATUS_INSUFFICIENT_RESOURCES; } // // copy buffer // RtlCopyMemory(DeviceString, Buffer, Index * sizeof(WCHAR)); DeviceString[Index] = UNICODE_NULL; UsbChildExtension->usHardwareIds.Buffer = DeviceString; UsbChildExtension->usHardwareIds.Length = (Index + 1) * sizeof(WCHAR); UsbChildExtension->usHardwareIds.MaximumLength = (Index + 1) * sizeof(WCHAR); DPRINT("usHardWareIds %wZ\n", &UsbChildExtension->usHardwareIds); // // FIXME: Handle Lang ids // // // Get the product string if obe provided // if (UsbChildExtension->DeviceDesc.iProduct) { Status = GetUsbStringDescriptor(UsbChildDeviceObject, UsbChildExtension->DeviceDesc.iProduct, 0, (PVOID*)&UsbChildExtension->usTextDescription.Buffer, &UsbChildExtension->usTextDescription.Length); if (!NT_SUCCESS(Status)) { DPRINT1("USBHUB: GetUsbStringDescriptor failed with status %x\n", Status); RtlInitUnicodeString(&UsbChildExtension->usTextDescription, L"USB Device"); // FIXME NON-NLS } else { UsbChildExtension->usTextDescription.MaximumLength = UsbChildExtension->usTextDescription.Length; DPRINT("Usb TextDescription %wZ\n", &UsbChildExtension->usTextDescription); } } // // Get the Serial Number string if obe provided // if (UsbChildExtension->DeviceDesc.iSerialNumber) { LPWSTR SerialBuffer = NULL; Status = GetUsbStringDescriptor(UsbChildDeviceObject, UsbChildExtension->DeviceDesc.iSerialNumber, 0, (PVOID*)&SerialBuffer, &UsbChildExtension->usInstanceId.Length); if (NT_SUCCESS(Status)) { // construct instance id buffer Index = swprintf(Buffer, L"%04d&%s", HubDeviceExtension->InstanceCount, SerialBuffer) + 1; UsbChildExtension->usInstanceId.Buffer = (LPWSTR)ExAllocatePool(NonPagedPool, Index * sizeof(WCHAR)); if (UsbChildExtension->usInstanceId.Buffer == NULL) { DPRINT1("Error: failed to allocate %lu bytes\n", Index * sizeof(WCHAR)); return STATUS_INSUFFICIENT_RESOURCES; } // // copy instance id // RtlCopyMemory(UsbChildExtension->usInstanceId.Buffer, Buffer, Index * sizeof(WCHAR)); UsbChildExtension->usInstanceId.Length = UsbChildExtension->usInstanceId.MaximumLength = Index * sizeof(WCHAR); ExFreePool(SerialBuffer); DPRINT("Usb InstanceId %wZ InstanceCount %x\n", &UsbChildExtension->usInstanceId, HubDeviceExtension->InstanceCount); return Status; } } // // the device did not provide a serial number, or failed to retrieve the serial number // lets create a pseudo instance id // Index = swprintf(Buffer, L"%04d&%04d", HubDeviceExtension->InstanceCount, UsbChildExtension->PortNumber) + 1; UsbChildExtension->usInstanceId.Buffer = (LPWSTR)ExAllocatePool(NonPagedPool, Index * sizeof(WCHAR)); if (UsbChildExtension->usInstanceId.Buffer == NULL) { DPRINT1("Error: failed to allocate %lu bytes\n", Index * sizeof(WCHAR)); Status = STATUS_INSUFFICIENT_RESOURCES; return Status; } // // copy instance id // RtlCopyMemory(UsbChildExtension->usInstanceId.Buffer, Buffer, Index * sizeof(WCHAR)); UsbChildExtension->usInstanceId.Length = UsbChildExtension->usInstanceId.MaximumLength = Index * sizeof(WCHAR); DPRINT("usDeviceId %wZ\n", &UsbChildExtension->usInstanceId); return STATUS_SUCCESS; } NTSTATUS DestroyUsbChildDeviceObject( IN PDEVICE_OBJECT UsbHubDeviceObject, IN LONG PortId) { PHUB_DEVICE_EXTENSION HubDeviceExtension = (PHUB_DEVICE_EXTENSION)UsbHubDeviceObject->DeviceExtension; PHUB_CHILDDEVICE_EXTENSION UsbChildExtension = NULL; PDEVICE_OBJECT ChildDeviceObject = NULL; ULONG Index = 0; DPRINT("Removing device on port %d (Child index: %d)\n", PortId, Index); for (Index = 0; Index < USB_MAXCHILDREN; Index++) { if (HubDeviceExtension->ChildDeviceObject[Index]) { UsbChildExtension = (PHUB_CHILDDEVICE_EXTENSION)HubDeviceExtension->ChildDeviceObject[Index]->DeviceExtension; /* Check if it matches the port ID */ if (UsbChildExtension->PortNumber == PortId) { /* We found it */ ChildDeviceObject = HubDeviceExtension->ChildDeviceObject[Index]; break; } } } /* Fail the request if the device doesn't exist */ if (!ChildDeviceObject) { DPRINT1("Removal request for non-existant device!\n"); return STATUS_UNSUCCESSFUL; } /* Remove the device from the table */ HubDeviceExtension->ChildDeviceObject[Index] = NULL; /* Invalidate device relations for the root hub */ IoInvalidateDeviceRelations(HubDeviceExtension->RootHubPhysicalDeviceObject, BusRelations); /* The rest of the removal process takes place in IRP_MN_REMOVE_DEVICE handling for the PDO */ return STATUS_SUCCESS; } NTSTATUS CreateUsbChildDeviceObject( IN PDEVICE_OBJECT UsbHubDeviceObject, IN LONG PortId, OUT PDEVICE_OBJECT *UsbChildDeviceObject, IN ULONG PortStatus) { NTSTATUS Status; PDEVICE_OBJECT RootHubDeviceObject, NewChildDeviceObject; PHUB_DEVICE_EXTENSION HubDeviceExtension; PHUB_CHILDDEVICE_EXTENSION UsbChildExtension; PUSB_BUS_INTERFACE_HUB_V5 HubInterface; ULONG ChildDeviceCount, UsbDeviceNumber = 0; WCHAR CharDeviceName[64]; UNICODE_STRING DeviceName; ULONG ConfigDescSize, DeviceDescSize, DeviceInfoSize; PVOID HubInterfaceBusContext; USB_CONFIGURATION_DESCRIPTOR ConfigDesc; HubDeviceExtension = (PHUB_DEVICE_EXTENSION) UsbHubDeviceObject->DeviceExtension; HubInterface = &HubDeviceExtension->HubInterface; RootHubDeviceObject = HubDeviceExtension->RootHubPhysicalDeviceObject; HubInterfaceBusContext = HubDeviceExtension->UsbDInterface.BusContext; // // Find an empty slot in the child device array // for (ChildDeviceCount = 0; ChildDeviceCount < USB_MAXCHILDREN; ChildDeviceCount++) { if (HubDeviceExtension->ChildDeviceObject[ChildDeviceCount] == NULL) { DPRINT("Found unused entry at %d\n", ChildDeviceCount); break; } } // // Check if the limit has been reached for maximum usb devices // if (ChildDeviceCount == USB_MAXCHILDREN) { DPRINT1("USBHUB: Too many child devices!\n"); return STATUS_UNSUCCESSFUL; } while (TRUE) { // // Create a Device Name // swprintf(CharDeviceName, L"\\Device\\USBPDO-%d", UsbDeviceNumber); // // Initialize UnicodeString // RtlInitUnicodeString(&DeviceName, CharDeviceName); // // Create a DeviceObject // Status = IoCreateDevice(UsbHubDeviceObject->DriverObject, sizeof(HUB_CHILDDEVICE_EXTENSION), NULL, FILE_DEVICE_CONTROLLER, FILE_AUTOGENERATED_DEVICE_NAME, FALSE, &NewChildDeviceObject); // // Check if the name is already in use // if ((Status == STATUS_OBJECT_NAME_EXISTS) || (Status == STATUS_OBJECT_NAME_COLLISION)) { // // Try next name // UsbDeviceNumber++; continue; } // // Check for other errors // if (!NT_SUCCESS(Status)) { DPRINT1("USBHUB: IoCreateDevice failed with status %x\n", Status); return Status; } DPRINT("USBHUB: Created Device %x\n", NewChildDeviceObject); break; } NewChildDeviceObject->Flags |= DO_BUS_ENUMERATED_DEVICE; // // Assign the device extensions // UsbChildExtension = (PHUB_CHILDDEVICE_EXTENSION)NewChildDeviceObject->DeviceExtension; RtlZeroMemory(UsbChildExtension, sizeof(HUB_CHILDDEVICE_EXTENSION)); UsbChildExtension->ParentDeviceObject = UsbHubDeviceObject; UsbChildExtension->PortNumber = PortId; // copy device interface RtlCopyMemory(&UsbChildExtension->DeviceInterface, &HubDeviceExtension->DeviceInterface, sizeof(USB_BUS_INTERFACE_USBDI_V2)); // // Create the UsbDeviceObject // Status = HubInterface->CreateUsbDevice(HubInterfaceBusContext, (PVOID)&UsbChildExtension->UsbDeviceHandle, HubDeviceExtension->RootHubHandle, PortStatus, PortId); if (!NT_SUCCESS(Status)) { DPRINT1("USBHUB: CreateUsbDevice failed with status %x\n", Status); goto Cleanup; } // copy device interface RtlCopyMemory(&UsbChildExtension->DeviceInterface, &HubDeviceExtension->DeviceInterface, sizeof(USB_BUS_INTERFACE_USBDI_V2)); // FIXME replace buscontext UsbChildExtension->DeviceInterface.BusContext = UsbChildExtension->UsbDeviceHandle; // // Initialize UsbDevice // Status = HubInterface->InitializeUsbDevice(HubInterfaceBusContext, UsbChildExtension->UsbDeviceHandle); if (!NT_SUCCESS(Status)) { DPRINT1("USBHUB: InitializeUsbDevice failed with status %x\n", Status); goto Cleanup; } DPRINT("Usb Device Handle %x\n", UsbChildExtension->UsbDeviceHandle); ConfigDescSize = sizeof(USB_CONFIGURATION_DESCRIPTOR); DeviceDescSize = sizeof(USB_DEVICE_DESCRIPTOR); // // Get the descriptors // Status = HubInterface->GetUsbDescriptors(HubInterfaceBusContext, UsbChildExtension->UsbDeviceHandle, (PUCHAR)&UsbChildExtension->DeviceDesc, &DeviceDescSize, (PUCHAR)&ConfigDesc, &ConfigDescSize); if (!NT_SUCCESS(Status)) { DPRINT1("USBHUB: GetUsbDescriptors failed with status %x\n", Status); goto Cleanup; } //DumpDeviceDescriptor(&UsbChildExtension->DeviceDesc); //DumpConfigurationDescriptor(&ConfigDesc); // // FIXME: Support more than one configuration and one interface? // if (UsbChildExtension->DeviceDesc.bNumConfigurations > 1) { DPRINT1("Warning: Device has more than one configuration. Only one configuration (the first) is supported!\n"); } if (ConfigDesc.bNumInterfaces > 1) { DPRINT1("Warning: Device has more that one interface. Only one interface (the first) is currently supported\n"); } ConfigDescSize = ConfigDesc.wTotalLength; // // Allocate memory for the first full descriptor, including interfaces and endpoints. // UsbChildExtension->FullConfigDesc = ExAllocatePoolWithTag(PagedPool, ConfigDescSize, USB_HUB_TAG); // // Retrieve the full configuration descriptor // Status = GetUsbDeviceDescriptor(NewChildDeviceObject, USB_CONFIGURATION_DESCRIPTOR_TYPE, 0, 0, UsbChildExtension->FullConfigDesc, ConfigDescSize); if (!NT_SUCCESS(Status)) { DPRINT1("USBHUB: GetUsbDeviceDescriptor failed with status %x\n", Status); goto Cleanup; } // query device details Status = HubInterface->QueryDeviceInformation(HubInterfaceBusContext, UsbChildExtension->UsbDeviceHandle, &UsbChildExtension->DeviceInformation, sizeof(USB_DEVICE_INFORMATION_0), &DeviceInfoSize); //DumpFullConfigurationDescriptor(UsbChildExtension->FullConfigDesc); // // Construct all the strings that will described the device to PNP // Status = CreateDeviceIds(NewChildDeviceObject); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to create strings needed to describe device to PNP.\n"); goto Cleanup; } HubDeviceExtension->ChildDeviceObject[ChildDeviceCount] = NewChildDeviceObject; HubDeviceExtension->InstanceCount++; IoInvalidateDeviceRelations(RootHubDeviceObject, BusRelations); return STATUS_SUCCESS; Cleanup: // // Remove the usb device if it was created // if (UsbChildExtension->UsbDeviceHandle) HubInterface->RemoveUsbDevice(HubInterfaceBusContext, UsbChildExtension->UsbDeviceHandle, 0); // // Free full configuration descriptor if one was allocated // if (UsbChildExtension->FullConfigDesc) ExFreePool(UsbChildExtension->FullConfigDesc); // // Delete the device object // IoDeleteDevice(NewChildDeviceObject); return Status; } NTSTATUS USBHUB_FdoQueryBusRelations( IN PDEVICE_OBJECT DeviceObject, OUT PDEVICE_RELATIONS* pDeviceRelations) { PHUB_DEVICE_EXTENSION HubDeviceExtension; PDEVICE_RELATIONS DeviceRelations; ULONG i; ULONG Children = 0; ULONG NeededSize; HubDeviceExtension = (PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension; // // Count the number of children // for (i = 0; i < USB_MAXCHILDREN; i++) { if (HubDeviceExtension->ChildDeviceObject[i] == NULL) { continue; } Children++; } NeededSize = sizeof(DEVICE_RELATIONS); if (Children > 1) NeededSize += (Children - 1) * sizeof(PDEVICE_OBJECT); // // Allocate DeviceRelations // DeviceRelations = (PDEVICE_RELATIONS)ExAllocatePool(PagedPool, NeededSize); if (!DeviceRelations) return STATUS_INSUFFICIENT_RESOURCES; DeviceRelations->Count = Children; Children = 0; // // Fill in return structure // for (i = 0; i < USB_MAXCHILDREN; i++) { if (HubDeviceExtension->ChildDeviceObject[i]) { ObReferenceObject(HubDeviceExtension->ChildDeviceObject[i]); HubDeviceExtension->ChildDeviceObject[i]->Flags &= ~DO_DEVICE_INITIALIZING; DeviceRelations->Objects[Children++] = HubDeviceExtension->ChildDeviceObject[i]; } } ASSERT(Children == DeviceRelations->Count); *pDeviceRelations = DeviceRelations; return STATUS_SUCCESS; } VOID NTAPI RootHubInitCallbackFunction( PVOID Context) { PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)Context; NTSTATUS Status; ULONG PortId; PHUB_DEVICE_EXTENSION HubDeviceExtension; PORT_STATUS_CHANGE StatusChange; HubDeviceExtension = (PHUB_DEVICE_EXTENSION) DeviceObject->DeviceExtension; DPRINT("RootHubInitCallbackFunction Sending the initial SCE Request %x\n", DeviceObject); // // Send the first SCE Request // QueryStatusChangeEndpoint(DeviceObject); for (PortId = 1; PortId <= HubDeviceExtension->HubDescriptor.bNumberOfPorts; PortId++) { // // get port status // Status = GetPortStatusAndChange(HubDeviceExtension->RootHubPhysicalDeviceObject, PortId, &StatusChange); if (NT_SUCCESS(Status)) { // // is there a device connected // if (StatusChange.Status & USB_PORT_STATUS_CONNECT) { // // reset port // Status = SetPortFeature(HubDeviceExtension->RootHubPhysicalDeviceObject, PortId, PORT_RESET); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to reset on port %d\n", PortId); } else { // // wait for the reset to be handled since we want to enumerate synchronously // KeWaitForSingleObject(&HubDeviceExtension->ResetComplete, Executive, KernelMode, FALSE, NULL); KeClearEvent(&HubDeviceExtension->ResetComplete); } } } } } BOOLEAN USBHUB_IsRootHubFDO( IN PDEVICE_OBJECT DeviceObject) { NTSTATUS Status; PDEVICE_OBJECT RootHubPhysicalDeviceObject = NULL; PHUB_DEVICE_EXTENSION HubDeviceExtension; // get hub device extension HubDeviceExtension = (PHUB_DEVICE_EXTENSION) DeviceObject->DeviceExtension; // Get the Root Hub Pdo Status = SubmitRequestToRootHub(HubDeviceExtension->LowerDeviceObject, IOCTL_INTERNAL_USB_GET_ROOTHUB_PDO, &RootHubPhysicalDeviceObject, NULL); // FIXME handle error ASSERT(NT_SUCCESS(Status)); // physical device object is only obtained for root hubs return (RootHubPhysicalDeviceObject != NULL); } NTSTATUS USBHUB_FdoStartDevice( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PURB Urb; PUSB_INTERFACE_DESCRIPTOR Pid; ULONG Result = 0, PortId; USBD_INTERFACE_LIST_ENTRY InterfaceList[2] = {{NULL, NULL}, {NULL, NULL}}; PURB ConfigUrb = NULL; ULONG HubStatus; NTSTATUS Status = STATUS_SUCCESS; PHUB_DEVICE_EXTENSION HubDeviceExtension; PDEVICE_OBJECT RootHubDeviceObject; PVOID HubInterfaceBusContext; PORT_STATUS_CHANGE StatusChange; // get hub device extension HubDeviceExtension = (PHUB_DEVICE_EXTENSION) DeviceObject->DeviceExtension; DPRINT("IRP_MJ_PNP / IRP_MN_START_DEVICE\n"); // Allocated size including the sizeof USBD_INTERFACE_LIST_ENTRY Urb = ExAllocatePoolWithTag(NonPagedPool, sizeof(URB) + sizeof(USBD_INTERFACE_LIST_ENTRY), USB_HUB_TAG); if (!Urb) { // no memory return STATUS_INSUFFICIENT_RESOURCES; } // zero urb RtlZeroMemory(Urb, sizeof(URB) + sizeof(USBD_INTERFACE_LIST_ENTRY)); // Get the Root Hub Pdo Status = SubmitRequestToRootHub(HubDeviceExtension->LowerDeviceObject, IOCTL_INTERNAL_USB_GET_ROOTHUB_PDO, &HubDeviceExtension->RootHubPhysicalDeviceObject, &HubDeviceExtension->RootHubFunctionalDeviceObject); if (!NT_SUCCESS(Status)) { // failed to obtain hub pdo DPRINT1("IOCTL_INTERNAL_USB_GET_ROOTHUB_PDO failed with %x\n", Status); ExFreePool(Urb); return Status; } // sanity checks ASSERT(HubDeviceExtension->RootHubPhysicalDeviceObject); ASSERT(HubDeviceExtension->RootHubFunctionalDeviceObject); // get roothub RootHubDeviceObject = HubDeviceExtension->RootHubPhysicalDeviceObject; // Send the StartDevice to RootHub Status = ForwardIrpAndWait(RootHubDeviceObject, Irp); if (!NT_SUCCESS(Status)) { // failed to start pdo DPRINT1("Failed to start the RootHub PDO\n"); ExFreePool(Urb); return Status; } // Get the current number of hubs Status = SubmitRequestToRootHub(RootHubDeviceObject, IOCTL_INTERNAL_USB_GET_HUB_COUNT, &HubDeviceExtension->NumberOfHubs, NULL); if (!NT_SUCCESS(Status)) { // failed to get number of hubs DPRINT1("IOCTL_INTERNAL_USB_GET_HUB_COUNT failed with %x\n", Status); ExFreePool(Urb); return Status; } // Get the Hub Interface Status = QueryInterface(RootHubDeviceObject, USB_BUS_INTERFACE_HUB_GUID, sizeof(USB_BUS_INTERFACE_HUB_V5), USB_BUSIF_HUB_VERSION_5, (PVOID)&HubDeviceExtension->HubInterface); if (!NT_SUCCESS(Status)) { // failed to get root hub interface DPRINT1("Failed to get HUB_GUID interface with status 0x%08lx\n", Status); ExFreePool(Urb); return Status; } HubInterfaceBusContext = HubDeviceExtension->HubInterface.BusContext; // Get the USBDI Interface Status = QueryInterface(RootHubDeviceObject, USB_BUS_INTERFACE_USBDI_GUID, sizeof(USB_BUS_INTERFACE_USBDI_V2), USB_BUSIF_USBDI_VERSION_2, (PVOID)&HubDeviceExtension->UsbDInterface); if (!NT_SUCCESS(Status)) { // failed to get usbdi interface DPRINT1("Failed to get USBDI_GUID interface with status 0x%08lx\n", Status); ExFreePool(Urb); return Status; } // Get Root Hub Device Handle Status = SubmitRequestToRootHub(RootHubDeviceObject, IOCTL_INTERNAL_USB_GET_DEVICE_HANDLE, &HubDeviceExtension->RootHubHandle, NULL); if (!NT_SUCCESS(Status)) { // failed DPRINT1("IOCTL_INTERNAL_USB_GET_DEVICE_HANDLE failed with status 0x%08lx\n", Status); ExFreePool(Urb); return Status; } // // Get Hub Device Information // Status = HubDeviceExtension->HubInterface.QueryDeviceInformation(HubInterfaceBusContext, HubDeviceExtension->RootHubHandle, &HubDeviceExtension->DeviceInformation, sizeof(USB_DEVICE_INFORMATION_0), &Result); DPRINT1("Status %x, Result 0x%08lx\n", Status, Result); DPRINT1("InformationLevel %x\n", HubDeviceExtension->DeviceInformation.InformationLevel); DPRINT1("ActualLength %x\n", HubDeviceExtension->DeviceInformation.ActualLength); DPRINT1("PortNumber %x\n", HubDeviceExtension->DeviceInformation.PortNumber); DPRINT1("DeviceDescriptor %x\n", HubDeviceExtension->DeviceInformation.DeviceDescriptor); DPRINT1("HubAddress %x\n", HubDeviceExtension->DeviceInformation.HubAddress); DPRINT1("NumberofPipes %x\n", HubDeviceExtension->DeviceInformation.NumberOfOpenPipes); // Get Root Hubs Device Descriptor UsbBuildGetDescriptorRequest(Urb, sizeof(Urb->UrbControlDescriptorRequest), USB_DEVICE_DESCRIPTOR_TYPE, 0, 0, &HubDeviceExtension->HubDeviceDescriptor, NULL, sizeof(USB_DEVICE_DESCRIPTOR), NULL); // set device handle Urb->UrbHeader.UsbdDeviceHandle = HubDeviceExtension->RootHubHandle; // get hub device descriptor Status = SubmitRequestToRootHub(RootHubDeviceObject, IOCTL_INTERNAL_USB_SUBMIT_URB, Urb, NULL); if (!NT_SUCCESS(Status)) { // failed to get device descriptor of hub DPRINT1("Failed to get HubDeviceDescriptor!\n"); ExFreePool(Urb); return Status; } // build configuration request UsbBuildGetDescriptorRequest(Urb, sizeof(Urb->UrbControlDescriptorRequest), USB_CONFIGURATION_DESCRIPTOR_TYPE, 0, 0, &HubDeviceExtension->HubConfigDescriptor, NULL, sizeof(USB_CONFIGURATION_DESCRIPTOR) + sizeof(USB_INTERFACE_DESCRIPTOR) + sizeof(USB_ENDPOINT_DESCRIPTOR), NULL); // set device handle Urb->UrbHeader.UsbdDeviceHandle = HubDeviceExtension->RootHubHandle; // request configuration descriptor Status = SubmitRequestToRootHub(RootHubDeviceObject, IOCTL_INTERNAL_USB_SUBMIT_URB, Urb, NULL); if (!NT_SUCCESS(Status)) { // failed to get configuration descriptor DPRINT1("Failed to get RootHub Configuration with status %x\n", Status); ExFreePool(Urb); return Status; } // sanity checks ASSERT(HubDeviceExtension->HubConfigDescriptor.wTotalLength == sizeof(USB_CONFIGURATION_DESCRIPTOR) + sizeof(USB_INTERFACE_DESCRIPTOR) + sizeof(USB_ENDPOINT_DESCRIPTOR)); ASSERT(HubDeviceExtension->HubConfigDescriptor.bDescriptorType == USB_CONFIGURATION_DESCRIPTOR_TYPE); ASSERT(HubDeviceExtension->HubConfigDescriptor.bLength == sizeof(USB_CONFIGURATION_DESCRIPTOR)); ASSERT(HubDeviceExtension->HubConfigDescriptor.bNumInterfaces == 1); ASSERT(HubDeviceExtension->HubInterfaceDescriptor.bLength == sizeof(USB_INTERFACE_DESCRIPTOR)); ASSERT(HubDeviceExtension->HubInterfaceDescriptor.bDescriptorType == USB_INTERFACE_DESCRIPTOR_TYPE); ASSERT(HubDeviceExtension->HubInterfaceDescriptor.bNumEndpoints == 1); ASSERT(HubDeviceExtension->HubEndPointDescriptor.bDescriptorType == USB_ENDPOINT_DESCRIPTOR_TYPE); ASSERT(HubDeviceExtension->HubEndPointDescriptor.bLength == sizeof(USB_ENDPOINT_DESCRIPTOR)); ASSERT(HubDeviceExtension->HubEndPointDescriptor.bmAttributes == USB_ENDPOINT_TYPE_INTERRUPT); ASSERT(HubDeviceExtension->HubEndPointDescriptor.bEndpointAddress == 0x81); // interrupt in // get hub information Status = HubDeviceExtension->HubInterface.GetExtendedHubInformation(HubInterfaceBusContext, RootHubDeviceObject, &HubDeviceExtension->UsbExtHubInfo, sizeof(USB_EXTHUB_INFORMATION_0), &Result); if (!NT_SUCCESS(Status)) { // failed to get hub information DPRINT1("Failed to extended hub information. Unable to determine the number of ports!\n"); ExFreePool(Urb); return Status; } if (!HubDeviceExtension->UsbExtHubInfo.NumberOfPorts) { // bogus port driver DPRINT1("Failed to retrieve the number of ports\n"); ExFreePool(Urb); return STATUS_UNSUCCESSFUL; } DPRINT1("HubDeviceExtension->UsbExtHubInfo.NumberOfPorts %x\n", HubDeviceExtension->UsbExtHubInfo.NumberOfPorts); // Build hub descriptor request UsbBuildVendorRequest(Urb, URB_FUNCTION_CLASS_DEVICE, sizeof(Urb->UrbControlVendorClassRequest), USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK, 0, USB_REQUEST_GET_DESCRIPTOR, USB_DEVICE_CLASS_RESERVED, 0, &HubDeviceExtension->HubDescriptor, NULL, sizeof(USB_HUB_DESCRIPTOR), NULL); // set device handle Urb->UrbHeader.UsbdDeviceHandle = HubDeviceExtension->RootHubHandle; // send request Status = SubmitRequestToRootHub(RootHubDeviceObject, IOCTL_INTERNAL_USB_SUBMIT_URB, Urb, NULL); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to get Hub Descriptor!\n"); ExFreePool(Urb); return STATUS_UNSUCCESSFUL; } // sanity checks ASSERT(HubDeviceExtension->HubDescriptor.bDescriptorLength == sizeof(USB_HUB_DESCRIPTOR)); ASSERT(HubDeviceExtension->HubDescriptor.bNumberOfPorts == HubDeviceExtension->UsbExtHubInfo.NumberOfPorts); ASSERT(HubDeviceExtension->HubDescriptor.bDescriptorType == 0x29); // build get status request HubStatus = 0; UsbBuildGetStatusRequest(Urb, URB_FUNCTION_GET_STATUS_FROM_DEVICE, 0, &HubStatus, 0, NULL); // set device handle Urb->UrbHeader.UsbdDeviceHandle = HubDeviceExtension->RootHubHandle; // send request Status = SubmitRequestToRootHub(RootHubDeviceObject, IOCTL_INTERNAL_USB_SUBMIT_URB, Urb, NULL); if (!NT_SUCCESS(Status)) { // failed to get hub status DPRINT1("Failed to get Hub Status!\n"); ExFreePool(Urb); return STATUS_UNSUCCESSFUL; } // Allocate memory for PortStatusChange to hold 2 USHORTs for each port on hub HubDeviceExtension->PortStatusChange = ExAllocatePoolWithTag(NonPagedPool, sizeof(ULONG) * HubDeviceExtension->UsbExtHubInfo.NumberOfPorts, USB_HUB_TAG); // Get the first Configuration Descriptor Pid = USBD_ParseConfigurationDescriptorEx(&HubDeviceExtension->HubConfigDescriptor, &HubDeviceExtension->HubConfigDescriptor, -1, -1, -1, -1, -1); if (Pid == NULL) { // failed parse hub descriptor DPRINT1("Failed to parse configuration descriptor\n"); ExFreePool(Urb); return STATUS_UNSUCCESSFUL; } // create configuration request InterfaceList[0].InterfaceDescriptor = Pid; ConfigUrb = USBD_CreateConfigurationRequestEx(&HubDeviceExtension->HubConfigDescriptor, (PUSBD_INTERFACE_LIST_ENTRY)&InterfaceList); if (ConfigUrb == NULL) { // failed to build urb DPRINT1("Failed to allocate urb\n"); ExFreePool(Urb); return STATUS_INSUFFICIENT_RESOURCES; } // send request Status = SubmitRequestToRootHub(RootHubDeviceObject, IOCTL_INTERNAL_USB_SUBMIT_URB, ConfigUrb, NULL); if (!NT_SUCCESS(Status)) { // failed to select configuration DPRINT1("Failed to select configuration with %x\n", Status); ExFreePool(Urb); ExFreePool(ConfigUrb); return Status; } // store configuration & pipe handle HubDeviceExtension->ConfigurationHandle = ConfigUrb->UrbSelectConfiguration.ConfigurationHandle; HubDeviceExtension->PipeHandle = ConfigUrb->UrbSelectConfiguration.Interface.Pipes[0].PipeHandle; DPRINT("Configuration Handle %x\n", HubDeviceExtension->ConfigurationHandle); FDO_QueryInterface(DeviceObject, &HubDeviceExtension->DeviceInterface); // free urb ExFreePool(ConfigUrb); // check if function is available if (HubDeviceExtension->UsbDInterface.IsDeviceHighSpeed) { // is it high speed bus if (HubDeviceExtension->UsbDInterface.IsDeviceHighSpeed(HubInterfaceBusContext)) { // initialize usb 2.0 hub Status = HubDeviceExtension->HubInterface.Initialize20Hub(HubInterfaceBusContext, HubDeviceExtension->RootHubHandle, 1); DPRINT("Status %x\n", Status); // FIXME handle error ASSERT(Status == STATUS_SUCCESS); } } // Enable power on all ports DPRINT("Enabling PortPower on all ports!\n"); for (PortId = 1; PortId <= HubDeviceExtension->HubDescriptor.bNumberOfPorts; PortId++) { Status = SetPortFeature(RootHubDeviceObject, PortId, PORT_POWER); if (!NT_SUCCESS(Status)) DPRINT1("Failed to power on port %d\n", PortId); Status = ClearPortFeature(RootHubDeviceObject, PortId, C_PORT_CONNECTION); if (!NT_SUCCESS(Status)) DPRINT1("Failed to power on port %d\n", PortId); } // init root hub notification if (HubDeviceExtension->HubInterface.RootHubInitNotification) { Status = HubDeviceExtension->HubInterface.RootHubInitNotification(HubInterfaceBusContext, DeviceObject, RootHubInitCallbackFunction); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to set callback\n"); ExFreePool(Urb); return Status; } } else { // Send the first SCE Request QueryStatusChangeEndpoint(DeviceObject); // // reset ports // for (PortId = 1; PortId <= HubDeviceExtension->HubDescriptor.bNumberOfPorts; PortId++) { // // get port status // Status = GetPortStatusAndChange(HubDeviceExtension->RootHubPhysicalDeviceObject, PortId, &StatusChange); if (NT_SUCCESS(Status)) { // // is there a device connected // if (StatusChange.Status & USB_PORT_STATUS_CONNECT) { // // reset port // Status = SetPortFeature(HubDeviceExtension->RootHubPhysicalDeviceObject, PortId, PORT_RESET); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to reset on port %d\n", PortId); } else { // // wait for the reset to be handled since we want to enumerate synchronously // KeWaitForSingleObject(&HubDeviceExtension->ResetComplete, Executive, KernelMode, FALSE, NULL); KeClearEvent(&HubDeviceExtension->ResetComplete); } } } } } // free urb ExFreePool(Urb); // done return Status; } NTSTATUS USBHUB_FdoHandlePnp( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PIO_STACK_LOCATION Stack; NTSTATUS Status = STATUS_SUCCESS; ULONG_PTR Information = 0; PHUB_DEVICE_EXTENSION HubDeviceExtension; HubDeviceExtension = (PHUB_DEVICE_EXTENSION) DeviceObject->DeviceExtension; Stack = IoGetCurrentIrpStackLocation(Irp); switch (Stack->MinorFunction) { case IRP_MN_START_DEVICE: { if (USBHUB_IsRootHubFDO(DeviceObject)) { // start root hub fdo Status = USBHUB_FdoStartDevice(DeviceObject, Irp); } else { Status = USBHUB_ParentFDOStartDevice(DeviceObject, Irp); } break; } case IRP_MN_QUERY_DEVICE_RELATIONS: { switch (Stack->Parameters.QueryDeviceRelations.Type) { case BusRelations: { PDEVICE_RELATIONS DeviceRelations = NULL; DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations\n"); Status = USBHUB_FdoQueryBusRelations(DeviceObject, &DeviceRelations); Information = (ULONG_PTR)DeviceRelations; break; } case RemovalRelations: { DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations\n"); return ForwardIrpAndForget(DeviceObject, Irp); } default: DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / Unknown type 0x%lx\n", Stack->Parameters.QueryDeviceRelations.Type); return ForwardIrpAndForget(DeviceObject, Irp); } break; } case IRP_MN_QUERY_REMOVE_DEVICE: case IRP_MN_QUERY_STOP_DEVICE: { Irp->IoStatus.Status = STATUS_SUCCESS; return ForwardIrpAndForget(DeviceObject, Irp); } case IRP_MN_REMOVE_DEVICE: { Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); IoDetachDevice(HubDeviceExtension->LowerDeviceObject); IoDeleteDevice(DeviceObject); return STATUS_SUCCESS; } case IRP_MN_QUERY_BUS_INFORMATION: { DPRINT("IRP_MN_QUERY_BUS_INFORMATION\n"); break; } case IRP_MN_QUERY_ID: { DPRINT("IRP_MN_QUERY_ID\n"); break; } case IRP_MN_QUERY_CAPABILITIES: { DPRINT("IRP_MN_QUERY_CAPABILITIES\n"); break; } default: { DPRINT(" IRP_MJ_PNP / unknown minor function 0x%lx\n", Stack->MinorFunction); return ForwardIrpAndForget(DeviceObject, Irp); } } Irp->IoStatus.Information = Information; Irp->IoStatus.Status = Status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return Status; } NTSTATUS USBHUB_FdoHandleDeviceControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PIO_STACK_LOCATION IoStack; NTSTATUS Status = STATUS_NOT_IMPLEMENTED; PUSB_NODE_INFORMATION NodeInformation; PHUB_DEVICE_EXTENSION HubDeviceExtension; PUSB_NODE_CONNECTION_INFORMATION NodeConnectionInfo; PHUB_CHILDDEVICE_EXTENSION ChildDeviceExtension; PUSB_NODE_CONNECTION_DRIVERKEY_NAME NodeKey; PUSB_NODE_CONNECTION_NAME ConnectionName; ULONG Index, Length; // get stack location IoStack = IoGetCurrentIrpStackLocation(Irp); // get device extension HubDeviceExtension = (PHUB_DEVICE_EXTENSION) DeviceObject->DeviceExtension; if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_USB_GET_NODE_INFORMATION) { // is the buffer big enough if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(USB_NODE_INFORMATION)) { // buffer too small Status = STATUS_BUFFER_TOO_SMALL; } else { // get buffer NodeInformation = (PUSB_NODE_INFORMATION)Irp->AssociatedIrp.SystemBuffer; // sanity check ASSERT(NodeInformation); // init buffer NodeInformation->NodeType = UsbHub; RtlCopyMemory(&NodeInformation->u.HubInformation.HubDescriptor, &HubDeviceExtension->HubDescriptor, sizeof(USB_HUB_DESCRIPTOR)); // FIXME is hub powered NodeInformation->u.HubInformation.HubIsBusPowered = TRUE; // done Irp->IoStatus.Information = sizeof(USB_NODE_INFORMATION); Status = STATUS_SUCCESS; } } else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_USB_GET_NODE_CONNECTION_INFORMATION) { if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(USB_NODE_CONNECTION_INFORMATION)) { // buffer too small Status = STATUS_BUFFER_TOO_SMALL; } else { // get node connection info NodeConnectionInfo = (PUSB_NODE_CONNECTION_INFORMATION)Irp->AssociatedIrp.SystemBuffer; // sanity checks ASSERT(NodeConnectionInfo); for(Index = 0; Index < USB_MAXCHILDREN; Index++) { if (HubDeviceExtension->ChildDeviceObject[Index] == NULL) continue; // get child device extension ChildDeviceExtension = (PHUB_CHILDDEVICE_EXTENSION)HubDeviceExtension->ChildDeviceObject[Index]->DeviceExtension; if (ChildDeviceExtension->PortNumber != NodeConnectionInfo->ConnectionIndex) continue; // init node connection info RtlCopyMemory(&NodeConnectionInfo->DeviceDescriptor, &ChildDeviceExtension->DeviceDesc, sizeof(USB_DEVICE_DESCRIPTOR)); NodeConnectionInfo->CurrentConfigurationValue = ChildDeviceExtension->FullConfigDesc->bConfigurationValue; NodeConnectionInfo->DeviceIsHub = FALSE; //FIXME support hubs NodeConnectionInfo->LowSpeed = ChildDeviceExtension->DeviceInformation.DeviceSpeed == UsbLowSpeed; NodeConnectionInfo->DeviceAddress = ChildDeviceExtension->DeviceInformation.DeviceAddress; NodeConnectionInfo->NumberOfOpenPipes = ChildDeviceExtension->DeviceInformation.NumberOfOpenPipes; NodeConnectionInfo->ConnectionStatus = DeviceConnected; //FIXME if (NodeConnectionInfo->NumberOfOpenPipes) { DPRINT1("Need to copy pipe information\n"); } break; } // done Irp->IoStatus.Information = sizeof(USB_NODE_INFORMATION); Status = STATUS_SUCCESS; } } else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME) { if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(USB_NODE_CONNECTION_INFORMATION)) { // buffer too small Status = STATUS_BUFFER_TOO_SMALL; } else { // get node connection info NodeKey = (PUSB_NODE_CONNECTION_DRIVERKEY_NAME)Irp->AssociatedIrp.SystemBuffer; // sanity checks ASSERT(NodeKey); for(Index = 0; Index < USB_MAXCHILDREN; Index++) { if (HubDeviceExtension->ChildDeviceObject[Index] == NULL) continue; // get child device extension ChildDeviceExtension = (PHUB_CHILDDEVICE_EXTENSION)HubDeviceExtension->ChildDeviceObject[Index]->DeviceExtension; if (ChildDeviceExtension->PortNumber != NodeKey->ConnectionIndex) continue; // get driver key Status = IoGetDeviceProperty(HubDeviceExtension->ChildDeviceObject[Index], DevicePropertyDriverKeyName, IoStack->Parameters.DeviceIoControl.OutputBufferLength - sizeof(USB_NODE_CONNECTION_DRIVERKEY_NAME), NodeKey->DriverKeyName, &Length); if (Status == STATUS_BUFFER_TOO_SMALL) { // normalize status Status = STATUS_SUCCESS; } if (Length + sizeof(USB_NODE_CONNECTION_DRIVERKEY_NAME) > IoStack->Parameters.DeviceIoControl.OutputBufferLength) { // terminate node key name NodeKey->DriverKeyName[0] = UNICODE_NULL; Irp->IoStatus.Information = sizeof(USB_NODE_CONNECTION_DRIVERKEY_NAME); } else { // result size Irp->IoStatus.Information = Length + sizeof(USB_NODE_CONNECTION_DRIVERKEY_NAME); } // length of driver name NodeKey->ActualLength = Length + sizeof(USB_NODE_CONNECTION_DRIVERKEY_NAME); break; } } } else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_USB_GET_NODE_CONNECTION_NAME) { if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(USB_NODE_CONNECTION_NAME)) { // buffer too small Status = STATUS_BUFFER_TOO_SMALL; } else { // FIXME support hubs ConnectionName = (PUSB_NODE_CONNECTION_NAME)Irp->AssociatedIrp.SystemBuffer; ConnectionName->ActualLength = 0; ConnectionName->NodeName[0] = UNICODE_NULL; // done Irp->IoStatus.Information = sizeof(USB_NODE_CONNECTION_NAME); Status = STATUS_SUCCESS; } } else { DPRINT1("UNIMPLEMENTED FdoHandleDeviceControl IoCtl %x InputBufferLength %x OutputBufferLength %x\n", IoStack->Parameters.DeviceIoControl.IoControlCode, IoStack->Parameters.DeviceIoControl.InputBufferLength, IoStack->Parameters.DeviceIoControl.OutputBufferLength); } // finish irp Irp->IoStatus.Status = Status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return Status; }