[USBHUB]
[reactos.git] / drivers / usb / usbhub_new / fdo.c
index 0611409..14bea1a 100644 (file)
@@ -21,6 +21,11 @@ CreateUsbChildDeviceObject(
     IN LONG PortId,
     OUT PDEVICE_OBJECT *UsbChildDeviceObject);
 
+NTSTATUS
+DestroyUsbChildDeviceObject(
+    IN PDEVICE_OBJECT UsbHubDeviceObject,
+    IN LONG PortId);
+
 NTSTATUS
 SubmitRequestToRootHub(
     IN PDEVICE_OBJECT RootHubDeviceObject,
@@ -304,9 +309,11 @@ DeviceStatusChangeThread(
             {
                 DPRINT1("Device disconnected from port %d\n", PortId);
 
-                //
-                // FIXME: Remove the device, and deallocate memory
-                //
+                Status = DestroyUsbChildDeviceObject(DeviceObject, PortId);
+                if (!NT_SUCCESS(Status))
+                {
+                    DPRINT1("Failed to delete child device object after disconnect\n");
+                }
             }
             else
             {
@@ -428,6 +435,7 @@ DeviceStatusChangeThread(
 }
 
 NTSTATUS
+NTAPI
 StatusChangeEndpointCompletion(
     IN PDEVICE_OBJECT DeviceObject,
     IN PIRP Irp,
@@ -457,7 +465,7 @@ StatusChangeEndpointCompletion(
     }
     WorkItemData->Context = RealDeviceObject;
     DPRINT1("Initialize work item\n");
-    ExInitializeWorkItem(&WorkItemData->WorkItem, (PWORKER_THREAD_ROUTINE)DeviceStatusChangeThread, (PVOID)WorkItemData);
+    ExInitializeWorkItem(&WorkItemData->WorkItem, DeviceStatusChangeThread, (PVOID)WorkItemData);
 
     //
     // Queue the work item to handle initializing the device
@@ -550,7 +558,7 @@ QueryStatusChangeEndpoint(
     // Set the completion routine for when device is connected to root hub
     //
     IoSetCompletionRoutine(HubDeviceExtension->PendingSCEIrp,
-                           (PIO_COMPLETION_ROUTINE) StatusChangeEndpointCompletion,
+                           StatusChangeEndpointCompletion,
                            DeviceObject,
                            TRUE,
                            TRUE,
@@ -796,23 +804,80 @@ GetUsbStringDescriptor(
     return STATUS_SUCCESS;
 }
 
+ULONG
+IsCompositeDevice(
+    IN PUSB_DEVICE_DESCRIPTOR DeviceDescriptor,
+    IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor)
+{
+    if (DeviceDescriptor->bNumConfigurations != 1)
+    {
+         //
+         // composite device must have only one configuration
+         //
+         return FALSE;
+    }
+
+    if (ConfigurationDescriptor->bNumInterfaces < 2)
+    {
+        //
+        // composite device must have multiple interfaces
+        //
+        return FALSE;
+    }
+
+    if (DeviceDescriptor->bDeviceClass == 0)
+    {
+        //
+        // composite device
+        //
+        ASSERT(DeviceDescriptor->bDeviceSubClass == 0);
+        ASSERT(DeviceDescriptor->bDeviceProtocol == 0);
+        return TRUE;
+    }
+
+    if (DeviceDescriptor->bDeviceClass == 0xEF && 
+        DeviceDescriptor->bDeviceSubClass == 0x02 &&
+        DeviceDescriptor->bDeviceProtocol == 0x01)
+    {
+        //
+        // USB-IF association descriptor
+        //
+        return TRUE;
+    }
+
+    //
+    // not a composite device
+    //
+    return FALSE;
+}
+
 NTSTATUS
 CreateDeviceIds(
     PDEVICE_OBJECT UsbChildDeviceObject)
 {
-    NTSTATUS Status;
+    NTSTATUS Status = STATUS_SUCCESS;
     ULONG Index;
     PWCHAR BufferPtr;
+    WCHAR Buffer[100];
     PHUB_CHILDDEVICE_EXTENSION UsbChildExtension;
+    PUSB_DEVICE_DESCRIPTOR DeviceDescriptor;
+    PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor;
+    PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor;
 
+    //
+    // get child device extension
+    //
     UsbChildExtension = (PHUB_CHILDDEVICE_EXTENSION)UsbChildDeviceObject->DeviceExtension;
 
     //
     // Initialize the CompatibleIds String
     //
-    UsbChildExtension->usCompatibleIds.Length = 144;
+    UsbChildExtension->usCompatibleIds.Length = 144; //FIXME
     UsbChildExtension->usCompatibleIds.MaximumLength = UsbChildExtension->usCompatibleIds.Length;
 
+    //
+    // allocate mem for compatible id string
+    //
     BufferPtr = ExAllocatePoolWithTag(NonPagedPool,
                                       UsbChildExtension->usCompatibleIds.Length,
                                       USB_HUB_TAG);
@@ -823,39 +888,86 @@ CreateDeviceIds(
     }
 
     RtlZeroMemory(BufferPtr, UsbChildExtension->usCompatibleIds.Length);
-
     Index = 0;
+
+    //
+    // get device descriptor
+    //
+    DeviceDescriptor = &UsbChildExtension->DeviceDesc;
+
+    //
+    // get configuration descriptor
+    //
+    ConfigurationDescriptor = UsbChildExtension->FullConfigDesc;
+
+    //
+    // get interface descriptor
+    //
+    InterfaceDescriptor = (PUSB_INTERFACE_DESCRIPTOR)(ConfigurationDescriptor + 1);
+
     //
     // Construct the CompatibleIds
     //
-    if (UsbChildExtension->DeviceDesc.bDeviceClass == 0)
+    if (IsCompositeDevice(DeviceDescriptor, ConfigurationDescriptor))
     {
-        PUSB_INTERFACE_DESCRIPTOR InterDesc = (PUSB_INTERFACE_DESCRIPTOR)
-            ((ULONG_PTR)UsbChildExtension->FullConfigDesc + sizeof(USB_CONFIGURATION_DESCRIPTOR));
+        //
+        // sanity checks
+        //
+        ASSERT(DeviceDescriptor->bNumConfigurations == 1);
+        ASSERT(ConfigurationDescriptor->bNumInterfaces > 1);
 
         Index += swprintf(&BufferPtr[Index], 
-                          L"USB\\Class_%02x&SubClass_%02x&Prot_%02x",
-                          InterDesc->bInterfaceClass,InterDesc->bInterfaceSubClass,InterDesc->bInterfaceProtocol) + 1;
+                          L"USB\\DevClass_%02x&SubClass_%02x&Prot_%02x",
+                          DeviceDescriptor->bDeviceClass, DeviceDescriptor->bDeviceSubClass, DeviceDescriptor->bDeviceProtocol) + 1;
         Index += swprintf(&BufferPtr[Index],
-                          L"USB\\Class_%02x&SubClass_%02x",
-                          InterDesc->bInterfaceClass,InterDesc->bInterfaceSubClass) + 1;
+                          L"USB\\DevClass_%02x&SubClass_%02x",
+                          DeviceDescriptor->bDeviceClass, DeviceDescriptor->bDeviceSubClass) + 1;
         Index += swprintf(&BufferPtr[Index],
-                          L"USB\\Class_%02x",
-                          InterDesc->bInterfaceClass) + 1;
+                          L"USB\\DevClass_%02x",
+                          DeviceDescriptor->bDeviceClass) + 1;
+        Index += swprintf(&BufferPtr[Index],
+                          L"USB\\COMPOSITE") + 1;
     }
     else
     {
-        PUSB_DEVICE_DESCRIPTOR DevDesc = &UsbChildExtension->DeviceDesc;
-        Index += swprintf(&BufferPtr[Index], 
+        //
+        // sanity checks for simple usb device
+        //
+        ASSERT(ConfigurationDescriptor->bNumInterfaces == 1);
+
+        //
+        // FIXME: support multiple configurations
+        //
+        ASSERT(DeviceDescriptor->bNumConfigurations == 1);
+
+        if (DeviceDescriptor->bDeviceClass == 0)
+        {
+            Index += swprintf(&BufferPtr[Index], 
                           L"USB\\Class_%02x&SubClass_%02x&Prot_%02x",
-                          DevDesc->bDeviceClass,DevDesc->bDeviceSubClass,DevDesc->bDeviceProtocol) + 1;
-        Index += swprintf(&BufferPtr[Index],
+                          InterfaceDescriptor->bInterfaceClass, InterfaceDescriptor->bInterfaceSubClass, InterfaceDescriptor->bInterfaceProtocol) + 1;
+            Index += swprintf(&BufferPtr[Index],
                           L"USB\\Class_%02x&SubClass_%02x",
-                          DevDesc->bDeviceClass,DevDesc->bDeviceSubClass) + 1;
-        Index += swprintf(&BufferPtr[Index],
+                          InterfaceDescriptor->bInterfaceClass, InterfaceDescriptor->bInterfaceSubClass) + 1;
+            Index += swprintf(&BufferPtr[Index],
+                          L"USB\\Class_%02x",
+                          InterfaceDescriptor->bInterfaceClass) + 1;
+        }
+        else
+        {
+            Index += swprintf(&BufferPtr[Index], 
+                          L"USB\\Class_%02x&SubClass_%02x&Prot_%02x",
+                          DeviceDescriptor->bDeviceClass, DeviceDescriptor->bDeviceSubClass, DeviceDescriptor->bDeviceProtocol) + 1;
+            Index += swprintf(&BufferPtr[Index],
+                          L"USB\\Class_%02x&SubClass_%02x",
+                          DeviceDescriptor->bDeviceClass, DeviceDescriptor->bDeviceSubClass) + 1;
+            Index += swprintf(&BufferPtr[Index],
                           L"USB\\Class_%02x",
-                          DevDesc->bDeviceClass) + 1;
+                          DeviceDescriptor->bDeviceClass) + 1;
+
+
+        }
     }
+
     BufferPtr[Index] = UNICODE_NULL;
     UsbChildExtension->usCompatibleIds.Buffer = BufferPtr;
     DPRINT1("usCompatibleIds %wZ\n", &UsbChildExtension->usCompatibleIds);
@@ -954,6 +1066,29 @@ CreateDeviceIds(
         UsbChildExtension->usInstanceId.MaximumLength = UsbChildExtension->usInstanceId.Length;
         DPRINT1("Usb InstanceId %wZ\n", &UsbChildExtension->usInstanceId);
     }
+    else
+    {
+       //
+       // the device did not provide a serial number, lets create a pseudo instance id
+       //
+       Index = swprintf(Buffer, L"0&%04d", 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;
+           goto Cleanup;
+       }
+
+       //
+       // copy instance id
+       //
+       RtlCopyMemory(UsbChildExtension->usInstanceId.Buffer, Buffer, Index * sizeof(WCHAR));
+       UsbChildExtension->usInstanceId.Length = UsbChildExtension->usDeviceId.MaximumLength = Index * sizeof(WCHAR);
+
+       DPRINT1("usDeviceId %wZ\n", &UsbChildExtension->usInstanceId);
+    }
+
 
     return Status;
 
@@ -975,6 +1110,51 @@ Cleanup:
     return Status;
 }
 
+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;
+
+    DPRINT1("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,
@@ -1258,13 +1438,44 @@ USBHUB_FdoQueryBusRelations(
 }
 
 VOID
+NTAPI
 RootHubInitCallbackFunction(
     PVOID Context)
 {
     PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)Context;
+/*
+    NTSTATUS Status;
+    //ULONG PortId;
+    //PHUB_DEVICE_EXTENSION HubDeviceExtension;
+    //PORT_STATUS_CHANGE StatusChange;
+
+    HubDeviceExtension = (PHUB_DEVICE_EXTENSION) DeviceObject->DeviceExtension;
 
-    DPRINT1("Sending the initial SCE Request %x\n", DeviceObject);
+    DPRINT1("RootHubInitCallbackFunction Sending the initial SCE Request %x\n", DeviceObject);
 
+    for (PortId = 1; PortId <= HubDeviceExtension->HubDescriptor.bNumberOfPorts; PortId++)
+    {
+        //
+        // get port status
+        //
+        Status = GetPortStatusAndChange(HubDeviceExtension->RootHubPhysicalDeviceObject, PortId, &StatusChange);
+        if (NT_SUCCESS(Status))
+        {
+            //
+            // is there a device connected
+            //
+            if (StatusChange.Status & USB_PORT_STATUS_CONNECT)
+            {
+                //
+                // reset port
+                //
+                Status = SetPortFeature(HubDeviceExtension->RootHubPhysicalDeviceObject, PortId, PORT_RESET);
+                if (!NT_SUCCESS(Status))
+                    DPRINT1("Failed to reset on port %d\n", PortId);
+            }
+        }
+    }
+*/
     //
     // Send the first SCE Request
     //
@@ -1282,6 +1493,7 @@ USBHUB_FdoHandlePnp(
     PHUB_DEVICE_EXTENSION HubDeviceExtension;
     PDEVICE_OBJECT RootHubDeviceObject;
     PVOID HubInterfaceBusContext , UsbDInterfaceBusContext;
+    //PORT_STATUS_CHANGE StatusChange;
 
     HubDeviceExtension = (PHUB_DEVICE_EXTENSION) DeviceObject->DeviceExtension;
 
@@ -1610,7 +1822,7 @@ USBHUB_FdoHandlePnp(
             {
                 Status = HubDeviceExtension->HubInterface.RootHubInitNotification(HubInterfaceBusContext,
                                                                                   DeviceObject,
-                                                                                  (PRH_INIT_CALLBACK)RootHubInitCallbackFunction);
+                                                                                  RootHubInitCallbackFunction);
                 if (!NT_SUCCESS(Status))
                 {
                     DPRINT1("Failed to set callback\n");
@@ -1618,6 +1830,33 @@ USBHUB_FdoHandlePnp(
             }
             else
             {
+/*
+                //
+                // 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);
+                        }
+                    }
+                }
+*/
                 //
                 // Send the first SCE Request
                 //