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