1 /*************************************************************************** 2 * 3 * devinfo_usb.h : USB devices 4 * 5 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 6 * Use is subject to license terms. 7 * 8 * Licensed under the Academic Free License version 2.1 9 * 10 **************************************************************************/ 11 12 #ifdef HAVE_CONFIG_H 13 # include <config.h> 14 #endif 15 16 #include <stdio.h> 17 #include <string.h> 18 #include <libdevinfo.h> 19 #include <unistd.h> 20 #include <dirent.h> 21 #include <sys/types.h> 22 #include <sys/mkdev.h> 23 #include <sys/stat.h> 24 #include <sys/usb/usbai.h> 25 26 #include "../osspec.h" 27 #include "../logger.h" 28 #include "../hald.h" 29 #include "../hald_dbus.h" 30 #include "../device_info.h" 31 #include "../util.h" 32 #include "../ids.h" 33 #include "hotplug.h" 34 #include "devinfo.h" 35 #include "devinfo_usb.h" 36 37 static HalDevice *devinfo_usb_if_add(HalDevice *d, di_node_t node, gchar *devfs_path, 38 gchar *if_devfs_path, int ifnum); 39 static HalDevice *devinfo_usb_scsa2usb_add(HalDevice *d, di_node_t node); 40 static HalDevice *devinfo_usb_printer_add(HalDevice *usbd, di_node_t node); 41 static HalDevice *devinfo_usb_input_add(HalDevice *usbd, di_node_t node); 42 const gchar *devinfo_printer_prnio_get_prober(HalDevice *d, int *timeout); 43 const gchar *devinfo_keyboard_get_prober(HalDevice *d, int *timeout); 44 static void set_usb_properties(HalDevice *d, di_node_t node, gchar *devfs_path, char *driver_name); 45 46 DevinfoDevHandler devinfo_usb_handler = { 47 devinfo_usb_add, 48 NULL, 49 NULL, 50 NULL, 51 NULL, 52 NULL 53 }; 54 55 DevinfoDevHandler devinfo_usb_printer_handler = { 56 devinfo_usb_add, 57 NULL, 58 NULL, 59 NULL, 60 NULL, 61 devinfo_printer_prnio_get_prober 62 }; 63 64 DevinfoDevHandler devinfo_usb_keyboard_handler = { 65 devinfo_usb_add, 66 NULL, 67 NULL, 68 NULL, 69 NULL, 70 devinfo_keyboard_get_prober 71 }; 72 73 static gboolean 74 is_usb_node(di_node_t node) 75 { 76 int rc; 77 char *s; 78 79 /* 80 * USB device nodes will have "compatible" propety values that 81 * begins with "usb". 82 */ 83 rc = di_prop_lookup_strings(DDI_DEV_T_ANY, node, "compatible", &s); 84 while (rc-- > 0) { 85 if (strncmp(s, "usb", 3) == 0) { 86 return (TRUE); 87 } 88 s += (strlen(s) + 1); 89 } 90 91 return (FALSE); 92 } 93 94 static char * 95 get_hid_devlink(char *devfs_path) 96 { 97 char *result = NULL; 98 DIR *dp; 99 100 if ((dp = opendir("/dev/usb")) != NULL) { 101 struct dirent *ep; 102 103 while ((ep = readdir(dp)) != NULL) { 104 char path[MAXPATHLEN], lpath[MAXPATHLEN]; 105 106 snprintf(path, sizeof (path), "/dev/usb/%s", 107 ep->d_name); 108 memset(lpath, 0, sizeof (lpath)); 109 if ((readlink(path, lpath, sizeof (lpath)) > 0) && 110 (strstr(lpath, devfs_path) != NULL)) { 111 result = strdup(path); 112 break; 113 } 114 } 115 closedir(dp); 116 } 117 118 return (result); 119 } 120 121 HalDevice * 122 devinfo_usb_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type) 123 { 124 HalDevice *d, *nd = NULL; 125 char *s; 126 int *i; 127 char *driver_name, *binding_name; 128 char if_devfs_path[HAL_PATH_MAX]; 129 di_devlink_handle_t hdl; 130 double k; 131 132 if (is_usb_node(node) == FALSE) { 133 return (NULL); 134 } 135 136 driver_name = di_driver_name (node); 137 138 if (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "interface", &i) < 0) { 139 /* It is a USB device node. */ 140 141 d = hal_device_new (); 142 143 devinfo_set_default_properties (d, parent, node, devfs_path); 144 hal_device_property_set_string (d, "info.subsystem", "usb_device"); 145 PROP_STR(d, node, s, "usb-product-name", "info.product"); 146 PROP_STR(d, node, s, "usb-product-name", "usb_device.product"); 147 PROP_STR(d, node, s, "usb-vendor-name", "usb_device.vendor"); 148 PROP_INT(d, node, i, "usb-vendor-id", "usb_device.vendor_id"); 149 PROP_INT(d, node, i, "usb-product-id", "usb_device.product_id"); 150 PROP_INT(d, node, i, "usb-revision-id", "usb_device.device_revision_bcd"); 151 PROP_STR(d, node, s, "usb-serialno", "usb_device.serial"); 152 PROP_INT(d, node, i, "usb-port-count", "usb_device.num_ports"); 153 PROP_INT(d, node, i, "usb-num-configs", "usb_device.num_configurations"); 154 PROP_INT(d, node, i, "assigned-address", "usb_device.bus_number"); 155 156 if (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "usb-release", &i) > 0) { 157 k = (double)bcd(*i); 158 hal_device_property_set_double (d, "usb_device.version", k / 100); 159 } 160 161 if (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "low-speed", &i) >= 0) { 162 k = 1.5; 163 } else if (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "high-speed", &i) >= 0) { 164 k = 480.0; 165 } else { 166 /* It is the full speed device. */ 167 k = 12.0; 168 } 169 hal_device_property_set_double (d, "usb_device.speed", k); 170 171 set_usb_properties (d, node, devfs_path, driver_name); 172 173 /* wait for the ugen node's creation */ 174 if ((driver_name != NULL) && (strcmp (driver_name, "usb_mid") == 0)) { 175 if (hdl = di_devlink_init (devfs_path, DI_MAKE_LINK)) { 176 di_devlink_fini (&hdl); 177 } 178 } 179 180 devinfo_add_enqueue (d, devfs_path, &devinfo_usb_handler); 181 182 /* add to TDL so preprobing callouts and prober can access it */ 183 hal_device_store_add (hald_get_tdl (), d); 184 185 if (((binding_name = di_binding_name (node)) != NULL) && 186 (strncmp (binding_name, "usbif,", sizeof ("usbif,") - 1) == 0)) { 187 188 snprintf (if_devfs_path, sizeof (if_devfs_path), "%s:if%d", 189 devfs_path, 0); 190 if ((nd = devinfo_usb_if_add (d, node, if_devfs_path, 191 if_devfs_path, 0)) != NULL) { 192 d = nd; 193 nd = NULL; 194 devfs_path = if_devfs_path; 195 } 196 } 197 } else { 198 /* It is a USB interface node or IA node. */ 199 int *j; 200 201 if (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "interface-count", &j) > 0) { 202 /* 203 * The USB IA node properties are not defined in 204 * HAL spec so far. So IA node udi has "ia" sign 205 * now, different from the IF node udi with "if". 206 */ 207 snprintf (if_devfs_path, sizeof (if_devfs_path), 208 "%s:ia%d", devfs_path, *i); 209 } else { 210 snprintf (if_devfs_path, sizeof (if_devfs_path), 211 "%s:if%d", devfs_path, *i); 212 } 213 214 d = devinfo_usb_if_add (parent, node, devfs_path, if_devfs_path, *i); 215 } 216 217 /* driver specific */ 218 if (driver_name != NULL) { 219 if (strcmp (driver_name, "scsa2usb") == 0) { 220 nd = devinfo_usb_scsa2usb_add (d, node); 221 } else if (strcmp (driver_name, "usbprn") == 0) { 222 nd = devinfo_usb_printer_add (d, node); 223 } else if (strcmp(driver_name, "hid") == 0) { 224 if (hdl = di_devlink_init(devfs_path, DI_MAKE_LINK)) { 225 di_devlink_fini(&hdl); 226 } 227 nd = devinfo_usb_input_add(d, node); 228 } 229 } 230 231 out: 232 if (nd != NULL) { 233 return (nd); 234 } else { 235 return (d); 236 } 237 } 238 239 240 static void 241 set_usb_properties(HalDevice *d, di_node_t node, gchar *devfs_path, char *driver_name) 242 { 243 usb_dev_descr_t *dev_descrp = NULL; /* device descriptor */ 244 usb_cfg_descr_t *cfg_descrp = NULL; /* configuration descriptor */ 245 unsigned char *rdata = NULL; 246 char *p; 247 int i = 0; 248 249 hal_device_property_set_int (d, "usb_device.port_number", 250 atoi (devfs_path + strlen (devfs_path) -1)); 251 252 if (di_prop_lookup_bytes (DDI_DEV_T_ANY, node, "usb-dev-descriptor", 253 &rdata) > 0) { 254 dev_descrp = (usb_dev_descr_t *)rdata; 255 256 if (dev_descrp != NULL) { 257 hal_device_property_set_int (d, "usb_device.device_class", 258 dev_descrp->bDeviceClass); 259 hal_device_property_set_int (d, "usb_device.device_subclass", 260 dev_descrp->bDeviceSubClass); 261 hal_device_property_set_int (d, "usb_device.device_protocol", 262 dev_descrp->bDeviceProtocol); 263 } 264 } 265 266 if (di_prop_lookup_bytes (DDI_DEV_T_ANY, node, "usb-raw-cfg-descriptors", 267 &rdata) > 0) { 268 cfg_descrp = (usb_cfg_descr_t *)(rdata); 269 270 if (cfg_descrp != NULL) { 271 hal_device_property_set_int (d, "usb_device.configuration_value", 272 cfg_descrp->bConfigurationValue); 273 hal_device_property_set_int (d, "usb_device.max_power", 274 cfg_descrp->bMaxPower); 275 hal_device_property_set_int (d, "usb_device.num_interfaces", 276 cfg_descrp->bNumInterfaces); 277 hal_device_property_set_bool (d, "usb_device.can_wake_up", 278 (cfg_descrp->bmAttributes & 0x20) ? TRUE : FALSE); 279 hal_device_property_set_bool (d, "usb_device.is_self_powered", 280 (cfg_descrp->bmAttributes & 0x40) ? TRUE : FALSE); 281 } 282 } 283 284 /* get the node's usb tree level by counting hub numbers */ 285 do { 286 if (p = strstr (devfs_path, "/hub@")) { 287 devfs_path = p + strlen ("/hub@"); 288 i ++; 289 } 290 } while (p != NULL); 291 292 if ((driver_name != NULL) && (strcmp (driver_name, "hubd") == 0) && (i > 0)) 293 i --; 294 295 hal_device_property_set_int (d, "usb_device.level_number", i); 296 } 297 298 299 static usb_if_descr_t * 300 parse_usb_if_descr(di_node_t node, int ifnum) 301 { 302 unsigned char *rdata = NULL; 303 usb_if_descr_t *if_descrp=NULL; /* interface descriptor */ 304 di_node_t tmp_node = DI_NODE_NIL; 305 uint8_t num, length, type; 306 int rlen; 307 gchar *devpath = NULL; 308 309 if ((rlen = di_prop_lookup_bytes (DDI_DEV_T_ANY, node, 310 "usb-raw-cfg-descriptors", &rdata)) < 0) { 311 312 char *p; 313 int i; 314 315 if ((devpath = di_devfs_path (node)) == NULL) 316 goto out; 317 318 /* Look up its parent that may be a USB IA or USB mid. */ 319 for (i = 0; i < 2; i++) { 320 p = strrchr (devpath, '/'); 321 if (p == NULL) 322 goto out; 323 *p = '\0'; 324 325 if ((tmp_node = di_init (devpath, DINFOCPYALL)) == DI_NODE_NIL) 326 goto out; 327 328 if ((rlen = di_prop_lookup_bytes (DDI_DEV_T_ANY, tmp_node, 329 "usb-raw-cfg-descriptors", &rdata)) > 0) 330 break; 331 332 di_fini (tmp_node); 333 } 334 } 335 336 if (rdata == NULL) 337 goto out; 338 339 do { 340 length = (uint8_t)*rdata; 341 type = (uint8_t)*(rdata + 1); 342 if (type == USB_DESCR_TYPE_IF) { 343 num = (uint8_t)*(rdata + 2); 344 if (num == ifnum) { 345 if_descrp = (usb_if_descr_t *)rdata; 346 break; 347 } 348 } 349 rdata += length; 350 rlen -= length; 351 } while ((length > 0 ) && (rlen > 0)); 352 353 out: 354 if (devpath != NULL) 355 di_devfs_path_free (devpath); 356 if (tmp_node != DI_NODE_NIL) 357 di_fini (tmp_node); 358 return (if_descrp); 359 } 360 361 362 static HalDevice * 363 devinfo_usb_if_add(HalDevice *parent, di_node_t node, gchar *devfs_path, 364 gchar *if_devfs_path, int ifnum) 365 { 366 HalDevice *d = NULL; 367 char udi[HAL_PATH_MAX]; 368 const char *parent_info; 369 usb_if_descr_t *if_descrp=NULL; /* interface descriptor */ 370 371 d = hal_device_new (); 372 373 devinfo_set_default_properties (d, parent, node, if_devfs_path); 374 375 /* Set the existed physical device path. */ 376 hal_device_property_set_string (d, "solaris.devfs_path", devfs_path); 377 hal_device_property_set_string (d, "info.subsystem", "usb"); 378 hal_device_property_set_string (d, "info.product", "USB Device Interface"); 379 380 /* Set usb interface properties to interface node. */ 381 if (strstr (if_devfs_path, ":ia") == NULL) { 382 if_descrp = parse_usb_if_descr (node, ifnum); 383 384 if (if_descrp != NULL) { 385 hal_device_property_set_int (d, "usb.interface.class", 386 if_descrp->bInterfaceClass); 387 hal_device_property_set_int (d, "usb.interface.subclass", 388 if_descrp->bInterfaceSubClass); 389 hal_device_property_set_int (d, "usb.interface.protocol", 390 if_descrp->bInterfaceProtocol); 391 hal_device_property_set_int (d, "usb.interface.number", 392 if_descrp->bInterfaceNumber); 393 } 394 } 395 396 /* copy parent's usb_device.* properties */ 397 parent_info = hal_device_property_get_string (parent, "info.subsystem"); 398 if (parent_info != NULL) { 399 if (strcmp (parent_info, "usb_device") == 0) { 400 hal_device_merge_with_rewrite (d, parent, "usb.", "usb_device."); 401 } else if (strcmp (parent_info, "usb") == 0) { 402 /* for the case that the parent is IA node */ 403 hal_device_merge_with_rewrite (d, parent, "usb.", "usb."); 404 } 405 } 406 407 devinfo_add_enqueue (d, devfs_path, &devinfo_usb_handler); 408 409 /* add to TDL so preprobing callouts and prober can access it */ 410 hal_device_store_add (hald_get_tdl (), d); 411 412 return (d); 413 } 414 415 416 static void 417 get_dev_link_path(di_node_t node, char *nodetype, char *re, char **devlink, char **minor_path, char **minor_name) 418 { 419 di_devlink_handle_t devlink_hdl; 420 int major; 421 di_minor_t minor; 422 dev_t devt; 423 424 *devlink = NULL; 425 *minor_path = NULL; 426 *minor_name = NULL; 427 428 if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) { 429 return; 430 } 431 432 major = di_driver_major(node); 433 minor = DI_MINOR_NIL; 434 while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) { 435 devt = di_minor_devt(minor); 436 if (major != major(devt)) { 437 continue; 438 } 439 440 if (di_minor_type(minor) != DDM_MINOR) { 441 continue; 442 } 443 444 if ((*minor_path = di_devfs_minor_path(minor)) == NULL) { 445 continue; 446 } 447 448 if (strcmp(di_minor_nodetype(minor), nodetype) == 0) { 449 *devlink = get_devlink(devlink_hdl, re, *minor_path); 450 /* 451 * During hotplugging, devlink could be NULL for hid 452 * devices due to devlink database has not yet been 453 * updated when hal try to read from it although the 454 * actually dev link path has been created. In such a 455 * situation, we will read the devlink name from 456 * /dev/usb directory. 457 */ 458 if ((*devlink == NULL) && (strstr(re, "hid") != NULL)) { 459 *devlink = get_hid_devlink(*minor_path); 460 } 461 462 if (*devlink != NULL) { 463 *minor_name = di_minor_name(minor); 464 break; 465 } 466 } 467 468 di_devfs_path_free (*minor_path); 469 *minor_path = NULL; 470 } 471 di_devlink_fini (&devlink_hdl); 472 } 473 474 static HalDevice * 475 devinfo_usb_input_add(HalDevice *usbd, di_node_t node) 476 { 477 HalDevice *d = NULL; 478 int major; 479 di_minor_t minor; 480 dev_t devt; 481 char *devlink = NULL; 482 char *minor_path = NULL; 483 char *minor_name = NULL; 484 char udi[HAL_PATH_MAX]; 485 486 get_dev_link_path(node, "ddi_pseudo", 487 "^usb/hid+", &devlink, &minor_path, &minor_name); 488 489 if ((minor_path == NULL) || (devlink == NULL)) { 490 491 goto out; 492 } 493 494 HAL_DEBUG(("devlink %s, minor_name %s", devlink, minor_name)); 495 if ((strcmp(minor_name, "keyboard") != 0) && 496 (strcmp(minor_name, "mouse") != 0)) { 497 498 goto out; 499 } 500 501 d = hal_device_new(); 502 503 devinfo_set_default_properties(d, usbd, node, minor_path); 504 hal_device_property_set_string(d, "info.subsystem", "input"); 505 hal_device_property_set_string(d, "info.category", "input"); 506 507 hal_device_add_capability(d, "input"); 508 509 if (strcmp(minor_name, "keyboard") == 0) { 510 hal_device_add_capability(d, "input.keyboard"); 511 hal_device_add_capability(d, "input.keys"); 512 hal_device_add_capability(d, "button"); 513 } else if (strcmp(minor_name, "mouse") == 0) { 514 hal_device_add_capability (d, "input.mouse"); 515 } 516 517 hal_device_property_set_string(d, "input.device", devlink); 518 hal_device_property_set_string(d, "input.originating_device", 519 hal_device_get_udi(usbd)); 520 521 hal_util_compute_udi(hald_get_gdl(), udi, sizeof (udi), 522 "%s_logicaldev_input", hal_device_get_udi(usbd)); 523 524 hal_device_set_udi(d, udi); 525 hal_device_property_set_string(d, "info.udi", udi); 526 527 if (strcmp(minor_name, "keyboard") == 0) { 528 devinfo_add_enqueue(d, minor_path, &devinfo_usb_keyboard_handler); 529 } else { 530 devinfo_add_enqueue(d, minor_path, &devinfo_usb_handler); 531 } 532 533 /* add to TDL so preprobing callouts and prober can access it */ 534 hal_device_store_add(hald_get_tdl(), d); 535 536 out: 537 if (devlink) { 538 free(devlink); 539 } 540 541 if (minor_path) { 542 di_devfs_path_free(minor_path); 543 } 544 545 return (d); 546 } 547 548 static HalDevice * 549 devinfo_usb_scsa2usb_add(HalDevice *usbd, di_node_t node) 550 { 551 HalDevice *d = NULL; 552 di_devlink_handle_t devlink_hdl; 553 int major; 554 di_minor_t minor; 555 dev_t devt; 556 char *minor_path = NULL; 557 char *minor_name = NULL; 558 char *devlink = NULL; 559 char udi[HAL_PATH_MAX]; 560 561 get_dev_link_path(node, "ddi_ctl:devctl:scsi", NULL, &devlink, &minor_path, &minor_name); 562 563 if ((devlink == NULL) || (minor_path == NULL)) { 564 goto out; 565 } 566 567 d = hal_device_new (); 568 569 devinfo_set_default_properties (d, usbd, node, minor_path); 570 hal_device_property_set_string (d, "scsi_host.solaris.device", devlink); 571 hal_device_property_set_string (d, "info.category", "scsi_host"); 572 hal_device_property_set_int (d, "scsi_host.host", 0); 573 574 hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi), 575 "%s/scsi_host%d", hal_device_get_udi (usbd), 576 hal_device_property_get_int (d, "scsi_host.host")); 577 hal_device_set_udi (d, udi); 578 hal_device_property_set_string (d, "info.udi", udi); 579 hal_device_property_set_string (d, "info.product", "SCSI Host Adapter"); 580 581 devinfo_add_enqueue (d, minor_path, &devinfo_usb_handler); 582 583 out: 584 if (devlink) { 585 free(devlink); 586 } 587 if (minor_path) { 588 di_devfs_path_free (minor_path); 589 } 590 591 return (d); 592 } 593 594 static HalDevice * 595 devinfo_usb_printer_add(HalDevice *parent, di_node_t node) 596 { 597 char *properties[] = { "vendor", "product", "serial", NULL }; 598 int i; 599 HalDevice *d = NULL; 600 char udi[HAL_PATH_MAX]; 601 char *s; 602 char *devlink = NULL, *minor_path = NULL, *minor_name = NULL; 603 const char *subsystem; 604 605 get_dev_link_path(node, "ddi_printer", "printers/.+", &devlink, &minor_path, &minor_name); 606 607 if ((devlink == NULL) || (minor_path == NULL)) { 608 goto out; 609 } 610 611 d = hal_device_new (); 612 613 devinfo_set_default_properties (d, parent, node, minor_path); 614 hal_device_property_set_string (d, "info.category", "printer"); 615 hal_device_add_capability (d, "printer"); 616 617 /* add printer properties */ 618 hal_device_property_set_string (d, "printer.device", devlink); 619 620 /* copy parent's selected usb* properties to printer properties */ 621 subsystem = hal_device_property_get_string (parent, "info.subsystem"); 622 for (i = 0; properties[i] != NULL; i++) { 623 char src[32], dst[32]; /* "subsystem.property" names */ 624 625 snprintf(src, sizeof (src), "%s.%s", subsystem, properties[i]); 626 snprintf(dst, sizeof (dst), "printer.%s", properties[i]); 627 hal_device_copy_property(parent, src, d, dst); 628 } 629 630 devinfo_add_enqueue (d, minor_path, &devinfo_usb_printer_handler); 631 632 out: 633 if (devlink) { 634 free(devlink); 635 } 636 if (minor_path) { 637 di_devfs_path_free (minor_path); 638 } 639 640 return (d); 641 } 642 643 const gchar * 644 devinfo_printer_prnio_get_prober (HalDevice *d, int *timeout) 645 { 646 *timeout = 5 * 1000; /* 5 second timeout */ 647 return ("hald-probe-printer"); 648 } 649 650 const gchar * 651 devinfo_keyboard_get_prober(HalDevice *d, int *timeout) 652 { 653 *timeout = 5 * 1000; /* 5 second timeout */ 654 return ("hald-probe-xkb"); 655 } 656