diff options
Diffstat (limited to 'usr/src/uts/common/io/usb')
-rw-r--r-- | usr/src/uts/common/io/usb/inc.flg | 2 | ||||
-rw-r--r-- | usr/src/uts/common/io/usb/usb_ia/usb_ia.c | 1230 | ||||
-rw-r--r-- | usr/src/uts/common/io/usb/usb_mid/usb_mid.c | 400 | ||||
-rw-r--r-- | usr/src/uts/common/io/usb/usba/hcdi.c | 10 | ||||
-rw-r--r-- | usr/src/uts/common/io/usb/usba/parser.c | 40 | ||||
-rw-r--r-- | usr/src/uts/common/io/usb/usba/usba.c | 314 | ||||
-rw-r--r-- | usr/src/uts/common/io/usb/usba/usbai_util.c | 267 |
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; + } + } +} |