[PATCH 1/5] winebus.sys: Handle linux input event device via udev

Sebastian Lackner sebastian at fds-team.de
Mon Feb 27 10:00:22 CST 2017


On 27.02.2017 16:09, Aric Stewart wrote:
> Signed-off-by: Aric Stewart <aric at codeweavers.com>
> ---
>  dlls/winebus.sys/bus.h      |   3 +
>  dlls/winebus.sys/bus_udev.c | 150 ++++++++++++++++++++++++++++++++++++++++++--
>  dlls/winebus.sys/main.c     |  26 ++++++++
>  3 files changed, 173 insertions(+), 6 deletions(-)
> 

I assume this is meant as a fallback, if we don't have access to hidraw, right?
If you plan to use this as a replacement for dinput, it would be better to
implement it without fixed dependency on udev.

> 
> 
> 0001-winebus.sys-Handle-linux-input-event-device-via-udev.txt
> 
> 
> diff --git a/dlls/winebus.sys/bus.h b/dlls/winebus.sys/bus.h
> index effbd4afd0..fed35e8c1e 100644
> --- a/dlls/winebus.sys/bus.h
> +++ b/dlls/winebus.sys/bus.h
> @@ -43,3 +43,6 @@ DEVICE_OBJECT *bus_find_hid_device(const platform_vtbl *vtbl, void *platform_dev
>  void bus_remove_hid_device(DEVICE_OBJECT *device) DECLSPEC_HIDDEN;
>  NTSTATUS WINAPI hid_internal_dispatch(DEVICE_OBJECT *device, IRP *irp) DECLSPEC_HIDDEN;
>  void process_hid_report(DEVICE_OBJECT *device, BYTE *report, DWORD length) DECLSPEC_HIDDEN;
> +
> +/* General Bus Functions */
> +DWORD check_bus_option(UNICODE_STRING *registry_path, const UNICODE_STRING *option) DECLSPEC_HIDDEN;
> diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c
> index 415bd3db08..2dcc7fb442 100644
> --- a/dlls/winebus.sys/bus_udev.c
> +++ b/dlls/winebus.sys/bus_udev.c
> @@ -42,6 +42,14 @@
>  # include <sys/ioctl.h>
>  #endif
>  
> +#ifdef HAVE_LINUX_INPUT_H
> +# include <linux/input.h>
> +# undef SW_MAX
> +# if defined(EVIOCGBIT) && defined(EV_ABS) && defined(BTN_PINKIE)
> +#  define HAS_PROPER_INPUT_HEADER
> +# endif
> +#endif
> +
>  #define NONAMELESSUNION
>  
>  #include "ntstatus.h"
> @@ -65,11 +73,15 @@ WINE_DECLARE_DEBUG_CHANNEL(hid_report);
>  
>  static struct udev *udev_context = NULL;
>  static DRIVER_OBJECT *udev_driver_obj = NULL;
> +static DWORD disable_hidraw = 0;
> +static DWORD disable_input = 0;
>  
>  static const WCHAR hidraw_busidW[] = {'H','I','D','R','A','W',0};
> +static const WCHAR lnxev_busidW[] = {'L','N','X','E','V',0};
>  
>  #include "initguid.h"
>  DEFINE_GUID(GUID_DEVCLASS_HIDRAW, 0x3def44ad,0x242e,0x46e5,0x82,0x6d,0x70,0x72,0x13,0xf3,0xaa,0x81);
> +DEFINE_GUID(GUID_DEVCLASS_LINUXEVENT, 0x1b932c0d,0xfea7,0x42cd,0x8e,0xaa,0x0e,0x48,0x79,0xb6,0x9e,0xaa);
>  
>  struct platform_private
>  {
> @@ -392,6 +404,52 @@ static const platform_vtbl hidraw_vtbl =
>      hidraw_set_feature_report,
>  };
>  
> +#ifdef HAS_PROPER_INPUT_HEADER
> +
> +static NTSTATUS lnxev_get_reportdescriptor(DEVICE_OBJECT *device, BYTE *buffer, DWORD length, DWORD *out_length)
> +{
> +    return STATUS_NOT_IMPLEMENTED;
> +}
> +
> +static NTSTATUS lnxev_get_string(DEVICE_OBJECT *device, DWORD index, WCHAR *buffer, DWORD length)
> +{
> +    return STATUS_NOT_IMPLEMENTED;
> +}
> +
> +static NTSTATUS lnxev_begin_report_processing(DEVICE_OBJECT *device)
> +{
> +    return STATUS_NOT_IMPLEMENTED;
> +}
> +
> +static NTSTATUS lnxev_set_output_report(DEVICE_OBJECT *device, UCHAR id, BYTE *report, DWORD length, ULONG_PTR *written)
> +{
> +    *written = 0;
> +    return STATUS_NOT_IMPLEMENTED;
> +}
> +
> +static NTSTATUS lnxev_get_feature_report(DEVICE_OBJECT *device, UCHAR id, BYTE *report, DWORD length, ULONG_PTR *read)
> +{
> +    *read = 0;
> +    return STATUS_NOT_IMPLEMENTED;
> +}
> +
> +static NTSTATUS lnxev_set_feature_report(DEVICE_OBJECT *device, UCHAR id, BYTE *report, DWORD length, ULONG_PTR *written)
> +{
> +    *written = 0;
> +    return STATUS_NOT_IMPLEMENTED;
> +}
> +
> +static const platform_vtbl lnxev_vtbl = {
> +    compare_platform_device,
> +    lnxev_get_reportdescriptor,
> +    lnxev_get_string,
> +    lnxev_begin_report_processing,
> +    lnxev_set_output_report,
> +    lnxev_get_feature_report,
> +    lnxev_set_feature_report,
> +};
> +#endif
> +
>  static void try_add_device(struct udev_device *dev)
>  {
>      DWORD vid = 0, pid = 0, version = 0;
> @@ -400,6 +458,7 @@ static void try_add_device(struct udev_device *dev)
>      const char *subsystem;
>      const char *devnode;
>      WCHAR *serial = NULL;
> +    const char* gamepad = NULL;
>      int fd;
>  
>      if (!(devnode = udev_device_get_devnode(dev)))
> @@ -419,6 +478,26 @@ static void try_add_device(struct udev_device *dev)
>          version = get_sysattr_dword(usbdev, "version", 10);
>          serial  = get_sysattr_string(usbdev, "serial");
>      }
> +#ifdef HAS_PROPER_INPUT_HEADER
> +    else
> +    {
> +        struct input_id device_id = {0};
> +        char device_uid[255] = {0};
> +
> +        if (ioctl(fd, EVIOCGID, &device_id) == -1)
> +            WARN("ioctl(EVIOCGID) failed: %d %s\n", errno, strerror(errno));
> +        if (ioctl(fd, EVIOCGUNIQ(254), device_uid) && device_uid[0])
> +            MultiByteToWideChar(CP_UNIXCP, 0, device_uid, -1, serial, 255);

I don't think this will work as expected. serial is a NULL pointer, and the check
if ioctl() was successful is also done wrong. On error a value < 0 is returned.

> +
> +        gamepad = udev_device_get_property_value(dev, "ID_INPUT_JOYSTICK");
> +        vid = device_id.vendor;
> +        pid = device_id.product;
> +        version = device_id.version;
> +    }
> +#else
> +    else
> +        WARN("Could not get device to query VID, PID, Version and Serial\n");
> +#endif
>  
>      TRACE("Found udev device %s (vid %04x, pid %04x, version %u, serial %s)\n",
>            debugstr_a(devnode), vid, pid, version, debugstr_w(serial));
> @@ -429,6 +508,12 @@ static void try_add_device(struct udev_device *dev)
>          device = bus_create_hid_device(udev_driver_obj, hidraw_busidW, vid, pid, version, 0, serial, FALSE,
>                                         &GUID_DEVCLASS_HIDRAW, &hidraw_vtbl, sizeof(struct platform_private));
>      }
> +#ifdef HAS_PROPER_INPUT_HEADER
> +    else if (strcmp(subsystem, "input") == 0)
> +    {
> +        device = bus_create_hid_device(udev_driver_obj, lnxev_busidW, vid, pid, version, 0, serial, (gamepad != NULL), &GUID_DEVCLASS_LINUXEVENT, &lnxev_vtbl, sizeof(struct platform_private));

Some linebreaks wouldn't hurt.

> +    }
> +#endif
>  
>      if (device)
>      {
> @@ -448,8 +533,17 @@ static void try_add_device(struct udev_device *dev)
>  
>  static void try_remove_device(struct udev_device *dev)
>  {
> -    DEVICE_OBJECT *device = bus_find_hid_device(&hidraw_vtbl, dev);
> -    struct platform_private *private;
> +    DEVICE_OBJECT *device = NULL;
> +    const char* subsystem;
> +    struct platform_private* private;
> +
> +    subsystem = udev_device_get_subsystem(dev);
> +    if (strcmp(subsystem, "hidraw") == 0)
> +        device = bus_find_hid_device(&hidraw_vtbl, dev);
> +#ifdef HAS_PROPER_INPUT_HEADER
> +    else if (strcmp(subsystem, "input") == 0)
> +        device = bus_find_hid_device(&lnxev_vtbl, dev);
> +#endif

It might be easier to just check both after each other, like:

device = bus_find_hid_device(...);
if (!device) device = bus_find_hid_device(...);

>      if (!device) return;
>  
>      IoInvalidateDeviceRelations(device, RemovalRelations);
> @@ -483,8 +577,18 @@ static void build_initial_deviceset(void)
>          return;
>      }
>  
> -    if (udev_enumerate_add_match_subsystem(enumerate, "hidraw") < 0)
> -        WARN("Failed to add subsystem 'hidraw' to enumeration\n");
> +    if (!disable_hidraw)
> +        if (udev_enumerate_add_match_subsystem(enumerate, "hidraw") < 0)
> +            WARN("Failed to add subsystem 'hidraw' to enumeration\n");
> +#ifdef HAS_PROPER_INPUT_HEADER
> +    if (!disable_input)
> +    {
> +        if (udev_enumerate_add_match_subsystem(enumerate, "event") < 0)
> +            WARN("Failed to add subsystem 'event' to enumeration\n");

Why do you add this match when results are not processed anywhere?

> +        if (udev_enumerate_add_match_subsystem(enumerate, "input") < 0)
> +            WARN("Failed to add subsystem 'input' to enumeration\n");
> +    }
> +#endif
>  
>      if (udev_enumerate_scan_devices(enumerate) < 0)
>          WARN("Enumeration scan failed\n");
> @@ -509,6 +613,7 @@ static void build_initial_deviceset(void)
>  static struct udev_monitor *create_monitor(struct pollfd *pfd)
>  {
>      struct udev_monitor *monitor;
> +    int systems = 0;
>  
>      monitor = udev_monitor_new_from_netlink(udev_context, "udev");
>      if (!monitor)
> @@ -517,8 +622,27 @@ static struct udev_monitor *create_monitor(struct pollfd *pfd)
>          return NULL;
>      }
>  
> -    if (udev_monitor_filter_add_match_subsystem_devtype(monitor, "hidraw", NULL) < 0)
> -        WARN("Failed to add subsystem 'hidraw' to monitor\n");
> +    if (!disable_hidraw)
> +    {
> +        if (udev_monitor_filter_add_match_subsystem_devtype(monitor, "hidraw", NULL) < 0)
> +            WARN("Failed to add 'hidraw' subsystem to monitor\n");
> +        else
> +            systems++;
> +    }
> +#ifdef HAS_PROPER_INPUT_HEADER
> +    if (!disable_input)
> +    {
> +        if (udev_monitor_filter_add_match_subsystem_devtype(monitor, "input", NULL) < 0)
> +            WARN("Failed to add 'input' subsystem to monitor\n");
> +        else
> +            systems++;
> +    }
> +#endif
> +    if (systems == 0)
> +    {
> +        WARN("No subsystems added to monitor\n");
> +        goto error;
> +    }
>  
>      if (udev_monitor_enable_receiving(monitor) < 0)
>          goto error;
> @@ -589,6 +713,10 @@ NTSTATUS WINAPI udev_driver_init(DRIVER_OBJECT *driver, UNICODE_STRING *registry
>  {
>      HANDLE events[2];
>      DWORD result;
> +    static const WCHAR hidraw_disabledW[] = {'D','i','s','a','b','l','e',' ','h','i','d','r','a','w',0};
> +    static const UNICODE_STRING hidraw_disabled = {sizeof(hidraw_disabledW) - sizeof(WCHAR), sizeof(hidraw_disabledW), (WCHAR*)hidraw_disabledW};
> +    static const WCHAR input_disabledW[] = {'D','i','s','a','b','l','e',' ','i','n','p','u','t',0};
> +    static const UNICODE_STRING input_disabled = {sizeof(input_disabledW) - sizeof(WCHAR), sizeof(input_disabledW), (WCHAR*)input_disabledW};
>  
>      TRACE("(%p, %s)\n", driver, debugstr_w(registry_path->Buffer));
>  
> @@ -602,6 +730,16 @@ NTSTATUS WINAPI udev_driver_init(DRIVER_OBJECT *driver, UNICODE_STRING *registry
>      driver->MajorFunction[IRP_MJ_PNP] = common_pnp_dispatch;
>      driver->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = hid_internal_dispatch;
>  
> +    disable_hidraw = check_bus_option(registry_path, &hidraw_disabled);
> +    if (disable_hidraw)
> +        TRACE("UDEV hidraw devices disabled in registry\n");
> +
> +#ifdef HAS_PROPER_INPUT_HEADER
> +    disable_input = check_bus_option(registry_path, &input_disabled);
> +    if (disable_input)
> +        TRACE("UDEV input devices disabled in registry\n");
> +#endif

Does it hurt when both hidraw and input event devices are found? If yes, it
would be better to filter duplicates somewhere else, instead of using registry
keys.

> +
>      if (!(events[0] = CreateEventW(NULL, TRUE, FALSE, NULL)))
>          goto error;
>      if (!(events[1] = CreateThread(NULL, 0, deviceloop_thread, events[0], 0, NULL)))
> diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c
> index 978540bb17..0388f0263c 100644
> --- a/dlls/winebus.sys/main.c
> +++ b/dlls/winebus.sys/main.c
> @@ -625,6 +625,32 @@ void process_hid_report(DEVICE_OBJECT *device, BYTE *report, DWORD length)
>      LeaveCriticalSection(&ext->report_cs);
>  }
>  
> +DWORD check_bus_option(UNICODE_STRING *registry_path, const UNICODE_STRING *option)
> +{
> +    OBJECT_ATTRIBUTES attr;
> +    HANDLE key;
> +    DWORD output = 0;
> +
> +    InitializeObjectAttributes(&attr, registry_path, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
> +    if (NtOpenKey(&key, KEY_ALL_ACCESS, &attr) == STATUS_SUCCESS)
> +    {
> +        DWORD size;
> +        char buffer[FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[sizeof(DWORD)])];
> +
> +        KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION*)buffer;
> +
> +        if (NtQueryValueKey(key, option, KeyValuePartialInformation, info, sizeof(buffer), &size) == STATUS_SUCCESS)
> +        {
> +            if (info->Type == REG_DWORD)
> +                output = *(DWORD*)info->Data;
> +        }
> +
> +        NtClose(key);
> +    }
> +
> +    return output;
> +}
> +
>  NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path )
>  {
>      static const WCHAR udevW[] = {'\\','D','r','i','v','e','r','\\','U','D','E','V',0};
> 
> 
> 




More information about the wine-devel mailing list