// Copyright (C)2018-2025, 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.Message64.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);
}
}
}