Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Native BSD backend using uhid #608

Open
ghost opened this issue Aug 12, 2023 · 23 comments
Open

Native BSD backend using uhid #608

ghost opened this issue Aug 12, 2023 · 23 comments
Labels
bsd FreeBSD, NetBSD, OpenBSD, etc enhancement New feature or request

Comments

@ghost
Copy link

ghost commented Aug 12, 2023

I was curious about the pragmatics of supporting various BSDs. There seems to be a fairly common API for HID over USB on most common BSDs called usbhid which is intended to be used with the uhid kernel driver for generic HID devices. Support for this is likely a necessity because several BSD ports of libusb cannot be used with USB devices already reserved by a kernel driver.

I'm too unfamiliar with HID myself to work on this at present, but is there anyone else willing to put in the effort to add this as a hidapi backend?

@Youw
Copy link
Member

Youw commented Aug 12, 2023

Duplicate of #543 ?

@ghost
Copy link
Author

ghost commented Aug 12, 2023

Duplicate of #543 ?

I don't believe so. There's more BSDs than just FreeBSD. And #543 is about the hidraw compatibility layer that is only on FreeBSD. This is about the HID layer that exists on multiple BSDs, though they have diverged a bit over the years. It may be feasible to manage that with some clever use of the preprocessor.

@Youw Youw added enhancement New feature or request bsd FreeBSD, NetBSD, OpenBSD, etc labels Aug 12, 2023
@Youw Youw changed the title BSD Support Native BSD backend Aug 12, 2023
@Youw
Copy link
Member

Youw commented Aug 12, 2023

For those who potentially want to start implementing this but don't know where to start (from HIDAPI POV) - here is an example me adding an experimental WinRT backend: https://github.com/libusb/hidapi/compare/winrt

@ghost
Copy link
Author

ghost commented Aug 13, 2023

I don't believe a native port for DragonFly is feasible unless they fix their UHID ioctl situation. It doesn't appear to support getting any information about the device behind the UHID device such as the USB device information. The other 3 still appear to be viable.

@mcuee mcuee changed the title Native BSD backend Native BSD backend using uhid Aug 13, 2023
@mcuee
Copy link
Member

mcuee commented Aug 13, 2023

