[5/9] Add support of WDM drivers for USB devices.

Alexander Morozov amorozov at etersoft.ru
Mon Oct 6 02:20:41 CDT 2008


-------------- next part --------------
From ed6e12ca39e194829ad8d6be4fbd91d9f276d435 Mon Sep 17 00:00:00 2001
From: Alexander Morozov <amorozov at etersoft.ru>
Date: Fri, 3 Oct 2008 17:57:00 +0400
Subject: [PATCH] Add support of WDM drivers for USB devices.

---
 configure.ac                    |   14 ++
 dlls/wineusbhub/Makefile.in     |   15 ++
 dlls/wineusbhub/wineusbhub.c    |  291 +++++++++++++++++++++++++++++++++++++++
 dlls/wineusbhub/wineusbhub.spec |    7 +
 programs/winedevice/Makefile.in |    2 +-
 programs/winedevice/device.c    |   79 +++++++++++-
 programs/wineusb/Makefile.in    |   15 ++
 programs/wineusb/main.c         |  224 ++++++++++++++++++++++++++++++
 server/device.c                 |  111 +++++++++++++++
 server/protocol.def             |   16 ++
 tools/wine.inf.in               |   10 ++
 11 files changed, 782 insertions(+), 2 deletions(-)
 create mode 100644 dlls/wineusbhub/Makefile.in
 create mode 100644 dlls/wineusbhub/wineusbhub.c
 create mode 100644 dlls/wineusbhub/wineusbhub.spec
 create mode 100644 programs/wineusb/Makefile.in
 create mode 100644 programs/wineusb/main.c

