[USBHUB]
[reactos.git] / reactos / drivers / usb / usbhub / fdo.c
index b283299..ff56169 100644 (file)
-/*
- * COPYRIGHT:       See COPYING in the top level directory
- * PROJECT:         USB hub driver
- * FILE:            drivers/usb/cromwell/hub/fdo.c
- * PURPOSE:         IRP_MJ_PNP operations for FDOs
- *
- * PROGRAMMERS:     Herv� Poussineau (hpoussin@reactos.com)
+/*
+ * 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)
  */
 
 #define INITGUID
-#include <stdio.h>
-#define NDEBUG
 #include "usbhub.h"
-#include "usbdlib.h"
 
-#define IO_METHOD_FROM_CTL_CODE(ctlCode) (ctlCode&0x00000003)
+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);
+
 
-VOID DumpDeviceDescriptor(PUSB_DEVICE_DESCRIPTOR DeviceDescriptor)
+NTSTATUS
+GetPortStatusAndChange(
+    IN PDEVICE_OBJECT RootHubDeviceObject,
+    IN ULONG PortId,
+    OUT PPORT_STATUS_CHANGE StatusChange)
 {
-    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);
+    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;
 }
 
-VOID DumpFullConfigurationDescriptor(PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor)
+NTSTATUS
+SetPortFeature(
+    IN PDEVICE_OBJECT RootHubDeviceObject,
+    IN ULONG PortId,
+    IN ULONG Feature)
 {
-    PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor;
-    PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor;
-    LONG i, j;
+    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;
+    }
 
-    DPRINT1("Duming 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);
+    //
+    // 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);
 
-    InterfaceDescriptor = (PUSB_INTERFACE_DESCRIPTOR) ((ULONG_PTR)ConfigurationDescriptor + sizeof(USB_CONFIGURATION_DESCRIPTOR));
+    return Status;
+}
 
-    for (i=0; i < ConfigurationDescriptor->bNumInterfaces; i++)
+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++)
     {
-        DPRINT1("bLength %x\n", InterfaceDescriptor->bLength);
-        DPRINT1("bDescriptorType %x\n", InterfaceDescriptor->bDescriptorType);
-        DPRINT1("bInterfaceNumber %x\n", InterfaceDescriptor->bInterfaceNumber);
-        DPRINT1("bAlternateSetting %x\n", InterfaceDescriptor->bAlternateSetting);
-        DPRINT1("bNumEndpoints %x\n", InterfaceDescriptor->bNumEndpoints);
-        DPRINT1("bInterfaceClass %x\n", InterfaceDescriptor->bInterfaceClass);
-        DPRINT1("bInterfaceSubClass %x\n", InterfaceDescriptor->bInterfaceSubClass);
-        DPRINT1("bInterfaceProtocol %x\n", InterfaceDescriptor->bInterfaceProtocol);
-        DPRINT1("iInterface %x\n", InterfaceDescriptor->iInterface);
+        //
+        // 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;
+        }
 
-        EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR) ((ULONG_PTR)InterfaceDescriptor + sizeof(USB_INTERFACE_DESCRIPTOR));
+        DPRINT1("Port %d Status %x\n", PortId, PortStatus.Status);
+        DPRINT1("Port %d Change %x\n", PortId, PortStatus.Change);
 
-        for (j=0; j < InterfaceDescriptor->bNumEndpoints; j++)
+
+        //
+        // Check for new device connection
+        //
+        if (PortStatus.Change & USB_PORT_STATUS_CONNECT)
         {
-            DPRINT1("bLength %x\n", EndpointDescriptor->bLength);
-            DPRINT1("bDescriptorType %x\n", EndpointDescriptor->bDescriptorType);
-            DPRINT1("bEndpointAddress %x\n", EndpointDescriptor->bEndpointAddress);
-            DPRINT1("bmAttributes %x\n", EndpointDescriptor->bmAttributes);
-            DPRINT1("wMaxPacketSize %x\n", EndpointDescriptor->wMaxPacketSize);
-            DPRINT1("bInterval %x\n", EndpointDescriptor->bInterval);
+            //
+            // 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;
+            }
+
+            DPRINT1("Port %d Status %x\n", PortId, PortStatus.Status);
+            DPRINT1("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;
+            }
 
-        InterfaceDescriptor += sizeof(USB_ENDPOINT_DESCRIPTOR);
+            //
+            // 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
-DeviceArrivalCompletion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context)
+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;
 
-    PHUB_DEVICE_EXTENSION DeviceExtension;
-    LONG i;
-
-    DeviceExtension = (PHUB_DEVICE_EXTENSION)((PDEVICE_OBJECT)Context)->DeviceExtension;
+    DPRINT("Queuing work item\n");
 
-    /* FIXME: Need to use the number of ports returned from miniport at device start */
-    for (i=0; i < 8; i++)
-        DPRINT1("Port %x DeviceExtension->PortStatus %x\n",i, DeviceExtension->PortStatus[i]);
+    //
+    // Queue the work item to handle initializing the device
+    //
+    ExInitializeWorkItem(&WorkItemData->WorkItem, DeviceStatusChangeThread, (PVOID)WorkItemData);
+    ExQueueWorkItem(&WorkItemData->WorkItem, DelayedWorkQueue);
 
-    return STATUS_SUCCESS;
+    //
+    // Return more processing required so the IO Manger doesn’t try to mess with IRP just freed
+    //
+    return STATUS_MORE_PROCESSING_REQUIRED;
 }
 
-
 NTSTATUS
