]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/udev/udev-builtin-keyboard.c
Merge pull request #15669 from andir/systemd-ipv6-pd-subnet-id
[thirdparty/systemd.git] / src / udev / udev-builtin-keyboard.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
9d7d42bc 2
07a26e42
YW
3#include <errno.h>
4#include <fcntl.h>
9d7d42bc 5#include <stdlib.h>
9d7d42bc 6#include <sys/ioctl.h>
9d7d42bc
KS
7#include <linux/input.h>
8
786cc852 9#include "device-util.h"
3ffd4af2 10#include "fd-util.h"
6bedfcbb 11#include "parse-util.h"
15a5e950 12#include "stdio-util.h"
07630cea 13#include "string-util.h"
07a26e42
YW
14#include "strxcpyx.h"
15#include "udev-builtin.h"
9d7d42bc 16
65fbd939 17static const struct key_name *keyboard_lookup_key(const char *str, GPERF_LEN_TYPE len);
9d7d42bc 18#include "keyboard-keys-from-name.h"
9d7d42bc 19
786cc852
YW
20static int install_force_release(sd_device *dev, const unsigned *release, unsigned release_count) {
21 sd_device *atkbd;
9d7d42bc
KS
22 const char *cur;
23 char codes[4096];
24 char *s;
25 size_t l;
1fa2f38f 26 unsigned i;
786cc852 27 int r;
9d7d42bc 28
3b64e4d4
TG
29 assert(dev);
30 assert(release);
31
786cc852
YW
32 r = sd_device_get_parent_with_subsystem_devtype(dev, "serio", NULL, &atkbd);
33 if (r < 0)
2dccfe4c 34 return log_device_error_errno(dev, r, "Failed to get serio parent: %m");
9d7d42bc 35
786cc852
YW
36 r = sd_device_get_sysattr_value(atkbd, "force_release", &cur);
37 if (r < 0)
2dccfe4c 38 return log_device_error_errno(atkbd, r, "Failed to get force-release attribute: %m");
9d7d42bc
KS
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++)
1fa2f38f 48 l = strpcpyf(&s, l, ",%u", release[i]);
9d7d42bc 49
2dccfe4c 50 log_device_debug(atkbd, "keyboard: updating force-release list with '%s'", codes);
786cc852
YW
51 r = sd_device_set_sysattr_value(atkbd, "force_release", codes);
52 if (r < 0)
2dccfe4c 53 return log_device_error_errno(atkbd, r, "Failed to set force-release attribute: %m");
786cc852
YW
54
55 return 0;
9d7d42bc
KS
56}
57
2dccfe4c 58static int map_keycode(sd_device *dev, int fd, int scancode, const char *keycode) {
9d7d42bc 59 struct {
1fa2f38f
ZJS
60 unsigned scan;
61 unsigned key;
cfba2656 62 } map;
c9a8e340 63 char *endptr;
65fbd939 64 const struct key_name *k;
c9a8e340
PH
65 unsigned keycode_num;
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 keycode_num = strtoul(keycode, &endptr, 0);
2dccfe4c 74 if (endptr[0] !='\0')
a1193286 75 return log_device_error_errno(dev, SYNTHETIC_ERRNO(EINVAL), "Failed to parse key identifier '%s'", keycode);
c9a8e340
PH
76 }
77
78 map.scan = scancode;
79 map.key = keycode_num;
80
2dccfe4c
YW
81 log_device_debug(dev, "keyboard: mapping scan code %d (0x%x) to key code %d (0x%x)",
82 map.scan, map.scan, map.key, map.key);
c9a8e340
PH
83
84 if (ioctl(fd, EVIOCSKEYCODE, &map) < 0)
2dccfe4c
YW
85 return log_device_error_errno(dev, errno, "Failed to call EVIOCSKEYCODE with scan code 0x%x, and key code %d: %m", map.scan, map.key);
86
87 return 0;
c9a8e340
PH
88}
89
a1e92eee 90static char* parse_token(const char *current, int32_t *val_out) {
51c0c286
PH
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
2dccfe4c 110static int override_abs(sd_device *dev, int fd, unsigned evcode, const char *value) {
51c0c286 111 struct input_absinfo absinfo;
51c0c286 112 char *next;
2dccfe4c 113 int r;
51c0c286 114
2dccfe4c
YW
115 r = ioctl(fd, EVIOCGABS(evcode), &absinfo);
116 if (r < 0)
117 return log_device_error_errno(dev, errno, "Failed to call EVIOCGABS");
51c0c286
PH
118
119 next = parse_token(value, &absinfo.minimum);
120 next = parse_token(next, &absinfo.maximum);
121 next = parse_token(next, &absinfo.resolution);
122 next = parse_token(next, &absinfo.fuzz);
123 next = parse_token(next, &absinfo.flat);
2dccfe4c
YW
124 if (!next)
125 return log_device_error(dev, "Failed to parse EV_ABS override '%s'", value);
126
127 log_device_debug(dev, "keyboard: %x overridden with %"PRIi32"/%"PRIi32"/%"PRIi32"/%"PRIi32"/%"PRIi32,
128 evcode, absinfo.minimum, absinfo.maximum, absinfo.resolution, absinfo.fuzz, absinfo.flat);
129 r = ioctl(fd, EVIOCSABS(evcode), &absinfo);
130 if (r < 0)
131 return log_device_error_errno(dev, errno, "Failed to call EVIOCSABS");
51c0c286 132
2dccfe4c 133 return 0;
51c0c286
PH
134}
135
786cc852
YW
136static int set_trackpoint_sensitivity(sd_device *dev, const char *value) {
137 sd_device *pdev;
5defbb5f
HG
138 char val_s[DECIMAL_STR_MAX(int)];
139 int r, val_i;
140
3b64e4d4
TG
141 assert(dev);
142 assert(value);
143
5defbb5f 144 /* The sensitivity sysfs attr belongs to the serio parent device */
786cc852
YW
145 r = sd_device_get_parent_with_subsystem_devtype(dev, "serio", NULL, &pdev);
146 if (r < 0)
2dccfe4c 147 return log_device_error_errno(dev, r, "Failed to get serio parent: %m");
5defbb5f
HG
148
149 r = safe_atoi(value, &val_i);
786cc852 150 if (r < 0)
2dccfe4c 151 return log_device_error_errno(dev, r, "Failed to parse POINTINGSTICK_SENSITIVITY '%s': %m", value);
786cc852 152 else if (val_i < 0 || val_i > 255)
a1193286 153 return log_device_error_errno(dev, SYNTHETIC_ERRNO(ERANGE), "POINTINGSTICK_SENSITIVITY %d outside range [0..255]", val_i);
5defbb5f
HG
154
155 xsprintf(val_s, "%d", val_i);
156
786cc852 157 r = sd_device_set_sysattr_value(pdev, "sensitivity", val_s);
5defbb5f 158 if (r < 0)
2dccfe4c 159 return log_device_error_errno(dev, r, "Failed to write 'sensitivity' attribute: %m");
786cc852
YW
160
161 return 0;
5defbb5f
HG
162}
163
3fc2e9a2 164static int builtin_keyboard(sd_device *dev, int argc, char *argv[], bool test) {
1fa2f38f
ZJS
165 unsigned release[1024];
166 unsigned release_count = 0;
cfba2656 167 _cleanup_close_ int fd = -1;
786cc852
YW
168 const char *node, *key, *value;
169 int has_abs = -1, r;
786cc852
YW
170
171 r = sd_device_get_devname(dev, &node);
2dccfe4c
YW
172 if (r < 0)
173 return log_device_error_errno(dev, r, "Failed to get device name: %m");
9d7d42bc 174
786cc852 175 FOREACH_DEVICE_PROPERTY(dev, key, value) {
9d7d42bc 176 char *endptr;
9d7d42bc 177
8a0fd83c 178 if (startswith(key, "KEYBOARD_KEY_")) {
786cc852 179 const char *keycode = value;
8a0fd83c
PH
180 unsigned scancode;
181
182 /* KEYBOARD_KEY_<hex scan code>=<key identifier string> */
183 scancode = strtoul(key + 13, &endptr, 16);
184 if (endptr[0] != '\0') {
2dccfe4c 185 log_device_warning(dev, "Failed to parse scan code from \"%s\", ignoring", key);
8a0fd83c
PH
186 continue;
187 }
9d7d42bc 188
8a0fd83c
PH
189 /* a leading '!' needs a force-release entry */
190 if (keycode[0] == '!') {
191 keycode++;
9d7d42bc 192
8a0fd83c 193 release[release_count] = scancode;
49c603bd 194 if (release_count < ELEMENTSOF(release)-1)
8a0fd83c 195 release_count++;
9d7d42bc 196
8a0fd83c
PH
197 if (keycode[0] == '\0')
198 continue;
199 }
9d7d42bc 200
2dccfe4c
YW
201 if (fd < 0) {
202 fd = open(node, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
51c0c286 203 if (fd < 0)
2dccfe4c 204 return log_device_error_errno(dev, errno, "Failed to open device '%s': %m", node);
cfba2656 205 }
9d7d42bc 206
2dccfe4c 207 (void) map_keycode(dev, fd, scancode, keycode);
51c0c286
PH
208 } else if (startswith(key, "EVDEV_ABS_")) {
209 unsigned evcode;
210
211 /* EVDEV_ABS_<EV_ABS code>=<min>:<max>:<res>:<fuzz>:<flat> */
212 evcode = strtoul(key + 10, &endptr, 16);
213 if (endptr[0] != '\0') {
2dccfe4c 214 log_device_warning(dev, "Failed to parse EV_ABS code from \"%s\", ignoring", key);
51c0c286
PH
215 continue;
216 }
217
2dccfe4c
YW
218 if (fd < 0) {
219 fd = open(node, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
51c0c286 220 if (fd < 0)
2dccfe4c 221 return log_device_error_errno(dev, errno, "Failed to open device '%s': %m", node);
51c0c286
PH
222 }
223
855cf359
PH
224 if (has_abs == -1) {
225 unsigned long bits;
226 int rc;
227
228 rc = ioctl(fd, EVIOCGBIT(0, sizeof(bits)), &bits);
d354690e 229 if (rc < 0)
2dccfe4c 230 return log_device_error_errno(dev, errno, "Failed to set EVIOCGBIT");
855cf359
PH
231
232 has_abs = !!(bits & (1 << EV_ABS));
233 if (!has_abs)
2dccfe4c 234 log_device_warning(dev, "EVDEV_ABS override set but no EV_ABS present on device");
855cf359
PH
235 }
236
237 if (!has_abs)
238 continue;
239
2dccfe4c 240 (void) override_abs(dev, fd, evcode, value);
1f6b4113 241 } else if (streq(key, "POINTINGSTICK_SENSITIVITY"))
2dccfe4c 242 (void) set_trackpoint_sensitivity(dev, value);
9d7d42bc
KS
243 }
244
cfba2656
PH
245 /* install list of force-release codes */
246 if (release_count > 0)
2dccfe4c 247 (void) install_force_release(dev, release, release_count);
cfba2656 248
d354690e 249 return 0;
9d7d42bc
KS
250}
251
25de7aa7 252const UdevBuiltin udev_builtin_keyboard = {
9d7d42bc
KS
253 .name = "keyboard",
254 .cmd = builtin_keyboard,
5ac0162c 255 .help = "Keyboard scan code to key mapping",
9d7d42bc 256};