# HG changeset patch # User sl # Date 1417816239 -3600 # Node ID 8f7e35c3bfd162b9bd9f93a637c831ba6d535cec # Parent 9a3e776550313356891c73f1493a26a246ee9dc9 Draft implementation of proper HID input report parsing based on HID descriptor. diff -r 9a3e77655031 -r 8f7e35c3bfd1 RawInput.cs --- a/RawInput.cs Wed Dec 03 21:54:45 2014 +0100 +++ b/RawInput.cs Fri Dec 05 22:50:39 2014 +0100 @@ -5,6 +5,9 @@ namespace Win32 { + /// + /// Provide some utility functions for raw input handling. + /// static class RawInput { /// @@ -90,5 +93,145 @@ return success; } + /// + /// + /// + /// + /// + public static IntPtr GetPreParsedData(IntPtr hDevice) + { + uint ppDataSize = 256; + int result = Win32.Function.GetRawInputDeviceInfo(hDevice, Win32.Const.RIDI_PREPARSEDDATA, IntPtr.Zero, ref ppDataSize); + if (result != 0) + { + Debug.WriteLine("Failed to get raw input pre-parsed data size" + result + Marshal.GetLastWin32Error()); + return IntPtr.Zero; + } + + IntPtr ppData = Marshal.AllocHGlobal((int)ppDataSize); + result = Win32.Function.GetRawInputDeviceInfo(hDevice, Win32.Const.RIDI_PREPARSEDDATA, ppData, ref ppDataSize); + if (result <= 0) + { + Debug.WriteLine("Failed to get raw input pre-parsed data" + result + Marshal.GetLastWin32Error()); + return IntPtr.Zero; + } + return ppData; + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /* + private bool GetUsageFromRawInput(TwinhanHidDriver driver, TwinhanHid device, NativeMethods.RAWINPUT input, IntPtr rawInput, out UsageType usageType, out int usage, out string usageName) + { + usageType = 0; + usage = 0; + usageName = string.Empty; + if (input.header.dwType == NativeMethods.RawInputDeviceType.RIM_TYPEKEYBOARD) + { + if (input.data.keyboard.Flags == NativeMethods.RawInputKeyboardFlag.RI_KEY_BREAK) + { + _modifiers = 0; + // Key up event. We don't handle repeats, so ignore this. + return false; + } + NativeMethods.VirtualKey vk = input.data.keyboard.VKey; + if (vk == NativeMethods.VirtualKey.VK_CONTROL) + { + _modifiers |= VirtualKeyModifier.Control; + return false; + } + if (vk == NativeMethods.VirtualKey.VK_SHIFT) + { + _modifiers |= VirtualKeyModifier.Shift; + return false; + } + if (vk == NativeMethods.VirtualKey.VK_MENU) + { + _modifiers |= VirtualKeyModifier.Alt; + return false; + } + usageType = UsageType.Keyboard; + usage = (int)vk | (int)_modifiers; + usageName = vk.ToString(); + if (_modifiers != 0) + { + usageName += string.Format(", modifiers = {0}", _modifiers); + } + } + else if (input.header.dwType == NativeMethods.RawInputDeviceType.RIM_TYPEHID) + { + if ((!driver.IsTerraTec && device.Name.Contains("Col03")) || (driver.IsTerraTec && device.Name.Contains("Col02"))) + { + usageType = UsageType.Raw; + usage = Marshal.ReadByte(rawInput, HID_INPUT_DATA_OFFSET + 1); + usageName = string.Format("0x{0:x2}", usage); + } + else if (device.Name.Contains("Col05")) + { + usageType = UsageType.Ascii; + usage = Marshal.ReadByte(rawInput, HID_INPUT_DATA_OFFSET + 1); + usageName = string.Format("0x{0:x2}", usage); + } + else + { + byte[] report = new byte[input.data.hid.dwSizeHid]; + Marshal.Copy(IntPtr.Add(rawInput, HID_INPUT_DATA_OFFSET), report, 0, report.Length); + uint usageCount = input.data.hid.dwCount; + NativeMethods.USAGE_AND_PAGE[] usages = new NativeMethods.USAGE_AND_PAGE[usageCount]; + NativeMethods.HidStatus status = NativeMethods.HidP_GetUsagesEx(NativeMethods.HIDP_REPORT_TYPE.HidP_Input, 0, usages, ref usageCount, device.PreParsedData, report, (uint)report.Length); + if (status != NativeMethods.HidStatus.HIDP_STATUS_SUCCESS) + { + this.LogError("Twinhan HID remote: failed to get raw input HID usages, device = {0}, status = {1}", device.Name, status); + Dump.DumpBinary(rawInput, (int)input.header.dwSize); + return false; + } + if (usageCount > 1) + { + this.LogWarn("Twinhan HID remote: multiple simultaneous HID usages not supported"); + } + NativeMethods.USAGE_AND_PAGE up = usages[0]; + HidUsagePage page = (HidUsagePage)up.UsagePage; + usage = up.Usage; + if (page != HidUsagePage.MceRemote && usage == 0) + { + // Key up event. We don't handle repeats, so ignore this. + return false; + } + if (page == HidUsagePage.Consumer) + { + usageType = UsageType.Consumer; + usageName = Enum.GetName(typeof(HidConsumerUsage), up.Usage); + } + else if (page == HidUsagePage.MceRemote) + { + usageType = UsageType.Mce; + usageName = Enum.GetName(typeof(MceRemoteUsage), up.Usage); + } + else + { + this.LogError("Twinhan HID remote: unexpected usage page, device = {0}, page = {1}, usage = {2}", device.Name, page, up.Usage); + return false; + } + } + } + else + { + this.LogError("Twinhan HID remote: received input from unsupported input device type, device = {0}, type = {1}", device.Name, input.header.dwType); + return false; + } + return true; + } + * */ + } } \ No newline at end of file diff -r 9a3e77655031 -r 8f7e35c3bfd1 RemoteControlDevice.cs --- a/RemoteControlDevice.cs Wed Dec 03 21:54:45 2014 +0100 +++ b/RemoteControlDevice.cs Fri Dec 05 22:50:39 2014 +0100 @@ -395,8 +395,10 @@ Debug.WriteLine("================BACKGROUND"); } - //Declare a pointer + //Declare some pointers IntPtr rawInputBuffer = IntPtr.Zero; + //My understanding is that this is basically our HID descriptor + IntPtr preParsedData = IntPtr.Zero; try { @@ -421,6 +423,9 @@ //Get Usage Page and Usage Debug.WriteLine("Usage Page: 0x" + deviceInfo.hid.usUsagePage.ToString("X4") + " Usage ID: 0x" + deviceInfo.hid.usUsage.ToString("X4")); + + preParsedData = RawInput.GetPreParsedData(rawInput.header.hDevice); + // HidUsageHandler usagePageHandler=null; @@ -449,7 +454,7 @@ //Allocate a buffer for one HID input - byte[] hidInput = new byte[rawInput.hid.dwSizeHid]; + byte[] hidInputReport = new byte[rawInput.hid.dwSizeHid]; //For each HID input in our raw input for (int i = 0; i < rawInput.hid.dwCount; i++) @@ -464,11 +469,11 @@ } //Copy HID input into our buffer - Marshal.Copy(new IntPtr(hidInputOffset), hidInput, 0, rawInput.hid.dwSizeHid); + Marshal.Copy(new IntPtr(hidInputOffset), hidInputReport, 0, (int)rawInput.hid.dwSizeHid); //Print HID raw input in our debug output string hidDump = "HID " + rawInput.hid.dwCount + "/" + rawInput.hid.dwSizeHid + ":"; - foreach (byte b in hidInput) + foreach (byte b in hidInputReport) { hidDump += b.ToString("X2"); } @@ -476,19 +481,40 @@ ushort usage = 0; //hidInput[0] //Not sure what's the meaning of the code at offset 0 - if (hidInput.Length == 2) + if (hidInputReport.Length == 2) { //Single byte code - usage = hidInput[1]; //Get button code + usage = hidInputReport[1]; //Get button code } - else if (hidInput.Length > 2) //Defensive + else if (hidInputReport.Length > 2) //Defensive { //Assuming double bytes code - usage = (ushort)((hidInput[2] << 8) + hidInput[1]); + usage = (ushort)((hidInputReport[2] << 8) + hidInputReport[1]); } Debug.WriteLine("Usage: 0x" + usage.ToString("X4")); + //Proper parsing now + //byte[] report = new byte[input.data.hid.dwSizeHid]; + //Marshal.Copy(IntPtr.Add(rawInput, HID_INPUT_DATA_OFFSET), report, 0, report.Length); + uint usageCount = rawInput.hid.dwCount; + Win32.USAGE_AND_PAGE[] usages = new Win32.USAGE_AND_PAGE[usageCount]; + Win32.HidStatus status = Win32.Function.HidP_GetUsagesEx(Win32.HIDP_REPORT_TYPE.HidP_Input, 0, usages, ref usageCount, preParsedData, hidInputReport, (uint)hidInputReport.Length); + if (status != Win32.HidStatus.HIDP_STATUS_SUCCESS) + { + Debug.WriteLine("Could not parse HID data!"); + //this.LogError("Twinhan HID remote: failed to get raw input HID usages, device = {0}, status = {1}", device.Name, status); + //Dump.DumpBinary(rawInput, (int)input.header.dwSize); + //return false; + } + else + { + Debug.WriteLine("UsagePage: 0x" + usages[0].UsagePage.ToString("X4")); + Debug.WriteLine("Usage: 0x" + usages[0].Usage.ToString("X4")); + } + + + //Call on our Usage Page handler usagePageHandler(usage); } @@ -510,6 +536,7 @@ { //Always executed when leaving our try block Marshal.FreeHGlobal(rawInputBuffer); + Marshal.FreeHGlobal(preParsedData); } } diff -r 9a3e77655031 -r 8f7e35c3bfd1 RemoteControlSample.csproj --- a/RemoteControlSample.csproj Wed Dec 03 21:54:45 2014 +0100 +++ b/RemoteControlSample.csproj Fri Dec 05 22:50:39 2014 +0100 @@ -132,6 +132,7 @@ Code + diff -r 9a3e77655031 -r 8f7e35c3bfd1 Win32Hid.cs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Win32Hid.cs Fri Dec 05 22:50:39 2014 +0100 @@ -0,0 +1,69 @@ +using System; +using System.Runtime.InteropServices; + +namespace Win32 +{ + + static partial class Function + { + [DllImport("hid.dll", CharSet = CharSet.Unicode)] + public static extern HidStatus HidP_GetUsagesEx(HIDP_REPORT_TYPE ReportType, ushort LinkCollection, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] USAGE_AND_PAGE[] ButtonList, ref uint UsageLength, IntPtr PreparsedData, [MarshalAs(UnmanagedType.LPArray)] byte[] Report, uint ReportLength); + } + + + static partial class Macro + { + + + } + + + static partial class Const + { + + + } + + + public enum HIDP_REPORT_TYPE : ushort + { + HidP_Input = 0, + HidP_Output, + HidP_Feature + } + + + public enum HidStatus : uint + { + HIDP_STATUS_SUCCESS = 0x110000, + HIDP_STATUS_NULL = 0x80110001, + HIDP_STATUS_INVALID_PREPARSED_DATA = 0xc0110001, + HIDP_STATUS_INVALID_REPORT_TYPE = 0xc0110002, + HIDP_STATUS_INVALID_REPORT_LENGTH = 0xc0110003, + HIDP_STATUS_USAGE_NOT_FOUND = 0xc0110004, + HIDP_STATUS_VALUE_OUT_OF_RANGE = 0xc0110005, + HIDP_STATUS_BAD_LOG_PHY_VALUES = 0xc0110006, + HIDP_STATUS_BUFFER_TOO_SMALL = 0xc0110007, + HIDP_STATUS_INTERNAL_ERROR = 0xc0110008, + HIDP_STATUS_I8042_TRANS_UNKNOWN = 0xc0110009, + HIDP_STATUS_INCOMPATIBLE_REPORT_ID = 0xc011000a, + HIDP_STATUS_NOT_VALUE_ARRAY = 0xc011000b, + HIDP_STATUS_IS_VALUE_ARRAY = 0xc011000c, + HIDP_STATUS_DATA_INDEX_NOT_FOUND = 0xc011000d, + HIDP_STATUS_DATA_INDEX_OUT_OF_RANGE = 0xc011000e, + HIDP_STATUS_BUTTON_NOT_PRESSED = 0xc011000f, + HIDP_STATUS_REPORT_DOES_NOT_EXIST = 0xc0110010, + HIDP_STATUS_NOT_IMPLEMENTED = 0xc0110020, + HIDP_STATUS_I8242_TRANS_UNKNOWN = 0xc0110009 + } + + + [StructLayout(LayoutKind.Sequential)] + public struct USAGE_AND_PAGE + { + public ushort Usage; + public ushort UsagePage; + }; + + +} \ No newline at end of file diff -r 9a3e77655031 -r 8f7e35c3bfd1 Win32RawInput.cs --- a/Win32RawInput.cs Wed Dec 03 21:54:45 2014 +0100 +++ b/Win32RawInput.cs Fri Dec 05 22:50:39 2014 +0100 @@ -219,9 +219,9 @@ internal struct RAWHID { [MarshalAs(UnmanagedType.U4)] - public int dwSizeHid; + public uint dwSizeHid; [MarshalAs(UnmanagedType.U4)] - public int dwCount; + public uint dwCount; // //BYTE bRawData[1]; }