OS X上的HIDAPI选项

时间:2013-03-12 17:45:57

标签: mono serial-port hidapi

我一直在使用C和HIDAPI,并试图让Mono使用interop与HIDAPI进行通信。我已经做了很多搜索,并且找不到任何已经让HIDAPI在OS X上使用Mono的人。

有没有人知道我是否可以使用HIDAPI将输出从HID设备重定向到本地虚拟串行端口,然后让Mono只是从串口读取?

另一种选择,有人会知道我是否可以使用像Arduino leonardo或Circuits @ Home USB Host Shield这样的东西?

至少在我可以在单声道上整理PInvoke之前。

谢谢

2 个答案:

答案 0 :(得分:2)

我从其他地方找到并改编了这个代码,虽然我不能为我的生活找到源头。如果有人知道,请告诉我,以便我可以正确归因并链接到它。它在Windows和OS X上都很适合我。显然,你必须为每个平台构建hidapi。

using System;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;

namespace HidApiCommunicationLayer
{
    internal class HidApiInteropCommLayer
    {
        #region Interop

#if WINDOWS
        private const string HIDAPI_DLL = "hidapi.dll";
#else
        private const string HIDAPI_DLL = "hidapi.dylib";
#endif

        protected IntPtr _device;

        private Object _locker = new object();

        public bool IsOpen()
        {
            return _device != IntPtr.Zero;
        }

        public void Open(ushort vid, ushort hid, string serial)
        {
            if (_device != IntPtr.Zero) throw new Exception("a device is already opened; close it first.");
            IntPtr ret = hid_open(vid, hid, serial);
            _device = ret;
            //if (_device != IntPtr.Zero)
            //    hid_set_nonblocking(_device, true);
        }

        public int Read(byte[] buffer, int length)
        {
            lock (_locker)
            {
                AssertValidDev();
                int ret = hid_read_timeout(_device, buffer, (uint)length, 1);
                if (ret < 0)
                    throw new Exception("Failed to Read.");

                return ret;
            }
        }

        public void Close()
        {
            AssertValidDev();
            hid_close(_device);
            _device = IntPtr.Zero;
        }

        public int ExitHidAPI()
        {
            return hid_exit();
        }

        public String GetProductString()
        {
            AssertValidDev();
            byte[] buf = new byte[1000];
            int ret = HidApiInteropCommLayer.hid_get_product_string(_device, buf, (uint)(buf.Length / 4) - 1);
            if (ret < 0)
                throw new Exception("failed to receive product string");
            return EncodeBuffer(buf);
        }

        public String GetManufacturerString()
        {
            AssertValidDev();
            byte[] buf = new byte[1000];
            int ret = HidApiInteropCommLayer.hid_get_manufacturer_string(_device, buf, (uint)(buf.Length / 4) - 1);
            if (ret < 0)
                throw new Exception("failed to receive manufacturer string");
            return EncodeBuffer(buf);
        }

        public int GetFeatureReport(byte[] buffer, int length)
        {
            AssertValidDev();
            int ret = hid_get_feature_report(_device, buffer, (uint)length);
            if (ret < 0)
                throw new Exception("failed to get feature report");
            return ret;
        }

        public int SendFeatureReport(byte[] buffer)
        {
            int ret = hid_send_feature_report(_device, buffer, (uint)buffer.Length);
            //if (ret < 0)
            //  throw new Exception ("failed to send feature report");
            return ret;
        }

        public int Write(byte[] buffer)
        {
            lock (_locker)
            {
                AssertValidDev();
                int ret = hid_write(_device, buffer, HID_MAX_PACKET_SIZE + 1);
                //if (ret < 0)
                //    Custom logging
                return ret;
            }
        }

        public String Error()
        {
            AssertValidDev();
            IntPtr ret = hid_error(_device);
            return Marshal.PtrToStringAuto(ret);
        }

