summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/io/usb
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/io/usb')
-rw-r--r--usr/src/uts/common/io/usb/inc.flg2
-rw-r--r--usr/src/uts/common/io/usb/usb_ia/usb_ia.c1230
-rw-r--r--usr/src/uts/common/io/usb/usb_mid/usb_mid.c400
-rw-r--r--usr/src/uts/common/io/usb/usba/hcdi.c10
-rw-r--r--usr/src/uts/common/io/usb/usba/parser.c40
-rw-r--r--usr/src/uts/common/io/usb/usba/usba.c314
-rw-r--r--usr/src/uts/common/io/usb/usba/usbai_util.c267
7 files changed, 1933 insertions, 330 deletions
diff --git a/usr/src/uts/common/io/usb/inc.flg b/usr/src/uts/common/io/usb/inc.flg
index 95b00e986c..516db45458 100644
--- a/usr/src/uts/common/io/usb/inc.flg
+++ b/usr/src/uts/common/io/usb/inc.flg
@@ -64,6 +64,7 @@ find_files "s.*" \
usr/src/uts/sparc/hubd \
usr/src/uts/sparc/scsa2usb \
usr/src/uts/sparc/usb_mid \
+ usr/src/uts/sparc/usb_ia \
usr/src/uts/sparc/usbkbm \
usr/src/uts/sparc/usbms \
usr/src/uts/sparc/usbprn \
@@ -85,6 +86,7 @@ find_files "s.*" \
usr/src/uts/intel/ehci \
usr/src/uts/intel/hubd \
usr/src/uts/intel/usb_mid \
+ usr/src/uts/intel/usb_ia \
usr/src/uts/intel/usba \
usr/src/uts/intel/usbkbm \
usr/src/uts/intel/usbms \
diff --git a/usr/src/uts/common/io/usb/usb_ia/usb_ia.c b/usr/src/uts/common/io/usb/usb_ia/usb_ia.c
new file mode 100644
index 0000000000..fcc534096d
--- /dev/null
+++ b/usr/src/uts/common/io/usb/usb_ia/usb_ia.c
@@ -0,0 +1,1230 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * usb interface association driver
+ *
+ * this driver attempts to the interface association node and
+ * creates/manages child nodes for the included interfaces.
+ */
+
+#if defined(lint) && !defined(DEBUG)
+#define DEBUG 1
+#endif
+#include <sys/usb/usba/usbai_version.h>
+#include <sys/usb/usba.h>
+#include <sys/usb/usba/usba_types.h>
+#include <sys/usb/usba/usba_impl.h>
+#include <sys/usb/usb_ia/usb_iavar.h>
+
+/* Debugging support */
+uint_t usb_ia_errlevel = USB_LOG_L4;
+uint_t usb_ia_errmask = (uint_t)DPRINT_MASK_ALL;
+uint_t usb_ia_instance_debug = (uint_t)-1;
+uint_t usb_ia_bus_config_debug = 0;
+
+_NOTE(DATA_READABLE_WITHOUT_LOCK(usb_ia_errlevel))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(usb_ia_errmask))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(usb_ia_instance_debug))
+
+_NOTE(SCHEME_PROTECTS_DATA("unique", msgb))
+_NOTE(SCHEME_PROTECTS_DATA("unique", dev_info))
+_NOTE(SCHEME_PROTECTS_DATA("unique", usb_pipe_policy))
+
+static struct cb_ops usb_ia_cb_ops = {
+ nodev, /* open */
+ nodev, /* close */
+ nodev, /* strategy */
+ nodev, /* print */
+ nodev, /* dump */
+ nodev, /* read */
+ nodev, /* write */
+ nodev, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ nochpoll, /* poll */
+ ddi_prop_op, /* prop_op */
+ NULL, /* aread */
+ D_MP
+};
+
+static int usb_ia_busop_get_eventcookie(dev_info_t *dip,
+ dev_info_t *rdip,
+ char *eventname,
+ ddi_eventcookie_t *cookie);
+static int usb_ia_busop_add_eventcall(dev_info_t *dip,
+ dev_info_t *rdip,
+ ddi_eventcookie_t cookie,
+ void (*callback)(dev_info_t *dip,
+ ddi_eventcookie_t cookie, void *arg,
+ void *bus_impldata),
+ void *arg, ddi_callback_id_t *cb_id);
+static int usb_ia_busop_remove_eventcall(dev_info_t *dip,
+ ddi_callback_id_t cb_id);
+static int usb_ia_busop_post_event(dev_info_t *dip,
+ dev_info_t *rdip,
+ ddi_eventcookie_t cookie,
+ void *bus_impldata);
+static int usb_ia_bus_config(dev_info_t *dip,
+ uint_t flag,
+ ddi_bus_config_op_t op,
+ void *arg,
+ dev_info_t **child);
+static int usb_ia_bus_unconfig(dev_info_t *dip,
+ uint_t flag,
+ ddi_bus_config_op_t op,
+ void *arg);
+
+/*
+ * autoconfiguration data and routines.
+ */
+static int usb_ia_info(dev_info_t *, ddi_info_cmd_t,
+ void *, void **);
+static int usb_ia_attach(dev_info_t *, ddi_attach_cmd_t);
+static int usb_ia_detach(dev_info_t *, ddi_detach_cmd_t);
+
+/* other routines */
+static void usb_ia_create_pm_components(dev_info_t *, usb_ia_t *);
+static int usb_ia_bus_ctl(dev_info_t *, dev_info_t *,
+ ddi_ctl_enum_t, void *, void *);
+static int usb_ia_power(dev_info_t *, int, int);
+static int usb_ia_restore_device_state(dev_info_t *, usb_ia_t *);
+static usb_ia_t *usb_ia_obtain_state(dev_info_t *);
+static void usb_ia_event_cb(dev_info_t *, ddi_eventcookie_t, void *, void *);
+
+/* prototypes */
+static void usb_ia_create_children(usb_ia_t *);
+static int usb_ia_cleanup(usb_ia_t *);
+
+/*
+ * Busops vector
+ */
+static struct bus_ops usb_ia_busops = {
+ BUSO_REV,
+ nullbusmap, /* bus_map */
+ NULL, /* bus_get_intrspec */
+ NULL, /* bus_add_intrspec */
+ NULL, /* bus_remove_intrspec */
+ NULL, /* XXXX bus_map_fault */
+ ddi_dma_map, /* bus_dma_map */
+ ddi_dma_allochdl,
+ ddi_dma_freehdl,
+ ddi_dma_bindhdl,
+ ddi_dma_unbindhdl,
+ ddi_dma_flush,
+ ddi_dma_win,
+ ddi_dma_mctl, /* bus_dma_ctl */
+ usb_ia_bus_ctl, /* bus_ctl */
+ ddi_bus_prop_op, /* bus_prop_op */
+ usb_ia_busop_get_eventcookie,
+ usb_ia_busop_add_eventcall,
+ usb_ia_busop_remove_eventcall,
+ usb_ia_busop_post_event, /* bus_post_event */
+ NULL, /* bus_intr_ctl */
+ usb_ia_bus_config, /* bus_config */
+ usb_ia_bus_unconfig, /* bus_unconfig */
+ NULL, /* bus_fm_init */
+ NULL, /* bus_fm_fini */
+ NULL, /* bus_fm_access_enter */
+ NULL, /* bus_fm_access_exit */
+ NULL /* bus_power */
+};
+
+
+static struct dev_ops usb_ia_ops = {
+ DEVO_REV, /* devo_rev, */
+ 0, /* refcnt */
+ usb_ia_info, /* info */
+ nulldev, /* identify */
+ nulldev, /* probe */
+ usb_ia_attach, /* attach */
+ usb_ia_detach, /* detach */
+ nodev, /* reset */
+ &usb_ia_cb_ops, /* driver operations */
+ &usb_ia_busops, /* bus operations */
+ usb_ia_power /* power */
+};
+
+static struct modldrv modldrv = {
+ &mod_driverops, /* Type of module. This one is a driver */
+ "USB Interface Association Driver %I%", /* Name of the module. */
+ &usb_ia_ops, /* driver ops */
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, (void *)&modldrv, NULL
+};
+
+#define USB_IA_INITIAL_SOFT_SPACE 4
+static void *usb_ia_statep;
+
+/*
+ * event definition
+ */
+static ndi_event_definition_t usb_ia_ndi_event_defs[] = {
+ {USBA_EVENT_TAG_HOT_REMOVAL, DDI_DEVI_REMOVE_EVENT, EPL_KERNEL,
+ NDI_EVENT_POST_TO_ALL},
+ {USBA_EVENT_TAG_HOT_INSERTION, DDI_DEVI_INSERT_EVENT, EPL_KERNEL,
+ NDI_EVENT_POST_TO_ALL},
+ {USBA_EVENT_TAG_POST_RESUME, USBA_POST_RESUME_EVENT, EPL_KERNEL,
+ NDI_EVENT_POST_TO_ALL},
+ {USBA_EVENT_TAG_PRE_SUSPEND, USBA_PRE_SUSPEND_EVENT, EPL_KERNEL,
+ NDI_EVENT_POST_TO_ALL}
+};
+
+#define USB_IA_N_NDI_EVENTS \
+ (sizeof (usb_ia_ndi_event_defs) / sizeof (ndi_event_definition_t))
+
+static ndi_event_set_t usb_ia_ndi_events = {
+ NDI_EVENTS_REV1, USB_IA_N_NDI_EVENTS, usb_ia_ndi_event_defs};
+
+
+/*
+ * standard driver entry points
+ */
+int
+_init(void)
+{
+ int rval;
+
+ rval = ddi_soft_state_init(&usb_ia_statep, sizeof (struct usb_ia),
+ USB_IA_INITIAL_SOFT_SPACE);
+ if (rval != 0) {
+ return (rval);
+ }
+
+ if ((rval = mod_install(&modlinkage)) != 0) {
+ ddi_soft_state_fini(&usb_ia_statep);
+ return (rval);
+ }
+
+ return (rval);
+}
+
+
+int
+_fini(void)
+{
+ int rval;
+
+ rval = mod_remove(&modlinkage);
+
+ if (rval) {
+ return (rval);
+ }
+
+ ddi_soft_state_fini(&usb_ia_statep);
+
+ return (rval);
+}
+
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+
+/*ARGSUSED*/
+static int
+usb_ia_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
+{
+ usb_ia_t *usb_ia;
+ int instance = getminor((dev_t)arg);
+ int error = DDI_FAILURE;
+
+ switch (infocmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ if ((usb_ia = ddi_get_soft_state(usb_ia_statep,
+ instance)) != NULL) {
+ *result = (void *)usb_ia->ia_dip;
+ if (*result != NULL) {
+ error = DDI_SUCCESS;
+ }
+ } else {
+ *result = NULL;
+ }
+ break;
+
+ case DDI_INFO_DEVT2INSTANCE:
+ *result = (void *)(intptr_t)instance;
+ error = DDI_SUCCESS;
+ break;
+ default:
+ break;
+ }
+
+ return (error);
+}
+
+
+/*
+ * child post attach/detach notification
+ */
+static void
+usb_ia_post_attach(usb_ia_t *usb_ia, uint8_t ifno, struct attachspec *as)
+{
+ USB_DPRINTF_L4(DPRINT_MASK_PM, usb_ia->ia_log_handle,
+ "usb_ia_post_attach: ifno = %d result = %d", ifno, as->result);
+
+}
+
+
+static void
+usb_ia_post_detach(usb_ia_t *usb_ia, uint8_t ifno, struct detachspec *ds)
+{
+ USB_DPRINTF_L4(DPRINT_MASK_PM, usb_ia->ia_log_handle,
+ "usb_ia_post_detach: ifno = %d result = %d", ifno, ds->result);
+
+}
+
+
+/*
+ * bus ctl support. we handle notifications here and the
+ * rest goes up to root hub/hcd
+ */
+/*ARGSUSED*/
+static int
+usb_ia_bus_ctl(dev_info_t *dip,
+ dev_info_t *rdip,
+ ddi_ctl_enum_t op,
+ void *arg,
+ void *result)
+{
+ usba_device_t *hub_usba_device = usba_get_usba_device(rdip);
+ dev_info_t *root_hub_dip = hub_usba_device->usb_root_hub_dip;
+ usb_ia_t *usb_ia;
+ struct attachspec *as;
+ struct detachspec *ds;
+
+ usb_ia = usb_ia_obtain_state(dip);
+
+ USB_DPRINTF_L4(DPRINT_MASK_PM, usb_ia->ia_log_handle,
+ "usb_ia_bus_ctl:\n\t"
+ "dip = 0x%p, rdip = 0x%p, op = 0x%x, arg = 0x%p",
+ dip, rdip, op, arg);
+
+ switch (op) {
+ case DDI_CTLOPS_ATTACH:
+ as = (struct attachspec *)arg;
+
+ switch (as->when) {
+ case DDI_PRE :
+ /* nothing to do basically */
+ USB_DPRINTF_L2(DPRINT_MASK_PM, usb_ia->ia_log_handle,
+ "DDI_PRE DDI_CTLOPS_ATTACH");
+ break;
+ case DDI_POST :
+ usb_ia_post_attach(usb_ia, usba_get_ifno(rdip),
+ (struct attachspec *)arg);
+ break;
+ }
+
+ break;
+ case DDI_CTLOPS_DETACH:
+ ds = (struct detachspec *)arg;
+
+ switch (ds->when) {
+ case DDI_PRE :
+ /* nothing to do basically */
+ USB_DPRINTF_L2(DPRINT_MASK_PM, usb_ia->ia_log_handle,
+ "DDI_PRE DDI_CTLOPS_DETACH");
+ break;
+ case DDI_POST :
+ usb_ia_post_detach(usb_ia, usba_get_ifno(rdip),
+ (struct detachspec *)arg);
+ break;
+ }
+
+ break;
+ default:
+ /* pass to root hub to handle */
+ return (usba_bus_ctl(root_hub_dip, rdip, op, arg, result));
+ }
+
+ return (DDI_SUCCESS);
+}
+
+
+/*
+ * bus enumeration entry points
+ */
+static int
+usb_ia_bus_config(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
+ void *arg, dev_info_t **child)
+{
+ int rval, circ;
+ usb_ia_t *usb_ia = usb_ia_obtain_state(dip);
+
+ USB_DPRINTF_L4(DPRINT_MASK_ALL, usb_ia->ia_log_handle,
+ "usb_ia_bus_config: op=%d", op);
+
+ if (usb_ia_bus_config_debug) {
+ flag |= NDI_DEVI_DEBUG;
+ }
+
+ ndi_devi_enter(dip, &circ);
+
+ /* enumerate each interface below us */
+ mutex_enter(&usb_ia->ia_mutex);
+ usb_ia_create_children(usb_ia);
+ mutex_exit(&usb_ia->ia_mutex);
+
+ rval = ndi_busop_bus_config(dip, flag, op, arg, child, 0);
+ ndi_devi_exit(dip, circ);
+
+ return (rval);
+}
+
+
+static int
+usb_ia_bus_unconfig(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
+ void *arg)
+{
+ usb_ia_t *usb_ia = usb_ia_obtain_state(dip);
+
+ dev_info_t *cdip, *mdip;
+ int interface, circular_count;
+ int rval = NDI_SUCCESS;
+
+ USB_DPRINTF_L4(DPRINT_MASK_ALL, usb_ia->ia_log_handle,
+ "usb_ia_bus_unconfig: op=%d", op);
+
+ if (usb_ia_bus_config_debug) {
+ flag |= NDI_DEVI_DEBUG;
+ }
+
+ /*
+ * first offline and if offlining successful, then
+ * remove children
+ */
+ if (op == BUS_UNCONFIG_ALL) {
+ flag &= ~(NDI_DEVI_REMOVE | NDI_UNCONFIG);
+ }
+
+ ndi_devi_enter(dip, &circular_count);
+ rval = ndi_busop_bus_unconfig(dip, flag, op, arg);
+
+ if (op == BUS_UNCONFIG_ALL && rval == NDI_SUCCESS &&
+ (flag & NDI_AUTODETACH) == 0) {
+ flag |= NDI_DEVI_REMOVE;
+ rval = ndi_busop_bus_unconfig(dip, flag, op, arg);
+ }
+
+ /* update children's list */
+ mutex_enter(&usb_ia->ia_mutex);
+ for (interface = 0; usb_ia->ia_children_dips &&
+ (interface < usb_ia->ia_n_ifs); interface++) {
+ mdip = usb_ia->ia_children_dips[interface];
+
+ /* now search if this dip still exists */
+ for (cdip = ddi_get_child(dip); cdip && (cdip != mdip);
+ cdip = ddi_get_next_sibling(cdip));
+
+ if (cdip != mdip) {
+ /* we lost the dip on this interface */
+ usb_ia->ia_children_dips[interface] = NULL;
+ } else if (cdip) {
+ /*
+ * keep in DS_INITALIZED to prevent parent
+ * from detaching
+ */
+ (void) ddi_initchild(ddi_get_parent(cdip), cdip);
+ }
+ }
+ mutex_exit(&usb_ia->ia_mutex);
+
+ ndi_devi_exit(dip, circular_count);
+
+ USB_DPRINTF_L4(DPRINT_MASK_ALL, usb_ia->ia_log_handle,
+ "usb_ia_bus_config: rval=%d", rval);
+
+ return (rval);
+}
+
+
+/* power entry point */
+/* ARGSUSED */
+static int
+usb_ia_power(dev_info_t *dip, int comp, int level)
+{
+ usb_ia_t *usb_ia;
+ usb_common_power_t *pm;
+ int rval = DDI_FAILURE;
+
+ usb_ia = usb_ia_obtain_state(dip);
+
+ USB_DPRINTF_L4(DPRINT_MASK_PM, usb_ia->ia_log_handle,
+ "usb_ia_power: Begin: usb_ia = %p, level = %d", usb_ia, level);
+
+ mutex_enter(&usb_ia->ia_mutex);
+ pm = usb_ia->ia_pm;
+
+ /* check if we are transitioning to a legal power level */
+ if (USB_DEV_PWRSTATE_OK(pm->uc_pwr_states, level)) {
+ USB_DPRINTF_L2(DPRINT_MASK_PM, usb_ia->ia_log_handle,
+ "usb_ia_power: illegal power level = %d "
+ "uc_pwr_states = %x", level, pm->uc_pwr_states);
+
+ mutex_exit(&usb_ia->ia_mutex);
+
+ return (rval);
+ }
+
+ rval = usba_common_power(dip, pm, &(usb_ia->ia_dev_state), level);
+
+ mutex_exit(&usb_ia->ia_mutex);
+
+ return (rval);
+}
+
+/*
+ * attach/resume entry point
+ */
+static int
+usb_ia_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ int instance = ddi_get_instance(dip);
+ usb_ia_t *usb_ia = NULL;
+ uint_t n_ifs;
+ size_t size;
+
+ switch (cmd) {
+ case DDI_ATTACH:
+
+ break;
+ case DDI_RESUME:
+ usb_ia = ddi_get_soft_state(usb_ia_statep, instance);
+ (void) usb_ia_restore_device_state(dip, usb_ia);
+
+ return (DDI_SUCCESS);
+ default:
+
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * Attach:
+ *
+ * Allocate soft state and initialize
+ */
+ if (ddi_soft_state_zalloc(usb_ia_statep, instance) != DDI_SUCCESS) {
+ goto fail;
+ }
+
+ usb_ia = ddi_get_soft_state(usb_ia_statep, instance);
+ if (usb_ia == NULL) {
+
+ goto fail;
+ }
+
+ /* allocate handle for logging of messages */
+ usb_ia->ia_log_handle = usb_alloc_log_hdl(dip, "ia",
+ &usb_ia_errlevel,
+ &usb_ia_errmask, &usb_ia_instance_debug,
+ 0);
+
+ usb_ia->ia_dip = dip;
+ usb_ia->ia_instance = instance;
+ usb_ia->ia_first_if = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
+ DDI_PROP_DONTPASS, "interface", -1);
+ usb_ia->ia_n_ifs = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
+ DDI_PROP_DONTPASS, "interface-count", -1);
+
+ if (usb_ia->ia_first_if < 0 || usb_ia->ia_n_ifs < 0) {
+ USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_ia->ia_log_handle,
+ "interface-association property failed");
+
+ goto fail;
+ }
+
+ /* attach client driver to USBA */
+ if (usb_client_attach(dip, USBDRV_VERSION, 0) != USB_SUCCESS) {
+ USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_ia->ia_log_handle,
+ "usb_client_attach failed");
+ goto fail;
+ }
+ if (usb_get_dev_data(dip, &usb_ia->ia_dev_data, USB_PARSE_LVL_NONE,
+ 0) != USB_SUCCESS) {
+ USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_ia->ia_log_handle,
+ "usb_get_dev_data failed");
+ goto fail;
+ }
+
+ mutex_init(&usb_ia->ia_mutex, NULL, MUTEX_DRIVER,
+ usb_ia->ia_dev_data->dev_iblock_cookie);
+
+ usb_free_dev_data(dip, usb_ia->ia_dev_data);
+ usb_ia->ia_dev_data = NULL;
+
+ usb_ia->ia_init_state |= USB_IA_LOCK_INIT;
+
+ if (ddi_create_minor_node(dip, "usb_ia", S_IFCHR, instance,
+ DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
+ USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_ia->ia_log_handle,
+ "cannot create devctl minor node");
+ goto fail;
+ }
+
+ usb_ia->ia_init_state |= USB_IA_MINOR_NODE_CREATED;
+
+ /*
+ * allocate array for keeping track of child dips
+ */
+ n_ifs = usb_ia->ia_n_ifs;
+ usb_ia->ia_cd_list_length = size = (sizeof (dev_info_t *)) * n_ifs;
+
+ usb_ia->ia_children_dips = kmem_zalloc(size, KM_SLEEP);
+ usb_ia->ia_child_events = kmem_zalloc(sizeof (uint8_t) * n_ifs,
+ KM_SLEEP);
+ /*
+ * Event handling: definition and registration
+ * get event handle for events that we have defined
+ */
+ (void) ndi_event_alloc_hdl(dip, 0, &usb_ia->ia_ndi_event_hdl,
+ NDI_SLEEP);
+
+ /* bind event set to the handle */
+ if (ndi_event_bind_set(usb_ia->ia_ndi_event_hdl, &usb_ia_ndi_events,
+ NDI_SLEEP)) {
+ USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_ia->ia_log_handle,
+ "usb_ia_attach: binding event set failed");
+
+ goto fail;
+ }
+
+ usb_ia->ia_dev_state = USB_DEV_ONLINE;
+
+ /*
+ * now create components to power manage this device
+ * before attaching children
+ */
+ usb_ia_create_pm_components(dip, usb_ia);
+
+ /* event registration for events from our parent */
+ usba_common_register_events(dip, n_ifs, usb_ia_event_cb);
+
+ usb_ia->ia_init_state |= USB_IA_EVENTS_REGISTERED;
+
+ ddi_report_dev(dip);
+
+ return (DDI_SUCCESS);
+
+fail:
+ USB_DPRINTF_L2(DPRINT_MASK_ATTA, NULL, "usb_ia%d cannot attach",
+ instance);
+
+ if (usb_ia) {
+ (void) usb_ia_cleanup(usb_ia);
+ }
+
+ return (DDI_FAILURE);
+}
+
+
+/* detach or suspend this instance */
+static int
+usb_ia_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ usb_ia_t *usb_ia = usb_ia_obtain_state(dip);
+
+ USB_DPRINTF_L4(DPRINT_MASK_ATTA, usb_ia->ia_log_handle,
+ "usb_ia_detach: cmd = 0x%x", cmd);
+
+ switch (cmd) {
+ case DDI_DETACH:
+
+ return (usb_ia_cleanup(usb_ia));
+ case DDI_SUSPEND:
+ /* nothing to do */
+ mutex_enter(&usb_ia->ia_mutex);
+ usb_ia->ia_dev_state = USB_DEV_SUSPENDED;
+ mutex_exit(&usb_ia->ia_mutex);
+
+ return (DDI_SUCCESS);
+ default:
+
+ return (DDI_FAILURE);
+ }
+
+ _NOTE(NOT_REACHED)
+ /* NOTREACHED */
+}
+
+
+/*
+ * usb_ia_cleanup:
+ * cleanup usb_ia and deallocate. this function is called for
+ * handling attach failures and detaching including dynamic
+ * reconfiguration
+ */
+/*ARGSUSED*/
+static int
+usb_ia_cleanup(usb_ia_t *usb_ia)
+{
+ usb_common_power_t *iapm;
+ int rval;
+ dev_info_t *dip = usb_ia->ia_dip;
+
+ USB_DPRINTF_L4(DPRINT_MASK_ATTA, usb_ia->ia_log_handle,
+ "usb_ia_cleanup:");
+
+ if ((usb_ia->ia_init_state & USB_IA_LOCK_INIT) == 0) {
+
+ goto done;
+ }
+
+ /*
+ * deallocate events, if events are still registered
+ * (ie. children still attached) then we have to fail the detach
+ */
+ if (usb_ia->ia_ndi_event_hdl &&
+ (ndi_event_free_hdl(usb_ia->ia_ndi_event_hdl) != NDI_SUCCESS)) {
+
+ USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_ia->ia_log_handle,
+ "usb_ia_cleanup: ndi_event_free_hdl failed");
+
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * Disable the event callbacks, after this point, event
+ * callbacks will never get called. Note we shouldn't hold
+ * mutex while unregistering events because there may be a
+ * competing event callback thread. Event callbacks are done
+ * with ndi mutex held and this can cause a potential deadlock.
+ * Note that cleanup can't fail after deregistration of events.
+ */
+ if (usb_ia->ia_init_state & USB_IA_EVENTS_REGISTERED) {
+
+ usba_common_unregister_events(usb_ia->ia_dip, usb_ia->ia_n_ifs);
+ }
+
+ iapm = usb_ia->ia_pm;
+
+ mutex_enter(&usb_ia->ia_mutex);
+
+ if ((iapm) && (usb_ia->ia_dev_state != USB_DEV_DISCONNECTED)) {
+
+ mutex_exit(&usb_ia->ia_mutex);
+
+ (void) pm_busy_component(dip, 0);
+ if (iapm->uc_wakeup_enabled) {
+
+ /* First bring the device to full power */
+ (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
+
+ rval = usb_handle_remote_wakeup(dip,
+ USB_REMOTE_WAKEUP_DISABLE);
+
+ if (rval != DDI_SUCCESS) {
+ USB_DPRINTF_L2(DPRINT_MASK_EVENTS,
+ usb_ia->ia_log_handle,
+ "usb_cleanup: disable remote "
+ "wakeup failed, rval=%d", rval);
+ }
+ }
+
+ (void) pm_lower_power(usb_ia->ia_dip, 0, USB_DEV_OS_PWR_OFF);
+ (void) pm_idle_component(dip, 0);
+ } else {
+ mutex_exit(&usb_ia->ia_mutex);
+ }
+
+ if (iapm) {
+ kmem_free(iapm, sizeof (usb_common_power_t));
+ }
+
+ /* free children list */
+ if (usb_ia->ia_children_dips) {
+ kmem_free(usb_ia->ia_children_dips,
+ usb_ia->ia_cd_list_length);
+ }
+
+ if (usb_ia->ia_child_events) {
+ kmem_free(usb_ia->ia_child_events, sizeof (uint8_t) *
+ usb_ia->ia_n_ifs);
+ }
+
+ if (usb_ia->ia_init_state & USB_IA_MINOR_NODE_CREATED) {
+ ddi_remove_minor_node(dip, NULL);
+ }
+
+ mutex_destroy(&usb_ia->ia_mutex);
+
+done:
+ usb_client_detach(dip, usb_ia->ia_dev_data);
+
+ usb_free_log_hdl(usb_ia->ia_log_handle);
+ ddi_soft_state_free(usb_ia_statep, ddi_get_instance(dip));
+
+ ddi_prop_remove_all(dip);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * usb_ia_create_children:
+ */
+static void
+usb_ia_create_children(usb_ia_t *usb_ia)
+{
+ usba_device_t *usba_device;
+ uint_t n_ifs, first_if;
+ uint_t i;
+ dev_info_t *cdip;
+
+ usba_device = usba_get_usba_device(usb_ia->ia_dip);
+
+ USB_DPRINTF_L4(DPRINT_MASK_ATTA, usb_ia->ia_log_handle,
+ "usb_ia_attach_child_drivers: port = %d, address = %d",
+ usba_device->usb_port, usba_device->usb_addr);
+
+ n_ifs = usb_ia->ia_n_ifs;
+ first_if = usb_ia->ia_first_if;
+
+ /*
+ * create all children if not already present
+ */
+ for (i = 0; i < n_ifs; i++) {
+ if (usb_ia->ia_children_dips[i] != NULL) {
+
+ continue;
+ }
+
+ mutex_exit(&usb_ia->ia_mutex);
+ cdip = usba_ready_interface_node(usb_ia->ia_dip, first_if + i);
+ mutex_enter(&usb_ia->ia_mutex);
+
+ if (cdip != NULL) {
+ (void) usba_bind_driver(cdip);
+ usb_ia->ia_children_dips[i] = cdip;
+ }
+ }
+
+}
+
+
+/*
+ * event support
+ */
+static int
+usb_ia_busop_get_eventcookie(dev_info_t *dip,
+ dev_info_t *rdip, char *eventname, ddi_eventcookie_t *cookie)
+{
+ usb_ia_t *usb_ia = usb_ia_obtain_state(dip);
+
+ USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
+ "usb_ia_busop_get_eventcookie: dip=0x%p, rdip=0x%p, "
+ "event=%s", (void *)dip, (void *)rdip, eventname);
+ USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
+ "(dip=%s%d rdip=%s%d)",
+ ddi_driver_name(dip), ddi_get_instance(dip),
+ ddi_driver_name(rdip), ddi_get_instance(rdip));
+
+ /* return event cookie, iblock cookie, and level */
+ return (ndi_event_retrieve_cookie(usb_ia->ia_ndi_event_hdl,
+ rdip, eventname, cookie, NDI_EVENT_NOPASS));
+}
+
+
+static int
+usb_ia_busop_add_eventcall(dev_info_t *dip,
+ dev_info_t *rdip,
+ ddi_eventcookie_t cookie,
+ void (*callback)(dev_info_t *dip,
+ ddi_eventcookie_t cookie, void *arg,
+ void *bus_impldata),
+ void *arg, ddi_callback_id_t *cb_id)
+{
+ int ifno;
+ usb_ia_t *usb_ia = usb_ia_obtain_state(dip);
+
+ mutex_enter(&usb_ia->ia_mutex);
+ ifno = usba_get_ifno(rdip)- usb_ia->ia_first_if;
+ mutex_exit(&usb_ia->ia_mutex);
+
+ if (ifno < 0) {
+ ifno = 0;
+ }
+
+ USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
+ "usb_ia_busop_add_eventcall: dip=0x%p, rdip=0x%p "
+ "cookie=0x%p, cb=0x%p, arg=0x%p",
+ (void *)dip, (void *)rdip, (void *)cookie, (void *)callback, arg);
+ USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
+ "(dip=%s%d rdip=%s%d event=%s)",
+ ddi_driver_name(dip), ddi_get_instance(dip),
+ ddi_driver_name(rdip), ddi_get_instance(rdip),
+ ndi_event_cookie_to_name(usb_ia->ia_ndi_event_hdl, cookie));
+
+ /* Set flag on children registering events */
+ switch (ndi_event_cookie_to_tag(usb_ia->ia_ndi_event_hdl, cookie)) {
+ case USBA_EVENT_TAG_HOT_REMOVAL:
+ mutex_enter(&usb_ia->ia_mutex);
+ usb_ia->ia_child_events[ifno] |=
+ USB_IA_CHILD_EVENT_DISCONNECT;
+ mutex_exit(&usb_ia->ia_mutex);
+
+ break;
+ case USBA_EVENT_TAG_PRE_SUSPEND:
+ mutex_enter(&usb_ia->ia_mutex);
+ usb_ia->ia_child_events[ifno] |=
+ USB_IA_CHILD_EVENT_PRESUSPEND;
+ mutex_exit(&usb_ia->ia_mutex);
+
+ break;
+ default:
+
+ break;
+ }
+ /* add callback (perform registration) */
+ return (ndi_event_add_callback(usb_ia->ia_ndi_event_hdl,
+ rdip, cookie, callback, arg, NDI_SLEEP, cb_id));
+}
+
+
+static int
+usb_ia_busop_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id)
+{
+ usb_ia_t *usb_ia = usb_ia_obtain_state(dip);
+ ndi_event_callbacks_t *cb = (ndi_event_callbacks_t *)cb_id;
+
+ ASSERT(cb);
+
+ USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
+ "usb_ia_busop_remove_eventcall: dip=0x%p, rdip=0x%p "
+ "cookie=0x%p", (void *)dip, cb->ndi_evtcb_dip,
+ cb->ndi_evtcb_cookie);
+ USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
+ "(dip=%s%d rdip=%s%d event=%s)",
+ ddi_driver_name(dip), ddi_get_instance(dip),
+ ddi_driver_name(cb->ndi_evtcb_dip),
+ ddi_get_instance(cb->ndi_evtcb_dip),
+ ndi_event_cookie_to_name(usb_ia->ia_ndi_event_hdl,
+ cb->ndi_evtcb_cookie));
+
+ /* remove event registration from our event set */
+ return (ndi_event_remove_callback(usb_ia->ia_ndi_event_hdl, cb_id));
+}
+
+
+static int
+usb_ia_busop_post_event(dev_info_t *dip,
+ dev_info_t *rdip,
+ ddi_eventcookie_t cookie,
+ void *bus_impldata)
+{
+ usb_ia_t *usb_ia = usb_ia_obtain_state(dip);
+
+ USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
+ "usb_ia_busop_post_event: dip=0x%p, rdip=0x%p "
+ "cookie=0x%p, impl=0x%p",
+ (void *)dip, (void *)rdip, (void *)cookie, bus_impldata);
+ USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
+ "(dip=%s%d rdip=%s%d event=%s)",
+ ddi_driver_name(dip), ddi_get_instance(dip),
+ ddi_driver_name(rdip), ddi_get_instance(rdip),
+ ndi_event_cookie_to_name(usb_ia->ia_ndi_event_hdl, cookie));
+
+ /* post event to all children registered for this event */
+ return (ndi_event_run_callbacks(usb_ia->ia_ndi_event_hdl, rdip,
+ cookie, bus_impldata));
+}
+
+
+/*
+ * usb_ia_restore_device_state
+ * set the original configuration of the device
+ */
+static int
+usb_ia_restore_device_state(dev_info_t *dip, usb_ia_t *usb_ia)
+{
+ usb_common_power_t *iapm;
+
+ USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
+ "usb_ia_restore_device_state: usb_ia = %p", usb_ia);
+
+ mutex_enter(&usb_ia->ia_mutex);
+ iapm = usb_ia->ia_pm;
+ mutex_exit(&usb_ia->ia_mutex);
+
+ /* First bring the device to full power */
+ (void) pm_busy_component(dip, 0);
+ (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
+
+ if (usb_check_same_device(dip, usb_ia->ia_log_handle, USB_LOG_L0,
+ DPRINT_MASK_EVENTS, USB_CHK_VIDPID, NULL) != USB_SUCCESS) {
+
+ /* change the device state from suspended to disconnected */
+ mutex_enter(&usb_ia->ia_mutex);
+ usb_ia->ia_dev_state = USB_DEV_DISCONNECTED;
+ mutex_exit(&usb_ia->ia_mutex);
+ (void) pm_idle_component(dip, 0);
+
+ return (USB_FAILURE);
+ }
+
+ /*
+ * if the device had remote wakeup earlier,
+ * enable it again
+ */
+ if (iapm->uc_wakeup_enabled) {
+ (void) usb_handle_remote_wakeup(usb_ia->ia_dip,
+ USB_REMOTE_WAKEUP_ENABLE);
+ }
+
+ mutex_enter(&usb_ia->ia_mutex);
+ usb_ia->ia_dev_state = USB_DEV_ONLINE;
+ mutex_exit(&usb_ia->ia_mutex);
+
+ (void) pm_idle_component(dip, 0);
+
+ return (USB_SUCCESS);
+}
+
+
+/*
+ * usb_ia_event_cb()
+ * handle disconnect and connect events
+ */
+static void
+usb_ia_event_cb(dev_info_t *dip, ddi_eventcookie_t cookie,
+ void *arg, void *bus_impldata)
+{
+ int i, tag;
+ usb_ia_t *usb_ia = usb_ia_obtain_state(dip);
+ dev_info_t *child_dip;
+ ddi_eventcookie_t rm_cookie, ins_cookie, suspend_cookie, resume_cookie;
+
+ USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
+ "usb_ia_event_cb: dip=0x%p, cookie=0x%p, "
+ "arg=0x%p, impl=0x%p",
+ (void *)dip, (void *)cookie, arg, bus_impldata);
+ USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
+ "(dip=%s%d event=%s)",
+ ddi_driver_name(dip), ddi_get_instance(dip),
+ ndi_event_cookie_to_name(usb_ia->ia_ndi_event_hdl, cookie));
+
+ tag = NDI_EVENT_TAG(cookie);
+ rm_cookie = ndi_event_tag_to_cookie(
+ usb_ia->ia_ndi_event_hdl, USBA_EVENT_TAG_HOT_REMOVAL);
+ suspend_cookie = ndi_event_tag_to_cookie(
+ usb_ia->ia_ndi_event_hdl, USBA_EVENT_TAG_PRE_SUSPEND);
+ ins_cookie = ndi_event_tag_to_cookie(
+ usb_ia->ia_ndi_event_hdl, USBA_EVENT_TAG_HOT_INSERTION);
+ resume_cookie = ndi_event_tag_to_cookie(
+ usb_ia->ia_ndi_event_hdl, USBA_EVENT_TAG_POST_RESUME);
+
+ mutex_enter(&usb_ia->ia_mutex);
+ switch (tag) {
+ case USBA_EVENT_TAG_HOT_REMOVAL:
+ if (usb_ia->ia_dev_state == USB_DEV_DISCONNECTED) {
+ USB_DPRINTF_L2(DPRINT_MASK_EVENTS,
+ usb_ia->ia_log_handle,
+ "usb_ia_event_cb: Device already disconnected");
+ } else {
+ /* we are disconnected so set our state now */
+ usb_ia->ia_dev_state = USB_DEV_DISCONNECTED;
+ for (i = 0; i < usb_ia->ia_n_ifs; i++) {
+ usb_ia->ia_child_events[i] &= ~
+ USB_IA_CHILD_EVENT_DISCONNECT;
+ }
+ mutex_exit(&usb_ia->ia_mutex);
+
+ /* pass disconnect event to all the children */
+ (void) ndi_event_run_callbacks(
+ usb_ia->ia_ndi_event_hdl, NULL,
+ rm_cookie, bus_impldata);
+
+ mutex_enter(&usb_ia->ia_mutex);
+ }
+ break;
+ case USBA_EVENT_TAG_PRE_SUSPEND:
+ /* set our state *after* suspending children */
+ mutex_exit(&usb_ia->ia_mutex);
+
+ /* pass pre_suspend event to all the children */
+ (void) ndi_event_run_callbacks(usb_ia->ia_ndi_event_hdl,
+ NULL, suspend_cookie, bus_impldata);
+
+ mutex_enter(&usb_ia->ia_mutex);
+ for (i = 0; i < usb_ia->ia_n_ifs; i++) {
+ usb_ia->ia_child_events[i] &= ~
+ USB_IA_CHILD_EVENT_PRESUSPEND;
+ }
+ break;
+ case USBA_EVENT_TAG_HOT_INSERTION:
+ mutex_exit(&usb_ia->ia_mutex);
+ if (usb_ia_restore_device_state(dip, usb_ia) == USB_SUCCESS) {
+
+ /*
+ * Check to see if this child has missed the disconnect
+ * event before it registered for event cb
+ */
+ mutex_enter(&usb_ia->ia_mutex);
+ for (i = 0; i < usb_ia->ia_n_ifs; i++) {
+ if (usb_ia->ia_child_events[i] &
+ USB_IA_CHILD_EVENT_DISCONNECT) {
+ usb_ia->ia_child_events[i] &=
+ ~USB_IA_CHILD_EVENT_DISCONNECT;
+ child_dip =
+ usb_ia->ia_children_dips[i];
+ mutex_exit(&usb_ia->ia_mutex);
+
+ /* post the missed disconnect */
+ (void) ndi_event_do_callback(
+ usb_ia->ia_ndi_event_hdl,
+ child_dip,
+ rm_cookie,
+ bus_impldata);
+ mutex_enter(&usb_ia->ia_mutex);
+ }
+ }
+ mutex_exit(&usb_ia->ia_mutex);
+
+ /* pass reconnect event to all the children */
+ (void) ndi_event_run_callbacks(
+ usb_ia->ia_ndi_event_hdl, NULL,
+ ins_cookie, bus_impldata);
+
+ }
+ mutex_enter(&usb_ia->ia_mutex);
+ break;
+ case USBA_EVENT_TAG_POST_RESUME:
+ /*
+ * Check to see if this child has missed the pre-suspend
+ * event before it registered for event cb
+ */
+ for (i = 0; i < usb_ia->ia_n_ifs; i++) {
+ if (usb_ia->ia_child_events[i] &
+ USB_IA_CHILD_EVENT_PRESUSPEND) {
+ usb_ia->ia_child_events[i] &=
+ ~USB_IA_CHILD_EVENT_PRESUSPEND;
+ child_dip = usb_ia->ia_children_dips[i];
+ mutex_exit(&usb_ia->ia_mutex);
+
+ /* post the missed pre-suspend event */
+ (void) ndi_event_do_callback(
+ usb_ia->ia_ndi_event_hdl,
+ child_dip, suspend_cookie,
+ bus_impldata);
+ mutex_enter(&usb_ia->ia_mutex);
+ }
+ }
+ mutex_exit(&usb_ia->ia_mutex);
+
+ /* pass post_resume event to all the children */
+ (void) ndi_event_run_callbacks(usb_ia->ia_ndi_event_hdl,
+ NULL, resume_cookie, bus_impldata);
+
+ mutex_enter(&usb_ia->ia_mutex);
+ break;
+ }
+ mutex_exit(&usb_ia->ia_mutex);
+
+}
+
+/*
+ * create the pm components required for power management
+ */
+static void
+usb_ia_create_pm_components(dev_info_t *dip, usb_ia_t *usb_ia)
+{
+ usb_common_power_t *iapm;
+ uint_t pwr_states;
+
+ USB_DPRINTF_L4(DPRINT_MASK_PM, usb_ia->ia_log_handle,
+ "usb_ia_create_pm_components: Begin");
+
+ /* Allocate the PM state structure */
+ iapm = kmem_zalloc(sizeof (usb_common_power_t), KM_SLEEP);
+
+ mutex_enter(&usb_ia->ia_mutex);
+ usb_ia->ia_pm = iapm;
+ iapm->uc_usb_statep = usb_ia;
+ iapm->uc_pm_capabilities = 0; /* XXXX should this be 0?? */
+ iapm->uc_current_power = USB_DEV_OS_FULL_PWR;
+ mutex_exit(&usb_ia->ia_mutex);
+
+ /*
+ * By not enabling parental notification, PM enforces
+ * "strict parental dependency" meaning, usb_ia won't
+ * power off until any of its children are in full power.
+ */
+
+ /*
+ * there are 3 scenarios:
+ * 1. a well behaved device should have remote wakeup
+ * at interface and device level. If the interface
+ * wakes up, usb_ia will wake up
+ * 2. if the device doesn't have remote wake up and
+ * the interface has, PM will still work, ie.
+ * the interfaces wakes up and usb_ia wakes up
+ * 3. if neither the interface nor device has remote
+ * wakeup, the interface will wake up when it is opened
+ * and goes to sleep after being closed for a while
+ * In this case usb_ia should also go to sleep shortly
+ * thereafter
+ * In all scenarios it doesn't really matter whether
+ * remote wakeup at the device level is enabled or not
+ * but we do it anyways
+ */
+ if (usb_handle_remote_wakeup(dip, USB_REMOTE_WAKEUP_ENABLE) ==
+ USB_SUCCESS) {
+ USB_DPRINTF_L3(DPRINT_MASK_PM, usb_ia->ia_log_handle,
+ "usb_ia_create_pm_components: "
+ "Remote Wakeup Enabled");
+ iapm->uc_wakeup_enabled = 1;
+ }
+
+ if (usb_create_pm_components(dip, &pwr_states) ==
+ USB_SUCCESS) {
+ iapm->uc_pwr_states = (uint8_t)pwr_states;
+ (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
+ }
+
+ USB_DPRINTF_L4(DPRINT_MASK_PM, usb_ia->ia_log_handle,
+ "usb_ia_create_pm_components: End");
+}
+
+
+/*
+ * usb_ia_obtain_state:
+ */
+static usb_ia_t *
+usb_ia_obtain_state(dev_info_t *dip)
+{
+ int instance = ddi_get_instance(dip);
+ usb_ia_t *statep = ddi_get_soft_state(usb_ia_statep, instance);
+
+ ASSERT(statep != NULL);
+
+ return (statep);
+}
diff --git a/usr/src/uts/common/io/usb/usb_mid/usb_mid.c b/usr/src/uts/common/io/usb/usb_mid/usb_mid.c
index f814538f98..5f8a1ead10 100644
--- a/usr/src/uts/common/io/usb/usb_mid/usb_mid.c
+++ b/usr/src/uts/common/io/usb/usb_mid/usb_mid.c
@@ -131,6 +131,7 @@ static int usb_mid_bus_ctl(dev_info_t *, dev_info_t *,
static int usb_mid_power(dev_info_t *, int, int);
static int usb_mid_restore_device_state(dev_info_t *, usb_mid_t *);
static usb_mid_t *usb_mid_obtain_state(dev_info_t *);
+static void usb_mid_event_cb(dev_info_t *, ddi_eventcookie_t, void *, void *);
/*
* Busops vector
@@ -200,12 +201,6 @@ static void *usb_mid_statep;
*/
static void usb_mid_create_children(usb_mid_t *usb_mid);
static int usb_mid_cleanup(dev_info_t *dip, usb_mid_t *usb_mid);
-static void usb_mid_register_events(usb_mid_t *usb_mid);
-static void usb_mid_unregister_events(usb_mid_t *usb_mid);
-
-/* usbai private */
-char *usba_get_mfg_prod_sn_str(dev_info_t *dip,
- char *buffer, int buflen);
/*
* event definition
@@ -467,7 +462,7 @@ usb_mid_bus_unconfig(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
dev_info_t *cdip, *mdip;
int interface, circular_count;
- int rval = NDI_SUCCESS;
+ int rval = NDI_SUCCESS;
USB_DPRINTF_L4(DPRINT_MASK_ALL, usb_mid->mi_log_handle,
"usb_mid_bus_unconfig: op=%d", op);
@@ -496,12 +491,13 @@ usb_mid_bus_unconfig(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
/* update children's list */
mutex_enter(&usb_mid->mi_mutex);
for (interface = 0; usb_mid->mi_children_dips &&
- (interface < usb_mid->mi_n_ifs); interface++) {
+ (interface < usb_mid->mi_n_ifs) &&
+ (usb_mid->mi_children_ifs[interface]); interface++) {
mdip = usb_mid->mi_children_dips[interface];
/* now search if this dip still exists */
for (cdip = ddi_get_child(dip); cdip && (cdip != mdip);
- cdip = ddi_get_next_sibling(cdip));
+ cdip = ddi_get_next_sibling(cdip));
if (cdip != mdip) {
/* we lost the dip on this interface */
@@ -524,106 +520,15 @@ usb_mid_bus_unconfig(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
return (rval);
}
-/*
- * functions to handle power transition for OS levels 0 -> 3
- */
-static int
-usb_mid_pwrlvl0(usb_mid_t *usb_mid)
-{
- int rval;
-
- switch (usb_mid->mi_dev_state) {
- case USB_DEV_ONLINE:
- /* Issue USB D3 command to the device here */
- rval = usb_set_device_pwrlvl3(usb_mid->mi_dip);
- ASSERT(rval == USB_SUCCESS);
-
- usb_mid->mi_dev_state = USB_DEV_PWRED_DOWN;
- usb_mid->mi_pm->mip_current_power =
- USB_DEV_OS_PWR_OFF;
- /* FALLTHRU */
- case USB_DEV_DISCONNECTED:
- case USB_DEV_SUSPENDED:
- /* allow a disconnected/cpr'ed device to go to low pwr */
-
- return (USB_SUCCESS);
- case USB_DEV_PWRED_DOWN:
- default:
- return (USB_FAILURE);
- }
-}
-
-
-/* ARGSUSED */
-static int
-usb_mid_pwrlvl1(usb_mid_t *usb_mid)
-{
- int rval;
-
- /* Issue USB D2 command to the device here */
- rval = usb_set_device_pwrlvl2(usb_mid->mi_dip);
- ASSERT(rval == USB_SUCCESS);
-
- return (USB_FAILURE);
-}
-
-
-/* ARGSUSED */
-static int
-usb_mid_pwrlvl2(usb_mid_t *usb_mid)
-{
- int rval;
-
- /* Issue USB D1 command to the device here */
- rval = usb_set_device_pwrlvl1(usb_mid->mi_dip);
- ASSERT(rval == USB_SUCCESS);
-
- return (USB_FAILURE);
-}
-
-
-static int
-usb_mid_pwrlvl3(usb_mid_t *usb_mid)
-{
- int rval;
-
- switch (usb_mid->mi_dev_state) {
- case USB_DEV_PWRED_DOWN:
- /* Issue USB D0 command to the device here */
- rval = usb_set_device_pwrlvl0(usb_mid->mi_dip);
- ASSERT(rval == USB_SUCCESS);
-
- usb_mid->mi_dev_state = USB_DEV_ONLINE;
- usb_mid->mi_pm->mip_current_power = USB_DEV_OS_FULL_PWR;
-
- /* FALLTHRU */
- case USB_DEV_ONLINE:
- /* we are already in full power */
-
- /* FALLTHRU */
- case USB_DEV_DISCONNECTED:
- case USB_DEV_SUSPENDED:
- /* allow a disconnected/cpr'ed device to go to low power */
-
- return (USB_SUCCESS);
- default:
- USB_DPRINTF_L2(DPRINT_MASK_PM, usb_mid->mi_log_handle,
- "usb_mid_pwrlvl3: Illegal state (%s)",
- usb_str_dev_state(usb_mid->mi_dev_state));
-
- return (USB_FAILURE);
- }
-}
-
/* power entry point */
/* ARGSUSED */
static int
usb_mid_power(dev_info_t *dip, int comp, int level)
{
- usb_mid_t *usb_mid;
- usb_mid_power_t *midpm;
- int rval = DDI_FAILURE;
+ usb_mid_t *usb_mid;
+ usb_common_power_t *midpm;
+ int rval = DDI_FAILURE;
usb_mid = usb_mid_obtain_state(dip);
@@ -634,34 +539,21 @@ usb_mid_power(dev_info_t *dip, int comp, int level)
midpm = usb_mid->mi_pm;
/* check if we are transitioning to a legal power level */
- if (USB_DEV_PWRSTATE_OK(midpm->mip_pwr_states, level)) {
+ if (USB_DEV_PWRSTATE_OK(midpm->uc_pwr_states, level)) {
USB_DPRINTF_L2(DPRINT_MASK_PM, usb_mid->mi_log_handle,
"usb_mid_power: illegal power level = %d "
- "mip_pwr_states = %x", level, midpm->mip_pwr_states);
+ "uc_pwr_states = %x", level, midpm->uc_pwr_states);
mutex_exit(&usb_mid->mi_mutex);
return (rval);
}
- switch (level) {
- case USB_DEV_OS_PWR_OFF:
- rval = usb_mid_pwrlvl0(usb_mid);
- break;
- case USB_DEV_OS_PWR_1:
- rval = usb_mid_pwrlvl1(usb_mid);
- break;
- case USB_DEV_OS_PWR_2:
- rval = usb_mid_pwrlvl2(usb_mid);
- break;
- case USB_DEV_OS_FULL_PWR:
- rval = usb_mid_pwrlvl3(usb_mid);
- break;
- }
+ rval = usba_common_power(dip, midpm, &(usb_mid->mi_dev_state), level);
mutex_exit(&usb_mid->mi_mutex);
- return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
+ return (rval);
}
@@ -673,7 +565,7 @@ usb_mid_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
int instance = ddi_get_instance(dip);
usb_mid_t *usb_mid = NULL;
- uint_t n_ifs;
+ uint_t n_ifs, i;
size_t size;
switch (cmd) {
@@ -762,6 +654,12 @@ usb_mid_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
usb_mid->mi_children_dips = kmem_zalloc(size, KM_SLEEP);
usb_mid->mi_child_events = kmem_zalloc(sizeof (uint8_t) * n_ifs,
KM_SLEEP);
+ usb_mid->mi_children_ifs = kmem_zalloc(sizeof (uint_t) * n_ifs,
+ KM_SLEEP);
+ for (i = 0; i < n_ifs; i++) {
+ usb_mid->mi_children_ifs[i] = 1;
+ }
+
/*
* Event handling: definition and registration
* get event handle for events that we have defined
@@ -787,7 +685,7 @@ usb_mid_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
usb_mid_create_pm_components(dip, usb_mid);
/* event registration for events from our parent */
- usb_mid_register_events(usb_mid);
+ usba_common_register_events(usb_mid->mi_dip, 1, usb_mid_event_cb);
usb_mid->mi_init_state |= USB_MID_EVENTS_REGISTERED;
@@ -854,7 +752,7 @@ usb_mid_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
static int
usb_mid_cleanup(dev_info_t *dip, usb_mid_t *usb_mid)
{
- usb_mid_power_t *midpm;
+ usb_common_power_t *midpm;
int rval;
USB_DPRINTF_L4(DPRINT_MASK_ATTA, usb_mid->mi_log_handle,
@@ -887,7 +785,7 @@ usb_mid_cleanup(dev_info_t *dip, usb_mid_t *usb_mid)
* Note that cleanup can't fail after deregistration of events.
*/
if (usb_mid->mi_init_state & USB_MID_EVENTS_REGISTERED) {
- usb_mid_unregister_events(usb_mid);
+ usba_common_unregister_events(usb_mid->mi_dip, 1);
}
midpm = usb_mid->mi_pm;
@@ -899,7 +797,7 @@ usb_mid_cleanup(dev_info_t *dip, usb_mid_t *usb_mid)
mutex_exit(&usb_mid->mi_mutex);
(void) pm_busy_component(dip, 0);
- if (midpm->mip_wakeup_enabled) {
+ if (midpm->uc_wakeup_enabled) {
/* First bring the device to full power */
(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
@@ -922,7 +820,7 @@ usb_mid_cleanup(dev_info_t *dip, usb_mid_t *usb_mid)
}
if (midpm) {
- kmem_free(midpm, sizeof (usb_mid_power_t));
+ kmem_free(midpm, sizeof (usb_common_power_t));
}
/* free children list */
@@ -936,6 +834,11 @@ usb_mid_cleanup(dev_info_t *dip, usb_mid_t *usb_mid)
usb_mid->mi_n_ifs);
}
+ if (usb_mid->mi_children_ifs) {
+ kmem_free(usb_mid->mi_children_ifs, sizeof (uint_t) *
+ usb_mid->mi_n_ifs);
+ }
+
if (usb_mid->mi_init_state & USB_MID_MINOR_NODE_CREATED) {
ddi_remove_minor_node(dip, NULL);
}
@@ -959,49 +862,6 @@ done:
}
-int
-usb_mid_devi_bind_driver(usb_mid_t *usb_mid, dev_info_t *dip)
-{
- char *name;
- uint8_t if_num = usba_get_ifno(dip);
- int rval = USB_SUCCESS;
-
- USB_DPRINTF_L4(DPRINT_MASK_ATTA, usb_mid->mi_log_handle,
- "usb_mid_devi_bind_driver: dip = 0x%p, if_num = 0x%x", dip, if_num);
-
- name = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
-
- /* bind device to the driver */
- if (ndi_devi_bind_driver(dip, 0) != NDI_SUCCESS) {
- /* if we fail to bind report an error */
- (void) usba_get_mfg_prod_sn_str(dip, name, MAXNAMELEN);
- if (name[0] != '\0') {
- if (!usb_owns_device(dip)) {
- USB_DPRINTF_L1(DPRINT_MASK_ATTA,
- usb_mid->mi_log_handle,
- "no driver found for "
- "interface %d (nodename: '%s') of %s",
- if_num, ddi_node_name(dip), name);
- } else {
- USB_DPRINTF_L1(DPRINT_MASK_ATTA,
- usb_mid->mi_log_handle,
- "no driver found for device %s", name);
- }
- } else {
- (void) ddi_pathname(dip, name);
- USB_DPRINTF_L1(DPRINT_MASK_ATTA,
- usb_mid->mi_log_handle,
- "no driver found for device %s", name);
- }
- rval = USB_FAILURE;
- }
-
- kmem_free(name, MAXNAMELEN);
-
- return (rval);
-}
-
-
static void
usb_mid_ugen_attach(usb_mid_t *usb_mid, boolean_t remove_children)
{
@@ -1053,9 +913,9 @@ static void
usb_mid_create_children(usb_mid_t *usb_mid)
{
usba_device_t *usba_device;
- uint_t n_ifs;
- uint_t i;
- dev_info_t *cdip;
+ uint_t n_ifs, if_count;
+ uint_t i, j;
+ dev_info_t *cdip, *ia_dip;
uint_t ugen_bound = 0;
uint_t bound_children = 0;
@@ -1071,6 +931,7 @@ usb_mid_create_children(usb_mid_t *usb_mid)
}
n_ifs = usb_mid->mi_n_ifs;
+ if_count = 1;
USB_DPRINTF_L4(DPRINT_MASK_ATTA, usb_mid->mi_log_handle,
"usb_mid_create_children: #interfaces = %d", n_ifs);
@@ -1078,7 +939,14 @@ usb_mid_create_children(usb_mid_t *usb_mid)
/*
* create all children if not already present
*/
- for (i = 0; i < n_ifs; i++) {
+ for (i = 0; i < n_ifs; i += if_count) {
+
+ /* ignore since this if is included by an ia */
+ if (usb_mid->mi_children_ifs[i] == 0) {
+
+ continue;
+ }
+
if (usb_mid->mi_children_dips[i] != NULL) {
if (i_ddi_node_state(
usb_mid->mi_children_dips[i]) >=
@@ -1090,10 +958,36 @@ usb_mid_create_children(usb_mid_t *usb_mid)
}
mutex_exit(&usb_mid->mi_mutex);
+ ia_dip = usba_ready_interface_association_node(usb_mid->mi_dip,
+ i, &if_count);
+
+ if (ia_dip != NULL) {
+ if (usba_bind_driver(ia_dip) == USB_SUCCESS) {
+ bound_children++;
+ if (strcmp(ddi_driver_name(ia_dip),
+ "ugen") == 0) {
+ ugen_bound++;
+ }
+ }
+
+ /*
+ * IA node owns if_count interfaces.
+ * The rest interfaces own none.
+ */
+ mutex_enter(&usb_mid->mi_mutex);
+ usb_mid->mi_children_dips[i] = ia_dip;
+ usb_mid->mi_children_ifs[i] = if_count;
+ for (j = i + 1; j < i + if_count; j++) {
+ usb_mid->mi_children_ifs[j] = 0;
+ }
+
+ continue;
+ }
+
cdip = usba_ready_interface_node(usb_mid->mi_dip, i);
- mutex_enter(&usb_mid->mi_mutex);
+
if (cdip != NULL) {
- if (usb_mid_devi_bind_driver(usb_mid, cdip) ==
+ if (usba_bind_driver(cdip) ==
USB_SUCCESS) {
bound_children++;
if (strcmp(ddi_driver_name(cdip),
@@ -1102,9 +996,17 @@ usb_mid_create_children(usb_mid_t *usb_mid)
}
}
+ /*
+ * interface node owns 1 interface always.
+ */
+ mutex_enter(&usb_mid->mi_mutex);
usb_mid->mi_children_dips[i] = cdip;
+ usb_mid->mi_children_ifs[i] = 1;
+ mutex_exit(&usb_mid->mi_mutex);
}
+
+ mutex_enter(&usb_mid->mi_mutex);
}
usb_mid->mi_removed_children = (bound_children ? B_FALSE : B_TRUE);
@@ -1262,7 +1164,7 @@ usb_mid_busop_post_event(dev_info_t *dip,
static int
usb_mid_restore_device_state(dev_info_t *dip, usb_mid_t *usb_mid)
{
- usb_mid_power_t *midpm;
+ usb_common_power_t *midpm;
USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_mid->mi_log_handle,
"usb_mid_restore_device_state: usb_mid = %p", usb_mid);
@@ -1291,7 +1193,7 @@ usb_mid_restore_device_state(dev_info_t *dip, usb_mid_t *usb_mid)
* if the device had remote wakeup earlier,
* enable it again
*/
- if (midpm->mip_wakeup_enabled) {
+ if (midpm->uc_wakeup_enabled) {
(void) usb_handle_remote_wakeup(usb_mid->mi_dip,
USB_REMOTE_WAKEUP_ENABLE);
}
@@ -1390,8 +1292,9 @@ usb_mid_event_cb(dev_info_t *dip, ddi_eventcookie_t cookie,
*/
mutex_enter(&usb_mid->mi_mutex);
for (i = 0; i < usb_mid->mi_n_ifs; i++) {
- if (usb_mid->mi_child_events[i] &
- USB_MID_CHILD_EVENT_DISCONNECT) {
+ if ((usb_mid->mi_child_events[i] &
+ USB_MID_CHILD_EVENT_DISCONNECT) &&
+ usb_mid->mi_children_ifs[i]) {
usb_mid->mi_child_events[i] &=
~USB_MID_CHILD_EVENT_DISCONNECT;
child_dip =
@@ -1427,8 +1330,9 @@ usb_mid_event_cb(dev_info_t *dip, ddi_eventcookie_t cookie,
* event before it registered for event cb
*/
for (i = 0; i < usb_mid->mi_n_ifs; i++) {
- if (usb_mid->mi_child_events[i] &
- USB_MID_CHILD_EVENT_PRESUSPEND) {
+ if ((usb_mid->mi_child_events[i] &
+ USB_MID_CHILD_EVENT_PRESUSPEND) &&
+ usb_mid->mi_children_ifs[i]) {
usb_mid->mi_child_events[i] &=
~USB_MID_CHILD_EVENT_PRESUSPEND;
child_dip = usb_mid->mi_children_dips[i];
@@ -1457,145 +1361,25 @@ usb_mid_event_cb(dev_info_t *dip, ddi_eventcookie_t cookie,
/*
- * register and unregister for events from our parent
- *
- * Note: usb_mid doesn't use the cookie fields in usba_device structure.
- * They are used/shared by children of usb_mid.
- */
-static void
-usb_mid_register_events(usb_mid_t *usb_mid)
-{
- int rval;
- usba_evdata_t *evdata;
- ddi_eventcookie_t cookie;
-
- USB_DPRINTF_L4(DPRINT_MASK_PM, usb_mid->mi_log_handle,
- "usb_mid_register_events:");
-
- evdata = usba_get_evdata(usb_mid->mi_dip);
-
- /* get event cookie, discard level and icookie for now */
- rval = ddi_get_eventcookie(usb_mid->mi_dip, DDI_DEVI_REMOVE_EVENT,
- &cookie);
-
- if (rval == DDI_SUCCESS) {
- rval = ddi_add_event_handler(usb_mid->mi_dip,
- cookie, usb_mid_event_cb, NULL, &evdata->ev_rm_cb_id);
-
- if (rval != DDI_SUCCESS) {
-
- goto fail;
- }
- }
- rval = ddi_get_eventcookie(usb_mid->mi_dip, DDI_DEVI_INSERT_EVENT,
- &cookie);
- if (rval == DDI_SUCCESS) {
- rval = ddi_add_event_handler(usb_mid->mi_dip,
- cookie, usb_mid_event_cb, NULL, &evdata->ev_ins_cb_id);
-
- if (rval != DDI_SUCCESS) {
-
- goto fail;
- }
- }
- rval = ddi_get_eventcookie(usb_mid->mi_dip, USBA_PRE_SUSPEND_EVENT,
- &cookie);
- if (rval == DDI_SUCCESS) {
- rval = ddi_add_event_handler(usb_mid->mi_dip,
- cookie, usb_mid_event_cb, NULL, &evdata->ev_suspend_cb_id);
-
- if (rval != DDI_SUCCESS) {
-
- goto fail;
- }
- }
- rval = ddi_get_eventcookie(usb_mid->mi_dip, USBA_POST_RESUME_EVENT,
- &cookie);
- if (rval == DDI_SUCCESS) {
- rval = ddi_add_event_handler(usb_mid->mi_dip,
- cookie, usb_mid_event_cb, NULL,
- &evdata->ev_resume_cb_id);
-
- if (rval != DDI_SUCCESS) {
-
- goto fail;
- }
- }
-
- return;
-
-
-fail:
- usb_mid_unregister_events(usb_mid);
-
-}
-
-
-static void
-usb_mid_unregister_events(usb_mid_t *usb_mid)
-{
- int rval;
- usba_evdata_t *evdata;
- usba_device_t *usba_device = usba_get_usba_device(usb_mid->mi_dip);
-
- USB_DPRINTF_L4(DPRINT_MASK_PM, usb_mid->mi_log_handle,
- "usb_mid_unregister_events:");
-
- evdata = usba_get_evdata(usb_mid->mi_dip);
- if (evdata) {
- if (evdata->ev_rm_cb_id) {
- rval = ddi_remove_event_handler(evdata->ev_rm_cb_id);
- ASSERT(rval == DDI_SUCCESS);
- }
-
- if (evdata->ev_ins_cb_id) {
- rval = ddi_remove_event_handler(evdata->ev_ins_cb_id);
- ASSERT(rval == DDI_SUCCESS);
- }
-
- if (evdata->ev_resume_cb_id) {
- rval =
- ddi_remove_event_handler(evdata->ev_resume_cb_id);
- ASSERT(rval == DDI_SUCCESS);
- }
-
- if (evdata->ev_suspend_cb_id) {
- rval =
- ddi_remove_event_handler(evdata->ev_suspend_cb_id);
- ASSERT(rval == DDI_SUCCESS);
- }
- }
-
- /* clear event data for children, required for cfgmadm unconfigure */
- usba_free_evdata(usba_device->usb_evdata);
- usba_device->usb_evdata = NULL;
- usba_device->rm_cookie = NULL;
- usba_device->ins_cookie = NULL;
- usba_device->suspend_cookie = NULL;
- usba_device->resume_cookie = NULL;
-}
-
-
-/*
* create the pm components required for power management
*/
static void
usb_mid_create_pm_components(dev_info_t *dip, usb_mid_t *usb_mid)
{
- usb_mid_power_t *midpm;
+ usb_common_power_t *midpm;
uint_t pwr_states;
USB_DPRINTF_L4(DPRINT_MASK_PM, usb_mid->mi_log_handle,
"usb_mid_create_pm_components: Begin");
/* Allocate the PM state structure */
- midpm = kmem_zalloc(sizeof (usb_mid_power_t), KM_SLEEP);
+ midpm = kmem_zalloc(sizeof (usb_common_power_t), KM_SLEEP);
mutex_enter(&usb_mid->mi_mutex);
usb_mid->mi_pm = midpm;
- midpm->mip_usb_mid = usb_mid;
- midpm->mip_pm_capabilities = 0; /* XXXX should this be 0?? */
- midpm->mip_current_power = USB_DEV_OS_FULL_PWR;
+ midpm->uc_usb_statep = usb_mid;
+ midpm->uc_pm_capabilities = 0; /* XXXX should this be 0?? */
+ midpm->uc_current_power = USB_DEV_OS_FULL_PWR;
mutex_exit(&usb_mid->mi_mutex);
/*
@@ -1626,12 +1410,12 @@ usb_mid_create_pm_components(dev_info_t *dip, usb_mid_t *usb_mid)
USB_DPRINTF_L3(DPRINT_MASK_PM, usb_mid->mi_log_handle,
"usb_mid_create_pm_components: "
"Remote Wakeup Enabled");
- midpm->mip_wakeup_enabled = 1;
+ midpm->uc_wakeup_enabled = 1;
}
if (usb_create_pm_components(dip, &pwr_states) ==
USB_SUCCESS) {
- midpm->mip_pwr_states = (uint8_t)pwr_states;
+ midpm->uc_pwr_states = (uint8_t)pwr_states;
(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
}
diff --git a/usr/src/uts/common/io/usb/usba/hcdi.c b/usr/src/uts/common/io/usb/usba/hcdi.c
index 13a72b588f..f4b47dd233 100644
--- a/usr/src/uts/common/io/usb/usba/hcdi.c
+++ b/usr/src/uts/common/io/usb/usba/hcdi.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
@@ -195,6 +194,9 @@ usba_hcdi_register(usba_hcdi_register_args_t *args, uint_t flags)
} else if (strcmp(datap, "interface") == 0) {
hcdi->hcdi_ugen_default_binding =
USBA_UGEN_INTERFACE_BINDING;
+ } else if (strcmp(datap, "interface-association") == 0) {
+ hcdi->hcdi_ugen_default_binding =
+ USBA_UGEN_INTERFACE_ASSOCIATION_BINDING;
} else {
USB_DPRINTF_L2(DPRINT_MASK_HCDI,
hcdi->hcdi_log_handle,
diff --git a/usr/src/uts/common/io/usb/usba/parser.c b/usr/src/uts/common/io/usb/usba/parser.c
index e6d1893ebe..f4e0af84e5 100644
--- a/usr/src/uts/common/io/usb/usba/parser.c
+++ b/usr/src/uts/common/io/usb/usba/parser.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -297,6 +296,39 @@ usba_parse_cfg_pwr_descr(
size_t
+usb_parse_ia_descr(uchar_t *buf, /* from GET_DESCRIPTOR(CONFIGURATION) */
+ size_t buflen,
+ size_t first_if,
+ usb_ia_descr_t *ret_descr,
+ size_t ret_buf_len)
+{
+ uchar_t *bufend = buf + buflen;
+
+ if ((buf == NULL) || (ret_descr == NULL)) {
+
+ return (USB_PARSE_ERROR);
+ }
+
+ while (buf + USB_IA_DESCR_SIZE <= bufend) {
+ if ((buf[1] == USB_DESCR_TYPE_IA) &&
+ (buf[2] == first_if)) {
+
+ return (usb_parse_data("cccccccc",
+ buf, bufend - buf, ret_descr, ret_buf_len));
+ }
+
+ /*
+ * Check for a bad buffer.
+ * If buf[0] is 0, then this will be an infinite loop
+ */
+ INCREMENT_BUF(buf);
+ }
+
+ return (USB_PARSE_ERROR);
+}
+
+
+size_t
usb_parse_if_descr(uchar_t *buf, /* from GET_DESCRIPTOR(CONFIGURATION) */
size_t buflen,
uint_t if_number,
diff --git a/usr/src/uts/common/io/usb/usba/usba.c b/usr/src/uts/common/io/usb/usba/usba.c
index 27f5cd9cfc..9dd3547609 100644
--- a/usr/src/uts/common/io/usb/usba/usba.c
+++ b/usr/src/uts/common/io/usb/usba/usba.c
@@ -83,7 +83,7 @@ extern struct mod_ops mod_miscops;
struct modlmisc modlmisc = {
&mod_miscops, /* Type of module */
- "USBA: USB Architecture 2.0 %I%"
+ "USBA: USB Architecture 2.0 1.66"
};
struct modlinkage modlinkage = {
@@ -152,6 +152,14 @@ _info(struct modinfo *modinfop)
return (mod_info(&modlinkage, modinfop));
}
+boolean_t
+usba_owns_ia(dev_info_t *dip)
+{
+ int if_count = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "interface-count", 0);
+
+ return ((if_count) ? B_TRUE : B_FALSE);
+}
/*
* common bus ctl for hcd, usb_mid, and hubd
@@ -199,6 +207,14 @@ usba_bus_ctl(dev_info_t *dip,
"usb%x,%x",
usba_device->usb_dev_descr->idVendor,
usba_device->usb_dev_descr->idProduct);
+ } else if (usba_owns_ia(rdip)) {
+ (void) snprintf(compat_name,
+ sizeof (compat_name),
+ "usbia%x,%x.config%x.%x",
+ usba_device->usb_dev_descr->idVendor,
+ usba_device->usb_dev_descr->idProduct,
+ usba_device->usb_cfg_value,
+ usb_get_if_number(rdip));
} else {
(void) snprintf(compat_name,
sizeof (compat_name),
@@ -230,7 +246,9 @@ usba_bus_ctl(dev_info_t *dip,
"%s@%s, %s%d at bus address %d\n",
(usba_device->usb_dev_descr->bcdUSB & 0xff00) >> 8,
usba_device->usb_dev_descr->bcdUSB & 0xff,
- (usb_owns_device(rdip) ? "device" : "interface"),
+ (usb_owns_device(rdip) ? "device" :
+ ((usba_owns_ia(rdip) ? "interface-association" :
+ "interface"))),
compat_name, speed,
(hub_usba_device->usb_dev_descr->bcdUSB &
0xff00) >> 8,
@@ -1386,9 +1404,21 @@ usba_set_usba_device(dev_info_t *dip, usba_device_t *usba_device)
static node_name_entry_t device_node_name_table[] = {
{ USB_CLASS_COMM, DONTCARE, DONTCARE, "communications" },
{ USB_CLASS_HUB, DONTCARE, DONTCARE, "hub" },
+{ USB_CLASS_DIAG, DONTCARE, DONTCARE, "diagnostics" },
+{ USB_CLASS_MISC, DONTCARE, DONTCARE, "miscellaneous" },
{ DONTCARE, DONTCARE, DONTCARE, "device" }
};
+/* interface-association node table */
+static node_name_entry_t ia_node_name_table[] = {
+{ USB_CLASS_AUDIO, DONTCARE, DONTCARE, "audio" },
+{ USB_CLASS_VIDEO, DONTCARE, DONTCARE, "video" },
+{ USB_CLASS_WIRELESS, USB_SUBCLS_WUSB_2, USB_PROTO_WUSB_DWA,
+ "device-wire-adaptor" },
+{ USB_CLASS_WIRELESS, DONTCARE, DONTCARE, "wireless-controller" },
+{ DONTCARE, DONTCARE, DONTCARE, "interface-association" }
+};
+
/* interface node table, refer to section 3.3.2.1 */
static node_name_entry_t if_node_name_table[] = {
{ USB_CLASS_AUDIO, USB_SUBCLS_AUD_CONTROL, DONTCARE, "sound-control" },
@@ -1403,7 +1433,7 @@ static node_name_entry_t if_node_name_table[] = {
{ USB_CLASS_COMM, USB_SUBCLS_CDCC_ISDN, DONTCARE, "isdn" },
{ USB_CLASS_COMM, USB_SUBCLS_CDCC_ETHERNET, DONTCARE, "ethernet" },
{ USB_CLASS_COMM, USB_SUBCLS_CDCC_ATM_NETWORK, DONTCARE, "atm-network" },
-{ USB_CLASS_COMM, DONTCARE, DONTCARE, "control" },
+{ USB_CLASS_COMM, DONTCARE, DONTCARE, "communications" },
{ USB_CLASS_HID, USB_SUBCLS_HID_1, USB_PROTO_HID_KEYBOARD, "keyboard" },
{ USB_CLASS_HID, USB_SUBCLS_HID_1, USB_PROTO_HID_MOUSE, "mouse" },
@@ -1413,6 +1443,8 @@ static node_name_entry_t if_node_name_table[] = {
{ USB_CLASS_PHYSICAL, DONTCARE, DONTCARE, "physical" },
+{ USB_CLASS_IMAGE, DONTCARE, DONTCARE, "image" },
+
{ USB_CLASS_PRINTER, DONTCARE, DONTCARE, "printer" },
{ USB_CLASS_MASS_STORAGE, DONTCARE, DONTCARE, "storage" },
@@ -1421,6 +1453,10 @@ static node_name_entry_t if_node_name_table[] = {
{ USB_CLASS_SECURITY, DONTCARE, DONTCARE, "security" },
+{ USB_CLASS_VIDEO, USB_SUBCLS_VIDEO_CONTROL, DONTCARE, "video-control" },
+{ USB_CLASS_VIDEO, USB_SUBCLS_VIDEO_STREAM, DONTCARE, "video-stream" },
+{ USB_CLASS_VIDEO, DONTCARE, DONTCARE, "video" },
+
{ USB_CLASS_APP, USB_SUBCLS_APP_FIRMWARE, DONTCARE, "firmware" },
{ USB_CLASS_APP, USB_SUBCLS_APP_IRDA, DONTCARE, "IrDa" },
{ USB_CLASS_APP, USB_SUBCLS_APP_TEST, DONTCARE, "test" },
@@ -1443,7 +1479,7 @@ static node_name_entry_t combined_node_name_table[] = {
{ USB_CLASS_COMM, USB_SUBCLS_CDCC_ISDN, DONTCARE, "isdn" },
{ USB_CLASS_COMM, USB_SUBCLS_CDCC_ETHERNET, DONTCARE, "ethernet" },
{ USB_CLASS_COMM, USB_SUBCLS_CDCC_ATM_NETWORK, DONTCARE, "atm-network" },
-{ USB_CLASS_COMM, DONTCARE, DONTCARE, "control" },
+{ USB_CLASS_COMM, DONTCARE, DONTCARE, "communications" },
{ USB_CLASS_HID, USB_SUBCLS_HID_1, USB_PROTO_HID_KEYBOARD, "keyboard" },
{ USB_CLASS_HID, USB_SUBCLS_HID_1, USB_PROTO_HID_MOUSE, "mouse" },
@@ -1451,25 +1487,41 @@ static node_name_entry_t combined_node_name_table[] = {
{ USB_CLASS_PHYSICAL, DONTCARE, DONTCARE, "physical" },
+{ USB_CLASS_IMAGE, DONTCARE, DONTCARE, "image" },
+
{ USB_CLASS_PRINTER, DONTCARE, DONTCARE, "printer" },
+{ USB_CLASS_MASS_STORAGE, USB_SUBCLS_MS_RBC_T10, DONTCARE, "storage" },
+{ USB_CLASS_MASS_STORAGE, USB_SUBCLS_MS_SFF8020I, DONTCARE, "cdrom" },
+{ USB_CLASS_MASS_STORAGE, USB_SUBCLS_MS_QIC_157, DONTCARE, "tape" },
+{ USB_CLASS_MASS_STORAGE, USB_SUBCLS_MS_UFI, DONTCARE, "floppy" },
+{ USB_CLASS_MASS_STORAGE, USB_SUBCLS_MS_SFF8070I, DONTCARE, "storage" },
+{ USB_CLASS_MASS_STORAGE, USB_SUBCLS_MS_SCSI, DONTCARE, "storage" },
{ USB_CLASS_MASS_STORAGE, DONTCARE, DONTCARE, "storage" },
{ USB_CLASS_CDC_DATA, DONTCARE, DONTCARE, "data" },
{ USB_CLASS_SECURITY, DONTCARE, DONTCARE, "security" },
+{ USB_CLASS_VIDEO, USB_SUBCLS_VIDEO_CONTROL, DONTCARE, "video-control" },
+{ USB_CLASS_VIDEO, USB_SUBCLS_VIDEO_STREAM, DONTCARE, "video-stream" },
+{ USB_CLASS_VIDEO, DONTCARE, DONTCARE, "video" },
+
{ USB_CLASS_APP, USB_SUBCLS_APP_FIRMWARE, DONTCARE, "firmware" },
{ USB_CLASS_APP, USB_SUBCLS_APP_IRDA, DONTCARE, "IrDa" },
{ USB_CLASS_APP, USB_SUBCLS_APP_TEST, DONTCARE, "test" },
-{ USB_CLASS_HUB, DONTCARE, DONTCARE, "hub" },
{ USB_CLASS_COMM, DONTCARE, DONTCARE, "communications" },
-{ DONTCARE, DONTCARE, DONTCARE, "device" },
+{ USB_CLASS_HUB, DONTCARE, DONTCARE, "hub" },
+{ USB_CLASS_DIAG, DONTCARE, DONTCARE, "diagnostics" },
+{ USB_CLASS_MISC, DONTCARE, DONTCARE, "miscellaneous" },
+{ DONTCARE, DONTCARE, DONTCARE, "device" }
};
static size_t device_node_name_table_size =
sizeof (device_node_name_table)/sizeof (struct node_name_entry);
+static size_t ia_node_name_table_size =
+ sizeof (ia_node_name_table)/sizeof (struct node_name_entry);
static size_t if_node_name_table_size =
sizeof (if_node_name_table)/sizeof (struct node_name_entry);
static size_t combined_node_name_table_size =
@@ -1485,6 +1537,11 @@ usba_set_node_name(dev_info_t *dip, uint8_t class, uint8_t subclass,
node_name_entry_t *node_name_table;
switch (flag) {
+ /* interface share node names with interface-association */
+ case FLAG_INTERFACE_ASSOCIATION_NODE:
+ node_name_table = ia_node_name_table;
+ size = ia_node_name_table_size;
+ break;
case FLAG_INTERFACE_NODE:
node_name_table = if_node_name_table;
size = if_node_name_table_size;
@@ -1511,6 +1568,7 @@ usba_set_node_name(dev_info_t *dip, uint8_t class, uint8_t subclass,
((s == DONTCARE) || (s == subclass)) &&
((p == DONTCARE) || (p == protocol))) {
char *name = node_name_table[i].name;
+
(void) ndi_devi_set_nodename(dip, name, 0);
break;
}
@@ -2094,6 +2152,248 @@ usba_ready_device_node(dev_info_t *child_dip)
/*
+ * driver binding at interface association level. the first arg is the parent
+ * dip. if_count returns amount of interfaces which are associated within
+ * this interface-association that starts from first_if.
+ */
+/*ARGSUSED*/
+dev_info_t *
+usba_ready_interface_association_node(dev_info_t *dip,
+ uint_t first_if,
+ uint_t *if_count)
+{
+ dev_info_t *child_dip = NULL;
+ usba_device_t *child_ud = usba_get_usba_device(dip);
+ usb_dev_descr_t *usb_dev_descr;
+ size_t usb_cfg_length;
+ uchar_t *usb_cfg;
+ usb_ia_descr_t ia_descr;
+ int i, n, rval;
+ int reg[2];
+ size_t size;
+ usb_port_status_t port_status;
+ char *force_bind = NULL;
+
+ usb_cfg = usb_get_raw_cfg_data(dip, &usb_cfg_length);
+
+ mutex_enter(&child_ud->usb_mutex);
+
+ usb_dev_descr = child_ud->usb_dev_descr;
+
+ /*
+ * for each interface association, determine all compatible names
+ */
+ USB_DPRINTF_L3(DPRINT_MASK_USBA, usba_log_handle,
+ "usba_ready_ia_node: "
+ "port %d, interface = %d, port_status = %x",
+ child_ud->usb_port, first_if, child_ud->usb_port_status);
+
+ /* Parse the interface descriptor */
+ size = usb_parse_ia_descr(
+ usb_cfg,
+ usb_cfg_length,
+ first_if, /* interface index */
+ &ia_descr,
+ USB_IA_DESCR_SIZE);
+
+ *if_count = 1;
+ if (size != USB_IA_DESCR_SIZE) {
+ USB_DPRINTF_L2(DPRINT_MASK_USBA, usba_log_handle,
+ "parsing ia: size (%lu) != USB_IA_DESCR_SIZE (%d)",
+ size, USB_IA_DESCR_SIZE);
+ mutex_exit(&child_ud->usb_mutex);
+
+ return (NULL);
+ }
+
+ port_status = child_ud->usb_port_status;
+
+ /* create reg property */
+ reg[0] = first_if;
+ reg[1] = child_ud->usb_cfg_value;
+
+ mutex_exit(&child_ud->usb_mutex);
+
+ /* clone this dip */
+ rval = usba_create_child_devi(dip,
+ "interface-association",
+ NULL, /* usba_hcdi ops */
+ NULL, /* root hub dip */
+ port_status, /* port status */
+ child_ud, /* share this usba_device */
+ &child_dip);
+
+ if (rval != USB_SUCCESS) {
+
+ goto fail;
+ }
+
+ rval = ndi_prop_update_int_array(
+ DDI_DEV_T_NONE, child_dip, "reg", reg, 2);
+
+ if (rval != DDI_PROP_SUCCESS) {
+
+ goto fail;
+ }
+
+ usba_set_node_name(child_dip, ia_descr.bFunctionClass,
+ ia_descr.bFunctionSubClass, ia_descr.bFunctionProtocol,
+ FLAG_INTERFACE_ASSOCIATION_NODE);
+
+ /* check force binding */
+ if (usba_ugen_force_binding ==
+ USBA_UGEN_INTERFACE_ASSOCIATION_BINDING) {
+ force_bind = "ugen";
+ }
+
+ /*
+ * check whether there is another dip with this name and address
+ */
+ ASSERT(usba_find_existing_node(child_dip) == NULL);
+
+ mutex_enter(&usba_mutex);
+ n = 0;
+
+ if (force_bind) {
+ (void) ndi_devi_set_nodename(child_dip, force_bind, 0);
+ (void) strncpy(usba_name[n++], force_bind,
+ USBA_MAX_COMPAT_NAME_LEN);
+ }
+
+ /* 1) usbiaVID,PID.REV.configCN.FN */
+ (void) sprintf(usba_name[n++],
+ "usbia%x,%x.%x.config%x.%x",
+ usb_dev_descr->idVendor,
+ usb_dev_descr->idProduct,
+ usb_dev_descr->bcdDevice,
+ child_ud->usb_cfg_value,
+ first_if);
+
+ /* 2) usbiaVID,PID.configCN.FN */
+ (void) sprintf(usba_name[n++],
+ "usbia%x,%x.config%x.%x",
+ usb_dev_descr->idVendor,
+ usb_dev_descr->idProduct,
+ child_ud->usb_cfg_value,
+ first_if);
+
+
+ if (ia_descr.bFunctionClass) {
+ /* 3) usbiaVID,classFC.FSC.FPROTO */
+ (void) sprintf(usba_name[n++],
+ "usbia%x,class%x.%x.%x",
+ usb_dev_descr->idVendor,
+ ia_descr.bFunctionClass,
+ ia_descr.bFunctionSubClass,
+ ia_descr.bFunctionProtocol);
+
+ /* 4) usbiaVID,classFC.FSC */
+ (void) sprintf(usba_name[n++],
+ "usbia%x,class%x.%x",
+ usb_dev_descr->idVendor,
+ ia_descr.bFunctionClass,
+ ia_descr.bFunctionSubClass);
+
+ /* 5) usbiaVID,classFC */
+ (void) sprintf(usba_name[n++],
+ "usbia%x,class%x",
+ usb_dev_descr->idVendor,
+ ia_descr.bFunctionClass);
+
+ /* 6) usbia,classFC.FSC.FPROTO */
+ (void) sprintf(usba_name[n++],
+ "usbia,class%x.%x.%x",
+ ia_descr.bFunctionClass,
+ ia_descr.bFunctionSubClass,
+ ia_descr.bFunctionProtocol);
+
+ /* 7) usbia,classFC.FSC */
+ (void) sprintf(usba_name[n++],
+ "usbia,class%x.%x",
+ ia_descr.bFunctionClass,
+ ia_descr.bFunctionSubClass);
+
+ /* 8) usbia,classFC */
+ (void) sprintf(usba_name[n++],
+ "usbia,class%x",
+ ia_descr.bFunctionClass);
+ }
+
+ if (usba_get_ugen_binding(child_dip) ==
+ USBA_UGEN_INTERFACE_ASSOCIATION_BINDING) {
+ /* 9) ugen */
+ (void) sprintf(usba_name[n++], "ugen");
+ } else {
+
+ (void) sprintf(usba_name[n++], "usb,ia");
+ }
+
+ for (i = 0; i < n; i += 2) {
+ USB_DPRINTF_L3(DPRINT_MASK_USBA, usba_log_handle,
+ "compatible name:\t%s\t%s", usba_compatible[i],
+ (((i+1) < n)? usba_compatible[i+1] : ""));
+ }
+ mutex_exit(&usba_mutex);
+
+ /* create compatible property */
+ if (n) {
+ rval = ndi_prop_update_string_array(
+ DDI_DEV_T_NONE, child_dip,
+ "compatible", (char **)usba_compatible,
+ n);
+
+ if (rval != DDI_PROP_SUCCESS) {
+
+ goto fail;
+ }
+ }
+
+ /* update the address property */
+ rval = ndi_prop_update_int(DDI_DEV_T_NONE, child_dip,
+ "assigned-address", child_ud->usb_addr);
+ if (rval != DDI_PROP_SUCCESS) {
+ USB_DPRINTF_L2(DPRINT_MASK_USBA, usba_log_handle,
+ "usba_ready_interface_node: address update failed");
+ }
+
+ /* create property with first interface number */
+ rval = ndi_prop_update_int(DDI_DEV_T_NONE, child_dip,
+ "interface", ia_descr.bFirstInterface);
+
+ if (rval != DDI_PROP_SUCCESS) {
+
+ goto fail;
+ }
+
+ /* create property with the count of interfaces in this ia */
+ rval = ndi_prop_update_int(DDI_DEV_T_NONE, child_dip,
+ "interface-count", ia_descr.bInterfaceCount);
+
+ if (rval != DDI_PROP_SUCCESS) {
+
+ goto fail;
+ }
+
+ USB_DPRINTF_L2(DPRINT_MASK_USBA, usba_log_handle,
+ "%s%d port %d: %s, dip = 0x%p",
+ ddi_node_name(ddi_get_parent(dip)),
+ ddi_get_instance(ddi_get_parent(dip)),
+ child_ud->usb_port, ddi_node_name(child_dip), child_dip);
+
+ *if_count = ia_descr.bInterfaceCount;
+ usba_set_usba_device(child_dip, child_ud);
+ ASSERT(!mutex_owned(&(usba_get_usba_device(child_dip)->usb_mutex)));
+
+ return (child_dip);
+
+fail:
+ (void) usba_destroy_child_devi(child_dip, NDI_DEVI_REMOVE);
+
+ return (NULL);
+}
+
+
+/*
* driver binding at interface level, the first arg will be the
* the parent dip
*/
@@ -2184,12 +2484,10 @@ usba_ready_interface_node(dev_info_t *dip, uint_t intf)
force_bind = "ugen";
}
-#ifdef DEBUG
/*
* check whether there is another dip with this name and address
*/
ASSERT(usba_find_existing_node(child_dip) == NULL);
-#endif
mutex_enter(&usba_mutex);
n = 0;
diff --git a/usr/src/uts/common/io/usb/usba/usbai_util.c b/usr/src/uts/common/io/usb/usba/usbai_util.c
index b9f979f86f..899a8eb444 100644
--- a/usr/src/uts/common/io/usb/usba/usbai_util.c
+++ b/usr/src/uts/common/io/usb/usba/usbai_util.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -35,6 +34,8 @@
#include <sys/usb/usba/usba_impl.h>
#include <sys/usb/usba/hcdi_impl.h>
+extern void usba_free_evdata(usba_evdata_t *);
+
static mblk_t *usba_get_cfg_cloud(dev_info_t *, usb_pipe_handle_t, int);
/* local functions */
@@ -815,6 +816,21 @@ usb_owns_device(dev_info_t *dip)
}
+/* check whether the interface is in this interface association */
+boolean_t
+usba_check_if_in_ia(dev_info_t *dip, int n_if)
+{
+ int first_if, if_count;
+
+ first_if = usb_get_if_number(dip);
+ if_count = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
+ DDI_PROP_DONTPASS, "interface-count", -1);
+ if_count += first_if;
+
+ return ((n_if >= first_if && n_if < if_count) ? B_TRUE : B_FALSE);
+}
+
+
uint8_t
usba_get_ifno(dev_info_t *dip)
{
@@ -917,8 +933,8 @@ usba_sync_set_alt_if(dev_info_t *dip,
"uf=0x%x", ddi_node_name(dip), interface,
alt_number, flags);
- /* if we don't own the device, we must own the interface */
- if (!usb_owns_device(dip) &&
+ /* if we don't own the device, we must own the interface or ia */
+ if (!usb_owns_device(dip) && !usba_check_if_in_ia(dip, interface) &&
(interface != usb_get_if_number(dip))) {
usba_release_ph_data(ph_data->p_ph_impl);
@@ -2177,3 +2193,242 @@ usba_test_allocb(size_t size, uint_t pri)
}
}
#endif
+
+
+/*
+ * usb common power management for usb_mid, usb_ia and maybe other simple
+ * drivers.
+ */
+
+/*
+ * functions to handle power transition for OS levels 0 -> 3
+ */
+static int
+usb_common_pwrlvl0(dev_info_t *dip, usb_common_power_t *pm, int *dev_state)
+{
+ int rval;
+
+ switch (*dev_state) {
+ case USB_DEV_ONLINE:
+ /* Issue USB D3 command to the device here */
+ rval = usb_set_device_pwrlvl3(dip);
+ ASSERT(rval == USB_SUCCESS);
+
+ *dev_state = USB_DEV_PWRED_DOWN;
+ pm->uc_current_power = USB_DEV_OS_PWR_OFF;
+ /* FALLTHRU */
+ case USB_DEV_DISCONNECTED:
+ case USB_DEV_SUSPENDED:
+ /* allow a disconnected/cpr'ed device to go to low pwr */
+
+ return (USB_SUCCESS);
+ case USB_DEV_PWRED_DOWN:
+ default:
+ return (USB_FAILURE);
+ }
+}
+
+
+/* ARGSUSED */
+static int
+usb_common_pwrlvl1(dev_info_t *dip, usb_common_power_t *pm, int *dev_state)
+{
+ int rval;
+
+ /* Issue USB D2 command to the device here */
+ rval = usb_set_device_pwrlvl2(dip);
+ ASSERT(rval == USB_SUCCESS);
+
+ return (USB_FAILURE);
+}
+
+
+/* ARGSUSED */
+static int
+usb_common_pwrlvl2(dev_info_t *dip, usb_common_power_t *pm, int *dev_state)
+{
+ int rval;
+
+ /* Issue USB D1 command to the device here */
+ rval = usb_set_device_pwrlvl1(dip);
+ ASSERT(rval == USB_SUCCESS);
+
+ return (USB_FAILURE);
+}
+
+
+static int
+usb_common_pwrlvl3(dev_info_t *dip, usb_common_power_t *pm, int *dev_state)
+{
+ int rval;
+
+ switch (*dev_state) {
+ case USB_DEV_PWRED_DOWN:
+ /* Issue USB D0 command to the device here */
+ rval = usb_set_device_pwrlvl0(dip);
+ ASSERT(rval == USB_SUCCESS);
+
+ *dev_state = USB_DEV_ONLINE;
+ pm->uc_current_power = USB_DEV_OS_FULL_PWR;
+
+ /* FALLTHRU */
+ case USB_DEV_ONLINE:
+ /* we are already in full power */
+
+ /* FALLTHRU */
+ case USB_DEV_DISCONNECTED:
+ case USB_DEV_SUSPENDED:
+ /* allow a disconnected/cpr'ed device to go to low power */
+
+ return (USB_SUCCESS);
+ default:
+ USB_DPRINTF_L2(DPRINT_MASK_USBA, usbai_log_handle,
+ "usb_common_pwrlvl3: Illegal state (%s)",
+ usb_str_dev_state(*dev_state));
+
+ return (USB_FAILURE);
+ }
+}
+
+/* power management */
+int
+usba_common_power(dev_info_t *dip, usb_common_power_t *pm, int *dev_state,
+ int level)
+{
+ int rval = DDI_FAILURE;
+
+ switch (level) {
+ case USB_DEV_OS_PWR_OFF:
+ rval = usb_common_pwrlvl0(dip, pm, dev_state);
+ break;
+ case USB_DEV_OS_PWR_1:
+ rval = usb_common_pwrlvl1(dip, pm, dev_state);
+ break;
+ case USB_DEV_OS_PWR_2:
+ rval = usb_common_pwrlvl2(dip, pm, dev_state);
+ break;
+ case USB_DEV_OS_FULL_PWR:
+ rval = usb_common_pwrlvl3(dip, pm, dev_state);
+ break;
+ }
+
+ return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
+}
+
+/*
+ * register and unregister for events from our parent for usb_mid and usb_ia
+ * and maybe other nexus driver.
+ *
+ * Note: The cookie fields in usba_device structure is not used. They are
+ * used/shared by children.
+ */
+void
+usba_common_register_events(dev_info_t *dip, uint_t if_num,
+ void (*event_cb)(dev_info_t *, ddi_eventcookie_t, void *, void *))
+{
+ int rval;
+ usba_evdata_t *evdata;
+ ddi_eventcookie_t cookie;
+
+ USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle,
+ "usb_common_register_events:");
+
+ evdata = usba_get_evdata(dip);
+
+ /* get event cookie, discard level and icookie for now */
+ rval = ddi_get_eventcookie(dip, DDI_DEVI_REMOVE_EVENT,
+ &cookie);
+
+ if (rval == DDI_SUCCESS) {
+ rval = ddi_add_event_handler(dip,
+ cookie, event_cb, NULL, &evdata->ev_rm_cb_id);
+
+ if (rval != DDI_SUCCESS) {
+
+ goto fail;
+ }
+ }
+ rval = ddi_get_eventcookie(dip, DDI_DEVI_INSERT_EVENT,
+ &cookie);
+ if (rval == DDI_SUCCESS) {
+ rval = ddi_add_event_handler(dip, cookie, event_cb,
+ NULL, &evdata->ev_ins_cb_id);
+
+ if (rval != DDI_SUCCESS) {
+
+ goto fail;
+ }
+ }
+ rval = ddi_get_eventcookie(dip, USBA_PRE_SUSPEND_EVENT, &cookie);
+ if (rval == DDI_SUCCESS) {
+ rval = ddi_add_event_handler(dip,
+ cookie, event_cb, NULL, &evdata->ev_suspend_cb_id);
+
+ if (rval != DDI_SUCCESS) {
+
+ goto fail;
+ }
+ }
+ rval = ddi_get_eventcookie(dip, USBA_POST_RESUME_EVENT, &cookie);
+ if (rval == DDI_SUCCESS) {
+ rval = ddi_add_event_handler(dip, cookie, event_cb, NULL,
+ &evdata->ev_resume_cb_id);
+
+ if (rval != DDI_SUCCESS) {
+
+ goto fail;
+ }
+ }
+
+ return;
+
+
+fail:
+ usba_common_unregister_events(dip, if_num);
+
+}
+
+void
+usba_common_unregister_events(dev_info_t *dip, uint_t if_num)
+{
+ usba_evdata_t *evdata;
+ usba_device_t *usba_device = usba_get_usba_device(dip);
+ int i;
+
+ evdata = usba_get_evdata(dip);
+
+ if (evdata->ev_rm_cb_id != NULL) {
+ (void) ddi_remove_event_handler(evdata->ev_rm_cb_id);
+ evdata->ev_rm_cb_id = NULL;
+ }
+
+ if (evdata->ev_ins_cb_id != NULL) {
+ (void) ddi_remove_event_handler(evdata->ev_ins_cb_id);
+ evdata->ev_ins_cb_id = NULL;
+ }
+
+ if (evdata->ev_suspend_cb_id != NULL) {
+ (void) ddi_remove_event_handler(evdata->ev_suspend_cb_id);
+ evdata->ev_suspend_cb_id = NULL;
+ }
+
+ if (evdata->ev_resume_cb_id != NULL) {
+ (void) ddi_remove_event_handler(evdata->ev_resume_cb_id);
+ evdata->ev_resume_cb_id = NULL;
+ }
+
+ /* clear event data for children, required for cfgmadm unconfigure */
+ if (usb_owns_device(dip)) {
+ usba_free_evdata(usba_device->usb_evdata);
+ usba_device->usb_evdata = NULL;
+ usba_device->rm_cookie = NULL;
+ usba_device->ins_cookie = NULL;
+ usba_device->suspend_cookie = NULL;
+ usba_device->resume_cookie = NULL;
+ } else {
+ for (i = 0; i < if_num; i++) {
+ usba_device->usb_client_flags[usba_get_ifno(dip) + i]
+ &= ~USBA_CLIENT_FLAG_EV_CBS;
+ }
+ }
+}