Skip to content

Linux and MacOS Support

Christian Findlay edited this page Feb 3, 2019 · 13 revisions

Linux and MacOS support is here! Check out the MacOS sample in the develop branch. Grab the NuGet for Device.Net.LibUsb.

The most common Linux/Windows cross platform library is called LibUsb. There is a C# wrapper for it called Device.Net.LibUsb via LibUsbDotNet. You can use this to bring Linux and MacOS support to your Device.Net based app. As long as you use the IDevice interface across platforms, code will be 100% compatible. Here is the wrapper class:

public class LibUsbDevice : IDevice
    {
        #region Fields
        private UsbEndpointReader _UsbEndpointReader;
        private UsbEndpointWriter _UsbEndpointWriter;
        private int ReadPacketSize;
        private SemaphoreSlim _WriteAndReadLock = new SemaphoreSlim(1, 1);
        #endregion

        #region Public Properties
        public UsbDevice UsbDevice { get; }
        public int VendorId => UsbDevice.UsbRegistryInfo.Vid;
        public int ProductId => UsbDevice.UsbRegistryInfo.Pid;
        public int Timeout { get; }
        public bool IsInitialized => true;
        public ConnectedDeviceDefinitionBase ConnectedDeviceDefinition => throw new NotImplementedException();
        public string DeviceId => throw new NotImplementedException();
        #endregion

        #region Events
        public event EventHandler Connected;
        public event EventHandler Disconnected;
        #endregion

        #region Constructor
        public LibUsbDevice(UsbDevice usbDevice, int timeout)
        {
            UsbDevice = usbDevice;
            Timeout = timeout;
        }
        #endregion

        #region Implementation
        public void Dispose()
        {
            //TODO: Release the device...
            // UsbDevice.Dispose();
        }


        public async Task InitializeAsync()
        {
            await Task.Run(() =>
            {

                //TODO: Error handling etc.
                UsbDevice.Open();

                //TODO: This is far beyond not cool.
                if (UsbDevice is MonoUsbDevice monoUsbDevice)
                {
                    monoUsbDevice.ClaimInterface(0);
                }
                else
                {
                    ((IUsbDevice)UsbDevice).ClaimInterface(0);
                }

                _UsbEndpointWriter = UsbDevice.OpenEndpointWriter(WriteEndpointID.Ep01);
                _UsbEndpointReader = UsbDevice.OpenEndpointReader(ReadEndpointID.Ep01);
                ReadPacketSize = _UsbEndpointReader.EndpointInfo.Descriptor.MaxPacketSize;
            });
        }

        public async Task<byte[]> ReadAsync()
        {
            await _WriteAndReadLock.WaitAsync();

            try
            {
                return await Task.Run(() =>
                {
                    var buffer = new byte[ReadPacketSize];

                    _UsbEndpointReader.Read(buffer, Timeout, out var bytesRead);

                    return buffer;
                });
            }
            finally
            {
                _WriteAndReadLock.Release();
            }
        }

        public async Task WriteAsync(byte[] data)
        {
            await _WriteAndReadLock.WaitAsync();

            try
            {
                await Task.Run(() =>
                {
                    _UsbEndpointWriter.Write(data, Timeout, out var bytesWritten);
                });
            }
            finally
            {
                _WriteAndReadLock.Release();
            }
        }

        public async Task<byte[]> WriteAndReadAsync(byte[] writeBuffer)
        {
            await WriteAsync(writeBuffer);
            return await ReadAsync();
        }
        #endregion
    }

Code Reference

Here is a simple Mac sample.

    public partial class ViewController : NSViewController
    {
        TrezorExample TrezorExample = new TrezorExample();

        public ViewController(IntPtr handle) : base(handle)
        {

        }

        public async override void ViewDidLoad()
        {
            base.ViewDidLoad();

            LibUsbUsbDeviceFactory.Register();

            await TrezorExample.InitializeTrezorAsync();
            var buffer = await TrezorExample.WriteAndReadFromDeviceAsync();

            var alert = new NSAlert
            {
                MessageText = $"Got it! {buffer[0]},{buffer[1]},{buffer[2]}.  Warning: Make sure you unplug and replug before trying again.",
                AlertStyle = NSAlertStyle.Informational
            };

            alert.AddButton("OK");

            var returnValue = alert.RunModal();
        }

        public override NSObject RepresentedObject
        {
            get
            {
                return base.RepresentedObject;
            }
            set
            {
                base.RepresentedObject = value;
                // Update the view, if already loaded.
            }
        }
    }

Code Reference

Trezor Example Code Reference

HIDSharp is another cross platform library that looks promising. It seems to have providers for MacOS and Linux. I haven't tested this, but I'd really like to hear from anyone who has tried it.