unbit / foohid

OSX IOKit driver for implementing virtual HID devices (joypads, keyboards, mices, ...) from userspace
MIT License
265 stars 36 forks source link

Implement setReport (send report to HID device) #7

Closed btoews closed 7 years ago

btoews commented 7 years ago

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 7 years ago

@aldur i think the cleanest solution is 2 ?

aldur commented 7 years ago

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

btoews commented 7 years ago

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?

btoews commented 7 years ago

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 7 years ago

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.

btoews commented 7 years ago

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.