199ca880aSopenharmony_ci/* 299ca880aSopenharmony_ci * expose input properties via udev 399ca880aSopenharmony_ci * 499ca880aSopenharmony_ci * Copyright (C) 2009 Martin Pitt <martin.pitt@ubuntu.com> 599ca880aSopenharmony_ci * Portions Copyright (C) 2004 David Zeuthen, <david@fubar.dk> 699ca880aSopenharmony_ci * Copyright (C) 2011 Kay Sievers <kay@vrfy.org> 799ca880aSopenharmony_ci * Copyright (C) 2014 Carlos Garnacho <carlosg@gnome.org> 899ca880aSopenharmony_ci * Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com> 999ca880aSopenharmony_ci * 1099ca880aSopenharmony_ci * This program is free software: you can redistribute it and/or modify 1199ca880aSopenharmony_ci * it under the terms of the GNU General Public License as published by 1299ca880aSopenharmony_ci * the Free Software Foundation, either version 2 of the License, or 1399ca880aSopenharmony_ci * (at your option) any later version. 1499ca880aSopenharmony_ci * 1599ca880aSopenharmony_ci * This program is distributed in the hope that it will be useful, 1699ca880aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 1799ca880aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1899ca880aSopenharmony_ci * GNU General Public License for more details. 1999ca880aSopenharmony_ci * 2099ca880aSopenharmony_ci * You should have received a copy of the GNU General Public License 2199ca880aSopenharmony_ci * along with this program. If not, see <http://www.gnu.org/licenses/>. 2299ca880aSopenharmony_ci */ 2399ca880aSopenharmony_ci 2499ca880aSopenharmony_ci#include <stdio.h> 2599ca880aSopenharmony_ci#include <stdlib.h> 2699ca880aSopenharmony_ci#include <stdarg.h> 2799ca880aSopenharmony_ci#include <unistd.h> 2899ca880aSopenharmony_ci#include <string.h> 2999ca880aSopenharmony_ci#include <errno.h> 3099ca880aSopenharmony_ci#include <linux/limits.h> 3199ca880aSopenharmony_ci#include <linux/input.h> 3299ca880aSopenharmony_ci 3399ca880aSopenharmony_ci#include "udev.h" 3499ca880aSopenharmony_ci#include "util.h" 3599ca880aSopenharmony_ci#include "missing.h" 3699ca880aSopenharmony_ci 3799ca880aSopenharmony_ci/* we must use this kernel-compatible implementation */ 3899ca880aSopenharmony_ci#define BITS_PER_LONG (sizeof(unsigned long) * 8) 3999ca880aSopenharmony_ci#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) 4099ca880aSopenharmony_ci#define OFF(x) ((x)%BITS_PER_LONG) 4199ca880aSopenharmony_ci#define BIT(x) (1UL<<OFF(x)) 4299ca880aSopenharmony_ci#define LONG(x) ((x)/BITS_PER_LONG) 4399ca880aSopenharmony_ci#define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1) 4499ca880aSopenharmony_ci 4599ca880aSopenharmony_cistruct range { 4699ca880aSopenharmony_ci unsigned start; 4799ca880aSopenharmony_ci unsigned end; 4899ca880aSopenharmony_ci}; 4999ca880aSopenharmony_ci 5099ca880aSopenharmony_ci/* key code ranges above BTN_MISC (start is inclusive, stop is exclusive)*/ 5199ca880aSopenharmony_cistatic const struct range high_key_blocks[] = { 5299ca880aSopenharmony_ci { KEY_OK, BTN_DPAD_UP }, 5399ca880aSopenharmony_ci { KEY_ALS_TOGGLE, BTN_TRIGGER_HAPPY } 5499ca880aSopenharmony_ci}; 5599ca880aSopenharmony_ci 5699ca880aSopenharmony_cistatic inline int abs_size_mm(const struct input_absinfo *absinfo) { 5799ca880aSopenharmony_ci /* Resolution is defined to be in units/mm for ABS_X/Y */ 5899ca880aSopenharmony_ci return (absinfo->maximum - absinfo->minimum) / absinfo->resolution; 5999ca880aSopenharmony_ci} 6099ca880aSopenharmony_ci 6199ca880aSopenharmony_cistatic void extract_info(struct udev_device *dev, const char *devpath, bool test) { 6299ca880aSopenharmony_ci char width[DECIMAL_STR_MAX(int)], height[DECIMAL_STR_MAX(int)]; 6399ca880aSopenharmony_ci struct input_absinfo xabsinfo = {}, yabsinfo = {}; 6499ca880aSopenharmony_ci _cleanup_close_ int fd = -1; 6599ca880aSopenharmony_ci 6699ca880aSopenharmony_ci fd = open(devpath, O_RDONLY|O_CLOEXEC); 6799ca880aSopenharmony_ci if (fd < 0) 6899ca880aSopenharmony_ci return; 6999ca880aSopenharmony_ci 7099ca880aSopenharmony_ci if (ioctl(fd, EVIOCGABS(ABS_X), &xabsinfo) < 0 || 7199ca880aSopenharmony_ci ioctl(fd, EVIOCGABS(ABS_Y), &yabsinfo) < 0) 7299ca880aSopenharmony_ci return; 7399ca880aSopenharmony_ci 7499ca880aSopenharmony_ci if (xabsinfo.resolution <= 0 || yabsinfo.resolution <= 0) 7599ca880aSopenharmony_ci return; 7699ca880aSopenharmony_ci 7799ca880aSopenharmony_ci snprintf(width, sizeof(width), "%d", abs_size_mm(&xabsinfo)); 7899ca880aSopenharmony_ci snprintf(height, sizeof(height), "%d", abs_size_mm(&yabsinfo)); 7999ca880aSopenharmony_ci 8099ca880aSopenharmony_ci udev_builtin_add_property(dev, test, "ID_INPUT_WIDTH_MM", width); 8199ca880aSopenharmony_ci udev_builtin_add_property(dev, test, "ID_INPUT_HEIGHT_MM", height); 8299ca880aSopenharmony_ci} 8399ca880aSopenharmony_ci 8499ca880aSopenharmony_ci/* 8599ca880aSopenharmony_ci * Read a capability attribute and return bitmask. 8699ca880aSopenharmony_ci * @param dev udev_device 8799ca880aSopenharmony_ci * @param attr sysfs attribute name (e. g. "capabilities/key") 8899ca880aSopenharmony_ci * @param bitmask: Output array which has a sizeof of bitmask_size 8999ca880aSopenharmony_ci */ 9099ca880aSopenharmony_cistatic void get_cap_mask(struct udev_device *dev, 9199ca880aSopenharmony_ci struct udev_device *pdev, const char* attr, 9299ca880aSopenharmony_ci unsigned long *bitmask, size_t bitmask_size, 9399ca880aSopenharmony_ci bool test) { 9499ca880aSopenharmony_ci const char *v; 9599ca880aSopenharmony_ci char text[4096]; 9699ca880aSopenharmony_ci unsigned i; 9799ca880aSopenharmony_ci char* word; 9899ca880aSopenharmony_ci unsigned long val; 9999ca880aSopenharmony_ci 10099ca880aSopenharmony_ci v = udev_device_get_sysattr_value(pdev, attr); 10199ca880aSopenharmony_ci if (!v) 10299ca880aSopenharmony_ci v = ""; 10399ca880aSopenharmony_ci 10499ca880aSopenharmony_ci snprintf(text, sizeof(text), "%s", v); 10599ca880aSopenharmony_ci log_debug("%s raw kernel attribute: %s", attr, text); 10699ca880aSopenharmony_ci 10799ca880aSopenharmony_ci memzero(bitmask, bitmask_size); 10899ca880aSopenharmony_ci i = 0; 10999ca880aSopenharmony_ci while ((word = strrchr(text, ' ')) != NULL) { 11099ca880aSopenharmony_ci val = strtoul (word+1, NULL, 16); 11199ca880aSopenharmony_ci if (i < bitmask_size/sizeof(unsigned long)) 11299ca880aSopenharmony_ci bitmask[i] = val; 11399ca880aSopenharmony_ci else 11499ca880aSopenharmony_ci log_debug("ignoring %s block %lX which is larger than maximum size", attr, val); 11599ca880aSopenharmony_ci *word = '\0'; 11699ca880aSopenharmony_ci ++i; 11799ca880aSopenharmony_ci } 11899ca880aSopenharmony_ci val = strtoul (text, NULL, 16); 11999ca880aSopenharmony_ci if (i < bitmask_size / sizeof(unsigned long)) 12099ca880aSopenharmony_ci bitmask[i] = val; 12199ca880aSopenharmony_ci else 12299ca880aSopenharmony_ci log_debug("ignoring %s block %lX which is larger than maximum size", attr, val); 12399ca880aSopenharmony_ci 12499ca880aSopenharmony_ci if (test) { 12599ca880aSopenharmony_ci /* printf pattern with the right unsigned long number of hex chars */ 12699ca880aSopenharmony_ci snprintf(text, sizeof(text), " bit %%4u: %%0%zulX\n", 12799ca880aSopenharmony_ci 2 * sizeof(unsigned long)); 12899ca880aSopenharmony_ci log_debug("%s decoded bit map:", attr); 12999ca880aSopenharmony_ci val = bitmask_size / sizeof (unsigned long); 13099ca880aSopenharmony_ci /* skip over leading zeros */ 13199ca880aSopenharmony_ci while (bitmask[val-1] == 0 && val > 0) 13299ca880aSopenharmony_ci --val; 13399ca880aSopenharmony_ci for (i = 0; i < val; ++i) { 13499ca880aSopenharmony_ci DISABLE_WARNING_FORMAT_NONLITERAL; 13599ca880aSopenharmony_ci log_debug(text, i * BITS_PER_LONG, bitmask[i]); 13699ca880aSopenharmony_ci REENABLE_WARNING; 13799ca880aSopenharmony_ci } 13899ca880aSopenharmony_ci } 13999ca880aSopenharmony_ci} 14099ca880aSopenharmony_ci 14199ca880aSopenharmony_ci/* pointer devices */ 14299ca880aSopenharmony_cistatic bool test_pointers(struct udev_device *dev, 14399ca880aSopenharmony_ci const unsigned long* bitmask_ev, 14499ca880aSopenharmony_ci const unsigned long* bitmask_abs, 14599ca880aSopenharmony_ci const unsigned long* bitmask_key, 14699ca880aSopenharmony_ci const unsigned long* bitmask_rel, 14799ca880aSopenharmony_ci const unsigned long* bitmask_props, 14899ca880aSopenharmony_ci bool test) { 14999ca880aSopenharmony_ci int button, axis; 15099ca880aSopenharmony_ci bool has_abs_coordinates = false; 15199ca880aSopenharmony_ci bool has_rel_coordinates = false; 15299ca880aSopenharmony_ci bool has_mt_coordinates = false; 15399ca880aSopenharmony_ci bool has_joystick_axes_or_buttons = false; 15499ca880aSopenharmony_ci bool is_direct = false; 15599ca880aSopenharmony_ci bool has_touch = false; 15699ca880aSopenharmony_ci bool has_3d_coordinates = false; 15799ca880aSopenharmony_ci bool has_keys = false; 15899ca880aSopenharmony_ci bool stylus_or_pen = false; 15999ca880aSopenharmony_ci bool finger_but_no_pen = false; 16099ca880aSopenharmony_ci bool has_mouse_button = false; 16199ca880aSopenharmony_ci bool is_mouse = false; 16299ca880aSopenharmony_ci bool is_touchpad = false; 16399ca880aSopenharmony_ci bool is_touchscreen = false; 16499ca880aSopenharmony_ci bool is_tablet = false; 16599ca880aSopenharmony_ci bool is_joystick = false; 16699ca880aSopenharmony_ci bool is_accelerometer = false; 16799ca880aSopenharmony_ci bool is_pointing_stick= false; 16899ca880aSopenharmony_ci 16999ca880aSopenharmony_ci has_keys = test_bit(EV_KEY, bitmask_ev); 17099ca880aSopenharmony_ci has_abs_coordinates = test_bit(ABS_X, bitmask_abs) && test_bit(ABS_Y, bitmask_abs); 17199ca880aSopenharmony_ci has_3d_coordinates = has_abs_coordinates && test_bit(ABS_Z, bitmask_abs); 17299ca880aSopenharmony_ci is_accelerometer = test_bit(INPUT_PROP_ACCELEROMETER, bitmask_props); 17399ca880aSopenharmony_ci 17499ca880aSopenharmony_ci if (!has_keys && has_3d_coordinates) 17599ca880aSopenharmony_ci is_accelerometer = true; 17699ca880aSopenharmony_ci 17799ca880aSopenharmony_ci if (is_accelerometer) { 17899ca880aSopenharmony_ci udev_builtin_add_property(dev, test, "ID_INPUT_ACCELEROMETER", "1"); 17999ca880aSopenharmony_ci return true; 18099ca880aSopenharmony_ci } 18199ca880aSopenharmony_ci 18299ca880aSopenharmony_ci is_pointing_stick = test_bit(INPUT_PROP_POINTING_STICK, bitmask_props); 18399ca880aSopenharmony_ci stylus_or_pen = test_bit(BTN_STYLUS, bitmask_key) || test_bit(BTN_TOOL_PEN, bitmask_key); 18499ca880aSopenharmony_ci finger_but_no_pen = test_bit(BTN_TOOL_FINGER, bitmask_key) && !test_bit(BTN_TOOL_PEN, bitmask_key); 18599ca880aSopenharmony_ci for (button = BTN_MOUSE; button < BTN_JOYSTICK && !has_mouse_button; button++) 18699ca880aSopenharmony_ci has_mouse_button = test_bit(button, bitmask_key); 18799ca880aSopenharmony_ci has_rel_coordinates = test_bit(EV_REL, bitmask_ev) && test_bit(REL_X, bitmask_rel) && test_bit(REL_Y, bitmask_rel); 18899ca880aSopenharmony_ci has_mt_coordinates = test_bit(ABS_MT_POSITION_X, bitmask_abs) && test_bit(ABS_MT_POSITION_Y, bitmask_abs); 18999ca880aSopenharmony_ci 19099ca880aSopenharmony_ci /* unset has_mt_coordinates if devices claims to have all abs axis */ 19199ca880aSopenharmony_ci if (has_mt_coordinates && test_bit(ABS_MT_SLOT, bitmask_abs) && test_bit(ABS_MT_SLOT - 1, bitmask_abs)) 19299ca880aSopenharmony_ci has_mt_coordinates = false; 19399ca880aSopenharmony_ci is_direct = test_bit(INPUT_PROP_DIRECT, bitmask_props); 19499ca880aSopenharmony_ci has_touch = test_bit(BTN_TOUCH, bitmask_key); 19599ca880aSopenharmony_ci 19699ca880aSopenharmony_ci /* joysticks don't necessarily have buttons; e. g. 19799ca880aSopenharmony_ci * rudders/pedals are joystick-like, but buttonless; they have 19899ca880aSopenharmony_ci * other fancy axes. Others have buttons only but no axes. 19999ca880aSopenharmony_ci * 20099ca880aSopenharmony_ci * The BTN_JOYSTICK range starts after the mouse range, so a mouse 20199ca880aSopenharmony_ci * with more than 16 buttons runs into the joystick range (e.g. Mad 20299ca880aSopenharmony_ci * Catz Mad Catz M.M.O.TE). Skip those. 20399ca880aSopenharmony_ci */ 20499ca880aSopenharmony_ci if (!test_bit(BTN_JOYSTICK - 1, bitmask_key)) { 20599ca880aSopenharmony_ci for (button = BTN_JOYSTICK; button < BTN_DIGI && !has_joystick_axes_or_buttons; button++) 20699ca880aSopenharmony_ci has_joystick_axes_or_buttons = test_bit(button, bitmask_key); 20799ca880aSopenharmony_ci for (button = BTN_TRIGGER_HAPPY1; button <= BTN_TRIGGER_HAPPY40 && !has_joystick_axes_or_buttons; button++) 20899ca880aSopenharmony_ci has_joystick_axes_or_buttons = test_bit(button, bitmask_key); 20999ca880aSopenharmony_ci for (button = BTN_DPAD_UP; button <= BTN_DPAD_RIGHT && !has_joystick_axes_or_buttons; button++) 21099ca880aSopenharmony_ci has_joystick_axes_or_buttons = test_bit(button, bitmask_key); 21199ca880aSopenharmony_ci } 21299ca880aSopenharmony_ci for (axis = ABS_RX; axis < ABS_PRESSURE && !has_joystick_axes_or_buttons; axis++) 21399ca880aSopenharmony_ci has_joystick_axes_or_buttons = test_bit(axis, bitmask_abs); 21499ca880aSopenharmony_ci 21599ca880aSopenharmony_ci if (has_abs_coordinates) { 21699ca880aSopenharmony_ci if (stylus_or_pen) 21799ca880aSopenharmony_ci is_tablet = true; 21899ca880aSopenharmony_ci else if (finger_but_no_pen && !is_direct) 21999ca880aSopenharmony_ci is_touchpad = true; 22099ca880aSopenharmony_ci else if (has_mouse_button) 22199ca880aSopenharmony_ci /* This path is taken by VMware's USB mouse, which has 22299ca880aSopenharmony_ci * absolute axes, but no touch/pressure button. */ 22399ca880aSopenharmony_ci is_mouse = true; 22499ca880aSopenharmony_ci else if (has_touch || is_direct) 22599ca880aSopenharmony_ci is_touchscreen = true; 22699ca880aSopenharmony_ci else if (has_joystick_axes_or_buttons) 22799ca880aSopenharmony_ci is_joystick = true; 22899ca880aSopenharmony_ci } else if (has_joystick_axes_or_buttons) { 22999ca880aSopenharmony_ci is_joystick = true; 23099ca880aSopenharmony_ci } 23199ca880aSopenharmony_ci 23299ca880aSopenharmony_ci if (has_mt_coordinates) { 23399ca880aSopenharmony_ci if (stylus_or_pen) 23499ca880aSopenharmony_ci is_tablet = true; 23599ca880aSopenharmony_ci else if (finger_but_no_pen && !is_direct) 23699ca880aSopenharmony_ci is_touchpad = true; 23799ca880aSopenharmony_ci else if (has_touch || is_direct) 23899ca880aSopenharmony_ci is_touchscreen = true; 23999ca880aSopenharmony_ci } 24099ca880aSopenharmony_ci 24199ca880aSopenharmony_ci if (!is_tablet && !is_touchpad && !is_joystick && 24299ca880aSopenharmony_ci has_mouse_button && 24399ca880aSopenharmony_ci (has_rel_coordinates || 24499ca880aSopenharmony_ci !has_abs_coordinates)) /* mouse buttons and no axis */ 24599ca880aSopenharmony_ci is_mouse = true; 24699ca880aSopenharmony_ci 24799ca880aSopenharmony_ci if (is_pointing_stick) 24899ca880aSopenharmony_ci udev_builtin_add_property(dev, test, "ID_INPUT_POINTINGSTICK", "1"); 24999ca880aSopenharmony_ci if (is_mouse) 25099ca880aSopenharmony_ci udev_builtin_add_property(dev, test, "ID_INPUT_MOUSE", "1"); 25199ca880aSopenharmony_ci if (is_touchpad) 25299ca880aSopenharmony_ci udev_builtin_add_property(dev, test, "ID_INPUT_TOUCHPAD", "1"); 25399ca880aSopenharmony_ci if (is_touchscreen) 25499ca880aSopenharmony_ci udev_builtin_add_property(dev, test, "ID_INPUT_TOUCHSCREEN", "1"); 25599ca880aSopenharmony_ci if (is_joystick) 25699ca880aSopenharmony_ci udev_builtin_add_property(dev, test, "ID_INPUT_JOYSTICK", "1"); 25799ca880aSopenharmony_ci if (is_tablet) 25899ca880aSopenharmony_ci udev_builtin_add_property(dev, test, "ID_INPUT_TABLET", "1"); 25999ca880aSopenharmony_ci 26099ca880aSopenharmony_ci return is_tablet || is_mouse || is_touchpad || is_touchscreen || is_joystick || is_pointing_stick; 26199ca880aSopenharmony_ci} 26299ca880aSopenharmony_ci 26399ca880aSopenharmony_ci/* key like devices */ 26499ca880aSopenharmony_cistatic bool test_key(struct udev_device *dev, 26599ca880aSopenharmony_ci const unsigned long* bitmask_ev, 26699ca880aSopenharmony_ci const unsigned long* bitmask_key, 26799ca880aSopenharmony_ci bool test) { 26899ca880aSopenharmony_ci unsigned i; 26999ca880aSopenharmony_ci unsigned long found; 27099ca880aSopenharmony_ci unsigned long mask; 27199ca880aSopenharmony_ci bool ret = false; 27299ca880aSopenharmony_ci 27399ca880aSopenharmony_ci /* do we have any KEY_* capability? */ 27499ca880aSopenharmony_ci if (!test_bit(EV_KEY, bitmask_ev)) { 27599ca880aSopenharmony_ci log_debug("test_key: no EV_KEY capability"); 27699ca880aSopenharmony_ci return false; 27799ca880aSopenharmony_ci } 27899ca880aSopenharmony_ci 27999ca880aSopenharmony_ci /* only consider KEY_* here, not BTN_* */ 28099ca880aSopenharmony_ci found = 0; 28199ca880aSopenharmony_ci for (i = 0; i < BTN_MISC/BITS_PER_LONG; ++i) { 28299ca880aSopenharmony_ci found |= bitmask_key[i]; 28399ca880aSopenharmony_ci log_debug("test_key: checking bit block %lu for any keys; found=%i", (unsigned long)i*BITS_PER_LONG, found > 0); 28499ca880aSopenharmony_ci } 28599ca880aSopenharmony_ci /* If there are no keys in the lower block, check the higher blocks */ 28699ca880aSopenharmony_ci if (!found) { 28799ca880aSopenharmony_ci unsigned block; 28899ca880aSopenharmony_ci for (block = 0; block < (sizeof(high_key_blocks) / sizeof(struct range)); ++block) { 28999ca880aSopenharmony_ci for (i = high_key_blocks[block].start; i < high_key_blocks[block].end; ++i) { 29099ca880aSopenharmony_ci if (test_bit(i, bitmask_key)) { 29199ca880aSopenharmony_ci log_debug("test_key: Found key %x in high block", i); 29299ca880aSopenharmony_ci found = 1; 29399ca880aSopenharmony_ci break; 29499ca880aSopenharmony_ci } 29599ca880aSopenharmony_ci } 29699ca880aSopenharmony_ci } 29799ca880aSopenharmony_ci } 29899ca880aSopenharmony_ci 29999ca880aSopenharmony_ci if (found > 0) { 30099ca880aSopenharmony_ci udev_builtin_add_property(dev, test, "ID_INPUT_KEY", "1"); 30199ca880aSopenharmony_ci ret = true; 30299ca880aSopenharmony_ci } 30399ca880aSopenharmony_ci 30499ca880aSopenharmony_ci /* the first 32 bits are ESC, numbers, and Q to D; if we have all of 30599ca880aSopenharmony_ci * those, consider it a full keyboard; do not test KEY_RESERVED, though */ 30699ca880aSopenharmony_ci mask = 0xFFFFFFFE; 30799ca880aSopenharmony_ci if ((bitmask_key[0] & mask) == mask) { 30899ca880aSopenharmony_ci udev_builtin_add_property(dev, test, "ID_INPUT_KEYBOARD", "1"); 30999ca880aSopenharmony_ci ret = true; 31099ca880aSopenharmony_ci } 31199ca880aSopenharmony_ci 31299ca880aSopenharmony_ci return ret; 31399ca880aSopenharmony_ci} 31499ca880aSopenharmony_ci 31599ca880aSopenharmony_cistatic int builtin_input_id(struct udev_device *dev, int argc, char *argv[], bool test) { 31699ca880aSopenharmony_ci struct udev_device *pdev; 31799ca880aSopenharmony_ci unsigned long bitmask_ev[NBITS(EV_MAX)]; 31899ca880aSopenharmony_ci unsigned long bitmask_abs[NBITS(ABS_MAX)]; 31999ca880aSopenharmony_ci unsigned long bitmask_key[NBITS(KEY_MAX)]; 32099ca880aSopenharmony_ci unsigned long bitmask_rel[NBITS(REL_MAX)]; 32199ca880aSopenharmony_ci unsigned long bitmask_props[NBITS(INPUT_PROP_MAX)]; 32299ca880aSopenharmony_ci const char *sysname, *devnode; 32399ca880aSopenharmony_ci bool is_pointer; 32499ca880aSopenharmony_ci bool is_key; 32599ca880aSopenharmony_ci 32699ca880aSopenharmony_ci /* walk up the parental chain until we find the real input device; the 32799ca880aSopenharmony_ci * argument is very likely a subdevice of this, like eventN */ 32899ca880aSopenharmony_ci pdev = dev; 32999ca880aSopenharmony_ci while (pdev != NULL && udev_device_get_sysattr_value(pdev, "capabilities/ev") == NULL) 33099ca880aSopenharmony_ci pdev = udev_device_get_parent_with_subsystem_devtype(pdev, "input", NULL); 33199ca880aSopenharmony_ci 33299ca880aSopenharmony_ci if (pdev) { 33399ca880aSopenharmony_ci /* Use this as a flag that input devices were detected, so that this 33499ca880aSopenharmony_ci * program doesn't need to be called more than once per device */ 33599ca880aSopenharmony_ci udev_builtin_add_property(dev, test, "ID_INPUT", "1"); 33699ca880aSopenharmony_ci get_cap_mask(dev, pdev, "capabilities/ev", bitmask_ev, sizeof(bitmask_ev), test); 33799ca880aSopenharmony_ci get_cap_mask(dev, pdev, "capabilities/abs", bitmask_abs, sizeof(bitmask_abs), test); 33899ca880aSopenharmony_ci get_cap_mask(dev, pdev, "capabilities/rel", bitmask_rel, sizeof(bitmask_rel), test); 33999ca880aSopenharmony_ci get_cap_mask(dev, pdev, "capabilities/key", bitmask_key, sizeof(bitmask_key), test); 34099ca880aSopenharmony_ci get_cap_mask(dev, pdev, "properties", bitmask_props, sizeof(bitmask_props), test); 34199ca880aSopenharmony_ci is_pointer = test_pointers(dev, bitmask_ev, bitmask_abs, 34299ca880aSopenharmony_ci bitmask_key, bitmask_rel, 34399ca880aSopenharmony_ci bitmask_props, test); 34499ca880aSopenharmony_ci is_key = test_key(dev, bitmask_ev, bitmask_key, test); 34599ca880aSopenharmony_ci /* Some evdev nodes have only a scrollwheel */ 34699ca880aSopenharmony_ci if (!is_pointer && !is_key && test_bit(EV_REL, bitmask_ev) && 34799ca880aSopenharmony_ci (test_bit(REL_WHEEL, bitmask_rel) || test_bit(REL_HWHEEL, bitmask_rel))) 34899ca880aSopenharmony_ci udev_builtin_add_property(dev, test, "ID_INPUT_KEY", "1"); 34999ca880aSopenharmony_ci if (test_bit(EV_SW, bitmask_ev)) 35099ca880aSopenharmony_ci udev_builtin_add_property(dev, test, "ID_INPUT_SWITCH", "1"); 35199ca880aSopenharmony_ci 35299ca880aSopenharmony_ci } 35399ca880aSopenharmony_ci 35499ca880aSopenharmony_ci devnode = udev_device_get_devnode(dev); 35599ca880aSopenharmony_ci sysname = udev_device_get_sysname(dev); 35699ca880aSopenharmony_ci if (devnode && sysname && startswith(sysname, "event")) 35799ca880aSopenharmony_ci extract_info(dev, devnode, test); 35899ca880aSopenharmony_ci 35999ca880aSopenharmony_ci return EXIT_SUCCESS; 36099ca880aSopenharmony_ci} 36199ca880aSopenharmony_ci 36299ca880aSopenharmony_ciconst struct udev_builtin udev_builtin_input_id = { 36399ca880aSopenharmony_ci .name = "input_id", 36499ca880aSopenharmony_ci .cmd = builtin_input_id, 36599ca880aSopenharmony_ci .help = "Input device properties", 36699ca880aSopenharmony_ci}; 367