usbhub.sys: add stubbed usbhub.sys

Damjan Jovanovic damjan.jov at gmail.com
Sun Apr 11 15:57:49 CDT 2010


On Thu, Mar 25, 2010 at 9:06 PM, Alexandre Julliard <julliard at winehq.org> wrote:
> Damjan Jovanovic <damjan.jov at gmail.com> writes:
>
>> I don't know enough about writing kernel drivers yet to decide this
>> (and several other things), so I think it's best I hack at it until I
>> have it working, then I'll start submitting patches again.
>>
>> In the meanwhile, is the use of libusb-1.0 in Wine ok? Or should I use
>> libusb-0.1 or some other library?
>
> I think 1.0 is fine, but I'd like to see some code using it first.
>
> --
> Alexandre Julliard
> julliard at winehq.org
>

I've hacked at this enough to get some basic USB I/O working, so now I
have a better idea of what's necessary.

I like usbhub.sys separate from usbd.sys because:
* The service is called Usbhub in the registry, drivers might depend
on the service, and having the service load usbd.sys instead of
usbhub.sys is confusing.
* usbd.sys isn't part of the device stack in any way, it's just a
utility library used to do miscellaneous things like parse USB
descriptors. Putting I/O code in there would be confusing.
* Windows from 2000 onwards uses usbhub.sys for device stack
management and I/O. No Windows version uses usbd.sys like that.
* If we don't want dlls that export nothing, mountmgr.sys also exports
nothing, so it should be part of ntoskrnl.exe by the same logic.
* There will no more usbXXX.sys files for basic USB after usbhub.sys.
We might eventually want higher-level device class drivers
(usbstor.sys, usbprint.sys) and Microsoft-provided generic drivers
(usbscan.sys) though.
* The driver doesn't load first and load usbd.sys via an import which
creates a device stack, usbhub.sys loads first and then loads drivers
(into the same process) on-demand, as their USB devices are plugged
in.

A *very hacked* patch is attached, if you want to see how libusb-1.0
is used. It only works just enough for the driver to read descriptors
from the device and send a few basic I/O requests.

I'd like to start sending real patches soon, so can I add a separate
usbhub.sys or do I have to stick with usbd.sys? And is libusb-1.0 ok?

Thank you
Damjan Jovanovic
-------------- next part --------------
diff --git a/configure.ac b/configure.ac
index 142d32a..6080b0f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -69,6 +69,7 @@ AC_ARG_WITH(png,       AS_HELP_STRING([--without-png],[do not use PNG]),
 AC_ARG_WITH(pthread,   AS_HELP_STRING([--without-pthread],[do not use the pthread library]),
             [if test "x$withval" = "xno"; then ac_cv_header_pthread_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 libusb]))
 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(v4l,       AS_HELP_STRING([--without-v4l],[do not use v4l1 (v4l support)]))
