[5/5] usbhub.sys: add usbhub.sys
Damjan Jovanovic
damjan.jov at gmail.com
Sun Apr 18 03:32:07 CDT 2010
Changelog:
* usbhub.sys: add usbhub.sys
Damjan Jovanovic
-------------- next part --------------
diff --git a/configure.ac b/configure.ac
index 4aa7cfd..acb5202 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2573,6 +2596,7 @@ WINE_CONFIG_DLL(url,,[url])
WINE_CONFIG_DLL(urlmon,,[urlmon])
WINE_CONFIG_TEST(dlls/urlmon/tests)
WINE_CONFIG_DLL(usbd.sys,,[usbd.sys])
+WINE_CONFIG_DLL(usbhub.sys)
WINE_CONFIG_DLL(user.exe16,enable_win16)
WINE_CONFIG_DLL(user32,,[user32])
WINE_CONFIG_TEST(dlls/user32/tests)
diff --git a/dlls/usbhub.sys/Makefile.in b/dlls/usbhub.sys/Makefile.in
new file mode 100644
index 0000000..56f7a38
--- /dev/null
+++ b/dlls/usbhub.sys/Makefile.in
@@ -0,0 +1,14 @@
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../..
+SRCDIR = @srcdir@
+VPATH = @srcdir@
+MODULE = usbhub.sys
+IMPORTS = kernel32 ntoskrnl.exe ntdll advapi32 setupapi
+EXTRADLLFLAGS = -Wb,--subsystem,native
+EXTRAINCL = @LIBUSBINCL@
+EXTRALIBS = @LIBUSBLIBS@
+
+C_SRCS = \
+ usbhub.c
+
+ at MAKE_DLL_RULES@
diff --git a/dlls/usbhub.sys/usbhub.c b/dlls/usbhub.sys/usbhub.c
new file mode 100644
index 0000000..c1d0377
--- /dev/null
+++ b/dlls/usbhub.sys/usbhub.c
@@ -0,0 +1,395 @@
+/*
+ * Copyright 2010 Damjan Jovanovic
+ *
+ * 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 <stdarg.h>
+
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winbase.h"
+#include "winternl.h"
+#include "winioctl.h"
+#include "winreg.h"
+#include "winuser.h"
+#include "setupapi.h"
+#include "cfgmgr32.h"
+#include "ddk/wdm.h"
+#include "ddk/usb.h"
+#include "wine/wine_ioctl.h"
+#include "wine/unicode.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(usbhub);
+
+#ifdef HAVE_LIBUSB
+
+#include <libusb.h>
+
+static libusb_context *usb_context;
+static DRIVER_OBJECT *usbhub_driver;
+static DEVICE_OBJECT *usbhub_master_device;
+
+static BOOL serial_numbers_match( libusb_device *device, uint8_t index, const char *other_serial )
+{
+ BOOL match = FALSE;
+ libusb_device_handle *handle;
+ int ret;
+ ret = libusb_open( device, &handle );
+ if (ret == LIBUSB_SUCCESS)
+ {
+ char *string = HeapAlloc( GetProcessHeap(), 0, lstrlenA( other_serial ) + 1 );
+ if (string)
+ {
+ ret = libusb_get_string_descriptor_ascii( handle, index, (UCHAR*)string, lstrlenA( other_serial ) + 1 );
+ if (ret >= 0)
+ match = !strcmp( string, other_serial );
+ HeapFree( GetProcessHeap(), 0, string );
+ }
+ libusb_close( handle );
+ }
+ return match;
+}
+
+static libusb_device* find_libusb_device( WINE_USBHUB_NEW_DEVICE_INFO *newdev )
+{
+ libusb_device *matching_device = NULL;
+ libusb_device **list;
+ ssize_t size;
+ size = libusb_get_device_list( usb_context, &list );
+ if (size >= 0)
+ {
+ int i;
+ for (i = 0; i < size; i++)
+ {
+ struct libusb_device_descriptor device_descr;
+ int ret;
+ ret = libusb_get_device_descriptor( list[i], &device_descr );
+ if (ret == LIBUSB_SUCCESS)
+ {
+ if (device_descr.idVendor == newdev->vendor_id &&
+ device_descr.idProduct == newdev->product_id &&
+ libusb_get_bus_number( list[i] ) == newdev->bus_number &&
+ ((device_descr.iSerialNumber == 0 && !newdev->has_serial) ||
+ serial_numbers_match( list[i], device_descr.iSerialNumber, newdev->serial )))
+ {
+ matching_device = list[i];
+ libusb_ref_device( matching_device );
+ break;
+ }
+ }
+ else
+ ERR( "could not get device descriptor, error %d\n", ret );
+ }
+ libusb_free_device_list( list, 1 );
+ }
+ else
+ ERR( "libusb_get_device_list failed with error %d\n", size );
+ return matching_device;
+}
+
+static BOOL is_any_usb_interface_unclaimed( libusb_device *device, libusb_device_handle *handle )
+{
+ struct libusb_config_descriptor *config_desc = NULL;
+ int i;
+ BOOL anyInterfaceUnclaimed = FALSE;
+ int ret;
+
+ ret = libusb_get_active_config_descriptor( device, &config_desc );
+ if (ret != LIBUSB_SUCCESS)
+ {
+ ERR( "could not get active configuration's descriptor\n" );
+ goto done;
+ }
+
+ for (i = 0; i < config_desc->bNumInterfaces; i++)
+ {
+ struct libusb_interface_descriptor interface_desc =
+ config_desc->interface[i].altsetting[0];
+ ret = libusb_kernel_driver_active( handle, interface_desc.bInterfaceNumber );
+ if (ret == 0)
+ {
+ anyInterfaceUnclaimed = TRUE;
+ break;
+ }
+ else if (ret == 1)
+ ;
+ else
+ {
+ ERR( "checking for active kernel driver for USB interface failed with %d\n", ret );
+ goto done;
+ }
+ }
+ /* It's still theoretically possible a user-space driver has claimed interfaces we think are free,
+ * but the problem also exists between other user-space drivers, with no clear solution.
+ */
+
+done:
+ libusb_free_config_descriptor( config_desc );
+ return anyInterfaceUnclaimed;
+}
+
+static BOOL should_wine_drive_device( WINE_USBHUB_NEW_DEVICE_INFO *newdev )
+{
+ libusb_device *device = NULL;
+ BOOL should_drive = FALSE;
+ device = find_libusb_device( newdev );
+ if (device)
+ {
+ libusb_device_handle *handle = NULL;
+ int ret;
+ ret = libusb_open( device, &handle );
+ if (ret == LIBUSB_SUCCESS)
+ {
+ should_drive = is_any_usb_interface_unclaimed( device, handle );
+ if (!should_drive)
+ TRACE( "not driving device because it's being driven by kernel drivers\n" );
+ libusb_close( handle );
+ }
+ else
+ ERR( "error %d opening device\n", ret );
+ libusb_unref_device( device );
+ }
+ return should_drive;
+}
+
+static void launch_driver( WINE_USBHUB_NEW_DEVICE_INFO *newdev )
+{
+ static const WCHAR wine_usb_vendor_idW[] =
+ {'W','I','N','E','_','U','S','B','_','V','E','N','D','O','R','_','I','D',0};
+ static const WCHAR wine_usb_product_idW[] =
+ {'W','I','N','E','_','U','S','B','_','P','R','O','D','U','C','T','_','I','D',0};
+ static const WCHAR wine_usb_bus_numW[] =
+ {'W','I','N','E','_','U','S','B','_','B','U','S','_','N','U','M',0};
+ static const WCHAR wine_usb_serialW[] =
+ {'W','I','N','E','_','U','S','B','_','S','E','R','I','A','L',0};
+ static const WCHAR percentage_dW[] = {'%','d',0};
+ WCHAR pathW[] = {'w','i','n','e','d','e','v','i','c','e','.','e','x','e',' ','U','s','b','h','u','b',' ','-','-','u','n','m','a','n','a','g','e','d',0};
+ WCHAR buffer[20];
+ WCHAR *serial;
+ STARTUPINFOW si;
+ PROCESS_INFORMATION pi;
+
+ TRACE( "launching driver for device\n" );
+ if (newdev->serial)
+ {
+ int count = MultiByteToWideChar( CP_ACP, 0, newdev->serial, -1, NULL, 0 );
+ serial = HeapAlloc( GetProcessHeap(), 0, (count+1)*sizeof(WCHAR) );
+ if (serial)
+ MultiByteToWideChar( CP_ACP, 0, newdev->serial, -1, serial, -1 );
+ else
+ {
+ ERR( "out of memory\n" );
+ return;
+ }
+ }
+ else
+ serial = NULL;
+
+ snprintfW( buffer, sizeof(buffer)/sizeof(WCHAR), percentage_dW, newdev->vendor_id );
+ SetEnvironmentVariableW( wine_usb_vendor_idW, (LPCWSTR)buffer );
+ snprintfW( buffer, sizeof(buffer)/sizeof(WCHAR), percentage_dW, newdev->product_id );
+ SetEnvironmentVariableW( wine_usb_product_idW, (LPCWSTR)buffer );
+ snprintfW( buffer, sizeof(buffer)/sizeof(WCHAR), percentage_dW, newdev->bus_number );
+ SetEnvironmentVariableW( wine_usb_bus_numW, (LPCWSTR)buffer );
+ SetEnvironmentVariableW( wine_usb_serialW, serial );
+
+ memset( &si, 0, sizeof(si) );
+ si.cb = sizeof(si);
+
+ if (CreateProcessW(NULL, pathW, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
+ {
+ CloseHandle( pi.hThread );
+ CloseHandle( pi.hProcess );
+ }
+ else
+ ERR( "error %d starting winedevice\n", GetLastError() );
+ HeapFree( GetProcessHeap(), 0, serial );
+}
+
+static NTSTATUS usb_device_inserted( IRP *irp )
+{
+ IO_STACK_LOCATION *irpsp;
+ WINE_USBHUB_NEW_DEVICE_INFO *newdev;
+
+ irpsp = IoGetCurrentIrpStackLocation( irp );
+ if (irpsp->DeviceObject != usbhub_master_device)
+ return STATUS_ACCESS_DENIED;
+
+ newdev = irpsp->Parameters.DeviceIoControl.Type3InputBuffer;
+ TRACE( "new device inserted, vendor=0x%04X, product=0x%04X, serial=%s, bus=%d\n",
+ newdev->vendor_id, newdev->product_id, newdev->has_serial ? newdev->serial : "", newdev->bus_number );
+ if (should_wine_drive_device( newdev ))
+ launch_driver( newdev );
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS WINAPI usbhub_ioctl( DEVICE_OBJECT *device, IRP *irp )
+{
+ IO_STACK_LOCATION *irpsp;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ TRACE( "(%p, %p)\n", device, irp );
+
+ irpsp = IoGetCurrentIrpStackLocation( irp );
+ switch (irpsp->Parameters.DeviceIoControl.IoControlCode)
+ {
+ case WINE_USBHUB_DEVICE_ADDED:
+ status = usb_device_inserted( irp );
+ break;
+ default:
+ FIXME( "unimplemented ioctl 0x%08X\n", irpsp->Parameters.DeviceIoControl.IoControlCode );
+ status = STATUS_NOT_IMPLEMENTED;
+ }
+
+ irp->IoStatus.u.Status = status;
+ IoCompleteRequest( irp, IO_NO_INCREMENT );
+ return status;
+}
+
+static BOOL heap_get_envW( const WCHAR *name, WCHAR **value )
+{
+ DWORD size = 256;
+ DWORD ret;
+ while (1)
+ {
+ *value = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size );
+ if (!value)
+ {
+ ERR( "out of memory reading environment variable\n" );
+ return FALSE;
+ }
+ ret = GetEnvironmentVariableW( name, *value, size );
+ if (ret <= size)
+ return TRUE;
+ HeapFree( GetProcessHeap(), 0, *value );
+ }
+}
+
+static BOOL load_request( WINE_USBHUB_NEW_DEVICE_INFO **newdev )
+{
+ static const WCHAR wine_usb_vendor_idW[] =
+ {'W','I','N','E','_','U','S','B','_','V','E','N','D','O','R','_','I','D',0};
+ static const WCHAR wine_usb_product_idW[] =
+ {'W','I','N','E','_','U','S','B','_','P','R','O','D','U','C','T','_','I','D',0};
+ static const WCHAR wine_usb_bus_numW[] =
+ {'W','I','N','E','_','U','S','B','_','B','U','S','_','N','U','M',0};
+ static const WCHAR wine_usb_serialW[] =
+ {'W','I','N','E','_','U','S','B','_','S','E','R','I','A','L',0};
+
+ WCHAR *vendor_id = NULL;
+ WCHAR *product_id = NULL;
+ WCHAR *bus_num = NULL;
+ WCHAR *serial = NULL;
+ BOOL ret = FALSE;
+
+ *newdev = NULL;
+ if (!heap_get_envW( wine_usb_vendor_idW, &vendor_id ))
+ goto done;
+ if (!heap_get_envW( wine_usb_product_idW, &product_id ))
+ goto done;
+ if (!heap_get_envW( wine_usb_bus_numW, &bus_num ))
+ goto done;
+ if (!heap_get_envW( wine_usb_serialW, &serial ))
+ goto done;
+
+ ret = TRUE;
+
+ if (strlenW( vendor_id ) == 0)
+ goto done;
+
+ *newdev = HeapAlloc( GetProcessHeap(), 0, sizeof(*newdev) + WideCharToMultiByte( CP_ACP, 0, serial, -1, NULL, 0, NULL, NULL ));
+ if (!newdev)
+ goto done;
+ (*newdev)->vendor_id = atoiW( vendor_id );
+ (*newdev)->product_id = atoiW( product_id );
+ (*newdev)->bus_number = atoiW( bus_num );
+ WideCharToMultiByte( CP_ACP, 0, serial, -1, (*newdev)->serial, -1, NULL, NULL );
+
+done:
+ HeapFree( GetProcessHeap(), 0, vendor_id );
+ HeapFree( GetProcessHeap(), 0, product_id );
+ HeapFree( GetProcessHeap(), 0, bus_num );
+ HeapFree( GetProcessHeap(), 0, serial );
+ return ret;
+}
+
+static VOID WINAPI usbhub_unload( DRIVER_OBJECT *driver )
+{
+ TRACE( "(%p)\n", driver );
+ libusb_exit( usb_context );
+}
+
+#endif /* HAVE_LIBUSB */
+
+NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path )
+{
+ static const WCHAR wine_usb_masterW[] =
+ {'\\','?','?','\\','W','I','N','E','_','U','S','B','_','M','A','S','T','E','R',0};
+ WINE_USBHUB_NEW_DEVICE_INFO *newdev = NULL;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ TRACE( "(%p, %s)\n", driver, debugstr_w(path->Buffer) );
+
+#ifdef HAVE_LIBUSB
+ if (load_request( &newdev ))
+ {
+ if (newdev == NULL)
+ {
+ /* Master */
+ UNICODE_STRING name;
+ RtlInitUnicodeString( &name, wine_usb_masterW );
+ status = IoCreateDevice( driver, 0, &name, FILE_DEVICE_WINE, 0, FALSE, &usbhub_master_device );
+ if (status != STATUS_SUCCESS)
+ ERR( "Failed to create USB master device, status = 0x%X\n", status );
+ }
+ else
+ {
+ /* Slave */
+ /* TODO: make PDO and find/load/start drivers */
+ }
+
+ if (status == STATUS_SUCCESS)
+ {
+ int ret = libusb_init( &usb_context );
+ if (ret == LIBUSB_SUCCESS)
+ {
+ driver->DriverUnload = usbhub_unload;
+ driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = usbhub_ioctl;
+ usbhub_driver = driver;
+ }
+ else
+ {
+ ERR( "libusb_init() failed with %d\n", ret );
+ status = STATUS_UNSUCCESSFUL;
+ }
+ }
+ }
+ else
+ {
+ ERR( "failed to read environment variables\n" );
+ status = STATUS_UNSUCCESSFUL;
+ }
+#endif
+
+ return status;
+}
+
diff --git a/dlls/usbhub.sys/usbhub.sys.spec b/dlls/usbhub.sys/usbhub.sys.spec
new file mode 100644
index 0000000..76421d7
--- /dev/null
+++ b/dlls/usbhub.sys/usbhub.sys.spec
@@ -0,0 +1 @@
+# nothing to export
More information about the wine-patches
mailing list