13341Sgc161489 /*
23341Sgc161489 * CDDL HEADER START
33341Sgc161489 *
43341Sgc161489 * The contents of this file are subject to the terms of the
53341Sgc161489 * Common Development and Distribution License (the "License").
63341Sgc161489 * You may not use this file except in compliance with the License.
73341Sgc161489 *
83341Sgc161489 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93341Sgc161489 * or http://www.opensolaris.org/os/licensing.
103341Sgc161489 * See the License for the specific language governing permissions
113341Sgc161489 * and limitations under the License.
123341Sgc161489 *
133341Sgc161489 * When distributing Covered Code, include this CDDL HEADER in each
143341Sgc161489 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153341Sgc161489 * If applicable, add the following below this CDDL HEADER, with the
163341Sgc161489 * fields enclosed by brackets "[]" replaced with your own identifying
173341Sgc161489 * information: Portions Copyright [yyyy] [name of copyright owner]
183341Sgc161489 *
193341Sgc161489 * CDDL HEADER END
203341Sgc161489 */
213341Sgc161489 /*
22*8688SRaymond.Chen@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
233341Sgc161489 * Use is subject to license terms.
243341Sgc161489 */
253341Sgc161489
263341Sgc161489
273341Sgc161489 /*
283341Sgc161489 * usb interface association driver
293341Sgc161489 *
303341Sgc161489 * this driver attempts to the interface association node and
313341Sgc161489 * creates/manages child nodes for the included interfaces.
323341Sgc161489 */
333341Sgc161489
343341Sgc161489 #if defined(lint) && !defined(DEBUG)
353341Sgc161489 #define DEBUG 1
363341Sgc161489 #endif
373341Sgc161489 #include <sys/usb/usba/usbai_version.h>
383341Sgc161489 #include <sys/usb/usba.h>
393341Sgc161489 #include <sys/usb/usba/usba_types.h>
403341Sgc161489 #include <sys/usb/usba/usba_impl.h>
413341Sgc161489 #include <sys/usb/usb_ia/usb_iavar.h>
423341Sgc161489
433341Sgc161489 /* Debugging support */
443341Sgc161489 uint_t usb_ia_errlevel = USB_LOG_L4;
453341Sgc161489 uint_t usb_ia_errmask = (uint_t)DPRINT_MASK_ALL;
463341Sgc161489 uint_t usb_ia_instance_debug = (uint_t)-1;
473341Sgc161489 uint_t usb_ia_bus_config_debug = 0;
483341Sgc161489
493341Sgc161489 _NOTE(DATA_READABLE_WITHOUT_LOCK(usb_ia_errlevel))
503341Sgc161489 _NOTE(DATA_READABLE_WITHOUT_LOCK(usb_ia_errmask))
513341Sgc161489 _NOTE(DATA_READABLE_WITHOUT_LOCK(usb_ia_instance_debug))
523341Sgc161489
533341Sgc161489 _NOTE(SCHEME_PROTECTS_DATA("unique", msgb))
543341Sgc161489 _NOTE(SCHEME_PROTECTS_DATA("unique", dev_info))
553341Sgc161489 _NOTE(SCHEME_PROTECTS_DATA("unique", usb_pipe_policy))
563341Sgc161489
573341Sgc161489 static struct cb_ops usb_ia_cb_ops = {
583341Sgc161489 nodev, /* open */
593341Sgc161489 nodev, /* close */
603341Sgc161489 nodev, /* strategy */
613341Sgc161489 nodev, /* print */
623341Sgc161489 nodev, /* dump */
633341Sgc161489 nodev, /* read */
643341Sgc161489 nodev, /* write */
653341Sgc161489 nodev, /* ioctl */
663341Sgc161489 nodev, /* devmap */
673341Sgc161489 nodev, /* mmap */
683341Sgc161489 nodev, /* segmap */
693341Sgc161489 nochpoll, /* poll */
703341Sgc161489 ddi_prop_op, /* prop_op */
713341Sgc161489 NULL, /* aread */
723341Sgc161489 D_MP
733341Sgc161489 };
743341Sgc161489
753341Sgc161489 static int usb_ia_busop_get_eventcookie(dev_info_t *dip,
763341Sgc161489 dev_info_t *rdip,
773341Sgc161489 char *eventname,
783341Sgc161489 ddi_eventcookie_t *cookie);
793341Sgc161489 static int usb_ia_busop_add_eventcall(dev_info_t *dip,
803341Sgc161489 dev_info_t *rdip,
813341Sgc161489 ddi_eventcookie_t cookie,
823341Sgc161489 void (*callback)(dev_info_t *dip,
833341Sgc161489 ddi_eventcookie_t cookie, void *arg,
843341Sgc161489 void *bus_impldata),
853341Sgc161489 void *arg, ddi_callback_id_t *cb_id);
863341Sgc161489 static int usb_ia_busop_remove_eventcall(dev_info_t *dip,
873341Sgc161489 ddi_callback_id_t cb_id);
883341Sgc161489 static int usb_ia_busop_post_event(dev_info_t *dip,
893341Sgc161489 dev_info_t *rdip,
903341Sgc161489 ddi_eventcookie_t cookie,
913341Sgc161489 void *bus_impldata);
923341Sgc161489 static int usb_ia_bus_config(dev_info_t *dip,
933341Sgc161489 uint_t flag,
943341Sgc161489 ddi_bus_config_op_t op,
953341Sgc161489 void *arg,
963341Sgc161489 dev_info_t **child);
973341Sgc161489 static int usb_ia_bus_unconfig(dev_info_t *dip,
983341Sgc161489 uint_t flag,
993341Sgc161489 ddi_bus_config_op_t op,
1003341Sgc161489 void *arg);
1013341Sgc161489
1023341Sgc161489 /*
1033341Sgc161489 * autoconfiguration data and routines.
1043341Sgc161489 */
1053341Sgc161489 static int usb_ia_info(dev_info_t *, ddi_info_cmd_t,
1063341Sgc161489 void *, void **);
1073341Sgc161489 static int usb_ia_attach(dev_info_t *, ddi_attach_cmd_t);
1083341Sgc161489 static int usb_ia_detach(dev_info_t *, ddi_detach_cmd_t);
1093341Sgc161489
1103341Sgc161489 /* other routines */
1113341Sgc161489 static void usb_ia_create_pm_components(dev_info_t *, usb_ia_t *);
1123341Sgc161489 static int usb_ia_bus_ctl(dev_info_t *, dev_info_t *,
1133341Sgc161489 ddi_ctl_enum_t, void *, void *);
1143341Sgc161489 static int usb_ia_power(dev_info_t *, int, int);
1153341Sgc161489 static int usb_ia_restore_device_state(dev_info_t *, usb_ia_t *);
1163341Sgc161489 static usb_ia_t *usb_ia_obtain_state(dev_info_t *);
1173341Sgc161489 static void usb_ia_event_cb(dev_info_t *, ddi_eventcookie_t, void *, void *);
1183341Sgc161489
1193341Sgc161489 /* prototypes */
1203341Sgc161489 static void usb_ia_create_children(usb_ia_t *);
1213341Sgc161489 static int usb_ia_cleanup(usb_ia_t *);
1223341Sgc161489
1233341Sgc161489 /*
1243341Sgc161489 * Busops vector
1253341Sgc161489 */
1263341Sgc161489 static struct bus_ops usb_ia_busops = {
1273341Sgc161489 BUSO_REV,
1283341Sgc161489 nullbusmap, /* bus_map */
1293341Sgc161489 NULL, /* bus_get_intrspec */
1303341Sgc161489 NULL, /* bus_add_intrspec */
1313341Sgc161489 NULL, /* bus_remove_intrspec */
1323341Sgc161489 NULL, /* XXXX bus_map_fault */
1333341Sgc161489 ddi_dma_map, /* bus_dma_map */
1343341Sgc161489 ddi_dma_allochdl,
1353341Sgc161489 ddi_dma_freehdl,
1363341Sgc161489 ddi_dma_bindhdl,
1373341Sgc161489 ddi_dma_unbindhdl,
1383341Sgc161489 ddi_dma_flush,
1393341Sgc161489 ddi_dma_win,
1403341Sgc161489 ddi_dma_mctl, /* bus_dma_ctl */
1413341Sgc161489 usb_ia_bus_ctl, /* bus_ctl */
1423341Sgc161489 ddi_bus_prop_op, /* bus_prop_op */
1433341Sgc161489 usb_ia_busop_get_eventcookie,
1443341Sgc161489 usb_ia_busop_add_eventcall,
1453341Sgc161489 usb_ia_busop_remove_eventcall,
1463341Sgc161489 usb_ia_busop_post_event, /* bus_post_event */
1473341Sgc161489 NULL, /* bus_intr_ctl */
1483341Sgc161489 usb_ia_bus_config, /* bus_config */
1493341Sgc161489 usb_ia_bus_unconfig, /* bus_unconfig */
1503341Sgc161489 NULL, /* bus_fm_init */
1513341Sgc161489 NULL, /* bus_fm_fini */
1523341Sgc161489 NULL, /* bus_fm_access_enter */
1533341Sgc161489 NULL, /* bus_fm_access_exit */
1543341Sgc161489 NULL /* bus_power */
1553341Sgc161489 };
1563341Sgc161489
1573341Sgc161489
1583341Sgc161489 static struct dev_ops usb_ia_ops = {
1593341Sgc161489 DEVO_REV, /* devo_rev, */
1603341Sgc161489 0, /* refcnt */
1613341Sgc161489 usb_ia_info, /* info */
1623341Sgc161489 nulldev, /* identify */
1633341Sgc161489 nulldev, /* probe */
1643341Sgc161489 usb_ia_attach, /* attach */
1653341Sgc161489 usb_ia_detach, /* detach */
1663341Sgc161489 nodev, /* reset */
1673341Sgc161489 &usb_ia_cb_ops, /* driver operations */
1683341Sgc161489 &usb_ia_busops, /* bus operations */
1697656SSherry.Moore@Sun.COM usb_ia_power, /* power */
170*8688SRaymond.Chen@Sun.COM ddi_quiesce_not_needed, /* devo_quiesce */
1713341Sgc161489 };
1723341Sgc161489
1733341Sgc161489 static struct modldrv modldrv = {
1743341Sgc161489 &mod_driverops, /* Type of module. This one is a driver */
1757425SGongtian.Zhao@Sun.COM "USB Interface Association Driver", /* Name of the module. */
1763341Sgc161489 &usb_ia_ops, /* driver ops */
1773341Sgc161489 };
1783341Sgc161489
1793341Sgc161489 static struct modlinkage modlinkage = {
1803341Sgc161489 MODREV_1, (void *)&modldrv, NULL
1813341Sgc161489 };
1823341Sgc161489
1833341Sgc161489 #define USB_IA_INITIAL_SOFT_SPACE 4
1843341Sgc161489 static void *usb_ia_statep;
1853341Sgc161489
1863341Sgc161489 /*
1873341Sgc161489 * event definition
1883341Sgc161489 */
1893341Sgc161489 static ndi_event_definition_t usb_ia_ndi_event_defs[] = {
1903341Sgc161489 {USBA_EVENT_TAG_HOT_REMOVAL, DDI_DEVI_REMOVE_EVENT, EPL_KERNEL,
1913341Sgc161489 NDI_EVENT_POST_TO_ALL},
1923341Sgc161489 {USBA_EVENT_TAG_HOT_INSERTION, DDI_DEVI_INSERT_EVENT, EPL_KERNEL,
1933341Sgc161489 NDI_EVENT_POST_TO_ALL},
1943341Sgc161489 {USBA_EVENT_TAG_POST_RESUME, USBA_POST_RESUME_EVENT, EPL_KERNEL,
1953341Sgc161489 NDI_EVENT_POST_TO_ALL},
1963341Sgc161489 {USBA_EVENT_TAG_PRE_SUSPEND, USBA_PRE_SUSPEND_EVENT, EPL_KERNEL,
1973341Sgc161489 NDI_EVENT_POST_TO_ALL}
1983341Sgc161489 };
1993341Sgc161489
2003341Sgc161489 #define USB_IA_N_NDI_EVENTS \
2013341Sgc161489 (sizeof (usb_ia_ndi_event_defs) / sizeof (ndi_event_definition_t))
2023341Sgc161489
2033341Sgc161489 static ndi_event_set_t usb_ia_ndi_events = {
2043341Sgc161489 NDI_EVENTS_REV1, USB_IA_N_NDI_EVENTS, usb_ia_ndi_event_defs};
2053341Sgc161489
2063341Sgc161489
2073341Sgc161489 /*
2083341Sgc161489 * standard driver entry points
2093341Sgc161489 */
2103341Sgc161489 int
_init(void)2113341Sgc161489 _init(void)
2123341Sgc161489 {
2133341Sgc161489 int rval;
2143341Sgc161489
2153341Sgc161489 rval = ddi_soft_state_init(&usb_ia_statep, sizeof (struct usb_ia),
2163341Sgc161489 USB_IA_INITIAL_SOFT_SPACE);
2173341Sgc161489 if (rval != 0) {
2183341Sgc161489 return (rval);
2193341Sgc161489 }
2203341Sgc161489
2213341Sgc161489 if ((rval = mod_install(&modlinkage)) != 0) {
2223341Sgc161489 ddi_soft_state_fini(&usb_ia_statep);
2233341Sgc161489 return (rval);
2243341Sgc161489 }
2253341Sgc161489
2263341Sgc161489 return (rval);
2273341Sgc161489 }
2283341Sgc161489
2293341Sgc161489
2303341Sgc161489 int
_fini(void)2313341Sgc161489 _fini(void)
2323341Sgc161489 {
2333341Sgc161489 int rval;
2343341Sgc161489
2353341Sgc161489 rval = mod_remove(&modlinkage);
2363341Sgc161489
2373341Sgc161489 if (rval) {
2383341Sgc161489 return (rval);
2393341Sgc161489 }
2403341Sgc161489
2413341Sgc161489 ddi_soft_state_fini(&usb_ia_statep);
2423341Sgc161489
2433341Sgc161489 return (rval);
2443341Sgc161489 }
2453341Sgc161489
2463341Sgc161489
2473341Sgc161489 int
_info(struct modinfo * modinfop)2483341Sgc161489 _info(struct modinfo *modinfop)
2493341Sgc161489 {
2503341Sgc161489 return (mod_info(&modlinkage, modinfop));
2513341Sgc161489 }
2523341Sgc161489
2533341Sgc161489
2543341Sgc161489 /*ARGSUSED*/
2553341Sgc161489 static int
usb_ia_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)2563341Sgc161489 usb_ia_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
2573341Sgc161489 {
2583341Sgc161489 usb_ia_t *usb_ia;
2593341Sgc161489 int instance = getminor((dev_t)arg);
2603341Sgc161489 int error = DDI_FAILURE;
2613341Sgc161489
2623341Sgc161489 switch (infocmd) {
2633341Sgc161489 case DDI_INFO_DEVT2DEVINFO:
2643341Sgc161489 if ((usb_ia = ddi_get_soft_state(usb_ia_statep,
2653341Sgc161489 instance)) != NULL) {
2663341Sgc161489 *result = (void *)usb_ia->ia_dip;
2673341Sgc161489 if (*result != NULL) {
2683341Sgc161489 error = DDI_SUCCESS;
2693341Sgc161489 }
2703341Sgc161489 } else {
2713341Sgc161489 *result = NULL;
2723341Sgc161489 }
2733341Sgc161489 break;
2743341Sgc161489
2753341Sgc161489 case DDI_INFO_DEVT2INSTANCE:
2763341Sgc161489 *result = (void *)(intptr_t)instance;
2773341Sgc161489 error = DDI_SUCCESS;
2783341Sgc161489 break;
2793341Sgc161489 default:
2803341Sgc161489 break;
2813341Sgc161489 }
2823341Sgc161489
2833341Sgc161489 return (error);
2843341Sgc161489 }
2853341Sgc161489
2863341Sgc161489
2873341Sgc161489 /*
2883341Sgc161489 * child post attach/detach notification
2893341Sgc161489 */
2903341Sgc161489 static void
usb_ia_post_attach(usb_ia_t * usb_ia,uint8_t ifno,struct attachspec * as)2913341Sgc161489 usb_ia_post_attach(usb_ia_t *usb_ia, uint8_t ifno, struct attachspec *as)
2923341Sgc161489 {
2933341Sgc161489 USB_DPRINTF_L4(DPRINT_MASK_PM, usb_ia->ia_log_handle,
2943341Sgc161489 "usb_ia_post_attach: ifno = %d result = %d", ifno, as->result);
2953341Sgc161489
2963341Sgc161489 }
2973341Sgc161489
2983341Sgc161489
2993341Sgc161489 static void
usb_ia_post_detach(usb_ia_t * usb_ia,uint8_t ifno,struct detachspec * ds)3003341Sgc161489 usb_ia_post_detach(usb_ia_t *usb_ia, uint8_t ifno, struct detachspec *ds)
3013341Sgc161489 {
3023341Sgc161489 USB_DPRINTF_L4(DPRINT_MASK_PM, usb_ia->ia_log_handle,
3033341Sgc161489 "usb_ia_post_detach: ifno = %d result = %d", ifno, ds->result);
3043341Sgc161489
3053341Sgc161489 }
3063341Sgc161489
3073341Sgc161489
3083341Sgc161489 /*
3093341Sgc161489 * bus ctl support. we handle notifications here and the
3103341Sgc161489 * rest goes up to root hub/hcd
3113341Sgc161489 */
3123341Sgc161489 /*ARGSUSED*/
3133341Sgc161489 static int
usb_ia_bus_ctl(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t op,void * arg,void * result)3143341Sgc161489 usb_ia_bus_ctl(dev_info_t *dip,
3153341Sgc161489 dev_info_t *rdip,
3163341Sgc161489 ddi_ctl_enum_t op,
3173341Sgc161489 void *arg,
3183341Sgc161489 void *result)
3193341Sgc161489 {
3203341Sgc161489 usba_device_t *hub_usba_device = usba_get_usba_device(rdip);
3213341Sgc161489 dev_info_t *root_hub_dip = hub_usba_device->usb_root_hub_dip;
3223341Sgc161489 usb_ia_t *usb_ia;
3233341Sgc161489 struct attachspec *as;
3243341Sgc161489 struct detachspec *ds;
3253341Sgc161489
3263341Sgc161489 usb_ia = usb_ia_obtain_state(dip);
3273341Sgc161489
3283341Sgc161489 USB_DPRINTF_L4(DPRINT_MASK_PM, usb_ia->ia_log_handle,
3293341Sgc161489 "usb_ia_bus_ctl:\n\t"
3303341Sgc161489 "dip = 0x%p, rdip = 0x%p, op = 0x%x, arg = 0x%p",
3316898Sfb209375 (void *)dip, (void *)rdip, op, arg);
3323341Sgc161489
3333341Sgc161489 switch (op) {
3343341Sgc161489 case DDI_CTLOPS_ATTACH:
3353341Sgc161489 as = (struct attachspec *)arg;
3363341Sgc161489
3373341Sgc161489 switch (as->when) {
3383341Sgc161489 case DDI_PRE :
3393341Sgc161489 /* nothing to do basically */
3403341Sgc161489 USB_DPRINTF_L2(DPRINT_MASK_PM, usb_ia->ia_log_handle,
3413341Sgc161489 "DDI_PRE DDI_CTLOPS_ATTACH");
3423341Sgc161489 break;
3433341Sgc161489 case DDI_POST :
3443341Sgc161489 usb_ia_post_attach(usb_ia, usba_get_ifno(rdip),
3453341Sgc161489 (struct attachspec *)arg);
3463341Sgc161489 break;
3473341Sgc161489 }
3483341Sgc161489
3493341Sgc161489 break;
3503341Sgc161489 case DDI_CTLOPS_DETACH:
3513341Sgc161489 ds = (struct detachspec *)arg;
3523341Sgc161489
3533341Sgc161489 switch (ds->when) {
3543341Sgc161489 case DDI_PRE :
3553341Sgc161489 /* nothing to do basically */
3563341Sgc161489 USB_DPRINTF_L2(DPRINT_MASK_PM, usb_ia->ia_log_handle,
3573341Sgc161489 "DDI_PRE DDI_CTLOPS_DETACH");
3583341Sgc161489 break;
3593341Sgc161489 case DDI_POST :
3603341Sgc161489 usb_ia_post_detach(usb_ia, usba_get_ifno(rdip),
3613341Sgc161489 (struct detachspec *)arg);
3623341Sgc161489 break;
3633341Sgc161489 }
3643341Sgc161489
3653341Sgc161489 break;
3663341Sgc161489 default:
3673341Sgc161489 /* pass to root hub to handle */
3683341Sgc161489 return (usba_bus_ctl(root_hub_dip, rdip, op, arg, result));
3693341Sgc161489 }
3703341Sgc161489
3713341Sgc161489 return (DDI_SUCCESS);
3723341Sgc161489 }
3733341Sgc161489
3743341Sgc161489
3753341Sgc161489 /*
3763341Sgc161489 * bus enumeration entry points
3773341Sgc161489 */
3783341Sgc161489 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)3793341Sgc161489 usb_ia_bus_config(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
3803341Sgc161489 void *arg, dev_info_t **child)
3813341Sgc161489 {
3823341Sgc161489 int rval, circ;
3833341Sgc161489 usb_ia_t *usb_ia = usb_ia_obtain_state(dip);
3843341Sgc161489
3853341Sgc161489 USB_DPRINTF_L4(DPRINT_MASK_ALL, usb_ia->ia_log_handle,
3863341Sgc161489 "usb_ia_bus_config: op=%d", op);
3873341Sgc161489
3883341Sgc161489 if (usb_ia_bus_config_debug) {
3893341Sgc161489 flag |= NDI_DEVI_DEBUG;
3903341Sgc161489 }
3913341Sgc161489
3923341Sgc161489 ndi_devi_enter(dip, &circ);
3933341Sgc161489
3943341Sgc161489 /* enumerate each interface below us */
3953341Sgc161489 mutex_enter(&usb_ia->ia_mutex);
3963341Sgc161489 usb_ia_create_children(usb_ia);
3973341Sgc161489 mutex_exit(&usb_ia->ia_mutex);
3983341Sgc161489
3993341Sgc161489 rval = ndi_busop_bus_config(dip, flag, op, arg, child, 0);
4003341Sgc161489 ndi_devi_exit(dip, circ);
4013341Sgc161489
4023341Sgc161489 return (rval);
4033341Sgc161489 }
4043341Sgc161489
4053341Sgc161489
4063341Sgc161489 static int
usb_ia_bus_unconfig(dev_info_t * dip,uint_t flag,ddi_bus_config_op_t op,void * arg)4073341Sgc161489 usb_ia_bus_unconfig(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
4083341Sgc161489 void *arg)
4093341Sgc161489 {
4103341Sgc161489 usb_ia_t *usb_ia = usb_ia_obtain_state(dip);
4113341Sgc161489
4123341Sgc161489 dev_info_t *cdip, *mdip;
4133341Sgc161489 int interface, circular_count;
4143341Sgc161489 int rval = NDI_SUCCESS;
4153341Sgc161489
4163341Sgc161489 USB_DPRINTF_L4(DPRINT_MASK_ALL, usb_ia->ia_log_handle,
4173341Sgc161489 "usb_ia_bus_unconfig: op=%d", op);
4183341Sgc161489
4193341Sgc161489 if (usb_ia_bus_config_debug) {
4203341Sgc161489 flag |= NDI_DEVI_DEBUG;
4213341Sgc161489 }
4223341Sgc161489
4233341Sgc161489 /*
4243341Sgc161489 * first offline and if offlining successful, then
4253341Sgc161489 * remove children
4263341Sgc161489 */
4273341Sgc161489 if (op == BUS_UNCONFIG_ALL) {
4283341Sgc161489 flag &= ~(NDI_DEVI_REMOVE | NDI_UNCONFIG);
4293341Sgc161489 }
4303341Sgc161489
4313341Sgc161489 ndi_devi_enter(dip, &circular_count);
4323341Sgc161489 rval = ndi_busop_bus_unconfig(dip, flag, op, arg);
4333341Sgc161489
4343341Sgc161489 if (op == BUS_UNCONFIG_ALL && rval == NDI_SUCCESS &&
4353341Sgc161489 (flag & NDI_AUTODETACH) == 0) {
4363341Sgc161489 flag |= NDI_DEVI_REMOVE;
4373341Sgc161489 rval = ndi_busop_bus_unconfig(dip, flag, op, arg);
4383341Sgc161489 }
4393341Sgc161489
4403341Sgc161489 /* update children's list */
4413341Sgc161489 mutex_enter(&usb_ia->ia_mutex);
4423341Sgc161489 for (interface = 0; usb_ia->ia_children_dips &&
4433341Sgc161489 (interface < usb_ia->ia_n_ifs); interface++) {
4443341Sgc161489 mdip = usb_ia->ia_children_dips[interface];
4453341Sgc161489
4463341Sgc161489 /* now search if this dip still exists */
4476898Sfb209375 for (cdip = ddi_get_child(dip); cdip && (cdip != mdip); )
4486898Sfb209375 cdip = ddi_get_next_sibling(cdip);
4493341Sgc161489
4503341Sgc161489 if (cdip != mdip) {
4513341Sgc161489 /* we lost the dip on this interface */
4523341Sgc161489 usb_ia->ia_children_dips[interface] = NULL;
4533341Sgc161489 } else if (cdip) {
4543341Sgc161489 /*
4553341Sgc161489 * keep in DS_INITALIZED to prevent parent
4563341Sgc161489 * from detaching
4573341Sgc161489 */
4583341Sgc161489 (void) ddi_initchild(ddi_get_parent(cdip), cdip);
4593341Sgc161489 }
4603341Sgc161489 }
4613341Sgc161489 mutex_exit(&usb_ia->ia_mutex);
4623341Sgc161489
4633341Sgc161489 ndi_devi_exit(dip, circular_count);
4643341Sgc161489
4653341Sgc161489 USB_DPRINTF_L4(DPRINT_MASK_ALL, usb_ia->ia_log_handle,
4663341Sgc161489 "usb_ia_bus_config: rval=%d", rval);
4673341Sgc161489
4683341Sgc161489 return (rval);
4693341Sgc161489 }
4703341Sgc161489
4713341Sgc161489
4723341Sgc161489 /* power entry point */
4733341Sgc161489 /* ARGSUSED */
4743341Sgc161489 static int
usb_ia_power(dev_info_t * dip,int comp,int level)4753341Sgc161489 usb_ia_power(dev_info_t *dip, int comp, int level)
4763341Sgc161489 {
4773341Sgc161489 usb_ia_t *usb_ia;
4783341Sgc161489 usb_common_power_t *pm;
4793341Sgc161489 int rval = DDI_FAILURE;
4803341Sgc161489
4813341Sgc161489 usb_ia = usb_ia_obtain_state(dip);
4823341Sgc161489
4833341Sgc161489 USB_DPRINTF_L4(DPRINT_MASK_PM, usb_ia->ia_log_handle,
4846898Sfb209375 "usb_ia_power: Begin: usb_ia = %p, level = %d",
4856898Sfb209375 (void *)usb_ia, level);
4863341Sgc161489
4873341Sgc161489 mutex_enter(&usb_ia->ia_mutex);
4883341Sgc161489 pm = usb_ia->ia_pm;
4893341Sgc161489
4903341Sgc161489 /* check if we are transitioning to a legal power level */
4913341Sgc161489 if (USB_DEV_PWRSTATE_OK(pm->uc_pwr_states, level)) {
4923341Sgc161489 USB_DPRINTF_L2(DPRINT_MASK_PM, usb_ia->ia_log_handle,
4933341Sgc161489 "usb_ia_power: illegal power level = %d "
4943341Sgc161489 "uc_pwr_states = %x", level, pm->uc_pwr_states);
4953341Sgc161489
4963341Sgc161489 mutex_exit(&usb_ia->ia_mutex);
4973341Sgc161489
4983341Sgc161489 return (rval);
4993341Sgc161489 }
5003341Sgc161489
5013528Sgc161489 rval = usba_common_power(dip, &(pm->uc_current_power),
5023528Sgc161489 &(usb_ia->ia_dev_state), level);
5033341Sgc161489
5043341Sgc161489 mutex_exit(&usb_ia->ia_mutex);
5053341Sgc161489
5063341Sgc161489 return (rval);
5073341Sgc161489 }
5083341Sgc161489
5093341Sgc161489 /*
5103341Sgc161489 * attach/resume entry point
5113341Sgc161489 */
5123341Sgc161489 static int
usb_ia_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)5133341Sgc161489 usb_ia_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
5143341Sgc161489 {
5153341Sgc161489 int instance = ddi_get_instance(dip);
5163341Sgc161489 usb_ia_t *usb_ia = NULL;
5173341Sgc161489 uint_t n_ifs;
5183341Sgc161489 size_t size;
5193341Sgc161489
5203341Sgc161489 switch (cmd) {
5213341Sgc161489 case DDI_ATTACH:
5223341Sgc161489
5233341Sgc161489 break;
5243341Sgc161489 case DDI_RESUME:
5253341Sgc161489 usb_ia = ddi_get_soft_state(usb_ia_statep, instance);
5263341Sgc161489 (void) usb_ia_restore_device_state(dip, usb_ia);
5273341Sgc161489
5283341Sgc161489 return (DDI_SUCCESS);
5293341Sgc161489 default:
5303341Sgc161489
5313341Sgc161489 return (DDI_FAILURE);
5323341Sgc161489 }
5333341Sgc161489
5343341Sgc161489 /*
5353341Sgc161489 * Attach:
5363341Sgc161489 *
5373341Sgc161489 * Allocate soft state and initialize
5383341Sgc161489 */
5393341Sgc161489 if (ddi_soft_state_zalloc(usb_ia_statep, instance) != DDI_SUCCESS) {
5403341Sgc161489 goto fail;
5413341Sgc161489 }
5423341Sgc161489
5433341Sgc161489 usb_ia = ddi_get_soft_state(usb_ia_statep, instance);
5443341Sgc161489 if (usb_ia == NULL) {
5453341Sgc161489
5463341Sgc161489 goto fail;
5473341Sgc161489 }
5483341Sgc161489
5493341Sgc161489 /* allocate handle for logging of messages */
5503341Sgc161489 usb_ia->ia_log_handle = usb_alloc_log_hdl(dip, "ia",
5516898Sfb209375 &usb_ia_errlevel,
5526898Sfb209375 &usb_ia_errmask, &usb_ia_instance_debug,
5536898Sfb209375 0);
5543341Sgc161489
5553341Sgc161489 usb_ia->ia_dip = dip;
5563341Sgc161489 usb_ia->ia_instance = instance;
5573341Sgc161489 usb_ia->ia_first_if = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
5583341Sgc161489 DDI_PROP_DONTPASS, "interface", -1);
5593341Sgc161489 usb_ia->ia_n_ifs = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
5603341Sgc161489 DDI_PROP_DONTPASS, "interface-count", -1);
5613341Sgc161489
5623341Sgc161489 if (usb_ia->ia_first_if < 0 || usb_ia->ia_n_ifs < 0) {
5633341Sgc161489 USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_ia->ia_log_handle,
5643341Sgc161489 "interface-association property failed");
5653341Sgc161489
5663341Sgc161489 goto fail;
5673341Sgc161489 }
5683341Sgc161489
5693341Sgc161489 /* attach client driver to USBA */
5703341Sgc161489 if (usb_client_attach(dip, USBDRV_VERSION, 0) != USB_SUCCESS) {
5713341Sgc161489 USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_ia->ia_log_handle,
5723341Sgc161489 "usb_client_attach failed");
5733341Sgc161489 goto fail;
5743341Sgc161489 }
5753341Sgc161489 if (usb_get_dev_data(dip, &usb_ia->ia_dev_data, USB_PARSE_LVL_NONE,
5763341Sgc161489 0) != USB_SUCCESS) {
5773341Sgc161489 USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_ia->ia_log_handle,
5783341Sgc161489 "usb_get_dev_data failed");
5793341Sgc161489 goto fail;
5803341Sgc161489 }
5813341Sgc161489
5823341Sgc161489 mutex_init(&usb_ia->ia_mutex, NULL, MUTEX_DRIVER,
5833341Sgc161489 usb_ia->ia_dev_data->dev_iblock_cookie);
5843341Sgc161489
5853341Sgc161489 usb_free_dev_data(dip, usb_ia->ia_dev_data);
5863341Sgc161489 usb_ia->ia_dev_data = NULL;
5873341Sgc161489
5883341Sgc161489 usb_ia->ia_init_state |= USB_IA_LOCK_INIT;
5893341Sgc161489
5903341Sgc161489 if (ddi_create_minor_node(dip, "usb_ia", S_IFCHR, instance,
5913341Sgc161489 DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
5923341Sgc161489 USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_ia->ia_log_handle,
5933341Sgc161489 "cannot create devctl minor node");
5943341Sgc161489 goto fail;
5953341Sgc161489 }
5963341Sgc161489
5973341Sgc161489 usb_ia->ia_init_state |= USB_IA_MINOR_NODE_CREATED;
5983341Sgc161489
5993341Sgc161489 /*
6003341Sgc161489 * allocate array for keeping track of child dips
6013341Sgc161489 */
6023341Sgc161489 n_ifs = usb_ia->ia_n_ifs;
6033341Sgc161489 usb_ia->ia_cd_list_length = size = (sizeof (dev_info_t *)) * n_ifs;
6043341Sgc161489
6053341Sgc161489 usb_ia->ia_children_dips = kmem_zalloc(size, KM_SLEEP);
6063341Sgc161489 usb_ia->ia_child_events = kmem_zalloc(sizeof (uint8_t) * n_ifs,
6076898Sfb209375 KM_SLEEP);
6083341Sgc161489 /*
6093341Sgc161489 * Event handling: definition and registration
6103341Sgc161489 * get event handle for events that we have defined
6113341Sgc161489 */
6123341Sgc161489 (void) ndi_event_alloc_hdl(dip, 0, &usb_ia->ia_ndi_event_hdl,
6136898Sfb209375 NDI_SLEEP);
6143341Sgc161489
6153341Sgc161489 /* bind event set to the handle */
6163341Sgc161489 if (ndi_event_bind_set(usb_ia->ia_ndi_event_hdl, &usb_ia_ndi_events,
6173341Sgc161489 NDI_SLEEP)) {
6183341Sgc161489 USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_ia->ia_log_handle,
6193341Sgc161489 "usb_ia_attach: binding event set failed");
6203341Sgc161489
6213341Sgc161489 goto fail;
6223341Sgc161489 }
6233341Sgc161489
6243341Sgc161489 usb_ia->ia_dev_state = USB_DEV_ONLINE;
6253341Sgc161489
6263341Sgc161489 /*
6273341Sgc161489 * now create components to power manage this device
6283341Sgc161489 * before attaching children
6293341Sgc161489 */
6303341Sgc161489 usb_ia_create_pm_components(dip, usb_ia);
6313341Sgc161489
6323341Sgc161489 /* event registration for events from our parent */
6333341Sgc161489 usba_common_register_events(dip, n_ifs, usb_ia_event_cb);
6343341Sgc161489
6353341Sgc161489 usb_ia->ia_init_state |= USB_IA_EVENTS_REGISTERED;
6363341Sgc161489
6373341Sgc161489 ddi_report_dev(dip);
6383341Sgc161489
6393341Sgc161489 return (DDI_SUCCESS);
6403341Sgc161489
6413341Sgc161489 fail:
6423341Sgc161489 USB_DPRINTF_L2(DPRINT_MASK_ATTA, NULL, "usb_ia%d cannot attach",
6433341Sgc161489 instance);
6443341Sgc161489
6453341Sgc161489 if (usb_ia) {
6463341Sgc161489 (void) usb_ia_cleanup(usb_ia);
6473341Sgc161489 }
6483341Sgc161489
6493341Sgc161489 return (DDI_FAILURE);
6503341Sgc161489 }
6513341Sgc161489
6523341Sgc161489
6533341Sgc161489 /* detach or suspend this instance */
6543341Sgc161489 static int
usb_ia_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)6553341Sgc161489 usb_ia_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
6563341Sgc161489 {
6573341Sgc161489 usb_ia_t *usb_ia = usb_ia_obtain_state(dip);
6583341Sgc161489
6593341Sgc161489 USB_DPRINTF_L4(DPRINT_MASK_ATTA, usb_ia->ia_log_handle,
6603341Sgc161489 "usb_ia_detach: cmd = 0x%x", cmd);
6613341Sgc161489
6623341Sgc161489 switch (cmd) {
6633341Sgc161489 case DDI_DETACH:
6643341Sgc161489
6653341Sgc161489 return (usb_ia_cleanup(usb_ia));
6663341Sgc161489 case DDI_SUSPEND:
6673341Sgc161489 /* nothing to do */
6683341Sgc161489 mutex_enter(&usb_ia->ia_mutex);
6693341Sgc161489 usb_ia->ia_dev_state = USB_DEV_SUSPENDED;
6703341Sgc161489 mutex_exit(&usb_ia->ia_mutex);
6713341Sgc161489
6723341Sgc161489 return (DDI_SUCCESS);
6733341Sgc161489 default:
6743341Sgc161489
6753341Sgc161489 return (DDI_FAILURE);
6763341Sgc161489 }
6773341Sgc161489
6783341Sgc161489 _NOTE(NOT_REACHED)
6793341Sgc161489 /* NOTREACHED */
6803341Sgc161489 }
6813341Sgc161489
6823341Sgc161489
6833341Sgc161489 /*
6843341Sgc161489 * usb_ia_cleanup:
6853341Sgc161489 * cleanup usb_ia and deallocate. this function is called for
6863341Sgc161489 * handling attach failures and detaching including dynamic
6873341Sgc161489 * reconfiguration
6883341Sgc161489 */
6893341Sgc161489 /*ARGSUSED*/
6903341Sgc161489 static int
usb_ia_cleanup(usb_ia_t * usb_ia)6913341Sgc161489 usb_ia_cleanup(usb_ia_t *usb_ia)
6923341Sgc161489 {
6933341Sgc161489 usb_common_power_t *iapm;
6943341Sgc161489 int rval;
6953341Sgc161489 dev_info_t *dip = usb_ia->ia_dip;
6963341Sgc161489
6973341Sgc161489 USB_DPRINTF_L4(DPRINT_MASK_ATTA, usb_ia->ia_log_handle,
6983341Sgc161489 "usb_ia_cleanup:");
6993341Sgc161489
7003341Sgc161489 if ((usb_ia->ia_init_state & USB_IA_LOCK_INIT) == 0) {
7013341Sgc161489
7023341Sgc161489 goto done;
7033341Sgc161489 }
7043341Sgc161489
7053341Sgc161489 /*
7063341Sgc161489 * deallocate events, if events are still registered
7073341Sgc161489 * (ie. children still attached) then we have to fail the detach
7083341Sgc161489 */
7093341Sgc161489 if (usb_ia->ia_ndi_event_hdl &&
7103341Sgc161489 (ndi_event_free_hdl(usb_ia->ia_ndi_event_hdl) != NDI_SUCCESS)) {
7113341Sgc161489
7123341Sgc161489 USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_ia->ia_log_handle,
7133341Sgc161489 "usb_ia_cleanup: ndi_event_free_hdl failed");
7143341Sgc161489
7153341Sgc161489 return (DDI_FAILURE);
7163341Sgc161489 }
7173341Sgc161489
7183341Sgc161489 /*
7193341Sgc161489 * Disable the event callbacks, after this point, event
7203341Sgc161489 * callbacks will never get called. Note we shouldn't hold
7213341Sgc161489 * mutex while unregistering events because there may be a
7223341Sgc161489 * competing event callback thread. Event callbacks are done
7233341Sgc161489 * with ndi mutex held and this can cause a potential deadlock.
7243341Sgc161489 * Note that cleanup can't fail after deregistration of events.
7253341Sgc161489 */
7263341Sgc161489 if (usb_ia->ia_init_state & USB_IA_EVENTS_REGISTERED) {
7273341Sgc161489
7283341Sgc161489 usba_common_unregister_events(usb_ia->ia_dip, usb_ia->ia_n_ifs);
7293341Sgc161489 }
7303341Sgc161489
7313341Sgc161489 iapm = usb_ia->ia_pm;
7323341Sgc161489
7333341Sgc161489 mutex_enter(&usb_ia->ia_mutex);
7343341Sgc161489
7353341Sgc161489 if ((iapm) && (usb_ia->ia_dev_state != USB_DEV_DISCONNECTED)) {
7363341Sgc161489
7373341Sgc161489 mutex_exit(&usb_ia->ia_mutex);
7383341Sgc161489
7393341Sgc161489 (void) pm_busy_component(dip, 0);
7403341Sgc161489 if (iapm->uc_wakeup_enabled) {
7413341Sgc161489
7423341Sgc161489 /* First bring the device to full power */
7433341Sgc161489 (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
7443341Sgc161489
7453341Sgc161489 rval = usb_handle_remote_wakeup(dip,
7463341Sgc161489 USB_REMOTE_WAKEUP_DISABLE);
7473341Sgc161489
7483341Sgc161489 if (rval != DDI_SUCCESS) {
7493341Sgc161489 USB_DPRINTF_L2(DPRINT_MASK_EVENTS,
7503341Sgc161489 usb_ia->ia_log_handle,
7513341Sgc161489 "usb_cleanup: disable remote "
7523341Sgc161489 "wakeup failed, rval=%d", rval);
7533341Sgc161489 }
7543341Sgc161489 }
7553341Sgc161489
7563341Sgc161489 (void) pm_lower_power(usb_ia->ia_dip, 0, USB_DEV_OS_PWR_OFF);
7573341Sgc161489 (void) pm_idle_component(dip, 0);
7583341Sgc161489 } else {
7593341Sgc161489 mutex_exit(&usb_ia->ia_mutex);
7603341Sgc161489 }
7613341Sgc161489
7623341Sgc161489 if (iapm) {
7633341Sgc161489 kmem_free(iapm, sizeof (usb_common_power_t));
7643341Sgc161489 }
7653341Sgc161489
7663341Sgc161489 /* free children list */
7673341Sgc161489 if (usb_ia->ia_children_dips) {
7683341Sgc161489 kmem_free(usb_ia->ia_children_dips,
7696898Sfb209375 usb_ia->ia_cd_list_length);
7703341Sgc161489 }
7713341Sgc161489
7723341Sgc161489 if (usb_ia->ia_child_events) {
7733341Sgc161489 kmem_free(usb_ia->ia_child_events, sizeof (uint8_t) *
7746898Sfb209375 usb_ia->ia_n_ifs);
7753341Sgc161489 }
7763341Sgc161489
7773341Sgc161489 if (usb_ia->ia_init_state & USB_IA_MINOR_NODE_CREATED) {
7783341Sgc161489 ddi_remove_minor_node(dip, NULL);
7793341Sgc161489 }
7803341Sgc161489
7813341Sgc161489 mutex_destroy(&usb_ia->ia_mutex);
7823341Sgc161489
7833341Sgc161489 done:
7843341Sgc161489 usb_client_detach(dip, usb_ia->ia_dev_data);
7853341Sgc161489
7863341Sgc161489 usb_free_log_hdl(usb_ia->ia_log_handle);
7873341Sgc161489 ddi_soft_state_free(usb_ia_statep, ddi_get_instance(dip));
7883341Sgc161489
7893341Sgc161489 ddi_prop_remove_all(dip);
7903341Sgc161489
7913341Sgc161489 return (DDI_SUCCESS);
7923341Sgc161489 }
7933341Sgc161489
7943341Sgc161489 /*
7953341Sgc161489 * usb_ia_create_children:
7963341Sgc161489 */
7973341Sgc161489 static void
usb_ia_create_children(usb_ia_t * usb_ia)7983341Sgc161489 usb_ia_create_children(usb_ia_t *usb_ia)
7993341Sgc161489 {
8003341Sgc161489 usba_device_t *usba_device;
8013341Sgc161489 uint_t n_ifs, first_if;
8023341Sgc161489 uint_t i;
8033341Sgc161489 dev_info_t *cdip;
8043341Sgc161489
8053341Sgc161489 usba_device = usba_get_usba_device(usb_ia->ia_dip);
8063341Sgc161489
8073341Sgc161489 USB_DPRINTF_L4(DPRINT_MASK_ATTA, usb_ia->ia_log_handle,
8083341Sgc161489 "usb_ia_attach_child_drivers: port = %d, address = %d",
8093341Sgc161489 usba_device->usb_port, usba_device->usb_addr);
8103341Sgc161489
8113341Sgc161489 n_ifs = usb_ia->ia_n_ifs;
8123341Sgc161489 first_if = usb_ia->ia_first_if;
8133341Sgc161489
8143341Sgc161489 /*
8153341Sgc161489 * create all children if not already present
8163341Sgc161489 */
8173341Sgc161489 for (i = 0; i < n_ifs; i++) {
8183341Sgc161489 if (usb_ia->ia_children_dips[i] != NULL) {
8193341Sgc161489
8203341Sgc161489 continue;
8213341Sgc161489 }
8223341Sgc161489
8233341Sgc161489 mutex_exit(&usb_ia->ia_mutex);
8243341Sgc161489 cdip = usba_ready_interface_node(usb_ia->ia_dip, first_if + i);
8253341Sgc161489 mutex_enter(&usb_ia->ia_mutex);
8263341Sgc161489
8273341Sgc161489 if (cdip != NULL) {
8283341Sgc161489 (void) usba_bind_driver(cdip);
8293341Sgc161489 usb_ia->ia_children_dips[i] = cdip;
8303341Sgc161489 }
8313341Sgc161489 }
8323341Sgc161489
8333341Sgc161489 }
8343341Sgc161489
8353341Sgc161489
8363341Sgc161489 /*
8373341Sgc161489 * event support
8383341Sgc161489 */
8393341Sgc161489 static int
usb_ia_busop_get_eventcookie(dev_info_t * dip,dev_info_t * rdip,char * eventname,ddi_eventcookie_t * cookie)8403341Sgc161489 usb_ia_busop_get_eventcookie(dev_info_t *dip,
8413341Sgc161489 dev_info_t *rdip, char *eventname, ddi_eventcookie_t *cookie)
8423341Sgc161489 {
8433341Sgc161489 usb_ia_t *usb_ia = usb_ia_obtain_state(dip);
8443341Sgc161489
8453341Sgc161489 USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
8463341Sgc161489 "usb_ia_busop_get_eventcookie: dip=0x%p, rdip=0x%p, "
8473341Sgc161489 "event=%s", (void *)dip, (void *)rdip, eventname);
8483341Sgc161489 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
8493341Sgc161489 "(dip=%s%d rdip=%s%d)",
8503341Sgc161489 ddi_driver_name(dip), ddi_get_instance(dip),
8513341Sgc161489 ddi_driver_name(rdip), ddi_get_instance(rdip));
8523341Sgc161489
8533341Sgc161489 /* return event cookie, iblock cookie, and level */
8543341Sgc161489 return (ndi_event_retrieve_cookie(usb_ia->ia_ndi_event_hdl,
8553341Sgc161489 rdip, eventname, cookie, NDI_EVENT_NOPASS));
8563341Sgc161489 }
8573341Sgc161489
8583341Sgc161489
8593341Sgc161489 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)8603341Sgc161489 usb_ia_busop_add_eventcall(dev_info_t *dip,
8613341Sgc161489 dev_info_t *rdip,
8623341Sgc161489 ddi_eventcookie_t cookie,
8633341Sgc161489 void (*callback)(dev_info_t *dip,
8643341Sgc161489 ddi_eventcookie_t cookie, void *arg,
8653341Sgc161489 void *bus_impldata),
8663341Sgc161489 void *arg, ddi_callback_id_t *cb_id)
8673341Sgc161489 {
8683341Sgc161489 int ifno;
8693341Sgc161489 usb_ia_t *usb_ia = usb_ia_obtain_state(dip);
8703341Sgc161489
8713341Sgc161489 mutex_enter(&usb_ia->ia_mutex);
8723341Sgc161489 ifno = usba_get_ifno(rdip)- usb_ia->ia_first_if;
8733341Sgc161489 mutex_exit(&usb_ia->ia_mutex);
8743341Sgc161489
8753341Sgc161489 if (ifno < 0) {
8763341Sgc161489 ifno = 0;
8773341Sgc161489 }
8783341Sgc161489
8793341Sgc161489 USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
8803341Sgc161489 "usb_ia_busop_add_eventcall: dip=0x%p, rdip=0x%p "
8813341Sgc161489 "cookie=0x%p, cb=0x%p, arg=0x%p",
8823341Sgc161489 (void *)dip, (void *)rdip, (void *)cookie, (void *)callback, arg);
8833341Sgc161489 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
8843341Sgc161489 "(dip=%s%d rdip=%s%d event=%s)",
8853341Sgc161489 ddi_driver_name(dip), ddi_get_instance(dip),
8863341Sgc161489 ddi_driver_name(rdip), ddi_get_instance(rdip),
8873341Sgc161489 ndi_event_cookie_to_name(usb_ia->ia_ndi_event_hdl, cookie));
8883341Sgc161489
8893341Sgc161489 /* Set flag on children registering events */
8903341Sgc161489 switch (ndi_event_cookie_to_tag(usb_ia->ia_ndi_event_hdl, cookie)) {
8913341Sgc161489 case USBA_EVENT_TAG_HOT_REMOVAL:
8923341Sgc161489 mutex_enter(&usb_ia->ia_mutex);
8933341Sgc161489 usb_ia->ia_child_events[ifno] |=
8943341Sgc161489 USB_IA_CHILD_EVENT_DISCONNECT;
8953341Sgc161489 mutex_exit(&usb_ia->ia_mutex);
8963341Sgc161489
8973341Sgc161489 break;
8983341Sgc161489 case USBA_EVENT_TAG_PRE_SUSPEND:
8993341Sgc161489 mutex_enter(&usb_ia->ia_mutex);
9003341Sgc161489 usb_ia->ia_child_events[ifno] |=
9013341Sgc161489 USB_IA_CHILD_EVENT_PRESUSPEND;
9023341Sgc161489 mutex_exit(&usb_ia->ia_mutex);
9033341Sgc161489
9043341Sgc161489 break;
9053341Sgc161489 default:
9063341Sgc161489
9073341Sgc161489 break;
9083341Sgc161489 }
9093341Sgc161489 /* add callback (perform registration) */
9103341Sgc161489 return (ndi_event_add_callback(usb_ia->ia_ndi_event_hdl,
9113341Sgc161489 rdip, cookie, callback, arg, NDI_SLEEP, cb_id));
9123341Sgc161489 }
9133341Sgc161489
9143341Sgc161489
9153341Sgc161489 static int
usb_ia_busop_remove_eventcall(dev_info_t * dip,ddi_callback_id_t cb_id)9163341Sgc161489 usb_ia_busop_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id)
9173341Sgc161489 {
9183341Sgc161489 usb_ia_t *usb_ia = usb_ia_obtain_state(dip);
9193341Sgc161489 ndi_event_callbacks_t *cb = (ndi_event_callbacks_t *)cb_id;
9203341Sgc161489
9213341Sgc161489 ASSERT(cb);
9223341Sgc161489
9233341Sgc161489 USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
9243341Sgc161489 "usb_ia_busop_remove_eventcall: dip=0x%p, rdip=0x%p "
9256898Sfb209375 "cookie=0x%p", (void *)dip, (void *)cb->ndi_evtcb_dip,
9266898Sfb209375 (void *)cb->ndi_evtcb_cookie);
9273341Sgc161489 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
9283341Sgc161489 "(dip=%s%d rdip=%s%d event=%s)",
9293341Sgc161489 ddi_driver_name(dip), ddi_get_instance(dip),
9303341Sgc161489 ddi_driver_name(cb->ndi_evtcb_dip),
9313341Sgc161489 ddi_get_instance(cb->ndi_evtcb_dip),
9323341Sgc161489 ndi_event_cookie_to_name(usb_ia->ia_ndi_event_hdl,
9333341Sgc161489 cb->ndi_evtcb_cookie));
9343341Sgc161489
9353341Sgc161489 /* remove event registration from our event set */
9363341Sgc161489 return (ndi_event_remove_callback(usb_ia->ia_ndi_event_hdl, cb_id));
9373341Sgc161489 }
9383341Sgc161489
9393341Sgc161489
9403341Sgc161489 static int
usb_ia_busop_post_event(dev_info_t * dip,dev_info_t * rdip,ddi_eventcookie_t cookie,void * bus_impldata)9413341Sgc161489 usb_ia_busop_post_event(dev_info_t *dip,
9423341Sgc161489 dev_info_t *rdip,
9433341Sgc161489 ddi_eventcookie_t cookie,
9443341Sgc161489 void *bus_impldata)
9453341Sgc161489 {
9463341Sgc161489 usb_ia_t *usb_ia = usb_ia_obtain_state(dip);
9473341Sgc161489
9483341Sgc161489 USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
9493341Sgc161489 "usb_ia_busop_post_event: dip=0x%p, rdip=0x%p "
9503341Sgc161489 "cookie=0x%p, impl=0x%p",
9513341Sgc161489 (void *)dip, (void *)rdip, (void *)cookie, bus_impldata);
9523341Sgc161489 USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
9533341Sgc161489 "(dip=%s%d rdip=%s%d event=%s)",
9543341Sgc161489 ddi_driver_name(dip), ddi_get_instance(dip),
9553341Sgc161489 ddi_driver_name(rdip), ddi_get_instance(rdip),
9563341Sgc161489 ndi_event_cookie_to_name(usb_ia->ia_ndi_event_hdl, cookie));
9573341Sgc161489
9583341Sgc161489 /* post event to all children registered for this event */
9593341Sgc161489 return (ndi_event_run_callbacks(usb_ia->ia_ndi_event_hdl, rdip,
9603341Sgc161489 cookie, bus_impldata));
9613341Sgc161489 }
9623341Sgc161489
9633341Sgc161489
9643341Sgc161489 /*
9653341Sgc161489 * usb_ia_restore_device_state
9663341Sgc161489 * set the original configuration of the device
9673341Sgc161489 */
9683341Sgc161489 static int
usb_ia_restore_device_state(dev_info_t * dip,usb_ia_t * usb_ia)9693341Sgc161489 usb_ia_restore_device_state(dev_info_t *dip, usb_ia_t *usb_ia)
9703341Sgc161489 {
9713341Sgc161489 usb_common_power_t *iapm;
9723341Sgc161489
9733341Sgc161489 USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
9746898Sfb209375 "usb_ia_restore_device_state: usb_ia = %p", (void *)usb_ia);
9753341Sgc161489
9763341Sgc161489 mutex_enter(&usb_ia->ia_mutex);
9773341Sgc161489 iapm = usb_ia->ia_pm;
9783341Sgc161489 mutex_exit(&usb_ia->ia_mutex);
9793341Sgc161489
9803341Sgc161489 /* First bring the device to full power */
9813341Sgc161489 (void) pm_busy_component(dip, 0);
9823341Sgc161489 (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
9833341Sgc161489
9843341Sgc161489 if (usb_check_same_device(dip, usb_ia->ia_log_handle, USB_LOG_L0,
9853341Sgc161489 DPRINT_MASK_EVENTS, USB_CHK_VIDPID, NULL) != USB_SUCCESS) {
9863341Sgc161489
9873341Sgc161489 /* change the device state from suspended to disconnected */
9883341Sgc161489 mutex_enter(&usb_ia->ia_mutex);
9893341Sgc161489 usb_ia->ia_dev_state = USB_DEV_DISCONNECTED;
9903341Sgc161489 mutex_exit(&usb_ia->ia_mutex);
9913341Sgc161489 (void) pm_idle_component(dip, 0);
9923341Sgc161489
9933341Sgc161489 return (USB_FAILURE);
9943341Sgc161489 }
9953341Sgc161489
9963341Sgc161489 /*
9973341Sgc161489 * if the device had remote wakeup earlier,
9983341Sgc161489 * enable it again
9993341Sgc161489 */
10003341Sgc161489 if (iapm->uc_wakeup_enabled) {
10013341Sgc161489 (void) usb_handle_remote_wakeup(usb_ia->ia_dip,
10023341Sgc161489 USB_REMOTE_WAKEUP_ENABLE);
10033341Sgc161489 }
10043341Sgc161489
10053341Sgc161489 mutex_enter(&usb_ia->ia_mutex);
10063341Sgc161489 usb_ia->ia_dev_state = USB_DEV_ONLINE;
10073341Sgc161489 mutex_exit(&usb_ia->ia_mutex);
10083341Sgc161489
10093341Sgc161489 (void) pm_idle_component(dip, 0);
10103341Sgc161489
10113341Sgc161489 return (USB_SUCCESS);
10123341Sgc161489 }
10133341Sgc161489
10143341Sgc161489
10153341Sgc161489 /*
10163341Sgc161489 * usb_ia_event_cb()
10173341Sgc161489 * handle disconnect and connect events
10183341Sgc161489 */
10193341Sgc161489 static void
usb_ia_event_cb(dev_info_t * dip,ddi_eventcookie_t cookie,void * arg,void * bus_impldata)10203341Sgc161489 usb_ia_event_cb(dev_info_t *dip, ddi_eventcookie_t cookie,
10213341Sgc161489 void *arg, void *bus_impldata)
10223341Sgc161489 {
10233341Sgc161489 int i, tag;
10243341Sgc161489 usb_ia_t *usb_ia = usb_ia_obtain_state(dip);
10253341Sgc161489 dev_info_t *child_dip;
10263341Sgc161489 ddi_eventcookie_t rm_cookie, ins_cookie, suspend_cookie, resume_cookie;
10273341Sgc161489
10283341Sgc161489 USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
10293341Sgc161489 "usb_ia_event_cb: dip=0x%p, cookie=0x%p, "
10303341Sgc161489 "arg=0x%p, impl=0x%p",
10313341Sgc161489 (void *)dip, (void *)cookie, arg, bus_impldata);
10323341Sgc161489 USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
10333341Sgc161489 "(dip=%s%d event=%s)",
10343341Sgc161489 ddi_driver_name(dip), ddi_get_instance(dip),
10353341Sgc161489 ndi_event_cookie_to_name(usb_ia->ia_ndi_event_hdl, cookie));
10363341Sgc161489
10373341Sgc161489 tag = NDI_EVENT_TAG(cookie);
10383341Sgc161489 rm_cookie = ndi_event_tag_to_cookie(
10393341Sgc161489 usb_ia->ia_ndi_event_hdl, USBA_EVENT_TAG_HOT_REMOVAL);
10403341Sgc161489 suspend_cookie = ndi_event_tag_to_cookie(
10413341Sgc161489 usb_ia->ia_ndi_event_hdl, USBA_EVENT_TAG_PRE_SUSPEND);
10423341Sgc161489 ins_cookie = ndi_event_tag_to_cookie(
10433341Sgc161489 usb_ia->ia_ndi_event_hdl, USBA_EVENT_TAG_HOT_INSERTION);
10443341Sgc161489 resume_cookie = ndi_event_tag_to_cookie(
10453341Sgc161489 usb_ia->ia_ndi_event_hdl, USBA_EVENT_TAG_POST_RESUME);
10463341Sgc161489
10473341Sgc161489 mutex_enter(&usb_ia->ia_mutex);
10483341Sgc161489 switch (tag) {
10493341Sgc161489 case USBA_EVENT_TAG_HOT_REMOVAL:
10503341Sgc161489 if (usb_ia->ia_dev_state == USB_DEV_DISCONNECTED) {
10513341Sgc161489 USB_DPRINTF_L2(DPRINT_MASK_EVENTS,
10523341Sgc161489 usb_ia->ia_log_handle,
10533341Sgc161489 "usb_ia_event_cb: Device already disconnected");
10543341Sgc161489 } else {
10553341Sgc161489 /* we are disconnected so set our state now */
10563341Sgc161489 usb_ia->ia_dev_state = USB_DEV_DISCONNECTED;
10573341Sgc161489 for (i = 0; i < usb_ia->ia_n_ifs; i++) {
10583341Sgc161489 usb_ia->ia_child_events[i] &= ~
10593341Sgc161489 USB_IA_CHILD_EVENT_DISCONNECT;
10603341Sgc161489 }
10613341Sgc161489 mutex_exit(&usb_ia->ia_mutex);
10623341Sgc161489
10633341Sgc161489 /* pass disconnect event to all the children */
10643341Sgc161489 (void) ndi_event_run_callbacks(
10653341Sgc161489 usb_ia->ia_ndi_event_hdl, NULL,
10663341Sgc161489 rm_cookie, bus_impldata);
10673341Sgc161489
10683341Sgc161489 mutex_enter(&usb_ia->ia_mutex);
10693341Sgc161489 }
10703341Sgc161489 break;
10713341Sgc161489 case USBA_EVENT_TAG_PRE_SUSPEND:
10723341Sgc161489 /* set our state *after* suspending children */
10733341Sgc161489 mutex_exit(&usb_ia->ia_mutex);
10743341Sgc161489
10753341Sgc161489 /* pass pre_suspend event to all the children */
10763341Sgc161489 (void) ndi_event_run_callbacks(usb_ia->ia_ndi_event_hdl,
10773341Sgc161489 NULL, suspend_cookie, bus_impldata);
10783341Sgc161489
10793341Sgc161489 mutex_enter(&usb_ia->ia_mutex);
10803341Sgc161489 for (i = 0; i < usb_ia->ia_n_ifs; i++) {
10813341Sgc161489 usb_ia->ia_child_events[i] &= ~
10823341Sgc161489 USB_IA_CHILD_EVENT_PRESUSPEND;
10833341Sgc161489 }
10843341Sgc161489 break;
10853341Sgc161489 case USBA_EVENT_TAG_HOT_INSERTION:
10863341Sgc161489 mutex_exit(&usb_ia->ia_mutex);
10873341Sgc161489 if (usb_ia_restore_device_state(dip, usb_ia) == USB_SUCCESS) {
10883341Sgc161489
10893341Sgc161489 /*
10903341Sgc161489 * Check to see if this child has missed the disconnect
10913341Sgc161489 * event before it registered for event cb
10923341Sgc161489 */
10933341Sgc161489 mutex_enter(&usb_ia->ia_mutex);
10943341Sgc161489 for (i = 0; i < usb_ia->ia_n_ifs; i++) {
10953341Sgc161489 if (usb_ia->ia_child_events[i] &
10963341Sgc161489 USB_IA_CHILD_EVENT_DISCONNECT) {
10973341Sgc161489 usb_ia->ia_child_events[i] &=
10983341Sgc161489 ~USB_IA_CHILD_EVENT_DISCONNECT;
10993341Sgc161489 child_dip =
11003341Sgc161489 usb_ia->ia_children_dips[i];
11013341Sgc161489 mutex_exit(&usb_ia->ia_mutex);
11023341Sgc161489
11033341Sgc161489 /* post the missed disconnect */
11043341Sgc161489 (void) ndi_event_do_callback(
11053341Sgc161489 usb_ia->ia_ndi_event_hdl,
11063341Sgc161489 child_dip,
11073341Sgc161489 rm_cookie,
11083341Sgc161489 bus_impldata);
11093341Sgc161489 mutex_enter(&usb_ia->ia_mutex);
11103341Sgc161489 }
11113341Sgc161489 }
11123341Sgc161489 mutex_exit(&usb_ia->ia_mutex);
11133341Sgc161489
11143341Sgc161489 /* pass reconnect event to all the children */
11153341Sgc161489 (void) ndi_event_run_callbacks(
11163341Sgc161489 usb_ia->ia_ndi_event_hdl, NULL,
11173341Sgc161489 ins_cookie, bus_impldata);
11183341Sgc161489
11193341Sgc161489 }
11203341Sgc161489 mutex_enter(&usb_ia->ia_mutex);
11213341Sgc161489 break;
11223341Sgc161489 case USBA_EVENT_TAG_POST_RESUME:
11233341Sgc161489 /*
11243341Sgc161489 * Check to see if this child has missed the pre-suspend
11253341Sgc161489 * event before it registered for event cb
11263341Sgc161489 */
11273341Sgc161489 for (i = 0; i < usb_ia->ia_n_ifs; i++) {
11283341Sgc161489 if (usb_ia->ia_child_events[i] &
11293341Sgc161489 USB_IA_CHILD_EVENT_PRESUSPEND) {
11303341Sgc161489 usb_ia->ia_child_events[i] &=
11313341Sgc161489 ~USB_IA_CHILD_EVENT_PRESUSPEND;
11323341Sgc161489 child_dip = usb_ia->ia_children_dips[i];
11333341Sgc161489 mutex_exit(&usb_ia->ia_mutex);
11343341Sgc161489
11353341Sgc161489 /* post the missed pre-suspend event */
11363341Sgc161489 (void) ndi_event_do_callback(
11373341Sgc161489 usb_ia->ia_ndi_event_hdl,
11383341Sgc161489 child_dip, suspend_cookie,
11393341Sgc161489 bus_impldata);
11403341Sgc161489 mutex_enter(&usb_ia->ia_mutex);
11413341Sgc161489 }
11423341Sgc161489 }
11433341Sgc161489 mutex_exit(&usb_ia->ia_mutex);
11443341Sgc161489
11453341Sgc161489 /* pass post_resume event to all the children */
11463341Sgc161489 (void) ndi_event_run_callbacks(usb_ia->ia_ndi_event_hdl,
11473341Sgc161489 NULL, resume_cookie, bus_impldata);
11483341Sgc161489
11493341Sgc161489 mutex_enter(&usb_ia->ia_mutex);
11503341Sgc161489 break;
11513341Sgc161489 }
11523341Sgc161489 mutex_exit(&usb_ia->ia_mutex);
11533341Sgc161489
11543341Sgc161489 }
11553341Sgc161489
11563341Sgc161489 /*
11573341Sgc161489 * create the pm components required for power management
11583341Sgc161489 */
11593341Sgc161489 static void
usb_ia_create_pm_components(dev_info_t * dip,usb_ia_t * usb_ia)11603341Sgc161489 usb_ia_create_pm_components(dev_info_t *dip, usb_ia_t *usb_ia)
11613341Sgc161489 {
11623341Sgc161489 usb_common_power_t *iapm;
11633341Sgc161489 uint_t pwr_states;
11643341Sgc161489
11653341Sgc161489 USB_DPRINTF_L4(DPRINT_MASK_PM, usb_ia->ia_log_handle,
11663341Sgc161489 "usb_ia_create_pm_components: Begin");
11673341Sgc161489
11683341Sgc161489 /* Allocate the PM state structure */
11693341Sgc161489 iapm = kmem_zalloc(sizeof (usb_common_power_t), KM_SLEEP);
11703341Sgc161489
11713341Sgc161489 mutex_enter(&usb_ia->ia_mutex);
11723341Sgc161489 usb_ia->ia_pm = iapm;
11733341Sgc161489 iapm->uc_usb_statep = usb_ia;
11743341Sgc161489 iapm->uc_pm_capabilities = 0; /* XXXX should this be 0?? */
11753341Sgc161489 iapm->uc_current_power = USB_DEV_OS_FULL_PWR;
11763341Sgc161489 mutex_exit(&usb_ia->ia_mutex);
11773341Sgc161489
11783341Sgc161489 /*
11793341Sgc161489 * By not enabling parental notification, PM enforces
11803341Sgc161489 * "strict parental dependency" meaning, usb_ia won't
11813341Sgc161489 * power off until any of its children are in full power.
11823341Sgc161489 */
11833341Sgc161489
11843341Sgc161489 /*
11853341Sgc161489 * there are 3 scenarios:
11863341Sgc161489 * 1. a well behaved device should have remote wakeup
11873341Sgc161489 * at interface and device level. If the interface
11883341Sgc161489 * wakes up, usb_ia will wake up
11893341Sgc161489 * 2. if the device doesn't have remote wake up and
11903341Sgc161489 * the interface has, PM will still work, ie.
11913341Sgc161489 * the interfaces wakes up and usb_ia wakes up
11923341Sgc161489 * 3. if neither the interface nor device has remote
11933341Sgc161489 * wakeup, the interface will wake up when it is opened
11943341Sgc161489 * and goes to sleep after being closed for a while
11953341Sgc161489 * In this case usb_ia should also go to sleep shortly
11963341Sgc161489 * thereafter
11973341Sgc161489 * In all scenarios it doesn't really matter whether
11983341Sgc161489 * remote wakeup at the device level is enabled or not
11993341Sgc161489 * but we do it anyways
12003341Sgc161489 */
12013341Sgc161489 if (usb_handle_remote_wakeup(dip, USB_REMOTE_WAKEUP_ENABLE) ==
12023341Sgc161489 USB_SUCCESS) {
12033341Sgc161489 USB_DPRINTF_L3(DPRINT_MASK_PM, usb_ia->ia_log_handle,
12043341Sgc161489 "usb_ia_create_pm_components: "
12053341Sgc161489 "Remote Wakeup Enabled");
12063341Sgc161489 iapm->uc_wakeup_enabled = 1;
12073341Sgc161489 }
12083341Sgc161489
12093341Sgc161489 if (usb_create_pm_components(dip, &pwr_states) ==
12103341Sgc161489 USB_SUCCESS) {
12113341Sgc161489 iapm->uc_pwr_states = (uint8_t)pwr_states;
12123341Sgc161489 (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
12133341Sgc161489 }
12143341Sgc161489
12153341Sgc161489 USB_DPRINTF_L4(DPRINT_MASK_PM, usb_ia->ia_log_handle,
12163341Sgc161489 "usb_ia_create_pm_components: End");
12173341Sgc161489 }
12183341Sgc161489
12193341Sgc161489
12203341Sgc161489 /*
12213341Sgc161489 * usb_ia_obtain_state:
12223341Sgc161489 */
12233341Sgc161489 static usb_ia_t *
usb_ia_obtain_state(dev_info_t * dip)12243341Sgc161489 usb_ia_obtain_state(dev_info_t *dip)
12253341Sgc161489 {
12263341Sgc161489 int instance = ddi_get_instance(dip);
12273341Sgc161489 usb_ia_t *statep = ddi_get_soft_state(usb_ia_statep, instance);
12283341Sgc161489
12293341Sgc161489 ASSERT(statep != NULL);
12303341Sgc161489
12313341Sgc161489 return (statep);
12323341Sgc161489 }
1233