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 <sys/types.h> 20 #include <sys/mkdev.h> 21 #include <sys/stat.h> 22 #include <sys/usb/usbai.h> 23 24 #include "../osspec.h" 25 #include "../logger.h" 26 #include "../hald.h" 27 #include "../hald_dbus.h" 28 #include "../device_info.h" 29 #include "../util.h" 30 #include "../ids.h" 31 #include "hotplug.h" 32 #include "devinfo.h" 33 #include "devinfo_usb.h" 34 35 static HalDevice *devinfo_usb_if_add(HalDevice *d, di_node_t node, gchar *devfs_path, 36 gchar *if_devfs_path, int ifnum); 37 static HalDevice *devinfo_usb_scsa2usb_add(HalDevice *d, di_node_t node); 38 static HalDevice *devinfo_usb_printer_add(HalDevice *usbd, di_node_t node); 39 const gchar *devinfo_printer_prnio_get_prober (HalDevice *d, int *timeout); 40 static void set_usb_properties(HalDevice *d, di_node_t node, gchar *devfs_path, char *driver_name); 41 42 DevinfoDevHandler devinfo_usb_handler = { 43 devinfo_usb_add, 44 NULL, 45 NULL, 46 NULL, 47 NULL, 48 NULL 49 }; 50 51 DevinfoDevHandler devinfo_usb_printer_handler = { 52 devinfo_usb_add, 53 NULL, 54 NULL, 55 NULL, 56 NULL, 57 devinfo_printer_prnio_get_prober 58 }; 59 60 static gboolean 61 is_usb_node(di_node_t node) 62 { 63 int rc; 64 char *s; 65 66 /* 67 * USB device nodes will have "compatible" propety values that 68 * begins with "usb". 69 */ 70 rc = di_prop_lookup_strings(DDI_DEV_T_ANY, node, "compatible", &s); 71 while (rc-- > 0) { 72 if (strncmp(s, "usb", 3) == 0) { 73 return (TRUE); 74 } 75 s += (strlen(s) + 1); 76 } 77 78 return (FALSE); 79 } 80 81 HalDevice * 82 devinfo_usb_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type) 83 { 84 HalDevice *d, *nd = NULL; 85 char *s; 86 int *i; 87 char *driver_name, *binding_name; 88 char if_devfs_path[HAL_PATH_MAX]; 89 di_devlink_handle_t hdl; 90 double k; 91 92 if (is_usb_node(node) == FALSE) { 93 return (NULL); 94 } 95 96 driver_name = di_driver_name (node); 97 98 if (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "interface", &i) < 0) { 99 /* It is a USB device node. */ 100 101 d = hal_device_new (); 102 103 devinfo_set_default_properties (d, parent, node, devfs_path); 104 hal_device_property_set_string (d, "info.subsystem", "usb_device"); 105 PROP_STR(d, node, s, "usb-product-name", "info.product"); 106 PROP_STR(d, node, s, "usb-product-name", "usb_device.product"); 107 PROP_STR(d, node, s, "usb-vendor-name", "usb_device.vendor"); 108 PROP_INT(d, node, i, "usb-vendor-id", "usb_device.vendor_id"); 109 PROP_INT(d, node, i, "usb-product-id", "usb_device.product_id"); 110 PROP_INT(d, node, i, "usb-revision-id", "usb_device.device_revision_bcd"); 111 PROP_STR(d, node, s, "usb-serialno", "usb_device.serial"); 112 PROP_INT(d, node, i, "usb-port-count", "usb_device.num_ports"); 113 PROP_INT(d, node, i, "usb-num-configs", "usb_device.num_configurations"); 114 PROP_INT(d, node, i, "assigned-address", "usb_device.bus_number"); 115 116 if (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "usb-release", &i) > 0) { 117 k = (double)bcd(*i); 118 hal_device_property_set_double (d, "usb_device.version", k / 100); 119 } 120 121 if (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "low-speed", &i) >= 0) { 122 k = 1.5; 123 } else if (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "high-speed", &i) >= 0) { 124 k = 480.0; 125 } else { 126 /* It is the full speed device. */ 127 k = 12.0; 128 } 129 hal_device_property_set_double (d, "usb_device.speed", k); 130 131 set_usb_properties (d, node, devfs_path, driver_name); 132 133 /* wait for the ugen node's creation */ 134 if ((driver_name != NULL) && (strcmp (driver_name, "usb_mid") == 0)) { 135 if (hdl = di_devlink_init (devfs_path, DI_MAKE_LINK)) { 136 di_devlink_fini (&hdl); 137 } 138 } 139 140 devinfo_add_enqueue (d, devfs_path, &devinfo_usb_handler); 141 142 /* add to TDL so preprobing callouts and prober can access it */ 143 hal_device_store_add (hald_get_tdl (), d); 144 145 if (((binding_name = di_binding_name (node)) != NULL) && 146 (strncmp (binding_name, "usbif,", sizeof ("usbif,") - 1) == 0)) { 147 148 snprintf (if_devfs_path, sizeof (if_devfs_path), "%s:if%d", 149 devfs_path, 0); 150 if ((nd = devinfo_usb_if_add (d, node, if_devfs_path, 151 if_devfs_path, 0)) != NULL) { 152 153 d = nd; 154 nd = NULL; 155 devfs_path = if_devfs_path; 156 } 157 } 158 } else { 159 /* It is a USB interface node or IA node. */ 160 int *j; 161 162 if (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "interface-count", &j) > 0) { 163 /* 164 * The USB IA node properties are not defined in 165 * HAL spec so far. So IA node udi has "ia" sign 166 * now, different from the IF node udi with "if". 167 */ 168 snprintf (if_devfs_path, sizeof (if_devfs_path), 169 "%s:ia%d", devfs_path, *i); 170 } else { 171 snprintf (if_devfs_path, sizeof (if_devfs_path), 172 "%s:if%d", devfs_path, *i); 173 } 174 175 d = devinfo_usb_if_add (parent, node, devfs_path, if_devfs_path, *i); 176 } 177 178 /* driver specific */ 179 if ((driver_name != NULL) && (strcmp (driver_name, "scsa2usb") == 0)) { 180 nd = devinfo_usb_scsa2usb_add (d, node); 181 } else if ((driver_name != NULL) && 182 (strcmp (driver_name, "usbprn") == 0)) { 183 nd = devinfo_usb_printer_add (d, node); 184 } 185 186 out: 187 if (nd != NULL) { 188 return (nd); 189 } else { 190 return (d); 191 } 192 } 193 194 195 static void 196 set_usb_properties(HalDevice *d, di_node_t node, gchar *devfs_path, char *driver_name) 197 { 198 usb_dev_descr_t *dev_descrp = NULL; /* device descriptor */ 199 usb_cfg_descr_t *cfg_descrp = NULL; /* configuration descriptor */ 200 unsigned char *rdata = NULL; 201 char *p; 202 int i = 0; 203 204 hal_device_property_set_int (d, "usb_device.port_number", 205 atoi (devfs_path + strlen (devfs_path) -1)); 206 207 if (di_prop_lookup_bytes (DDI_DEV_T_ANY, node, "usb-dev-descriptor", 208 &rdata) > 0) { 209 dev_descrp = (usb_dev_descr_t *)rdata; 210 211 if (dev_descrp != NULL) { 212 hal_device_property_set_int (d, "usb_device.device_class", 213 dev_descrp->bDeviceClass); 214 hal_device_property_set_int (d, "usb_device.device_subclass", 215 dev_descrp->bDeviceSubClass); 216 hal_device_property_set_int (d, "usb_device.device_protocol", 217 dev_descrp->bDeviceProtocol); 218 } 219 } 220 221 if (di_prop_lookup_bytes (DDI_DEV_T_ANY, node, "usb-raw-cfg-descriptors", 222 &rdata) > 0) { 223 cfg_descrp = (usb_cfg_descr_t *)(rdata); 224 225 if (cfg_descrp != NULL) { 226 hal_device_property_set_int (d, "usb_device.configuration_value", 227 cfg_descrp->bConfigurationValue); 228 hal_device_property_set_int (d, "usb_device.max_power", 229 cfg_descrp->bMaxPower); 230 hal_device_property_set_int (d, "usb_device.num_interfaces", 231 cfg_descrp->bNumInterfaces); 232 hal_device_property_set_bool (d, "usb_device.can_wake_up", 233 (cfg_descrp->bmAttributes & 0x20) ? TRUE : FALSE); 234 hal_device_property_set_bool (d, "usb_device.is_self_powered", 235 (cfg_descrp->bmAttributes & 0x40) ? TRUE : FALSE); 236 } 237 } 238 239 /* get the node's usb tree level by counting hub numbers */ 240 do { 241 if (p = strstr (devfs_path, "/hub@")) { 242 devfs_path = p + strlen ("/hub@"); 243 i ++; 244 } 245 } while (p != NULL); 246 247 if ((driver_name != NULL) && (strcmp (driver_name, "hubd") == 0) && (i > 0)) 248 i --; 249 250 hal_device_property_set_int (d, "usb_device.level_number", i); 251 } 252 253 254 static usb_if_descr_t * 255 parse_usb_if_descr(di_node_t node, int ifnum) 256 { 257 unsigned char *rdata = NULL; 258 usb_if_descr_t *if_descrp=NULL; /* interface descriptor */ 259 di_node_t tmp_node = DI_NODE_NIL; 260 uint8_t num, length, type; 261 int rlen; 262 gchar *devpath = NULL; 263 264 if ((rlen = di_prop_lookup_bytes (DDI_DEV_T_ANY, node, 265 "usb-raw-cfg-descriptors", &rdata)) < 0) { 266 267 char *p; 268 int i; 269 270 if ((devpath = di_devfs_path (node)) == NULL) 271 goto out; 272 273 /* Look up its parent that may be a USB IA or USB mid. */ 274 for (i = 0; i < 2; i++) { 275 p = strrchr (devpath, '/'); 276 if (p == NULL) 277 goto out; 278 *p = '\0'; 279 280 if ((tmp_node = di_init (devpath, DINFOCPYALL)) == DI_NODE_NIL) 281 goto out; 282 283 if ((rlen = di_prop_lookup_bytes (DDI_DEV_T_ANY, tmp_node, 284 "usb-raw-cfg-descriptors", &rdata)) > 0) 285 break; 286 287 di_fini (tmp_node); 288 } 289 } 290 291 if (rdata == NULL) 292 goto out; 293 294 do { 295 length = (uint8_t)*rdata; 296 type = (uint8_t)*(rdata + 1); 297 if (type == USB_DESCR_TYPE_IF) { 298 num = (uint8_t)*(rdata + 2); 299 if (num == ifnum) { 300 if_descrp = (usb_if_descr_t *)rdata; 301 break; 302 } 303 } 304 rdata += length; 305 rlen -= length; 306 } while ((length > 0 ) && (rlen > 0)); 307 308 out: 309 if (devpath != NULL) 310 di_devfs_path_free (devpath); 311 if (tmp_node != DI_NODE_NIL) 312 di_fini (tmp_node); 313 return (if_descrp); 314 } 315 316 317 static HalDevice * 318 devinfo_usb_if_add(HalDevice *parent, di_node_t node, gchar *devfs_path, 319 gchar *if_devfs_path, int ifnum) 320 { 321 HalDevice *d = NULL; 322 char udi[HAL_PATH_MAX]; 323 const char *parent_info; 324 usb_if_descr_t *if_descrp=NULL; /* interface descriptor */ 325 326 d = hal_device_new (); 327 328 devinfo_set_default_properties (d, parent, node, if_devfs_path); 329 330 /* Set the existed physical device path. */ 331 hal_device_property_set_string (d, "solaris.devfs_path", devfs_path); 332 hal_device_property_set_string (d, "info.subsystem", "usb"); 333 hal_device_property_set_string (d, "info.product", "USB Device Interface"); 334 335 /* Set usb interface properties to interface node. */ 336 if (strstr (if_devfs_path, ":ia") == NULL) { 337 if_descrp = parse_usb_if_descr (node, ifnum); 338 339 if (if_descrp != NULL) { 340 hal_device_property_set_int (d, "usb.interface.class", 341 if_descrp->bInterfaceClass); 342 hal_device_property_set_int (d, "usb.interface.subclass", 343 if_descrp->bInterfaceSubClass); 344 hal_device_property_set_int (d, "usb.interface.protocol", 345 if_descrp->bInterfaceProtocol); 346 hal_device_property_set_int (d, "usb.interface.number", 347 if_descrp->bInterfaceNumber); 348 } 349 } 350 351 /* copy parent's usb_device.* properties */ 352 parent_info = hal_device_property_get_string (parent, "info.subsystem"); 353 if (parent_info != NULL) { 354 if (strcmp (parent_info, "usb_device") == 0) { 355 hal_device_merge_with_rewrite (d, parent, "usb.", "usb_device."); 356 } else if (strcmp (parent_info, "usb") == 0) { 357 /* for the case that the parent is IA node */ 358 hal_device_merge_with_rewrite (d, parent, "usb.", "usb."); 359 } 360 } 361 362 devinfo_add_enqueue (d, devfs_path, &devinfo_usb_handler); 363 364 /* add to TDL so preprobing callouts and prober can access it */ 365 hal_device_store_add (hald_get_tdl (), d); 366 367 return (d); 368 } 369 370 371 static void 372 get_dev_link_path(di_node_t node, char *nodetype, char *re, char **devlink, char **minor_path) 373 { 374 di_devlink_handle_t devlink_hdl; 375 int major; 376 di_minor_t minor; 377 dev_t devt; 378 379 *devlink = NULL; 380 *minor_path = NULL; 381 382 if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) { 383 printf("di_devlink_init() failed\n"); 384 return; 385 } 386 387 major = di_driver_major(node); 388 minor = DI_MINOR_NIL; 389 while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) { 390 devt = di_minor_devt(minor); 391 if (major != major(devt)) { 392 continue; 393 } 394 395 if (di_minor_type(minor) != DDM_MINOR) { 396 continue; 397 } 398 399 if ((*minor_path = di_devfs_minor_path(minor)) == NULL) { 400 continue; 401 } 402 403 if ((strcmp (di_minor_nodetype(minor), nodetype) == 0) && 404 ((*devlink = get_devlink(devlink_hdl, re, *minor_path)) != NULL)) { 405 break; 406 } 407 di_devfs_path_free (*minor_path); 408 *minor_path = NULL; 409 } 410 di_devlink_fini (&devlink_hdl); 411 } 412 413 static HalDevice * 414 devinfo_usb_scsa2usb_add(HalDevice *usbd, di_node_t node) 415 { 416 HalDevice *d = NULL; 417 di_devlink_handle_t devlink_hdl; 418 int major; 419 di_minor_t minor; 420 dev_t devt; 421 char *minor_path = NULL; 422 char *devlink = NULL; 423 char udi[HAL_PATH_MAX]; 424 425 get_dev_link_path(node, "ddi_ctl:devctl:scsi", NULL, &devlink, &minor_path); 426 427 if ((devlink == NULL) || (minor_path == NULL)) { 428 goto out; 429 } 430 431 d = hal_device_new (); 432 433 devinfo_set_default_properties (d, usbd, node, minor_path); 434 hal_device_property_set_string (d, "scsi_host.solaris.device", devlink); 435 hal_device_property_set_string (d, "info.category", "scsi_host"); 436 hal_device_property_set_int (d, "scsi_host.host", 0); 437 438 hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi), 439 "%s/scsi_host%d", hal_device_get_udi (usbd), 440 hal_device_property_get_int (d, "scsi_host.host")); 441 hal_device_set_udi (d, udi); 442 hal_device_property_set_string (d, "info.udi", udi); 443 hal_device_property_set_string (d, "info.product", "SCSI Host Adapter"); 444 445 devinfo_add_enqueue (d, minor_path, &devinfo_usb_handler); 446 447 out: 448 if (devlink) { 449 free(devlink); 450 } 451 if (minor_path) { 452 di_devfs_path_free (minor_path); 453 } 454 455 return (d); 456 } 457 458 static HalDevice * 459 devinfo_usb_printer_add(HalDevice *parent, di_node_t node) 460 { 461 char *properties[] = { "vendor", "product", "serial", NULL }; 462 int i; 463 HalDevice *d = NULL; 464 char udi[HAL_PATH_MAX]; 465 char *s; 466 char *devlink = NULL, *minor_path = NULL; 467 const char *subsystem; 468 469 get_dev_link_path(node, "ddi_printer", "printers/.+", &devlink, &minor_path); 470 471 if ((devlink == NULL) || (minor_path == NULL)) { 472 goto out; 473 } 474 475 d = hal_device_new (); 476 477 devinfo_set_default_properties (d, parent, node, minor_path); 478 hal_device_property_set_string (d, "info.category", "printer"); 479 hal_device_add_capability (d, "printer"); 480 481 /* add printer properties */ 482 hal_device_property_set_string (d, "printer.device", devlink); 483 484 /* copy parent's selected usb* properties to printer properties */ 485 subsystem = hal_device_property_get_string (parent, "info.subsystem"); 486 for (i = 0; properties[i] != NULL; i++) { 487 char src[32], dst[32]; /* "subsystem.property" names */ 488 489 snprintf(src, sizeof (src), "%s.%s", subsystem, properties[i]); 490 snprintf(dst, sizeof (dst), "printer.%s", properties[i]); 491 hal_device_copy_property(parent, src, d, dst); 492 } 493 494 devinfo_add_enqueue (d, minor_path, &devinfo_usb_printer_handler); 495 496 out: 497 if (devlink) { 498 free(devlink); 499 } 500 if (minor_path) { 501 di_devfs_path_free (minor_path); 502 } 503 504 return (d); 505 } 506 507 const gchar * 508 devinfo_printer_prnio_get_prober (HalDevice *d, int *timeout) 509 { 510 *timeout = 5 * 1000; /* 5 second timeout */ 511 return ("hald-probe-printer"); 512 } 513