-WaitForUsbDeviceArrivalNotification(PDEVICE_OBJECT DeviceObject)
+QueryStatusChangeEndpoint(
+    IN PDEVICE_OBJECT DeviceObject)
 {
-    PURB Urb;
-    PIRP Irp;
     NTSTATUS Status;
-    PIO_STACK_LOCATION Stack = NULL;
-    IO_STATUS_BLOCK IoStatus;
-    PHUB_DEVICE_EXTENSION DeviceExtension;
+    PDEVICE_OBJECT RootHubDeviceObject;
+    PIO_STACK_LOCATION Stack;
+    PHUB_DEVICE_EXTENSION HubDeviceExtension;
+    PURB PendingSCEUrb;
 
-    DeviceExtension = (PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+    HubDeviceExtension = (PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+    RootHubDeviceObject = HubDeviceExtension->RootHubPhysicalDeviceObject;
 
-    Urb = ExAllocatePoolWithTag(NonPagedPool, sizeof(URB), USB_HUB_TAG);
+    //
+    // Allocate a URB
+    //
+    PendingSCEUrb = ExAllocatePoolWithTag(NonPagedPool, sizeof(URB), USB_HUB_TAG);
 
-    /* Send URB to the miniports Status Change Endpoint SCE */
-    UsbBuildInterruptOrBulkTransferRequest(Urb,
+    //
+    // Initialize URB for Status Change Endpoint request
+    //
+    UsbBuildInterruptOrBulkTransferRequest(PendingSCEUrb,
                                            sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
-                                           DeviceExtension->PipeHandle,
-                                           &DeviceExtension->PortStatus,
+                                           HubDeviceExtension->PipeHandle,
+                                           HubDeviceExtension->PortStatusChange,
                                            NULL,
-                                           sizeof(ULONG) * 2,
+                                           sizeof(USHORT) * 2 * HubDeviceExtension->UsbExtHubInfo.NumberOfPorts,
                                            USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK,
                                            NULL);
 
-    Urb->UrbHeader.UsbdDeviceHandle = DeviceExtension->RootHubUsbDevice;
+    // Set the device handle
+    PendingSCEUrb->UrbHeader.UsbdDeviceHandle = HubDeviceExtension->RootHubHandle;
 
-    Irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_USB_SUBMIT_URB,
-                                        DeviceExtension->RootHubPdo,
-                                        NULL, 0,
-                                        NULL, 0,
-                                        TRUE,
-                                        NULL,
-                                        &IoStatus);
+    //
+    // 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 (Irp == NULL)
+    if (!HubDeviceExtension->PendingSCEIrp)
     {
-        DPRINT("Usbhub: IoBuildDeviceIoControlRequest() failed\n");
+        DPRINT1("USBHUB: Failed to allocate IRP for SCE request!\n");
         return STATUS_INSUFFICIENT_RESOURCES;
     }
 
-    /* Initialize the status block before sending the IRP */
-    IoStatus.Status = STATUS_NOT_SUPPORTED;
-    IoStatus.Information = 0;
+    //
+    // 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);
+    Status = 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;
 
-    Stack->Parameters.Others.Argument1 = Urb;
-    Stack->Parameters.Others.Argument2 = NULL;
+    Status = IoCallDriver(DeviceObject, Irp);
 
-    IoSetCompletionRoutineEx(DeviceExtension->RootHubPdo, Irp, (PIO_COMPLETION_ROUTINE)DeviceArrivalCompletion, DeviceObject, TRUE, TRUE, TRUE);
+    if (Status == STATUS_PENDING)
+    {
+        DPRINT("Operation pending\n");
+        KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
+        Status = IoStatus.Status;
+    }
 
-    Status = IoCallDriver(DeviceExtension->RootHubPdo, Irp);
+    return Status;
+}
 
-    return STATUS_MORE_PROCESSING_REQUIRED;
+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
-QueryRootHub(IN PDEVICE_OBJECT Pdo, IN ULONG IoControlCode, OUT PVOID OutParameter1, OUT PVOID OutParameter2)
+GetUsbStringDescriptor(
+    IN PDEVICE_OBJECT ChildDeviceObject,
+    IN UCHAR Index,
+    IN USHORT LangId,
+    OUT PVOID *TransferBuffer,
+    OUT USHORT *Size)
 {
-    KEVENT Event;
-    PIRP Irp;
-    IO_STATUS_BLOCK IoStatus;
     NTSTATUS Status;
-    PIO_STACK_LOCATION Stack = NULL;
+    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;
+    }
 
-    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+    //
+    // 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);
 
-    Irp = IoBuildDeviceIoControlRequest(IoControlCode,
-                                        Pdo,
-                                        NULL, 0,
-                                        NULL, 0,
-                                        TRUE,
-                                        &Event,
-                                        &IoStatus);
+    //
+    // 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;
+    }
 
-    if (Irp == NULL)
+    //
+    // Allocate Buffer to return
+    //
+    Buffer = ExAllocatePoolWithTag(NonPagedPool,
+                                   SizeNeeded,
+                                   USB_HUB_TAG);
+    if (!Buffer)
     {
-        DPRINT("Usbhub: IoBuildDeviceIoControlRequest() failed\n");
+        DPRINT1("Failed to allocate buffer for string!\n");
+        ExFreePool(StringDesc);
         return STATUS_INSUFFICIENT_RESOURCES;
     }
 
-    /* Initialize the status block before sending the IRP */
-    IoStatus.Status = STATUS_NOT_SUPPORTED;
-    IoStatus.Information = 0;
+    RtlZeroMemory(Buffer, SizeNeeded);
 
-    Stack = IoGetNextIrpStackLocation(Irp);
+    //
+    // Copy the string to destination
+    //
+    RtlCopyMemory(Buffer, StringDesc->bString, SizeNeeded - FIELD_OFFSET(USB_STRING_DESCRIPTOR, bString));
+    *Size = SizeNeeded;
+    *TransferBuffer = Buffer;
 
-    Stack->Parameters.Others.Argument1 = OutParameter1;
-    Stack->Parameters.Others.Argument2 = OutParameter2;
+    ExFreePool(StringDesc);
 
-    Status = IoCallDriver(Pdo, Irp);
+    return STATUS_SUCCESS;
+}
 
