From f9a8038beca7f5b56b9b119732118c9ba33c56a6 Mon Sep 17 00:00:00 2001 From: Alexander Morozov Date: Sat, 10 Sep 2011 17:53:08 +0400 Subject: [PATCH 1/2] Add support of native Windows drivers for USB tokens. --- configure.ac | 28 + dlls/mountmgr.sys/Makefile.in | 8 +- dlls/mountmgr.sys/device.c | 24 +- dlls/mountmgr.sys/hal.c | 7 + dlls/mountmgr.sys/mountmgr.c | 79 +- dlls/mountmgr.sys/mountmgr.h | 8 + dlls/mountmgr.sys/usbhub.c | 2022 +++++++++++++++++++++++++++++++++++ dlls/ntoskrnl.exe/Makefile.in | 1 + dlls/ntoskrnl.exe/ntoskrnl.c | 1420 +++++++++++++++++++++++-- dlls/ntoskrnl.exe/ntoskrnl.exe.spec | 46 +- dlls/usbd.sys/usbd.c | 9 + dlls/usbd.sys/usbd.sys.spec | 2 +- include/cfgmgr32.h | 1 + include/ddk/ntddk.h | 9 + include/ddk/usb100.h | 10 + include/ddk/usbdlib.h | 1 + include/ddk/usbdrivr.h | 28 + include/ddk/usbioctl.h | 97 ++ include/ddk/usbiodef.h | 35 + include/ddk/wdm.h | 45 +- include/ddk/wdmguid.h | 28 + programs/services/services.c | 21 + programs/winedevice/device.c | 302 +++++- server/device.c | 25 + server/protocol.def | 9 + 25 files changed, 4092 insertions(+), 173 deletions(-) create mode 100644 dlls/mountmgr.sys/usbhub.c create mode 100644 include/ddk/usbdrivr.h create mode 100644 include/ddk/usbioctl.h create mode 100644 include/ddk/usbiodef.h create mode 100644 include/ddk/wdmguid.h diff --git a/configure.ac b/configure.ac index 1401072..6c7d94e 100644 --- a/configure.ac +++ b/configure.ac @@ -76,6 +76,7 @@ AC_ARG_WITH(pthread, AS_HELP_STRING([--without-pthread],[do not use the pthrea AC_ARG_WITH(sane, AS_HELP_STRING([--without-sane],[do not use SANE (scanner support)])) AC_ARG_WITH(tiff, AS_HELP_STRING([--without-tiff],[do not use TIFF]), [if test "x$withval" = "xno"; then ac_cv_header_tiffio_h=no; fi]) +AC_ARG_WITH(usb, AS_HELP_STRING([--without-usb],[do not use USB])) AC_ARG_WITH(v4l, AS_HELP_STRING([--without-v4l],[do not use v4l1 (v4l support)])) AC_ARG_WITH(xcomposite,AS_HELP_STRING([--without-xcomposite],[do not use the Xcomposite extension]), [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_Xcomposite_h=no; fi]) @@ -1285,6 +1286,33 @@ fi WINE_NOTICE_WITH(v4l,[test "x$ac_cv_lib_soname_v4l1" = "x"], [libv4l ${notice_platform}development files not found.]) +dnl **** Check for LIBUSB **** +AC_SUBST(USBLIBS,"") +AC_SUBST(USBINCL,"") +if test "x$with_usb" != "xno" +then + AC_CHECK_HEADERS([usb.h], + AC_CHECK_LIB(usb, usb_init, + [AC_DEFINE(HAVE_LIBUSB, 1, [Define if you have the libusb library and header]) + USBLIBS="-lusb"])) + ac_save_CPPFLAGS="$CPPFLAGS" + if test "$PKG_CONFIG" != "false" + then + ac_usb_incl="`$PKG_CONFIG --cflags libusb-1.0`" + ac_usb_libs="`$PKG_CONFIG --libs libusb-1.0`" + CPPFLAGS="$ac_usb_incl $CPPFLAGS" + fi + AC_CHECK_HEADERS([libusb.h], + AC_CHECK_LIB(usb-1.0, libusb_init, + [AC_DEFINE(HAVE_LIBUSB, 1, [Define if you have the libusb library and header]) + USBLIBS="$ac_usb_libs" + USBINCL="$ac_usb_incl"])) + CPPFLAGS="$ac_save_CPPFLAGS" +fi +WINE_NOTICE_WITH(usb,[test "x$ac_cv_lib_usb_usb_init" != "xyes" -a "x$ac_cv_lib_usb_1_0_libusb_init" != "xyes"], + [libusb ${notice_platform}development files not found, USB won't be supported.]) + + dnl **** Check for libgphoto2 **** if test "x$with_gphoto" != "xno" then diff --git a/dlls/mountmgr.sys/Makefile.in b/dlls/mountmgr.sys/Makefile.in index 91203e1..c5ea234 100644 --- a/dlls/mountmgr.sys/Makefile.in +++ b/dlls/mountmgr.sys/Makefile.in @@ -1,14 +1,16 @@ MODULE = mountmgr.sys -IMPORTS = uuid advapi32 ntoskrnl.exe +IMPORTS = uuid advapi32 ntoskrnl.exe setupapi DELAYIMPORTS = user32 EXTRADLLFLAGS = -Wb,--subsystem,native EXTRADEFS = @HALINCL@ -EXTRALIBS = @DISKARBITRATIONLIB@ +EXTRAINCL = @USBINCL@ +EXTRALIBS = @DISKARBITRATIONLIB@ @USBLIBS@ C_SRCS = \ device.c \ diskarb.c \ hal.c \ - mountmgr.c + mountmgr.c \ + usbhub.c @MAKE_DLL_RULES@ diff --git a/dlls/mountmgr.sys/device.c b/dlls/mountmgr.sys/device.c index a3aa81c..9788be7 100644 --- a/dlls/mountmgr.sys/device.c +++ b/dlls/mountmgr.sys/device.c @@ -870,6 +870,7 @@ static NTSTATUS WINAPI harddisk_ioctl( DEVICE_OBJECT *device, IRP *irp ) { IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp ); struct disk_device *dev = device->DeviceExtension; + NTSTATUS status; TRACE( "ioctl %x insize %u outsize %u\n", irpsp->Parameters.DeviceIoControl.IoControlCode, @@ -890,9 +891,9 @@ static NTSTATUS WINAPI harddisk_ioctl( DEVICE_OBJECT *device, IRP *irp ) info.TracksPerCylinder = 255; info.SectorsPerTrack = 63; info.BytesPerSector = 512; - memcpy( irp->MdlAddress->StartVa, &info, len ); + memcpy( irp->AssociatedIrp.SystemBuffer, &info, len ); irp->IoStatus.Information = len; - irp->IoStatus.u.Status = STATUS_SUCCESS; + status = STATUS_SUCCESS; break; } case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX: @@ -910,31 +911,31 @@ static NTSTATUS WINAPI harddisk_ioctl( DEVICE_OBJECT *device, IRP *irp ) info.DiskSize.QuadPart = info.Geometry.Cylinders.QuadPart * info.Geometry.TracksPerCylinder * info.Geometry.SectorsPerTrack * info.Geometry.BytesPerSector; info.Data[0] = 0; - memcpy( irp->MdlAddress->StartVa, &info, len ); + memcpy( irp->AssociatedIrp.SystemBuffer, &info, len ); irp->IoStatus.Information = len; - irp->IoStatus.u.Status = STATUS_SUCCESS; + status = STATUS_SUCCESS; break; } case IOCTL_STORAGE_GET_DEVICE_NUMBER: { DWORD len = min( sizeof(dev->devnum), irpsp->Parameters.DeviceIoControl.OutputBufferLength ); - memcpy( irp->MdlAddress->StartVa, &dev->devnum, len ); + memcpy( irp->AssociatedIrp.SystemBuffer, &dev->devnum, len ); irp->IoStatus.Information = len; - irp->IoStatus.u.Status = STATUS_SUCCESS; + status = STATUS_SUCCESS; break; } case IOCTL_CDROM_READ_TOC: - irp->IoStatus.u.Status = STATUS_INVALID_DEVICE_REQUEST; + status = STATUS_INVALID_DEVICE_REQUEST; break; case IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS: { DWORD len = min( 32, irpsp->Parameters.DeviceIoControl.OutputBufferLength ); FIXME( "returning zero-filled buffer for IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS\n" ); - memset( irp->MdlAddress->StartVa, 0, len ); + memset( irp->AssociatedIrp.SystemBuffer, 0, len ); irp->IoStatus.Information = len; - irp->IoStatus.u.Status = STATUS_SUCCESS; + status = STATUS_SUCCESS; break; } default: @@ -942,14 +943,15 @@ static NTSTATUS WINAPI harddisk_ioctl( DEVICE_OBJECT *device, IRP *irp ) ULONG code = irpsp->Parameters.DeviceIoControl.IoControlCode; FIXME("Unsupported ioctl %x (device=%x access=%x func=%x method=%x)\n", code, code >> 16, (code >> 14) & 3, (code >> 2) & 0xfff, code & 3); - irp->IoStatus.u.Status = STATUS_NOT_SUPPORTED; + status = STATUS_NOT_SUPPORTED; break; } } LeaveCriticalSection( &device_section ); + irp->IoStatus.u.Status = status; IoCompleteRequest( irp, IO_NO_INCREMENT ); - return irp->IoStatus.u.Status; + return status; } /* driver entry point for the harddisk driver */ diff --git a/dlls/mountmgr.sys/hal.c b/dlls/mountmgr.sys/hal.c index d369879..e325221 100644 --- a/dlls/mountmgr.sys/hal.c +++ b/dlls/mountmgr.sys/hal.c @@ -138,6 +138,7 @@ static GUID *parse_uuid( GUID *guid, const char *str ) static void new_device( LibHalContext *ctx, const char *udi ) { DBusError error; + char *subsys = NULL; char *parent = NULL; char *mount_point = NULL; char *device = NULL; @@ -148,6 +149,10 @@ static void new_device( LibHalContext *ctx, const char *udi ) p_dbus_error_init( &error ); + if ((subsys = p_libhal_device_get_property_string( ctx, udi, "info.subsystem", NULL )) && + !strcmp( subsys, "usb_device" )) + add_usb_devices(); + if (!(device = p_libhal_device_get_property_string( ctx, udi, "block.device", &error ))) goto done; @@ -178,6 +183,7 @@ static void new_device( LibHalContext *ctx, const char *udi ) else if (guid_ptr) add_volume( udi, device, mount_point, DEVICE_HARDDISK_VOL, guid_ptr ); done: + if (subsys) p_libhal_free_string( subsys ); if (type) p_libhal_free_string( type ); if (parent) p_libhal_free_string( parent ); if (device) p_libhal_free_string( device ); @@ -193,6 +199,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.c b/dlls/mountmgr.sys/mountmgr.c index 4e9900f..94b14fa 100644 --- a/dlls/mountmgr.sys/mountmgr.c +++ b/dlls/mountmgr.sys/mountmgr.c @@ -353,6 +353,7 @@ done: static NTSTATUS WINAPI mountmgr_ioctl( DEVICE_OBJECT *device, IRP *irp ) { IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp ); + NTSTATUS status; TRACE( "ioctl %x insize %u outsize %u\n", irpsp->Parameters.DeviceIoControl.IoControlCode, @@ -362,37 +363,71 @@ static NTSTATUS WINAPI mountmgr_ioctl( DEVICE_OBJECT *device, IRP *irp ) switch(irpsp->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_MOUNTMGR_QUERY_POINTS: - if (irpsp->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_MOUNT_POINT)) - return STATUS_INVALID_PARAMETER; - irp->IoStatus.u.Status = query_mount_points( irpsp->Parameters.DeviceIoControl.Type3InputBuffer, - irpsp->Parameters.DeviceIoControl.InputBufferLength, - irp->MdlAddress->StartVa, - irpsp->Parameters.DeviceIoControl.OutputBufferLength, - &irp->IoStatus ); + { + void *in_buff; + ULONG in_size = irpsp->Parameters.DeviceIoControl.InputBufferLength; + + if (in_size < sizeof(MOUNTMGR_MOUNT_POINT)) + { + status = STATUS_INVALID_PARAMETER; + break; + } + in_buff = ExAllocatePool( NonPagedPool, in_size ); + if (!in_buff) + { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + memcpy( in_buff, irp->AssociatedIrp.SystemBuffer, in_size ); + status = query_mount_points( in_buff, in_size, + irp->AssociatedIrp.SystemBuffer, + irpsp->Parameters.DeviceIoControl.OutputBufferLength, + &irp->IoStatus ); + ExFreePool( in_buff ); break; + } case IOCTL_MOUNTMGR_DEFINE_UNIX_DRIVE: if (irpsp->Parameters.DeviceIoControl.InputBufferLength < sizeof(struct mountmgr_unix_drive)) - return STATUS_INVALID_PARAMETER; + { + status = STATUS_INVALID_PARAMETER; + break; + } irp->IoStatus.Information = 0; - irp->IoStatus.u.Status = define_unix_drive( irpsp->Parameters.DeviceIoControl.Type3InputBuffer, - irpsp->Parameters.DeviceIoControl.InputBufferLength ); + status = define_unix_drive( irp->AssociatedIrp.SystemBuffer, + irpsp->Parameters.DeviceIoControl.InputBufferLength ); break; case IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE: - if (irpsp->Parameters.DeviceIoControl.InputBufferLength < sizeof(struct mountmgr_unix_drive)) - return STATUS_INVALID_PARAMETER; - irp->IoStatus.u.Status = query_unix_drive( irpsp->Parameters.DeviceIoControl.Type3InputBuffer, - irpsp->Parameters.DeviceIoControl.InputBufferLength, - irp->MdlAddress->StartVa, - irpsp->Parameters.DeviceIoControl.OutputBufferLength, - &irp->IoStatus ); - break; + { + void *in_buff; + ULONG in_size = irpsp->Parameters.DeviceIoControl.InputBufferLength; + + if (in_size < sizeof(struct mountmgr_unix_drive)) + { + status = STATUS_INVALID_PARAMETER; + break; + } + in_buff = ExAllocatePool( NonPagedPool, in_size ); + if (!in_buff) + { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + memcpy( in_buff, irp->AssociatedIrp.SystemBuffer, in_size ); + status = query_unix_drive( in_buff, in_size, + irp->AssociatedIrp.SystemBuffer, + irpsp->Parameters.DeviceIoControl.OutputBufferLength, + &irp->IoStatus ); + ExFreePool( in_buff ); + break; + } default: FIXME( "ioctl %x not supported\n", irpsp->Parameters.DeviceIoControl.IoControlCode ); - irp->IoStatus.u.Status = STATUS_NOT_SUPPORTED; + status = STATUS_NOT_SUPPORTED; break; } + irp->IoStatus.u.Status = status; IoCompleteRequest( irp, IO_NO_INCREMENT ); - return irp->IoStatus.u.Status; + return status; } /* main entry point for the mount point manager driver */ @@ -402,6 +437,7 @@ NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) static const WCHAR device_mountmgrW[] = {'\\','D','e','v','i','c','e','\\','M','o','u','n','t','P','o','i','n','t','M','a','n','a','g','e','r',0}; static const WCHAR link_mountmgrW[] = {'\\','?','?','\\','M','o','u','n','t','P','o','i','n','t','M','a','n','a','g','e','r',0}; static const WCHAR harddiskW[] = {'\\','D','r','i','v','e','r','\\','H','a','r','d','d','i','s','k',0}; + static const WCHAR usbhubW[] = {'\\','D','r','i','v','e','r','\\','u','s','b','h','u','b',0}; static const WCHAR devicemapW[] = {'H','A','R','D','W','A','R','E','\\','D','E','V','I','C','E','M','A','P',0}; static const WCHAR parallelW[] = {'P','A','R','A','L','L','E','L',' ','P','O','R','T','S',0}; static const WCHAR serialW[] = {'S','E','R','I','A','L','C','O','M','M',0}; @@ -443,6 +479,9 @@ NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) RtlInitUnicodeString( &nameW, harddiskW ); status = IoCreateDriver( &nameW, harddisk_driver_entry ); + RtlInitUnicodeString( &nameW, usbhubW ); + status = IoCreateDriver( &nameW, usbhub_driver_entry ); + initialize_hal(); initialize_diskarbitration(); diff --git a/dlls/mountmgr.sys/mountmgr.h b/dlls/mountmgr.sys/mountmgr.h index a47a3e3..59bcd4e 100644 --- a/dlls/mountmgr.sys/mountmgr.h +++ b/dlls/mountmgr.sys/mountmgr.h @@ -38,6 +38,9 @@ extern void initialize_hal(void) DECLSPEC_HIDDEN; extern void initialize_diskarbitration(void) DECLSPEC_HIDDEN; +extern NTSTATUS WINAPI usbhub_driver_entry( DRIVER_OBJECT *driver, + UNICODE_STRING *path ) DECLSPEC_HIDDEN; + /* device functions */ enum device_type @@ -71,3 +74,8 @@ extern struct mount_point *add_volume_mount_point( DEVICE_OBJECT *device, UNICOD const GUID *guid ) DECLSPEC_HIDDEN; extern void delete_mount_point( struct mount_point *mount ) DECLSPEC_HIDDEN; extern void set_mount_point_id( struct mount_point *mount, const void *id, unsigned int id_len ) DECLSPEC_HIDDEN; + +/* usb functions */ + +extern void add_usb_devices(void) DECLSPEC_HIDDEN; +extern void remove_usb_devices(void) DECLSPEC_HIDDEN; diff --git a/dlls/mountmgr.sys/usbhub.c b/dlls/mountmgr.sys/usbhub.c new file mode 100644 index 0000000..698cf7b --- /dev/null +++ b/dlls/mountmgr.sys/usbhub.c @@ -0,0 +1,2022 @@ +/* + * Copyright 2008 - 2011 Alexander Morozov for Etersoft + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" + +#include +#include + +#ifdef HAVE_LIBUSB_H +#include +#elif defined(HAVE_USB_H) +#include +#undef USB_ENDPOINT_TYPE_MASK +#undef USB_ENDPOINT_TYPE_CONTROL +#undef USB_ENDPOINT_TYPE_ISOCHRONOUS +#undef USB_ENDPOINT_TYPE_BULK +#undef USB_ENDPOINT_TYPE_INTERRUPT +#endif + +#define NONAMELESSUNION +#define NONAMELESSSTRUCT +#define INITGUID + +#include "mountmgr.h" +#include "winreg.h" +#include "winsvc.h" +#include "winuser.h" +#include "setupapi.h" +#include "cfgmgr32.h" +#include "devguid.h" +#include "ddk/usbdrivr.h" +#include "ddk/usbioctl.h" +#include "wine/unicode.h" +#include "wine/debug.h" +#include "wine/list.h" + +WINE_DEFAULT_DEBUG_CHANNEL(usbhub); + +#ifdef HAVE_LIBUSB + +extern NTSTATUS CDECL __wine_add_device( DRIVER_OBJECT *driver, DEVICE_OBJECT *dev ); +extern DRIVER_OBJECT * CDECL __wine_get_driver_object( const WCHAR *service ); +extern NTSTATUS CDECL __wine_start_device( DEVICE_OBJECT *device ); +extern BOOL CDECL __wine_start_service( const WCHAR *name ); + +#define NUMBER_OF_PORTS 8 + +static const WCHAR usbW[] = {'U','S','B',0}; + +static struct list HostControllers = LIST_INIT(HostControllers); +static struct list Devices = LIST_INIT(Devices); + +struct HCDInstance +{ + struct list entry; + DEVICE_OBJECT *dev; + WCHAR *root_hub_name; +}; + +struct DeviceInstance +{ + struct list entry; + USHORT vid; + USHORT pid; + char *instance_id; + WCHAR *service; + DEVICE_OBJECT *pdo; +#ifdef HAVE_LIBUSB_H + libusb_device *dev; +#else + struct usb_device *dev; +#endif +}; + +struct PdoExtension +{ + struct DeviceInstance *instance; +}; + +static DRIVER_OBJECT *usbhub_driver; + +static CRITICAL_SECTION usbhub_cs; +static CRITICAL_SECTION_DEBUG usbhub_cs_debug = +{ + 0, 0, &usbhub_cs, + { &usbhub_cs_debug.ProcessLocksList, &usbhub_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": usbhub_cs") } +}; +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 struct HCDInstance *get_hcd_instance( DEVICE_OBJECT *device ) +{ + struct HCDInstance *instance; + + LIST_FOR_EACH_ENTRY( instance, &HostControllers, struct HCDInstance, entry ) + if (instance->dev == device) + return instance; + return NULL; +} + +static void add_data( unsigned char **dst, ULONG *dst_size, const void *src, ULONG src_size ) +{ + int copy; + + copy = (src_size >= *dst_size) ? *dst_size : src_size; + memcpy( *dst, src, copy ); + *dst += copy; + *dst_size -= copy; +} + +#ifdef HAVE_LIBUSB_H + +struct DeviceInstance *get_device_by_index( libusb_device *device, + ULONG connection_index, ULONG *addr ) +{ + struct DeviceInstance *instance; + uint8_t bus_number = libusb_get_bus_number( device ); + ULONG index = 0; + + LIST_FOR_EACH_ENTRY( instance, &Devices, struct DeviceInstance, entry ) + if (instance->dev && instance->dev != device && + libusb_get_bus_number( instance->dev ) == bus_number && + ++index == connection_index) + { + if (addr) + *addr = libusb_get_device_address( instance->dev ); + return instance; + } + return NULL; +} + +#else /* HAVE_LIBUSB_H */ + +struct DeviceInstance *get_device_by_index( struct usb_device *device, + ULONG connection_index, ULONG *addr ) +{ + struct DeviceInstance *instance; + struct usb_device *dev; + ULONG index = 0; + + for (dev = device->next; dev; dev = dev->next) + if (++index == connection_index) + LIST_FOR_EACH_ENTRY( instance, &Devices, struct DeviceInstance, entry ) + if (instance->dev == dev) + { + if (addr) + *addr = dev->devnum; + return instance; + } + return NULL; +} + +#endif /* HAVE_LIBUSB_H */ + +static NTSTATUS get_root_hub_name( struct HCDInstance *instance, void *buff, + ULONG size, ULONG_PTR *outsize ) +{ + USB_HCD_DRIVERKEY_NAME *name = buff; + ULONG name_size; + + if (size < sizeof(*name)) + return STATUS_BUFFER_TOO_SMALL; + RtlZeroMemory( buff, size ); + name_size = (strlenW(instance->root_hub_name) - 4 + 1) * sizeof(WCHAR); + name->ActualLength = sizeof(*name) - sizeof(WCHAR) + name_size; + if (size >= name->ActualLength) + { + memcpy( name->DriverKeyName, instance->root_hub_name + 4, name_size ); + *outsize = name->ActualLength; + } + else + *outsize = sizeof(*name); + return STATUS_SUCCESS; +} + +static NTSTATUS get_node_info( void *buff, ULONG size, ULONG_PTR *outsize ) +{ + USB_NODE_INFORMATION *node_info = buff; + + if (size < sizeof(*node_info)) + return STATUS_BUFFER_TOO_SMALL; + RtlZeroMemory( node_info, sizeof(*node_info) ); + node_info->u.HubInformation.HubDescriptor.bDescriptorLength = 9; + node_info->u.HubInformation.HubDescriptor.bDescriptorType = 41; + node_info->u.HubInformation.HubDescriptor.bNumberOfPorts = NUMBER_OF_PORTS; + *outsize = sizeof(*node_info); + return STATUS_SUCCESS; +} + +#ifdef HAVE_LIBUSB_H + +static NTSTATUS get_node_conn_info( struct DeviceInstance *inst, void *buff, + ULONG size, ULONG_PTR *outsize ) +{ + USB_NODE_CONNECTION_INFORMATION *conn_info = buff; + ULONG index = 0; + struct DeviceInstance *instance; + uint8_t bus_number = libusb_get_bus_number( inst->dev ); + NTSTATUS status = STATUS_UNSUCCESSFUL; + + if (size < sizeof(*conn_info)) + return STATUS_BUFFER_TOO_SMALL; + if (!conn_info->ConnectionIndex || + conn_info->ConnectionIndex > NUMBER_OF_PORTS) + return STATUS_INVALID_PARAMETER; + RtlZeroMemory( (ULONG *)conn_info + 1, sizeof(*conn_info) - sizeof(ULONG) ); + LIST_FOR_EACH_ENTRY( instance, &Devices, struct DeviceInstance, entry ) + { + if (instance->dev && instance->dev != inst->dev && + libusb_get_bus_number( instance->dev ) == bus_number && + ++index == conn_info->ConnectionIndex) + { + struct libusb_device_descriptor desc; + libusb_device_handle *husb; + int config, ret; + + if (libusb_get_device_descriptor( instance->dev, &desc )) + break; + memcpy( &conn_info->DeviceDescriptor, &desc, + sizeof(USB_DEVICE_DESCRIPTOR) ); + ret = libusb_open( instance->dev, &husb ); + if (!ret) + { + ret = libusb_get_configuration( husb, &config ); + if (!ret) + conn_info->CurrentConfigurationValue = config; + libusb_close( husb ); + } + if (!ret) + { + conn_info->ConnectionStatus = 1; + *outsize = sizeof(*conn_info); + status = STATUS_SUCCESS; + } + break; + } + } + return status; +} + +#else /* HAVE_LIBUSB_H */ + +static NTSTATUS get_node_conn_info( struct DeviceInstance *inst, void *buff, + ULONG size, ULONG_PTR *outsize ) +{ + USB_NODE_CONNECTION_INFORMATION *conn_info = buff; + ULONG index = 0; + struct usb_device *dev; + NTSTATUS status = STATUS_UNSUCCESSFUL; + + if (size < sizeof(*conn_info)) + return STATUS_BUFFER_TOO_SMALL; + if (!conn_info->ConnectionIndex || + conn_info->ConnectionIndex > NUMBER_OF_PORTS) + return STATUS_INVALID_PARAMETER; + RtlZeroMemory( (ULONG *)conn_info + 1, sizeof(*conn_info) - sizeof(ULONG) ); + for (dev = inst->dev->next; dev; dev = dev->next) + if (++index == conn_info->ConnectionIndex) + { + usb_dev_handle *husb; + int ret = -1; + + memcpy( &conn_info->DeviceDescriptor, &dev->descriptor, + sizeof(USB_DEVICE_DESCRIPTOR) ); + husb = usb_open( inst->dev ); + if (husb) + { + ret = usb_control_msg( husb, 1 << 7, USB_REQ_GET_CONFIGURATION, + 0, 0, (char *)&conn_info->CurrentConfigurationValue, + sizeof(UCHAR), 0 ); + usb_close( husb ); + } + if (ret >= 0) + { + conn_info->ConnectionStatus = 1; + *outsize = sizeof(*conn_info); + status = STATUS_SUCCESS; + } + break; + } + return status; +} + +#endif /* HAVE_LIBUSB_H */ + +static NTSTATUS get_node_conn_driverkey_name( struct DeviceInstance *inst, + void *buff, ULONG size, ULONG_PTR *outsize ) +{ + static const WCHAR device_idW[] = {'U','S','B','\\', + 'V','i','d','_','%','0','4','x','&', + 'P','i','d','_','%','0','4','x','\\',0}; + + USB_NODE_CONNECTION_DRIVERKEY_NAME *driver_key_name = buff; + NTSTATUS status = STATUS_UNSUCCESSFUL; + WCHAR *dev_instance_idW, *bufW; + struct DeviceInstance *instance; + HDEVINFO set; + SP_DEVINFO_DATA devInfo = { sizeof(devInfo), { 0 } }; + ULONG len, index = 0; + + if (size < sizeof(*driver_key_name)) + return STATUS_BUFFER_TOO_SMALL; + instance = get_device_by_index( inst->dev, + driver_key_name->ConnectionIndex, NULL ); + if (instance == NULL) + return STATUS_INVALID_PARAMETER; + bufW = HeapAlloc( GetProcessHeap(), 0, + 2 * MAX_DEVICE_ID_LEN * sizeof(WCHAR) ); + if (bufW == NULL) + return STATUS_INSUFFICIENT_RESOURCES; + dev_instance_idW = bufW + MAX_DEVICE_ID_LEN; + snprintfW( dev_instance_idW, MAX_DEVICE_ID_LEN, device_idW, instance->vid, + instance->pid ); + len = strlenW(dev_instance_idW); + RtlMultiByteToUnicodeN( dev_instance_idW + len, + (MAX_DEVICE_ID_LEN - len) * sizeof(WCHAR), NULL, + instance->instance_id, strlen(instance->instance_id) + 1 ); + set = SetupDiGetClassDevsW( NULL, usbW, 0, DIGCF_ALLCLASSES ); + if (set == INVALID_HANDLE_VALUE) + { + HeapFree( GetProcessHeap(), 0, bufW ); + return STATUS_UNSUCCESSFUL; + } + while (SetupDiEnumDeviceInfo( set, index++, &devInfo )) + { + if (!SetupDiGetDeviceInstanceIdW( set, &devInfo, bufW, + MAX_DEVICE_ID_LEN, NULL )) + break; + if (!strcmpiW( dev_instance_idW, bufW )) + { + SetupDiGetDeviceRegistryPropertyW( set, &devInfo, SPDRP_DRIVER, + NULL, NULL, 0, &len ); + driver_key_name->ActualLength = 2 * sizeof(ULONG) + len; + if (size < driver_key_name->ActualLength) + { + status = STATUS_SUCCESS; + *outsize = sizeof(*driver_key_name); + } + else if (SetupDiGetDeviceRegistryPropertyW( set, &devInfo, + SPDRP_DRIVER, NULL, (BYTE *)driver_key_name->DriverKeyName, + len, NULL )) + { + status = STATUS_SUCCESS; + *outsize = driver_key_name->ActualLength; + } + break; + } + } + SetupDiDestroyDeviceInfoList( set ); + HeapFree( GetProcessHeap(), 0, bufW ); + return status; +} + +static NTSTATUS WINAPI usbhub_ioctl( DEVICE_OBJECT *device, IRP *irp ) +{ + IO_STACK_LOCATION *irpsp; + NTSTATUS status = STATUS_UNSUCCESSFUL; + struct DeviceInstance *inst; + struct HCDInstance *hcd_inst; + ULONG_PTR info = 0; + + TRACE( "%p, %p\n", device, irp ); + + EnterCriticalSection( &usbhub_cs ); + irpsp = IoGetCurrentIrpStackLocation( irp ); + if (device_exists( device )) + { + inst = ((struct PdoExtension *)device->DeviceExtension)->instance; + if (inst->service) goto done; + + switch (irpsp->Parameters.DeviceIoControl.IoControlCode) + { + case IOCTL_USB_GET_NODE_INFORMATION: + status = get_node_info( irp->AssociatedIrp.SystemBuffer, + irpsp->Parameters.DeviceIoControl.OutputBufferLength, &info ); + break; + case IOCTL_USB_GET_NODE_CONNECTION_INFORMATION: + status = get_node_conn_info( inst, irp->AssociatedIrp.SystemBuffer, + irpsp->Parameters.DeviceIoControl.OutputBufferLength, &info ); + break; + case IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME: + status = get_node_conn_driverkey_name( inst, + irp->AssociatedIrp.SystemBuffer, + irpsp->Parameters.DeviceIoControl.OutputBufferLength, &info ); + break; + default: + FIXME( "IOCTL %08x is not implemented\n", + irpsp->Parameters.DeviceIoControl.IoControlCode ); + } + } + else if ((hcd_inst = get_hcd_instance( device ))) + { + switch (irpsp->Parameters.DeviceIoControl.IoControlCode) + { + case IOCTL_USB_GET_ROOT_HUB_NAME: + status = get_root_hub_name( hcd_inst, irp->AssociatedIrp.SystemBuffer, + irpsp->Parameters.DeviceIoControl.OutputBufferLength, &info ); + break; + default: + FIXME( "IOCTL %08x is not implemented for HCD\n", + irpsp->Parameters.DeviceIoControl.IoControlCode ); + } + } + +done: + LeaveCriticalSection( &usbhub_cs ); + irp->IoStatus.u.Status = status; + irp->IoStatus.Information = info; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + + return status; +} + +#ifdef HAVE_LIBUSB_H + +static NTSTATUS WINAPI usbhub_internal_ioctl( DEVICE_OBJECT *device, IRP *irp ) +{ + IO_STACK_LOCATION *irpsp; + URB *urb; + NTSTATUS status = STATUS_UNSUCCESSFUL; + struct DeviceInstance *inst; + + 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 = IoGetCurrentIrpStackLocation( irp ); + urb = irpsp->Parameters.Others.Argument1; + + switch (irpsp->Parameters.DeviceIoControl.IoControlCode) + { + case IOCTL_INTERNAL_USB_SUBMIT_URB: + switch (urb->u.UrbHeader.Function) + { + case URB_FUNCTION_SELECT_CONFIGURATION: + { + struct _URB_SELECT_CONFIGURATION *request = + &urb->u.UrbSelectConfiguration; + libusb_device_handle *husb; + + TRACE( "URB_FUNCTION_SELECT_CONFIGURATION\n" ); + + if (!libusb_open( inst->dev, &husb )) + { + USB_CONFIGURATION_DESCRIPTOR *conf_desc = + request->ConfigurationDescriptor; + struct libusb_config_descriptor *conf; + int ret; + + ret = libusb_set_configuration( husb, (conf_desc != NULL) ? + conf_desc->bConfigurationValue : -1 ); + if (ret < 0) + ; + else if (conf_desc == NULL) + status = STATUS_SUCCESS; + else if (!libusb_get_active_config_descriptor( inst->dev, &conf )) + { + USBD_INTERFACE_INFORMATION *if_info = &request->Interface; + const struct libusb_interface_descriptor *intf; + ULONG k, n; + + /* FIXME: case of num_altsetting > 1 */ + + for (n = 0; n < conf_desc->bNumInterfaces; ++n) + { + intf = &conf->interface[n].altsetting[0]; + if_info->Class = intf->bInterfaceClass; + if_info->SubClass = intf->bInterfaceSubClass; + if_info->Protocol = intf->bInterfaceProtocol; + if_info->InterfaceHandle = + (void *)(intf->bInterfaceNumber + 1); + for (k = 0; k < if_info->NumberOfPipes; ++k) + { + if_info->Pipes[k].MaximumPacketSize = + intf->endpoint[k].wMaxPacketSize; + if_info->Pipes[k].EndpointAddress = + intf->endpoint[k].bEndpointAddress; + if_info->Pipes[k].Interval = + intf->endpoint[k].bInterval; + if_info->Pipes[k].PipeType = + intf->endpoint[k].bmAttributes & 3; + if_info->Pipes[k].PipeHandle = + (void *)(intf->endpoint[k].bEndpointAddress + + ((intf->bInterfaceNumber + 1) << 8)); + } + if_info = (USBD_INTERFACE_INFORMATION *) + ((char *)if_info + if_info->Length); + } + libusb_free_config_descriptor( conf ); + status = STATUS_SUCCESS; + } + libusb_close( husb ); + } + } + break; + case URB_FUNCTION_SELECT_INTERFACE: + { + struct _URB_SELECT_INTERFACE *request = + &urb->u.UrbSelectInterface; + libusb_device_handle *husb; + + TRACE( "URB_FUNCTION_SELECT_INTERFACE\n" ); + + if (!libusb_open( inst->dev, &husb )) + { + int ret; + + ret = libusb_claim_interface( husb, + request->Interface.InterfaceNumber ); + if (!ret) + { + ret = libusb_set_interface_alt_setting( husb, + request->Interface.InterfaceNumber, + request->Interface.AlternateSetting ); + if (!libusb_release_interface( husb, + request->Interface.InterfaceNumber ) && !ret) + status = STATUS_SUCCESS; + } + libusb_close( husb ); + } + } + break; + case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: + { + struct _URB_BULK_OR_INTERRUPT_TRANSFER *request = + &urb->u.UrbBulkOrInterruptTransfer; + unsigned char *buf = request->TransferBuffer; + libusb_device_handle *husb; + + TRACE( "URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER\n" ); + + if (buf == NULL && request->TransferBufferMDL != NULL) + buf = request->TransferBufferMDL->MappedSystemVa; + if (!libusb_open( inst->dev, &husb )) + { + int ret, transferred; + + ret = libusb_claim_interface( husb, + ((int)request->PipeHandle >> 8) - 1 ); + if (!ret) + { + /* FIXME: add support for an interrupt transfer */ + ret = libusb_bulk_transfer( husb, + (unsigned int)request->PipeHandle, + buf, request->TransferBufferLength, + &transferred, 0 ); + if (!libusb_release_interface( husb, + ((int)request->PipeHandle >> 8) - 1 ) && !ret) + { + request->TransferBufferLength = transferred; + status = STATUS_SUCCESS; + } + } + libusb_close( husb ); + } + } + break; + case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE: + { + struct _URB_CONTROL_DESCRIPTOR_REQUEST *request = + &urb->u.UrbControlDescriptorRequest; + ULONG size = request->TransferBufferLength; + unsigned char *buf = request->TransferBuffer; + + TRACE( "URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE\n" ); + + if (!size) + { + status = STATUS_SUCCESS; + break; + } + if (buf == NULL && request->TransferBufferMDL != NULL) + buf = request->TransferBufferMDL->MappedSystemVa; + if (buf == NULL) + { + status = STATUS_INVALID_PARAMETER; + break; + } + + switch (request->DescriptorType) + { + case USB_DEVICE_DESCRIPTOR_TYPE: + TRACE( "USB_DEVICE_DESCRIPTOR_TYPE\n" ); + { + struct libusb_device_descriptor desc; + + if (libusb_get_device_descriptor( inst->dev, &desc )) + break; + memcpy( buf, &desc, (size < sizeof(USB_DEVICE_DESCRIPTOR)) ? + size : sizeof(USB_DEVICE_DESCRIPTOR) ); + status = STATUS_SUCCESS; + } + break; + case USB_CONFIGURATION_DESCRIPTOR_TYPE: + TRACE( "USB_CONFIGURATION_DESCRIPTOR_TYPE\n" ); + { + unsigned int i, k; + struct libusb_config_descriptor *conf; + const struct libusb_interface_descriptor *intf; + const struct libusb_endpoint_descriptor *endp; + + /* FIXME: case of num_altsetting > 1 */ + + if (libusb_get_active_config_descriptor( inst->dev, &conf )) + break; + add_data( &buf, &size, conf, + sizeof(USB_CONFIGURATION_DESCRIPTOR) ); + if (size > 0 && conf->extra) + add_data( &buf, &size, conf->extra, conf->extra_length ); + for (i = 0; i < conf->bNumInterfaces; ++i) + { + intf = &conf->interface[i].altsetting[0]; + if (size > 0) + add_data( &buf, &size, intf, + sizeof(USB_INTERFACE_DESCRIPTOR) ); + if (size > 0 && intf->extra) + add_data( &buf, &size, intf->extra, intf->extra_length ); + for (k = 0; k < intf->bNumEndpoints; ++k) + { + endp = &intf->endpoint[k]; + if (size > 0) + add_data( &buf, &size, endp, + sizeof(USB_ENDPOINT_DESCRIPTOR) ); + if (size > 0 && endp->extra) + add_data( &buf, &size, endp->extra, + endp->extra_length ); + } + } + libusb_free_config_descriptor( conf ); + status = STATUS_SUCCESS; + } + break; + case USB_STRING_DESCRIPTOR_TYPE: + TRACE( "USB_STRING_DESCRIPTOR_TYPE\n" ); + { + libusb_device_handle *husb; + int ret; + + if (!libusb_open( inst->dev, &husb )) + { + ret = libusb_get_string_descriptor( husb, request->Index, + request->LanguageId, buf, size ); + libusb_close( husb ); + if (ret < 0) break; + status = STATUS_SUCCESS; + } + } + } + } + break; + case URB_FUNCTION_GET_STATUS_FROM_DEVICE: + { + struct _URB_CONTROL_GET_STATUS_REQUEST *request = + &urb->u.UrbControlGetStatusRequest; + void *buf = request->TransferBuffer; + libusb_device_handle *husb; + int ret; + + TRACE( "URB_FUNCTION_GET_STATUS_FROM_DEVICE\n" ); + + if (buf == NULL && request->TransferBufferMDL != NULL) + buf = request->TransferBufferMDL->MappedSystemVa; + if (buf == NULL || request->TransferBufferLength < sizeof(USHORT)) + { + status = STATUS_INVALID_PARAMETER; + break; + } + if (!libusb_open( inst->dev, &husb )) + { + ret = libusb_control_transfer( husb, 1 << 7, + LIBUSB_REQUEST_GET_STATUS, 0, request->Index, buf, + sizeof(USHORT), 0 ); + libusb_close( husb ); + if (ret < 0) break; + status = STATUS_SUCCESS; + } + } + break; + case URB_FUNCTION_VENDOR_DEVICE: + case URB_FUNCTION_VENDOR_INTERFACE: + case URB_FUNCTION_VENDOR_ENDPOINT: + case URB_FUNCTION_CLASS_DEVICE: + case URB_FUNCTION_CLASS_INTERFACE: + case URB_FUNCTION_CLASS_ENDPOINT: + { + libusb_device_handle *husb; + struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST *request = + &urb->u.UrbControlVendorClassRequest; + unsigned char *req_buf = request->TransferBuffer; + ULONG size = request->TransferBufferLength; + + TRACE( "URB_FUNCTION_{VENDOR,CLASS}_*\n" ); + + if (req_buf == NULL && request->TransferBufferMDL != NULL) + req_buf = request->TransferBufferMDL->MappedSystemVa; + if (size && req_buf == NULL) + { + status = STATUS_INVALID_PARAMETER; + break; + } + if (!libusb_open( inst->dev, &husb )) + { + UCHAR req_type = request->RequestTypeReservedBits; + unsigned char *buf; + int ret; + + switch (urb->u.UrbHeader.Function) + { + case URB_FUNCTION_VENDOR_DEVICE: req_type |= 0x40; break; + case URB_FUNCTION_VENDOR_INTERFACE: req_type |= 0x41; break; + case URB_FUNCTION_VENDOR_ENDPOINT: req_type |= 0x42; break; + case URB_FUNCTION_CLASS_DEVICE: req_type |= 0x20; break; + case URB_FUNCTION_CLASS_INTERFACE: req_type |= 0x21; break; + case URB_FUNCTION_CLASS_ENDPOINT: req_type |= 0x22; break; + } + buf = HeapAlloc( GetProcessHeap(), 0, size ); + if (buf != NULL) + { + memcpy( buf, req_buf, size ); + if (request->TransferFlags & USBD_TRANSFER_DIRECTION_IN) + req_type |= (1 << 7); + ret = libusb_control_transfer( husb, req_type, + request->Request, request->Value, request->Index, + buf, size, 0 ); + if (ret >= 0) + { + if (request->TransferFlags & USBD_TRANSFER_DIRECTION_IN) + { + request->TransferBufferLength = + (ret < size) ? ret : size; + memcpy( req_buf, buf, request->TransferBufferLength ); + } + status = STATUS_SUCCESS; + } + HeapFree( GetProcessHeap(), 0, buf ); + } + libusb_close( husb ); + } + } + break; + case URB_FUNCTION_GET_CONFIGURATION: + { + struct _URB_CONTROL_GET_CONFIGURATION_REQUEST *request = + &urb->u.UrbControlGetConfigurationRequest; + char *buf = request->TransferBuffer; + libusb_device_handle *husb; + int ret, config; + + TRACE( "URB_FUNCTION_GET_CONFIGURATION\n" ); + + if (buf == NULL && request->TransferBufferMDL != NULL) + buf = request->TransferBufferMDL->MappedSystemVa; + if (buf == NULL || request->TransferBufferLength < 1) + { + status = STATUS_INVALID_PARAMETER; + break; + } + if (!libusb_open( inst->dev, &husb )) + { + ret = libusb_get_configuration( husb, &config ); + libusb_close( husb ); + if (ret < 0) break; + *buf = config; + status = STATUS_SUCCESS; + } + } + break; + default: + FIXME( "unsupported URB function %x\n", urb->u.UrbHeader.Function ); + } + urb->u.UrbHeader.Status = status; + break; + default: + FIXME( "IOCTL %08x is not implemented\n", + irpsp->Parameters.DeviceIoControl.IoControlCode ); + } + +done: + LeaveCriticalSection( &usbhub_cs ); + irp->IoStatus.u.Status = status; + irp->IoStatus.Information = 0; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + + return status; +} + +#else /* HAVE_LIBUSB_H */ + +static NTSTATUS WINAPI usbhub_internal_ioctl( DEVICE_OBJECT *device, IRP *irp ) +{ + IO_STACK_LOCATION *irpsp; + URB *urb; + NTSTATUS status = STATUS_UNSUCCESSFUL; + struct DeviceInstance *inst; + + 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 = IoGetCurrentIrpStackLocation( irp ); + urb = irpsp->Parameters.Others.Argument1; + + switch (irpsp->Parameters.DeviceIoControl.IoControlCode) + { + case IOCTL_INTERNAL_USB_SUBMIT_URB: + switch (urb->u.UrbHeader.Function) + { + case URB_FUNCTION_SELECT_CONFIGURATION: + { + struct _URB_SELECT_CONFIGURATION *request = + &urb->u.UrbSelectConfiguration; + usb_dev_handle *husb; + + TRACE( "URB_FUNCTION_SELECT_CONFIGURATION\n" ); + + husb = usb_open( inst->dev ); + if (husb) + { + USB_CONFIGURATION_DESCRIPTOR *conf_desc = + urb->u.UrbSelectConfiguration.ConfigurationDescriptor; + int ret; + + ret = usb_set_configuration( husb, (conf_desc != NULL) ? + conf_desc->bConfigurationValue : -1 ); + if (ret < 0) + ; + else if (conf_desc == NULL) + status = STATUS_SUCCESS; + else + { + USBD_INTERFACE_INFORMATION *if_info = &request->Interface; + struct usb_config_descriptor *conf; + struct usb_interface_descriptor *intf; + ULONG k, n; + + /* FIXME: case of num_altsetting > 1 */ + + for (n = 0; n < inst->dev->descriptor.bNumConfigurations; ++n) + if (inst->dev->config[n].bConfigurationValue == + conf_desc->bConfigurationValue) + { + conf = &inst->dev->config[n]; + break; + } + for (n = 0; n < conf_desc->bNumInterfaces; ++n) + { + intf = &conf->interface[n].altsetting[0]; + if_info->Class = intf->bInterfaceClass; + if_info->SubClass = intf->bInterfaceSubClass; + if_info->Protocol = intf->bInterfaceProtocol; + if_info->SubClass = intf->bInterfaceSubClass; + if_info->Protocol = intf->bInterfaceProtocol; + if_info->InterfaceHandle = + (void *)(intf->bInterfaceNumber + 1); + for (k = 0; k < if_info->NumberOfPipes; ++k) + { + if_info->Pipes[k].MaximumPacketSize = + intf->endpoint[k].wMaxPacketSize; + if_info->Pipes[k].EndpointAddress = + intf->endpoint[k].bEndpointAddress; + if_info->Pipes[k].Interval = + intf->endpoint[k].bInterval; + if_info->Pipes[k].PipeType = + intf->endpoint[k].bmAttributes & 3; + if_info->Pipes[k].PipeHandle = + (void *)(intf->endpoint[k].bEndpointAddress + + ((intf->bInterfaceNumber + 1) << 8)); + } + if_info = (USBD_INTERFACE_INFORMATION *) + ((char *)if_info + if_info->Length); + } + status = STATUS_SUCCESS; + } + usb_close( husb ); + } + } + break; + case URB_FUNCTION_SELECT_INTERFACE: + { + struct _URB_SELECT_INTERFACE *request = + &urb->u.UrbSelectInterface; + usb_dev_handle *husb; + + TRACE( "URB_FUNCTION_SELECT_INTERFACE\n" ); + + husb = usb_open( inst->dev ); + if (husb) + { + int ret; + + ret = usb_claim_interface( husb, + request->Interface.InterfaceNumber ); + if (!ret) + { + ret = usb_set_altinterface( husb, + request->Interface.AlternateSetting ); + if (!usb_release_interface( husb, + request->Interface.InterfaceNumber ) && !ret) + status = STATUS_SUCCESS; + } + usb_close( husb ); + } + } + break; + case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: + { + struct _URB_BULK_OR_INTERRUPT_TRANSFER *request = + &urb->u.UrbBulkOrInterruptTransfer; + char *buf = request->TransferBuffer; + usb_dev_handle *husb; + + TRACE( "URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER\n" ); + + if (buf == NULL && request->TransferBufferMDL != NULL) + buf = request->TransferBufferMDL->MappedSystemVa; + husb = usb_open( inst->dev ); + if (husb) + { + int ret; + + ret = usb_claim_interface( husb, + ((int)request->PipeHandle >> 8) - 1 ); + if (!ret) + { + /* FIXME: add support for an interrupt transfer */ + if (request->TransferFlags & USBD_TRANSFER_DIRECTION_IN) + ret = usb_bulk_read( husb, (int)request->PipeHandle & 0xff, + buf, request->TransferBufferLength, 0 ); + else + ret = usb_bulk_write( husb, (int)request->PipeHandle & 0xff, + buf, request->TransferBufferLength, 0 ); + if (!usb_release_interface( husb, + ((int)request->PipeHandle >> 8) - 1 ) && ret >= 0) + { + request->TransferBufferLength = ret; + status = STATUS_SUCCESS; + } + } + usb_close( husb ); + } + } + break; + case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE: + { + struct _URB_CONTROL_DESCRIPTOR_REQUEST *request = + &urb->u.UrbControlDescriptorRequest; + ULONG size = request->TransferBufferLength; + unsigned char *buf = request->TransferBuffer; + + TRACE( "URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE\n" ); + + if (!size) + { + status = STATUS_SUCCESS; + break; + } + if (buf == NULL && request->TransferBufferMDL != NULL) + buf = request->TransferBufferMDL->MappedSystemVa; + if (buf == NULL) + { + status = STATUS_INVALID_PARAMETER; + break; + } + + switch (request->DescriptorType) + { + case USB_DEVICE_DESCRIPTOR_TYPE: + TRACE( "USB_DEVICE_DESCRIPTOR_TYPE\n" ); + memcpy( buf, &inst->dev->descriptor, + (size < sizeof(USB_DEVICE_DESCRIPTOR)) ? + size : sizeof(USB_DEVICE_DESCRIPTOR) ); + status = STATUS_SUCCESS; + break; + case USB_CONFIGURATION_DESCRIPTOR_TYPE: + TRACE( "USB_CONFIGURATION_DESCRIPTOR_TYPE\n" ); + { + unsigned int i, k; + struct usb_config_descriptor *conf = &inst->dev->config[0]; + struct usb_interface_descriptor *intf; + struct usb_endpoint_descriptor *endp; + + /* FIXME: case of num_altsetting > 1 */ + + add_data( &buf, &size, conf, + sizeof(USB_CONFIGURATION_DESCRIPTOR) ); + if (size > 0 && conf->extra) + add_data( &buf, &size, conf->extra, conf->extralen ); + for (i = 0; i < conf->bNumInterfaces; ++i) + { + intf = &conf->interface[i].altsetting[0]; + if (size > 0) + add_data( &buf, &size, intf, + sizeof(USB_INTERFACE_DESCRIPTOR) ); + if (size > 0 && intf->extra) + add_data( &buf, &size, intf->extra, intf->extralen ); + for (k = 0; k < intf->bNumEndpoints; ++k) + { + endp = &intf->endpoint[k]; + if (size > 0) + add_data( &buf, &size, endp, + sizeof(USB_ENDPOINT_DESCRIPTOR) ); + if (size > 0 && endp->extra) + add_data( &buf, &size, endp->extra, + endp->extralen ); + } + } + status = STATUS_SUCCESS; + } + break; + case USB_STRING_DESCRIPTOR_TYPE: + TRACE( "USB_STRING_DESCRIPTOR_TYPE\n" ); + { + usb_dev_handle *husb; + int ret; + + husb = usb_open( inst->dev ); + if (husb) + { + ret = usb_get_string( husb, request->Index, + request->LanguageId, (void *)buf, size ); + if (ret >= 0) + status = STATUS_SUCCESS; + usb_close( husb ); + } + } + } + } + break; + case URB_FUNCTION_GET_STATUS_FROM_DEVICE: + { + struct _URB_CONTROL_GET_STATUS_REQUEST *request = + &urb->u.UrbControlGetStatusRequest; + void *buf = request->TransferBuffer; + usb_dev_handle *husb; + int ret; + + TRACE( "URB_FUNCTION_GET_STATUS_FROM_DEVICE\n" ); + + if (buf == NULL && request->TransferBufferMDL != NULL) + buf = request->TransferBufferMDL->MappedSystemVa; + if (buf == NULL || request->TransferBufferLength < sizeof(USHORT)) + { + status = STATUS_INVALID_PARAMETER; + break; + } + husb = usb_open( inst->dev ); + if (husb) + { + ret = usb_control_msg( husb, 1 << 7, USB_REQ_GET_STATUS, 0, + request->Index, buf, sizeof(USHORT), 0 ); + if (ret >= 0) + status = STATUS_SUCCESS; + usb_close( husb ); + } + } + break; + case URB_FUNCTION_VENDOR_DEVICE: + case URB_FUNCTION_VENDOR_INTERFACE: + case URB_FUNCTION_VENDOR_ENDPOINT: + case URB_FUNCTION_CLASS_DEVICE: + case URB_FUNCTION_CLASS_INTERFACE: + case URB_FUNCTION_CLASS_ENDPOINT: + { + usb_dev_handle *husb; + struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST *request = + &urb->u.UrbControlVendorClassRequest; + unsigned char *req_buf = request->TransferBuffer; + ULONG size = request->TransferBufferLength; + + TRACE( "URB_FUNCTION_{VENDOR,CLASS}_*\n" ); + + if (req_buf == NULL && request->TransferBufferMDL != NULL) + req_buf = request->TransferBufferMDL->MappedSystemVa; + if (size && req_buf == NULL) + { + status = STATUS_INVALID_PARAMETER; + break; + } + husb = usb_open( inst->dev ); + if (husb) + { + UCHAR req_type = request->RequestTypeReservedBits; + char *buf; + int ret; + + switch (urb->u.UrbHeader.Function) + { + case URB_FUNCTION_VENDOR_DEVICE: req_type |= 0x40; break; + case URB_FUNCTION_VENDOR_INTERFACE: req_type |= 0x41; break; + case URB_FUNCTION_VENDOR_ENDPOINT: req_type |= 0x42; break; + case URB_FUNCTION_CLASS_DEVICE: req_type |= 0x20; break; + case URB_FUNCTION_CLASS_INTERFACE: req_type |= 0x21; break; + case URB_FUNCTION_CLASS_ENDPOINT: req_type |= 0x22; break; + } + buf = HeapAlloc( GetProcessHeap(), 0, size ); + if (buf != NULL) + { + memcpy( buf, req_buf, size ); + if (request->TransferFlags & USBD_TRANSFER_DIRECTION_IN) + req_type |= (1 << 7); + ret = usb_control_msg( husb, req_type, request->Request, + request->Value, request->Index, buf, size, 0 ); + if (ret >= 0) + { + if (request->TransferFlags & USBD_TRANSFER_DIRECTION_IN) + { + request->TransferBufferLength = + (ret < size) ? ret : size; + memcpy( req_buf, buf, request->TransferBufferLength ); + } + status = STATUS_SUCCESS; + } + HeapFree( GetProcessHeap(), 0, buf ); + } + usb_close( husb ); + } + } + break; + case URB_FUNCTION_GET_CONFIGURATION: + { + struct _URB_CONTROL_GET_CONFIGURATION_REQUEST *request = + &urb->u.UrbControlGetConfigurationRequest; + char *buf = request->TransferBuffer; + usb_dev_handle *husb; + int ret; + + TRACE( "URB_FUNCTION_GET_CONFIGURATION\n" ); + + if (buf == NULL && request->TransferBufferMDL != NULL) + buf = request->TransferBufferMDL->MappedSystemVa; + if (buf == NULL || request->TransferBufferLength < 1) + { + status = STATUS_INVALID_PARAMETER; + break; + } + husb = usb_open( inst->dev ); + if (husb) + { + ret = usb_control_msg( husb, 1 << 7, + USB_REQ_GET_CONFIGURATION, 0, 0, buf, 1, 0 ); + if (ret >= 0) + status = STATUS_SUCCESS; + usb_close( husb ); + } + } + break; + default: + FIXME( "unsupported URB function %x\n", urb->u.UrbHeader.Function ); + } + urb->u.UrbHeader.Status = status; + break; + default: + FIXME( "IOCTL %08x is not implemented\n", + irpsp->Parameters.DeviceIoControl.IoControlCode ); + } + +done: + LeaveCriticalSection( &usbhub_cs ); + irp->IoStatus.u.Status = status; + irp->IoStatus.Information = 0; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + + return status; +} + +#endif /* HAVE_LIBUSB_H */ + +static NTSTATUS WINAPI usbhub_dispatch_pnp( DEVICE_OBJECT *device, IRP *irp ) +{ + static const WCHAR device_idW[] = {'U','S','B','\\', + 'V','i','d','_','%','0','4','x','&', + 'P','i','d','_','%','0','4','x',0}; + static const WCHAR root_hub_idW[] = {'U','S','B','\\', + 'R','O','O','T','_','H','U','B',0}; + + struct PdoExtension *dx; + IO_STACK_LOCATION *irpsp; + NTSTATUS status = STATUS_UNSUCCESSFUL; + ULONG_PTR info = 0; + + TRACE( "%p, %p\n", device, irp ); + + EnterCriticalSection( &usbhub_cs ); + irpsp = IoGetCurrentIrpStackLocation( irp ); + 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: + /* dx->instance->service is NULL for root hubs */ + if (dx->instance->service) + { + status = irp->IoStatus.u.Status; + info = irp->IoStatus.Information; + } + else + { + FIXME( "IRP_MN_QUERY_DEVICE_RELATIONS is not implemented for root hubs\n" ); + status = STATUS_NOT_IMPLEMENTED; + } + break; + case IRP_MN_QUERY_ID: + switch (irpsp->Parameters.QueryId.IdType) + { + case BusQueryDeviceID: + { + WCHAR *device_id = ExAllocatePool( PagedPool, dx->instance->service ? + sizeof(device_idW) : sizeof(root_hub_idW) ); + + if (device_id == NULL) + { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + if (dx->instance->service) + snprintfW( device_id, strlenW(device_idW) + 1, device_idW, + dx->instance->vid, dx->instance->pid ); + else + strcpyW( device_id, root_hub_idW ); + status = STATUS_SUCCESS; + info = (ULONG_PTR)device_id; + break; + } + case BusQueryInstanceID: + { + char *instance_id; + ULONG len; + ULONG size; + WCHAR *instance_idW; + + instance_id = strrchr( dx->instance->instance_id, '&' ); + instance_id = instance_id ? (instance_id + 1) : dx->instance->instance_id; + len = strlen(instance_id) + 1; + size = len * sizeof(WCHAR); + instance_idW = ExAllocatePool( PagedPool, size ); + if (instance_idW == NULL) + { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + RtlMultiByteToUnicodeN( instance_idW, size, NULL, instance_id, len ); + status = STATUS_SUCCESS; + info = (ULONG_PTR)instance_idW; + break; + } + default: + FIXME( "IRP_MN_QUERY_ID: IdType %u is not implemented\n", + irpsp->Parameters.QueryId.IdType ); + status = STATUS_NOT_IMPLEMENTED; + } + break; + default: + status = STATUS_SUCCESS; + } + +done: + LeaveCriticalSection( &usbhub_cs ); + irp->IoStatus.u.Status = status; + irp->IoStatus.Information = info; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + + return status; +} + +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','\\', + 'U','S','B','P','D','O','-','%','u',0}; + + static unsigned int last_pdo_num; + WCHAR *buf = RtlAllocateHeap( GetProcessHeap(), 0, 30 * sizeof(WCHAR) ); + + if (buf == NULL) return FALSE; + snprintfW( buf, 30, usbpdoW, last_pdo_num++ ); + RtlInitUnicodeString( pdo_name, buf ); + return TRUE; +} + +static DEVICE_OBJECT *create_pdo( struct DeviceInstance *inst, + DRIVER_OBJECT *hubdrv, ULONG flags ) +{ + UNICODE_STRING pdo_name; + DEVICE_OBJECT *usbdev = NULL; + + if (!create_pdo_name( &pdo_name )) return NULL; + if (IoCreateDevice( hubdrv, sizeof(struct PdoExtension), &pdo_name, + 0, 0, FALSE, &usbdev ) == STATUS_SUCCESS) + { + ((struct PdoExtension *)usbdev->DeviceExtension)->instance = inst; + usbdev->Flags |= flags; + usbdev->Flags &= ~DO_DEVICE_INITIALIZING; + } + RtlFreeUnicodeString( &pdo_name ); + return usbdev; +} + +static BOOL register_root_hub_device( DEVICE_OBJECT *dev, + unsigned int instance_id, UNICODE_STRING *link ) +{ + static const WCHAR root_hub_idW[] = {'U','S','B', + '\\','R','O','O','T','_','H','U','B', + '\\','%','u',0}; + + HDEVINFO set; + SP_DEVINFO_DATA devInfo; + WCHAR *devnameW; + ULONG size; + BOOL ret; + NTSTATUS status = STATUS_UNSUCCESSFUL; + + size = sizeof(root_hub_idW) + 16 * sizeof(WCHAR); + devnameW = HeapAlloc( GetProcessHeap(), 0, size ); + if (devnameW == NULL) return FALSE; + snprintfW( devnameW, size / sizeof(WCHAR), root_hub_idW, instance_id ); + + set = SetupDiGetClassDevsW( NULL, usbW, 0, DIGCF_ALLCLASSES ); + if (set == INVALID_HANDLE_VALUE) goto done; + devInfo.cbSize = sizeof(SP_DEVINFO_DATA); + ret = SetupDiCreateDeviceInfoW( set, devnameW, &GUID_DEVCLASS_USB, + NULL, NULL, 0, &devInfo ); + if (ret) + { + ret = SetupDiRegisterDeviceInfo( set, &devInfo, 0, NULL, NULL, NULL ); + if (!ret) goto done; + } + else if (ERROR_DEVINST_ALREADY_EXISTS != GetLastError()) goto done; + + status = IoRegisterDeviceInterface( dev, &GUID_DEVINTERFACE_USB_HUB, + NULL, link ); + if (status == STATUS_SUCCESS) + IoSetDeviceInterfaceState( link, TRUE ); +done: + if (set != INVALID_HANDLE_VALUE) + SetupDiDestroyDeviceInfoList( set ); + HeapFree( GetProcessHeap(), 0, devnameW ); + return (status == STATUS_SUCCESS) ? TRUE : FALSE; +} + +static void create_hcd_device( unsigned int instance_id, DRIVER_OBJECT *hubdrv, + UNICODE_STRING *link ) +{ + static const WCHAR usbfdoW[] = {'\\','D','e','v','i','c','e', + '\\','U','S','B','F','D','O','-','%','u',0}; + static const WCHAR usbhcdW[] = {'\\','D','o','s','D','e','v','i','c','e','s', + '\\','H','C','D','%','u',0}; + + WCHAR *fdo_buf = RtlAllocateHeap( GetProcessHeap(), 0, 30 * sizeof(WCHAR) ); + WCHAR *hcd_buf = RtlAllocateHeap( GetProcessHeap(), 0, 30 * sizeof(WCHAR) ); + UNICODE_STRING fdo_name, hcd_name; + struct HCDInstance *instance = NULL; + NTSTATUS status = STATUS_UNSUCCESSFUL; + + if (fdo_buf == NULL || hcd_buf == NULL) goto done; + instance = HeapAlloc( GetProcessHeap(), 0, sizeof(*instance) ); + if (instance == NULL) goto done; + instance->root_hub_name = HeapAlloc( GetProcessHeap(), 0, + link->Length + sizeof(WCHAR) ); + if (instance->root_hub_name == NULL) goto done; + memcpy( instance->root_hub_name, link->Buffer, link->Length ); + instance->root_hub_name[link->Length / sizeof(WCHAR)] = 0; + + snprintfW( fdo_buf, 30, usbfdoW, instance_id ); + RtlInitUnicodeString( &fdo_name, fdo_buf ); + snprintfW( hcd_buf, 30, usbhcdW, instance_id ); + RtlInitUnicodeString( &hcd_name, hcd_buf ); + + status = IoCreateDevice( hubdrv, 0, &fdo_name, 0, 0, FALSE, &instance->dev ); + if (status != STATUS_SUCCESS) goto done; + IoCreateSymbolicLink( &hcd_name, &fdo_name ); + instance->dev->Flags &= ~DO_DEVICE_INITIALIZING; + list_add_tail( &HostControllers, &instance->entry ); +done: + if (status != STATUS_SUCCESS && instance != NULL) + { + HeapFree( GetProcessHeap(), 0, instance->root_hub_name ); + HeapFree( GetProcessHeap(), 0, instance ); + } + RtlFreeUnicodeString( &fdo_name ); + RtlFreeUnicodeString( &hcd_name ); +} + +static void create_root_hub_device( USHORT vid, USHORT pid, void *dev, + DRIVER_OBJECT *hubdrv ) +{ + static unsigned int instance_id; + struct DeviceInstance *instance = NULL; + UNICODE_STRING link; + + instance = HeapAlloc( GetProcessHeap(), 0, sizeof(*instance) ); + if (instance == NULL) return; + instance->instance_id = HeapAlloc( GetProcessHeap(), 0, 16 ); + if (instance->instance_id == NULL) goto fail; + instance->vid = vid; + instance->pid = pid; + snprintf( instance->instance_id, 16, "%u", instance_id ); + instance->service = NULL; + instance->dev = dev; + + instance->pdo = create_pdo( instance, hubdrv, DO_POWER_PAGABLE ); + if (instance->pdo == NULL) goto fail; + list_add_tail( &Devices, &instance->entry ); + if (register_root_hub_device( instance->pdo, instance_id, &link )) + { + create_hcd_device( instance_id, hubdrv, &link ); + RtlFreeUnicodeString( &link ); + } + ++instance_id; + return; +fail: + HeapFree( GetProcessHeap(), 0, instance->instance_id ); + HeapFree( GetProcessHeap(), 0, instance ); + return; +} + +static BOOL enum_reg_usb_devices(void) +{ + SP_DEVINFO_DATA devInfo = { sizeof(devInfo), { 0 } }; + char *instance_id = NULL; + struct DeviceInstance *instance, *instance2; + HDEVINFO set; + DWORD size, i = 0; + USHORT vid, pid; + char *str, *buf; + BOOL ret; + + set = SetupDiGetClassDevsW( NULL, usbW, 0, DIGCF_ALLCLASSES ); + if (set == INVALID_HANDLE_VALUE) return FALSE; + + while (SetupDiEnumDeviceInfo( set, i++, &devInfo )) + { + /* get VID, PID and instance ID */ + buf = HeapAlloc( GetProcessHeap(), 0, MAX_DEVICE_ID_LEN ); + if (buf == NULL) goto fail; + ret = SetupDiGetDeviceInstanceIdA( set, &devInfo, buf, + MAX_DEVICE_ID_LEN, NULL ); + if (!ret) goto fail; + str = strstr( buf, "VID_" ); + if (str != NULL) + { + str += 4; + vid = strtol( str, NULL, 16 ); + str = strstr( str, "PID_" ); + } + if (str == NULL) + { + HeapFree( GetProcessHeap(), 0, buf ); + continue; + } + str += 4; + pid = strtol( str, NULL, 16 ); + str = strrchr( str, '\\' ); + if (str != NULL) ++str; + if (str == NULL || *str == 0) + { + ERR( "bad instance ID\n" ); + HeapFree( GetProcessHeap(), 0, buf ); + continue; + } + instance_id = HeapAlloc( GetProcessHeap(), 0, strlen(str) + 1 ); + if (instance_id == NULL) goto fail; + strcpy( instance_id, str ); + HeapFree( GetProcessHeap(), 0, buf ); + + /* get service name */ + SetupDiGetDeviceRegistryPropertyW( set, &devInfo, SPDRP_SERVICE, + NULL, NULL, 0, &size ); + buf = HeapAlloc( GetProcessHeap(), 0, size ); + if (buf == NULL) goto fail; + ret = SetupDiGetDeviceRegistryPropertyW( set, &devInfo, SPDRP_SERVICE, + NULL, (BYTE *)buf, size, NULL ); + if (!ret) + { + HeapFree( GetProcessHeap(), 0, buf ); + buf = NULL; + } + + /* add DeviceInstance structure to Devices list */ + instance = HeapAlloc( GetProcessHeap(), 0, sizeof(*instance) ); + if (instance == NULL) goto fail; + instance->vid = vid; + instance->pid = pid; + instance->instance_id = instance_id; + instance->service = (WCHAR *)buf; + instance->pdo = NULL; + instance->dev = NULL; + list_add_tail( &Devices, &instance->entry ); + instance_id = NULL; + } + + SetupDiDestroyDeviceInfoList( set ); + return TRUE; +fail: + HeapFree( GetProcessHeap(), 0, buf ); + HeapFree( GetProcessHeap(), 0, instance_id ); + SetupDiDestroyDeviceInfoList( set ); + LIST_FOR_EACH_ENTRY_SAFE( instance, instance2, &Devices, + struct DeviceInstance, entry ) + { + HeapFree( GetProcessHeap(), 0, instance->instance_id ); + HeapFree( GetProcessHeap(), 0, instance->service ); + list_remove( &instance->entry ); + HeapFree( GetProcessHeap(), 0, instance ); + } + return FALSE; +} + +static char *new_instance_id( USHORT vid, USHORT pid ) +{ + struct DeviceInstance *instance; + char *p, *prefix = NULL; + unsigned int id = 0, n, prefix_len = 0; + char *ret; + + LIST_FOR_EACH_ENTRY( instance, &Devices, struct DeviceInstance, entry ) + { + if (vid == instance->vid && pid == instance->pid) + { + if (prefix == NULL) + { + prefix = instance->instance_id; + p = strrchr( instance->instance_id, '&' ); + if (p == NULL) prefix_len = 0; + else prefix_len = p + 1 - prefix; + id = strtoul( prefix + prefix_len, NULL, 10 ) + 1; + } + else + { + p = strrchr( instance->instance_id, '&' ); + if (prefix_len) + { + if (p == NULL || p + 1 - instance->instance_id != prefix_len || + strncmp( instance->instance_id, prefix, prefix_len )) + continue; + } + else if (p != NULL) continue; + n = strtoul( instance->instance_id + prefix_len, NULL, 10 ) + 1; + if (n > id) id = n; + } + } + } + ret = HeapAlloc( GetProcessHeap(), 0, prefix_len + 16 ); + if (ret == NULL) return NULL; + memcpy( ret, prefix, prefix_len ); + snprintf( ret + prefix_len, prefix_len + 16, "%d", id ); + return ret; +} + +static void register_usb_device( USHORT vid, USHORT pid, void *dev ) +{ + static const WCHAR id_fmtW[] = {'U','S','B', + '\\','V','i','d','_','%','0','4','x', + '&','P','i','d','_','%','0','4','x', + '\\','%','s',0}; + + struct DeviceInstance *instance; + HDEVINFO set = INVALID_HANDLE_VALUE; + SP_DEVINFO_DATA devInfo; + WCHAR *devnameW = NULL, *instance_idW = NULL; + char *instance_id; + ULONG size; + + instance_id = new_instance_id( vid, pid ); + if (instance_id == NULL) return; + + instance = HeapAlloc( GetProcessHeap(), 0, sizeof(*instance) ); + if (instance == NULL) + { + HeapFree( GetProcessHeap(), 0, instance_id ); + goto done; + } + instance->vid = vid; + instance->pid = pid; + instance->instance_id = instance_id; + instance->service = NULL; + instance->pdo = NULL; + instance->dev = dev; + list_add_tail( &Devices, &instance->entry ); + + size = (strlen(instance_id) + 1) * sizeof(WCHAR); + instance_idW = HeapAlloc( GetProcessHeap(), 0, size ); + if (instance_idW == NULL) goto done; + RtlMultiByteToUnicodeN( instance_idW, size, NULL, + instance_id, strlen(instance_id) + 1 ); + + size = sizeof(id_fmtW) + (strlenW(instance_idW) - 2) * sizeof(WCHAR); + devnameW = HeapAlloc( GetProcessHeap(), 0, size ); + if (devnameW == NULL) goto done; + snprintfW( devnameW, size / sizeof(WCHAR), id_fmtW, vid, pid, instance_idW ); + + set = SetupDiGetClassDevsW( NULL, usbW, 0, DIGCF_ALLCLASSES ); + if (set == INVALID_HANDLE_VALUE) goto done; + devInfo.cbSize = sizeof(SP_DEVINFO_DATA); + if (SetupDiCreateDeviceInfoW( set, devnameW, &GUID_DEVCLASS_USB, + NULL, NULL, 0, &devInfo )) + SetupDiRegisterDeviceInfo( set, &devInfo, 0, NULL, NULL, NULL ); +done: + if (set != INVALID_HANDLE_VALUE) + SetupDiDestroyDeviceInfoList( set ); + HeapFree( GetProcessHeap(), 0, devnameW ); + HeapFree( GetProcessHeap(), 0, instance_idW ); +} + +static void start_device_drivers( DRIVER_OBJECT *hubdrv ) +{ + struct DeviceInstance *instance; + DRIVER_OBJECT *driver; + DEVICE_OBJECT *dev; + NTSTATUS status; + + LIST_FOR_EACH_ENTRY( instance, &Devices, struct DeviceInstance, entry ) + { + if (instance->service == NULL || instance->dev == NULL || + instance->pdo != NULL) continue; + if (__wine_start_service( instance->service )) + { + instance->pdo = create_pdo( instance, hubdrv, + DO_BUS_ENUMERATED_DEVICE | DO_POWER_PAGABLE ); + if (instance->pdo == NULL) continue; + while (!(driver = __wine_get_driver_object( instance->service ))) + Sleep( 100 ); + status = __wine_add_device( driver, instance->pdo ); + dev = instance->pdo->AttachedDevice; + if (status == STATUS_SUCCESS && dev != NULL) + __wine_start_device( dev ); + } + } +} + +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 = IoGetNextIrpStackLocation( irp ); + 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; + + LIST_FOR_EACH_ENTRY( instance, &Devices, struct DeviceInstance, entry ) + if (instance->dev == dev) + return FALSE; + 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) +{ + return libusb_init( NULL ); +} + +void add_usb_devices(void) +{ + libusb_device **devs, *dev; + struct libusb_device_descriptor desc; + unsigned int i = 0; + struct DeviceInstance *instance; + BOOL new_device; + + EnterCriticalSection( &usbhub_cs ); + if (!libusb_initialized || libusb_get_device_list( NULL, &devs ) < 0) + goto end; + while ((dev = devs[i++])) + { + if (!is_new( dev )) + continue; + if (libusb_get_device_descriptor( dev, &desc )) + { + ERR( "failed to get USB device descriptor\n" ); + continue; + } + TRACE( "add %04x:%04x\n", desc.idVendor, desc.idProduct ); + libusb_ref_device( dev ); + if (libusb_get_device_address( dev ) == 1) + { + create_root_hub_device( desc.idVendor, desc.idProduct, dev, usbhub_driver ); + continue; + } + new_device = TRUE; + LIST_FOR_EACH_ENTRY( instance, &Devices, struct DeviceInstance, entry ) + { + if (instance->dev == NULL && desc.idVendor == instance->vid && + desc.idProduct == instance->pid) + { + instance->dev = dev; + new_device = FALSE; + break; + } + } + if (new_device) + register_usb_device( desc.idVendor, desc.idProduct, dev ); + } + libusb_free_device_list( devs, 1 ); + start_device_drivers( usbhub_driver ); +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 /* HAVE_LIBUSB_H */ + +static int initialize_libusb(void) +{ + usb_init(); + return 0; +} + +void add_usb_devices(void) +{ + struct usb_device *dev; + struct usb_bus *bus; + struct usb_device_descriptor *desc; + struct DeviceInstance *instance; + BOOL new_device; + + EnterCriticalSection( &usbhub_cs ); + if (!libusb_initialized) + goto end; + usb_find_busses(); + usb_find_devices(); + for (bus = usb_busses; bus; bus = bus->next) + for (dev = bus->devices; dev; dev = dev->next) + { + if (dev->devnum > 1 || !is_new( dev )) continue; + desc = &dev->descriptor; + TRACE( "add %04x:%04x\n", desc->idVendor, desc->idProduct ); + create_root_hub_device( desc->idVendor, desc->idProduct, dev, + usbhub_driver ); + } + for (bus = usb_busses; bus; bus = bus->next) + for (dev = bus->devices; dev; dev = dev->next) + { + if (dev->devnum <= 1 || !is_new( dev )) continue; + desc = &dev->descriptor; + TRACE( "add %04x:%04x\n", desc->idVendor, desc->idProduct ); + new_device = TRUE; + LIST_FOR_EACH_ENTRY( instance, &Devices, struct DeviceInstance, entry ) + { + if (instance->dev == NULL && desc->idVendor == instance->vid && + desc->idProduct == instance->pid) + { + instance->dev = dev; + new_device = FALSE; + break; + } + } + if (new_device) + register_usb_device( desc->idVendor, desc->idProduct, dev ); + } + start_device_drivers( usbhub_driver ); +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 /* HAVE_LIBUSB_H */ + +#else /* HAVE_LIBUSB */ + +void add_usb_devices(void) +{ +} + +void remove_usb_devices(void) +{ +} + +#endif /* HAVE_LIBUSB */ + +#ifdef HAVE_LIBUSB + +static DWORD CALLBACK initialize_usbhub( void *arg ) +{ + static const WCHAR usbhub_started_eventW[] = {'_','_','w','i','n','e', + '_','U','s','b','h','u','b', + 'S','t','a','r','t','e','d',0}; + + HANDLE event; + + EnterCriticalSection( &usbhub_cs ); + if (!enum_reg_usb_devices()) + ERR( "failed to enumerate USB devices\n" ); + else if (initialize_libusb()) + ERR( "failed to initialize libusb\n" ); + else + libusb_initialized = TRUE; + LeaveCriticalSection( &usbhub_cs ); + add_usb_devices(); + event = CreateEventW( NULL, TRUE, FALSE, usbhub_started_eventW ); + SetEvent( event ); + CloseHandle( event ); + return 0; +} + +#endif /* HAVE_LIBUSB */ + +NTSTATUS WINAPI usbhub_driver_entry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) +{ +#ifdef HAVE_LIBUSB + HANDLE thread; + + usbhub_driver = driver; + driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = usbhub_ioctl; + driver->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = usbhub_internal_ioctl; + driver->MajorFunction[IRP_MJ_PNP] = usbhub_dispatch_pnp; + + thread = CreateThread( NULL, 0, initialize_usbhub, NULL, 0, NULL ); + if (!thread) return STATUS_UNSUCCESSFUL; + CloseHandle( thread ); +#else + TRACE( "USB support not compiled in\n" ); +#endif + return STATUS_SUCCESS; +} diff --git a/dlls/ntoskrnl.exe/Makefile.in b/dlls/ntoskrnl.exe/Makefile.in index 2ab6d49..792d875 100644 --- a/dlls/ntoskrnl.exe/Makefile.in +++ b/dlls/ntoskrnl.exe/Makefile.in @@ -1,5 +1,6 @@ MODULE = ntoskrnl.exe IMPORTLIB = ntoskrnl.exe +IMPORTS = setupapi advapi32 C_SRCS = \ instr.c \ diff --git a/dlls/ntoskrnl.exe/ntoskrnl.c b/dlls/ntoskrnl.exe/ntoskrnl.c index e5668fd..05ad6e5 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/ntoskrnl.c @@ -26,13 +26,22 @@ #define NONAMELESSUNION #define NONAMELESSSTRUCT +#define INITGUID #include "ntstatus.h" #define WIN32_NO_STATUS #include "windef.h" #include "winternl.h" +#include "winioctl.h" +#include "winbase.h" +#include "winsvc.h" +#include "winuser.h" +#include "winreg.h" +#include "setupapi.h" +#include "cfgmgr32.h" #include "excpt.h" #include "ddk/ntddk.h" +#include "ddk/wdmguid.h" #include "wine/unicode.h" #include "wine/server.h" #include "wine/list.h" @@ -60,6 +69,15 @@ KSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable[4] = { { 0 } }; typedef void (WINAPI *PCREATE_PROCESS_NOTIFY_ROUTINE)(HANDLE,HANDLE,BOOLEAN); typedef void (WINAPI *PCREATE_THREAD_NOTIFY_ROUTINE)(HANDLE,HANDLE,BOOLEAN); +static CRITICAL_SECTION cs; +static CRITICAL_SECTION_DEBUG cs_debug = +{ + 0, 0, &cs, + { &cs_debug.ProcessLocksList, &cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": cs") } +}; +static CRITICAL_SECTION cs = { &cs_debug, -1, 0, 0, 0, 0 }; + static struct list Irps = LIST_INIT(Irps); struct IrpInstance @@ -68,6 +86,65 @@ struct IrpInstance IRP *irp; }; +static struct list DriverObjExtensions = LIST_INIT(DriverObjExtensions); + +struct DriverObjExtension +{ + struct list entry; + void *ptr; + DRIVER_OBJECT *driver; + void *id_addr; +}; + +static struct list Drivers = LIST_INIT(Drivers); + +struct DriverInstance +{ + struct list entry; + DRIVER_OBJECT *driver; + const WCHAR *service; + DWORD driver_thread_id; + DWORD process_id; +}; + +static struct list Interfaces = LIST_INIT(Interfaces); + +struct InterfaceInstance +{ + struct list entry; + WCHAR *link; + UNICODE_STRING target; + GUID guid; + int active; +}; + +static struct list InterfaceChangeNotifications = LIST_INIT(InterfaceChangeNotifications); + +struct InterfaceChangeNotification +{ + struct list entry; + GUID interface_class; + PDRIVER_NOTIFICATION_CALLBACK_ROUTINE callback; + void *context; +}; + +struct callback +{ + struct list entry; + PDRIVER_NOTIFICATION_CALLBACK_ROUTINE routine; + void *context; +}; + +static struct list Handles = LIST_INIT(Handles); + +struct HandleInstance +{ + struct list entry; + void *object; + HANDLE handle; + ULONG refs; +}; + #ifdef __i386__ #define DEFINE_FASTCALL1_ENTRYPOINT( name ) \ __ASM_STDCALL_FUNC( name, 4, \ @@ -97,6 +174,192 @@ static inline LPCSTR debugstr_us( const UNICODE_STRING *us ) return debugstr_wn( us->Buffer, us->Length / sizeof(WCHAR) ); } +BOOL CDECL __wine_start_service( const WCHAR *name ) +{ + SC_HANDLE scm, service; + BOOL ret; + + scm = OpenSCManagerA( NULL, NULL, SC_MANAGER_ALL_ACCESS ); + if (scm == NULL) + return FALSE; + + service = OpenServiceW( scm, name, SERVICE_ALL_ACCESS ); + if (service == NULL) + { + CloseServiceHandle( scm ); + return FALSE; + } + + do { + ret = StartServiceW( service, 0, NULL ); + if (!ret) + { + if (ERROR_SERVICE_ALREADY_RUNNING == GetLastError()) + ret = TRUE; + else if (ERROR_SERVICE_DATABASE_LOCKED == GetLastError()) + Sleep( 100 ); + else + break; + } + } while (!ret); + + CloseServiceHandle( service ); + CloseServiceHandle( scm ); + + return ret; +} + +/* get name of driver service for device with given id */ +static BOOL get_service( WCHAR *device_id, WCHAR **service_name ) +{ + SP_DEVINFO_DATA devInfo = { sizeof(devInfo), { 0 } }; + HDEVINFO set; + WCHAR *ptr, *enum_name, *id = NULL; + DWORD size, i = 0; + BOOL ret; + + *service_name = NULL; + ptr = strchrW( device_id, '\\' ); + if (!ptr) return FALSE; + size = ptr - device_id + 1; + enum_name = RtlAllocateHeap( GetProcessHeap(), 0, size * sizeof(WCHAR) ); + if (!enum_name) return FALSE; + lstrcpynW( enum_name, device_id, size ); + + set = SetupDiGetClassDevsW( NULL, enum_name, 0, DIGCF_ALLCLASSES ); + if (set == INVALID_HANDLE_VALUE) goto end; + while (SetupDiEnumDeviceInfo( set, i++, &devInfo )) + { + SetupDiGetDeviceRegistryPropertyW( set, &devInfo, SPDRP_HARDWAREID, + NULL, NULL, 0, &size ); + if (id) RtlFreeHeap( GetProcessHeap(), 0, id ); + id = RtlAllocateHeap( GetProcessHeap(), 0, size ); + if (!id) break; + ret = SetupDiGetDeviceRegistryPropertyW( set, &devInfo, SPDRP_HARDWAREID, + NULL, (BYTE *)id, size, NULL ); + if (!ret) break; + if (strcmpiW( device_id, id )) continue; + SetupDiGetDeviceRegistryPropertyW( set, &devInfo, SPDRP_SERVICE, + NULL, NULL, 0, &size ); + *service_name = RtlAllocateHeap( GetProcessHeap(), 0, size ); + if (!*service_name) break; + ret = SetupDiGetDeviceRegistryPropertyW( set, &devInfo, SPDRP_SERVICE, + NULL, (BYTE *)*service_name, size, NULL ); + if (!ret) + { + RtlFreeHeap( GetProcessHeap(), 0, *service_name ); + *service_name = NULL; + break; + } + } + SetupDiDestroyDeviceInfoList( set ); +end: + if (id) RtlFreeHeap( GetProcessHeap(), 0, id ); + if (enum_name) RtlFreeHeap( GetProcessHeap(), 0, enum_name ); + return (*service_name != NULL); +} + +static NTSTATUS get_device_id( DEVICE_OBJECT *pdo, BUS_QUERY_ID_TYPE id_type, + WCHAR **id ) +{ + NTSTATUS status; + IO_STACK_LOCATION *irpsp; + IRP *irp; + + *id = NULL; + irp = IoAllocateIrp( pdo->StackSize, FALSE ); + if (irp == NULL) return STATUS_NO_MEMORY; + irpsp = IoGetNextIrpStackLocation( irp ); + irpsp->MajorFunction = IRP_MJ_PNP; + irpsp->MinorFunction = IRP_MN_QUERY_ID; + irpsp->Parameters.QueryId.IdType = id_type; + status = IoCallDriver( pdo, irp ); + if (status == STATUS_SUCCESS) + *id = (WCHAR *)irp->IoStatus.Information; + IoFreeIrp( irp ); + return status; +} + +static BOOL compare_ids( WCHAR *hardware_id, WCHAR *instance_id, + WCHAR *device_instance_id ) +{ + WCHAR *ptr, *ptr2; + + ptr = strrchrW( device_instance_id, '\\' ); + if (ptr == NULL) return FALSE; + if (strncmpiW( hardware_id, device_instance_id, ptr - device_instance_id )) + return FALSE; + ++ptr; + ptr2 = strrchrW( ptr, '&' ); + ptr2 = ptr2 ? (ptr2 + 1) : ptr; + if (strcmpiW( instance_id, ptr2 )) + return FALSE; + return TRUE; +} + +/* caller is responsible for proper locking to prevent modifying Interfaces list */ +static struct InterfaceInstance *get_registered_interface( WCHAR *name, USHORT len ) +{ + struct InterfaceInstance *interf; + + LIST_FOR_EACH_ENTRY( interf, &Interfaces, struct InterfaceInstance, entry ) + { + if (!strncmpW( name, interf->link, len )) + return interf; + } + return NULL; +} + +static void call_interface_change_callbacks( const GUID *interface_class, + UNICODE_STRING *link_name ) +{ + struct list callbacks = LIST_INIT(callbacks); + struct InterfaceChangeNotification *notification; + struct callback *cb, *cb2; + DEVICE_INTERFACE_CHANGE_NOTIFICATION change_notification; + NTSTATUS callback_status; + + EnterCriticalSection( &cs ); + LIST_FOR_EACH_ENTRY( notification, &InterfaceChangeNotifications, + struct InterfaceChangeNotification, entry ) + { + if (!memcmp( interface_class, ¬ification->interface_class, + sizeof(*interface_class) )) + { + cb = HeapAlloc( GetProcessHeap(), 0, sizeof(*cb) ); + if (cb == NULL) break; + cb->routine = notification->callback; + cb->context = notification->context; + list_add_tail( &callbacks, &cb->entry ); + } + } + LeaveCriticalSection( &cs ); + + change_notification.Version = 1; + change_notification.Size = sizeof(change_notification); + change_notification.Event = GUID_DEVICE_INTERFACE_ARRIVAL; + change_notification.InterfaceClassGuid = *interface_class; + change_notification.SymbolicLinkName = link_name; + + LIST_FOR_EACH_ENTRY_SAFE( cb, cb2, &callbacks, struct callback, entry ) + { + if (TRACE_ON(relay)) + DPRINTF( "%04x:Call callback %p (notification=%p,context=%p)\n", + GetCurrentThreadId(), cb->routine, &change_notification, + cb->context ); + + callback_status = cb->routine( &change_notification, cb->context ); + + if (TRACE_ON(relay)) + DPRINTF( "%04x:Ret callback %p (notification=%p,context=%p) retval=%08x\n", + GetCurrentThreadId(), cb->routine, &change_notification, + cb->context, callback_status ); + + list_remove( &cb->entry ); + HeapFree( GetProcessHeap(), 0, cb ); + } +} + static HANDLE get_device_manager(void) { static HANDLE device_manager; @@ -125,61 +388,114 @@ static HANDLE get_device_manager(void) return ret; } +static NTSTATUS get_autogenerated_device_name( UNICODE_STRING *name ) +{ + static const WCHAR autogen_nameW[] = {'\\','D','e','v','i','c','e', + '\\','%','0','8','x',0}; + + NTSTATUS status; + WCHAR *nameW; + HANDLE handle; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + unsigned int k = 1; + + if (!(nameW = RtlAllocateHeap( GetProcessHeap(), 0, 17 * sizeof(WCHAR) ))) + return STATUS_NO_MEMORY; + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.ObjectName = name; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + for (;;) + { + sprintfW( nameW, autogen_nameW, k ); + RtlInitUnicodeString( name, nameW ); + status = NtCreateFile( &handle, 0, &attr, &io, NULL, 0, 0, + FILE_OPEN, 0, NULL, 0 ); + if (status != STATUS_SUCCESS) break; + NtClose( handle ); + ++k; + } + return STATUS_SUCCESS; +} + +/* get id of the process whose request is being handled */ +static DWORD get_pid(void) +{ + DWORD ret = 0, thread_id = GetCurrentThreadId(); + struct DriverInstance *drv; + + EnterCriticalSection( &cs ); + LIST_FOR_EACH_ENTRY( drv, &Drivers, struct DriverInstance, entry ) + { + if (drv->driver_thread_id == thread_id) + { + ret = drv->process_id; + break; + } + } + LeaveCriticalSection( &cs ); + return ret; +} + +/* save id of the process whose request is being handled */ +static void save_pid( DWORD pid ) +{ + DWORD thread_id = GetCurrentThreadId(); + struct DriverInstance *drv; + + EnterCriticalSection( &cs ); + LIST_FOR_EACH_ENTRY( drv, &Drivers, struct DriverInstance, entry ) + { + if (drv->driver_thread_id == thread_id) + { + drv->process_id = pid; + break; + } + } + LeaveCriticalSection( &cs ); +} + /* process an ioctl request for a given device */ static NTSTATUS process_ioctl( DEVICE_OBJECT *device, ULONG code, void *in_buff, ULONG in_size, void *out_buff, ULONG *out_size ) { - IRP irp; - MDL mdl; - IO_STACK_LOCATION irpsp; - PDRIVER_DISPATCH dispatch = device->DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]; + PIRP irp; + PIO_STACK_LOCATION irpsp; + PFILE_OBJECT file; NTSTATUS status; LARGE_INTEGER count; + IO_STATUS_BLOCK iosb; TRACE( "ioctl %x device %p in_size %u out_size %u\n", code, device, in_size, *out_size ); - /* so we can spot things that we should initialize */ - memset( &irp, 0x55, sizeof(irp) ); - memset( &irpsp, 0x66, sizeof(irpsp) ); - memset( &mdl, 0x77, sizeof(mdl) ); - - irp.RequestorMode = UserMode; - irp.AssociatedIrp.SystemBuffer = in_buff; - irp.UserBuffer = out_buff; - irp.MdlAddress = &mdl; - irp.Tail.Overlay.s.u2.CurrentStackLocation = &irpsp; - irp.UserIosb = NULL; - - irpsp.MajorFunction = IRP_MJ_DEVICE_CONTROL; - irpsp.Parameters.DeviceIoControl.OutputBufferLength = *out_size; - irpsp.Parameters.DeviceIoControl.InputBufferLength = in_size; - irpsp.Parameters.DeviceIoControl.IoControlCode = code; - irpsp.Parameters.DeviceIoControl.Type3InputBuffer = in_buff; - irpsp.DeviceObject = device; - irpsp.CompletionRoutine = NULL; - - mdl.Next = NULL; - mdl.Size = 0; - mdl.StartVa = out_buff; - mdl.ByteCount = *out_size; - mdl.ByteOffset = 0; - - device->CurrentIrp = &irp; + file = ExAllocatePool( NonPagedPool, sizeof(*file) ); + if (file == NULL) + return STATUS_NO_MEMORY; + irp = IoBuildDeviceIoControlRequest( code, device, in_buff, in_size, + out_buff, *out_size, FALSE, NULL, &iosb ); + if (irp == NULL) + { + ExFreePool( file ); + return STATUS_NO_MEMORY; + } + irpsp = IoGetNextIrpStackLocation( irp ); + irp->RequestorMode = UserMode; + irp->IoStatus.u.Status = STATUS_NOT_SUPPORTED; + irp->Tail.Overlay.OriginalFileObject = file; + device->CurrentIrp = irp; + file->Type = IO_TYPE_FILE; + file->Size = sizeof(*file); + file->DeviceObject = device; KeQueryTickCount( &count ); /* update the global KeTickCount */ - - if (TRACE_ON(relay)) - DPRINTF( "%04x:Call driver dispatch %p (device=%p,irp=%p)\n", - GetCurrentThreadId(), dispatch, device, &irp ); - - status = dispatch( device, &irp ); - - if (TRACE_ON(relay)) - DPRINTF( "%04x:Ret driver dispatch %p (device=%p,irp=%p) retval=%08x\n", - GetCurrentThreadId(), dispatch, device, &irp, status ); - - *out_size = (irp.IoStatus.u.Status >= 0) ? irp.IoStatus.Information : 0; - return irp.IoStatus.u.Status; + status = IoCallDriver( device, irp ); + *out_size = (status >= 0) ? iosb.Information : 0; + return status; } @@ -196,6 +512,7 @@ NTSTATUS CDECL wine_ntoskrnl_main_loop( HANDLE stop_event ) DEVICE_OBJECT *device = NULL; ULONG in_size = 4096, out_size = 0; HANDLE handles[2]; + DWORD pid = 0; if (!(in_buff = HeapAlloc( GetProcessHeap(), 0, in_size ))) { @@ -219,6 +536,7 @@ NTSTATUS CDECL wine_ntoskrnl_main_loop( HANDLE stop_event ) { code = reply->code; ioctl = reply->next; + pid = reply->pid; device = wine_server_get_ptr( reply->user_ptr ); in_size = reply->in_size; out_size = reply->out_size; @@ -232,12 +550,16 @@ NTSTATUS CDECL wine_ntoskrnl_main_loop( HANDLE stop_event ) } SERVER_END_REQ; + save_pid( pid ); + switch(status) { case STATUS_SUCCESS: HeapFree( GetProcessHeap(), 0, out_buff ); if (out_size) out_buff = HeapAlloc( GetProcessHeap(), 0, out_size ); else out_buff = NULL; + while (device->AttachedDevice) + device = device->AttachedDevice; status = process_ioctl( device, code, in_buff, in_size, out_buff, &out_size ); break; case STATUS_BUFFER_OVERFLOW: @@ -259,6 +581,164 @@ NTSTATUS CDECL wine_ntoskrnl_main_loop( HANDLE stop_event ) /*********************************************************************** + * __wine_add_driver_object (Not a Windows API) + */ +BOOL CDECL __wine_add_driver_object( DRIVER_OBJECT *driver, const WCHAR *service ) +{ + struct DriverInstance *drv; + + drv = HeapAlloc( GetProcessHeap(), 0, sizeof(*drv) ); + if (drv == NULL) return FALSE; + drv->driver = driver; + drv->service = service; + drv->driver_thread_id = GetCurrentThreadId(); + drv->process_id = 0; + EnterCriticalSection( &cs ); + list_add_tail( &Drivers, &drv->entry ); + LeaveCriticalSection( &cs ); + return TRUE; +} + + +/*********************************************************************** + * __wine_del_driver_object (Not a Windows API) + */ +void CDECL __wine_del_driver_object( const DRIVER_OBJECT *driver ) +{ + struct DriverInstance *drv; + + EnterCriticalSection( &cs ); + LIST_FOR_EACH_ENTRY( drv, &Drivers, struct DriverInstance, entry ) + { + if (drv->driver == driver) + { + list_remove( &drv->entry ); + HeapFree( GetProcessHeap(), 0, drv ); + break; + } + } + LeaveCriticalSection( &cs ); +} + + +/*********************************************************************** + * __wine_get_driver_object (Not a Windows API) + */ +DRIVER_OBJECT * CDECL __wine_get_driver_object( const WCHAR *service ) +{ + struct DriverInstance *drv; + DRIVER_OBJECT *driver_obj = NULL; + + EnterCriticalSection( &cs ); + LIST_FOR_EACH_ENTRY( drv, &Drivers, struct DriverInstance, entry ) + { + if (!strcmpiW( drv->service, service )) + { + driver_obj = drv->driver; + break; + } + } + LeaveCriticalSection( &cs ); + return driver_obj; +} + + +/*********************************************************************** + * __wine_add_device (Not a Windows API) + */ +NTSTATUS CDECL __wine_add_device( DRIVER_OBJECT *driver, DEVICE_OBJECT *dev ) +{ + NTSTATUS status; + NTSTATUS (WINAPI *AddDevice)( PDRIVER_OBJECT, PDEVICE_OBJECT ) = + driver->DriverExtension->AddDevice; + + if (TRACE_ON(relay)) + DPRINTF( "%04x:Call AddDevice %p (%p,%p)\n", + GetCurrentThreadId(), AddDevice, driver, dev ); + + status = AddDevice( driver, dev ); + + if (TRACE_ON(relay)) + DPRINTF( "%04x:Ret AddDevice %p (%p,%p) retval=%08x\n", + GetCurrentThreadId(), AddDevice, driver, dev, status ); + + return status; +} + + +/*********************************************************************** + * __wine_start_device (Not a Windows API) + */ +NTSTATUS CDECL __wine_start_device( DEVICE_OBJECT *device ) +{ + 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 = IoGetNextIrpStackLocation( irp ); + irp->RequestorMode = KernelMode; + irp->IoStatus.u.Status = STATUS_NOT_SUPPORTED; + irpsp->MajorFunction = IRP_MJ_PNP; + irpsp->MinorFunction = IRP_MN_START_DEVICE; + irpsp->DeviceObject = device; + device->CurrentIrp = irp; + status = IoCallDriver( device, irp ); + IoFreeIrp( irp ); + return status; +} + + +/*********************************************************************** + * ExAcquireFastMutexUnsafe (NTOSKRNL.EXE.@) + */ +#ifdef DEFINE_FASTCALL1_ENTRYPOINT +DEFINE_FASTCALL1_ENTRYPOINT( ExAcquireFastMutexUnsafe ) +void WINAPI __regs_ExAcquireFastMutexUnsafe( PFAST_MUTEX FastMutex ) +#else +void WINAPI ExAcquireFastMutexUnsafe( PFAST_MUTEX FastMutex ) +#endif +{ + FIXME( "stub: %p\n", FastMutex ); +} + + +/*********************************************************************** + * ExReleaseFastMutexUnsafe (NTOSKRNL.EXE.@) + */ +#ifdef DEFINE_FASTCALL1_ENTRYPOINT +DEFINE_FASTCALL1_ENTRYPOINT( ExReleaseFastMutexUnsafe ) +void WINAPI __regs_ExReleaseFastMutexUnsafe( PFAST_MUTEX FastMutex ) +#else +void WINAPI ExReleaseFastMutexUnsafe( PFAST_MUTEX FastMutex ) +#endif +{ + FIXME( "stub: %p\n", FastMutex ); +} + + +/*********************************************************************** + * IoAcquireCancelSpinLock (NTOSKRNL.EXE.@) + */ +void WINAPI IoAcquireCancelSpinLock( PKIRQL Irql ) +{ + FIXME( "stub: %p\n", Irql ); +} + +/*********************************************************************** + * IoReleaseCancelSpinLock (NTOSKRNL.EXE.@) + */ +void WINAPI IoReleaseCancelSpinLock( KIRQL Irql ) +{ + FIXME( "stub: %u\n", Irql ); +} + +/*********************************************************************** * IoAllocateDriverObjectExtension (NTOSKRNL.EXE.@) */ NTSTATUS WINAPI IoAllocateDriverObjectExtension( PDRIVER_OBJECT DriverObject, @@ -266,11 +746,31 @@ NTSTATUS WINAPI IoAllocateDriverObjectExtension( PDRIVER_OBJECT DriverObject, ULONG DriverObjectExtensionSize, PVOID *DriverObjectExtension ) { - FIXME( "stub: %p, %p, %u, %p\n", DriverObject, ClientIdentificationAddress, + struct DriverObjExtension *ext; + + TRACE( "%p, %p, %u, %p\n", DriverObject, ClientIdentificationAddress, DriverObjectExtensionSize, DriverObjectExtension ); - return STATUS_NOT_IMPLEMENTED; -} + *DriverObjectExtension = NULL; + if (IoGetDriverObjectExtension( DriverObject, ClientIdentificationAddress )) + return STATUS_OBJECT_NAME_COLLISION; + ext = ExAllocatePool( NonPagedPool, sizeof(*ext) ); + if (ext == NULL) + return STATUS_INSUFFICIENT_RESOURCES; + ext->ptr = ExAllocatePool( NonPagedPool, DriverObjectExtensionSize ); + if (ext->ptr == NULL) + { + ExFreePool( ext ); + return STATUS_INSUFFICIENT_RESOURCES; + } + ext->driver = DriverObject; + ext->id_addr = ClientIdentificationAddress; + EnterCriticalSection( &cs ); + list_add_tail( &DriverObjExtensions, &ext->entry ); + LeaveCriticalSection( &cs ); + *DriverObjectExtension = ext->ptr; + return STATUS_SUCCESS; +} /*********************************************************************** * IoGetDriverObjectExtension (NTOSKRNL.EXE.@) @@ -278,10 +778,24 @@ NTSTATUS WINAPI IoAllocateDriverObjectExtension( PDRIVER_OBJECT DriverObject, PVOID WINAPI IoGetDriverObjectExtension( PDRIVER_OBJECT DriverObject, PVOID ClientIdentificationAddress ) { - FIXME( "stub: %p, %p\n", DriverObject, ClientIdentificationAddress ); - return NULL; -} + struct DriverObjExtension *ext; + void *ext_ptr = NULL; + + TRACE( "%p, %p\n", DriverObject, ClientIdentificationAddress ); + EnterCriticalSection( &cs ); + LIST_FOR_EACH_ENTRY( ext, &DriverObjExtensions, struct DriverObjExtension, entry ) + { + if (DriverObject == ext->driver && + ClientIdentificationAddress == ext->id_addr) + { + ext_ptr = ext->ptr; + break; + } + } + LeaveCriticalSection( &cs ); + return ext_ptr; +} /*********************************************************************** * IoInitializeIrp (NTOSKRNL.EXE.@) @@ -371,12 +885,73 @@ PVOID WINAPI IoAllocateErrorLogEntry( PVOID IoObject, UCHAR EntrySize ) */ PMDL WINAPI IoAllocateMdl( PVOID VirtualAddress, ULONG Length, BOOLEAN SecondaryBuffer, BOOLEAN ChargeQuota, PIRP Irp ) { - FIXME( "stub: %p, %u, %i, %i, %p\n", VirtualAddress, Length, SecondaryBuffer, ChargeQuota, Irp ); + HANDLE process; + PMDL mdl; + PVOID ptr; + SIZE_T bytes_read; + DWORD process_id = get_pid(); + + TRACE( "%p, %u, %i, %i, %p\n", VirtualAddress, Length, SecondaryBuffer, + ChargeQuota, Irp ); + + mdl = ExAllocatePool( NonPagedPool, sizeof(*mdl) ); + if (NULL == mdl) + return NULL; + + RtlZeroMemory( mdl, sizeof(*mdl) ); + mdl->ByteCount = Length; + mdl->StartVa = VirtualAddress; + + if (process_id) + { + ptr = ExAllocatePool( NonPagedPool, Length ); + if (NULL == ptr) + goto fail; + process = OpenProcess( PROCESS_ALL_ACCESS, FALSE, process_id ); + if (NULL == process) + goto fail; + NtReadVirtualMemory( process, VirtualAddress, ptr, Length, &bytes_read ); + CloseHandle( process ); + mdl->MappedSystemVa = ptr; + } + else + mdl->MappedSystemVa = VirtualAddress; + + return mdl; +fail: + if (ptr) ExFreePool( ptr ); + if (mdl) ExFreePool( mdl ); return NULL; } /*********************************************************************** + * IoFreeMdl (NTOSKRNL.EXE.@) + */ +void WINAPI IoFreeMdl( MDL *mdl ) +{ + HANDLE process; + SIZE_T bytes_written; + DWORD process_id = get_pid(); + + TRACE( "%p\n", mdl ); + + if (process_id) + { + process = OpenProcess( PROCESS_ALL_ACCESS, FALSE, process_id ); + if (NULL != process) + { + NtWriteVirtualMemory( process, mdl->StartVa, mdl->MappedSystemVa, + mdl->ByteCount, &bytes_written ); + CloseHandle( process ); + } + ExFreePool( mdl->MappedSystemVa ); + } + ExFreePool( mdl ); +} + + +/*********************************************************************** * IoAllocateWorkItem (NTOSKRNL.EXE.@) */ PIO_WORKITEM WINAPI IoAllocateWorkItem( PDEVICE_OBJECT DeviceObject ) @@ -393,6 +968,8 @@ PDEVICE_OBJECT WINAPI IoAttachDeviceToDeviceStack( DEVICE_OBJECT *source, DEVICE_OBJECT *target ) { TRACE( "%p, %p\n", source, target ); + while (target->AttachedDevice) + target = target->AttachedDevice; target->AttachedDevice = source; source->StackSize = target->StackSize + 1; return target; @@ -400,6 +977,16 @@ PDEVICE_OBJECT WINAPI IoAttachDeviceToDeviceStack( DEVICE_OBJECT *source, /*********************************************************************** + * IoDetachDevice (NTOSKRNL.EXE.@) + */ +void WINAPI IoDetachDevice( DEVICE_OBJECT *device ) +{ + TRACE( "%p\n", device ); + device->AttachedDevice = NULL; +} + + +/*********************************************************************** * IoBuildDeviceIoControlRequest (NTOSKRNL.EXE.@) */ PIRP WINAPI IoBuildDeviceIoControlRequest( ULONG IoControlCode, @@ -415,6 +1002,8 @@ PIRP WINAPI IoBuildDeviceIoControlRequest( ULONG IoControlCode, PIRP irp; PIO_STACK_LOCATION irpsp; struct IrpInstance *instance; + CHAR *buf = NULL; + MDL *mdl = NULL; TRACE( "%x, %p, %p, %u, %p, %u, %u, %p, %p\n", IoControlCode, DeviceObject, InputBuffer, InputBufferLength, @@ -428,23 +1017,59 @@ PIRP WINAPI IoBuildDeviceIoControlRequest( ULONG IoControlCode, if (irp == NULL) return NULL; - instance = HeapAlloc( GetProcessHeap(), 0, sizeof(struct IrpInstance) ); - if (instance == NULL) - { - IoFreeIrp( irp ); - return NULL; - } - instance->irp = irp; - list_add_tail( &Irps, &instance->entry ); - irpsp = IoGetNextIrpStackLocation( irp ); irpsp->MajorFunction = InternalDeviceIoControl ? IRP_MJ_INTERNAL_DEVICE_CONTROL : IRP_MJ_DEVICE_CONTROL; irpsp->Parameters.DeviceIoControl.IoControlCode = IoControlCode; + irpsp->Parameters.DeviceIoControl.InputBufferLength = InputBufferLength; + irpsp->Parameters.DeviceIoControl.OutputBufferLength = OutputBufferLength; irp->UserIosb = IoStatusBlock; irp->UserEvent = Event; + switch (IoControlCode & 3) + { + case METHOD_BUFFERED: + buf = ExAllocatePool( NonPagedPool, max( OutputBufferLength, InputBufferLength ) ); + if (buf == NULL) + goto err; + memcpy( buf, InputBuffer, InputBufferLength ); + irp->AssociatedIrp.SystemBuffer = buf; + irp->UserBuffer = OutputBuffer; + break; + case METHOD_NEITHER: + irpsp->Parameters.DeviceIoControl.Type3InputBuffer = InputBuffer; + irp->UserBuffer = OutputBuffer; + break; + default: + irp->AssociatedIrp.SystemBuffer = InputBuffer; + mdl = ExAllocatePool( NonPagedPool, sizeof(*mdl) ); + if (mdl == NULL) + goto err; + mdl->Next = NULL; + mdl->Size = 0; + mdl->StartVa = OutputBuffer; + mdl->MappedSystemVa = OutputBuffer; + mdl->ByteCount = OutputBufferLength; + mdl->ByteOffset = 0; + irp->MdlAddress = mdl; + } + + instance = HeapAlloc( GetProcessHeap(), 0, sizeof(struct IrpInstance) ); + if (instance == NULL) + goto err; + instance->irp = irp; + EnterCriticalSection( &cs ); + list_add_tail( &Irps, &instance->entry ); + LeaveCriticalSection( &cs ); + return irp; +err: + if (buf) + ExFreePool( buf ); + if (mdl) + ExFreePool( mdl ); + IoFreeIrp( irp ); + return NULL; } @@ -507,6 +1132,7 @@ NTSTATUS WINAPI IoCreateDevice( DRIVER_OBJECT *driver, ULONG ext_size, DEVICE_OBJECT *device; HANDLE handle = 0; HANDLE manager = get_device_manager(); + UNICODE_STRING generated_name; TRACE( "(%p, %u, %s, %u, %x, %u, %p)\n", driver, ext_size, debugstr_us(name), type, characteristics, exclusive, ret_device ); @@ -514,6 +1140,17 @@ NTSTATUS WINAPI IoCreateDevice( DRIVER_OBJECT *driver, ULONG ext_size, if (!(device = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*device) + ext_size ))) return STATUS_NO_MEMORY; + if (characteristics & FILE_AUTOGENERATED_DEVICE_NAME) + { + status = get_autogenerated_device_name( &generated_name ); + if (status != STATUS_SUCCESS) + { + HeapFree( GetProcessHeap(), 0, device ); + return status; + } + name = &generated_name; + } + SERVER_START_REQ( create_device ) { req->access = 0; @@ -528,7 +1165,11 @@ NTSTATUS WINAPI IoCreateDevice( DRIVER_OBJECT *driver, ULONG ext_size, if (status == STATUS_SUCCESS) { + device->Type = IO_TYPE_DEVICE; + device->Size = sizeof(*device) + ext_size; device->DriverObject = driver; + device->Flags = DO_DEVICE_INITIALIZING; + if (name) device->Flags |= DO_DEVICE_HAS_NAME; device->DeviceExtension = device + 1; device->DeviceType = type; device->StackSize = 1; @@ -541,6 +1182,8 @@ NTSTATUS WINAPI IoCreateDevice( DRIVER_OBJECT *driver, ULONG ext_size, } else HeapFree( GetProcessHeap(), 0, device ); + if (characteristics & FILE_AUTOGENERATED_DEVICE_NAME) + RtlFreeUnicodeString( &generated_name ); return status; } @@ -594,6 +1237,188 @@ NTSTATUS WINAPI IoCreateSymbolicLink( UNICODE_STRING *name, UNICODE_STRING *targ /*********************************************************************** + * IoInvalidateDeviceRelations (NTOSKRNL.EXE.@) + */ +void WINAPI IoInvalidateDeviceRelations( PDEVICE_OBJECT DeviceObject, + DEVICE_RELATION_TYPE Type ) +{ + TRACE( "%p, %u\n", DeviceObject, Type ); + + while (DeviceObject->AttachedDevice) + DeviceObject = DeviceObject->AttachedDevice; + if (Type == BusRelations) + { + DEVICE_RELATIONS *rel; + IO_STACK_LOCATION *irpsp; + IRP *irp; + NTSTATUS status; + + irp = IoAllocateIrp( DeviceObject->StackSize, FALSE ); + if (irp == NULL) return; + irpsp = IoGetNextIrpStackLocation( irp ); + irpsp->MajorFunction = IRP_MJ_PNP; + irpsp->MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS; + irpsp->Parameters.QueryDeviceRelations.Type = BusRelations; + status = IoCallDriver( DeviceObject, irp ); + rel = (DEVICE_RELATIONS *)irp->IoStatus.Information; + if (status == STATUS_SUCCESS && rel && rel->Count) + { + unsigned int k; + + for (k = 0; k < rel->Count; ++k) + { + IoFreeIrp( irp ); + irp = IoAllocateIrp( rel->Objects[k]->StackSize, FALSE ); + if (irp == NULL) return; + irpsp = IoGetNextIrpStackLocation( irp ); + irpsp->MajorFunction = IRP_MJ_PNP; + irpsp->MinorFunction = IRP_MN_QUERY_ID; + irpsp->Parameters.QueryId.IdType = BusQueryDeviceID; + status = IoCallDriver( rel->Objects[k], irp ); + if (status == STATUS_SUCCESS) + { + WCHAR *service; + + if (get_service( (WCHAR *)irp->IoStatus.Information, &service ) + && __wine_start_service( service )) + { + DRIVER_OBJECT *driver; + + while (!(driver = __wine_get_driver_object( service ))) + Sleep( 100 ); + status = __wine_add_device( driver, rel->Objects[k] ); + if (status == STATUS_SUCCESS && + rel->Objects[k]->AttachedDevice) + __wine_start_device( rel->Objects[k]->AttachedDevice ); + } + if (service) RtlFreeHeap( GetProcessHeap(), 0, service ); + } + ExFreePool( (void *)irp->IoStatus.Information ); + } + ExFreePool( rel ); + } + IoFreeIrp( irp ); + } + else + FIXME( "DEVICE_RELATION_TYPE %u not implemented\n", Type ); +} + + +/*********************************************************************** + * IoRegisterDeviceInterface (NTOSKRNL.EXE.@) + */ +NTSTATUS WINAPI IoRegisterDeviceInterface( PDEVICE_OBJECT PhysicalDeviceObject, + CONST GUID *InterfaceClassGuid, + PUNICODE_STRING ReferenceString, + PUNICODE_STRING SymbolicLinkName ) +{ + WCHAR *hardware_id = NULL, *instance_id = NULL, *id = NULL; + WCHAR *ptr, *target, *enumerator = NULL; + SP_DEVICE_INTERFACE_DETAIL_DATA_W *detail = NULL; + HDEVINFO set; + SP_DEVINFO_DATA devInfo; + SP_DEVICE_INTERFACE_DATA interfaceData; + DWORD i = 0; + NTSTATUS status; + struct InterfaceInstance *interf; + DWORD size; + + TRACE( "%p %s %s %p\n", PhysicalDeviceObject, + debugstr_guid(InterfaceClassGuid), debugstr_us(ReferenceString), + SymbolicLinkName ); + + status = get_device_id( PhysicalDeviceObject, BusQueryInstanceID, &instance_id ); + if (status != STATUS_SUCCESS) goto end; + status = get_device_id( PhysicalDeviceObject, BusQueryDeviceID, &hardware_id ); + if (status != STATUS_SUCCESS) goto end; + ptr = strchrW( hardware_id, '\\' ) + 1; + size = (char *)ptr - (char *)hardware_id; + enumerator = RtlAllocateHeap( GetProcessHeap(), 0, size ); + id = RtlAllocateHeap( GetProcessHeap(), 0, MAX_DEVICE_ID_LEN ); + if (enumerator == NULL || id == NULL) + { + status = STATUS_NO_MEMORY; + goto end; + } + lstrcpynW( enumerator, hardware_id, size / sizeof(WCHAR) ); + + status = STATUS_UNSUCCESSFUL; + set = SetupDiGetClassDevsW( NULL, enumerator, NULL, DIGCF_ALLCLASSES ); + if (INVALID_HANDLE_VALUE == set) goto end; + devInfo.cbSize = sizeof(devInfo); + while (SetupDiEnumDeviceInfo( set, i++, &devInfo )) + if (SetupDiGetDeviceInstanceIdW( set, &devInfo, id, MAX_DEVICE_ID_LEN, NULL ) + && compare_ids( hardware_id, instance_id, id )) + { + interfaceData.cbSize = sizeof(interfaceData); + if (SetupDiCreateDeviceInterfaceW( set, &devInfo, + InterfaceClassGuid, NULL, 0, &interfaceData )) + { + SetupDiGetDeviceInterfaceDetailW( set, &interfaceData, NULL, 0, + &size, NULL ); + detail = RtlAllocateHeap( GetProcessHeap(), 0, size ); + if (detail == NULL) break; + detail->cbSize = sizeof(*detail); + if (!SetupDiGetDeviceInterfaceDetailW( set, &interfaceData, + detail, size, NULL, NULL )) + break; + interf = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*interf) ); + if (interf == NULL) break; + interf->link = RtlAllocateHeap( GetProcessHeap(), 0, + (strlenW(detail->DevicePath) + 1) * sizeof(WCHAR) ); + if (interf->link == NULL) + { + RtlFreeHeap( GetProcessHeap(), 0, interf ); + break; + } + detail->DevicePath[1] = '?'; + strcpyW( interf->link, detail->DevicePath ); + target = RtlAllocateHeap( GetProcessHeap(), 0, + MAX_PATH * sizeof(WCHAR) ); + if (target == NULL) + { + RtlFreeHeap( GetProcessHeap(), 0, interf->link ); + RtlFreeHeap( GetProcessHeap(), 0, interf ); + break; + } + status = IoGetDeviceProperty( PhysicalDeviceObject, + DevicePropertyPhysicalDeviceObjectName, + MAX_PATH * sizeof(WCHAR), target, &size ); + if (status == STATUS_SUCCESS) + { + RtlInitUnicodeString( &interf->target, target ); + interf->guid = *InterfaceClassGuid; + interf->active = 0; + EnterCriticalSection( &cs ); + if (!get_registered_interface( interf->link, + strlenW(interf->link) )) + { + list_add_tail( &Interfaces, &interf->entry ); + LeaveCriticalSection( &cs ); + break; + } + LeaveCriticalSection( &cs ); + } + RtlFreeHeap( GetProcessHeap(), 0, target ); + RtlFreeHeap( GetProcessHeap(), 0, interf->link ); + RtlFreeHeap( GetProcessHeap(), 0, interf ); + } + break; + } + SetupDiDestroyDeviceInfoList( set ); + + if (STATUS_SUCCESS == status) + RtlCreateUnicodeString( SymbolicLinkName, detail->DevicePath ); +end: + if (detail) RtlFreeHeap( GetProcessHeap(), 0, detail ); + if (id) RtlFreeHeap( GetProcessHeap(), 0, id ); + if (enumerator) RtlFreeHeap( GetProcessHeap(), 0, enumerator ); + if (hardware_id) ExFreePool( hardware_id ); + if (instance_id) ExFreePool( instance_id ); + return status; +} + +/*********************************************************************** * IoDeleteSymbolicLink (NTOSKRNL.EXE.@) */ NTSTATUS WINAPI IoDeleteSymbolicLink( UNICODE_STRING *name ) @@ -637,6 +1462,51 @@ NTSTATUS WINAPI IoGetDeviceInterfaces( CONST GUID *InterfaceClassGuid, /*********************************************************************** + * IoSetDeviceInterfaceState (NTOSKRNL.EXE.@) + */ +NTSTATUS WINAPI IoSetDeviceInterfaceState( PUNICODE_STRING SymbolicLinkName, + BOOLEAN Enable ) +{ + TRACE( "%s %d\n", debugstr_us(SymbolicLinkName), Enable ); + + if (Enable) + { + struct InterfaceInstance *interf; + NTSTATUS status; + GUID guid; + int changed = 0; + + status = STATUS_OBJECT_NAME_NOT_FOUND; + EnterCriticalSection( &cs ); + interf = get_registered_interface( SymbolicLinkName->Buffer, + SymbolicLinkName->Length / sizeof(WCHAR) ); + if (interf != NULL) + { + if (!interf->active) + { + guid = interf->guid; + status = IoCreateSymbolicLink( SymbolicLinkName, &interf->target ); + if (status == STATUS_SUCCESS) + { + interf->active = 1; + changed = 1; + } + } + else status = STATUS_SUCCESS; + } + LeaveCriticalSection( &cs ); + if (changed) call_interface_change_callbacks( &guid, SymbolicLinkName ); + return status; + } + else + { + FIXME( "Disabling interface is not supported\n" ); + return STATUS_NOT_IMPLEMENTED; + } +} + + +/*********************************************************************** * IoGetDeviceObjectPointer (NTOSKRNL.EXE.@) */ NTSTATUS WINAPI IoGetDeviceObjectPointer( UNICODE_STRING *name, ACCESS_MASK access, PFILE_OBJECT *file, PDEVICE_OBJECT *device ) @@ -652,9 +1522,68 @@ NTSTATUS WINAPI IoGetDeviceObjectPointer( UNICODE_STRING *name, ACCESS_MASK acc NTSTATUS WINAPI IoGetDeviceProperty( DEVICE_OBJECT *device, DEVICE_REGISTRY_PROPERTY device_property, ULONG buffer_length, PVOID property_buffer, PULONG result_length ) { - FIXME( "%p %d %u %p %p: stub\n", device, device_property, buffer_length, + NTSTATUS status; + + TRACE( "%p %d %u %p %p\n", device, device_property, buffer_length, property_buffer, result_length ); - return STATUS_NOT_IMPLEMENTED; + + switch (device_property) + { + case DevicePropertyHardwareID: + { + WCHAR *hardware_id; + + status = get_device_id( device, BusQueryDeviceID, &hardware_id ); + if (status != STATUS_SUCCESS) break; + *result_length = (strlenW(hardware_id) + 1) * sizeof(WCHAR); + if (buffer_length >= *result_length) + strcpyW( property_buffer, hardware_id ); + else + status = STATUS_BUFFER_TOO_SMALL; + ExFreePool( hardware_id ); + break; + } + case DevicePropertyPhysicalDeviceObjectName: + { + static const WCHAR deviceW[] = {'\\','D','e','v','i','c','e','\\',0}; + WCHAR device_name[MAX_PATH]; + data_size_t len; + + SERVER_START_REQ( get_device_name ) + { + req->handle = wine_server_obj_handle( device->Reserved ); + wine_server_set_reply( req, device_name, + sizeof(device_name) - sizeof(WCHAR) ); + status = wine_server_call( req ); + len = wine_server_reply_size( reply ); + } + SERVER_END_REQ; + + if (status != STATUS_SUCCESS) break; + *result_length = len + sizeof(deviceW); + if (buffer_length >= *result_length) + { + strcpyW( property_buffer, deviceW ); + device_name[len / sizeof(WCHAR)] = 0; + strcatW( property_buffer, device_name ); + } + else status = STATUS_BUFFER_TOO_SMALL; + break; + } + default: + FIXME( "device property %u is not supported\n", device_property ); + status = STATUS_NOT_IMPLEMENTED; + } + + return status; +} + + +static NTSTATUS WINAPI invalid_request_handler( DEVICE_OBJECT *device, IRP *irp ) +{ + irp->IoStatus.u.Status = STATUS_INVALID_DEVICE_REQUEST; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + return STATUS_INVALID_DEVICE_REQUEST; } @@ -671,9 +1600,21 @@ NTSTATUS WINAPI IoCallDriver( DEVICE_OBJECT *device, IRP *irp ) --irp->CurrentLocation; irpsp = --irp->Tail.Overlay.s.u2.CurrentStackLocation; + irpsp->DeviceObject = device; dispatch = device->DriverObject->MajorFunction[irpsp->MajorFunction]; + if (!dispatch) + dispatch = invalid_request_handler; + + if (TRACE_ON(relay)) + DPRINTF( "%04x:Call driver dispatch %p (device=%p,irp=%p)\n", + GetCurrentThreadId(), dispatch, device, irp ); + status = dispatch( device, irp ); + if (TRACE_ON(relay)) + DPRINTF( "%04x:Ret driver dispatch %p (device=%p,irp=%p) retval=%08x\n", + GetCurrentThreadId(), dispatch, device, irp, status ); + return status; } @@ -692,6 +1633,14 @@ NTSTATUS WINAPI IofCallDriver( DEVICE_OBJECT *device, IRP *irp ) return IoCallDriver( device, irp ); } +/*********************************************************************** + * IoGetAttachedDeviceReference (NTOSKRNL.EXE.@) + */ +PDEVICE_OBJECT WINAPI IoGetAttachedDeviceReference( PDEVICE_OBJECT obj ) +{ + FIXME( "stub: %p\n", obj ); + return obj; +} /*********************************************************************** * IoGetRelatedDeviceObject (NTOSKRNL.EXE.@) @@ -803,6 +1752,97 @@ void WINAPI IoRegisterDriverReinitialization( PDRIVER_OBJECT obj, PDRIVER_REINIT /*********************************************************************** + * IoRegisterPlugPlayNotification (NTOSKRNL.EXE.@) + */ +NTSTATUS WINAPI IoRegisterPlugPlayNotification( IO_NOTIFICATION_EVENT_CATEGORY + EventCategory, + ULONG EventCategoryFlags, + PVOID EventCategoryData, + PDRIVER_OBJECT DriverObject, + PDRIVER_NOTIFICATION_CALLBACK_ROUTINE + CallbackRoutine, PVOID Context, + PVOID *NotificationEntry ) +{ + TRACE( "%u %u %p %p %p %p %p\n", EventCategory, EventCategoryFlags, + EventCategoryData, DriverObject, CallbackRoutine, Context, + NotificationEntry ); + + if (EventCategory == EventCategoryDeviceInterfaceChange) + { + struct InterfaceChangeNotification *notification = + HeapAlloc( GetProcessHeap(), 0, sizeof(*notification) ); + struct list interfs = LIST_INIT(interfs); + struct InterfaceInstance *interf, *interf2; + UNICODE_STRING link; + + if (notification == NULL) return STATUS_NO_MEMORY; + notification->interface_class = *(GUID *)EventCategoryData; + notification->callback = CallbackRoutine; + notification->context = Context; + + EnterCriticalSection( &cs ); + list_add_tail( &InterfaceChangeNotifications, ¬ification->entry ); + if (EventCategoryFlags & PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES) + { + LIST_FOR_EACH_ENTRY( interf, &Interfaces, struct InterfaceInstance, entry ) + { + if (interf->active && !memcmp( ¬ification->interface_class, + &interf->guid, sizeof(GUID) )) + { + interf2 = HeapAlloc( GetProcessHeap(), 0, sizeof(*interf2) ); + if (interf2 == NULL) break; + interf2->link = HeapAlloc( GetProcessHeap(), 0, + (strlenW(interf->link) + 1) * sizeof(WCHAR) ); + if (interf2->link == NULL) break; + strcpyW( interf2->link, interf->link ); + interf2->guid = interf->guid; + list_add_tail( &interfs, &interf2->entry ); + } + } + } + LeaveCriticalSection( &cs ); + + LIST_FOR_EACH_ENTRY_SAFE( interf, interf2, &interfs, + struct InterfaceInstance, entry ) + { + list_remove( &interf->entry ); + if (interf->link) + { + RtlInitUnicodeString( &link, interf->link ); + call_interface_change_callbacks( &interf->guid, &link ); + HeapFree( GetProcessHeap(), 0, interf->link ); + } + HeapFree( GetProcessHeap(), 0, interf ); + } + *NotificationEntry = notification; + return STATUS_SUCCESS; + } + else + { + FIXME( "event category %u is not supported\n", EventCategory ); + return STATUS_NOT_IMPLEMENTED; + } +} + + +/*********************************************************************** + * IoUnregisterPlugPlayNotification (NTOSKRNL.EXE.@) + */ +NTSTATUS WINAPI IoUnregisterPlugPlayNotification( PVOID NotificationEntry ) +{ + struct InterfaceChangeNotification *notification = NotificationEntry; + + TRACE( "%p\n", NotificationEntry ); + + EnterCriticalSection( &cs ); + list_remove( ¬ification->entry ); + LeaveCriticalSection( &cs ); + HeapFree( GetProcessHeap(), 0, notification ); + return STATUS_SUCCESS; +} + + +/*********************************************************************** * IoRegisterShutdownNotification (NTOSKRNL.EXE.@) */ NTSTATUS WINAPI IoRegisterShutdownNotification( PDEVICE_OBJECT obj ) @@ -835,6 +1875,7 @@ VOID WINAPI IoCompleteRequest( IRP *irp, UCHAR priority_boost ) IO_STATUS_BLOCK *iosb; struct IrpInstance *instance; NTSTATUS status, stat; + KEVENT *event; int call_flag = 0; TRACE( "%p %u\n", irp, priority_boost ); @@ -843,7 +1884,7 @@ VOID WINAPI IoCompleteRequest( IRP *irp, UCHAR priority_boost ) status = irp->IoStatus.u.Status; while (irp->CurrentLocation <= irp->StackCount) { - irpsp = irp->Tail.Overlay.s.u2.CurrentStackLocation; + irpsp = IoGetCurrentIrpStackLocation( irp ); routine = irpsp->CompletionRoutine; call_flag = 0; /* FIXME: add SL_INVOKE_ON_CANCEL support */ @@ -859,28 +1900,47 @@ VOID WINAPI IoCompleteRequest( IRP *irp, UCHAR priority_boost ) if (call_flag) { TRACE( "calling %p( %p, %p, %p )\n", routine, - irpsp->DeviceObject, irp, irpsp->Context ); - stat = routine( irpsp->DeviceObject, irp, irpsp->Context ); + (irpsp + 1)->DeviceObject, irp, irpsp->Context ); + stat = routine( (irpsp + 1)->DeviceObject, irp, irpsp->Context ); TRACE( "CompletionRoutine returned %x\n", stat ); if (STATUS_MORE_PROCESSING_REQUIRED == stat) return; } } - if (iosb && STATUS_SUCCESS == status) + if (iosb && status >= 0) { iosb->u.Status = irp->IoStatus.u.Status; iosb->Information = irp->IoStatus.Information; } + event = irp->UserEvent; + EnterCriticalSection( &cs ); LIST_FOR_EACH_ENTRY( instance, &Irps, struct IrpInstance, entry ) { if (instance->irp == irp) { + void *buf = irp->AssociatedIrp.SystemBuffer; + MDL *mdl = irp->MdlAddress; + struct _FILE_OBJECT *file = irp->Tail.Overlay.OriginalFileObject; + list_remove( &instance->entry ); HeapFree( GetProcessHeap(), 0, instance ); + if (mdl) + { + ExFreePool( mdl ); + } + else if (buf) + { + memcpy( irp->UserBuffer, buf, irp->IoStatus.Information ); + ExFreePool( buf ); + } + if (file) ExFreePool( file ); IoFreeIrp( irp ); break; } } + LeaveCriticalSection( &cs ); + if (event) + KeSetEvent( event, 0, FALSE ); } @@ -1083,12 +2143,40 @@ PRKTHREAD WINAPI KeGetCurrentThread(void) return NULL; } + +/*********************************************************************** + * KeDelayExecutionThread (NTOSKRNL.EXE.@) + */ +NTSTATUS WINAPI KeDelayExecutionThread ( KPROCESSOR_MODE WaitMode, + BOOLEAN Alertable, PLARGE_INTEGER Interval ) +{ + FIXME( "stub: %d %d %p\n", WaitMode, Alertable, Interval ); + return STATUS_SUCCESS; +} + + /*********************************************************************** * KeInitializeEvent (NTOSKRNL.EXE.@) */ void WINAPI KeInitializeEvent( PRKEVENT Event, EVENT_TYPE Type, BOOLEAN State ) { - FIXME( "stub: %p %d %d\n", Event, Type, State ); + TRACE( "%p %d %d\n", Event, Type, State ); + RtlZeroMemory( Event, sizeof(KEVENT) ); + Event->Header.Type = Type; + Event->Header.Size = 4; + if (State) + Event->Header.SignalState = 1; + InitializeListHead( &Event->Header.WaitListHead ); +} + + +/*********************************************************************** + * KeClearEvent (NTOSKRNL.EXE.@) + */ +void WINAPI KeClearEvent( PRKEVENT Event ) +{ + TRACE( "%p\n", Event ); + InterlockedExchange( &Event->Header.SignalState, 0 ); } @@ -1097,7 +2185,13 @@ void WINAPI KeInitializeEvent( PRKEVENT Event, EVENT_TYPE Type, BOOLEAN State ) */ void WINAPI KeInitializeMutex(PRKMUTEX Mutex, ULONG Level) { - FIXME( "stub: %p, %u\n", Mutex, Level ); + TRACE( "%p, %u\n", Mutex, Level ); + RtlZeroMemory( Mutex, sizeof(KMUTEX) ); + Mutex->Header.Type = 2; + Mutex->Header.Size = 8; + Mutex->Header.SignalState = 1; + InitializeListHead( &Mutex->Header.WaitListHead ); + Mutex->ApcDisable = 1; } @@ -1118,7 +2212,7 @@ NTSTATUS WINAPI KeWaitForMutexObject(PRKMUTEX Mutex, KWAIT_REASON WaitReason, KP LONG WINAPI KeReleaseMutex(PRKMUTEX Mutex, BOOLEAN Wait) { FIXME( "stub: %p, %d\n", Mutex, Wait ); - return STATUS_NOT_IMPLEMENTED; + return STATUS_SUCCESS; } @@ -1128,6 +2222,8 @@ LONG WINAPI KeReleaseMutex(PRKMUTEX Mutex, BOOLEAN Wait) void WINAPI KeInitializeSemaphore( PRKSEMAPHORE Semaphore, LONG Count, LONG Limit ) { FIXME( "(%p %d %d) stub\n", Semaphore , Count, Limit ); + RtlZeroMemory( Semaphore, sizeof(KSEMAPHORE) ); + Semaphore->Header.Type = 5; } @@ -1146,6 +2242,8 @@ void WINAPI KeInitializeSpinLock( PKSPIN_LOCK SpinLock ) void WINAPI KeInitializeTimerEx( PKTIMER Timer, TIMER_TYPE Type ) { FIXME( "stub: %p %d\n", Timer, Type ); + RtlZeroMemory( Timer, sizeof(KTIMER) ); + Timer->Header.Type = Type ? 9 : 8; } @@ -1238,8 +2336,8 @@ ULONG WINAPI KeQueryTimeIncrement(void) */ LONG WINAPI KeResetEvent( PRKEVENT Event ) { - FIXME("(%p): stub\n", Event); - return 0; + TRACE("(%p)\n", Event); + return InterlockedExchange( &Event->Header.SignalState, 0 ); } @@ -1248,8 +2346,23 @@ LONG WINAPI KeResetEvent( PRKEVENT Event ) */ LONG WINAPI KeSetEvent( PRKEVENT Event, KPRIORITY Increment, BOOLEAN Wait ) { - FIXME("(%p, %d, %d): stub\n", Event, Increment, Wait); - return 0; + struct HandleInstance *inst; + LONG ret; + + TRACE("(%p, %d, %d)\n", Event, Increment, Wait); + + ret = InterlockedExchange( &Event->Header.SignalState, 1 ); + EnterCriticalSection( &cs ); + LIST_FOR_EACH_ENTRY( inst, &Handles, struct HandleInstance, entry ) + { + if (inst->object == Event) + { + NtSetEvent( inst->handle, NULL ); + break; + } + } + LeaveCriticalSection( &cs ); + return ret; } @@ -1272,8 +2385,84 @@ NTSTATUS WINAPI KeWaitForSingleObject(PVOID Object, BOOLEAN Alertable, PLARGE_INTEGER Timeout) { - FIXME( "stub: %p, %d, %d, %d, %p\n", Object, WaitReason, WaitMode, Alertable, Timeout ); - return STATUS_NOT_IMPLEMENTED; + DISPATCHER_HEADER *header = Object; + NTSTATUS status = STATUS_SUCCESS; + + TRACE( "%p, %d, %d, %d, %p\n", Object, WaitReason, WaitMode, Alertable, Timeout ); + + switch (header->Type) + { + case NotificationEvent: + case SynchronizationEvent: + { + struct HandleInstance *inst; + HANDLE event_handle = NULL; + + if (InterlockedCompareExchange( &header->SignalState, 0, header->Type )) + { + status = STATUS_SUCCESS; + break; + } + + EnterCriticalSection( &cs ); + LIST_FOR_EACH_ENTRY( inst, &Handles, struct HandleInstance, entry ) + { + if (inst->object == Object) + { + event_handle = inst->handle; + ++inst->refs; + break; + } + } + while (event_handle == NULL) + { + OBJECT_ATTRIBUTES attr; + + RtlZeroMemory( &attr, sizeof(attr) ); + attr.Length = sizeof(attr); + status = NtCreateEvent( &event_handle, EVENT_ALL_ACCESS, &attr, + !header->Type, FALSE ); + if (status != STATUS_SUCCESS) + break; + inst = HeapAlloc( GetProcessHeap(), 0, sizeof(*inst) ); + if (inst == NULL) + { + NtClose( event_handle ); + status = STATUS_NO_MEMORY; + break; + } + inst->object = Object; + inst->handle = event_handle; + inst->refs = 1; + list_add_head( &Handles, &inst->entry ); + } + LeaveCriticalSection( &cs ); + if (status != STATUS_SUCCESS) + break; + + status = NtWaitForSingleObject( event_handle, Alertable, Timeout ); + + EnterCriticalSection( &cs ); + LIST_FOR_EACH_ENTRY( inst, &Handles, struct HandleInstance, entry ) + { + if (inst->object == Object) + { + if (!--inst->refs) + { + list_remove( &inst->entry ); + NtClose( inst->handle ); + HeapFree( GetProcessHeap(), 0, inst ); + } + break; + } + } + LeaveCriticalSection( &cs ); + break; + } + default: + WARN( "synchronization object %u is not supported\n", header->Type ); + } + return status; } /*********************************************************************** @@ -1371,6 +2560,16 @@ PVOID WINAPI MmMapIoSpace( PHYSICAL_ADDRESS PhysicalAddress, DWORD NumberOfBytes return NULL; } + /*********************************************************************** + * MmMapLockedPages (NTOSKRNL.EXE.@) + */ +PVOID WINAPI MmMapLockedPages(PMDL MemoryDescriptorList, + KPROCESSOR_MODE AccessMode) +{ + TRACE("%p %d\n", MemoryDescriptorList, AccessMode); + return MemoryDescriptorList->MappedSystemVa; +} + /*********************************************************************** * MmPageEntireDriver (NTOSKRNL.EXE.@) */ @@ -1381,6 +2580,16 @@ PVOID WINAPI MmPageEntireDriver(PVOID AddrInSection) } /*********************************************************************** + * MmProbeAndLockPages (NTOSKRNL.EXE.@) + */ +void WINAPI MmProbeAndLockPages(PMDL MemoryDescriptorList, + KPROCESSOR_MODE AccessMode, + LOCK_OPERATION Operation) +{ + FIXME("stub: %p %d %u\n", MemoryDescriptorList, AccessMode, Operation); +} + +/*********************************************************************** * MmResetDriverPaging (NTOSKRNL.EXE.@) */ void WINAPI MmResetDriverPaging(PVOID AddrInSection) @@ -1433,6 +2642,36 @@ NTSTATUS WINAPI ObReferenceObjectByName( UNICODE_STRING *ObjectName, } /*********************************************************************** + * MmUnmapLockedPages (NTOSKRNL.EXE.@) + */ +void WINAPI MmUnmapLockedPages(PVOID BaseAddress, PMDL MemoryDescriptorList) +{ + TRACE("%p %p\n", BaseAddress, MemoryDescriptorList); +} + + +/*********************************************************************** + * ObReferenceObjectByPointer (NTOSKRNL.EXE.@) + */ +NTSTATUS WINAPI ObReferenceObjectByPointer( VOID *obj, ACCESS_MASK access, + POBJECT_TYPE type, + KPROCESSOR_MODE mode ) +{ + FIXME( "stub: %p %x %p %d\n", obj, access, type, mode ); + return STATUS_NOT_IMPLEMENTED; +} + + +/*********************************************************************** + * ObDereferenceObject (NTOSKRNL.EXE.@) + */ +void WINAPI ObDereferenceObject( VOID *obj ) +{ + FIXME( "stub: %p\n", obj ); +} + + +/*********************************************************************** * ObfDereferenceObject (NTOSKRNL.EXE.@) */ #ifdef DEFINE_FASTCALL1_ENTRYPOINT @@ -1442,11 +2681,19 @@ void WINAPI __regs_ObfDereferenceObject( VOID *obj ) void WINAPI ObfDereferenceObject( VOID *obj ) #endif { - FIXME( "stub: %p\n", obj ); + ObDereferenceObject( obj ); } /*********************************************************************** + * MmUnlockPages (NTOSKRNL.EXE.@) + */ +void WINAPI MmUnlockPages(PMDL MemoryDescriptorList) +{ + FIXME("stub: %p\n", MemoryDescriptorList); +} + +/*********************************************************************** * PsCreateSystemThread (NTOSKRNL.EXE.@) */ NTSTATUS WINAPI PsCreateSystemThread(PHANDLE ThreadHandle, ULONG DesiredAccess, @@ -1597,10 +2844,10 @@ VOID WINAPI READ_REGISTER_BUFFER_UCHAR(PUCHAR Register, PUCHAR Buffer, ULONG Cou /***************************************************** * PoSetPowerState (NTOSKRNL.EXE.@) */ -POWER_STATE WINAPI PoSetPowerState(PDEVICE_OBJECT DeviceObject, POWER_STATE_TYPE Type, POWER_STATE State) +UINT WINAPI PoSetPowerState(PDEVICE_OBJECT DeviceObject, POWER_STATE_TYPE Type, POWER_STATE State) { FIXME("(%p %u %u) stub\n", DeviceObject, Type, State.DeviceState); - return State; + return State.DeviceState; } /***************************************************** @@ -1647,6 +2894,8 @@ BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, LPVOID reserved ) { static void *handler; LARGE_INTEGER count; + struct DriverObjExtension *ext, *ext2; + struct InterfaceInstance *intf, *intf2; switch(reason) { @@ -1659,6 +2908,21 @@ BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, LPVOID reserved ) break; case DLL_PROCESS_DETACH: RtlRemoveVectoredExceptionHandler( handler ); + LIST_FOR_EACH_ENTRY_SAFE( ext, ext2, &DriverObjExtensions, + struct DriverObjExtension, entry ) + { + list_remove( &ext->entry ); + ExFreePool( ext->ptr ); + ExFreePool( ext ); + } + LIST_FOR_EACH_ENTRY_SAFE( intf, intf2, &Interfaces, + struct InterfaceInstance, entry ) + { + list_remove( &intf->entry ); + RtlFreeUnicodeString( &intf->target ); + RtlFreeHeap( GetProcessHeap(), 0, intf->link ); + RtlFreeHeap( GetProcessHeap(), 0, intf ); + } break; } return TRUE; diff --git a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec index 3ecb7ec..3cf7c6a 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec +++ b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec @@ -1,4 +1,4 @@ -@ stub ExAcquireFastMutexUnsafe +@ stdcall -norelay ExAcquireFastMutexUnsafe(ptr) @ stub ExAcquireRundownProtection @ stub ExAcquireRundownProtectionEx @ stub ExInitializeRundownProtection @@ -8,7 +8,7 @@ @ stub ExInterlockedPopEntrySList @ stub ExInterlockedPushEntrySList @ stub ExReInitializeRundownProtection -@ stub ExReleaseFastMutexUnsafe +@ stdcall -norelay ExReleaseFastMutexUnsafe(ptr) @ stub ExReleaseResourceLite @ stub ExReleaseRundownProtection @ stub ExReleaseRundownProtectionEx @@ -303,7 +303,7 @@ @ stub InbvSetTextColor @ stub InbvSolidColorFill @ stub InitSafeBootMode -@ stub IoAcquireCancelSpinLock +@ stdcall IoAcquireCancelSpinLock(ptr) @ stub IoAcquireRemoveLockEx @ stub IoAcquireVpbSpinLock @ stub IoAdapterObjectType @@ -356,7 +356,7 @@ @ stdcall IoDeleteDevice(ptr) @ stdcall IoDeleteDriver(ptr) @ stdcall IoDeleteSymbolicLink(ptr) -@ stub IoDetachDevice +@ stdcall IoDetachDevice(ptr) @ stub IoDeviceHandlerObjectSize @ stub IoDeviceHandlerObjectType @ stub IoDeviceObjectType @@ -371,10 +371,10 @@ @ stub IoFreeController @ stub IoFreeErrorLogEntry @ stdcall IoFreeIrp(ptr) -@ stub IoFreeMdl +@ stdcall IoFreeMdl(ptr) @ stub IoFreeWorkItem @ stub IoGetAttachedDevice -@ stub IoGetAttachedDeviceReference +@ stdcall IoGetAttachedDeviceReference(ptr) @ stub IoGetBaseFileSystemDeviceObject @ stub IoGetBootDiskInformation @ stdcall IoGetConfigurationInformation() @@ -400,7 +400,7 @@ @ stdcall IoInitializeIrp(ptr long long) @ stub IoInitializeRemoveLockEx @ stdcall IoInitializeTimer(ptr ptr ptr) -@ stub IoInvalidateDeviceRelations +@ stdcall IoInvalidateDeviceRelations(ptr long) @ stub IoInvalidateDeviceState @ stub IoIsFileOriginRemote @ stub IoIsOperationSynchronous @@ -425,14 +425,14 @@ @ stub IoReadPartitionTableEx @ stub IoReadTransferCount @ stub IoRegisterBootDriverReinitialization -@ stub IoRegisterDeviceInterface +@ stdcall IoRegisterDeviceInterface(ptr ptr ptr ptr) @ stdcall IoRegisterDriverReinitialization(ptr ptr ptr) @ stdcall IoRegisterFileSystem(ptr) @ stub IoRegisterFsRegistrationChange @ stub IoRegisterLastChanceShutdownNotification -@ stub IoRegisterPlugPlayNotification +@ stdcall IoRegisterPlugPlayNotification(long long ptr ptr ptr ptr ptr) @ stdcall IoRegisterShutdownNotification(ptr) -@ stub IoReleaseCancelSpinLock +@ stdcall IoReleaseCancelSpinLock(long) @ stub IoReleaseRemoveLockAndWaitEx @ stub IoReleaseRemoveLockEx @ stub IoReleaseVpbSpinLock @@ -446,7 +446,7 @@ @ stub IoRequestDeviceEject @ stub IoReuseIrp @ stub IoSetCompletionRoutineEx -@ stub IoSetDeviceInterfaceState +@ stdcall IoSetDeviceInterfaceState(ptr long) @ stub IoSetDeviceToVerify @ stub IoSetFileOrigin @ stub IoSetHardErrorOrVerifyDevice @@ -469,7 +469,7 @@ @ stub IoThreadToProcess @ stdcall IoUnregisterFileSystem(ptr) @ stub IoUnregisterFsRegistrationChange -@ stub IoUnregisterPlugPlayNotification +@ stdcall IoUnregisterPlugPlayNotification(ptr) @ stub IoUnregisterShutdownNotification @ stub IoUpdateShareAccess @ stub IoValidateDeviceIoControlAccess @@ -515,10 +515,10 @@ @ stub KeBugCheckEx @ stub KeCancelTimer @ stub KeCapturePersistentThreadState -@ stub KeClearEvent +@ stdcall KeClearEvent(ptr) @ stub KeConnectInterrupt @ stub KeDcacheFlushCount -@ stub KeDelayExecutionThread +@ stdcall KeDelayExecutionThread(long long ptr) @ stub KeDeregisterBugCheckCallback @ stub KeDeregisterBugCheckReasonCallback @ stub KeDetachProcess @@ -689,7 +689,7 @@ @ stub MmLockPagableImageSection @ stub MmLockPagableSectionByHandle @ stdcall MmMapIoSpace(long long long long) -@ stub MmMapLockedPages +@ stdcall MmMapLockedPages(ptr long) @ stub MmMapLockedPagesSpecifyCache @ stub MmMapLockedPagesWithReservedMapping @ stub MmMapMemoryDumpMdl @@ -702,7 +702,7 @@ @ stub MmMarkPhysicalMemoryAsGood @ stdcall MmPageEntireDriver(ptr) @ stub MmPrefetchPages -@ stub MmProbeAndLockPages +@ stdcall MmProbeAndLockPages(ptr long long) @ stub MmProbeAndLockProcessPages @ stub MmProbeAndLockSelectedPages @ stub MmProtectMdlSystemAddress @@ -717,9 +717,9 @@ @ stub MmSystemRangeStart @ stub MmTrimAllSystemPagableMemory @ stub MmUnlockPagableImageSection -@ stub MmUnlockPages +@ stdcall MmUnlockPages(ptr) @ stdcall MmUnmapIoSpace(ptr long) -@ stub MmUnmapLockedPages +@ stdcall MmUnmapLockedPages(ptr ptr) @ stub MmUnmapReservedMapping @ stub MmUnmapVideoDisplay @ stub MmUnmapViewInSessionSpace @@ -798,7 +798,7 @@ @ stub ObCloseHandle @ stub ObCreateObject @ stub ObCreateObjectType -@ stub ObDereferenceObject +@ stdcall ObDereferenceObject(ptr) @ stub ObDereferenceSecurityDescriptor @ stub ObFindHandleForObject @ stub ObGetObjectSecurity @@ -811,7 +811,7 @@ @ stub ObQueryObjectAuditingByHandle @ stdcall ObReferenceObjectByHandle(long long ptr long ptr ptr) @ stdcall ObReferenceObjectByName(ptr long ptr long ptr long ptr ptr) -@ stub ObReferenceObjectByPointer +@ stdcall ObReferenceObjectByPointer(ptr long ptr long) @ stub ObReferenceSecurityDescriptor @ stub ObReleaseObjectSecurity @ stub ObSetHandleAttributes @@ -1490,3 +1490,9 @@ # or 'wine_' (for user-visible functions) to avoid namespace conflicts. @ cdecl wine_ntoskrnl_main_loop(long) +@ cdecl __wine_add_device(ptr ptr) +@ cdecl __wine_add_driver_object(ptr wstr) +@ cdecl __wine_del_driver_object(ptr) +@ cdecl __wine_get_driver_object(wstr) +@ cdecl __wine_start_device(ptr) +@ cdecl __wine_start_service(wstr) diff --git a/dlls/usbd.sys/usbd.c b/dlls/usbd.sys/usbd.c index 80fd02e..f20c481 100644 --- a/dlls/usbd.sys/usbd.c +++ b/dlls/usbd.sys/usbd.c @@ -198,6 +198,15 @@ PUSB_INTERFACE_DESCRIPTOR WINAPI USBD_ParseConfigurationDescriptorEx( return NULL; } +PUSB_INTERFACE_DESCRIPTOR WINAPI USBD_ParseConfigurationDescriptor( + PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor, + UCHAR InterfaceNumber, UCHAR AlternateSetting ) +{ + TRACE( "(%p, %u, %u)\n", ConfigurationDescriptor, InterfaceNumber, AlternateSetting ); + return USBD_ParseConfigurationDescriptorEx( ConfigurationDescriptor, ConfigurationDescriptor, + InterfaceNumber, AlternateSetting, -1, -1, -1 ); +} + PUSB_COMMON_DESCRIPTOR WINAPI USBD_ParseDescriptors( PVOID DescriptorBuffer, ULONG TotalLength, diff --git a/dlls/usbd.sys/usbd.sys.spec b/dlls/usbd.sys/usbd.sys.spec index 1cb507e..8104baa 100644 --- a/dlls/usbd.sys/usbd.sys.spec +++ b/dlls/usbd.sys/usbd.sys.spec @@ -20,7 +20,7 @@ @ stdcall USBD_GetUSBDIVersion(ptr) @ stub USBD_InitializeDevice @ stub USBD_MakePdoName -@ stub USBD_ParseConfigurationDescriptor +@ stdcall USBD_ParseConfigurationDescriptor(ptr long long) @ stdcall _USBD_ParseConfigurationDescriptorEx@28(ptr ptr long long long long long) USBD_ParseConfigurationDescriptorEx @ stdcall _USBD_ParseDescriptors@16(ptr long ptr long) USBD_ParseDescriptors @ stub USBD_QueryBusTime diff --git a/include/cfgmgr32.h b/include/cfgmgr32.h index cd26df8..f8d3237 100644 --- a/include/cfgmgr32.h +++ b/include/cfgmgr32.h @@ -100,6 +100,7 @@ typedef DWORD CONFIGRET; #define MAX_DEVICE_ID_LEN 200 #define MAX_DEVNODE_ID_LEN MAX_DEVICE_ID_LEN +#define MAX_DEVICE_ID_LEN 200 #define MAX_CLASS_NAME_LEN 32 #define MAX_GUID_STRING_LEN 39 #define MAX_PROFILE_LEN 80 diff --git a/include/ddk/ntddk.h b/include/ddk/ntddk.h index df58951..f0100f4 100644 --- a/include/ddk/ntddk.h +++ b/include/ddk/ntddk.h @@ -127,6 +127,15 @@ typedef struct _IMAGE_INFO ULONG ImageSectionNumber; } IMAGE_INFO, *PIMAGE_INFO; +#define IRP_MN_QUERY_LEGACY_BUS_INFORMATION 0x18 + +#define DO_VERIFY_VOLUME 0x00000002 +#define DO_DEVICE_HAS_NAME 0x00000040 +#define DO_SYSTEM_BOOT_PARTITION 0x00000100 +#define DO_LONG_TERM_REQUESTS 0x00000200 +#define DO_NEVER_LAST_DEVICE 0x00000400 +#define DO_LOW_PRIORITY_FILESYSTEM 0x00010000 + typedef VOID (WINAPI *PDRIVER_REINITIALIZE)(PDRIVER_OBJECT,PVOID,ULONG); typedef VOID (WINAPI *PLOAD_IMAGE_NOTIFY_ROUTINE)(PUNICODE_STRING,HANDLE,PIMAGE_INFO); typedef NTSTATUS (WINAPI *PIO_QUERY_DEVICE_ROUTINE)(PVOID,PUNICODE_STRING,INTERFACE_TYPE,ULONG, diff --git a/include/ddk/usb100.h b/include/ddk/usb100.h index b013f21..a85e0b7 100644 --- a/include/ddk/usb100.h +++ b/include/ddk/usb100.h @@ -102,6 +102,16 @@ typedef struct _USB_COMMON_DESCRIPTOR { } USB_COMMON_DESCRIPTOR; typedef struct _USB_COMMON_DESCRIPTOR *PUSB_COMMON_DESCRIPTOR; +typedef struct _USB_HUB_DESCRIPTOR { + UCHAR bDescriptorLength; + UCHAR bDescriptorType; + UCHAR bNumberOfPorts; + USHORT wHubCharacteristics; + UCHAR bPowerOnToPowerGood; + UCHAR bHubControlCurrent; + UCHAR bRemoveAndPowerMask[64]; +} USB_HUB_DESCRIPTOR, *PUSB_HUB_DESCRIPTOR; + #include #endif diff --git a/include/ddk/usbdlib.h b/include/ddk/usbdlib.h index 0003816..ade33a2 100644 --- a/include/ddk/usbdlib.h +++ b/include/ddk/usbdlib.h @@ -30,6 +30,7 @@ PURB WINAPI USBD_CreateConfigurationRequestEx(PUSB_CONFIGURATION_DESCRIPTOR,PUSB ULONG WINAPI USBD_GetInterfaceLength(PUSB_INTERFACE_DESCRIPTOR,PUCHAR); VOID WINAPI USBD_GetUSBDIVersion(PUSBD_VERSION_INFORMATION); PUSB_COMMON_DESCRIPTOR WINAPI USBD_ParseDescriptors(PVOID,ULONG,PVOID,LONG); +PUSB_INTERFACE_DESCRIPTOR WINAPI USBD_ParseConfigurationDescriptor(PUSB_CONFIGURATION_DESCRIPTOR,UCHAR,UCHAR); PUSB_INTERFACE_DESCRIPTOR WINAPI USBD_ParseConfigurationDescriptorEx(PUSB_CONFIGURATION_DESCRIPTOR,PVOID,LONG,LONG,LONG,LONG,LONG); #endif diff --git a/include/ddk/usbdrivr.h b/include/ddk/usbdrivr.h new file mode 100644 index 0000000..557cbe1 --- /dev/null +++ b/include/ddk/usbdrivr.h @@ -0,0 +1,28 @@ +/* + * Copyright 2009 Alexander Morozov for Etersoft + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __USBDRIVR_H__ +#define __USBDRIVR_H__ + +#include +#include +#include + +#define IOCTL_INTERNAL_USB_SUBMIT_URB CTL_CODE(FILE_DEVICE_USB, USB_SUBMIT_URB, METHOD_NEITHER, FILE_ANY_ACCESS) + +#endif /* __USBDRIVR_H__ */ diff --git a/include/ddk/usbioctl.h b/include/ddk/usbioctl.h new file mode 100644 index 0000000..25b2bde --- /dev/null +++ b/include/ddk/usbioctl.h @@ -0,0 +1,97 @@ +/* + * Copyright 2009 Alexander Morozov for Etersoft + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __USBIOCTL_H__ +#define __USBIOCTL_H__ + +#include +#include + +#define IOCTL_USB_GET_NODE_INFORMATION CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_INFORMATION, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_DRIVERKEY_NAME, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_ROOT_HUB_NAME CTL_CODE(FILE_DEVICE_USB, HCD_GET_ROOT_HUB_NAME, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#include + +typedef enum _USB_HUB_NODE { + UsbHub, + UsbMIParent +} USB_HUB_NODE; + +typedef struct _USB_HUB_INFORMATION { + USB_HUB_DESCRIPTOR HubDescriptor; + BOOLEAN HubIsBusPowered; +} USB_HUB_INFORMATION, *PUSB_HUB_INFORMATION; + +typedef struct _USB_MI_PARENT_INFORMATION { + ULONG NumberOfInterfaces; +} USB_MI_PARENT_INFORMATION, *PUSB_MI_PARENT_INFORMATION; + +typedef struct _USB_NODE_INFORMATION { + USB_HUB_NODE NodeType; + union { + USB_HUB_INFORMATION HubInformation; + USB_MI_PARENT_INFORMATION MiParentInformation; + } u; +} USB_NODE_INFORMATION, *PUSB_NODE_INFORMATION; + +typedef struct _USB_PIPE_INFO { + USB_ENDPOINT_DESCRIPTOR EndpointDescriptor; + ULONG ScheduleOffset; +} USB_PIPE_INFO, *PUSB_PIPE_INFO; + +typedef enum _USB_CONNECTION_STATUS { + NoDeviceConnected, + DeviceConnected, + DeviceFailedEnumeration, + DeviceGeneralFailure, + DeviceCausedOvercurrent, + DeviceNotEnoughPower, + DeviceNotEnoughBandwidth, + DeviceHubNestedTooDeeply, + DeviceInLegacyHub +} USB_CONNECTION_STATUS, *PUSB_CONNECTION_STATUS; + +typedef struct _USB_NODE_CONNECTION_INFORMATION { + ULONG ConnectionIndex; + USB_DEVICE_DESCRIPTOR DeviceDescriptor; + UCHAR CurrentConfigurationValue; + BOOLEAN LowSpeed; + BOOLEAN DeviceIsHub; + USHORT DeviceAddress; + ULONG NumberOfOpenPipes; + USB_CONNECTION_STATUS ConnectionStatus; + USB_PIPE_INFO PipeList[0]; +} USB_NODE_CONNECTION_INFORMATION, *PUSB_NODE_CONNECTION_INFORMATION; + +typedef struct _USB_NODE_CONNECTION_DRIVERKEY_NAME { + ULONG ConnectionIndex; + ULONG ActualLength; + WCHAR DriverKeyName[1]; +} USB_NODE_CONNECTION_DRIVERKEY_NAME, *PUSB_NODE_CONNECTION_DRIVERKEY_NAME; + +typedef struct _USB_HCD_DRIVERKEY_NAME { + ULONG ActualLength; + WCHAR DriverKeyName[1]; +} USB_HCD_DRIVERKEY_NAME, *PUSB_HCD_DRIVERKEY_NAME; + +#include + +#endif /* __USBIOCTL_H__ */ diff --git a/include/ddk/usbiodef.h b/include/ddk/usbiodef.h new file mode 100644 index 0000000..dbce800 --- /dev/null +++ b/include/ddk/usbiodef.h @@ -0,0 +1,35 @@ +/* + * Copyright 2009 Alexander Morozov for Etersoft + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __USBIODEF_H__ +#define __USBIODEF_H__ + +#define USB_SUBMIT_URB 0 + +#define USB_GET_NODE_INFORMATION 258 +#define USB_GET_NODE_CONNECTION_INFORMATION 259 +#define USB_GET_NODE_CONNECTION_DRIVERKEY_NAME 264 + +#define HCD_GET_ROOT_HUB_NAME 258 + +DEFINE_GUID( GUID_DEVINTERFACE_USB_HUB, + 0xF18A0E88, 0xC30C, 0x11D0, 0x88, 0x15, 0x00, 0xA0, 0xC9, 0x06, 0xBE, 0xD8 ); + +#define FILE_DEVICE_USB FILE_DEVICE_UNKNOWN + +#endif /* __USBIODEF_H__ */ diff --git a/include/ddk/wdm.h b/include/ddk/wdm.h index 8f5aa04..cf685de 100644 --- a/include/ddk/wdm.h +++ b/include/ddk/wdm.h @@ -1064,6 +1064,36 @@ typedef enum _MM_SYSTEM_SIZE MmLargeSystem } MM_SYSTEMSIZE; +typedef enum _LOCK_OPERATION { + IoReadAccess, + IoWriteAccess, + IoModifyAccess +} LOCK_OPERATION; + +typedef struct _DEVICE_RELATIONS { + ULONG Count; + PDEVICE_OBJECT Objects[1]; +} DEVICE_RELATIONS, *PDEVICE_RELATIONS; + +typedef enum _IO_NOTIFICATION_EVENT_CATEGORY { + EventCategoryReserved, + EventCategoryHardwareProfileChange, + EventCategoryDeviceInterfaceChange, + EventCategoryTargetDeviceChange +} IO_NOTIFICATION_EVENT_CATEGORY; + +#define PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES 0x00000001 + +typedef struct _DEVICE_INTERFACE_CHANGE_NOTIFICATION { + USHORT Version; + USHORT Size; + GUID Event; + GUID InterfaceClassGuid; + PUNICODE_STRING SymbolicLinkName; +} DEVICE_INTERFACE_CHANGE_NOTIFICATION, *PDEVICE_INTERFACE_CHANGE_NOTIFICATION; + +typedef NTSTATUS (WINAPI *PDRIVER_NOTIFICATION_CALLBACK_ROUTINE)(PVOID,PVOID); + NTSTATUS WINAPI ObCloseHandle(IN HANDLE handle); #ifdef NONAMELESSUNION @@ -1108,6 +1138,7 @@ void WINAPI ExFreePoolWithTag(PVOID,ULONG); NTSTATUS WINAPI IoAllocateDriverObjectExtension(PDRIVER_OBJECT,PVOID,ULONG,PVOID*); PVOID WINAPI IoAllocateErrorLogEntry(PVOID,UCHAR); PIRP WINAPI IoAllocateIrp(CCHAR,BOOLEAN); +PIRP WINAPI IoBuildDeviceIoControlRequest(ULONG,PDEVICE_OBJECT,PVOID,ULONG,PVOID,ULONG,BOOLEAN,PKEVENT,PIO_STATUS_BLOCK); NTSTATUS WINAPI IoCallDriver(DEVICE_OBJECT*,IRP*); VOID WINAPI IoCompleteRequest(IRP*,UCHAR); NTSTATUS WINAPI IoCreateDevice(DRIVER_OBJECT*,ULONG,UNICODE_STRING*,DEVICE_TYPE,ULONG,BOOLEAN,DEVICE_OBJECT**); @@ -1116,7 +1147,9 @@ NTSTATUS WINAPI IoCreateSymbolicLink(UNICODE_STRING*,UNICODE_STRING*); void WINAPI IoDeleteDevice(DEVICE_OBJECT*); void WINAPI IoDeleteDriver(DRIVER_OBJECT*); NTSTATUS WINAPI IoDeleteSymbolicLink(UNICODE_STRING*); +void WINAPI IoDetachDevice(DEVICE_OBJECT*); void WINAPI IoFreeIrp(IRP*); +PDEVICE_OBJECT WINAPI IoGetAttachedDeviceReference(PDEVICE_OBJECT); PEPROCESS WINAPI IoGetCurrentProcess(void); NTSTATUS WINAPI IoGetDeviceInterfaces(CONST GUID*,PDEVICE_OBJECT,ULONG,PWSTR*); NTSTATUS WINAPI IoGetDeviceObjectPointer(UNICODE_STRING*,ACCESS_MASK,PFILE_OBJECT*,PDEVICE_OBJECT*); @@ -1124,9 +1157,17 @@ NTSTATUS WINAPI IoGetDeviceProperty(PDEVICE_OBJECT,DEVICE_REGISTRY_PROPERTY,ULO PVOID WINAPI IoGetDriverObjectExtension(PDRIVER_OBJECT,PVOID); PDEVICE_OBJECT WINAPI IoGetRelatedDeviceObject(PFILE_OBJECT); void WINAPI IoInitializeIrp(IRP*,USHORT,CCHAR); +void WINAPI IoInvalidateDeviceRelations(PDEVICE_OBJECT,DEVICE_RELATION_TYPE); +NTSTATUS WINAPI IoRegisterDeviceInterface(PDEVICE_OBJECT,CONST GUID*,PUNICODE_STRING,PUNICODE_STRING); +NTSTATUS WINAPI IoRegisterPlugPlayNotification(IO_NOTIFICATION_EVENT_CATEGORY,ULONG,PVOID,PDRIVER_OBJECT,PDRIVER_NOTIFICATION_CALLBACK_ROUTINE,PVOID,PVOID*); +NTSTATUS WINAPI IoSetDeviceInterfaceState(PUNICODE_STRING,BOOLEAN); +NTSTATUS WINAPI IoUnregisterPlugPlayNotification(PVOID); NTSTATUS WINAPI IoWMIRegistrationControl(PDEVICE_OBJECT,ULONG); +void WINAPI KeClearEvent(PRKEVENT); +NTSTATUS WINAPI KeDelayExecutionThread(KPROCESSOR_MODE,BOOLEAN,PLARGE_INTEGER); PKTHREAD WINAPI KeGetCurrentThread(void); +void WINAPI KeInitializeEvent(PRKEVENT,EVENT_TYPE,BOOLEAN); void WINAPI KeQuerySystemTime(LARGE_INTEGER*); void WINAPI KeQueryTickCount(LARGE_INTEGER*); ULONG WINAPI KeQueryTimeIncrement(void); @@ -1134,6 +1175,7 @@ LONG WINAPI KeReleaseSemaphore(PRKSEMAPHORE,KPRIORITY,LONG,BOOLEAN); LONG WINAPI KeResetEvent(PRKEVENT); LONG WINAPI KeSetEvent(PRKEVENT,KPRIORITY,BOOLEAN); KPRIORITY WINAPI KeSetPriorityThread(PKTHREAD,KPRIORITY); +NTSTATUS WINAPI KeWaitForSingleObject(PVOID,KWAIT_REASON,KPROCESSOR_MODE,BOOLEAN,PLARGE_INTEGER); PVOID WINAPI MmAllocateContiguousMemory(SIZE_T,PHYSICAL_ADDRESS); PVOID WINAPI MmAllocateNonCachedMemory(SIZE_T); @@ -1141,9 +1183,10 @@ PMDL WINAPI MmAllocatePagesForMdl(PHYSICAL_ADDRESS,PHYSICAL_ADDRESS,PHYSICA void WINAPI MmFreeNonCachedMemory(PVOID,SIZE_T); MM_SYSTEMSIZE WINAPI MmQuerySystemSize(void); +void WINAPI ObDereferenceObject(VOID*); NTSTATUS WINAPI ObReferenceObjectByHandle(HANDLE,ACCESS_MASK,POBJECT_TYPE,KPROCESSOR_MODE,PVOID*,POBJECT_HANDLE_INFORMATION); +NTSTATUS WINAPI ObReferenceObjectByPointer(VOID*,ACCESS_MASK,POBJECT_TYPE,KPROCESSOR_MODE); -POWER_STATE WINAPI PoSetPowerState(PDEVICE_OBJECT,POWER_STATE_TYPE,POWER_STATE); NTSTATUS WINAPI PsCreateSystemThread(PHANDLE,ULONG,POBJECT_ATTRIBUTES,HANDLE,PCLIENT_ID,PKSTART_ROUTINE,PVOID); #define PsGetCurrentProcess() IoGetCurrentProcess() #define PsGetCurrentThread() ((PETHREAD)KeGetCurrentThread()) diff --git a/include/ddk/wdmguid.h b/include/ddk/wdmguid.h new file mode 100644 index 0000000..f506380 --- /dev/null +++ b/include/ddk/wdmguid.h @@ -0,0 +1,28 @@ +/* + * Copyright 2009 Alexander Morozov for Etersoft + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_WDMGUID_H +#define __WINE_WDMGUID_H + +DEFINE_GUID(GUID_DEVICE_INTERFACE_ARRIVAL, +0xcb3a4004L, 0x46f0, 0x11d0, 0xb0, 0x8f, 0x00, 0x60, 0x97, 0x13, 0x05, 0x3f); + +DEFINE_GUID(GUID_DEVICE_INTERFACE_REMOVAL, +0xcb3a4005L, 0x46f0, 0x11d0, 0xb0, 0x8f, 0x00, 0x60, 0x97, 0x13, 0x05, 0x3f); + +#endif /* __WINE_WDMGUID_H */ diff --git a/programs/services/services.c b/programs/services/services.c index 2aa3683..4bd0220 100644 --- a/programs/services/services.c +++ b/programs/services/services.c @@ -20,6 +20,8 @@ #define WIN32_LEAN_AND_MEAN +#include "config.h" + #include #include #include @@ -252,6 +254,11 @@ DWORD scmdatabase_remove_service(struct scmdatabase *db, struct service_entry *s static void scmdatabase_autostart_services(struct scmdatabase *db) { +#ifdef HAVE_LIBUSB + static const WCHAR usbhub_started_event[] = {'_','_','w','i','n','e', + '_','U','s','b','h','u','b','S','t','a','r','t','e','d',0}; + static const WCHAR mountmgr[] = {'M','o','u','n','t','M','g','r',0}; +#endif struct service_entry **services_list; unsigned int i = 0; unsigned int size = 32; @@ -289,15 +296,29 @@ static void scmdatabase_autostart_services(struct scmdatabase *db) size = i; for (i = 0; i < size; i++) { +#ifdef HAVE_LIBUSB + HANDLE event = NULL; +#endif DWORD err; const WCHAR *argv[2]; service = services_list[i]; argv[0] = service->name; argv[1] = NULL; +#ifdef HAVE_LIBUSB + if (!strcmpW(service->name, mountmgr)) + event = CreateEventW(NULL, TRUE, FALSE, usbhub_started_event); +#endif err = service_start(service, 1, argv); if (err != ERROR_SUCCESS) WINE_FIXME("Auto-start service %s failed to start: %d\n", wine_dbgstr_w(service->name), err); +#ifdef HAVE_LIBUSB + else if (event) + { + WaitForSingleObject(event, 30000); + CloseHandle(event); + } +#endif release_service(service); } diff --git a/programs/winedevice/device.c b/programs/winedevice/device.c index 2ce8c15..1598694 100644 --- a/programs/winedevice/device.c +++ b/programs/winedevice/device.c @@ -29,9 +29,8 @@ #include "winbase.h" #include "winternl.h" #include "winreg.h" -#include "winnls.h" #include "winsvc.h" -#include "ddk/wdm.h" +#include "ddk/ntddk.h" #include "wine/unicode.h" #include "wine/debug.h" @@ -39,13 +38,21 @@ WINE_DEFAULT_DEBUG_CHANNEL(winedevice); WINE_DECLARE_DEBUG_CHANNEL(relay); extern NTSTATUS CDECL wine_ntoskrnl_main_loop( HANDLE stop_event ); +extern BOOL CDECL __wine_add_driver_object( DRIVER_OBJECT *driver, const WCHAR *service ); +extern void CDECL __wine_del_driver_object( const DRIVER_OBJECT *driver ); +extern HANDLE CDECL __wine_make_process_system(void); +#define EVENT_NAME_LEN (30 * sizeof(WCHAR)) + +static const WCHAR pipe_nameW[] = {'\\','\\','.','\\','p','i','p','e', + '\\','w','i','n','e','d','e','v','i','c','e',0}; +static const WCHAR winedevice_mutexW[] = {'_','_','w','i','n','e','_', + 'W','i','n','e','d','e','v','i','c','e',0}; + +/* these variables are used only by "winedevice driver_name" */ static WCHAR *driver_name; static SERVICE_STATUS_HANDLE service_handle; -static HKEY driver_hkey; static HANDLE stop_event; -static DRIVER_OBJECT driver_obj; -static DRIVER_EXTENSION driver_extension; /* find the LDR_MODULE corresponding to the driver module */ static LDR_MODULE *find_ldr_module( HMODULE module ) @@ -130,7 +137,9 @@ error: } /* call the driver init entry point */ -static NTSTATUS init_driver( HMODULE module, UNICODE_STRING *keyname ) +static NTSTATUS init_driver( HMODULE module, UNICODE_STRING *keyname, + const WCHAR *drv_name, PDRIVER_OBJECT driver_obj, + PDRIVER_EXTENSION driver_extension ) { unsigned int i; NTSTATUS status; @@ -138,36 +147,38 @@ static NTSTATUS init_driver( HMODULE module, UNICODE_STRING *keyname ) if (!nt->OptionalHeader.AddressOfEntryPoint) return STATUS_SUCCESS; - driver_obj.Size = sizeof(driver_obj); - driver_obj.DriverSection = find_ldr_module( module ); - driver_obj.DriverInit = (PDRIVER_INITIALIZE)((char *)module + nt->OptionalHeader.AddressOfEntryPoint); - driver_obj.DriverExtension = &driver_extension; + driver_obj->Size = sizeof(DRIVER_OBJECT); + driver_obj->DriverSection = find_ldr_module( module ); + driver_obj->DriverInit = (PDRIVER_INITIALIZE)((char *)module + nt->OptionalHeader.AddressOfEntryPoint); + driver_obj->DriverExtension = driver_extension; - driver_extension.DriverObject = &driver_obj; - driver_extension.ServiceKeyName = *keyname; + driver_extension->DriverObject = driver_obj; + driver_extension->ServiceKeyName = *keyname; if (WINE_TRACE_ON(relay)) WINE_DPRINTF( "%04x:Call driver init %p (obj=%p,str=%s)\n", GetCurrentThreadId(), - driver_obj.DriverInit, &driver_obj, wine_dbgstr_w(keyname->Buffer) ); + driver_obj->DriverInit, driver_obj, wine_dbgstr_w(keyname->Buffer) ); - status = driver_obj.DriverInit( &driver_obj, keyname ); + status = driver_obj->DriverInit( driver_obj, keyname ); if (WINE_TRACE_ON(relay)) WINE_DPRINTF( "%04x:Ret driver init %p (obj=%p,str=%s) retval=%08x\n", GetCurrentThreadId(), - driver_obj.DriverInit, &driver_obj, wine_dbgstr_w(keyname->Buffer), status ); + driver_obj->DriverInit, driver_obj, wine_dbgstr_w(keyname->Buffer), status ); - WINE_TRACE( "init done for %s obj %p\n", wine_dbgstr_w(driver_name), &driver_obj ); - WINE_TRACE( "- DriverInit = %p\n", driver_obj.DriverInit ); - WINE_TRACE( "- DriverStartIo = %p\n", driver_obj.DriverStartIo ); - WINE_TRACE( "- DriverUnload = %p\n", driver_obj.DriverUnload ); + WINE_TRACE( "init done for %s obj %p\n", wine_dbgstr_w(drv_name), driver_obj ); + WINE_TRACE( "- DriverInit = %p\n", driver_obj->DriverInit ); + WINE_TRACE( "- DriverStartIo = %p\n", driver_obj->DriverStartIo ); + WINE_TRACE( "- DriverUnload = %p\n", driver_obj->DriverUnload ); + WINE_TRACE( "- AddDevice = %p\n", driver_extension->AddDevice ); for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) - WINE_TRACE( "- MajorFunction[%d] = %p\n", i, driver_obj.MajorFunction[i] ); + WINE_TRACE( "- MajorFunction[%d] = %p\n", i, driver_obj->MajorFunction[i] ); return status; } /* load the .sys module for a device driver */ -static BOOL load_driver(void) +static HMODULE load_driver( const WCHAR *drv_name, PDRIVER_OBJECT driver_obj, + PDRIVER_EXTENSION driver_extension ) { static const WCHAR driversW[] = {'\\','d','r','i','v','e','r','s','\\',0}; static const WCHAR postfixW[] = {'.','s','y','s',0}; @@ -179,20 +190,22 @@ static BOOL load_driver(void) '\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t', '\\','S','e','r','v','i','c','e','s','\\',0}; + HKEY driver_hkey; UNICODE_STRING keypath; HMODULE module; LPWSTR path = NULL, str; DWORD type, size; + NTSTATUS status; - str = HeapAlloc( GetProcessHeap(), 0, sizeof(servicesW) + strlenW(driver_name)*sizeof(WCHAR) ); + str = HeapAlloc( GetProcessHeap(), 0, sizeof(servicesW) + strlenW(drv_name)*sizeof(WCHAR) ); lstrcpyW( str, servicesW ); - lstrcatW( str, driver_name ); + lstrcatW( str, drv_name ); if (RegOpenKeyW( HKEY_LOCAL_MACHINE, str + 18 /* skip \registry\machine */, &driver_hkey )) { WINE_ERR( "cannot open key %s, err=%u\n", wine_dbgstr_w(str), GetLastError() ); HeapFree( GetProcessHeap(), 0, str); - return FALSE; + return NULL; } RtlInitUnicodeString( &keypath, str ); @@ -208,7 +221,7 @@ static BOOL load_driver(void) ExpandEnvironmentStringsW(str,path,size); } HeapFree( GetProcessHeap(), 0, str ); - if (!path) return FALSE; + if (!path) return NULL; } else { @@ -216,11 +229,11 @@ static BOOL load_driver(void) WCHAR buffer[MAX_PATH]; GetSystemDirectoryW(buffer, MAX_PATH); path = HeapAlloc(GetProcessHeap(),0, - (strlenW(buffer) + strlenW(driversW) + strlenW(driver_name) + strlenW(postfixW) + 1) + (strlenW(buffer) + strlenW(driversW) + strlenW(drv_name) + strlenW(postfixW) + 1) *sizeof(WCHAR)); lstrcpyW(path, buffer); lstrcatW(path, driversW); - lstrcatW(path, driver_name); + lstrcatW(path, drv_name); lstrcatW(path, postfixW); } @@ -232,10 +245,32 @@ static BOOL load_driver(void) module = load_driver_module( str ); HeapFree( GetProcessHeap(), 0, path ); - if (!module) return FALSE; + if (!module) return NULL; - init_driver( module, &keypath ); - return TRUE; + status = init_driver( module, &keypath, drv_name, driver_obj, driver_extension ); + if (status != STATUS_SUCCESS) + { + FreeLibrary( module ); + return NULL; + } + return module; +} + +static void unload_driver( HMODULE module, DRIVER_OBJECT *driver_obj ) +{ + if (driver_obj->DriverUnload) + { + if (WINE_TRACE_ON(relay)) + WINE_DPRINTF( "%04x:Call driver unload %p (obj=%p)\n", + GetCurrentThreadId(), driver_obj->DriverUnload, driver_obj ); + + driver_obj->DriverUnload( driver_obj ); + + if (WINE_TRACE_ON(relay)) + WINE_DPRINTF( "%04x:Ret driver unload %p (obj=%p)\n", + GetCurrentThreadId(), driver_obj->DriverUnload, driver_obj ); + } + FreeLibrary( module ); } static DWORD WINAPI service_handler( DWORD ctrl, DWORD event_type, LPVOID event_data, LPVOID context ) @@ -260,24 +295,144 @@ static DWORD WINAPI service_handler( DWORD ctrl, DWORD event_type, LPVOID event_ SetEvent( stop_event ); return NO_ERROR; default: - WINE_FIXME( "got service ctrl %x for %s\n", ctrl, wine_dbgstr_w(driver_name) ); + WINE_FIXME( "got service ctrl %x for %s\n", ctrl, + wine_dbgstr_w(driver_name) ); status.dwCurrentState = SERVICE_RUNNING; SetServiceStatus( service_handle, &status ); return NO_ERROR; } } +static int loading_request( WCHAR *event_name ) +{ + static WCHAR winedeviceW[] = {'\\','w','i','n','e','d','e','v','i','c','e','.','e','x','e',0}; + + WCHAR *driver_process_cmd; + PROCESS_INFORMATION pi; + STARTUPINFOW si; + HANDLE pipe; + DWORD count, len; + BOOL ret, loaded; + + /* create winedevice.exe process which will load drivers */ + + len = GetSystemDirectoryW( NULL, 0 ); + driver_process_cmd = HeapAlloc( GetProcessHeap(), 0, sizeof(winedeviceW) + + sizeof(WCHAR) * len ); + if (!driver_process_cmd) return 1; + GetSystemDirectoryW( driver_process_cmd, len ); + strcpyW( driver_process_cmd + len - 1, winedeviceW ); + + RtlZeroMemory( &si, sizeof(STARTUPINFOW) ); + si.cb = sizeof(STARTUPINFOW); + ret = CreateProcessW( NULL, driver_process_cmd, NULL, NULL, FALSE, 0, + NULL, NULL, &si, &pi ); + HeapFree( GetProcessHeap(), 0, driver_process_cmd ); + if (!ret) return 1; + CloseHandle( pi.hThread ); + CloseHandle( pi.hProcess ); + + /* send driver and event names and receive loading result */ + + do { + WaitNamedPipeW( pipe_nameW, NMPWAIT_WAIT_FOREVER ); + pipe = CreateFileW( pipe_nameW, GENERIC_READ | GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); + } while (pipe == INVALID_HANDLE_VALUE); + len = (strlenW(driver_name) + 1) * sizeof(WCHAR); + ret = WriteFile( pipe, &len, sizeof(DWORD), &count, NULL ); + if (!ret || count != sizeof(DWORD)) goto fail; + ret = WriteFile( pipe, driver_name, len, &count, NULL ); + if (!ret || count != len) goto fail; + ret = WriteFile( pipe, event_name, EVENT_NAME_LEN, &count, NULL ); + if (!ret || count != EVENT_NAME_LEN) goto fail; + ret = ReadFile( pipe, &loaded, sizeof(BOOL), &count, NULL ); + if (!ret || count != sizeof(BOOL)) goto fail; + if (loaded) + { + CloseHandle( pipe ); + return 0; + } +fail: + CloseHandle( pipe ); + return 1; +} + +static HMODULE handle_loading_request( HANDLE pipe, DRIVER_OBJECT *driver_obj, + DRIVER_EXTENSION *driver_extension, + WCHAR **drv_name, WCHAR **event_name ) +{ + HMODULE module = NULL; + BOOL ret, loaded = FALSE; + DWORD count, len; + + *drv_name = NULL; + *event_name = NULL; + ret = ReadFile( pipe, &len, sizeof(DWORD), &count, NULL ); + if (!ret || count != sizeof(DWORD)) goto end; + *drv_name = HeapAlloc( GetProcessHeap(), 0, len ); + if (!*drv_name) goto end; + ret = ReadFile( pipe, *drv_name, len, &count, NULL ); + if (!ret || count != len) goto end; + *event_name = HeapAlloc( GetProcessHeap(), 0, EVENT_NAME_LEN ); + if (!*event_name) goto end; + ret = ReadFile( pipe, *event_name, EVENT_NAME_LEN, &count, NULL ); + if (!ret || count != EVENT_NAME_LEN) goto end; + module = load_driver( *drv_name, driver_obj, driver_extension ); + if (module) loaded = TRUE; + ret = WriteFile( pipe, &loaded, sizeof(BOOL), &count, NULL ); + if (module && (!ret || count != sizeof(BOOL))) + { + unload_driver( module, driver_obj ); + module = NULL; + } +end: + DisconnectNamedPipe( pipe ); + CloseHandle( pipe ); + if (!module) + { + if (*drv_name) HeapFree( GetProcessHeap(), 0, *drv_name ); + if (*event_name) HeapFree( GetProcessHeap(), 0, *drv_name ); + } + return module; +} + +static HANDLE create_named_event( WCHAR **event_name ) +{ + static const WCHAR event_nameW[] = {'_','_','w','i','n','e','_', + 'W','i','n','e','d','e','v','i','c','e','_','%','u',0}; + + HANDLE event; + unsigned int k = 0; + + *event_name = HeapAlloc( GetProcessHeap(), 0, EVENT_NAME_LEN ); + if (!*event_name) return NULL; + for (;;) + { + snprintfW( *event_name, EVENT_NAME_LEN / sizeof(WCHAR), event_nameW, k++ ); + event = CreateEventW( NULL, TRUE, FALSE, *event_name ); + if (event && GetLastError() != ERROR_ALREADY_EXISTS) + return event; + CloseHandle( event ); + } +} + static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv ) { SERVICE_STATUS status; + WCHAR *event_name; WINE_TRACE( "starting service %s\n", wine_dbgstr_w(driver_name) ); - stop_event = CreateEventW( NULL, TRUE, FALSE, NULL ); - + stop_event = create_named_event( &event_name ); + if (!stop_event) + return; service_handle = RegisterServiceCtrlHandlerExW( driver_name, service_handler, NULL ); if (!service_handle) + { + HeapFree( GetProcessHeap(), 0, event_name ); return; + } status.dwServiceType = SERVICE_WIN32; status.dwCurrentState = SERVICE_START_PENDING; @@ -288,31 +443,100 @@ static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv ) status.dwWaitHint = 10000; SetServiceStatus( service_handle, &status ); - if (load_driver()) + if (!loading_request( event_name )) { status.dwCurrentState = SERVICE_RUNNING; status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; SetServiceStatus( service_handle, &status ); - wine_ntoskrnl_main_loop( stop_event ); + WaitForSingleObject( stop_event, INFINITE ); } else WINE_ERR( "driver %s failed to load\n", wine_dbgstr_w(driver_name) ); + HeapFree( GetProcessHeap(), 0, event_name ); status.dwCurrentState = SERVICE_STOPPED; status.dwControlsAccepted = 0; SetServiceStatus( service_handle, &status ); WINE_TRACE( "service %s stopped\n", wine_dbgstr_w(driver_name) ); } -int wmain( int argc, WCHAR *argv[] ) +static DWORD CALLBACK driver_thread( HANDLE pipe ) { - SERVICE_TABLE_ENTRYW service_table[2]; + DRIVER_OBJECT driver_obj; + DRIVER_EXTENSION driver_extension; + WCHAR *drv_name, *event_name; + HMODULE module; - if (!(driver_name = argv[1])) + RtlZeroMemory( &driver_obj, sizeof(driver_obj) ); + RtlZeroMemory( &driver_extension, sizeof(driver_extension) ); + module = handle_loading_request( pipe, &driver_obj, &driver_extension, + &drv_name, &event_name ); + if (module) { - WINE_ERR( "missing device name, winedevice isn't supposed to be run manually\n" ); + HANDLE loop_event; + + loop_event = CreateEventW( NULL, TRUE, FALSE, event_name ); + if (__wine_add_driver_object( &driver_obj, drv_name )) + { + wine_ntoskrnl_main_loop( loop_event ); + __wine_del_driver_object( &driver_obj ); + } + /* stop service if wine_ntoskrnl_main_loop exits */ + SetEvent( loop_event ); + CloseHandle( loop_event ); + unload_driver( module, &driver_obj ); + HeapFree( GetProcessHeap(), 0, drv_name ); + HeapFree( GetProcessHeap(), 0, event_name ); + } + return 0; +} + +static int driver_process(void) +{ + HANDLE pipe, winedevice_mutex, thread; + + __wine_make_process_system(); + winedevice_mutex = CreateMutexW( NULL, TRUE, winedevice_mutexW ); + if (GetLastError() == ERROR_ALREADY_EXISTS) + { + CloseHandle( winedevice_mutex ); return 1; } + for (;;) + { + pipe = CreateNamedPipeW( pipe_nameW, PIPE_ACCESS_DUPLEX, + PIPE_TYPE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, + 256, 256, 10000, NULL ); + if (pipe == INVALID_HANDLE_VALUE) + { + WINE_ERR( "failed to create pipe\n" ); + continue; + } + if (!ConnectNamedPipe( pipe, NULL ) && + GetLastError() != ERROR_PIPE_CONNECTED) + { + CloseHandle( pipe ); + continue; + } + + thread = CreateThread( NULL, 0, driver_thread, pipe, 0, NULL ); + if (!thread) + { + WINE_ERR( "failed to create thread\n" ); + DisconnectNamedPipe( pipe ); + CloseHandle( pipe ); + continue; + } + CloseHandle( thread ); + } +} + +int wmain( int argc, WCHAR *argv[] ) +{ + SERVICE_TABLE_ENTRYW service_table[2]; + + if (!argv[1]) return driver_process(); + driver_name = argv[1]; service_table[0].lpServiceName = argv[1]; service_table[0].lpServiceProc = ServiceMain; diff --git a/server/device.c b/server/device.c index 4d134a3..acfcd2b 100644 --- a/server/device.c +++ b/server/device.c @@ -32,6 +32,7 @@ #include "file.h" #include "handle.h" #include "request.h" +#include "process.h" struct ioctl_call { @@ -509,6 +510,7 @@ DECL_HANDLER(get_next_device_request) { ioctl = LIST_ENTRY( ptr, struct ioctl_call, mgr_entry ); reply->code = ioctl->code; + reply->pid = get_process_id( ioctl->thread->process ); reply->user_ptr = ioctl->device->user_ptr; reply->in_size = ioctl->in_size; reply->out_size = ioctl->out_size; @@ -554,3 +556,26 @@ DECL_HANDLER(get_ioctl_result) } release_object( device ); } + + +/* get a device name */ +DECL_HANDLER(get_device_name) +{ + struct device *device; + const WCHAR *device_name; + data_size_t device_name_len; + + if (!(device = (struct device *)get_handle_obj( current->process, req->handle, 0, &device_ops ))) + return; + + if ((device_name = get_object_name( &device->obj, &device_name_len ))) + { + if (device_name_len <= get_reply_max_size()) + set_reply_data( device_name, device_name_len ); + else + set_error( STATUS_BUFFER_TOO_SMALL ); + } + else set_error( STATUS_INVALID_DEVICE_REQUEST ); + + release_object( device ); +} diff --git a/server/protocol.def b/server/protocol.def index bec2e3c..e8acb93 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3202,6 +3202,7 @@ enum coords_relative @REPLY obj_handle_t next; /* handle to the next ioctl */ ioctl_code_t code; /* ioctl code */ + process_id_t pid; /* process id */ client_ptr_t user_ptr; /* opaque ptr for the device */ data_size_t in_size; /* total needed input size */ data_size_t out_size; /* needed output size */ @@ -3367,3 +3368,11 @@ enum coords_relative @REQ(set_suspend_context) VARARG(context,context); /* thread context */ @END + + +/* Get a device name */ +@REQ(get_device_name) + obj_handle_t handle; /* device handle */ +@REPLY + VARARG(name,unicode_str); /* device name */ +@END -- 1.7.6.1