diff --git a/configure.ac b/configure.ac
index 52f5752..7db258e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -56,6 +56,7 @@ AC_ARG_WITH(oss,       AS_HELP_STRING([--without-oss],[do not use the OSS sound
 AC_ARG_WITH(png,       AS_HELP_STRING([--without-png],[do not use PNG]),
             [if test "x$withval" = "xno"; then ac_cv_header_png_h=no; fi])
 AC_ARG_WITH(sane,      AS_HELP_STRING([--without-sane],[do not use SANE (scanner support)]))
+AC_ARG_WITH(usb,       AS_HELP_STRING([--without-usb],[do not use USB]))
 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])
 AC_ARG_WITH(xcursor,   AS_HELP_STRING([--without-xcursor],[do not use the Xcursor extension]),
@@ -332,6 +333,7 @@ AC_CHECK_HEADERS(\
 	syscall.h \
 	termios.h \
 	unistd.h \
+	usb.h \
 	utime.h \
 	valgrind/memcheck.h \
 	valgrind/valgrind.h
@@ -956,6 +958,18 @@ fi
 WINE_NOTICE_WITH(sane,[test "x$ac_cv_lib_soname_sane" = "x"],
                  [libsane development files not found, scanners won't be supported.])
 
+dnl **** Check for LIBUSB ****
+AC_SUBST(USBLIBS,"")
+if test "$ac_cv_header_usb_h" = "yes"
+then
+    AC_CHECK_LIB(usb, usb_init,
+        [AC_DEFINE(HAVE_LIBUSB, 1, [Define if you have the libusb library and header])
+         USBLIBS="-lusb"])
+fi
+WINE_NOTICE_WITH(usb,[test "$ac_cv_lib_usb_usb_init" != "yes"],
+                 [libusb development files not found, USB won't be supported.])
+
+
 dnl **** Check for libgphoto2 ****
 AC_CHECK_PROG(gphoto2_devel,gphoto2-config,gphoto2-config,no)
 AC_CHECK_PROG(gphoto2port_devel,gphoto2-port-config,gphoto2-port-config,no)
diff --git a/dlls/wineusbhub/Makefile.in b/dlls/wineusbhub/Makefile.in
new file mode 100644
index 0000000..4954a5d
--- /dev/null
+++ b/dlls/wineusbhub/Makefile.in
@@ -0,0 +1,15 @@
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../..
+SRCDIR    = @srcdir@
+VPATH     = @srcdir@
+MODULE    = wineusbhub.dll
+IMPORTLIB = wineusbhub
+IMPORTS   = ntoskrnl.exe kernel32
+EXTRALIBS = @USBLIBS@
+
+C_SRCS = \
+	wineusbhub.c
+
+ at MAKE_DLL_RULES@
+
+ at DEPENDENCIES@  # everything below this line is overwritten by make depend
diff --git a/dlls/wineusbhub/wineusbhub.c b/dlls/wineusbhub/wineusbhub.c
new file mode 100644
index 0000000..a7985cf
--- /dev/null
+++ b/dlls/wineusbhub/wineusbhub.c
@@ -0,0 +1,291 @@
+/*
+ * 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 <stdarg.h>
+
+#ifdef HAVE_USB_H
+#include <usb.h>
+#endif
+
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winbase.h"
+#include "winternl.h"
+#include "ddk/ntddk.h"
+#include "ddk/usb.h"
+#include "wine/unicode.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(wineusbhub);
+
+#if defined(HAVE_LIBUSB) && defined(HAVE_USB_H)
+extern void WINAPI IofCompleteRequest( IRP *irp, UCHAR priority_boost );
+
+DRIVER_OBJECT hubdrv;
+DEVICE_OBJECT *usbdev;      /* USB PDO */
+static struct usb_device *dev;
+
+static void add_data( char **dst, int *dst_size, void *src, int src_size )
+{
+    int copy;
+
+    copy = (src_size >= *dst_size) ? *dst_size : src_size;
+    memcpy( *dst, src, copy );
+    *dst += copy;
+    *dst_size -= copy;
+}
+
+void WINAPI __wine_IofCompleteRequest( IRP *irp, UCHAR priority_boost )
+{
+    #ifdef __i386__
+    __asm__( "movl %1,%%edx\n\t"
+             "movl %0,%%ecx\n\t"
+             "call " __ASM_NAME("IofCompleteRequest")
+             : : "g" (irp), "g" (priority_boost) );
+    #else
+    IofCompleteRequest( irp, priority_boost );
+    #endif
+}
+
+NTSTATUS WINAPI __wine_usbhub_internal_ioctl( DEVICE_OBJECT *device, IRP *irp )
+{
+    IO_STACK_LOCATION *irpsp;
+    URB *urb;
+    NTSTATUS status = STATUS_UNSUCCESSFUL;
+
+    TRACE( "%p, %p\n", device, irp );
+
+    irpsp = irp->Tail.Overlay.s.u.CurrentStackLocation;
+    urb = irpsp->Parameters.Others.Argument1;
+
+    switch (urb->u.UrbHeader.Function)
+    {
+    case URB_FUNCTION_SELECT_CONFIGURATION:
+        {
+            USB_CONFIGURATION_DESCRIPTOR *conf_desc =
+                    urb->u.UrbSelectConfiguration.ConfigurationDescriptor;
+            usb_dev_handle *husb;
+
+            TRACE( "URB_FUNCTION_SELECT_CONFIGURATION\n" );
+
+            husb = usb_open( dev );
+            if (husb)
+            {
+                int ret;
+
+                ret = usb_set_configuration( husb, conf_desc->bConfigurationValue );
+                if (ret < 0)
+                    ERR( "%s\n", usb_strerror() );
+                else
+                    status = STATUS_SUCCESS;
+                usb_close( husb );
+            }
+        }
+        break;
+    case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE:
+        {
+            struct _URB_CONTROL_DESCRIPTOR_REQUEST *request =
+                    &urb->u.UrbControlDescriptorRequest;
+
+            TRACE( "URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE\n" );
+
+            switch (request->DescriptorType)
+            {
+            case USB_DEVICE_DESCRIPTOR_TYPE:
+                TRACE( "USB_DEVICE_DESCRIPTOR_TYPE\n" );
+                if (request->TransferBuffer == NULL)
+                    break;
+                if (sizeof(USB_DEVICE_DESCRIPTOR) <= request->TransferBufferLength)
+                {
+                    memcpy( request->TransferBuffer, &dev->descriptor,
+                            sizeof(USB_DEVICE_DESCRIPTOR) );
+                    status = STATUS_SUCCESS;
+                }
+                break;
+            case USB_CONFIGURATION_DESCRIPTOR_TYPE:
+                TRACE( "USB_CONFIGURATION_DESCRIPTOR_TYPE\n" );
+                {
+                    unsigned int i, k;
+                    char *buf = request->TransferBuffer;
+                    struct usb_config_descriptor *conf = &dev->config[0];
+                    struct usb_interface_descriptor *intf;
+                    struct usb_endpoint_descriptor *endp;
+                    int size = request->TransferBufferLength;
+
+                    /* FIXME: case of num_altsetting > 1 */
+
+                    if (buf == NULL)
+                        break;
+                    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;
+            default:
+                FIXME( "unsupported descriptor type %x\n", request->DescriptorType );
+            }
+        }
+        break;
+    case URB_FUNCTION_VENDOR_DEVICE:
+    case URB_FUNCTION_VENDOR_INTERFACE:
+    case URB_FUNCTION_VENDOR_ENDPOINT:
+        {
+            usb_dev_handle *husb;
+            struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST *request =
+                    &urb->u.UrbControlVendorClassRequest;
+
+            TRACE( "URB_FUNCTION_VENDOR_*\n" );
+
+            husb = usb_open( dev );
+            if (husb)
+            {
+                UCHAR req_type = request->RequestTypeReservedBits | (2 << 5);
+                char *buf;
+                int ret;
+
+                if (urb->u.UrbHeader.Function == URB_FUNCTION_VENDOR_INTERFACE)
+                    req_type |= 1;
+                else if (urb->u.UrbHeader.Function == URB_FUNCTION_VENDOR_ENDPOINT)
+                    req_type |= 2;
+                buf = HeapAlloc( GetProcessHeap(), 0, request->TransferBufferLength );
+                if (buf != NULL)
+                {
+                    memcpy( buf, request->TransferBuffer, request->TransferBufferLength );
+                    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, request->TransferBufferLength, 1000 );
+                    if (ret < 0)
+                        ERR( "%s\n", usb_strerror() );
+                    else
+                    {
+                        if (request->TransferFlags & USBD_TRANSFER_DIRECTION_IN)
+                        {
+                            request->TransferBufferLength =
+                                    (ret <= request->TransferBufferLength) ?
+                                    ret : request->TransferBufferLength;
+                            memcpy( request->TransferBuffer, buf,
+                                    request->TransferBufferLength );
+                        }
+                        status = STATUS_SUCCESS;
+                    }
+                    HeapFree( GetProcessHeap(), 0, buf );
+                }
+                usb_close( husb );
+            }
+        }
+        break;
+    default:
+        FIXME( "unsupported URB function %x\n", urb->u.UrbHeader.Function );
+    }
+
+    urb->u.UrbHeader.Status = status;
+    if (irp->UserIosb != NULL)
+    {
+        irp->UserIosb->u.Status = status;
+        irp->UserIosb->Information = 0;
+    }
+    irp->IoStatus.u.Status = status;
+    irp->IoStatus.Information = 0;
+    __wine_IofCompleteRequest( irp, IO_NO_INCREMENT );
+
+    return status;
+}
+
+NTSTATUS WINAPI __wine_usbhub_dispatch_pnp( DEVICE_OBJECT *device, IRP *irp )
+{
+    TRACE( "%p, %p\n", device, irp );
+
+    irp->IoStatus.u.Status = STATUS_SUCCESS;
+    irp->IoStatus.Information = 0;
+    __wine_IofCompleteRequest( irp, IO_NO_INCREMENT );
+
+    return STATUS_SUCCESS;
+}
+#endif
+
+DEVICE_OBJECT *__wine_usbhub_get_pdo( UCHAR *pdo_info )
+{
+#if defined(HAVE_LIBUSB) && defined(HAVE_USB_H)
+    static const WCHAR usbpdoW[] = {'\\','D','e','v','i','c','e','\\',
+                                    'U','S','B','P','D','O','-','%','u',0};
+    static WCHAR bufW[20];
+    UNICODE_STRING pdo_name;
+    struct usb_bus *bus;
+
+    TRACE( "%u, %s, %s\n", pdo_info[0], pdo_info + 1,
+            pdo_info + 2 + strlen( (char *)(pdo_info + 1) ) );
+
+    for (bus = usb_busses; bus; bus = bus->next)
+        for (dev = bus->devices; dev; dev = dev->next)
+            if (!strcmp( bus->dirname, (char *)(pdo_info + 1) ) &&
+                    !strcmp( dev->filename, (char *)(pdo_info + 2 + strlen( bus->dirname )) ))
+            {
+                hubdrv.MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = __wine_usbhub_internal_ioctl;
+                hubdrv.MajorFunction[IRP_MJ_PNP] = __wine_usbhub_dispatch_pnp;
+
+                snprintfW( bufW, sizeof(bufW), usbpdoW, pdo_info[0] );
+                RtlInitUnicodeString( &pdo_name, bufW );
+
+                if (STATUS_SUCCESS == IoCreateDevice( &hubdrv, 0, &pdo_name, 0, 0, FALSE, &usbdev ))
+                {
+                    usbdev->Flags |= DO_BUS_ENUMERATED_DEVICE | DO_POWER_PAGABLE;
+                    return usbdev;
+                }
+            }
+#endif
+    return NULL;
+}
+
+BOOL WINAPI DllMain( HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpv )
+{
+#if defined(HAVE_LIBUSB) && defined(HAVE_USB_H)
+    if (fdwReason == DLL_PROCESS_ATTACH)
+    {
+        usb_init();
+        usb_find_busses();
+        usb_find_devices();
+    }
+#endif
+    return TRUE;
+}
diff --git a/dlls/wineusbhub/wineusbhub.spec b/dlls/wineusbhub/wineusbhub.spec
new file mode 100644
index 0000000..85e5361
--- /dev/null
+++ b/dlls/wineusbhub/wineusbhub.spec
@@ -0,0 +1,7 @@
+##################
+# Wine extensions
+#
+# All functions must be prefixed with '__wine_' (for internal functions)
+# or 'wine_' (for user-visible functions) to avoid namespace conflicts.
+
+@ cdecl __wine_usbhub_get_pdo(ptr)
diff --git a/programs/winedevice/Makefile.in b/programs/winedevice/Makefile.in
index c644b7a..8b8b8dd 100644
--- a/programs/winedevice/Makefile.in
+++ b/programs/winedevice/Makefile.in
@@ -5,7 +5,7 @@ SRCDIR    = @srcdir@
 VPATH     = @srcdir@
 MODULE    = winedevice.exe
 APPMODE   = -mwindows -municode
-IMPORTS   = advapi32 ntoskrnl.exe kernel32 ntdll
+IMPORTS   = advapi32 ntoskrnl.exe kernel32 ntdll wineusbhub
 
 C_SRCS = \
 	device.c
diff --git a/programs/winedevice/device.c b/programs/winedevice/device.c
index 7afe97e..81de49d 100644
--- a/programs/winedevice/device.c
+++ b/programs/winedevice/device.c
@@ -22,6 +22,10 @@
 #include "wine/port.h"
 
 #include <stdarg.h>
+#include <limits.h>
+
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
 
 #include "ntstatus.h"
 #define WIN32_NO_STATUS
@@ -31,13 +35,15 @@
 #include "winreg.h"
 #include "winnls.h"
 #include "winsvc.h"
-#include "ddk/wdm.h"
+#include "ddk/ntddk.h"
 #include "wine/unicode.h"
+#include "wine/server.h"
 #include "wine/debug.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(winedevice);
 WINE_DECLARE_DEBUG_CHANNEL(relay);
 
+extern DEVICE_OBJECT *__wine_usbhub_get_pdo( UCHAR *pdo_info );
 extern NTSTATUS wine_ntoskrnl_main_loop( HANDLE stop_event );
 
 static WCHAR *driver_name;
@@ -138,6 +144,7 @@ static NTSTATUS init_driver( HMODULE module, UNICODE_STRING *keyname )
     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] );
 
@@ -268,10 +275,80 @@ static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv )
 
     if (load_driver())
     {
+        UNICODE_STRING drvname;
+        NTSTATUS ret = STATUS_SUCCESS;
+        IRP *irp;
+        IO_STACK_LOCATION *irpsp;
+        PDRIVER_DISPATCH dispatch;
+
         status.dwCurrentState     = SERVICE_RUNNING;
         status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
         SetServiceStatus( service_handle, &status );
 
+        RtlInitUnicodeString( &drvname, driver_name );
+
+        if (driver_extension.AddDevice)
+        {
+            NTSTATUS (WINAPI *AddDevice)( PDRIVER_OBJECT, PDEVICE_OBJECT ) =
+                    driver_extension.AddDevice;
+            PDEVICE_OBJECT pdev_obj = NULL;
+            UCHAR pdo_info[PATH_MAX + 3];
+            data_size_t reply_size = 0;
+
+            while (!reply_size)
+                SERVER_START_REQ( get_add_device_request )
+                {
+                    wine_server_add_data( req, drvname.Buffer, drvname.Length );
+                    wine_server_set_reply( req, pdo_info, sizeof(pdo_info) );
+                    ret = wine_server_call( req );
+                    if (STATUS_SUCCESS == ret)
+                        reply_size = wine_server_reply_size( reply );
+                }
+                SERVER_END_REQ;
+
+            pdev_obj = __wine_usbhub_get_pdo( pdo_info );
+            if (pdev_obj)
+            {
+                WINE_TRACE( "calling AddDevice( %p, %p )\n", &driver_obj, pdev_obj );
+                ret = AddDevice( &driver_obj, pdev_obj );
+                if (STATUS_SUCCESS != ret)
+                    WINE_ERR( "AddDevice failed: %x\n", ret );
+            }
+            else
+            {
+                ret = STATUS_UNSUCCESSFUL;
+                WINE_ERR( "wineusbhub error\n" );
+            }
+        }
+
+        if (driver_obj.MajorFunction[IRP_MJ_PNP])
+        {
+            irp = IoAllocateIrp( driver_obj.DeviceObject->StackSize, FALSE );
+            if (irp != NULL)
+            {
+                --irp->CurrentLocation;
+                irpsp = --irp->Tail.Overlay.s.u.CurrentStackLocation;
+
+                irp->RequestorMode = KernelMode;
+                irp->IoStatus.u.Status = STATUS_NOT_SUPPORTED;
+
+                irpsp->MajorFunction = IRP_MJ_PNP;
+                irpsp->MinorFunction = IRP_MN_START_DEVICE;
+                irpsp->DeviceObject = driver_obj.DeviceObject;
+
+                driver_obj.DeviceObject->CurrentIrp = irp;
+
+                dispatch = driver_obj.MajorFunction[IRP_MJ_PNP];
+                ret = dispatch( driver_obj.DeviceObject, irp );
+                if (STATUS_SUCCESS != ret)
+                    WINE_ERR( "MajorFunction[IRP_MJ_PNP] failed: %x\n", ret );
+
+                IoFreeIrp( irp );
+            }
+            else
+                ret = STATUS_UNSUCCESSFUL;
+        }
+
         wine_ntoskrnl_main_loop( stop_event );
     }
     else WINE_ERR( "driver %s failed to load\n", wine_dbgstr_w(driver_name) );
diff --git a/programs/wineusb/Makefile.in b/programs/wineusb/Makefile.in
new file mode 100644
index 0000000..d2d0fdf
--- /dev/null
+++ b/programs/wineusb/Makefile.in
@@ -0,0 +1,15 @@
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../..
+SRCDIR    = @srcdir@
+VPATH     = @srcdir@
+MODULE    = wineusb.exe
+APPMODE   = -mwindows -municode
+IMPORTS   = advapi32 ntoskrnl.exe kernel32 ntdll setupapi
+EXTRALIBS = @USBLIBS@
+
+C_SRCS = \
+	main.c
+
+ at MAKE_PROG_RULES@
+
+ at DEPENDENCIES@  # everything below this line is overwritten by make depend
diff --git a/programs/wineusb/main.c b/programs/wineusb/main.c
new file mode 100644
index 0000000..864376d
--- /dev/null
+++ b/programs/wineusb/main.c
@@ -0,0 +1,224 @@
+/*
+ * Service process to call USB driver`s AddDevice routine
+ *
+ * Based on winedevice
+ *
+ * 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 <stdarg.h>
+
+#ifdef HAVE_USB_H
+#include <usb.h>
+#endif
+
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winbase.h"
+#include "winternl.h"
+#include "winreg.h"
+#include "winnls.h"
+#include "winsvc.h"
+#include "winuser.h"
+#include "setupapi.h"
+#include "ddk/wdm.h"
+#include "ddk/usb.h"
+#include "wine/unicode.h"
+#include "wine/server.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(wineusb);
+
+static WCHAR service_nameW[] = {'w','i','n','e','u','s','b',0};
+static SERVICE_STATUS_HANDLE service_handle;
+static HANDLE stop_event;
+
+static DWORD WINAPI service_handler( DWORD ctrl, DWORD event_type, LPVOID event_data, LPVOID context )
+{
+    SERVICE_STATUS status;
+
+    status.dwServiceType             = SERVICE_WIN32;
+    status.dwControlsAccepted        = SERVICE_ACCEPT_STOP;
+    status.dwWin32ExitCode           = 0;
+    status.dwServiceSpecificExitCode = 0;
+    status.dwCheckPoint              = 0;
+    status.dwWaitHint                = 0;
+
+    switch(ctrl)
+    {
+    case SERVICE_CONTROL_STOP:
+    case SERVICE_CONTROL_SHUTDOWN:
+        WINE_TRACE( "shutting down\n" );
+        status.dwCurrentState     = SERVICE_STOP_PENDING;
+        status.dwControlsAccepted = 0;
+        SetServiceStatus( service_handle, &status );
+        SetEvent( stop_event );
+        return NO_ERROR;
+    default:
+        status.dwCurrentState = SERVICE_RUNNING;
+        SetServiceStatus( service_handle, &status );
+        return NO_ERROR;
+    }
+}
+
+static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv )
+{
+#if defined(HAVE_LIBUSB) && defined(HAVE_USB_H)
+    HDEVINFO set;
+    GUID guid = {0x36FC9E60, 0xC465, 0x11CF, {0x80,0x56,0x44,0x45,0x53,0x54,0x00,0x00}};
+#endif
+    SERVICE_STATUS status;
+
+    WINE_TRACE( "starting service\n" );
+
+    stop_event = CreateEventW( NULL, TRUE, FALSE, NULL );
+
+    service_handle = RegisterServiceCtrlHandlerExW( service_nameW, service_handler, NULL );
+    if (!service_handle)
+        return;
+
+    status.dwServiceType             = SERVICE_WIN32;
+    status.dwCurrentState            = SERVICE_START_PENDING;
+    status.dwControlsAccepted        = 0;
+    status.dwWin32ExitCode           = 0;
+    status.dwServiceSpecificExitCode = 0;
+    status.dwCheckPoint              = 0;
+    status.dwWaitHint                = 10000;
+    SetServiceStatus( service_handle, &status );
+
+#if defined(HAVE_LIBUSB) && defined(HAVE_USB_H)
+    set = SetupDiGetClassDevsW( &guid, NULL, 0, 0 );
+    if (set != INVALID_HANDLE_VALUE)
+    {
+        UNICODE_STRING drvname;
+        UCHAR pdo_info[PATH_MAX + 3] = {0};
+        struct usb_device *dev;
+        struct usb_bus *bus;
+        SP_DEVINFO_DATA devInfo = { sizeof(devInfo), { 0 } };
+        DWORD size = 0, i = 0;
+        USHORT vid, pid;
+        char *str;
+        BYTE *buf;
+        BOOL ret;
+
+        usb_init();
+        usb_find_busses();
+        usb_find_devices();
+
+        while (SetupDiEnumDeviceInfo( set, i++, &devInfo ))
+        {
+            SetupDiGetDeviceRegistryPropertyA( set, &devInfo, SPDRP_HARDWAREID,
+                    NULL, NULL, 0, &size );
+            buf = HeapAlloc( GetProcessHeap(), 0, size );
+            if (buf == NULL)
+            {
+                WINE_ERR( "insufficient memory\n" );
+                continue;
+            }
+            ret = SetupDiGetDeviceRegistryPropertyA( set, &devInfo, SPDRP_HARDWAREID,
+                    NULL, buf, size, NULL );
+            if (!ret)
+            {
+                WINE_ERR( "SetupDiGetDeviceRegistryPropertyA failed\n" );
+                HeapFree( GetProcessHeap(), 0, buf );
+                continue;
+            }
+            str = strstr( (char *)buf, "Vid_" );
+            if (str != NULL)
+            {
+                str += 4;
+                vid = strtol( str, NULL, 16 );
+                str = strstr( str, "Pid_" );
+            }
+            if (str == NULL)
+            {
+                WINE_ERR( "bad hardware ID\n" );
+                HeapFree( GetProcessHeap(), 0, buf );
+                continue;
+            }
+            str += 4;
+            pid = strtol( str, NULL, 16 );
+            HeapFree( GetProcessHeap(), 0, buf );
+
+            for (bus = usb_busses; bus && ret; bus = bus->next)
+                for (dev = bus->devices; dev && ret; dev = dev->next)
+                    if (dev->descriptor.idVendor == vid &&
+                            dev->descriptor.idProduct == pid)
+                    {
+                        SetupDiGetDeviceRegistryPropertyW( set, &devInfo,
+                                SPDRP_SERVICE, NULL, NULL, 0, &size );
+                        buf = HeapAlloc( GetProcessHeap(), 0, size );
+                        if (buf == NULL)
+                        {
+                            WINE_ERR( "insufficient memory\n" );
+                            ret = FALSE;
+                            break;
+                        }
+                        ret = SetupDiGetDeviceRegistryPropertyW( set, &devInfo,
+                                SPDRP_SERVICE, NULL, buf, size, NULL );
+                        if (!ret)
+                        {
+                            WINE_ERR( "SetupDiGetDeviceRegistryPropertyW failed\n" );
+                            HeapFree( GetProcessHeap(), 0, buf );
+                            break;
+                        }
+                        /* FIXME: check if driver is loaded */
+                        RtlInitUnicodeString( &drvname, (PWSTR)buf );
+                        strcpy( (char *)(pdo_info + 1), bus->dirname );
+                        strcpy( (char *)(pdo_info + 2 + strlen( bus->dirname )), dev->filename );
+
+                        SERVER_START_REQ( call_add_device )
+                        {
+                            req->drvname_len = drvname.Length;
+                            wine_server_add_data( req, drvname.Buffer, drvname.Length );
+                            wine_server_add_data( req, pdo_info, sizeof(pdo_info) );
+                            wine_server_call( req );
+                        }
+                        SERVER_END_REQ;
+
+                        ++pdo_info[0];
+                        HeapFree( GetProcessHeap(), 0, buf );
+                        ret = FALSE;
+                        break;
+                    }
+        }
+        SetupDiDestroyDeviceInfoList( set );
+    }
+    else
+        WINE_ERR( "SetupDiGetClassDevsW failed\n" );
+#endif
+
+    status.dwCurrentState     = SERVICE_STOPPED;
+    status.dwControlsAccepted = 0;
+    SetServiceStatus( service_handle, &status );
+    WINE_TRACE( "service stopped\n" );
+}
+
+int wmain( int argc, WCHAR *argv[] )
+{
+    SERVICE_TABLE_ENTRYW service_table[2];
+
+    service_table[0].lpServiceName = service_nameW;
+    service_table[0].lpServiceProc = ServiceMain;
+    service_table[1].lpServiceName = NULL;
+    service_table[1].lpServiceProc = NULL;
+
+    StartServiceCtrlDispatcherW( service_table );
+    return 0;
+}
diff --git a/server/device.c b/server/device.c
index abf0ac5..eb1b593 100644
--- a/server/device.c
+++ b/server/device.c
@@ -32,6 +32,7 @@
 #include "file.h"
 #include "handle.h"
 #include "request.h"
