3

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
7
  • @chicks If you add <kbd></kbd>, please make sure that what is between those tags reflects what is on a keyboard, including casing, abbreviations and signs.
    – Anthon
    Jan 1, 2017 at 7:38
  • 1
    I can confirm your interpretation: The keyboard sends some raw HID events for Fn that are not understood by either the kernel HID parser, or the input layer. Now the question is what gets send. Can you include a hexdump of the data on /dev/hidraw0? Does anything happen on the corresponding /dev/hiddev0?
    – dirkt
    Jan 1, 2017 at 7:41
  • @orange Please try to make and keep a single coherent question. If you feel the need to insert "Update" (or Edit) in the text, there is something wrong. We have edit history, for anyone interested in how the question evolved, the rest of us don't care about information being an afterthought and only get distracted by it.
    – Anthon
    Jan 1, 2017 at 7:41
  • 1
    @orange It might be convenient for you, but it is against the purpose of the site to make a quality Q&A repository.
    – Anthon
    Jan 1, 2017 at 10:02
  • 1
    @Anthon Feel free to edit the post.
    – orange
    Jan 1, 2017 at 10:18

2 Answers 2

3

With help from the hints above a was able to create a patch that makes the Fn key work when the Apple Magic Keyboard is connected via bluetooth.

Part of the problem was that the Magic Keyboard shows up with vendor id 0x004c (bluetooth vendor id for Apple) and hid-apple only looks for 0x05ac (Apple USB vendor id) so the hid-generic module was used instead of hid-apple.

Second issue was in apple_input_mapping which originally comes from code in hidinput_configure_usage according git log.

Patch is posted here: https://bugzilla.kernel.org/show_bug.cgi?id=99881#c41

1
  • Thanks for the patch. Looking forward to this being integrated in the Fedora kernel.
    – orange
    Sep 5, 2017 at 3:57
2

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.

9
  • Thanks for the comprehensive answer. I've updated my question accordingly. I was thinking along the same lines of reading hidraw and emitting events, but for that to work I would need to know how to encode the hidraw file (so far I can't make sense of the format). Why did you ask the report descriptor? Is this helpful for encoding the hidraw output or to troubleshoot the problem with the Apple keyboard?
    – orange
    Jan 7, 2017 at 0:19
  • I think the report descriptor cannot be recognised by hidrd-convert. I get Failed to read input item at line 1, column 680: invalid item
    – orange
    Jan 7, 2017 at 7:04
  • 1
    Yes, the report descriptor is immensly helpful to decode the output. Answer updated.
    – dirkt
    Jan 7, 2017 at 9:34
  • Great answer and thanks for spending so much time on this! I can confirm that 0x02 in column VB is always set when the Fn key is pressed. Just one quick correct: I had to use hexdump -e '10/1 "%02X ""\n"' as it's actually a 10 byte datagram (I think you forgot to account for the ID). I think I have everything to detect the key and hope it's easy enough to simulate the Fn key event.
    – orange
    Jan 7, 2017 at 10:08
  • You'll still have to unbind the hidraw event from the input subsystem somehow, otherwise you'll get the same events for Fn + arrow keys as for the arrow keys alone. If you manage to get it work, please document it (e.g. write a new question, supply your own answer, and put a comment here). There's still some work to do, and I'm curious about how the missing details will turn out. :-)
    – dirkt
    Jan 7, 2017 at 10:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .