1/* 2 * Copyright © 2015 Red Hat, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 * DEALINGS IN THE SOFTWARE. 22 */ 23 24#include "config.h" 25 26#include <stdio.h> 27#include <stdlib.h> 28#include <string.h> 29#include <libudev.h> 30 31#include "libinput-util.h" 32 33#if HAVE_LIBWACOM 34#include <libwacom/libwacom.h> 35 36static void 37wacom_handle_paired(struct udev_device *device, 38 int *vendor_id, 39 int *product_id) 40{ 41 WacomDeviceDatabase *db = NULL; 42 WacomDevice *tablet = NULL; 43 const WacomMatch *paired; 44 45 db = libwacom_database_new(); 46 if (!db) 47 goto out; 48 49 tablet = libwacom_new_from_usbid(db, *vendor_id, *product_id, NULL); 50 if (!tablet) 51 goto out; 52 paired = libwacom_get_paired_device(tablet); 53 if (!paired) 54 goto out; 55 56 *vendor_id = libwacom_match_get_vendor_id(paired); 57 *product_id = libwacom_match_get_product_id(paired); 58 59out: 60 if (tablet) 61 libwacom_destroy(tablet); 62 if (db) 63 libwacom_database_destroy(db); 64} 65 66static int 67find_tree_distance(struct udev_device *a, struct udev_device *b) 68{ 69 struct udev_device *ancestor_a = a; 70 int dist_a = 0; 71 72 while (ancestor_a != NULL) { 73 const char *path_a = udev_device_get_syspath(ancestor_a); 74 struct udev_device *ancestor_b = b; 75 int dist_b = 0; 76 77 while (ancestor_b != NULL) { 78 const char *path_b = udev_device_get_syspath(ancestor_b); 79 80 if (streq(path_a, path_b)) 81 return dist_a + dist_b; 82 83 dist_b++; 84 ancestor_b = udev_device_get_parent(ancestor_b); 85 } 86 87 dist_a++; 88 ancestor_a = udev_device_get_parent(ancestor_a); 89 } 90 return -1; 91} 92 93static void 94wacom_handle_ekr(struct udev_device *device, 95 int *vendor_id, 96 int *product_id, 97 char **phys_attr) 98{ 99 struct udev *udev; 100 struct udev_enumerate *e; 101 struct udev_list_entry *entry = NULL; 102 int best_dist = -1; 103 104 udev = udev_device_get_udev(device); 105 e = udev_enumerate_new(udev); 106 udev_enumerate_add_match_subsystem(e, "input"); 107 udev_enumerate_add_match_sysname(e, "input*"); 108 udev_enumerate_scan_devices(e); 109 110 udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) { 111 struct udev_device *d; 112 const char *path, *phys; 113 const char *pidstr, *vidstr; 114 int pid, vid, dist; 115 116 /* Find and use the closest Wacom device on the system, 117 * relying on wacom_handle_paired() to fix our ID later 118 * if needed. 119 */ 120 path = udev_list_entry_get_name(entry); 121 d = udev_device_new_from_syspath(udev, path); 122 if (!d) 123 continue; 124 125 vidstr = udev_device_get_property_value(d, "ID_VENDOR_ID"); 126 pidstr = udev_device_get_property_value(d, "ID_MODEL_ID"); 127 phys = udev_device_get_sysattr_value(d, "phys"); 128 129 if (vidstr && pidstr && phys && 130 safe_atoi_base(vidstr, &vid, 16) && 131 safe_atoi_base(pidstr, &pid, 16) && 132 vid == VENDOR_ID_WACOM && 133 pid != PRODUCT_ID_WACOM_EKR) { 134 dist = find_tree_distance(device, d); 135 if (dist > 0 && (dist < best_dist || best_dist < 0)) { 136 *vendor_id = vid; 137 *product_id = pid; 138 best_dist = dist; 139 140 free(*phys_attr); 141 *phys_attr = safe_strdup(phys); 142 } 143 } 144 145 udev_device_unref(d); 146 } 147 148 udev_enumerate_unref(e); 149} 150#endif 151 152int main(int argc, char **argv) 153{ 154 int rc = 1; 155 struct udev *udev = NULL; 156 struct udev_device *device = NULL; 157 const char *syspath, 158 *phys = NULL; 159 const char *product; 160 int bustype, vendor_id, product_id, version; 161 char group[1024]; 162 char *str; 163 164 if (argc != 2) 165 return 1; 166 167 syspath = argv[1]; 168 169 udev = udev_new(); 170 if (!udev) 171 goto out; 172 173 device = udev_device_new_from_syspath(udev, syspath); 174 if (!device) 175 goto out; 176 177 /* Find the first parent with ATTRS{phys} set. For tablets that 178 * value looks like usb-0000:00:14.0-1/input1. Drop the /input1 179 * bit and use the remainder as device group identifier */ 180 while (device != NULL) { 181 struct udev_device *parent; 182 183 phys = udev_device_get_sysattr_value(device, "phys"); 184 if (phys) 185 break; 186 187 parent = udev_device_get_parent(device); 188 udev_device_ref(parent); 189 udev_device_unref(device); 190 device = parent; 191 } 192 193 if (!phys) 194 goto out; 195 196 /* udev sets PRODUCT on the same device we find PHYS on, let's rely 197 on that*/ 198 product = udev_device_get_property_value(device, "PRODUCT"); 199 if (!product) 200 product = "00/00/00/00"; 201 202 if (sscanf(product, 203 "%x/%x/%x/%x", 204 &bustype, 205 &vendor_id, 206 &product_id, 207 &version) != 4) { 208 snprintf(group, sizeof(group), "%s:%s", product, phys); 209 } else { 210 char *physmatch = NULL; 211 212#if HAVE_LIBWACOM 213 if (vendor_id == VENDOR_ID_WACOM) { 214 if (product_id == PRODUCT_ID_WACOM_EKR) 215 wacom_handle_ekr(device, 216 &vendor_id, 217 &product_id, 218 &physmatch); 219 /* This is called for the EKR as well */ 220 wacom_handle_paired(device, 221 &vendor_id, 222 &product_id); 223 } 224#endif 225 snprintf(group, 226 sizeof(group), 227 "%x/%x/%x:%s", 228 bustype, 229 vendor_id, 230 product_id, 231 physmatch ? physmatch : phys); 232 233 free(physmatch); 234 } 235 236 str = strstr(group, "/input"); 237 if (str) 238 *str = '\0'; 239 240 /* Cintiq 22HD Touch has 241 usb-0000:00:14.0-6.3.1/input0 for the touch 242 usb-0000:00:14.0-6.3.0/input0 for the pen 243 Check if there's a . after the last -, if so, cut off the string 244 there. 245 */ 246 str = strrchr(group, '.'); 247 if (str && str > strrchr(group, '-')) 248 *str = '\0'; 249 250 printf("LIBINPUT_DEVICE_GROUP=%s\n", group); 251 252 rc = 0; 253out: 254 if (device) 255 udev_device_unref(device); 256 if (udev) 257 udev_unref(udev); 258 259 return rc; 260} 261