        public string GetIndexedString(int index)
        {
            AssertValidDev();
            byte[] buf = new byte[1000];
            int ret = HidApiInteropCommLayer.hid_get_indexed_string(_device, index, buf, (uint)(buf.Length / 4) - 1);
            if (ret < 0)
                throw new Exception("failed to receive indexed string");
            return EncodeBuffer(buf);
        }

        public string GetSerialNumberString()
        {
            AssertValidDev();
            byte[] buf = new byte[1000];
            int ret = HidApiInteropCommLayer.hid_get_serial_number_string(_device, buf, (uint)(buf.Length / 4) - 1);
            if (ret < 0)
                throw new Exception("failed to receive serial number string");
            return EncodeBuffer(buf);
        }

        private string EncodeBuffer(byte[] buffer)
        {
            return Encoding.Unicode.GetString(buffer).Trim('\0');
        }

        private void AssertValidDev()
        {
            if (_device == IntPtr.Zero) throw new Exception("No device opened");
        }

        #region DllImports

        [DllImport(HIDAPI_DLL)]
        private static extern int hid_read(IntPtr device, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] data, uint length);

        [DllImport(HIDAPI_DLL)]
        private static extern int hid_read_timeout(IntPtr device, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] data, uint length, int timeout);

        [DllImport(HIDAPI_DLL)]
        private static extern IntPtr hid_open(ushort vid, ushort pid, [MarshalAs(UnmanagedType.LPWStr)] string serial);

        [DllImport(HIDAPI_DLL)]
        private static extern void hid_close(IntPtr device);

        [DllImport(HIDAPI_DLL)]
        private static extern int hid_init();

        [DllImport(HIDAPI_DLL)]
        private static extern int hid_exit();

        [DllImport(HIDAPI_DLL)]
        private static extern int hid_get_product_string(IntPtr device, [Out] byte[] _string, uint length);

        [DllImport(HIDAPI_DLL)]
        private static extern int hid_get_manufacturer_string(IntPtr device, [Out] byte[] _string, uint length);

        [DllImport(HIDAPI_DLL)]
        private static extern int hid_get_feature_report(IntPtr device, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] data, uint length);

        [DllImport(HIDAPI_DLL)]
        private static extern int hid_get_serial_number_string(IntPtr device, [Out] byte[] serial, uint maxlen);

        [DllImport(HIDAPI_DLL)]
        private static extern int hid_get_indexed_string(IntPtr device, int string_index, [Out] byte[] _string, uint maxlen);

        [DllImport(HIDAPI_DLL)]
        private static extern IntPtr hid_error(IntPtr device);

        [DllImport(HIDAPI_DLL)]
        private static extern int hid_send_feature_report(IntPtr device, [In, MarshalAs(UnmanagedType.LPArray)] byte[] data, uint length);

        [DllImport(HIDAPI_DLL)]
        private static extern int hid_set_nonblocking(IntPtr device, [In, MarshalAs(UnmanagedType.SysInt)] bool nonblock);

        [DllImport(HIDAPI_DLL)]
        private static extern int hid_write(IntPtr device, [In, MarshalAs(UnmanagedType.LPArray)] byte[] data, uint length);

        [DllImport(HIDAPI_DLL)]
        private static extern IntPtr hid_open_path([In, MarshalAs(UnmanagedType.LPStr)] string path);

        #endregion DllImports

        #endregion Interop

        #region Constructors

        public static HidApiInteropCommLayer GetDevice(ushort vid, ushort pid)
        {
            try
            {
                HidApiInteropCommLayer layer = new HidApiInteropCommLayer();
                layer.Open(vid, pid, null);
                return layer._device == IntPtr.Zero ? null : layer;
            }
            catch (System.BadImageFormatException fx)
            {
                //Custom logging
                return null;
            }
            catch (Exception ex)
            {
                //Custom logging
                return null;
            }
        }

        #endregion Constructors

        private const int HID_MAX_PACKET_SIZE = 1024;

        #region ICommunicationLayer

        public void Init()
        {
            try
            {
                if (IsOpen())
                {
                    ContinueReadProcessing = true;
                    ReadThread = new Thread(new ThreadStart(ReadLoop));
                    ReadThread.Name = "HidApiReadThread";
                    ReadThread.Start();
                }
                else
                {
                    Disconnect();
                }
            }
            catch (Exception ex)
            {
                //Custom logging
                throw;
            }
        }

        public bool SendData(byte[] data)
        {
            try
            {
                MemoryStream stream = new MemoryStream(HID_MAX_PACKET_SIZE + 1);
                stream.WriteByte(0);
                stream.Write(data, 0, HID_MAX_PACKET_SIZE);
                int ret = Write(stream.ToArray());
                if (ret >= 0)
                    return true;
                else
                {
                    return false;
                }
            }
            catch (Exception ex)
            {
                //Custom logging
                return false;
            }
        }

        public event EventHandler<DataEventArgs> DataReceived;

        public event EventHandler Disconnected;

        public void Start()
        {
            ContinueReadProcessing = true;
        }

        public void Stop()
        {
            Disconnect();
        }

        #endregion ICommunicationLayer

        private Thread ReadThread = null;

        protected volatile bool ContinueReadProcessing = true;

        private void ReadLoop()
        {
            var culture = CultureInfo.InvariantCulture;
            Thread.CurrentThread.CurrentCulture = culture;
            Thread.CurrentThread.CurrentUICulture = culture;
            Thread.CurrentThread.Priority = ThreadPriority.AboveNormal;

            while (ContinueReadProcessing)
            {
                try
                {
                    byte[] report = new byte[HID_MAX_PACKET_SIZE];

                    var result = Read(report, HID_MAX_PACKET_SIZE);

                    if (result > 0)
                    {
                        DataReceived(this, new DataEventArgs(report));
                    }
                    else if (result < 0)
                    {
                        Disconnect();
                    }
                }
                catch (Exception ex)
                {
                    Disconnect();
                }

                Thread.Sleep(1);
            }
        }

        private void Disconnect()
        {
            ContinueReadProcessing = false;
            Disconnected(this, EventArgs.Empty);
        }

        #region IDisposable Members

        public void Dispose()
        {
            ContinueReadProcessing = false;
            ReadThread.Join(500);
            if (ReadThread.IsAlive)
            {
                ReadThread.Abort();
            }

            if (IsOpen())
                Close();
            int res = ExitHidAPI();
        }

        #endregion IDisposable Members
    }

    internal class Utf32Marshaler : ICustomMarshaler
    {
        private static Utf32Marshaler instance = new Utf32Marshaler();

        public static ICustomMarshaler GetInstance(string s)
        {
            return instance;
        }

        public void CleanUpManagedData(object o)
        {
        }

        public void CleanUpNativeData(IntPtr pNativeData)
        {
            Marshal.FreeHGlobal(pNativeData);
            //UnixMarshal.FreeHeap(pNativeData);
        }

        public int GetNativeDataSize()
        {
            return IntPtr.Size;
        }

        public IntPtr MarshalManagedToNative(object obj)
        {
            string s = obj as string;
            if (s == null)
                return IntPtr.Zero;
            return Marshal.StringToHGlobalAuto(s);
        }

        public object MarshalNativeToManaged(IntPtr pNativeData)
        {
            return Marshal.PtrToStringAuto(pNativeData);
        }
    }

    public class DataEventArgs : EventArgs
    {
        public DataEventArgs(byte[] data)
        {
            Data = data;
        }

        public byte[] Data { get; private set; }
    }
}

答案 1 :(得分:0)

Arduino Leonardo不能充当USB主机。

原则上,是的,您可以将设备充当设备端的CDC串行端口,并将其作为主机USB连接到HID设备。

或者,您可以完全跳过HIDAPI步骤并使用HidSharp。 :)它将直接调用/调用MacOS X本机API。

希望这有帮助

詹姆斯