I'm writing a desktop app for Mac OS Catalina. GPL licensed for now. I might make it MIT licensed in the future, but that's secondary. It is written in Swift and supports some gamepads using user space IOKit. I got it working for Dualshock 4 and Xbox 360 controller using HID APIs. Now I'm implementing support for the Xbox One controller, but it needs to receive a byte sequence before it starts sending reports (0x05, 0x20). I saw this in several C/C++ kext projects but I'm struggling to figure out how to do this within my app in user space. I'm able to get an IOHIDDevice but I couldn't figure out how to get an endpoint from there. Can this even be done from a HID level or do I need to use a lower level API like USB or Bluetooth? I wanted to avoid using libusb just for this since I already got it working with other controllers.
Other suggestion I found was on this question: Gamepad and joystick support on Mac OS X in user space but no sample code for Mac was provided. And replacing a device descriptor sounds a bit overkill for me just to accomplish this. Also, it suggests libusb.
Some parts of my code:
let hidManager = IOHIDManagerCreate(kCFAllocatorDefault, IOOptionBits(kIOHIDOptionsTypeNone))
let deviceCriteria:NSArray = [
[
kIOHIDDeviceUsagePageKey: kHIDPage_GenericDesktop,
kIOHIDDeviceUsageKey: kHIDUsage_GD_GamePad
]
]
IOHIDManagerSetDeviceMatchingMultiple(hidManager, deviceCriteria)
IOHIDManagerScheduleWithRunLoop(hidManager, CFRunLoopGetCurrent(), CFRunLoopMode.defaultMode.rawValue)
IOHIDManagerOpen(hidManager, IOOptionBits(kIOHIDOptionsTypeNone))
IOHIDManagerRegisterDeviceMatchingCallback(...)
func hidDeviceAddedCallback(_ result:IOReturn, sender:UnsafeMutableRawPointer, device:IOHIDDevice) {
// from here I usually send hid reports using IOHIDDeviceSetReport(device, kIOHIDReportTypeOutput, etc...)
// but now I need to write directly to and endpoint of an interface
}
You can grab a reference to the IOService
object corresponding to an IOHIDDevice
using IOHIDDeviceGetService()
. You can then walk up the I/O registry provider chain to find the underlying IOUSBInterface
/IOUSBHostInterface
object. However, the generic HID driver will presumably have acquired exclusive access to the USB interface, so submitting a transfer on the pipe presumably won't be permitted without kicking off the HID driver first.