IntergatedCircuits / HidSharp

HIDSharp is a multiplatform C# library for USB HID devices by James F. Bellinger
https://www.zer7.com/software/hidsharp
Other
121 stars 34 forks source link

Question about recreating functionality from a Windows only HID library using HidSharp on linux #9

Closed MostHated closed 2 years ago

MostHated commented 2 years ago

Hey there, I was hoping to get some advice, or at least pointed in the right direction on the best way to go about recreating something from another Windows-only HID library in this one, as this one seems to work well with my OS (Pop_OS). The original code uses HIDLibrary and looks like this:


HidDevice _leds;

private void send(byte[] data)
{
    if (data.Length != PACKET_SIZE)
    {
        throw new ArgumentException("Incorrect packet size");
    }

    log("SEND: " + convertBytesToStr(data));

    // the _leds object below is of type HidDevice()
    var report = _leds.CreateReport();           // -- Seen below at CreateReport section
    report.Data = data;
    report.ReportId = 1;  
    var result = _leds.WriteFeatureData(data);   // --- Seen below at WriteFeatureData section
        ...
        ...
}

When it calls create report above, this is the method it is calling in the library. The _leds object in which the call is from is of type HidDevice (https://github.com/mikeobrien/HidLibrary/blob/master/src/HidLibrary/HidDevice.cs)

CreateReport https://github.com/mikeobrien/HidLibrary/blob/c767adb79bf0a914cad58aa07c3630f97ecd0687/src/HidLibrary/HidDevice.cs#L430 ```cs public HidReport CreateReport() { return new HidReport(Capabilities.OutputReportByteLength); } ```

Then when it calls WriteFeatureData and passes the byte array, this is what it is passing it to.

WriteFeatureData() https://github.com/mikeobrien/HidLibrary/blob/c767adb79bf0a914cad58aa07c3630f97ecd0687/src/HidLibrary/HidDevice.cs#L435 ```cs public bool WriteFeatureData(byte[] data) { if (_deviceCapabilities.FeatureReportByteLength <= 0) return false; var buffer = CreateFeatureOutputBuffer(); Array.Copy(data, 0, buffer, 0, Math.Min(data.Length, _deviceCapabilities.FeatureReportByteLength)); IntPtr hidHandle = IntPtr.Zero; bool success = false; try { if (IsOpen) hidHandle = WriteHandle; else hidHandle = OpenDeviceIO(_devicePath, NativeMethods.ACCESS_NONE); //var overlapped = new NativeOverlapped(); success = NativeMethods.HidD_SetFeature(hidHandle, buffer, buffer.Length); } catch (Exception exception) { throw new Exception(string.Format("Error accessing HID device '{0}'.", _devicePath), exception); } finally { if (hidHandle != IntPtr.Zero && hidHandle != WriteHandle) CloseDeviceIO(hidHandle); } return success; } ```

I connected my device to a Windows VM and passed it through directly to the OS and everything worked as expected, so I attempted to see if I could recreate the functionality using this library, but I am stuck at this part. I am able to connect to the device and seemingly am able to send data to it, but it just seems like I am not doing it the right way. Unfortunately, I know next to nothing about working with USB devices and this is my first go at it. I am sure that what I need to do is probably pretty simple, but since I don't exactly know what I am supposed to be looking for, it's proven to be a bit difficult to properly get it working.

In my attempt, I currently have the HidDevice _leds; from the original replaced with a HidStream _leds; object and I have the HidDevice _device object, as they seemed to have a similar set of methods available to it (such as being able to get a report from _device, as well as _HidStream having the SetFeature() method)), but I am not even 100% positive that is what I should be using.

I figured I would give it a shot using the code below. I get to the point where I get the log message containing "SEND:" message but then nothing ever happens after that. I am guessing that it may be because I am not reading a report and setting the report type and assigning the data like the original code did?

My "attempt" ```cs HidDevice _device; HidStream _leds; private void send(byte[] data) { if (data.Length != PACKET_SIZE) { throw new ArgumentException("Incorrect packet size"); } log("SEND: " + convertBytesToStr(data)); // var report = _device.GetReportDescriptor(); // var rep = report.GetReport(HidSharp.Reports.ReportType.Output, 1); // rep. <-- I gave up here. I saw there is ReportID byte and DataItems, but didn't want to keep trying things in which I don't know the consequences. _leds.SetFeature(data); // This is probably not even right? ... ... } ```

It could very well be that using the HidStream type along with the SetFeature(data) method isn't even the proper thing I should be using. That is where I am hoping to get some advice if possible. If there are any recommendations that could be shared, I would be extremely grateful! I have been afraid to try much more than this, as the device (keyboard) is brand new and was definitely not cheap and I don't know if attempting to do this incorrectly might cause any issues.

Thanks, -MH

benedekkupper commented 2 years ago

Back in the day I also made the foolish mistake to start with HidLibrary instead of this one, huge mistake, the capabilities of that library are dwarfed by this, and they couldn't even merge my single commit after N+1 years...

The basic idea is this:

            // these are just descriptors, they only store information about the HID device, not passed for communication
            var reportDescriptor = hiddev.GetReportDescriptor();
            var deviceItem = reportDescriptor.DeviceItems.First();
            var freports = deviceItem.FeatureReports;

            // create a buffer with the proper size and report ID based on the report information
            var buffer = freports.First().CreateBuffer();
            // TODO put your data in the buffer, but keep in mind that buffer[0] is the report ID, the actual data comes afterwards
            Assert(buffer[0] == 1);

            // send actual data to HID device, choose between these two calls depending on FEATURE or OUTPUT report type
            stream.SetFeature(buffer);
            //stream.Write(buffer);
MostHated commented 2 years ago

This is great info, and makes sense. I do appreciate it. I am about to give it a try and see how it goes. It may seem like a silly question, but is there any sort of risk to the device by attempting to write to it like this and I need to be careful, or am I safe to try things out?

benedekkupper commented 2 years ago

That depends on what the device does with the data.

MostHated commented 2 years ago

Thanks a lot, I really do appreciate it. Looks like we're good to go now. :+1:

https://i.imgur.com/lDyHjce.gif