Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 23 additions & 10 deletions HidSharp/Platform/HidSelector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>() 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<Libusb.LibusbHidManager<Libusb.WinNativeMethods>>(),
SafeCreate<Libusb.LibusbHidManager<Libusb.LinuxNativeMethods>>(),
SafeCreate<Libusb.LibusbHidManager<Libusb.MacOSNativeMethods>>(),
SafeCreate<Windows.WinHidManager>(),
SafeCreate<Linux.LinuxHidManager>(),
SafeCreate<MacOS.MacHidManager>(),
SafeCreate<Unsupported.UnsupportedHidManager>()
};

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" };
Expand Down
204 changes: 204 additions & 0 deletions HidSharp/Platform/Libusb/INativeMethods.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>libusb_endpoint_descriptor[]</summary>
public unsafe libusb_endpoint_descriptor* endpoints;
public IntPtr extra;
public int extra_length;
}

[StructLayout(LayoutKind.Sequential)]
public struct libusb_interface
{
/// <summary>libusb_interface_descriptor[]</summary>
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;
/// <summary> libusb_interface[] </summary>
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);
}
}
97 changes: 97 additions & 0 deletions HidSharp/Platform/Libusb/LibusbHidDevice.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
using System;
using System.Text;
using static HidSharp.Platform.Libusb.INativeMethods;

namespace HidSharp.Platform.Libusb
{
sealed class LibusbHidDevice<T> : 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<T> TryCreate(LibUsbDevice device)
{
T libusb = new T();
var hid = new LibusbHidDevice<T> { _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<T>(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();
}
}
}
Loading