unbit/foohid

Implement setReport (send report to HID device)

btoews opened this issue · 6 comments

I'm wanting to use foohid to implement a software U2F token (U2FHID specification). In order to do this, I'd need to be able to communicate in both directions though (HID device <-> some user-land app).

I know foohid currently only implements APIs for sending data from a virtual HID device, but I'm wondering how feasible it would be to add something to the device-interface for sending data back to the virtual HID device. It seems like one could:

  1. Override registerNotificationPort on it_unbit_foohid_userclient so virtual-HID code can register a port to receive notifications on using IOConnectSetNotificationPort .
  2. Override some method on it_unbit_foohid_device that handles receiving reports (setReport maybe?)
  3. Use mach_msg_send_from_kernel to send reports to the port registered by the virtual-HID code when the device receives a report.

Does that sound vaguely correct/possible? I'm a total kernel/driver noob here, so any advice would be appreciated.

unbit commented

@aldur i think the cleanest solution is 2 ?

aldur commented

Yes, overriding setReport(IOMemoryDescriptor *, IOHIDReportType, IOOptionBits) seems like a good idea :)

I've got this project building/loading locally. I tried overriding setReport like so

// foohid_device.h
class it_unbit_foohid_device : public IOHIDDevice {
  ...
  virtual IOReturn setReport(IOMemoryDescriptor *report, IOHIDReportType reportType, IOOptionBits options = 0) override;
}
// foohid_device.cpp
IOReturn it_unbit_foohid_device::setReport(IOMemoryDescriptor *report, IOHIDReportType reportType, IOOptionBits options) {
    LogD("setReport called");
    return super::setReport(report, reportType, options);
}

Using examples/keyboard.c, I was hoping that setReport would get called when I toggle caps-lock on my physical keyboard. I'm seeing the other LogD messages, but it doesn't look like setReport is getting called. The report-descriptor in keyboard.c includes the LEDs usage page, which looks to be correct. Looking at some other OSX driver examples, I do think setReport is the correct method to be overriding.

Any idea why this isn't working?

I'm still not sure why setReport isn't working with setting LEDs on the virtual keyboard, but it does work for my virtual U2F device! I copied the report descriptor from my physical U2F device and then had my browser probe for U2F devices. My LogD("setReport called"); got called! 😀

aldur commented

I'm not sure this is the case, but I remember that sometimes macOS stands in the way with HID physical keyboards. This could explain why it is called with the U2F device. On the other side, great work! :) Let us know if you need any help.

I ended up writing my own driver for this (https://github.com/mastahyeti/SoftU2F) based on foohid, mostly because it was easier during development. Now that I figured out the setReport stuff though, I'd like to contribute those changes back to foohid instead of maintaining my own driver. I'm going to open up a few pull requests.