+#include "unicode.h"
 
 struct ioctl_call
 {
@@ -160,6 +161,41 @@ static const struct fd_ops device_fd_ops =
 };
 
 
+static struct list add_dev_requests_list = LIST_INIT(add_dev_requests_list);
+
+struct add_dev_request
+{
+    struct object          obj;       /* object header */
+    struct unicode_str     drvname;   /* driver name */
+    void                  *data;      /* driver specific data */
+    data_size_t            size;      /* driver specific data size */
+    struct list            entry;     /* entry in add_dev_requests_list */
+};
+
+static void add_dev_req_dump( struct object *obj, int verbose );
+static void add_dev_req_destroy( struct object *obj );
+
+static const struct object_ops add_dev_requests_ops =
+{
+    sizeof(struct add_dev_request),   /* size */
+    add_dev_req_dump,                 /* dump */
+    no_get_type,                      /* get_type */
+    no_add_queue,                     /* add_queue */
+    NULL,                             /* remove_queue */
+    NULL,                             /* signaled */
+    no_satisfied,                     /* satisfied */
+    no_signal,                        /* signal */
+    no_get_fd,                        /* get_fd */
+    no_map_access,                    /* map_access */
+    default_get_sd,                   /* get_sd */
+    default_set_sd,                   /* set_sd */
+    no_lookup_name,                   /* lookup_name */
+    no_open_file,                     /* open_file */
+    no_close_handle,                  /* close_handle */
+    add_dev_req_destroy               /* destroy */
+};
+
+
 static void ioctl_call_dump( struct object *obj, int verbose )
 {
     struct ioctl_call *ioctl = (struct ioctl_call *)obj;
@@ -425,6 +461,27 @@ static struct device_manager *create_device_manager(void)
 }
 
 
