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