private TreeViewItem BuildElementItem(int index, TreeViewItem parent, HID.HIDElementDescriptor element, ref int id, ref int runningBitOffset, ref HID.HIDReportType currentReportType) { var item = AddChild(parent, string.Format("Element {0} ({1})", index, element.reportType), ref id); string usagePageString; string usageString; HID.UsageToString(element.usagePage, element.usage, out usagePageString, out usageString); AddChild(item, string.Format("Usage Page: 0x{0:X} ({1})", (uint)element.usagePage, usagePageString), ref id); if (usageString != null) { AddChild(item, string.Format("Usage: 0x{0:X} ({1})", element.usage, usageString), ref id); } else { AddChild(item, string.Format("Usage: 0x{0:X}", element.usage), ref id); } AddChild(item, "Report Type: " + element.reportType, ref id); AddChild(item, "Report ID: " + element.reportId, ref id); AddChild(item, "Report Size in Bits: " + element.reportSizeInBits, ref id); AddChild(item, "Report Count: " + element.reportCount, ref id); AddChild(item, "Collection Index: " + element.collectionIndex, ref id); AddChild(item, string.Format("Unit: {0:X}", element.unit), ref id); AddChild(item, string.Format("Unit Exponent: {0:X}", element.unitExponent), ref id); AddChild(item, "Logical Min: " + element.logicalMin, ref id); AddChild(item, "Logical Max: " + element.logicalMax, ref id); AddChild(item, "Physical Min: " + element.physicalMin, ref id); AddChild(item, "Physical Max: " + element.physicalMax, ref id); AddChild(item, "Has Null State?: " + element.hasNullState, ref id); AddChild(item, "Has Preferred State?: " + element.hasPreferredState, ref id); AddChild(item, "Is Array?: " + element.isArray, ref id); AddChild(item, "Is Non-Linear?: " + element.isNonLinear, ref id); AddChild(item, "Is Relative?: " + element.isRelative, ref id); AddChild(item, "Is Virtual?: " + element.isVirtual, ref id); AddChild(item, "Is Wrapping?: " + element.isWrapping, ref id); if (currentReportType != element.reportType) { currentReportType = element.reportType; runningBitOffset = 0; } AddChild(item, string.Format("Inferred Offset: byte #{0}, bit #{1}", runningBitOffset / 8, runningBitOffset % 8), ref id); runningBitOffset += element.reportSizeInBits; return(item); }
private TreeViewItem BuildElementItem(int index, TreeViewItem parent, HID.HIDElementDescriptor element, ref int id) { var item = AddChild(parent, string.Format("Element {0} ({1})", index, element.reportType), ref id); string usagePageString = HID.UsagePageToString(element.usagePage); string usageString = HID.UsageToString(element.usagePage, element.usage); AddChild(item, string.Format("Usage Page: 0x{0:X} ({1})", (uint)element.usagePage, usagePageString), ref id); if (usageString != null) { AddChild(item, string.Format("Usage: 0x{0:X} ({1})", element.usage, usageString), ref id); } else { AddChild(item, string.Format("Usage: 0x{0:X}", element.usage), ref id); } AddChild(item, "Report Type: " + element.reportType, ref id); AddChild(item, "Report ID: " + element.reportId, ref id); AddChild(item, "Report Size in Bits: " + element.reportSizeInBits, ref id); AddChild(item, "Report Bit Offset: " + element.reportOffsetInBits, ref id); AddChild(item, "Collection Index: " + element.collectionIndex, ref id); AddChild(item, string.Format("Unit: {0:X}", element.unit), ref id); AddChild(item, string.Format("Unit Exponent: {0:X}", element.unitExponent), ref id); AddChild(item, "Logical Min: " + element.logicalMin, ref id); AddChild(item, "Logical Max: " + element.logicalMax, ref id); AddChild(item, "Physical Min: " + element.physicalMin, ref id); AddChild(item, "Physical Max: " + element.physicalMax, ref id); AddChild(item, "Has Null State?: " + element.hasNullState, ref id); AddChild(item, "Has Preferred State?: " + element.hasPreferredState, ref id); AddChild(item, "Is Array?: " + element.isArray, ref id); AddChild(item, "Is Non-Linear?: " + element.isNonLinear, ref id); AddChild(item, "Is Relative?: " + element.isRelative, ref id); AddChild(item, "Is Constant?: " + element.isConstant, ref id); AddChild(item, "Is Wrapping?: " + element.isWrapping, ref id); return(item); }
public unsafe static bool ParseReportDescriptor(byte *bufferPtr, int bufferLength, ref HID.HIDDeviceDescriptor deviceDescriptor) { // Item state. var localItemState = new HIDItemStateLocal(); var globalItemState = new HIDItemStateGlobal(); // Lists where we accumulate the data from the HID items. var reports = new List <HIDReportData>(); var elements = new List <HID.HIDElementDescriptor>(); var collections = new List <HID.HIDCollectionDescriptor>(); var currentCollection = -1; // Parse the linear list of items. var endPtr = bufferPtr + bufferLength; var currentPtr = bufferPtr; while (currentPtr < endPtr) { var firstByte = *currentPtr; ////TODO if (firstByte == 0xFE) { throw new NotImplementedException("long item support"); } // Read item header. var itemSize = (byte)(firstByte & 0x3); var itemTypeAndTag = (byte)(firstByte & 0xFC); ++currentPtr; // Process item. switch (itemTypeAndTag) { // ------------ Global Items -------------- // These set item state permanently until it is reset. // Usage Page case (int)HIDItemTypeAndTag.UsagePage: globalItemState.usagePage = ReadData(itemSize, currentPtr, endPtr); break; // Report Count case (int)HIDItemTypeAndTag.ReportCount: globalItemState.reportCount = ReadData(itemSize, currentPtr, endPtr); break; // Report Size case (int)HIDItemTypeAndTag.ReportSize: globalItemState.reportSize = ReadData(itemSize, currentPtr, endPtr); break; // Report ID case (int)HIDItemTypeAndTag.ReportID: globalItemState.reportId = ReadData(itemSize, currentPtr, endPtr); break; // Logical Minimum case (int)HIDItemTypeAndTag.LogicalMinimum: globalItemState.logicalMinimum = ReadData(itemSize, currentPtr, endPtr); break; // Logical Maximum case (int)HIDItemTypeAndTag.LogicalMaximum: globalItemState.logicalMaximum = ReadData(itemSize, currentPtr, endPtr); break; // Physical Minimum case (int)HIDItemTypeAndTag.PhysicalMinimum: globalItemState.physicalMinimum = ReadData(itemSize, currentPtr, endPtr); break; // Physical Maximum case (int)HIDItemTypeAndTag.PhysicalMaximum: globalItemState.physicalMaximum = ReadData(itemSize, currentPtr, endPtr); break; // Unit Exponent case (int)HIDItemTypeAndTag.UnitExponent: globalItemState.unitExponent = ReadData(itemSize, currentPtr, endPtr); break; // Unit case (int)HIDItemTypeAndTag.Unit: globalItemState.unit = ReadData(itemSize, currentPtr, endPtr); break; // ------------ Local Items -------------- // These set the state for the very next elements to be generated. // Usage case (int)HIDItemTypeAndTag.Usage: localItemState.SetUsage(ReadData(itemSize, currentPtr, endPtr)); break; // Usage Minimum case (int)HIDItemTypeAndTag.UsageMinimum: localItemState.usageMinimum = ReadData(itemSize, currentPtr, endPtr); break; // Usage Maximum case (int)HIDItemTypeAndTag.UsageMaximum: localItemState.usageMaximum = ReadData(itemSize, currentPtr, endPtr); break; // ------------ Main Items -------------- // These emit things into the descriptor based on the local and global item state. // Collection case (int)HIDItemTypeAndTag.Collection: // Start new collection. var parentCollection = currentCollection; currentCollection = collections.Count; collections.Add(new HID.HIDCollectionDescriptor { type = (HID.HIDCollectionType)ReadData(itemSize, currentPtr, endPtr), parent = parentCollection, usagePage = globalItemState.GetUsagePage(0, ref localItemState), usage = localItemState.GetUsage(0), firstChild = elements.Count }); HIDItemStateLocal.Reset(ref localItemState); break; // EndCollection case (int)HIDItemTypeAndTag.EndCollection: if (currentCollection == -1) { return(false); } // Close collection. var collection = collections[currentCollection]; collection.childCount = elements.Count - collection.firstChild; collections[currentCollection] = collection; // Switch back to parent collection (if any). currentCollection = collection.parent; HIDItemStateLocal.Reset(ref localItemState); break; // Input/Output/Feature case (int)HIDItemTypeAndTag.Input: case (int)HIDItemTypeAndTag.Output: case (int)HIDItemTypeAndTag.Feature: // Determine report type. var reportType = itemTypeAndTag == (int)HIDItemTypeAndTag.Input ? HID.HIDReportType.Input : itemTypeAndTag == (int)HIDItemTypeAndTag.Output ? HID.HIDReportType.Output : HID.HIDReportType.Feature; // Find report. var reportIndex = HIDReportData.FindOrAddReport(globalItemState.reportId, reportType, reports); var report = reports[reportIndex]; // If we have a report ID, then reports start with an 8 byte report ID. // Shift our offsets accordingly. if (report.currentBitOffset == 0 && globalItemState.reportId.HasValue) { report.currentBitOffset = 8; } // Add elements to report. var reportCount = globalItemState.reportCount.GetValueOrDefault(1); var flags = ReadData(itemSize, currentPtr, endPtr); for (var i = 0; i < reportCount; ++i) { var element = new HID.HIDElementDescriptor { usage = localItemState.GetUsage(i) & 0xFFFF, // Mask off usage page, if set. usagePage = globalItemState.GetUsagePage(i, ref localItemState), reportType = reportType, reportSizeInBits = globalItemState.reportSize.GetValueOrDefault(8), reportOffsetInBits = report.currentBitOffset, reportId = globalItemState.reportId.GetValueOrDefault(1), flags = (HID.HIDElementFlags)flags, logicalMin = globalItemState.logicalMinimum.GetValueOrDefault(0), logicalMax = globalItemState.logicalMaximum.GetValueOrDefault(0), physicalMin = globalItemState.GetPhysicalMin(), physicalMax = globalItemState.GetPhysicalMax(), unitExponent = globalItemState.unitExponent.GetValueOrDefault(0), unit = globalItemState.unit.GetValueOrDefault(0), }; report.currentBitOffset += element.reportSizeInBits; elements.Add(element); } reports[reportIndex] = report; HIDItemStateLocal.Reset(ref localItemState); break; } if (itemSize == 3) { currentPtr += 4; } else { currentPtr += itemSize; } } deviceDescriptor.elements = elements.ToArray(); deviceDescriptor.collections = collections.ToArray(); // Set usage and usage page on device descriptor to what's // on the toplevel application collection. foreach (var collection in collections) { if (collection.parent == -1 && collection.type == HID.HIDCollectionType.Application) { deviceDescriptor.usage = collection.usage; deviceDescriptor.usagePage = collection.usagePage; break; } } return(true); }