mckset / KD100

A Linux driver for the Huion KD100 Mini Keydial written in C
GNU General Public License v3.0
15 stars 4 forks source link

Move to udev-hid-bpf? #11

Open whot opened 1 month ago

whot commented 1 month ago

Hi, I just found this while looking for the vid/pid of the KD100 while I'm working on https://github.com/linuxwacom/libwacom/pull/779 and gnome-control-center#3216

You may not be aware of it but we're currently aiming to replace the hid-uclogic kernel driver for currently unsupported devices with BPF programs via udev-hid-bpf. The idea is that we'll then be able to just load the BPF and the device works out of the box - just like a true kernel driver.

I don't have access to a KD100 but if you're have the time we'd very much appreciate it if you can have a look at udev-hid-bpf, in particular the H610 BPF which might actually get you 90% of the way anyway (huion tends to share a lot of firmware bits so there's a good chance you can copy that one and re-use it).

Note you'll probably need huion-switcher too since this devices seems to have a shared PID.

Happy to answer any questions.

mckset commented 1 month ago

@whot You are correct that I was unaware of this but by the sounds of it, it will be a proper replacement. I don't use gnome so I'm testing this all in an Ubuntu VM. So far, Ubuntu selected the H750P tablet as a driver for my device without your program and I'm guessing your program supposed to override that choice and give it a proper driver.

I've never worked with BPF programs before so you'll have to bear with me if what I am doing is wrong, but after compiling with meson by following the instructions in the readme, I do not see an option for the H610 BPF. I noticed it's in the "userhack" folder which I'm guessing is supposed to be ignored just by how the readme talks about it. So I tried using

udev-hid-bpf install 0010-Huion__H610-Pro.bpf.c

as mentioned in the readme and got

"Error: Expected a bpf.o file as argument, not "0010-Huion__H610-Pro.bpf.c""

So I'm guessing that I am installing it wrong. I also tried compiling it via

clang -O2 -emit-llvm -c 0010-Huion__H610-Pro.bpf.c -o - | llc -march=bpf -filetype=obj -o 0010-Huion__H610-Pro.bpf.o

then tried installing it and got "Error: Failed to read BPF from 0010-Huion__H610-Pro.bpf.o": bpf call "libbpf_rs::btf::Btf::from_path::inner::{{closure}}" returned NULL"

Not sure if any of that helps since I'm 90% sure I'm installing it wrong and just missed something or I just don't know enough about bpf files. Let me know if there's a way to fix this. I'll try to help as much as I can.

whot commented 1 month ago

I don't use gnome so I'm testing this all in an Ubuntu VM.

There's a bit to unpack in this sentence :)

First: udev-hid-bpf is an approach to fix the kernel, the BPFs are similar to a kernel module. GNOME has nothing to do with that.

Second: Traditionally the kernel driver for Huion devices was hid-uclogic but that one doesn't support all devices and specifically it doesn't seem to support the KD100 because otherwise this project here wouldn't need to exist :) The current plan is to add support for new devices to udev-hid-bpf while leaving devices previously supported by hid-uclogic as-is so we don't break anything. That's the reason why the H610 is in userhacks - that particular device is supported by hid-uclogic and we're doubling up on support here.

Ubuntu selected the H750P tablet as a driver

I think you're referring to what libwacom thinks this device is? The cause for this is that huion re-uses PIDs and the 0x6d PID is used by a multitude of devices so until quite recently libwacom wasn't able to differ between the devices. It still isn't because the firmware ID somewhat requires the hid-uclogic driver though we're working on that.

The real kernel driver for your device will be listed in

$ udevadm info -p /sys/class/hidraw/hidraw0/device | grep DRIVER
E: DRIVER=hid-generic

Obviously adjust hidraw0 for your device's hidraw node.

Testing the existing BPF

I just realised the H610 BPF needs kernel 6.11 so if ubuntu doesn't use that one yet it won't work anyway, so you can skip the rest below...

First, I recommend copying the H610 sources and editing the copy because otherwise this will get a mess quickly.

To actually try the H610 you'll need to add this line to HID_BPF_CONFIG at the top:

    HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_HUION, 0x6d)

with or without a nice #define for it but without the 0x6d pid it won't simply won't attach to your device.

Then you need to run (and install) huion-switcher to check what the firmware ID is because the BPF checks for that, grep for EXPECTED_FIRMWARE_ID in the sources. If the firmware differs just update that string for yours so the BPF doesn't bail out.

udev-hid-bpf install 0010-Huion__H610-Pro.bpf.c

The .bpf.c file is the source file but you need to pass the compiled bpf.o:

udev-hid-bpf install --install-exe  builddir/src/bpf/0010-Huion__H610-Pro.bpf.o

