From 7aad4c73093c235ee1074d3f1e76596570373c93 Mon Sep 17 00:00:00 2001 From: Alexander Morozov Date: Tue, 1 Mar 2011 21:51:53 +0300 Subject: [PATCH] mountmgr.sys: Add support for removing USB devices (eterbug #4301). --- dlls/mountmgr.sys/hal.c | 3 +- dlls/mountmgr.sys/mountmgr.h | 3 +- dlls/mountmgr.sys/usbhub.c | 215 ++++++++++++++++++++++++++++++++++- 3 files changed, 213 insertions(+), 8 deletions(-) diff --git a/dlls/mountmgr.sys/hal.c b/dlls/mountmgr.sys/hal.c index fcb53715a8..fd9c2b40dd 100644 --- a/dlls/mountmgr.sys/hal.c +++ b/dlls/mountmgr.sys/hal.c @@ -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 ); diff --git a/dlls/mountmgr.sys/mountmgr.h b/dlls/mountmgr.sys/mountmgr.h index fe33c9fd5f..fb0ab197ee 100644 --- a/dlls/mountmgr.sys/mountmgr.h +++ b/dlls/mountmgr.sys/mountmgr.h @@ -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); diff --git a/dlls/mountmgr.sys/usbhub.c b/dlls/mountmgr.sys/usbhub.c index d47467de33..4a1bcf77e2 100644 --- a/dlls/mountmgr.sys/usbhub.c +++ b/dlls/mountmgr.sys/usbhub.c @@ -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, ©->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 ); -- 2.33.8