4

I'm developing an application in which I need to read the input of a trigger on a webcam to tell my application to do something.

The project involves OpenCV built with QT support, and I will randomly get crashes when I apply this trigger (ASSERT: "false" in file qasciikey.cpp, line 495).

So I figured an easier and more robust way is to map the external trigger to a key (spacebar, specifically). I don't want to override the spacebar, just to have the trigger look like the spacebar was pressed.

So far, using evtest, I select my device:

/dev/input/event13: See3CAM_CU51

And can see when I trigger the camera, it displays the following:

Event: time 1507757166.472300, type 1 (EV_KEY), code 212 (KEY_CAMERA), value 1
Event: time 1507757166.472300, -------------- SYN_REPORT ------------
Event: time 1507757167.147649, type 1 (EV_KEY), code 212 (KEY_CAMERA), value 0
Event: time 1507757167.147649, -------------- SYN_REPORT ------------

The "value 1" is when the pins are pulled high (triggered), and "value 0" are when the pins are separated. So I think I only need to remap KEY_CAMERA to the spacebar, but I'm not sure the cleanest way to do it using Ubuntu 16.04.

When I select my keyboard device:

/dev/input/event4: AT Translated Set 2 keyboard

And I press spacebar, I get the following output:

Event: time 1507757327.011812, -------------- SYN_REPORT ------------
Event: time 1507757328.818177, type 4 (EV_MSC), code 4 (MSC_SCAN), value 39
Event: time 1507757328.818177, type 1 (EV_KEY), code 57 (KEY_SPACE), value 1
Event: time 1507757328.818177, -------------- SYN_REPORT ------------
Event: time 1507757328.896970, type 4 (EV_MSC), code 4 (MSC_SCAN), value 39
Event: time 1507757328.896970, type 1 (EV_KEY), code 57 (KEY_SPACE), value 0

I've been reading about writing the hwdb udev file, but I've yet to see a way to map an input from one device all the way to another. Is this possible? What's the easiest way of getting this done?

Thanks in advance.

2
  • Of course your application could catch the camera event and issue a key event, but what makes you think the behaviour would change when using a different event? What is that assertion for?
    – Philippos
    Oct 12, 2017 at 6:43
  • My application is using OpenCV built with QT. QT is detecting key events (I assume). In my program I can catch a spacebar key event fine. But when I trigger the camera, the program sometimes fails, sometimes succeeds, sometimes only works once, etc. I'm blaming this one on QT. This is where it fails: github.com/openwebos/qt/blob/…
    – justynnuff
    Oct 12, 2017 at 22:07

2 Answers 2

1

Probably the right answer is to write an [hwdb] file to override the interpretation of the keycode KEY_CAMERA into KEY_SPACE. This seems to be an example, but it is a little lacking in details.

The format of the file is mostly described in a comment at the head of the file /usr/lib/udev/hwdb.d/60-keyboard.hwdb (on my system), and you can see some archlinux discussion of that. Beware, this format depends on the release of systemd, so check your own file. You can match your input device with one of 3 patterns:

  • Generic input devices match:

      evdev:input:bZZZZvYYYYpXXXXeWWWW-VVVV

    This matches on the kernel modalias of the input-device, mainly: ZZZZ is the bus-id (see /usr/include/linux/input.h BUS_*), YYYY, XXXX and WWW are the 4-digit hex uppercase vendor, product and version ID and VVVV is an arbitrary length input-modalias describing the device capabilities.

  • AT keyboard DMI data matches:

      evdev:atkbd:dmi:bvn*:bvr*:bd*:svnvendor:pnproduct:pvr*

    vendor and product are the firmware-provided strings exported by the kernel DMI modalias.

  • Input driver device name and DMI data match:

      evdev:name:input device name:dmi:bvn*:bvr*:bd*:svnvendor:pn*

    input device name is the name device specified by the driver, vendor is the firmware-provided string exported by the kernel DMI modalias.

Another possibility is to write a small program to read the webcam event stream, like evtest does, and when the key is seen, inject a KEY_SPACE into the other event stream. There seems to a lot more documentation on this, and many Python examples like this tutorial snippet to inject an event.

3
  • I don't get the syntax of the top of the hwdb files. Ie, evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pn123*. Where can I find that for my keyboard and camera? Do you know?
    – justynnuff
    Oct 12, 2017 at 22:06
  • @justynnuff I added the only information I have on the format.
    – meuh
    Oct 13, 2017 at 6:59
  • 1
    @justynnuff, I've just dug into hwdb a bit more and added a new answer with more details, including an answer to your question (it became a bit too much for a comment ;-p) Feb 4, 2023 at 10:56
1

Adding to the answer by @meuh about using hwdb, here's a few things I learned about how hwdb works. This is mostly about keyboard scancode mappings from hwdb, but a lot of this will be useful for other hwdb info as well.

Sources:

For a practical example of what I did with the info I collected here, see this post about repurposing an "Ecobutton", a single-button "keyboard" that sends a key sequence when pressed.

What identifiers are used in hwdb?

