I'm having trouble using my Apple Magic Keyboard (bluetooth wireless with LiIon battery, Lightning port for charging and tethered usage) with Fedora 25 (kernel: 4.8.15-300.fc25.x86_64
).
The problem is that when used in wireless mode, the Fn key does not seem to register. I tried xev
and the key itself doesn't trigger any event, nor does the key pressed with another key cause the triggered event to be any different compared to the other key just being pressed on its own. The reason why I'd like to use the Fn key is because I want to map Fn + ←/→ to Home and End respectively and also use the multimedia keys which are now function keys by default.
The interesting thing is that this keyboard acts as a normal Apple wired keyboard when I connect it with the lightning cable to the computer in which I assume is due to the bluetooth radio not being used and resorting to USB hardware/drivers (perhaps it's registered with a different USB device ID than the original Apple aluminium keyboard, I didn't verify). Doing so allows function key usage and all the tricks like function keys or multimedia keys by default that you find on the internet.
However, I would like to have the same features available when using it as a bluetooth keyboard. I would go as far as patching the kernel, but have no idea where to start and how to test and debug (obviously I would like to try out less "invasive" means first).
Any idea on how to address this problem are welcome.
Update
When I read from /dev/hidraw0
, I get some activity when hitting the Fn key, so this could mean the fn keypress is registered by the system, but gets lost somewhere along the way…
Update2
evtest
does not show any event when pressing the Fn key and /dev/input/event4
(which is the event device for the Magic Keyboard) does not trigger an event (other keys do). So I think the problem is that the Fn key gets read by the system (implied by /dev/hidraw0
showing data) but it doesn't get passed on to /dev/input/event4
. But this is just speculation as I don't know how the flow of user input data is meant to be working in Linux.
Update 3
This is what several fn key presses (press+release) produce:
> sudo cat /dev/hidraw2 | hexdump
0000000 0001 0000 0000 0000 0000 0001 0000 0000
0000010 0000 0200 0001 0000 0000 0000 0000 0001
0000020 0000 0000 0000 0200 0001 0000 0000 0000
0000030 0000 0001 0000 0000 0000 0200 0001 0000
0000040 0000 0000 0000 0001 0000 0000 0000 0200
0000050 0001 0000 0000 0000 0000 0001 0000 0000
0000060 0000 0200 0001 0000 0000 0000 0000 0001
0000070 0000 0000 0000 0200 0001 0000 0000 0000
0000080 0000 0001 0000 0000 0000 0200 0001 0000
0000090 0000 0000 0000 0001 0000 0000 0000 0200
00000a0 0001 0000 0000 0000 0000 0001 0000 0000
00000b0 0000 0200 0001 0000 0000 0000 0000 0001
00000c0 0000 0000 0000 0200 0001 0000 0000 0000
00000d0 0000 0001 0000 0000 0000 0200 0001 0000
00000e0 0000 0000 0000 0001 0000 0000 0000 0200
00000f0 0001 0000 0000 0000 0000 0001 0000 0000
0000100 0000 0200 0001 0000 0000 0000 0000 0001
0000110 0000 0000 0000 0200 0001 0000 0000 0000
0000120 0000 0001 0000 0000 0000 0200 0001 0000
0000130 0000 0000 0000 0001 0000 0000 0000 0200
0000140 0001 0000 0000 0000 0000 0001 0000 0000
0000150 0000 0200 0001 0000 0000 0000 0000 0001
0000160 0000 0000 0000 0200 0001 0000 0000 0000
0000170 0000 0001 0000 0000 0000 0200 0001 0000
0000180 0000 0000 0000 0001 0000 0000 0000 0200
0000190 0001 0000 0000 0000 0000 0001 0000 0000
00001a0 0000 0200 0001 0000 0000 0000 0000 0001
00001b0 0000 0000 0000 0200 0001 0000 0000 0000
00001c0 0000 0001 0000 0000 0000 0200 0001 0000
00001d0 0000 0000 0000 0001 0000 0000 0000 0200
00001e0 0001 0000 0000 0000 0000 0001 0000 0000
00001f0 0000 0200 0001 0000 0000 0000 0000 0001
0000200 0000 0000 0000 0200 0001 0000 0000 0000
0000210 0000 0001 0000 0000 0000 0200 0001 0000
0000220 0000 0000 0000 0001 0000 0000 0000 0200
0000230 0001 0000 0000 0000 0000 0001 0000 0000
Weirdly enough, sometimes 2 lines but mostly 1 line is printed after releasing fn.
This is what F2 and Fn+F2 respectively look like:
sudo cat /dev/hidraw2 | hexdump
0000000 0001 0000 0000 0000 0000 0001 3b00 0000
^[OQ0000010 0000 0000 0001 0000 0000 0000 0000 0001
^[OQ0000020 3b00 0000 0000 0000 0001 0000 0000 0000
^[OQ0000030 0000 0001 3b00 0000 0000 0000 0001 0000
0000040 0000 0000 0000 0001 3b00 0000 0000 0000
^[OQ0000050 0001 0000 0000 0000 0000 0001 3b00 0000
^[OQ0000060 0000 0000 0001 0000 0000 0000 0000 0001
^[OQ0000070 3b00 0000 0000 0000 0001 0000 0000 0000
0000080 0000 0101 0000 0000 0000 0000 0101 0600
^C
Fn+F2:
> sudo cat /dev/hidraw2 | hexdump
0000000 0001 0000 0000 0000 0000 0001 0000 0000
^[OQ0000010 0000 0200 0001 3b00 0000 0000 0200 0001
0000020 0000 0000 0000 0200 0001 3b00 0000 0000
^[OQ0000030 0200 0001 0000 0000 0000 0200 0001 3b00
^[OQ0000040 0000 0000 0200 0001 0000 0000 0000 0200
^[OQ0000050 0001 3b00 0000 0000 0200 0001 0000 0000
^[OQ0000060 0000 0200 0001 3b00 0000 0000 0200 0001
0000070 0000 0000 0000 0200 0001 3b00 0000 0000
^[OQ0000080 0200 0001 0000 0000 0000 0200 0001 3b00
^[OQ0000090 0000 0000 0200 0001 0000 0000 0000 0200
^[OQ00000a0 0001 3b00 0000 0000 0200 0001 0000 0000
00000b0 0000 0200 0001 0000 0000 0000 0000 0101
00000c0 0000 0000 0000 0000 0101 0600 0000 0000
^C
Update 4
As requested from @dirkt, here's the report descriptor information (I couldn't run the line as per the comment, so here's the full dump; also note that it's now hidraw2
as I had to replace the keyboard):
> sudo ./hid-desc /dev/hidraw2
Report Descriptor Size: 171
Report Descriptor:
05 01 09 06 a1 01 85 01 05 07 15 00 25 01 19 e0 29 e7 75 01 95 08 81 02 95 05 75 01 05 08 19 01 29 05 91 02 95 01 75 03 91 03 95 08 75 01 15 00 25 01 06 00 ff 09 03 81 03 95 06 75 08 15 00 25 65 05 07 19 00 29 65 81 00 95 01 75 01 15 00 25 01 05 0c 09 b8 81 02 95 01 75 01 06 00 ff 09 03 81 02 95 01 75 06 81 03 06 02 ff 09 55 85 55 15 00 26 ff 00 75 08 95 40 b1 a2 c0 06 00 ff 09 14 a1 01 85 90 05 84 75 01 95 03 15 00 25 01 09 61 05 85 09 44 09 46 81 02 95 05 81 01 75 08 95 01 15 00 26 ff 00 09 65 81 02 c0 00
Raw Name: Magic Keyboard
Raw Phys: 00:c2:c6:f7:eb:57
Raw Info:
bustype: 5 (Bluetooth)
vendor: 0x004c
product: 0x0267
Best Answer
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
. Ashiddev.txt
explains, the dataflow for an event is like this:In
drivers/hid/hid-input.c
, in particular in the routinehidinput_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 examplehttps://github.com/DIGImend/usbhid-dump
to get the descriptor (USB only), andhttps://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 tousbhid-dump
. You'll have to use this for Bluetooth, so I put it in a pastebin. Compile withmake
.(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
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 toThere is one input event report with id hex
01
, one battery status report with id hex90
, 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
is the report it.MM
are the standard 8 modifier bits, which don't have room for theFn
key.K1
toK6
are up to 6 keys pressed simultanously.VA
andVB
are vendor specific. Assuming you held Fn and just pressed and releasedF2
in the last example, my guess is that bit 1 inVB
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
andsamples/uhid/uhid-example.c
in the kernel.