+static void add_dev_req_dump( struct object *obj, int verbose )
+{
+    struct add_dev_request *add_dev_req = (struct add_dev_request *)obj;
+    struct unicode_str *drvname = &add_dev_req->drvname;
+
+    fprintf( stderr, "AddDevice " );
+    if (drvname)
+        dump_strW( drvname->str, drvname->len / sizeof(WCHAR), stderr, "" );
+    fputc( '\n', stderr );
+}
+
+static void add_dev_req_destroy( struct object *obj )
+{
+    struct add_dev_request *add_dev_req = (struct add_dev_request *)obj;
+
+    list_remove( &add_dev_req->entry );
+    /* data and drvname.str are in the same memory block pointed to by drvname.str */
+    free( (void *)add_dev_req->drvname.str );
+}
+
+
 /* create a device manager */
 DECL_HANDLER(create_device_manager)
 {
@@ -554,3 +611,57 @@ DECL_HANDLER(get_ioctl_result)
     }
     release_object( device );
 }
+
+
+DECL_HANDLER(call_add_device)
+{
+    void *p;
+    struct add_dev_request *add_dev_req;
+    data_size_t size;
+
+    size = get_req_data_size();
+    p = mem_alloc( size );
+    if (p)
+    {
+        add_dev_req = alloc_object( &add_dev_requests_ops );
+        if (add_dev_req)
+        {
+            memcpy( p, get_req_data(), size );
+            add_dev_req->drvname.len = req->drvname_len;
+            add_dev_req->drvname.str = p;
+            add_dev_req->data = (char *)p + req->drvname_len;
+            add_dev_req->size = size - req->drvname_len;
+            list_add_tail( &add_dev_requests_list, &add_dev_req->entry );
+        }
+        else
+            free( p );
+    }
+}
+
+
+DECL_HANDLER(get_add_device_request)
+{
+    struct unicode_str drvname;
+    struct add_dev_request *add_dev_req;
+    int found = 0;
+
+    get_req_unicode_str( &drvname );
+    LIST_FOR_EACH_ENTRY( add_dev_req, &add_dev_requests_list, struct add_dev_request, entry )
+    {
+        struct unicode_str *p = &add_dev_req->drvname;
+
+        if (p->len != drvname.len) continue;
+        if (!strncmpiW( drvname.str, p->str, p->len/sizeof(WCHAR) ))
+        {
+            found = 1;
+            break;
+        }
+    }
+    if (found)
+    {
+        set_reply_data( add_dev_req->data, add_dev_req->size );
+        release_object( add_dev_req );
+    }
+    else
+        set_reply_data( NULL, 0 );
+}
diff --git a/server/protocol.def b/server/protocol.def
index d9fd48c..f0789f2 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -3077,3 +3077,19 @@ enum message_type
     unsigned int   alpha;         /* alpha (0..255) */
     unsigned int   flags;         /* LWA_* flags */
 @END
