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