(--install-exe if you don't have udev-hid-bpf installed locally yet)

Oh, and finally: there's a tutorial which explains the bits and pieces so you don't have to guess what everything means.

So in summary:

mckset commented 1 month ago

Sorry, for some reason I assumed that the project was related to gnome. I switched over to an Arch VM and I was able to get everything to build without errors but I am guessing that I would need another program to configure the device correct? Either way, after install the driver and using

udev-hid-bpf install --install-exe  builddir/src/bpf/0010-Huion__H610-Pro.bpf.o

The driver still shows that it is using uclogic. Which I assume is still ok. I followed a bit of the tutorial to create a new bpf file to mess with and it does show that the file working in "/sys/fs/bpf/hid". I'll be working on this in my free time but I'm still very new to bpf files so it may take me a week or two.

mckset commented 1 month ago

@whot I might need a bit more help working on this. I've got a bpf program that is being assigned to the device but I haven't gotten much further than that. I used hid-recorder and got more information on the 2 interfaces that the device uses. And that's about as far as I got. I cannot get huion-switcher to do anything with the device as it prints the error:

"thread 'main' panicked at src/main.rs:53:10: called 'Results::unwrap()' on an 'Err' value: BadDescriptor"

I'm not sure if you want to see the backtrace or how important it is for the program but that's not entirely what I'm stuck on. The tutorial was quite helpful on how to make a bpf program but I believe it's for a mouse correct? I'm not exactly sure what to do beyond that point. This is probably because I don't work with kernal drivers and maybe my program is a bit misleading as it's not a kernal driver but runs as an application. Is there some sort of standard or protocol that I need to try and make the device follow to get it to act as a configurable keypad?

The way my program works is it reads the raw USB data and then handles it depending on the users configuration. I'm assuming that your driver doesn't actually handle the input but instead corrects the input so that something like gnome-control-center or libwacom can read it correct?

whot commented 1 month ago

Sorry about the silence, I was swamped with something else. So there are a few comments here, the most important one is though: if anything isn't clear about udev-hid-bpf please file an issue there and we can fix the documentation. The goal is long-term to have this replace many HID drivers so ideally users can just come in, fix their device and done. If that's not the case, documentation is missing but we'll need help identifying where this is.

fwiw, I've filed this mr for better docs on the high-level approach.

If huion-switcher doesn't do the right thing that's an indication that the device may not immediately work with udev-hid-bpf anyway since we do need to switch the device into raw mode (eventually).

Having said that, there's the possiblity that uc-logic interferes, can you do an rmmod hid_uclogic and try again?

Basically: the KD100 is a hid device and it (probably) has multiple report descriptors. Those describe how to interpret the data - in the form "bit 3 is button left, bytes 3 and 4 are x/y" and so on. But one of the rdescs is a pure "vendor" report, i.e. without any details, just vendor-specific data. The kernel ignores that since we don't know what the data means. If you run hid-recorder you should see at least 2, more likely 3 reports.

If the tablet is in firmware mode it will send key events, hid-recorder should show those. The idea behind the BPFs is that we convert those events into proper button events and that we also convert the vendor report into a proper tablet report instead (that can be handled by the kernel). So the BPF I linked to actually does two different things, depending on which report the device sends (which will depend on whether huion-switcher changes it into raw mode).

Maybe have a look at the Inspiroy BPF which I wrote and has lots of comments (because I was learning myself at the time).

In other words: You plug the device in, it is in firmware mode, hid-recorder should show 3 HID reports, you get keyboard events from one of them.

If you load the (correct) BPF without huion-switcher, the BPF should convert the incoming reports to tablet events instead (and maybe change the report descriptor if need be). That part is identical to what you appear do do in this utility here (https://github.com/mckset/KD100/blob/main/KD100.c#L326 is basically https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/-/blob/main/src/bpf/stable/0010-Huion__Inspiroy-2-S.bpf.c#L433)

If you load the (correct) BPF with huion-switcher, the BPF changes the report descriptor from lots of "vendor specific, haha" to a proper tablet report ("button X is bit Y", etc). The inspiroy bpf does this here - and that's a matter of a memcpy. Once that's done any event coming over that HID report will be interpreted correctly by the kernel and no further action is necessary.

It changes this (approximately)

device -> kernel -> hidraw -> evdev -> KD100 -> xdotool
   +---------- key event------------->

to this:

device ----------> bpf -> kernel -> hidraw -> evdev -> ....
  _+- key event ->  +------ tablet button event ----->
mckset commented 1 month ago

I don't mind the wait. This project doesn't seem like it's too urgent. A couple of things I want to note.

1) I tried rmmod and it still gave me the same error but there might be a reason for this. The KD100, at least mine, always outputs raw data so I don't know if Huion recently added the need to the switcher or not. I had a user who was trying to use the K20 with my "driver" but it didn't work for him. See https://github.com/mckset/KD100/issues/10
Another user reported that it would work but only after loading Huion's software and then closing it. My guess is that it put the device in the same state huion-switcher does. But my KD100 doesn't require any of that. Even then, I was able to get udev-hid-bpf to find the device and assign a bpf program to it.

2) hid-recorder does display multiple reports. Sometimes 2 and other times 3. Both times, the first report is always the one to receive input from the device. However, if 3 reports are shown, the first report receives data similar to what I saw with the USB data but if only 2 are shown, the data is changed a little bit.

What you sent did help me get a bit further but it's still the same issue where I'm not exactly sure how to change the data for the device to be read properly. If you want, I can open an issue on the project and ask for more documentation. Right now I have all the data from hid-recorder and the data that is sent from each button and the wheel. So I think I have everything I need from the device but it's just a matter of figuring out how fix it in a way that can be interpreted by the operating system.

whot commented 1 month ago

but only after loading Huion's software and then closing it.

yeah, the software fetches the magic usb field (see the huion-switcher code, it's very simple) so that may explain why it works. Once it's switched to raw mode you need to unplug to get the firmware mode again.

Even then, I was able to get udev-hid-bpf to find the device and assign a bpf program to it.

I'm basing this off my Inspiroy 2S and the other tablets we've seen in udev-hid-bpf: the tablets always work OOTB, but the difference is that in the firmware mode they'll send keyboard shortcuts whereas in raw mode they'll send proper tablet button events (BTN_0 and friends) - except that in raw mode it will send those over a vendor collection so the kernel won't know what to do with those unless the BPF changes the report descriptor.

I suspect that is what you are seeing? the keyboard shortcuts?

I think we're at the point where it's best to open an issue in udev-hid-bpf though, with all the hid-recorder output for the various report ids attached. That way we should be able to proceed faster.