-    if (Status == STATUS_PENDING)
+ULONG
+IsCompositeDevice(
+    IN PUSB_DEVICE_DESCRIPTOR DeviceDescriptor,
+    IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor)
+{
+    if (DeviceDescriptor->bNumConfigurations != 1)
     {
-        DPRINT("Usbhub: Operation pending\n");
-        KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
-        Status = IoStatus.Status;
+         //
+         // composite device must have only one configuration
+         //
+         DPRINT1("IsCompositeDevice bNumConfigurations %x\n", DeviceDescriptor->bNumConfigurations);
+         return FALSE;
     }
 
-    return Status;
+    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 QueryInterface(IN PDEVICE_OBJECT Pdo, IN CONST GUID InterfaceType, IN LONG Size, IN LONG Version, OUT PVOID Interface)
+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);
+    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))
+        {
+            DPRINT1("USBHUB: GetUsbStringDescriptor failed with status %x\n", Status);
+            return 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));
+            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);
+        ExFreePool(SerialBuffer);
+
+        DPRINT("Usb InstanceId %wZ InstanceCount %x\n", &UsbChildExtension->usInstanceId, HubDeviceExtension->InstanceCount);
+    }
+    else
+    {
+       //
+       // the device did not provide a 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)
 {
-    KEVENT Event;
-    PIRP Irp;
-    IO_STATUS_BLOCK IoStatus;
     NTSTATUS Status;
-    PIO_STACK_LOCATION Stack = NULL;
+    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");
+    }
 
-    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+    if (ConfigDesc.bNumInterfaces > 1)
+    {
+        DPRINT1("Warning: Device has more that one interface. Only one interface (the first) is currently supported\n");
+    }
 
-    Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
-                                       Pdo,
-                                       NULL,
-                                       0,
-                                       NULL,
-                                       &Event,
-                                       &IoStatus);
+    ConfigDescSize = ConfigDesc.wTotalLength;
 
-    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;
+    //
+    // Allocate memory for the first full descriptor, including interfaces and endpoints.
+    //
+    UsbChildExtension->FullConfigDesc = ExAllocatePoolWithTag(PagedPool, ConfigDescSize, USB_HUB_TAG);
 
-    Status = IoCallDriver(Pdo, Irp);
+    //
+    // Retrieve the full configuration descriptor
+    //
+    Status = GetUsbDeviceDescriptor(NewChildDeviceObject,
+                                    USB_CONFIGURATION_DESCRIPTOR_TYPE,
+                                    0,
+                                    0,
+                                    UsbChildExtension->FullConfigDesc,
+                                    ConfigDescSize);
 
-    if (Status == STATUS_PENDING)
+    if (!NT_SUCCESS(Status))
     {
-        DPRINT("Usbhub: Operation pending\n");
-        KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
-        Status = IoStatus.Status;
+        DPRINT1("USBHUB: GetUsbDeviceDescriptor failed with status %x\n", Status);
+        goto Cleanup;
     }
 
-    return Status;
-}
+    // query device details
+    Status = HubInterface->QueryDeviceInformation(HubInterfaceBusContext, 
+                                         UsbChildExtension->UsbDeviceHandle,
+                                         &UsbChildExtension->DeviceInformation,
+                                         sizeof(USB_DEVICE_INFORMATION_0),
+                                         &DeviceInfoSize);
 
-static VOID
-UsbhubGetUserBuffers(IN PIRP Irp, IN ULONG IoControlCode, OUT PVOID* BufferIn, OUT PVOID* BufferOut)
-{
-    ASSERT(Irp);
-    ASSERT(BufferIn);
-    ASSERT(BufferOut);
 
-    switch (IO_METHOD_FROM_CTL_CODE(IoControlCode))
+    //DumpFullConfigurationDescriptor(UsbChildExtension->FullConfigDesc);
+
+    //
+    // Construct all the strings that will described the device to PNP
+    //
+    Status = CreateDeviceIds(NewChildDeviceObject);
+    if (!NT_SUCCESS(Status))
     {
-        case METHOD_BUFFERED:
-            *BufferIn = *BufferOut = Irp->AssociatedIrp.SystemBuffer;
-            break;
-        case METHOD_IN_DIRECT:
-        case METHOD_OUT_DIRECT:
-            *BufferIn = Irp->AssociatedIrp.SystemBuffer;
-            *BufferOut = MmGetSystemAddressForMdl(Irp->MdlAddress);
-            break;
-        case METHOD_NEITHER:
-            *BufferIn = IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl.Type3InputBuffer;
-            *BufferOut = Irp->UserBuffer;
-            break;
-        default:
-            /* Should never happen */
-            *BufferIn = NULL;
-            *BufferOut = NULL;
-            break;
+        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
-UsbhubFdoQueryBusRelations(IN PDEVICE_OBJECT DeviceObject, OUT PDEVICE_RELATIONS* pDeviceRelations)
+USBHUB_FdoQueryBusRelations(
+    IN PDEVICE_OBJECT DeviceObject,
+    OUT PDEVICE_RELATIONS* pDeviceRelations)
 {
-    PHUB_DEVICE_EXTENSION DeviceExtension;
+    PHUB_DEVICE_EXTENSION HubDeviceExtension;
     PDEVICE_RELATIONS DeviceRelations;
-    PDEVICE_OBJECT Pdo;
-    PHUB_DEVICE_EXTENSION PdoExtension;
     ULONG i;
     ULONG Children = 0;
     ULONG NeededSize;
 
-    DeviceExtension = (PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
-    DPRINT1("USBHUB: Query Bus Relations\n");
-    /* Create PDOs that are missing */
+    HubDeviceExtension = (PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+    //
+    // Count the number of children
+    //
     for (i = 0; i < USB_MAXCHILDREN; i++)
     {
 
-        if (DeviceExtension->Children[i] == NULL)
+        if (HubDeviceExtension->ChildDeviceObject[i] == NULL)
         {
             continue;
         }
         Children++;
-        Pdo = DeviceExtension->Children[i];
-        Pdo->Flags |= DO_BUS_ENUMERATED_DEVICE;
-
-        PdoExtension = Pdo->DeviceExtension;
-
-        RtlZeroMemory(PdoExtension, sizeof(HUB_DEVICE_EXTENSION));
-
-        PdoExtension->IsFDO = FALSE;
-
-        Pdo->Flags &= ~DO_DEVICE_INITIALIZING;
     }
 
-    /* Fill returned structure */
     NeededSize = sizeof(DEVICE_RELATIONS);
     if (Children > 1)
         NeededSize += (Children - 1) * sizeof(PDEVICE_OBJECT);
 
+    //
+    // Allocate DeviceRelations
+    //
     DeviceRelations = (PDEVICE_RELATIONS)ExAllocatePool(PagedPool,
                                                         NeededSize);
 
@@ -315,264 +1401,649 @@ UsbhubFdoQueryBusRelations(IN PDEVICE_OBJECT DeviceObject, OUT PDEVICE_RELATIONS
     DeviceRelations->Count = Children;
     Children = 0;
 
+    //
+    // Fill in return structure
+    //
     for (i = 0; i < USB_MAXCHILDREN; i++)
     {
-        if (DeviceExtension->Children[i])
+        if (HubDeviceExtension->ChildDeviceObject[i])
         {
-            ObReferenceObject(DeviceExtension->Children[i]);
-            DeviceRelations->Objects[Children++] = DeviceExtension->Children[i];
+            ObReferenceObject(HubDeviceExtension->ChildDeviceObject[i]);
+            HubDeviceExtension->ChildDeviceObject[i]->Flags &= ~DO_DEVICE_INITIALIZING;
+            DeviceRelations->Objects[Children++] = HubDeviceExtension->ChildDeviceObject[i];
         }
     }
 
     ASSERT(Children == DeviceRelations->Count);
     *pDeviceRelations = DeviceRelations;
 
-    WaitForUsbDeviceArrivalNotification(DeviceObject);
-
     return STATUS_SUCCESS;
 }
 
-NTSTATUS NTAPI
-UsbhubPnpFdo(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
+VOID
+NTAPI
+RootHubInitCallbackFunction(
+    PVOID Context)
 {
-    PIO_STACK_LOCATION IrpSp;
-    NTSTATUS Status = STATUS_SUCCESS;
-    ULONG MinorFunction;
-    ULONG_PTR Information = 0;
-    PHUB_DEVICE_EXTENSION DeviceExtension;
+    PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)Context;
+    NTSTATUS Status;
+    ULONG PortId;
+    PHUB_DEVICE_EXTENSION HubDeviceExtension;
+    PORT_STATUS_CHANGE StatusChange;
+
+    HubDeviceExtension = (PHUB_DEVICE_EXTENSION) DeviceObject->DeviceExtension;
 
-    IrpSp = IoGetCurrentIrpStackLocation(Irp);
-    MinorFunction = IrpSp->MinorFunction;
+    DPRINT("RootHubInitCallbackFunction Sending the initial SCE Request %x\n", DeviceObject);
 
-    DeviceExtension = (PHUB_DEVICE_EXTENSION) DeviceObject->DeviceExtension;
+    //
+    // Send the first SCE Request
+    //
+    QueryStatusChangeEndpoint(DeviceObject);
 
-    switch (MinorFunction)
+    for (PortId = 1; PortId <= HubDeviceExtension->HubDescriptor.bNumberOfPorts; PortId++)
     {
-        case IRP_MN_START_DEVICE: /* 0x0 */
+        //
+        // get port status
+        //
+        Status = GetPortStatusAndChange(HubDeviceExtension->RootHubPhysicalDeviceObject, PortId, &StatusChange);
+        if (NT_SUCCESS(Status))
         {
-            PURB Urb;
-            ULONG Result = 0;
+            //
+            // 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);
+                }
+            }
+        }
+    }
+}
 
-            /* We differ from windows on hubpdo because we dont have usbport.sys which manages all usb device objects */
-            DPRINT1("Usbhub: IRP_MJ_PNP / IRP_MN_START_DEVICE\n");
+BOOLEAN
+USBHUB_IsRootHubFDO(
+   IN PDEVICE_OBJECT DeviceObject)
+{
+    NTSTATUS Status;
+    PDEVICE_OBJECT RootHubPhysicalDeviceObject = NULL;
+    PHUB_DEVICE_EXTENSION HubDeviceExtension;
 
-            /* Allocating size including the sizeof USBD_INTERFACE_LIST_ENTRY */
-            Urb = ExAllocatePoolWithTag(NonPagedPool, sizeof(URB) + sizeof(USBD_INTERFACE_LIST_ENTRY), USB_HUB_TAG);
-            RtlZeroMemory(Urb, sizeof(URB) + sizeof(USBD_INTERFACE_LIST_ENTRY));
+    // get hub device extension
+    HubDeviceExtension = (PHUB_DEVICE_EXTENSION) DeviceObject->DeviceExtension;
 
-            /* Get the hubs PDO */
-            QueryRootHub(DeviceObject, IOCTL_INTERNAL_USB_GET_ROOTHUB_PDO, &DeviceExtension->RootHubPdo, &DeviceExtension->RootHubFdo);
-            ASSERT(DeviceExtension->RootHubPdo);
-            ASSERT(DeviceExtension->RootHubFdo);
+    // Get the Root Hub Pdo
+    Status = SubmitRequestToRootHub(HubDeviceExtension->LowerDeviceObject,
+                                    IOCTL_INTERNAL_USB_GET_ROOTHUB_PDO,
+                                    &RootHubPhysicalDeviceObject,
+                                    NULL);
 
-            /* Send the START_DEVICE irp down to the PDO of RootHub */
-            Status = ForwardIrpAndWait(DeviceExtension->RootHubPdo, Irp);
+    // FIXME handle error
+    ASSERT(NT_SUCCESS(Status));
 
-            if (!NT_SUCCESS(Status))
-            {
-                DPRINT1("Failed to start the RootHub PDO\n");
-                ASSERT(FALSE);
-            }
+    // physical device object is only obtained for root hubs
+    return (RootHubPhysicalDeviceObject != NULL);
+}
 
-            /* Get the current number of hubs */
-            QueryRootHub(DeviceExtension->RootHubPdo,IOCTL_INTERNAL_USB_GET_HUB_COUNT, &DeviceExtension->HubCount, NULL);
 
-            /* Get the Direct Call Interfaces */
-            Status = QueryInterface(DeviceExtension->RootHubPdo, 
-                                    USB_BUS_INTERFACE_HUB_GUID,
-                                    sizeof(USB_BUS_INTERFACE_HUB_V5),
-                                    5,
-                                    (PVOID)&DeviceExtension->HubInterface);
-            if (!NT_SUCCESS(Status))
-            {
-                DPRINT1("UsbhubM Failed to get HUB_GUID interface with status 0x%08lx\n", Status);
-                return STATUS_UNSUCCESSFUL;
-            }
+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;
+    PIO_STACK_LOCATION Stack;
+    NTSTATUS Status = STATUS_SUCCESS;
+    PHUB_DEVICE_EXTENSION HubDeviceExtension;
+    PDEVICE_OBJECT RootHubDeviceObject;
+    PVOID HubInterfaceBusContext , UsbDInterfaceBusContext;
+    PORT_STATUS_CHANGE StatusChange;
 
-            Status = QueryInterface(DeviceExtension->RootHubPdo,
-                                    USB_BUS_INTERFACE_USBDI_GUID,
-                                    sizeof(USB_BUS_INTERFACE_USBDI_V2),
-                                    2,
-                                    (PVOID)&DeviceExtension->UsbDInterface);
-            if (!NT_SUCCESS(Status))
-            {
-                DPRINT1("UsbhubM Failed to get USBDI_GUID interface with status 0x%08lx\n", Status);
-                return STATUS_UNSUCCESSFUL;
-            }
+    // get current stack location
+    Stack = IoGetCurrentIrpStackLocation(Irp);
 
-            /* Get roothub device handle */
-            Status = QueryRootHub(DeviceExtension->RootHubPdo, IOCTL_INTERNAL_USB_GET_DEVICE_HANDLE, &DeviceExtension->RootHubUsbDevice, NULL);
-            if (!NT_SUCCESS(Status))
-            {
-                DPRINT1("Usbhub: GetRootHubDeviceHandle failed with status 0x%08lx\n", Status);
-                return Status;
-            }
+    // get hub device extension
+    HubDeviceExtension = (PHUB_DEVICE_EXTENSION) DeviceObject->DeviceExtension;
 
-            /* FIXME: This gets nothing from MS miniport */
-            Status = DeviceExtension->HubInterface.QueryDeviceInformation(DeviceExtension->RootHubPdo,
-                                                                 DeviceExtension->RootHubUsbDevice,
-                                                                 &DeviceExtension->DeviceInformation,
-                                                                 sizeof(USB_DEVICE_INFORMATION_0),
-                                                                 &Result);
-
-
-            DPRINT1("Status %x, Result %x\n", Status, Result);
-            DPRINT1("InformationLevel %x\n", DeviceExtension->DeviceInformation.InformationLevel);
-            DPRINT1("ActualLength %x\n", DeviceExtension->DeviceInformation.ActualLength);
-            DPRINT1("PortNumber %x\n", DeviceExtension->DeviceInformation.PortNumber);
-            DPRINT1("DeviceDescriptor %x\n", DeviceExtension->DeviceInformation.DeviceDescriptor);
-            DPRINT1("HubAddress %x\n", DeviceExtension->DeviceInformation.HubAddress);
-            DPRINT1("NumberofPipes %x\n", DeviceExtension->DeviceInformation.NumberOfOpenPipes);
-
-            /* Get roothubs device descriptor */
-            UsbBuildGetDescriptorRequest(Urb,
-                                         sizeof(Urb->UrbControlDescriptorRequest),
-                                         USB_DEVICE_DESCRIPTOR_TYPE,
-                                         0,
-                                         0,
-                                         &DeviceExtension->HubDeviceDescriptor,
-                                         NULL,
-                                         sizeof(USB_DEVICE_DESCRIPTOR),
-                                         NULL);
-
-            Urb->UrbHeader.UsbdDeviceHandle = DeviceExtension->RootHubUsbDevice;
-
-            Status = QueryRootHub(DeviceExtension->RootHubPdo, IOCTL_INTERNAL_USB_SUBMIT_URB, Urb, NULL);
-            if (!NT_SUCCESS(Status))
-            {
-                DPRINT1("Usbhub: Failed to get HubDeviceDescriptor!\n");
-            }
+    DPRINT("IRP_MJ_PNP / IRP_MN_START_DEVICE\n");
 
-            DumpDeviceDescriptor(&DeviceExtension->HubDeviceDescriptor);
-
-            /* Get roothubs configuration descriptor */
-            UsbBuildGetDescriptorRequest(Urb,
-                                         sizeof(Urb->UrbControlDescriptorRequest),
-                                         USB_CONFIGURATION_DESCRIPTOR_TYPE,
-                                         0,
-                                         0,
-                                         &DeviceExtension->HubConfigDescriptor,
-                                         NULL,
-                                         sizeof(USB_CONFIGURATION_DESCRIPTOR) + sizeof(USB_INTERFACE_DESCRIPTOR) + sizeof(USB_ENDPOINT_DESCRIPTOR),
-                                         NULL);
-            Urb->UrbHeader.UsbdDeviceHandle = DeviceExtension->RootHubUsbDevice;
-
-            Status = QueryRootHub(DeviceExtension->RootHubPdo, IOCTL_INTERNAL_USB_SUBMIT_URB, Urb, NULL);
-            if (!NT_SUCCESS(Status))
-            {
-                DPRINT1("Usbhub: Failed to get RootHub Configuration with status %x\n", Status);
-                ASSERT(FALSE);
-            }
-            ASSERT(DeviceExtension->HubConfigDescriptor.wTotalLength);
+    // 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;
+    }
 
-            DumpFullConfigurationDescriptor(&DeviceExtension->HubConfigDescriptor);
-            //DPRINT1("DeviceExtension->HubConfigDescriptor.wTotalLength %x\n", DeviceExtension->HubConfigDescriptor.wTotalLength);
+    // zero urb
+    RtlZeroMemory(Urb, sizeof(URB) + sizeof(USBD_INTERFACE_LIST_ENTRY));
 
-            Status = DeviceExtension->HubInterface.GetExtendedHubInformation(DeviceExtension->RootHubPdo,
-                                                                    DeviceExtension->RootHubPdo,
-                                                                    &DeviceExtension->UsbExtHubInfo,
-                                                                    sizeof(USB_EXTHUB_INFORMATION_0),
-                                                                    &Result);
-            if (!NT_SUCCESS(Status))
-            {
-                DPRINT1("Usbhub: Failed to extended hub information. Unable to determine the number of ports!\n");
-                ASSERT(FALSE);
-            }
+    // 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;
+    }
+
+    UsbDInterfaceBusContext = HubDeviceExtension->UsbDInterface.BusContext;
+
+    // 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;
+    }
 
