Partial answer: Making sense of the HID infrastructure and the HID raw data
(Disclaimer: I've only done all this for USB, but I suppose it will apply in the same or a similar way to Bluetooth).
HID devices can send and receive reports in a well-defined format. The format for a particular device is given by the HID descriptor, which for USB is very similar to the other USB descriptors (e.g. lsusb
can list them if they are not bound). Details (for USB) can be found in the Device Class Definition for Human Interface Devices (HID) PDF document.
Kernel documentation for HID can be found Documentation/hid
. As hiddev.txt
explains, the dataflow for an event is like this:
usb.c --> hid-core.c --> hid-input.c --> input-subsystem
In drivers/hid/hid-input.c
, in particular in the routine hidinput_configure_usage
, a report is parsed according to the HID descriptor.
So if you can't see the Fn key, that's where things go wrong.
The output seen at hidraw0
looks suspiciously like there are several kinds of reports with different IDs (this report has ID 1, normal keyboard reports have ID 0).
But to make sure, we need the HID descriptor(s). HID descriptors are available via an ioctl on the hidraw
device. You can use for example https://github.com/DIGImend/usbhid-dump
to get the descriptor (USB only), and https://github.com/DIGImend/hidrd
to parse it. There's also /samples/hidraw/hid-example.c
file in the kernel source that shows how to get the HID descriptor via the ioctl; it can be easily modified to produce an hex-dump similar to usbhid-dump
. You'll have to use this for Bluetooth, so I put it in a pastebin. Compile with make
.
(If you are not used to compiling external projects: Download zip file for both, unpack each into an empty directory, ./bootstrap
, ./configure
, make
. Now you can use the binaries directly, add them $PATH
, etc.)
Now you can parse the descriptor using
sudo ./hid-desc /dev/hidraw0 | tail -n+3 | head -1 | hidrd-convert -ihex -ospec
In addition to providing this output (or the hexdump, if anything doesn't work), please test what happens on hidraw
if you press the Fn in combination with various other keys (alphabetic, arrows). Also test what happens for normal keypresses.
I'm not sure about the best way to proceed if it's not possible to make the kernel recognize the special reports. Maybe the simplest way is to write a C program that analyzes events from hidraw
and produces additional input-events, similarly to input-create.
Update: The HID descriptor contains an extra 00
at the end. If you remove that, it parses to
Usage Page (Desktop), ; Generic desktop controls (01h)
Usage (Keyboard), ; Keyboard (06h, application collection)
Collection (Application),
Report ID (1), ; +00 report id
Usage Page (Keyboard), ; Keyboard/keypad (07h)
Logical Minimum (0),
Logical Maximum (1),
Usage Minimum (KB Leftcontrol), ; Keyboard left control (E0h, dynamic value)
Usage Maximum (KB Right GUI), ; Keyboard right GUI (E7h, dynamic value)
Report Size (1),
Report Count (8),
Input (Variable), ; +01 modifier
Report Count (5),
Report Size (1),
Usage Page (LED), ; LEDs (08h)
Usage Minimum (01h),
Usage Maximum (05h),
Output (Variable),
Report Count (1),
Report Size (3),
Output (Constant, Variable),
Report Count (8),
Report Size (1),
Logical Minimum (0),
Logical Maximum (1),
Usage Page (FF00h), ; FF00h, vendor-defined
Usage (03h),
Input (Constant, Variable), ; +02 vendor
Report Count (6),
Report Size (8),
Logical Minimum (0),
Logical Maximum (101),
Usage Page (Keyboard), ; Keyboard/keypad (07h)
Usage Minimum (None), ; No event (00h, selector)
Usage Maximum (KB Application), ; Keyboard Application (65h, selector)
Input, ; +03 6 keysym bytes
Report Count (1),
Report Size (1),
Logical Minimum (0),
Logical Maximum (1),
Usage Page (Consumer), ; Consumer (0Ch)
Usage (Eject), ; Eject (B8h, one-shot control)
Input (Variable), : +09.0
Report Count (1),
Report Size (1),
Usage Page (FF00h), ; FF00h, vendor-defined
Usage (03h),
Input (Variable), ; +09.1
Report Count (1),
Report Size (6),
Input (Constant, Variable), : +09.2-7
Usage Page (FF02h), ; FF02h, vendor-defined
Usage (55h),
Report ID (85),
Logical Minimum (0),
Logical Maximum (255),
Report Size (8),
Report Count (64),
Feature (Variable, No Preferred, Volatile),
End Collection,
Usage Page (FF00h), ; FF00h, vendor-defined
Usage (14h),
Collection (Application),
Report ID (144),
Usage Page (Power Device), ; Power device (84h, power page)
Report Size (1),
Report Count (3),
Logical Minimum (0),
Logical Maximum (1),
Usage (61h),
Usage Page (Power Batsys), ; Power battery system (85h, power page)
Usage (44h),
Usage (46h),
Input (Variable),
Report Count (5),
Input (Constant),
Report Size (8),
Report Count (1),
Logical Minimum (0),
Logical Maximum (255),
Usage (65h),
Input (Variable),
End Collection
There is one input event report with id hex 01
, one battery status report with id hex 90
, one output to set the LEDs as usual, and one vendor-specific feature control.
I marked the bytes for the input event report. There's several vendor defined field where we don't know what they do, and have to guess.
The input event report consists of 10 bytes, and your examples decode as follows:
ID MM VA K1 K2 K3 K4 K5 K6 VB
01 00 00 00 00 00 00 00 00 02 ; press? Fn
01 00 00 00 00 00 00 00 00 00 ; release? Fn
01 00 00 3b 00 00 00 00 00 00 ; press F2
01 00 00 00 00 00 00 00 00 00 ; release
01 00 00 00 00 00 00 00 00 00 ;
01 00 00 00 00 00 00 00 00 02 ; press Fn?
01 00 00 3b 00 00 00 00 00 02 ; press F2
01 00 00 00 00 00 00 00 00 02 ; release F2 (but not Fn?)
ID
is the report it. MM
are the standard 8 modifier bits, which don't have room for the Fn
key. K1
to K6
are up to 6 keys pressed simultanously. VA
and VB
are vendor specific. Assuming you held Fn and just pressed and released F2
in the last example, my guess is that bit 1 in VB
represents the modifier for Fn (or at least something related to it).
Use hexdump -e '10/1 "%02X ""\n"'
to get 9 bytes of output per line, and test this hypothesis by combining Fn with several keys, including those combinations you want to redefine in the end.
Update: For completeness and future reference, though I assume it's not relevant anymore for this particular case: It's possible to inject HID events using UHID, see Documentation/hid/uhid.txt
and samples/uhid/uhid-example.c
in the kernel.
Best Answer
I have tried to make a proper patch for this bug. It is a problem in the kernel rather than with the keyboard, although it could be argued that the keyboard behave in a strange way. Anyway, the patch is submitted to the linux-input list for review but there are no comments on it yet.
This should fix the problem mentioned here with the QPAD MK-85, but the same problem exists with Corsair K70, Gigabyte Osmium and other similar keyboards. If you have a keyboard that has the bug it would be great it you can test the patch. If you test it let me know if it works and also what keyboard you have, it is also important what language version you are using, US and non-US keyboards will behave differently. Note that the backslash key on US keyboards will have other labels on other versions of the keyboard.
Here is the mail from linux-input with the patch:
http://article.gmane.org/gmane.linux.kernel.input/37583