summaryrefslogtreecommitdiff
path: root/src/udev/keymap
diff options
context:
space:
mode:
Diffstat (limited to 'src/udev/keymap')
-rw-r--r--src/udev/keymap/.gitignore5
-rw-r--r--src/udev/keymap/95-keyboard-force-release.rules57
-rw-r--r--src/udev/keymap/95-keymap.rules175
-rw-r--r--src/udev/keymap/README.keymap.txt97
-rwxr-xr-xsrc/udev/keymap/check-keymaps.sh38
-rwxr-xr-xsrc/udev/keymap/findkeyboards68
-rwxr-xr-xsrc/udev/keymap/keyboard-force-release.sh.in22
-rw-r--r--src/udev/keymap/keymap.c453
8 files changed, 915 insertions, 0 deletions
diff --git a/src/udev/keymap/.gitignore b/src/udev/keymap/.gitignore
new file mode 100644
index 0000000000..4567584f4e
--- /dev/null
+++ b/src/udev/keymap/.gitignore
@@ -0,0 +1,5 @@
+keyboard-force-release.sh
+keys-from-name.gperf
+keys-from-name.h
+keys-to-name.h
+keys.txt
diff --git a/src/udev/keymap/95-keyboard-force-release.rules b/src/udev/keymap/95-keyboard-force-release.rules
new file mode 100644
index 0000000000..39f01a4d3e
--- /dev/null
+++ b/src/udev/keymap/95-keyboard-force-release.rules
@@ -0,0 +1,57 @@
+# Set model specific atkbd force_release quirk
+#
+# Several laptops have hotkeys which don't generate release events,
+# which can cause problems with software key repeat.
+# The atkbd driver has a quirk handler for generating synthetic
+# release events, which can be configured via sysfs since 2.6.32.
+# Simply add a file with a list of scancodes for your laptop model
+# in /usr/lib/udev/keymaps, and add a rule here.
+# If the hotkeys also need a keymap assignment you can copy the
+# scancodes from the keymap file, otherwise you can run
+# /usr/lib/udev/keymap -i /dev/input/eventX
+# on a Linux vt to find out.
+
+ACTION=="remove", GOTO="force_release_end"
+SUBSYSTEM!="serio", GOTO="force_release_end"
+KERNEL!="serio*", GOTO="force_release_end"
+DRIVER!="atkbd", GOTO="force_release_end"
+
+ENV{DMI_VENDOR}="$attr{[dmi/id]sys_vendor}"
+
+ENV{DMI_VENDOR}=="[sS][aA][mM][sS][uU][nN][gG]*", RUN+="keyboard-force-release.sh $devpath samsung-other"
+ENV{DMI_VENDOR}=="[sS][aA][mM][sS][uU][nN][gG]*", ATTR{[dmi/id]product_name}=="*90X3A*", RUN+="keyboard-force-release.sh $devpath samsung-90x3a"
+
+ENV{DMI_VENDOR}=="Dell Inc.", ATTR{[dmi/id]product_name}=="Studio 1557|Studio 1558", RUN+="keyboard-force-release.sh $devpath common-volume-keys"
+ENV{DMI_VENDOR}=="Dell Inc.", ATTR{[dmi/id]product_name}=="Latitude E*|Latitude *U|Precision M*", RUN+="keyboard-force-release.sh $devpath dell-touchpad"
+ENV{DMI_VENDOR}=="Dell Inc.", ATTR{[dmi/id]product_name}=="XPS*", RUN+="keyboard-force-release.sh $devpath dell-xps"
+
+ENV{DMI_VENDOR}=="FUJITSU SIEMENS", ATTR{[dmi/id]product_name}=="AMILO*", RUN+="keyboard-force-release.sh $devpath common-volume-keys"
+
+ENV{DMI_VENDOR}=="FOXCONN", ATTR{[dmi/id]product_name}=="QBOOK", RUN+="keyboard-force-release.sh $devpath common-volume-keys"
+
+ENV{DMI_VENDOR}=="MTC", ATTR{[dmi/id]product_version}=="A0", RUN+="keyboard-force-release.sh $devpath common-volume-keys"
+
+ENV{DMI_VENDOR}=="Mio Technology", ATTR{[dmi/id]product_name}=="N890", RUN+="keyboard-force-release.sh $devpath common-volume-keys"
+
+ENV{DMI_VENDOR}=="PEGATRON CORP.", ATTR{[dmi/id]product_name}=="Spring Peak", RUN+="keyboard-force-release.sh $devpath common-volume-keys"
+
+ENV{DMI_VENDOR}=="TOSHIBA", ATTR{[dmi/id]product_name}=="Satellite [uU]300*|Satellite Pro [uU]300*|Satellite [uU]305*|SATELLITE [uU]500*", RUN+="keyboard-force-release.sh $devpath common-volume-keys"
+
+ENV{DMI_VENDOR}=="Viooo Corporation", ATTR{[dmi/id]product_name}=="PT17", RUN+="keyboard-force-release.sh $devpath common-volume-keys"
+
+# These are all the HP laptops that setup a touchpad toggle key
+ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*[pP][aA][vV][iI][lL][iI][oO][nN]*", RUN+="keyboard-force-release.sh $devpath hp-other"
+ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*[tT][xX]2*", RUN+="keyboard-force-release.sh $devpath hp-other"
+ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*2510p*|*2530p*|HP G60 Notebook PC", RUN+="keyboard-force-release.sh $devpath hp-other"
+
+ENV{DMI_VENDOR}=="Zepto", ATTR{[dmi/id]product_name}=="Znote 6615WD", RUN+="keyboard-force-release.sh $devpath common-volume-keys"
+
+ENV{DMI_VENDOR}=="Zepto", ATTR{[dmi/id]product_name}=="Znote", ATTR{[dmi/id]product_version}=="6625WD", RUN+="keyboard-force-release.sh $devpath common-volume-keys"
+
+ENV{DMI_VENDOR}=="HANNspree", ATTR{[dmi/id]product_name}=="SN10E100", RUN+="keyboard-force-release.sh $devpath common-volume-keys"
+
+ENV{DMI_VENDOR}=="GIGABYTE", ATTR{[dmi/id]product_name}=="i1520M", RUN+="keyboard-force-release.sh $devpath common-volume-keys"
+
+ENV{DMI_VENDOR}=="BenQ", ATTR{[dmi/id]product_name}=="*nScreen*", RUN+="keyboard-force-release.sh $devpath common-volume-keys"
+
+LABEL="force_release_end"
diff --git a/src/udev/keymap/95-keymap.rules b/src/udev/keymap/95-keymap.rules
new file mode 100644
index 0000000000..506310ef70
--- /dev/null
+++ b/src/udev/keymap/95-keymap.rules
@@ -0,0 +1,175 @@
+# Set model specific hotkey keycodes.
+#
+# Key map overrides can be specified by either giving scancode/keyname pairs
+# directly as keymap arguments (if there are just one or two to change), or as
+# a file name (in /usr/lib/udev/keymaps), which has to contain scancode/keyname
+# pairs.
+
+ACTION=="remove", GOTO="keyboard_end"
+KERNEL!="event*", GOTO="keyboard_end"
+ENV{ID_INPUT_KEY}=="", GOTO="keyboard_end"
+SUBSYSTEMS=="bluetooth", GOTO="keyboard_end"
+
+SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id"
+SUBSYSTEMS=="usb", GOTO="keyboard_usbcheck"
+GOTO="keyboard_modulecheck"
+
+#
+# The following are external USB keyboards
+#
+
+LABEL="keyboard_usbcheck"
+
+ENV{ID_VENDOR}=="Genius", ENV{ID_MODEL_ID}=="0708", ENV{ID_USB_INTERFACE_NUM}=="01", RUN+="keymap $name genius-slimstar-320"
+ENV{ID_VENDOR}=="Logitech*", ATTRS{name}=="Logitech USB Multimedia Keyboard", RUN+="keymap $name logitech-wave"
+ENV{ID_VENDOR}=="Logitech*", ATTRS{name}=="Logitech USB Receiver", RUN+="keymap $name logitech-wave-cordless"
+# Logitech Cordless Wave Pro looks slightly weird; some hotkeys are coming through the mouse interface
+ENV{ID_VENDOR_ID}=="046d", ENV{ID_MODEL_ID}=="c52[9b]", ATTRS{name}=="Logitech USB Receiver", RUN+="keymap $name logitech-wave-pro-cordless"
+
+ENV{ID_VENDOR}=="Lite-On_Technology_Corp*", ATTRS{name}=="Lite-On Technology Corp. ThinkPad USB Keyboard with TrackPoint", RUN+="keymap $name lenovo-thinkpad-usb-keyboard-trackpoint"
+ENV{ID_VENDOR_ID}=="04b3", ENV{ID_MODEL_ID}=="301[89]", RUN+="keymap $name ibm-thinkpad-usb-keyboard-trackpoint"
+
+ENV{ID_VENDOR}=="Microsoft", ENV{ID_MODEL_ID}=="00db", RUN+="keymap $name 0xc022d zoomin 0xc022e zoomout"
+
+GOTO="keyboard_end"
+
+#
+# The following are exposed as separate input devices with low key codes, thus
+# we need to check their input device product name
+#
+
+LABEL="keyboard_modulecheck"
+
+ENV{DMI_VENDOR}="$attr{[dmi/id]sys_vendor}"
+ENV{DMI_VENDOR}=="", GOTO="keyboard_end"
+
+ENV{DMI_VENDOR}=="LENOVO*", KERNELS=="input*", ATTRS{name}=="ThinkPad Extra Buttons", RUN+="keymap $name module-lenovo"
+ENV{DMI_VENDOR}=="LENOVO*", KERNELS=="input*", ATTRS{name}=="Lenovo ThinkPad SL Series extra buttons", RUN+="keymap $name 0x0E bluetooth"
+ENV{DMI_VENDOR}=="LENOVO*", KERNELS=="input*", ATTRS{name}=="Ideapad extra buttons", RUN+="keymap $name 0x42 f23 0x43 f22"
+
+ENV{DMI_VENDOR}=="ASUS*", KERNELS=="input*", ATTRS{name}=="Asus Extra Buttons", ATTR{[dmi/id]product_name}=="W3J", RUN+="keymap $name module-asus-w3j"
+ENV{DMI_VENDOR}=="ASUS*", KERNELS=="input*", ATTRS{name}=="Eee PC WMI hotkeys|Asus Laptop Support|Asus*WMI*", RUN+="keymap $name 0x6B f21"
+ENV{DMI_VENDOR}=="ASUS*", KERNELS=="input*", ATTRS{name}=="Eee PC Hotkey Driver", RUN+="keymap $name 0x37 f21"
+
+ENV{DMI_VENDOR}=="IBM*", KERNELS=="input*", ATTRS{name}=="ThinkPad Extra Buttons", RUN+="keymap $name module-ibm"
+ENV{DMI_VENDOR}=="Sony*", KERNELS=="input*", ATTRS{name}=="Sony Vaio Keys", RUN+="keymap $name module-sony"
+ENV{DMI_VENDOR}=="Acer*", KERNELS=="input*", ATTRS{name}=="Acer WMI hotkeys", RUN+="keymap $name 0x82 f21"
+ENV{DMI_VENDOR}=="MICRO-STAR*|Micro-Star*", KERNELS=="input*", ATTRS{name}=="MSI Laptop hotkeys", RUN+="keymap $name 0x213 f22 0x214 f23"
+
+# Older Vaios have some different keys
+ENV{DMI_VENDOR}=="Sony*", ATTR{[dmi/id]product_name}=="*PCG-C1*|*PCG-K25*|*PCG-F1*|*PCG-F2*|*PCG-F3*|*PCG-F4*|*PCG-F5*|*PCG-F6*|*PCG-FX*|*PCG-FRV*|*PCG-GR*|*PCG-TR*|*PCG-NV*|*PCG-Z*|*VGN-S360*", ATTRS{name}=="Sony Vaio Keys", RUN+="keymap $name module-sony-old"
+
+# Some Sony VGN/VPC models have yet another one
+ENV{DMI_VENDOR}=="Sony*", ATTR{[dmi/id]product_name}=="VGN-AR71*|VGN-FW*|VGN-Z21*", ATTRS{name}=="Sony Vaio Keys", RUN+="keymap $name module-sony-vgn"
+ENV{DMI_VENDOR}=="Sony*", ATTR{[dmi/id]product_name}=="VPC*", ATTRS{name}=="Sony Vaio Keys", RUN+="keymap $name module-sony-vpc"
+
+
+#
+# The following rules belong to standard i8042 AT keyboard with high key codes.
+#
+
+DRIVERS=="atkbd", GOTO="keyboard_vendorcheck"
+GOTO="keyboard_end"
+
+LABEL="keyboard_vendorcheck"
+
+ENV{DMI_VENDOR}=="Dell*", RUN+="keymap $name dell"
+ENV{DMI_VENDOR}=="Dell*", ATTR{[dmi/id]product_name}=="Inspiron 910|Inspiron 1010|Inspiron 1011|Inspiron 1012|Inspiron 1110|Inspiron 1210", RUN+="keymap $name 0x84 wlan"
+ENV{DMI_VENDOR}=="Dell*", ATTR{[dmi/id]product_name}=="Latitude XT2", RUN+="keymap $name dell-latitude-xt2"
+
+ENV{DMI_VENDOR}=="Compaq*", ATTR{[dmi/id]product_name}=="*E500*|*Evo N*", RUN+="keymap $name compaq-e_evo"
+
+ENV{DMI_VENDOR}=="LENOVO*", ATTR{[dmi/id]product_version}=="*3000*", RUN+="keymap $name lenovo-3000"
+ENV{DMI_VENDOR}=="LENOVO*", ATTR{[dmi/id]product_version}=="ThinkPad X6*", ATTR{[dmi/id]product_version}=="* Tablet", RUN+="keymap $name lenovo-thinkpad_x6_tablet"
+ENV{DMI_VENDOR}=="LENOVO*", ATTR{[dmi/id]product_version}=="ThinkPad X2* Tablet*", ATTR{[dmi/id]product_version}=="* Tablet", RUN+="keymap $name lenovo-thinkpad_x200_tablet"
+ENV{DMI_VENDOR}=="LENOVO*", ATTR{[dmi/id]product_version}=="*IdeaPad*", RUN+="keymap $name lenovo-ideapad"
+ENV{DMI_VENDOR}=="LENOVO*", ATTR{[dmi/id]product_name}=="S10-*", RUN+="keymap $name lenovo-ideapad"
+ENV{DMI_VENDOR}=="LENOVO", ATTR{[dmi/id]product_version}=="*IdeaPad Y550*", RUN+="keymap $name 0x95 media 0xA3 play"
+ENV{DMI_VENDOR}=="LENOVO", ATTR{[dmi/id]product_version}=="*Lenovo V480*", RUN+="keymap $name 0xf1 f21"
+
+ENV{DMI_VENDOR}=="Hewlett-Packard*", RUN+="keymap $name hewlett-packard"
+ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*[tT][aA][bB][lL][eE][tT]*", RUN+="keymap $name hewlett-packard-tablet"
+ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*[pP][aA][vV][iI][lL][iI][oO][nN]*", RUN+="keymap $name hewlett-packard-pavilion"
+ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*Compaq*|*EliteBook*|*2230s*", RUN+="keymap $name hewlett-packard-compaq_elitebook"
+ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*Presario*CQ*", RUN+="keymap $name 0xD8 f21 0xD9 f21"
+ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*2510p*|*2530p*|HP G60 Notebook PC", RUN+="keymap $name hewlett-packard-2510p_2530p"
+ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*[tT][xX]2*", RUN+="keymap $name hewlett-packard-tx2"
+ENV{DMI_VENDOR}=="Hewlett-Packard", ATTR{[dmi/id]product_name}=="Presario 2100*", RUN+="keymap $name hewlett-packard-presario-2100"
+ENV{DMI_VENDOR}=="Hewlett-Packard", ATTR{[dmi/id]product_name}=="HP G62 Notebook PC", RUN+="keymap $name 0xB2 www"
+ENV{DMI_VENDOR}=="Hewlett-Packard", ATTR{[dmi/id]product_name}=="HP ProBook*", RUN+="keymap $name 0xF8 rfkill"
+ENV{DMI_VENDOR}=="Hewlett-Packard", ATTR{[dmi/id]product_name}=="HP EliteBook 8440p", RUN+="keymap $name hewlett-packard_elitebook-8440p"
+# HP Pavillion dv6315ea has empty DMI_VENDOR
+ATTR{[dmi/id]board_vendor}=="Quanta", ATTR{[dmi/id]board_name}=="30B7", ATTR{[dmi/id]board_version}=="65.2B", RUN+="keymap $name 0x88 media" # "quick play
+
+# Gateway clone of Acer Aspire One AOA110/AOA150
+ENV{DMI_VENDOR}=="Gateway*", ATTR{[dmi/id]product_name}=="*AOA1*", RUN+="keymap $name acer"
+
+ENV{DMI_VENDOR}=="Acer*", RUN+="keymap $name acer"
+ENV{DMI_VENDOR}=="Acer*", ATTR{[dmi/id]product_name}=="Extensa*", ATTR{[dmi/id]product_name}=="*5210*|*5220*|*5610*|*5620*|*5720*", RUN+="keymap $name 0xEE screenlock"
+ENV{DMI_VENDOR}=="Acer*", ATTR{[dmi/id]product_name}=="TravelMate*C3[01]0*", RUN+="keymap $name acer-travelmate_c300"
+ENV{DMI_VENDOR}=="Acer*", ATTR{[dmi/id]product_name}=="TravelMate*6292*|TravelMate*8471*|TravelMate*4720*|TravelMate*7720*|Aspire 1810T*|AO751h|AO531h", RUN+="keymap $name 0xD9 bluetooth"
+ENV{DMI_VENDOR}=="Acer*", ATTR{[dmi/id]product_name}=="TravelMate*4720*", RUN+="keymap $name 0xB2 www 0xEE screenlock"
+ENV{DMI_VENDOR}=="Acer*", ATTR{[dmi/id]product_name}=="TravelMate 6593|Aspire 1640", RUN+="keymap $name 0xB2 www 0xEE screenlock"
+ENV{DMI_VENDOR}=="Acer*", ATTR{[dmi/id]product_name}=="Aspire 6920", RUN+="keymap $name acer-aspire_6920"
+ENV{DMI_VENDOR}=="Acer*", ATTR{[dmi/id]product_name}=="Aspire 5920G", RUN+="keymap $name acer-aspire_5920g"
+ENV{DMI_VENDOR}=="Acer*", ATTR{[dmi/id]product_name}=="Aspire 5720*", RUN+="keymap $name acer-aspire_5720"
+ENV{DMI_VENDOR}=="Acer*", ATTR{[dmi/id]product_name}=="Aspire 8930", RUN+="keymap $name acer-aspire_8930"
+ENV{DMI_VENDOR}=="Acer*", ATTR{[dmi/id]product_serial}=="ZG8*", RUN+="keymap $name acer-aspire_5720"
+
+ENV{DMI_VENDOR}=="*BenQ*", ATTR{[dmi/id]product_name}=="*Joybook R22*", RUN+="keymap $name 0x6E wlan"
+
+ENV{DMI_VENDOR}=="FUJITSU*", ATTR{[dmi/id]product_name}=="*AMILO Pro V3205*", RUN+="keymap $name fujitsu-amilo_pro_v3205"
+ENV{DMI_VENDOR}=="FUJITSU*", ATTR{[dmi/id]product_name}=="*AMILO Pa 2548*", RUN+="keymap $name fujitsu-amilo_pa_2548"
+ENV{DMI_VENDOR}=="FUJITSU*", ATTR{[dmi/id]product_name}=="*ESPRIMO Mobile V5*", RUN+="keymap $name fujitsu-esprimo_mobile_v5"
+ENV{DMI_VENDOR}=="FUJITSU*", ATTR{[dmi/id]product_name}=="*ESPRIMO Mobile V6*", RUN+="keymap $name fujitsu-esprimo_mobile_v6"
+ENV{DMI_VENDOR}=="FUJITSU*", ATTR{[dmi/id]product_name}=="*AMILO Pro Edition V3505*", RUN+="keymap $name fujitsu-amilo_pro_edition_v3505"
+ENV{DMI_VENDOR}=="FUJITSU*", ATTR{[dmi/id]product_name}=="*Amilo Si 1520*", RUN+="keymap $name fujitsu-amilo_si_1520"
+ENV{DMI_VENDOR}=="FUJITSU*", ATTR{[dmi/id]product_name}=="AMILO*M*", RUN+="keymap $name 0x97 prog2 0x9F prog1"
+ENV{DMI_VENDOR}=="FUJITSU*", ATTR{[dmi/id]product_name}=="Amilo Li 1718", RUN+="keymap $name 0xD6 wlan"
+ENV{DMI_VENDOR}=="FUJITSU*", ATTR{[dmi/id]product_name}=="AMILO Li 2732", RUN+="keymap $name fujitsu-amilo_li_2732"
+
+ENV{DMI_VENDOR}=="LG*", ATTR{[dmi/id]product_name}=="*X110*", RUN+="keymap $name lg-x110"
+
+ENV{DMI_VENDOR}=="MEDION*", ATTR{[dmi/id]product_name}=="*FID2060*", RUN+="keymap $name medion-fid2060"
+ENV{DMI_VENDOR}=="MEDIONNB", ATTR{[dmi/id]product_name}=="A555*", RUN+="keymap $name medionnb-a555"
+
+ENV{DMI_VENDOR}=="MICRO-STAR*|Micro-Star*", RUN+="keymap $name micro-star"
+
+# some MSI models generate ACPI/input events on the LNXVIDEO input devices,
+# plus some extra synthesized ones on atkbd as an echo of actually changing the
+# brightness; so ignore those atkbd ones, to avoid loops
+ENV{DMI_VENDOR}=="MICRO-STAR*", ATTR{[dmi/id]product_name}=="*U-100*|*U100*|*N033", RUN+="keymap $name 0xF7 reserved 0xF8 reserved"
+
+ENV{DMI_VENDOR}=="INVENTEC", ATTR{[dmi/id]product_name}=="SYMPHONY 6.0/7.0", RUN+="keymap $name inventec-symphony_6.0_7.0"
+
+ENV{DMI_VENDOR}=="MAXDATA", ATTR{[dmi/id]product_name}=="Pro 7000*", RUN+="keymap $name maxdata-pro_7000"
+
+ENV{DMI_VENDOR}=="[sS][aA][mM][sS][uU][nN][gG]*", RUN+="keymap $name samsung-other"
+ENV{DMI_VENDOR}=="[sS][aA][mM][sS][uU][nN][gG]*", ATTR{[dmi/id]product_name}=="*SX20S*", RUN+="keymap $name samsung-sx20s"
+ENV{DMI_VENDOR}=="[sS][aA][mM][sS][uU][nN][gG]*", ATTR{[dmi/id]product_name}=="SQ1US", RUN+="keymap $name samsung-sq1us"
+ENV{DMI_VENDOR}=="[sS][aA][mM][sS][uU][nN][gG]*", ATTR{[dmi/id]product_name}=="*700Z*", RUN+="keymap $name 0xBA ejectcd 0x96 keyboardbrightnessup 0x97 keyboardbrightnessdown"
+ENV{DMI_VENDOR}=="[sS][aA][mM][sS][uU][nN][gG]*", ATTR{[dmi/id]product_name}=="*90X3A*", RUN+="keymap $name samsung-90x3a"
+
+ENV{DMI_VENDOR}=="TOSHIBA", ATTR{[dmi/id]product_name}=="SATELLITE A100", RUN+="keymap $name toshiba-satellite_a100"
+ENV{DMI_VENDOR}=="TOSHIBA", ATTR{[dmi/id]product_name}=="Satellite A110", RUN+="keymap $name toshiba-satellite_a110"
+ENV{DMI_VENDOR}=="TOSHIBA", ATTR{[dmi/id]product_name}=="Satellite M30X", RUN+="keymap $name toshiba-satellite_m30x"
+
+ENV{DMI_VENDOR}=="OQO Inc.*", ATTR{[dmi/id]product_name}=="OQO Model 2*", RUN+="keymap $name oqo-model2"
+
+ENV{DMI_VENDOR}=="ONKYO CORPORATION", ATTR{[dmi/id]product_name}=="ONKYOPC", RUN+="keymap $name onkyo"
+
+ENV{DMI_VENDOR}=="ASUS", RUN+="keymap $name asus"
+
+ENV{DMI_VENDOR}=="VIA", ATTR{[dmi/id]product_name}=="K8N800", ATTR{[dmi/id]product_version}=="VT8204B", RUN+="keymap $name 0x81 prog1"
+
+ENV{DMI_VENDOR}=="Zepto", ATTR{[dmi/id]product_name}=="Znote", ATTR{[dmi/id]product_version}=="62*|63*", RUN+="keymap $name zepto-znote"
+
+ENV{DMI_VENDOR}=="Everex", ATTR{[dmi/id]product_name}=="XT5000*", RUN+="keymap $name everex-xt5000"
+
+ENV{DMI_VENDOR}=="COMPAL", ATTR{[dmi/id]product_name}=="HEL80I", RUN+="keymap $name 0x84 wlan"
+
+ENV{DMI_VENDOR}=="OLPC", ATTR{[dmi/id]product_name}=="XO", RUN+="keymap $name olpc-xo"
+
+ENV{DMI_VENDOR}=="Alienware*", ATTR{[dmi/id]product_name}=="M14xR1", RUN+="keymap $name 0x8A ejectcd"
+
+LABEL="keyboard_end"
diff --git a/src/udev/keymap/README.keymap.txt b/src/udev/keymap/README.keymap.txt
new file mode 100644
index 0000000000..2cf2a4e88c
--- /dev/null
+++ b/src/udev/keymap/README.keymap.txt
@@ -0,0 +1,97 @@
+= The udev keymap tool =
+
+== Introduction ==
+
+This udev extension configures computer model specific key mappings. This is
+particularly necessary for the non-standard extra keys found on many laptops,
+such as "brightness up", "next song", "www browser", or "suspend". Often these
+are accessed with the Fn key.
+
+Every key produces a "scan code", which is highly vendor/model specific for the
+nonstandard keys. This tool maintains mappings for these scan codes to standard
+"key codes", which denote the "meaning" of the key. The key codes are defined
+in /usr/include/linux/input.h.
+
+If some of your keys on your keyboard are not working at all, or produce the
+wrong effect, then a very likely cause of this is that the scan code -> key
+code mapping is incorrect on your computer.
+
+== Structure ==
+
+udev-keymap consists of the following parts:
+
+ keymaps/*:: mappings of scan codes to key code names
+
+ 95-keymap.rules:: udev rules for mapping system vendor/product names and
+ input module names to one of the keymaps above
+
+ keymap:: manipulate an evdev input device:
+ * write a key map file into a device (used by udev rules)
+ * dump current scan → key code mapping
+ * interactively display scan and key codes of pressed keys
+
+ findkeyboards:: display evdev input devices which belong to actual keyboards,
+ i. e. those suitable for the keymap program
+
+== Fixing broken keys ==
+
+In order to make a broken key work on your system and send it back to upstream
+for inclusion you need to do the following steps:
+
+ 1. Find the keyboard device.
+
+ Run /usr/lib/udev/findkeyboards. This should always give you an "AT
+ keyboard" and possibly a "module". Some laptops (notably Thinkpads, Sonys, and
+ Acers) have multimedia/function keys on a separate input device instead of the
+ primary keyboard. The keyboard device should have a name like "input/event3".
+ In the following commands, the name will be written as "input/eventX" (replace
+ X with the appropriate number).
+
+ 2. Find broken scan codes:
+
+ sudo /usr/lib/udev/keymap -i input/eventX
+
+ Press all multimedia/function keys and check if the key name that gets printed
+ out is plausible. If it is unknown or wrong, write down the scan code (looks
+ like "0x1E") and the intended functionality of this key. Look in
+ /usr/include/linux/input.h for an available KEY_XXXXX constant which most
+ closely approximates this functionality and write it down as the new key code.
+
+ For example, you might press a key labeled "web browser" which currently
+ produces "unknown". Note down this:
+
+ 0x1E www # Fn+F2 web browser
+
+ Repeat that for all other keys. Write the resulting list into a file. Look at
+ /usr/lib/udev/keymaps/ for existing key map files and make sure that you use the
+ same structure.
+
+ If the key only ever works once and then your keyboard (or the entire desktop)
+ gets stuck for a long time, then it is likely that the BIOS fails to send a
+ corresponding "key release" event after the key press event. Please note down
+ this case as well, as it can be worked around in
+ /usr/lib/udev/keymaps/95-keyboard-force-release.rules .
+
+ 3. Find out your system vendor and product:
+
+ cat /sys/class/dmi/id/sys_vendor
+ cat /sys/class/dmi/id/product_name
+
+ 4. Generate a device dump with "udevadm info --export-db > /tmp/udev-db.txt".
+
+ 6. Send the system vendor/product names, the key mapping from step 2,
+ and /tmp/udev-db.txt from step 4 to the linux-hotplug@vger.kernel.org mailing
+ list, so that they can be included in the next release.
+
+For local testing, copy your map file to /usr/lib/udev/keymaps/ with an appropriate
+name, and add an appropriate udev rule to /usr/lib/udev/rules.d/95-keymap.rules:
+
+ * If you selected an "AT keyboard", add the rule to the section after
+ 'LABEL="keyboard_vendorcheck"'.
+
+ * If you selected a "module", add the rule to the top section where the
+ "ThinkPad Extra Buttons" are.
+
+== Author ==
+
+keymap is written and maintained by Martin Pitt <martin.pitt@ubuntu.com>.
diff --git a/src/udev/keymap/check-keymaps.sh b/src/udev/keymap/check-keymaps.sh
new file mode 100755
index 0000000000..c4572745e0
--- /dev/null
+++ b/src/udev/keymap/check-keymaps.sh
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+# check that all key names in keymaps/* are known in <linux/input.h>
+# and that all key maps listed in the rules are valid and present in
+# Makefile.am
+SRCDIR=${1:-.}
+KEYLIST=${2:-src/udev/keymap/keys.txt}
+KEYMAPS_DIR=$SRCDIR/keymaps
+RULES=$SRCDIR/src/udev/keymap/95-keymap.rules
+
+[ -e "$KEYLIST" ] || {
+ echo "need $KEYLIST please build first" >&2
+ exit 1
+}
+
+missing=$(join -v 2 <(awk '{print tolower(substr($1,5))}' $KEYLIST | sort -u) \
+ <(grep -hv '^#' ${KEYMAPS_DIR}/*| awk '{print $2}' | sort -u))
+[ -z "$missing" ] || {
+ echo "ERROR: unknown key names in keymaps/*:" >&2
+ echo "$missing" >&2
+ exit 1
+}
+
+# check that all maps referred to in $RULES exist
+maps=$(sed -rn '/keymap \$name/ { s/^.*\$name ([^"[:space:]]+).*$/\1/; p }' $RULES)
+for m in $maps; do
+ # ignore inline mappings
+ [ "$m" = "${m#0x}" ] || continue
+
+ [ -e ${KEYMAPS_DIR}/$m ] || {
+ echo "ERROR: unknown map name in $RULES: $m" >&2
+ exit 1
+ }
+ grep -q "keymaps/$m\>" $SRCDIR/Makefile.am || {
+ echo "ERROR: map file $m is not added to Makefile.am" >&2
+ exit 1
+ }
+done
diff --git a/src/udev/keymap/findkeyboards b/src/udev/keymap/findkeyboards
new file mode 100755
index 0000000000..9ce27429b2
--- /dev/null
+++ b/src/udev/keymap/findkeyboards
@@ -0,0 +1,68 @@
+#!/bin/sh -e
+# Find "real" keyboard devices and print their device path.
+# Author: Martin Pitt <martin.pitt@ubuntu.com>
+#
+# Copyright (C) 2009, Canonical Ltd.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+
+# returns OK if $1 contains $2
+strstr() {
+ [ "${1#*$2*}" != "$1" ]
+}
+
+# returns OK if $1 contains $2 at the beginning
+str_starts() {
+ [ "${1#$2*}" != "$1" ]
+}
+
+str_line_starts() {
+ while read a; do str_starts "$a" "$1" && return 0;done
+ return 1;
+}
+
+# print a list of input devices which are keyboard-like
+keyboard_devices() {
+ # standard AT keyboard
+ for dev in `udevadm trigger --dry-run --verbose --property-match=ID_INPUT_KEYBOARD=1`; do
+ walk=`udevadm info --attribute-walk --path=$dev`
+ env=`udevadm info --query=env --path=$dev`
+ # filter out non-event devices, such as the parent input devices which have no devnode
+ if ! echo "$env" | str_line_starts 'DEVNAME='; then
+ continue
+ fi
+ if strstr "$walk" 'DRIVERS=="atkbd"'; then
+ echo -n 'AT keyboard: '
+ elif echo "$env" | str_line_starts 'ID_USB_DRIVER=usbhid'; then
+ echo -n 'USB keyboard: '
+ else
+ echo -n 'Unknown type: '
+ fi
+ udevadm info --query=name --path=$dev
+ done
+
+ # modules
+ module=$(udevadm trigger --verbose --dry-run --subsystem-match=input --attr-match=name='*Extra Buttons')
+ module="$module
+ $(udevadm trigger --verbose --dry-run --subsystem-match=input --attr-match=name='*extra buttons')"
+ module="$module
+ $(udevadm trigger --verbose --dry-run --subsystem-match=input --attr-match=name='Sony Vaio Keys')"
+ for m in $module; do
+ for evdev in $m/event*/dev; do
+ if [ -e "$evdev" ]; then
+ echo -n 'module: '
+ udevadm info --query=name --path=${evdev%%/dev}
+ fi
+ done
+ done
+}
+
+keyboard_devices
diff --git a/src/udev/keymap/keyboard-force-release.sh.in b/src/udev/keymap/keyboard-force-release.sh.in
new file mode 100755
index 0000000000..b82674840f
--- /dev/null
+++ b/src/udev/keymap/keyboard-force-release.sh.in
@@ -0,0 +1,22 @@
+#!/bin/sh -e
+# read list of scancodes, convert hex to decimal and
+# append to the atkbd force_release sysfs attribute
+# $1 sysfs devpath for serioX
+# $2 file with scancode list (hex or dec)
+
+case "$2" in
+ /*) scf="$2" ;;
+ *) scf="@udevlibexecdir@/keymaps/force-release/$2" ;;
+esac
+
+read attr <"/sys/$1/force_release"
+while read scancode dummy; do
+ case "$scancode" in
+ \#*) ;;
+ *)
+ scancode=$(($scancode))
+ attr="$attr${attr:+,}$scancode"
+ ;;
+ esac
+done <"$scf"
+echo "$attr" >"/sys/$1/force_release"
diff --git a/src/udev/keymap/keymap.c b/src/udev/keymap/keymap.c
new file mode 100644
index 0000000000..939407fd0b
--- /dev/null
+++ b/src/udev/keymap/keymap.c
@@ -0,0 +1,453 @@
+/*
+ * keymap - dump keymap of an evdev device or set a new keymap from a file
+ *
+ * Based on keyfuzz by Lennart Poettering <mzqrovna@0pointer.net>
+ * Adapted for udev-extras by Martin Pitt <martin.pitt@ubuntu.com>
+ *
+ * Copyright (C) 2006, Lennart Poettering
+ * Copyright (C) 2009, Canonical Ltd.
+ *
+ * keymap is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * keymap is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with keymap; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <linux/limits.h>
+#include <linux/input.h>
+
+const struct key* lookup_key (const char *str, unsigned int len);
+
+#include "keys-from-name.h"
+#include "keys-to-name.h"
+#include "macro.h"
+#include "util.h"
+
+#define MAX_SCANCODES 1024
+
+static int evdev_open(const char *dev)
+{
+ int fd;
+ char fn[PATH_MAX];
+
+ if (!startswith(dev, "/dev")) {
+ snprintf(fn, sizeof(fn), "/dev/%s", dev);
+ dev = fn;
+ }
+
+ if ((fd = open(dev, O_RDWR)) < 0) {
+ fprintf(stderr, "error open('%s'): %m\n", dev);
+ return -1;
+ }
+ return fd;
+}
+
+static int evdev_get_keycode(int fd, unsigned scancode, int e)
+{
+ unsigned codes[2];
+
+ codes[0] = scancode;
+ if (ioctl(fd, EVIOCGKEYCODE, codes) < 0) {
+ if (e && errno == EINVAL) {
+ return -2;
+ } else {
+ fprintf(stderr, "EVIOCGKEYCODE for scan code 0x%x: %m\n", scancode);
+ return -1;
+ }
+ }
+ return codes[1];
+}
+
+static int evdev_set_keycode(int fd, unsigned scancode, int keycode)
+{
+ unsigned codes[2];
+
+ codes[0] = scancode;
+ codes[1] = (unsigned) keycode;
+
+ if (ioctl(fd, EVIOCSKEYCODE, codes) < 0) {
+ fprintf(stderr, "EVIOCSKEYCODE: %m\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int evdev_driver_version(int fd, char *v, size_t l)
+{
+ int version;
+
+ if (ioctl(fd, EVIOCGVERSION, &version)) {
+ fprintf(stderr, "EVIOCGVERSION: %m\n");
+ return -1;
+ }
+
+ snprintf(v, l, "%i.%i.%i.", version >> 16, (version >> 8) & 0xff, version & 0xff);
+ return 0;
+}
+
+static int evdev_device_name(int fd, char *n, size_t l)
+{
+ if (ioctl(fd, EVIOCGNAME(l), n) < 0) {
+ fprintf(stderr, "EVIOCGNAME: %m\n");
+ return -1;
+ }
+ return 0;
+}
+
+/* Return a lower-case string with KEY_ prefix removed */
+static const char* format_keyname(const char* key) {
+ static char result[101];
+ const char* s;
+ int len;
+
+ for (s = key+4, len = 0; *s && len < 100; ++len, ++s)
+ result[len] = tolower(*s);
+ result[len] = '\0';
+ return result;
+}
+
+static int dump_table(int fd) {
+ char version[256], name[256];
+ unsigned scancode;
+ int r = -1;
+
+ if (evdev_driver_version(fd, version, sizeof(version)) < 0)
+ goto fail;
+
+ if (evdev_device_name(fd, name, sizeof(name)) < 0)
+ goto fail;
+
+ printf("### evdev %s, driver '%s'\n", version, name);
+
+ r = 0;
+ for (scancode = 0; scancode < MAX_SCANCODES; scancode++) {
+ int keycode;
+
+ if ((keycode = evdev_get_keycode(fd, scancode, 1)) < 0) {
+ if (keycode == -2)
+ continue;
+ r = -1;
+ break;
+ }
+
+ if (keycode < KEY_MAX && key_names[keycode])
+ printf("0x%03x %s\n", scancode, format_keyname(key_names[keycode]));
+ else
+ printf("0x%03x 0x%03x\n", scancode, keycode);
+ }
+fail:
+ return r;
+}
+
+static void set_key(int fd, const char* scancode_str, const char* keyname)
+{
+ unsigned scancode;
+ char *endptr;
+ char t[105] = "KEY_UNKNOWN";
+ const struct key *k;
+
+ scancode = (unsigned) strtol(scancode_str, &endptr, 0);
+ if (*endptr != '\0') {
+ fprintf(stderr, "ERROR: Invalid scancode\n");
+ exit(1);
+ }
+
+ snprintf(t, sizeof(t), "KEY_%s", keyname);
+
+ if (!(k = lookup_key(t, strlen(t)))) {
+ fprintf(stderr, "ERROR: Unknown key name '%s'\n", keyname);
+ exit(1);
+ }
+
+ if (evdev_set_keycode(fd, scancode, k->id) < 0)
+ fprintf(stderr, "setting scancode 0x%2X to key code %i failed\n",
+ scancode, k->id);
+ else
+ printf("setting scancode 0x%2X to key code %i\n",
+ scancode, k->id);
+}
+
+static int merge_table(int fd, FILE *f) {
+ int r = 0;
+ int line = 0;
+
+ while (!feof(f)) {
+ char s[256], *p;
+ unsigned scancode;
+ int new_keycode, old_keycode;
+
+ if (!fgets(s, sizeof(s), f))
+ break;
+
+ line++;
+ p = s+strspn(s, "\t ");
+ if (*p == '#' || *p == '\n')
+ continue;
+
+ if (sscanf(p, "%i %i", &scancode, &new_keycode) != 2) {
+ char t[105] = "KEY_UNKNOWN";
+ const struct key *k;
+
+ if (sscanf(p, "%i %100s", &scancode, t+4) != 2) {
+ fprintf(stderr, "WARNING: Parse failure at line %i, ignoring.\n", line);
+ r = -1;
+ continue;
+ }
+
+ if (!(k = lookup_key(t, strlen(t)))) {
+ fprintf(stderr, "WARNING: Unknown key '%s' at line %i, ignoring.\n", t, line);
+ r = -1;
+ continue;
+ }
+
+ new_keycode = k->id;
+ }
+
+
+ if ((old_keycode = evdev_get_keycode(fd, scancode, 0)) < 0) {
+ r = -1;
+ continue;
+ }
+
+ if (evdev_set_keycode(fd, scancode, new_keycode) < 0) {
+ r = -1;
+ continue;
+ }
+
+ if (new_keycode != old_keycode)
+ fprintf(stderr, "Remapped scancode 0x%02x to 0x%02x (prior: 0x%02x)\n",
+ scancode, new_keycode, old_keycode);
+ }
+
+ fclose(f);
+ return r;
+}
+
+
+/* read one event; return 1 if valid */
+static int read_event(int fd, struct input_event* ev)
+{
+ int ret;
+ ret = read(fd, ev, sizeof(struct input_event));
+
+ if (ret < 0) {
+ perror("read");
+ return 0;
+ }
+ if (ret != sizeof(struct input_event)) {
+ fprintf(stderr, "did not get enough data for event struct, aborting\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+static void print_key(unsigned scancode, uint16_t keycode, int has_scan, int has_key)
+{
+ const char *keyname;
+
+ /* ignore key release events */
+ if (has_key == 1)
+ return;
+
+ if (has_key == 0 && has_scan != 0) {
+ fprintf(stderr, "got scan code event 0x%02X without a key code event\n",
+ scancode);
+ return;
+ }
+
+ if (has_scan != 0)
+ printf("scan code: 0x%02X ", scancode);
+ else
+ printf("(no scan code received) ");
+
+ keyname = key_names[keycode];
+ if (keyname != NULL)
+ printf("key code: %s\n", format_keyname(keyname));
+ else
+ printf("key code: %03X\n", keycode);
+}
+
+static void interactive(int fd)
+{
+ struct input_event ev;
+ unsigned last_scan = 0;
+ uint16_t last_key = 0;
+ int has_scan; /* boolean */
+ int has_key; /* 0: none, 1: release, 2: press */
+
+ /* grab input device */
+ ioctl(fd, EVIOCGRAB, 1);
+ puts("Press ESC to finish, or Control-C if this device is not your primary keyboard");
+
+ has_scan = has_key = 0;
+ while (read_event(fd, &ev)) {
+ /* Drivers usually send the scan code first, then the key code,
+ * then a SYN. Some drivers (like thinkpad_acpi) send the key
+ * code first, and some drivers might not send SYN events, so
+ * keep a robust state machine which can deal with any of those
+ */
+
+ if (ev.type == EV_MSC && ev.code == MSC_SCAN) {
+ if (has_scan) {
+ fputs("driver did not send SYN event in between key events; previous event:\n",
+ stderr);
+ print_key(last_scan, last_key, has_scan, has_key);
+ has_key = 0;
+ }
+
+ last_scan = ev.value;
+ has_scan = 1;
+ /*printf("--- got scan %u; has scan %i key %i\n", last_scan, has_scan, has_key); */
+ }
+ else if (ev.type == EV_KEY) {
+ if (has_key) {
+ fputs("driver did not send SYN event in between key events; previous event:\n",
+ stderr);
+ print_key(last_scan, last_key, has_scan, has_key);
+ has_scan = 0;
+ }
+
+ last_key = ev.code;
+ has_key = 1 + ev.value;
+ /*printf("--- got key %hu; has scan %i key %i\n", last_key, has_scan, has_key);*/
+
+ /* Stop on ESC */
+ if (ev.code == KEY_ESC && ev.value == 0)
+ break;
+ }
+ else if (ev.type == EV_SYN) {
+ /*printf("--- got SYN; has scan %i key %i\n", has_scan, has_key);*/
+ print_key(last_scan, last_key, has_scan, has_key);
+
+ has_scan = has_key = 0;
+ }
+
+ }
+
+ /* release input device */
+ ioctl(fd, EVIOCGRAB, 0);
+}
+
+_noreturn_ static void help(int error)
+{
+ const char* h = "Usage: keymap <event device> [<map file>]\n"
+ " keymap <event device> scancode keyname [...]\n"
+ " keymap -i <event device>\n";
+ if (error) {
+ fputs(h, stderr);
+ exit(2);
+ } else {
+ fputs(h, stdout);
+ exit(0);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "interactive", no_argument, NULL, 'i' },
+ {}
+ };
+ int fd = -1;
+ int opt_interactive = 0;
+ int i;
+
+ while (1) {
+ int option;
+
+ option = getopt_long(argc, argv, "hi", options, NULL);
+ if (option == -1)
+ break;
+
+ switch (option) {
+ case 'h':
+ help(0);
+
+ case 'i':
+ opt_interactive = 1;
+ break;
+ default:
+ return 1;
+ }
+ }
+
+ if (argc < optind+1)
+ help (1);
+
+ if ((fd = evdev_open(argv[optind])) < 0)
+ return 3;
+
+ /* one argument (device): dump or interactive */
+ if (argc == optind+1) {
+ if (opt_interactive)
+ interactive(fd);
+ else
+ dump_table(fd);
+ return 0;
+ }
+
+ /* two arguments (device, mapfile): set map file */
+ if (argc == optind+2) {
+ const char *filearg = argv[optind+1];
+ if (strchr(filearg, '/')) {
+ /* Keymap file argument is a path */
+ FILE *f = fopen(filearg, "re");
+ if (f)
+ merge_table(fd, f);
+ else
+ perror(filearg);
+ } else {
+ /* Keymap file argument is a filename */
+ /* Open override file if present, otherwise default file */
+ char keymap_path[PATH_MAX];
+ FILE *f;
+
+ snprintf(keymap_path, sizeof(keymap_path), "/etc/udev/keymaps/%s", filearg);
+ f = fopen(keymap_path, "re");
+ if (f) {
+ merge_table(fd, f);
+ } else {
+ snprintf(keymap_path, sizeof(keymap_path), UDEVLIBEXECDIR "/keymaps/%s", filearg);
+ f = fopen(keymap_path, "re");
+ if (f)
+ merge_table(fd, f);
+ else
+ perror(keymap_path);
+ }
+ }
+ return 0;
+ }
+
+ /* more arguments (device, scancode/keyname pairs): set keys directly */
+ if ((argc - optind - 1) % 2 == 0) {
+ for (i = optind+1; i < argc; i += 2)
+ set_key(fd, argv[i], argv[i+1]);
+ return 0;
+ }
+
+ /* invalid number of arguments */
+ help(1);
+ return 1; /* not reached */
+}