From: Aric Stewart Subject: [PATCH v2 5/8] wineplugplay.sys: Build initial hidraw device set Message-Id: Date: Mon, 22 Aug 2016 09:20:01 -0500 Includes implementation of common HID_PNP_CreateDevice and configuration changes for linux udev Signed-off-by: Aric Stewart --- configure | 117 +++++++++++++++++++++++ configure.ac | 14 +++ dlls/wineplugplay.sys/Makefile.in | 3 +- dlls/wineplugplay.sys/bus_hid_common.c | 163 +++++++++++++++++++++++++++++++++ dlls/wineplugplay.sys/bus_hidraw.c | 81 ++++++++++++++++ dlls/wineplugplay.sys/pnp.h | 4 + include/config.h.in | 6 ++ 7 files changed, 387 insertions(+), 1 deletion(-) diff --git a/configure b/configure index c60bce8..069c530 100755 --- a/configure +++ b/configure @@ -652,6 +652,8 @@ FONTCONFIG_CFLAGS CUPS_CFLAGS CAPI20_LIBS CAPI20_CFLAGS +UDEV_LIBS +UDEV_CFLAGS OSS4_CFLAGS ALSA_LIBS GSTREAMER_LIBS @@ -1579,6 +1581,8 @@ GSTREAMER_CFLAGS GSTREAMER_LIBS CAPI20_CFLAGS CAPI20_LIBS +UDEV_LIBS +UDEV_CFLAGS FONTCONFIG_CFLAGS FONTCONFIG_LIBS JPEG_CFLAGS @@ -2263,6 +2267,7 @@ Optional Packages: --without-sane do not use SANE (scanner support) --without-tiff do not use TIFF --without-v4l do not use v4l1 (v4l support) + --without-udev do not use udev --without-xcomposite do not use the Xcomposite extension --without-xcursor do not use the Xcursor extension --without-xinerama do not use Xinerama (multi-monitor support) @@ -2332,6 +2337,8 @@ Some influential environment variables: CAPI20_CFLAGS C compiler flags for capi20, overriding pkg-config CAPI20_LIBS Linker flags for capi20, overriding pkg-config + UDEV_CFLAGS C compiler flags for udev, overriding pkg-config + UDEV_LIBS Linker flags for udev, overriding pkg-config FONTCONFIG_CFLAGS C compiler flags for fontconfig, overriding pkg-config FONTCONFIG_LIBS @@ -3533,6 +3540,12 @@ if test "${with_tiff+set}" = set; then : fi +# Check whether --with-udev was given. +if test "${with_udev+set}" = set; then : + withval=$with_udev; +fi + + # Check whether --with-v4l was given. if test "${with_v4l+set}" = set; then : withval=$with_v4l; @@ -12889,6 +12902,108 @@ esac enable_wineoss_drv=${enable_wineoss_drv:-no} fi +if test "x$with_udev" != "xno" +then + if ${UDEV_CFLAGS:+false} :; then : + if ${PKG_CONFIG+:} false; then : + UDEV_CFLAGS=`$PKG_CONFIG --cflags udev 2>/dev/null` +fi +fi + +if ${UDEV_LIBS:+false} :; then : + if ${PKG_CONFIG+:} false; then : + UDEV_LIBS=`$PKG_CONFIG --libs udev 2>/dev/null` +fi +fi + + +$as_echo "$as_me:${as_lineno-$LINENO}: udev cflags: $UDEV_CFLAGS" >&5 +$as_echo "$as_me:${as_lineno-$LINENO}: udev libs: $UDEV_LIBS" >&5 +ac_save_CPPFLAGS=$CPPFLAGS +CPPFLAGS="$CPPFLAGS $UDEV_CFLAGS" +for ac_header in libudev.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "libudev.h" "ac_cv_header_libudev_h" "$ac_includes_default" +if test "x$ac_cv_header_libudev_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBUDEV_H 1 +_ACEOF + +fi + +done + + if test "$ac_cv_header_libudev_h" = "yes" -a "$ac_cv_header_libudev_h" = "yes" + then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -ludev" >&5 +$as_echo_n "checking for -ludev... " >&6; } +if ${ac_cv_lib_soname_udev+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_soname_save_LIBS=$LIBS +LIBS="-ludev $LIBS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char udev_new (); +int +main () +{ +return udev_new (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + case "$LIBEXT" in + dll) ac_cv_lib_soname_udev=`$ac_cv_path_LDD conftest.exe | grep "udev" | sed -e "s/dll.*/dll/"';2,$d'` ;; + dylib) ac_cv_lib_soname_udev=`$OTOOL -L conftest$ac_exeext | grep "libudev\\.[0-9A-Za-z.]*dylib" | sed -e "s/^.*\/\(libudev\.[0-9A-Za-z.]*dylib\).*$/\1/"';2,$d'` ;; + *) ac_cv_lib_soname_udev=`$READELF -d conftest$ac_exeext | grep "NEEDED.*libudev\\.$LIBEXT" | sed -e "s/^.*\\[\\(libudev\\.$LIBEXT[^ ]*\\)\\].*$/\1/"';2,$d'` + if ${ac_cv_lib_soname_udev:+false} :; then : + ac_cv_lib_soname_udev=`$LDD conftest$ac_exeext | grep "libudev\\.$LIBEXT" | sed -e "s/^.*\(libudev\.$LIBEXT[^ ]*\).*$/\1/"';2,$d'` +fi ;; + esac +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LIBS=$ac_check_soname_save_LIBS +fi +if ${ac_cv_lib_soname_udev:+false} :; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5 +$as_echo "not found" >&6; } + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_udev" >&5 +$as_echo "$ac_cv_lib_soname_udev" >&6; } + +cat >>confdefs.h <<_ACEOF +#define SONAME_LIBUDEV "$ac_cv_lib_soname_udev" +_ACEOF + + UDEV_LIBS="-ludev" + +fi + fi +CPPFLAGS=$ac_save_CPPFLAGS +test -z "$UDEV_CFLAGS" || UDEV_CFLAGS=`echo " $UDEV_CFLAGS" | sed 's/ -I\([^/]\)/ -I\$(top_builddir)\/\1/g'` +test -z "$UDEV_LIBS" || UDEV_LIBS=`echo " $UDEV_LIBS" | sed 's/ -L\([^/]\)/ -L\$(top_builddir)\/\1/g'` + +fi +if test "x$ac_cv_lib_soname_udev" = "x"; then : + case "x$with_udev" in + x) as_fn_append wine_notices "|libudev ${notice_platform}development files not found, hidraw HID will not be supported." ;; + xno) ;; + *) as_fn_error $? "libudev ${notice_platform}development files not found, hidraw HID will not be supported. +This is an error since --with-udev was requested." "$LINENO" 5 ;; +esac +fi + if test "x$with_capi" != "xno" then if ${CAPI20_CFLAGS:+false} :; then : @@ -17339,6 +17454,8 @@ ALSA_LIBS = $ALSA_LIBS OSS4_CFLAGS = $OSS4_CFLAGS CAPI20_CFLAGS = $CAPI20_CFLAGS CAPI20_LIBS = $CAPI20_LIBS +UDEV_CFLAGS = $UDEV_CFLAGS +UDEV_LIBS = $UDEV_LIBS CUPS_CFLAGS = $CUPS_CFLAGS FONTCONFIG_CFLAGS = $FONTCONFIG_CFLAGS FONTCONFIG_LIBS = $FONTCONFIG_LIBS diff --git a/configure.ac b/configure.ac index 074f14b..1bb751d 100644 --- a/configure.ac +++ b/configure.ac @@ -103,6 +103,7 @@ AC_ARG_WITH(zlib, AS_HELP_STRING([--without-zlib],[do not use Zlib (data co AC_ARG_WITH(wine-tools,AS_HELP_STRING([--with-wine-tools=DIR],[use Wine tools from directory DIR])) AC_ARG_WITH(wine64, AS_HELP_STRING([--with-wine64=DIR],[use the 64-bit Wine in DIR for a Wow64 build])) +AC_ARG_WITH(udev, AS_HELP_STRING([--without-udev],[do not use udev])) AC_CANONICAL_HOST @@ -1519,6 +1520,19 @@ WINE_NOTICE_WITH(oss,[test "x$ac_cv_member_oss_sysinfo_numaudioengines" != xyes] [OSS sound system found but too old (OSSv4 needed), OSS won't be supported.], [enable_wineoss_drv]) +dnl **** Check for libudev**** +if test "x$with_udev" != "xno" +then + WINE_PACKAGE_FLAGS(UDEV,[udev],,,, + [AC_CHECK_HEADERS([libudev.h]) + if test "$ac_cv_header_libudev_h" = "yes" -a "$ac_cv_header_libudev_h" = "yes" + then + WINE_CHECK_SONAME(udev,udev_new,AC_SUBST(UDEV_LIBS,"-ludev"),,[$UDEV_LIBS]) + fi]) +fi +WINE_NOTICE_WITH(udev,[test "x$ac_cv_lib_soname_udev" = "x"], + [libudev ${notice_platform}development files not found, hidraw HID not supported.]) + dnl **** Check for capi4linux **** if test "x$with_capi" != "xno" then diff --git a/dlls/wineplugplay.sys/Makefile.in b/dlls/wineplugplay.sys/Makefile.in index 882d8ac..084790c 100644 --- a/dlls/wineplugplay.sys/Makefile.in +++ b/dlls/wineplugplay.sys/Makefile.in @@ -1,5 +1,6 @@ MODULE = wineplugplay.sys -IMPORTS = ntoskrnl advapi32 +IMPORTS = ntoskrnl advapi32 setupapi +EXTRALIBS = $(UDEV_LIBS) EXTRADLLFLAGS = -Wb,--subsystem,native C_SRCS = \ diff --git a/dlls/wineplugplay.sys/bus_hid_common.c b/dlls/wineplugplay.sys/bus_hid_common.c index 6015dfd..d9377a4 100644 --- a/dlls/wineplugplay.sys/bus_hid_common.c +++ b/dlls/wineplugplay.sys/bus_hid_common.c @@ -17,6 +17,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#include "config.h" #define NONAMELESSUNION #include #include "ntstatus.h" @@ -39,6 +40,128 @@ WINE_DEFAULT_DEBUG_CHANNEL(plugplay); +static const WCHAR hwidfmtW[] = {'%','s','\\','V','i','d','_','%','0','4','x','&','P','i','d','_','%','0','4','x','&','%','s','\\','%','i','&','%','s','&','%','p',0}; +static const WCHAR im_fmtW[] = {'I','M','_','%','i',0}; +static const WCHAR ig_fmtW[] = {'I','G','_','%','i',0}; + +typedef struct _pnp_device { + struct list entry; + + void *device; + DWORD extension; +} pnp_device; + +typedef struct { + LPVOID native; /* Must be the first member of the structure */ + + DWORD vid, pid, version, uid; + BOOL isGamepad; + WCHAR serial[255]; + WCHAR const *busidW; /* Expected to be a static constant */ +} extension; + +static struct list pnp_devset = LIST_INIT(pnp_devset); + +static void add_device(void* device, DWORD ext) +{ + pnp_device *pnp_dev; + + pnp_dev = HeapAlloc(GetProcessHeap(), 0, sizeof(*pnp_dev)); + pnp_dev->device = device; + pnp_dev->extension = ext; + list_add_tail(&pnp_devset, &pnp_dev->entry); +} + +static INT get_vidpid_index(DEVICE_OBJECT *device, DWORD vidpid) +{ + pnp_device *ptr; + int index = 0; + LIST_FOR_EACH_ENTRY(ptr, &pnp_devset, pnp_device, entry) + { + if (ptr->extension == vidpid) + index++; + if (ptr->device == device) + return index; + } + ERR("Device not found in list\n"); + return 0; +} + +static int get_instance_id(DEVICE_OBJECT *device, WCHAR* id) +{ + int idx; + WCHAR index_string[256]; + + extension *ext = (extension*)device->DeviceExtension; + + idx = get_vidpid_index(device, MAKELONG(ext->vid,ext->pid)); + + if (ext->isGamepad) + sprintfW(index_string, ig_fmtW, idx); + else + sprintfW(index_string, im_fmtW, idx); + + return sprintfW(id, hwidfmtW, ext->busidW, ext->vid, ext->pid, index_string, ext->version, ext->serial, ext->uid); +} + + +DEVICE_OBJECT *HID_PNP_CreateDevice(DRIVER_OBJECT *driver, const WCHAR *busidW, LPVOID native, WORD vid, WORD pid, DWORD version, DWORD uid, const WCHAR *serial, BOOL isGamepad, const GUID *class) +{ + NTSTATUS status; + DEVICE_OBJECT *device; + static const WCHAR device_name_fmtW[] = {'\\','D','e','v','i','c','e', + '\\','%','s','#','%','p',0}; + static const WCHAR wZeroSerial[]= {'0','0','0','0',0}; + WCHAR dev_name[255]; + UNICODE_STRING nameW; + HDEVINFO devinfo; + extension* ext; + + sprintfW(dev_name, device_name_fmtW, busidW, native); + RtlInitUnicodeString(&nameW, dev_name); + status = IoCreateDevice(driver, sizeof(extension), &nameW, 0, 0, FALSE, &device); + if (status) + { + FIXME( "failed to create device error %x\n", status ); + return NULL; + } + + ext = (extension*)device->DeviceExtension; + ext->native = native; + ext->vid = vid; + ext->pid = pid; + ext->uid = uid; + ext->isGamepad = isGamepad; + ext->version = version; + ext->busidW = busidW; + if (serial) + lstrcpyW(ext->serial, serial); + else + lstrcpyW(ext->serial, wZeroSerial); + + add_device(device, MAKELONG(vid, pid)); + + devinfo = SetupDiGetClassDevsW(class, NULL, NULL, DIGCF_DEVICEINTERFACE); + if (!devinfo) + FIXME( "failed to get ClassDevs %x\n", GetLastError()); + else + { + SP_DEVINFO_DATA Data; + WCHAR id[MAX_DEVICE_ID_LEN]; + + get_instance_id(device, id); + Data.cbSize = sizeof(Data); + SetupDiCreateDeviceInfoW(devinfo, id, class, NULL, NULL, DICD_INHERIT_CLASSDRVS, &Data); + if (!SetupDiRegisterDeviceInfo(devinfo, &Data, 0, NULL, NULL, NULL )) + FIXME( "failed to Register Device Info %x\n", GetLastError()); + SetupDiDestroyDeviceInfoList(devinfo); + } + + PNP_IoInvalidateDeviceRelations(device, BusRelations); + + return device; +} + NTSTATUS WINAPI HID_PNP_Dispatch(DEVICE_OBJECT *device, IRP *irp) { NTSTATUS rc = STATUS_NOT_SUPPORTED; @@ -88,3 +211,43 @@ DWORD HID_check_BusDisabled(UNICODE_STRING *RegistryPath) return disabled; } + +#ifdef HAVE_LIBUDEV_H + +# include + +NTSTATUS UDEV_BuildInitialDeviceSet(void *udev, const char **subsystems, void (*TryAddDevice)(void *udev_device)) +{ + struct udev_enumerate *enumerate; + struct udev_list_entry *udevices, *dev_list_entry; + const char **p; + + enumerate = udev_enumerate_new(udev); + p = subsystems; + while (*p) + { + udev_enumerate_add_match_subsystem(enumerate, *p); + p++; + } + udev_enumerate_scan_devices(enumerate); + udevices = udev_enumerate_get_list_entry(enumerate); + + udev_list_entry_foreach(dev_list_entry, udevices) { + const char *path; + struct udev_device *dev; + + path = udev_list_entry_get_name(dev_list_entry); + dev = udev_device_new_from_syspath(udev, path); + TryAddDevice(dev); + } + + udev_enumerate_unref(enumerate); + return STATUS_SUCCESS; +} +#else +NTSTATUS UDEV_BuildInitialDeviceSet(void *udev, const char **subsystems, void (*TryAddDevice)(void *udev_device)) +{ + ERR("No UDEV system present."); + return STATUS_NOT_IMPLEMENTED; +} +#endif diff --git a/dlls/wineplugplay.sys/bus_hidraw.c b/dlls/wineplugplay.sys/bus_hidraw.c index a6ce776..5193236 100644 --- a/dlls/wineplugplay.sys/bus_hidraw.c +++ b/dlls/wineplugplay.sys/bus_hidraw.c @@ -21,6 +21,10 @@ #include "wine/port.h" #include +#ifdef HAVE_LINUX_HIDRAW_H +# include +#endif + #ifdef HAVE_SELECT # include #endif @@ -29,6 +33,10 @@ # include #endif +#ifdef HAVE_LIBUDEV_H +# include +#endif + #include #define NONAMELESSUNION @@ -49,11 +57,82 @@ WINE_DEFAULT_DEBUG_CHANNEL(plugplay); static DRIVER_OBJECT *hr_driver_obj = NULL; +#include "initguid.h" +DEFINE_GUID(GUID_DEVCLASS_HIDRAW, 0x3def44ad,0x242e,0x46e5,0x82,0x6d,0x70,0x72,0x13,0xf3,0xaa,0x81); + +static const WCHAR busidW[] = {'H','I','D','R','A','W',0}; + +/* udev based PNP support */ +static struct udev *udev = NULL; + +static void TryAddDevice(void *dev) +{ + const char *devnode; + int fd; + DWORD vid=0, pid=0, uid=0, version=0; + WCHAR serial[255] = {'0','0','0','0','0',0}; + DEVICE_OBJECT *device; + struct udev_device *usbdev; + + devnode = udev_device_get_devnode(dev); + if ((fd = open(devnode, O_RDWR)) == -1) + { + TRACE_(plugplay)("Unable to open hidraw device %s\n",devnode); + return; + } + close(fd); + + TRACE_(plugplay)("Got hidraw device %s\n", devnode); + + usbdev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", + "usb_device"); + if (usbdev) + { + char *endptr; + const char* srl = NULL; + vid = strtol(udev_device_get_sysattr_value(usbdev,"idVendor"), &endptr, 16); + pid = strtol(udev_device_get_sysattr_value(usbdev,"idProduct"), &endptr, 16); + version = strtol(udev_device_get_sysattr_value(usbdev,"version"), &endptr, 10); + srl = udev_device_get_sysattr_value(usbdev,"serial"); + if (srl) + MultiByteToWideChar(CP_UNIXCP, 0, srl, -1, serial, 255); + } + + device = HID_PNP_CreateDevice(hr_driver_obj, busidW, dev, vid, pid, version, uid, serial, FALSE, &GUID_DEVCLASS_HIDRAW); + if (!device) + ERR("Failed to create device\n"); +} + static VOID WINAPI Unload(DRIVER_OBJECT *driver) { TRACE("Linux Hidraw Driver Unload\n"); } +/* This is our main even loop for reading devices */ +static DWORD CALLBACK deviceloop_thread(VOID *args) +{ + static const char *subsystems[] = {"hidraw", 0}; + + if (udev) + { + TRACE_(plugplay)("Udev Set already built\n"); + return STATUS_SUCCESS; + } + + udev = udev_new(); + + if (!udev) + { + ERR("Can't create udev\n"); + return STATUS_UNSUCCESSFUL; + } + + /* PNP Bus initialization */ + UDEV_BuildInitialDeviceSet(udev, subsystems, TryAddDevice); + + return 1; +} + NTSTATUS WINAPI udevraw_DriverInit(DRIVER_OBJECT *driver, UNICODE_STRING *RegistryPath) { TRACE("Linux udev hidraw Driver Init\n"); @@ -68,6 +147,8 @@ NTSTATUS WINAPI udevraw_DriverInit(DRIVER_OBJECT *driver, UNICODE_STRING *Regist return STATUS_SUCCESS; } + CreateThread(NULL, 0, deviceloop_thread, NULL, 0, NULL); + return STATUS_SUCCESS; } diff --git a/dlls/wineplugplay.sys/pnp.h b/dlls/wineplugplay.sys/pnp.h index 69d434a..a2c7ccf 100644 --- a/dlls/wineplugplay.sys/pnp.h +++ b/dlls/wineplugplay.sys/pnp.h @@ -24,5 +24,9 @@ void Initialize_DriverStore(void) DECLSPEC_HIDDEN; VOID PNP_IoInvalidateDeviceRelations(DEVICE_OBJECT *DeviceObject, DEVICE_RELATION_TYPE Type) DECLSPEC_HIDDEN; /* HID Plug and Play Bus */ +DEVICE_OBJECT *HID_PNP_CreateDevice(DRIVER_OBJECT *driver, const WCHAR *busidW, LPVOID native, WORD vid, WORD pid, DWORD version, DWORD uid, const WCHAR *serial, BOOL isGamepad, const GUID *class) DECLSPEC_HIDDEN; NTSTATUS WINAPI HID_PNP_Dispatch(DEVICE_OBJECT *device, IRP *irp) DECLSPEC_HIDDEN; DWORD HID_check_BusDisabled(UNICODE_STRING *RegistryPath) DECLSPEC_HIDDEN; + +/* UDEV Bus */ +NTSTATUS UDEV_BuildInitialDeviceSet(void *udev, const char **subsystems, void (*TryAddDevice)(void *udev_device)) DECLSPEC_HIDDEN; diff --git a/include/config.h.in b/include/config.h.in index 382beb6..b9f9f19 100644 --- a/include/config.h.in +++ b/include/config.h.in @@ -378,6 +378,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_LIBPROC_H +/* Define to 1 if you have the header file. */ +#undef HAVE_LIBUDEV_H + /* Define to 1 if you have the header file. */ #undef HAVE_LIBUNWIND_H @@ -1466,6 +1469,9 @@ /* Define to the soname of the libXxf86vm library. */ #undef SONAME_LIBXXF86VM +/* Define to the soname of the libudev library. */ +#undef SONAME_LIBUDEV + /* Define to 1 if the `S_IS*' macros in do not work properly. */ #undef STAT_MACROS_BROKEN