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