I am not so sure if there is any interest to have the uhid backend for FreeBSD as the existing libusb backedn works fine (libusb is part of FreeBSD now, and it is using FreeBSD's own libusb implementation with BSD license). So you can arguably call it native for FreeBD as well. hidraw backend seems to be more interesting for FreeBSD.

But I agree that it will be good to have uhid backend for NetBSD and OpenBSD, if it is possible. HIDAPI libusb backend for NetBSD/OpenBSD require rebuilding the kernel last time I checked, so it is not really that useful.

@ghost
Copy link
Author

ghost commented Aug 13, 2023

Further research reveals a problem with the current hidapi public API. There's a symbol conflict between hidapi and usbhid. The function hid_init is used in both. At the very least this makes it impossible to combine hidapi and usbhid in the same program. I can see two options. Just don't use usbhid and hope the developer doesn't either or rename hid_init in hidapi which would break the library ABI. usbhid mainly exists as an optional BSD library to handle a subset of what hidapi does but I was wanting to use its report parsing functionality.

@Youw
Copy link
Member

Youw commented Aug 13, 2023

Instead of linking against uhid usbhid HIDAPI could load it dynamically - that way we avoid symbol conflict.
Also, for HIDAPI v1 I think hid_init will be renamed to hidapi_initialize (and it will become context-specific initialisation, similar to what libusb uses).

@ghost
Copy link
Author

ghost commented Aug 13, 2023

Actually uhid API is entirely contained in headers since it all goes through ioctl. What I was referring to it the optional high level library usbhid. I think it will be a non-issue since I can just avoid using it.

@ghost
Copy link
Author

ghost commented Aug 14, 2023

I have done a fair amount of work on a NetBSD port via uhid and have discovered uhid appears to be intended to be used with usbhid at the same time. The uhid API appears to assign a single device with multiple report IDs to separate uhid devices, and you are expected to get the report ID and descriptor from the uhid kernel APIs and to then parse them with usbhid or another HID parser to get the missing information for how to properly use the device.

Is there even an API in hidapi to handle this? It seems to assume you can just pass any arbitrary report ID through the API and it will just handle it but uhid already decides which one you are assigned and expects you to structure your reports in accordance to the exposed information. In the case of a device with multiple report IDs being split across multiple devices, how should I approach this?

@Youw
Copy link
Member

Youw commented Aug 15, 2023

What I was referring to it the optional high level library usbhid

Excuse me for confusion, I really meant usbhid.

Is there even an API in hidapi to handle this?
In the case of a device with multiple report IDs being split across multiple devices, how should I approach this?

This is an interesting problem.
As per current HIDAPI design, each hid_device is only as unique as VID/PID/SerialNumber/UsagePage/Usage combination (and it is not even always true for the UsagePage/Usage part - but that's a different story).

HIDAPI doesn't support "device-per-report" abstraction as it assumes that a single device may support multiple reports (that's why a report ID is explicitly passed as part of data payload).

To support the uhid abstraction, the HIDAPI implementation would have to do it under the hood:

  • parse the report descriptor;
  • find a list of reports supported by the device;
  • first time a report is tried to read/write - open a corresponding per-report uhid device and use it to send/receive data.

I don't see any better way.

@ghost
Copy link
Author

ghost commented Aug 15, 2023

I am considering an approach that opens all the uhid devices and uses their USB device information to construct a proper abstraction. We could use an internal per device lookup table to quickly see if there's a corresponding device for the requested report ID to know where to forward the IO requests to.

@Youw
Copy link
Member

Youw commented Aug 15, 2023

I believe opening all devices at once would be overkill, if even possible (what about exclusive/shared access, read/write permissions, etc.?). Lookup table - yes, plus lazy device opening, only if/when needed.

@ghost
Copy link
Author

ghost commented Aug 15, 2023

I believe opening all devices at once would be overkill, if even possible (what about exclusive/shared access, read/write permissions, etc.?). Lookup table - yes, plus lazy device opening, only if/when needed.

Well, we have to do that anyway to properly enumerate devices, don't we? I only intended this to be done during an initial search for all the uhid devices connected to the same HID device or during enumeration.

@ghost
Copy link
Author

ghost commented Aug 15, 2023

Ok. I am beginning to think a uhid implementation may be untenable due to serious limitations. Devices are opened exclusively. So how does one even do enumeration when another program might be using them? And I know of no other way to interact with the HID layer. I think I am done exploring this. I can see now why hidraw on FreeBSD is considered a big deal.

@Youw
Copy link
Member

Youw commented Aug 16, 2023

Nevertheless thanks for great investigation.
No one has check it this deep in context of HIDAPI.

I'll keep this ticket open for now. Maybe there will be more findings/details.

@ghost
Copy link
Author

ghost commented Aug 16, 2023

Don't fret. I at least did learn that uhid is going to need a lot of help from other platform specific APIs to integrate well with hidapi. After looking at usbdevs source code on NetBSD I discovered you can use drvctl API combined with usb API to enumerate the uhid parent device, uhidev. This avoids the major problem with the exclusive lock enforced by the kernel and you only have to open an uhid device to get the report descriptor now, which is just needed for the the usage fields. This can be left out if the device is already in use.

@ghost
Copy link
Author

ghost commented Aug 17, 2023

That should do it for the majority of the work. I thought I might as well put my research into actual code.

@ghost
Copy link
Author

ghost commented Aug 18, 2023

I also looked into making a native one for OpenBSD but despite having similar APIs to NetBSD there is no apparent way to connect the uhid parent device to the uhid devices it is exposing so I cannot see myself being able to write a port that would be comparable to the level that the NetBSD one is at.

Without the ability to explore the device relationships like this I cannot see a way to workaround the uhid limitations and design differences like I did for NetBSD. You need something akin to NetBSD's drvctl interface which allows you to walk the device tree. I used this extensively in the port to avoid needlessly opening uhid devices and also to connect the parent device uhidev to its uhid children.

All that said I believe a port to FreeBSD is still possible due to how advanced its sysctl interface is. It should be possible to explore the device tree sufficiently to get the needed information for a hidraw or uhid interface. In this situation it might be a good idea to share code between the two since they are operating on the same platform just different native APIs.

@ghost
Copy link
Author

ghost commented Aug 27, 2023

@Youw Does the libusb backend on FreeBSD have to completely disable the HID kernel driver to work? I was considering that in some niche situations it may be good to have the option to use the native kernel interface since devices in some cases will expose multiple HID interfaces.

@mcuee
Copy link
Member

mcuee commented Aug 27, 2023

@Youw Does the libusb backend on FreeBSD have to completely disable the HID kernel driver to work? I was considering that in some niche situations it may be good to have the option to use the native kernel interface since devices in some cases will expose multiple HID interfaces.

No. That is the beauty of FreeBSD USB stack. It exposes ugen driver along with uhid for generic USB HID devices, so that you can use libusb backend out of box.

Unfortunately Hans Petter Selasky passed away recently. If not I would ask him to explain how it works.

@ghost
Copy link
Author

ghost commented Aug 27, 2023

No. That is the beauty of FreeBSD USB stack. It exposes ugen driver along with uhid for generic USB HID devices, so that you can use libusb backend out of box.

Unfortunately Hans Petter Selasky passed away recently. If not I would ask him to explain how it works.

I see. How unfortunate. I think I will just work on OpenBSD backend and consider uhid work complete. I have an idea for how to make it work. It won't be ideal but it is still an improvement due to how limited libusb on OpenBSD is. Notable limitations would be that it would not be possible to enumerate uhid devices that are already in use.

@mcuee
Copy link
Member

mcuee commented Aug 27, 2023

I see. How unfortunate. I think I will just work on OpenBSD backend and consider uhid work complete. I have an idea for how to make it work. It won't be ideal but it is still an improvement due to how limited libusb on OpenBSD is. Notable limitations would be that it would not be possible to enumerate uhid devices that are already in use.

Having a uhid backend for OpenBSD will be great. Thanks. As you mentioned, it will still be much better than libusb backend.

In fact I consider HIDAPI libusb backend not so useful (in fact kind of useless) for NetBSD/OpenBSD users.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bsd FreeBSD, NetBSD, OpenBSD, etc enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants