diff --git a/HidSharp/Platform/Linux/LinuxHidDevice.cs b/HidSharp/Platform/Linux/LinuxHidDevice.cs index d7dc664..cb26f65 100644 --- a/HidSharp/Platform/Linux/LinuxHidDevice.cs +++ b/HidSharp/Platform/Linux/LinuxHidDevice.cs @@ -16,6 +16,7 @@ under the License. */ #endregion using System; +using System.Globalization; using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; @@ -90,6 +91,26 @@ internal static LinuxHidDevice TryCreate(string path) return d; } } + + // Devices without USB parents expose their identity through HID udev properties. + IntPtr hidParent = NativeMethodsLibudev.Instance.udev_device_get_parent_with_subsystem_devtype(device, "hid", null); + if (IntPtr.Zero != hidParent) + { + string hidId = NativeMethodsLibudev.Instance.udev_device_get_property_value(hidParent, "HID_ID"); + + int vid, pid; + if (TryParseHidId(hidId, out vid, out pid)) + { + d._vid = vid; + d._pid = pid; + d._version = 0; + d._manufacturer = null; + d._productName = NativeMethodsLibudev.Instance.udev_device_get_property_value(hidParent, "HID_NAME"); + d._serialNumber = null; + + return d; + } + } } } } @@ -108,6 +129,23 @@ internal static LinuxHidDevice TryCreate(string path) return null; } + static bool TryParseHidId(string hidId, out int vid, out int pid) + { + vid = 0; + pid = 0; + + if (hidId == null) { return false; } + + // HID_ID is BUS:VID:PID. + string[] fields = hidId.Split(':'); + if (fields.Length != 3) { return false; } + + return int.TryParse(fields[1], + NumberStyles.HexNumber, CultureInfo.InvariantCulture, out vid) && + int.TryParse(fields[2], + NumberStyles.HexNumber, CultureInfo.InvariantCulture, out pid); + } + protected override DeviceStream OpenDeviceDirectly(OpenConfiguration openConfig) { RequiresGetInfo(); @@ -282,10 +320,14 @@ unsafe string GetUsbPath() { using (var udev = new SafeUdevHandle(NativeMethodsLibudev.Instance.udev_new())) { - var handle = NativeMethodsLibudev.Instance.udev_device_new_from_syspath(udev.DangerousGetHandle(), _path); - using (var parent = new SafeUdevDeviceHandle(NativeMethodsLibudev.Instance.udev_device_get_parent_with_subsystem_devtype(handle, "usb", "usb_device"))) + var devicePtr = NativeMethodsLibudev.Instance.udev_device_new_from_syspath(udev.DangerousGetHandle(), _path); + using (var device = new SafeUdevDeviceHandle(devicePtr)) { - var parentPtr = parent.DangerousGetHandle(); + if (device.IsInvalid) { throw DeviceException.CreateIOException(this, "Failed to find device in udev."); } + + var parentPtr = NativeMethodsLibudev.Instance.udev_device_get_parent_with_subsystem_devtype( + device.DangerousGetHandle(), "usb", "usb_device"); + if (parentPtr == IntPtr.Zero) { throw DeviceException.CreateIOException(this, "Device has no USB parent."); } string devNum = NativeMethodsLibudev.Instance.udev_device_get_sysattr_value(parentPtr, "devnum"); string busNum = NativeMethodsLibudev.Instance.udev_device_get_sysattr_value(parentPtr, "busnum"); diff --git a/HidSharp/Platform/Linux/NativeMethodsLibudev.cs b/HidSharp/Platform/Linux/NativeMethodsLibudev.cs index 3759a38..a80690d 100644 --- a/HidSharp/Platform/Linux/NativeMethodsLibudev.cs +++ b/HidSharp/Platform/Linux/NativeMethodsLibudev.cs @@ -101,5 +101,7 @@ public abstract string FriendlyName public abstract string udev_device_get_sysattr_value(IntPtr device, string sysattr); public abstract int udev_device_get_is_initialized(IntPtr device); + + public abstract string udev_device_get_property_value(IntPtr device, string key); } } diff --git a/HidSharp/Platform/Linux/NativeMethodsLibudev0.cs b/HidSharp/Platform/Linux/NativeMethodsLibudev0.cs index b4167c4..25e3da9 100644 --- a/HidSharp/Platform/Linux/NativeMethodsLibudev0.cs +++ b/HidSharp/Platform/Linux/NativeMethodsLibudev0.cs @@ -215,5 +215,14 @@ public override int udev_device_get_is_initialized(IntPtr device) { return native_udev_device_get_is_initialized(device); } + + [DllImport(libudev, EntryPoint = "udev_device_get_property_value")] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))] + static extern string native_udev_device_get_property_value(IntPtr device, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))] string key); + public override string udev_device_get_property_value(IntPtr device, string key) + { + return native_udev_device_get_property_value(device, key); + } } } diff --git a/HidSharp/Platform/Linux/NativeMethodsLibudev1.cs b/HidSharp/Platform/Linux/NativeMethodsLibudev1.cs index 484db01..1e136c1 100644 --- a/HidSharp/Platform/Linux/NativeMethodsLibudev1.cs +++ b/HidSharp/Platform/Linux/NativeMethodsLibudev1.cs @@ -215,5 +215,14 @@ public override int udev_device_get_is_initialized(IntPtr device) { return native_udev_device_get_is_initialized(device); } + + [DllImport(libudev, EntryPoint = "udev_device_get_property_value")] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))] + static extern string native_udev_device_get_property_value(IntPtr device, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))] string key); + public override string udev_device_get_property_value(IntPtr device, string key) + { + return native_udev_device_get_property_value(device, key); + } } }