[1/3] winebus.sys: Create bus devices. (v9)

Sebastian Lackner sebastian at fds-team.de
Wed Sep 14 02:28:51 CDT 2016


From: Aric Stewart <aric at codeweavers.com>

Signed-off-by: Aric Stewart <aric at codeweavers.com>
Signed-off-by: Sebastian Lackner <sebastian at fds-team.de>
---

Changes in v9 (already shared privately with Aric):

* Avoid LPVOID type in bus.h.
* Avoid typedefs for both pnp_device and device extension structure.
* Store vid/pid as WORD in the device extension structure (instead of DWORD).
* Allow serial numbers with arbitrary length (store as pointer instead of fixed size array).
  A fixed size array would probably also be fine, but it does not make sense to use a bigger
  buffer for serial than for the combined instance ID.
* Simplify get_vidpid_index by passing both vid and pid as arguments.
* Return a string pointer from get_instance_id. This avoids careful thinking about the correct
  choice of buffer sizes. The caller is responsible to free the pointer. As an alternative,
  we could maybe also store the instance ID directly in the extension struct (instead of all
  the separate fields which cannot be queried anyway).
* Trace all function arguments in bus_create_hid_device. I admit that its a lot of information,
  but the "native" pointer alone is also not really meaningful.
* Merge add_device() into bus_create_hid_device(). As far as I understand this is the only
  place where devices are added - also, we have to be careful to make sure the function is
  really thread-safe when multiple threads try to add devices. The lock in get_vidpid_index
  is no longer necessary when all actions are done in the CS.

 dlls/winebus.sys/Makefile.in |    2 
 dlls/winebus.sys/bus.h       |    3 
 dlls/winebus.sys/bus_udev.c  |   19 +++++
 dlls/winebus.sys/main.c      |  150 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 173 insertions(+), 1 deletion(-)

diff --git a/dlls/winebus.sys/Makefile.in b/dlls/winebus.sys/Makefile.in
index b24416b..9d3a0c2 100644
--- a/dlls/winebus.sys/Makefile.in
+++ b/dlls/winebus.sys/Makefile.in
@@ -1,5 +1,5 @@
 MODULE    = winebus.sys
-IMPORTS   = ntoskrnl
+IMPORTS   = ntoskrnl setupapi
 EXTRALIBS = $(UDEV_LIBS)
 EXTRAINCL = $(UDEV_CFLAGS)
 EXTRADLLFLAGS = -Wb,--subsystem,native
