1 /***************************************************************************
3 * devinfo_usb.h : USB devices
5 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
6 * Use is subject to license terms.
8 * Licensed under the Academic Free License version 2.1
10 **************************************************************************/
18 #include <libdevinfo.h>
21 #include <sys/types.h>
22 #include <sys/mkdev.h>
24 #include <sys/usb/usbai.h>
26 #include "../osspec.h"
27 #include "../logger.h"
29 #include "../hald_dbus.h"
30 #include "../device_info.h"
35 #include "devinfo_usb.h"
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 static HalDevice
*devinfo_usb_video4linux_add(HalDevice
*usbd
, di_node_t node
);
43 const gchar
*devinfo_printer_prnio_get_prober(HalDevice
*d
, int *timeout
);
44 const gchar
*devinfo_keyboard_get_prober(HalDevice
*d
, int *timeout
);
45 static void set_usb_properties(HalDevice
*d
, di_node_t node
, gchar
*devfs_path
, char *driver_name
);
47 DevinfoDevHandler devinfo_usb_handler
= {
56 DevinfoDevHandler devinfo_usb_printer_handler
= {
62 devinfo_printer_prnio_get_prober
65 DevinfoDevHandler devinfo_usb_keyboard_handler
= {
71 devinfo_keyboard_get_prober
75 is_usb_node(di_node_t node
)
81 * USB device nodes will have "compatible" propety values that
84 rc
= di_prop_lookup_strings(DDI_DEV_T_ANY
, node
, "compatible", &s
);
86 if (strncmp(s
, "usb", 3) == 0) {
96 get_usb_devlink(char *devfs_path
, const char *dir_name
)
101 if ((dp
= opendir(dir_name
)) != NULL
) {
104 while ((ep
= readdir(dp
)) != NULL
) {
105 char path
[MAXPATHLEN
], lpath
[MAXPATHLEN
];
107 strncpy(path
, dir_name
, strlen(dir_name
));
108 strncat(path
, ep
->d_name
, strlen(ep
->d_name
));
109 memset(lpath
, 0, sizeof (lpath
));
110 if ((readlink(path
, lpath
, sizeof (lpath
)) > 0) &&
111 (strstr(lpath
, devfs_path
) != NULL
)) {
112 result
= strdup(path
);
115 memset(path
, 0, sizeof (path
));
124 devinfo_usb_add(HalDevice
*parent
, di_node_t node
, char *devfs_path
, char *device_type
)
126 HalDevice
*d
, *nd
= NULL
;
129 char *driver_name
, *binding_name
;
130 char if_devfs_path
[HAL_PATH_MAX
];
131 di_devlink_handle_t hdl
;
134 if (is_usb_node(node
) == FALSE
) {
138 driver_name
= di_driver_name (node
);
140 if (di_prop_lookup_ints (DDI_DEV_T_ANY
, node
, "interface", &i
) < 0) {
141 /* It is a USB device node. */
143 d
= hal_device_new ();
145 devinfo_set_default_properties (d
, parent
, node
, devfs_path
);
146 hal_device_property_set_string (d
, "info.subsystem", "usb_device");
147 PROP_STR(d
, node
, s
, "usb-product-name", "info.product");
148 PROP_STR(d
, node
, s
, "usb-product-name", "usb_device.product");
149 PROP_STR(d
, node
, s
, "usb-vendor-name", "usb_device.vendor");
150 PROP_INT(d
, node
, i
, "usb-vendor-id", "usb_device.vendor_id");
151 PROP_INT(d
, node
, i
, "usb-product-id", "usb_device.product_id");
152 PROP_INT(d
, node
, i
, "usb-revision-id", "usb_device.device_revision_bcd");
153 PROP_STR(d
, node
, s
, "usb-serialno", "usb_device.serial");
154 PROP_INT(d
, node
, i
, "usb-port-count", "usb_device.num_ports");
155 PROP_INT(d
, node
, i
, "usb-num-configs", "usb_device.num_configurations");
156 PROP_INT(d
, node
, i
, "assigned-address", "usb_device.bus_number");
158 if (di_prop_lookup_ints (DDI_DEV_T_ANY
, node
, "usb-release", &i
) > 0) {
160 hal_device_property_set_double (d
, "usb_device.version", k
/ 100);
163 if (di_prop_lookup_ints (DDI_DEV_T_ANY
, node
, "low-speed", &i
) >= 0) {
165 } else if (di_prop_lookup_ints (DDI_DEV_T_ANY
, node
, "high-speed", &i
) >= 0) {
168 /* It is the full speed device. */
171 hal_device_property_set_double (d
, "usb_device.speed", k
);
173 set_usb_properties (d
, node
, devfs_path
, driver_name
);
175 /* wait for the ugen node's creation */
176 if ((driver_name
!= NULL
) && (strcmp (driver_name
, "usb_mid") == 0)) {
177 if (hdl
= di_devlink_init (devfs_path
, DI_MAKE_LINK
)) {
178 di_devlink_fini (&hdl
);
182 devinfo_add_enqueue (d
, devfs_path
, &devinfo_usb_handler
);
184 /* add to TDL so preprobing callouts and prober can access it */
185 hal_device_store_add (hald_get_tdl (), d
);
187 if (((binding_name
= di_binding_name (node
)) != NULL
) &&
188 (strncmp (binding_name
, "usbif,", sizeof ("usbif,") - 1) == 0)) {
190 snprintf (if_devfs_path
, sizeof (if_devfs_path
), "%s:if%d",
192 if ((nd
= devinfo_usb_if_add (d
, node
, if_devfs_path
,
193 if_devfs_path
, 0)) != NULL
) {
196 devfs_path
= if_devfs_path
;
200 /* It is a USB interface node or IA node. */
203 if (di_prop_lookup_ints (DDI_DEV_T_ANY
, node
, "interface-count", &j
) > 0) {
205 * The USB IA node properties are not defined in
206 * HAL spec so far. So IA node udi has "ia" sign
207 * now, different from the IF node udi with "if".
209 snprintf (if_devfs_path
, sizeof (if_devfs_path
),
210 "%s:ia%d", devfs_path
, *i
);
212 snprintf (if_devfs_path
, sizeof (if_devfs_path
),
213 "%s:if%d", devfs_path
, *i
);
216 d
= devinfo_usb_if_add (parent
, node
, devfs_path
, if_devfs_path
, *i
);
219 /* driver specific */
220 if (driver_name
!= NULL
) {
221 if (strcmp (driver_name
, "scsa2usb") == 0) {
222 nd
= devinfo_usb_scsa2usb_add (d
, node
);
223 } else if (strcmp (driver_name
, "usbprn") == 0) {
224 nd
= devinfo_usb_printer_add (d
, node
);
225 } else if (strcmp(driver_name
, "hid") == 0) {
226 if (hdl
= di_devlink_init(devfs_path
, DI_MAKE_LINK
)) {
227 di_devlink_fini(&hdl
);
229 nd
= devinfo_usb_input_add(d
, node
);
230 } else if (strcmp(driver_name
, "usbvc") == 0) {
231 if (hdl
= di_devlink_init(devfs_path
, DI_MAKE_LINK
)) {
232 di_devlink_fini(&hdl
);
234 nd
= devinfo_usb_video4linux_add(d
, node
);
248 set_usb_properties(HalDevice
*d
, di_node_t node
, gchar
*devfs_path
, char *driver_name
)
250 usb_dev_descr_t
*dev_descrp
= NULL
; /* device descriptor */
251 usb_cfg_descr_t
*cfg_descrp
= NULL
; /* configuration descriptor */
252 unsigned char *rdata
= NULL
;
256 hal_device_property_set_int (d
, "usb_device.port_number",
257 atoi (devfs_path
+ strlen (devfs_path
) -1));
259 if (di_prop_lookup_bytes (DDI_DEV_T_ANY
, node
, "usb-dev-descriptor",
261 dev_descrp
= (usb_dev_descr_t
*)rdata
;
263 if (dev_descrp
!= NULL
) {
264 hal_device_property_set_int (d
, "usb_device.device_class",
265 dev_descrp
->bDeviceClass
);
266 hal_device_property_set_int (d
, "usb_device.device_subclass",
267 dev_descrp
->bDeviceSubClass
);
268 hal_device_property_set_int (d
, "usb_device.device_protocol",
269 dev_descrp
->bDeviceProtocol
);
273 if (di_prop_lookup_bytes (DDI_DEV_T_ANY
, node
, "usb-raw-cfg-descriptors",
275 cfg_descrp
= (usb_cfg_descr_t
*)(rdata
);
277 if (cfg_descrp
!= NULL
) {
278 hal_device_property_set_int (d
, "usb_device.configuration_value",
279 cfg_descrp
->bConfigurationValue
);
280 hal_device_property_set_int (d
, "usb_device.max_power",
281 cfg_descrp
->bMaxPower
);
282 hal_device_property_set_int (d
, "usb_device.num_interfaces",
283 cfg_descrp
->bNumInterfaces
);
284 hal_device_property_set_bool (d
, "usb_device.can_wake_up",
285 (cfg_descrp
->bmAttributes
& 0x20) ? TRUE
: FALSE
);
286 hal_device_property_set_bool (d
, "usb_device.is_self_powered",
287 (cfg_descrp
->bmAttributes
& 0x40) ? TRUE
: FALSE
);
291 /* get the node's usb tree level by counting hub numbers */
293 if (p
= strstr (devfs_path
, "/hub@")) {
294 devfs_path
= p
+ strlen ("/hub@");
299 if ((driver_name
!= NULL
) && (strcmp (driver_name
, "hubd") == 0) && (i
> 0))
302 hal_device_property_set_int (d
, "usb_device.level_number", i
);
306 static usb_if_descr_t
*
307 parse_usb_if_descr(di_node_t node
, int ifnum
)
309 unsigned char *rdata
= NULL
;
310 usb_if_descr_t
*if_descrp
=NULL
; /* interface descriptor */
311 di_node_t tmp_node
= DI_NODE_NIL
;
312 uint8_t num
, length
, type
;
314 gchar
*devpath
= NULL
;
316 if ((rlen
= di_prop_lookup_bytes (DDI_DEV_T_ANY
, node
,
317 "usb-raw-cfg-descriptors", &rdata
)) < 0) {
322 if ((devpath
= di_devfs_path (node
)) == NULL
)
325 /* Look up its parent that may be a USB IA or USB mid. */
326 for (i
= 0; i
< 2; i
++) {
327 p
= strrchr (devpath
, '/');
332 if ((tmp_node
= di_init (devpath
, DINFOCPYALL
)) == DI_NODE_NIL
)
335 if ((rlen
= di_prop_lookup_bytes (DDI_DEV_T_ANY
, tmp_node
,
336 "usb-raw-cfg-descriptors", &rdata
)) > 0)
347 length
= (uint8_t)*rdata
;
348 type
= (uint8_t)*(rdata
+ 1);
349 if (type
== USB_DESCR_TYPE_IF
) {
350 num
= (uint8_t)*(rdata
+ 2);
352 if_descrp
= (usb_if_descr_t
*)rdata
;
358 } while ((length
> 0 ) && (rlen
> 0));
362 di_devfs_path_free (devpath
);
363 if (tmp_node
!= DI_NODE_NIL
)
370 devinfo_usb_if_add(HalDevice
*parent
, di_node_t node
, gchar
*devfs_path
,
371 gchar
*if_devfs_path
, int ifnum
)
374 char udi
[HAL_PATH_MAX
];
375 const char *parent_info
;
376 usb_if_descr_t
*if_descrp
=NULL
; /* interface descriptor */
378 d
= hal_device_new ();
380 devinfo_set_default_properties (d
, parent
, node
, if_devfs_path
);
382 /* Set the existed physical device path. */
383 hal_device_property_set_string (d
, "solaris.devfs_path", devfs_path
);
384 hal_device_property_set_string (d
, "info.subsystem", "usb");
385 hal_device_property_set_string (d
, "info.product", "USB Device Interface");
387 /* Set usb interface properties to interface node. */
388 if (strstr (if_devfs_path
, ":ia") == NULL
) {
389 if_descrp
= parse_usb_if_descr (node
, ifnum
);
391 if (if_descrp
!= NULL
) {
392 hal_device_property_set_int (d
, "usb.interface.class",
393 if_descrp
->bInterfaceClass
);
394 hal_device_property_set_int (d
, "usb.interface.subclass",
395 if_descrp
->bInterfaceSubClass
);
396 hal_device_property_set_int (d
, "usb.interface.protocol",
397 if_descrp
->bInterfaceProtocol
);
398 hal_device_property_set_int (d
, "usb.interface.number",
399 if_descrp
->bInterfaceNumber
);
403 /* copy parent's usb_device.* properties */
404 parent_info
= hal_device_property_get_string (parent
, "info.subsystem");
405 if (parent_info
!= NULL
) {
406 if (strcmp (parent_info
, "usb_device") == 0) {
407 hal_device_merge_with_rewrite (d
, parent
, "usb.", "usb_device.");
408 } else if (strcmp (parent_info
, "usb") == 0) {
409 /* for the case that the parent is IA node */
410 hal_device_merge_with_rewrite (d
, parent
, "usb.", "usb.");
414 devinfo_add_enqueue (d
, devfs_path
, &devinfo_usb_handler
);
416 /* add to TDL so preprobing callouts and prober can access it */
417 hal_device_store_add (hald_get_tdl (), d
);
424 get_dev_link_path(di_node_t node
, char *nodetype
, char *re
, char **devlink
, char **minor_path
, char **minor_name
)
426 di_devlink_handle_t devlink_hdl
;
435 if ((devlink_hdl
= di_devlink_init(NULL
, 0)) == NULL
) {
439 major
= di_driver_major(node
);
440 minor
= DI_MINOR_NIL
;
441 while ((minor
= di_minor_next(node
, minor
)) != DI_MINOR_NIL
) {
442 devt
= di_minor_devt(minor
);
443 if (major
!= major(devt
)) {
447 if (di_minor_type(minor
) != DDM_MINOR
) {
451 if ((*minor_path
= di_devfs_minor_path(minor
)) == NULL
) {
455 if (strcmp(di_minor_nodetype(minor
), nodetype
) == 0) {
456 *devlink
= get_devlink(devlink_hdl
, re
, *minor_path
);
458 * During hotplugging, devlink could be NULL for usb
459 * devices due to devlink database has not yet been
460 * updated when hal try to read from it although the
461 * actually dev link path has been created. In such a
462 * situation, we will read the devlink name from
463 * /dev/usb directory.
465 if ((*devlink
== NULL
) && (re
!= NULL
) &&
466 ((strstr(re
, "hid") != NULL
) || (strstr(re
, "video") != NULL
))) {
467 *devlink
= get_usb_devlink(*minor_path
, "/dev/usb/");
470 if (*devlink
!= NULL
) {
471 *minor_name
= di_minor_name(minor
);
476 di_devfs_path_free (*minor_path
);
479 di_devlink_fini (&devlink_hdl
);
483 devinfo_usb_video4linux_add(HalDevice
*usbd
, di_node_t node
)
489 char *devlink
= NULL
;
490 char *dev_videolink
= NULL
;
491 char *minor_path
= NULL
;
492 char *minor_name
= NULL
;
493 char udi
[HAL_PATH_MAX
];
496 get_dev_link_path(node
, "usb_video",
497 "^usb/video[0-9]+", &devlink
, &minor_path
, &minor_name
);
499 if ((minor_path
== NULL
) || (devlink
== NULL
)) {
504 HAL_DEBUG(("devlink %s, minor_name %s", devlink
, minor_name
));
505 if (strcmp(minor_name
, "usbvc") != 0) {
510 d
= hal_device_new();
512 devinfo_set_default_properties(d
, usbd
, node
, minor_path
);
513 hal_device_property_set_string(d
, "info.subsystem", "video4linux");
514 hal_device_property_set_string(d
, "info.category", "video4linux");
516 hal_device_add_capability(d
, "video4linux");
518 /* Get logic link under /dev (/dev/video+) */
519 dev_videolink
= get_usb_devlink(strstr(devlink
, "usb"), "/dev/");
521 hal_device_property_set_string(d
, "video4linux.device", dev_videolink
);
523 hal_util_compute_udi(hald_get_gdl(), udi
, sizeof (udi
),
524 "%s_video4linux", hal_device_get_udi(usbd
));
526 hal_device_set_udi(d
, udi
);
527 hal_device_property_set_string(d
, "info.udi", udi
);
528 PROP_STR(d
, node
, s
, "usb-product-name", "info.product");
530 devinfo_add_enqueue(d
, minor_path
, &devinfo_usb_handler
);
539 di_devfs_path_free(minor_path
);
546 devinfo_usb_input_add(HalDevice
*usbd
, di_node_t node
)
552 char *devlink
= NULL
;
553 char *minor_path
= NULL
;
554 char *minor_name
= NULL
;
555 char udi
[HAL_PATH_MAX
];
557 get_dev_link_path(node
, "ddi_pseudo",
558 "^usb/hid[0-9]+", &devlink
, &minor_path
, &minor_name
);
560 if ((minor_path
== NULL
) || (devlink
== NULL
)) {
565 HAL_DEBUG(("devlink %s, minor_name %s", devlink
, minor_name
));
566 if ((strcmp(minor_name
, "keyboard") != 0) &&
567 (strcmp(minor_name
, "mouse") != 0)) {
572 d
= hal_device_new();
574 devinfo_set_default_properties(d
, usbd
, node
, minor_path
);
575 hal_device_property_set_string(d
, "info.subsystem", "input");
576 hal_device_property_set_string(d
, "info.category", "input");
578 hal_device_add_capability(d
, "input");
580 if (strcmp(minor_name
, "keyboard") == 0) {
581 hal_device_add_capability(d
, "input.keyboard");
582 hal_device_add_capability(d
, "input.keys");
583 hal_device_add_capability(d
, "button");
584 } else if (strcmp(minor_name
, "mouse") == 0) {
585 hal_device_add_capability (d
, "input.mouse");
588 hal_device_property_set_string(d
, "input.device", devlink
);
589 hal_device_property_set_string(d
, "input.originating_device",
590 hal_device_get_udi(usbd
));
592 hal_util_compute_udi(hald_get_gdl(), udi
, sizeof (udi
),
593 "%s_logicaldev_input", hal_device_get_udi(usbd
));
595 hal_device_set_udi(d
, udi
);
596 hal_device_property_set_string(d
, "info.udi", udi
);
598 if (strcmp(minor_name
, "keyboard") == 0) {
599 devinfo_add_enqueue(d
, minor_path
, &devinfo_usb_keyboard_handler
);
601 devinfo_add_enqueue(d
, minor_path
, &devinfo_usb_handler
);
604 /* add to TDL so preprobing callouts and prober can access it */
605 hal_device_store_add(hald_get_tdl(), d
);
613 di_devfs_path_free(minor_path
);
620 devinfo_usb_scsa2usb_add(HalDevice
*usbd
, di_node_t node
)
623 di_devlink_handle_t devlink_hdl
;
627 char *minor_path
= NULL
;
628 char *minor_name
= NULL
;
629 char *devlink
= NULL
;
630 char udi
[HAL_PATH_MAX
];
632 get_dev_link_path(node
, "ddi_ctl:devctl:scsi",
633 "^usb/mass-storage[0-9]+", &devlink
, &minor_path
, &minor_name
);
635 if ((devlink
== NULL
) || (minor_path
== NULL
)) {
639 d
= hal_device_new ();
641 devinfo_set_default_properties (d
, usbd
, node
, minor_path
);
642 hal_device_property_set_string (d
, "scsi_host.solaris.device", devlink
);
643 hal_device_property_set_string (d
, "info.category", "scsi_host");
644 hal_device_property_set_int (d
, "scsi_host.host", 0);
646 hal_util_compute_udi (hald_get_gdl (), udi
, sizeof (udi
),
647 "%s/scsi_host%d", hal_device_get_udi (usbd
),
648 hal_device_property_get_int (d
, "scsi_host.host"));
649 hal_device_set_udi (d
, udi
);
650 hal_device_property_set_string (d
, "info.udi", udi
);
651 hal_device_property_set_string (d
, "info.product", "SCSI Host Adapter");
653 devinfo_add_enqueue (d
, minor_path
, &devinfo_usb_handler
);
660 di_devfs_path_free (minor_path
);
667 devinfo_usb_printer_add(HalDevice
*parent
, di_node_t node
)
669 char *properties
[] = { "vendor", "product", "serial", NULL
};
672 char udi
[HAL_PATH_MAX
];
674 char *devlink
= NULL
, *minor_path
= NULL
, *minor_name
= NULL
;
675 const char *subsystem
;
677 get_dev_link_path(node
, "ddi_printer", "printers/.+", &devlink
, &minor_path
, &minor_name
);
679 if ((devlink
== NULL
) || (minor_path
== NULL
)) {
683 d
= hal_device_new ();
685 devinfo_set_default_properties (d
, parent
, node
, minor_path
);
686 hal_device_property_set_string (d
, "info.category", "printer");
687 hal_device_add_capability (d
, "printer");
689 /* add printer properties */
690 hal_device_property_set_string (d
, "printer.device", devlink
);
692 /* copy parent's selected usb* properties to printer properties */
693 subsystem
= hal_device_property_get_string (parent
, "info.subsystem");
694 for (i
= 0; properties
[i
] != NULL
; i
++) {
695 char src
[32], dst
[32]; /* "subsystem.property" names */
697 snprintf(src
, sizeof (src
), "%s.%s", subsystem
, properties
[i
]);
698 snprintf(dst
, sizeof (dst
), "printer.%s", properties
[i
]);
699 hal_device_copy_property(parent
, src
, d
, dst
);
702 devinfo_add_enqueue (d
, minor_path
, &devinfo_usb_printer_handler
);
709 di_devfs_path_free (minor_path
);
716 devinfo_printer_prnio_get_prober (HalDevice
*d
, int *timeout
)
718 *timeout
= 5 * 1000; /* 5 second timeout */
719 return ("hald-probe-printer");
723 devinfo_keyboard_get_prober(HalDevice
*d
, int *timeout
)
725 *timeout
= 5 * 1000; /* 5 second timeout */
726 return ("hald-probe-xkb");