[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