Search code examples
winapiusbhid

HidD_SetFeature() works great, HidD_GetFeature() fails with ERROR_CRC (23). What could cause this?


I'm developing a USB device as a standard HID keyboard with an 8-byte feature report added to the report descriptor; I'm writing an host app to configure the device. I'm trying to adapt the wonderful HidLibrary to utilize the HidD_GetFeature() function in hid.dll.

Before I start posting c# code, I will say that I have successfully tested my firmware using the SimpleHidWrite utility with both Get and Set Feature commands, so I'm fairly confident that's not the issue.

The HidD_SetFeature() function, as wrapped in the HidLibrary API, works great. I can write 8 bytes to the device and I've verified using the SimpleHidWrite tool that they are stored correctly. However, I can't pull those bytes back using HidD_GetFeature(), and I'm baffled as to why.

Here are what I believe to be the pertinent details.

First, the CreateFile call built into the library with values:

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    internal static IntPtr CreateFile(
        string lpFileName,
        uint dwDesiredAccess,
        int dwShareMode,
        ref HidLibrary.NativeMethods.SECURITY_ATTRIBUTES lpSecurityAttributes,
        int dwCreationDisposition,
        int dwFlagsAndAttributes,
        int hTemplateFile);

where (using test vid/pid):

lpFileName = "\\?\hid#vid_03eb&pid_2042#7&1fef463f&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}"
dwDesiredAccess = 0
dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE (0x01 | 0x02)
lpSecurityAttributes = { bInheritHandle = true, lpSecurityDescriptor = 0, nLength = 12 }
dwCreationDisposition = OPEN_EXISTING (3)
dwFlagsAndAttributes = 0
hTemplateFile = 0

The import definition:

[DllImport("hid.dll", SetLastError = true)]
    static internal extern bool HidD_GetFeature(IntPtr hidDeviceObject, ref byte[] lpReportBuffer, int reportBufferLength);

And finally the API method I'm creating that is currently failing on the HidD_GetFeature() call with error 23 (ERROR_CRC):

    public bool ReadFeatureData(byte reportId, out byte[] data)
    {
        if (_deviceCapabilities.FeatureReportByteLength <= 0)
        {
            data = new byte[0];
            return false;
        }
        // FeatureReportByteLength returns 9 (byte 0 is the report id and 8 bytes for the actual report length)
        data = new byte[_deviceCapabilities.FeatureReportByteLength];

        //yields a 9-byte array
        var buffer = this.CreateFeatureInputBuffer(); 
        buffer[0] = reportId;

        IntPtr hidHandle = IntPtr.Zero;
        bool success = false;
        try
        {
            // Performs the CreateFile call above resulting in an IntPtr handle
            hidHandle = OpenDeviceIO(_devicePath, NativeMethods.ACCESS_NONE);

            success = NativeMethods.HidD_GetFeature(hidHandle, ref buffer, buffer.Length);
            // at this point, success is false, and buffer has gone from 9 bytes to 1

            if(success)
            {
                Array.Copy(buffer, 0, data, 0, Math.Min(data.Length, _deviceCapabilities.FeatureReportByteLength));
            }
            else
            {
                //Yes, i know casting to a byte isn't good here; it's dirty but helping me debug for now
                data[0] = (byte)Marshal.GetLastWin32Error(); //returns 23 (verified, actual) - ERROR_CRC
            }

        }
        catch (Exception exception)
        {
            throw new Exception(string.Format("Error accessing HID device '{0}'.", _devicePath), exception);
        }
        finally
        {
            if (hidHandle != IntPtr.Zero)
                CloseDeviceIO(hidHandle);
        }

        return success;
    }

I've read there are some issues with working with system devices like keyboards and mice, and that may or may not play in here, however I know that it's possible to interact with the device in the way I'm trying because I was able to do so with SimpleHidWrite. That said, I haven't ruled anything out and any ideas are welcome.

Let me know if I need to provide any more info.


Solution

  • I think this declaration may be the problem.

    [DllImport("hid.dll", SetLastError = true)]
        static internal extern bool HidD_GetFeature(IntPtr hidDeviceObject, ref byte[] lpReportBuffer, int reportBufferLength);
    

    Try changing the lpReportBuffer ref parameter to out, or omitting it altogether.