mountmgr.sys: Add support for removing USB devices (eterbug #4301).
authorAlexander Morozov <amorozov@etersoft.ru>
Tue, 1 Mar 2011 18:51:53 +0000 (21:51 +0300)
committerTest Robot <wine-patches-test@office.etersoft.ru>
Fri, 4 Mar 2011 13:56:50 +0000 (16:56 +0300)
dlls/mountmgr.sys/hal.c
dlls/mountmgr.sys/mountmgr.h
dlls/mountmgr.sys/usbhub.c

index fcb53715a8c9064eaf3baeadfd5bf6a2efaba616..fd9c2b40dd910602f343a82c641a54cc84bb5e5b 100644 (file)
@@ -166,7 +166,7 @@ static void new_device( LibHalContext *ctx, const char *udi )
 
     if ((subsys = p_libhal_device_get_property_string( ctx, udi, "info.subsystem", NULL )) &&
         !strcmp( subsys, "usb_device" ))
-        enum_usb_devices();
+        add_usb_devices();
 
     if (automount_disabled())
         goto done;
@@ -245,6 +245,7 @@ static void removed_device( LibHalContext *ctx, const char *udi )
 
     TRACE( "removed %s\n", wine_dbgstr_a(udi) );
 
+    remove_usb_devices();
     if (!remove_dos_device( -1, udi ))
     {
         p_dbus_error_init( &error );
index fe33c9fd5f373fc3d5a1c87095cd4a1fdfc7babe..fb0ab197ee8ba97a82637785a3d9d80f03f77747 100644 (file)
@@ -78,4 +78,5 @@ extern void set_mount_point_id( struct mount_point *mount, const void *id, unsig
 
 /* usb functions */
 
-extern void enum_usb_devices(void);
+extern void add_usb_devices(void);
+extern void remove_usb_devices(void);
index d47467de330c322095033f886fdc8112831001c7..4a1bcf77e21882966461f7c3127e267bbddf1398 100644 (file)
@@ -98,6 +98,16 @@ static CRITICAL_SECTION usbhub_cs = { &usbhub_cs_debug, -1, 0, 0, 0, 0 };
 
 static BOOL libusb_initialized;
 
+static BOOL device_exists( DEVICE_OBJECT *device )
+{
+    struct DeviceInstance *instance;
+
+    LIST_FOR_EACH_ENTRY( instance, &Devices, struct DeviceInstance, entry )
+        if (instance->pdo == device)
+            return TRUE;
+    return FALSE;
+}
+
 static void add_data( unsigned char **dst, ULONG *dst_size, const void *src, ULONG src_size )
 {
     int copy;
@@ -124,6 +134,7 @@ static NTSTATUS WINAPI usbhub_ioctl( DEVICE_OBJECT *device, IRP *irp )
     TRACE( "%p, %p\n", device, irp );
 
     EnterCriticalSection( &usbhub_cs );
+    if (!device_exists( device )) goto done;
     inst = ((struct PdoExtension *)device->DeviceExtension)->instance;
     if (inst->service) goto done;
     irpsp = irp->Tail.Overlay.s.u.CurrentStackLocation;
@@ -392,6 +403,7 @@ static NTSTATUS WINAPI usbhub_internal_ioctl( DEVICE_OBJECT *device, IRP *irp )
     TRACE( "%p, %p\n", device, irp );
 
     EnterCriticalSection( &usbhub_cs );
+    if (!device_exists( device )) goto done;
     inst = ((struct PdoExtension *)device->DeviceExtension)->instance;
     if (!inst->service) goto done;
     irpsp = irp->Tail.Overlay.s.u.CurrentStackLocation;
@@ -741,6 +753,7 @@ static NTSTATUS WINAPI usbhub_internal_ioctl( DEVICE_OBJECT *device, IRP *irp )
     TRACE( "%p, %p\n", device, irp );
 
     EnterCriticalSection( &usbhub_cs );
+    if (!device_exists( device )) goto done;
     inst = ((struct PdoExtension *)device->DeviceExtension)->instance;
     if (!inst->service) goto done;
     irpsp = irp->Tail.Overlay.s.u.CurrentStackLocation;
@@ -1099,14 +1112,21 @@ static NTSTATUS WINAPI usbhub_dispatch_pnp( DEVICE_OBJECT *device, IRP *irp )
 
     struct PdoExtension *dx;
     IO_STACK_LOCATION *irpsp;
-    NTSTATUS status;
+    NTSTATUS status = STATUS_UNSUCCESSFUL;
     ULONG_PTR info = 0;
 
     TRACE( "%p, %p\n", device, irp );
 
     EnterCriticalSection( &usbhub_cs );
-    dx = device->DeviceExtension;
     irpsp = irp->Tail.Overlay.s.u.CurrentStackLocation;
+    if (!device_exists( device ))
+    {
+        if (irpsp->MinorFunction == IRP_MN_SURPRISE_REMOVAL ||
+            irpsp->MinorFunction == IRP_MN_REMOVE_DEVICE)
+            status = STATUS_SUCCESS;
+        goto done;
+    }
+    dx = device->DeviceExtension;
     switch (irpsp->MinorFunction)
     {
     case IRP_MN_QUERY_DEVICE_RELATIONS:
@@ -1176,6 +1196,7 @@ static NTSTATUS WINAPI usbhub_dispatch_pnp( DEVICE_OBJECT *device, IRP *irp )
         status = STATUS_SUCCESS;
     }
 
+done:
     LeaveCriticalSection( &usbhub_cs );
     irp->IoStatus.u.Status = status;
     irp->IoStatus.Information = info;
@@ -1219,6 +1240,28 @@ static BOOL start_service( const WCHAR *name )
     return ret;
 }
 
+static void stop_service( const WCHAR *name )
+{
+    SC_HANDLE scm, service;
+    SERVICE_STATUS ss;
+
+    scm = OpenSCManagerA( NULL, NULL, SC_MANAGER_ALL_ACCESS );
+    if (scm == NULL)
+        return;
+
+    service = OpenServiceW( scm, name, SERVICE_ALL_ACCESS );
+    if (service == NULL)
+    {
+        CloseServiceHandle( scm );
+        return;
+    }
+
+    ControlService( service, SERVICE_CONTROL_STOP, &ss );
+
+    CloseServiceHandle( service );
+    CloseServiceHandle( scm );
+}
+
 static BOOL create_pdo_name( UNICODE_STRING *pdo_name )
 {
     static const WCHAR usbpdoW[] = {'\\','D','e','v','i','c','e','\\',
@@ -1622,6 +1665,72 @@ static void start_device_drivers( DRIVER_OBJECT *hubdrv )
     }
 }
 
+static NTSTATUS call_pnp_func( DEVICE_OBJECT *device, UCHAR minor_func )
+{
+    DRIVER_OBJECT *driver = device->DriverObject;
+    IO_STACK_LOCATION *irpsp;
+    PIRP irp;
+    NTSTATUS status;
+
+    if (driver->MajorFunction[IRP_MJ_PNP] == NULL)
+        return STATUS_NOT_SUPPORTED;
+    irp = IoAllocateIrp( device->StackSize, FALSE );
+    if (irp == NULL) return STATUS_NO_MEMORY;
+
+    irpsp = irp->Tail.Overlay.s.u.CurrentStackLocation - 1;
+    irp->RequestorMode = KernelMode;
+    irp->IoStatus.u.Status = STATUS_NOT_SUPPORTED;
+    irpsp->MajorFunction = IRP_MJ_PNP;
+    irpsp->MinorFunction = minor_func;
+    irpsp->DeviceObject = device;
+    device->CurrentIrp = irp;
+    status = IoCallDriver( device, irp );
+    IoFreeIrp( irp );
+    return status;
+}
+
+static void stop_device_driver( struct DeviceInstance *instance )
+{
+    if (instance->pdo)
+    {
+        NTSTATUS status;
+        DEVICE_OBJECT *attd = instance->pdo->AttachedDevice;
+        DEVICE_OBJECT *dev = (attd != NULL) ? attd : instance->pdo;
+
+        status = call_pnp_func( dev, IRP_MN_SURPRISE_REMOVAL );
+        if (status != STATUS_SUCCESS)
+            WARN( "handling IRP_MN_SURPRISE_REMOVAL failed: %08x\n", status );
+        status = call_pnp_func( dev, IRP_MN_REMOVE_DEVICE );
+        if (status != STATUS_SUCCESS)
+            WARN( "handling IRP_MN_REMOVE_DEVICE failed: %08x\n", status );
+        IoDeleteDevice( instance->pdo );
+    }
+    if (instance->service)
+    {
+        struct DeviceInstance *it;
+        BOOL stop = TRUE;
+
+        EnterCriticalSection( &usbhub_cs );
+        LIST_FOR_EACH_ENTRY( it, &Devices, struct DeviceInstance, entry )
+            if (it->pdo != NULL && it->service != NULL &&
+                !strcmpiW( it->service, instance->service ))
+            {
+                stop = FALSE;
+                break;
+            }
+        LeaveCriticalSection( &usbhub_cs );
+        if (stop)
+            stop_service( instance->service );
+    }
+    else
+        HeapFree( GetProcessHeap(), 0, instance->instance_id );
+#ifdef HAVE_LIBUSB_H
+    libusb_unref_device( instance->dev );
+#endif
+    list_remove( &instance->entry );
+    HeapFree( GetProcessHeap(), 0, instance );
+}
+
 static BOOL is_new( void *dev )
 {
     struct DeviceInstance *instance;
@@ -1632,6 +1741,24 @@ static BOOL is_new( void *dev )
     return TRUE;
 }
 
+static int add_to_remove_list( struct DeviceInstance *it, struct list *remove )
+{
+    struct DeviceInstance *copy;
+
+    if (it->service)
+    {
+        copy = HeapAlloc( GetProcessHeap(), 0, sizeof(*copy) );
+        if (!copy) return 1;
+        memcpy( copy, it, sizeof(struct DeviceInstance) );
+        copy->pdo = NULL;
+        copy->dev = NULL;
+        list_add_tail( &Devices, &copy->entry);
+    }
+    list_remove( &it->entry );
+    list_add_tail( remove, &it->entry );
+    return 0;
+}
+
 #ifdef HAVE_LIBUSB_H
 
 static int initialize_libusb(void)
@@ -1639,7 +1766,7 @@ static int initialize_libusb(void)
     return libusb_init( NULL );
 }
 
-void enum_usb_devices(void)
+void add_usb_devices(void)
 {
     libusb_device **devs, *dev;
     struct libusb_device_descriptor desc;
@@ -1686,6 +1813,41 @@ end:
     LeaveCriticalSection( &usbhub_cs );
 }
 
+void remove_usb_devices(void)
+{
+    struct list remove_list = LIST_INIT(remove_list);
+    libusb_device **devs, *dev;
+    struct DeviceInstance *it, *next;
+    unsigned int i;
+    BOOL found;
+
+    EnterCriticalSection( &usbhub_cs );
+    if (!libusb_initialized || libusb_get_device_list( NULL, &devs ) < 0)
+        goto end;
+    LIST_FOR_EACH_ENTRY_SAFE( it, next, &Devices, struct DeviceInstance, entry )
+    {
+        if (!it->dev)
+            continue;
+        found = FALSE;
+        i = 0;
+        while ((dev = devs[i++]))
+            if (it->dev == dev)
+            {
+                found = TRUE;
+                break;
+            }
+        if (!found && add_to_remove_list( it, &remove_list ))
+            break;
+    }
+end:
+    LeaveCriticalSection( &usbhub_cs );
+    LIST_FOR_EACH_ENTRY_SAFE( it, next, &remove_list, struct DeviceInstance, entry )
+    {
+        TRACE( "remove %04x:%04x\n", it->vid, it->pid );
+        stop_device_driver( it );
+    }
+}
+
 #else
 
 static int initialize_libusb(void)
@@ -1694,7 +1856,7 @@ static int initialize_libusb(void)
     return 0;
 }
 
-void enum_usb_devices(void)
+void add_usb_devices(void)
 {
     struct usb_device *dev;
     struct usb_bus *bus;
@@ -1741,11 +1903,52 @@ end:
     LeaveCriticalSection( &usbhub_cs );
 }
 
+void remove_usb_devices(void)
+{
+    struct list remove_list = LIST_INIT(remove_list);
+    struct usb_device *dev;
+    struct usb_bus *bus;
+    struct DeviceInstance *it, *next;
+    BOOL found;
+
+    EnterCriticalSection( &usbhub_cs );
+    if (!libusb_initialized)
+        goto end;
+    usb_find_busses();
+    usb_find_devices();
+    LIST_FOR_EACH_ENTRY_SAFE( it, next, &Devices, struct DeviceInstance, entry )
+    {
+        if (!it->dev)
+            continue;
+        found = FALSE;
+        for (bus = usb_busses; bus; bus = bus->next)
+            for (dev = bus->devices; dev; dev = dev->next)
+                if (it->dev == dev)
+                {
+                    found = TRUE;
+                    break;
+                }
+        if (!found && add_to_remove_list( it, &remove_list ))
+            break;
+    }
+end:
+    LeaveCriticalSection( &usbhub_cs );
+    LIST_FOR_EACH_ENTRY_SAFE( it, next, &remove_list, struct DeviceInstance, entry )
+    {
+        TRACE( "remove %04x:%04x\n", it->vid, it->pid );
+        stop_device_driver( it );
+    }
+}
+
 #endif
 
 #else
 
-void enum_usb_devices(void)
+void add_usb_devices(void)
+{
+}
+
+void remove_usb_devices(void)
 {
 }
 
@@ -1770,7 +1973,7 @@ static DWORD CALLBACK initialize_usbhub( void *arg )
     else
         libusb_initialized = TRUE;
     LeaveCriticalSection( &usbhub_cs );
-    enum_usb_devices();
+    add_usb_devices();
     event = CreateEventW( NULL, TRUE, FALSE, usbhub_started_eventW );
     SetEvent( event );
     CloseHandle( event );