void ParseMain(MainItemTag tag, uint value) { LocalIndexes indexes = null; switch (tag) { case MainItemTag.Collection: ReportCollection collection = new ReportCollection(); collection.Parent = CurrentCollection; collection.Type = (CollectionType)value; CurrentCollection = collection; indexes = collection.Indexes; break; case MainItemTag.EndCollection: CurrentCollection = CurrentCollection.Parent; break; case MainItemTag.Input: case MainItemTag.Output: case MainItemTag.Feature: ParseDataMain(tag, value, out indexes); break; } if (indexes != null) { ParseMainIndexes(indexes); } }
public void AddMainItem(MainItemTag mainItemTag, uint dataValue) { _items.Add(new EncodedItem() { ItemType = ItemType.Main, TagForMain = mainItemTag, DataValue = dataValue }); }
void ParseDataMain(MainItemTag tag, uint value, out LocalIndexes indexes) { ReportSegment segment = new ReportSegment(); segment.Flags = (DataMainItemFlags)value; segment.Parent = CurrentCollection; segment.ElementCount = (int)GetGlobalItemValue(GlobalItemTag.ReportCount); segment.ElementSize = (int)GetGlobalItemValue(GlobalItemTag.ReportSize); segment.Unit = new Units.Unit(GetGlobalItemValue(GlobalItemTag.Unit)); segment.UnitExponent = Units.Unit.DecodeExponent(GetGlobalItemValue(GlobalItemTag.UnitExponent)); indexes = segment.Indexes; EncodedItem logicalMinItem = GetGlobalItem(GlobalItemTag.LogicalMinimum); EncodedItem logicalMaxItem = GetGlobalItem(GlobalItemTag.LogicalMaximum); segment.LogicalIsSigned = (logicalMinItem != null && logicalMinItem.DataValueMayBeNegative) || (logicalMaxItem != null && logicalMaxItem.DataValueMayBeNegative); int logicalMinimum = logicalMinItem == null ? 0 : segment.LogicalIsSigned ? logicalMinItem.DataValueSigned : (int)logicalMinItem.DataValue; int logicalMaximum = logicalMaxItem == null ? 0 : segment.LogicalIsSigned ? logicalMaxItem.DataValueSigned : (int)logicalMaxItem.DataValue; int physicalMinimum = (int)GetGlobalItemValue(GlobalItemTag.PhysicalMinimum); int physicalMaximum = (int)GetGlobalItemValue(GlobalItemTag.PhysicalMaximum); if (!IsGlobalItemSet(GlobalItemTag.PhysicalMinimum) || !IsGlobalItemSet(GlobalItemTag.PhysicalMaximum) || (physicalMinimum == 0 && physicalMaximum == 0)) { physicalMinimum = logicalMinimum; physicalMaximum = logicalMaximum; } segment.LogicalMinimum = logicalMinimum; segment.LogicalMaximum = logicalMaximum; segment.PhysicalMinimum = physicalMinimum; segment.PhysicalMaximum = physicalMaximum; Report report; ReportType reportType = tag == MainItemTag.Output ? ReportType.Output : tag == MainItemTag.Feature ? ReportType.Feature : ReportType.Input; uint reportID = GetGlobalItemValue(GlobalItemTag.ReportID); if (!TryGetReport(reportType, (byte)reportID, out report)) { report = new Report() { ID = (byte)reportID, Type = reportType }; Reports.Add(report); } segment.Report = report; }
void ParseMain(MainItemTag tag, uint value) { switch (tag) { case MainItemTag.Collection: ParseMainCollection(value); break; case MainItemTag.EndCollection: ParseMainCollectionEnd(); break; case MainItemTag.Input: case MainItemTag.Output: case MainItemTag.Feature: ParseMainData(tag, value); break; } }
void PadReport(MainItemTag mainItemTag, int padToBit, ref int currentBit) { if (currentBit > padToBit) { throw new NotImplementedException(); } // Overlapping... // Padding... int paddingBitCount = padToBit - currentBit; if (paddingBitCount > 0) { AddButtonGlobalItems(paddingBitCount); _builder.AddMainItem(mainItemTag, (uint)(DataItemFlags.Constant | DataItemFlags.Variable)); currentBit += paddingBitCount; } }
void ParseMainData(MainItemTag tag, uint value) { DataItem dataItem = new DataItem(); dataItem.Flags = (DataItemFlags)value; dataItem.ParentItem = State.CurrentCollectionItem; dataItem.ElementCount = (int)State.GetGlobalItemValue(GlobalItemTag.ReportCount); dataItem.ElementBits = (int)State.GetGlobalItemValue(GlobalItemTag.ReportSize); dataItem.Unit = new Units.Unit(State.GetGlobalItemValue(GlobalItemTag.Unit)); dataItem.UnitExponent = Units.Unit.DecodeExponent(State.GetGlobalItemValue(GlobalItemTag.UnitExponent)); EncodedItem logicalMinItem = State.GetGlobalItem(GlobalItemTag.LogicalMinimum); EncodedItem logicalMaxItem = State.GetGlobalItem(GlobalItemTag.LogicalMaximum); dataItem.IsLogicalSigned = !dataItem.IsArray && ((logicalMinItem != null ? logicalMinItem.DataValue : 0) > (logicalMaxItem != null ? logicalMaxItem.DataValue : 0)); int logicalMinimum = logicalMinItem == null ? 0 : dataItem.IsLogicalSigned ? logicalMinItem.DataValueSigned : (int)logicalMinItem.DataValue; int logicalMaximum = logicalMaxItem == null ? 0 : dataItem.IsLogicalSigned ? logicalMaxItem.DataValueSigned : (int)logicalMaxItem.DataValue; EncodedItem physicalMinItem = State.GetGlobalItem(GlobalItemTag.PhysicalMinimum); EncodedItem physicalMaxItem = State.GetGlobalItem(GlobalItemTag.PhysicalMaximum); bool isPhysicalSigned = !dataItem.IsArray && ((physicalMinItem != null ? physicalMinItem.DataValue : 0) > (physicalMaxItem != null ? physicalMaxItem.DataValue : 0)); int physicalMinimum = physicalMinItem == null ? 0 : isPhysicalSigned ? physicalMinItem.DataValueSigned : (int)physicalMinItem.DataValue; int physicalMaximum = physicalMaxItem == null ? 0 : isPhysicalSigned ? physicalMaxItem.DataValueSigned : (int)physicalMaxItem.DataValue; if (physicalMinimum == 0 && physicalMaximum == 0) { physicalMinimum = logicalMinimum; physicalMaximum = logicalMaximum; } dataItem.LogicalMinimum = logicalMinimum; dataItem.LogicalMaximum = logicalMaximum; dataItem.RawPhysicalMinimum = physicalMinimum; dataItem.RawPhysicalMaximum = physicalMaximum; Report report; ReportType reportType = tag == MainItemTag.Output ? ReportType.Output : tag == MainItemTag.Feature ? ReportType.Feature : ReportType.Input; uint reportID = State.GetGlobalItemValue(GlobalItemTag.ReportID); if (!TryGetReport(reportType, (byte)reportID, out report)) { report = new Report() { ReportID = (byte)reportID, ReportType = reportType }; Reports.Add(report); var collection = State.CurrentCollectionItem; while (collection != null && !(collection is DeviceItem)) { collection = collection.ParentItem; } if (collection is DeviceItem) { ((DeviceItem)collection).Reports.Add(report); } } report.DataItems.Add(dataItem); ParseMainIndexes(dataItem); }
void EncodeReports(NativeMethods.HIDP_REPORT_TYPE reportType, MainItemTag mainItemTag) { var types = _types[(int)reportType]; var reportBytes = new byte[types.ReportLength]; var reports = types.Items.GroupBy(y => y.Item.ReportID); foreach (var report in reports) { var reportID = report.Key; var reportItemList = report.ToArray(); if (reportID != 0) { _builder.AddGlobalItem(GlobalItemTag.ReportID, reportID); } int maxBit = (reportBytes.Length - 1) * 8; // Determine the location of all report items. for (int reportItemIndex = 0; reportItemIndex < reportItemList.Length; reportItemIndex++) { var reportItem = reportItemList[reportItemIndex]; var button = reportItem.Button; var item = reportItem.Item; if (item.IsAlias != 0) { throw new NotImplementedException(); } var flags = (DataItemFlags)item.BitField; int dataIndexCount = (item.IsRange != 0) ? item.DataIndexMax - item.DataIndex + 1 : 1; if (button) { reportItem.ReportCount = dataIndexCount; reportItem.ReportSize = 1; } else { reportItem.ReportCount = item.VALUE_ReportCount; reportItem.ReportSize = item.VALUE_ReportSize; } InitData(reportBytes, reportID); if (dataIndexCount == reportItem.ReportCount) { // Individual fields... var dataList = new NativeMethods.HIDP_DATA() { DataIndex = item.DataIndex, RawValue = 0xffffffffu }; int dataCount = 1; int hr = NativeMethods.HidP_SetData(reportType, ref dataList, ref dataCount, _preparsed, reportBytes, reportBytes.Length); if (hr == NativeMethods.HIDP_STATUS_SUCCESS) { GetDataStartBit(reportBytes, reportItem, maxBit); } else if (hr == NativeMethods.HIDP_STATUS_IS_VALUE_ARRAY) { // TODO reportItem.BitOffset = maxBit; // According to https://docs.microsoft.com/en-us/windows-hardware/drivers/hid/button-capability-arrays#button-usages-in-a-variable-main-item: /* * If the number of usages specified for a variable item is less than the number of buttons in the item, * the capability array contains only one capability structure that describes one button usage (the last * usage specified in the report descriptor for the variable main item). However, see Usage Value Array * for information about usage values that have a report count greater than one. */ // How to reconstruct it, then? The following does not work: /* * var usage = (ushort)item.UsageIndex; int usageCount = 1; * hr = NativeMethods.HidP_SetUsages(reportType, item.UsagePage, 0, ref usage, ref usageCount, _preparsed, reportBytes, reportBytes.Length); * if (hr != NativeMethods.HIDP_STATUS_SUCCESS) { throw new NotImplementedException(); } */ } else { throw new NotImplementedException(); } } else if (dataIndexCount == 1) { // Array... int bitCount = reportItem.ReportCount * reportItem.ReportSize; var usageValue = new byte[(bitCount + 7) / 8]; for (int i = 0; i < usageValue.Length; i++) { usageValue[i] = 0xff; } int hr = NativeMethods.HidP_SetUsageValueArray(reportType, item.UsagePage, item.LinkCollection, item.UsageIndex, usageValue, (ushort)usageValue.Length, _preparsed, reportBytes, reportBytes.Length); if (hr == NativeMethods.HIDP_STATUS_SUCCESS) { GetDataStartBit(reportBytes, reportItem, maxBit); } else { // TODO need to set the usage value array here instead // but we only want to find the bit offset, let's just do an educated guess if (reportItemIndex == 0) { reportItem.BitOffset = 0; } else { var prevItem = reportItemList[reportItemIndex - 1]; reportItem.BitOffset = prevItem.BitOffset + prevItem.ReportCount * prevItem.ReportSize; } } } else { // Not sure... reportItem.BitOffset = maxBit; } } // Write the report descriptors. int currentBit = 0; var reportItems = report.Where(x => x.BitOffset != maxBit).OrderBy(x => x.BitOffset).ToArray(); foreach (var reportItem in reportItems) { var button = reportItem.Button; var item = reportItem.Item; int startBit = reportItem.BitOffset; int bitCount = reportItem.ReportCount * reportItem.ReportSize; if (currentBit > startBit) { throw new NotImplementedException(); } // Overlapping... SetCollection(item.LinkCollection); PadReport(mainItemTag, startBit, ref currentBit); // The entry... _builder.AddGlobalItem(GlobalItemTag.UsagePage, item.UsagePage); uint usageMin = item.UsageIndex, usageMax = (item.IsRange != 0) ? item.UsageMax : usageMin; if (item.IsRange != 0) { _builder.AddLocalItem(LocalItemTag.UsageMinimum, usageMin); _builder.AddLocalItem(LocalItemTag.UsageMaximum, usageMax); } else { _builder.AddLocalItem(LocalItemTag.Usage, usageMin); } if (button) { AddButtonGlobalItems(reportItem.ReportCount); } else { _builder.AddGlobalItemSigned(GlobalItemTag.LogicalMinimum, item.VALUE_LogicalMin); _builder.AddGlobalItemSigned(GlobalItemTag.LogicalMaximum, item.VALUE_LogicalMax); _builder.AddGlobalItemSigned(GlobalItemTag.PhysicalMinimum, item.VALUE_PhysicalMin); _builder.AddGlobalItemSigned(GlobalItemTag.PhysicalMaximum, item.VALUE_PhysicalMax); _builder.AddGlobalItem(GlobalItemTag.Unit, item.VALUE_Units); _builder.AddGlobalItem(GlobalItemTag.UnitExponent, item.VALUE_UnitsExp); _builder.AddGlobalItem(GlobalItemTag.ReportSize, (uint)reportItem.ReportSize); _builder.AddGlobalItem(GlobalItemTag.ReportCount, (uint)reportItem.ReportCount); } _builder.AddMainItem(mainItemTag, item.BitField); currentBit += bitCount; } PadReport(mainItemTag, maxBit, ref currentBit); } }