1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
7 #include <linux/input.h>
9 #include "device-util.h"
11 #include "parse-util.h"
12 #include "stdio-util.h"
13 #include "string-util.h"
15 #include "udev-builtin.h"
17 static const struct key_name
*keyboard_lookup_key(const char *str
, GPERF_LEN_TYPE len
);
18 #include "keyboard-keys-from-name.h"
20 static int install_force_release(sd_device
*dev
, const unsigned *release
, unsigned release_count
) {
32 r
= sd_device_get_parent_with_subsystem_devtype(dev
, "serio", NULL
, &atkbd
);
34 return log_device_error_errno(dev
, r
, "Failed to get serio parent: %m");
36 r
= sd_device_get_sysattr_value(atkbd
, "force_release", &cur
);
38 return log_device_error_errno(atkbd
, r
, "Failed to get force-release attribute: %m");
43 /* copy current content */
44 l
= strpcpy(&s
, l
, cur
);
46 /* append new codes */
47 for (i
= 0; i
< release_count
; i
++)
48 l
= strpcpyf(&s
, l
, ",%u", release
[i
]);
50 log_device_debug(atkbd
, "keyboard: updating force-release list with '%s'", codes
);
51 r
= sd_device_set_sysattr_value(atkbd
, "force_release", codes
);
53 return log_device_error_errno(atkbd
, r
, "Failed to set force-release attribute: %m");
58 static int map_keycode(sd_device
*dev
, int fd
, int scancode
, const char *keycode
) {
63 const struct key_name
*k
;
67 /* translate identifier to key code */
68 k
= keyboard_lookup_key(keycode
, strlen(keycode
));
72 /* check if it's a numeric code already */
73 r
= safe_atou(keycode
, &keycode_num
);
75 return log_device_error_errno(dev
, r
, "Failed to parse key identifier '%s': %m", keycode
);
79 map
.key
= keycode_num
;
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
);
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
);
90 static const char* parse_token(const char *current
, int32_t *val_out
) {
97 val
= strtol(current
, &next
, 0);
98 if (*next
&& *next
!= ':')
110 static int override_abs(sd_device
*dev
, int fd
, unsigned evcode
, const char *value
) {
111 struct input_absinfo absinfo
;
114 if (ioctl(fd
, EVIOCGABS(evcode
), &absinfo
) < 0)
115 return log_device_error_errno(dev
, errno
, "Failed to call EVIOCGABS");
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
);
123 return log_device_error_errno(dev
, SYNTHETIC_ERRNO(EINVAL
),
124 "Failed to parse EV_ABS override '%s'", value
);
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");
134 static int set_trackpoint_sensitivity(sd_device
*dev
, const char *value
) {
136 char val_s
[DECIMAL_STR_MAX(int)];
142 /* The sensitivity sysfs attr belongs to the serio parent device */
143 r
= sd_device_get_parent_with_subsystem_devtype(dev
, "serio", NULL
, &pdev
);
145 return log_device_error_errno(dev
, r
, "Failed to get serio parent: %m");
147 r
= safe_atoi(value
, &val_i
);
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
);
153 xsprintf(val_s
, "%d", val_i
);
155 r
= sd_device_set_sysattr_value(pdev
, "sensitivity", val_s
);
157 return log_device_error_errno(dev
, r
, "Failed to write 'sensitivity' attribute: %m");
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
;
170 r
= sd_device_get_devname(dev
, &node
);
172 return log_device_error_errno(dev
, r
, "Failed to get device name: %m");
174 FOREACH_DEVICE_PROPERTY(dev
, key
, value
)
175 if (startswith(key
, "KEYBOARD_KEY_")) {
176 const char *keycode
= value
;
179 /* KEYBOARD_KEY_<hex scan code>=<key identifier string> */
180 r
= safe_atou_full(key
+ 13, 16, &scancode
);
182 log_device_warning_errno(dev
, r
, "Failed to parse scan code from \"%s\", ignoring: %m", key
);
186 /* a leading '!' needs a force-release entry */
187 if (keycode
[0] == '!') {
190 release
[release_count
] = scancode
;
191 if (release_count
< ELEMENTSOF(release
)-1)
194 if (keycode
[0] == '\0')
199 fd
= sd_device_open(dev
, O_RDWR
|O_CLOEXEC
|O_NONBLOCK
|O_NOCTTY
);
201 return log_device_error_errno(dev
, fd
, "Failed to open device '%s': %m", node
);
204 (void) map_keycode(dev
, fd
, scancode
, keycode
);
205 } else if (startswith(key
, "EVDEV_ABS_")) {
208 /* EVDEV_ABS_<EV_ABS code>=<min>:<max>:<res>:<fuzz>:<flat> */
209 r
= safe_atou_full(key
+ 10, 16, &evcode
);
211 log_device_warning_errno(dev
, r
, "Failed to parse EV_ABS code from \"%s\", ignoring: %m", key
);
216 fd
= sd_device_open(dev
, O_RDWR
|O_CLOEXEC
|O_NONBLOCK
|O_NOCTTY
);
218 return log_device_error_errno(dev
, fd
, "Failed to open device '%s': %m", node
);
225 rc
= ioctl(fd
, EVIOCGBIT(0, sizeof(bits
)), &bits
);
227 return log_device_error_errno(dev
, errno
, "Failed to set EVIOCGBIT");
229 has_abs
= !!(bits
& (1 << EV_ABS
));
231 log_device_warning(dev
, "EVDEV_ABS override set but no EV_ABS present on device");
237 (void) override_abs(dev
, fd
, evcode
, value
);
238 } else if (streq(key
, "POINTINGSTICK_SENSITIVITY"))
239 (void) set_trackpoint_sensitivity(dev
, value
);
241 /* install list of force-release codes */
242 if (release_count
> 0)
243 (void) install_force_release(dev
, release
, release_count
);
248 const UdevBuiltin udev_builtin_keyboard
= {
250 .cmd
= builtin_keyboard
,
251 .help
= "Keyboard scancode mapping and touchpad/pointingstick characteristics",