-            DPRINT1("DeviceExtension->UsbExtHubInfo.NumberOfPorts %x\n", DeviceExtension->UsbExtHubInfo.NumberOfPorts);
+    // store configuration & pipe handle
+    HubDeviceExtension->ConfigurationHandle = ConfigUrb->UrbSelectConfiguration.ConfigurationHandle;
+    HubDeviceExtension->PipeHandle = ConfigUrb->UrbSelectConfiguration.Interface.Pipes[0].PipeHandle;
+    DPRINT("Configuration Handle %x\n", HubDeviceExtension->ConfigurationHandle);
 
-            UsbBuildVendorRequest(Urb,
-                                  URB_FUNCTION_CLASS_DEVICE,
-                                  sizeof(Urb->UrbControlVendorClassRequest),
-                                  USBD_TRANSFER_DIRECTION_IN,
-                                  0,
-                                  USB_DEVICE_CLASS_RESERVED,
-                                  0,
-                                  0,
-                                  &DeviceExtension->HubDescriptor,
-                                  NULL,
-                                  sizeof(USB_HUB_DESCRIPTOR),
-                                  NULL);
+    FDO_QueryInterface(DeviceObject, &HubDeviceExtension->DeviceInterface);
 
-            Urb->UrbHeader.UsbdDeviceHandle = DeviceExtension->RootHubUsbDevice;
 
