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