1a46c0ec8Sopenharmony_ci#!/usr/bin/env python3
2a46c0ec8Sopenharmony_ci# vim: set expandtab shiftwidth=4:
3a46c0ec8Sopenharmony_ci# -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */
4a46c0ec8Sopenharmony_ci#
5a46c0ec8Sopenharmony_ci# Copyright © 2018 Red Hat, Inc.
6a46c0ec8Sopenharmony_ci#
7a46c0ec8Sopenharmony_ci# Permission is hereby granted, free of charge, to any person obtaining a
8a46c0ec8Sopenharmony_ci# copy of this software and associated documentation files (the 'Software'),
9a46c0ec8Sopenharmony_ci# to deal in the Software without restriction, including without limitation
10a46c0ec8Sopenharmony_ci# the rights to use, copy, modify, merge, publish, distribute, sublicense,
11a46c0ec8Sopenharmony_ci# and/or sell copies of the Software, and to permit persons to whom the
12a46c0ec8Sopenharmony_ci# Software is furnished to do so, subject to the following conditions:
13a46c0ec8Sopenharmony_ci#
14a46c0ec8Sopenharmony_ci# The above copyright notice and this permission notice (including the next
15a46c0ec8Sopenharmony_ci# paragraph) shall be included in all copies or substantial portions of the
16a46c0ec8Sopenharmony_ci# Software.
17a46c0ec8Sopenharmony_ci#
18a46c0ec8Sopenharmony_ci# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19a46c0ec8Sopenharmony_ci# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20a46c0ec8Sopenharmony_ci# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
21a46c0ec8Sopenharmony_ci# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22a46c0ec8Sopenharmony_ci# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23a46c0ec8Sopenharmony_ci# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24a46c0ec8Sopenharmony_ci# DEALINGS IN THE SOFTWARE.
25a46c0ec8Sopenharmony_ci
26a46c0ec8Sopenharmony_ciimport argparse
27a46c0ec8Sopenharmony_ciimport sys
28a46c0ec8Sopenharmony_ci
29a46c0ec8Sopenharmony_citry:
30a46c0ec8Sopenharmony_ci    import pyudev
31a46c0ec8Sopenharmony_ciexcept ModuleNotFoundError as e:
32a46c0ec8Sopenharmony_ci    print("Error: {}".format(str(e)), file=sys.stderr)
33a46c0ec8Sopenharmony_ci    print(
34a46c0ec8Sopenharmony_ci        "One or more python modules are missing. Please install those "
35a46c0ec8Sopenharmony_ci        "modules and re-run this tool."
36a46c0ec8Sopenharmony_ci    )
37a46c0ec8Sopenharmony_ci    sys.exit(1)
38a46c0ec8Sopenharmony_ci
39a46c0ec8Sopenharmony_ci
40a46c0ec8Sopenharmony_cidef list_devices():
41a46c0ec8Sopenharmony_ci    devices = {}
42a46c0ec8Sopenharmony_ci    context = pyudev.Context()
43a46c0ec8Sopenharmony_ci    for device in context.list_devices(subsystem="input"):
44a46c0ec8Sopenharmony_ci        if (device.device_node or "").startswith("/dev/input/event"):
45a46c0ec8Sopenharmony_ci            parent = device.parent
46a46c0ec8Sopenharmony_ci            if parent is not None:
47a46c0ec8Sopenharmony_ci                name = parent.properties["NAME"] or ""
48a46c0ec8Sopenharmony_ci                # The udev name includes enclosing quotes
49a46c0ec8Sopenharmony_ci                devices[device.device_node] = name[1:-1]
50a46c0ec8Sopenharmony_ci
51a46c0ec8Sopenharmony_ci    def versionsort(key):
52a46c0ec8Sopenharmony_ci        return int(key[len("/dev/input/event") :])
53a46c0ec8Sopenharmony_ci
54a46c0ec8Sopenharmony_ci    for k in sorted(devices, key=versionsort):
55a46c0ec8Sopenharmony_ci        print(f"{k}:\t{devices[k]}")
56a46c0ec8Sopenharmony_ci
57a46c0ec8Sopenharmony_ci
58a46c0ec8Sopenharmony_ciclass HidDevice:
59a46c0ec8Sopenharmony_ci    def __init__(self, name, driver, vendor, product, devpath):
60a46c0ec8Sopenharmony_ci        self.name = name
61a46c0ec8Sopenharmony_ci        self.driver = driver
62a46c0ec8Sopenharmony_ci        self.vendor = vendor
63a46c0ec8Sopenharmony_ci        self.product = product
64a46c0ec8Sopenharmony_ci        self.devpath = devpath
65a46c0ec8Sopenharmony_ci        self.hidraws = []
66a46c0ec8Sopenharmony_ci        self.evdevs = []
67a46c0ec8Sopenharmony_ci
68a46c0ec8Sopenharmony_ci
69a46c0ec8Sopenharmony_cidef list_hid_devices():
70a46c0ec8Sopenharmony_ci    devices = []
71a46c0ec8Sopenharmony_ci    context = pyudev.Context()
72a46c0ec8Sopenharmony_ci    for device in context.list_devices(subsystem="hid"):
73a46c0ec8Sopenharmony_ci        name = device.properties.get("HID_NAME")
74a46c0ec8Sopenharmony_ci        driver = device.properties.get("DRIVER")
75a46c0ec8Sopenharmony_ci        devpath = device.properties.get("DEVPATH")
76a46c0ec8Sopenharmony_ci        id = device.properties.get("HID_ID") or "0:0:0"
77a46c0ec8Sopenharmony_ci        _, vendor, product = (int(x, 16) for x in id.split(":"))
78a46c0ec8Sopenharmony_ci        devices.append(HidDevice(name, driver, vendor, product, devpath))
79a46c0ec8Sopenharmony_ci
80a46c0ec8Sopenharmony_ci    for device in context.list_devices(subsystem="hidraw"):
81a46c0ec8Sopenharmony_ci        devpath = device.properties["DEVPATH"]
82a46c0ec8Sopenharmony_ci
83a46c0ec8Sopenharmony_ci        for hid in devices:
84a46c0ec8Sopenharmony_ci            if devpath.startswith(hid.devpath):
85a46c0ec8Sopenharmony_ci                hid.hidraws.append(f"'{device.device_node}'")
86a46c0ec8Sopenharmony_ci
87a46c0ec8Sopenharmony_ci    for device in context.list_devices(subsystem="input"):
88a46c0ec8Sopenharmony_ci        if (device.device_node or "").startswith("/dev/input/event"):
89a46c0ec8Sopenharmony_ci            devpath = device.properties["DEVPATH"]
90a46c0ec8Sopenharmony_ci
91a46c0ec8Sopenharmony_ci            for hid in devices:
92a46c0ec8Sopenharmony_ci                if devpath.startswith(hid.devpath):
93a46c0ec8Sopenharmony_ci                    hid.evdevs.append(f"'{device.device_node}'")
94a46c0ec8Sopenharmony_ci
95a46c0ec8Sopenharmony_ci    print("hid:")
96a46c0ec8Sopenharmony_ci    for d in devices:
97a46c0ec8Sopenharmony_ci        print(f"- name:   '{d.name}'")
98a46c0ec8Sopenharmony_ci        print(f"  id:     '{d.vendor:04x}:{d.product:04x}'")
99a46c0ec8Sopenharmony_ci        print(f"  driver: '{d.driver}'")
100a46c0ec8Sopenharmony_ci        print(f"  hidraw: [{', '.join(h for h in d.hidraws)}]")
101a46c0ec8Sopenharmony_ci        print(f"  evdev:  [{', '.join(h for h in d.evdevs)}]")
102a46c0ec8Sopenharmony_ci        print("")
103a46c0ec8Sopenharmony_ci
104a46c0ec8Sopenharmony_ci
105a46c0ec8Sopenharmony_cidef main():
106a46c0ec8Sopenharmony_ci    parser = argparse.ArgumentParser(description="List kernel devices")
107a46c0ec8Sopenharmony_ci    parser.add_argument("--hid", action="store_true", default=False)
108a46c0ec8Sopenharmony_ci    args = parser.parse_args()
109a46c0ec8Sopenharmony_ci
110a46c0ec8Sopenharmony_ci    if args.hid:
111a46c0ec8Sopenharmony_ci        list_hid_devices()
112a46c0ec8Sopenharmony_ci    else:
113a46c0ec8Sopenharmony_ci        list_devices()
114a46c0ec8Sopenharmony_ci
115a46c0ec8Sopenharmony_ci
116a46c0ec8Sopenharmony_ciif __name__ == "__main__":
117a46c0ec8Sopenharmony_ci    try:
118a46c0ec8Sopenharmony_ci        main()
119a46c0ec8Sopenharmony_ci    except KeyboardInterrupt:
120a46c0ec8Sopenharmony_ci        print("Exited on user request")
121