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 
install_force_release(sd_device * dev,const unsigned * release,unsigned release_count)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 
map_keycode(sd_device * dev,int fd,int scancode,const char * keycode)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 %d (0x%x) to key code %d (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 %d: %m", map.scan, map.key);
86 
87         return 0;
88 }
89 
parse_token(const char * current,int32_t * val_out)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 
override_abs(sd_device * dev,int fd,unsigned evcode,const char * value)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 
set_trackpoint_sensitivity(sd_device * dev,const char * value)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 
builtin_keyboard(sd_device * dev,sd_netlink ** rtnl,int argc,char * argv[],bool test)162 static int builtin_keyboard(sd_device *dev, sd_netlink **rtnl, int argc, char *argv[], bool test) {
163         unsigned release[1024];
164         unsigned release_count = 0;
165         _cleanup_close_ int fd = -1;
166         const char *node, *key, *value;
167         int has_abs = -1, r;
168 
169         r = sd_device_get_devname(dev, &node);
170         if (r < 0)
171                 return log_device_error_errno(dev, r, "Failed to get device name: %m");
172 
173         FOREACH_DEVICE_PROPERTY(dev, key, value)
174                 if (startswith(key, "KEYBOARD_KEY_")) {
175                         const char *keycode = value;
176                         unsigned scancode;
177 
178                         /* KEYBOARD_KEY_<hex scan code>=<key identifier string> */
179                         r = safe_atou_full(key + 13, 16, &scancode);
180                         if (r < 0) {
181                                 log_device_warning_errno(dev, r, "Failed to parse scan code from \"%s\", ignoring: %m", key);
182                                 continue;
183                         }
184 
185                         /* a leading '!' needs a force-release entry */
186                         if (keycode[0] == '!') {
187                                 keycode++;
188 
189                                 release[release_count] = scancode;
190                                 if (release_count < ELEMENTSOF(release)-1)
191                                         release_count++;
192 
193                                 if (keycode[0] == '\0')
194                                         continue;
195                         }
196 
197                         if (fd < 0) {
198                                 fd = sd_device_open(dev, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
199                                 if (fd < 0)
200                                         return log_device_error_errno(dev, fd, "Failed to open device '%s': %m", node);
201                         }
202 
203                         (void) map_keycode(dev, fd, scancode, keycode);
204                 } else if (startswith(key, "EVDEV_ABS_")) {
205                         unsigned evcode;
206 
207                         /* EVDEV_ABS_<EV_ABS code>=<min>:<max>:<res>:<fuzz>:<flat> */
208                         r = safe_atou_full(key + 10, 16, &evcode);
209                         if (r < 0) {
210                                 log_device_warning_errno(dev, r, "Failed to parse EV_ABS code from \"%s\", ignoring: %m", key);
211                                 continue;
212                         }
213 
214                         if (fd < 0) {
215                                 fd = sd_device_open(dev, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
216                                 if (fd < 0)
217                                         return log_device_error_errno(dev, fd, "Failed to open device '%s': %m", node);
218                         }
219 
220                         if (has_abs == -1) {
221                                 unsigned long bits;
222                                 int rc;
223 
224                                 rc = ioctl(fd, EVIOCGBIT(0, sizeof(bits)), &bits);
225                                 if (rc < 0)
226                                         return log_device_error_errno(dev, errno, "Failed to set EVIOCGBIT");
227 
228                                 has_abs = !!(bits & (1 << EV_ABS));
229                                 if (!has_abs)
230                                         log_device_warning(dev, "EVDEV_ABS override set but no EV_ABS present on device");
231                         }
232 
233                         if (!has_abs)
234                                 continue;
235 
236                         (void) override_abs(dev, fd, evcode, value);
237                 } else if (streq(key, "POINTINGSTICK_SENSITIVITY"))
238                         (void) set_trackpoint_sensitivity(dev, value);
239 
240         /* install list of force-release codes */
241         if (release_count > 0)
242                 (void) install_force_release(dev, release, release_count);
243 
244         return 0;
245 }
246 
247 const UdevBuiltin udev_builtin_keyboard = {
248         .name = "keyboard",
249         .cmd = builtin_keyboard,
250         .help = "Keyboard scan code to key mapping",
251 };
252