diff --git a/HidSharp/Platform/HidSelector.cs b/HidSharp/Platform/HidSelector.cs index a6a15e1..4b6aa8f 100644 --- a/HidSharp/Platform/HidSelector.cs +++ b/HidSharp/Platform/HidSelector.cs @@ -22,22 +22,35 @@ namespace HidSharp.Platform sealed class HidSelector { public static readonly HidManager Instance; - static readonly Thread ManagerThread; + static readonly Thread ManagerThread; + + internal static T SafeCreate() where T : class, new() + { + try + { + return new T(); + } + catch { return null; } + } static HidSelector() { - foreach (var hidManager in new HidManager[] - { - new Windows.WinHidManager(), - new Linux.LinuxHidManager(), - new MacOS.MacHidManager(), - new Unsupported.UnsupportedHidManager() - }) + var hidManagerList = new HidManager[] + { + SafeCreate>(), + SafeCreate>(), + SafeCreate>(), + SafeCreate(), + SafeCreate(), + SafeCreate(), + SafeCreate() + }; + + foreach (var hidManager in hidManagerList) { - if (hidManager.IsSupported) + if (hidManager != null && hidManager.IsSupported) { var readyEvent = new ManualResetEvent(false); - Instance = hidManager; Instance.InitializeEventManager(); ManagerThread = new Thread(Instance.RunImpl) { IsBackground = true, Name = "HID Manager" }; diff --git a/HidSharp/Platform/Libusb/INativeMethods.cs b/HidSharp/Platform/Libusb/INativeMethods.cs new file mode 100644 index 0000000..d3d336f --- /dev/null +++ b/HidSharp/Platform/Libusb/INativeMethods.cs @@ -0,0 +1,204 @@ +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 + } + + public enum HotplugEvent + { + Arrived = 0x01, + Left = 0x02 + } + + public enum HotplugFlag + { + NoFlags, + Enumerate + } + + [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 unsafe libusb_endpoint_descriptor* endpoints; + public IntPtr extra; + public int extra_length; + } + + [StructLayout(LayoutKind.Sequential)] + public struct libusb_interface + { + /// libusb_interface_descriptor[] + public unsafe 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; + /// libusb_interface[] + public unsafe libusb_interface* interfaces; + public IntPtr extra; + 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); + + 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 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); + + 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); + + 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); + } +} \ No newline at end of file diff --git a/HidSharp/Platform/Libusb/LibusbHidDevice.cs b/HidSharp/Platform/Libusb/LibusbHidDevice.cs new file mode 100644 index 0000000..b1af96f --- /dev/null +++ b/HidSharp/Platform/Libusb/LibusbHidDevice.cs @@ -0,0 +1,97 @@ +using System; +using System.Text; +using static HidSharp.Platform.Libusb.INativeMethods; + +namespace HidSharp.Platform.Libusb +{ + sealed class LibusbHidDevice : HidDevice where T : INativeMethods, new() + { + public override int ProductID { get => _descriptor.idProduct; } + + public override int ReleaseNumberBcd { get => _descriptor.bcdUSB; } + + public override int VendorID { get => _descriptor.idVendor; } + + private T libusb = new T(); + + // Unsupported by libusb + public override string DevicePath {get => ""; } + + private LibUsbDevice _dev; + private IntPtr _deviceHandle; + private libusb_device_descriptor _descriptor; + + internal static LibusbHidDevice TryCreate(LibUsbDevice device) + { + 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 = libusb.get_device_descriptor(hid._dev.Device, out hid._descriptor); + if (err > 0) + { + libusb.close(hid._dev.Device); + return null; + } + + return hid; + } + + public override string GetFileSystemName() + { + return ""; + } + + public override string GetManufacturer() + { + return GetDeviceString(_descriptor.iManufacturer); + } + + public override int GetMaxFeatureReportLength() + { + throw new NotImplementedException(); + } + + public override int GetMaxInputReportLength() + { + return (int)_dev.Endpoint.InputReportLength; + } + + public override int GetMaxOutputReportLength() + { + return (int)_dev.Endpoint.OutputReportLength; + } + + public override string GetProductName() + { + return GetDeviceString(_descriptor.iProduct); + } + + public override string GetSerialNumber() + { + return GetDeviceString(_descriptor.iSerialNumber); + } + + protected override DeviceStream OpenDeviceDirectly(OpenConfiguration openConfig) + { + var stream = new LibusbHidStream(this); + try { stream.Init(_deviceHandle, (byte)_dev.Endpoint.Interface, (byte)_dev.Endpoint.Address); return stream; } + catch { stream.Close(); throw; } + } + + public override string GetDeviceString(int index) + { + var deviceString = new StringBuilder(256); + var err = libusb.get_string_descriptor_ascii(_deviceHandle, _descriptor.iSerialNumber, deviceString, 256); + if (err < 0) + { + throw DeviceException.CreateIOException(this, "Failed to read string descriptor."); + } + return deviceString.ToString(); + } + } +} \ No newline at end of file diff --git a/HidSharp/Platform/Libusb/LibusbHidManager.cs b/HidSharp/Platform/Libusb/LibusbHidManager.cs index 4e91404..c6dd3ca 100644 --- a/HidSharp/Platform/Libusb/LibusbHidManager.cs +++ b/HidSharp/Platform/Libusb/LibusbHidManager.cs @@ -16,12 +16,278 @@ under the License. */ #endregion using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using HidSharp.Utility; +using static HidSharp.Platform.Libusb.INativeMethods; namespace HidSharp.Platform.Libusb { - sealed class LibusbHidManager - { - // TODO - } + sealed class LibusbHidManager : HidManager where T : INativeMethods, new() + { + private bool _isSupported; + + private Dictionary> _devices; + + public override string FriendlyName => "Libusb HID"; + + public override bool IsSupported { get => _isSupported; } + + private readonly T libusb = new T(); + + private libusb_hotplug_delegate hotplugDelegate; + private GCHandle delegateHandle; + + private int callbackHandle; + + private object _deviceLock = new object(); + + public LibusbHidManager() + { + try + { + libusb.init(IntPtr.Zero); + _isSupported = true; + } + catch + { + _isSupported = false; + } + } + + private int HotPlug(IntPtr ctx, IntPtr device, HotplugEvent hotplugEvent, IntPtr user_data) + { + 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; + } + + internal unsafe void CreateDevice(IntPtr device, Dictionary> deviceList) + { + libusb.get_device_descriptor(device, out var deviceDescriptor); + + var configCount = deviceDescriptor.bNumConfigurations; + + for (byte configIndex = 0; configIndex < configCount; configIndex++) + { + if (libusb.get_config_descriptor(device, configIndex, out var configDescriptor) < 0) + continue; + + for (int interfaceIndex = 0; interfaceIndex < configDescriptor->bNumInterfaces; interfaceIndex++) + { + libusb_interface myInterface = configDescriptor->interfaces[interfaceIndex]; + + for (int settingIndex = 0; settingIndex < myInterface.num_altsetting; settingIndex++) + { + var mySetting = myInterface.altsetting[settingIndex]; + var endpointDict = new Dictionary(); + + for (int endpointIndex = 0; endpointIndex < mySetting.bNumEndpoints; endpointIndex++) + { + var myEndpoint = mySetting.endpoints[endpointIndex]; + 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); + } + } + + foreach (var endpoint in endpointDict.Values) + { + LibUsbDevice libusbdevice = new LibUsbDevice + { + Device = device, + VendorID = deviceDescriptor.idVendor, + ProductID = deviceDescriptor.idProduct, + Endpoint = endpoint + }; + lock(_deviceLock) + { + if (!deviceList.ContainsKey(device)) + { + deviceList.Add(device, new List()); + } + deviceList[device].Add(libusbdevice); + } + } + } + } + } + return; + } + + private void GenerateDeviceList() + { + _devices = new Dictionary>(); + 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); + } + } + + protected override void Run(Action readyCallback) + { + // Cache device list since this is slow + GenerateDeviceList(); + hotplugDelegate = new libusb_hotplug_delegate(HotPlug); + delegateHandle = GCHandle.Alloc(hotplugDelegate); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + + RunWindowsHotPlug(readyCallback); + } + else + { + // 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.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(); + } + } + + 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(); + } + + // 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 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((LibUsbDevice)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..a818af0 --- /dev/null +++ b/HidSharp/Platform/Libusb/LibusbHidStream.cs @@ -0,0 +1,103 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using static HidSharp.Platform.Libusb.INativeMethods; + +namespace HidSharp.Platform.Libusb +{ + 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) + { + + } + + ~LibusbHidStream() + { + libusb.release_interface(_handle, _interfaceNum); + libusb.close(_handle); + Close(); + } + + // To follow HidSharp's structure and style + internal void Init(IntPtr deviceHandle, byte interfaceNum, byte endpoint) + { + libusb.set_auto_detach_kernel_driver(deviceHandle, 1); + + 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; + } + + 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."); + Error error = Error.None; + lock (_readsync) + { + error = libusb.interrupt_transfer(_handle, endpointMode, buffer, count, ref transferred); + if (error < 0) + { + throw new IOException("Failed reading from device. Reason: " + Enum.GetName(typeof(Error), error)); + } + return transferred; + } + } + + 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."); + + lock (_writesync) + { + Error error = libusb.interrupt_transfer(_handle, endpointMode, buffer, count, ref transferred); + if (error < 0) + { + throw new IOException("Failed to write to device. Reason: " + Enum.GetName(typeof(Error), error)); + } + } + } + + internal override void HandleFree() + { + // Should not free handle while we still have intention of performing I/O + } + + protected override void Dispose(bool disposing) + { + libusb.release_interface(_handle, _interfaceNum); + libusb.close(_handle); + base.Dispose(disposing); + } + } +} \ No newline at end of file diff --git a/HidSharp/Platform/Libusb/LinuxNativeMethods.cs b/HidSharp/Platform/Libusb/LinuxNativeMethods.cs new file mode 100644 index 0000000..071a1ae --- /dev/null +++ b/HidSharp/Platform/Libusb/LinuxNativeMethods.cs @@ -0,0 +1,150 @@ +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 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); + + [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); + + [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); + + [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); + } + + 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 unsafe Error get_config_descriptor(IntPtr device, byte configIndex, out libusb_config_descriptor* 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); + } + + 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); + } + + 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 new file mode 100644 index 0000000..d78efa7 --- /dev/null +++ b/HidSharp/Platform/Libusb/MacOSNativeMethods.cs @@ -0,0 +1,150 @@ +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 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); + + [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); + + [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); + + [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); + } + + 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 unsafe Error get_config_descriptor(IntPtr device, byte configIndex, out libusb_config_descriptor* 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); + } + + 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); + } + + public void hotplug_deregister_callback(IntPtr ctx, int callbackHandle) + { + libusb_hotplug_deregister_callback(ctx, callbackHandle); + } + } +} + diff --git a/HidSharp/Platform/Libusb/NativeMethods.cs b/HidSharp/Platform/Libusb/NativeMethods.cs deleted file mode 100644 index 052db71..0000000 --- a/HidSharp/Platform/Libusb/NativeMethods.cs +++ /dev/null @@ -1,234 +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; - -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, - AccessDenied = -3, - NoDevice = -4, - NotFound = -5, - Busy = -6, - Timeout = -7, - Overflow = -8, - Pipe = -9, - Interrupted = -10, - OutOfMemory = -11, - NotSupported = -12 - } - - [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 unsafe extern uint libusb_get_device_list(IntPtr context, out IntPtr list); - - [DllImport(Libusb)] - public static unsafe 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 IntPtr libusb_open_device_with_vid_pid(IntPtr context, ushort idVendor, ushort idProduct); - - [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); - - 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); - } - - 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); - } -} - diff --git a/HidSharp/Platform/Libusb/WinNativeMethods.cs b/HidSharp/Platform/Libusb/WinNativeMethods.cs new file mode 100644 index 0000000..8c9afe0 --- /dev/null +++ b/HidSharp/Platform/Libusb/WinNativeMethods.cs @@ -0,0 +1,150 @@ +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 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); + + [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); + + [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); + + [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); + } + + 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 unsafe Error get_config_descriptor(IntPtr device, byte configIndex, out libusb_config_descriptor* 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); + } + + 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); + } + + public void hotplug_deregister_callback(IntPtr ctx, int callbackHandle) + { + libusb_hotplug_deregister_callback(ctx, callbackHandle); + } + } +} +