// Copyright (C)2018-2023, Philip Munts dba Munts Technologies. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. using System; namespace IO.Objects.RemoteIO.HID { /// /// Encapsulates USB raw HID devices, using the HidSharp library or /// libsimpleio. /// public class Messenger : IO.Interfaces.Message64.Messenger { // Transport library selector private enum Transport { libsimpleio, HidSharp, }; private Transport transport; // Private information for the libsimpleio library private class libHIDRaw { [System.Runtime.InteropServices.DllImport("simpleio")] public static extern void HIDRAW_open3(int VID, int PID, string serial, out int fd, out int error); [System.Runtime.InteropServices.DllImport("simpleio")] public static extern void HIDRAW_close(int fd, out int error); [System.Runtime.InteropServices.DllImport("simpleio")] public static extern void HIDRAW_get_name(int fd, System.Text.StringBuilder name, int size, out int error); [System.Runtime.InteropServices.DllImport("simpleio")] public static extern void HIDRAW_send(int fd, byte[] buf, int bufsize, out int count, out int error); [System.Runtime.InteropServices.DllImport("simpleio")] public static extern void HIDRAW_receive(int fd, byte[] buf, int bufsize, out int count, out int error); } private class libLinux { public const int POLLIN = 0x01; [System.Runtime.InteropServices.DllImport("simpleio")] public static extern void LINUX_poll(int numfiles, int[] files, int[] events, int[] results, int timeoutms, out int error); } private int fd = -1; private int timeout = 0; // Private information for the HidSharp library private int reportID_offset = 1; // Skip the report ID byte private HidSharp.HidDevice hid_device; private HidSharp.HidStream hid_stream; /// /// Create a 64-byte messenger object bound to a USB HID device. /// /// Vendor ID /// Product ID /// Serial number /// Time in milliseconds to wait for /// read and write operations to complete. Zero means wait /// forever. public Messenger(int vid = IO.Devices.USB.Munts.HID.Vendor, int pid = IO.Devices.USB.Munts.HID.Product, string serialnumber = null, int timeoutms = 1000) { // Validate parameters if (timeoutms < 0) { throw new Exception("Invalid timeout"); } if ((vid < 0) || (vid > 65535)) { throw new Exception("Invalid vendor ID"); } if ((pid < 0) || (pid > 65535)) { throw new Exception("Invalid product ID"); } if (System.IO.File.Exists("/usr/lib/libsimpleio.so") || System.IO.File.Exists("/usr/local/lib/libsimpleio.so")) { // Use libsimpleio libHIDRaw this.transport = Transport.libsimpleio; // Open the raw HID device int error; libHIDRaw.HIDRAW_open3(vid, pid, serialnumber, out this.fd, out error); if (error != 0) { throw new Exception("Cannot open USB device"); } } else { // Use HidSharp library this.transport = Transport.HidSharp; try { // Open the USB HID device HidSharp.DeviceList list = HidSharp.DeviceList.Local; if (!list.TryGetHidDevice(out hid_device, vid, pid, null, serialnumber)) { throw new Exception("DeviceList.TryGetHidDevice() failed"); } hid_stream = hid_device.Open(); hid_stream.ReadTimeout = timeoutms; hid_stream.WriteTimeout = timeoutms; } catch (System.Exception e) { throw new Exception("Cannot open USB HID device, " + e.Message); } } this.timeout = timeoutms; } /// /// Information string from the USB HID device. /// public String Info { get { switch (this.transport) { case Transport.libsimpleio: { System.Text.StringBuilder buf = new System.Text.StringBuilder(256); int error; libHIDRaw.HIDRAW_get_name(this.fd, buf, buf.Capacity, out error); if (error != 0) { throw new Exception("HIDRAW_get_name() failed"); } return buf.ToString(); } case Transport.HidSharp: { return this.hid_device.GetManufacturer().Trim() + " " + this.hid_device.GetProductName().Trim() + " " + this.hid_device.GetSerialNumber().Trim(); } default: { return ""; } } } } /// /// Send a 64-byte message to a USB HID device. /// /// Message to be sent. public void Send(IO.Interfaces.Message64.Message cmd) { switch (this.transport) { case Transport.libsimpleio: { int count; int error; libHIDRaw.HIDRAW_send(this.fd, cmd.payload, IO.Interfaces.Message64.Message.Size, out count, out error); if (error != 0) { throw new Exception("HIDRAW_send() failed"); } return; } case Transport.HidSharp: { byte[] outbuf = new byte[IO.Interfaces.Message64.Message.Size + 1]; outbuf[0] = 0; for (int i = 0; i < IO.Interfaces.Message64.Message.Size; i++) outbuf[i + this.reportID_offset] = cmd.payload[i]; this.hid_stream.Write(outbuf); return; } } } /// /// Receive a 64-byte message from a USB HID device. /// /// Message received. public void Receive(IO.Interfaces.Message64.Message resp) { switch (this.transport) { case Transport.libsimpleio: { int count; int error; if (this.timeout > 0) { int[] files = { this.fd }; int[] events = { libLinux.POLLIN }; int[] results = { 0 }; libLinux.LINUX_poll(1, files, events, results, this.timeout, out error); if (error != 0) { throw new Exception("LINUX_poll() failed"); } } libHIDRaw.HIDRAW_receive(this.fd, resp.payload, IO.Interfaces.Message64.Message.Size, out count, out error); if (error != 0) { throw new Exception("HIDRAW_send() failed"); } return; } case Transport.HidSharp: { byte[] inbuf = new byte[IO.Interfaces.Message64.Message.Size + 1]; int len = this.hid_stream.Read(inbuf); if (len != IO.Interfaces.Message64.Message.Size + this.reportID_offset) throw new Exception("Invalid response message size"); for (int i = 0; i < IO.Interfaces.Message64.Message.Size; i++) resp.payload[i] = inbuf[i + this.reportID_offset]; return; } } } /// /// Send a 64-byte command message to and receive a 64-byte response /// message from a USB HID device. /// /// Message to be sent. /// Message received. public void Transaction(IO.Interfaces.Message64.Message cmd, IO.Interfaces.Message64.Message resp) { Send(cmd); Receive(resp); } } }