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

Usage Page and Usage on Linux with hidraw #139

Merged
merged 11 commits into from
Nov 23, 2020
Merged

Conversation

haata
Copy link
Contributor

@haata haata commented Jan 22, 2020

Adapted from ApeironTuska's PR to PastaJ36/hidapi (PastaJ36/hidapi#1)
Which was adapted from djpnewton's PR to signal11/hidapi (signal11/hidapi#6)

Also addresses some of the issues mentioned in (signal11/hidapi#6)

  • hid_open_path and hid_enumerate both need to retrieve the usage page
    as the user may call hid_open_path directly without using hid_enumerate
  • Added get_hid_item_size() for hid parsing, used in both uses_numbered_reports() and get_hid_usage()

NOTE: This commit does not handle composite HID descriptors
I am interested in adding support for composite descriptors though I still need to find a
device with a composite descriptor to test it correctly

  • hidtest-hidraw test -

Device Found
type: 308f 0015
path: /dev/hidraw10
serial_number: 53373100323943353230353139363032 - sam4s2
Manufacturer: Kiibohd
Product: Keyboard - None PartialMap USBxUART
Release: 4ac
Interface: 0
Usage (page): 0x6 (0x1)

Device Found
type: 308f 0015
path: /dev/hidraw11
serial_number: 53373100323943353230353139363032 - sam4s2
Manufacturer: Kiibohd
Product: Keyboard - None PartialMap USBxUART
Release: 4ac
Interface: 1
Usage (page): 0x6 (0x1)

Device Found
type: 308f 0015
path: /dev/hidraw12
serial_number: 53373100323943353230353139363032 - sam4s2
Manufacturer: Kiibohd
Product: Keyboard - None PartialMap USBxUART
Release: 4ac
Interface: 2
Usage (page): 0x1 (0xc)

Device Found
type: 308f 0015
path: /dev/hidraw13
serial_number: 53373100323943353230353139363032 - sam4s2
Manufacturer: Kiibohd
Product: Keyboard - None PartialMap USBxUART
Release: 4ac
Interface: 3
Usage (page): 0x2 (0x1)

Device Found
type: 308f 0015
path: /dev/hidraw14
serial_number: 53373100323943353230353139363032 - sam4s2
Manufacturer: Kiibohd
Product: Keyboard - None PartialMap USBxUART
Release: 4ac
Interface: 4
Usage (page): 0x1100 (0xff1c)

@Youw Youw added the hidraw Related to Linux/hidraw backend label Jan 22, 2020
@Youw
Copy link
Member

Youw commented Jan 22, 2020

Thanks you very much for doing this PR.
We're looking forward to accept the changes.

Before I go deep into the changes, a few general comments:

  1. Please remove all white-space changes: they are irrelevant to the change itself and makes harder to look thru history of changes, e.g. it took me some time to figure that only 2 files had been changed, not 4;

  2. get_bytes is extremely unfriendly name for a function, that actually interpret array of bytes as big-endian number;

@haata
Copy link
Contributor Author

haata commented Jan 22, 2020

Sounds good, I'll update the commit shortly.

@haata haata changed the title Usage Page and Usage on Linux with hidraw and whitespace cleanup Usage Page and Usage on Linux with hidraw Jan 22, 2020
@haata
Copy link
Contributor Author

haata commented Jan 22, 2020

Updated the commit.

  • Removed the whitespace changes
  • Changed get_bytes to get_hid_report_bytes and added _hid to get get_hid_item_size, get_hid_usage and get_hid_report_descriptor to make it more consistent. I've left uses_numbered_reports as it's a very hid specific thing (and would require a bunch more renaming to make consistent).

@haata
Copy link
Contributor Author

haata commented Jan 22, 2020

Also, for reference it seems that in order for the Linux kernel to separate composite descriptors into multiple devices as multiple udev nodes a kernel patch (per device) is required to add HID_QUIRK_MULTI_INPUT to https://github.com/torvalds/linux/blob/master/drivers/hid/hid-quirks.c

https://electronics.stackexchange.com/a/400268

Given that there are only a handful of devices that support this (and there doesn't seem to be any additional udev flags to help identify the order in the descriptor), I don't think any additional code for Composite HID descriptors will be useful in Linux without a bunch of kernel work (and any hidraw code will have to know how to deal with older and newer kernels).

Possibly one change that could help would be retrieving a list of all of the usages and usage pages from the descriptor and providing a list in Linux for situations where matching is more important.

@haata
Copy link
Contributor Author

haata commented Jan 23, 2020

I was able to add support for Bluetooth Usage Page and Usage.

Device Found
  type: 093a 2801
  path: /dev/hidraw12
  serial_number: DA:33:7F:D0:17:40
  Manufacturer:
  Product:      Hexgears-BK2
  Release:      0
  Interface:    -1
  Usage (page): 0x6 (0x1)

@haata
Copy link
Contributor Author

haata commented Jan 26, 2020

I believe I have per-usage page/usage pair working with hidraw. The implementation is a bit cleaner.

The device listing should match to what macOS currently does with hidapi.

@z3ntu
Copy link
Collaborator

z3ntu commented Apr 14, 2020

I'm getting a different behavior as root and as non-root:

normal user:

Device Found
  type: 1532 006c
  path: /dev/hidraw11
  serial_number: 
  Manufacturer: Razer
  Product:      Razer Mamba Elite
  Release:      200
  Interface:    0
  Usage (page): 0x0 (0x0)

Device Found
  type: 1532 006c
  path: /dev/hidraw12
  serial_number: 
  Manufacturer: Razer
  Product:      Razer Mamba Elite
  Release:      200
  Interface:    1
  Usage (page): 0x0 (0x0)

Device Found
  type: 1532 006c
  path: /dev/hidraw13
  serial_number: 
  Manufacturer: Razer
  Product:      Razer Mamba Elite
  Release:      200
  Interface:    2
  Usage (page): 0x0 (0x0)

With sudo:

Device Found
  type: 1532 006c
  path: /dev/hidraw11
  serial_number: 
  Manufacturer: Razer
  Product:      Razer Mamba Elite
  Release:      200
  Interface:    0
  Usage (page): 0x2 (0x1)

Device Found
  type: 1532 006c
  path: /dev/hidraw11
  serial_number: 
  Manufacturer: Razer
  Product:      Razer Mamba Elite
  Release:      200
  Interface:    0
  Usage (page): 0x1 (0x1)

Device Found
  type: 1532 006c
  path: /dev/hidraw12
  serial_number: 
  Manufacturer: Razer
  Product:      Razer Mamba Elite
  Release:      200
  Interface:    1
  Usage (page): 0x6 (0x1)

Device Found
  type: 1532 006c
  path: /dev/hidraw12
  serial_number: 
  Manufacturer: Razer
  Product:      Razer Mamba Elite
  Release:      200
  Interface:    1
  Usage (page): 0x1 (0xc)

Device Found
  type: 1532 006c
  path: /dev/hidraw12
  serial_number: 
  Manufacturer: Razer
  Product:      Razer Mamba Elite
  Release:      200
  Interface:    1
  Usage (page): 0x80 (0x1)

Device Found
  type: 1532 006c
  path: /dev/hidraw12
  serial_number: 
  Manufacturer: Razer
  Product:      Razer Mamba Elite
  Release:      200
  Interface:    1
  Usage (page): 0x0 (0x1)

Device Found
  type: 1532 006c
  path: /dev/hidraw12
  serial_number: 
  Manufacturer: Razer
  Product:      Razer Mamba Elite
  Release:      200
  Interface:    1
  Usage (page): 0x0 (0x1)

Device Found
  type: 1532 006c
  path: /dev/hidraw13
  serial_number: 
  Manufacturer: Razer
  Product:      Razer Mamba Elite
  Release:      200
  Interface:    2
  Usage (page): 0x6 (0x1)

The second to last and third to last entries seem to be identical though - at least from the information printed by hidtest. With one of those removed the output seems to match the macOS version.

@haata
Copy link
Contributor Author

haata commented Apr 14, 2020

You must have device permissions in order to read from hidraw. Either by setting up udev or using root.

Because you can't read the hid descriptors without permissions, it's not possible to scan all of the different composite descriptors and will just return 0s in the Usage Page.

@haata
Copy link
Contributor Author

haata commented Apr 15, 2020

@z3ntu Can you include the full hid descriptor here? I can try to debug the issue.

@z3ntu
Copy link
Collaborator

z3ntu commented Apr 15, 2020

@haata Can you give me a command to get this descriptor please? :)

@haata
Copy link
Contributor Author

haata commented Apr 15, 2020

I've used this utility in the past, not sure if it's available on your distro.
https://github.com/DIGImend/usbhid-dump

The last example will convert the hid descriptor to human readable format.

@z3ntu
Copy link
Collaborator

z3ntu commented Apr 15, 2020

Is this good?

$ sudo ./src/usbhid-dump --model=1532:006C
001:028:002:DESCRIPTOR         1586977867.981403
 05 01 09 06 A1 01 05 07 19 E0 29 E7 15 00 25 01
 75 01 95 08 81 02 81 01 19 00 2A FF 00 15 00 26
 FF 00 75 08 95 06 81 00 05 08 19 01 29 03 15 00
 25 01 75 01 95 03 91 02 95 05 91 01 C0

001:028:001:DESCRIPTOR         1586977867.982019
 05 01 09 06 A1 01 85 01 05 07 19 E0 29 E7 15 00
 25 01 75 01 95 08 81 02 19 00 2A FF 00 15 00 26
 FF 00 75 08 95 0E 81 00 C0 05 0C 09 01 A1 01 85
 02 19 00 2A 3C 02 15 00 26 3C 02 95 01 75 10 81
 00 75 08 95 0D 81 01 C0 05 01 09 80 A1 01 85 03
 19 81 29 83 15 00 25 01 75 01 95 03 81 02 95 05
 81 01 75 08 95 0E 81 01 C0 05 01 09 00 A1 01 85
 04 09 03 15 00 26 FF 00 35 00 46 FF 00 75 08 95
 0F 81 00 C0 05 01 09 00 A1 01 85 05 09 03 15 00
 26 FF 00 35 00 46 FF 00 75 08 95 0F 81 00 C0

001:028:000:DESCRIPTOR         1586977867.982856
 05 01 09 02 A1 01 09 01 A1 00 05 09 19 01 29 05
 15 00 25 01 75 01 95 05 81 02 75 01 95 03 81 03
 06 00 FF 09 40 75 08 95 02 15 81 25 7F 81 02 05
 01 09 38 15 81 25 7F 75 08 95 01 81 06 09 30 09
 31 16 00 80 26 FF 7F 75 10 95 02 81 06 C0 06 00
 FF 09 02 15 00 25 01 75 08 95 5A B1 01 C0

Copy link

@FFY00 FFY00 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems a bit silly. Why are we duplicating the code to parse the report descriptor in every backend? There is nothing platform specific about it.

Also, is creating a new device per usage part of the API?
Don't get me wrong, but this seems awful, IMO it is a really bad way to handle the issue. Why can't we just provide an array with the usages?

A quick test on my desktop:

$ sudo hidtest/hidtest-hidraw | grep 'Device Found' | wc -l
59

yikes...

Not to mention this is a bad API for users, let's say I want to read the raw HID events from mice.

I have to look the list of devices and search for the devices with all the usages I want with the same path.

$ sudo hidtest/hidtest-hidraw | grep 4079 -A 5 -B 1
Device Found
  type: 046d 4079
  path: /dev/hidraw4
  serial_number: 4079-df-7a-84-07
  Manufacturer: Logitech
  Product:      USB Receiver
  Release:      4000
  Interface:    2
  Usage (page): 0x6 (0x1)
--
Device Found
  type: 046d 4079
  path: /dev/hidraw4
  serial_number: 4079-df-7a-84-07
  Manufacturer: Logitech
  Product:      USB Receiver
  Release:      4000
  Interface:    2
  Usage (page): 0x2 (0x1)
--
Device Found
  type: 046d 4079
  path: /dev/hidraw4
  serial_number: 4079-df-7a-84-07
  Manufacturer: Logitech
  Product:      USB Receiver
  Release:      4000
  Interface:    2
  Usage (page): 0x1 (0x1)
--
Device Found
  type: 046d 4079
  path: /dev/hidraw4
  serial_number: 4079-df-7a-84-07
  Manufacturer: Logitech
  Product:      USB Receiver
  Release:      4000
  Interface:    2
  Usage (page): 0x1 (0xff00)
--
Device Found
  type: 046d 4079
  path: /dev/hidraw4
  serial_number: 4079-df-7a-84-07
  Manufacturer: Logitech
  Product:      USB Receiver
  Release:      4000
  Interface:    2
  Usage (page): 0x2 (0xff00)
--
Device Found
  type: 046d 4079
  path: /dev/hidraw4
  serial_number: 4079-df-7a-84-07
  Manufacturer: Logitech
  Product:      USB Receiver
  Release:      4000
  Interface:    2
  Usage (page): 0x4 (0xff00)

@Youw
Copy link
Member

Youw commented Apr 25, 2020

Also, is creating a new device per usage part of the API?

This is the default and only behavior on Windows. Best approach to make the API consistent - implement the wider behavior on all platforms

Why can't we just provide an array with the usages?

  1. This is a big API change. HIDAPI has a very stable API and many applications that rely on it. Developer can't just tell to all library users, that they need to change their implementation

  2. On Window each usage is reported as a separate device

@FFY00
Copy link

FFY00 commented Apr 25, 2020

Also, is creating a new device per usage part of the API?

This is the default and only behavior on Windows. Best approach to make the API consistent - implement the wider behavior on all platforms

Why can't we just provide an array with the usages?

1. This is a big API change. HIDAPI has a very stable API and many applications that rely on it. Developer can't just tell to all library users, that they need to change their implementation

2. On Window each usage is reported as a separate device

Well, that's unfortunate. If you can tie the those HID devices in the Windows API down to a physical device then you can wrap around the behavior. This could be introduced in our API without breaking changes, just ABI changes.

@FFY00
Copy link

FFY00 commented Apr 25, 2020

Looks like it can be done with GetPhysicalDescriptor, so I don't see any blocker. Let me know if I am wrong.

@haata
Copy link
Contributor Author

haata commented Apr 25, 2020

I'm not sure there's a way to get the full HID descriptor on macOS (though I would be happy to be proven wrong).

@Youw
Copy link
Member

Youw commented Apr 25, 2020

Looks like it can be done with GetPhysicalDescriptor

Have you tried it? Does it work in all cases for all possible users of this library in production?

could be introduced in our API without breaking changes

Replacing a property field with an array of values - is a very definition of breaking change

just ABI changes

That's a breaking change too

@Youw
Copy link
Member

Youw commented Apr 25, 2020

This seems a bit silly. Why are we duplicating the code to parse the report descriptor in every backend?

When you're the only developer and the user of a project, it may sound like something that can be refactored, improved, to make your self feel better.

When hundreds of users rely on the behavior they got used to in years - introducing a new source file with some common implementation is a big deal. In ideal world, agree that it would be much better that way. In practice - I personally know a few users, who rely of the fact, that this library consist of a single hid.c source file.

Secondly, this project is maintained by an open-source community purely on their enthusiasm.
If you wan't to help - PRs are welcome (no breaking changes!).

let's say I want to read the raw HID events from mice

Good luck with that. macOS and Windows already prohibit that as a security concern (because you shouldn't be allowed to sniff some passwords, user may want to type in). My opinion - it is a question of "when" not "if" Linux would prohibit it too.

@haata
Copy link
Contributor Author

haata commented Nov 18, 2020

Is there anything pending for this PR that I can help with?

Copy link
Member

@Youw Youw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is active pending actions

linux/hid.c Outdated Show resolved Hide resolved
linux/hid.c Outdated Show resolved Hide resolved
@Youw
Copy link
Member

Youw commented Nov 18, 2020

I'm getting a different behavior as root and as non-root:

@z3ntu
what about after ab2516f ?

@haata
Copy link
Contributor Author

haata commented Nov 18, 2020

I'm getting a different behavior as root and as non-root:

@z3ntu
what about after ab2516f ?

I did a quick test removing my udev rules and it didn't work. We have to read directly from the hidraw interface /dev/hidraw* so unless udev has given some read access the default looks like this (at least on Arch Linux)

crw-------  1 root root 237, 2 Nov 18 13:44 /dev/hidraw2
crw-------  1 root root 237, 3 Nov 18 13:44 /dev/hidraw3
crw-------  1 root root 237, 4 Nov 18 13:44 /dev/hidraw4

@Youw
Copy link
Member

Youw commented Nov 18, 2020

in such case I'd rather suggest using a whole different approach:
instead of sending ioctl, read the descriptor from sysfs, e.g.: /sys/class/hidraw/hidraw0/device/report_descriptor

Minimum supported kernel 2.6.39 has the implementation for this sysfs file, so there will be no backward compatibility issues.

Some thoughts out loud:
In such way, we could also enumerate usage/usage_page for libusb backend on linux.

@haata
Copy link
Contributor Author

haata commented Nov 18, 2020

Yeah, I think this might work well.
Let me put something together for hidraw.

libusb is possible if it's just for Linux (might take some work to map libusb to hidraw; hidraw doesn't make it easy to go in reverse, I've tried)
Something else might have to be done for freebsd (I haven't used BSD that much). But if the hid descriptors are exposed the parsing code would be the same.

- Elevated permissions are no longer required to query usage and usage
  pages
- Some additional work is required when opening a /dev/hidraw device
  in order to locate the sysfs path
  * Needed to determine if we're using numbered reports or not
@haata
Copy link
Contributor Author

haata commented Nov 19, 2020

A little bit more string manipulation than I would have liked, but I think it works well.

The only part I'm a bit concerned about is during the open where I have to locate the report_descriptor. I'm using the basename of the filepath as the udev sysname to do the sysfs path lookup.
This will break if a symlink is used, so I may also need to resolve any symlinks.
I am open to a better solution though.

Also, elevated permissions no longer required 😄

- In most cases enumeration will be used to get the absolute path to
  /dev/hidraw*
  However it is possible to use a symlink to open the hidraw interface
@z3ntu
Copy link
Collaborator

z3ntu commented Nov 19, 2020

hidtest output is now idential as root and non-root with my current devices (and it wasn't on commit ab2516f or before)

Copy link
Member

@Youw Youw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't realize we had a sysfs_path already available from udev, during enumeration.
This makes the implementation even simpler. I do like how it looks.
But still, have a few comments below.

linux/hid.c Outdated Show resolved Hide resolved
linux/hid.c Outdated Show resolved Hide resolved
linux/hid.c Outdated Show resolved Hide resolved
linux/hid.c Outdated Show resolved Hide resolved
linux/hid.c Outdated Show resolved Hide resolved
linux/hid.c Outdated Show resolved Hide resolved
- Code review fixes
- Simplify get_next_hid_usage loops
linux/hid.c Outdated Show resolved Hide resolved
linux/hid.c Outdated Show resolved Hide resolved
Co-authored-by: Ihor Dutchak <ihor.youw@gmail.com>
linux/hid.c Outdated Show resolved Hide resolved
Copy link
Member

@Youw Youw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nicely done!

I'd like to check it myself with some of my devices (over the weekend).

@haata
Copy link
Contributor Author

haata commented Nov 19, 2020

I'd like to check it myself with some of my devices (over the weekend).

I'll do some additional checking myself too. Bluetooth devices tend to have more corner cases as they tend to favour multiple usage/usage page pairs instead of multiple devices.

Should I squash all of the commits together?

@Youw
Copy link
Member

Youw commented Nov 19, 2020

Not necessary, I'd do a squash-merge anyway.

@Youw Youw merged commit 48cacfa into libusb:master Nov 23, 2020
@haata haata deleted the usage_linux branch November 23, 2020 19:20
Youw pushed a commit that referenced this pull request Nov 23, 2020
NOTE: This commit does not handle composite HID descriptors
I am interested in adding support for composite descriptors though I still need to find a
device with a composite descriptor to test it correctly.

The implementation idea is similar, as in #125 for macOS.
break;
}

/* Skip over this key and it's associated data */
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's "its"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
hidraw Related to Linux/hidraw backend
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants