]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/udev-builtin-keyboard.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / udev / udev-builtin-keyboard.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2013 Kay Sievers <kay@vrfy.org>
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/ioctl.h>
25 #include <linux/input.h>
26
27 #include "fd-util.h"
28 #include "parse-util.h"
29 #include "stdio-util.h"
30 #include "string-util.h"
31 #include "udev.h"
32
33 static const struct key_name *keyboard_lookup_key(const char *str, GPERF_LEN_TYPE len);
34 #include "keyboard-keys-from-name.h"
35
36 static int install_force_release(struct udev_device *dev, const unsigned *release, unsigned release_count) {
37 struct udev_device *atkbd;
38 const char *cur;
39 char codes[4096];
40 char *s;
41 size_t l;
42 unsigned i;
43 int ret;
44
45 assert(dev);
46 assert(release);
47
48 atkbd = udev_device_get_parent_with_subsystem_devtype(dev, "serio", NULL);
49 if (!atkbd)
50 return -ENODEV;
51
52 cur = udev_device_get_sysattr_value(atkbd, "force_release");
53 if (!cur)
54 return -ENODEV;
55
56 s = codes;
57 l = sizeof(codes);
58
59 /* copy current content */
60 l = strpcpy(&s, l, cur);
61
62 /* append new codes */
63 for (i = 0; i < release_count; i++)
64 l = strpcpyf(&s, l, ",%u", release[i]);
65
66 log_debug("keyboard: updating force-release list with '%s'", codes);
67 ret = udev_device_set_sysattr_value(atkbd, "force_release", codes);
68 if (ret < 0)
69 log_error_errno(ret, "Error writing force-release attribute: %m");
70 return ret;
71 }
72
73 static void map_keycode(int fd, const char *devnode, int scancode, const char *keycode)
74 {
75 struct {
76 unsigned scan;
77 unsigned key;
78 } map;
79 char *endptr;
80 const struct key_name *k;
81 unsigned keycode_num;
82
83 /* translate identifier to key code */
84 k = keyboard_lookup_key(keycode, strlen(keycode));
85 if (k) {
86 keycode_num = k->id;
87 } else {
88 /* check if it's a numeric code already */
89 keycode_num = strtoul(keycode, &endptr, 0);
90 if (endptr[0] !='\0') {
91 log_error("Unknown key identifier '%s'", keycode);
92 return;
93 }
94 }
95
96 map.scan = scancode;
97 map.key = keycode_num;
98
99 log_debug("keyboard: mapping scan code %d (0x%x) to key code %d (0x%x)",
100 map.scan, map.scan, map.key, map.key);
101
102 if (ioctl(fd, EVIOCSKEYCODE, &map) < 0)
103 log_error_errno(errno, "Error calling EVIOCSKEYCODE on device node '%s' (scan code 0x%x, key code %d): %m", devnode, map.scan, map.key);
104 }
105
106 static inline char* parse_token(const char *current, int32_t *val_out) {
107 char *next;
108 int32_t val;
109
110 if (!current)
111 return NULL;
112
113 val = strtol(current, &next, 0);
114 if (*next && *next != ':')
115 return NULL;
116
117 if (next != current)
118 *val_out = val;
119
120 if (*next)
121 next++;
122
123 return next;
124 }
125
126 static void override_abs(int fd, const char *devnode,
127 unsigned evcode, const char *value) {
128 struct input_absinfo absinfo;
129 int rc;
130 char *next;
131
132 rc = ioctl(fd, EVIOCGABS(evcode), &absinfo);
133 if (rc < 0) {
134 log_error_errno(errno, "Unable to EVIOCGABS device \"%s\"", devnode);
135 return;
136 }
137
138 next = parse_token(value, &absinfo.minimum);
139 next = parse_token(next, &absinfo.maximum);
140 next = parse_token(next, &absinfo.resolution);
141 next = parse_token(next, &absinfo.fuzz);
142 next = parse_token(next, &absinfo.flat);
143 if (!next) {
144 log_error("Unable to parse EV_ABS override '%s' for '%s'", value, devnode);
145 return;
146 }
147
148 log_debug("keyboard: %x overridden with %"PRIi32"/%"PRIi32"/%"PRIi32"/%"PRIi32"/%"PRIi32" for \"%s\"",
149 evcode,
150 absinfo.minimum, absinfo.maximum, absinfo.resolution, absinfo.fuzz, absinfo.flat,
151 devnode);
152 rc = ioctl(fd, EVIOCSABS(evcode), &absinfo);
153 if (rc < 0)
154 log_error_errno(errno, "Unable to EVIOCSABS device \"%s\"", devnode);
155 }
156
157 static void set_trackpoint_sensitivity(struct udev_device *dev, const char *value)
158 {
159 struct udev_device *pdev;
160 char val_s[DECIMAL_STR_MAX(int)];
161 int r, val_i;
162
163 assert(dev);
164 assert(value);
165
166 /* The sensitivity sysfs attr belongs to the serio parent device */
167 pdev = udev_device_get_parent_with_subsystem_devtype(dev, "serio", NULL);
168 if (!pdev) {
169 log_warning("Failed to get serio parent for '%s'", udev_device_get_devnode(dev));
170 return;
171 }
172
173 r = safe_atoi(value, &val_i);
174 if (r < 0) {
175 log_error("Unable to parse POINTINGSTICK_SENSITIVITY '%s' for '%s'", value, udev_device_get_devnode(dev));
176 return;
177 } else if (val_i < 0 || val_i > 255) {
178 log_error("POINTINGSTICK_SENSITIVITY %d outside range [0..255] for '%s' ", val_i, udev_device_get_devnode(dev));
179 return;
180 }
181
182 xsprintf(val_s, "%d", val_i);
183
184 r = udev_device_set_sysattr_value(pdev, "sensitivity", val_s);
185 if (r < 0)
186 log_error_errno(r, "Failed to write 'sensitivity' attribute for '%s': %m", udev_device_get_devnode(pdev));
187 }
188
189 static int open_device(const char *devnode) {
190 int fd;
191
192 fd = open(devnode, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
193 if (fd < 0)
194 return log_error_errno(errno, "Error opening device \"%s\": %m", devnode);
195
196 return fd;
197 }
198
199 static int builtin_keyboard(struct udev_device *dev, int argc, char *argv[], bool test) {
200 struct udev_list_entry *entry;
201 unsigned release[1024];
202 unsigned release_count = 0;
203 _cleanup_close_ int fd = -1;
204 const char *node;
205 int has_abs = -1;
206
207 node = udev_device_get_devnode(dev);
208 if (!node) {
209 log_error("No device node for \"%s\"", udev_device_get_syspath(dev));
210 return EXIT_FAILURE;
211 }
212
213 udev_list_entry_foreach(entry, udev_device_get_properties_list_entry(dev)) {
214 const char *key;
215 char *endptr;
216
217 key = udev_list_entry_get_name(entry);
218 if (startswith(key, "KEYBOARD_KEY_")) {
219 const char *keycode;
220 unsigned scancode;
221
222 /* KEYBOARD_KEY_<hex scan code>=<key identifier string> */
223 scancode = strtoul(key + 13, &endptr, 16);
224 if (endptr[0] != '\0') {
225 log_warning("Unable to parse scan code from \"%s\"", key);
226 continue;
227 }
228
229 keycode = udev_list_entry_get_value(entry);
230
231 /* a leading '!' needs a force-release entry */
232 if (keycode[0] == '!') {
233 keycode++;
234
235 release[release_count] = scancode;
236 if (release_count < ELEMENTSOF(release)-1)
237 release_count++;
238
239 if (keycode[0] == '\0')
240 continue;
241 }
242
243 if (fd == -1) {
244 fd = open_device(node);
245 if (fd < 0)
246 return EXIT_FAILURE;
247 }
248
249 map_keycode(fd, node, scancode, keycode);
250 } else if (startswith(key, "EVDEV_ABS_")) {
251 unsigned evcode;
252
253 /* EVDEV_ABS_<EV_ABS code>=<min>:<max>:<res>:<fuzz>:<flat> */
254 evcode = strtoul(key + 10, &endptr, 16);
255 if (endptr[0] != '\0') {
256 log_warning("Unable to parse EV_ABS code from \"%s\"", key);
257 continue;
258 }
259
260 if (fd == -1) {
261 fd = open_device(node);
262 if (fd < 0)
263 return EXIT_FAILURE;
264 }
265
266 if (has_abs == -1) {
267 unsigned long bits;
268 int rc;
269
270 rc = ioctl(fd, EVIOCGBIT(0, sizeof(bits)), &bits);
271 if (rc < 0) {
272 log_error_errno(errno, "Unable to EVIOCGBIT device \"%s\"", node);
273 return EXIT_FAILURE;
274 }
275
276 has_abs = !!(bits & (1 << EV_ABS));
277 if (!has_abs)
278 log_warning("EVDEV_ABS override set but no EV_ABS present on device \"%s\"", node);
279 }
280
281 if (!has_abs)
282 continue;
283
284 override_abs(fd, node, evcode, udev_list_entry_get_value(entry));
285 } else if (streq(key, "POINTINGSTICK_SENSITIVITY"))
286 set_trackpoint_sensitivity(dev, udev_list_entry_get_value(entry));
287 }
288
289 /* install list of force-release codes */
290 if (release_count > 0)
291 install_force_release(dev, release, release_count);
292
293 return EXIT_SUCCESS;
294 }
295
296 const struct udev_builtin udev_builtin_keyboard = {
297 .name = "keyboard",
298 .cmd = builtin_keyboard,
299 .help = "Keyboard scan code to key mapping",
300 };