@@ -1162,6 +1163,28 @@ fi
 WINE_NOTICE_WITH(sane,[test "x$ac_cv_lib_soname_sane" = "x"],
                  [libsane ${notice_platform}development files not found, scanners won't be supported.])
 
+dnl *** Check for libusb-1.0 ****
+AC_SUBST(LIBUSBINCL,"")
+AC_SUBST(LIBUSBLIBS,"")
+if test "x$with_usb" != "xno"
+then
+    ac_save_CPPFLAGS="$CPPFLAGS"
+    if test "$PKG_CONFIG" != "false"
+    then
+        ac_usb_libs="`$PKG_CONFIG --libs libusb-1.0 2>/dev/null`"
+        ac_usb_cflags="`$PKG_CONFIG --cflags libusb-1.0 2>/dev/null`"
+        CPPFLAGS="$CPPFLAGS $ac_usb_cflags"
+    fi
+    AC_CHECK_HEADER(libusb.h,
+        [AC_CHECK_LIB(usb-1.0,libusb_init,
+             [AC_DEFINE(HAVE_LIBUSB, 1, [Define if you have the libusb-1.0 library and header])
+              LIBUSBLIBS="$ac_usb_libs"
+              LIBUSBINCL="$ac_usb_cflags"],,$ac_usb_libs)])
+    CPPFLAGS="$ac_save_CPPFLAGS"
+fi
+WINE_NOTICE_WITH(usb,[test "x$ac_cv_lib_usb_1_0_libusb_init" != "xyes"],
+                 [libusb-1.0 ${notice_platform}development files not found, USB won't be supported.])
+
 dnl **** Check for libv4l1 ****
 if test "x$with_v4l" != "xno"
 then
@@ -2543,6 +2566,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,,[usbhub.sys])
 WINE_CONFIG_DLL(user.exe16,enable_win16)
 WINE_CONFIG_DLL(user32,,[user32])
 WINE_CONFIG_TEST(dlls/user32/tests)
diff --git a/dlls/ntoskrnl.exe/ntoskrnl.c b/dlls/ntoskrnl.exe/ntoskrnl.c
index 7a5af73..0f77660 100644
--- a/dlls/ntoskrnl.exe/ntoskrnl.c
+++ b/dlls/ntoskrnl.exe/ntoskrnl.c
@@ -632,14 +632,9 @@ NTSTATUS  WINAPI IoGetDeviceObjectPointer( UNICODE_STRING *name, ACCESS_MASK acc
 
 
 /***********************************************************************
- *           IofCallDriver   (NTOSKRNL.EXE.@)
+ *           IoCallDriver   (NTOSKRNL.EXE.@)
  */
-#ifdef DEFINE_FASTCALL2_ENTRYPOINT
-DEFINE_FASTCALL2_ENTRYPOINT( IofCallDriver )
-NTSTATUS WINAPI __regs_IofCallDriver( DEVICE_OBJECT *device, IRP *irp )
-#else
-NTSTATUS WINAPI IofCallDriver( DEVICE_OBJECT *device, IRP *irp )
-#endif
+NTSTATUS WINAPI IoCallDriver( DEVICE_OBJECT *device, IRP *irp )
 {
     PDRIVER_DISPATCH dispatch;
     IO_STACK_LOCATION *irpsp;
@@ -650,6 +645,8 @@ NTSTATUS WINAPI IofCallDriver( DEVICE_OBJECT *device, IRP *irp )
     --irp->CurrentLocation;
     irpsp = --irp->Tail.Overlay.s.u2.CurrentStackLocation;
     dispatch = device->DriverObject->MajorFunction[irpsp->MajorFunction];
+if (dispatch == NULL) { FIXME("AVOIDING CRASH DUE TO UNIMPLEMENTED MAJOR FUNCTION 0x%X\n", irpsp->MajorFunction); return STATUS_SUCCESS; }
+FIXME("calling the driver at %p, function 0x%X !!!\n", dispatch, irpsp->MajorFunction);
     status = dispatch( device, irp );
 
     return status;
@@ -657,6 +654,21 @@ NTSTATUS WINAPI IofCallDriver( DEVICE_OBJECT *device, IRP *irp )
 
 
 /***********************************************************************
+ *           IofCallDriver   (NTOSKRNL.EXE.@)
+ */
+#ifdef DEFINE_FASTCALL2_ENTRYPOINT
+DEFINE_FASTCALL2_ENTRYPOINT( IofCallDriver )
+NTSTATUS WINAPI __regs_IofCallDriver( DEVICE_OBJECT *device, IRP *irp )
+#else
+NTSTATUS WINAPI IofCallDriver( DEVICE_OBJECT *device, IRP *irp )
+#endif
+{
+    TRACE( "%p %p\n", device, irp );
+    return IoCallDriver( device, irp );
+}
+
+
+/***********************************************************************
  *           IoGetRelatedDeviceObject    (NTOSKRNL.EXE.@)
  */
 PDEVICE_OBJECT WINAPI IoGetRelatedDeviceObject( PFILE_OBJECT obj )
@@ -679,6 +691,20 @@ PCONFIGURATION_INFORMATION WINAPI IoGetConfigurationInformation(void)
 
 
 /***********************************************************************
+ *           IoGetDeviceProperty     (NTOSKRNL.EXE.@)
+ */
+NTSTATUS WINAPI IoGetDeviceProperty( PDEVICE_OBJECT DeviceObject,
+                                     int DeviceProperty,
+                                     ULONG BufferLength,
+                                     PVOID PropertyBuffer,
+                                     PULONG ResultLength )
+{
+    FIXME( "%p %d %u %p %p:stub\n", DeviceObject, DeviceProperty, BufferLength, PropertyBuffer, ResultLength );
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+
+/***********************************************************************
  *           IoIsWdmVersionAvailable     (NTOSKRNL.EXE.@)
  */
 NTSTATUS WINAPI IoIsWdmVersionAvailable(UCHAR MajorVersion, UCHAR MinorVersion)
@@ -757,6 +783,20 @@ NTSTATUS WINAPI IoQueryDeviceDescription(PINTERFACE_TYPE itype, PULONG bus, PCON
 
 
 /***********************************************************************
+ *           IoRegisterDeviceInterface    (NTOSKRNL.EXE.@)
+ */
+NTSTATUS WINAPI IoRegisterDeviceInterface( PDEVICE_OBJECT obj, const GUID *interface_class_guid,
+                                           PUNICODE_STRING reference_string, PUNICODE_STRING symlink_name )
+{
+    static const WCHAR devicepath[] = {'D','E','V',0};
+    FIXME( "(%p, %s, %p, %p)\n", obj, debugstr_guid(interface_class_guid),
+           reference_string, symlink_name );
+    RtlCreateUnicodeString( symlink_name, devicepath );
+    return STATUS_SUCCESS;//NOT_IMPLEMENTED;
+}
+
+
+/***********************************************************************
  *           IoRegisterDriverReinitialization    (NTOSKRNL.EXE.@)
  */
 void WINAPI IoRegisterDriverReinitialization( PDRIVER_OBJECT obj, PDRIVER_REINITIALIZE reinit, PVOID context )
@@ -1167,6 +1207,16 @@ LONG WINAPI KeReleaseSemaphore( PRKSEMAPHORE Semaphore, KPRIORITY Increment,
 
 
 /***********************************************************************
+ *           KeResetEvent   (NTOSKRNL.EXE.@)
+ */
+LONG WINAPI KeResetEvent( PRKEVENT Event )
+{
+    FIXME("(%p): stub\n", Event);
+    return 0;
+}
+
+
+/***********************************************************************
  *           KeQueryTimeIncrement   (NTOSKRNL.EXE.@)
  */
 ULONG WINAPI KeQueryTimeIncrement(void)
@@ -1176,6 +1226,16 @@ ULONG WINAPI KeQueryTimeIncrement(void)
 
 
 /***********************************************************************
+ *           KeSetEvent   (NTOSKRNL.EXE.@)
+ */
+LONG WINAPI KeSetEvent( PRKEVENT Event, KPRIORITY Increment, BOOLEAN Wait )
+{
+    FIXME("(%p, %d, %d): stub\n", Event, Increment, Wait);
+    return 0;
+}
+
+
+/***********************************************************************
  *           KeSetPriorityThread   (NTOSKRNL.EXE.@)
  */
 KPRIORITY WINAPI KeSetPriorityThread( PKTHREAD Thread, KPRIORITY Priority )
diff --git a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec
index 993de77..ceb02a4 100644
--- a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec
+++ b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec
@@ -323,7 +323,7 @@
 @ stdcall IoBuildDeviceIoControlRequest(long ptr ptr long ptr long long ptr ptr)
 @ stub IoBuildPartialMdl
 @ stub IoBuildSynchronousFsdRequest
-@ stub IoCallDriver
+@ stdcall IoCallDriver(ptr ptr)
 @ stub IoCancelFileOpen
 @ stub IoCancelIrp
 @ stub IoCheckDesiredAccess
@@ -383,7 +383,7 @@
 @ stub IoGetDeviceInterfaceAlias
 @ stub IoGetDeviceInterfaces
 @ stdcall IoGetDeviceObjectPointer(ptr long ptr ptr)
-@ stub IoGetDeviceProperty
+@ stdcall IoGetDeviceProperty(ptr long long ptr ptr)
 @ stub IoGetDeviceToVerify
 @ stub IoGetDiskDeviceObject
 @ stub IoGetDmaAdapter
@@ -425,7 +425,7 @@
 @ stub IoReadPartitionTableEx
 @ stub IoReadTransferCount
 @ stub IoRegisterBootDriverReinitialization
-@ stub IoRegisterDeviceInterface
+@ stdcall IoRegisterDeviceInterface(ptr ptr ptr ptr)
 @ stdcall IoRegisterDriverReinitialization(ptr ptr ptr)
 @ stdcall IoRegisterFileSystem(ptr)
 @ stub IoRegisterFsRegistrationChange
@@ -597,7 +597,7 @@
 @ stub KeRemoveQueue
 @ stub KeRemoveQueueDpc
 @ stub KeRemoveSystemServiceTable
-@ stub KeResetEvent
+@ stdcall KeResetEvent(ptr)
 @ stub KeRestoreFloatingPointState
 @ stub KeRevertToUserAffinityThread
 @ stub KeRundownQueue
@@ -607,7 +607,7 @@
 @ stub KeSetAffinityThread
 @ stub KeSetBasePriorityThread
 @ stub KeSetDmaIoCoherency
-@ stub KeSetEvent
+@ stdcall KeSetEvent(ptr long long)
 @ stub KeSetEventBoostPriority
 @ stub KeSetIdealProcessorThread
 @ stub KeSetImportanceDpc
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..0cc8c61
--- /dev/null
+++ b/dlls/usbhub.sys/usbhub.c
@@ -0,0 +1,763 @@
+/*
+ * 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"
+
+// FIXME
+#include "wine/port.h"
+#include <stdio.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 "ddk/usbioctl.h"
+#include "wine/unicode.h"
+#include "wine/debug.h"
+
+#ifdef HAVE_LIBUSB
+#include <libusb.h>
+
+static libusb_context *context;
+#endif
+
+WINE_DEFAULT_DEBUG_CHANNEL(usbhub);
+
+#ifdef HAVE_LIBUSB
+
+typedef struct {
+    libusb_device *device;
+    libusb_device_handle *handle;
+} WineUsbDeviceExtension;
+
+static DRIVER_OBJECT *usbhub_driver;
+// FIXME
+    DRIVER_EXTENSION driver_extension;
+    DRIVER_OBJECT driver_obj;
+
+static USBD_STATUS libusb_to_usbd_status(int ret)
+{
+    switch (ret)
+    {
+        case LIBUSB_SUCCESS:
+            return USBD_STATUS_SUCCESS;
+        case LIBUSB_ERROR_IO:
+            return USBD_STATUS_INTERNAL_HC_ERROR;
+        case LIBUSB_ERROR_INVALID_PARAM:
+            return USBD_STATUS_INVALID_PARAMETER;
+        case LIBUSB_ERROR_ACCESS:
+            return USBD_STATUS_INTERNAL_HC_ERROR;
+        case LIBUSB_ERROR_NO_DEVICE:
+            return USBD_STATUS_DEVICE_GONE;
+        case LIBUSB_ERROR_NOT_FOUND:
+            return USBD_STATUS_INTERNAL_HC_ERROR;
+        case LIBUSB_ERROR_BUSY:
+            return USBD_STATUS_ERROR_BUSY;
+        case LIBUSB_ERROR_TIMEOUT:
+            return USBD_STATUS_TIMEOUT;
+        case LIBUSB_ERROR_OVERFLOW:
+            /* http://www.cypress.com/?rID=37596 */
+            return USBD_STATUS_BUFFER_OVERRUN;
+        case LIBUSB_ERROR_PIPE:
+            return USBD_STATUS_STALL_PID;
+        case LIBUSB_ERROR_INTERRUPTED:
+            return USBD_STATUS_INTERNAL_HC_ERROR;
+        case LIBUSB_ERROR_NO_MEM:
+            return USBD_STATUS_NO_MEMORY;
+        case LIBUSB_ERROR_NOT_SUPPORTED:
+            return USBD_STATUS_NOT_SUPPORTED;
+    }
+    return USBD_STATUS_INTERNAL_HC_ERROR;
+}
+
+static NTSTATUS usb_vendor_class_control_request( URB *urb, UCHAR typeAndRecipient, WineUsbDeviceExtension *device_extension )
+{
+    UCHAR requestType;
+    UCHAR *buffer;
+    int ret;
+
+    requestType = typeAndRecipient;
+    if (urb->u.UrbControlVendorClassRequest.TransferFlags & USBD_TRANSFER_DIRECTION_IN)
+        requestType |= 0x80;
+    if (requestType & urb->u.UrbControlVendorClassRequest.RequestTypeReservedBits)
+    {
+        FIXME( "buggy driver wants to set reserved bits 0x%02X in USB control request, aborting I/O, please report\n",
+            requestType & urb->u.UrbControlVendorClassRequest.RequestTypeReservedBits);
+        urb->u.UrbHeader.Status = USBD_STATUS_INVALID_PARAMETER;
+        return STATUS_INVALID_PARAMETER;
+    }
+    requestType |= urb->u.UrbControlVendorClassRequest.RequestTypeReservedBits;
+
+    buffer = urb->u.UrbControlVendorClassRequest.TransferBuffer;
+    if (buffer == NULL)
+        buffer = urb->u.UrbControlVendorClassRequest.TransferBufferMDL->MappedSystemVa;
+    if (buffer == NULL)
+    {
+        urb->u.UrbHeader.Status = USBD_STATUS_INVALID_PARAMETER;
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    ret = libusb_control_transfer( device_extension->handle, requestType,
+        urb->u.UrbControlVendorClassRequest.Request,
+        urb->u.UrbControlVendorClassRequest.Value,
+        urb->u.UrbControlVendorClassRequest.Index,
+        buffer, urb->u.UrbControlVendorClassRequest.TransferBufferLength, 0 );
+    if (ret >= 0)
+    {
+        urb->u.UrbControlVendorClassRequest.TransferBufferLength = ret;
+        urb->u.UrbHeader.Status = USBD_STATUS_SUCCESS;
+        return STATUS_SUCCESS;
+    }
+    else
+    {
+        urb->u.UrbHeader.Status = libusb_to_usbd_status( ret );
+        return STATUS_UNSUCCESSFUL;
+    }
+}
+
+static NTSTATUS submit_urb( URB *urb, WineUsbDeviceExtension *device_extension )
+{
+    NTSTATUS status = STATUS_NOT_IMPLEMENTED;
+    int ret;
+
+    switch (urb->u.UrbHeader.Function)
+    {
+        case URB_FUNCTION_SELECT_CONFIGURATION:
+        {
+            TRACE( "URB_FUNCTION_SELECT_CONFIGURATION\n" );
+            break;
+        }
+        case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE:
+        {
+            unsigned char *buffer;
+            TRACE( "URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE\n" );
+
+            buffer = urb->u.UrbControlDescriptorRequest.TransferBuffer;
+            if (buffer == NULL)
+                buffer = urb->u.UrbControlDescriptorRequest.TransferBufferMDL->MappedSystemVa;
+            if (buffer == NULL)
+            {
+                urb->u.UrbHeader.Status = USBD_STATUS_INVALID_PARAMETER;
+                status = STATUS_INVALID_PARAMETER;
+                break;
+            }
+
+            switch (urb->u.UrbControlDescriptorRequest.DescriptorType)
+            {
+                case USB_DEVICE_DESCRIPTOR_TYPE:
+                {
+                    struct libusb_device_descriptor device_descriptor;
+                    TRACE( "USB_DEVICE_DESCRIPTOR_TYPE\n" );
+                    ret = libusb_get_device_descriptor( device_extension->device, &device_descriptor );
+                    if (ret == LIBUSB_SUCCESS)
+                    {
+                        ULONG size = urb->u.UrbControlDescriptorRequest.TransferBufferLength;
+                        if (size > sizeof(device_descriptor))
+                            size = sizeof(device_descriptor);
+                        memcpy( buffer, &device_descriptor, size );
+                        urb->u.UrbHeader.Status = USBD_STATUS_SUCCESS;
+                        status = STATUS_SUCCESS;
+                    }
+                    else
+                    {
+                        ERR( "libusb error %d getting device descriptor\n", ret );
+                        urb->u.UrbHeader.Status = libusb_to_usbd_status( ret );
+                        status = STATUS_UNSUCCESSFUL;
+                    }
+                    break;
+                }
+                case USB_CONFIGURATION_DESCRIPTOR_TYPE:
+                {
+                    struct libusb_config_descriptor *config_descriptor;
+                    TRACE( "USB_CONFIGURATION_DESCRIPTOR_TYPE\n" );
+                    ret = libusb_get_active_config_descriptor( device_extension->device, &config_descriptor );
+                    if (ret == LIBUSB_SUCCESS)
+                    {
+                        ULONG remaining = urb->u.UrbControlDescriptorRequest.TransferBufferLength;
+                        int i;
+                        size_t size = sizeof(USB_CONFIGURATION_DESCRIPTOR);
+                        if (size > remaining)
+                            size = remaining;
+                        memcpy( buffer, config_descriptor, size );
+                        buffer += size;
+                        remaining -= size;
+                        size = config_descriptor->extra_length;
+                        if (size > remaining)
+                            size = remaining;
+                        memcpy( buffer, config_descriptor->extra, size );
+                        buffer += size;
+                        remaining -= size;
+                        for (i = 0; i < config_descriptor->bNumInterfaces; i++)
+                        {
+                            int j;
+                            size = sizeof(USB_INTERFACE_DESCRIPTOR);
+                            if (size > remaining)
+                                size = remaining;
+                            memcpy( buffer, &config_descriptor->interface[i].altsetting[0], size );
+                            buffer += size;
+                            remaining -= size;
+                            size = config_descriptor->interface[i].altsetting[0].extra_length;
+                            if (size > remaining)
+                                size = remaining;
+                            memcpy( buffer, &config_descriptor->interface[i].altsetting[0].extra, size );
+                            buffer += size;
+                            remaining -= size;
+                            for (j = 0; j < config_descriptor->interface[i].altsetting[0].bNumEndpoints; j++)
+                            {
+                                size = sizeof(USB_ENDPOINT_DESCRIPTOR);
+                                if (size > remaining)
+                                    size = remaining;
+                                memcpy( buffer, &config_descriptor->interface[i].altsetting[0].endpoint[j], size );
+                                buffer += size;
+                                remaining -= size;
+                                size = config_descriptor->interface[i].altsetting[0].endpoint[j].extra_length;
+                                if (size > remaining)
+                                    size = remaining;
+                                memcpy( buffer, &config_descriptor->interface[i].altsetting[0].endpoint[j].extra, size );
+                                buffer += size;
+                                remaining -= size;
+                            }
+                        }
+                        urb->u.UrbHeader.Status = USBD_STATUS_SUCCESS;
+                        status = STATUS_SUCCESS;
+                        libusb_free_config_descriptor( config_descriptor );
+                    }
+                    else
+                    {
+                        ERR( "libusb error %d getting configuration descriptor\n", ret );
+                        urb->u.UrbHeader.Status = libusb_to_usbd_status( ret );
+                        status = STATUS_UNSUCCESSFUL;
+                    }
+                    break;
+                }
+                case USB_STRING_DESCRIPTOR_TYPE:
+                    FIXME( "USB_STRING_DESCRIPTOR_TYPE: stub\n" );
+                    urb->u.UrbHeader.Status = STATUS_NOT_SUPPORTED;
+                    status = STATUS_NOT_IMPLEMENTED;
+                    break;
+                default:
+                    FIXME( "unknown USB descriptor type %d\n", urb->u.UrbControlDescriptorRequest.DescriptorType );
+                    urb->u.UrbHeader.Status = USBD_STATUS_NOT_SUPPORTED;
+                    status = STATUS_NOT_IMPLEMENTED;
+            }
+            break;
+        }
+        case URB_FUNCTION_VENDOR_DEVICE:
+        {
+            TRACE( "URB_FUNCTION_VENDOR_DEVICE\n" );
+            status = usb_vendor_class_control_request( urb, 0x40, device_extension );
+            break;
+        }
+        default:
+            FIXME( "unimplemented URB function %d\n", urb->u.UrbHeader.Function );
+            urb->u.UrbHeader.Status = USBD_STATUS_NOT_SUPPORTED;
+            status = STATUS_NOT_IMPLEMENTED;
+            break;
+    }
+    return status;
+}
+
+static NTSTATUS WINAPI usbhub_internal_ioctl( DEVICE_OBJECT *device, IRP *irp )
+{
+    IO_STACK_LOCATION *irpsp;
+    URB *urb = NULL;
+    WineUsbDeviceExtension *deviceExtension;
+    NTSTATUS status = STATUS_NOT_IMPLEMENTED;
+
+    TRACE( "(%p, %p)\n", device, irp );
+
+    deviceExtension = device->DeviceExtension;
+    irp->IoStatus.Information = 0;
+    irpsp = IoGetCurrentIrpStackLocation( irp );
+
+    switch (irpsp->Parameters.DeviceIoControl.IoControlCode)
+    {
+        case IOCTL_INTERNAL_USB_SUBMIT_URB:
+            urb = irpsp->Parameters.Others.Argument1;
+            status = submit_urb( urb, deviceExtension );
+            break;
+        default:
+            FIXME( "ioctl code 0x%08X is unimplemented\n", irpsp->Parameters.DeviceIoControl.IoControlCode );
+            if (urb)
+                urb->u.UrbHeader.Status = USBD_STATUS_NOT_SUPPORTED;
+            status = STATUS_NOT_IMPLEMENTED;
+    }
+
+    irp->IoStatus.u.Status = status;
+    IoCompleteRequest( irp, IO_NO_INCREMENT );
+    return status;
+}
+
+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;
+}
+
+/* find the LDR_MODULE corresponding to the driver module */
+static LDR_MODULE *find_ldr_module( HMODULE module )
+{
+    LIST_ENTRY *entry, *list = &NtCurrentTeb()->Peb->LdrData->InMemoryOrderModuleList;
+
+    for (entry = list->Flink; entry != list; entry = entry->Flink)
+    {
+        LDR_MODULE *ldr = CONTAINING_RECORD(entry, LDR_MODULE, InMemoryOrderModuleList);
+        if (ldr->BaseAddress == module) return ldr;
+        if (ldr->BaseAddress > (void *)module) break;
+    }
+    return NULL;
+}
+
+/* load the driver module file */
+static HMODULE load_driver_module( const WCHAR *name )
+{
+    IMAGE_NT_HEADERS *nt;
+    const IMAGE_IMPORT_DESCRIPTOR *imports;
+    size_t page_size = getpagesize();
+    int i, delta;
+    ULONG size;
+    HMODULE module = LoadLibraryW( name );
+
+    if (!module) return NULL;
+    nt = RtlImageNtHeader( module );
+
+    if (!(delta = (char *)module - (char *)nt->OptionalHeader.ImageBase)) return module;
+
+    /* the loader does not apply relocations to non page-aligned binaries or executables,
+     * we have to do it ourselves */
+
+    if (nt->OptionalHeader.SectionAlignment < page_size ||
+        !(nt->FileHeader.Characteristics & IMAGE_FILE_DLL))
+    {
+        DWORD old;
+        IMAGE_BASE_RELOCATION *rel, *end;
+
+        if ((rel = RtlImageDirectoryEntryToData( module, TRUE, IMAGE_DIRECTORY_ENTRY_BASERELOC, &size )))
+        {
+            WINE_TRACE( "%s: relocating from %p to %p\n",
+                        wine_dbgstr_w(name), (char *)module - delta, module );
+            end = (IMAGE_BASE_RELOCATION *)((char *)rel + size);
+            while (rel < end && rel->SizeOfBlock)
+            {
+                void *page = (char *)module + rel->VirtualAddress;
+                VirtualProtect( page, page_size, PAGE_EXECUTE_READWRITE, &old );
+                rel = LdrProcessRelocationBlock( page, (rel->SizeOfBlock - sizeof(*rel)) / sizeof(USHORT),
+                                                 (USHORT *)(rel + 1), delta );
+                if (old != PAGE_EXECUTE_READWRITE) VirtualProtect( page, page_size, old, NULL );
+                if (!rel) goto error;
+            }
+            /* make sure we don't try again */
+            nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = 0;
+        }
+    }
+
+    /* make sure imports are relocated too */
+    if ((imports = RtlImageDirectoryEntryToData( module, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size )))
+    {
+        for (i = 0; imports[i].Name && imports[i].FirstThunk; i++)
+        {
+            char *name = (char *)module + imports[i].Name;
+            WCHAR buffer[32], *p = buffer;
+
+            while (p < buffer + 32) if (!(*p++ = *name++)) break;
+            if (p <= buffer + 32) FreeLibrary( load_driver_module( buffer ) );
+        }
+    }
+
+    return module;
+
+error:
+    FreeLibrary( module );
+    return NULL;
+}
+
+/* call the driver init entry point */
+static NTSTATUS init_driver( WCHAR *driver_name, HMODULE module, UNICODE_STRING *keyname )
+{
+    unsigned int i;
+    NTSTATUS status;
+    const IMAGE_NT_HEADERS *nt = RtlImageNtHeader( module );
+
+    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_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) );
+
+    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 );
+    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 );
+    for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
+        WINE_TRACE( "- MajorFunction[%d] = %p\n", i, driver_obj.MajorFunction[i] );
+
+    return status;
+}
+/* load the .sys module for a device driver */
+static HMODULE load_driver(WCHAR *driver_name)
+{
+    static const WCHAR driversW[] = {'\\','d','r','i','v','e','r','s','\\',0};
+    static const WCHAR postfixW[] = {'.','s','y','s',0};
+    static const WCHAR ntprefixW[] = {'\\','?','?','\\',0};
+    static const WCHAR ImagePathW[] = {'I','m','a','g','e','P','a','t','h',0};
+    static const WCHAR servicesW[] = {'\\','R','e','g','i','s','t','r','y',
+                                      '\\','M','a','c','h','i','n','e',
+                                      '\\','S','y','s','t','e','m',
+                                      '\\','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};
+
+    UNICODE_STRING keypath;
+    HMODULE module;
+    LPWSTR path = NULL, str;
+    DWORD type, size;
+    HKEY driver_hkey;
+
+    str = HeapAlloc( GetProcessHeap(), 0, sizeof(servicesW) + strlenW(driver_name)*sizeof(WCHAR) );
+    lstrcpyW( str, servicesW );
+    lstrcatW( str, driver_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 NULL;
+    }
+    RtlInitUnicodeString( &keypath, str );
+
+    /* read the executable path from memory */
+    size = 0;
+    if (!RegQueryValueExW( driver_hkey, ImagePathW, NULL, &type, NULL, &size ))
+    {
+        str = HeapAlloc( GetProcessHeap(), 0, size );
+        if (!RegQueryValueExW( driver_hkey, ImagePathW, NULL, &type, (LPBYTE)str, &size ))
+        {
+            size = ExpandEnvironmentStringsW(str,NULL,0);
+            path = HeapAlloc(GetProcessHeap(),0,size*sizeof(WCHAR));
+            ExpandEnvironmentStringsW(str,path,size);
+        }
+        HeapFree( GetProcessHeap(), 0, str );
+        if (!path) return NULL;
+    }
+    else
+    {
+        /* default is to use the driver name + ".sys" */
+        WCHAR buffer[MAX_PATH];
+        GetSystemDirectoryW(buffer, MAX_PATH);
+        path = HeapAlloc(GetProcessHeap(),0,
+          (strlenW(buffer) + strlenW(driversW) + strlenW(driver_name) + strlenW(postfixW) + 1)
+          *sizeof(WCHAR));
+        lstrcpyW(path, buffer);
+        lstrcatW(path, driversW);
+        lstrcatW(path, driver_name);
+        lstrcatW(path, postfixW);
+    }
+
+    /* GameGuard uses an NT-style path name */
+    str = path;
+    if (!strncmpW( path, ntprefixW, 4 )) str += 4;
+
+    WINE_TRACE( "loading driver %s\n", wine_dbgstr_w(str) );
+
+    module = load_driver_module( str );
+    HeapFree( GetProcessHeap(), 0, path );
+    if (!module) return NULL;
+
+    init_driver( driver_name, module, &keypath );
+    return module;
+}
+
+
+static void start_service( WCHAR *service, libusb_device *device, libusb_device_handle *handle )
+{
+    HMODULE module;
+    module = load_driver(service);
+    if (module)
+    {
+        DEVICE_OBJECT *device_object;
+        NTSTATUS status;
+
+        FIXME("loaded driver\n");
+        status = IoCreateDevice( usbhub_driver, sizeof(WineUsbDeviceExtension), NULL, FILE_DEVICE_UNKNOWN, 0, FALSE, &device_object );
+        if (status == STATUS_SUCCESS)
+        {
+            NTSTATUS (WINAPI *AddDevice)( PDRIVER_OBJECT, PDEVICE_OBJECT ) =
+                driver_obj.DriverExtension->AddDevice;
+            ((WineUsbDeviceExtension*)device_object->DeviceExtension)->device = device;
+            ((WineUsbDeviceExtension*)device_object->DeviceExtension)->handle = handle;
+            FIXME("calling add device at %p\n", AddDevice);
+            status = AddDevice( &driver_obj, device_object );
+            FIXME("add device gave 0x%X\n", status);
+            if (status == STATUS_SUCCESS)
+            {
+                if (driver_obj.MajorFunction[IRP_MJ_PNP] == NULL)
+                    FIXME("MJ_PNP not supported\n");
+                else
+                {
+                    PIRP irp;
+                    irp = IoAllocateIrp( device_object->StackSize, FALSE );
+                    if (irp)
+                    {
+                        IO_STACK_LOCATION *irpsp;
+                        irpsp = IoGetNextIrpStackLocation( irp );
+                        irp->RequestorMode = KernelMode;
+                        irpsp->MajorFunction = IRP_MJ_PNP;
+                        irpsp->MinorFunction = IRP_MN_START_DEVICE;
+                        irpsp->DeviceObject = device_object;
+device_object->CurrentIrp = irp;
+device_object->DriverObject = usbhub_driver;
+irp->IoStatus.u.Status = STATUS_NOT_SUPPORTED;
+                        status = IoCallDriver( device_object->AttachedDevice, irp );
+                        FIXME("IoCallDriver gave 0x%x\n", status);
+                        IoFreeIrp( irp );
+                    }
+                    else
+                        FIXME("IoAllocateIrp failed\n");
+                }
+            }
+        }
+        else
+            ERR("IoCreateDevice failed, status 0x%X\n", status);
+    }
+    else
+        ERR("loading driver failed\n");
+}
+
+static void load_usb_driver( libusb_device *device, libusb_device_handle *handle )
+{
+    static const WCHAR usbW[] = {'U','S','B',0};
+    int ret;
+    struct libusb_device_descriptor dev_desc;
+    DWORD i;
+    BOOL found = FALSE;
+    HDEVINFO devices = INVALID_HANDLE_VALUE;
+    SP_DEVINFO_DATA devinfo;
+
+    ret = libusb_get_device_descriptor( device, &dev_desc );
+    if (ret != LIBUSB_SUCCESS)
+    {
+        ERR( "getting USB device descriptor failed, error %d\n", ret );
+        goto end;
+    }
+
+    /* Windows could create an enum key here,
+     * but we do all that from winecfg
+     * and only when asked */
+    devices = SetupDiGetClassDevsW( NULL, usbW, NULL, DIGCF_ALLCLASSES );
+    if (devices == INVALID_HANDLE_VALUE)
+    {
+        ERR( "listing USB devices failed with error %d\n", GetLastError() );
+        goto end;
+    }
+
+    memset( &devinfo, 0, sizeof(devinfo) );
+    devinfo.cbSize = sizeof(devinfo);
+    for (i = 0; SetupDiEnumDeviceInfo( devices, i, &devinfo ); i++)
+    {
+        CHAR deviceId[MAX_DEVICE_ID_LEN];
+        if (SetupDiGetDeviceInstanceIdA( devices, &devinfo, deviceId, MAX_DEVICE_ID_LEN, NULL ))
+        {
+#if 0
+            static const WCHAR pattern[] = {
+                'U','S','B','\\','V','I','D','_','%','0','4','X','&',
+                                 'P','I','D','_','%','0','4','X',0
+            };
+#endif
+            int vendorId, productId;
+            if (sscanf( deviceId, "USB\\VID_%04X&PID_%04X", &vendorId, &productId ) == 2 &&
+                dev_desc.idVendor == vendorId && dev_desc.idProduct == productId)
+            {
+                WCHAR *service;
+                DWORD size = 0;
+                SetupDiGetDeviceRegistryPropertyW( devices, &devinfo, SPDRP_SERVICE,
+                    NULL, NULL, 0, &size );
+                service = HeapAlloc( GetProcessHeap(), 0, size );
+                if (service)
+                {
+                    if (SetupDiGetDeviceRegistryPropertyW( devices, &devinfo, SPDRP_SERVICE,
+                            NULL, (PBYTE)service, size, &size ))
+                    {
+                        start_service( service, device, handle );
+                    }
+                    else
+                        ERR(" error %d reading service name\n", GetLastError() );
+                    HeapFree( GetProcessHeap(), 0, service );
+                }
+            }
+        }
+        else
+            ERR( "couldn't get device instance id, error %d\n", GetLastError() );
+    }
+    if (!found && GetLastError() != ERROR_NO_MORE_ITEMS)
+        ERR( "enumerating USB devices failed with error %d\n", GetLastError() );
+
+end:
+    if (devices != INVALID_HANDLE_VALUE)
+        SetupDiDestroyDeviceInfoList( devices );
+}
+
+static void add_usb_device( libusb_device *device )
+{
+    int ret;
+    libusb_device_handle *handle;
+
+    ret = libusb_open( device, &handle );
+    if (ret == LIBUSB_SUCCESS)
+    {
+        if (is_any_usb_interface_unclaimed( device, handle ))
+        {
+FIXME("found free device\n");
+            load_usb_driver( device, handle );
+        }
+        libusb_close( handle );
+    }
+    else
+        ERR("could not open libusb device, error %d\n", ret);
+}
+
+static DWORD WINAPI detect_usb_devices( LPVOID arg )
+{
+    /* HAL is deprecated,
+     * its official replacements - devkit and udev - are 100% Linux-only (wow, progress!),
+     * libusb-1.0 plans to support device hotplugging from version 1.1,
+     * so we have to poll for now.
+     */
+    while (1)
+    {
+        libusb_device **list;
+        ssize_t size;
+        size = libusb_get_device_list( context, &list );
+        if (size >= 0)
+        {
+            int i;
+            for (i = 0; i < size; i++)
+            {
+                add_usb_device( list[i] );
+            }
+            libusb_free_device_list( list, 1 );
+        }
+        else
+            ERR( "libusb_get_device_list failed with error %d\n", size );
+        Sleep( 1000 );
+//FIXME:
+break;
+
+    }
+    return 0;
+}
+
+static VOID WINAPI usbhub_unload( DRIVER_OBJECT *driver )
+{
+    TRACE( "(%p)\n", driver );
+    libusb_exit( context );
+}
+
+#endif
+
+NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path )
+{
+    int ret = 0;
+    NTSTATUS status = STATUS_SUCCESS;
+
+    TRACE( "(%p, %s)\n", driver, debugstr_w(path->Buffer) );
+
+#ifdef HAVE_LIBUSB
+    ret = libusb_init( &context );
+    if (ret == LIBUSB_SUCCESS)
+    {
+        if (CreateThread( NULL, 0, detect_usb_devices, NULL, 0, NULL ) != NULL)
+        {
+            driver->DriverUnload = usbhub_unload;
+            driver->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = usbhub_internal_ioctl;
+            usbhub_driver = driver;
+        }
+        else
+        {
+            ERR( "couldn't create thread to detect USB devices, error %d\n", GetLastError() );
+            status = STATUS_UNSUCCESSFUL;
+        }
+    }
+    else
+    {
+        ERR( "libusb_init() failed with %d\n", ret );
+        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..e69de29
diff --git a/include/ddk/usb.h b/include/ddk/usb.h
index af1c10d..0093478 100644
--- a/include/ddk/usb.h
+++ b/include/ddk/usb.h
@@ -103,6 +103,7 @@ typedef PVOID USBD_INTERFACE_HANDLE;
 #define USBD_STATUS_BABBLE_DETECTED                  ((USBD_STATUS)0xC0000012)
 #define USBD_STATUS_DATA_BUFFER_ERROR                ((USBD_STATUS)0xC0000013)
 #define USBD_STATUS_ENDPOINT_HALTED                  ((USBD_STATUS)0xC0000030)
+#define USBD_STATUS_NO_MEMORY                        ((USBD_STATUS)0x80000100)
 #define USBD_STATUS_INVALID_URB_FUNCTION             ((USBD_STATUS)0x80000200)
 #define USBD_STATUS_INVALID_PARAMETER                ((USBD_STATUS)0x80000300)
 #define USBD_STATUS_ERROR_BUSY                       ((USBD_STATUS)0x80000400)
diff --git a/include/ddk/usbioctl.h b/include/ddk/usbioctl.h
new file mode 100644
index 0000000..8fcce74
--- /dev/null
+++ b/include/ddk/usbioctl.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 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
+ */
+
+#ifndef __DDK_USBIOCTL_H__
+#define __DDK_USBIOCTL_H__
+
+#include "ntddk.h"
+#include "usbiodef.h"
+
+#define IOCTL_INTERNAL_USB_SUBMIT_URB \
+    CTL_CODE(FILE_DEVICE_USB, USB_SUBMIT_URB, METHOD_NEITHER, FILE_ANY_ACCESS)
+
+#endif
diff --git a/include/ddk/usbiodef.h b/include/ddk/usbiodef.h
new file mode 100644
index 0000000..0c34cdd
--- /dev/null
+++ b/include/ddk/usbiodef.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 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
+ */
+
+#ifndef __DDK_USBIODEF_H__
+#define __DDK_USBIODEF_H__
+
+#include "ntddk.h"
+
+#define USB_SUBMIT_URB 0
+
+#define FILE_DEVICE_USB FILE_DEVICE_UNKNOWN
+
+#endif
diff --git a/include/ddk/wdm.h b/include/ddk/wdm.h
index 102dda8..89c9805 100644
--- a/include/ddk/wdm.h
+++ b/include/ddk/wdm.h
@@ -1024,14 +1024,18 @@ NTSTATUS WINAPI ObCloseHandle(IN HANDLE handle);
 #ifdef NONAMELESSUNION
 # ifdef NONAMELESSSTRUCT
 #  define IoGetCurrentIrpStackLocation(_Irp) ((_Irp)->Tail.Overlay.s.u2.CurrentStackLocation)
+#  define IoGetNextIrpStackLocation(_Irp) ((_Irp)->Tail.Overlay.s.u2.CurrentStackLocation - 1)
 # else
 #  define IoGetCurrentIrpStackLocation(_Irp) ((_Irp)->Tail.Overlay.u2.CurrentStackLocation)
+#  define IoGetNextIrpStackLocation(_Irp) ((_Irp)->Tail.Overlay.u2.CurrentStackLocation - 1)
 # endif
 #else
 # ifdef NONAMELESSSTRUCT
 #  define IoGetCurrentIrpStackLocation(_Irp) ((_Irp)->Tail.Overlay.s.CurrentStackLocation)
+#  define IoGetNextIrpStackLocation(_Irp) ((_Irp)->Tail.Overlay.s.CurrentStackLocation - 1)
 # else
 #  define IoGetCurrentIrpStackLocation(_Irp) ((_Irp)->Tail.Overlay.CurrentStackLocation)
+#  define IoGetNextIrpStackLocation(_Irp) ((_Irp)->Tail.Overlay.CurrentStackLocation - 1)
 # endif
 #endif
 
@@ -1059,6 +1063,7 @@ void      WINAPI ExFreePoolWithTag(PVOID,ULONG);
 NTSTATUS  WINAPI IoAllocateDriverObjectExtension(PDRIVER_OBJECT,PVOID,ULONG,PVOID*);
 PVOID     WINAPI IoAllocateErrorLogEntry(PVOID,UCHAR);
 PIRP      WINAPI IoAllocateIrp(CCHAR,BOOLEAN);
+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**);
 NTSTATUS  WINAPI IoCreateDriver(UNICODE_STRING*,PDRIVER_INITIALIZE);


More information about the wine-devel mailing list