diff --git a/dlls/winebus.sys/bus.h b/dlls/winebus.sys/bus.h
index 3d53a46..dcb50f2 100644
--- a/dlls/winebus.sys/bus.h
+++ b/dlls/winebus.sys/bus.h
@@ -21,3 +21,6 @@ NTSTATUS WINAPI udev_driver_init(DRIVER_OBJECT *driver, UNICODE_STRING *registry
 
 /* HID Plug and Play Bus */
 NTSTATUS WINAPI common_pnp_dispatch(DEVICE_OBJECT *device, IRP *irp) DECLSPEC_HIDDEN;
+DEVICE_OBJECT *bus_create_hid_device(DRIVER_OBJECT *driver, const WCHAR *busidW, void *native, WORD vid,
+                                     WORD pid, DWORD version, DWORD uid, const WCHAR *serialW, BOOL is_gamepad,
+                                     const GUID *class) DECLSPEC_HIDDEN;
diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c
index d47a556..1525861 100644
--- a/dlls/winebus.sys/bus_udev.c
+++ b/dlls/winebus.sys/bus_udev.c
@@ -50,6 +50,11 @@ WINE_DEFAULT_DEBUG_CHANNEL(plugplay);
 static struct udev *udev_context = NULL;
 static DRIVER_OBJECT *udev_driver_obj = NULL;
 
+static const WCHAR hidraw_busidW[] = {'H','I','D','R','A','W',0};
+
+#include "initguid.h"
+DEFINE_GUID(GUID_DEVCLASS_HIDRAW, 0x3def44ad,0x242e,0x46e5,0x82,0x6d,0x70,0x72,0x13,0xf3,0xaa,0x81);
+
 static DWORD get_sysattr_dword(struct udev_device *dev, const char *sysattr, int base)
 {
     const char *attr = udev_device_get_sysattr_value(dev, sysattr);
@@ -81,6 +86,8 @@ static void try_add_device(struct udev_device *dev)
 {
     DWORD vid = 0, pid = 0, version = 0;
     struct udev_device *usbdev;
+    DEVICE_OBJECT *device = NULL;
+    const char *subsystem;
     const char *devnode;
     WCHAR *serial = NULL;
     int fd;
@@ -107,6 +114,18 @@ static void try_add_device(struct udev_device *dev)
     TRACE("Found udev device %s (vid %04x, pid %04x, version %u, serial %s)\n",
           debugstr_a(devnode), vid, pid, version, debugstr_w(serial));
 
+    subsystem = udev_device_get_subsystem(dev);
+    if (strcmp(subsystem, "hidraw") == 0)
+    {
+        device = bus_create_hid_device(udev_driver_obj, hidraw_busidW, dev, vid, pid,
+                                       version, 0, serial, FALSE, &GUID_DEVCLASS_HIDRAW);
+    }
+
+    if (device)
+        udev_device_ref(dev);
+    else
+        WARN("Ignoring device %s with subsystem %s\n", debugstr_a(devnode), subsystem);
+
     HeapFree(GetProcessHeap(), 0, serial);
 }
 
diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c
index 43de25a..9c0f5aa 100644
--- a/dlls/winebus.sys/main.c
+++ b/dlls/winebus.sys/main.c
@@ -26,14 +26,164 @@
 #define WIN32_NO_STATUS
 #include "windef.h"
 #include "winbase.h"
+#include "winuser.h"
 #include "winternl.h"
+#include "winreg.h"
+#include "setupapi.h"
 #include "ddk/wdm.h"
 #include "wine/debug.h"
+#include "wine/unicode.h"
+#include "wine/list.h"
 
 #include "bus.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(plugplay);
 
+struct pnp_device
+{
+    struct list entry;
+    DEVICE_OBJECT *device;
+};
+
+struct device_extension
+{
+    void *native;  /* Must be the first member of the structure */
+
+    WORD vid, pid;
+    DWORD uid, version, index;
+    BOOL is_gamepad;
+    WCHAR *serial;
+    const WCHAR *busid;  /* Expected to be a static constant */
+};
+
+static CRITICAL_SECTION device_list_cs;
+static CRITICAL_SECTION_DEBUG critsect_debug =
+{
+    0, 0, &device_list_cs,
+    { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
+      0, 0, { (DWORD_PTR)(__FILE__ ": device_list_cs") }
+};
+static CRITICAL_SECTION device_list_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
+
+static struct list pnp_devset = LIST_INIT(pnp_devset);
+
+static const WCHAR zero_serialW[]= {'0','0','0','0',0};
+static const WCHAR imW[] = {'I','M',0};
+static const WCHAR igW[] = {'I','G',0};
+
+static inline WCHAR *strdupW(const WCHAR *src)
+{
+    WCHAR *dst;
+    if (!src) return NULL;
+    dst = HeapAlloc(GetProcessHeap(), 0, (strlenW(src) + 1)*sizeof(WCHAR));
+    if (dst) strcpyW(dst, src);
+    return dst;
+}
+
+static DWORD get_vidpid_index(WORD vid, WORD pid)
+{
+    struct pnp_device *ptr;
+    DWORD index = 1;
+
+    LIST_FOR_EACH_ENTRY(ptr, &pnp_devset, struct pnp_device, entry)
+    {
+        struct device_extension *ext = (struct device_extension *)ptr->device->DeviceExtension;
+        if (ext->vid == vid && ext->pid == pid)
+            index = max(ext->index + 1, index);
+    }
+
+    return index;
+}
+
+static WCHAR *get_instance_id(DEVICE_OBJECT *device)
+{
+    static const WCHAR formatW[] =  {'%','s','\\','V','i','d','_','%','0','4','x','&', 'P','i','d','_','%','0','4','x','&',
+                                     '%','s','_','%','i','\\','%','i','&','%','s','&','%','x',0};
+    struct device_extension *ext = (struct device_extension *)device->DeviceExtension;
+    const WCHAR *serial = ext->serial ? ext->serial : zero_serialW;
+    DWORD len = strlenW(ext->busid) + strlenW(serial) + 64;
+    WCHAR *dst;
+
+    if ((dst = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))))
+        sprintfW(dst, formatW, ext->busid, ext->vid, ext->pid, ext->is_gamepad ? igW : imW,
+                 ext->index, ext->version, serial, ext->uid);
+
+    return dst;
+}
+
+DEVICE_OBJECT *bus_create_hid_device(DRIVER_OBJECT *driver, const WCHAR *busidW, void *native, WORD vid,
+                                     WORD pid, DWORD version, DWORD uid, const WCHAR *serialW, BOOL is_gamepad,
+                                     const GUID *class)
+{
+    static const WCHAR device_name_fmtW[] = {'\\','D','e','v','i','c','e','\\','%','s','#','%','p',0};
+    struct device_extension *ext;
+    struct pnp_device *pnp_dev;
+    DEVICE_OBJECT *device;
+    UNICODE_STRING nameW;
+    WCHAR dev_name[256];
+    HDEVINFO devinfo;
+    NTSTATUS status;
+
+    TRACE("(%p, %s, %p, %04x, %04x, %u, %u, %s, %u, %s)\n", driver, debugstr_w(busidW), native,
+          vid, pid, version, uid, debugstr_w(serialW), is_gamepad, debugstr_guid(class));
+
+    if (!(pnp_dev = HeapAlloc(GetProcessHeap(), 0, sizeof(*pnp_dev))))
+        return NULL;
+
+    sprintfW(dev_name, device_name_fmtW, busidW, native);
+    RtlInitUnicodeString(&nameW, dev_name);
+    status = IoCreateDevice(driver, sizeof(*ext), &nameW, 0, 0, FALSE, &device);
+    if (status)
+    {
+        FIXME("failed to create device error %x\n", status);
+        HeapFree(GetProcessHeap(), 0, pnp_dev);
+        return NULL;
+    }
+
+    EnterCriticalSection(&device_list_cs);
+
+    /* fill out device_extension struct */
+    ext = (struct device_extension *)device->DeviceExtension;
+    ext->native     = native;
+    ext->vid        = vid;
+    ext->pid        = pid;
+    ext->uid        = uid;
+    ext->version    = version;
+    ext->index      = get_vidpid_index(vid, pid);
+    ext->is_gamepad = is_gamepad;
+    ext->serial     = strdupW(serialW);
+    ext->busid      = busidW;
+
+    /* add to list of pnp devices */
+    pnp_dev->device = device;
+    list_add_tail(&pnp_devset, &pnp_dev->entry);
+
+    LeaveCriticalSection(&device_list_cs);
+
+    devinfo = SetupDiGetClassDevsW(class, NULL, NULL, DIGCF_DEVICEINTERFACE);
+    if (devinfo)
+    {
+        SP_DEVINFO_DATA data;
+        WCHAR *instance;
+
+        data.cbSize = sizeof(data);
+        if (!(instance = get_instance_id(device)))
+            ERR("failed to generate instance id\n");
+        else if (!SetupDiCreateDeviceInfoW(devinfo, instance, class, NULL, NULL, DICD_INHERIT_CLASSDRVS, &data))
+            ERR("failed to create device info: %x\n", GetLastError());
+        else if (!SetupDiRegisterDeviceInfo(devinfo, &data, 0, NULL, NULL, NULL))
+            ERR("failed to register device info: %x\n", GetLastError());
+
+        HeapFree(GetProcessHeap(), 0, instance);
+        SetupDiDestroyDeviceInfoList(devinfo);
+    }
+    else
+        ERR("failed to get ClassDevs: %x\n", GetLastError());
+
+    IoInvalidateDeviceRelations(device, BusRelations);
+    return device;
+}
+
 NTSTATUS WINAPI common_pnp_dispatch(DEVICE_OBJECT *device, IRP *irp)
 {
     NTSTATUS status = irp->IoStatus.u.Status;
-- 
2.9.0



More information about the wine-patches mailing list