-            Status = QueryRootHub(DeviceExtension->RootHubPdo, IOCTL_INTERNAL_USB_SUBMIT_URB, Urb, NULL);
+    // free urb
+    ExFreePool(ConfigUrb);
 
-            DPRINT1("bDescriptorType %x\n", DeviceExtension->HubDescriptor.bDescriptorType);
+    // 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);
 
-            /* Select the configuration */
-            /* FIXME: Use USBD_CreateConfigurationRequestEx instead */
-            RtlZeroMemory(Urb, sizeof(URB));
-            UsbBuildSelectConfigurationRequest(Urb,
-                                               sizeof(Urb->UrbSelectConfiguration),
-                                               &DeviceExtension->HubConfigDescriptor);
+        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);
 
-            Urb->UrbSelectConfiguration.Interface.Length = sizeof(USBD_INTERFACE_INFORMATION);
-            Urb->UrbSelectConfiguration.Interface.NumberOfPipes = 1;
-            Urb->UrbSelectConfiguration.Interface.Pipes[0].MaximumTransferSize = 4096;
+        //
+        // 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);
+                    }
+                }
+            }
+        }
+    }
 
-            Status = QueryRootHub(DeviceExtension->RootHubPdo, IOCTL_INTERNAL_USB_SUBMIT_URB, Urb, NULL);
+    // free urb
+    ExFreePool(Urb);
 
-            DPRINT1("Status %x\n", Status);
+    // done
+    return Status;
+}
 
-            DeviceExtension->ConfigurationHandle = Urb->UrbSelectConfiguration.ConfigurationHandle;
-            DeviceExtension->PipeHandle = Urb->UrbSelectConfiguration.Interface.Pipes[0].PipeHandle;
-            DPRINT1("Configuration Handle %x\n", DeviceExtension->ConfigurationHandle);
+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;
 
-            Status = DeviceExtension->HubInterface.Initialize20Hub(DeviceExtension->RootHubPdo, DeviceExtension->RootHubUsbDevice, 1);
+    HubDeviceExtension = (PHUB_DEVICE_EXTENSION) DeviceObject->DeviceExtension;
 