To actually find out which hwdb identifiers are being used, you can use udevadm:

$ sudo udevadm test /sys/class/input/event256 |& grep builtin.command..hwdb
event256: /usr/lib/udev/rules.d/60-evdev.rules:8 Importing properties from results of builtin command 'hwdb --subsystem=input --lookup-prefix=evdev:'
event256: /usr/lib/udev/rules.d/60-evdev.rules:18 Importing properties from results of builtin command 'hwdb 'evdev:name:HID 3412:7856:phys:usb-0000:00:14.0-1.1.2.4/input0:ev:120013:dmi:bvnINSYDECorp.:bvr03.17:bd10/27/2022:br3.17:svnFramework:pnLaptop:pvrAB:rvnFramework:rnFRANBMCP0B:rvrAB:cvnFramework:ct10:cvrAB:skuFRANBMCP0B:''
event256: /usr/lib/udev/rules.d/60-evdev.rules:23 Importing properties from results of builtin command 'hwdb 'evdev:name:HID 3412:7856:dmi:bvnINSYDECorp.:bvr03.17:bd10/27/2022:br3.17:svnFramework:pnLaptop:pvrAB:rvnFramework:rnFRANBMCP0B:rvrAB:cvnFramework:ct10:cvrAB:skuFRANBMCP0B:''
event256: /usr/lib/udev/rules.d/60-input-id.rules:6 Importing properties from results of builtin command 'hwdb --subsystem=input --lookup-prefix=id-input:modalias:'
event256: /usr/lib/udev/rules.d/65-libwacom.rules:19 Importing properties from results of builtin command 'hwdb --subsystem=input '--lookup-prefix=libwacom:name:HID 3412:7856:''

Note that this tries three of the four hwdb commands shown in the udev rules above, the evdev:atkbd one is only done for DRIVERS=="atkbd" and does not apply to this USB keyboard.

For example, the above output contains:

event256: /usr/lib/udev/rules.d/60-evdev.rules:18 Importing properties from results of builtin command 'hwdb 'evdev:name:HID 3412:7856:phys:usb-0000:00:14.0-1.1.2.4/input0:ev:120013:dmi:bvnINSYDECorp.:bvr03.17:bd10/27/2022:br3.17:svnFramework:pnLaptop:pvrAB:rvnFramework:rnFRANBMCP0B:rvrAB:cvnFramework:ct10:cvrAB:skuFRANBMCP0B:''

It seems this contains a lot of identifiers specific to my laptop, not the USB keyboard I've attached, but the USB vidpid (3412:7856) is contained at the start and could be matched like this:

evdev:name:HID 3412:7856:*
  KEYBOARD_KEY_70028=backspace

Alternatively, the evdev output also contains (second line is not shown above due to the grep, but it is there in the full output):

event256: /usr/lib/udev/rules.d/60-evdev.rules:8 Importing properties from results of builtin command 'hwdb --subsystem=input --lookup-prefix=evdev:'
event256: hwdb modalias key: "input:b0003v3412p7856e0100-e0,1,4,11,14,k74,75,77,7D,7E,7F,B7,ram4,l0,1,2,3,4,sfw"

It seems that this hwdb call does not get the full match key specified like in the later ones, but it is built from the prefix and (by hwdb) autogenerated modalias key. So the above results in full key of:

evdev:input:b0003v3412p7856e0100-e0,1,4,11,14,k74,75,77,7D,7E,7F,B7,ram4,l0,1,2,3,4,sfw

This match the USB vid/pid from this, you can use something like:

evdev:input:b????v3412p7856e*
  KEYBOARD_KEY_70028=backspace

Note that this matches the busnumber using ? wildcards, to ensure this still matches when you plug into another USB bus/port. It also uses a * wildcard for the version number and other modalias stuff after it, which is probably not relevant here.

What scancodes should I use?

The most reliable way to get the right scancode seems to be using evtest. Run it, and then press a button gives something like:

sudo evtest --grab /dev/input/event256 
[ snip debug output]
Event: time 1675505630.859393, -------------- SYN_REPORT ------------
Event: time 1675505630.867333, type 4 (EV_MSC), code 4 (MSC_SCAN), value 70028
Event: time 1675505630.867333, type 1 (EV_KEY), code 28 (KEY_ENTER), value 0

