public HID.UsagePage GetUsagePage(int index, ref HIDItemStateLocal localItemState) { if (!usagePage.HasValue) { var usage = localItemState.GetUsage(index); return((HID.UsagePage)(usage >> 16)); } return((HID.UsagePage)usagePage.Value); }
// Wipe state but preserve usageList allocation. public static void Reset(ref HIDItemStateLocal state) { var usageList = state.usageList; state = new HIDItemStateLocal(); if (usageList != null) { usageList.Clear(); state.usageList = usageList; } }
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); }