-            DPRINT1("Status %x\n", Status);
+    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: /* (optional) 0x7 */
+        case IRP_MN_QUERY_DEVICE_RELATIONS:
         {
-            switch (IrpSp->Parameters.QueryDeviceRelations.Type)
+            switch (Stack->Parameters.QueryDeviceRelations.Type)
             {
                 case BusRelations:
                 {
                     PDEVICE_RELATIONS DeviceRelations = NULL;
-                    DPRINT1("Usbhub: IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations\n");
+                    DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations\n");
 
-                    Status = UsbhubFdoQueryBusRelations(DeviceObject, &DeviceRelations);
+                    Status = USBHUB_FdoQueryBusRelations(DeviceObject, &DeviceRelations);
 
                     Information = (ULONG_PTR)DeviceRelations;
                     break;
                 }
                 case RemovalRelations:
                 {
-                    DPRINT1("Usbhub: IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations\n");
+                    DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations\n");
                     return ForwardIrpAndForget(DeviceObject, Irp);
                 }
                 default:
-                    DPRINT1("Usbhub: IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / Unknown type 0x%lx\n",
-                        IrpSp->Parameters.QueryDeviceRelations.Type);
+                    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:
         {
-            DPRINT1("IRP_MN_QUERY_BUS_INFORMATION\n");
+            DPRINT("IRP_MN_QUERY_BUS_INFORMATION\n");
             break;
         }
         case IRP_MN_QUERY_ID:
         {
-            DPRINT1("IRP_MN_QUERY_ID\n");
+            DPRINT("IRP_MN_QUERY_ID\n");
             break;
         }
         case IRP_MN_QUERY_CAPABILITIES:
         {
-            DPRINT1("IRP_MN_QUERY_CAPABILITIES\n");
+            DPRINT("IRP_MN_QUERY_CAPABILITIES\n");
             break;
         }
         default:
         {
-            DPRINT1("Usbhub: IRP_MJ_PNP / unknown minor function 0x%lx\n", MinorFunction);
+            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);
@@ -580,172 +2051,187 @@ UsbhubPnpFdo(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
 }
 
 NTSTATUS
-UsbhubDeviceControlFdo(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
+USBHUB_FdoHandleDeviceControl(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp)
 {
-    PIO_STACK_LOCATION Stack;
-    ULONG IoControlCode;
-    PHUB_DEVICE_EXTENSION DeviceExtension;
-    ULONG LengthIn, LengthOut;
-    ULONG_PTR Information = 0;
-    PVOID BufferIn, BufferOut;
-    NTSTATUS Status = STATUS_UNSUCCESSFUL;
+    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;
 
-    Stack = IoGetCurrentIrpStackLocation(Irp);
-    LengthIn = Stack->Parameters.DeviceIoControl.InputBufferLength;
-    LengthOut = Stack->Parameters.DeviceIoControl.OutputBufferLength;
-    DeviceExtension = (PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
-    IoControlCode = Stack->Parameters.DeviceIoControl.IoControlCode;
-    UsbhubGetUserBuffers(Irp, IoControlCode, &BufferIn, &BufferOut);
+            // 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;
+        }
 
-    switch (IoControlCode)
+
+    }
+    else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_USB_GET_NODE_CONNECTION_INFORMATION)
     {
-        case IOCTL_USB_GET_NODE_INFORMATION:
+        if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(USB_NODE_CONNECTION_INFORMATION))
         {
-            //PUSB_NODE_INFORMATION NodeInformation;
-
-            DPRINT1("Usbhub: IOCTL_USB_GET_NODE_INFORMATION\n");
-            if (LengthOut < sizeof(USB_NODE_INFORMATION))
-                Status = STATUS_BUFFER_TOO_SMALL;
-            else if (BufferOut == NULL)
-                Status = STATUS_INVALID_PARAMETER;
-            else
-            {
-                /*NodeInformation = (PUSB_NODE_INFORMATION)BufferOut;
-                dev = ((PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->dev;
-                NodeInformation->NodeType = UsbHub;
-                RtlCopyMemory(
-                    &NodeInformation->u.HubInformation.HubDescriptor,
-                    ((struct usb_hub *)usb_get_intfdata(to_usb_interface(&dev->actconfig->interface[0].dev)))->descriptor,
-                    sizeof(USB_HUB_DESCRIPTOR));
-                NodeInformation->u.HubInformation.HubIsBusPowered = dev->actconfig->desc.bmAttributes & 0x80;
-                Information = sizeof(USB_NODE_INFORMATION);*/
-                Status = STATUS_SUCCESS;
-            }
-            break;
+            // buffer too small
+            Status = STATUS_BUFFER_TOO_SMALL;
         }
-        case IOCTL_USB_GET_NODE_CONNECTION_NAME:
-        {
-            PHUB_DEVICE_EXTENSION DeviceExtension;
-            PUSB_NODE_CONNECTION_NAME ConnectionName;
-            DeviceExtension = (PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
-            ConnectionName = (PUSB_NODE_CONNECTION_NAME)BufferOut;
-
-            DPRINT1("Usbhub: IOCTL_USB_GET_NODE_CONNECTION_NAME\n");
-            if (LengthOut < sizeof(USB_NODE_CONNECTION_NAME))
-                Status = STATUS_BUFFER_TOO_SMALL;
-            else if (BufferOut == NULL)
-                Status = STATUS_INVALID_PARAMETER;
-            else if (ConnectionName->ConnectionIndex < 1
-                || ConnectionName->ConnectionIndex > USB_MAXCHILDREN)
-                Status = STATUS_INVALID_PARAMETER;
-            else if (DeviceExtension->Children[ConnectionName->ConnectionIndex - 1] == NULL)
-                Status = STATUS_INVALID_PARAMETER;
-            else
+        else
+        {
+            // get node connection info
+            NodeConnectionInfo = (PUSB_NODE_CONNECTION_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
+
+            // sanity checks
+            ASSERT(NodeConnectionInfo);
+
+            for(Index = 0; Index < USB_MAXCHILDREN; Index++)
             {
-                ULONG NeededStructureSize;
-                DeviceExtension = (PHUB_DEVICE_EXTENSION)DeviceExtension->Children[ConnectionName->ConnectionIndex - 1]->DeviceExtension;
-                NeededStructureSize = DeviceExtension->SymbolicLinkName.Length + sizeof(UNICODE_NULL) + FIELD_OFFSET(USB_NODE_CONNECTION_NAME, NodeName);
-                if (ConnectionName->ActualLength < NeededStructureSize / sizeof(WCHAR)
-                    || LengthOut < NeededStructureSize)
-                {
-                    /* Buffer too small */
-                    ConnectionName->ActualLength = NeededStructureSize / sizeof(WCHAR);
-                    Information = sizeof(USB_NODE_CONNECTION_NAME);
-                    Status = STATUS_BUFFER_TOO_SMALL;
-                }
-                else
+                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)
                 {
-                    RtlCopyMemory(
-                        ConnectionName->NodeName,
-                        DeviceExtension->SymbolicLinkName.Buffer,
-                        DeviceExtension->SymbolicLinkName.Length);
-                    ConnectionName->NodeName[DeviceExtension->SymbolicLinkName.Length / sizeof(WCHAR)] = UNICODE_NULL;
-                    DPRINT1("Usbhub: IOCTL_USB_GET_NODE_CONNECTION_NAME returns '%S'\n", ConnectionName->NodeName);
-                    ConnectionName->ActualLength = NeededStructureSize / sizeof(WCHAR);
-                    Information = NeededStructureSize;
-                    Status = STATUS_SUCCESS;
+                    DPRINT1("Need to copy pipe information\n");
                 }
-                Information = LengthOut;
+                break;
             }
-            break;
+            // done
+            Irp->IoStatus.Information = sizeof(USB_NODE_INFORMATION);
+            Status = STATUS_SUCCESS;
         }
-        case IOCTL_USB_GET_NODE_CONNECTION_INFORMATION:
+    }
+    else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME)
+    {
+        if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(USB_NODE_CONNECTION_INFORMATION))
         {
-            PUSB_NODE_CONNECTION_INFORMATION ConnectionInformation;
-/*
-            ULONG i, j, k;
-            struct usb_device* dev;
-            ULONG NumberOfOpenPipes = 0;
-            ULONG SizeOfOpenPipesArray;
-*/
-            ConnectionInformation = (PUSB_NODE_CONNECTION_INFORMATION)BufferOut;
-
-            DPRINT1("Usbhub: IOCTL_USB_GET_NODE_CONNECTION_INFORMATION\n");
-            if (LengthOut < sizeof(USB_NODE_CONNECTION_INFORMATION))
-                Status = STATUS_BUFFER_TOO_SMALL;
-            else if (BufferOut == NULL)
-                Status = STATUS_INVALID_PARAMETER;
-            else if (ConnectionInformation->ConnectionIndex < 1
-                || ConnectionInformation->ConnectionIndex > USB_MAXCHILDREN)
-                Status = STATUS_INVALID_PARAMETER;
-            else
-            {
-                DPRINT1("Usbhub: We should succeed\n");
-            }
-            break;
+            // buffer too small
+            Status = STATUS_BUFFER_TOO_SMALL;
         }
-        case IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION:
+        else
         {
-            //PUSB_DESCRIPTOR_REQUEST Descriptor;
-            DPRINT1("Usbhub: IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION\n");
-            Information = 0;
-            Status = STATUS_NOT_IMPLEMENTED;
-            break;
-        }
-        case IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME:
-        {
-            PHUB_DEVICE_EXTENSION DeviceExtension;
-            PUSB_NODE_CONNECTION_DRIVERKEY_NAME StringDescriptor;
-            DPRINT1("Usbhub: IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME\n");
-            DeviceExtension = (PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
-            StringDescriptor = (PUSB_NODE_CONNECTION_DRIVERKEY_NAME)BufferOut;
-            if (LengthOut < sizeof(USB_NODE_CONNECTION_DRIVERKEY_NAME))
-                Status = STATUS_BUFFER_TOO_SMALL;
-            else if (StringDescriptor == NULL)
-                Status = STATUS_INVALID_PARAMETER;
-            else if (StringDescriptor->ConnectionIndex < 1
-                || StringDescriptor->ConnectionIndex > USB_MAXCHILDREN)
-                Status = STATUS_INVALID_PARAMETER;
-            else if (DeviceExtension->Children[StringDescriptor->ConnectionIndex - 1] == NULL)
-                Status = STATUS_INVALID_PARAMETER;
-            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++)
             {
-                ULONG StringSize;
-                Status = IoGetDeviceProperty(
-                    DeviceExtension->Children[StringDescriptor->ConnectionIndex - 1],
-                    DevicePropertyDriverKeyName,
-                    LengthOut - FIELD_OFFSET(USB_NODE_CONNECTION_DRIVERKEY_NAME, DriverKeyName),
-                    StringDescriptor->DriverKeyName,
-                    &StringSize);
-                if (NT_SUCCESS(Status) || Status == STATUS_BUFFER_TOO_SMALL)
+                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)
                 {
-                    StringDescriptor->ActualLength = StringSize + FIELD_OFFSET(USB_NODE_CONNECTION_DRIVERKEY_NAME, DriverKeyName);
-                    Information = LengthOut;
+                    // 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;
             }
-            break;
         }
-        default:
+    }
+    else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_USB_GET_NODE_CONNECTION_NAME)
+    {
+        if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(USB_NODE_CONNECTION_NAME))
         {
-            /* Pass Irp to lower driver */
-            DPRINT1("Usbhub: Unknown IOCTL code 0x%lx\n", Stack->Parameters.DeviceIoControl.IoControlCode);
-            return ForwardIrpAndForget(DeviceObject, Irp);
+            // 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);
+    }
 
-    Irp->IoStatus.Information = Information;
+    // finish irp
     Irp->IoStatus.Status = Status;
     IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
     return Status;
 }
+