+
+
+/* Call AddDevice function in driver */
+ at REQ(call_add_device)
+    data_size_t    drvname_len;   /* driver name length */
+    VARARG(drvname,unicode_str);  /* driver name */
+    VARARG(data,bytes);           /* driver specific data */
+ at END
+
+
+/* Check if it should call AddDevice function */
+ at REQ(get_add_device_request)
+    VARARG(drvname,unicode_str);  /* driver name */
+ at REPLY
+    VARARG(data,bytes);           /* driver specific data */
+ at END
diff --git a/tools/wine.inf.in b/tools/wine.inf.in
index 00e9b97..c952f82 100644
--- a/tools/wine.inf.in
+++ b/tools/wine.inf.in
@@ -78,10 +78,12 @@ AddReg=\
 [DefaultInstall.Services]
 AddService=MountMgr,0x800,MountMgrService
 AddService=Spooler,0,SpoolerService
+AddService=Wineusb,0,WineusbService
 
 [DefaultInstall.NT.Services]
 AddService=MountMgr,0x800,MountMgrService
 AddService=Spooler,0,SpoolerService
+AddService=Wineusb,0,WineusbService
 
 [Strings]
 MciExtStr="Software\Microsoft\Windows NT\CurrentVersion\MCI Extensions"
@@ -2709,6 +2711,14 @@ StartType=4
 ErrorControl=1
 LoadOrderGroup="SpoolerGroup"
 
+[WineusbService]
+Description="WineUSB"
+DisplayName="WineUSB"
+ServiceBinary="%11%\wineusb.exe"
+ServiceType=0x10
+StartType=2
+ErrorControl=1
+
 [Services]
 HKLM,"System\CurrentControlSet\Services\VxD\MSTCP",,,""
 
-- 
1.5.6.5.GIT



More information about the wine-patches mailing list