Here, the 70028 is the scancode (note that it is actually hexadecimal, but this is also what the KEYBOARD_KEY_xx expects (e.g. KEYBOARD_KEY_70028=backspace). Note that it seems that USB keyboards use 700xx scancodes, while old-school AT keyboards seem to use lower scancodes, so best just test with your keyboard rather than look up codes online.

Also note that it seems that these scancodes are written in hexadecimal (with the 0x prefix omitted everywhere), while keycodes are written and parsed in decimal, but I'm not 100% sure.

See also this page

What keycodes should I use?

The canonical source of these keycodes is input-event-codes in the kernel, all the KEY_xxx constants.

It seems that the numeric value, the KEY_xxx and just the xxx value are all accepted. E.g. the following are all equivalent to map the enter key to produce backspace:

 KEYBOARD_KEY_70028=14
 KEYBOARD_KEY_70028=key_backspace
 KEYBOARD_KEY_70028=backspace

It seems the same keycodes are shown by evtest on startup (as supported EV_KEY event codes), in evtest output when pressing a key (as EV_KEY event code)

See also this page

How can I see the current scancode mapping for an input device?

I'm assuming this should be query-able using the input device and some ioctl, but I'm not sure what existing commandline tool can do this for you. If you find out, leave a comment.

It does seem that the supported EV_KEY list printed on evtest startup provides a hint: That list seems to only contain keycodes that have some scancode mapped to it.

How to apply changes to a hwdb file?

You need to recompile the hwdb files into a /etc/udev/hwdb.bin using:

$ sudo systemd-hwdb update

The replug the USB device, or force udev to re-run its rules using:

$ sudo udevadm trigger /sys/class/input/event256

Where is hwdb called from?

Keys and values in hwdb are not hardcoded in udev, but determined by rules files. For example, /usr/lib/udev/rules.d/60-evdev.rules handles input/keyboard rules:

IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=evdev:", \
  IMPORT{builtin}="keyboard", GOTO="evdev_end"

# AT keyboard matching by the machine's DMI data
DRIVERS=="atkbd", \
  IMPORT{builtin}="hwdb 'evdev:atkbd:$attr{[dmi/id]modalias}'", \
  IMPORT{builtin}="keyboard", GOTO="evdev_end"

# device matching the input device name + properties + the machine's DMI data
KERNELS=="input*", \
  IMPORT{builtin}="hwdb 'evdev:name:$attr{name}:phys:$attr{phys}:ev:$attr{capabilities/ev}:$attr{[dmi/id]modalias}'", \
  IMPORT{builtin}="keyboard", GOTO="evdev_end"

# device matching the input device name and the machine's DMI data
KERNELS=="input*", \
  IMPORT{builtin}="hwdb 'evdev:name:$attr{name}:$attr{[dmi/id]modalias}'", \
  IMPORT{builtin}="keyboard", GOTO="evdev_end"

So the above rules show exactly how the hwdb identifiers are constructed. Furthermore, the idea is that 60-evdev.rules and 60-keyboard.hwdb are correlated, but that's just convention - all hwdb entries are put into a single database and can be matched by any udev rule that provides the right identifier.

How are hwdb properties processed?

The effect of these IMPORT{builtin}="hwdb ..." lines is that the hwdb is consulted and any properties listed in there are exposed as regular udev device properties (i.e. you can query them with udevadm info).

Furthermore, the rules above also contain IMPORT{builtin}="keyboard", which calls a udev builtin utility that processes KEYBOARD_* properties and uses them to configure scancode-to-keycode mappings in the kernel (using the EVIOCSKEYCODE ioctl on the input device, see source).

Again, using udevadmin info as shown above is helpful here, as it should show any keymappings that the builtin decides to apply.

$ sudo udevadm test /sys/class/input/event256 |& grep mapping
event256: keyboard: mapping scan code 18 (0x12) to key code 0 (0x0)
event256: keyboard: mapping scan code 458770 (0x70012) to key code 0 (0x0)
event256: keyboard: mapping scan code 36 (0x24) to key code 0 (0x0)

(note that running udevadm test like this actually does apply these keymappings directly, even though documentation suggests it should only do a dry run. This is probably a bug, because I suspect the "builtins" mechanism was originally intended to collect info only and not apply changes)

Setting hwdb / KEYBOARD properties from udev rules

Hwdb entries essentially just set device properties, just like udev rules can do directly (but I guess hwdb was invented to make it easier to compactly set multiple properties without needing very verbose udev rules). However, this means that it should be possible to do this key remapping using just udev rules instead of the somewhat more complicated hwdb, e.g.:

SUBSYSTEM=="input", KERNEL=="event3", ENV{KEYBOARD_KEY_01}="capslock", ENV{KEYBOARD_KEY_3a}="esc"
SUBSYSTEM=="input", KERNEL=="event3", IMPORT{builtin}="keyboard"

Note that the udev rules in 60-evdev.rules that run the "keyboard" plugin do this in the same rule as the hwdb builtin. This means, I believe, that the "keyboard" rule is run only if the hwdb builtin found any information.

If your hwdb rules do not match this particular device, the "keyboard" builtin might not run, so I added an explicit rule above to run it (this must be a separate rule, since when they are in the same rule it seems that the ENV entries are applied only after the builtin is ran, not before).

If your hwdb rules do match this particular device, you can omit the second rule above and rely on 60-evdev.rules to run the keyboard builtin (but the the above udev rule should be in a rules file that runs before 60-evdev.rules, not after. It seems that older versions of udev/eudev called the keyboard builtin using RUN instead of IMPORT and did not have this ordering constraint).

Source of part of this info is this issue.

Where are these udev builtins documented?

Nowhere, it seems. See this answer for some pointers into the source for these builtins.

You must log in to answer this question.

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