]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/udev-builtin-keyboard.c
device-util: Declare iterator variables inline
[thirdparty/systemd.git] / src / udev / udev-builtin-keyboard.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <stdlib.h>
6 #include <sys/ioctl.h>
7 #include <linux/input.h>
8
9 #include "device-util.h"
10 #include "fd-util.h"
11 #include "parse-util.h"
12 #include "stdio-util.h"
13 #include "string-util.h"
14 #include "strxcpyx.h"
15 #include "udev-builtin.h"
16
17 static const struct key_name *keyboard_lookup_key(const char *str, GPERF_LEN_TYPE len);
18 #include "keyboard-keys-from-name.h"
19
20 static int install_force_release(sd_device *dev, const unsigned *release, unsigned release_count) {
21 sd_device *atkbd;
22 const char *cur;
23 char codes[4096];
24 char *s;
25 size_t l;
26 unsigned i;
27 int r;
28
29 assert(dev);
30 assert(release);
31
32 r = sd_device_get_parent_with_subsystem_devtype(dev, "serio", NULL, &atkbd);
33 if (r < 0)
34 return log_device_error_errno(dev, r, "Failed to get serio parent: %m");
35
36 r = sd_device_get_sysattr_value(atkbd, "force_release", &cur);
37 if (r < 0)
38 return log_device_error_errno(atkbd, r, "Failed to get force-release attribute: %m");
39
40 s = codes;
41 l = sizeof(codes);
42
43 /* copy current content */
44 l = strpcpy(&s, l, cur);
45
46 /* append new codes */
47 for (i = 0; i < release_count; i++)
48 l = strpcpyf(&s, l, ",%u", release[i]);
49
50 log_device_debug(atkbd, "keyboard: updating force-release list with '%s'", codes);
51 r = sd_device_set_sysattr_value(atkbd, "force_release", codes);
52 if (r < 0)
53 return log_device_error_errno(atkbd, r, "Failed to set force-release attribute: %m");
54
55 return 0;
56 }
57
58 static int map_keycode(sd_device *dev, int fd, int scancode, const char *keycode) {
59 struct {
60 unsigned scan;
61 unsigned key;
62 } map;
63 const struct key_name *k;
64 unsigned keycode_num;
65 int r;
66
67 /* translate identifier to key code */
68 k = keyboard_lookup_key(keycode, strlen(keycode));
69 if (k)
70 keycode_num = k->id;
71 else {
72 /* check if it's a numeric code already */
73 r = safe_atou(keycode, &keycode_num);
74 if (r < 0)
75 return log_device_error_errno(dev, r, "Failed to parse key identifier '%s': %m", keycode);
76 }
77
78 map.scan = scancode;
79 map.key = keycode_num;
80
81 log_device_debug(dev, "keyboard: mapping scan code %u (0x%x) to key code %u (0x%x)",
82 map.scan, map.scan, map.key, map.key);
83
84 if (ioctl(fd, EVIOCSKEYCODE, &map) < 0)
85 return log_device_error_errno(dev, errno, "Failed to call EVIOCSKEYCODE with scan code 0x%x, and key code %u: %m", map.scan, map.key);
86
87 return 0;
88 }
89
90 static const char* parse_token(const char *current, int32_t *val_out) {
91 char *next;
92 int32_t val;
93
94 if (!current)
95 return NULL;
96
97 val = strtol(current, &next, 0);
98 if (*next && *next != ':')
99 return NULL;
100
101 if (next != current)
102 *val_out = val;
103
104 if (*next)
105 next++;
106
107 return next;
108 }
109
110 static int override_abs(sd_device *dev, int fd, unsigned evcode, const char *value) {
111 struct input_absinfo absinfo;
112 const char *next;
113
114 if (ioctl(fd, EVIOCGABS(evcode), &absinfo) < 0)
115 return log_device_error_errno(dev, errno, "Failed to call EVIOCGABS");
116
117 next = parse_token(value, &absinfo.minimum);
118 next = parse_token(next, &absinfo.maximum);
119 next = parse_token(next, &absinfo.resolution);
120 next = parse_token(next, &absinfo.fuzz);
121 next = parse_token(next, &absinfo.flat);
122 if (!next)
123 return log_device_error_errno(dev, SYNTHETIC_ERRNO(EINVAL),
124 "Failed to parse EV_ABS override '%s'", value);
125
126 log_device_debug(dev, "keyboard: %x overridden with %"PRIi32"/%"PRIi32"/%"PRIi32"/%"PRIi32"/%"PRIi32,
127 evcode, absinfo.minimum, absinfo.maximum, absinfo.resolution, absinfo.fuzz, absinfo.flat);
128 if (ioctl(fd, EVIOCSABS(evcode), &absinfo) < 0)
129 return log_device_error_errno(dev, errno, "Failed to call EVIOCSABS");
130
131 return 0;
132 }
133
134 static int set_trackpoint_sensitivity(sd_device *dev, const char *value) {
135 sd_device *pdev;
136 char val_s[DECIMAL_STR_MAX(int)];
137 int r, val_i;
138
139 assert(dev);
140 assert(value);
141
142 /* The sensitivity sysfs attr belongs to the serio parent device */
143 r = sd_device_get_parent_with_subsystem_devtype(dev, "serio", NULL, &pdev);
144 if (r < 0)
145 return log_device_error_errno(dev, r, "Failed to get serio parent: %m");
146
147 r = safe_atoi(value, &val_i);
148 if (r < 0)
149 return log_device_error_errno(dev, r, "Failed to parse POINTINGSTICK_SENSITIVITY '%s': %m", value);
150 else if (val_i < 0 || val_i > 255)
151 return log_device_error_errno(dev, SYNTHETIC_ERRNO(ERANGE), "POINTINGSTICK_SENSITIVITY %d outside range [0..255]", val_i);
152
153 xsprintf(val_s, "%d", val_i);
154
155 r = sd_device_set_sysattr_value(pdev, "sensitivity", val_s);
156 if (r < 0)
157 return log_device_error_errno(dev, r, "Failed to write 'sensitivity' attribute: %m");
158
159 return 0;
160 }
161
162 static int builtin_keyboard(UdevEvent *event, int argc, char *argv[], bool test) {
163 sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
164 unsigned release[1024];
165 unsigned release_count = 0;
166 _cleanup_close_ int fd = -EBADF;
167 const char *node;
168 int has_abs = -1, r;
169
170 r = sd_device_get_devname(dev, &node);
171 if (r < 0)
172 return log_device_error_errno(dev, r, "Failed to get device name: %m");
173
174 FOREACH_DEVICE_PROPERTY(dev, key, value)
175 if (startswith(key, "KEYBOARD_KEY_")) {
176 const char *keycode = value;
177 unsigned scancode;
178
179 /* KEYBOARD_KEY_<hex scan code>=<key identifier string> */
180 r = safe_atou_full(key + 13, 16, &scancode);
181 if (r < 0) {
182 log_device_warning_errno(dev, r, "Failed to parse scan code from \"%s\", ignoring: %m", key);
183 continue;
184 }
185
186 /* a leading '!' needs a force-release entry */
187 if (keycode[0] == '!') {
188 keycode++;
189
190 release[release_count] = scancode;
191 if (release_count < ELEMENTSOF(release)-1)
192 release_count++;
193
194 if (keycode[0] == '\0')
195 continue;
196 }
197
198 if (fd < 0) {
199 fd = sd_device_open(dev, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
200 if (fd < 0)
201 return log_device_error_errno(dev, fd, "Failed to open device '%s': %m", node);
202 }
203
204 (void) map_keycode(dev, fd, scancode, keycode);
205 } else if (startswith(key, "EVDEV_ABS_")) {
206 unsigned evcode;
207
208 /* EVDEV_ABS_<EV_ABS code>=<min>:<max>:<res>:<fuzz>:<flat> */
209 r = safe_atou_full(key + 10, 16, &evcode);
210 if (r < 0) {
211 log_device_warning_errno(dev, r, "Failed to parse EV_ABS code from \"%s\", ignoring: %m", key);
212 continue;
213 }
214
215 if (fd < 0) {
216 fd = sd_device_open(dev, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
217 if (fd < 0)
218 return log_device_error_errno(dev, fd, "Failed to open device '%s': %m", node);
219 }
220
221 if (has_abs == -1) {
222 unsigned long bits;
223 int rc;
224
225 rc = ioctl(fd, EVIOCGBIT(0, sizeof(bits)), &bits);
226 if (rc < 0)
227 return log_device_error_errno(dev, errno, "Failed to set EVIOCGBIT");
228
229 has_abs = !!(bits & (1 << EV_ABS));
230 if (!has_abs)
231 log_device_warning(dev, "EVDEV_ABS override set but no EV_ABS present on device");
232 }
233
234 if (!has_abs)
235 continue;
236
237 (void) override_abs(dev, fd, evcode, value);
238 } else if (streq(key, "POINTINGSTICK_SENSITIVITY"))
239 (void) set_trackpoint_sensitivity(dev, value);
240
241 /* install list of force-release codes */
242 if (release_count > 0)
243 (void) install_force_release(dev, release, release_count);
244
245 return 0;
246 }
247
248 const UdevBuiltin udev_builtin_keyboard = {
249 .name = "keyboard",
250 .cmd = builtin_keyboard,
251 .help = "Keyboard scancode mapping and touchpad/pointingstick characteristics",
252 };