diff options
author | gc161489 <none@none> | 2006-12-27 22:00:25 -0800 |
---|---|---|
committer | gc161489 <none@none> | 2006-12-27 22:00:25 -0800 |
commit | d73ae94e59c019f5cc3221ee0a0012d02091b40e (patch) | |
tree | 193b1ba4909675d6fe1bb07678ab5a9970a230c3 | |
parent | b051ecf6ce2f5fb47d73df411b0b95fa2b53ab9f (diff) | |
download | illumos-joyent-d73ae94e59c019f5cc3221ee0a0012d02091b40e.tar.gz |
PSARC 2006/649 USB Interface Association support
6463853 USB IAD should be supported in solaris (updated usb bindings, FWARC 2006/671)
25 files changed, 2691 insertions, 386 deletions
diff --git a/usr/src/pkgdefs/SUNWusb/postinstall b/usr/src/pkgdefs/SUNWusb/postinstall index bb0211c804..4f865c3169 100644 --- a/usr/src/pkgdefs/SUNWusb/postinstall +++ b/usr/src/pkgdefs/SUNWusb/postinstall @@ -39,9 +39,9 @@ LINKDIR=${BASEDIR}/dev/usb LINKFILES="hub* mass-storage* device*" # since ohci, uhci and ehci are self probing nexus drivers, -# add_drv -n hid, hubd and usb_mid before add_drv ohci/uhci/ehci. +# add_drv -n hid, hubd, usb_ia and usb_mid before add_drv ohci/uhci/ehci. # ohci/uhci/ehci will create the hub and usb,device nodes and attach -# hubd and usb_mid drivers +# hubd, usb_mid and usb_ia drivers not_installed() { driver=$1 @@ -61,6 +61,9 @@ not_installed hubd || add_drv ${BASEDIR_OPT} -m '* 0644 root sys' \ not_installed scsa2usb || add_drv ${BASEDIR_OPT} \ -i '"usbif,class8" "usb584,222"' -n scsa2usb || EXIT=1 +not_installed usb_ia || add_drv ${BASEDIR_OPT} -i '"usb,ia"' -n usb_ia || +EXIT=1 + not_installed usb_mid || add_drv ${BASEDIR_OPT} -i '"usb,device"' -n usb_mid || EXIT=1 diff --git a/usr/src/pkgdefs/SUNWusb/preremove b/usr/src/pkgdefs/SUNWusb/preremove index abd0183b04..1a9d27dc07 100644 --- a/usr/src/pkgdefs/SUNWusb/preremove +++ b/usr/src/pkgdefs/SUNWusb/preremove @@ -3,9 +3,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. @@ -21,7 +20,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" @@ -49,6 +48,11 @@ then rem_drv -b ${BASEDIR} hubd || EXIT=1 fi +if installed usb_ia +then + rem_drv -b ${BASEDIR} usb_ia || EXIT=1 +fi + if installed usb_mid then rem_drv -b ${BASEDIR} usb_mid || EXIT=1 diff --git a/usr/src/pkgdefs/SUNWusb/prototype_i386 b/usr/src/pkgdefs/SUNWusb/prototype_i386 index 03013c2703..da2bdab5e6 100644 --- a/usr/src/pkgdefs/SUNWusb/prototype_i386 +++ b/usr/src/pkgdefs/SUNWusb/prototype_i386 @@ -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. # @@ -57,6 +56,7 @@ f none kernel/drv/ehci 0755 root sys f none kernel/drv/hubd 0755 root sys f none kernel/drv/hid 0755 root sys f none kernel/drv/usb_mid 0755 root sys +f none kernel/drv/usb_ia 0755 root sys f none kernel/drv/scsa2usb 0755 root sys f none kernel/drv/usbprn 0755 root sys f none kernel/drv/usb_ac 0755 root sys @@ -76,6 +76,7 @@ f none kernel/drv/amd64/ehci 0755 root sys f none kernel/drv/amd64/hubd 0755 root sys f none kernel/drv/amd64/hid 0755 root sys f none kernel/drv/amd64/usb_mid 0755 root sys +f none kernel/drv/amd64/usb_ia 0755 root sys f none kernel/drv/amd64/scsa2usb 0755 root sys f none kernel/drv/amd64/usbprn 0755 root sys f none kernel/drv/amd64/usb_ac 0755 root sys diff --git a/usr/src/pkgdefs/SUNWusb/prototype_sparc b/usr/src/pkgdefs/SUNWusb/prototype_sparc index fbadb1329b..4645033258 100644 --- a/usr/src/pkgdefs/SUNWusb/prototype_sparc +++ b/usr/src/pkgdefs/SUNWusb/prototype_sparc @@ -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. # @@ -59,6 +58,7 @@ f none kernel/drv/sparcv9/uhci 0755 root sys f none kernel/drv/sparcv9/hubd 0755 root sys f none kernel/drv/sparcv9/hid 0755 root sys f none kernel/drv/sparcv9/usb_mid 0755 root sys +f none kernel/drv/sparcv9/usb_ia 0755 root sys f none kernel/drv/sparcv9/scsa2usb 0755 root sys f none kernel/drv/sparcv9/usbprn 0755 root sys f none kernel/drv/sparcv9/usb_ac 0755 root sys diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files index 0f757837ff..413ad69d02 100644 --- a/usr/src/uts/common/Makefile.files +++ b/usr/src/uts/common/Makefile.files @@ -1260,6 +1260,8 @@ HUBD_OBJS += hubd.o USB_MID_OBJS += usb_mid.o +USB_IA_OBJS += usb_ia.o + SCSA2USB_OBJS += scsa2usb.o usb_ms_bulkonly.o usb_ms_cbi.o PHX_OBJS += phx.o diff --git a/usr/src/uts/common/Makefile.rules b/usr/src/uts/common/Makefile.rules index 120d63aea4..b256e5bb82 100644 --- a/usr/src/uts/common/Makefile.rules +++ b/usr/src/uts/common/Makefile.rules @@ -739,6 +739,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/usb/usb_mid/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) +$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/usb/usb_ia/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/usb/usba/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) @@ -1431,6 +1435,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/usb/scsa2usb/%.c $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/usb/usb_mid/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) +$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/usb/usb_ia/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/usb/usba/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) 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; + } + } +} diff --git a/usr/src/uts/common/io/warlock/usb_ia.wlcmd b/usr/src/uts/common/io/warlock/usb_ia.wlcmd new file mode 100644 index 0000000000..c0c370f9bb --- /dev/null +++ b/usr/src/uts/common/io/warlock/usb_ia.wlcmd @@ -0,0 +1,32 @@ +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# 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 +# +#ident "%Z%%M% %I% %E% SMI" + +one usb_ia +one usba_device + +### currently unused functions +root usb_ia_event_cb +root usb_ia_power +root usb_ia_bus_ctl diff --git a/usr/src/uts/common/io/warlock/usb_ia_with_usba.wlcmd b/usr/src/uts/common/io/warlock/usb_ia_with_usba.wlcmd new file mode 100644 index 0000000000..6fbe635964 --- /dev/null +++ b/usr/src/uts/common/io/warlock/usb_ia_with_usba.wlcmd @@ -0,0 +1,172 @@ +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# 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 +# +#ident "%Z%%M% %I% %E% SMI" + + +one ohci_state +one ehci_state +one uhci_state +one usb_ia +one usba_device + +### specify the root functions + +root usba_ascii_string_descr +root usb_console_output_init +root usb_console_output_fini +root usb_console_output_enter +root usb_console_output_exit +root usb_console_write +root usb_console_input_enter +root usb_console_input_exit +root usb_console_input_fini +root usb_console_input_init +root usb_console_read +root hubd_hotplug_thread +root hubd_cpr_post_user_callb +root usb_get_dev_descr +root usb_get_if_number +root usb_parse_CV_cfg_descr +root usb_parse_CV_ep_descr +root usb_parse_CV_if_descr +root usb_pipe_reset +root usb_pipe_get_private +root usb_get_current_frame_number +root usb_get_max_isoc_pkts +root usb_pipe_set_private +root usba_ready_interface_node +root usba_free_hcdi_ops +root ohci_intr +root ehci_intr + +root usba_dbuf_tail +root usb_log +root usb_ia_event_cb +root hubd_bus_power +root usba_hubdi_power +root usba_hubdi_root_hub_power +root usba_hubdi_bus_ctl +root usb_set_device_pwrlvl0 +root usb_set_device_pwrlvl1 +root usb_set_device_pwrlvl2 +root usb_set_device_pwrlvl3 +root usba_async_req_raise_power +root usba_async_req_lower_power +root usb_req_raise_power +root usb_req_lower_power +root usb_is_pm_enabled +root usb_pipe_bulk_transfer_size +root usba_get_hotplug_stats +root usba_reset_hotplug_stats +root usb_async_req +root usb_get_ep_data +root usba_pipe_get_policy +root usb_pipe_ctrl_xfer_wait +root usb_get_current_cfgidx + +root usb_alloc_bulk_req +root usb_clear_feature +root usb_free_bulk_req +root usb_get_alt_if +root usb_get_ep_descr +root usb_get_if_descr +root usb_pipe_bulk_xfer +root usb_pipe_isoc_xfer +root usb_pipe_stop_isoc_polling +root usb_set_alt_if +root usb_set_cfg +root usb_get_cfg +root usb_get_status +root usb_ep_num +root usb_register_event_cbs +root usb_unregister_event_cbs +root usb_log_descr_tree +root usb_print_descr_tree +root usb_pipe_drain_reqs +root usb_try_serialize_access +root usb_fini_serialization +root usb_init_serialization +root usb_release_access +root usb_serialize_access +root usb_rval2errno +root usb_clr_feature +root usb_get_ep_data +root usb_register_hotplug_cbs +root usb_register_client +root usb_ugen_power +root usb_ugen_attach +root usb_ugen_close +root usb_ugen_detach +root usb_ugen_disconnect_ev_cb +root usb_ugen_get_hdl +root usb_ugen_open +root usb_ugen_poll +root usb_ugen_read +root usb_ugen_reconnect_ev_cb +root usb_ugen_write +root usba_ready_interface_association_node + + +root hubd_root_hub_cleanup_thread +root hubd_restore_state_cb +root hubd_disconnect_event_cb +root hubd_post_resume_event_cb +root hubd_pre_suspend_event_cb +root hubd_reconnect_event_cb + + +root hcdi_autoclearing +root hcdi_cb_thread +root hcdi_shared_cb_thread + + +root usba_pipe_do_async_func_thread +root usba_get_hc_dma_attr +root usba_hcdi_get_req_private +root usba_hcdi_set_req_private +root usba_move_list +root usba_taskq_destroy +root usba_mk_mctl +root usb_fail_checkpoint + +add usb_bulk_req::bulk_cb targets warlock_dummy +add usb_bulk_req::bulk_exc_cb targets warlock_dummy +add usb_ctrl_req::ctrl_cb targets warlock_dummy +add usb_ctrl_req::ctrl_exc_cb targets warlock_dummy +add usb_isoc_req::isoc_cb targets warlock_dummy +add usb_isoc_req::isoc_exc_cb targets warlock_dummy +add usba_pipe_async_req::sync_func targets warlock_dummy + +add usba_pm_req::cb targets warlock_dummy + +add usba_pipe_async_req::callback targets warlock_dummy + +add ohci_trans_wrapper::tw_handle_td targets ohci_handle_ctrl_td +add ohci_trans_wrapper::tw_handle_td targets ohci_handle_bulk_td +add ohci_trans_wrapper::tw_handle_td targets ohci_handle_intr_td +add ohci_trans_wrapper::tw_handle_td targets ohci_handle_isoc_td + +add ehci_trans_wrapper::tw_handle_qtd targets ehci_handle_ctrl_qtd +add ehci_trans_wrapper::tw_handle_qtd targets ehci_handle_bulk_qtd +add ehci_trans_wrapper::tw_handle_qtd targets ehci_handle_intr_qtd diff --git a/usr/src/uts/common/sys/usb/usb_ia/usb_iavar.h b/usr/src/uts/common/sys/usb/usb_ia/usb_iavar.h new file mode 100644 index 0000000000..bb7194dcf7 --- /dev/null +++ b/usr/src/uts/common/sys/usb/usb_ia/usb_iavar.h @@ -0,0 +1,114 @@ +/* + * 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. + */ + +#ifndef _SYS_USB_USB_IA_H +#define _SYS_USB_USB_IA_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/usb/usba/usbai_private.h> + +/* + * soft state information for this usb_ia + */ +typedef struct usb_ia { + int ia_instance; + + uint_t ia_init_state; + + kmutex_t ia_mutex; + + /* + * dev_info_t reference + */ + dev_info_t *ia_dip; + + /* pointer to usb_ia_power_t */ + usb_common_power_t *ia_pm; + + int ia_dev_state; + + int ia_first_if; + int ia_n_ifs; + + /* track event registration of children */ + uint8_t *ia_child_events; + /* + * ia_children_dips is a array for holding + * each child dip indexed by interface number + */ + dev_info_t **ia_children_dips; + + size_t ia_cd_list_length; + + /* logging of messages */ + usb_log_handle_t ia_log_handle; + + /* usb registration */ + usb_client_dev_data_t *ia_dev_data; + + /* event support */ + ndi_event_hdl_t ia_ndi_event_hdl; + +} usb_ia_t; + +_NOTE(MUTEX_PROTECTS_DATA(usb_ia::ia_mutex, usb_ia)) +_NOTE(MUTEX_PROTECTS_DATA(usb_ia::ia_mutex, usb_common_power_t)) +_NOTE(DATA_READABLE_WITHOUT_LOCK(usb_ia::ia_instance + usb_ia::ia_ndi_event_hdl + usb_ia::ia_dev_data + usb_ia::ia_log_handle + usb_ia::ia_dip + usb_ia::ia_pm)) + +/* init state */ +#define USB_IA_LOCK_INIT 0x0001 +#define USB_IA_MINOR_NODE_CREATED 0x0002 +#define USB_IA_EVENTS_REGISTERED 0x0004 + +/* Tracking events registered by children */ +#define USB_IA_CHILD_EVENT_DISCONNECT 0x01 +#define USB_IA_CHILD_EVENT_PRESUSPEND 0x02 + +/* + * Debug printing + * Masks + */ +#define DPRINT_MASK_ATTA 0x00000001 +#define DPRINT_MASK_CBOPS 0x00000002 +#define DPRINT_MASK_EVENTS 0x00000004 +#define DPRINT_MASK_PM 0x00000010 +#define DPRINT_MASK_ALL 0xFFFFFFFF + + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_USB_USB_IA_H */ diff --git a/usr/src/uts/common/sys/usb/usb_mid/usb_midvar.h b/usr/src/uts/common/sys/usb/usb_mid/usb_midvar.h index 3668fbcc5d..65409ee966 100644 --- a/usr/src/uts/common/sys/usb/usb_mid/usb_midvar.h +++ b/usr/src/uts/common/sys/usb/usb_mid/usb_midvar.h @@ -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. @@ -18,8 +17,9 @@ * information: Portions Copyright [yyyy] [name of copyright owner] * * 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. */ @@ -34,26 +34,6 @@ extern "C" { #include <sys/usb/usba/usbai_private.h> -typedef struct usb_mid_power_struct { - void *mip_usb_mid; /* points back to usb_mid_t */ - - uint8_t mip_wakeup_enabled; - - /* this is the bit mask of the power states that device has */ - uint8_t mip_pwr_states; - - /* wakeup and power transistion capabilites of an interface */ - uint8_t mip_pm_capabilities; - - uint8_t mip_current_power; /* current power level */ -} usb_mid_power_t; - -/* warlock directives, stable data */ -_NOTE(DATA_READABLE_WITHOUT_LOCK(usb_mid_power_t::mip_usb_mid)) -_NOTE(DATA_READABLE_WITHOUT_LOCK(usb_mid_power_t::mip_wakeup_enabled)) -_NOTE(DATA_READABLE_WITHOUT_LOCK(usb_mid_power_t::mip_pwr_states)) -_NOTE(DATA_READABLE_WITHOUT_LOCK(usb_mid_power_t::mip_pm_capabilities)) - /* * soft state information for this usb_mid @@ -71,8 +51,8 @@ typedef struct usb_mid { */ dev_info_t *mi_dip; - /* pointer to usb_mid_power_t */ - usb_mid_power_t *mi_pm; + /* pointer to usb_common_power_t */ + usb_common_power_t *mi_pm; /* * save the usba_device pointer @@ -87,11 +67,16 @@ typedef struct usb_mid { /* track event registration of children */ uint8_t *mi_child_events; + + /* record the interface num of each child node */ + uint_t *mi_children_ifs; + /* - * mi_children_dips is a array for holding + * mi_children_dips is an array for holding * each child dip indexed by interface number */ dev_info_t **mi_children_dips; + boolean_t mi_removed_children; size_t mi_cd_list_length; @@ -112,7 +97,7 @@ typedef struct usb_mid { } usb_mid_t; _NOTE(MUTEX_PROTECTS_DATA(usb_mid::mi_mutex, usb_mid)) -_NOTE(MUTEX_PROTECTS_DATA(usb_mid::mi_mutex, usb_mid_power_t)) +_NOTE(MUTEX_PROTECTS_DATA(usb_mid::mi_mutex, usb_common_power_t)) _NOTE(DATA_READABLE_WITHOUT_LOCK(usb_mid::mi_instance usb_mid::mi_ndi_event_hdl usb_mid::mi_dev_data diff --git a/usr/src/uts/common/sys/usb/usba/usba_impl.h b/usr/src/uts/common/sys/usb/usba/usba_impl.h index 5a5e50516c..b0d29f6316 100644 --- a/usr/src/uts/common/sys/usb/usba/usba_impl.h +++ b/usr/src/uts/common/sys/usb/usba/usba_impl.h @@ -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 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -47,6 +46,7 @@ extern "C" { */ #define USBA_UGEN_DEVICE_BINDING 1 #define USBA_UGEN_INTERFACE_BINDING 2 +#define USBA_UGEN_INTERFACE_ASSOCIATION_BINDING 3 /* * Allocating a USB address @@ -345,6 +345,7 @@ _NOTE(SCHEME_PROTECTS_DATA("USBA managed data", usba_log_handle_impl)) #define FLAG_INTERFACE_NODE 0 #define FLAG_DEVICE_NODE 1 #define FLAG_COMBINED_NODE 2 +#define FLAG_INTERFACE_ASSOCIATION_NODE 3 typedef struct node_name_entry { int16_t class; diff --git a/usr/src/uts/common/sys/usb/usba/usba_private.h b/usr/src/uts/common/sys/usb/usba/usba_private.h index b681e273d1..baeb84fb66 100644 --- a/usr/src/uts/common/sys/usb/usba/usba_private.h +++ b/usr/src/uts/common/sys/usb/usba/usba_private.h @@ -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 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -71,9 +70,10 @@ extern "C" { * ret_descr buffer the data is to be returned in * ret_buf_len size of the buffer at ret_descr * - * if_index the index in the array of concurrent interfaces + * first_if the first interace associated with current iad + * if_index the index in the array of concurrent interfaces * supported by this configuration - * alt_if_setting alternate setting for the interface identified + * alt_if_setting alternate setting for the interface identified * by if_index * ep_index the index in the array of endpoints supported by * this configuration @@ -100,6 +100,14 @@ size_t usb_parse_cfg_descr( size_t ret_buf_len); +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); + + size_t usb_parse_if_descr( uchar_t *buf, /* from GET_DESCRIPTOR(CONFIGURATION) */ size_t buflen, @@ -281,10 +289,15 @@ ddi_dma_attr_t *usba_get_hc_dma_attr(dev_info_t *dip); */ int usba_bind_driver(dev_info_t *); +/* check whether the dip owns an interface-associaiton */ +boolean_t usba_owns_ia(dev_info_t *dip); + /* * Driver binding functions */ dev_info_t *usba_ready_device_node(dev_info_t *); +dev_info_t *usba_ready_interface_association_node(dev_info_t *, + uint_t, uint_t *); dev_info_t *usba_ready_interface_node(dev_info_t *, uint_t); /* Some Nexus driver functions. */ @@ -345,6 +358,7 @@ extern usb_ep_descr_t usba_default_ep_descr; #define USB_IF_DESCR_SIZE 9 /* interface descr size */ #define USBA_IF_PWR_DESCR_SIZE 15 /* interface pwr descr size */ #define USB_EP_DESCR_SIZE 7 /* endpoint descr size */ +#define USB_IA_DESCR_SIZE 8 /* interface association descr size */ /* * For compatibility with old code. @@ -410,6 +424,45 @@ size_t usba_parse_if_pwr_descr(uchar_t *, size_t buflen, uint_t, */ size_t usba_ascii_string_descr(uchar_t *, size_t, char *, size_t); + +/* + * usb common power management, for usb_mid, usb_ia and maybe other simple + * drivers. + */ +typedef struct usb_common_power_struct { + void *uc_usb_statep; /* points back to state structure */ + + uint8_t uc_wakeup_enabled; + + /* this is the bit mask of the power states that device has */ + uint8_t uc_pwr_states; + + /* wakeup and power transition capabilites of an interface */ + uint8_t uc_pm_capabilities; + + uint8_t uc_current_power; /* current power level */ +} usb_common_power_t; + +/* warlock directives, stable data */ +_NOTE(DATA_READABLE_WITHOUT_LOCK(usb_common_power_t::uc_usb_statep)) +_NOTE(DATA_READABLE_WITHOUT_LOCK(usb_common_power_t::uc_wakeup_enabled)) +_NOTE(DATA_READABLE_WITHOUT_LOCK(usb_common_power_t::uc_pwr_states)) +_NOTE(DATA_READABLE_WITHOUT_LOCK(usb_common_power_t::uc_pm_capabilities)) + +/* power management */ +int usba_common_power(dev_info_t *, usb_common_power_t *, int *, int); + +/* + * usb common events handler for usb_mid, usb_ia and maybe other nexus + * drivers. + */ + +void usba_common_register_events(dev_info_t *, uint_t, + void (*)(dev_info_t *, ddi_eventcookie_t, void *, void *)); + +void usba_common_unregister_events(dev_info_t *, uint_t); + + #ifdef __cplusplus } #endif diff --git a/usr/src/uts/common/sys/usb/usbai.h b/usr/src/uts/common/sys/usb/usbai.h index 43958caef9..1bea82b9d1 100644 --- a/usr/src/uts/common/sys/usb/usbai.h +++ b/usr/src/uts/common/sys/usb/usbai.h @@ -317,6 +317,22 @@ typedef struct usb_other_speed_cfg_descr { /* + * usb_ia_descr: + * usb interface association descriptor, refer to USB 2.0 ECN(IAD) + */ +typedef struct usb_ia_descr { + uint8_t bLength; /* descriptor size */ + uint8_t bDescriptorType; /* INTERFACE_ASSOCIATION */ + uint8_t bFirstInterface; /* 1st interface number */ + uint8_t bInterfaceCount; /* number of interfaces */ + uint8_t bFunctionClass; /* class code */ + uint8_t bFunctionSubClass; /* sub class code */ + uint8_t bFunctionProtocol; /* protocol code */ + uint8_t iFunction; /* description string */ +} usb_ia_descr_t; + + +/* * usb_if_descr: * usb interface descriptor, refer to USB 2.0/9.6.5 */ @@ -1326,6 +1342,7 @@ typedef struct usb_ctrl_req { #define USB_DESCR_TYPE_DEV_QLF 0x06 #define USB_DESCR_TYPE_OTHER_SPEED_CFG 0x07 #define USB_DESCR_TYPE_IF_PWR 0x08 +#define USB_DESCR_TYPE_IA 0x0B /* * device request type @@ -2335,14 +2352,16 @@ void usb_unregister_hotplug_cbs(dev_info_t *dip); #define USB_CLASS_CDC_CTRL 2 /* CDC-control iface class, also 2 */ #define USB_CLASS_HID 3 #define USB_CLASS_PHYSICAL 5 +#define USB_CLASS_IMAGE 6 #define USB_CLASS_PRINTER 7 #define USB_CLASS_MASS_STORAGE 8 #define USB_CLASS_HUB 9 /* Device class */ #define USB_CLASS_CDC_DATA 10 #define USB_CLASS_CCID 11 #define USB_CLASS_SECURITY 13 +#define USB_CLASS_VIDEO 14 #define USB_CLASS_DIAG 220 /* Device class */ -#define USB_CLASS_WIRELESS 224 /* Device class */ +#define USB_CLASS_WIRELESS 224 #define USB_CLASS_MISC 239 /* Device class */ #define USB_CLASS_APP 254 #define USB_CLASS_VENDOR_SPEC 255 /* Device class */ @@ -2393,7 +2412,17 @@ void usb_unregister_hotplug_cbs(dev_info_t *dip); #define USB_SUBCLS_APP_IRDA 0x02 /* app spec IrDa subclass */ #define USB_SUBCLS_APP_TEST 0x03 /* app spec test subclass */ - +/* Video subclasses */ +#define USB_SUBCLS_VIDEO_CONTROL 0x01 /* video control */ +#define USB_SUBCLS_VIDEO_STREAM 0x02 /* video stream */ +#define USB_SUBCLS_VIDEO_COLLECTION 0x03 /* video interface collection */ + +/* Wireless controller subclasses and protocols */ +#define USB_SUBCLS_WUSB_1 0x01 +#define USB_SUBCLS_WUSB_2 0x02 +#define USB_PROTO_WUSB_HWA 0x01 /* host wire adapter */ +#define USB_PROTO_WUSB_DWA 0x02 /* device wire adapter */ +#define USB_PROTO_WUSB_DWA_ISO 0x03 /* device wire adapter isoc */ #ifdef __cplusplus } diff --git a/usr/src/uts/intel/Makefile.intel.shared b/usr/src/uts/intel/Makefile.intel.shared index 749ccf7d77..f02a017c55 100644 --- a/usr/src/uts/intel/Makefile.intel.shared +++ b/usr/src/uts/intel/Makefile.intel.shared @@ -374,6 +374,7 @@ DRV_KMODS += uhci DRV_KMODS += ehci DRV_KMODS += ohci DRV_KMODS += usb_mid +DRV_KMODS += usb_ia DRV_KMODS += scsa2usb DRV_KMODS += usbprn DRV_KMODS += ugen diff --git a/usr/src/uts/intel/usb_ia/Makefile b/usr/src/uts/intel/usb_ia/Makefile new file mode 100644 index 0000000000..cb1dc5321e --- /dev/null +++ b/usr/src/uts/intel/usb_ia/Makefile @@ -0,0 +1,149 @@ +# +# 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 +# +# +# uts/intel/usb_ia/Makefile +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# This makefile drives the production of the usb_ia driver kernel module. +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = usb_ia +OBJECTS = $(USB_IA_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(USB_IA_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) +WARLOCK_OUT = $(USB_IA_OBJS:%.o=%.ll) +WARLOCK_OK = $(MODULE).ok +WLCMD_DIR = $(UTSBASE)/common/io/warlock + +# +# Include common rules. +# +include $(UTSBASE)/intel/Makefile.intel + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +# +# Override defaults to build a unique, local modstubs.o. +# +MODSTUBS_DIR = $(OBJS_DIR) +CLEANFILES += $(MODSTUBS_O) + +# +# depends on misc/usba +# +LDFLAGS += -dy -Nmisc/usba + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + $(RM) $(WARLOCK_OUT) $(WARLOCK_OK) + +clobber: $(CLOBBER_DEPS) + $(RM) $(WARLOCK_OUT) $(WARLOCK_OK) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include $(UTSBASE)/intel/Makefile.targ + +# +# Defines for local commands. +# +WARLOCK = warlock +WLCC = wlcc +TOUCH = touch +SCCS = sccs +TEST = test + +# +# lock_lint rules +# +USBA_FILES = $(USBA_OBJS:%.o=../usba/%.ll) +UHCI_FILES = $(UHCI_OBJS:%.o=../uhci/%.ll) +OHCI_FILES = $(OHCI_OBJS:%.o=../ohci/%.ll) +EHCI_FILES = $(EHCI_OBJS:%.o=../ehci/%.ll) + +warlock: $(WARLOCK_OK) warlock_with_usba + +%.wlcmd: + cd $(WLCMD_DIR); $(TEST) -f $@ || $(SCCS) get $@ + +$(WARLOCK_OK): $(WARLOCK_OUT) usb_ia.wlcmd warlock_ddi.files + $(WARLOCK) -c $(WLCMD_DIR)/usb_ia.wlcmd $(WARLOCK_OUT) \ + -l ../warlock/ddi_dki_impl.ll + $(TOUCH) $@ + +%.ll: $(UTSBASE)/common/io/usb/usb_ia/%.c \ + $(UTSBASE)/common/sys/usb/usb_ia/usb_iavar.h + $(WLCC) $(CPPFLAGS) -DDEBUG -o $@ $< + +warlock_with_usba: usb_ia_with_usba.wlcmd $(WARLOCK_OUT) usba_files \ + ohci_files ehci_files uhci_files warlock_ddi.files + $(WARLOCK) -c $(WLCMD_DIR)/usb_ia_with_usba.wlcmd \ + $(USBA_FILES) $(OHCI_FILES) $(EHCI_FILES) $(UHCI_FILES) \ + $(WARLOCK_OUT) \ + -l ../warlock/ddi_dki_impl.ll + +usba_files: + @cd ../usba;pwd; $(MAKE) warlock + +uhci_files: + @cd ../uhci;pwd; $(MAKE) warlock + +ohci_files: + @cd ../ohci;pwd; $(MAKE) warlock + +ehci_files: + @cd ../ehci;pwd; $(MAKE) warlock + +warlock_ddi.files: + cd ../warlock; pwd; $(MAKE) warlock diff --git a/usr/src/uts/sparc/Makefile.sparc.shared b/usr/src/uts/sparc/Makefile.sparc.shared index 5d394d8f6f..97c3f7789d 100644 --- a/usr/src/uts/sparc/Makefile.sparc.shared +++ b/usr/src/uts/sparc/Makefile.sparc.shared @@ -267,7 +267,7 @@ DRV_KMODS += bge bpp eri esp fas hme DRV_KMODS += openeepr options sd ses st DRV_KMODS += ssd DRV_KMODS += ecpp -DRV_KMODS += hid hubd ehci ohci uhci usb_mid scsa2usb usbprn ugen +DRV_KMODS += hid hubd ehci ohci uhci usb_mid usb_ia scsa2usb usbprn ugen DRV_KMODS += usbser usbsacm usbsksp usbsprl DRV_KMODS += usb_as usb_ac DRV_KMODS += usbskel diff --git a/usr/src/uts/sparc/usb_ia/Makefile b/usr/src/uts/sparc/usb_ia/Makefile new file mode 100644 index 0000000000..0eebba45de --- /dev/null +++ b/usr/src/uts/sparc/usb_ia/Makefile @@ -0,0 +1,148 @@ +# +# 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 +# +# +# uts/sparc/usb_ia/Makefile + +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# This makefile drives the production of the usb_ia driver kernel module. +# sparc architecture dependent +# + +#ident "%Z%%M% %I% %E% SMI" + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = usb_ia +OBJECTS = $(USB_IA_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(USB_IA_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) +WARLOCK_OUT = $(USB_IA_OBJS:%.o=%.ll) +WARLOCK_OK = $(MODULE).ok +WLCMD_DIR = $(UTSBASE)/common/io/warlock + +# +# Include common rules. +# +include $(UTSBASE)/sparc/Makefile.sparc + +# +# lint pass one enforcement +# +CFLAGS += $(CCVERBOSE) + +# +# depends on misc/usba +# +LDFLAGS += -dy -Nmisc/usba + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +.KEEP_STATE: + +all: $(ALL_DEPS) + +def: $(DEF_DEPS) + +clean: $(CLEAN_DEPS) + $(RM) $(WARLOCK_OUT) $(WARLOCK_OK) + +clobber: $(CLOBBER_DEPS) + $(RM) $(WARLOCK_OUT) $(WARLOCK_OK) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include $(UTSBASE)/sparc/Makefile.targ + +# +# Defines for local commands. +# +WARLOCK = warlock +WLCC = wlcc +TOUCH = touch +SCCS = sccs +TEST = test + +# +# lock_lint rules +# +USBA_FILES = $(USBA_OBJS:%.o=../usba/%.ll) +UHCI_FILES = $(UHCI_OBJS:%.o=../uhci/%.ll) +OHCI_FILES = $(OHCI_OBJS:%.o=../ohci/%.ll) +EHCI_FILES = $(EHCI_OBJS:%.o=../ehci/%.ll) + +warlock: $(WARLOCK_OK) warlock_with_usba + +%.wlcmd: + cd $(WLCMD_DIR); $(TEST) -f $@ || $(SCCS) get $@ + +$(WARLOCK_OK): $(WARLOCK_OUT) usb_ia.wlcmd warlock_ddi.files + $(WARLOCK) -c $(WLCMD_DIR)/usb_ia.wlcmd $(WARLOCK_OUT) \ + -l ../warlock/ddi_dki_impl.ll + $(TOUCH) $@ + +%.ll: $(UTSBASE)/common/io/usb/usb_ia/%.c \ + $(UTSBASE)/common/sys/usb/usb_ia/usb_iavar.h + $(WLCC) $(CPPFLAGS) -DDEBUG -o $@ $< + +warlock_with_usba: usb_ia_with_usba.wlcmd $(WARLOCK_OUT) usba_files \ + ohci_files ehci_files uhci_files warlock_ddi.files + $(WARLOCK) -c $(WLCMD_DIR)/usb_ia_with_usba.wlcmd \ + $(USBA_FILES) $(OHCI_FILES) $(EHCI_FILES) $(UHCI_FILES) \ + $(WARLOCK_OUT) \ + -l ../warlock/ddi_dki_impl.ll + + +usba_files: + @cd ../usba;pwd; $(MAKE) warlock + +uhci_files: + @cd ../uhci;pwd; $(MAKE) warlock + +ohci_files: + @cd ../ohci;pwd; $(MAKE) warlock + +ehci_files: + @cd ../ehci;pwd; $(MAKE) warlock + +warlock_ddi.files: + cd ../warlock; pwd; $(MAKE) warlock diff --git a/usr/src/uts/sparc/warlock/Makefile b/usr/src/uts/sparc/warlock/Makefile index a0de441e35..ccb3e4f575 100644 --- a/usr/src/uts/sparc/warlock/Makefile +++ b/usr/src/uts/sparc/warlock/Makefile @@ -77,6 +77,7 @@ warlock.usb: @cd ../usb_ah; make clean; $(MAKE) warlock @cd ../ugen; make clean; $(MAKE) warlock @cd ../usb_mid; make clean; $(MAKE) warlock + @cd ../usb_ia; make clean; $(MAKE) warlock @cd ../usbprn; make clean; $(MAKE) warlock @cd ../usbser; make clean; $(MAKE) warlock @cd $(CLOSED)/uts/sparc/usbser_edge; make clean; $(MAKE) warlock |