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