199ca880aSopenharmony_ci/*** 299ca880aSopenharmony_ci This file is part of systemd. 399ca880aSopenharmony_ci 499ca880aSopenharmony_ci Copyright 2013 Kay Sievers <kay@vrfy.org> 599ca880aSopenharmony_ci 699ca880aSopenharmony_ci systemd is free software; you can redistribute it and/or modify it 799ca880aSopenharmony_ci under the terms of the GNU Lesser General Public License as published by 899ca880aSopenharmony_ci the Free Software Foundation; either version 2.1 of the License, or 999ca880aSopenharmony_ci (at your option) any later version. 1099ca880aSopenharmony_ci 1199ca880aSopenharmony_ci systemd is distributed in the hope that it will be useful, but 1299ca880aSopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 1399ca880aSopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1499ca880aSopenharmony_ci Lesser General Public License for more details. 1599ca880aSopenharmony_ci 1699ca880aSopenharmony_ci You should have received a copy of the GNU Lesser General Public License 1799ca880aSopenharmony_ci along with systemd; If not, see <http://www.gnu.org/licenses/>. 1899ca880aSopenharmony_ci***/ 1999ca880aSopenharmony_ci 2099ca880aSopenharmony_ci#include <stdio.h> 2199ca880aSopenharmony_ci#include <errno.h> 2299ca880aSopenharmony_ci#include <string.h> 2399ca880aSopenharmony_ci#include <stdlib.h> 2499ca880aSopenharmony_ci#include <fcntl.h> 2599ca880aSopenharmony_ci#include <sys/ioctl.h> 2699ca880aSopenharmony_ci#include <linux/limits.h> 2799ca880aSopenharmony_ci#include <linux/input.h> 2899ca880aSopenharmony_ci 2999ca880aSopenharmony_ci#include "udev.h" 3099ca880aSopenharmony_ci 3199ca880aSopenharmony_ci#include "keyboard-keys-from-name.h" 3299ca880aSopenharmony_ci#include "keyboard-keys-to-name.h" 3399ca880aSopenharmony_ci 3499ca880aSopenharmony_cistatic int install_force_release(struct udev_device *dev, const unsigned *release, unsigned release_count) { 3599ca880aSopenharmony_ci struct udev_device *atkbd; 3699ca880aSopenharmony_ci const char *cur; 3799ca880aSopenharmony_ci char codes[4096]; 3899ca880aSopenharmony_ci char *s; 3999ca880aSopenharmony_ci size_t l; 4099ca880aSopenharmony_ci unsigned i; 4199ca880aSopenharmony_ci int ret; 4299ca880aSopenharmony_ci 4399ca880aSopenharmony_ci atkbd = udev_device_get_parent_with_subsystem_devtype(dev, "serio", NULL); 4499ca880aSopenharmony_ci if (!atkbd) 4599ca880aSopenharmony_ci return -ENODEV; 4699ca880aSopenharmony_ci 4799ca880aSopenharmony_ci cur = udev_device_get_sysattr_value(atkbd, "force_release"); 4899ca880aSopenharmony_ci if (!cur) 4999ca880aSopenharmony_ci return -ENODEV; 5099ca880aSopenharmony_ci 5199ca880aSopenharmony_ci s = codes; 5299ca880aSopenharmony_ci l = sizeof(codes); 5399ca880aSopenharmony_ci 5499ca880aSopenharmony_ci /* copy current content */ 5599ca880aSopenharmony_ci l = strpcpy(&s, l, cur); 5699ca880aSopenharmony_ci 5799ca880aSopenharmony_ci /* append new codes */ 5899ca880aSopenharmony_ci for (i = 0; i < release_count; i++) 5999ca880aSopenharmony_ci l = strpcpyf(&s, l, ",%u", release[i]); 6099ca880aSopenharmony_ci 6199ca880aSopenharmony_ci log_debug("keyboard: updating force-release list with '%s'", codes); 6299ca880aSopenharmony_ci ret = udev_device_set_sysattr_value(atkbd, "force_release", codes); 6399ca880aSopenharmony_ci if (ret < 0) 6499ca880aSopenharmony_ci log_error_errno(ret, "Error writing force-release attribute: %m"); 6599ca880aSopenharmony_ci return ret; 6699ca880aSopenharmony_ci} 6799ca880aSopenharmony_ci 6899ca880aSopenharmony_cistatic void map_keycode(int fd, const char *devnode, int scancode, const char *keycode) 6999ca880aSopenharmony_ci{ 7099ca880aSopenharmony_ci struct { 7199ca880aSopenharmony_ci unsigned scan; 7299ca880aSopenharmony_ci unsigned key; 7399ca880aSopenharmony_ci } map; 7499ca880aSopenharmony_ci char *endptr; 7599ca880aSopenharmony_ci const struct key *k; 7699ca880aSopenharmony_ci unsigned keycode_num; 7799ca880aSopenharmony_ci 7899ca880aSopenharmony_ci /* translate identifier to key code */ 7999ca880aSopenharmony_ci k = keyboard_lookup_key(keycode, strlen(keycode)); 8099ca880aSopenharmony_ci if (k) { 8199ca880aSopenharmony_ci keycode_num = k->id; 8299ca880aSopenharmony_ci } else { 8399ca880aSopenharmony_ci /* check if it's a numeric code already */ 8499ca880aSopenharmony_ci keycode_num = strtoul(keycode, &endptr, 0); 8599ca880aSopenharmony_ci if (endptr[0] !='\0') { 8699ca880aSopenharmony_ci log_error("Unknown key identifier '%s'", keycode); 8799ca880aSopenharmony_ci return; 8899ca880aSopenharmony_ci } 8999ca880aSopenharmony_ci } 9099ca880aSopenharmony_ci 9199ca880aSopenharmony_ci map.scan = scancode; 9299ca880aSopenharmony_ci map.key = keycode_num; 9399ca880aSopenharmony_ci 9499ca880aSopenharmony_ci log_debug("keyboard: mapping scan code %d (0x%x) to key code %d (0x%x)", 9599ca880aSopenharmony_ci map.scan, map.scan, map.key, map.key); 9699ca880aSopenharmony_ci 9799ca880aSopenharmony_ci if (ioctl(fd, EVIOCSKEYCODE, &map) < 0) 9899ca880aSopenharmony_ci log_error_errno(errno, "Error calling EVIOCSKEYCODE on device node '%s' (scan code 0x%x, key code %d): %m", devnode, map.scan, map.key); 9999ca880aSopenharmony_ci} 10099ca880aSopenharmony_ci 10199ca880aSopenharmony_cistatic inline char* parse_token(const char *current, int32_t *val_out) { 10299ca880aSopenharmony_ci char *next; 10399ca880aSopenharmony_ci int32_t val; 10499ca880aSopenharmony_ci 10599ca880aSopenharmony_ci if (!current) 10699ca880aSopenharmony_ci return NULL; 10799ca880aSopenharmony_ci 10899ca880aSopenharmony_ci val = strtol(current, &next, 0); 10999ca880aSopenharmony_ci if (*next && *next != ':') 11099ca880aSopenharmony_ci return NULL; 11199ca880aSopenharmony_ci 11299ca880aSopenharmony_ci if (next != current) 11399ca880aSopenharmony_ci *val_out = val; 11499ca880aSopenharmony_ci 11599ca880aSopenharmony_ci if (*next) 11699ca880aSopenharmony_ci next++; 11799ca880aSopenharmony_ci 11899ca880aSopenharmony_ci return next; 11999ca880aSopenharmony_ci} 12099ca880aSopenharmony_ci 12199ca880aSopenharmony_cistatic void override_abs(int fd, const char *devnode, 12299ca880aSopenharmony_ci unsigned evcode, const char *value) { 12399ca880aSopenharmony_ci struct input_absinfo absinfo; 12499ca880aSopenharmony_ci int rc; 12599ca880aSopenharmony_ci char *next; 12699ca880aSopenharmony_ci 12799ca880aSopenharmony_ci rc = ioctl(fd, EVIOCGABS(evcode), &absinfo); 12899ca880aSopenharmony_ci if (rc < 0) { 12999ca880aSopenharmony_ci log_error_errno(errno, "Unable to EVIOCGABS device \"%s\"", devnode); 13099ca880aSopenharmony_ci return; 13199ca880aSopenharmony_ci } 13299ca880aSopenharmony_ci 13399ca880aSopenharmony_ci next = parse_token(value, &absinfo.minimum); 13499ca880aSopenharmony_ci next = parse_token(next, &absinfo.maximum); 13599ca880aSopenharmony_ci next = parse_token(next, &absinfo.resolution); 13699ca880aSopenharmony_ci next = parse_token(next, &absinfo.fuzz); 13799ca880aSopenharmony_ci next = parse_token(next, &absinfo.flat); 13899ca880aSopenharmony_ci if (!next) { 13999ca880aSopenharmony_ci log_error("Unable to parse EV_ABS override '%s' for '%s'", value, devnode); 14099ca880aSopenharmony_ci return; 14199ca880aSopenharmony_ci } 14299ca880aSopenharmony_ci 14399ca880aSopenharmony_ci log_debug("keyboard: %x overridden with %"PRIi32"/%"PRIi32"/%"PRIi32"/%"PRIi32"/%"PRIi32" for \"%s\"", 14499ca880aSopenharmony_ci evcode, 14599ca880aSopenharmony_ci absinfo.minimum, absinfo.maximum, absinfo.resolution, absinfo.fuzz, absinfo.flat, 14699ca880aSopenharmony_ci devnode); 14799ca880aSopenharmony_ci rc = ioctl(fd, EVIOCSABS(evcode), &absinfo); 14899ca880aSopenharmony_ci if (rc < 0) 14999ca880aSopenharmony_ci log_error_errno(errno, "Unable to EVIOCSABS device \"%s\"", devnode); 15099ca880aSopenharmony_ci} 15199ca880aSopenharmony_ci 15299ca880aSopenharmony_cistatic void set_trackpoint_sensitivity(struct udev_device *dev, const char *value) 15399ca880aSopenharmony_ci{ 15499ca880aSopenharmony_ci struct udev_device *pdev; 15599ca880aSopenharmony_ci char val_s[DECIMAL_STR_MAX(int)]; 15699ca880aSopenharmony_ci int r, val_i; 15799ca880aSopenharmony_ci 15899ca880aSopenharmony_ci /* The sensitivity sysfs attr belongs to the serio parent device */ 15999ca880aSopenharmony_ci pdev = udev_device_get_parent_with_subsystem_devtype(dev, "serio", NULL); 16099ca880aSopenharmony_ci if (!pdev) { 16199ca880aSopenharmony_ci log_warning("Failed to get serio parent for '%s'", udev_device_get_devnode(dev)); 16299ca880aSopenharmony_ci return; 16399ca880aSopenharmony_ci } 16499ca880aSopenharmony_ci 16599ca880aSopenharmony_ci r = safe_atoi(value, &val_i); 16699ca880aSopenharmony_ci if (r < 0) { 16799ca880aSopenharmony_ci log_error("Unable to parse POINTINGSTICK_SENSITIVITY '%s' for '%s'", value, udev_device_get_devnode(dev)); 16899ca880aSopenharmony_ci return; 16999ca880aSopenharmony_ci } 17099ca880aSopenharmony_ci 17199ca880aSopenharmony_ci xsprintf(val_s, "%d", val_i); 17299ca880aSopenharmony_ci 17399ca880aSopenharmony_ci r = udev_device_set_sysattr_value(pdev, "sensitivity", val_s); 17499ca880aSopenharmony_ci if (r < 0) 17599ca880aSopenharmony_ci log_error_errno(r, "Failed to write 'sensitivity' attribute for '%s': %m", udev_device_get_devnode(pdev)); 17699ca880aSopenharmony_ci} 17799ca880aSopenharmony_ci 17899ca880aSopenharmony_cistatic int open_device(const char *devnode) { 17999ca880aSopenharmony_ci int fd; 18099ca880aSopenharmony_ci 18199ca880aSopenharmony_ci fd = open(devnode, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); 18299ca880aSopenharmony_ci if (fd < 0) 18399ca880aSopenharmony_ci return log_error_errno(errno, "Error opening device \"%s\": %m", devnode); 18499ca880aSopenharmony_ci 18599ca880aSopenharmony_ci return fd; 18699ca880aSopenharmony_ci} 18799ca880aSopenharmony_ci 18899ca880aSopenharmony_cistatic int builtin_keyboard(struct udev_device *dev, int argc, char *argv[], bool test) { 18999ca880aSopenharmony_ci struct udev_list_entry *entry; 19099ca880aSopenharmony_ci unsigned release[1024]; 19199ca880aSopenharmony_ci unsigned release_count = 0; 19299ca880aSopenharmony_ci _cleanup_close_ int fd = -1; 19399ca880aSopenharmony_ci const char *node; 19499ca880aSopenharmony_ci 19599ca880aSopenharmony_ci node = udev_device_get_devnode(dev); 19699ca880aSopenharmony_ci if (!node) { 19799ca880aSopenharmony_ci log_error("No device node for \"%s\"", udev_device_get_syspath(dev)); 19899ca880aSopenharmony_ci return EXIT_FAILURE; 19999ca880aSopenharmony_ci } 20099ca880aSopenharmony_ci 20199ca880aSopenharmony_ci udev_list_entry_foreach(entry, udev_device_get_properties_list_entry(dev)) { 20299ca880aSopenharmony_ci const char *key; 20399ca880aSopenharmony_ci char *endptr; 20499ca880aSopenharmony_ci 20599ca880aSopenharmony_ci key = udev_list_entry_get_name(entry); 20699ca880aSopenharmony_ci if (startswith(key, "KEYBOARD_KEY_")) { 20799ca880aSopenharmony_ci const char *keycode; 20899ca880aSopenharmony_ci unsigned scancode; 20999ca880aSopenharmony_ci 21099ca880aSopenharmony_ci /* KEYBOARD_KEY_<hex scan code>=<key identifier string> */ 21199ca880aSopenharmony_ci scancode = strtoul(key + 13, &endptr, 16); 21299ca880aSopenharmony_ci if (endptr[0] != '\0') { 21399ca880aSopenharmony_ci log_warning("Unable to parse scan code from \"%s\"", key); 21499ca880aSopenharmony_ci continue; 21599ca880aSopenharmony_ci } 21699ca880aSopenharmony_ci 21799ca880aSopenharmony_ci keycode = udev_list_entry_get_value(entry); 21899ca880aSopenharmony_ci 21999ca880aSopenharmony_ci /* a leading '!' needs a force-release entry */ 22099ca880aSopenharmony_ci if (keycode[0] == '!') { 22199ca880aSopenharmony_ci keycode++; 22299ca880aSopenharmony_ci 22399ca880aSopenharmony_ci release[release_count] = scancode; 22499ca880aSopenharmony_ci if (release_count < ELEMENTSOF(release)-1) 22599ca880aSopenharmony_ci release_count++; 22699ca880aSopenharmony_ci 22799ca880aSopenharmony_ci if (keycode[0] == '\0') 22899ca880aSopenharmony_ci continue; 22999ca880aSopenharmony_ci } 23099ca880aSopenharmony_ci 23199ca880aSopenharmony_ci if (fd == -1) { 23299ca880aSopenharmony_ci fd = open_device(node); 23399ca880aSopenharmony_ci if (fd < 0) 23499ca880aSopenharmony_ci return EXIT_FAILURE; 23599ca880aSopenharmony_ci } 23699ca880aSopenharmony_ci 23799ca880aSopenharmony_ci map_keycode(fd, node, scancode, keycode); 23899ca880aSopenharmony_ci } else if (startswith(key, "EVDEV_ABS_")) { 23999ca880aSopenharmony_ci unsigned evcode; 24099ca880aSopenharmony_ci 24199ca880aSopenharmony_ci /* EVDEV_ABS_<EV_ABS code>=<min>:<max>:<res>:<fuzz>:<flat> */ 24299ca880aSopenharmony_ci evcode = strtoul(key + 10, &endptr, 16); 24399ca880aSopenharmony_ci if (endptr[0] != '\0') { 24499ca880aSopenharmony_ci log_warning("Unable to parse EV_ABS code from \"%s\"", key); 24599ca880aSopenharmony_ci continue; 24699ca880aSopenharmony_ci } 24799ca880aSopenharmony_ci 24899ca880aSopenharmony_ci if (fd == -1) { 24999ca880aSopenharmony_ci fd = open_device(node); 25099ca880aSopenharmony_ci if (fd < 0) 25199ca880aSopenharmony_ci return EXIT_FAILURE; 25299ca880aSopenharmony_ci } 25399ca880aSopenharmony_ci 25499ca880aSopenharmony_ci override_abs(fd, node, evcode, udev_list_entry_get_value(entry)); 25599ca880aSopenharmony_ci } else if (streq(key, "POINTINGSTICK_SENSITIVITY")) { 25699ca880aSopenharmony_ci set_trackpoint_sensitivity(dev, udev_list_entry_get_value(entry)); 25799ca880aSopenharmony_ci } 25899ca880aSopenharmony_ci } 25999ca880aSopenharmony_ci 26099ca880aSopenharmony_ci /* install list of force-release codes */ 26199ca880aSopenharmony_ci if (release_count > 0) 26299ca880aSopenharmony_ci install_force_release(dev, release, release_count); 26399ca880aSopenharmony_ci 26499ca880aSopenharmony_ci return EXIT_SUCCESS; 26599ca880aSopenharmony_ci} 26699ca880aSopenharmony_ci 26799ca880aSopenharmony_ciconst struct udev_builtin udev_builtin_keyboard = { 26899ca880aSopenharmony_ci .name = "keyboard", 26999ca880aSopenharmony_ci .cmd = builtin_keyboard, 27099ca880aSopenharmony_ci .help = "Keyboard scan code to key mapping", 27199ca880aSopenharmony_ci}; 272