From 832885e77fa835e9b87d6af18dd3e3e4401e3bc9 Mon Sep 17 00:00:00 2001 From: X9VoiD Date: Sat, 8 Aug 2020 21:18:20 +0800 Subject: [PATCH 01/17] [WIP] Implement libusb backend --- HidSharp/Platform/HidSelector.cs | 1 + HidSharp/Platform/Libusb/LibusbHidDevice.cs | 106 ++++++ HidSharp/Platform/Libusb/LibusbHidManager.cs | 77 ++++- HidSharp/Platform/Libusb/LibusbHidStream.cs | 128 +++++++ HidSharp/Platform/Libusb/NativeMethods.cs | 332 ++++++++----------- 5 files changed, 450 insertions(+), 194 deletions(-) create mode 100644 HidSharp/Platform/Libusb/LibusbHidDevice.cs create mode 100644 HidSharp/Platform/Libusb/LibusbHidStream.cs diff --git a/HidSharp/Platform/HidSelector.cs b/HidSharp/Platform/HidSelector.cs index a6a15e1..c65bcbe 100644 --- a/HidSharp/Platform/HidSelector.cs +++ b/HidSharp/Platform/HidSelector.cs @@ -28,6 +28,7 @@ static HidSelector() { foreach (var hidManager in new HidManager[] { + new Libusb.LibusbHidManager(), new Windows.WinHidManager(), new Linux.LinuxHidManager(), new MacOS.MacHidManager(), diff --git a/HidSharp/Platform/Libusb/LibusbHidDevice.cs b/HidSharp/Platform/Libusb/LibusbHidDevice.cs new file mode 100644 index 0000000..85ff72a --- /dev/null +++ b/HidSharp/Platform/Libusb/LibusbHidDevice.cs @@ -0,0 +1,106 @@ +using System; +using System.Text; + +namespace HidSharp.Platform.Libusb +{ + sealed class LibusbHidDevice : HidDevice + { + public override int ProductID { get => _descriptor.idProduct; } + + public override int ReleaseNumberBcd { get => _descriptor.bcdUSB; } + + public override int VendorID { get => _descriptor.idVendor; } + + // Unsupported by libusb + public override string DevicePath {get => ""; } + + private IntPtr _device; + private IntPtr _deviceHandle; + private NativeMethods.libusb_device_descriptor _descriptor; + private byte _endpoint, _interfaceNum; + private int _maxOutputReportLength, _maxInputReportLength; + + internal static LibusbHidDevice TryCreate(NativeMethods.CombinedEndpoint combinedEndpoint) + { + var hid = new LibusbHidDevice { _device = combinedEndpoint.DevicePtr }; + var err = NativeMethods.libusb_open(hid._device, out hid._deviceHandle); + if (err > 0) + { + return null; + } + + err = NativeMethods.libusb_get_device_descriptor(hid._device, out hid._descriptor); + if (err > 0) + { + NativeMethods.libusb_close(hid._device); + return null; + } + + hid._interfaceNum = combinedEndpoint.InterfaceNum; + hid._endpoint = combinedEndpoint.Address; + hid._maxInputReportLength = combinedEndpoint.MaxInputPacketSize; + hid._maxOutputReportLength = combinedEndpoint.MaxOutputPacketSize; + return hid; + } + + public override string GetFileSystemName() + { + throw new NotImplementedException(); + } + + public override string GetManufacturer() + { + var manufacturer = new StringBuilder(256); + var err = NativeMethods.libusb_get_string_descriptor_ascii(_deviceHandle, _descriptor.iManufacturer, manufacturer, 256); + if (err < 0) + { + throw DeviceException.CreateIOException(this, "Failed to read report descriptor."); + } + return manufacturer.ToString(); + } + + public override int GetMaxFeatureReportLength() + { + throw new NotImplementedException(); + } + + public override int GetMaxInputReportLength() + { + return _maxInputReportLength; + } + + public override int GetMaxOutputReportLength() + { + return _maxOutputReportLength; + } + + public override string GetProductName() + { + var product = new StringBuilder(256); + var err = NativeMethods.libusb_get_string_descriptor_ascii(_deviceHandle, _descriptor.iProduct, product, 256); + if (err < 0) + { + throw DeviceException.CreateIOException(this, "Failed to read report descriptor."); + } + return product.ToString(); + } + + public override string GetSerialNumber() + { + var serial = new StringBuilder(256); + var err = NativeMethods.libusb_get_string_descriptor_ascii(_deviceHandle, _descriptor.iSerialNumber, serial, 256); + if (err < 0) + { + throw DeviceException.CreateIOException(this, "Failed to read report descriptor."); + } + return serial.ToString(); + } + + protected override DeviceStream OpenDeviceDirectly(OpenConfiguration openConfig) + { + var stream = new LibusbHidStream(this); + try { stream.Init(_deviceHandle, _interfaceNum, _endpoint); return stream; } + catch { stream.Close(); throw; } + } + } +} \ No newline at end of file diff --git a/HidSharp/Platform/Libusb/LibusbHidManager.cs b/HidSharp/Platform/Libusb/LibusbHidManager.cs index 4e91404..07adba4 100644 --- a/HidSharp/Platform/Libusb/LibusbHidManager.cs +++ b/HidSharp/Platform/Libusb/LibusbHidManager.cs @@ -16,12 +16,81 @@ under the License. */ #endregion using System; +using System.Collections.Generic; +using System.Linq; namespace HidSharp.Platform.Libusb { - sealed class LibusbHidManager - { - // TODO - } + sealed class LibusbHidManager : HidManager + { + public override string FriendlyName => "Libusb HID"; + + public override bool IsSupported { get => true; } + + protected override object[] GetBleDeviceKeys() + { + throw new NotImplementedException(); + } + + // TODO: cleanup + protected override object[] GetHidDeviceKeys() + { + NativeMethods.libusb_get_device_list(IntPtr.Zero, out IntPtr[] deviceList); + var list = new List(); + foreach (var device in deviceList) + { + NativeMethods.libusb_get_config_descriptor(device, out var configDescriptor); + foreach (var iinterface in configDescriptor.interfaces) + { + foreach (var iinterfaceSetting in iinterface.altsetting) + { + var endpointDict = new Dictionary(); + foreach (var endpoint in iinterfaceSetting.endpoints) + { + var endpointAddress = (byte)(endpoint.bEndpointAddress & 0x0F); + var endpointDirection = ((endpoint.bEndpointAddress & 0x80) >> 7) == 1; + if (!endpointDict.ContainsKey(endpointAddress)) + { + endpointDict.Add(endpointAddress, new NativeMethods.CombinedEndpoint(device, iinterfaceSetting.bInterfaceNumber, endpointAddress, + endpointDirection ? (UInt16)0 : endpoint.wMaxPacketSize, + endpointDirection ? endpoint.wMaxPacketSize : (UInt16)0)); + } + else + { + endpointDict[endpointAddress].SetPacketSize(endpointDirection, endpoint.wMaxPacketSize); + } + } + foreach (var endpoint in endpointDict.Values) + { + list.Add(endpoint); + } + } + } + } + NativeMethods.libusb_free_device_list(deviceList, 1); + return list.Cast().ToArray(); + } + + protected override object[] GetSerialDeviceKeys() + { + throw new NotImplementedException(); + } + + protected override bool TryCreateBleDevice(object key, out Device device) + { + throw new NotImplementedException(); + } + + protected override bool TryCreateHidDevice(object key, out Device device) + { + device = LibusbHidDevice.TryCreate((NativeMethods.CombinedEndpoint)key); + return device != null; + } + + protected override bool TryCreateSerialDevice(object key, out Device device) + { + throw new NotImplementedException(); + } + } } diff --git a/HidSharp/Platform/Libusb/LibusbHidStream.cs b/HidSharp/Platform/Libusb/LibusbHidStream.cs new file mode 100644 index 0000000..badd76d --- /dev/null +++ b/HidSharp/Platform/Libusb/LibusbHidStream.cs @@ -0,0 +1,128 @@ +using System; +using System.IO; + +namespace HidSharp.Platform.Libusb +{ + sealed class LibusbHidStream : SysHidStream + { + private IntPtr _handle; + private byte _endpoint, _interfaceNum; + private object _readsync = new object(); + private object _writesync = new object(); + + internal LibusbHidStream(LibusbHidDevice device) : base(device) + { + + } + + ~LibusbHidStream() + { + NativeMethods.libusb_release_interface(_handle, _interfaceNum); + NativeMethods.libusb_close(_handle); + Close(); + } + + // To follow HidSharp's structure and style + internal void Init(IntPtr deviceHandle, byte interfaceNum, byte endpoint) + { + var err = NativeMethods.libusb_claim_interface(deviceHandle, interfaceNum); + if (err != NativeMethods.Error.None) + { + switch (err) + { + case NativeMethods.Error.Busy: + throw new IOException("Device interface busy"); + } + } + _handle = deviceHandle; + _interfaceNum = interfaceNum; + _endpoint = endpoint; + } + + public override void GetFeature(byte[] buffer, int offset, int count) + { + throw new NotImplementedException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + byte endpointMode = (byte)(_endpoint | (0x80)); + int transferred = 0; + int minIn = Device.GetMaxInputReportLength(); + if (minIn <= 0) + throw new IOException("Can't read from this device."); + NativeMethods.Error error = NativeMethods.Error.None; + try + { + lock(_readsync) + { + error = NativeMethods.libusb_interrupt_transfer(_handle, endpointMode, buffer, count, ref transferred); + if (error != NativeMethods.Error.None) + { + throw new Exception(); + } + else + { + if (transferred == 0) + { + throw new IOException("Unexpected zero read."); + } + } + return transferred; + } + } + catch + { + throw new IOException("Reading from device failed."); + } + } + + public override void SetFeature(byte[] buffer, int offset, int count) + { + throw new NotImplementedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + byte endpointMode = (byte)(_endpoint & ~0x80); + int transferred = 0; + int minOut = Device.GetMaxInputReportLength(); + if (minOut <= 0) + throw new IOException("Can't write to this device."); + try + { + lock (_writesync) + { + NativeMethods.Error error = NativeMethods.libusb_interrupt_transfer(_handle, endpointMode, buffer, count, ref transferred); + if (error != NativeMethods.Error.None) + { + throw new Exception(); + } + else + { + if (transferred == 0) + { + throw new IOException("Unexpected zero write."); + } + } + } + } + catch + { + throw new IOException("Writing to device failed."); + } + } + + internal override void HandleFree() + { + // Should not free handle while we still have intention of performing I/O + } + + protected override void Dispose(bool disposing) + { + NativeMethods.libusb_release_interface(_handle, _interfaceNum); + NativeMethods.libusb_close(_handle); + base.Dispose(disposing); + } + } +} \ No newline at end of file diff --git a/HidSharp/Platform/Libusb/NativeMethods.cs b/HidSharp/Platform/Libusb/NativeMethods.cs index 0a40cd8..9d0d234 100644 --- a/HidSharp/Platform/Libusb/NativeMethods.cs +++ b/HidSharp/Platform/Libusb/NativeMethods.cs @@ -17,87 +17,21 @@ under the License. */ using System; using System.Runtime.InteropServices; +using System.Text; namespace HidSharp.Platform.Libusb { static class NativeMethods { - const string Libusb = "libusb-1.0"; - - [StructLayout(LayoutKind.Sequential)] - public struct DeviceDescriptor - { - public byte bLength, bDescriptorType; - public ushort bcdUSB; - public byte bDeviceClass, bDeviceSubClass, bDeviceProtocol, bMaxPacketSize0; - public ushort idVendor, idProduct, bcdDevice; - public byte iManufacturer, iProduct, iSerialNumber, bNumConfigurations; - } - - public enum DeviceClass : byte - { - HID = 0x03, - MassStorage = 0x08, - VendorSpecific = 0xff - } - - public enum DescriptorType : byte - { - Device = 0x01, - Configuration = 0x02, - String = 0x03, - Interface = 0x04, - Endpoint = 0x05, - HID = 0x21, - Report = 0x22, - Physical = 0x23, - Hub = 0x29 - } - - public enum EndpointDirection : byte - { - In = 0x80, - Out = 0, - } - - public enum Request : byte - { - GetDescriptor = 0x06 - } - - public enum RequestRecipient : byte - { - Device = 0, - Interface = 1, - Endpoint = 2, - Other = 3 - } - - public enum RequestType : byte - { - Standard = 0x00, - Class = 0x20, - Vendor = 0x40 - } - - public enum TransferType : byte - { - Control = 0, - Isochronous, - Bulk, - Interrupt - } - - public struct Version - { - public ushort Major, Minor, Micro, Nano; - } - - public enum Error - { - None = 0, - IO = -1, - InvalidParameter = -2, + + // const string Libusb = "libusb-1.0.so.0"; + const string Libusb = "libusb-1.0.dll"; + + public enum Error + { + None = 0, + IO = -1, + InvalidParameter = -2, AccessDenied = -3, NoDevice = -4, NotFound = -5, @@ -107,125 +41,143 @@ public enum Error Pipe = -9, Interrupted = -10, OutOfMemory = -11, - NotSupported = -12 - } + NotSupported = -12, + Other = -99 + } + + [StructLayout(LayoutKind.Sequential)] + public struct libusb_device_descriptor + { + public byte bLength; + public byte bDescriptorType; + public UInt16 bcdUSB; + public byte bDeviceClass; + public byte bDeviceSubClass; + public byte bDeviceProtocol; + public byte bMaxPacketSize0; + public UInt16 idVendor; + public UInt16 idProduct; + public UInt16 bcdDevice; + public byte iManufacturer; + public byte iProduct; + public byte iSerialNumber; + public byte bNumConfigurations; + } + + [StructLayout(LayoutKind.Sequential)] + public struct libusb_endpoint_descriptor + { + public byte bLength; + public byte bDescriptorType; + public byte bEndpointAddress; + public byte bmAttributes; + public UInt16 wMaxPacketSize; + public byte bInterval; + public byte bRefresh; + public byte bSynchAddress; + public IntPtr extra; + public int extra_length; + } + + [StructLayout(LayoutKind.Sequential)] + public struct libusb_interface_descriptor + { + public byte bLength; + public byte bDescriptorType; + public byte bInterfaceNumber; + public byte bAlternateSetting; + public byte bNumEndpoints; + public byte bInterfaceClass; + public byte bInterfaceSubClass; + public byte bInterfaceProtocol; + public byte iInterface; + public libusb_endpoint_descriptor[] endpoints; + public IntPtr extra; + public int extra_length; + } + + [StructLayout(LayoutKind.Sequential)] + public struct libusb_interface + { + public libusb_interface_descriptor[] altsetting; + public int num_altsetting; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe struct libusb_config_descriptor + { + public byte bLength; + public byte bDescriptorType; + public UInt16 wTotalLength; + public byte bNumInterfaces; + public byte bConfigurationValue; + public byte iConfiguration; + public byte bmAttributes; + public byte MaxPower; + public libusb_interface[] interfaces; + public IntPtr extra; + public int extra_length; + } + + public struct CombinedEndpoint + { + public IntPtr DevicePtr; + public byte InterfaceNum; + public byte Address; + public UInt16 MaxOutputPacketSize; + public UInt16 MaxInputPacketSize; + public CombinedEndpoint(IntPtr devicePtr, byte interfaceNum, byte endpoint, UInt16 maxOutputPacketSize, UInt16 maxInputPacketSize) + { + DevicePtr = devicePtr; + InterfaceNum = interfaceNum; + Address = endpoint; + MaxOutputPacketSize = maxOutputPacketSize; + MaxInputPacketSize = maxInputPacketSize; + } + public void SetPacketSize(bool inDirection, UInt16 maxPacketSize) + { + if (inDirection) + MaxInputPacketSize = maxPacketSize; + else + MaxOutputPacketSize = maxPacketSize; + } + } [DllImport(Libusb)] public static extern Error libusb_init(out IntPtr context); - - [DllImport(Libusb)] - public static extern void libusb_set_debug(IntPtr context, int level); - - [DllImport(Libusb)] - public static extern void libusb_exit(IntPtr context); - - [DllImport(Libusb)] - public static extern IntPtr libusb_get_device_list(IntPtr context, out IntPtr list); - - [DllImport(Libusb)] - public static extern void libusb_free_device_list(IntPtr context, IntPtr list); - - [DllImport(Libusb)] - public static extern IntPtr libusb_ref_device(IntPtr device); - - [DllImport(Libusb)] - public static extern void libusb_unref_device(IntPtr device); - - [DllImport(Libusb)] - public static extern int libusb_get_max_packet_size(IntPtr device, byte endpoint); - - [DllImport(Libusb)] - public static extern Error libusb_open(IntPtr device, out IntPtr deviceHandle); - - [DllImport(Libusb)] - public static extern void libusb_close(IntPtr deviceHandle); - - [DllImport(Libusb)] - public static extern Error libusb_get_configuration(IntPtr deviceHandle, out int configuration); - - [DllImport(Libusb)] - public static extern Error libusb_set_configuration(IntPtr deviceHandle, int configuration); - - [DllImport(Libusb)] - public static extern Error libusb_claim_interface(IntPtr deviceHandle, int @interface); - - [DllImport(Libusb)] - public static extern Error libusb_release_interface(IntPtr deviceHandle, int @interface); - - [DllImport(Libusb)] - public static extern Error libusb_set_interface_alt_setting(IntPtr deviceHandle, int @interface, int altSetting); - - [DllImport(Libusb)] - public static extern Error libusb_clear_halt(IntPtr deviceHandle, byte endpoint); - - [DllImport(Libusb)] - public static extern Error libusb_reset_device(IntPtr deviceHandle); - - [DllImport(Libusb)] - public static extern Error libusb_kernel_driver_active(IntPtr deviceHandle, int @interface); - - [DllImport(Libusb)] - public static extern Error libusb_detach_kernel_driver(IntPtr deviceHandle, int @interface); - - [DllImport(Libusb)] - public static extern Error libusb_attach_kernel_driver(IntPtr deviceHandle, int @interface); - - [DllImport(Libusb)] - public static extern IntPtr libusb_get_version(); - - [DllImport(Libusb)] - public static extern Error libusb_get_device_descriptor(IntPtr device, out DeviceDescriptor descriptor); - - [DllImport(Libusb)] - public static extern Error libusb_get_active_config_descriptor(IntPtr device, out IntPtr configuration); - [DllImport(Libusb)] - public static extern Error libusb_get_config_descriptor_by_value(IntPtr device, byte index, out IntPtr configuration); - - [DllImport(Libusb)] - public static extern void libusb_free_config_descriptor(IntPtr configuration); + [DllImport(Libusb)] + public static extern void libusb_exit(IntPtr context); - static Error libusb_get_descriptor_core(IntPtr deviceHandle, DescriptorType type, byte index, byte[] data, ushort wLength, ushort wIndex) - { - return libusb_control_transfer(deviceHandle, - (byte)EndpointDirection.In, (byte)Request.GetDescriptor, - (ushort)((byte)DescriptorType.String << 8 | index), - wIndex, data, wLength, 1000); - } + [DllImport(Libusb)] + public static extern int libusb_get_device_list(IntPtr context, out IntPtr[] deviceList); - public static Error libusb_get_descriptor(IntPtr deviceHandle, DescriptorType type, byte index, byte[] data, ushort wLength) - { - return libusb_get_descriptor_core(deviceHandle, - type, index, data, wLength, 0); - } - - public static Error libusb_get_string_descriptor(IntPtr deviceHandle, DescriptorType type, byte index, ushort languageID, byte[] data, ushort wLength) - { - return libusb_get_descriptor_core(deviceHandle, - DescriptorType.String, index, - data, wLength, languageID); - } - - [DllImport(Libusb)] - public static extern Error libusb_control_transfer(IntPtr deviceHandle, - byte bmRequestType, byte bRequest, - ushort wValue, ushort wIndex, - byte[] data, ushort wLength, - uint timeout); - - [DllImport(Libusb)] - public static extern Error libusb_bulk_transfer(IntPtr deviceHandle, - byte endpoint, - byte[] data, int length, - out int transferred, - uint timeout); - - [DllImport(Libusb)] - public static extern Error libusb_interrupt_transfer(IntPtr deviceHandle, - byte endpoint, - byte[] data, int length, - out int transferred, - uint timeout); - } + [DllImport(Libusb)] + public static extern void libusb_free_device_list(IntPtr[] deviceList, int unref); + + [DllImport(Libusb)] + public static extern Error libusb_open(IntPtr device, out IntPtr deviceHandle); + + [DllImport(Libusb)] + public static extern void libusb_close(IntPtr deviceHandle); + + [DllImport(Libusb)] + public static extern Error libusb_get_device_descriptor(IntPtr device, out libusb_device_descriptor deviceDescriptor); + + [DllImport(Libusb)] + public static extern int libusb_get_string_descriptor_ascii(IntPtr deviceHandle, byte index, StringBuilder data, int length); + + [DllImport(Libusb)] + public static extern Error libusb_get_config_descriptor(IntPtr deviceHandle, out libusb_config_descriptor configDescriptor); + + [DllImport(Libusb)] + public static extern Error libusb_interrupt_transfer(IntPtr deviceHandle, byte endpoint, byte[] data, int length, ref int actual_length, uint timeout = 0); + + [DllImport(Libusb)] + public static extern Error libusb_claim_interface(IntPtr deviceHandle, int interfaceNum); + + [DllImport(Libusb)] + public static extern Error libusb_release_interface(IntPtr deviceHandle, int interfaceNum); + } } From d2e5c5d4da156d48ab7d06cba4b69d96b9b92c7b Mon Sep 17 00:00:00 2001 From: X9VoiD Date: Sun, 9 Aug 2020 02:29:09 +0800 Subject: [PATCH 02/17] [WIP] Marshal --- HidSharp/Platform/Libusb/LibusbHidManager.cs | 57 +++++++++++++------- HidSharp/Platform/Libusb/NativeMethods.cs | 27 ++++++++-- 2 files changed, 59 insertions(+), 25 deletions(-) diff --git a/HidSharp/Platform/Libusb/LibusbHidManager.cs b/HidSharp/Platform/Libusb/LibusbHidManager.cs index 07adba4..717f60c 100644 --- a/HidSharp/Platform/Libusb/LibusbHidManager.cs +++ b/HidSharp/Platform/Libusb/LibusbHidManager.cs @@ -18,6 +18,7 @@ under the License. */ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.InteropServices; namespace HidSharp.Platform.Libusb { @@ -33,41 +34,57 @@ protected override object[] GetBleDeviceKeys() } // TODO: cleanup - protected override object[] GetHidDeviceKeys() + protected override unsafe object[] GetHidDeviceKeys() { - NativeMethods.libusb_get_device_list(IntPtr.Zero, out IntPtr[] deviceList); + NativeMethods.libusb_init(IntPtr.Zero); + + var count = NativeMethods.libusb_get_device_list(IntPtr.Zero, out IntPtr deviceListRaw); + IntPtr[] deviceList = new IntPtr[count]; + Marshal.Copy(deviceListRaw, deviceList, 0, count); + var list = new List(); + foreach (var device in deviceList) { - NativeMethods.libusb_get_config_descriptor(device, out var configDescriptor); - foreach (var iinterface in configDescriptor.interfaces) + NativeMethods.libusb_get_device_descriptor(device, out var deviceDescriptor); + byte configCount = deviceDescriptor.bNumConfigurations; + for (byte configIndex = 0; configIndex < configCount; configIndex++) { - foreach (var iinterfaceSetting in iinterface.altsetting) + if (NativeMethods.libusb_get_config_descriptor(device, configIndex, out var configDescriptor) != NativeMethods.Error.None) + continue; + + if (configDescriptor.bDescriptorType != (byte)NativeMethods.libusb_descriptor_type.LIBUSB_DT_CONFIG) + continue; + + foreach (var iinterface in configDescriptor.interfaces) { - var endpointDict = new Dictionary(); - foreach (var endpoint in iinterfaceSetting.endpoints) + foreach (var iinterfaceSetting in iinterface.altsetting) { - var endpointAddress = (byte)(endpoint.bEndpointAddress & 0x0F); - var endpointDirection = ((endpoint.bEndpointAddress & 0x80) >> 7) == 1; - if (!endpointDict.ContainsKey(endpointAddress)) + var endpointDict = new Dictionary(); + foreach (var endpoint in iinterfaceSetting.endpoints) { - endpointDict.Add(endpointAddress, new NativeMethods.CombinedEndpoint(device, iinterfaceSetting.bInterfaceNumber, endpointAddress, - endpointDirection ? (UInt16)0 : endpoint.wMaxPacketSize, - endpointDirection ? endpoint.wMaxPacketSize : (UInt16)0)); + var endpointAddress = (byte)(endpoint.bEndpointAddress & 0x0F); + var endpointDirection = ((endpoint.bEndpointAddress & 0x80) >> 7) == 1; + if (!endpointDict.ContainsKey(endpointAddress)) + { + endpointDict.Add(endpointAddress, new NativeMethods.CombinedEndpoint(device, iinterfaceSetting.bInterfaceNumber, endpointAddress, + endpointDirection ? (UInt16)0 : endpoint.wMaxPacketSize, + endpointDirection ? endpoint.wMaxPacketSize : (UInt16)0)); + } + else + { + endpointDict[endpointAddress].SetPacketSize(endpointDirection, endpoint.wMaxPacketSize); + } } - else + foreach (var endpoint in endpointDict.Values) { - endpointDict[endpointAddress].SetPacketSize(endpointDirection, endpoint.wMaxPacketSize); + list.Add(endpoint); } } - foreach (var endpoint in endpointDict.Values) - { - list.Add(endpoint); - } } } } - NativeMethods.libusb_free_device_list(deviceList, 1); + NativeMethods.libusb_free_device_list(deviceListRaw, 1); return list.Cast().ToArray(); } diff --git a/HidSharp/Platform/Libusb/NativeMethods.cs b/HidSharp/Platform/Libusb/NativeMethods.cs index 9d0d234..6463533 100644 --- a/HidSharp/Platform/Libusb/NativeMethods.cs +++ b/HidSharp/Platform/Libusb/NativeMethods.cs @@ -45,6 +45,23 @@ public enum Error Other = -99 } + public enum libusb_descriptor_type + { + LIBUSB_DT_DEVICE = 0x01, + LIBUSB_DT_CONFIG = 0x02, + LIBUSB_DT_STRING = 0x03, + LIBUSB_DT_INTERFACE = 0x04, + LIBUSB_DT_ENDPOINT = 0x05, + LIBUSB_DT_BOS = 0x0f, + LIBUSB_DT_DEVICE_CAPABILITY = 0x10, + LIBUSB_DT_HID = 0x21, + LIBUSB_DT_REPORT = 0x22, + LIBUSB_DT_PHYSICAL = 0x23, + LIBUSB_DT_HUB = 0x29, + LIBUSB_DT_SUPERSPEED_HUB = 0x2a, + LIBUSB_DT_SS_ENDPOINT_COMPANION = 0x30 + } + [StructLayout(LayoutKind.Sequential)] public struct libusb_device_descriptor { @@ -104,7 +121,7 @@ public struct libusb_interface } [StructLayout(LayoutKind.Sequential)] - public unsafe struct libusb_config_descriptor + public struct libusb_config_descriptor { public byte bLength; public byte bDescriptorType; @@ -144,16 +161,16 @@ public void SetPacketSize(bool inDirection, UInt16 maxPacketSize) } [DllImport(Libusb)] - public static extern Error libusb_init(out IntPtr context); + public static extern Error libusb_init(IntPtr context); [DllImport(Libusb)] public static extern void libusb_exit(IntPtr context); [DllImport(Libusb)] - public static extern int libusb_get_device_list(IntPtr context, out IntPtr[] deviceList); + public static extern int libusb_get_device_list(IntPtr context, out IntPtr deviceList); [DllImport(Libusb)] - public static extern void libusb_free_device_list(IntPtr[] deviceList, int unref); + public static extern void libusb_free_device_list(IntPtr deviceList, int unref); [DllImport(Libusb)] public static extern Error libusb_open(IntPtr device, out IntPtr deviceHandle); @@ -168,7 +185,7 @@ public void SetPacketSize(bool inDirection, UInt16 maxPacketSize) public static extern int libusb_get_string_descriptor_ascii(IntPtr deviceHandle, byte index, StringBuilder data, int length); [DllImport(Libusb)] - public static extern Error libusb_get_config_descriptor(IntPtr deviceHandle, out libusb_config_descriptor configDescriptor); + public static unsafe extern Error libusb_get_config_descriptor(IntPtr device, byte configIndex, out libusb_config_descriptor configDescriptor); [DllImport(Libusb)] public static extern Error libusb_interrupt_transfer(IntPtr deviceHandle, byte endpoint, byte[] data, int length, ref int actual_length, uint timeout = 0); From 1a078317bdcabf95c477bdfc9401add3b43b75a2 Mon Sep 17 00:00:00 2001 From: X9VoiD Date: Mon, 10 Aug 2020 18:41:47 +0800 Subject: [PATCH 03/17] [WIP] Add support for all platforms supported by HidSharp --- HidSharp/Platform/HidSelector.cs | 10 +- HidSharp/Platform/Libusb/INativeMethods.cs | 184 ++++++++++++++++ HidSharp/Platform/Libusb/LibusbHidDevice.cs | 40 ++-- HidSharp/Platform/Libusb/LibusbHidManager.cs | 128 ++++++++--- HidSharp/Platform/Libusb/LibusbHidStream.cs | 89 ++++---- .../Platform/Libusb/LinuxNativeMethods.cs | 126 +++++++++++ .../Platform/Libusb/MacOSNativeMethods.cs | 126 +++++++++++ HidSharp/Platform/Libusb/NativeMethods.cs | 200 ------------------ HidSharp/Platform/Libusb/WinNativeMethods.cs | 126 +++++++++++ 9 files changed, 724 insertions(+), 305 deletions(-) create mode 100644 HidSharp/Platform/Libusb/INativeMethods.cs create mode 100644 HidSharp/Platform/Libusb/LinuxNativeMethods.cs create mode 100644 HidSharp/Platform/Libusb/MacOSNativeMethods.cs delete mode 100644 HidSharp/Platform/Libusb/NativeMethods.cs create mode 100644 HidSharp/Platform/Libusb/WinNativeMethods.cs diff --git a/HidSharp/Platform/HidSelector.cs b/HidSharp/Platform/HidSelector.cs index c65bcbe..80049ca 100644 --- a/HidSharp/Platform/HidSelector.cs +++ b/HidSharp/Platform/HidSelector.cs @@ -28,10 +28,12 @@ static HidSelector() { foreach (var hidManager in new HidManager[] { - new Libusb.LibusbHidManager(), - new Windows.WinHidManager(), - new Linux.LinuxHidManager(), - new MacOS.MacHidManager(), + new Libusb.LibusbHidManager(), + new Libusb.LibusbHidManager(), + new Libusb.LibusbHidManager(), + // new Windows.WinHidManager(), + // new Linux.LinuxHidManager(), + // new MacOS.MacHidManager(), new Unsupported.UnsupportedHidManager() }) { diff --git a/HidSharp/Platform/Libusb/INativeMethods.cs b/HidSharp/Platform/Libusb/INativeMethods.cs new file mode 100644 index 0000000..2d69a61 --- /dev/null +++ b/HidSharp/Platform/Libusb/INativeMethods.cs @@ -0,0 +1,184 @@ +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace HidSharp.Platform.Libusb +{ + public interface INativeMethods + { + internal class Endpoint + { + public int Interface; + public int Address; + public uint OutputReportLength; + public uint InputReportLength; + public bool IsReadable { get => InputReportLength > 0; } + + public bool IsWritable { get => OutputReportLength > 0; } + + public Endpoint(int Interface, int address, uint output = 0, uint input = 0) + { + this.Interface = Interface; + Address = address; + OutputReportLength = output; + InputReportLength = input; + } + + public void SetReportLength(bool inDirection, uint length) + { + if (inDirection) + InputReportLength = length; + else + OutputReportLength = length; + } + } + + internal class LibUsbDevice + { + public IntPtr Device; + public int VendorID; + public int ProductID; + public Endpoint Endpoint; + } + public enum Error + { + None = 0, + IO = -1, + InvalidParameter = -2, + AccessDenied = -3, + NoDevice = -4, + NotFound = -5, + Busy = -6, + Timeout = -7, + Overflow = -8, + Pipe = -9, + Interrupted = -10, + OutOfMemory = -11, + NotSupported = -12, + Other = -99 + } + + public enum libusb_descriptor_type + { + LIBUSB_DT_DEVICE = 0x01, + LIBUSB_DT_CONFIG = 0x02, + LIBUSB_DT_STRING = 0x03, + LIBUSB_DT_INTERFACE = 0x04, + LIBUSB_DT_ENDPOINT = 0x05, + LIBUSB_DT_BOS = 0x0f, + LIBUSB_DT_DEVICE_CAPABILITY = 0x10, + LIBUSB_DT_HID = 0x21, + LIBUSB_DT_REPORT = 0x22, + LIBUSB_DT_PHYSICAL = 0x23, + LIBUSB_DT_HUB = 0x29, + LIBUSB_DT_SUPERSPEED_HUB = 0x2a, + LIBUSB_DT_SS_ENDPOINT_COMPANION = 0x30 + } + + [StructLayout(LayoutKind.Sequential)] + public struct libusb_device_descriptor + { + public byte bLength; + public byte bDescriptorType; + public UInt16 bcdUSB; + public byte bDeviceClass; + public byte bDeviceSubClass; + public byte bDeviceProtocol; + public byte bMaxPacketSize0; + public UInt16 idVendor; + public UInt16 idProduct; + public UInt16 bcdDevice; + public byte iManufacturer; + public byte iProduct; + public byte iSerialNumber; + public byte bNumConfigurations; + } + + [StructLayout(LayoutKind.Sequential)] + public struct libusb_endpoint_descriptor + { + public byte bLength; + public byte bDescriptorType; + public byte bEndpointAddress; + public byte bmAttributes; + public UInt16 wMaxPacketSize; + public byte bInterval; + public byte bRefresh; + public byte bSynchAddress; + public IntPtr extra; + public int extra_length; + } + + [StructLayout(LayoutKind.Sequential)] + public struct libusb_interface_descriptor + { + public byte bLength; + public byte bDescriptorType; + public byte bInterfaceNumber; + public byte bAlternateSetting; + public byte bNumEndpoints; + public byte bInterfaceClass; + public byte bInterfaceSubClass; + public byte bInterfaceProtocol; + public byte iInterface; + + /// libusb_endpoint_descriptor[] + public IntPtr endpoints; + public IntPtr extra; + public int extra_length; + } + + [StructLayout(LayoutKind.Sequential)] + public struct libusb_interface + { + /// libusb_interface_descriptor[] + public IntPtr altsetting; + public int num_altsetting; + } + + [StructLayout(LayoutKind.Sequential)] + public struct libusb_config_descriptor + { + public byte bLength; + public byte bDescriptorType; + public UInt16 wTotalLength; + public byte bNumInterfaces; + public byte bConfigurationValue; + public byte iConfiguration; + public byte bmAttributes; + public byte MaxPower; + /// libusb_interface[] + public IntPtr interfaces; + public IntPtr extra; + public int extra_length; + } + + public Error init(IntPtr context); + + public void exit(IntPtr context); + + public int get_device_list(IntPtr context, out IntPtr deviceList); + + public void free_device_list(IntPtr deviceList, int unref); + + public Error open(IntPtr device, out IntPtr deviceHandle); + + public void close(IntPtr deviceHandle); + + public Error get_device_descriptor(IntPtr device, out libusb_device_descriptor deviceDescriptor); + + public int get_string_descriptor_ascii(IntPtr deviceHandle, byte index, StringBuilder data, int length); + + public Error get_config_descriptor(IntPtr device, byte configIndex, out IntPtr configDescriptor); + + public Error interrupt_transfer(IntPtr deviceHandle, byte endpoint, byte[] data, int length, ref int actual_length, uint timeout = 0); + + public Error claim_interface(IntPtr deviceHandle, int interfaceNum); + + public Error release_interface(IntPtr deviceHandle, int interfaceNum); + + public Error detach_kernel_driver(IntPtr deviceHandle, int interfaceNum); + + public Error attach_kernel_driver(IntPtr deviceHandle, int interfaceNum); + } +} \ No newline at end of file diff --git a/HidSharp/Platform/Libusb/LibusbHidDevice.cs b/HidSharp/Platform/Libusb/LibusbHidDevice.cs index 85ff72a..ac883e1 100644 --- a/HidSharp/Platform/Libusb/LibusbHidDevice.cs +++ b/HidSharp/Platform/Libusb/LibusbHidDevice.cs @@ -1,9 +1,10 @@ using System; using System.Text; +using static HidSharp.Platform.Libusb.INativeMethods; namespace HidSharp.Platform.Libusb { - sealed class LibusbHidDevice : HidDevice + sealed class LibusbHidDevice : HidDevice where T : INativeMethods, new() { public override int ProductID { get => _descriptor.idProduct; } @@ -11,35 +12,32 @@ sealed class LibusbHidDevice : HidDevice public override int VendorID { get => _descriptor.idVendor; } + private T libusb = new T(); + // Unsupported by libusb public override string DevicePath {get => ""; } - private IntPtr _device; + private LibUsbDevice _dev; private IntPtr _deviceHandle; - private NativeMethods.libusb_device_descriptor _descriptor; - private byte _endpoint, _interfaceNum; - private int _maxOutputReportLength, _maxInputReportLength; + private libusb_device_descriptor _descriptor; - internal static LibusbHidDevice TryCreate(NativeMethods.CombinedEndpoint combinedEndpoint) + internal static LibusbHidDevice TryCreate(LibUsbDevice device) { - var hid = new LibusbHidDevice { _device = combinedEndpoint.DevicePtr }; - var err = NativeMethods.libusb_open(hid._device, out hid._deviceHandle); + T libusb = new T(); + var hid = new LibusbHidDevice { _dev = device }; + var err = libusb.open(hid._dev.Device, out hid._deviceHandle); if (err > 0) { return null; } - err = NativeMethods.libusb_get_device_descriptor(hid._device, out hid._descriptor); + err = libusb.get_device_descriptor(hid._dev.Device, out hid._descriptor); if (err > 0) { - NativeMethods.libusb_close(hid._device); + libusb.close(hid._dev.Device); return null; } - hid._interfaceNum = combinedEndpoint.InterfaceNum; - hid._endpoint = combinedEndpoint.Address; - hid._maxInputReportLength = combinedEndpoint.MaxInputPacketSize; - hid._maxOutputReportLength = combinedEndpoint.MaxOutputPacketSize; return hid; } @@ -51,7 +49,7 @@ public override string GetFileSystemName() public override string GetManufacturer() { var manufacturer = new StringBuilder(256); - var err = NativeMethods.libusb_get_string_descriptor_ascii(_deviceHandle, _descriptor.iManufacturer, manufacturer, 256); + var err = libusb.get_string_descriptor_ascii(_deviceHandle, _descriptor.iManufacturer, manufacturer, 256); if (err < 0) { throw DeviceException.CreateIOException(this, "Failed to read report descriptor."); @@ -66,18 +64,18 @@ public override int GetMaxFeatureReportLength() public override int GetMaxInputReportLength() { - return _maxInputReportLength; + return (int)_dev.Endpoint.InputReportLength; } public override int GetMaxOutputReportLength() { - return _maxOutputReportLength; + return (int)_dev.Endpoint.OutputReportLength; } public override string GetProductName() { var product = new StringBuilder(256); - var err = NativeMethods.libusb_get_string_descriptor_ascii(_deviceHandle, _descriptor.iProduct, product, 256); + var err = libusb.get_string_descriptor_ascii(_deviceHandle, _descriptor.iProduct, product, 256); if (err < 0) { throw DeviceException.CreateIOException(this, "Failed to read report descriptor."); @@ -88,7 +86,7 @@ public override string GetProductName() public override string GetSerialNumber() { var serial = new StringBuilder(256); - var err = NativeMethods.libusb_get_string_descriptor_ascii(_deviceHandle, _descriptor.iSerialNumber, serial, 256); + var err = libusb.get_string_descriptor_ascii(_deviceHandle, _descriptor.iSerialNumber, serial, 256); if (err < 0) { throw DeviceException.CreateIOException(this, "Failed to read report descriptor."); @@ -98,8 +96,8 @@ public override string GetSerialNumber() protected override DeviceStream OpenDeviceDirectly(OpenConfiguration openConfig) { - var stream = new LibusbHidStream(this); - try { stream.Init(_deviceHandle, _interfaceNum, _endpoint); return stream; } + var stream = new LibusbHidStream(this); + try { stream.Init(_deviceHandle, (byte)_dev.Endpoint.Interface, (byte)_dev.Endpoint.Address); return stream; } catch { stream.Close(); throw; } } } diff --git a/HidSharp/Platform/Libusb/LibusbHidManager.cs b/HidSharp/Platform/Libusb/LibusbHidManager.cs index 717f60c..1dd763e 100644 --- a/HidSharp/Platform/Libusb/LibusbHidManager.cs +++ b/HidSharp/Platform/Libusb/LibusbHidManager.cs @@ -19,14 +19,62 @@ under the License. */ using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; +using static HidSharp.Platform.Libusb.INativeMethods; namespace HidSharp.Platform.Libusb { - sealed class LibusbHidManager : HidManager + sealed class LibusbHidManager : HidManager where T : INativeMethods, new() { public override string FriendlyName => "Libusb HID"; - public override bool IsSupported { get => true; } + public override bool IsSupported + { + get + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + try + { + Marshal.PrelinkAll(typeof(WinNativeMethods)); + return true; + } + catch + { + return false; + } + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + try + { + Marshal.PrelinkAll(typeof(LinuxNativeMethods)); + return true; + } + catch + { + return false; + } + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + try + { + Marshal.PrelinkAll(typeof(MacOSNativeMethods)); + return true; + } + catch + { + return false; + } + } + else + { + return false; + } + } + } + + public T libusb = new T(); protected override object[] GetBleDeviceKeys() { @@ -34,58 +82,76 @@ protected override object[] GetBleDeviceKeys() } // TODO: cleanup - protected override unsafe object[] GetHidDeviceKeys() + protected unsafe override object[] GetHidDeviceKeys() { - NativeMethods.libusb_init(IntPtr.Zero); - - var count = NativeMethods.libusb_get_device_list(IntPtr.Zero, out IntPtr deviceListRaw); - IntPtr[] deviceList = new IntPtr[count]; - Marshal.Copy(deviceListRaw, deviceList, 0, count); + var devices = new List(); - var list = new List(); + var devCount = libusb.get_device_list(IntPtr.Zero, out var deviceListRaw); + var deviceList = new IntPtr[devCount]; + Marshal.Copy(deviceListRaw, deviceList, 0, devCount); foreach (var device in deviceList) { - NativeMethods.libusb_get_device_descriptor(device, out var deviceDescriptor); - byte configCount = deviceDescriptor.bNumConfigurations; - for (byte configIndex = 0; configIndex < configCount; configIndex++) + libusb.get_device_descriptor(device, out var deviceDescriptor); + + if (libusb.open(device, out var deviceHandle) < 0) { - if (NativeMethods.libusb_get_config_descriptor(device, configIndex, out var configDescriptor) != NativeMethods.Error.None) - continue; + // invalid, skip + continue; + } - if (configDescriptor.bDescriptorType != (byte)NativeMethods.libusb_descriptor_type.LIBUSB_DT_CONFIG) - continue; + var configCount = deviceDescriptor.bNumConfigurations; - foreach (var iinterface in configDescriptor.interfaces) + for (byte configIndex = 0; configIndex < configCount; configIndex++) + { + libusb.get_config_descriptor(device, configIndex, out var configDescriptorPtr); + libusb_config_descriptor configDescriptor; + configDescriptor = (libusb_config_descriptor)Marshal.PtrToStructure(configDescriptorPtr, typeof(libusb_config_descriptor)); + + for (int interfaceIndex = 0; interfaceIndex < configDescriptor.bNumInterfaces; interfaceIndex++) { - foreach (var iinterfaceSetting in iinterface.altsetting) + var myInterface = (libusb_interface)Marshal.PtrToStructure(configDescriptor.interfaces + sizeof(libusb_interface) * interfaceIndex, typeof(libusb_interface)); + + for (int settingIndex = 0; settingIndex < myInterface.num_altsetting; settingIndex++) { - var endpointDict = new Dictionary(); - foreach (var endpoint in iinterfaceSetting.endpoints) + var mySetting = (libusb_interface_descriptor)Marshal.PtrToStructure(myInterface.altsetting + sizeof(libusb_interface_descriptor) * settingIndex, typeof(libusb_interface_descriptor)); + var endpointDict = new Dictionary(); + + for (int endpointIndex = 0; endpointIndex < mySetting.bNumEndpoints; endpointIndex++) { - var endpointAddress = (byte)(endpoint.bEndpointAddress & 0x0F); - var endpointDirection = ((endpoint.bEndpointAddress & 0x80) >> 7) == 1; - if (!endpointDict.ContainsKey(endpointAddress)) + var myEndpoint = (libusb_endpoint_descriptor)Marshal.PtrToStructure(mySetting.endpoints + sizeof(libusb_endpoint_descriptor) * endpointIndex, typeof(libusb_endpoint_descriptor)); + var direction = (myEndpoint.bEndpointAddress & (0x80)) == 0x80; + var address = myEndpoint.bEndpointAddress & (0x0F); + + if (!endpointDict.ContainsKey(address)) { - endpointDict.Add(endpointAddress, new NativeMethods.CombinedEndpoint(device, iinterfaceSetting.bInterfaceNumber, endpointAddress, - endpointDirection ? (UInt16)0 : endpoint.wMaxPacketSize, - endpointDirection ? endpoint.wMaxPacketSize : (UInt16)0)); + endpointDict[address] = new Endpoint(interfaceIndex, address); + endpointDict[address].SetReportLength(direction, myEndpoint.wMaxPacketSize); } else { - endpointDict[endpointAddress].SetPacketSize(endpointDirection, endpoint.wMaxPacketSize); + endpointDict[address].SetReportLength(direction, myEndpoint.wMaxPacketSize); } } + foreach (var endpoint in endpointDict.Values) { - list.Add(endpoint); + LibUsbDevice libusbdevice = new LibUsbDevice + { + Device = device, + VendorID = deviceDescriptor.idVendor, + ProductID = deviceDescriptor.idProduct, + Endpoint = endpoint + }; + devices.Add(libusbdevice); } } } } + libusb.close(deviceHandle); } - NativeMethods.libusb_free_device_list(deviceListRaw, 1); - return list.Cast().ToArray(); + libusb.free_device_list(deviceListRaw, 1); + return devices.Cast().ToArray(); } protected override object[] GetSerialDeviceKeys() @@ -100,7 +166,7 @@ protected override bool TryCreateBleDevice(object key, out Device device) protected override bool TryCreateHidDevice(object key, out Device device) { - device = LibusbHidDevice.TryCreate((NativeMethods.CombinedEndpoint)key); + device = LibusbHidDevice.TryCreate((LibUsbDevice)key); return device != null; } diff --git a/HidSharp/Platform/Libusb/LibusbHidStream.cs b/HidSharp/Platform/Libusb/LibusbHidStream.cs index badd76d..d17dc86 100644 --- a/HidSharp/Platform/Libusb/LibusbHidStream.cs +++ b/HidSharp/Platform/Libusb/LibusbHidStream.cs @@ -1,39 +1,53 @@ using System; using System.IO; +using System.Runtime.InteropServices; +using static HidSharp.Platform.Libusb.INativeMethods; namespace HidSharp.Platform.Libusb { - sealed class LibusbHidStream : SysHidStream + sealed class LibusbHidStream : SysHidStream where T : INativeMethods, new() { private IntPtr _handle; private byte _endpoint, _interfaceNum; private object _readsync = new object(); private object _writesync = new object(); + private T libusb = new T(); - internal LibusbHidStream(LibusbHidDevice device) : base(device) + internal LibusbHidStream(LibusbHidDevice device) : base(device) { } ~LibusbHidStream() { - NativeMethods.libusb_release_interface(_handle, _interfaceNum); - NativeMethods.libusb_close(_handle); + libusb.release_interface(_handle, _interfaceNum); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + // return device to kernel + libusb.attach_kernel_driver(_handle, _interfaceNum); + } + libusb.close(_handle); Close(); } // To follow HidSharp's structure and style internal void Init(IntPtr deviceHandle, byte interfaceNum, byte endpoint) { - var err = NativeMethods.libusb_claim_interface(deviceHandle, interfaceNum); - if (err != NativeMethods.Error.None) + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { - switch (err) + var retD = libusb.detach_kernel_driver(deviceHandle, interfaceNum); + if (retD < 0) { - case NativeMethods.Error.Busy: - throw new IOException("Device interface busy"); + throw new IOException("Failed to detach device interface from kernel. Reason: " + Enum.GetName(typeof(Error), retD)); } } + + var retI = libusb.claim_interface(deviceHandle, interfaceNum); + if (retI != Error.None) + { + throw new IOException("Failed to claim interface. Reason: " + Enum.GetName(typeof(Error), retI)); + } + _handle = deviceHandle; _interfaceNum = interfaceNum; _endpoint = endpoint; @@ -51,29 +65,15 @@ public override int Read(byte[] buffer, int offset, int count) int minIn = Device.GetMaxInputReportLength(); if (minIn <= 0) throw new IOException("Can't read from this device."); - NativeMethods.Error error = NativeMethods.Error.None; - try + Error error = Error.None; + lock (_readsync) { - lock(_readsync) + error = libusb.interrupt_transfer(_handle, endpointMode, buffer, count, ref transferred); + if (error < 0) { - error = NativeMethods.libusb_interrupt_transfer(_handle, endpointMode, buffer, count, ref transferred); - if (error != NativeMethods.Error.None) - { - throw new Exception(); - } - else - { - if (transferred == 0) - { - throw new IOException("Unexpected zero read."); - } - } - return transferred; + throw new IOException("Failed reading from device. Reason: " + Enum.GetName(typeof(Error), error)); } - } - catch - { - throw new IOException("Reading from device failed."); + return transferred; } } @@ -89,28 +89,15 @@ public override void Write(byte[] buffer, int offset, int count) int minOut = Device.GetMaxInputReportLength(); if (minOut <= 0) throw new IOException("Can't write to this device."); - try + + lock (_writesync) { - lock (_writesync) + Error error = libusb.interrupt_transfer(_handle, endpointMode, buffer, count, ref transferred); + if (error < 0) { - NativeMethods.Error error = NativeMethods.libusb_interrupt_transfer(_handle, endpointMode, buffer, count, ref transferred); - if (error != NativeMethods.Error.None) - { - throw new Exception(); - } - else - { - if (transferred == 0) - { - throw new IOException("Unexpected zero write."); - } - } + throw new IOException("Failed to write to device. Reason: " + Enum.GetName(typeof(Error), error)); } } - catch - { - throw new IOException("Writing to device failed."); - } } internal override void HandleFree() @@ -120,8 +107,12 @@ internal override void HandleFree() protected override void Dispose(bool disposing) { - NativeMethods.libusb_release_interface(_handle, _interfaceNum); - NativeMethods.libusb_close(_handle); + libusb.release_interface(_handle, _interfaceNum); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + libusb.attach_kernel_driver(_handle, _interfaceNum); + } + libusb.close(_handle); base.Dispose(disposing); } } diff --git a/HidSharp/Platform/Libusb/LinuxNativeMethods.cs b/HidSharp/Platform/Libusb/LinuxNativeMethods.cs new file mode 100644 index 0000000..317ccba --- /dev/null +++ b/HidSharp/Platform/Libusb/LinuxNativeMethods.cs @@ -0,0 +1,126 @@ +using System; +using System.Runtime.InteropServices; +using System.Text; +using static HidSharp.Platform.Libusb.INativeMethods; + +namespace HidSharp.Platform.Libusb +{ + internal class LinuxNativeMethods : INativeMethods + { + const CallingConvention convention = CallingConvention.Cdecl; + const string Libusb = "libusb-1.0.so.0"; + + [DllImport(Libusb, CallingConvention = convention)] + private static extern Error libusb_init(IntPtr context); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern void libusb_exit(IntPtr context); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern int libusb_get_device_list(IntPtr context, out IntPtr deviceList); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern void libusb_free_device_list(IntPtr deviceList, int unref); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern Error libusb_open(IntPtr device, out IntPtr deviceHandle); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern void libusb_close(IntPtr deviceHandle); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern Error libusb_get_device_descriptor(IntPtr device, out libusb_device_descriptor deviceDescriptor); + + [DllImport(Libusb, CharSet = CharSet.Ansi, CallingConvention = convention)] + private static extern int libusb_get_string_descriptor_ascii(IntPtr deviceHandle, byte index, StringBuilder data, int length); + + [DllImport(Libusb, CallingConvention = convention)] + private static unsafe extern Error libusb_get_config_descriptor(IntPtr device, byte configIndex, out IntPtr configDescriptor); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern Error libusb_interrupt_transfer(IntPtr deviceHandle, byte endpoint, byte[] data, int length, ref int actual_length, uint timeout = 0); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern Error libusb_claim_interface(IntPtr deviceHandle, int interfaceNum); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern Error libusb_release_interface(IntPtr deviceHandle, int interfaceNum); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern Error libusb_detach_kernel_driver(IntPtr deviceHandle, int interfaceNum); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern Error libusb_attach_kernel_driver(IntPtr deviceHandle, int interfaceNum); + + public Error init(IntPtr context) + { + return libusb_init(context); + } + + public void exit(IntPtr context) + { + libusb_exit(context); + } + + public int get_device_list(IntPtr context, out IntPtr deviceList) + { + return libusb_get_device_list(context, out deviceList); + } + + public void free_device_list(IntPtr deviceList, int unref) + { + libusb_free_device_list(deviceList, unref); + } + + public Error open(IntPtr device, out IntPtr deviceHandle) + { + return libusb_open(device, out deviceHandle); + } + + public void close(IntPtr deviceHandle) + { + libusb_close(deviceHandle); + } + + public Error get_device_descriptor(IntPtr device, out libusb_device_descriptor deviceDescriptor) + { + return libusb_get_device_descriptor(device, out deviceDescriptor); + } + + public int get_string_descriptor_ascii(IntPtr deviceHandle, byte index, StringBuilder data, int length) + { + return libusb_get_string_descriptor_ascii(deviceHandle, index, data, length); + } + + public Error get_config_descriptor(IntPtr device, byte configIndex, out IntPtr configDescriptor) + { + return libusb_get_config_descriptor(device, configIndex, out configDescriptor); + } + + public Error interrupt_transfer(IntPtr deviceHandle, byte endpoint, byte[] data, int length, ref int actual_length, uint timeout = 0) + { + return libusb_interrupt_transfer(deviceHandle, endpoint, data, length, ref actual_length, timeout); + } + + public Error claim_interface(IntPtr deviceHandle, int interfaceNum) + { + return libusb_claim_interface(deviceHandle, interfaceNum); + } + + public Error release_interface(IntPtr deviceHandle, int interfaceNum) + { + return libusb_release_interface(deviceHandle, interfaceNum); + } + + public Error detach_kernel_driver(IntPtr deviceHandle, int interfaceNum) + { + return libusb_detach_kernel_driver(deviceHandle, interfaceNum); + } + + public Error attach_kernel_driver(IntPtr deviceHandle, int interfaceNum) + { + return libusb_attach_kernel_driver(deviceHandle, interfaceNum); + } + } +} + diff --git a/HidSharp/Platform/Libusb/MacOSNativeMethods.cs b/HidSharp/Platform/Libusb/MacOSNativeMethods.cs new file mode 100644 index 0000000..0c48c30 --- /dev/null +++ b/HidSharp/Platform/Libusb/MacOSNativeMethods.cs @@ -0,0 +1,126 @@ +using System; +using System.Runtime.InteropServices; +using System.Text; +using static HidSharp.Platform.Libusb.INativeMethods; + +namespace HidSharp.Platform.Libusb +{ + internal class MacOSNativeMethods : INativeMethods + { + const CallingConvention convention = CallingConvention.Cdecl; + const string Libusb = "libusb-1.0.0.dylib"; + + [DllImport(Libusb, CallingConvention = convention)] + private static extern Error libusb_init(IntPtr context); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern void libusb_exit(IntPtr context); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern int libusb_get_device_list(IntPtr context, out IntPtr deviceList); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern void libusb_free_device_list(IntPtr deviceList, int unref); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern Error libusb_open(IntPtr device, out IntPtr deviceHandle); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern void libusb_close(IntPtr deviceHandle); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern Error libusb_get_device_descriptor(IntPtr device, out libusb_device_descriptor deviceDescriptor); + + [DllImport(Libusb, CharSet = CharSet.Ansi, CallingConvention = convention)] + private static extern int libusb_get_string_descriptor_ascii(IntPtr deviceHandle, byte index, StringBuilder data, int length); + + [DllImport(Libusb, CallingConvention = convention)] + private static unsafe extern Error libusb_get_config_descriptor(IntPtr device, byte configIndex, out IntPtr configDescriptor); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern Error libusb_interrupt_transfer(IntPtr deviceHandle, byte endpoint, byte[] data, int length, ref int actual_length, uint timeout = 0); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern Error libusb_claim_interface(IntPtr deviceHandle, int interfaceNum); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern Error libusb_release_interface(IntPtr deviceHandle, int interfaceNum); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern Error libusb_detach_kernel_driver(IntPtr deviceHandle, int interfaceNum); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern Error libusb_attach_kernel_driver(IntPtr deviceHandle, int interfaceNum); + + public Error init(IntPtr context) + { + return libusb_init(context); + } + + public void exit(IntPtr context) + { + libusb_exit(context); + } + + public int get_device_list(IntPtr context, out IntPtr deviceList) + { + return libusb_get_device_list(context, out deviceList); + } + + public void free_device_list(IntPtr deviceList, int unref) + { + libusb_free_device_list(deviceList, unref); + } + + public Error open(IntPtr device, out IntPtr deviceHandle) + { + return libusb_open(device, out deviceHandle); + } + + public void close(IntPtr deviceHandle) + { + libusb_close(deviceHandle); + } + + public Error get_device_descriptor(IntPtr device, out libusb_device_descriptor deviceDescriptor) + { + return libusb_get_device_descriptor(device, out deviceDescriptor); + } + + public int get_string_descriptor_ascii(IntPtr deviceHandle, byte index, StringBuilder data, int length) + { + return libusb_get_string_descriptor_ascii(deviceHandle, index, data, length); + } + + public Error get_config_descriptor(IntPtr device, byte configIndex, out IntPtr configDescriptor) + { + return libusb_get_config_descriptor(device, configIndex, out configDescriptor); + } + + public Error interrupt_transfer(IntPtr deviceHandle, byte endpoint, byte[] data, int length, ref int actual_length, uint timeout = 0) + { + return libusb_interrupt_transfer(deviceHandle, endpoint, data, length, ref actual_length, timeout); + } + + public Error claim_interface(IntPtr deviceHandle, int interfaceNum) + { + return libusb_claim_interface(deviceHandle, interfaceNum); + } + + public Error release_interface(IntPtr deviceHandle, int interfaceNum) + { + return libusb_release_interface(deviceHandle, interfaceNum); + } + + public Error detach_kernel_driver(IntPtr deviceHandle, int interfaceNum) + { + return libusb_detach_kernel_driver(deviceHandle, interfaceNum); + } + + public Error attach_kernel_driver(IntPtr deviceHandle, int interfaceNum) + { + return libusb_attach_kernel_driver(deviceHandle, interfaceNum); + } + } +} + diff --git a/HidSharp/Platform/Libusb/NativeMethods.cs b/HidSharp/Platform/Libusb/NativeMethods.cs deleted file mode 100644 index 6463533..0000000 --- a/HidSharp/Platform/Libusb/NativeMethods.cs +++ /dev/null @@ -1,200 +0,0 @@ -#region License -/* Copyright 2012 James F. Bellinger - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. */ -#endregion - -using System; -using System.Runtime.InteropServices; -using System.Text; - -namespace HidSharp.Platform.Libusb -{ - static class NativeMethods - { - - // const string Libusb = "libusb-1.0.so.0"; - const string Libusb = "libusb-1.0.dll"; - - public enum Error - { - None = 0, - IO = -1, - InvalidParameter = -2, - AccessDenied = -3, - NoDevice = -4, - NotFound = -5, - Busy = -6, - Timeout = -7, - Overflow = -8, - Pipe = -9, - Interrupted = -10, - OutOfMemory = -11, - NotSupported = -12, - Other = -99 - } - - public enum libusb_descriptor_type - { - LIBUSB_DT_DEVICE = 0x01, - LIBUSB_DT_CONFIG = 0x02, - LIBUSB_DT_STRING = 0x03, - LIBUSB_DT_INTERFACE = 0x04, - LIBUSB_DT_ENDPOINT = 0x05, - LIBUSB_DT_BOS = 0x0f, - LIBUSB_DT_DEVICE_CAPABILITY = 0x10, - LIBUSB_DT_HID = 0x21, - LIBUSB_DT_REPORT = 0x22, - LIBUSB_DT_PHYSICAL = 0x23, - LIBUSB_DT_HUB = 0x29, - LIBUSB_DT_SUPERSPEED_HUB = 0x2a, - LIBUSB_DT_SS_ENDPOINT_COMPANION = 0x30 - } - - [StructLayout(LayoutKind.Sequential)] - public struct libusb_device_descriptor - { - public byte bLength; - public byte bDescriptorType; - public UInt16 bcdUSB; - public byte bDeviceClass; - public byte bDeviceSubClass; - public byte bDeviceProtocol; - public byte bMaxPacketSize0; - public UInt16 idVendor; - public UInt16 idProduct; - public UInt16 bcdDevice; - public byte iManufacturer; - public byte iProduct; - public byte iSerialNumber; - public byte bNumConfigurations; - } - - [StructLayout(LayoutKind.Sequential)] - public struct libusb_endpoint_descriptor - { - public byte bLength; - public byte bDescriptorType; - public byte bEndpointAddress; - public byte bmAttributes; - public UInt16 wMaxPacketSize; - public byte bInterval; - public byte bRefresh; - public byte bSynchAddress; - public IntPtr extra; - public int extra_length; - } - - [StructLayout(LayoutKind.Sequential)] - public struct libusb_interface_descriptor - { - public byte bLength; - public byte bDescriptorType; - public byte bInterfaceNumber; - public byte bAlternateSetting; - public byte bNumEndpoints; - public byte bInterfaceClass; - public byte bInterfaceSubClass; - public byte bInterfaceProtocol; - public byte iInterface; - public libusb_endpoint_descriptor[] endpoints; - public IntPtr extra; - public int extra_length; - } - - [StructLayout(LayoutKind.Sequential)] - public struct libusb_interface - { - public libusb_interface_descriptor[] altsetting; - public int num_altsetting; - } - - [StructLayout(LayoutKind.Sequential)] - public struct libusb_config_descriptor - { - public byte bLength; - public byte bDescriptorType; - public UInt16 wTotalLength; - public byte bNumInterfaces; - public byte bConfigurationValue; - public byte iConfiguration; - public byte bmAttributes; - public byte MaxPower; - public libusb_interface[] interfaces; - public IntPtr extra; - public int extra_length; - } - - public struct CombinedEndpoint - { - public IntPtr DevicePtr; - public byte InterfaceNum; - public byte Address; - public UInt16 MaxOutputPacketSize; - public UInt16 MaxInputPacketSize; - public CombinedEndpoint(IntPtr devicePtr, byte interfaceNum, byte endpoint, UInt16 maxOutputPacketSize, UInt16 maxInputPacketSize) - { - DevicePtr = devicePtr; - InterfaceNum = interfaceNum; - Address = endpoint; - MaxOutputPacketSize = maxOutputPacketSize; - MaxInputPacketSize = maxInputPacketSize; - } - public void SetPacketSize(bool inDirection, UInt16 maxPacketSize) - { - if (inDirection) - MaxInputPacketSize = maxPacketSize; - else - MaxOutputPacketSize = maxPacketSize; - } - } - - [DllImport(Libusb)] - public static extern Error libusb_init(IntPtr context); - - [DllImport(Libusb)] - public static extern void libusb_exit(IntPtr context); - - [DllImport(Libusb)] - public static extern int libusb_get_device_list(IntPtr context, out IntPtr deviceList); - - [DllImport(Libusb)] - public static extern void libusb_free_device_list(IntPtr deviceList, int unref); - - [DllImport(Libusb)] - public static extern Error libusb_open(IntPtr device, out IntPtr deviceHandle); - - [DllImport(Libusb)] - public static extern void libusb_close(IntPtr deviceHandle); - - [DllImport(Libusb)] - public static extern Error libusb_get_device_descriptor(IntPtr device, out libusb_device_descriptor deviceDescriptor); - - [DllImport(Libusb)] - public static extern int libusb_get_string_descriptor_ascii(IntPtr deviceHandle, byte index, StringBuilder data, int length); - - [DllImport(Libusb)] - public static unsafe extern Error libusb_get_config_descriptor(IntPtr device, byte configIndex, out libusb_config_descriptor configDescriptor); - - [DllImport(Libusb)] - public static extern Error libusb_interrupt_transfer(IntPtr deviceHandle, byte endpoint, byte[] data, int length, ref int actual_length, uint timeout = 0); - - [DllImport(Libusb)] - public static extern Error libusb_claim_interface(IntPtr deviceHandle, int interfaceNum); - - [DllImport(Libusb)] - public static extern Error libusb_release_interface(IntPtr deviceHandle, int interfaceNum); - } -} - diff --git a/HidSharp/Platform/Libusb/WinNativeMethods.cs b/HidSharp/Platform/Libusb/WinNativeMethods.cs new file mode 100644 index 0000000..c2bcdd9 --- /dev/null +++ b/HidSharp/Platform/Libusb/WinNativeMethods.cs @@ -0,0 +1,126 @@ +using System; +using System.Runtime.InteropServices; +using System.Text; +using static HidSharp.Platform.Libusb.INativeMethods; + +namespace HidSharp.Platform.Libusb +{ + internal class WinNativeMethods : INativeMethods + { + const CallingConvention convention = CallingConvention.StdCall; + const string Libusb = "libusb-1.0.dll"; + + [DllImport(Libusb, CallingConvention = convention)] + private static extern Error libusb_init(IntPtr context); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern void libusb_exit(IntPtr context); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern int libusb_get_device_list(IntPtr context, out IntPtr deviceList); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern void libusb_free_device_list(IntPtr deviceList, int unref); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern Error libusb_open(IntPtr device, out IntPtr deviceHandle); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern void libusb_close(IntPtr deviceHandle); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern Error libusb_get_device_descriptor(IntPtr device, out libusb_device_descriptor deviceDescriptor); + + [DllImport(Libusb, CharSet = CharSet.Ansi, CallingConvention = convention)] + private static extern int libusb_get_string_descriptor_ascii(IntPtr deviceHandle, byte index, StringBuilder data, int length); + + [DllImport(Libusb, CallingConvention = convention)] + private static unsafe extern Error libusb_get_config_descriptor(IntPtr device, byte configIndex, out IntPtr configDescriptor); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern Error libusb_interrupt_transfer(IntPtr deviceHandle, byte endpoint, byte[] data, int length, ref int actual_length, uint timeout = 0); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern Error libusb_claim_interface(IntPtr deviceHandle, int interfaceNum); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern Error libusb_release_interface(IntPtr deviceHandle, int interfaceNum); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern Error libusb_detach_kernel_driver(IntPtr deviceHandle, int interfaceNum); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern Error libusb_attach_kernel_driver(IntPtr deviceHandle, int interfaceNum); + + public Error init(IntPtr context) + { + return libusb_init(context); + } + + public void exit(IntPtr context) + { + libusb_exit(context); + } + + public int get_device_list(IntPtr context, out IntPtr deviceList) + { + return libusb_get_device_list(context, out deviceList); + } + + public void free_device_list(IntPtr deviceList, int unref) + { + libusb_free_device_list(deviceList, unref); + } + + public Error open(IntPtr device, out IntPtr deviceHandle) + { + return libusb_open(device, out deviceHandle); + } + + public void close(IntPtr deviceHandle) + { + libusb_close(deviceHandle); + } + + public Error get_device_descriptor(IntPtr device, out libusb_device_descriptor deviceDescriptor) + { + return libusb_get_device_descriptor(device, out deviceDescriptor); + } + + public int get_string_descriptor_ascii(IntPtr deviceHandle, byte index, StringBuilder data, int length) + { + return libusb_get_string_descriptor_ascii(deviceHandle, index, data, length); + } + + public Error get_config_descriptor(IntPtr device, byte configIndex, out IntPtr configDescriptor) + { + return libusb_get_config_descriptor(device, configIndex, out configDescriptor); + } + + public Error interrupt_transfer(IntPtr deviceHandle, byte endpoint, byte[] data, int length, ref int actual_length, uint timeout = 0) + { + return libusb_interrupt_transfer(deviceHandle, endpoint, data, length, ref actual_length, timeout); + } + + public Error claim_interface(IntPtr deviceHandle, int interfaceNum) + { + return libusb_claim_interface(deviceHandle, interfaceNum); + } + + public Error release_interface(IntPtr deviceHandle, int interfaceNum) + { + return libusb_release_interface(deviceHandle, interfaceNum); + } + + public Error detach_kernel_driver(IntPtr deviceHandle, int interfaceNum) + { + return libusb_detach_kernel_driver(deviceHandle, interfaceNum); + } + + public Error attach_kernel_driver(IntPtr deviceHandle, int interfaceNum) + { + return libusb_attach_kernel_driver(deviceHandle, interfaceNum); + } + } +} + From 83265be482a94400c13463160d4996f5f847777f Mon Sep 17 00:00:00 2001 From: X9VoiD Date: Mon, 10 Aug 2020 18:51:24 +0800 Subject: [PATCH 04/17] Simplify libusb support detection --- HidSharp/Platform/Libusb/LibusbHidManager.cs | 39 ++------------------ 1 file changed, 4 insertions(+), 35 deletions(-) diff --git a/HidSharp/Platform/Libusb/LibusbHidManager.cs b/HidSharp/Platform/Libusb/LibusbHidManager.cs index 1dd763e..1c268ee 100644 --- a/HidSharp/Platform/Libusb/LibusbHidManager.cs +++ b/HidSharp/Platform/Libusb/LibusbHidManager.cs @@ -31,43 +31,12 @@ public override bool IsSupported { get { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + try { - try - { - Marshal.PrelinkAll(typeof(WinNativeMethods)); - return true; - } - catch - { - return false; - } - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - try - { - Marshal.PrelinkAll(typeof(LinuxNativeMethods)); - return true; - } - catch - { - return false; - } - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - try - { - Marshal.PrelinkAll(typeof(MacOSNativeMethods)); - return true; - } - catch - { - return false; - } + Marshal.PrelinkAll(typeof(T)); + return true; } - else + catch { return false; } From ec6659e18da3f95220d8c28770afaad6fe0af915 Mon Sep 17 00:00:00 2001 From: X9VoiD Date: Mon, 10 Aug 2020 19:09:51 +0800 Subject: [PATCH 05/17] Initialize libusb --- HidSharp/Platform/Libusb/LibusbHidManager.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/HidSharp/Platform/Libusb/LibusbHidManager.cs b/HidSharp/Platform/Libusb/LibusbHidManager.cs index 1c268ee..a4258fe 100644 --- a/HidSharp/Platform/Libusb/LibusbHidManager.cs +++ b/HidSharp/Platform/Libusb/LibusbHidManager.cs @@ -45,6 +45,11 @@ public override bool IsSupported public T libusb = new T(); + public LibusbHidManager() + { + libusb.init(IntPtr.Zero); + } + protected override object[] GetBleDeviceKeys() { throw new NotImplementedException(); From b681aca40f97124e42de2b993e800a1677de231c Mon Sep 17 00:00:00 2001 From: X9VoiD Date: Mon, 10 Aug 2020 21:05:24 +0800 Subject: [PATCH 06/17] Make loading HidManager safe --- HidSharp/Platform/HidSelector.cs | 33 ++++++++++----- HidSharp/Platform/Libusb/LibusbHidManager.cs | 43 +++++++++++--------- 2 files changed, 45 insertions(+), 31 deletions(-) diff --git a/HidSharp/Platform/HidSelector.cs b/HidSharp/Platform/HidSelector.cs index 80049ca..be42e08 100644 --- a/HidSharp/Platform/HidSelector.cs +++ b/HidSharp/Platform/HidSelector.cs @@ -15,6 +15,7 @@ specific language governing permissions and limitations under the License. */ #endregion +using System; using System.Threading; namespace HidSharp.Platform @@ -22,21 +23,31 @@ namespace HidSharp.Platform sealed class HidSelector { public static readonly HidManager Instance; - static readonly Thread ManagerThread; + static readonly Thread ManagerThread; static HidSelector() { - foreach (var hidManager in new HidManager[] - { - new Libusb.LibusbHidManager(), - new Libusb.LibusbHidManager(), - new Libusb.LibusbHidManager(), - // new Windows.WinHidManager(), - // new Linux.LinuxHidManager(), - // new MacOS.MacHidManager(), - new Unsupported.UnsupportedHidManager() - }) + var hidManagerList = new Type[] + { + typeof(Libusb.LibusbHidManager), + typeof(Libusb.LibusbHidManager), + typeof(Libusb.LibusbHidManager), + typeof(Windows.WinHidManager), + typeof(Linux.LinuxHidManager), + typeof(MacOS.MacHidManager), + typeof(Unsupported.UnsupportedHidManager) + }; + + foreach (var hidManagerType in hidManagerList) { + HidManager hidManager; + + try + { + hidManager = (HidManager)Activator.CreateInstance(hidManagerType); + } + catch { continue; } + if (hidManager.IsSupported) { var readyEvent = new ManualResetEvent(false); diff --git a/HidSharp/Platform/Libusb/LibusbHidManager.cs b/HidSharp/Platform/Libusb/LibusbHidManager.cs index a4258fe..095c36a 100644 --- a/HidSharp/Platform/Libusb/LibusbHidManager.cs +++ b/HidSharp/Platform/Libusb/LibusbHidManager.cs @@ -25,29 +25,34 @@ namespace HidSharp.Platform.Libusb { sealed class LibusbHidManager : HidManager where T : INativeMethods, new() { + private bool _isSupported; + + private IntPtr _deviceListRaw; + + private int _devCount; + public override string FriendlyName => "Libusb HID"; - public override bool IsSupported - { - get - { - try - { - Marshal.PrelinkAll(typeof(T)); - return true; - } - catch - { - return false; - } - } - } + public override bool IsSupported { get => _isSupported; } public T libusb = new T(); public LibusbHidManager() { - libusb.init(IntPtr.Zero); + try + { + Marshal.PrelinkAll(typeof(T)); + libusb.init(IntPtr.Zero); + _isSupported = true; + } + catch + { + _isSupported = false; + } + + // Cache device list since this is slow + // TODO: Hotplug Events to update device list + _devCount = libusb.get_device_list(IntPtr.Zero, out _deviceListRaw); } protected override object[] GetBleDeviceKeys() @@ -60,9 +65,8 @@ protected unsafe override object[] GetHidDeviceKeys() { var devices = new List(); - var devCount = libusb.get_device_list(IntPtr.Zero, out var deviceListRaw); - var deviceList = new IntPtr[devCount]; - Marshal.Copy(deviceListRaw, deviceList, 0, devCount); + var deviceList = new IntPtr[_devCount]; + Marshal.Copy(_deviceListRaw, deviceList, 0, _devCount); foreach (var device in deviceList) { @@ -124,7 +128,6 @@ protected unsafe override object[] GetHidDeviceKeys() } libusb.close(deviceHandle); } - libusb.free_device_list(deviceListRaw, 1); return devices.Cast().ToArray(); } From 54da51fc7f312030c77d750cc9fbe242c334ba48 Mon Sep 17 00:00:00 2001 From: X9VoiD Date: Tue, 11 Aug 2020 14:13:53 +0800 Subject: [PATCH 07/17] Optimize HidSelector --- HidSharp/Platform/HidSelector.cs | 37 ++++++++++++++++---------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/HidSharp/Platform/HidSelector.cs b/HidSharp/Platform/HidSelector.cs index be42e08..8215f89 100644 --- a/HidSharp/Platform/HidSelector.cs +++ b/HidSharp/Platform/HidSelector.cs @@ -15,7 +15,6 @@ specific language governing permissions and limitations under the License. */ #endregion -using System; using System.Threading; namespace HidSharp.Platform @@ -25,33 +24,33 @@ sealed class HidSelector public static readonly HidManager Instance; static readonly Thread ManagerThread; + internal static T SafeCreate() where T : class, new() + { + try + { + return new T(); + } + catch { return null; } + } + static HidSelector() { - var hidManagerList = new Type[] + var hidManagerList = new HidManager[] { - typeof(Libusb.LibusbHidManager), - typeof(Libusb.LibusbHidManager), - typeof(Libusb.LibusbHidManager), - typeof(Windows.WinHidManager), - typeof(Linux.LinuxHidManager), - typeof(MacOS.MacHidManager), - typeof(Unsupported.UnsupportedHidManager) + SafeCreate>(), + SafeCreate>(), + SafeCreate>(), + SafeCreate(), + SafeCreate(), + SafeCreate(), + SafeCreate() }; - foreach (var hidManagerType in hidManagerList) + foreach (var hidManager in hidManagerList) { - HidManager hidManager; - - try - { - hidManager = (HidManager)Activator.CreateInstance(hidManagerType); - } - catch { continue; } - if (hidManager.IsSupported) { var readyEvent = new ManualResetEvent(false); - Instance = hidManager; Instance.InitializeEventManager(); ManagerThread = new Thread(Instance.RunImpl) { IsBackground = true, Name = "HID Manager" }; From 9205935cbf5c3695550794cf4d47c9765468fde5 Mon Sep 17 00:00:00 2001 From: X9VoiD Date: Tue, 11 Aug 2020 23:10:31 +0800 Subject: [PATCH 08/17] Add hotplug support --- HidSharp/Platform/HidSelector.cs | 2 +- HidSharp/Platform/Libusb/INativeMethods.cs | 18 ++ HidSharp/Platform/Libusb/LibusbHidManager.cs | 156 ++++++++++++------ .../Platform/Libusb/LinuxNativeMethods.cs | 16 ++ .../Platform/Libusb/MacOSNativeMethods.cs | 16 ++ HidSharp/Platform/Libusb/WinNativeMethods.cs | 16 ++ 6 files changed, 168 insertions(+), 56 deletions(-) diff --git a/HidSharp/Platform/HidSelector.cs b/HidSharp/Platform/HidSelector.cs index 8215f89..4b6aa8f 100644 --- a/HidSharp/Platform/HidSelector.cs +++ b/HidSharp/Platform/HidSelector.cs @@ -48,7 +48,7 @@ static HidSelector() foreach (var hidManager in hidManagerList) { - if (hidManager.IsSupported) + if (hidManager != null && hidManager.IsSupported) { var readyEvent = new ManualResetEvent(false); Instance = hidManager; diff --git a/HidSharp/Platform/Libusb/INativeMethods.cs b/HidSharp/Platform/Libusb/INativeMethods.cs index 2d69a61..150c34f 100644 --- a/HidSharp/Platform/Libusb/INativeMethods.cs +++ b/HidSharp/Platform/Libusb/INativeMethods.cs @@ -75,6 +75,18 @@ public enum libusb_descriptor_type LIBUSB_DT_SS_ENDPOINT_COMPANION = 0x30 } + public enum HotplugEvent + { + Arrived = 0x01, + Left = 0x02 + } + + public enum HotplugFlag + { + NoFlags, + Enumerate + } + [StructLayout(LayoutKind.Sequential)] public struct libusb_device_descriptor { @@ -153,6 +165,8 @@ public struct libusb_config_descriptor public int extra_length; } + public delegate int libusb_hotplug_delegate(IntPtr ctx, IntPtr device, HotplugEvent hotplugEvent, IntPtr user_data); + public Error init(IntPtr context); public void exit(IntPtr context); @@ -180,5 +194,9 @@ public struct libusb_config_descriptor public Error detach_kernel_driver(IntPtr deviceHandle, int interfaceNum); public Error attach_kernel_driver(IntPtr deviceHandle, int interfaceNum); + + public Error hotplug_register_callback(IntPtr ctx, HotplugEvent events, HotplugFlag flags, int vendorId, int productId, int devClass, libusb_hotplug_delegate callback, IntPtr userData, ref int callbackHandle); + + public void hotplug_deregister_callback(IntPtr ctx, int callbackHandle); } } \ No newline at end of file diff --git a/HidSharp/Platform/Libusb/LibusbHidManager.cs b/HidSharp/Platform/Libusb/LibusbHidManager.cs index 095c36a..3b027ec 100644 --- a/HidSharp/Platform/Libusb/LibusbHidManager.cs +++ b/HidSharp/Platform/Libusb/LibusbHidManager.cs @@ -27,15 +27,19 @@ namespace HidSharp.Platform.Libusb { private bool _isSupported; - private IntPtr _deviceListRaw; - - private int _devCount; + private Dictionary> _devices = new Dictionary>(); public override string FriendlyName => "Libusb HID"; public override bool IsSupported { get => _isSupported; } - public T libusb = new T(); + private readonly T libusb = new T(); + + private static libusb_hotplug_delegate hotplugDelegate; + + private int callbackHandle; + + private object _deviceLock = new object(); public LibusbHidManager() { @@ -51,84 +55,126 @@ public LibusbHidManager() } // Cache device list since this is slow - // TODO: Hotplug Events to update device list - _devCount = libusb.get_device_list(IntPtr.Zero, out _deviceListRaw); + var _devCount = libusb.get_device_list(IntPtr.Zero, out var _deviceListRaw); + var deviceList = new IntPtr[_devCount]; + Marshal.Copy(_deviceListRaw, deviceList, 0, _devCount); + + foreach (var device in deviceList) + { + CreateDevice(device, _devices); + } + hotplugDelegate = new libusb_hotplug_delegate(HotPlug); + + var ret = libusb.hotplug_register_callback(IntPtr.Zero, HotplugEvent.Arrived | HotplugEvent.Left, HotplugFlag.Enumerate, -1, -1, -1, hotplugDelegate, IntPtr.Zero, ref callbackHandle); + if (ret < 0) + throw new ExternalException("Failed to register for libusb hotplug. Reason: " + Enum.GetName(typeof(Error), ret)); } - protected override object[] GetBleDeviceKeys() + private int HotPlug(IntPtr ctx, IntPtr device, HotplugEvent hotplugEvent, IntPtr user_data) { - throw new NotImplementedException(); + switch (hotplugEvent) + { + case HotplugEvent.Arrived: + lock(_deviceLock) + { + CreateDevice(device, _devices); + DeviceList.Local.RaiseChanged(); + } + break; + case HotplugEvent.Left: + lock(_deviceLock) + { + _devices.Remove(device); + DeviceList.Local.RaiseChanged(); + } + break; + } + return 0; } - // TODO: cleanup - protected unsafe override object[] GetHidDeviceKeys() + internal unsafe void CreateDevice(IntPtr device, Dictionary> deviceList) { - var devices = new List(); - - var deviceList = new IntPtr[_devCount]; - Marshal.Copy(_deviceListRaw, deviceList, 0, _devCount); + libusb.get_device_descriptor(device, out var deviceDescriptor); - foreach (var device in deviceList) + if (libusb.open(device, out var deviceHandle) < 0) { - libusb.get_device_descriptor(device, out var deviceDescriptor); + // invalid, skip + return; + } - if (libusb.open(device, out var deviceHandle) < 0) - { - // invalid, skip - continue; - } + var configCount = deviceDescriptor.bNumConfigurations; - var configCount = deviceDescriptor.bNumConfigurations; + for (byte configIndex = 0; configIndex < configCount; configIndex++) + { + libusb.get_config_descriptor(device, configIndex, out var configDescriptorPtr); + libusb_config_descriptor configDescriptor; + configDescriptor = (libusb_config_descriptor)Marshal.PtrToStructure(configDescriptorPtr, typeof(libusb_config_descriptor)); - for (byte configIndex = 0; configIndex < configCount; configIndex++) + for (int interfaceIndex = 0; interfaceIndex < configDescriptor.bNumInterfaces; interfaceIndex++) { - libusb.get_config_descriptor(device, configIndex, out var configDescriptorPtr); - libusb_config_descriptor configDescriptor; - configDescriptor = (libusb_config_descriptor)Marshal.PtrToStructure(configDescriptorPtr, typeof(libusb_config_descriptor)); + var myInterface = (libusb_interface)Marshal.PtrToStructure(configDescriptor.interfaces + sizeof(libusb_interface) * interfaceIndex, typeof(libusb_interface)); - for (int interfaceIndex = 0; interfaceIndex < configDescriptor.bNumInterfaces; interfaceIndex++) + for (int settingIndex = 0; settingIndex < myInterface.num_altsetting; settingIndex++) { - var myInterface = (libusb_interface)Marshal.PtrToStructure(configDescriptor.interfaces + sizeof(libusb_interface) * interfaceIndex, typeof(libusb_interface)); + var mySetting = (libusb_interface_descriptor)Marshal.PtrToStructure(myInterface.altsetting + sizeof(libusb_interface_descriptor) * settingIndex, typeof(libusb_interface_descriptor)); + var endpointDict = new Dictionary(); - for (int settingIndex = 0; settingIndex < myInterface.num_altsetting; settingIndex++) + for (int endpointIndex = 0; endpointIndex < mySetting.bNumEndpoints; endpointIndex++) { - var mySetting = (libusb_interface_descriptor)Marshal.PtrToStructure(myInterface.altsetting + sizeof(libusb_interface_descriptor) * settingIndex, typeof(libusb_interface_descriptor)); - var endpointDict = new Dictionary(); + var myEndpoint = (libusb_endpoint_descriptor)Marshal.PtrToStructure(mySetting.endpoints + sizeof(libusb_endpoint_descriptor) * endpointIndex, typeof(libusb_endpoint_descriptor)); + var direction = (myEndpoint.bEndpointAddress & (0x80)) == 0x80; + var address = myEndpoint.bEndpointAddress & (0x0F); - for (int endpointIndex = 0; endpointIndex < mySetting.bNumEndpoints; endpointIndex++) + if (!endpointDict.ContainsKey(address)) { - var myEndpoint = (libusb_endpoint_descriptor)Marshal.PtrToStructure(mySetting.endpoints + sizeof(libusb_endpoint_descriptor) * endpointIndex, typeof(libusb_endpoint_descriptor)); - var direction = (myEndpoint.bEndpointAddress & (0x80)) == 0x80; - var address = myEndpoint.bEndpointAddress & (0x0F); - - if (!endpointDict.ContainsKey(address)) - { - endpointDict[address] = new Endpoint(interfaceIndex, address); - endpointDict[address].SetReportLength(direction, myEndpoint.wMaxPacketSize); - } - else - { - endpointDict[address].SetReportLength(direction, myEndpoint.wMaxPacketSize); - } + endpointDict[address] = new Endpoint(interfaceIndex, address); + endpointDict[address].SetReportLength(direction, myEndpoint.wMaxPacketSize); + } + else + { + endpointDict[address].SetReportLength(direction, myEndpoint.wMaxPacketSize); } + } - foreach (var endpoint in endpointDict.Values) + foreach (var endpoint in endpointDict.Values) + { + LibUsbDevice libusbdevice = new LibUsbDevice + { + Device = device, + VendorID = deviceDescriptor.idVendor, + ProductID = deviceDescriptor.idProduct, + Endpoint = endpoint + }; + lock(_deviceLock) { - LibUsbDevice libusbdevice = new LibUsbDevice + if (!deviceList.ContainsKey(device)) { - Device = device, - VendorID = deviceDescriptor.idVendor, - ProductID = deviceDescriptor.idProduct, - Endpoint = endpoint - }; - devices.Add(libusbdevice); + deviceList.Add(device, new List()); + } + deviceList[device].Add(libusbdevice); } } } } - libusb.close(deviceHandle); } - return devices.Cast().ToArray(); + libusb.close(deviceHandle); + return; + } + + protected override object[] GetBleDeviceKeys() + { + throw new NotImplementedException(); + } + + // TODO: cleanup + protected unsafe override object[] GetHidDeviceKeys() + { + var list = new List(); + foreach (var deviceList in _devices.Values) + foreach (var device in deviceList) + list.Add(device); + + return _devices.Values.Cast().ToArray(); } protected override object[] GetSerialDeviceKeys() diff --git a/HidSharp/Platform/Libusb/LinuxNativeMethods.cs b/HidSharp/Platform/Libusb/LinuxNativeMethods.cs index 317ccba..3a027f0 100644 --- a/HidSharp/Platform/Libusb/LinuxNativeMethods.cs +++ b/HidSharp/Platform/Libusb/LinuxNativeMethods.cs @@ -52,6 +52,12 @@ internal class LinuxNativeMethods : INativeMethods [DllImport(Libusb, CallingConvention = convention)] private static extern Error libusb_attach_kernel_driver(IntPtr deviceHandle, int interfaceNum); + [DllImport(Libusb, CallingConvention = convention)] + private static extern Error libusb_hotplug_register_callback(IntPtr ctx, HotplugEvent events, HotplugFlag flags, int vendorId, int productId, int devClass, libusb_hotplug_delegate callback, IntPtr userData, ref int callbackHandle); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern void libusb_hotplug_deregister_callback(IntPtr ctx, int callbackHandle); + public Error init(IntPtr context) { return libusb_init(context); @@ -121,6 +127,16 @@ public Error attach_kernel_driver(IntPtr deviceHandle, int interfaceNum) { return libusb_attach_kernel_driver(deviceHandle, interfaceNum); } + + public Error hotplug_register_callback(IntPtr ctx, HotplugEvent events, HotplugFlag flags, int vendorId, int productId, int devClass, libusb_hotplug_delegate callback, IntPtr userData, ref int callbackHandle) + { + return hotplug_register_callback(ctx, events, flags, vendorId, productId, devClass, callback, userData, ref callbackHandle); + } + + public void hotplug_deregister_callback(IntPtr ctx, int callbackHandle) + { + libusb_hotplug_deregister_callback(ctx, callbackHandle); + } } } diff --git a/HidSharp/Platform/Libusb/MacOSNativeMethods.cs b/HidSharp/Platform/Libusb/MacOSNativeMethods.cs index 0c48c30..ae3263f 100644 --- a/HidSharp/Platform/Libusb/MacOSNativeMethods.cs +++ b/HidSharp/Platform/Libusb/MacOSNativeMethods.cs @@ -52,6 +52,12 @@ internal class MacOSNativeMethods : INativeMethods [DllImport(Libusb, CallingConvention = convention)] private static extern Error libusb_attach_kernel_driver(IntPtr deviceHandle, int interfaceNum); + [DllImport(Libusb, CallingConvention = convention)] + private static extern Error libusb_hotplug_register_callback(IntPtr ctx, HotplugEvent events, HotplugFlag flags, int vendorId, int productId, int devClass, libusb_hotplug_delegate callback, IntPtr userData, ref int callbackHandle); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern void libusb_hotplug_deregister_callback(IntPtr ctx, int callbackHandle); + public Error init(IntPtr context) { return libusb_init(context); @@ -121,6 +127,16 @@ public Error attach_kernel_driver(IntPtr deviceHandle, int interfaceNum) { return libusb_attach_kernel_driver(deviceHandle, interfaceNum); } + + public Error hotplug_register_callback(IntPtr ctx, HotplugEvent events, HotplugFlag flags, int vendorId, int productId, int devClass, libusb_hotplug_delegate callback, IntPtr userData, ref int callbackHandle) + { + return hotplug_register_callback(ctx, events, flags, vendorId, productId, devClass, callback, userData, ref callbackHandle); + } + + public void hotplug_deregister_callback(IntPtr ctx, int callbackHandle) + { + libusb_hotplug_deregister_callback(ctx, callbackHandle); + } } } diff --git a/HidSharp/Platform/Libusb/WinNativeMethods.cs b/HidSharp/Platform/Libusb/WinNativeMethods.cs index c2bcdd9..f489cad 100644 --- a/HidSharp/Platform/Libusb/WinNativeMethods.cs +++ b/HidSharp/Platform/Libusb/WinNativeMethods.cs @@ -52,6 +52,12 @@ internal class WinNativeMethods : INativeMethods [DllImport(Libusb, CallingConvention = convention)] private static extern Error libusb_attach_kernel_driver(IntPtr deviceHandle, int interfaceNum); + [DllImport(Libusb, CallingConvention = convention)] + private static extern Error libusb_hotplug_register_callback(IntPtr ctx, HotplugEvent events, HotplugFlag flags, int vendorId, int productId, int devClass, libusb_hotplug_delegate callback, IntPtr userData, ref int callbackHandle); + + [DllImport(Libusb, CallingConvention = convention)] + private static extern void libusb_hotplug_deregister_callback(IntPtr ctx, int callbackHandle); + public Error init(IntPtr context) { return libusb_init(context); @@ -121,6 +127,16 @@ public Error attach_kernel_driver(IntPtr deviceHandle, int interfaceNum) { return libusb_attach_kernel_driver(deviceHandle, interfaceNum); } + + public Error hotplug_register_callback(IntPtr ctx, HotplugEvent events, HotplugFlag flags, int vendorId, int productId, int devClass, libusb_hotplug_delegate callback, IntPtr userData, ref int callbackHandle) + { + return libusb_hotplug_register_callback(ctx, events, flags, vendorId, productId, devClass, callback, userData, ref callbackHandle); + } + + public void hotplug_deregister_callback(IntPtr ctx, int callbackHandle) + { + libusb_hotplug_deregister_callback(ctx, callbackHandle); + } } } From 80f68c17d0418adf45ef0413d7a640e31fa02db7 Mon Sep 17 00:00:00 2001 From: X9VoiD Date: Tue, 11 Aug 2020 23:19:57 +0800 Subject: [PATCH 09/17] Fix oversight --- HidSharp/Platform/Libusb/LinuxNativeMethods.cs | 2 +- HidSharp/Platform/Libusb/MacOSNativeMethods.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/HidSharp/Platform/Libusb/LinuxNativeMethods.cs b/HidSharp/Platform/Libusb/LinuxNativeMethods.cs index 3a027f0..ccd55e7 100644 --- a/HidSharp/Platform/Libusb/LinuxNativeMethods.cs +++ b/HidSharp/Platform/Libusb/LinuxNativeMethods.cs @@ -130,7 +130,7 @@ public Error attach_kernel_driver(IntPtr deviceHandle, int interfaceNum) public Error hotplug_register_callback(IntPtr ctx, HotplugEvent events, HotplugFlag flags, int vendorId, int productId, int devClass, libusb_hotplug_delegate callback, IntPtr userData, ref int callbackHandle) { - return hotplug_register_callback(ctx, events, flags, vendorId, productId, devClass, callback, userData, ref callbackHandle); + return libusb_hotplug_register_callback(ctx, events, flags, vendorId, productId, devClass, callback, userData, ref callbackHandle); } public void hotplug_deregister_callback(IntPtr ctx, int callbackHandle) diff --git a/HidSharp/Platform/Libusb/MacOSNativeMethods.cs b/HidSharp/Platform/Libusb/MacOSNativeMethods.cs index ae3263f..b2d0ae7 100644 --- a/HidSharp/Platform/Libusb/MacOSNativeMethods.cs +++ b/HidSharp/Platform/Libusb/MacOSNativeMethods.cs @@ -130,7 +130,7 @@ public Error attach_kernel_driver(IntPtr deviceHandle, int interfaceNum) public Error hotplug_register_callback(IntPtr ctx, HotplugEvent events, HotplugFlag flags, int vendorId, int productId, int devClass, libusb_hotplug_delegate callback, IntPtr userData, ref int callbackHandle) { - return hotplug_register_callback(ctx, events, flags, vendorId, productId, devClass, callback, userData, ref callbackHandle); + return libusb_hotplug_register_callback(ctx, events, flags, vendorId, productId, devClass, callback, userData, ref callbackHandle); } public void hotplug_deregister_callback(IntPtr ctx, int callbackHandle) From c4a0e2c588e427e06ed21e0ff4c9219456422f0f Mon Sep 17 00:00:00 2001 From: X9VoiD Date: Tue, 11 Aug 2020 23:55:34 +0800 Subject: [PATCH 10/17] Fix oversight --- HidSharp/Platform/Libusb/LibusbHidManager.cs | 6 ++---- HidSharp/Platform/Libusb/LibusbHidStream.cs | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/HidSharp/Platform/Libusb/LibusbHidManager.cs b/HidSharp/Platform/Libusb/LibusbHidManager.cs index 3b027ec..c8c6d2a 100644 --- a/HidSharp/Platform/Libusb/LibusbHidManager.cs +++ b/HidSharp/Platform/Libusb/LibusbHidManager.cs @@ -65,9 +65,7 @@ public LibusbHidManager() } hotplugDelegate = new libusb_hotplug_delegate(HotPlug); - var ret = libusb.hotplug_register_callback(IntPtr.Zero, HotplugEvent.Arrived | HotplugEvent.Left, HotplugFlag.Enumerate, -1, -1, -1, hotplugDelegate, IntPtr.Zero, ref callbackHandle); - if (ret < 0) - throw new ExternalException("Failed to register for libusb hotplug. Reason: " + Enum.GetName(typeof(Error), ret)); + libusb.hotplug_register_callback(IntPtr.Zero, HotplugEvent.Arrived | HotplugEvent.Left, HotplugFlag.Enumerate, -1, -1, -1, hotplugDelegate, IntPtr.Zero, ref callbackHandle); } private int HotPlug(IntPtr ctx, IntPtr device, HotplugEvent hotplugEvent, IntPtr user_data) @@ -174,7 +172,7 @@ protected unsafe override object[] GetHidDeviceKeys() foreach (var device in deviceList) list.Add(device); - return _devices.Values.Cast().ToArray(); + return list.Cast().ToArray(); } protected override object[] GetSerialDeviceKeys() diff --git a/HidSharp/Platform/Libusb/LibusbHidStream.cs b/HidSharp/Platform/Libusb/LibusbHidStream.cs index d17dc86..fccf3de 100644 --- a/HidSharp/Platform/Libusb/LibusbHidStream.cs +++ b/HidSharp/Platform/Libusb/LibusbHidStream.cs @@ -36,7 +36,7 @@ internal void Init(IntPtr deviceHandle, byte interfaceNum, byte endpoint) if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { var retD = libusb.detach_kernel_driver(deviceHandle, interfaceNum); - if (retD < 0) + if (retD < 0 && retD != Error.NotFound) { throw new IOException("Failed to detach device interface from kernel. Reason: " + Enum.GetName(typeof(Error), retD)); } From 6d0f71d06ed64d7f03bcb79d47f5aee106ca97b5 Mon Sep 17 00:00:00 2001 From: X9VoiD Date: Wed, 12 Aug 2020 00:44:52 +0800 Subject: [PATCH 11/17] Cache device list using HidSharp's ManagerThread --- HidSharp/Platform/Libusb/LibusbHidManager.cs | 37 +++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/HidSharp/Platform/Libusb/LibusbHidManager.cs b/HidSharp/Platform/Libusb/LibusbHidManager.cs index c8c6d2a..393394f 100644 --- a/HidSharp/Platform/Libusb/LibusbHidManager.cs +++ b/HidSharp/Platform/Libusb/LibusbHidManager.cs @@ -53,19 +53,6 @@ public LibusbHidManager() { _isSupported = false; } - - // Cache device list since this is slow - var _devCount = libusb.get_device_list(IntPtr.Zero, out var _deviceListRaw); - var deviceList = new IntPtr[_devCount]; - Marshal.Copy(_deviceListRaw, deviceList, 0, _devCount); - - foreach (var device in deviceList) - { - CreateDevice(device, _devices); - } - hotplugDelegate = new libusb_hotplug_delegate(HotPlug); - - libusb.hotplug_register_callback(IntPtr.Zero, HotplugEvent.Arrived | HotplugEvent.Left, HotplugFlag.Enumerate, -1, -1, -1, hotplugDelegate, IntPtr.Zero, ref callbackHandle); } private int HotPlug(IntPtr ctx, IntPtr device, HotplugEvent hotplugEvent, IntPtr user_data) @@ -159,6 +146,30 @@ internal unsafe void CreateDevice(IntPtr device, Dictionary Date: Wed, 12 Aug 2020 01:37:31 +0800 Subject: [PATCH 12/17] Workaround the lack of libusb hotplug support on Windows --- HidSharp/Platform/Libusb/LibusbHidManager.cs | 94 +++++++++++++++++++- 1 file changed, 90 insertions(+), 4 deletions(-) diff --git a/HidSharp/Platform/Libusb/LibusbHidManager.cs b/HidSharp/Platform/Libusb/LibusbHidManager.cs index 393394f..a444113 100644 --- a/HidSharp/Platform/Libusb/LibusbHidManager.cs +++ b/HidSharp/Platform/Libusb/LibusbHidManager.cs @@ -19,6 +19,7 @@ under the License. */ using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; +using HidSharp.Utility; using static HidSharp.Platform.Libusb.INativeMethods; namespace HidSharp.Platform.Libusb @@ -27,7 +28,7 @@ namespace HidSharp.Platform.Libusb { private bool _isSupported; - private Dictionary> _devices = new Dictionary>(); + private Dictionary> _devices; public override string FriendlyName => "Libusb HID"; @@ -146,9 +147,9 @@ internal unsafe void CreateDevice(IntPtr device, Dictionary>(); var _devCount = libusb.get_device_list(IntPtr.Zero, out var _deviceListRaw); var deviceList = new IntPtr[_devCount]; Marshal.Copy(_deviceListRaw, deviceList, 0, _devCount); @@ -156,11 +157,17 @@ protected override void Run(Action readyCallback) { CreateDevice(device, _devices); } + } + + protected override void Run(Action readyCallback) + { + // Cache device list since this is slow + GenerateDeviceList(); hotplugDelegate = new libusb_hotplug_delegate(HotPlug); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - readyCallback(); + RunWindowsHotPlug(readyCallback); } else { @@ -170,6 +177,85 @@ protected override void Run(Action readyCallback) } } + private void RunWindowsHotPlug(Action readyCallback) + { + const string className = "HidSharpDeviceMonitor"; + + Windows.NativeMethods.WindowProc windowProc = DeviceMonitorWindowProc; + var wc = new Windows.NativeMethods.WNDCLASS() { ClassName = className, WindowProc = windowProc }; + RunAssert(0 != Windows.NativeMethods.RegisterClass(ref wc), "HidSharp RegisterClass failed."); + + var hwnd = Windows.NativeMethods.CreateWindowEx(0, className, className, 0, + Windows.NativeMethods.CW_USEDEFAULT, Windows.NativeMethods.CW_USEDEFAULT, Windows.NativeMethods.CW_USEDEFAULT, Windows.NativeMethods.CW_USEDEFAULT, + Windows.NativeMethods.HWND_MESSAGE, + IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + RunAssert(hwnd != IntPtr.Zero, "HidSharp CreateWindow failed."); + + var hidNotifyHandle = RegisterDeviceNotification(hwnd, Windows.NativeMethods.HidD_GetHidGuid()); + + readyCallback(); + + while (true) + { + int result = Windows.NativeMethods.GetMessage(out Windows.NativeMethods.MSG msg, hwnd, 0, 0); + if (result == 0 || result == -1) { break; } + + Windows.NativeMethods.TranslateMessage(ref msg); + Windows.NativeMethods.DispatchMessage(ref msg); + } + + UnregisterDeviceNotification(hidNotifyHandle); + RunAssert(Windows.NativeMethods.DestroyWindow(hwnd), "HidSharp DestroyWindow failed."); + RunAssert(Windows.NativeMethods.UnregisterClass(className, IntPtr.Zero), "HidSharp UnregisterClass failed."); + GC.KeepAlive(windowProc); + } + + private IntPtr RegisterDeviceNotification(IntPtr hwnd, Guid guid) + { + var notifyFilter = new Windows.NativeMethods.DEV_BROADCAST_DEVICEINTERFACE() + { + Size = Marshal.SizeOf(typeof(Windows.NativeMethods.DEV_BROADCAST_DEVICEINTERFACE)), + ClassGuid = guid, + DeviceType = Windows.NativeMethods.DBT_DEVTYP_DEVICEINTERFACE + }; + var notifyHandle = Windows.NativeMethods.RegisterDeviceNotification(hwnd, ref notifyFilter, 0); + RunAssert(notifyHandle != IntPtr.Zero, "HidSharp RegisterDeviceNotification failed."); + return notifyHandle; + } + + private void UnregisterDeviceNotification(IntPtr handle) + { + RunAssert(Windows.NativeMethods.UnregisterDeviceNotification(handle), "HidSharp UnregisterDeviceNotification failed."); + } + + unsafe IntPtr DeviceMonitorWindowProc(IntPtr window, uint message, IntPtr wParam, IntPtr lParam) + { + if (message == Windows.NativeMethods.WM_DEVICECHANGE) + { + var ev = (Windows.NativeMethods.WM_DEVICECHANGE_wParam)(int)(long)wParam; + HidSharpDiagnostics.Trace("Received a device change event, {0}.", ev); + + var eventArgs = (Windows.NativeMethods.DEV_BROADCAST_HDR*)(void*)lParam; + + if (ev == Windows.NativeMethods.WM_DEVICECHANGE_wParam.DBT_DEVICEARRIVAL || ev == Windows.NativeMethods.WM_DEVICECHANGE_wParam.DBT_DEVICEREMOVECOMPLETE) + { + if (eventArgs->DeviceType == Windows.NativeMethods.DBT_DEVTYP_DEVICEINTERFACE) + { + var diEventArgs = (Windows.NativeMethods.DEV_BROADCAST_DEVICEINTERFACE*)eventArgs; + + if (diEventArgs->ClassGuid == Windows.NativeMethods.HidD_GetHidGuid()) + { + // We won't know what device is added or removed so regenerate + GenerateDeviceList(); + DeviceList.Local.RaiseChanged(); + } + } + } + return (IntPtr)1; + } + return Windows.NativeMethods.DefWindowProc(window, message, wParam, lParam); + } + protected override object[] GetBleDeviceKeys() { throw new NotImplementedException(); From 9410286577014d4b8679b01b9d1c1208f68ee64c Mon Sep 17 00:00:00 2001 From: X9VoiD Date: Wed, 12 Aug 2020 17:20:36 +0800 Subject: [PATCH 13/17] Offload some responsibility to libusb --- HidSharp/Platform/Libusb/INativeMethods.cs | 2 ++ HidSharp/Platform/Libusb/LibusbHidManager.cs | 11 +++++++---- HidSharp/Platform/Libusb/LibusbHidStream.cs | 18 +----------------- HidSharp/Platform/Libusb/LinuxNativeMethods.cs | 8 ++++++++ HidSharp/Platform/Libusb/MacOSNativeMethods.cs | 8 ++++++++ HidSharp/Platform/Libusb/WinNativeMethods.cs | 8 ++++++++ 6 files changed, 34 insertions(+), 21 deletions(-) diff --git a/HidSharp/Platform/Libusb/INativeMethods.cs b/HidSharp/Platform/Libusb/INativeMethods.cs index 150c34f..585166f 100644 --- a/HidSharp/Platform/Libusb/INativeMethods.cs +++ b/HidSharp/Platform/Libusb/INativeMethods.cs @@ -195,6 +195,8 @@ public struct libusb_config_descriptor public Error attach_kernel_driver(IntPtr deviceHandle, int interfaceNum); + public Error set_auto_detach_kernel_driver(IntPtr deviceHandle, int enable); + public Error hotplug_register_callback(IntPtr ctx, HotplugEvent events, HotplugFlag flags, int vendorId, int productId, int devClass, libusb_hotplug_delegate callback, IntPtr userData, ref int callbackHandle); public void hotplug_deregister_callback(IntPtr ctx, int callbackHandle); diff --git a/HidSharp/Platform/Libusb/LibusbHidManager.cs b/HidSharp/Platform/Libusb/LibusbHidManager.cs index a444113..8cf56b1 100644 --- a/HidSharp/Platform/Libusb/LibusbHidManager.cs +++ b/HidSharp/Platform/Libusb/LibusbHidManager.cs @@ -161,17 +161,20 @@ private void GenerateDeviceList() protected override void Run(Action readyCallback) { - // Cache device list since this is slow - GenerateDeviceList(); - hotplugDelegate = new libusb_hotplug_delegate(HotPlug); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { + // Cache device list since this is slow + GenerateDeviceList(); RunWindowsHotPlug(readyCallback); } else { - libusb.hotplug_register_callback(IntPtr.Zero, HotplugEvent.Arrived | HotplugEvent.Left, HotplugFlag.NoFlags, -1, -1, -1, hotplugDelegate, IntPtr.Zero, ref callbackHandle); + // Hotplug will enumerate every connected device for us upon registration + _devices = new Dictionary>(); + var ret = libusb.hotplug_register_callback(IntPtr.Zero, HotplugEvent.Arrived | HotplugEvent.Left, HotplugFlag.Enumerate, -1, -1, -1, hotplugDelegate, IntPtr.Zero, ref callbackHandle); + if (ret < 0) + throw new ExternalException("Unable to register for hotplug. Reason: " + Enum.GetName(typeof(Error), ret)); readyCallback(); GC.KeepAlive(hotplugDelegate); } diff --git a/HidSharp/Platform/Libusb/LibusbHidStream.cs b/HidSharp/Platform/Libusb/LibusbHidStream.cs index fccf3de..a818af0 100644 --- a/HidSharp/Platform/Libusb/LibusbHidStream.cs +++ b/HidSharp/Platform/Libusb/LibusbHidStream.cs @@ -21,11 +21,6 @@ internal LibusbHidStream(LibusbHidDevice device) : base(device) ~LibusbHidStream() { libusb.release_interface(_handle, _interfaceNum); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - // return device to kernel - libusb.attach_kernel_driver(_handle, _interfaceNum); - } libusb.close(_handle); Close(); } @@ -33,14 +28,7 @@ internal LibusbHidStream(LibusbHidDevice device) : base(device) // To follow HidSharp's structure and style internal void Init(IntPtr deviceHandle, byte interfaceNum, byte endpoint) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - var retD = libusb.detach_kernel_driver(deviceHandle, interfaceNum); - if (retD < 0 && retD != Error.NotFound) - { - throw new IOException("Failed to detach device interface from kernel. Reason: " + Enum.GetName(typeof(Error), retD)); - } - } + libusb.set_auto_detach_kernel_driver(deviceHandle, 1); var retI = libusb.claim_interface(deviceHandle, interfaceNum); if (retI != Error.None) @@ -108,10 +96,6 @@ internal override void HandleFree() protected override void Dispose(bool disposing) { libusb.release_interface(_handle, _interfaceNum); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - libusb.attach_kernel_driver(_handle, _interfaceNum); - } libusb.close(_handle); base.Dispose(disposing); } diff --git a/HidSharp/Platform/Libusb/LinuxNativeMethods.cs b/HidSharp/Platform/Libusb/LinuxNativeMethods.cs index ccd55e7..658c5a3 100644 --- a/HidSharp/Platform/Libusb/LinuxNativeMethods.cs +++ b/HidSharp/Platform/Libusb/LinuxNativeMethods.cs @@ -52,6 +52,9 @@ internal class LinuxNativeMethods : INativeMethods [DllImport(Libusb, CallingConvention = convention)] private static extern Error libusb_attach_kernel_driver(IntPtr deviceHandle, int interfaceNum); + [DllImport(Libusb, CallingConvention = convention)] + private static extern Error libusb_set_auto_detach_kernel_driver(IntPtr deviceHandle, int enable); + [DllImport(Libusb, CallingConvention = convention)] private static extern Error libusb_hotplug_register_callback(IntPtr ctx, HotplugEvent events, HotplugFlag flags, int vendorId, int productId, int devClass, libusb_hotplug_delegate callback, IntPtr userData, ref int callbackHandle); @@ -128,6 +131,11 @@ public Error attach_kernel_driver(IntPtr deviceHandle, int interfaceNum) return libusb_attach_kernel_driver(deviceHandle, interfaceNum); } + public Error set_auto_detach_kernel_driver(IntPtr deviceHandle, int enable) + { + return libusb_set_auto_detach_kernel_driver(deviceHandle, enable); + } + public Error hotplug_register_callback(IntPtr ctx, HotplugEvent events, HotplugFlag flags, int vendorId, int productId, int devClass, libusb_hotplug_delegate callback, IntPtr userData, ref int callbackHandle) { return libusb_hotplug_register_callback(ctx, events, flags, vendorId, productId, devClass, callback, userData, ref callbackHandle); diff --git a/HidSharp/Platform/Libusb/MacOSNativeMethods.cs b/HidSharp/Platform/Libusb/MacOSNativeMethods.cs index b2d0ae7..bd191bf 100644 --- a/HidSharp/Platform/Libusb/MacOSNativeMethods.cs +++ b/HidSharp/Platform/Libusb/MacOSNativeMethods.cs @@ -52,6 +52,9 @@ internal class MacOSNativeMethods : INativeMethods [DllImport(Libusb, CallingConvention = convention)] private static extern Error libusb_attach_kernel_driver(IntPtr deviceHandle, int interfaceNum); + [DllImport(Libusb, CallingConvention = convention)] + private static extern Error libusb_set_auto_detach_kernel_driver(IntPtr deviceHandle, int enable); + [DllImport(Libusb, CallingConvention = convention)] private static extern Error libusb_hotplug_register_callback(IntPtr ctx, HotplugEvent events, HotplugFlag flags, int vendorId, int productId, int devClass, libusb_hotplug_delegate callback, IntPtr userData, ref int callbackHandle); @@ -128,6 +131,11 @@ public Error attach_kernel_driver(IntPtr deviceHandle, int interfaceNum) return libusb_attach_kernel_driver(deviceHandle, interfaceNum); } + public Error set_auto_detach_kernel_driver(IntPtr deviceHandle, int enable) + { + return libusb_set_auto_detach_kernel_driver(deviceHandle, enable); + } + public Error hotplug_register_callback(IntPtr ctx, HotplugEvent events, HotplugFlag flags, int vendorId, int productId, int devClass, libusb_hotplug_delegate callback, IntPtr userData, ref int callbackHandle) { return libusb_hotplug_register_callback(ctx, events, flags, vendorId, productId, devClass, callback, userData, ref callbackHandle); diff --git a/HidSharp/Platform/Libusb/WinNativeMethods.cs b/HidSharp/Platform/Libusb/WinNativeMethods.cs index f489cad..ea8eb65 100644 --- a/HidSharp/Platform/Libusb/WinNativeMethods.cs +++ b/HidSharp/Platform/Libusb/WinNativeMethods.cs @@ -52,6 +52,9 @@ internal class WinNativeMethods : INativeMethods [DllImport(Libusb, CallingConvention = convention)] private static extern Error libusb_attach_kernel_driver(IntPtr deviceHandle, int interfaceNum); + [DllImport(Libusb, CallingConvention = convention)] + private static extern Error libusb_set_auto_detach_kernel_driver(IntPtr deviceHandle, int enable); + [DllImport(Libusb, CallingConvention = convention)] private static extern Error libusb_hotplug_register_callback(IntPtr ctx, HotplugEvent events, HotplugFlag flags, int vendorId, int productId, int devClass, libusb_hotplug_delegate callback, IntPtr userData, ref int callbackHandle); @@ -128,6 +131,11 @@ public Error attach_kernel_driver(IntPtr deviceHandle, int interfaceNum) return libusb_attach_kernel_driver(deviceHandle, interfaceNum); } + public Error set_auto_detach_kernel_driver(IntPtr deviceHandle, int enable) + { + return libusb_set_auto_detach_kernel_driver(deviceHandle, enable); + } + public Error hotplug_register_callback(IntPtr ctx, HotplugEvent events, HotplugFlag flags, int vendorId, int productId, int devClass, libusb_hotplug_delegate callback, IntPtr userData, ref int callbackHandle) { return libusb_hotplug_register_callback(ctx, events, flags, vendorId, productId, devClass, callback, userData, ref callbackHandle); From 50efada52af68a0108101cfb1682529d1ce29352 Mon Sep 17 00:00:00 2001 From: X9VoiD Date: Wed, 12 Aug 2020 20:12:04 +0800 Subject: [PATCH 14/17] Keep hotplug delegate alive correctly --- HidSharp/Platform/Libusb/LibusbHidManager.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/HidSharp/Platform/Libusb/LibusbHidManager.cs b/HidSharp/Platform/Libusb/LibusbHidManager.cs index 8cf56b1..2bb03b1 100644 --- a/HidSharp/Platform/Libusb/LibusbHidManager.cs +++ b/HidSharp/Platform/Libusb/LibusbHidManager.cs @@ -36,7 +36,8 @@ namespace HidSharp.Platform.Libusb private readonly T libusb = new T(); - private static libusb_hotplug_delegate hotplugDelegate; + private libusb_hotplug_delegate hotplugDelegate; + private GCHandle delegateHandle; private int callbackHandle; @@ -162,6 +163,7 @@ private void GenerateDeviceList() protected override void Run(Action readyCallback) { hotplugDelegate = new libusb_hotplug_delegate(HotPlug); + delegateHandle = GCHandle.Alloc(hotplugDelegate); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { // Cache device list since this is slow @@ -176,7 +178,6 @@ protected override void Run(Action readyCallback) if (ret < 0) throw new ExternalException("Unable to register for hotplug. Reason: " + Enum.GetName(typeof(Error), ret)); readyCallback(); - GC.KeepAlive(hotplugDelegate); } } From cfe2b64941459a95714f384b049e6dcc05d5bf22 Mon Sep 17 00:00:00 2001 From: X9VoiD Date: Wed, 12 Aug 2020 22:07:41 +0800 Subject: [PATCH 15/17] Don't open device during enumeration and partially revert 9410286577014d4b8679b01b9d1c1208f68ee64c --- HidSharp/Platform/Libusb/LibusbHidManager.cs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/HidSharp/Platform/Libusb/LibusbHidManager.cs b/HidSharp/Platform/Libusb/LibusbHidManager.cs index 2bb03b1..506e628 100644 --- a/HidSharp/Platform/Libusb/LibusbHidManager.cs +++ b/HidSharp/Platform/Libusb/LibusbHidManager.cs @@ -83,17 +83,13 @@ internal unsafe void CreateDevice(IntPtr device, Dictionary>(); - var ret = libusb.hotplug_register_callback(IntPtr.Zero, HotplugEvent.Arrived | HotplugEvent.Left, HotplugFlag.Enumerate, -1, -1, -1, hotplugDelegate, IntPtr.Zero, ref callbackHandle); + var ret = libusb.hotplug_register_callback(IntPtr.Zero, HotplugEvent.Arrived | HotplugEvent.Left, HotplugFlag.NoFlags, -1, -1, -1, hotplugDelegate, IntPtr.Zero, ref callbackHandle); if (ret < 0) throw new ExternalException("Unable to register for hotplug. Reason: " + Enum.GetName(typeof(Error), ret)); readyCallback(); From bbcfa5a5fe7e5e90bb04d257663c8e83a3f00ad0 Mon Sep 17 00:00:00 2001 From: InfinityGhost <39218853+InfinityGhost@users.noreply.github.com> Date: Thu, 13 Aug 2020 11:44:57 -0400 Subject: [PATCH 16/17] Convert all Marshal.PtrToStructure calls to unsafe --- HidSharp/Platform/Libusb/INativeMethods.cs | 8 ++++---- HidSharp/Platform/Libusb/LibusbHidManager.cs | 10 ++++------ HidSharp/Platform/Libusb/LinuxNativeMethods.cs | 4 ++-- HidSharp/Platform/Libusb/MacOSNativeMethods.cs | 4 ++-- HidSharp/Platform/Libusb/WinNativeMethods.cs | 4 ++-- 5 files changed, 14 insertions(+), 16 deletions(-) diff --git a/HidSharp/Platform/Libusb/INativeMethods.cs b/HidSharp/Platform/Libusb/INativeMethods.cs index 585166f..d3d336f 100644 --- a/HidSharp/Platform/Libusb/INativeMethods.cs +++ b/HidSharp/Platform/Libusb/INativeMethods.cs @@ -135,7 +135,7 @@ public struct libusb_interface_descriptor public byte iInterface; /// libusb_endpoint_descriptor[] - public IntPtr endpoints; + public unsafe libusb_endpoint_descriptor* endpoints; public IntPtr extra; public int extra_length; } @@ -144,7 +144,7 @@ public struct libusb_interface_descriptor public struct libusb_interface { /// libusb_interface_descriptor[] - public IntPtr altsetting; + public unsafe libusb_interface_descriptor* altsetting; public int num_altsetting; } @@ -160,7 +160,7 @@ public struct libusb_config_descriptor public byte bmAttributes; public byte MaxPower; /// libusb_interface[] - public IntPtr interfaces; + public unsafe libusb_interface* interfaces; public IntPtr extra; public int extra_length; } @@ -183,7 +183,7 @@ public struct libusb_config_descriptor public int get_string_descriptor_ascii(IntPtr deviceHandle, byte index, StringBuilder data, int length); - public Error get_config_descriptor(IntPtr device, byte configIndex, out IntPtr configDescriptor); + public unsafe Error get_config_descriptor(IntPtr device, byte configIndex, out libusb_config_descriptor* configDescriptor); public Error interrupt_transfer(IntPtr deviceHandle, byte endpoint, byte[] data, int length, ref int actual_length, uint timeout = 0); diff --git a/HidSharp/Platform/Libusb/LibusbHidManager.cs b/HidSharp/Platform/Libusb/LibusbHidManager.cs index 506e628..0f62880 100644 --- a/HidSharp/Platform/Libusb/LibusbHidManager.cs +++ b/HidSharp/Platform/Libusb/LibusbHidManager.cs @@ -47,7 +47,6 @@ public LibusbHidManager() { try { - Marshal.PrelinkAll(typeof(T)); libusb.init(IntPtr.Zero); _isSupported = true; } @@ -90,21 +89,20 @@ internal unsafe void CreateDevice(IntPtr device, Dictionary(); for (int endpointIndex = 0; endpointIndex < mySetting.bNumEndpoints; endpointIndex++) { - var myEndpoint = (libusb_endpoint_descriptor)Marshal.PtrToStructure(mySetting.endpoints + sizeof(libusb_endpoint_descriptor) * endpointIndex, typeof(libusb_endpoint_descriptor)); + var myEndpoint = mySetting.endpoints[endpointIndex]; var direction = (myEndpoint.bEndpointAddress & (0x80)) == 0x80; var address = myEndpoint.bEndpointAddress & (0x0F); diff --git a/HidSharp/Platform/Libusb/LinuxNativeMethods.cs b/HidSharp/Platform/Libusb/LinuxNativeMethods.cs index 658c5a3..071a1ae 100644 --- a/HidSharp/Platform/Libusb/LinuxNativeMethods.cs +++ b/HidSharp/Platform/Libusb/LinuxNativeMethods.cs @@ -35,7 +35,7 @@ internal class LinuxNativeMethods : INativeMethods private static extern int libusb_get_string_descriptor_ascii(IntPtr deviceHandle, byte index, StringBuilder data, int length); [DllImport(Libusb, CallingConvention = convention)] - private static unsafe extern Error libusb_get_config_descriptor(IntPtr device, byte configIndex, out IntPtr configDescriptor); + private static unsafe extern Error libusb_get_config_descriptor(IntPtr device, byte configIndex, out libusb_config_descriptor* configDescriptor); [DllImport(Libusb, CallingConvention = convention)] private static extern Error libusb_interrupt_transfer(IntPtr deviceHandle, byte endpoint, byte[] data, int length, ref int actual_length, uint timeout = 0); @@ -101,7 +101,7 @@ public int get_string_descriptor_ascii(IntPtr deviceHandle, byte index, StringBu return libusb_get_string_descriptor_ascii(deviceHandle, index, data, length); } - public Error get_config_descriptor(IntPtr device, byte configIndex, out IntPtr configDescriptor) + public unsafe Error get_config_descriptor(IntPtr device, byte configIndex, out libusb_config_descriptor* configDescriptor) { return libusb_get_config_descriptor(device, configIndex, out configDescriptor); } diff --git a/HidSharp/Platform/Libusb/MacOSNativeMethods.cs b/HidSharp/Platform/Libusb/MacOSNativeMethods.cs index bd191bf..d78efa7 100644 --- a/HidSharp/Platform/Libusb/MacOSNativeMethods.cs +++ b/HidSharp/Platform/Libusb/MacOSNativeMethods.cs @@ -35,7 +35,7 @@ internal class MacOSNativeMethods : INativeMethods private static extern int libusb_get_string_descriptor_ascii(IntPtr deviceHandle, byte index, StringBuilder data, int length); [DllImport(Libusb, CallingConvention = convention)] - private static unsafe extern Error libusb_get_config_descriptor(IntPtr device, byte configIndex, out IntPtr configDescriptor); + private static unsafe extern Error libusb_get_config_descriptor(IntPtr device, byte configIndex, out libusb_config_descriptor* configDescriptor); [DllImport(Libusb, CallingConvention = convention)] private static extern Error libusb_interrupt_transfer(IntPtr deviceHandle, byte endpoint, byte[] data, int length, ref int actual_length, uint timeout = 0); @@ -101,7 +101,7 @@ public int get_string_descriptor_ascii(IntPtr deviceHandle, byte index, StringBu return libusb_get_string_descriptor_ascii(deviceHandle, index, data, length); } - public Error get_config_descriptor(IntPtr device, byte configIndex, out IntPtr configDescriptor) + public unsafe Error get_config_descriptor(IntPtr device, byte configIndex, out libusb_config_descriptor* configDescriptor) { return libusb_get_config_descriptor(device, configIndex, out configDescriptor); } diff --git a/HidSharp/Platform/Libusb/WinNativeMethods.cs b/HidSharp/Platform/Libusb/WinNativeMethods.cs index ea8eb65..8c9afe0 100644 --- a/HidSharp/Platform/Libusb/WinNativeMethods.cs +++ b/HidSharp/Platform/Libusb/WinNativeMethods.cs @@ -35,7 +35,7 @@ internal class WinNativeMethods : INativeMethods private static extern int libusb_get_string_descriptor_ascii(IntPtr deviceHandle, byte index, StringBuilder data, int length); [DllImport(Libusb, CallingConvention = convention)] - private static unsafe extern Error libusb_get_config_descriptor(IntPtr device, byte configIndex, out IntPtr configDescriptor); + private static unsafe extern Error libusb_get_config_descriptor(IntPtr device, byte configIndex, out libusb_config_descriptor* configDescriptor); [DllImport(Libusb, CallingConvention = convention)] private static extern Error libusb_interrupt_transfer(IntPtr deviceHandle, byte endpoint, byte[] data, int length, ref int actual_length, uint timeout = 0); @@ -101,7 +101,7 @@ public int get_string_descriptor_ascii(IntPtr deviceHandle, byte index, StringBu return libusb_get_string_descriptor_ascii(deviceHandle, index, data, length); } - public Error get_config_descriptor(IntPtr device, byte configIndex, out IntPtr configDescriptor) + public unsafe Error get_config_descriptor(IntPtr device, byte configIndex, out libusb_config_descriptor* configDescriptor) { return libusb_get_config_descriptor(device, configIndex, out configDescriptor); } From 71d5c1099709c06e50ad68dad5eab8747797501c Mon Sep 17 00:00:00 2001 From: X9VoiD Date: Fri, 14 Aug 2020 00:09:37 +0800 Subject: [PATCH 17/17] Remove unneeded libusb_interface local variable --- HidSharp/Platform/Libusb/LibusbHidManager.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/HidSharp/Platform/Libusb/LibusbHidManager.cs b/HidSharp/Platform/Libusb/LibusbHidManager.cs index 0f62880..c6dd3ca 100644 --- a/HidSharp/Platform/Libusb/LibusbHidManager.cs +++ b/HidSharp/Platform/Libusb/LibusbHidManager.cs @@ -86,14 +86,12 @@ internal unsafe void CreateDevice(IntPtr device, DictionarybNumInterfaces; interfaceIndex++) { - libusb_interface myInterface = configDescriptor.interfaces[interfaceIndex]; + libusb_interface myInterface = configDescriptor->interfaces[interfaceIndex]; for (int settingIndex = 0; settingIndex < myInterface.num_altsetting; settingIndex++) {