Closed Bug 1198330 Opened 9 years ago Closed 8 years ago

Integrate hidapi library for USB/Bluetooth HID support

Categories

(Core :: DOM: Device Interfaces, enhancement)

enhancement
Not set
normal

Tracking

()

RESOLVED DUPLICATE of bug 1298838

People

(Reporter: grobinson, Unassigned)

References

Details

Attachments

(1 file, 1 obsolete file)

As explained in bug 1065729, comment 62, the primary blocker for U2F support in Firefox is the lack of USB HID API to allow communication between hardware and any potential U2F implementation.

Yubico's libu2f-host library (https://github.com/Yubico/libu2f-host) uses hidapi (https://github.com/signal11/hidapi) to communicate with USB/Bluetooth HID devices. It is cross-platform (Windows, Mac, Linux, FreeBSD) and licensed under new BSD (and GPLv3) so it is compatible with the Mozilla's licensing requirements for 3rd party code (https://www.mozilla.org/MPL/license-policy.html#Licensing_of_Third_Party_Code).

hidapi may optionally use libusb, which is licensed under LGPL 2.1 (https://github.com/libusb/libusb/wiki/FAQ#Licensing). libusb is needed for FreeBSD support and may also be used (but is not required) for Linux support (https://github.com/signal11/hidapi/blob/master/README.txt). According to the Mozilla licensing requirements, this library may be used but would need to be dynamically linked.

This bug should focus on integrating hidapi into mozilla-central so it may be used by other components, such as privileged JS that would use it to implement U2F. I propose that this bug focus on desktop Firefox on Windows, Mac, and Linux, since that is the primary use case for U2F at this time. Follow-up bugs should be used for issues including: libusb integration, mobile support (Firefox for Android, B2G).
As of Bug 1157768, we now have a mechanism to use LGPL code in firefox and have it link dynamically. Look at the media/libav or media/libsoundtouch directories for an example, though whoever implements this will also need to touch config/external/lgpllibs to add the build directories in, and of course add the license to about:licenses. 

Should be pretty straight-forward other than that though, feel free to ni? me if whoever gets assigned this has any questions.
This is a first try at integrating the Signal11 HIDAPI library into the Mozilla codebase. It adds support for communicating with HID devices on Windows, Mac, and Linux.

hidapi is licensed under several licenses. One of them is new-style BSD, so we can statically link the library according to the Mozilla licensing requirements.

To keep things simple on Linux, I used the implementation based on hidraw instead of the one based on libusb, since that would've required more to incorporate libusb as an additional dependency, and there are also some licensing complications since libusb is only licensed under the GPL.

I added the necessary source files and headers to a new hidapi directory in modules/, because that seemed to be a popular location for third-party code in the tree. I added a moz.build and included the new directory in config/external/moz.build, again following the patterns I observed for including other 3rd-party libraries such as zlib, brotli, etc.

I have only tried building this on Mac OS X so far, but the build is successful and I can tell that the hidapi code for Mac is compiling because I see a few compiler warnings emitted for mac/hidapi.c.
For macos version you probably should look at some patches from this fork https://github.com/mrpippy/hidapi/commits/iokit_path, upstream version has problem with handling composite devices, and for some consumers this is important (like some u2f devices).
BTW I'm happy to review when you're looking for that.
BTW, saw the instructions in the README. Having a script similar to /media/libsoundtouch/update.sh can be nice, but isn't required. Just makes a repeatable way to checkout new files and throw them in the tree the way we expect.
Status: UNCONFIRMED → NEW
Ever confirmed: true
Component: Untriaged → DOM: Device Interfaces
My only concern with this patch has always been that the Mac implementation includes some of its own concurrency (via pthreads), and I wasn't sure if that could lead to problems when integrating with Mozilla (itself a highly complex concurrent application).

I spent some more time investigating this situation. The Mac implementation uses pthreads because the native HID API on Mac (HID Manager: https://developer.apple.com/library/mac/technotes/tn2187/_index.html) uses callbacks for reports, so HIDAPI needs to buffer incoming reports. It does this with a per-device read thread, which receives events from a thread-specific CFRunLoop and adds new input reports to a linked list called input_reports. pthread locking is used to avoid corrupting input_reports while it is being updated, as well as to synchronize the creation and destruction of a hid_device with its read thread.

The Windows and Linux implementations, on the other hand, present HID devices as file handles/descriptors, so the OS buffers the device input. Since they do not need to handle buffering input reports, there is no need for additional concurrency/synchronization.

This does not seem like it should cause any problems to me, although I would appreciate it if someone more experienced could take a look as well.
One thing to note for the U2F use case is that we need to be able to query a device's usage page to determine if it is a U2F device (see u2fh_devs_discover in https://github.com/Yubico/libu2f-host/blob/master/u2f-host/devs.c). Unfortunately, Signal11's HIDAPI library only supports usage pages on Windows/Mac, and not on Linux (see the comment in https://github.com/signal11/hidapi/blob/master/hidapi/hidapi.h).

Yuibco's libu2f-host works around this by providing their own implementation for Linux (get_usages in https://github.com/Yubico/libu2f-host/blob/master/u2f-host/devs.c), and using the HIDAPI implementation for other platforms. We should probably just do the same thing for Mozilla, but I think it can wait until we actually begin implementing the interfaces to support U2F.
Drive-by comment, because I don't want to forget this link --

When it comes to doing continuous integration testing for USB devices, it may be possible on Linux and Windows to use USB/IP and something akin to this Python script that emulates a USB device: https://github.com/smulikHakipod/USB-Emulation .
I'm currently working on implementing nsIHidService and nsIHidDeviceInfo, so I'm just starting to use hidapi with other Mozilla code. The good news is that it works (I can currently enumerate attached HID devices from Chrome JS in Firefox). However, in the process I have encountered some issues with the library that I am still sorting through. They are enumerated below:

# hidapi uses wchar_t in hid_device_info

Several fields in the hid_device_info struct (serial_number, manufacturer_string, product_string) are of type wchar_t* : https://github.com/signal11/hidapi/blob/d17db57b9d4354752e0af42f5f33007a42ef2906/hidapi/hidapi.h#L49.

According to several conversations I've had on #developers, this creates a world of hurt. wchar_t is wildly inconsistent and poorly defined cross-platform. Things I have learned:

* wchar_t is 2 bytes on Windows, encoding is not defined (but I believe it is implicitly UTF-16 since that is the default on Windows).
* wchar_t is 4 bytes on Mac, encoding is not defined (but HIDAPI always gets UTF32LE: https://github.com/signal11/hidapi/blob/d17db57b9d4354752e0af42f5f33007a42ef2906/mac/hid.c#L246)
* wchar_t is 4 bytes on Linux, encoding is not defined and depends on the user's locale (!!!). HIDAPI appears to assume it will be utf-8 but as tromey pointed out on IRC, this is not a valid assumption.

Fortunately, we do not really _need_ the wchar_t* fields from hid_device_info. They mostly provide metadata that is useful to developers. They are not needed to enumerate devices, connect to them, or communicate with them. Additionally, there is a single obvious hook point to deal with this issue: where nsIHidDeviceInfo is constructed based on hid_device_info.

Possible solutions include:

1. Write platform-specific code to convert hidapi's wchar_t* to Mozilla strings in the nsIHidDeviceInfo constructor. I am working on this now. The current best options seem to be:
    * On Windows, just pass it to nsString (froydnj said on IRC that he thinks this will work).
    * On Mac, convert via iconv from UTF32LE to UTF8, then construct nsString from UTF8.
    * On Linux, convert via iconv from wchar_t (since we don't know what the encoding was) to UTF8, then construct nsString.
2. Don't implement access to a HID device's serialNumber, manufacturerString, or productString in nsIHidDeviceInfo. They are not necessary to use HID devices, although having such a limited API would probably make development and debugging more annoying.
3. Scrap HIDAPI and implement this functionality from scratch.

Comments welcome!

# hidapi calls setlocale on Linux

https://github.com/signal11/hidapi/blob/d17db57b9d4354752e0af42f5f33007a42ef2906/linux/hid.c#L365

As tromey pointed out on IRC, it is generally bad practice for a library to call setlocale. I am concerned that this might interfere with the rest of Mozilla's locale-related code. At the moment, it looks ok because all it achieves is setting the native environment locale ("") if one is not already set (checked by calling setlocale with NULL).

I'll keep an eye on this when I begin developing for Linux.

# hidapi can't retrieve the serial number of a device on Mac OS X

I just noticed this during testing on my Mac. hid_device->serial_number is always L"". I ran it in the debugger and this looks like an issue with the native API, which returns NULL when getting the SerialNumber property. It does not appear to be the fault of anything in hidapi. Nonetheless, this is annoying - the whole goal of using hidapi was to get a consistent cross-platform interface without having to implement it from scratch ourselves. I don't yet know if this issue will happen on other platforms as well, or if it is Mac-only.

Filed an issue upstream: https://github.com/signal11/hidapi/issues/256#issuecomment-179364611
Filed a dedicated issue for the lack of a serial number on Mac upstream: https://github.com/signal11/hidapi/issues/257 (previously it was reported as a comment on a different issue that has since been determined to be invalid).
Closed in preference for the approach in Bug 1298838, implementing HID API without the hidapi library.
Status: NEW → RESOLVED
Closed: 8 years ago
Resolution: --- → WONTFIX
Wouldn't it be better to resolve as duplicate of the preferred solution? I came here because of a depending bug. I'll do that. Feel free to undo if you feel it's inappropriate!
Resolution: WONTFIX → DUPLICATE
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: