I have a command line utility I wrote that has been working great up until Sequoia that reads the macro keys from a Logitech G600 gaming mouse and turns it in to custom commands. it was using the following code, checking if usage was 0x80
:
IOHIDManagerRegisterInputValueCallback(
g600HIDManager,
{ _, returnResult, callbackSender, valueRef in
let elem = IOHIDValueGetElement(valueRef)
let usage = IOHIDElementGetUsage(elem)
let pressed = IOHIDValueGetIntegerValue(valueRef)
Now i'm having issues with opening the HID manager:
IOHIDManagerOpen(g600HIDManager, IOOptionBits.zero)
After changing the system security from permissive to restrictive, It's giving the error code 0xE00002E2
, or no permission. I can't easily add the sandbox entitlements as this is just a simple CLI application, not a bundled app, and even after setting back to csrutil disable
, i'm still getting this error.
So now i'm trying to turn it in to a bundled app and use CoreHID instead. Unfortunately I'm not getting any notifications that aren't the mouse itself. From the above code that was working before, i was looking for usage values of 0x80
. I'm guessing that directly corresponds to the usage 0x80 in the HID descriptor. I am receiving notifications via
await deviceClient!.monitorNotifications(reportIDsToMonitor: [] , elementsToMonitor: [] )
which should pick up everything for the device. I know the usage i'm looking for is referenced in the device client because it's in the deviceClient.elements collection.
So is there something in CoreHID that specifically blocks Vendor specified Usage pages from being picked up by notifications?
I've also tried just requesting the elements using
let elemToMon = await deviceClient?.elements.filter({ ele in
return ele.usage.page == 0xFF80 && ele.usage.usage == 0x80
})
let request = HIDDeviceClient.RequestElementUpdate(elements: elemToMon!)
let results = await deviceClient!.updateElements([request])
but that call errors (still trying to figure out exactly how it errors).
Any help would be appreciated, either in figuring out why i'm not getting the HID reports in question using CoreHID, or even what has changed that is causing me to not be able to use IOKit.hid anymore.
Thanks in advance!
For reference, here's the decoded HID descriptor:
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
0x85, 0x01, // Report ID (1)
0x05, 0x07, // Usage Page (Kbrd/Keypad)
0x19, 0xE0, // Usage Minimum (0xE0)
0x29, 0xE7, // Usage Maximum (0xE7)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x08, // Report Size (8)
0x95, 0x05, // Report Count (5)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xA4, 0x00, // Logical Maximum (164)
0x19, 0x00, // Usage Minimum (0x00)
0x2A, 0xA4, 0x00, // Usage Maximum (0xA4)
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0x06, 0x80, 0xFF, // Usage Page (Vendor Defined 0xFF80)
0x09, 0x80, // Usage (0x80)
0xA1, 0x01, // Collection (Application)
0x85, 0x80, // Report ID (-128)
0x09, 0x80, // Usage (0x80)
0x75, 0x08, // Report Size (8)
0x95, 0x05, // Report Count (5)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x85, 0xF6, // Report ID (-10)
0x09, 0xF6, // Usage (0xF6)
0x75, 0x08, // Report Size (8)
0x95, 0x07, // Report Count (7)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x85, 0xF0, // Report ID (-16)
0x09, 0xF0, // Usage (0xF0)
0x95, 0x03, // Report Count (3)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xF1, // Report ID (-15)
0x09, 0xF1, // Usage (0xF1)
0x95, 0x07, // Report Count (7)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xF2, // Report ID (-14)
0x09, 0xF2, // Usage (0xF2)
0x95, 0x04, // Report Count (4)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xF3, // Report ID (-13)
0x09, 0xF3, // Usage (0xF3)
0x95, 0x99, // Report Count (-103)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xF4, // Report ID (-12)
0x09, 0xF4, // Usage (0xF4)
0x95, 0x99, // Report Count (-103)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xF5, // Report ID (-11)
0x09, 0xF5, // Usage (0xF5)
0x95, 0x99, // Report Count (-103)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xF6, // Report ID (-10)
0x09, 0xF6, // Usage (0xF6)
0x95, 0x07, // Report Count (7)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xF7, // Report ID (-9)
0x09, 0xF7, // Usage (0xF7)
0x75, 0x08, // Report Size (8)
0x95, 0x1F, // Report Count (31)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
So, I was able to spend some time on test with this morning and, while I'm not sure of exactly what went wrong, I was able to get your CoreHID to return valid data and I think I can give you the path forward. First off, in terms of replicating the problem:
-
I don't have a G600, but I do happen to have a G502. After modifying your code to match my hardware and tweaking some of your element code, I replicated the "kUSBHostReturnPipeStalled" error.
-
That error is in fact "real". Here are the errors coming from the USB stack:
2025-03-07 11:52:54.835554-0800 (IOUSBHostFamily) AppleUSBIORequest: AppleUSBIORequest::complete: device 8 (G502 HERO Gaming Mouse@02113000) endpoint 0x00: status 0xe0005000 (pipe stalled): 0 bytes transferred
2025-03-07 11:52:54.839564-0800 (IOUSBHostFamily) AppleUSBIORequest: AppleUSBIORequest::complete: device 8 (G502 HERO Gaming Mouse@02113000) endpoint 0x00: status 0xe0005000 (pipe stalled): 0 bytes transferred
-
The nature of working with hardware means that this moves the immediate issue out of our software stack (CoreHID or the USB stack). Basically, if you send a particular command to a device and the device chokes, the only choices are "fix the hardware" or "don't do that". Assuming you don't work for Logictech, that means going with "don't do that".
-
Similarly, the reason the IOKit code DID have the same failure... is that it didn't do what your CoreHID code did.
As an aside here, one thing to keep in mind if you're new to working with HID accessories is that the gap between a devices physical design and the HID interface it presents can be very... large. Case in point, the G502 is a lovely and complicated device, but it doesn't have the 956 buttons it's HIDElement list would imply. I didn't investigate further, but it's very likely that one or some combination of those elements are what caused the bus stall and your only option is "no do that".
Shifting to the path forward, the first thing to do is look* at the output of this code snippet:
let allElements = await deviceClient?.elements
print ("Report \(String(describing: allElements))")
*It's actually more helpful to stop in the debugger and print the description by right clicking, as the debugger provides some helpful indenting which "print" completely mangles.
Each element entry contains the data about the relevant report. So, for example, my hardware returned:
▿ 1 : CoreHID.HIDElement(client: CoreHID.HIDDeviceClient:(deviceID: 0x1007b952d,
primaryUsage: CoreHID.HIDUsage(page: 1, usage: 6), vendorID: 1133, productID: 49291),
type: CoreHID.HIDReportType.input,
usage: CoreHID.HIDUsage(page: 7, usage: 225), reportID: CoreHID.HIDReportID(1))
When I fed those values back into your (modified) code, everthing worked fine. Here's my output:
DeviceName: G502 HERO Gaming Mouse
dispatchGetReportRequest succeeded: 9 bytes
Report: <Optional([CoreHID.HIDElement.Value(element:...>
And my code:
As a quick side note, using NSLog (instead of print) can be EXTREMELY helpful in this sort of code because it includes a timestamp (never a bad thing) and, more importantly, it also prints to the system console. That lets you use your app's own logging to find the broader activity it's generating like, for example, the USB errors I showed above.
__
Kevin Elliott
DTS Engineer, CoreOS/Hardware