internal static MacHidDevice TryCreate(NativeMethods.io_string_t path) { var d = new MacHidDevice() { _path = path }; var service = NativeMethods.IORegistryEntryFromPath(0, ref path).ToIOObject(); if (!service.IsSet) { return(null); } using (service) { int?vid = NativeMethods.IORegistryEntryGetCFProperty_Int(service, NativeMethods.kIOHIDVendorIDKey); int?pid = NativeMethods.IORegistryEntryGetCFProperty_Int(service, NativeMethods.kIOHIDProductIDKey); int?version = NativeMethods.IORegistryEntryGetCFProperty_Int(service, NativeMethods.kIOHIDVersionNumberKey); if (vid == null || pid == null || version == null) { return(null); } // TODO: Craft the report descriptor from IOHIDElements so we can support it below OS X 10.8... // Also, our report sizes aren't correct for the no-report-ID case without this... d._vid = (int)vid; d._pid = (int)pid; d._version = (int)version; d._maxInput = NativeMethods.IORegistryEntryGetCFProperty_Int(service, NativeMethods.kIOHIDMaxInputReportSizeKey) ?? 0; d._maxOutput = NativeMethods.IORegistryEntryGetCFProperty_Int(service, NativeMethods.kIOHIDMaxOutputReportSizeKey) ?? 0; d._maxFeature = NativeMethods.IORegistryEntryGetCFProperty_Int(service, NativeMethods.kIOHIDMaxFeatureReportSizeKey) ?? 0; d._manufacturer = NativeMethods.IORegistryEntryGetCFProperty_String(service, NativeMethods.kIOHIDManufacturerKey); d._productName = NativeMethods.IORegistryEntryGetCFProperty_String(service, NativeMethods.kIOHIDProductKey); d._serialNumber = NativeMethods.IORegistryEntryGetCFProperty_String(service, NativeMethods.kIOHIDSerialNumberKey); d._reportDescriptor = NativeMethods.IORegistryEntryGetCFProperty_Data(service, NativeMethods.kIOHIDReportDescriptorKey); if (d._maxInput == 0 && d._maxOutput == 0 && d._maxFeature == 0) { return(null); } // Does this device use Report IDs? Let's find out. d._reportsUseID = false; bool hasInput = false, hasOutput = false, hasFeature = false; using (var device = NativeMethods.IOHIDDeviceCreate(IntPtr.Zero, service).ToCFType()) { if (!device.IsSet) { return(null); } using (var elementArray = NativeMethods.IOHIDDeviceCopyMatchingElements(device, IntPtr.Zero).ToCFType()) { if (!elementArray.IsSet) { return(null); } int elementCount = checked ((int)NativeMethods.CFArrayGetCount(elementArray)); for (int elementIndex = 0; elementIndex < elementCount; elementIndex++) { var element = NativeMethods.CFArrayGetValueAtIndex(elementArray, (IntPtr)elementIndex); if (element == IntPtr.Zero) { continue; } var elementType = NativeMethods.IOHIDElementGetType(element); switch (elementType) { case NativeMethods.IOHIDElementType.InputMisc: case NativeMethods.IOHIDElementType.InputButton: case NativeMethods.IOHIDElementType.InputAxis: case NativeMethods.IOHIDElementType.InputScanCodes: hasInput = true; break; case NativeMethods.IOHIDElementType.Output: hasOutput = true; break; case NativeMethods.IOHIDElementType.Feature: hasFeature = true; break; } if (NativeMethods.IOHIDElementGetReportID(element) != 0) { d._reportsUseID = true; } } } } if (!d._reportsUseID) { // It does not use Report IDs. MacOS's maximums do not include said Report ID. if (d._maxInput != 0) { d._maxInput++; } if (d._maxOutput != 0) { d._maxOutput++; } if (d._maxFeature != 0) { d._maxFeature++; } } if (!hasInput) { d._maxInput = 0; } if (!hasOutput) { d._maxOutput = 0; } if (!hasFeature) { d._maxFeature = 0; } } return(d); }