1*d73ae94eSgc /*
2*d73ae94eSgc  * CDDL HEADER START
3*d73ae94eSgc  *
4*d73ae94eSgc  * The contents of this file are subject to the terms of the
5*d73ae94eSgc  * Common Development and Distribution License (the "License").
6*d73ae94eSgc  * You may not use this file except in compliance with the License.
7*d73ae94eSgc  *
8*d73ae94eSgc  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*d73ae94eSgc  * or http://www.opensolaris.org/os/licensing.
10*d73ae94eSgc  * See the License for the specific language governing permissions
11*d73ae94eSgc  * and limitations under the License.
12*d73ae94eSgc  *
13*d73ae94eSgc  * When distributing Covered Code, include this CDDL HEADER in each
14*d73ae94eSgc  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*d73ae94eSgc  * If applicable, add the following below this CDDL HEADER, with the
16*d73ae94eSgc  * fields enclosed by brackets "[]" replaced with your own identifying
17*d73ae94eSgc  * information: Portions Copyright [yyyy] [name of copyright owner]
18*d73ae94eSgc  *
19*d73ae94eSgc  * CDDL HEADER END
20*d73ae94eSgc  */
21*d73ae94eSgc /*
22*d73ae94eSgc  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23*d73ae94eSgc  * Use is subject to license terms.
24*d73ae94eSgc  */
25*d73ae94eSgc 
26*d73ae94eSgc #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*d73ae94eSgc 
28*d73ae94eSgc /*
29*d73ae94eSgc  * usb interface association driver
30*d73ae94eSgc  *
31*d73ae94eSgc  *	this driver attempts to the interface association node and
32*d73ae94eSgc  *	creates/manages child nodes for the included interfaces.
33*d73ae94eSgc  */
34*d73ae94eSgc 
35*d73ae94eSgc #if defined(lint) && !defined(DEBUG)
36*d73ae94eSgc #define	DEBUG	1
37*d73ae94eSgc #endif
38*d73ae94eSgc #include <sys/usb/usba/usbai_version.h>
39*d73ae94eSgc #include <sys/usb/usba.h>
40*d73ae94eSgc #include <sys/usb/usba/usba_types.h>
41*d73ae94eSgc #include <sys/usb/usba/usba_impl.h>
42*d73ae94eSgc #include <sys/usb/usb_ia/usb_iavar.h>
43*d73ae94eSgc 
44*d73ae94eSgc /* Debugging support */
45*d73ae94eSgc uint_t usb_ia_errlevel = USB_LOG_L4;
46*d73ae94eSgc uint_t usb_ia_errmask = (uint_t)DPRINT_MASK_ALL;
47*d73ae94eSgc uint_t usb_ia_instance_debug = (uint_t)-1;
48*d73ae94eSgc uint_t usb_ia_bus_config_debug = 0;
49*d73ae94eSgc 
50*d73ae94eSgc _NOTE(DATA_READABLE_WITHOUT_LOCK(usb_ia_errlevel))
51*d73ae94eSgc _NOTE(DATA_READABLE_WITHOUT_LOCK(usb_ia_errmask))
52*d73ae94eSgc _NOTE(DATA_READABLE_WITHOUT_LOCK(usb_ia_instance_debug))
53*d73ae94eSgc 
54*d73ae94eSgc _NOTE(SCHEME_PROTECTS_DATA("unique", msgb))
55*d73ae94eSgc _NOTE(SCHEME_PROTECTS_DATA("unique", dev_info))
56*d73ae94eSgc _NOTE(SCHEME_PROTECTS_DATA("unique", usb_pipe_policy))
57*d73ae94eSgc 
58*d73ae94eSgc static struct cb_ops usb_ia_cb_ops = {
59*d73ae94eSgc 	nodev,		/* open */
60*d73ae94eSgc 	nodev,		/* close */
61*d73ae94eSgc 	nodev,		/* strategy */
62*d73ae94eSgc 	nodev,		/* print */
63*d73ae94eSgc 	nodev,		/* dump */
64*d73ae94eSgc 	nodev,		/* read */
65*d73ae94eSgc 	nodev,		/* write */
66*d73ae94eSgc 	nodev,		/* ioctl */
67*d73ae94eSgc 	nodev,		/* devmap */
68*d73ae94eSgc 	nodev,		/* mmap */
69*d73ae94eSgc 	nodev,		/* segmap */
70*d73ae94eSgc 	nochpoll,	/* poll */
71*d73ae94eSgc 	ddi_prop_op,	/* prop_op */
72*d73ae94eSgc 	NULL,		/* aread */
73*d73ae94eSgc 	D_MP
74*d73ae94eSgc };
75*d73ae94eSgc 
76*d73ae94eSgc static int usb_ia_busop_get_eventcookie(dev_info_t *dip,
77*d73ae94eSgc 			dev_info_t *rdip,
78*d73ae94eSgc 			char *eventname,
79*d73ae94eSgc 			ddi_eventcookie_t *cookie);
80*d73ae94eSgc static int usb_ia_busop_add_eventcall(dev_info_t *dip,
81*d73ae94eSgc 			dev_info_t *rdip,
82*d73ae94eSgc 			ddi_eventcookie_t cookie,
83*d73ae94eSgc 			void (*callback)(dev_info_t *dip,
84*d73ae94eSgc 				ddi_eventcookie_t cookie, void *arg,
85*d73ae94eSgc 				void *bus_impldata),
86*d73ae94eSgc 			void *arg, ddi_callback_id_t *cb_id);
87*d73ae94eSgc static int usb_ia_busop_remove_eventcall(dev_info_t *dip,
88*d73ae94eSgc 			ddi_callback_id_t cb_id);
89*d73ae94eSgc static int usb_ia_busop_post_event(dev_info_t *dip,
90*d73ae94eSgc 			dev_info_t *rdip,
91*d73ae94eSgc 			ddi_eventcookie_t cookie,
92*d73ae94eSgc 			void *bus_impldata);
93*d73ae94eSgc static int usb_ia_bus_config(dev_info_t *dip,
94*d73ae94eSgc 			uint_t flag,
95*d73ae94eSgc 			ddi_bus_config_op_t op,
96*d73ae94eSgc 			void *arg,
97*d73ae94eSgc 			dev_info_t **child);
98*d73ae94eSgc static int usb_ia_bus_unconfig(dev_info_t *dip,
99*d73ae94eSgc 			uint_t flag,
100*d73ae94eSgc 			ddi_bus_config_op_t op,
101*d73ae94eSgc 			void *arg);
102*d73ae94eSgc 
103*d73ae94eSgc /*
104*d73ae94eSgc  * autoconfiguration data and routines.
105*d73ae94eSgc  */
106*d73ae94eSgc static int	usb_ia_info(dev_info_t *, ddi_info_cmd_t,
107*d73ae94eSgc 				void *, void **);
108*d73ae94eSgc static int	usb_ia_attach(dev_info_t *, ddi_attach_cmd_t);
109*d73ae94eSgc static int	usb_ia_detach(dev_info_t *, ddi_detach_cmd_t);
110*d73ae94eSgc 
111*d73ae94eSgc /* other routines */
112*d73ae94eSgc static void usb_ia_create_pm_components(dev_info_t *, usb_ia_t *);
113*d73ae94eSgc static int usb_ia_bus_ctl(dev_info_t *, dev_info_t	*,
114*d73ae94eSgc 				ddi_ctl_enum_t, void *, void *);
115*d73ae94eSgc static int usb_ia_power(dev_info_t *, int, int);
116*d73ae94eSgc static int usb_ia_restore_device_state(dev_info_t *, usb_ia_t *);
117*d73ae94eSgc static usb_ia_t  *usb_ia_obtain_state(dev_info_t *);
118*d73ae94eSgc static void usb_ia_event_cb(dev_info_t *, ddi_eventcookie_t, void *, void *);
119*d73ae94eSgc 
120*d73ae94eSgc /* prototypes */
121*d73ae94eSgc static void usb_ia_create_children(usb_ia_t *);
122*d73ae94eSgc static int usb_ia_cleanup(usb_ia_t *);
123*d73ae94eSgc 
124*d73ae94eSgc /*
125*d73ae94eSgc  * Busops vector
126*d73ae94eSgc  */
127*d73ae94eSgc static struct bus_ops usb_ia_busops = {
128*d73ae94eSgc 	BUSO_REV,
129*d73ae94eSgc 	nullbusmap,			/* bus_map */
130*d73ae94eSgc 	NULL,				/* bus_get_intrspec */
131*d73ae94eSgc 	NULL,				/* bus_add_intrspec */
132*d73ae94eSgc 	NULL,				/* bus_remove_intrspec */
133*d73ae94eSgc 	NULL,				/* XXXX bus_map_fault */
134*d73ae94eSgc 	ddi_dma_map,			/* bus_dma_map */
135*d73ae94eSgc 	ddi_dma_allochdl,
136*d73ae94eSgc 	ddi_dma_freehdl,
137*d73ae94eSgc 	ddi_dma_bindhdl,
138*d73ae94eSgc 	ddi_dma_unbindhdl,
139*d73ae94eSgc 	ddi_dma_flush,
140*d73ae94eSgc 	ddi_dma_win,
141*d73ae94eSgc 	ddi_dma_mctl,			/* bus_dma_ctl */
142*d73ae94eSgc 	usb_ia_bus_ctl,		/* bus_ctl */
143*d73ae94eSgc 	ddi_bus_prop_op,		/* bus_prop_op */
144*d73ae94eSgc 	usb_ia_busop_get_eventcookie,
145*d73ae94eSgc 	usb_ia_busop_add_eventcall,
146*d73ae94eSgc 	usb_ia_busop_remove_eventcall,
147*d73ae94eSgc 	usb_ia_busop_post_event,	/* bus_post_event */
148*d73ae94eSgc 	NULL,				/* bus_intr_ctl */
149*d73ae94eSgc 	usb_ia_bus_config,		/* bus_config */
150*d73ae94eSgc 	usb_ia_bus_unconfig,		/* bus_unconfig */
151*d73ae94eSgc 	NULL,				/* bus_fm_init */
152*d73ae94eSgc 	NULL,				/* bus_fm_fini */
153*d73ae94eSgc 	NULL,				/* bus_fm_access_enter */
154*d73ae94eSgc 	NULL,				/* bus_fm_access_exit */
155*d73ae94eSgc 	NULL				/* bus_power */
156*d73ae94eSgc };
157*d73ae94eSgc 
158*d73ae94eSgc 
159*d73ae94eSgc static struct dev_ops usb_ia_ops = {
160*d73ae94eSgc 	DEVO_REV,		/* devo_rev, */
161*d73ae94eSgc 	0,			/* refcnt  */
162*d73ae94eSgc 	usb_ia_info,		/* info */
163*d73ae94eSgc 	nulldev,		/* identify */
164*d73ae94eSgc 	nulldev,		/* probe */
165*d73ae94eSgc 	usb_ia_attach,		/* attach */
166*d73ae94eSgc 	usb_ia_detach,		/* detach */
167*d73ae94eSgc 	nodev,			/* reset */
168*d73ae94eSgc 	&usb_ia_cb_ops,	/* driver operations */
169*d73ae94eSgc 	&usb_ia_busops,	/* bus operations */
170*d73ae94eSgc 	usb_ia_power		/* power */
171*d73ae94eSgc };
172*d73ae94eSgc 
173*d73ae94eSgc static struct modldrv modldrv = {
174*d73ae94eSgc 	&mod_driverops, /* Type of module. This one is a driver */
175*d73ae94eSgc 	"USB Interface Association Driver %I%", /* Name of the module. */
176*d73ae94eSgc 	&usb_ia_ops,	/* driver ops */
177*d73ae94eSgc };
178*d73ae94eSgc 
179*d73ae94eSgc static struct modlinkage modlinkage = {
180*d73ae94eSgc 	MODREV_1, (void *)&modldrv, NULL
181*d73ae94eSgc };
182*d73ae94eSgc 
183*d73ae94eSgc #define	USB_IA_INITIAL_SOFT_SPACE 4
184*d73ae94eSgc static	void	*usb_ia_statep;
185*d73ae94eSgc 
186*d73ae94eSgc /*
187*d73ae94eSgc  * event definition
188*d73ae94eSgc  */
189*d73ae94eSgc static ndi_event_definition_t usb_ia_ndi_event_defs[] = {
190*d73ae94eSgc 	{USBA_EVENT_TAG_HOT_REMOVAL, DDI_DEVI_REMOVE_EVENT, EPL_KERNEL,
191*d73ae94eSgc 						NDI_EVENT_POST_TO_ALL},
192*d73ae94eSgc 	{USBA_EVENT_TAG_HOT_INSERTION, DDI_DEVI_INSERT_EVENT, EPL_KERNEL,
193*d73ae94eSgc 						NDI_EVENT_POST_TO_ALL},
194*d73ae94eSgc 	{USBA_EVENT_TAG_POST_RESUME, USBA_POST_RESUME_EVENT, EPL_KERNEL,
195*d73ae94eSgc 						NDI_EVENT_POST_TO_ALL},
196*d73ae94eSgc 	{USBA_EVENT_TAG_PRE_SUSPEND, USBA_PRE_SUSPEND_EVENT, EPL_KERNEL,
197*d73ae94eSgc 						NDI_EVENT_POST_TO_ALL}
198*d73ae94eSgc };
199*d73ae94eSgc 
200*d73ae94eSgc #define	USB_IA_N_NDI_EVENTS \
201*d73ae94eSgc 	(sizeof (usb_ia_ndi_event_defs) / sizeof (ndi_event_definition_t))
202*d73ae94eSgc 
203*d73ae94eSgc static	ndi_event_set_t usb_ia_ndi_events = {
204*d73ae94eSgc 	NDI_EVENTS_REV1, USB_IA_N_NDI_EVENTS, usb_ia_ndi_event_defs};
205*d73ae94eSgc 
206*d73ae94eSgc 
207*d73ae94eSgc /*
208*d73ae94eSgc  * standard driver entry points
209*d73ae94eSgc  */
210*d73ae94eSgc int
211*d73ae94eSgc _init(void)
212*d73ae94eSgc {
213*d73ae94eSgc 	int rval;
214*d73ae94eSgc 
215*d73ae94eSgc 	rval = ddi_soft_state_init(&usb_ia_statep, sizeof (struct usb_ia),
216*d73ae94eSgc 	    USB_IA_INITIAL_SOFT_SPACE);
217*d73ae94eSgc 	if (rval != 0) {
218*d73ae94eSgc 		return (rval);
219*d73ae94eSgc 	}
220*d73ae94eSgc 
221*d73ae94eSgc 	if ((rval = mod_install(&modlinkage)) != 0) {
222*d73ae94eSgc 		ddi_soft_state_fini(&usb_ia_statep);
223*d73ae94eSgc 		return (rval);
224*d73ae94eSgc 	}
225*d73ae94eSgc 
226*d73ae94eSgc 	return (rval);
227*d73ae94eSgc }
228*d73ae94eSgc 
229*d73ae94eSgc 
230*d73ae94eSgc int
231*d73ae94eSgc _fini(void)
232*d73ae94eSgc {
233*d73ae94eSgc 	int	rval;
234*d73ae94eSgc 
235*d73ae94eSgc 	rval = mod_remove(&modlinkage);
236*d73ae94eSgc 
237*d73ae94eSgc 	if (rval) {
238*d73ae94eSgc 		return (rval);
239*d73ae94eSgc 	}
240*d73ae94eSgc 
241*d73ae94eSgc 	ddi_soft_state_fini(&usb_ia_statep);
242*d73ae94eSgc 
243*d73ae94eSgc 	return (rval);
244*d73ae94eSgc }
245*d73ae94eSgc 
246*d73ae94eSgc 
247*d73ae94eSgc int
248*d73ae94eSgc _info(struct modinfo *modinfop)
249*d73ae94eSgc {
250*d73ae94eSgc 	return (mod_info(&modlinkage, modinfop));
251*d73ae94eSgc }
252*d73ae94eSgc 
253*d73ae94eSgc 
254*d73ae94eSgc /*ARGSUSED*/
255*d73ae94eSgc static int
256*d73ae94eSgc usb_ia_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
257*d73ae94eSgc {
258*d73ae94eSgc 	usb_ia_t	*usb_ia;
259*d73ae94eSgc 	int		instance = getminor((dev_t)arg);
260*d73ae94eSgc 	int		error = DDI_FAILURE;
261*d73ae94eSgc 
262*d73ae94eSgc 	switch (infocmd) {
263*d73ae94eSgc 	case DDI_INFO_DEVT2DEVINFO:
264*d73ae94eSgc 		if ((usb_ia = ddi_get_soft_state(usb_ia_statep,
265*d73ae94eSgc 		    instance)) != NULL) {
266*d73ae94eSgc 			*result = (void *)usb_ia->ia_dip;
267*d73ae94eSgc 			if (*result != NULL) {
268*d73ae94eSgc 				error = DDI_SUCCESS;
269*d73ae94eSgc 			}
270*d73ae94eSgc 		} else {
271*d73ae94eSgc 			*result = NULL;
272*d73ae94eSgc 		}
273*d73ae94eSgc 		break;
274*d73ae94eSgc 
275*d73ae94eSgc 	case DDI_INFO_DEVT2INSTANCE:
276*d73ae94eSgc 		*result = (void *)(intptr_t)instance;
277*d73ae94eSgc 		error = DDI_SUCCESS;
278*d73ae94eSgc 		break;
279*d73ae94eSgc 	default:
280*d73ae94eSgc 		break;
281*d73ae94eSgc 	}
282*d73ae94eSgc 
283*d73ae94eSgc 	return (error);
284*d73ae94eSgc }
285*d73ae94eSgc 
286*d73ae94eSgc 
287*d73ae94eSgc /*
288*d73ae94eSgc  * child  post attach/detach notification
289*d73ae94eSgc  */
290*d73ae94eSgc static void
291*d73ae94eSgc usb_ia_post_attach(usb_ia_t *usb_ia, uint8_t ifno, struct attachspec *as)
292*d73ae94eSgc {
293*d73ae94eSgc 	USB_DPRINTF_L4(DPRINT_MASK_PM, usb_ia->ia_log_handle,
294*d73ae94eSgc 	    "usb_ia_post_attach: ifno = %d result = %d", ifno, as->result);
295*d73ae94eSgc 
296*d73ae94eSgc }
297*d73ae94eSgc 
298*d73ae94eSgc 
299*d73ae94eSgc static void
300*d73ae94eSgc usb_ia_post_detach(usb_ia_t *usb_ia, uint8_t ifno, struct detachspec *ds)
301*d73ae94eSgc {
302*d73ae94eSgc 	USB_DPRINTF_L4(DPRINT_MASK_PM, usb_ia->ia_log_handle,
303*d73ae94eSgc 	    "usb_ia_post_detach: ifno = %d result = %d", ifno, ds->result);
304*d73ae94eSgc 
305*d73ae94eSgc }
306*d73ae94eSgc 
307*d73ae94eSgc 
308*d73ae94eSgc /*
309*d73ae94eSgc  * bus ctl support. we handle notifications here and the
310*d73ae94eSgc  * rest goes up to root hub/hcd
311*d73ae94eSgc  */
312*d73ae94eSgc /*ARGSUSED*/
313*d73ae94eSgc static int
314*d73ae94eSgc usb_ia_bus_ctl(dev_info_t *dip,
315*d73ae94eSgc 	dev_info_t	*rdip,
316*d73ae94eSgc 	ddi_ctl_enum_t	op,
317*d73ae94eSgc 	void		*arg,
318*d73ae94eSgc 	void		*result)
319*d73ae94eSgc {
320*d73ae94eSgc 	usba_device_t *hub_usba_device = usba_get_usba_device(rdip);
321*d73ae94eSgc 	dev_info_t *root_hub_dip = hub_usba_device->usb_root_hub_dip;
322*d73ae94eSgc 	usb_ia_t  *usb_ia;
323*d73ae94eSgc 	struct attachspec *as;
324*d73ae94eSgc 	struct detachspec *ds;
325*d73ae94eSgc 
326*d73ae94eSgc 	usb_ia = usb_ia_obtain_state(dip);
327*d73ae94eSgc 
328*d73ae94eSgc 	USB_DPRINTF_L4(DPRINT_MASK_PM, usb_ia->ia_log_handle,
329*d73ae94eSgc 	    "usb_ia_bus_ctl:\n\t"
330*d73ae94eSgc 	    "dip = 0x%p, rdip = 0x%p, op = 0x%x, arg = 0x%p",
331*d73ae94eSgc 	    dip, rdip, op, arg);
332*d73ae94eSgc 
333*d73ae94eSgc 	switch (op) {
334*d73ae94eSgc 	case DDI_CTLOPS_ATTACH:
335*d73ae94eSgc 		as = (struct attachspec *)arg;
336*d73ae94eSgc 
337*d73ae94eSgc 		switch (as->when) {
338*d73ae94eSgc 		case DDI_PRE :
339*d73ae94eSgc 			/* nothing to do basically */
340*d73ae94eSgc 			USB_DPRINTF_L2(DPRINT_MASK_PM, usb_ia->ia_log_handle,
341*d73ae94eSgc 			    "DDI_PRE DDI_CTLOPS_ATTACH");
342*d73ae94eSgc 			break;
343*d73ae94eSgc 		case DDI_POST :
344*d73ae94eSgc 			usb_ia_post_attach(usb_ia, usba_get_ifno(rdip),
345*d73ae94eSgc 			    (struct attachspec *)arg);
346*d73ae94eSgc 			break;
347*d73ae94eSgc 		}
348*d73ae94eSgc 
349*d73ae94eSgc 		break;
350*d73ae94eSgc 	case DDI_CTLOPS_DETACH:
351*d73ae94eSgc 		ds = (struct detachspec *)arg;
352*d73ae94eSgc 
353*d73ae94eSgc 		switch (ds->when) {
354*d73ae94eSgc 		case DDI_PRE :
355*d73ae94eSgc 			/* nothing to do basically */
356*d73ae94eSgc 			USB_DPRINTF_L2(DPRINT_MASK_PM, usb_ia->ia_log_handle,
357*d73ae94eSgc 			    "DDI_PRE DDI_CTLOPS_DETACH");
358*d73ae94eSgc 			break;
359*d73ae94eSgc 		case DDI_POST :
360*d73ae94eSgc 			usb_ia_post_detach(usb_ia, usba_get_ifno(rdip),
361*d73ae94eSgc 			    (struct detachspec *)arg);
362*d73ae94eSgc 			break;
363*d73ae94eSgc 		}
364*d73ae94eSgc 
365*d73ae94eSgc 		break;
366*d73ae94eSgc 	default:
367*d73ae94eSgc 		/* pass to root hub to handle */
368*d73ae94eSgc 		return (usba_bus_ctl(root_hub_dip, rdip, op, arg, result));
369*d73ae94eSgc 	}
370*d73ae94eSgc 
371*d73ae94eSgc 	return (DDI_SUCCESS);
372*d73ae94eSgc }
373*d73ae94eSgc 
374*d73ae94eSgc 
375*d73ae94eSgc /*
376*d73ae94eSgc  * bus enumeration entry points
377*d73ae94eSgc  */
378*d73ae94eSgc static int
379*d73ae94eSgc usb_ia_bus_config(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
380*d73ae94eSgc     void *arg, dev_info_t **child)
381*d73ae94eSgc {
382*d73ae94eSgc 	int		rval, circ;
383*d73ae94eSgc 	usb_ia_t	*usb_ia = usb_ia_obtain_state(dip);
384*d73ae94eSgc 
385*d73ae94eSgc 	USB_DPRINTF_L4(DPRINT_MASK_ALL, usb_ia->ia_log_handle,
386*d73ae94eSgc 	    "usb_ia_bus_config: op=%d", op);
387*d73ae94eSgc 
388*d73ae94eSgc 	if (usb_ia_bus_config_debug) {
389*d73ae94eSgc 		flag |= NDI_DEVI_DEBUG;
390*d73ae94eSgc 	}
391*d73ae94eSgc 
392*d73ae94eSgc 	ndi_devi_enter(dip, &circ);
393*d73ae94eSgc 
394*d73ae94eSgc 	/* enumerate each interface below us */
395*d73ae94eSgc 	mutex_enter(&usb_ia->ia_mutex);
396*d73ae94eSgc 	usb_ia_create_children(usb_ia);
397*d73ae94eSgc 	mutex_exit(&usb_ia->ia_mutex);
398*d73ae94eSgc 
399*d73ae94eSgc 	rval = ndi_busop_bus_config(dip, flag, op, arg, child, 0);
400*d73ae94eSgc 	ndi_devi_exit(dip, circ);
401*d73ae94eSgc 
402*d73ae94eSgc 	return (rval);
403*d73ae94eSgc }
404*d73ae94eSgc 
405*d73ae94eSgc 
406*d73ae94eSgc static int
407*d73ae94eSgc usb_ia_bus_unconfig(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
408*d73ae94eSgc     void *arg)
409*d73ae94eSgc {
410*d73ae94eSgc 	usb_ia_t  *usb_ia = usb_ia_obtain_state(dip);
411*d73ae94eSgc 
412*d73ae94eSgc 	dev_info_t	*cdip, *mdip;
413*d73ae94eSgc 	int		interface, circular_count;
414*d73ae94eSgc 	int		rval = NDI_SUCCESS;
415*d73ae94eSgc 
416*d73ae94eSgc 	USB_DPRINTF_L4(DPRINT_MASK_ALL, usb_ia->ia_log_handle,
417*d73ae94eSgc 	    "usb_ia_bus_unconfig: op=%d", op);
418*d73ae94eSgc 
419*d73ae94eSgc 	if (usb_ia_bus_config_debug) {
420*d73ae94eSgc 		flag |= NDI_DEVI_DEBUG;
421*d73ae94eSgc 	}
422*d73ae94eSgc 
423*d73ae94eSgc 	/*
424*d73ae94eSgc 	 * first offline and if offlining successful, then
425*d73ae94eSgc 	 * remove children
426*d73ae94eSgc 	 */
427*d73ae94eSgc 	if (op == BUS_UNCONFIG_ALL) {
428*d73ae94eSgc 		flag &= ~(NDI_DEVI_REMOVE | NDI_UNCONFIG);
429*d73ae94eSgc 	}
430*d73ae94eSgc 
431*d73ae94eSgc 	ndi_devi_enter(dip, &circular_count);
432*d73ae94eSgc 	rval = ndi_busop_bus_unconfig(dip, flag, op, arg);
433*d73ae94eSgc 
434*d73ae94eSgc 	if (op == BUS_UNCONFIG_ALL && rval == NDI_SUCCESS &&
435*d73ae94eSgc 	    (flag & NDI_AUTODETACH) == 0) {
436*d73ae94eSgc 		flag |= NDI_DEVI_REMOVE;
437*d73ae94eSgc 		rval = ndi_busop_bus_unconfig(dip, flag, op, arg);
438*d73ae94eSgc 	}
439*d73ae94eSgc 
440*d73ae94eSgc 	/* update children's list */
441*d73ae94eSgc 	mutex_enter(&usb_ia->ia_mutex);
442*d73ae94eSgc 	for (interface = 0; usb_ia->ia_children_dips &&
443*d73ae94eSgc 	    (interface < usb_ia->ia_n_ifs); interface++) {
444*d73ae94eSgc 		mdip = usb_ia->ia_children_dips[interface];
445*d73ae94eSgc 
446*d73ae94eSgc 		/* now search if this dip still exists */
447*d73ae94eSgc 		for (cdip = ddi_get_child(dip); cdip && (cdip != mdip);
448*d73ae94eSgc 			cdip = ddi_get_next_sibling(cdip));
449*d73ae94eSgc 
450*d73ae94eSgc 		if (cdip != mdip) {
451*d73ae94eSgc 			/* we lost the dip on this interface */
452*d73ae94eSgc 			usb_ia->ia_children_dips[interface] = NULL;
453*d73ae94eSgc 		} else if (cdip) {
454*d73ae94eSgc 			/*
455*d73ae94eSgc 			 * keep in DS_INITALIZED to prevent parent
456*d73ae94eSgc 			 * from detaching
457*d73ae94eSgc 			 */
458*d73ae94eSgc 			(void) ddi_initchild(ddi_get_parent(cdip), cdip);
459*d73ae94eSgc 		}
460*d73ae94eSgc 	}
461*d73ae94eSgc 	mutex_exit(&usb_ia->ia_mutex);
462*d73ae94eSgc 
463*d73ae94eSgc 	ndi_devi_exit(dip, circular_count);
464*d73ae94eSgc 
465*d73ae94eSgc 	USB_DPRINTF_L4(DPRINT_MASK_ALL, usb_ia->ia_log_handle,
466*d73ae94eSgc 	    "usb_ia_bus_config: rval=%d", rval);
467*d73ae94eSgc 
468*d73ae94eSgc 	return (rval);
469*d73ae94eSgc }
470*d73ae94eSgc 
471*d73ae94eSgc 
472*d73ae94eSgc /* power entry point */
473*d73ae94eSgc /* ARGSUSED */
474*d73ae94eSgc static int
475*d73ae94eSgc usb_ia_power(dev_info_t *dip, int comp, int level)
476*d73ae94eSgc {
477*d73ae94eSgc 	usb_ia_t		*usb_ia;
478*d73ae94eSgc 	usb_common_power_t	*pm;
479*d73ae94eSgc 	int			rval = DDI_FAILURE;
480*d73ae94eSgc 
481*d73ae94eSgc 	usb_ia = usb_ia_obtain_state(dip);
482*d73ae94eSgc 
483*d73ae94eSgc 	USB_DPRINTF_L4(DPRINT_MASK_PM, usb_ia->ia_log_handle,
484*d73ae94eSgc 	    "usb_ia_power: Begin: usb_ia = %p, level = %d", usb_ia, level);
485*d73ae94eSgc 
486*d73ae94eSgc 	mutex_enter(&usb_ia->ia_mutex);
487*d73ae94eSgc 	pm = usb_ia->ia_pm;
488*d73ae94eSgc 
489*d73ae94eSgc 	/* check if we are transitioning to a legal power level */
490*d73ae94eSgc 	if (USB_DEV_PWRSTATE_OK(pm->uc_pwr_states, level)) {
491*d73ae94eSgc 		USB_DPRINTF_L2(DPRINT_MASK_PM, usb_ia->ia_log_handle,
492*d73ae94eSgc 		    "usb_ia_power: illegal power level = %d "
493*d73ae94eSgc 		    "uc_pwr_states = %x", level, pm->uc_pwr_states);
494*d73ae94eSgc 
495*d73ae94eSgc 		mutex_exit(&usb_ia->ia_mutex);
496*d73ae94eSgc 
497*d73ae94eSgc 		return (rval);
498*d73ae94eSgc 	}
499*d73ae94eSgc 
500*d73ae94eSgc 	rval = usba_common_power(dip, pm, &(usb_ia->ia_dev_state), level);
501*d73ae94eSgc 
502*d73ae94eSgc 	mutex_exit(&usb_ia->ia_mutex);
503*d73ae94eSgc 
504*d73ae94eSgc 	return (rval);
505*d73ae94eSgc }
506*d73ae94eSgc 
507*d73ae94eSgc /*
508*d73ae94eSgc  * attach/resume entry point
509*d73ae94eSgc  */
510*d73ae94eSgc static int
511*d73ae94eSgc usb_ia_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
512*d73ae94eSgc {
513*d73ae94eSgc 	int		instance = ddi_get_instance(dip);
514*d73ae94eSgc 	usb_ia_t	*usb_ia = NULL;
515*d73ae94eSgc 	uint_t		n_ifs;
516*d73ae94eSgc 	size_t		size;
517*d73ae94eSgc 
518*d73ae94eSgc 	switch (cmd) {
519*d73ae94eSgc 	case DDI_ATTACH:
520*d73ae94eSgc 
521*d73ae94eSgc 		break;
522*d73ae94eSgc 	case DDI_RESUME:
523*d73ae94eSgc 		usb_ia = ddi_get_soft_state(usb_ia_statep, instance);
524*d73ae94eSgc 		(void) usb_ia_restore_device_state(dip, usb_ia);
525*d73ae94eSgc 
526*d73ae94eSgc 		return (DDI_SUCCESS);
527*d73ae94eSgc 	default:
528*d73ae94eSgc 
529*d73ae94eSgc 		return (DDI_FAILURE);
530*d73ae94eSgc 	}
531*d73ae94eSgc 
532*d73ae94eSgc 	/*
533*d73ae94eSgc 	 * Attach:
534*d73ae94eSgc 	 *
535*d73ae94eSgc 	 * Allocate soft state and initialize
536*d73ae94eSgc 	 */
537*d73ae94eSgc 	if (ddi_soft_state_zalloc(usb_ia_statep, instance) != DDI_SUCCESS) {
538*d73ae94eSgc 		goto fail;
539*d73ae94eSgc 	}
540*d73ae94eSgc 
541*d73ae94eSgc 	usb_ia = ddi_get_soft_state(usb_ia_statep, instance);
542*d73ae94eSgc 	if (usb_ia == NULL) {
543*d73ae94eSgc 
544*d73ae94eSgc 		goto fail;
545*d73ae94eSgc 	}
546*d73ae94eSgc 
547*d73ae94eSgc 	/* allocate handle for logging of messages */
548*d73ae94eSgc 	usb_ia->ia_log_handle = usb_alloc_log_hdl(dip, "ia",
549*d73ae94eSgc 				&usb_ia_errlevel,
550*d73ae94eSgc 				&usb_ia_errmask, &usb_ia_instance_debug,
551*d73ae94eSgc 				0);
552*d73ae94eSgc 
553*d73ae94eSgc 	usb_ia->ia_dip	= dip;
554*d73ae94eSgc 	usb_ia->ia_instance = instance;
555*d73ae94eSgc 	usb_ia->ia_first_if = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
556*d73ae94eSgc 	    DDI_PROP_DONTPASS, "interface", -1);
557*d73ae94eSgc 	usb_ia->ia_n_ifs = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
558*d73ae94eSgc 	    DDI_PROP_DONTPASS, "interface-count", -1);
559*d73ae94eSgc 
560*d73ae94eSgc 	if (usb_ia->ia_first_if < 0 || usb_ia->ia_n_ifs < 0) {
561*d73ae94eSgc 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_ia->ia_log_handle,
562*d73ae94eSgc 		    "interface-association property failed");
563*d73ae94eSgc 
564*d73ae94eSgc 		goto fail;
565*d73ae94eSgc 	}
566*d73ae94eSgc 
567*d73ae94eSgc 	/* attach client driver to USBA */
568*d73ae94eSgc 	if (usb_client_attach(dip, USBDRV_VERSION, 0) != USB_SUCCESS) {
569*d73ae94eSgc 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_ia->ia_log_handle,
570*d73ae94eSgc 		    "usb_client_attach failed");
571*d73ae94eSgc 		goto fail;
572*d73ae94eSgc 	}
573*d73ae94eSgc 	if (usb_get_dev_data(dip, &usb_ia->ia_dev_data, USB_PARSE_LVL_NONE,
574*d73ae94eSgc 	    0) != USB_SUCCESS) {
575*d73ae94eSgc 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_ia->ia_log_handle,
576*d73ae94eSgc 		    "usb_get_dev_data failed");
577*d73ae94eSgc 		goto fail;
578*d73ae94eSgc 	}
579*d73ae94eSgc 
580*d73ae94eSgc 	mutex_init(&usb_ia->ia_mutex, NULL, MUTEX_DRIVER,
581*d73ae94eSgc 	    usb_ia->ia_dev_data->dev_iblock_cookie);
582*d73ae94eSgc 
583*d73ae94eSgc 	usb_free_dev_data(dip, usb_ia->ia_dev_data);
584*d73ae94eSgc 	usb_ia->ia_dev_data = NULL;
585*d73ae94eSgc 
586*d73ae94eSgc 	usb_ia->ia_init_state |= USB_IA_LOCK_INIT;
587*d73ae94eSgc 
588*d73ae94eSgc 	if (ddi_create_minor_node(dip, "usb_ia", S_IFCHR, instance,
589*d73ae94eSgc 	    DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
590*d73ae94eSgc 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_ia->ia_log_handle,
591*d73ae94eSgc 		    "cannot create devctl minor node");
592*d73ae94eSgc 		goto fail;
593*d73ae94eSgc 	}
594*d73ae94eSgc 
595*d73ae94eSgc 	usb_ia->ia_init_state |= USB_IA_MINOR_NODE_CREATED;
596*d73ae94eSgc 
597*d73ae94eSgc 	/*
598*d73ae94eSgc 	 * allocate array for keeping track of child dips
599*d73ae94eSgc 	 */
600*d73ae94eSgc 	n_ifs = usb_ia->ia_n_ifs;
601*d73ae94eSgc 	usb_ia->ia_cd_list_length = size = (sizeof (dev_info_t *)) * n_ifs;
602*d73ae94eSgc 
603*d73ae94eSgc 	usb_ia->ia_children_dips = kmem_zalloc(size, KM_SLEEP);
604*d73ae94eSgc 	usb_ia->ia_child_events = kmem_zalloc(sizeof (uint8_t) * n_ifs,
605*d73ae94eSgc 								KM_SLEEP);
606*d73ae94eSgc 	/*
607*d73ae94eSgc 	 * Event handling: definition and registration
608*d73ae94eSgc 	 * get event handle for events that we have defined
609*d73ae94eSgc 	 */
610*d73ae94eSgc 	(void) ndi_event_alloc_hdl(dip, 0, &usb_ia->ia_ndi_event_hdl,
611*d73ae94eSgc 								NDI_SLEEP);
612*d73ae94eSgc 
613*d73ae94eSgc 	/* bind event set to the handle */
614*d73ae94eSgc 	if (ndi_event_bind_set(usb_ia->ia_ndi_event_hdl, &usb_ia_ndi_events,
615*d73ae94eSgc 	    NDI_SLEEP)) {
616*d73ae94eSgc 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_ia->ia_log_handle,
617*d73ae94eSgc 		    "usb_ia_attach: binding event set failed");
618*d73ae94eSgc 
619*d73ae94eSgc 		goto fail;
620*d73ae94eSgc 	}
621*d73ae94eSgc 
622*d73ae94eSgc 	usb_ia->ia_dev_state = USB_DEV_ONLINE;
623*d73ae94eSgc 
624*d73ae94eSgc 	/*
625*d73ae94eSgc 	 * now create components to power manage this device
626*d73ae94eSgc 	 * before attaching children
627*d73ae94eSgc 	 */
628*d73ae94eSgc 	usb_ia_create_pm_components(dip, usb_ia);
629*d73ae94eSgc 
630*d73ae94eSgc 	/* event registration for events from our parent */
631*d73ae94eSgc 	usba_common_register_events(dip, n_ifs, usb_ia_event_cb);
632*d73ae94eSgc 
633*d73ae94eSgc 	usb_ia->ia_init_state |= USB_IA_EVENTS_REGISTERED;
634*d73ae94eSgc 
635*d73ae94eSgc 	ddi_report_dev(dip);
636*d73ae94eSgc 
637*d73ae94eSgc 	return (DDI_SUCCESS);
638*d73ae94eSgc 
639*d73ae94eSgc fail:
640*d73ae94eSgc 	USB_DPRINTF_L2(DPRINT_MASK_ATTA, NULL, "usb_ia%d cannot attach",
641*d73ae94eSgc 	    instance);
642*d73ae94eSgc 
643*d73ae94eSgc 	if (usb_ia) {
644*d73ae94eSgc 		(void) usb_ia_cleanup(usb_ia);
645*d73ae94eSgc 	}
646*d73ae94eSgc 
647*d73ae94eSgc 	return (DDI_FAILURE);
648*d73ae94eSgc }
649*d73ae94eSgc 
650*d73ae94eSgc 
651*d73ae94eSgc /* detach or suspend this instance */
652*d73ae94eSgc static int
653*d73ae94eSgc usb_ia_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
654*d73ae94eSgc {
655*d73ae94eSgc 	usb_ia_t	*usb_ia = usb_ia_obtain_state(dip);
656*d73ae94eSgc 
657*d73ae94eSgc 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, usb_ia->ia_log_handle,
658*d73ae94eSgc 	    "usb_ia_detach: cmd = 0x%x", cmd);
659*d73ae94eSgc 
660*d73ae94eSgc 	switch (cmd) {
661*d73ae94eSgc 	case DDI_DETACH:
662*d73ae94eSgc 
663*d73ae94eSgc 		return (usb_ia_cleanup(usb_ia));
664*d73ae94eSgc 	case DDI_SUSPEND:
665*d73ae94eSgc 		/* nothing to do */
666*d73ae94eSgc 		mutex_enter(&usb_ia->ia_mutex);
667*d73ae94eSgc 		usb_ia->ia_dev_state = USB_DEV_SUSPENDED;
668*d73ae94eSgc 		mutex_exit(&usb_ia->ia_mutex);
669*d73ae94eSgc 
670*d73ae94eSgc 		return (DDI_SUCCESS);
671*d73ae94eSgc 	default:
672*d73ae94eSgc 
673*d73ae94eSgc 		return (DDI_FAILURE);
674*d73ae94eSgc 	}
675*d73ae94eSgc 
676*d73ae94eSgc 	_NOTE(NOT_REACHED)
677*d73ae94eSgc 	/* NOTREACHED */
678*d73ae94eSgc }
679*d73ae94eSgc 
680*d73ae94eSgc 
681*d73ae94eSgc /*
682*d73ae94eSgc  * usb_ia_cleanup:
683*d73ae94eSgc  *	cleanup usb_ia and deallocate. this function is called for
684*d73ae94eSgc  *	handling attach failures and detaching including dynamic
685*d73ae94eSgc  *	reconfiguration
686*d73ae94eSgc  */
687*d73ae94eSgc /*ARGSUSED*/
688*d73ae94eSgc static int
689*d73ae94eSgc usb_ia_cleanup(usb_ia_t *usb_ia)
690*d73ae94eSgc {
691*d73ae94eSgc 	usb_common_power_t	*iapm;
692*d73ae94eSgc 	int			rval;
693*d73ae94eSgc 	dev_info_t	*dip = usb_ia->ia_dip;
694*d73ae94eSgc 
695*d73ae94eSgc 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, usb_ia->ia_log_handle,
696*d73ae94eSgc 	    "usb_ia_cleanup:");
697*d73ae94eSgc 
698*d73ae94eSgc 	if ((usb_ia->ia_init_state & USB_IA_LOCK_INIT) == 0) {
699*d73ae94eSgc 
700*d73ae94eSgc 		goto done;
701*d73ae94eSgc 	}
702*d73ae94eSgc 
703*d73ae94eSgc 	/*
704*d73ae94eSgc 	 * deallocate events, if events are still registered
705*d73ae94eSgc 	 * (ie. children still attached) then we have to fail the detach
706*d73ae94eSgc 	 */
707*d73ae94eSgc 	if (usb_ia->ia_ndi_event_hdl &&
708*d73ae94eSgc 	    (ndi_event_free_hdl(usb_ia->ia_ndi_event_hdl) != NDI_SUCCESS)) {
709*d73ae94eSgc 
710*d73ae94eSgc 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, usb_ia->ia_log_handle,
711*d73ae94eSgc 		    "usb_ia_cleanup: ndi_event_free_hdl failed");
712*d73ae94eSgc 
713*d73ae94eSgc 		return (DDI_FAILURE);
714*d73ae94eSgc 	}
715*d73ae94eSgc 
716*d73ae94eSgc 	/*
717*d73ae94eSgc 	 * Disable the event callbacks, after this point, event
718*d73ae94eSgc 	 * callbacks will never get called. Note we shouldn't hold
719*d73ae94eSgc 	 * mutex while unregistering events because there may be a
720*d73ae94eSgc 	 * competing event callback thread. Event callbacks are done
721*d73ae94eSgc 	 * with ndi mutex held and this can cause a potential deadlock.
722*d73ae94eSgc 	 * Note that cleanup can't fail after deregistration of events.
723*d73ae94eSgc 	 */
724*d73ae94eSgc 	if (usb_ia->ia_init_state & USB_IA_EVENTS_REGISTERED) {
725*d73ae94eSgc 
726*d73ae94eSgc 		usba_common_unregister_events(usb_ia->ia_dip, usb_ia->ia_n_ifs);
727*d73ae94eSgc 	}
728*d73ae94eSgc 
729*d73ae94eSgc 	iapm = usb_ia->ia_pm;
730*d73ae94eSgc 
731*d73ae94eSgc 	mutex_enter(&usb_ia->ia_mutex);
732*d73ae94eSgc 
733*d73ae94eSgc 	if ((iapm) && (usb_ia->ia_dev_state != USB_DEV_DISCONNECTED)) {
734*d73ae94eSgc 
735*d73ae94eSgc 		mutex_exit(&usb_ia->ia_mutex);
736*d73ae94eSgc 
737*d73ae94eSgc 		(void) pm_busy_component(dip, 0);
738*d73ae94eSgc 		if (iapm->uc_wakeup_enabled) {
739*d73ae94eSgc 
740*d73ae94eSgc 			/* First bring the device to full power */
741*d73ae94eSgc 			(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
742*d73ae94eSgc 
743*d73ae94eSgc 			rval = usb_handle_remote_wakeup(dip,
744*d73ae94eSgc 			    USB_REMOTE_WAKEUP_DISABLE);
745*d73ae94eSgc 
746*d73ae94eSgc 			if (rval != DDI_SUCCESS) {
747*d73ae94eSgc 				USB_DPRINTF_L2(DPRINT_MASK_EVENTS,
748*d73ae94eSgc 				    usb_ia->ia_log_handle,
749*d73ae94eSgc 				    "usb_cleanup: disable remote "
750*d73ae94eSgc 				    "wakeup failed, rval=%d", rval);
751*d73ae94eSgc 			}
752*d73ae94eSgc 		}
753*d73ae94eSgc 
754*d73ae94eSgc 		(void) pm_lower_power(usb_ia->ia_dip, 0, USB_DEV_OS_PWR_OFF);
755*d73ae94eSgc 		(void) pm_idle_component(dip, 0);
756*d73ae94eSgc 	} else {
757*d73ae94eSgc 		mutex_exit(&usb_ia->ia_mutex);
758*d73ae94eSgc 	}
759*d73ae94eSgc 
760*d73ae94eSgc 	if (iapm) {
761*d73ae94eSgc 		kmem_free(iapm, sizeof (usb_common_power_t));
762*d73ae94eSgc 	}
763*d73ae94eSgc 
764*d73ae94eSgc 	/* free children list */
765*d73ae94eSgc 	if (usb_ia->ia_children_dips) {
766*d73ae94eSgc 		kmem_free(usb_ia->ia_children_dips,
767*d73ae94eSgc 			usb_ia->ia_cd_list_length);
768*d73ae94eSgc 	}
769*d73ae94eSgc 
770*d73ae94eSgc 	if (usb_ia->ia_child_events) {
771*d73ae94eSgc 		kmem_free(usb_ia->ia_child_events, sizeof (uint8_t) *
772*d73ae94eSgc 			usb_ia->ia_n_ifs);
773*d73ae94eSgc 	}
774*d73ae94eSgc 
775*d73ae94eSgc 	if (usb_ia->ia_init_state & USB_IA_MINOR_NODE_CREATED) {
776*d73ae94eSgc 		ddi_remove_minor_node(dip, NULL);
777*d73ae94eSgc 	}
778*d73ae94eSgc 
779*d73ae94eSgc 	mutex_destroy(&usb_ia->ia_mutex);
780*d73ae94eSgc 
781*d73ae94eSgc done:
782*d73ae94eSgc 	usb_client_detach(dip, usb_ia->ia_dev_data);
783*d73ae94eSgc 
784*d73ae94eSgc 	usb_free_log_hdl(usb_ia->ia_log_handle);
785*d73ae94eSgc 	ddi_soft_state_free(usb_ia_statep, ddi_get_instance(dip));
786*d73ae94eSgc 
787*d73ae94eSgc 	ddi_prop_remove_all(dip);
788*d73ae94eSgc 
789*d73ae94eSgc 	return (DDI_SUCCESS);
790*d73ae94eSgc }
791*d73ae94eSgc 
792*d73ae94eSgc /*
793*d73ae94eSgc  * usb_ia_create_children:
794*d73ae94eSgc  */
795*d73ae94eSgc static void
796*d73ae94eSgc usb_ia_create_children(usb_ia_t *usb_ia)
797*d73ae94eSgc {
798*d73ae94eSgc 	usba_device_t		*usba_device;
799*d73ae94eSgc 	uint_t			n_ifs, first_if;
800*d73ae94eSgc 	uint_t			i;
801*d73ae94eSgc 	dev_info_t		*cdip;
802*d73ae94eSgc 
803*d73ae94eSgc 	usba_device = usba_get_usba_device(usb_ia->ia_dip);
804*d73ae94eSgc 
805*d73ae94eSgc 	USB_DPRINTF_L4(DPRINT_MASK_ATTA, usb_ia->ia_log_handle,
806*d73ae94eSgc 	    "usb_ia_attach_child_drivers: port = %d, address = %d",
807*d73ae94eSgc 	    usba_device->usb_port, usba_device->usb_addr);
808*d73ae94eSgc 
809*d73ae94eSgc 	n_ifs = usb_ia->ia_n_ifs;
810*d73ae94eSgc 	first_if = usb_ia->ia_first_if;
811*d73ae94eSgc 
812*d73ae94eSgc 	/*
813*d73ae94eSgc 	 * create all children if not already present
814*d73ae94eSgc 	 */
815*d73ae94eSgc 	for (i = 0; i < n_ifs; i++) {
816*d73ae94eSgc 		if (usb_ia->ia_children_dips[i] != NULL) {
817*d73ae94eSgc 
818*d73ae94eSgc 			continue;
819*d73ae94eSgc 		}
820*d73ae94eSgc 
821*d73ae94eSgc 		mutex_exit(&usb_ia->ia_mutex);
822*d73ae94eSgc 		cdip = usba_ready_interface_node(usb_ia->ia_dip, first_if + i);
823*d73ae94eSgc 		mutex_enter(&usb_ia->ia_mutex);
824*d73ae94eSgc 
825*d73ae94eSgc 		if (cdip != NULL) {
826*d73ae94eSgc 			(void) usba_bind_driver(cdip);
827*d73ae94eSgc 			usb_ia->ia_children_dips[i] = cdip;
828*d73ae94eSgc 		}
829*d73ae94eSgc 	}
830*d73ae94eSgc 
831*d73ae94eSgc }
832*d73ae94eSgc 
833*d73ae94eSgc 
834*d73ae94eSgc /*
835*d73ae94eSgc  * event support
836*d73ae94eSgc  */
837*d73ae94eSgc static int
838*d73ae94eSgc usb_ia_busop_get_eventcookie(dev_info_t *dip,
839*d73ae94eSgc 	dev_info_t *rdip, char *eventname, ddi_eventcookie_t *cookie)
840*d73ae94eSgc {
841*d73ae94eSgc 	usb_ia_t  *usb_ia = usb_ia_obtain_state(dip);
842*d73ae94eSgc 
843*d73ae94eSgc 	USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
844*d73ae94eSgc 	    "usb_ia_busop_get_eventcookie: dip=0x%p, rdip=0x%p, "
845*d73ae94eSgc 	    "event=%s", (void *)dip, (void *)rdip, eventname);
846*d73ae94eSgc 	USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
847*d73ae94eSgc 	    "(dip=%s%d rdip=%s%d)",
848*d73ae94eSgc 	    ddi_driver_name(dip), ddi_get_instance(dip),
849*d73ae94eSgc 	    ddi_driver_name(rdip), ddi_get_instance(rdip));
850*d73ae94eSgc 
851*d73ae94eSgc 	/* return event cookie, iblock cookie, and level */
852*d73ae94eSgc 	return (ndi_event_retrieve_cookie(usb_ia->ia_ndi_event_hdl,
853*d73ae94eSgc 	    rdip, eventname, cookie, NDI_EVENT_NOPASS));
854*d73ae94eSgc }
855*d73ae94eSgc 
856*d73ae94eSgc 
857*d73ae94eSgc static int
858*d73ae94eSgc usb_ia_busop_add_eventcall(dev_info_t *dip,
859*d73ae94eSgc 	dev_info_t *rdip,
860*d73ae94eSgc 	ddi_eventcookie_t cookie,
861*d73ae94eSgc 	void (*callback)(dev_info_t *dip,
862*d73ae94eSgc 	    ddi_eventcookie_t cookie, void *arg,
863*d73ae94eSgc 	    void *bus_impldata),
864*d73ae94eSgc 	void *arg, ddi_callback_id_t *cb_id)
865*d73ae94eSgc {
866*d73ae94eSgc 	int	ifno;
867*d73ae94eSgc 	usb_ia_t  *usb_ia = usb_ia_obtain_state(dip);
868*d73ae94eSgc 
869*d73ae94eSgc 	mutex_enter(&usb_ia->ia_mutex);
870*d73ae94eSgc 	ifno = usba_get_ifno(rdip)- usb_ia->ia_first_if;
871*d73ae94eSgc 	mutex_exit(&usb_ia->ia_mutex);
872*d73ae94eSgc 
873*d73ae94eSgc 	if (ifno < 0) {
874*d73ae94eSgc 		ifno = 0;
875*d73ae94eSgc 	}
876*d73ae94eSgc 
877*d73ae94eSgc 	USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
878*d73ae94eSgc 	    "usb_ia_busop_add_eventcall: dip=0x%p, rdip=0x%p "
879*d73ae94eSgc 	    "cookie=0x%p, cb=0x%p, arg=0x%p",
880*d73ae94eSgc 	    (void *)dip, (void *)rdip, (void *)cookie, (void *)callback, arg);
881*d73ae94eSgc 	USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
882*d73ae94eSgc 	    "(dip=%s%d rdip=%s%d event=%s)",
883*d73ae94eSgc 	    ddi_driver_name(dip), ddi_get_instance(dip),
884*d73ae94eSgc 	    ddi_driver_name(rdip), ddi_get_instance(rdip),
885*d73ae94eSgc 	    ndi_event_cookie_to_name(usb_ia->ia_ndi_event_hdl, cookie));
886*d73ae94eSgc 
887*d73ae94eSgc 	/* Set flag on children registering events */
888*d73ae94eSgc 	switch (ndi_event_cookie_to_tag(usb_ia->ia_ndi_event_hdl, cookie)) {
889*d73ae94eSgc 	case USBA_EVENT_TAG_HOT_REMOVAL:
890*d73ae94eSgc 		mutex_enter(&usb_ia->ia_mutex);
891*d73ae94eSgc 		usb_ia->ia_child_events[ifno] |=
892*d73ae94eSgc 		    USB_IA_CHILD_EVENT_DISCONNECT;
893*d73ae94eSgc 		mutex_exit(&usb_ia->ia_mutex);
894*d73ae94eSgc 
895*d73ae94eSgc 		break;
896*d73ae94eSgc 	case USBA_EVENT_TAG_PRE_SUSPEND:
897*d73ae94eSgc 		mutex_enter(&usb_ia->ia_mutex);
898*d73ae94eSgc 		usb_ia->ia_child_events[ifno] |=
899*d73ae94eSgc 		    USB_IA_CHILD_EVENT_PRESUSPEND;
900*d73ae94eSgc 		mutex_exit(&usb_ia->ia_mutex);
901*d73ae94eSgc 
902*d73ae94eSgc 		break;
903*d73ae94eSgc 	default:
904*d73ae94eSgc 
905*d73ae94eSgc 		break;
906*d73ae94eSgc 	}
907*d73ae94eSgc 	/* add callback (perform registration) */
908*d73ae94eSgc 	return (ndi_event_add_callback(usb_ia->ia_ndi_event_hdl,
909*d73ae94eSgc 	    rdip, cookie, callback, arg, NDI_SLEEP, cb_id));
910*d73ae94eSgc }
911*d73ae94eSgc 
912*d73ae94eSgc 
913*d73ae94eSgc static int
914*d73ae94eSgc usb_ia_busop_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id)
915*d73ae94eSgc {
916*d73ae94eSgc 	usb_ia_t  *usb_ia = usb_ia_obtain_state(dip);
917*d73ae94eSgc 	ndi_event_callbacks_t *cb = (ndi_event_callbacks_t *)cb_id;
918*d73ae94eSgc 
919*d73ae94eSgc 	ASSERT(cb);
920*d73ae94eSgc 
921*d73ae94eSgc 	USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
922*d73ae94eSgc 	    "usb_ia_busop_remove_eventcall: dip=0x%p, rdip=0x%p "
923*d73ae94eSgc 	    "cookie=0x%p", (void *)dip, cb->ndi_evtcb_dip,
924*d73ae94eSgc 	    cb->ndi_evtcb_cookie);
925*d73ae94eSgc 	USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
926*d73ae94eSgc 	    "(dip=%s%d rdip=%s%d event=%s)",
927*d73ae94eSgc 	    ddi_driver_name(dip), ddi_get_instance(dip),
928*d73ae94eSgc 	    ddi_driver_name(cb->ndi_evtcb_dip),
929*d73ae94eSgc 	    ddi_get_instance(cb->ndi_evtcb_dip),
930*d73ae94eSgc 	    ndi_event_cookie_to_name(usb_ia->ia_ndi_event_hdl,
931*d73ae94eSgc 	    cb->ndi_evtcb_cookie));
932*d73ae94eSgc 
933*d73ae94eSgc 	/* remove event registration from our event set */
934*d73ae94eSgc 	return (ndi_event_remove_callback(usb_ia->ia_ndi_event_hdl, cb_id));
935*d73ae94eSgc }
936*d73ae94eSgc 
937*d73ae94eSgc 
938*d73ae94eSgc static int
939*d73ae94eSgc usb_ia_busop_post_event(dev_info_t *dip,
940*d73ae94eSgc 	dev_info_t *rdip,
941*d73ae94eSgc 	ddi_eventcookie_t cookie,
942*d73ae94eSgc 	void *bus_impldata)
943*d73ae94eSgc {
944*d73ae94eSgc 	usb_ia_t  *usb_ia = usb_ia_obtain_state(dip);
945*d73ae94eSgc 
946*d73ae94eSgc 	USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
947*d73ae94eSgc 	    "usb_ia_busop_post_event: dip=0x%p, rdip=0x%p "
948*d73ae94eSgc 	    "cookie=0x%p, impl=0x%p",
949*d73ae94eSgc 	    (void *)dip, (void *)rdip, (void *)cookie, bus_impldata);
950*d73ae94eSgc 	USB_DPRINTF_L3(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
951*d73ae94eSgc 	    "(dip=%s%d rdip=%s%d event=%s)",
952*d73ae94eSgc 	    ddi_driver_name(dip), ddi_get_instance(dip),
953*d73ae94eSgc 	    ddi_driver_name(rdip), ddi_get_instance(rdip),
954*d73ae94eSgc 	    ndi_event_cookie_to_name(usb_ia->ia_ndi_event_hdl, cookie));
955*d73ae94eSgc 
956*d73ae94eSgc 	/* post event to all children registered for this event */
957*d73ae94eSgc 	return (ndi_event_run_callbacks(usb_ia->ia_ndi_event_hdl, rdip,
958*d73ae94eSgc 	    cookie, bus_impldata));
959*d73ae94eSgc }
960*d73ae94eSgc 
961*d73ae94eSgc 
962*d73ae94eSgc /*
963*d73ae94eSgc  * usb_ia_restore_device_state
964*d73ae94eSgc  *	set the original configuration of the device
965*d73ae94eSgc  */
966*d73ae94eSgc static int
967*d73ae94eSgc usb_ia_restore_device_state(dev_info_t *dip, usb_ia_t *usb_ia)
968*d73ae94eSgc {
969*d73ae94eSgc 	usb_common_power_t	*iapm;
970*d73ae94eSgc 
971*d73ae94eSgc 	USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
972*d73ae94eSgc 	    "usb_ia_restore_device_state: usb_ia = %p", usb_ia);
973*d73ae94eSgc 
974*d73ae94eSgc 	mutex_enter(&usb_ia->ia_mutex);
975*d73ae94eSgc 	iapm = usb_ia->ia_pm;
976*d73ae94eSgc 	mutex_exit(&usb_ia->ia_mutex);
977*d73ae94eSgc 
978*d73ae94eSgc 	/* First bring the device to full power */
979*d73ae94eSgc 	(void) pm_busy_component(dip, 0);
980*d73ae94eSgc 	(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
981*d73ae94eSgc 
982*d73ae94eSgc 	if (usb_check_same_device(dip, usb_ia->ia_log_handle, USB_LOG_L0,
983*d73ae94eSgc 	    DPRINT_MASK_EVENTS, USB_CHK_VIDPID, NULL) != USB_SUCCESS) {
984*d73ae94eSgc 
985*d73ae94eSgc 		/* change the device state from suspended to disconnected */
986*d73ae94eSgc 		mutex_enter(&usb_ia->ia_mutex);
987*d73ae94eSgc 		usb_ia->ia_dev_state = USB_DEV_DISCONNECTED;
988*d73ae94eSgc 		mutex_exit(&usb_ia->ia_mutex);
989*d73ae94eSgc 		(void) pm_idle_component(dip, 0);
990*d73ae94eSgc 
991*d73ae94eSgc 		return (USB_FAILURE);
992*d73ae94eSgc 	}
993*d73ae94eSgc 
994*d73ae94eSgc 	/*
995*d73ae94eSgc 	 * if the device had remote wakeup earlier,
996*d73ae94eSgc 	 * enable it again
997*d73ae94eSgc 	 */
998*d73ae94eSgc 	if (iapm->uc_wakeup_enabled) {
999*d73ae94eSgc 		(void) usb_handle_remote_wakeup(usb_ia->ia_dip,
1000*d73ae94eSgc 		    USB_REMOTE_WAKEUP_ENABLE);
1001*d73ae94eSgc 	}
1002*d73ae94eSgc 
1003*d73ae94eSgc 	mutex_enter(&usb_ia->ia_mutex);
1004*d73ae94eSgc 	usb_ia->ia_dev_state = USB_DEV_ONLINE;
1005*d73ae94eSgc 	mutex_exit(&usb_ia->ia_mutex);
1006*d73ae94eSgc 
1007*d73ae94eSgc 	(void) pm_idle_component(dip, 0);
1008*d73ae94eSgc 
1009*d73ae94eSgc 	return (USB_SUCCESS);
1010*d73ae94eSgc }
1011*d73ae94eSgc 
1012*d73ae94eSgc 
1013*d73ae94eSgc /*
1014*d73ae94eSgc  * usb_ia_event_cb()
1015*d73ae94eSgc  *	handle disconnect and connect events
1016*d73ae94eSgc  */
1017*d73ae94eSgc static void
1018*d73ae94eSgc usb_ia_event_cb(dev_info_t *dip, ddi_eventcookie_t cookie,
1019*d73ae94eSgc 	void *arg, void *bus_impldata)
1020*d73ae94eSgc {
1021*d73ae94eSgc 	int		i, tag;
1022*d73ae94eSgc 	usb_ia_t	*usb_ia = usb_ia_obtain_state(dip);
1023*d73ae94eSgc 	dev_info_t	*child_dip;
1024*d73ae94eSgc 	ddi_eventcookie_t rm_cookie, ins_cookie, suspend_cookie, resume_cookie;
1025*d73ae94eSgc 
1026*d73ae94eSgc 	USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
1027*d73ae94eSgc 	    "usb_ia_event_cb: dip=0x%p, cookie=0x%p, "
1028*d73ae94eSgc 	    "arg=0x%p, impl=0x%p",
1029*d73ae94eSgc 	    (void *)dip, (void *)cookie, arg, bus_impldata);
1030*d73ae94eSgc 	USB_DPRINTF_L4(DPRINT_MASK_EVENTS, usb_ia->ia_log_handle,
1031*d73ae94eSgc 	    "(dip=%s%d event=%s)",
1032*d73ae94eSgc 	    ddi_driver_name(dip), ddi_get_instance(dip),
1033*d73ae94eSgc 	    ndi_event_cookie_to_name(usb_ia->ia_ndi_event_hdl, cookie));
1034*d73ae94eSgc 
1035*d73ae94eSgc 	tag = NDI_EVENT_TAG(cookie);
1036*d73ae94eSgc 	rm_cookie = ndi_event_tag_to_cookie(
1037*d73ae94eSgc 	    usb_ia->ia_ndi_event_hdl, USBA_EVENT_TAG_HOT_REMOVAL);
1038*d73ae94eSgc 	suspend_cookie = ndi_event_tag_to_cookie(
1039*d73ae94eSgc 	    usb_ia->ia_ndi_event_hdl, USBA_EVENT_TAG_PRE_SUSPEND);
1040*d73ae94eSgc 	ins_cookie = ndi_event_tag_to_cookie(
1041*d73ae94eSgc 	    usb_ia->ia_ndi_event_hdl, USBA_EVENT_TAG_HOT_INSERTION);
1042*d73ae94eSgc 	resume_cookie = ndi_event_tag_to_cookie(
1043*d73ae94eSgc 	    usb_ia->ia_ndi_event_hdl, USBA_EVENT_TAG_POST_RESUME);
1044*d73ae94eSgc 
1045*d73ae94eSgc 	mutex_enter(&usb_ia->ia_mutex);
1046*d73ae94eSgc 	switch (tag) {
1047*d73ae94eSgc 	case USBA_EVENT_TAG_HOT_REMOVAL:
1048*d73ae94eSgc 		if (usb_ia->ia_dev_state == USB_DEV_DISCONNECTED) {
1049*d73ae94eSgc 			USB_DPRINTF_L2(DPRINT_MASK_EVENTS,
1050*d73ae94eSgc 			    usb_ia->ia_log_handle,
1051*d73ae94eSgc 			    "usb_ia_event_cb: Device already disconnected");
1052*d73ae94eSgc 		} else {
1053*d73ae94eSgc 			/* we are disconnected so set our state now */
1054*d73ae94eSgc 			usb_ia->ia_dev_state = USB_DEV_DISCONNECTED;
1055*d73ae94eSgc 			for (i = 0; i < usb_ia->ia_n_ifs; i++) {
1056*d73ae94eSgc 				usb_ia->ia_child_events[i] &= ~
1057*d73ae94eSgc 				    USB_IA_CHILD_EVENT_DISCONNECT;
1058*d73ae94eSgc 			}
1059*d73ae94eSgc 			mutex_exit(&usb_ia->ia_mutex);
1060*d73ae94eSgc 
1061*d73ae94eSgc 			/* pass disconnect event to all the children */
1062*d73ae94eSgc 			(void) ndi_event_run_callbacks(
1063*d73ae94eSgc 			    usb_ia->ia_ndi_event_hdl, NULL,
1064*d73ae94eSgc 			    rm_cookie, bus_impldata);
1065*d73ae94eSgc 
1066*d73ae94eSgc 			mutex_enter(&usb_ia->ia_mutex);
1067*d73ae94eSgc 		}
1068*d73ae94eSgc 		break;
1069*d73ae94eSgc 	case USBA_EVENT_TAG_PRE_SUSPEND:
1070*d73ae94eSgc 		/* set our state *after* suspending children */
1071*d73ae94eSgc 		mutex_exit(&usb_ia->ia_mutex);
1072*d73ae94eSgc 
1073*d73ae94eSgc 		/* pass pre_suspend event to all the children */
1074*d73ae94eSgc 		(void) ndi_event_run_callbacks(usb_ia->ia_ndi_event_hdl,
1075*d73ae94eSgc 		    NULL, suspend_cookie, bus_impldata);
1076*d73ae94eSgc 
1077*d73ae94eSgc 		mutex_enter(&usb_ia->ia_mutex);
1078*d73ae94eSgc 		for (i = 0; i < usb_ia->ia_n_ifs; i++) {
1079*d73ae94eSgc 			usb_ia->ia_child_events[i] &= ~
1080*d73ae94eSgc 			    USB_IA_CHILD_EVENT_PRESUSPEND;
1081*d73ae94eSgc 		}
1082*d73ae94eSgc 		break;
1083*d73ae94eSgc 	case USBA_EVENT_TAG_HOT_INSERTION:
1084*d73ae94eSgc 		mutex_exit(&usb_ia->ia_mutex);
1085*d73ae94eSgc 		if (usb_ia_restore_device_state(dip, usb_ia) == USB_SUCCESS) {
1086*d73ae94eSgc 
1087*d73ae94eSgc 			/*
1088*d73ae94eSgc 			 * Check to see if this child has missed the disconnect
1089*d73ae94eSgc 			 * event before it registered for event cb
1090*d73ae94eSgc 			 */
1091*d73ae94eSgc 			mutex_enter(&usb_ia->ia_mutex);
1092*d73ae94eSgc 			for (i = 0; i < usb_ia->ia_n_ifs; i++) {
1093*d73ae94eSgc 				if (usb_ia->ia_child_events[i] &
1094*d73ae94eSgc 				    USB_IA_CHILD_EVENT_DISCONNECT) {
1095*d73ae94eSgc 					usb_ia->ia_child_events[i] &=
1096*d73ae94eSgc 					    ~USB_IA_CHILD_EVENT_DISCONNECT;
1097*d73ae94eSgc 					child_dip =
1098*d73ae94eSgc 					    usb_ia->ia_children_dips[i];
1099*d73ae94eSgc 					mutex_exit(&usb_ia->ia_mutex);
1100*d73ae94eSgc 
1101*d73ae94eSgc 					/* post the missed disconnect */
1102*d73ae94eSgc 					(void) ndi_event_do_callback(
1103*d73ae94eSgc 					    usb_ia->ia_ndi_event_hdl,
1104*d73ae94eSgc 					    child_dip,
1105*d73ae94eSgc 					    rm_cookie,
1106*d73ae94eSgc 					    bus_impldata);
1107*d73ae94eSgc 					mutex_enter(&usb_ia->ia_mutex);
1108*d73ae94eSgc 				}
1109*d73ae94eSgc 			}
1110*d73ae94eSgc 			mutex_exit(&usb_ia->ia_mutex);
1111*d73ae94eSgc 
1112*d73ae94eSgc 			/* pass reconnect event to all the children */
1113*d73ae94eSgc 			(void) ndi_event_run_callbacks(
1114*d73ae94eSgc 			    usb_ia->ia_ndi_event_hdl, NULL,
1115*d73ae94eSgc 			    ins_cookie, bus_impldata);
1116*d73ae94eSgc 
1117*d73ae94eSgc 		}
1118*d73ae94eSgc 		mutex_enter(&usb_ia->ia_mutex);
1119*d73ae94eSgc 		break;
1120*d73ae94eSgc 	case USBA_EVENT_TAG_POST_RESUME:
1121*d73ae94eSgc 		/*
1122*d73ae94eSgc 		 * Check to see if this child has missed the pre-suspend
1123*d73ae94eSgc 		 * event before it registered for event cb
1124*d73ae94eSgc 		 */
1125*d73ae94eSgc 		for (i = 0; i < usb_ia->ia_n_ifs; i++) {
1126*d73ae94eSgc 			if (usb_ia->ia_child_events[i] &
1127*d73ae94eSgc 			    USB_IA_CHILD_EVENT_PRESUSPEND) {
1128*d73ae94eSgc 				usb_ia->ia_child_events[i] &=
1129*d73ae94eSgc 				    ~USB_IA_CHILD_EVENT_PRESUSPEND;
1130*d73ae94eSgc 				child_dip = usb_ia->ia_children_dips[i];
1131*d73ae94eSgc 				mutex_exit(&usb_ia->ia_mutex);
1132*d73ae94eSgc 
1133*d73ae94eSgc 				/* post the missed pre-suspend event */
1134*d73ae94eSgc 				(void) ndi_event_do_callback(
1135*d73ae94eSgc 				    usb_ia->ia_ndi_event_hdl,
1136*d73ae94eSgc 				    child_dip, suspend_cookie,
1137*d73ae94eSgc 				    bus_impldata);
1138*d73ae94eSgc 				mutex_enter(&usb_ia->ia_mutex);
1139*d73ae94eSgc 			}
1140*d73ae94eSgc 		}
1141*d73ae94eSgc 		mutex_exit(&usb_ia->ia_mutex);
1142*d73ae94eSgc 
1143*d73ae94eSgc 		/* pass post_resume event to all the children */
1144*d73ae94eSgc 		(void) ndi_event_run_callbacks(usb_ia->ia_ndi_event_hdl,
1145*d73ae94eSgc 		    NULL, resume_cookie, bus_impldata);
1146*d73ae94eSgc 
1147*d73ae94eSgc 		mutex_enter(&usb_ia->ia_mutex);
1148*d73ae94eSgc 		break;
1149*d73ae94eSgc 	}
1150*d73ae94eSgc 	mutex_exit(&usb_ia->ia_mutex);
1151*d73ae94eSgc 
1152*d73ae94eSgc }
1153*d73ae94eSgc 
1154*d73ae94eSgc /*
1155*d73ae94eSgc  * create the pm components required for power management
1156*d73ae94eSgc  */
1157*d73ae94eSgc static void
1158*d73ae94eSgc usb_ia_create_pm_components(dev_info_t *dip, usb_ia_t *usb_ia)
1159*d73ae94eSgc {
1160*d73ae94eSgc 	usb_common_power_t	*iapm;
1161*d73ae94eSgc 	uint_t			pwr_states;
1162*d73ae94eSgc 
1163*d73ae94eSgc 	USB_DPRINTF_L4(DPRINT_MASK_PM, usb_ia->ia_log_handle,
1164*d73ae94eSgc 	    "usb_ia_create_pm_components: Begin");
1165*d73ae94eSgc 
1166*d73ae94eSgc 	/* Allocate the PM state structure */
1167*d73ae94eSgc 	iapm = kmem_zalloc(sizeof (usb_common_power_t), KM_SLEEP);
1168*d73ae94eSgc 
1169*d73ae94eSgc 	mutex_enter(&usb_ia->ia_mutex);
1170*d73ae94eSgc 	usb_ia->ia_pm = iapm;
1171*d73ae94eSgc 	iapm->uc_usb_statep = usb_ia;
1172*d73ae94eSgc 	iapm->uc_pm_capabilities = 0; /* XXXX should this be 0?? */
1173*d73ae94eSgc 	iapm->uc_current_power = USB_DEV_OS_FULL_PWR;
1174*d73ae94eSgc 	mutex_exit(&usb_ia->ia_mutex);
1175*d73ae94eSgc 
1176*d73ae94eSgc 	/*
1177*d73ae94eSgc 	 * By not enabling parental notification, PM enforces
1178*d73ae94eSgc 	 * "strict parental dependency" meaning, usb_ia won't
1179*d73ae94eSgc 	 * power off until any of its children are in full power.
1180*d73ae94eSgc 	 */
1181*d73ae94eSgc 
1182*d73ae94eSgc 	/*
1183*d73ae94eSgc 	 * there are 3 scenarios:
1184*d73ae94eSgc 	 * 1. a well behaved device should have remote wakeup
1185*d73ae94eSgc 	 * at interface and device level. If the interface
1186*d73ae94eSgc 	 * wakes up, usb_ia will wake up
1187*d73ae94eSgc 	 * 2. if the device doesn't have remote wake up and
1188*d73ae94eSgc 	 * the interface has, PM will still work, ie.
1189*d73ae94eSgc 	 * the interfaces wakes up and usb_ia wakes up
1190*d73ae94eSgc 	 * 3. if neither the interface nor device has remote
1191*d73ae94eSgc 	 * wakeup, the interface will wake up when it is opened
1192*d73ae94eSgc 	 * and goes to sleep after being closed for a while
1193*d73ae94eSgc 	 * In this case usb_ia should also go to sleep shortly
1194*d73ae94eSgc 	 * thereafter
1195*d73ae94eSgc 	 * In all scenarios it doesn't really matter whether
1196*d73ae94eSgc 	 * remote wakeup at the device level is enabled or not
1197*d73ae94eSgc 	 * but we do it anyways
1198*d73ae94eSgc 	 */
1199*d73ae94eSgc 	if (usb_handle_remote_wakeup(dip, USB_REMOTE_WAKEUP_ENABLE) ==
1200*d73ae94eSgc 	    USB_SUCCESS) {
1201*d73ae94eSgc 		USB_DPRINTF_L3(DPRINT_MASK_PM, usb_ia->ia_log_handle,
1202*d73ae94eSgc 		    "usb_ia_create_pm_components: "
1203*d73ae94eSgc 		    "Remote Wakeup Enabled");
1204*d73ae94eSgc 		iapm->uc_wakeup_enabled = 1;
1205*d73ae94eSgc 	}
1206*d73ae94eSgc 
1207*d73ae94eSgc 	if (usb_create_pm_components(dip, &pwr_states) ==
1208*d73ae94eSgc 	    USB_SUCCESS) {
1209*d73ae94eSgc 		iapm->uc_pwr_states = (uint8_t)pwr_states;
1210*d73ae94eSgc 		(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
1211*d73ae94eSgc 	}
1212*d73ae94eSgc 
1213*d73ae94eSgc 	USB_DPRINTF_L4(DPRINT_MASK_PM, usb_ia->ia_log_handle,
1214*d73ae94eSgc 	    "usb_ia_create_pm_components: End");
1215*d73ae94eSgc }
1216*d73ae94eSgc 
1217*d73ae94eSgc 
1218*d73ae94eSgc /*
1219*d73ae94eSgc  * usb_ia_obtain_state:
1220*d73ae94eSgc  */
1221*d73ae94eSgc static usb_ia_t *
1222*d73ae94eSgc usb_ia_obtain_state(dev_info_t *dip)
1223*d73ae94eSgc {
1224*d73ae94eSgc 	int instance = ddi_get_instance(dip);
1225*d73ae94eSgc 	usb_ia_t *statep = ddi_get_soft_state(usb_ia_statep, instance);
1226*d73ae94eSgc 
1227*d73ae94eSgc 	ASSERT(statep != NULL);
1228*d73ae94eSgc 
1229*d73ae94eSgc 	return (statep);
1230*d73ae94eSgc }
1231