Well folks, this is a wild one. I recently wrote about a Roccat Kone Air mouse that I purchased for use exclusively with macOS. For a short while it worked great, but then the plan quickly fell apart.
Where are my profiles?
In my original blog post, I wrote about how the on-board profiles for the mouse had to be configured using Windows software; specifically, Roccat’s Swarm software. However, since the profiles are on-board, that implies that they are portable to different operating systems. To configure my mouse, I opened Roccat Swarm on a Parallels virtual machine of Windows 11 and began tweaking. After closing the virtual machine, all of my custom profiles worked on macOS as I expected… that is, until I unplugged the 2.4Ghz USB receiver.
It turns out, the USB receiver needs a special sauce that only the Roccat Swarm software can provide. Re-launching Roccat Swarm from the Windows 11 virtual machine and then closing the virtual machine fixes the issue, but this solution sucks. I don’t want to do this every time I dock my Mac. Even worse, I use this same dock for my separate work-provisioned Mac. This means the profiles and their custom button mappings simply are not useful for my work environment. At this point I considered giving up and switching to a different mouse. However… I am not sane, and I really like this mouse.
How does Roccat Swarm do it?
I got to thinking about how this whole thing works. I plug in the USB receiver… I launch Roccat Swarm… and it works. Even between operating systems (as long as the receiver is never unplugged), as proven by my Windows 11 virtual machine workflow. So maybe Roccat Swarm isn’t actually required. Maybe… Roccat Swarm sends some type of system call, wake up packet, etc to the USB receiver. On this hunch I started digging.
Wireshark magic
I hang out in a Discord with a few ethical hacker type folks and when I brought this problem up, one of them recommended I use Wireshark to capture and dump the USB HID calls made by Roccat Swarm. This sounded like a good starting point. I had no idea Wireshark could even be used for USB devices, but I was willing to try anything. As a bonus, listening in on these calls is a clean-house reverse engineering approach to the problem.
After a bit of research I fired up Wireshark on an old dedicated Windows machine using the USBPcap plugin. I then attached the USB 2.4Ghz receiver to the Windows machine and opened Roccat Swarm. Wouldn’t you know it, it worked like a charm!
Ok… so there is a lot here. I don’t know the first thing about USB HID specifications or how it fits together (or at least, I didn’t when I started). I did notice however a distinct set of calls for “SET_REPORT”, each call with a respective data payload. So now I know what Roccat Swarm is sending to the device on startup, and I have the exact data it sends over. Awesome.
Time to learn a new programming language… yes really
I have never programmed for macOS or iOS before, but I knew I was going to have to start. After a few failed attempts to use libusb with Python (Apple has locked out this approach, that’s a rant for a different time), I decided to look into Objective-C and Swift… but mostly Swift in order to preserve my sanity.
After digging for only a few hours I found an awesome library called USBDeviceSwift that abstracts away a lot of nitty gritty details of USB / HID interactions. I pulled this into my project using Swift’s package manager (wild times we live in) and started learning and implementing. This involved reading the USB dump from Wireshark and translating the required segments for SET_REPORT into Swift model objects in my project.
Success and Thoughts
After a few days of tinkering during my spare time I finally got what I was after. I now have a simple command line tool that invokes a list of SET_REPORT calls to the Roccat Kone Air USB receiver. Running this program successfully activates the on-board profiles and their respective button mappings on macOS.Â
Curiously, doing a single SET_REPORT for any of these inputs is enough to do the job… so I am not sure what that is about. I simply chose to do them all, as that is what the Roccat Swarm software does. Honestly? Totally worth the effort. I’m not exactly sure why this is necessary… and I’m not sure why Roccat engineered the device to require these calls for on-board profile activation, but at least I have it working.
As an aside, I wanted to try implementing this with DriverKit, but Apple wanted me to pay $100/yr for the privilege. I’ll stick with my command line program and a login script.Â