hwahc.c revision 10912:bb04b6e33d44
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*
27 * The Data Transfer Interface driver for Host Wire Adapter device
28 *
29 * HWA device has two interfaces, one is the data transfer interface,
30 * another is the radio control interface. This driver (hwahc) is only
31 * for data transfer interface support, but it depends on the radio
32 * control interface driver (hwarc) to work. That means the hwarc
33 * driver must be loaded while the hwahc is working. This is now
34 * ensured by holding hwarc open until hwahc detaches or powers down.
35 *
36 * The data transfer interface has three endpoints besides the default
37 * control endpoint which is shared between the two interfaces. The
38 * three endpoints are:
39 *
40 * - notification endpoint (intr in type, for asynchronous event
41 * notifications and transfer status notifications)
42 *
43 * - data transfer OUT endpoint (bulk out type, for sending transfer
44 * requests and transfer data from the host to the HWA device)
45 *
46 * - data transfer IN endpoint (bulk in type, for returning transfer
47 * status and transfer data from the HWA device to the host)
48 *
49 * The HWA device is a USB 2.0 device, so it supports the standard USB
50 * requests defined in chapter 9 of USB 2.0 specification as other USB
51 * client devices. But its most important functionality is to work as
52 * a wireless USB host. This means the hwahc driver needs to supply
53 * host controller functionalities, which include children hotplug
54 * support and data transfer support to children device endpoints.
55 *
56 * So hwahc driver is implemented as a nexus driver and it follows the
57 * event mechanism in existing USBA framework to support children
58 * hotplug events.
59 *
60 * The hwahc driver works as the root-hub on wireless USB bus. And it
61 * relays data transfers to/from wireless bus to the USB bus where ehci/
62 * ohci/uhci works as the root-hub. This makes a bus cascading topology.
63 *
64 * The data transfer to/from wireless device endpoints is implemented by
65 * remote pipe (rpipe) mechanism. The rpipe descriptor on the HWA defines
66 * the attributes of a wireless USB transfer, such as the transfer type,
67 * the target device address, the target endpoint address and the max
68 * packet size. And the transfer requests through data transfer OUT
69 * endpoint will take a certain rpipe as the transfer target, thus
70 * fulfills the data transfer across buses. Refer to chapter 8 of WUSB
71 * 1.0 specification for details of this.
72 */
73
74#define	USBDRV_MAJOR_VER	2
75#define	USBDRV_MINOR_VER	0
76
77#include <sys/usb/hwa/hwahc/hwahc.h>
78#include <sys/usb/hwa/hwahc/hwahc_util.h>
79#include <sys/usb/usba/wa.h>
80#include <sys/usb/usba/wusba.h>
81#include <sys/usb/usba/whcdi.h>
82#include <sys/usb/usba.h>
83#include <sys/usb/usba/usba_impl.h>
84#include <sys/usb/usba/usba_devdb.h>	/* for usba_devdb_refresh */
85#include <sys/usb/hubd/hubdvar.h>
86#include <sys/usb/hubd/hubd_impl.h>	/* for hubd_ioctl_data_t */
87#include <sys/strsubr.h>	/* for allocb_wait */
88#include <sys/strsun.h>		/* for MBLKL macro */
89#include <sys/fs/dv_node.h>	/* for devfs_clean */
90#include <sys/uwb/uwbai.h>	/* for uwb ioctls */
91#include <sys/random.h>
92
93void *hwahc_statep;
94
95/* number of instances */
96#define	HWAHC_INSTS	1
97
98/* default value for set number DNTS slots request */
99#define	HWAHC_DEFAULT_DNTS_INTERVAL	2 /* ms */
100#define	HWAHC_DEFAULT_DNTS_SLOT_NUM	4
101
102
103/* debug support */
104uint_t	hwahc_errmask	= (uint_t)PRINT_MASK_ALL;
105uint_t	hwahc_errlevel	= USB_LOG_L4;
106uint_t	hwahc_instance_debug = (uint_t)-1;
107
108/* bus config debug flag */
109uint_t	hwahc_bus_config_debug = 0;
110uint8_t	hwahc_enable_trust_timeout = 1;
111
112
113/*
114 * Use the default GTK for the whole life of HWA driver.
115 * Not so compatible with WUSB spec.
116 */
117static uint8_t	dft_gtk[16];
118static uint8_t	dft_gtkid[3];
119
120extern usb_log_handle_t	whcdi_log_handle;
121
122/*
123 * Function Prototypes
124 */
125/* driver operations (dev_ops) entry points */
126static int	hwahc_open(dev_t *, int, int, cred_t *);
127static int	hwahc_close(dev_t, int, int, cred_t *);
128static int	hwahc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
129
130static int	hwahc_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
131static int	hwahc_attach(dev_info_t *, ddi_attach_cmd_t);
132static int	hwahc_detach(dev_info_t *, ddi_detach_cmd_t);
133static int	hwahc_power(dev_info_t *, int, int);
134
135/* bus_ops entry points */
136static int	hwahc_bus_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t,
137		void *, void *);
138static int	hwahc_busop_get_eventcookie(dev_info_t *, dev_info_t *,
139		char *, ddi_eventcookie_t *);
140static int	hwahc_busop_add_eventcall(
141		dev_info_t *, dev_info_t *, ddi_eventcookie_t,
142		void (*)(dev_info_t *, ddi_eventcookie_t, void *, void *),
143		void *, ddi_callback_id_t *);
144static int	hwahc_busop_remove_eventcall(dev_info_t *, ddi_callback_id_t);
145static int	hwahc_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t,
146		void *, dev_info_t **);
147static int	hwahc_bus_unconfig(dev_info_t *, uint_t, ddi_bus_config_op_t,
148		void *);
149
150/* hotplug and power management supporting functions */
151static int	hwahc_disconnect_event_cb(dev_info_t *dip);
152static int	hwahc_reconnect_event_cb(dev_info_t *dip);
153static int	hwahc_pre_suspend_event_cb(dev_info_t *dip);
154static int	hwahc_post_resume_event_cb(dev_info_t *dip);
155static int	hwahc_cpr_suspend(dev_info_t *);
156static int	hwahc_cpr_resume(dev_info_t *);
157static void	hwahc_restore_device_state(dev_info_t *, hwahc_state_t *);
158static void	hwahc_run_callbacks(hwahc_state_t *, usba_event_t);
159static void	hwahc_post_event(hwahc_state_t *, usb_port_t, usba_event_t);
160
161static int	hwahc_cleanup(dev_info_t *, hwahc_state_t *);
162static void	hwahc_create_pm_components(dev_info_t *, hwahc_state_t *);
163static void	hwahc_destroy_pm_components(hwahc_state_t *);
164static void	hwahc_pm_busy_component(hwahc_state_t *);
165static void	hwahc_pm_idle_component(hwahc_state_t *);
166static int	hwahc_pwrlvl0(hwahc_state_t *);
167static int	hwahc_pwrlvl1(hwahc_state_t *);
168static int	hwahc_pwrlvl2(hwahc_state_t *);
169static int	hwahc_pwrlvl3(hwahc_state_t *);
170static int	hwahc_hc_channel_suspend(hwahc_state_t *);
171
172/* hardware initialization and deinitialization functions */
173static int	hwahc_parse_security_data(wusb_secrt_data_t *,
174		usb_cfg_data_t *);
175static void	hwahc_print_secrt_data(hwahc_state_t *);
176
177static int	hwahc_hub_attach(hwahc_state_t *);
178static int	hwahc_hub_detach(hwahc_state_t *);
179
180static int	hwahc_hc_initial_start(hwahc_state_t *);
181static int	hwahc_hc_final_stop(hwahc_state_t *);
182static int	hwahc_wa_start(hwahc_state_t *);
183static void	hwahc_wa_stop(hwahc_state_t *);
184static int	hwahc_hc_channel_start(hwahc_state_t *);
185static int	hwahc_hc_channel_stop(hwahc_state_t *);
186static void	hwahc_hc_data_init(hwahc_state_t *);
187static void	hwahc_hc_data_fini(hwahc_state_t *);
188
189/* ioctl support */
190static int	hwahc_cfgadm_ioctl(hwahc_state_t *, int, intptr_t, int,
191		cred_t *, int *);
192static int	hwahc_wusb_ioctl(hwahc_state_t *, int, intptr_t, int,
193		cred_t *, int *);
194
195/* callbacks registered to USBA */
196static void	hwahc_disconnect_dev(dev_info_t *, usb_port_t);
197static void	hwahc_reconnect_dev(dev_info_t *, usb_port_t);
198static int	hwahc_create_child(dev_info_t *, usb_port_t);
199static int	hwahc_destroy_child(dev_info_t *, usb_port_t);
200static int	hwahc_cleanup_child(dev_info_t *);
201static int	hwahc_delete_child(dev_info_t *, usb_port_t, uint_t, boolean_t);
202
203/* data transfer and notification handling */
204static void	hwahc_intr_cb(usb_pipe_handle_t, struct usb_intr_req *);
205static void	hwahc_intr_exc_cb(usb_pipe_handle_t, struct usb_intr_req *);
206static void	hwahc_handle_notif(hwahc_state_t *, mblk_t *);
207static void	hwahc_handle_xfer_result(hwahc_state_t *, uint8_t);
208static void	hwahc_stop_result_thread(hwahc_state_t *);
209static void	hwahc_result_thread(void *);
210static void	hwahc_handle_dn_notif(hwahc_state_t *, hwa_notif_dn_recvd_t *);
211static void	hwahc_notif_thread(void *);
212static void	hwahc_handle_dn(hwahc_state_t *, hwa_notif_dn_recvd_t *);
213static void	hwahc_drain_notif_queue(hwahc_state_t *);
214static void	hwahc_rpipe_xfer_cb(dev_info_t *, usba_pipe_handle_data_t *,
215		wusb_wa_trans_wrapper_t *, usb_cr_t);
216
217static void	hwahc_trust_timeout_handler(void *arg);
218static void	hwahc_stop_trust_timer(wusb_dev_info_t *dev);
219
220static int hwahc_pipe_submit_periodic_req(wusb_wa_data_t *wa_data,
221	usba_pipe_handle_data_t *ph);
222
223/* hwa specific requests */
224static int	hwahc_set_chid(hwahc_state_t *, uint8_t *);
225
226/* helper functions */
227static usb_port_t hwahc_get_port_num(hwahc_state_t *, struct devctl_iocdata *);
228static dev_info_t *hwahc_get_child_dip(hwahc_state_t *, usb_port_t);
229
230static struct cb_ops hwahc_cb_ops = {
231	hwahc_open,			/* Open */
232	hwahc_close,			/* Close */
233	nodev,				/* Strategy */
234	nodev,				/* Print */
235	nodev,				/* Dump */
236	nodev,				/* Read */
237	nodev,				/* Write */
238	hwahc_ioctl,			/* Ioctl */
239	nodev,				/* Devmap */
240	nodev,				/* Mmap */
241	nodev,				/* Segmap */
242	nochpoll,			/* Poll */
243	ddi_prop_op,			/* cb_prop_op */
244	NULL,				/* Streamtab */
245	D_MP				/* Driver compatibility flag */
246};
247
248static struct bus_ops hwahc_busops = {
249	BUSO_REV,
250	nullbusmap,			/* bus_map */
251	NULL,				/* bus_get_intrspec */
252	NULL,				/* bus_add_intrspec */
253	NULL,				/* bus_remove_intrspec */
254	NULL,				/* bus_map_fault */
255	ddi_dma_map,			/* bus_dma_map */
256	ddi_dma_allochdl,
257	ddi_dma_freehdl,
258	ddi_dma_bindhdl,
259	ddi_dma_unbindhdl,
260	ddi_dma_flush,
261	ddi_dma_win,
262	ddi_dma_mctl,			/* bus_dma_ctl */
263	hwahc_bus_ctl,			/* bus_ctl */
264	ddi_bus_prop_op,		/* bus_prop_op */
265	hwahc_busop_get_eventcookie,	/* bus_get_eventcookie */
266	hwahc_busop_add_eventcall,	/* bus_add_eventcall */
267	hwahc_busop_remove_eventcall,	/* bus_remove_eventcall */
268	NULL,				/* bus_post_event */
269	NULL,				/* bus_intr_ctl */
270	hwahc_bus_config,		/* bus_config */
271	hwahc_bus_unconfig,		/* bus_unconfig */
272	NULL,				/* bus_fm_init */
273	NULL,				/* bus_fm_fini */
274	NULL,				/* bus_fm_access_enter */
275	NULL,				/* bus_fm_access_exit */
276	NULL,				/* bus_power */
277};
278
279static struct dev_ops hwahc_ops = {
280	DEVO_REV,			/* Devo_rev */
281	0,				/* Refcnt */
282	hwahc_info,			/* Info */
283	nulldev,			/* Identify */
284	nulldev,			/* Probe */
285	hwahc_attach,			/* Attach */
286	hwahc_detach,			/* Detach */
287	nodev,				/* Reset */
288	&hwahc_cb_ops,			/* Driver operations */
289	&hwahc_busops,			/* Bus operations */
290	hwahc_power,			/* Power */
291	ddi_quiesce_not_needed,		/* devo_quiesce */
292};
293
294static struct modldrv hwahc_modldrv =	{
295	&mod_driverops,
296	"WUSB hwa-hc driver",
297	&hwahc_ops
298};
299
300static struct modlinkage modlinkage = {
301	MODREV_1,
302	&hwahc_modldrv,
303	NULL
304};
305
306/* events from parent */
307static usb_event_t hwahc_events = {
308	hwahc_disconnect_event_cb,
309	hwahc_reconnect_event_cb,
310	hwahc_pre_suspend_event_cb,
311	hwahc_post_resume_event_cb
312};
313
314/*
315 * events support for children
316 * A map tween USBA_EVENTs and DDI_EVENTs.
317 */
318static ndi_event_definition_t hwahc_ndi_event_defs[] = {
319	{USBA_EVENT_TAG_HOT_REMOVAL, DDI_DEVI_REMOVE_EVENT, EPL_KERNEL,
320						NDI_EVENT_POST_TO_ALL},
321	{USBA_EVENT_TAG_HOT_INSERTION, DDI_DEVI_INSERT_EVENT, EPL_KERNEL,
322						NDI_EVENT_POST_TO_ALL},
323	{USBA_EVENT_TAG_POST_RESUME, USBA_POST_RESUME_EVENT, EPL_KERNEL,
324						NDI_EVENT_POST_TO_ALL},
325	{USBA_EVENT_TAG_PRE_SUSPEND, USBA_PRE_SUSPEND_EVENT, EPL_KERNEL,
326						NDI_EVENT_POST_TO_ALL}
327};
328
329#define	HWAHC_N_NDI_EVENTS \
330	(sizeof (hwahc_ndi_event_defs) / sizeof (ndi_event_definition_t))
331
332static	ndi_event_set_t hwahc_ndi_events = {
333	NDI_EVENTS_REV1, HWAHC_N_NDI_EVENTS, hwahc_ndi_event_defs};
334
335/* transfer callbacks */
336static wusb_wa_cb_t hwahc_cbs = {
337	hwahc_pipe_submit_periodic_req,
338	hwahc_intr_cb,
339	hwahc_intr_exc_cb,
340	hwahc_rpipe_xfer_cb
341};
342
343
344/*
345 * Module-wide initialization routine.
346 */
347int
348_init(void)
349{
350	int rval;
351
352	if ((rval = ddi_soft_state_init(&hwahc_statep, sizeof (hwahc_state_t),
353	    HWAHC_INSTS)) != 0) {
354
355		return (rval);
356	}
357
358	if ((rval = mod_install(&modlinkage)) != 0) {
359		ddi_soft_state_fini(&hwahc_statep);
360	}
361
362	return (rval);
363}
364
365
366/*
367 * Module-wide tear-down routine.
368 */
369int
370_fini(void)
371{
372	int rval;
373
374	if ((rval = mod_remove(&modlinkage)) == 0) {
375		/* Release per module resources */
376		ddi_soft_state_fini(&hwahc_statep);
377	}
378
379	return (rval);
380}
381
382
383int
384_info(struct modinfo *modinfop)
385{
386	return (mod_info(&modlinkage, modinfop));
387}
388
389
390/*
391 * hwahc_info:
392 *	Get minor number, instance number, etc.
393 */
394/*ARGSUSED*/
395static int
396hwahc_info(dev_info_t *dip, ddi_info_cmd_t infocmd,
397	void *arg, void **result)
398{
399	hwahc_state_t	*hwahcp;
400	int error = DDI_FAILURE;
401	int instance = HWAHC_MINOR_TO_INSTANCE(getminor((dev_t)arg));
402
403	switch (infocmd) {
404	case DDI_INFO_DEVT2DEVINFO:
405		if ((hwahcp = ddi_get_soft_state(hwahc_statep,
406		    instance)) != NULL) {
407			*result = hwahcp->hwahc_dip;
408			if (*result != NULL) {
409				error = DDI_SUCCESS;
410			}
411		} else {
412			*result = NULL;
413		}
414		break;
415	case DDI_INFO_DEVT2INSTANCE:
416		*result = (void *)(uintptr_t)instance;
417		error = DDI_SUCCESS;
418		break;
419	default:
420		break;
421	}
422
423	return (error);
424}
425
426
427/*
428 * hwahc_attach:
429 *	Attach or resume.
430 *
431 *	For attach, initialize state and device, including:
432 *		state variables, locks, device node,
433 *		resource initialization, event registration,
434 *		device registration with system
435 *		power management, hotplugging
436 *	For resume, restore device and state
437 */
438static int
439hwahc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
440{
441	int				instance = ddi_get_instance(dip);
442	hwahc_state_t			*hwahcp = NULL;
443	usb_client_dev_data_t		*dev_data;
444	struct usb_cfg_data		*cfg_data;
445	usba_hcdi_register_args_t	hcdi_args;
446	int				rval;
447	char *pathname;
448
449	USB_DPRINTF_L3(PRINT_MASK_ATTA, NULL, "hwahc_attach: cmd=%d", cmd);
450
451	switch (cmd) {
452	case DDI_ATTACH:
453		break;
454	case DDI_RESUME:
455		(void) hwahc_cpr_resume(dip);
456
457		return (DDI_SUCCESS);
458	default:
459		USB_DPRINTF_L2(PRINT_MASK_ATTA, NULL,
460		    "hwahc_attach: failed");
461
462		return (DDI_FAILURE);
463	}
464
465	/*
466	 * Allocate soft state information.
467	 */
468	rval = ddi_soft_state_zalloc(hwahc_statep, instance);
469	if (rval != DDI_SUCCESS) {
470		USB_DPRINTF_L2(PRINT_MASK_ATTA, NULL,
471		    "hwahc_attach: cannot allocate soft state for instance %d",
472		    instance);
473
474		return (USB_FAILURE);
475	}
476
477	hwahcp = ddi_get_soft_state(hwahc_statep, instance);
478	if (hwahcp == NULL) {
479		USB_DPRINTF_L2(PRINT_MASK_ATTA, NULL,
480		    "hwahc_attach: get soft state failed for instance %d",
481		    instance);
482
483		return (USB_FAILURE);
484	}
485
486	hwahcp->hwahc_log_handle = usb_alloc_log_hdl(dip, "hwahc",
487	    &hwahc_errlevel, &hwahc_errmask, &hwahc_instance_debug, 0);
488
489	/* initialize hc state */
490	hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_INIT_STATE;
491	hwahcp->hwahc_dip = dip;
492	hwahcp->hwahc_instance = instance;
493
494	/* register with USBA as client driver */
495	if (usb_client_attach(dip, USBDRV_VERSION, 0) != USB_SUCCESS) {
496		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
497		    "hwahc_attach: client attach failed");
498
499		goto fail;
500	}
501
502	if (usb_get_dev_data(dip, &dev_data, USB_PARSE_LVL_IF, 0) !=
503	    USB_SUCCESS) {
504		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
505		    "hwahc_attach: cannot get dev_data");
506
507		goto fail;
508	}
509
510	/* initialize mutex and cv */
511	mutex_init(&hwahcp->hwahc_mutex, NULL, MUTEX_DRIVER,
512	    dev_data->dev_iblock_cookie);
513	cv_init(&hwahcp->hwahc_result_thread_cv, NULL, CV_DRIVER, NULL);
514
515	hwahcp->hwahc_flags |= HWAHC_LOCK_INITED;
516	hwahcp->hwahc_dev_data = dev_data;
517
518	/* initialize data transfer function related structure */
519	if (wusb_wa_data_init(dip, &hwahcp->hwahc_wa_data, &hwahc_cbs,
520	    dev_data, PRINT_MASK_ATTA,
521	    hwahcp->hwahc_log_handle) != USB_SUCCESS) {
522		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
523		    "hwahc_attach: init wa data failed");
524
525		goto fail;
526	}
527
528	hwahcp->hwahc_flags |= HWAHC_WA_INITED;
529	cfg_data = dev_data->dev_curr_cfg;
530
531	/* parse the security descrs from the configuration descr cloud */
532	if (hwahc_parse_security_data(&hwahcp->hwahc_secrt_data, cfg_data) !=
533	    USB_SUCCESS) {
534		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
535		    "hwahc_attach: parse security descrs failed");
536
537		goto fail;
538	}
539
540	hwahcp->hwahc_default_pipe = dev_data->dev_default_ph;
541	hwahcp->hwahc_wa_data.wa_private_data = (void *)hwahcp;
542	hwahcp->hwahc_wa_data.wa_default_pipe = hwahcp->hwahc_default_pipe;
543
544	usb_free_descr_tree(dip, dev_data);
545
546	hwahcp->hwahc_dev_state = USB_DEV_ONLINE;
547
548	/* now create components to power manage this device */
549	hwahc_create_pm_components(dip, hwahcp);
550
551	/*
552	 * Event definition and registration
553	 *
554	 * allocate a new NDI event handle as a nexus driver
555	 */
556	(void) ndi_event_alloc_hdl(dip, 0, &hwahcp->hwahc_ndi_event_hdl,
557	    NDI_SLEEP);
558
559	/*
560	 * bind our NDI events with the event handle,
561	 * i.e. Define the events set we're to support as a nexus driver.
562	 *
563	 * These events will be used by bus_ops functions to register callbacks.
564	 */
565	if (ndi_event_bind_set(hwahcp->hwahc_ndi_event_hdl, &hwahc_ndi_events,
566	    NDI_SLEEP)) {
567		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
568		    "hwahc_attach: binding event set failed");
569
570		goto fail;
571	}
572
573
574	/*
575	 * Register USB events to USBA(the parent) to get callbacks as a
576	 * child of (root) hub
577	 */
578	if (usb_register_event_cbs(dip, &hwahc_events, 0) != USB_SUCCESS) {
579		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
580		    "hwahc_attach: register_events failed");
581
582		goto fail;
583	}
584
585	hwahcp->hwahc_flags |= HWAHC_EVENTS_REGISTERED;
586
587	/* create minor nodes */
588	if (ddi_create_minor_node(dip, "hwahc", S_IFCHR,
589	    instance << HWAHC_MINOR_INSTANCE_SHIFT,
590	    DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
591
592		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
593		    "hwahc_attach: cannot create minor node");
594
595		goto fail;
596	}
597
598	hwahcp->hwahc_flags |= HWAHC_MINOR_NODE_CREATED;
599
600	hwahcp->hwahc_hcdi_ops = hwahc_alloc_hcdi_ops(hwahcp);
601
602	/* register this hc instance with usba HCD interface */
603	hcdi_args.usba_hcdi_register_version = HCDI_REGISTER_VERSION;
604	hcdi_args.usba_hcdi_register_dip = dip;
605	hcdi_args.usba_hcdi_register_ops = hwahcp->hwahc_hcdi_ops;
606
607	/* use parent dma attr here */
608	hcdi_args.usba_hcdi_register_dma_attr = usba_get_hc_dma_attr(dip);
609	hcdi_args.usba_hcdi_register_iblock_cookie = NULL;
610
611	if (usba_hcdi_register(&hcdi_args, 0) != USB_SUCCESS) {
612		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
613		    "hwahc_attach: usba_hcdi_register failed");
614
615		goto fail;
616	}
617
618	hwahcp->hwahc_flags |= HWAHC_HCDI_REGISTERED;
619
620	/* create hub minor node and register to usba HUBD interface */
621	if (hwahc_hub_attach(hwahcp) != USB_SUCCESS) {
622		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
623		    "hwahc_attach: hub attach failed");
624
625		goto fail;
626	}
627	hwahcp->hwahc_flags |= HWAHC_HUBREG;
628
629	/* intialize WUSB host function related structure */
630	hwahc_hc_data_init(hwahcp);
631	hwahcp->hwahc_flags |= HWAHC_HC_INITED;
632
633	/* can be combined with wusb_wa_data_init() */
634	if (hwahc_wa_start(hwahcp) != USB_SUCCESS) {
635
636		goto fail;
637	}
638
639	hwahcp->hwahc_flags |= HWAHC_WA_STARTED;
640
641	/* report this dev */
642	ddi_report_dev(dip);
643
644	hwahc_pm_idle_component(hwahcp);
645
646	mutex_enter(&(hwahcp->hwahc_mutex));
647	hwahc_print_secrt_data(hwahcp);
648	mutex_exit(&(hwahcp->hwahc_mutex));
649
650	if (uwb_dev_online(dip) != USB_SUCCESS) {
651		goto fail;
652	}
653
654	return (DDI_SUCCESS);
655
656fail:
657	pathname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
658
659	/* log this message to usba_debug_buf */
660	USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
661	    "cannot attach %s", ddi_pathname(dip, pathname));
662
663	kmem_free(pathname, MAXPATHLEN);
664
665	if (hwahcp) {
666		hwahc_pm_idle_component(hwahcp);
667
668		rval = hwahc_cleanup(dip, hwahcp);
669		if (rval != USB_SUCCESS) {
670			USB_DPRINTF_L2(PRINT_MASK_ATTA,
671			    hwahcp->hwahc_log_handle,
672			    "failure to complete cleanup after attach failure");
673		}
674	}
675
676	return (DDI_FAILURE);
677}
678
679
680/*
681 * hwahc_detach:
682 *	detach or suspend driver instance
683 *
684 * Note: in detach, only contention threads is from pm and disconnnect.
685 */
686static int
687hwahc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
688{
689	int		instance = ddi_get_instance(dip);
690	hwahc_state_t	*hwahcp = ddi_get_soft_state(hwahc_statep, instance);
691	int		rval = DDI_FAILURE;
692
693
694	USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
695	    "hwahc_detach: cmd = %d", cmd);
696
697	switch (cmd) {
698	case DDI_DETACH:
699		USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
700		    "offline uwb device for dip: 0x%p", (void *)dip);
701		/* offline the hwarc interface */
702		(void) uwb_dev_offline(dip);
703		if (hwahcp) {
704			rval = hwahc_cleanup(dip, hwahcp);
705		}
706
707		break;
708	case DDI_SUSPEND:
709		rval = hwahc_cpr_suspend(dip);
710
711		break;
712	default:
713
714		break;
715	}
716
717	return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
718}
719
720
721/*
722 * hwahc_cleanup:
723 *	clean up on attach failure or detach
724 */
725static int
726hwahc_cleanup(dev_info_t *dip, hwahc_state_t *hwahcp)
727{
728	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
729	    "hwahc_cleanup: start");
730
731	if ((hwahcp->hwahc_flags & HWAHC_LOCK_INITED) == 0) {
732
733		goto done;
734	}
735
736	/*
737	 * deallocate events, if events are still registered
738	 * (ie. children still attached) then we have to fail the detach
739	 */
740	if (hwahcp->hwahc_ndi_event_hdl &&
741	    (ndi_event_free_hdl(hwahcp->hwahc_ndi_event_hdl) != NDI_SUCCESS)) {
742
743		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
744		    "hwahc_cleanup: ndi_event_free_hdl failed");
745
746		return (USB_FAILURE);
747	}
748
749	if (hwahcp->hwahc_flags & HWAHC_EVENTS_REGISTERED) {
750		/* unregister events */
751		usb_unregister_event_cbs(dip, &hwahc_events);
752	}
753
754	if (hwahcp->hwahc_flags & HWAHC_HCDI_REGISTERED) {
755		/* unregister the instance with usba HCD interface */
756		usba_hcdi_unregister(hwahcp->hwahc_dip);
757	}
758
759	mutex_enter(&hwahcp->hwahc_mutex);
760
761	if (hwahcp->hwahc_hw_state != HWAHC_HW_STOPPED) {
762		/* stop the hw if it is enabled */
763		(void) hwahc_hc_final_stop(hwahcp);
764	}
765
766	if (hwahcp->hwahc_flags & HWAHC_WA_STARTED) {
767		/* can be combined with wusb_wa_data_fini() */
768		mutex_exit(&hwahcp->hwahc_mutex);
769		hwahc_wa_stop(hwahcp);
770		mutex_enter(&hwahcp->hwahc_mutex);
771	}
772
773	if (hwahcp->hwahc_flags & HWAHC_HC_INITED) {
774		/* deinitialize the WUSB host function related structure */
775		hwahc_hc_data_fini(hwahcp);
776	}
777
778	mutex_exit(&hwahcp->hwahc_mutex);
779
780	if (hwahcp->hwahc_pm) {
781		/* destroy power management components */
782		hwahc_destroy_pm_components(hwahcp);
783	}
784
785	if (hwahcp->hwahc_flags & HWAHC_HUBREG) {
786		/* unregister the instance from usba HUBD interface */
787		if (hwahc_hub_detach(hwahcp) != USB_SUCCESS) {
788
789			return (USB_FAILURE);
790		}
791	}
792
793	if (hwahcp->hwahc_hcdi_ops) {
794		usba_free_hcdi_ops(hwahcp->hwahc_hcdi_ops);
795	}
796
797	mutex_enter(&hwahcp->hwahc_mutex);
798	if (hwahcp->hwahc_secrt_data.secrt_encry_descr) {
799		/* free security descrs */
800		kmem_free(hwahcp->hwahc_secrt_data.secrt_encry_descr,
801		    sizeof (usb_encryption_descr_t) *
802		    hwahcp->hwahc_secrt_data.secrt_n_encry);
803	}
804
805	if (hwahcp->hwahc_flags & HWAHC_WA_INITED) {
806		/* deinitialize data transfer function related structure */
807		wusb_wa_data_fini(&hwahcp->hwahc_wa_data);
808	}
809	mutex_exit(&hwahcp->hwahc_mutex);
810
811	if (hwahcp->hwahc_flags & HWAHC_MINOR_NODE_CREATED) {
812		/* remove all the minor nodes */
813		ddi_remove_minor_node(dip, NULL);
814	}
815
816	/* destroy mutex and cv */
817	mutex_destroy(&hwahcp->hwahc_mutex);
818	cv_destroy(&hwahcp->hwahc_result_thread_cv);
819
820done:
821	/* unregister the client driver from usba */
822	usb_client_detach(dip, hwahcp->hwahc_dev_data);
823
824	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
825	    "hwahc_cleanup: end");
826
827	usb_free_log_hdl(hwahcp->hwahc_log_handle);
828
829	/* remove all properties created */
830	ddi_prop_remove_all(dip);
831
832	/* free the soft state information */
833	ddi_soft_state_free(hwahc_statep, ddi_get_instance(dip));
834
835	return (USB_SUCCESS);
836}
837
838
839/*ARGSUSED*/
840static int
841hwahc_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
842{
843	hwahc_state_t	*hwahcp;
844
845	if ((hwahcp = ddi_get_soft_state(hwahc_statep,
846	    HWAHC_MINOR_TO_INSTANCE(getminor(*devp)))) == NULL) {
847
848		return (ENXIO);
849	}
850
851	USB_DPRINTF_L4(PRINT_MASK_OPEN, hwahcp->hwahc_log_handle,
852	    "hwahc_open: start");
853
854	mutex_enter(&hwahcp->hwahc_mutex);
855	/* exclusive open */
856	if ((flag & FEXCL) && (hwahcp->hwahc_open_count > 0)) {
857		mutex_exit(&hwahcp->hwahc_mutex);
858
859		return (EBUSY);
860	}
861
862	if ((hwahcp->hwahc_dev_state == USB_DEV_DISCONNECTED) ||
863	    (hwahcp->hwahc_dev_state == USB_DEV_SUSPENDED)) {
864		mutex_exit(&hwahcp->hwahc_mutex);
865
866		return (EIO);
867	}
868
869	hwahcp->hwahc_open_count++;
870
871	mutex_exit(&hwahcp->hwahc_mutex);
872
873	/* raise to full power and keep it until close */
874	hwahc_pm_busy_component(hwahcp);
875	(void) pm_raise_power(hwahcp->hwahc_dip, 0, USB_DEV_OS_FULL_PWR);
876
877	USB_DPRINTF_L4(PRINT_MASK_OPEN, hwahcp->hwahc_log_handle,
878	    "hwahc_open: end");
879
880	return (0);
881}
882
883
884/*ARGSUSED*/
885static int
886hwahc_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
887{
888	hwahc_state_t	*hwahcp;
889
890	if ((hwahcp = ddi_get_soft_state(hwahc_statep,
891	    HWAHC_MINOR_TO_INSTANCE(getminor(dev)))) == NULL) {
892
893		return (ENXIO);
894	}
895
896	USB_DPRINTF_L4(PRINT_MASK_CLOSE, hwahcp->hwahc_log_handle,
897	    "hwahc_close: start");
898
899	mutex_enter(&hwahcp->hwahc_mutex);
900	if (hwahcp->hwahc_open_count == 0) {
901		USB_DPRINTF_L2(PRINT_MASK_CLOSE, hwahcp->hwahc_log_handle,
902		    "hwahc_close: already closed");
903		mutex_exit(&hwahcp->hwahc_mutex);
904
905		return (EINVAL);
906	}
907
908	hwahcp->hwahc_open_count--;
909	mutex_exit(&hwahcp->hwahc_mutex);
910
911	hwahc_pm_idle_component(hwahcp);
912
913	USB_DPRINTF_L4(PRINT_MASK_CLOSE, hwahcp->hwahc_log_handle,
914	    "hwahc_close: end");
915
916	return (0);
917}
918
919/* retrieve port number from devctl data */
920static usb_port_t
921hwahc_get_port_num(hwahc_state_t *hwahcp, struct devctl_iocdata *dcp)
922{
923	int32_t port;
924
925	ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
926
927	/* Get which port to operate on.  */
928	if (nvlist_lookup_int32(ndi_dc_get_ap_data(dcp), "port", &port) != 0) {
929		USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
930		    "hwahc_get_port_num: port lookup failed");
931		port = 0;
932	}
933
934	USB_DPRINTF_L4(PRINT_MASK_CBOPS,  hwahcp->hwahc_log_handle,
935	    "hwahc_get_port_num: hwahcp=0x%p, port=%d", (void *)hwahcp,
936	    port);
937
938	return ((usb_port_t)port);
939}
940
941/* return the child dip on a certain port */
942static dev_info_t *
943hwahc_get_child_dip(hwahc_state_t *hwahcp, usb_port_t port)
944{
945	wusb_hc_data_t		*hc_data;
946	dev_info_t		*child_dip;
947
948	hc_data = &hwahcp->hwahc_hc_data;
949
950	/* check port range to prevent an illegal number */
951	if (port > hc_data->hc_num_ports) {
952		return (NULL);
953	}
954
955	mutex_enter(&hc_data->hc_mutex);
956	child_dip = hc_data->hc_children_dips[port];
957	mutex_exit(&hc_data->hc_mutex);
958
959	return (child_dip);
960}
961
962/*
963 * hwahc_cfgadm_state:
964 *
965 *	child_dip list		child_state		cfgadm_state
966 *	--------------		----------		------------
967 *	!= NULL			connected		configured or
968 *							unconfigured
969 *	!= NULL			not connected		disconnect but
970 *							busy/still referenced
971 *	NULL			connected		logically disconnected
972 *	NULL			not connected		empty
973 */
974static uint_t
975hwahc_cfgadm_state(hwahc_state_t *hwahcp, usb_port_t port)
976{
977	uint_t		state;
978	dev_info_t	*child_dip = hwahc_get_child_dip(hwahcp, port);
979	wusb_hc_data_t	*hc_data = &hwahcp->hwahc_hc_data;
980	wusb_dev_info_t	*dev_info;
981
982	if (child_dip == NULL) {
983
984		return (HWAHC_CFGADM_INVALID);
985	}
986
987	mutex_enter(&hc_data->hc_mutex);
988	dev_info = hc_data->hc_dev_infos[port];
989	if (dev_info) {
990		if (dev_info->wdev_state == WUSB_STATE_CONFIGURED) {
991			if (child_dip &&
992			    (DEVI_IS_DEVICE_OFFLINE(child_dip) ||
993			    !i_ddi_devi_attached(child_dip))) {
994				state = HWAHC_CFGADM_UNCONFIGURED;
995			} else if (!child_dip) {
996				state = HWAHC_CFGADM_UNCONFIGURED;
997			} else {
998				state = HWAHC_CFGADM_CONFIGURED;
999			}
1000		} else if (dev_info->wdev_state == WUSB_STATE_UNCONNTED) {
1001			if (child_dip) {
1002				state = HWAHC_CFGADM_STILL_REFERENCED;
1003			} else {
1004				state = HWAHC_CFGADM_DISCONNECTED;
1005			}
1006		} else {
1007			if (child_dip) {
1008				state = HWAHC_CFGADM_STILL_REFERENCED;
1009			} else {
1010				state = HWAHC_CFGADM_UNCONFIGURED;
1011			}
1012		}
1013	} else {
1014		state = HWAHC_CFGADM_EMPTY;
1015	}
1016	mutex_exit(&hc_data->hc_mutex);
1017
1018	USB_DPRINTF_L4(PRINT_MASK_CBOPS,  hwahcp->hwahc_log_handle,
1019	    "hwahc_cfgadm_state: hwahcp=0x%p, port=%d state=0x%x",
1020	    (void *) hwahcp, port, state);
1021
1022	return (state);
1023}
1024
1025/* cfgadm ioctl support, now only implements list function */
1026/* ARGSUSED */
1027static int
1028hwahc_cfgadm_ioctl(hwahc_state_t *hwahcp, int cmd, intptr_t arg,
1029	int mode, cred_t *credp, int *rvalp)
1030{
1031	dev_info_t		*rh_dip;
1032	dev_info_t		*child_dip;
1033	struct devctl_iocdata	*dcp = NULL;
1034	usb_port_t		port = 0;
1035	devctl_ap_state_t	ap_state;
1036	int			circ, rh_circ, prh_circ;
1037	int			rv = 0;
1038	char			*msg;
1039
1040	/* read devctl ioctl data */
1041	if ((cmd != DEVCTL_AP_CONTROL) &&
1042	    (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)) {
1043
1044		return (EFAULT);
1045	}
1046
1047	mutex_enter(&hwahcp->hwahc_mutex);
1048
1049	rh_dip = hwahcp->hwahc_hubd->h_usba_device->usb_root_hub_dip;
1050
1051	switch (cmd) {
1052	case DEVCTL_AP_DISCONNECT:
1053	case DEVCTL_AP_UNCONFIGURE:
1054	case DEVCTL_AP_CONFIGURE:
1055		if (hwahcp->hwahc_dev_state == USB_DEV_DISCONNECTED) {
1056			USB_DPRINTF_L2(PRINT_MASK_ATTA,
1057			    hwahcp->hwahc_log_handle,
1058			    "hwahc_cfgadm_ioctl: dev already gone");
1059			mutex_exit(&hwahcp->hwahc_mutex);
1060			if (dcp) {
1061				ndi_dc_freehdl(dcp);
1062			}
1063
1064			return (EIO);
1065		}
1066		/* FALLTHROUGH */
1067	case DEVCTL_AP_GETSTATE:
1068		if ((port = hwahc_get_port_num(hwahcp, dcp)) == 0) {
1069			USB_DPRINTF_L2(PRINT_MASK_ATTA,
1070			    hwahcp->hwahc_log_handle,
1071			    "hwahc_cfgadm_ioctl: bad port");
1072			mutex_exit(&hwahcp->hwahc_mutex);
1073			if (dcp) {
1074				ndi_dc_freehdl(dcp);
1075			}
1076
1077			return (EINVAL);
1078		}
1079		break;
1080	case DEVCTL_AP_CONTROL:
1081
1082		break;
1083	default:
1084		mutex_exit(&hwahcp->hwahc_mutex);
1085		if (dcp) {
1086			ndi_dc_freehdl(dcp);
1087		}
1088
1089		return (ENOTTY);
1090	}
1091
1092	/* should not happen, just in case */
1093	if (hwahcp->hwahc_dev_state == USB_DEV_SUSPENDED) {
1094		mutex_exit(&hwahcp->hwahc_mutex);
1095		if (dcp) {
1096			ndi_dc_freehdl(dcp);
1097		}
1098
1099		return (EIO);
1100	}
1101
1102	mutex_exit(&hwahcp->hwahc_mutex);
1103
1104	ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
1105	ndi_devi_enter(rh_dip, &rh_circ);
1106	ndi_devi_enter(hwahcp->hwahc_dip, &circ);
1107
1108	mutex_enter(&hwahcp->hwahc_mutex);
1109
1110	switch (cmd) {
1111	case DEVCTL_AP_DISCONNECT:
1112		/* TODO: not supported now */
1113		rv = EIO;
1114		break;
1115	case DEVCTL_AP_UNCONFIGURE:
1116		/* TODO: not supported now */
1117		rv = EIO;
1118		break;
1119	case DEVCTL_AP_CONFIGURE:
1120		/* TODO: not supported now */
1121		rv = EIO;
1122		break;
1123	case DEVCTL_AP_GETSTATE:
1124		switch (hwahc_cfgadm_state(hwahcp, port)) {
1125		case HWAHC_CFGADM_DISCONNECTED:
1126			/* port previously 'disconnected' by cfgadm */
1127			ap_state.ap_rstate = AP_RSTATE_DISCONNECTED;
1128			ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED;
1129			ap_state.ap_condition = AP_COND_OK;
1130
1131			break;
1132		case HWAHC_CFGADM_UNCONFIGURED:
1133			ap_state.ap_rstate = AP_RSTATE_CONNECTED;
1134			ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED;
1135			ap_state.ap_condition = AP_COND_OK;
1136
1137			break;
1138		case HWAHC_CFGADM_CONFIGURED:
1139			ap_state.ap_rstate = AP_RSTATE_CONNECTED;
1140			ap_state.ap_ostate = AP_OSTATE_CONFIGURED;
1141			ap_state.ap_condition = AP_COND_OK;
1142
1143			break;
1144		case HWAHC_CFGADM_STILL_REFERENCED:
1145			ap_state.ap_rstate = AP_RSTATE_EMPTY;
1146			ap_state.ap_ostate = AP_OSTATE_CONFIGURED;
1147			ap_state.ap_condition = AP_COND_UNUSABLE;
1148
1149			break;
1150		case HWAHC_CFGADM_EMPTY:
1151		default:
1152			ap_state.ap_rstate = AP_RSTATE_EMPTY;
1153			ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED;
1154			ap_state.ap_condition = AP_COND_OK;
1155
1156			break;
1157		}
1158
1159		ap_state.ap_last_change = (time_t)-1;
1160		ap_state.ap_error_code = 0;
1161		ap_state.ap_in_transition = 0;
1162
1163		USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
1164		    "DEVCTL_AP_GETSTATE: "
1165		    "ostate=0x%x, rstate=0x%x, condition=0x%x",
1166		    ap_state.ap_ostate,
1167		    ap_state.ap_rstate, ap_state.ap_condition);
1168
1169		/* copy the return-AP-state information to the user space */
1170		if (ndi_dc_return_ap_state(&ap_state, dcp) != NDI_SUCCESS) {
1171			rv = EFAULT;
1172		}
1173
1174		break;
1175	case DEVCTL_AP_CONTROL:
1176	{
1177		/*
1178		 * Generic devctl for hardware-specific functionality.
1179		 * For list of sub-commands see hubd_impl.h
1180		 */
1181		hubd_ioctl_data_t	ioc;	/* for 64 byte copies */
1182
1183		/* copy user ioctl data in first */
1184#ifdef _MULTI_DATAMODEL
1185		if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
1186			hubd_ioctl_data_32_t ioc32;
1187
1188			if (ddi_copyin((void *)arg, (void *)&ioc32,
1189			    sizeof (ioc32), mode) != 0) {
1190				rv = EFAULT;
1191
1192				break;
1193			}
1194			ioc.cmd		= (uint_t)ioc32.cmd;
1195			ioc.port	= (uint_t)ioc32.port;
1196			ioc.get_size	= (uint_t)ioc32.get_size;
1197			ioc.buf		= (caddr_t)(uintptr_t)ioc32.buf;
1198			ioc.bufsiz	= (uint_t)ioc32.bufsiz;
1199			ioc.misc_arg	= (uint_t)ioc32.misc_arg;
1200		} else
1201#endif /* _MULTI_DATAMODEL */
1202		if (ddi_copyin((void *)arg, (void *)&ioc, sizeof (ioc),
1203		    mode) != 0) {
1204			rv = EFAULT;
1205
1206			break;
1207		}
1208
1209		USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
1210		    "DEVCTL_AP_CONTROL: ioc: cmd=0x%x port=%d get_size=%d"
1211		    "\n\tbuf=0x%p, bufsiz=%d,  misc_arg=%d", ioc.cmd,
1212		    ioc.port, ioc.get_size, (void *) ioc.buf, ioc.bufsiz,
1213		    ioc.misc_arg);
1214
1215		/*
1216		 * To avoid BE/LE and 32/64 issues, a get_size always
1217		 * returns a 32-bit number.
1218		 */
1219		if (ioc.get_size != 0 && ioc.bufsiz != (sizeof (uint32_t))) {
1220			rv = EINVAL;
1221
1222			break;
1223		}
1224
1225		switch (ioc.cmd) {
1226		case USB_DESCR_TYPE_DEV:
1227			msg = "DEVCTL_AP_CONTROL: GET_DEVICE_DESC";
1228			if (ioc.get_size) {
1229				/* uint32 so this works 32/64 */
1230				uint32_t size = sizeof (usb_dev_descr_t);
1231
1232				if (ddi_copyout((void *)&size, ioc.buf,
1233				    ioc.bufsiz, mode) != 0) {
1234					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1235					    hwahcp->hwahc_log_handle,
1236					    "%s: get_size copyout failed", msg);
1237					rv = EIO;
1238
1239					break;
1240				}
1241			} else {	/* send out the actual descr */
1242				usb_dev_descr_t *dev_descrp;
1243
1244				/* check child_dip */
1245				if ((child_dip = hwahc_get_child_dip(hwahcp,
1246				    ioc.port)) == NULL) {
1247					rv = EINVAL;
1248
1249					break;
1250				}
1251
1252				dev_descrp = usb_get_dev_descr(child_dip);
1253				if (ioc.bufsiz != sizeof (*dev_descrp)) {
1254					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1255					    hwahcp->hwahc_log_handle,
1256					    "%s: bufsize passed (%d) != sizeof "
1257					    "usba_device_descr_t (%d)", msg,
1258					    ioc.bufsiz, dev_descrp->bLength);
1259					rv = EINVAL;
1260
1261					break;
1262				}
1263
1264				if (ddi_copyout((void *)dev_descrp,
1265				    ioc.buf, ioc.bufsiz, mode) != 0) {
1266					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1267					    hwahcp->hwahc_log_handle,
1268					    "%s: copyout failed.", msg);
1269					rv = EIO;
1270
1271					break;
1272				}
1273			}
1274			break;
1275		case USB_DESCR_TYPE_CFG:
1276		{
1277			usba_device_t	*child_ud = NULL;
1278			uint32_t	idx = ioc.misc_arg;
1279			uint32_t	cfg_len = 0;
1280
1281			if ((child_dip =
1282			    hwahc_get_child_dip(hwahcp, ioc.port)) == NULL) {
1283				rv = EINVAL;
1284
1285				break;
1286			}
1287			child_ud = usba_get_usba_device(child_dip);
1288			cfg_len = (uint32_t)child_ud->usb_cfg_array_len[idx];
1289
1290			msg = "DEVCTL_AP_CONTROL: GET_CONFIG_DESC";
1291			if (ioc.get_size) {
1292				if (ddi_copyout((void *)&cfg_len, ioc.buf,
1293				    ioc.bufsiz, mode) != 0) {
1294					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1295					    hwahcp->hwahc_log_handle,
1296					    "%s: get_size copyout failed", msg);
1297					rv = EIO;
1298
1299					break;
1300				}
1301			} else {	/* send out the actual descr */
1302				uchar_t *cfg_descr =
1303				    child_ud->usb_cfg_array[idx];
1304
1305				if (ioc.bufsiz != cfg_len) {
1306					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1307					    hwahcp->hwahc_log_handle,
1308					    "%s: bufsize passed (%d) != size "
1309					    "of cfg_descr (%d)", msg,
1310					    ioc.bufsiz, cfg_len);
1311					rv = EINVAL;
1312
1313					break;
1314				}
1315
1316				if (ddi_copyout((void *)cfg_descr,
1317				    ioc.buf, ioc.bufsiz, mode) != 0) {
1318					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1319					    hwahcp->hwahc_log_handle,
1320					    "%s: copyout failed.", msg);
1321					rv = EIO;
1322
1323					break;
1324				}
1325			}
1326			break;
1327		}
1328		case USB_DESCR_TYPE_STRING:
1329		{
1330			char		*str;
1331			uint32_t	size;
1332			usba_device_t	*usba_device;
1333
1334			msg = "DEVCTL_AP_CONTROL: GET_STRING_DESCR";
1335			USB_DPRINTF_L4(PRINT_MASK_CBOPS,
1336			    hwahcp->hwahc_log_handle,
1337			    "%s: string request: %d", msg, ioc.misc_arg);
1338
1339			/* recheck */
1340			if ((child_dip =
1341			    hwahc_get_child_dip(hwahcp, ioc.port)) == NULL) {
1342				rv = EINVAL;
1343
1344				break;
1345			}
1346			usba_device = usba_get_usba_device(child_dip);
1347
1348			switch (ioc.misc_arg) {
1349			case HUBD_MFG_STR:
1350				str = usba_device->usb_mfg_str;
1351
1352				break;
1353			case HUBD_PRODUCT_STR:
1354				str = usba_device->usb_product_str;
1355
1356				break;
1357			case HUBD_SERIALNO_STR:
1358				str = usba_device->usb_serialno_str;
1359
1360				break;
1361			case HUBD_CFG_DESCR_STR:
1362				mutex_enter(&usba_device->usb_mutex);
1363				str = usba_device->usb_cfg_str_descr[
1364				    usba_device->usb_active_cfg_ndx];
1365				mutex_exit(&usba_device->usb_mutex);
1366
1367				break;
1368			default:
1369				USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1370				    hwahcp->hwahc_log_handle,
1371				    "%s: Invalid string request", msg);
1372				rv = EINVAL;
1373
1374				break;
1375			} /* end of switch */
1376
1377			if (rv != 0) {
1378
1379				break;
1380			}
1381
1382			size = (str != NULL) ? strlen(str) + 1 : 0;
1383			if (ioc.get_size) {
1384				if (ddi_copyout((void *)&size, ioc.buf,
1385				    ioc.bufsiz, mode) != 0) {
1386					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1387					    hwahcp->hwahc_log_handle,
1388					    "%s: copyout of size failed.", msg);
1389					rv = EIO;
1390
1391					break;
1392				}
1393			} else {
1394				if (size == 0) {
1395					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1396					    hwahcp->hwahc_log_handle,
1397					    "%s: String is NULL", msg);
1398					rv = EINVAL;
1399
1400					break;
1401				}
1402
1403				if (ioc.bufsiz != size) {
1404					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1405					    hwahcp->hwahc_log_handle,
1406					    "%s: string buf size wrong", msg);
1407					rv = EINVAL;
1408
1409					break;
1410				}
1411
1412				if (ddi_copyout((void *)str, ioc.buf,
1413				    ioc.bufsiz, mode) != 0) {
1414					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1415					    hwahcp->hwahc_log_handle,
1416					    "%s: copyout failed.", msg);
1417					rv = EIO;
1418
1419					break;
1420				}
1421			}
1422			break;
1423		}
1424		case HUBD_GET_CFGADM_NAME:
1425		{
1426			uint32_t   name_len;
1427			const char *name;
1428
1429			/* recheck */
1430			if ((child_dip =
1431			    hwahc_get_child_dip(hwahcp, ioc.port)) == NULL) {
1432				rv = EINVAL;
1433
1434				break;
1435			}
1436			name = ddi_node_name(child_dip);
1437			if (name == NULL) {
1438				name = "unsupported";
1439			}
1440			name_len = strlen(name) + 1;
1441
1442			msg = "DEVCTL_AP_CONTROL: HUBD_GET_CFGADM_NAME";
1443			USB_DPRINTF_L4(PRINT_MASK_CBOPS,
1444			    hwahcp->hwahc_log_handle,
1445			    "%s: name=%s name_len=%d", msg, name, name_len);
1446
1447			if (ioc.get_size) {
1448				if (ddi_copyout((void *)&name_len,
1449				    ioc.buf, ioc.bufsiz, mode) != 0) {
1450					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1451					    hwahcp->hwahc_log_handle,
1452					    "%s: copyout of size failed", msg);
1453					rv = EIO;
1454
1455					break;
1456				}
1457			} else {
1458				if (ioc.bufsiz != name_len) {
1459					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1460					    hwahcp->hwahc_log_handle,
1461					    "%s: string buf length wrong", msg);
1462					rv = EINVAL;
1463
1464					break;
1465				}
1466
1467				if (ddi_copyout((void *)name, ioc.buf,
1468				    ioc.bufsiz, mode) != 0) {
1469					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1470					    hwahcp->hwahc_log_handle,
1471					    "%s: copyout failed.", msg);
1472					rv = EIO;
1473
1474					break;
1475				}
1476			}
1477
1478			break;
1479		}
1480
1481		/*
1482		 * Return the config index for the currently-configured
1483		 * configuration.
1484		 */
1485		case HUBD_GET_CURRENT_CONFIG:
1486		{
1487			uint_t		config_index;
1488			uint32_t	size = sizeof (config_index);
1489			usba_device_t	*usba_device;
1490
1491			msg = "DEVCTL_AP_CONTROL: GET_CURRENT_CONFIG";
1492			USB_DPRINTF_L4(PRINT_MASK_CBOPS,
1493			    hwahcp->hwahc_log_handle, "%s", msg);
1494
1495			/*
1496			 * Return the config index for the configuration
1497			 * currently in use.
1498			 * Recheck if child_dip exists
1499			 */
1500			if ((child_dip =
1501			    hwahc_get_child_dip(hwahcp, ioc.port)) == NULL) {
1502				rv = EINVAL;
1503
1504				break;
1505			}
1506
1507			usba_device = usba_get_usba_device(child_dip);
1508			mutex_enter(&usba_device->usb_mutex);
1509			config_index = usba_device->usb_active_cfg_ndx;
1510			mutex_exit(&usba_device->usb_mutex);
1511
1512			if (ioc.get_size) {
1513				if (ddi_copyout((void *)&size,
1514				    ioc.buf, ioc.bufsiz, mode) != 0) {
1515					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1516					    hwahcp->hwahc_log_handle,
1517					    "%s: copyout of size failed.", msg);
1518					rv = EIO;
1519
1520					break;
1521				}
1522			} else {
1523				if (ioc.bufsiz != size) {
1524					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1525					    hwahcp->hwahc_log_handle,
1526					    "%s: buffer size wrong", msg);
1527					rv = EINVAL;
1528
1529					break;
1530				}
1531				if (ddi_copyout((void *)&config_index,
1532				    ioc.buf, ioc.bufsiz, mode) != 0) {
1533					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1534					    hwahcp->hwahc_log_handle,
1535					    "%s: copyout failed", msg);
1536					rv = EIO;
1537				}
1538			}
1539
1540			break;
1541		}
1542		case HUBD_GET_DEVICE_PATH:
1543		{
1544			char		*path;
1545			uint32_t	size;
1546
1547			msg = "DEVCTL_AP_CONTROL: GET_DEVICE_PATH";
1548			USB_DPRINTF_L4(PRINT_MASK_CBOPS,
1549			    hwahcp->hwahc_log_handle, "%s", msg);
1550
1551			/* Recheck if child_dip exists */
1552			if ((child_dip =
1553			    hwahc_get_child_dip(hwahcp, ioc.port)) == NULL) {
1554				rv = EINVAL;
1555
1556				break;
1557			}
1558
1559			/* ddi_pathname doesn't supply /devices, so we do. */
1560			path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
1561			(void) strcpy(path, "/devices");
1562			(void) ddi_pathname(child_dip, path + strlen(path));
1563			size = strlen(path) + 1;
1564
1565			USB_DPRINTF_L4(PRINT_MASK_CBOPS,
1566			    hwahcp->hwahc_log_handle,
1567			    "%s: device path=%s  size=%d", msg, path, size);
1568
1569			if (ioc.get_size) {
1570				if (ddi_copyout((void *)&size,
1571				    ioc.buf, ioc.bufsiz, mode) != 0) {
1572
1573					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1574					    hwahcp->hwahc_log_handle,
1575					    "%s: copyout of size failed.", msg);
1576					rv = EIO;
1577				}
1578			} else {
1579				if (ioc.bufsiz != size) {
1580					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1581					    hwahcp->hwahc_log_handle,
1582					    "%s: buffer wrong size.", msg);
1583					rv = EINVAL;
1584				} else if (ddi_copyout((void *)path,
1585				    ioc.buf, ioc.bufsiz, mode) != 0) {
1586					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1587					    hwahcp->hwahc_log_handle,
1588					    "%s: copyout failed.", msg);
1589					rv = EIO;
1590				}
1591			}
1592			kmem_free(path, MAXPATHLEN);
1593
1594			break;
1595		}
1596		case HUBD_REFRESH_DEVDB:
1597			msg = "DEVCTL_AP_CONTROL: HUBD_REFRESH_DEVDB";
1598			USB_DPRINTF_L4(PRINT_MASK_CBOPS,
1599			    hwahcp->hwahc_log_handle, "%s", msg);
1600
1601			if ((rv = usba_devdb_refresh()) != USB_SUCCESS) {
1602				USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1603				    hwahcp->hwahc_log_handle,
1604				    "%s: Failed: %d", msg, rv);
1605				rv = EIO;
1606			}
1607
1608			break;
1609		default:
1610			rv = ENOTSUP;
1611		}	/* end switch */
1612
1613		break;
1614	}
1615
1616	default:
1617		rv = ENOTTY;
1618	}
1619
1620	if (dcp) {
1621		ndi_dc_freehdl(dcp);
1622	}
1623
1624	mutex_exit(&hwahcp->hwahc_mutex);
1625
1626	ndi_devi_exit(hwahcp->hwahc_dip, circ);
1627	ndi_devi_exit(rh_dip, rh_circ);
1628	ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ);
1629
1630	return (rv);
1631}
1632
1633/* update CHID for the hc driver, return 0 on success */
1634static int
1635hwahc_set_chid(hwahc_state_t *hwahcp, uint8_t *chid)
1636{
1637	wusb_hc_data_t	*hc_data = &hwahcp->hwahc_hc_data;
1638
1639	ASSERT(!mutex_owned(&hc_data->hc_mutex));
1640
1641	/* same as the old CHID, return success */
1642	if (memcmp(chid, hc_data->hc_chid, 16) == 0) {
1643
1644		return (0);
1645	}
1646
1647	/*
1648	 * stop hw from working before updating CHID
1649	 * this may not be necessary but so far we don't know
1650	 * other ways to do it safely
1651	 */
1652	if (hwahcp->hwahc_hw_state == HWAHC_HW_STARTED) {
1653		/* use final_stop to fully stop the hwa */
1654		if (hwahc_hc_final_stop(hwahcp) != USB_SUCCESS) {
1655
1656			return (EIO);
1657		}
1658
1659		mutex_enter(&hc_data->hc_mutex);
1660		(void) memcpy(hc_data->hc_chid, chid, 16);
1661		mutex_exit(&hc_data->hc_mutex);
1662
1663		/* restart the host */
1664		if (hwahc_hc_initial_start(hwahcp) != USB_SUCCESS) {
1665
1666			return (EIO);
1667		}
1668
1669		return (0);
1670	}
1671
1672	/* hc is stopped or partially stopped, simply update */
1673	mutex_enter(&hc_data->hc_mutex);
1674	(void) memcpy(hc_data->hc_chid, chid, 16);
1675	mutex_exit(&hc_data->hc_mutex);
1676
1677	return (0);
1678}
1679
1680/*
1681 * wusbadm ioctl support
1682 */
1683/* ARGSUSED */
1684static int
1685hwahc_wusb_ioctl(hwahc_state_t *hwahcp, int cmd, intptr_t arg,
1686	int mode, cred_t *credp, int *rvalp)
1687{
1688	int		rv = 0;
1689	wusb_hc_data_t	*hc_data = &hwahcp->hwahc_hc_data;
1690
1691	if (drv_priv(credp) != 0) {
1692		USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
1693		    "hwahc_wusb_ioctl: user must have SYS_DEVICE privilege,"
1694		    "cmd=%x", cmd);
1695
1696		return (EPERM);
1697	}
1698
1699	mutex_enter(&hwahcp->hwahc_mutex);
1700
1701	switch (cmd) {
1702	case WUSB_HC_GET_DSTATE: /* Get device state: wusbadm list */
1703	{
1704		wusb_hc_get_dstate_t	state;
1705		usb_port_t		port = 0;
1706
1707		if (ddi_copyin((void *)arg, (void *)&state, sizeof (state),
1708		    mode) != 0) {
1709			rv = EFAULT;
1710
1711			break;
1712		}
1713
1714		mutex_enter(&hc_data->hc_mutex);
1715
1716		if (wusb_hc_is_dev_connected(hc_data, &state.cdid[0], &port)) {
1717			state.state = hc_data->hc_dev_infos[port]->wdev_state;
1718		} else {
1719			/* cdid not found */
1720			state.state = WUSB_STATE_UNCONNTED;
1721		}
1722
1723		USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
1724		    "hwahc_wusb_ioctl: hc_data=%p, port = %d, state=%d",
1725		    (void *) hc_data, port, state.state);
1726
1727		mutex_exit(&hc_data->hc_mutex);
1728
1729		if (state.state == WUSB_STATE_CONFIGURED) {
1730			/* Get the bind device node name of this child */
1731			(void) memset(state.nodename, 0, MAX_USB_NODENAME);
1732			(void) snprintf(state.nodename, MAX_USB_NODENAME, "%s",
1733			    ddi_node_name(hwahc_get_child_dip(hwahcp, port)));
1734
1735			USB_DPRINTF_L3(PRINT_MASK_CBOPS,
1736			    hwahcp->hwahc_log_handle,
1737			    "WUSB_HC_GET_DSTATE: nodename %s", state.nodename);
1738		}
1739
1740		if (ddi_copyout((void *)&state, (void *)arg,
1741		    sizeof (state), mode) != 0) {
1742			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1743			    hwahcp->hwahc_log_handle,
1744			    "WUSB_HC_GET_DSTATE: copyout failed");
1745			rv = EIO;
1746		}
1747
1748		break;
1749	}
1750
1751	case WUSB_HC_GET_MAC_ADDR: /* Get host MAC addr */
1752	{
1753		uint8_t		mac_addr[6];
1754
1755		bzero(mac_addr, 6);
1756
1757		/*
1758		 * get UWB 48-bit mac address
1759		 * Section 8.6.2.2.
1760		 */
1761		if (uwb_get_mac_addr(hwahcp->hwahc_dip, mac_addr) !=
1762		    USB_SUCCESS) {
1763			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1764			    hwahcp->hwahc_log_handle,
1765			    "WUSB_HC_GET_MAC_ADDR: get mac failed");
1766			rv = EIO;
1767
1768			break;
1769		}
1770
1771		if (ddi_copyout((void *)mac_addr, (void *)arg,
1772		    6, mode) != 0) {
1773			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1774			    hwahcp->hwahc_log_handle,
1775			    "WUSB_HC_GET_MAC_ADDR: copyout failed");
1776			rv = EIO;
1777		}
1778
1779		break;
1780	}
1781	case WUSB_HC_ADD_CC:
1782	{
1783	/*
1784	 * add a new device CC to host's list: wusbadm associate
1785	 * Or, the application can pass in a fake CC with only CHID set
1786	 * to set the host's CHID.
1787	 */
1788		wusb_hc_cc_list_t	*cc_list;
1789
1790		cc_list = kmem_zalloc(sizeof (wusb_hc_cc_list_t), KM_SLEEP);
1791
1792		if (ddi_copyin((void *)arg, (void *)&cc_list->cc,
1793		    sizeof (wusb_cc_t), mode) != 0) {
1794			rv = EFAULT;
1795			kmem_free(cc_list, sizeof (wusb_hc_cc_list_t));
1796
1797			break;
1798		}
1799
1800		/* update CHID only when cc list is empty */
1801		mutex_enter(&hc_data->hc_mutex);
1802		if (hc_data->hc_cc_list == NULL) {
1803			mutex_exit(&hc_data->hc_mutex);
1804
1805			if ((rv = hwahc_set_chid(hwahcp,
1806			    cc_list->cc.CHID)) != 0) {
1807				kmem_free(cc_list, sizeof (wusb_hc_cc_list_t));
1808
1809				break;
1810			}
1811
1812			mutex_enter(&hc_data->hc_mutex);
1813		} else {
1814			/* fail if the CHID in the new CC does not match */
1815			if (memcmp(cc_list->cc.CHID, hc_data->hc_chid,
1816			    16) != 0) {
1817				rv = EINVAL;
1818				kmem_free(cc_list, sizeof (wusb_hc_cc_list_t));
1819
1820				mutex_exit(&hc_data->hc_mutex);
1821				break;
1822			}
1823		}
1824		cc_list->next = NULL;
1825
1826		wusb_hc_add_cc(&hc_data->hc_cc_list, cc_list);
1827		mutex_exit(&hc_data->hc_mutex);
1828
1829		break;
1830	}
1831	case WUSB_HC_REM_CC:
1832	{
1833		wusb_cc_t	cc;
1834		usb_port_t	port;
1835
1836		if (ddi_copyin((void *)arg, (void *)&cc, sizeof (wusb_cc_t),
1837		    mode) != 0) {
1838			rv = EFAULT;
1839
1840			break;
1841		}
1842
1843		/* check if the CHID in the CC matches */
1844		if (memcmp(cc.CHID, hc_data->hc_chid, 16) != 0) {
1845			rv = EINVAL;
1846
1847			break;
1848		}
1849
1850		/* if the device is connected, disconnect it first */
1851		mutex_enter(&hc_data->hc_mutex);
1852		if (wusb_hc_is_dev_connected(hc_data, cc.CDID, &port)) {
1853			mutex_exit(&hc_data->hc_mutex);
1854			mutex_exit(&hwahcp->hwahc_mutex);
1855			/*
1856			 * clean up host side state, device not
1857			 * really disconnected. But user can safely remove
1858			 * the device now.
1859			 */
1860			(void) hwahc_destroy_child(hc_data->hc_dip, port);
1861			mutex_enter(&hwahcp->hwahc_mutex);
1862			mutex_enter(&hc_data->hc_mutex);
1863		}
1864
1865		wusb_hc_rem_cc(&hc_data->hc_cc_list, &cc);
1866		mutex_exit(&hc_data->hc_mutex);
1867
1868		break;
1869	}
1870	case WUSB_HC_SET_CHANNEL: /* for debug purpose */
1871	{
1872		uint8_t	channel;
1873
1874		channel = (uint8_t)arg;
1875
1876		if (hwahcp->hwahc_hc_data.hc_channel == channel) {
1877			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1878			    hwahcp->hwahc_log_handle,
1879			    "WUSB_HC_SET_CHANNEL ioctl: same as existing");
1880
1881			break;
1882		}
1883
1884		if (hwahcp->hwahc_hw_state != HWAHC_HW_STOPPED) {
1885			/* beacon is already started, stop it first */
1886			if (uwb_stop_beacon(hwahcp->hwahc_dip) != USB_SUCCESS) {
1887				USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1888				    hwahcp->hwahc_log_handle,
1889				    "WUSB_HC_SET_CHANNEL ioctl: "
1890				    "stop beacon failed");
1891				rv = EIO;
1892
1893				break;
1894			}
1895			/* update channel number */
1896			hwahcp->hwahc_hc_data.hc_channel = channel;
1897			/* restart beacon on the new channel */
1898			if (uwb_start_beacon(hwahcp->hwahc_dip,
1899			    channel) != USB_SUCCESS) {
1900				USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1901				    hwahcp->hwahc_log_handle,
1902				    "WUSB_HC_SET_CHANNEL ioctl: "
1903				    "restart beacon failed");
1904				rv = EIO;
1905			}
1906
1907			break;
1908		}
1909
1910		/* beacon is not started, simply update channel number */
1911		hwahcp->hwahc_hc_data.hc_channel = channel;
1912
1913		break;
1914	}
1915	case WUSB_HC_START:
1916	{
1917		int	flag;
1918
1919
1920		flag = (int)arg;
1921
1922		if (hwahcp->hwahc_hw_state == HWAHC_HW_STARTED) {
1923			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1924			    hwahcp->hwahc_log_handle,
1925			    "WUSB_HC_START ioctl: already started");
1926
1927			break;
1928		}
1929
1930		/*
1931		 * now we start hc only when the cc list is not NULL
1932		 * this limitation may be removed if we support
1933		 * numeric association, but CHID needs to be set
1934		 * in advance for the hc to work
1935		 */
1936		mutex_enter(&hc_data->hc_mutex);
1937		if (hc_data->hc_cc_list == NULL) {
1938			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1939			    hwahcp->hwahc_log_handle,
1940			    "WUSB_HC_START ioctl: cc list not inited");
1941			rv = EINVAL;
1942
1943			mutex_exit(&hc_data->hc_mutex);
1944
1945			break;
1946		}
1947		mutex_exit(&hc_data->hc_mutex);
1948
1949		/* cannot be both */
1950		if ((flag & WUSB_HC_INITIAL_START) && (flag &
1951		    WUSB_HC_CHANNEL_START)) {
1952			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1953			    hwahcp->hwahc_log_handle,
1954			    "WUSB_HC_START ioctl: flag cannot coexist");
1955			rv = EINVAL;
1956
1957			break;
1958		}
1959
1960		/*
1961		 * init Mac layer 16-bit dev addr. it is important for
1962		 * authentication. It'd be better to let UWB provide
1963		 * this address.
1964		 */
1965		mutex_enter(&hc_data->hc_mutex);
1966		if (hc_data->hc_addr == 0) {
1967			uint16_t dev_addr = HWAHC_DEV_ADDR_BASE +
1968			    ddi_get_instance(hwahcp->hwahc_dip);
1969
1970			mutex_exit(&hc_data->hc_mutex);
1971			/* set UWB 16-bit dev address */
1972			if (uwb_set_dev_addr(hwahcp->hwahc_dip,
1973			    dev_addr) != USB_SUCCESS) {
1974				USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1975				    hwahcp->hwahc_log_handle,
1976				    "WUSB_HC_START ioctl: set dev addr failed");
1977				rv = EIO;
1978
1979				break;
1980			}
1981
1982			/* verify the dev addr is set correctly */
1983			if (uwb_get_dev_addr(hwahcp->hwahc_dip,
1984			    &dev_addr) != USB_SUCCESS) {
1985				USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1986				    hwahcp->hwahc_log_handle,
1987				    "WUSB_HC_START ioctl: get dev addr failed");
1988				rv = EIO;
1989
1990				break;
1991			}
1992
1993			mutex_enter(&hc_data->hc_mutex);
1994			hc_data->hc_addr = dev_addr;
1995			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
1996			    hwahcp->hwahc_log_handle,
1997			    "host dev addr = 0x%x", dev_addr);
1998		}
1999		mutex_exit(&hc_data->hc_mutex);
2000
2001		/* start functions of wusb host */
2002		if ((flag & WUSB_HC_INITIAL_START) &&
2003		    (hwahcp->hwahc_hw_state == HWAHC_HW_STOPPED)) {
2004			if (hwahc_hc_initial_start(hwahcp) != USB_SUCCESS) {
2005				rv = EIO;
2006			}
2007		} else if ((flag & WUSB_HC_CHANNEL_START) &&
2008		    (hwahcp->hwahc_hw_state == HWAHC_HW_CH_STOPPED)) {
2009			if (hwahc_hc_channel_start(hwahcp) != USB_SUCCESS) {
2010				rv = EIO;
2011			}
2012		} else {
2013			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
2014			    hwahcp->hwahc_log_handle,
2015			    "WUSB_HC_START ioctl: unknown flag (%d) or "
2016			    "state (%d)", flag, hwahcp->hwahc_hw_state);
2017			rv = EINVAL;
2018		}
2019
2020		break;
2021	}
2022	case WUSB_HC_STOP:
2023	{
2024		int	flag;
2025
2026		flag = (int)arg;
2027
2028		/* cannot be both */
2029		if ((flag & WUSB_HC_FINAL_STOP) && (flag &
2030		    WUSB_HC_CHANNEL_STOP)) {
2031			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
2032			    hwahcp->hwahc_log_handle,
2033			    "WUSB_HC_STOP ioctl: flag cannot coexist");
2034			rv = EINVAL;
2035
2036			break;
2037		}
2038
2039		if (flag & WUSB_HC_FINAL_STOP) {
2040			if (hwahc_hc_final_stop(hwahcp) != USB_SUCCESS) {
2041				rv = EIO;
2042			}
2043		} else if (flag & WUSB_HC_CHANNEL_STOP) {
2044			if (hwahc_hc_channel_stop(hwahcp) != USB_SUCCESS) {
2045				rv = EIO;
2046			}
2047		} else {
2048			/* must be one of the STOP flag */
2049			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
2050			    hwahcp->hwahc_log_handle,
2051			    "WUSB_HC_STOP ioctl: invalid flag = %d", flag);
2052			rv = EINVAL;
2053		}
2054
2055		/* REM_ALL_CC flag is optional */
2056		if ((rv == 0) && (flag & WUSB_HC_REM_ALL_CC)) {
2057			mutex_enter(&hc_data->hc_mutex);
2058			if (hc_data->hc_cc_list) {
2059				wusb_hc_free_cc_list(hc_data->hc_cc_list);
2060				hc_data->hc_cc_list = NULL;
2061			}
2062			mutex_exit(&hc_data->hc_mutex);
2063		}
2064
2065		break;
2066	}
2067	case WUSB_HC_GET_HSTATE:
2068	{
2069		int	state;
2070
2071		if (hwahcp->hwahc_dev_state == USB_DEV_DISCONNECTED) {
2072			state = WUSB_HC_DISCONNTED;
2073		} else {
2074			switch (hwahcp->hwahc_hw_state) {
2075			case HWAHC_HW_STOPPED:
2076				state = WUSB_HC_STOPPED;
2077				break;
2078			case HWAHC_HW_STARTED:
2079				state = WUSB_HC_STARTED;
2080				break;
2081			case HWAHC_HW_CH_STOPPED:
2082				/*
2083				 * app can mark the hwa as disabled
2084				 * for this state
2085				 */
2086				state = WUSB_HC_CH_STOPPED;
2087				break;
2088			}
2089		}
2090
2091		if (ddi_copyout((void *)&state, (void *)arg,
2092		    sizeof (int), mode) != 0) {
2093			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
2094			    hwahcp->hwahc_log_handle,
2095			    "WUSB_HC_GET_HSTATE: copyout failed");
2096			rv = EIO;
2097		}
2098
2099		break;
2100	}
2101	default:
2102		USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
2103		    "hwahc_ioctl: unsupported command");
2104
2105		rv = ENOTSUP;
2106	}
2107	mutex_exit(&hwahcp->hwahc_mutex);
2108
2109	return (rv);
2110}
2111
2112static int
2113hwahc_ioctl(dev_t dev, int cmd, intptr_t arg,
2114	int mode, cred_t *credp, int *rvalp)
2115{
2116	hwahc_state_t	*hwahcp;
2117	int		rval;
2118
2119	if ((hwahcp = ddi_get_soft_state(hwahc_statep,
2120	    HWAHC_MINOR_TO_INSTANCE(getminor(dev)))) == NULL) {
2121
2122		return (ENXIO);
2123	}
2124
2125	USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
2126	    "hwahc_ioctl: cmd=%x, arg=%lx, mode=%x, cred=%p, rval=%p dev=0x%lx",
2127	    cmd, arg, mode, (void *) credp, (void *) rvalp, dev);
2128
2129	if (IS_DEVCTL(cmd)) {
2130		/* for cfgadm cmd support */
2131		rval = hwahc_cfgadm_ioctl(hwahcp, cmd, arg, mode, credp, rvalp);
2132	} else {
2133		/* for wusbadm cmd support */
2134		rval = hwahc_wusb_ioctl(hwahcp, cmd, arg, mode, credp, rvalp);
2135	}
2136
2137	return (rval);
2138}
2139
2140/* return the port number corresponding the child dip */
2141static usb_port_t
2142hwahc_child_dip2port(hwahc_state_t *hwahcp, dev_info_t *dip)
2143{
2144	usb_port_t	port;
2145	wusb_hc_data_t	*hc_data = &hwahcp->hwahc_hc_data;
2146
2147	mutex_enter(&hc_data->hc_mutex);
2148	for (port = 1; port <= hc_data->hc_num_ports; port++) {
2149		if (hc_data->hc_children_dips[port] == dip) {
2150
2151			break;
2152		}
2153	}
2154	ASSERT(port <= hc_data->hc_num_ports);
2155	mutex_exit(&hc_data->hc_mutex);
2156
2157	return (port);
2158}
2159
2160/*
2161 * child post attach/detach notification
2162 */
2163static void
2164hwahc_post_attach(hwahc_state_t *hwahcp, dev_info_t *rdip,
2165	struct attachspec *as)
2166{
2167	/* we don't need additional process for post-attach now */
2168	USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2169	    "hwahc_post_attach: rdip = 0x%p result = %d", (void *) rdip,
2170	    as->result);
2171}
2172
2173static void
2174hwahc_post_detach(hwahc_state_t *hwahcp, dev_info_t *rdip,
2175	struct detachspec *as)
2176{
2177	/* we don't need additional process for post-detach now */
2178	USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2179	    "hwahc_post_detach: rdip = 0x%p result = %d", (void *) rdip,
2180	    as->result);
2181}
2182
2183/*
2184 * bus ctl support.
2185 * To support different operations, such as a PreAttach preparation,
2186 * PostAttach operations. HWA only process the interested operations.
2187 * Other general ones are processed by usba_bus_ctl().
2188 */
2189static int
2190hwahc_bus_ctl(dev_info_t *dip, /* dip could be the parent */
2191	dev_info_t	*rdip, /* rdip is the dev node to be operated */
2192	ddi_ctl_enum_t	op,
2193	void		*arg,
2194	void		*result)
2195{
2196	usba_device_t *usba_device = usba_get_usba_device(rdip);
2197	dev_info_t *hubdip = usba_device->usb_root_hub_dip;
2198	hwahc_state_t *hwahcp;
2199	struct attachspec *as;
2200	struct detachspec *ds;
2201
2202	if ((hwahcp = ddi_get_soft_state(hwahc_statep,
2203	    ddi_get_instance(dip))) == NULL) {
2204
2205		return (DDI_FAILURE);
2206	}
2207
2208	USB_DPRINTF_L2(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2209	    "hwahc_bus_ctl:\n\t"
2210	    "dip = 0x%p, rdip = 0x%p, op = 0x%x, arg = 0x%p",
2211	    (void *) dip, (void *) rdip, op, (void *) arg);
2212
2213	switch (op) {
2214	case DDI_CTLOPS_ATTACH:
2215		as = (struct attachspec *)arg;
2216
2217		switch (as->when) {
2218		case DDI_PRE :
2219			/* nothing to do basically */
2220			USB_DPRINTF_L2(PRINT_MASK_EVENTS,
2221			    hwahcp->hwahc_log_handle,
2222			    "DDI_PRE DDI_CTLOPS_ATTACH");
2223			break;
2224		case DDI_POST :
2225			hwahc_post_attach(hwahcp, rdip,
2226			    (struct attachspec *)arg);
2227			break;
2228		}
2229
2230		break;
2231	case DDI_CTLOPS_DETACH:
2232		ds = (struct detachspec *)arg;
2233
2234		switch (ds->when) {
2235		case DDI_PRE :
2236			/* nothing to do basically */
2237			USB_DPRINTF_L2(PRINT_MASK_EVENTS,
2238			    hwahcp->hwahc_log_handle,
2239			    "DDI_PRE DDI_CTLOPS_DETACH");
2240			break;
2241		case DDI_POST :
2242			hwahc_post_detach(hwahcp, rdip,
2243			    (struct detachspec *)arg);
2244			break;
2245		}
2246
2247		break;
2248	case DDI_CTLOPS_REPORTDEV: /* the workhorse behind ddi_report_dev */
2249	{
2250		char *name, compat_name[64];
2251
2252		if (usb_owns_device(rdip)) {
2253			(void) snprintf(compat_name,
2254			    sizeof (compat_name),
2255			    "usb%x,%x",
2256			    usba_device->usb_dev_descr->idVendor,
2257			    usba_device->usb_dev_descr->idProduct);
2258		} else if (usba_owns_ia(rdip)) {
2259			(void) snprintf(compat_name,
2260			    sizeof (compat_name),
2261			    "usbia%x,%x.config%x.%x",
2262			    usba_device->usb_dev_descr->idVendor,
2263			    usba_device->usb_dev_descr->idProduct,
2264			    usba_device->usb_cfg_value,
2265			    usb_get_if_number(rdip));
2266		} else {
2267			(void) snprintf(compat_name,
2268			    sizeof (compat_name),
2269			    "usbif%x,%x.config%x.%x",
2270			    usba_device->usb_dev_descr->idVendor,
2271			    usba_device->usb_dev_descr->idProduct,
2272			    usba_device->usb_cfg_value,
2273			    usb_get_if_number(rdip));
2274		}
2275
2276		cmn_err(CE_CONT,
2277		    "?USB %x.%x %s (%s) operating wirelessly with "
2278		    "HWA device: "
2279		    "%s@%s, %s%d at bus address %d\n",
2280		    (usba_device->usb_dev_descr->bcdUSB & 0xff00) >> 8,
2281		    usba_device->usb_dev_descr->bcdUSB & 0xff,
2282		    (usb_owns_device(rdip) ? "device" :
2283		    ((usba_owns_ia(rdip) ? "interface-association" :
2284		    "interface"))),
2285		    compat_name,
2286		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
2287		    ddi_driver_name(rdip),
2288		    ddi_get_instance(rdip), usba_device->usb_addr);
2289
2290		name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
2291		(void) usba_get_mfg_prod_sn_str(rdip, name, MAXNAMELEN);
2292		if (name[0] != '\0') {
2293			cmn_err(CE_CONT, "?\t%s\n", name);
2294		}
2295		kmem_free(name, MAXNAMELEN);
2296
2297		break;
2298	}
2299	default:
2300		/* pass to usba to handle */
2301		return (usba_bus_ctl(hubdip, rdip, op, arg, result));
2302	}
2303
2304	return (DDI_SUCCESS);
2305}
2306
2307/*
2308 * bus enumeration entry points
2309 *  Configures the named device(BUS_CONFIG_ONE) or all devices under
2310 *  the nexus(BUS_CONFIG_ALL). Drives devinfo state to DS_READY,i.e.device
2311 *  is fully operational.
2312 *
2313 *  This operation is driven from devfs(reading /devices), devctl, libdevinfo;
2314 *  or from within the kernel to attach a boot device or layered underlying
2315 *  driver.
2316 */
2317static int
2318hwahc_bus_config(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
2319	void *arg, dev_info_t **child)
2320{
2321	hwahc_state_t	*hwahcp;
2322	int		rval, circ;
2323
2324	if ((hwahcp = ddi_get_soft_state(hwahc_statep,
2325	    ddi_get_instance(dip))) == NULL) {
2326
2327		return (NDI_FAILURE);
2328	}
2329
2330	USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2331	    "hwahc_bus_config: op=%d", op);
2332
2333	if (hwahc_bus_config_debug) {
2334		flag |= NDI_DEVI_DEBUG;
2335	}
2336
2337	ndi_devi_enter(dip, &circ);
2338	rval = ndi_busop_bus_config(dip, flag, op, arg, child, 0);
2339	ndi_devi_exit(dip, circ);
2340
2341	return (rval);
2342}
2343
2344/*
2345 * Unconfigures the named device or all devices under the nexus. The
2346 * devinfo state is not DS_READY anymore.
2347 * This operations is driven by modunload, devctl or DR branch removal or
2348 * rem_drv(1M).
2349 */
2350static int
2351hwahc_bus_unconfig(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
2352	void *arg)
2353{
2354	hwahc_state_t	*hwahcp;
2355	wusb_hc_data_t	*hc_data;
2356	dev_info_t	*cdip;
2357	usb_port_t	port;
2358	int		rval, circ;
2359
2360	if ((hwahcp = ddi_get_soft_state(hwahc_statep,
2361	    ddi_get_instance(dip))) == NULL) {
2362
2363		return (NDI_FAILURE);
2364	}
2365
2366	USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2367	    "hwahc_bus_unconfig: op=%d", op);
2368
2369	if (hwahc_bus_config_debug) {
2370		flag |= NDI_DEVI_DEBUG;
2371	}
2372
2373	if ((op == BUS_UNCONFIG_ALL) && (flag & NDI_AUTODETACH) == 0) {
2374		flag |= NDI_DEVI_REMOVE;
2375	}
2376
2377	/* serialize access */
2378	ndi_devi_enter(dip, &circ);
2379
2380	/* unconfig children, detach them */
2381	rval = ndi_busop_bus_unconfig(dip, flag, op, arg);
2382
2383	/* logically zap children's list */
2384	hc_data = &hwahcp->hwahc_hc_data;
2385
2386	mutex_enter(&hc_data->hc_mutex);
2387	for (port = 1; port <= hc_data->hc_num_ports; port++) {
2388		hc_data->hc_children_state[port] |= WUSB_CHILD_ZAP;
2389	}
2390	mutex_exit(&hc_data->hc_mutex);
2391
2392	/* fill in what's left */
2393	for (cdip = ddi_get_child(dip); cdip;
2394	    cdip = ddi_get_next_sibling(cdip)) {
2395		usba_device_t *usba_device = usba_get_usba_device(cdip);
2396
2397		if (usba_device == NULL) {
2398
2399			continue;
2400		}
2401		mutex_enter(&hc_data->hc_mutex);
2402		port = usba_device->usb_port;
2403		hc_data->hc_children_dips[port] = cdip;
2404		hc_data->hc_children_state[port] &= ~WUSB_CHILD_ZAP;
2405		mutex_exit(&hc_data->hc_mutex);
2406	}
2407
2408	/* physically zap the children we didn't find */
2409	mutex_enter(&hc_data->hc_mutex);
2410	for (port = 1; port <= hc_data->hc_num_ports; port++) {
2411		if (hc_data->hc_children_state[port] & WUSB_CHILD_ZAP) {
2412			wusb_dev_info_t		*dev_info;
2413			wusb_secrt_data_t	*csecrt_data;
2414			usba_device_t		*child_ud;
2415
2416			USB_DPRINTF_L3(PRINT_MASK_EVENTS,
2417			    hwahcp->hwahc_log_handle,
2418			    "hwahc_bus_unconfig: physically zap port %d", port);
2419
2420			child_ud = hc_data->hc_usba_devices[port];
2421			mutex_exit(&hc_data->hc_mutex);
2422			/* zap the dip and usba_device structure as well */
2423			usba_free_usba_device(child_ud);
2424			mutex_enter(&hc_data->hc_mutex);
2425			hc_data->hc_usba_devices[port] = NULL;
2426
2427			/* dip freed in usba_destroy_child_devi */
2428			hc_data->hc_children_dips[port] = NULL;
2429			hc_data->hc_children_state[port] &= ~WUSB_CHILD_ZAP;
2430
2431			/* free hc_dev_infos[port] */
2432			dev_info = hc_data->hc_dev_infos[port];
2433			if (dev_info == NULL) {
2434
2435				continue;
2436			}
2437
2438			/* stop the device's trust timer before deallocate it */
2439			hwahc_stop_trust_timer(dev_info);
2440
2441			if (dev_info->wdev_secrt_data.secrt_encry_descr) {
2442				csecrt_data = &dev_info->wdev_secrt_data;
2443				kmem_free(csecrt_data->secrt_encry_descr,
2444				    sizeof (usb_encryption_descr_t) *
2445				    csecrt_data->secrt_n_encry);
2446			}
2447			if (dev_info->wdev_uwb_descr) {
2448				kmem_free(dev_info->wdev_uwb_descr,
2449				    sizeof (usb_uwb_cap_descr_t));
2450			}
2451			kmem_free(dev_info, sizeof (wusb_dev_info_t));
2452			hc_data->hc_dev_infos[port] = NULL;
2453		}
2454	}
2455	mutex_exit(&hc_data->hc_mutex);
2456
2457	ndi_devi_exit(dip, circ);
2458
2459	USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2460	    "hwahc_bus_unconfig: rval=%d", rval);
2461
2462	return (rval);
2463}
2464
2465/*
2466 * busctl event support
2467 *
2468 * Called by ndi_busop_get_eventcookie(). Return a event cookie
2469 * associated with one event name.
2470 * The eventname should be the one we defined in hwahc_ndi_event_defs
2471 */
2472static int
2473hwahc_busop_get_eventcookie(dev_info_t *dip,
2474	dev_info_t	*rdip,
2475	char		*eventname,
2476	ddi_eventcookie_t *cookie)
2477{
2478	hwahc_state_t	*hwahcp;
2479
2480	if ((hwahcp = ddi_get_soft_state(hwahc_statep,
2481	    ddi_get_instance(dip))) == NULL) {
2482
2483		return (NDI_FAILURE);
2484	}
2485
2486	USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2487	    "hwahc_busop_get_eventcookie: dip=0x%p, rdip=0x%p, "
2488	    "event=%s", (void *)dip, (void *)rdip, eventname);
2489	USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2490	    "(dip=%s%d, rdip=%s%d)",
2491	    ddi_driver_name(dip), ddi_get_instance(dip),
2492	    ddi_driver_name(rdip), ddi_get_instance(rdip));
2493
2494	/* return event cookie, iblock cookie, and level */
2495	return (ndi_event_retrieve_cookie(hwahcp->hwahc_ndi_event_hdl,
2496	    rdip, eventname, cookie, NDI_EVENT_NOPASS));
2497}
2498
2499/*
2500 * Add event handler for a given event cookie
2501 */
2502static int
2503hwahc_busop_add_eventcall(dev_info_t *dip,
2504	dev_info_t	*rdip,
2505	ddi_eventcookie_t cookie,
2506	void		(*callback)(dev_info_t *dip,
2507			ddi_eventcookie_t cookie, void *arg,
2508			void *bus_impldata),
2509	void *arg, ddi_callback_id_t *cb_id)
2510{
2511	hwahc_state_t	*hwahcp;
2512	usb_port_t	port;
2513
2514	if ((hwahcp = ddi_get_soft_state(hwahc_statep,
2515	    ddi_get_instance(dip))) == NULL) {
2516
2517		return (NDI_FAILURE);
2518	}
2519
2520	port = hwahc_child_dip2port(hwahcp, rdip);
2521
2522	mutex_enter(&hwahcp->hwahc_mutex);
2523
2524	USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2525	    "hwahc_busop_add_eventcall: dip=0x%p, rdip=0x%p "
2526	    "cookie=0x%p, cb=0x%p, arg=0x%p",
2527	    (void *)dip, (void *)rdip, (void *)cookie, (void *)callback, arg);
2528	USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2529	    "(dip=%s%d, rdip=%s%d, event=%s)",
2530	    ddi_driver_name(dip), ddi_get_instance(dip),
2531	    ddi_driver_name(rdip), ddi_get_instance(rdip),
2532	    ndi_event_cookie_to_name(hwahcp->hwahc_ndi_event_hdl, cookie));
2533
2534	/* Set flag on children registering events */
2535	switch (ndi_event_cookie_to_tag(hwahcp->hwahc_ndi_event_hdl, cookie)) {
2536	case USBA_EVENT_TAG_HOT_REMOVAL:
2537		hwahcp->hwahc_child_events[port] |=
2538		    HWAHC_CHILD_EVENT_DISCONNECT;
2539
2540		break;
2541	case USBA_EVENT_TAG_PRE_SUSPEND:
2542		hwahcp->hwahc_child_events[port] |=
2543		    HWAHC_CHILD_EVENT_PRESUSPEND;
2544
2545		break;
2546	default:
2547
2548		break;
2549	}
2550
2551	mutex_exit(&hwahcp->hwahc_mutex);
2552
2553	/* add callback to our event set */
2554	return (ndi_event_add_callback(hwahcp->hwahc_ndi_event_hdl,
2555	    rdip, cookie, callback, arg, NDI_SLEEP, cb_id));
2556
2557}
2558
2559
2560/*
2561 * Remove a callback previously added by bus_add_eventcall()
2562 */
2563static int
2564hwahc_busop_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id)
2565{
2566	hwahc_state_t	*hwahcp;
2567	ndi_event_callbacks_t *id = (ndi_event_callbacks_t *)cb_id;
2568
2569	if ((hwahcp = ddi_get_soft_state(hwahc_statep,
2570	    ddi_get_instance(dip))) == NULL) {
2571
2572		return (NDI_FAILURE);
2573	}
2574
2575	USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2576	    "hwahc_busop_remove_eventcall: dip=0x%p, rdip=0x%p "
2577	    "cookie=0x%p", (void *)dip, (void *) id->ndi_evtcb_dip,
2578	    (void *)id->ndi_evtcb_cookie);
2579	USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2580	    "(dip=%s%d, rdip=%s%d, event=%s)",
2581	    ddi_driver_name(dip), ddi_get_instance(dip),
2582	    ddi_driver_name(id->ndi_evtcb_dip),
2583	    ddi_get_instance(id->ndi_evtcb_dip),
2584	    ndi_event_cookie_to_name(hwahcp->hwahc_ndi_event_hdl,
2585	    id->ndi_evtcb_cookie));
2586
2587	/* remove event registration from our event set */
2588	return (ndi_event_remove_callback(hwahcp->hwahc_ndi_event_hdl, cb_id));
2589}
2590
2591/*
2592 * hwahc_post_event
2593 *	post event to a single child on the port depending on the type, i.e.
2594 *	to invoke the child's registered callback.
2595 */
2596static void
2597hwahc_post_event(hwahc_state_t *hwahcp, usb_port_t port, usba_event_t type)
2598{
2599	int		rval;
2600	dev_info_t	*dip;
2601	usba_device_t	*usba_device;
2602	ddi_eventcookie_t cookie, rm_cookie, suspend_cookie;
2603	wusb_hc_data_t	*hc_data = &hwahcp->hwahc_hc_data;
2604
2605	USB_DPRINTF_L4(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2606	    "hwahc_post_event: port=%d event=%s", port,
2607	    ndi_event_tag_to_name(hwahcp->hwahc_ndi_event_hdl, type));
2608
2609	cookie = ndi_event_tag_to_cookie(hwahcp->hwahc_ndi_event_hdl, type);
2610	rm_cookie = ndi_event_tag_to_cookie(hwahcp->hwahc_ndi_event_hdl,
2611	    USBA_EVENT_TAG_HOT_REMOVAL);
2612	suspend_cookie = ndi_event_tag_to_cookie(hwahcp->hwahc_ndi_event_hdl,
2613	    USBA_EVENT_TAG_PRE_SUSPEND);
2614
2615	/*
2616	 * Hotplug daemon may be attaching a driver that may be registering
2617	 * event callbacks. So it already has got the device tree lock and
2618	 * event handle mutex. So to prevent a deadlock while posting events,
2619	 * we grab and release the locks in the same order.
2620	 */
2621	mutex_enter(&hwahcp->hwahc_mutex);
2622	dip = hwahcp->hwahc_hc_data.hc_children_dips[port];
2623	usba_device = hwahcp->hwahc_hc_data.hc_usba_devices[port];
2624	mutex_exit((&hwahcp->hwahc_mutex));
2625
2626	switch (type) {
2627	case USBA_EVENT_TAG_HOT_REMOVAL:
2628		/* stop this device's timer to prevent its further process */
2629		mutex_enter(&hc_data->hc_mutex);
2630
2631		hwahc_stop_trust_timer(hc_data->hc_dev_infos[port]);
2632		mutex_exit(&hc_data->hc_mutex);
2633
2634		/* Clear the registered event flag */
2635		mutex_enter(&hwahcp->hwahc_mutex);
2636		hwahcp->hwahc_child_events[port] &=
2637		    ~HWAHC_CHILD_EVENT_DISCONNECT;
2638		mutex_exit(&hwahcp->hwahc_mutex);
2639
2640		(void) ndi_event_do_callback(hwahcp->hwahc_ndi_event_hdl,
2641		    dip, cookie, NULL);
2642		usba_persistent_pipe_close(usba_device);
2643
2644		/*
2645		 * Mark the dip for deletion only after the driver has
2646		 * seen the disconnect event to prevent cleanup thread
2647		 * from stepping in between.
2648		 */
2649#ifndef __lock_lint
2650		mutex_enter(&DEVI(dip)->devi_lock);
2651		DEVI_SET_DEVICE_REMOVED(dip);
2652		mutex_exit(&DEVI(dip)->devi_lock);
2653#endif
2654
2655		break;
2656	case USBA_EVENT_TAG_PRE_SUSPEND:
2657		mutex_enter(&hwahcp->hwahc_mutex);
2658		hwahcp->hwahc_child_events[port] &=
2659		    ~HWAHC_CHILD_EVENT_PRESUSPEND;
2660		mutex_exit(&hwahcp->hwahc_mutex);
2661
2662		(void) ndi_event_do_callback(hwahcp->hwahc_ndi_event_hdl,
2663		    dip, cookie, NULL);
2664		/*
2665		 * persistent pipe close for this event is taken care by the
2666		 * caller after verfying that all children can suspend
2667		 */
2668
2669		break;
2670	case USBA_EVENT_TAG_HOT_INSERTION:
2671		/*
2672		 * Check if this child has missed the disconnect event before
2673		 * it registered for event callbacks
2674		 */
2675		mutex_enter(&hwahcp->hwahc_mutex);
2676		if (hwahcp->hwahc_child_events[port] &
2677		    HWAHC_CHILD_EVENT_DISCONNECT) {
2678			/* clear the flag and post disconnect event */
2679			hwahcp->hwahc_child_events[port] &=
2680			    ~HWAHC_CHILD_EVENT_DISCONNECT;
2681			mutex_exit(&hwahcp->hwahc_mutex);
2682
2683			(void) ndi_event_do_callback(
2684			    hwahcp->hwahc_ndi_event_hdl,
2685			    dip, rm_cookie, NULL);
2686			usba_persistent_pipe_close(usba_device);
2687			mutex_enter(&hwahcp->hwahc_mutex);
2688		}
2689		mutex_exit(&hwahcp->hwahc_mutex);
2690
2691		/*
2692		 * Mark the dip as reinserted to prevent cleanup thread
2693		 * from stepping in.
2694		 */
2695#ifndef __lock_lint
2696		mutex_enter(&(DEVI(dip)->devi_lock));
2697		DEVI_SET_DEVICE_REINSERTED(dip);
2698		mutex_exit(&(DEVI(dip)->devi_lock));
2699#endif
2700
2701		rval = usba_persistent_pipe_open(usba_device);
2702		if (rval != USB_SUCCESS) {
2703			USB_DPRINTF_L2(PRINT_MASK_EVENTS,
2704			    hwahcp->hwahc_log_handle,
2705			    "failed to reopen all pipes on reconnect");
2706		}
2707
2708		(void) ndi_event_do_callback(hwahcp->hwahc_ndi_event_hdl,
2709		    dip, cookie, NULL);
2710
2711		/*
2712		 * We might see a connect event only if hotplug thread for
2713		 * disconnect event don't run in time.
2714		 * Set the flag again, so we don't miss posting a
2715		 * disconnect event.
2716		 */
2717		mutex_enter(&hwahcp->hwahc_mutex);
2718		hwahcp->hwahc_child_events[port] |=
2719		    HWAHC_CHILD_EVENT_DISCONNECT;
2720		mutex_exit(&hwahcp->hwahc_mutex);
2721
2722		break;
2723	case USBA_EVENT_TAG_POST_RESUME:
2724		/*
2725		 * Check if this child has missed the pre-suspend event before
2726		 * it registered for event callbacks
2727		 */
2728		mutex_enter(&hwahcp->hwahc_mutex);
2729		if (hwahcp->hwahc_child_events[port] &
2730		    HWAHC_CHILD_EVENT_PRESUSPEND) {
2731			/* clear the flag and post pre_suspend event */
2732			hwahcp->hwahc_child_events[port] &=
2733			    ~HWAHC_CHILD_EVENT_PRESUSPEND;
2734			mutex_exit(&hwahcp->hwahc_mutex);
2735			(void) ndi_event_do_callback(
2736			    hwahcp->hwahc_ndi_event_hdl,
2737			    dip, suspend_cookie, NULL);
2738			mutex_enter(&hwahcp->hwahc_mutex);
2739		}
2740		mutex_exit(&hwahcp->hwahc_mutex);
2741
2742		mutex_enter(&usba_device->usb_mutex);
2743		usba_device->usb_no_cpr = 0;
2744		mutex_exit(&usba_device->usb_mutex);
2745
2746		/*
2747		 * Since the pipe has already been opened by whub
2748		 * at DDI_RESUME time, there is no need for a
2749		 * persistent pipe open
2750		 */
2751		(void) ndi_event_do_callback(hwahcp->hwahc_ndi_event_hdl,
2752		    dip, cookie, NULL);
2753
2754		/*
2755		 * Set the flag again, so we don't miss posting a
2756		 * pre-suspend event. This enforces a tighter
2757		 * dev_state model.
2758		 */
2759		mutex_enter(&hwahcp->hwahc_mutex);
2760		hwahcp->hwahc_child_events[port] |=
2761		    HWAHC_CHILD_EVENT_PRESUSPEND;
2762		mutex_exit(&hwahcp->hwahc_mutex);
2763		break;
2764	}
2765}
2766
2767/*
2768 * hwahc_run_callbacks:
2769 *	Send an event to all children
2770 */
2771static void
2772hwahc_run_callbacks(hwahc_state_t *hwahcp, usba_event_t type)
2773{
2774	usb_port_t	port;
2775	wusb_hc_data_t	*hc_data = &hwahcp->hwahc_hc_data;
2776
2777	USB_DPRINTF_L4(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2778	    "hwahc_run_callbacks:");
2779
2780	mutex_enter(&hc_data->hc_mutex);
2781	for (port = 1; port <= hc_data->hc_num_ports; port++) {
2782		if (hc_data->hc_children_dips[port]) {
2783			mutex_exit(&hc_data->hc_mutex);
2784			hwahc_post_event(hwahcp, port, type);
2785			mutex_enter(&hc_data->hc_mutex);
2786		}
2787	}
2788	mutex_exit(&hc_data->hc_mutex);
2789}
2790
2791/*
2792 * hwahc_disconnect_event_cb:
2793 *	Called when hwa device hotplug-removed.
2794 *		Close pipes
2795 *		Post event to child
2796 *		Set state to DISCONNECTED
2797 */
2798static int
2799hwahc_disconnect_event_cb(dev_info_t *dip)
2800{
2801	int		instance = ddi_get_instance(dip);
2802	hwahc_state_t	*hwahcp;
2803	int		circ;
2804
2805	if ((hwahcp = ddi_get_soft_state(hwahc_statep, instance)) == NULL) {
2806
2807		return (USB_FAILURE);
2808	}
2809
2810	USB_DPRINTF_L4(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2811	    "hwahc_disconnect_event_cb: dip = 0x%p", (void *)dip);
2812
2813	ndi_devi_enter(dip, &circ);
2814
2815	mutex_enter(&hwahcp->hwahc_mutex);
2816
2817	USB_DPRINTF_L4(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2818	    "hwahc_disconnect_event_cb: devstate= %d hw-state=%d",
2819	    hwahcp->hwahc_dev_state, hwahcp->hwahc_hw_state);
2820	switch (hwahcp->hwahc_dev_state) {
2821	case USB_DEV_ONLINE:
2822	case USB_DEV_PWRED_DOWN:
2823		hwahcp->hwahc_dev_state = USB_DEV_DISCONNECTED;
2824
2825		if (hwahcp->hwahc_hw_state != HWAHC_HW_STOPPED) {
2826			mutex_exit(&hwahcp->hwahc_mutex);
2827			wusb_wa_stop_nep(&hwahcp->hwahc_wa_data);
2828			mutex_enter(&hwahcp->hwahc_mutex);
2829			hwahc_stop_result_thread(hwahcp);
2830			hwahc_drain_notif_queue(hwahcp);
2831		}
2832		/* FALLTHROUGH */
2833	case USB_DEV_SUSPENDED:
2834		/* remain in this state */
2835		mutex_exit(&hwahcp->hwahc_mutex);
2836		hwahc_run_callbacks(hwahcp, USBA_EVENT_TAG_HOT_REMOVAL);
2837		mutex_enter(&hwahcp->hwahc_mutex);
2838
2839		hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_INIT_STATE;
2840
2841		break;
2842	case USB_DEV_DISCONNECTED:
2843		USB_DPRINTF_L2(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2844		    "hwahc_disconnect_event_cb: already disconnected");
2845
2846		break;
2847	default:
2848		USB_DPRINTF_L2(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2849		    "hwahc_disconnect_event_cb: illegal devstate=%d",
2850		    hwahcp->hwahc_dev_state);
2851
2852		break;
2853	}
2854	mutex_exit(&hwahcp->hwahc_mutex);
2855
2856	ndi_devi_exit(dip, circ);
2857
2858	return (USB_SUCCESS);
2859}
2860
2861
2862/*
2863 * hwahc_reconnect_event_cb:
2864 *	Called with device hotplug-inserted
2865 *		Restore state
2866 */
2867static int
2868hwahc_reconnect_event_cb(dev_info_t *dip)
2869{
2870	int		instance = ddi_get_instance(dip);
2871	hwahc_state_t	*hwahcp;
2872	int		circ;
2873
2874
2875	if ((hwahcp = ddi_get_soft_state(hwahc_statep, instance)) == NULL) {
2876
2877		return (USB_FAILURE);
2878	}
2879
2880	USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
2881	    "hwahc_reconnect_event_cb: dip = 0x%p", (void *)dip);
2882
2883	ndi_devi_enter(dip, &circ);
2884	hwahc_restore_device_state(dip, hwahcp);
2885	ndi_devi_exit(dip, circ);
2886
2887	return (USB_SUCCESS);
2888}
2889
2890
2891/*
2892 * hwahc_pre_suspend_event_cb:
2893 *	Called before HWA device suspend
2894 */
2895static int
2896hwahc_pre_suspend_event_cb(dev_info_t *dip)
2897{
2898	int		instance = ddi_get_instance(dip);
2899	hwahc_state_t	*hwahcp;
2900	int		circ;
2901
2902	if ((hwahcp = ddi_get_soft_state(hwahc_statep, instance)) == NULL) {
2903
2904		return (USB_FAILURE);
2905	}
2906
2907	USB_DPRINTF_L4(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2908	    "hwahc_pre_suspend_event_cb: dip = 0x%p", (void *)dip);
2909
2910	mutex_enter(&hwahcp->hwahc_mutex);
2911	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
2912	    "hwahc_pre_suspend_event_cb: start, hw state = %d, softstate = %d",
2913	    hwahcp->hwahc_hw_state, hwahcp->hwahc_hc_soft_state);
2914	mutex_exit(&hwahcp->hwahc_mutex);
2915
2916	/* keep PM out till we see a cpr resume */
2917	(void) hwahc_pm_busy_component(hwahcp);
2918	(void) pm_raise_power(hwahcp->hwahc_dip, 0, USB_DEV_OS_FULL_PWR);
2919
2920	ndi_devi_enter(dip, &circ);
2921	hwahc_run_callbacks(hwahcp, USBA_EVENT_TAG_PRE_SUSPEND);
2922	ndi_devi_exit(dip, circ);
2923
2924	/*
2925	 * rc driver is always suspended first, that fails the hc suspend.
2926	 * need to suspend hc before rc is suspended, so move the suspend
2927	 * operations here
2928	 */
2929	mutex_enter(&hwahcp->hwahc_mutex);
2930	if (hwahcp->hwahc_dev_state != USB_DEV_ONLINE) {
2931		USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2932		    "hwahc_pre_suspend_event_cb: dev_state = %d",
2933		    hwahcp->hwahc_dev_state);
2934		mutex_exit(&hwahcp->hwahc_mutex);
2935
2936		return (USB_SUCCESS);
2937	}
2938
2939	if (hwahcp->hwahc_hw_state == HWAHC_HW_STARTED) {
2940		/*
2941		 * notify children the host is going to stop
2942		 */
2943		(void) hwahc_hc_channel_suspend(hwahcp);
2944	}
2945
2946	/* stop the hc from functioning */
2947	if (hwahcp->hwahc_hw_state != HWAHC_HW_STOPPED) {
2948		mutex_exit(&hwahcp->hwahc_mutex);
2949		wusb_wa_stop_nep(&hwahcp->hwahc_wa_data);
2950
2951		mutex_enter(&hwahcp->hwahc_mutex);
2952		hwahc_stop_result_thread(hwahcp);
2953		hwahc_drain_notif_queue(hwahcp);
2954
2955		mutex_exit(&hwahcp->hwahc_mutex);
2956		(void) wusb_wa_disable(&hwahcp->hwahc_wa_data,
2957		    hwahcp->hwahc_default_pipe);
2958		mutex_enter(&hwahcp->hwahc_mutex);
2959
2960	}
2961
2962	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
2963	    "hwahc_pre_suspend_event_cb: end, devstate=%d "
2964	    "hwstate=%d softstate = %d",
2965	    hwahcp->hwahc_dev_state, hwahcp->hwahc_hw_state,
2966	    hwahcp->hwahc_hc_soft_state);
2967
2968	mutex_exit(&hwahcp->hwahc_mutex);
2969
2970	return (USB_SUCCESS);
2971}
2972
2973
2974/*
2975 * hwahc_post_resume_event_cb:
2976 *	Call after HWA device resume
2977 */
2978static int
2979hwahc_post_resume_event_cb(dev_info_t *dip)
2980{
2981	int		instance = ddi_get_instance(dip);
2982	hwahc_state_t	*hwahcp;
2983	int		circ;
2984
2985	if ((hwahcp = ddi_get_soft_state(hwahc_statep, instance)) == NULL) {
2986
2987		return (USB_FAILURE);
2988	}
2989
2990	USB_DPRINTF_L4(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
2991	    "hwahc_post_resume_event_cb: dip = 0x%p", (void *)dip);
2992
2993	mutex_enter(&hwahcp->hwahc_mutex);
2994
2995	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
2996	    "hwahc_post_resume_event_cb: start, hw state = %d, softstate = %d",
2997	    hwahcp->hwahc_hw_state, hwahcp->hwahc_hc_soft_state);
2998	mutex_exit(&hwahcp->hwahc_mutex);
2999
3000	ndi_devi_enter(dip, &circ);
3001
3002	/* need to place hc restore here to make sure rc has resumed */
3003	hwahc_restore_device_state(dip, hwahcp);
3004
3005	hwahc_run_callbacks(hwahcp, USBA_EVENT_TAG_POST_RESUME);
3006
3007	ndi_devi_exit(dip, circ);
3008
3009	/* enable PM */
3010	(void) hwahc_pm_idle_component(hwahcp);
3011
3012	return (USB_SUCCESS);
3013}
3014
3015
3016/*
3017 * hwahc_restore_device_state:
3018 *	Called during hotplug-reconnect and resume.
3019 *		re-enable power management
3020 *		Verify the device is the same as before the disconnect/suspend.
3021 *		Restore device state
3022 *		Thaw any IO which was frozen.
3023 *		Quiesce device.  (Other routines will activate if thawed IO.)
3024 *		Set device online.
3025 *		Leave device disconnected if there are problems.
3026 */
3027static void
3028hwahc_restore_device_state(dev_info_t *dip, hwahc_state_t *hwahcp)
3029{
3030	int	rval;
3031	int	old_hw_state;
3032
3033	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3034	    "hwahc_restore_device_state: dip = 0x%p", (void *)dip);
3035
3036	mutex_enter(&hwahcp->hwahc_mutex);
3037
3038	ASSERT((hwahcp->hwahc_dev_state == USB_DEV_DISCONNECTED) ||
3039	    (hwahcp->hwahc_dev_state == USB_DEV_SUSPENDED));
3040
3041	/* raise power */
3042	mutex_exit(&hwahcp->hwahc_mutex);
3043	hwahc_pm_busy_component(hwahcp);
3044	(void) pm_raise_power(hwahcp->hwahc_dip, 0, USB_DEV_OS_FULL_PWR);
3045
3046	/*
3047	 * Check if we are talking to the same device
3048	 * Some host controllers may see all devices disconnected
3049	 * when they just resume. This may be a cause of not
3050	 * finding the same device.
3051	 *
3052	 * Some HWA devices need to download firmware when it is
3053	 * powered on. Before the firmware is downloaded, the device
3054	 * will look differently.
3055	 */
3056	if (usb_check_same_device(dip, hwahcp->hwahc_log_handle,
3057	    USB_LOG_L0, PRINT_MASK_ALL,
3058	    USB_CHK_BASIC | USB_CHK_SERIAL | USB_CHK_VIDPID, NULL) !=
3059	    USB_SUCCESS) {
3060		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3061		    "hwahc_restore_device_state: not the same device");
3062		/* change the device state from suspended to disconnected */
3063		mutex_enter(&hwahcp->hwahc_mutex);
3064		hwahcp->hwahc_dev_state = USB_DEV_DISCONNECTED;
3065		hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_ERROR_STATE;
3066		mutex_exit(&hwahcp->hwahc_mutex);
3067		hwahc_pm_idle_component(hwahcp);
3068
3069		return;
3070	}
3071
3072	USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3073	    "hwahc_restore_device_state: Hwahc has been reconnected but"
3074	    " data may have been lost");
3075
3076	mutex_enter(&hwahcp->hwahc_mutex);
3077
3078	/* reinitialize the hw */
3079	hwahcp->hwahc_dev_state = USB_DEV_ONLINE;
3080	hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_INIT_STATE;
3081
3082	if (hwahcp->hwahc_hw_state == HWAHC_HW_STOPPED) {
3083		mutex_exit(&hwahcp->hwahc_mutex);
3084		/* no need to start hc */
3085		hwahc_pm_idle_component(hwahcp);
3086		USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3087		    "hwahc_restore_device_state: stopped hwa");
3088
3089		return;
3090	}
3091
3092
3093	rval = wusb_hc_set_cluster_id(&hwahcp->hwahc_hc_data,
3094	    hwahcp->hwahc_hc_data.hc_cluster_id);
3095
3096	if (rval != USB_SUCCESS) {
3097		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3098		    "hwahc_restore_device_state: set cluster id fails");
3099
3100		goto err;
3101	}
3102
3103	if (hwahcp->hwahc_hw_state == HWAHC_HW_STARTED) {
3104		old_hw_state = hwahcp->hwahc_hw_state;
3105		hwahcp->hwahc_hw_state = HWAHC_HW_CH_STOPPED;
3106		rval = hwahc_hc_channel_start(hwahcp);
3107		if (rval != USB_SUCCESS) {
3108			USB_DPRINTF_L2(PRINT_MASK_ATTA,
3109			    hwahcp->hwahc_log_handle,
3110			    "hwahc_restore_device_state: start hc fails");
3111			hwahcp->hwahc_hw_state = old_hw_state;
3112
3113			goto err;
3114		}
3115		hwahcp->hwahc_hw_state = old_hw_state;
3116	}
3117
3118	rval = wusb_hc_set_num_dnts(&hwahcp->hwahc_hc_data,
3119	    HWAHC_DEFAULT_DNTS_INTERVAL, HWAHC_DEFAULT_DNTS_SLOT_NUM);
3120	if (rval != USB_SUCCESS) {
3121		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3122		    "hwahc_restore_device_state: set num dnts fails");
3123
3124		goto err;
3125	}
3126
3127	/* set default GTK */
3128	rval = wusb_hc_set_gtk(&hwahcp->hwahc_hc_data, dft_gtk, dft_gtkid);
3129	if (rval != USB_SUCCESS) {
3130		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3131		    "hwahc_restore_device_state: set gtk fails");
3132
3133		goto err;
3134	}
3135
3136	mutex_exit(&hwahcp->hwahc_mutex);
3137
3138	rval = wusb_wa_enable(&hwahcp->hwahc_wa_data,
3139	    hwahcp->hwahc_default_pipe);
3140	mutex_enter(&hwahcp->hwahc_mutex);
3141	if (rval != USB_SUCCESS) {
3142		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3143		    "hwahc_restore_device_state: enable wa fails");
3144
3145		goto err;
3146	}
3147
3148	/*
3149	 * This is a workaround, sometimes the ioctl and reconnect will
3150	 * happen at the sametime, so the ioctl will start nep which makes
3151	 * the below sart nep fail. Need more work to do to avoid such
3152	 * issues
3153	 */
3154	(void) wusb_wa_stop_nep(&hwahcp->hwahc_wa_data);
3155
3156	rval = wusb_wa_start_nep(&hwahcp->hwahc_wa_data, USB_FLAGS_SLEEP);
3157	if (rval != USB_SUCCESS) {
3158		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3159		    "hwahc_restore_device_state: start notifep fails rval =%d",
3160		    rval);
3161		mutex_exit(&hwahcp->hwahc_mutex);
3162		(void) wusb_wa_disable(&hwahcp->hwahc_wa_data,
3163		    hwahcp->hwahc_default_pipe);
3164		mutex_enter(&hwahcp->hwahc_mutex);
3165
3166		goto err;
3167	}
3168
3169	/* Handle transfer results on bulk-in ep */
3170	rval = hwahc_start_result_thread(hwahcp);
3171	if (rval != USB_SUCCESS) {
3172		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3173		    "hwahc_restore_device_state: start result thread fails");
3174		mutex_exit(&hwahcp->hwahc_mutex);
3175		wusb_wa_stop_nep(&hwahcp->hwahc_wa_data);
3176		(void) wusb_wa_disable(&hwahcp->hwahc_wa_data,
3177		    hwahcp->hwahc_default_pipe);
3178		mutex_enter(&hwahcp->hwahc_mutex);
3179
3180		goto err;
3181	}
3182
3183	/* if the device had remote wakeup earlier, enable it again */
3184	if (hwahcp->hwahc_pm && hwahcp->hwahc_pm->hwahc_wakeup_enabled) {
3185		mutex_exit(&hwahcp->hwahc_mutex);
3186		(void) usb_handle_remote_wakeup(hwahcp->hwahc_dip,
3187		    USB_REMOTE_WAKEUP_ENABLE);
3188		mutex_enter(&hwahcp->hwahc_mutex);
3189	}
3190
3191	hwahcp->hwahc_hw_state = HWAHC_HW_STARTED;
3192	hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_OPERATIONAL_STATE;
3193	mutex_exit(&hwahcp->hwahc_mutex);
3194	hwahc_pm_idle_component(hwahcp);
3195
3196	return;
3197
3198err:
3199	hwahcp->hwahc_hw_state = HWAHC_HW_STOPPED;
3200	mutex_exit(&hwahcp->hwahc_mutex);
3201	hwahc_pm_idle_component(hwahcp);
3202}
3203
3204
3205/*
3206 * hwahc_cpr_suspend:
3207 *	Clean up device.
3208 *	Wait for any IO to finish, then close pipes.
3209 *	Quiesce device.
3210 * due to the dependency on hwarc, the actual suspend operations are
3211 * moved to hwahc_pre_suspend_event_cb function.
3212 */
3213static int
3214hwahc_cpr_suspend(dev_info_t *dip)
3215{
3216	int		instance = ddi_get_instance(dip);
3217	hwahc_state_t	*hwahcp = ddi_get_soft_state(hwahc_statep, instance);
3218
3219	if (hwahcp == NULL) {
3220
3221		return (USB_FAILURE);
3222	}
3223
3224	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3225	    "hwahc_cpr_suspend: start");
3226
3227	mutex_enter(&hwahcp->hwahc_mutex);
3228
3229	/* Don't suspend if the device is open. */
3230	if (hwahcp->hwahc_open_count > 0) {
3231		USB_DPRINTF_L2(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3232		    "hwahc_cpr_suspend: Device is open, cannot suspend");
3233		mutex_exit(&hwahcp->hwahc_mutex);
3234
3235		return (USB_FAILURE);
3236	}
3237
3238	mutex_exit(&hwahcp->hwahc_mutex);
3239	/* raise power */
3240	hwahc_pm_busy_component(hwahcp);
3241	(void) pm_raise_power(hwahcp->hwahc_dip, 0, USB_DEV_OS_FULL_PWR);
3242
3243	mutex_enter(&hwahcp->hwahc_mutex);
3244	switch (hwahcp->hwahc_dev_state) {
3245	case USB_DEV_ONLINE:
3246	/* real suspend operations put in pre_suspend function */
3247		/* FALLTHRU */
3248	case USB_DEV_DISCONNECTED:
3249	case USB_DEV_PWRED_DOWN:
3250		hwahcp->hwahc_dev_state = USB_DEV_SUSPENDED;
3251		hwahcp->hwahc_hw_state = HWAHC_HW_CH_SUSPEND;
3252
3253		break;
3254	case USB_DEV_SUSPENDED:
3255	default:
3256		USB_DPRINTF_L2(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3257		    "hwahc_cpr_suspend: illegal dev state=%d",
3258		    hwahcp->hwahc_dev_state);
3259
3260		break;
3261	}
3262
3263	mutex_exit(&hwahcp->hwahc_mutex);
3264	hwahc_pm_idle_component(hwahcp);
3265
3266	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3267	    "hwahc_cpr_suspend: end");
3268
3269	return (USB_SUCCESS);
3270}
3271
3272
3273/*
3274 * hwahc_cpr_resume:
3275 *
3276 *	hwahc_restore_device_state marks success by putting device back online
3277 */
3278static int
3279hwahc_cpr_resume(dev_info_t *dip)
3280{
3281	int		instance = ddi_get_instance(dip);
3282	hwahc_state_t	*hwahcp = ddi_get_soft_state(hwahc_statep, instance);
3283
3284	if (hwahcp == NULL) {
3285
3286		return (USB_FAILURE);
3287	}
3288
3289	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3290	    "hwahc_cpr_resume: hw state = %d, softstate = %d",
3291	    hwahcp->hwahc_hw_state, hwahcp->hwahc_hc_soft_state);
3292
3293	/*
3294	 * rc is always resumed after hc. restoring hc before rc would fail.
3295	 * move the restoring operations to hwahc_post_resume_event_cb.
3296	 */
3297
3298	return (USB_SUCCESS);
3299}
3300
3301/*
3302 * hwahc_create_pm_components:
3303 *	Create power managements components
3304 */
3305static void
3306hwahc_create_pm_components(dev_info_t *dip, hwahc_state_t *hwahcp)
3307{
3308	hwahc_power_t	*hwahcpm;
3309	uint_t		pwr_states;
3310
3311	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3312	    "hwahc_create_pm_components: Begin");
3313
3314	/* Allocate the state structure */
3315	hwahcpm = kmem_zalloc(sizeof (hwahc_power_t), KM_SLEEP);
3316	hwahcp->hwahc_pm = hwahcpm;
3317	hwahcpm->hwahc_state = hwahcp;
3318	hwahcpm->hwahc_pm_capabilities = 0;
3319	hwahcpm->hwahc_current_power = USB_DEV_OS_FULL_PWR;
3320
3321	if (usb_create_pm_components(dip, &pwr_states) == USB_SUCCESS) {
3322		USB_DPRINTF_L3(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3323		    "hwahc_create_pm_components: created PM components");
3324
3325		if (usb_handle_remote_wakeup(dip,
3326		    USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) {
3327			hwahcpm->hwahc_wakeup_enabled = 1;
3328		}
3329		hwahcpm->hwahc_pwr_states = (uint8_t)pwr_states;
3330		/* make device busy till end of attach */
3331		hwahc_pm_busy_component(hwahcp);
3332		(void) pm_raise_power(hwahcp->hwahc_dip, 0,
3333		    USB_DEV_OS_FULL_PWR);
3334	} else {
3335		USB_DPRINTF_L3(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3336		    "hwahc_create_pm_components: failed");
3337	}
3338
3339	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3340	    "hwahc_create_pm_components: End");
3341}
3342
3343/*
3344 * hwahc_destroy_pm_components:
3345 *	Shut down and destroy power management and remote wakeup functionality
3346 */
3347static void
3348hwahc_destroy_pm_components(hwahc_state_t *hwahcp)
3349{
3350	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3351	    "hwahc_destroy_pm_components: Begin");
3352
3353	ASSERT(!mutex_owned(&hwahcp->hwahc_mutex));
3354
3355	mutex_enter(&hwahcp->hwahc_mutex);
3356	if (hwahcp->hwahc_pm && (hwahcp->hwahc_dev_state !=
3357	    USB_DEV_DISCONNECTED)) {
3358		mutex_exit(&hwahcp->hwahc_mutex);
3359		hwahc_pm_busy_component(hwahcp);
3360		mutex_enter(&hwahcp->hwahc_mutex);
3361
3362		if (hwahcp->hwahc_pm->hwahc_wakeup_enabled) {
3363			int rval;
3364
3365			mutex_exit(&hwahcp->hwahc_mutex);
3366			(void) pm_raise_power(hwahcp->hwahc_dip, 0,
3367			    USB_DEV_OS_FULL_PWR);
3368
3369			if ((rval = usb_handle_remote_wakeup(
3370			    hwahcp->hwahc_dip,
3371			    USB_REMOTE_WAKEUP_DISABLE)) !=
3372			    USB_SUCCESS) {
3373				USB_DPRINTF_L3(PRINT_MASK_PM,
3374				    hwahcp->hwahc_log_handle,
3375				    "hwahc_destroy_pm_components: "
3376				    "Error disabling rmt wakeup: rval = %d",
3377				    rval);
3378			}
3379		} else {
3380			mutex_exit(&hwahcp->hwahc_mutex);
3381		}
3382
3383		/*
3384		 * Since remote wakeup is disabled now,
3385		 * no one can raise power and get to device
3386		 * once power is lowered here.
3387		 */
3388		(void) pm_lower_power(hwahcp->hwahc_dip, 0, USB_DEV_OS_PWR_OFF);
3389
3390		hwahc_pm_idle_component(hwahcp);
3391		mutex_enter(&hwahcp->hwahc_mutex);
3392	}
3393
3394	if (hwahcp->hwahc_pm) {
3395		kmem_free(hwahcp->hwahc_pm, sizeof (hwahc_power_t));
3396		hwahcp->hwahc_pm = NULL;
3397	}
3398	mutex_exit(&hwahcp->hwahc_mutex);
3399
3400	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3401	    "hwahc_destroy_pm_components: End");
3402}
3403
3404/* mark component busy */
3405static void
3406hwahc_pm_busy_component(hwahc_state_t *hwahcp)
3407{
3408	ASSERT(!mutex_owned(&hwahcp->hwahc_mutex));
3409
3410	if (hwahcp->hwahc_pm != NULL) {
3411		mutex_enter(&hwahcp->hwahc_mutex);
3412		hwahcp->hwahc_pm->hwahc_pm_busy++;
3413		USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3414		    "hwahc_pm_busy_component: %d",
3415		    hwahcp->hwahc_pm->hwahc_pm_busy);
3416		mutex_exit(&hwahcp->hwahc_mutex);
3417
3418		if (pm_busy_component(hwahcp->hwahc_dip, 0) !=
3419		    DDI_SUCCESS) {
3420			mutex_enter(&hwahcp->hwahc_mutex);
3421			hwahcp->hwahc_pm->hwahc_pm_busy--;
3422			USB_DPRINTF_L2(PRINT_MASK_PM,
3423			    hwahcp->hwahc_log_handle,
3424			    "hwahc_pm_busy_component failed: %d",
3425			    hwahcp->hwahc_pm->hwahc_pm_busy);
3426			mutex_exit(&hwahcp->hwahc_mutex);
3427		}
3428	}
3429}
3430
3431/* mark component idle */
3432static void
3433hwahc_pm_idle_component(hwahc_state_t *hwahcp)
3434{
3435	ASSERT(!mutex_owned(&hwahcp->hwahc_mutex));
3436
3437	if (hwahcp->hwahc_pm != NULL) {
3438
3439		if (pm_idle_component(hwahcp->hwahc_dip, 0) ==
3440		    DDI_SUCCESS) {
3441			mutex_enter(&hwahcp->hwahc_mutex);
3442			ASSERT(hwahcp->hwahc_pm->hwahc_pm_busy > 0);
3443			hwahcp->hwahc_pm->hwahc_pm_busy--;
3444			USB_DPRINTF_L4(PRINT_MASK_PM,
3445			    hwahcp->hwahc_log_handle,
3446			    "hwahc_pm_idle_component: %d",
3447			    hwahcp->hwahc_pm->hwahc_pm_busy);
3448			mutex_exit(&hwahcp->hwahc_mutex);
3449		}
3450	}
3451}
3452
3453/*
3454 * hwahc_power :
3455 *	Power entry point, the workhorse behind pm_raise_power, pm_lower_power,
3456 *	usb_req_raise_power and usb_req_lower_power.
3457 */
3458/* ARGSUSED */
3459static int
3460hwahc_power(dev_info_t *dip, int comp, int level)
3461{
3462	hwahc_state_t	*hwahcp;
3463	hwahc_power_t	*pm;
3464	int		rval = USB_FAILURE;
3465
3466	hwahcp = ddi_get_soft_state(hwahc_statep, ddi_get_instance(dip));
3467
3468	if (hwahcp == NULL) {
3469
3470		return (DDI_FAILURE);
3471	}
3472
3473	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3474	    "hwahc_power: dip = 0x%p", (void *)dip);
3475
3476	mutex_enter(&hwahcp->hwahc_mutex);
3477
3478	if (hwahcp->hwahc_pm == NULL) {
3479
3480		goto done;
3481	}
3482
3483	pm = hwahcp->hwahc_pm;
3484
3485	/* Check if we are transitioning to a legal power level */
3486	if (USB_DEV_PWRSTATE_OK(pm->hwahc_pwr_states, level)) {
3487		USB_DPRINTF_L2(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3488		    "hwahc_power: illegal power level = %d "
3489		    "pwr_states: %x", level, pm->hwahc_pwr_states);
3490
3491		goto done;
3492	}
3493
3494	switch (level) {
3495	case USB_DEV_OS_PWR_OFF :
3496		rval = hwahc_pwrlvl0(hwahcp);
3497
3498		break;
3499	case USB_DEV_OS_PWR_1:
3500		rval = hwahc_pwrlvl1(hwahcp);
3501
3502		break;
3503	case USB_DEV_OS_PWR_2:
3504		rval = hwahc_pwrlvl2(hwahcp);
3505
3506		break;
3507	case USB_DEV_OS_FULL_PWR :
3508		rval = hwahc_pwrlvl3(hwahcp);
3509
3510		break;
3511	}
3512done:
3513	mutex_exit(&hwahcp->hwahc_mutex);
3514
3515	return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
3516}
3517
3518/*
3519 * hwahc_pwrlvl0:
3520 * Functions to handle power transition for OS levels 0 -> 3
3521 *	OS 0 <--> USB D3, no or minimal power
3522 */
3523static int
3524hwahc_pwrlvl0(hwahc_state_t *hwahcp)
3525{
3526	int	rval;
3527
3528	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3529	    "hwahc_pwrlvl0: %d", hwahcp->hwahc_pm->hwahc_pm_busy);
3530
3531	switch (hwahcp->hwahc_dev_state) {
3532	case USB_DEV_ONLINE:
3533		/* Deny the powerdown request if the device is busy */
3534		if (hwahcp->hwahc_pm->hwahc_pm_busy != 0) {
3535			USB_DPRINTF_L2(PRINT_MASK_PM,
3536			    hwahcp->hwahc_log_handle,
3537			    "hwahc_pwrlvl0: hwahc_pm is busy");
3538
3539			return (USB_FAILURE);
3540		}
3541		/*
3542		 * only when final_stop gets called, we allow the system
3543		 * to do PM on us. At this moment, we don't need to do
3544		 * more operations other than those in final_stop.
3545		 */
3546
3547		/* Issue USB D3 command to the device here */
3548		rval = usb_set_device_pwrlvl3(hwahcp->hwahc_dip);
3549		ASSERT(rval == USB_SUCCESS);
3550
3551		hwahcp->hwahc_dev_state = USB_DEV_PWRED_DOWN;
3552
3553		hwahcp->hwahc_pm->hwahc_current_power = USB_DEV_OS_PWR_OFF;
3554
3555		break;
3556	case USB_DEV_DISCONNECTED:
3557	case USB_DEV_SUSPENDED:
3558	case USB_DEV_PWRED_DOWN:
3559	default:
3560		break;
3561	}
3562
3563	return (USB_SUCCESS);
3564}
3565
3566/*
3567 * hwahc_pwrlvl1:
3568 *	Functions to handle power transition to OS levels -> 2
3569 *	OS level 1 <--> D2
3570 */
3571static int
3572hwahc_pwrlvl1(hwahc_state_t *hwahcp)
3573{
3574	int	rval;
3575
3576	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3577	    "hwahc_pwrlvl1:");
3578
3579	/* Issue USB D2 command to the device here */
3580	rval = usb_set_device_pwrlvl2(hwahcp->hwahc_dip);
3581	ASSERT(rval == USB_SUCCESS);
3582
3583	return (USB_FAILURE);
3584}
3585
3586/*
3587 * hwahc_pwrlvl2:
3588 *	Functions to handle power transition to OS levels -> 1
3589 *	OS leve 2 <--> D1
3590 */
3591static int
3592hwahc_pwrlvl2(hwahc_state_t *hwahcp)
3593{
3594	int	rval;
3595
3596	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3597	    "hwahc_pwrlvl2:");
3598
3599	/* Issue USB D1 command to the device here */
3600	rval = usb_set_device_pwrlvl1(hwahcp->hwahc_dip);
3601	ASSERT(rval == USB_SUCCESS);
3602
3603	return (USB_FAILURE);
3604}
3605
3606
3607/*
3608 * hwahc_pwrlvl3:
3609 *	Functions to handle power transition to OS level -> 0
3610 *	OS level 3 <--> D0 (full power)
3611 */
3612static int
3613hwahc_pwrlvl3(hwahc_state_t *hwahcp)
3614{
3615	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3616	    "hwahc_pwrlvl3: %d", hwahcp->hwahc_pm->hwahc_pm_busy);
3617
3618	ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
3619
3620	switch (hwahcp->hwahc_dev_state) {
3621	case USB_DEV_PWRED_DOWN:
3622		/* Issue USB D0 command to the device here */
3623		(void) usb_set_device_pwrlvl0(hwahcp->hwahc_dip);
3624
3625		/*
3626		 * Due to our current PM policy, it's not possible
3627		 * for hwa to be in USB_DEV_PWRED_DOWN between
3628		 * initial_start and final_stop. If it's PWRED_DOWN,
3629		 * it should not start. We don't need to resume
3630		 * soft or hardware state in this case.
3631		 */
3632		if (hwahcp->hwahc_hw_state == HWAHC_HW_STOPPED) {
3633			/* no need to start hc */
3634			hwahcp->hwahc_dev_state = USB_DEV_ONLINE;
3635			hwahcp->hwahc_pm->hwahc_current_power =
3636			    USB_DEV_OS_FULL_PWR;
3637
3638			return (USB_SUCCESS);
3639		}
3640
3641		hwahcp->hwahc_pm->hwahc_current_power = USB_DEV_OS_FULL_PWR;
3642
3643		/* FALLTHRU */
3644	case USB_DEV_ONLINE:
3645		/* we are already in full power */
3646		/* FALLTHRU */
3647	case USB_DEV_DISCONNECTED:
3648	case USB_DEV_SUSPENDED:
3649		/*
3650		 * PM framework tries to put you in full power
3651		 * during system shutdown. If we are disconnected
3652		 * return success. Also, we should not change state
3653		 * when we are disconnected or suspended or about to
3654		 * transition to that state
3655		 */
3656
3657		return (USB_SUCCESS);
3658	default:
3659		USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3660		    "hwahc_pwrlvl3: illegal dev_state=%d",
3661		    hwahcp->hwahc_dev_state);
3662
3663
3664		return (USB_FAILURE);
3665	}
3666}
3667
3668/*
3669 * Host power management: stop channel
3670 * 	See Section 4.16.2.1 for details
3671 *	See Section 8.1.0 for HWA suspend/resume
3672 */
3673static int
3674hwahc_hc_channel_suspend(hwahc_state_t *hwahcp)
3675{
3676	int			rval;
3677
3678	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3679	    "hwahc_hc_channel_suspend:");
3680
3681	ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
3682
3683	/* no need to suspend if host hw was not started */
3684	if (hwahcp->hwahc_hw_state != HWAHC_HW_STARTED) {
3685		USB_DPRINTF_L3(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3686		    "hwahc_hc_channel_suspend: hw already stopped");
3687
3688		return (USB_SUCCESS);
3689	}
3690
3691	if (hwahcp->hwahc_hw_state == HWAHC_HW_CH_SUSPEND) {
3692		USB_DPRINTF_L3(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3693		    "hwahc_hc_channel_suspend: already suspended");
3694
3695		return (USB_SUCCESS);
3696	}
3697
3698	mutex_exit(&hwahcp->hwahc_mutex);
3699	/* suspend host, refer to WUSB 1.0 spec 8.5.3.14 */
3700	rval = wusb_hc_stop_ch(&hwahcp->hwahc_hc_data, 10000); /* 10ms */
3701	mutex_enter(&hwahcp->hwahc_mutex);
3702	if (rval != USB_SUCCESS) {
3703		USB_DPRINTF_L2(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
3704		    "hwahc_hc_channel_suspend: wusb channel stop fails");
3705
3706		return (rval);
3707	}
3708
3709	hwahcp->hwahc_hw_state = HWAHC_HW_CH_SUSPEND;
3710
3711	return (USB_SUCCESS);
3712}
3713
3714/*
3715 * Parse security descriptors, see T.8-43
3716 * 	put result in secrt_data
3717 */
3718static int
3719hwahc_parse_security_data(wusb_secrt_data_t *secrt_data,
3720	usb_cfg_data_t *cfg_data)
3721{
3722	int		i, j;
3723	usb_cvs_data_t	*cvs_data;
3724	size_t		count, len;
3725
3726	if ((secrt_data == NULL) || (cfg_data == NULL)) {
3727		return (USB_INVALID_ARGS);
3728	}
3729
3730	for (i = 0; i < cfg_data->cfg_n_cvs; i++) {
3731		cvs_data = &cfg_data->cfg_cvs[i];
3732		if (cvs_data == NULL) {
3733			continue;
3734		}
3735		if (cvs_data->cvs_buf[1] == USB_DESCR_TYPE_SECURITY) {
3736			count = usb_parse_data("ccsc",
3737			    cvs_data->cvs_buf, cvs_data->cvs_buf_len,
3738			    (void *)&secrt_data->secrt_descr,
3739			    (size_t)USB_SECURITY_DESCR_SIZE);
3740			if (count != USB_SECURITY_DESCR_SIZE) {
3741
3742				return (USB_FAILURE);
3743			} else {
3744				secrt_data->secrt_n_encry =
3745				    secrt_data->secrt_descr.bNumEncryptionTypes;
3746				len = sizeof (usb_encryption_descr_t) *
3747				    secrt_data->secrt_n_encry;
3748
3749				secrt_data->secrt_encry_descr =
3750				    (usb_encryption_descr_t *)kmem_alloc(len,
3751				    KM_SLEEP);
3752
3753				for (j = 0; j < secrt_data->secrt_n_encry;
3754				    j++) {
3755					cvs_data =
3756					    &cfg_data->cfg_cvs[i + j + 1];
3757					if (cvs_data->cvs_buf[1] !=
3758					    USB_DESCR_TYPE_ENCRYPTION) {
3759						kmem_free(secrt_data->
3760						    secrt_encry_descr, len);
3761
3762						return (USB_FAILURE);
3763					}
3764
3765					/* Table 7-34 */
3766					count = usb_parse_data("ccccc",
3767					    cvs_data->cvs_buf,
3768					    cvs_data->cvs_buf_len,
3769					    (void *)&secrt_data->
3770					    secrt_encry_descr[j],
3771					    USB_ENCRYPTION_DESCR_SIZE);
3772					if (count !=
3773					    USB_ENCRYPTION_DESCR_SIZE) {
3774						kmem_free(secrt_data->
3775						    secrt_encry_descr, len);
3776
3777						return (USB_FAILURE);
3778
3779					}
3780				}
3781				return (USB_SUCCESS);
3782			}
3783		}
3784	}
3785
3786	return (USB_FAILURE);
3787}
3788
3789/* initialize wusb_hc_data_t structure */
3790static void
3791hwahc_hc_data_init(hwahc_state_t *hwahcp)
3792{
3793	wusb_hc_data_t	*hc_data = &hwahcp->hwahc_hc_data;
3794
3795	hc_data->hc_dip = hwahcp->hwahc_dip;
3796	hc_data->hc_private_data = (void *)hwahcp;
3797
3798	(void) memset(hc_data->hc_chid, 0, sizeof (hc_data->hc_chid));
3799
3800	hc_data->hc_num_mmcies = hwahcp->hwahc_wa_data.wa_descr.bNumMMCIEs;
3801
3802	ASSERT(hc_data->hc_num_mmcies != 0);
3803
3804	hc_data->hc_mmcie_list = kmem_zalloc((hc_data->hc_num_mmcies *
3805	    sizeof (wusb_ie_header_t *)), KM_SLEEP);
3806
3807	/* initialize frequently used IE */
3808	hc_data->hc_alive_ie.bIEIdentifier = WUSB_IE_DEV_KEEPALIVE;
3809
3810	/* register callbacks */
3811	hc_data->disconnect_dev = hwahc_disconnect_dev;
3812	hc_data->reconnect_dev = hwahc_reconnect_dev;
3813	hc_data->create_child = hwahc_create_child;
3814	hc_data->destroy_child = hwahc_destroy_child;
3815
3816	/* HWA HC operation functions */
3817	hc_data->set_encrypt = hwahc_set_encrypt;
3818	hc_data->set_ptk = hwahc_set_ptk;
3819	hc_data->set_gtk = hwahc_set_gtk;
3820	hc_data->set_device_info = hwahc_set_device_info;
3821	hc_data->set_cluster_id = hwahc_set_cluster_id;
3822	hc_data->set_stream_idx = hwahc_set_stream_idx;
3823	hc_data->set_wusb_mas = hwahc_set_wusb_mas;
3824	hc_data->add_mmc_ie = hwahc_add_mmc_ie;
3825	hc_data->rem_mmc_ie = hwahc_remove_mmc_ie;
3826	hc_data->stop_ch = hwahc_stop_ch;
3827	hc_data->set_num_dnts = hwahc_set_num_dnts;
3828	hc_data->get_time = hwahc_get_time;
3829
3830	hc_data->hc_num_ports = hwahcp->hwahc_wa_data.wa_descr.bNumPorts;
3831
3832	hc_data->hc_cd_list_length = (sizeof (dev_info_t **)) *
3833	    (hc_data->hc_num_ports + 1);
3834
3835	hc_data->hc_children_dips = (dev_info_t **)kmem_zalloc(
3836	    hc_data->hc_cd_list_length, KM_SLEEP);
3837	hc_data->hc_usba_devices = (usba_device_t **)kmem_zalloc(
3838	    hc_data->hc_cd_list_length, KM_SLEEP);
3839	hc_data->hc_dev_infos = (wusb_dev_info_t **)kmem_zalloc(
3840	    hc_data->hc_cd_list_length, KM_SLEEP);
3841
3842	mutex_init(&hc_data->hc_mutex, NULL, MUTEX_DRIVER, NULL);
3843}
3844
3845/* deinitialize wusb_hc_data_t structure */
3846static void
3847hwahc_hc_data_fini(hwahc_state_t *hwahcp)
3848{
3849	int			i;
3850	wusb_hc_data_t		*hc_data = &hwahcp->hwahc_hc_data;
3851	wusb_ie_header_t	*hdr;
3852
3853#ifdef DEBUG
3854	usb_port_t	port;
3855#endif
3856
3857	if (hc_data->hc_mmcie_list) {
3858		/* Free all recorded IEs except statically allocated IEs */
3859		for (i = 0; i < hc_data->hc_num_mmcies; i++) {
3860			if (hc_data->hc_mmcie_list[i] != NULL) {
3861				hdr = hc_data->hc_mmcie_list[i];
3862				if ((hdr->bIEIdentifier !=
3863				    WUSB_IE_DEV_KEEPALIVE)) {
3864					kmem_free(hdr, hdr->bLength);
3865				}
3866				hc_data->hc_mmcie_list[i] = NULL;
3867			}
3868		}
3869
3870		kmem_free(hc_data->hc_mmcie_list,
3871		    hc_data->hc_num_mmcies * sizeof (wusb_ie_header_t *));
3872	}
3873
3874	if (hc_data->hc_cluster_id) {
3875		wusb_hc_free_cluster_id(hc_data->hc_cluster_id);
3876	}
3877
3878	if (hc_data->hc_cc_list) {
3879		wusb_hc_free_cc_list(hc_data->hc_cc_list);
3880	}
3881
3882#ifdef DEBUG
3883	for (port = 1; port <= hc_data->hc_num_ports; port++) {
3884		ASSERT(hc_data->hc_usba_devices[port] == NULL);
3885		ASSERT(hc_data->hc_children_dips[port] == NULL);
3886		ASSERT(hc_data->hc_dev_infos[port] == NULL);
3887	}
3888#endif
3889
3890	kmem_free(hc_data->hc_children_dips, hc_data->hc_cd_list_length);
3891	kmem_free(hc_data->hc_usba_devices, hc_data->hc_cd_list_length);
3892	kmem_free(hc_data->hc_dev_infos, hc_data->hc_cd_list_length);
3893
3894	mutex_destroy(&hc_data->hc_mutex);
3895}
3896
3897/* fully start the HWA hw */
3898static int
3899hwahc_hc_initial_start(hwahc_state_t *hwahcp)
3900{
3901	uint8_t	stream_idx;
3902	uint8_t	mas[WUSB_SET_WUSB_MAS_LEN];
3903	int	rval;
3904	uint8_t	cluster_id = 0;
3905
3906	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3907	    "hwahc_hc_initial_start:");
3908
3909	ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
3910
3911	if (hwahcp->hwahc_dev_state != USB_DEV_ONLINE) {
3912		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3913		    "hwahc_hc_initial_start: invalid dev state = %d",
3914		    hwahcp->hwahc_dev_state);
3915
3916		return (USB_INVALID_REQUEST);
3917	}
3918
3919	if (hwahcp->hwahc_hw_state != HWAHC_HW_STOPPED) {
3920		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3921		    "hwahc_hc_initial_start: invalid hw state");
3922
3923		return (USB_INVALID_REQUEST);
3924	}
3925
3926	/*
3927	 * start beacon of radio layer
3928	 * We're not sure if previouse channel is occupied or not. So, let
3929	 * UWB allocates a free channel for this hwa. Then we can start
3930	 * beacon.
3931	 */
3932	hwahcp->hwahc_hc_data.hc_channel =
3933	    uwb_allocate_channel(hwahcp->hwahc_dip);
3934	if (hwahcp->hwahc_hc_data.hc_channel  == 0) {
3935		USB_DPRINTF_L2(PRINT_MASK_ATTA,
3936		    hwahcp->hwahc_log_handle,
3937		    "wusb_hc_initial_start: channel = %d",
3938		    hwahcp->hwahc_hc_data.hc_channel);
3939		return (USB_FAILURE);
3940	}
3941
3942	if ((rval = uwb_start_beacon(hwahcp->hwahc_dip,
3943	    hwahcp->hwahc_hc_data.hc_channel)) != USB_SUCCESS) {
3944		USB_DPRINTF_L2(PRINT_MASK_ATTA,
3945		    hwahcp->hwahc_log_handle,
3946		    "wusb_hc_initial_start: start uwb beacon failed");
3947
3948		return (rval);
3949	}
3950
3951	mutex_exit(&hwahcp->hwahc_mutex);
3952	/* reset wire adapter */
3953	rval = wusb_wa_reset(&hwahcp->hwahc_wa_data,
3954	    hwahcp->hwahc_default_pipe);
3955	mutex_enter(&hwahcp->hwahc_mutex);
3956	if (rval != SUCCESS) {
3957		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3958		    "hwahc_hc_initial_start: reset wa fails");
3959
3960		goto err;
3961	}
3962
3963	/* reuse the old cluster id or assign one */
3964	if (hwahcp->hwahc_hc_data.hc_cluster_id) {
3965		cluster_id = hwahcp->hwahc_hc_data.hc_cluster_id;
3966	} else {
3967		cluster_id = wusb_hc_get_cluster_id();
3968		if (cluster_id == 0) {
3969			USB_DPRINTF_L2(PRINT_MASK_ATTA,
3970			    hwahcp->hwahc_log_handle,
3971			    "hwahc_hc_initial_start: cannot get cluster id");
3972			rval = USB_NO_RESOURCES;
3973
3974			goto err;
3975		}
3976	}
3977
3978	mutex_exit(&hwahcp->hwahc_mutex);
3979	/* set cluster id for the wusb channel */
3980	rval = wusb_hc_set_cluster_id(&hwahcp->hwahc_hc_data, cluster_id);
3981	if (rval != USB_SUCCESS) {
3982		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3983		    "hwahc_hc_initial_start: set cluster id %d fails",
3984		    cluster_id);
3985		mutex_enter(&hwahcp->hwahc_mutex);
3986
3987		goto err;
3988	}
3989
3990	/* UWB should be responsible for assigning stream index */
3991	stream_idx = 1;
3992
3993	rval = wusb_hc_set_stream_idx(&hwahcp->hwahc_hc_data, stream_idx);
3994	if (rval != USB_SUCCESS) {
3995		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
3996		    "hwahc_hc_initial_start: set stream idx %d fails",
3997		    stream_idx);
3998		mutex_enter(&hwahcp->hwahc_mutex);
3999
4000		goto err;
4001	}
4002
4003	/* set dnts slot */
4004	rval = wusb_hc_set_num_dnts(&hwahcp->hwahc_hc_data,
4005	    HWAHC_DEFAULT_DNTS_INTERVAL, HWAHC_DEFAULT_DNTS_SLOT_NUM);
4006
4007	if (rval != USB_SUCCESS) {
4008		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4009		    "hwahc_hc_initial_start: set num dnts fails");
4010		mutex_enter(&hwahcp->hwahc_mutex);
4011
4012		goto err;
4013	}
4014
4015	/* set host info IE */
4016	rval = wusb_hc_add_host_info(&hwahcp->hwahc_hc_data, stream_idx);
4017	if (rval != USB_SUCCESS) {
4018		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4019		    "hwahc_hc_initial_start: add hostinfo ie fails");
4020		mutex_enter(&hwahcp->hwahc_mutex);
4021
4022		goto err;
4023	}
4024
4025	/* reserve MAS slots for the host, need a way to assign */
4026	(void) memset(mas, 0xff, WUSB_SET_WUSB_MAS_LEN);
4027	mas[0] = 0xf0;	/* the first 4 slots are for beacons */
4028	rval = wusb_hc_set_wusb_mas(&hwahcp->hwahc_hc_data, mas);
4029	mutex_enter(&hwahcp->hwahc_mutex);
4030	if (rval != USB_SUCCESS) {
4031		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4032		    "hwahc_hc_initial_start: set wusb mas fails");
4033
4034		goto err;
4035	}
4036
4037	/* record the available MAS slots */
4038	(void) memcpy(hwahcp->hwahc_hc_data.hc_mas, mas, WUSB_SET_WUSB_MAS_LEN);
4039
4040	/* Set initial GTK/TKID to random values */
4041	(void) random_get_pseudo_bytes(dft_gtk, 16);
4042	(void) random_get_pseudo_bytes(dft_gtkid, 3);
4043
4044	/* set default GTK, need a way to dynamically compute it */
4045	mutex_exit(&hwahcp->hwahc_mutex);
4046	rval = wusb_hc_set_gtk(&hwahcp->hwahc_hc_data, dft_gtk, dft_gtkid);
4047	if (rval != USB_SUCCESS) {
4048		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4049		    "hwahc_hc_initial_start: set gtk fails");
4050		mutex_enter(&hwahcp->hwahc_mutex);
4051
4052		goto err;
4053	}
4054
4055	/* enable wire adapter */
4056	rval = wusb_wa_enable(&hwahcp->hwahc_wa_data,
4057	    hwahcp->hwahc_default_pipe);
4058	if (rval != USB_SUCCESS) {
4059		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4060		    "hwahc_hc_initial_start: enable wa fails");
4061		mutex_enter(&hwahcp->hwahc_mutex);
4062
4063		goto err;
4064	}
4065
4066	/* Start Notification endpoint */
4067	rval = wusb_wa_start_nep(&hwahcp->hwahc_wa_data, USB_FLAGS_SLEEP);
4068
4069	if (rval != USB_SUCCESS) {
4070		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4071		    "hwahc_hc_initial_start: start notification ep fails");
4072		(void) wusb_wa_disable(&hwahcp->hwahc_wa_data,
4073		    hwahcp->hwahc_default_pipe);
4074
4075		mutex_enter(&hwahcp->hwahc_mutex);
4076
4077		goto err;
4078	}
4079
4080	mutex_enter(&hwahcp->hwahc_mutex);
4081
4082	/*
4083	 * Handle transfer results on bulk-in ep
4084	 * The bulk-in ep needs to be polled no matter the completion
4085	 * notification is received or not to avoid miss result.
4086	 */
4087	rval = hwahc_start_result_thread(hwahcp);
4088	if (rval != USB_SUCCESS) {
4089		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4090		    "hwahc_hc_initial_start: start result thread fails, "
4091		    "rval = %d", rval);
4092		mutex_exit(&hwahcp->hwahc_mutex);
4093		wusb_wa_stop_nep(&hwahcp->hwahc_wa_data);
4094		(void) wusb_wa_disable(&hwahcp->hwahc_wa_data,
4095		    hwahcp->hwahc_default_pipe);
4096		mutex_enter(&hwahcp->hwahc_mutex);
4097
4098		goto err;
4099	}
4100	USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4101	    "hwahc_hc_initial_start: start result thread success");
4102
4103	hwahcp->hwahc_hw_state = HWAHC_HW_STARTED;
4104	hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_OPERATIONAL_STATE;
4105
4106	/* Don't do PM on an active beacon hwa until explicitly stopped */
4107	mutex_exit(&hwahcp->hwahc_mutex);
4108	hwahc_pm_busy_component(hwahcp);
4109	mutex_enter(&hwahcp->hwahc_mutex);
4110
4111	return (USB_SUCCESS);
4112
4113err:
4114	if (cluster_id != 0) {
4115		wusb_hc_free_cluster_id(cluster_id);
4116	}
4117
4118	mutex_exit(&hwahcp->hwahc_mutex);
4119	(void) uwb_stop_beacon(hwahcp->hwahc_dip);
4120	mutex_enter(&hwahcp->hwahc_mutex);
4121
4122	return (rval);
4123}
4124
4125/* entirely stop the HWA from working */
4126static int
4127hwahc_hc_final_stop(hwahc_state_t *hwahcp)
4128{
4129	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4130	    "hwahc_hc_final_stop:");
4131
4132	ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
4133
4134	if (hwahcp->hwahc_hw_state == HWAHC_HW_STOPPED) {
4135		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4136		    "hwahc_hc_final_stop: already stopped");
4137
4138		return (USB_SUCCESS);
4139	}
4140
4141	if (hwahcp->hwahc_dev_state == USB_DEV_SUSPENDED) {
4142		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4143		    "hwahc_hc_final_stop: invalid dev state = %d",
4144		    hwahcp->hwahc_dev_state);
4145
4146		return (USB_INVALID_REQUEST);
4147	}
4148
4149	/* might have been powered down before detaching */
4150	mutex_exit(&hwahcp->hwahc_mutex);
4151	(void) pm_raise_power(hwahcp->hwahc_dip, 0, USB_DEV_OS_FULL_PWR);
4152	mutex_enter(&hwahcp->hwahc_mutex);
4153
4154	if (hwahcp->hwahc_dev_state != USB_DEV_DISCONNECTED) {
4155		/* notify children the host is going to stop */
4156		(void) hwahc_hc_channel_suspend(hwahcp);
4157
4158		/* release mutex here to avoid deadlock with exc_cb */
4159		mutex_exit(&hwahcp->hwahc_mutex);
4160
4161		/* stop notification endpoint */
4162		wusb_wa_stop_nep(&hwahcp->hwahc_wa_data);
4163		mutex_enter(&hwahcp->hwahc_mutex);
4164
4165		/* stop bulk-in ept from listening result */
4166		hwahc_stop_result_thread(hwahcp);
4167
4168		/* drain the device notifications */
4169		hwahc_drain_notif_queue(hwahcp);
4170
4171		/* disable wire adapter */
4172		mutex_exit(&hwahcp->hwahc_mutex);
4173		(void) wusb_wa_disable(&hwahcp->hwahc_wa_data,
4174		    hwahcp->hwahc_default_pipe);
4175
4176		/* stop beaconing. Not necessary to unreserve mas */
4177		(void) uwb_stop_beacon(hwahcp->hwahc_dip);
4178
4179		wusb_hc_rem_host_info(&hwahcp->hwahc_hc_data);
4180
4181		/* Manually remove all connected children */
4182		hwahc_run_callbacks(hwahcp, USBA_EVENT_TAG_HOT_REMOVAL);
4183
4184		/* delete all the children */
4185		(void) hwahc_cleanup_child(hwahcp->hwahc_dip);
4186		mutex_enter(&hwahcp->hwahc_mutex);
4187	}
4188
4189	/*
4190	 * we make it busy at hwahc_hc_initial_start(). This idle operation
4191	 * is to match that busy operation.
4192	 * All other busy/idle operations should have been matched.
4193	 */
4194	if ((hwahcp->hwahc_hw_state == HWAHC_HW_STARTED) &&
4195	    (hwahcp->hwahc_hc_soft_state == HWAHC_CTRL_OPERATIONAL_STATE)) {
4196		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4197		    "hwahc_hc_final_stop: pm_busy=%d",
4198		    hwahcp->hwahc_pm->hwahc_pm_busy);
4199		mutex_exit(&hwahcp->hwahc_mutex);
4200		hwahc_pm_idle_component(hwahcp);
4201		mutex_enter(&hwahcp->hwahc_mutex);
4202	}
4203
4204	hwahcp->hwahc_hw_state = HWAHC_HW_STOPPED;
4205	if (hwahcp->hwahc_hc_soft_state == HWAHC_CTRL_OPERATIONAL_STATE) {
4206		hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_INIT_STATE;
4207	}
4208
4209	return (USB_SUCCESS);
4210}
4211
4212/*
4213 * init WUSB channel, this is only part of the full hw start operations
4214 * including setting wusb channel stream idx, wusb MAS slots reservation
4215 * and adding host info IE
4216 */
4217static int
4218hwahc_hc_channel_start(hwahc_state_t *hwahcp)
4219{
4220	uint8_t			stream_idx;
4221	uint8_t			mas[WUSB_SET_WUSB_MAS_LEN];
4222	int			rval;
4223
4224	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4225	    "hwahc_hc_channel_start:");
4226
4227	ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
4228
4229	if (hwahcp->hwahc_dev_state != USB_DEV_ONLINE) {
4230		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4231		    "hwahc_hc_channel_start: invalid dev_state = %d",
4232		    hwahcp->hwahc_dev_state);
4233
4234		return (USB_INVALID_REQUEST);
4235	}
4236
4237	if (hwahcp->hwahc_hw_state != HWAHC_HW_CH_STOPPED) {
4238		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4239		    "hwahc_hc_channel_start: invalid hw state");
4240
4241		return (USB_INVALID_REQUEST);
4242	}
4243
4244	/* set stream idx */
4245	stream_idx = 1;
4246
4247	mutex_exit(&hwahcp->hwahc_mutex);
4248	rval = wusb_hc_set_stream_idx(&hwahcp->hwahc_hc_data, stream_idx);
4249	if (rval != USB_SUCCESS) {
4250		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4251		    "hwahc_hc_channel_start: set stream idx %d fails",
4252		    stream_idx);
4253		mutex_enter(&hwahcp->hwahc_mutex);
4254
4255		return (rval);
4256	}
4257
4258	/* reserve MAS slots for the host. Should be allocated by UWB */
4259	(void) memset(mas, 0xff, WUSB_SET_WUSB_MAS_LEN);
4260	mas[0] = 0xf0;	/* for beacons */
4261	rval = wusb_hc_set_wusb_mas(&hwahcp->hwahc_hc_data, mas);
4262
4263	if (rval != USB_SUCCESS) {
4264		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4265		    "hwahc_hc_channel_start: set wusb mas fails");
4266		mutex_enter(&hwahcp->hwahc_mutex);
4267
4268		return (rval);
4269	}
4270	(void) memcpy(hwahcp->hwahc_hc_data.hc_mas, mas, WUSB_SET_WUSB_MAS_LEN);
4271
4272	/* set host info IE */
4273	rval = wusb_hc_add_host_info(&hwahcp->hwahc_hc_data, stream_idx);
4274
4275	if (rval != USB_SUCCESS) {
4276		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4277		    "hwahc_hc_channel_start: add hostinfo ie fails");
4278		mutex_enter(&hwahcp->hwahc_mutex);
4279
4280		return (rval);
4281	}
4282
4283	mutex_enter(&hwahcp->hwahc_mutex);
4284	hwahcp->hwahc_hw_state = HWAHC_HW_STARTED;
4285	hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_OPERATIONAL_STATE;
4286
4287	/* do not PM this device, once we're ready to accept DN */
4288	mutex_exit(&hwahcp->hwahc_mutex);
4289	hwahc_pm_busy_component(hwahcp);
4290	mutex_enter(&hwahcp->hwahc_mutex);
4291
4292	return (USB_SUCCESS);
4293}
4294
4295/*
4296 * stop WUSB channel, this only stops part of the hw function
4297 * it mainly unreserve the MAS slots and remove the host info IE
4298 */
4299static int
4300hwahc_hc_channel_stop(hwahc_state_t *hwahcp)
4301{
4302	uint8_t			stream_idx;
4303	uint8_t			mas[WUSB_SET_WUSB_MAS_LEN];
4304	int			rval;
4305
4306	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4307	    "hwahc_hc_channel_stop:");
4308
4309	ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
4310
4311	if (hwahcp->hwahc_dev_state != USB_DEV_ONLINE) {
4312		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4313		    "hwahc_hc_channel_stop: invalid dev state %d",
4314		    hwahcp->hwahc_dev_state);
4315
4316		return (USB_INVALID_REQUEST);
4317	}
4318
4319	if (hwahcp->hwahc_hw_state == HWAHC_HW_CH_STOPPED) {
4320		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4321		    "hwahc_hc_channel_stop: already partially stopped");
4322
4323		return (USB_SUCCESS);
4324	}
4325
4326	if (hwahcp->hwahc_hw_state == HWAHC_HW_STOPPED) {
4327		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4328		    "hwahc_hc_channel_stop: already stopped, invalid state");
4329
4330		return (USB_INVALID_REQUEST);
4331	}
4332
4333	/* send host disconect IE so that the children know to disconnect */
4334	mutex_exit(&hwahcp->hwahc_mutex);
4335	rval = wusb_hc_send_host_disconnect(&hwahcp->hwahc_hc_data);
4336	if (rval != USB_SUCCESS) {
4337		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4338		    "hwahc_hc_channel_stop: send host disconnect ie fails");
4339
4340		mutex_enter(&hwahcp->hwahc_mutex);
4341
4342		return (rval);
4343	}
4344
4345	/* remove host info IE */
4346	wusb_hc_rem_host_info(&hwahcp->hwahc_hc_data);
4347
4348	/* unset stream idx */
4349	stream_idx = 0;
4350
4351	rval = wusb_hc_set_stream_idx(&hwahcp->hwahc_hc_data, stream_idx);
4352	if (rval != USB_SUCCESS) {
4353		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4354		    "hwahc_hc_channel_stop: set stream idx 0 fails");
4355		mutex_enter(&hwahcp->hwahc_mutex);
4356
4357		return (rval);
4358	}
4359
4360	/* unreserve MAS slots */
4361	(void) memset(mas, 0, WUSB_SET_WUSB_MAS_LEN);
4362	rval = wusb_hc_set_wusb_mas(&hwahcp->hwahc_hc_data, mas);
4363
4364	if (rval != USB_SUCCESS) {
4365		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4366		    "hwahc_hc_channel_stop: set null wusb mas fails");
4367		mutex_enter(&hwahcp->hwahc_mutex);
4368
4369		return (rval);
4370	}
4371
4372	mutex_enter(&hwahcp->hwahc_mutex);
4373	(void) memcpy(hwahcp->hwahc_hc_data.hc_mas, mas, WUSB_SET_WUSB_MAS_LEN);
4374
4375	hwahcp->hwahc_hw_state = HWAHC_HW_CH_STOPPED;
4376
4377	/* Channel is stopped, can be PM'ed */
4378	mutex_exit(&hwahcp->hwahc_mutex);
4379	hwahc_pm_idle_component(hwahcp);
4380	mutex_enter(&hwahcp->hwahc_mutex);
4381
4382	return (USB_SUCCESS);
4383}
4384
4385/* initialize data transfer related resources */
4386static int
4387hwahc_wa_start(hwahc_state_t *hwahcp)
4388{
4389	int	rval;
4390
4391	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4392	    "hwahc_wa_start:");
4393
4394	/* get all rpipe descrs */
4395	if ((rval = wusb_wa_get_rpipe_descrs(&hwahcp->hwahc_wa_data,
4396	    hwahcp->hwahc_default_pipe, PRINT_MASK_ATTA,
4397	    hwahcp->hwahc_log_handle)) != USB_SUCCESS) {
4398		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4399		    "hwahc_wa_start: get rpipe descrs fails, rval=%d", rval);
4400
4401		return (rval);
4402	}
4403
4404	/* open all data transfer epts */
4405	if ((rval = wusb_wa_open_pipes(&hwahcp->hwahc_wa_data)) !=
4406	    USB_SUCCESS) {
4407		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4408		    "hwahc_wa_start: open pipes fails, rval=%d", rval);
4409		(void) wusb_wa_disable(&hwahcp->hwahc_wa_data,
4410		    hwahcp->hwahc_default_pipe);
4411
4412		return (rval);
4413	}
4414
4415	/* init notification list */
4416	usba_init_list(&hwahcp->hwahc_dn_notif_queue, NULL,
4417	    hwahcp->hwahc_dev_data->dev_iblock_cookie);
4418
4419	return (USB_SUCCESS);
4420}
4421
4422/* deinitialize data transfer related resources */
4423static void
4424hwahc_wa_stop(hwahc_state_t *hwahcp)
4425{
4426	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4427	    "hwahc_wa_stop:");
4428
4429	usba_destroy_list(&hwahcp->hwahc_dn_notif_queue);
4430	wusb_wa_close_pipes(&hwahcp->hwahc_wa_data);
4431}
4432
4433/*
4434 * HUBD related initialization
4435 * To mimic standard hub attach process to create a fake "root hub"
4436 * for HWA
4437 */
4438static int
4439hwahc_hub_attach(hwahc_state_t *hwahcp)
4440{
4441	hubd_t		*hubd = NULL;
4442	dev_info_t	*dip = hwahcp->hwahc_dip;
4443	int		instance = ddi_get_instance(dip);
4444	int		i;
4445	int		rval;
4446
4447	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4448	    "hwahc_hub_attach:");
4449
4450	if (ndi_prop_create_boolean(DDI_DEV_T_NONE, dip,
4451	    "wire-adapter") != NDI_SUCCESS) {
4452
4453		return (USB_FAILURE);
4454	}
4455
4456	/* allocate hubd structure */
4457	hubd = hwahcp->hwahc_hubd = kmem_zalloc(sizeof (hubd_t), KM_SLEEP);
4458
4459	hubd->h_log_handle = usb_alloc_log_hdl(dip, "husb", &hubd_errlevel,
4460	    &hubd_errmask, &hubd_instance_debug, 0);
4461	hubd->h_usba_device = usba_get_usba_device(dip);
4462	hubd->h_usba_device->usb_is_wa = TRUE;
4463	hubd->h_dip = dip;
4464	hubd->h_instance = instance;
4465	hubd->h_ignore_pwr_budget = B_TRUE;
4466	hubd->h_cleanup_child = hwahc_cleanup_child;
4467
4468	mutex_enter(&hubd->h_usba_device->usb_mutex);
4469	hubd->h_usba_device->usb_root_hubd = hubd;
4470	mutex_exit(&hubd->h_usba_device->usb_mutex);
4471
4472	if (usb_get_dev_data(dip, &hubd->h_dev_data,
4473	    USB_PARSE_LVL_IF, 0) != USB_SUCCESS) {
4474		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
4475		    "cannot get dev_data");
4476
4477		goto fail;
4478	}
4479
4480	/* init hubd mutex */
4481	mutex_init(HUBD_MUTEX(hubd), NULL, MUTEX_DRIVER,
4482	    hubd->h_dev_data->dev_iblock_cookie);
4483
4484	usb_free_descr_tree(dip, hubd->h_dev_data);
4485
4486	hubd->h_init_state |= HUBD_LOCKS_DONE;
4487
4488	/* register the instance to usba HUBDI */
4489	rval = usba_hubdi_register(dip, 0);
4490	if (rval != USB_SUCCESS) {
4491		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
4492		    "usba_hubdi_register failed");
4493
4494		goto fail;
4495	}
4496
4497	mutex_enter(HUBD_MUTEX(hubd));
4498	hubd->h_init_state |= HUBD_HUBDI_REGISTERED;
4499
4500	hubd->h_ancestry_str = (char *)kmem_zalloc(HUBD_APID_NAMELEN,
4501	    KM_SLEEP);
4502	hubd_get_ancestry_str(hubd);
4503
4504	/* create cfgadm minor nodes */
4505	for (i = 1; i <= hwahcp->hwahc_wa_data.wa_descr.bNumPorts; i++) {
4506		char ap_name[HUBD_APID_NAMELEN];
4507
4508		(void) snprintf(ap_name, HUBD_APID_NAMELEN, "%s%d",
4509		    hubd->h_ancestry_str, i);
4510		USB_DPRINTF_L3(DPRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4511		    "ap_name=%s", ap_name);
4512
4513		if (ddi_create_minor_node(dip, ap_name, S_IFCHR,
4514		    (instance << HWAHC_MINOR_INSTANCE_SHIFT) | i,
4515		    DDI_NT_USB_ATTACHMENT_POINT, 0) != DDI_SUCCESS) {
4516			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
4517			    "cannot create attachment point node (%d)",
4518			    instance);
4519			mutex_exit(HUBD_MUTEX(hubd));
4520
4521			goto fail;
4522		}
4523	}
4524	i = hwahcp->hwahc_wa_data.wa_descr.bNumPorts;
4525	mutex_exit(HUBD_MUTEX(hubd));
4526
4527	/* create hubd minor node */
4528	if (ddi_create_minor_node(dip, "hubd", S_IFCHR,
4529	    instance << HWAHC_MINOR_INSTANCE_SHIFT,
4530	    DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
4531		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
4532		    "cannot create devctl minor node (%d)", instance);
4533
4534		goto fail;
4535	}
4536
4537	mutex_enter(HUBD_MUTEX(hubd));
4538	hubd->h_init_state |= HUBD_MINOR_NODE_CREATED;
4539	mutex_exit(HUBD_MUTEX(hubd));
4540
4541	if (ndi_prop_update_int(DDI_DEV_T_NONE, dip,
4542	    "usb-port-count", i) != DDI_PROP_SUCCESS) {
4543		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
4544		    "usb-port-count update failed");
4545	}
4546
4547	return (USB_SUCCESS);
4548
4549fail:
4550	if (hwahc_hub_detach(hwahcp) != USB_SUCCESS) {
4551		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4552		    "fail to cleanup after hub attach failure");
4553	}
4554
4555	return (USB_FAILURE);
4556}
4557
4558/* HUBD related deinitialization */
4559static int
4560hwahc_hub_detach(hwahc_state_t *hwahcp)
4561{
4562	hubd_t		*hubd = hwahcp->hwahc_hubd;
4563	dev_info_t	*dip = hwahcp->hwahc_dip;
4564
4565	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4566	    "hwahc_hub_detach:");
4567
4568	if ((hubd->h_init_state & HUBD_LOCKS_DONE) == 0) {
4569		goto done;
4570	}
4571
4572	if (hubd->h_init_state & HUBD_MINOR_NODE_CREATED) {
4573		/* remove minor nodes */
4574		ddi_remove_minor_node(dip, NULL);
4575	}
4576
4577	if (hubd->h_init_state & HUBD_HUBDI_REGISTERED) {
4578		/* unregister with usba HUBDI */
4579		(void) usba_hubdi_unregister(dip);
4580	}
4581
4582	if (hubd->h_init_state & HUBD_LOCKS_DONE) {
4583		mutex_destroy(HUBD_MUTEX(hubd));
4584	}
4585
4586	if (hubd->h_ancestry_str) {
4587		kmem_free(hubd->h_ancestry_str, HUBD_APID_NAMELEN);
4588	}
4589
4590done:
4591	if (hubd->h_dev_data) {
4592		/* unregister client from usba */
4593		usb_client_detach(dip, hubd->h_dev_data);
4594	}
4595
4596	usb_free_log_hdl(hubd->h_log_handle);
4597	kmem_free(hubd, sizeof (hubd_t));
4598	ddi_prop_remove_all(dip);
4599
4600	return (USB_SUCCESS);
4601}
4602
4603/* print security descrs */
4604static void
4605hwahc_print_secrt_data(hwahc_state_t *hwahcp)
4606{
4607	int			i;
4608	wusb_secrt_data_t	*secrt_data = &hwahcp->hwahc_secrt_data;
4609
4610	USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4611	    "The Host Wire Adapter security descriptor:");
4612	USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4613	    "bLength = 0x%x\t\t bDescriptorType = 0x%x",
4614	    secrt_data->secrt_descr.bLength,
4615	    secrt_data->secrt_descr.bDescriptorType);
4616	USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4617	    "wTotalLength = 0x%x\t bNumEncryptionTypes = 0x%x",
4618	    secrt_data->secrt_descr.wTotalLength,
4619	    secrt_data->secrt_descr.bNumEncryptionTypes);
4620
4621	for (i = 0; i < secrt_data->secrt_n_encry; i++) {
4622		USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4623		    "The Host Wire Adapter encryption descriptor %d:", i + 1);
4624		USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4625		    "bLength = 0x%x\t\t bDescriptorType = 0x%x",
4626		    secrt_data->secrt_encry_descr[i].bLength,
4627		    secrt_data->secrt_encry_descr[i].bDescriptorType);
4628		USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4629		    "bEncryptionType = 0x%x\t bEncryptionValue = 0x%x",
4630		    secrt_data->secrt_encry_descr[i].bEncryptionType,
4631		    secrt_data->secrt_encry_descr[i].bEncryptionValue);
4632		USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4633		    "bAuthKeyIndex = 0x%x",
4634		    secrt_data->secrt_encry_descr[i].bAuthKeyIndex);
4635	}
4636}
4637
4638/* drain device notifications */
4639static void
4640hwahc_drain_notif_queue(hwahc_state_t *hwahcp)
4641{
4642	int	i;
4643
4644	ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
4645
4646	USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
4647	    "hwahc_drain_notif_queue: started");
4648
4649	if ((hwahcp->hwahc_notif_thread_id == NULL) &&
4650	    (usba_list_entry_count(&hwahcp->hwahc_dn_notif_queue) != 0)) {
4651		/* kick off a notif thread to drain the queue */
4652		if (usb_async_req(hwahcp->hwahc_dip, hwahc_notif_thread,
4653		    (void *)hwahcp, 0) != USB_SUCCESS) {
4654			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
4655			    hwahcp->hwahc_log_handle,
4656			    "hwahc_drain_notif_queue: no notif thread started");
4657		} else {
4658			hwahcp->hwahc_notif_thread_id = (kthread_t *)1;
4659		}
4660	}
4661
4662	for (i = 0; i < HWAHC_NOTIF_DRAIN_TIMEOUT; i++) {
4663		/* loop until the queue is completed or it timeouts */
4664		if ((hwahcp->hwahc_notif_thread_id == NULL) &&
4665		    (usba_list_entry_count(&hwahcp->hwahc_dn_notif_queue) ==
4666		    0)) {
4667
4668			break;
4669		}
4670		mutex_exit(&hwahcp->hwahc_mutex);
4671		delay(drv_usectohz(1000000));
4672		mutex_enter(&hwahcp->hwahc_mutex);
4673	}
4674
4675	/* cleanup the queue if not completed */
4676	while (usba_list_entry_count(&hwahcp->hwahc_dn_notif_queue) != 0) {
4677		hwahc_dn_notif_list_t	*nlist;
4678
4679		nlist = (hwahc_dn_notif_list_t *)usba_rm_first_pvt_from_list(
4680		    &hwahcp->hwahc_dn_notif_queue);
4681		ASSERT(nlist != NULL);
4682		ASSERT(nlist->dn_notif != NULL);
4683		usba_destroy_list(&nlist->notif_list);
4684		kmem_free(nlist->dn_notif, nlist->dn_notif->bLength);
4685		kmem_free(nlist, sizeof (hwahc_dn_notif_list_t));
4686	}
4687
4688	USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
4689	    "hwahc_drain_notif_queue: ended");
4690}
4691
4692
4693/* normal callback for notification ept */
4694static void
4695hwahc_intr_cb(usb_pipe_handle_t ph, struct usb_intr_req *reqp)
4696{
4697	dev_info_t		*dip = (USBA_REQ2WRP(reqp))->wr_dip;
4698	hwahc_state_t		*hwahcp;
4699	mblk_t			*data = reqp->intr_data;
4700
4701	ASSERT(dip != NULL);
4702	hwahcp = ddi_get_soft_state(hwahc_statep, ddi_get_instance(dip));
4703	ASSERT(hwahcp != NULL);
4704
4705	USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
4706	    "hwahc_intr_cb: ph = 0x%p reqp = 0x%p", (void *)ph,
4707	    (void *)reqp);
4708
4709	ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
4710
4711	if (data == NULL) {
4712		usb_free_intr_req(reqp);
4713
4714		return;
4715	}
4716
4717	/* handle the notification */
4718	hwahc_handle_notif(hwahcp, data);
4719
4720	usb_free_intr_req(reqp);
4721}
4722
4723/*
4724 * See Section 8.3.3.3 for Transfer Notification format and
4725 * Section 8.5.4 for HWA specific notifications.
4726 *	Three kinds of Notifications:
4727 *		- Transfer Completion
4728 *		- DN Received
4729 *		- BPST ADJ
4730 */
4731/* handle the notification according to notification type */
4732static void
4733hwahc_handle_notif(hwahc_state_t *hwahcp, mblk_t *data)
4734{
4735	int			len;
4736	uint8_t			*p;
4737	wa_notif_header_t	*hdr;
4738
4739	if (data == NULL) {
4740
4741		return;
4742	}
4743
4744	len = MBLKL(data);
4745	p = data->b_rptr;
4746	USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
4747	    "hwahc_handle_notif: data len = %d", len);
4748
4749	/*
4750	 * according to WUSB 1.0/8.1.2, multiple notifications might be sent
4751	 * at a time, need to parse one by one
4752	 */
4753	while (len > 0) {
4754		if (len < 2) {
4755			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
4756			    hwahcp->hwahc_log_handle,
4757			    "hwahc_handle_notif: short packet len = %d",
4758			    len);
4759
4760			break;
4761		}
4762
4763		hdr = (wa_notif_header_t *)p;
4764		if (len < hdr->bLength) {
4765			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
4766			    hwahcp->hwahc_log_handle,
4767			    "hwahc_handle_notif: length not match, "
4768			    "hdr length = %d, actual length = %d",
4769			    hdr->bLength, len);
4770
4771			break;
4772		}
4773
4774		switch (hdr->bNotifyType) {
4775		case WA_NOTIF_TYPE_TRANSFER:
4776		{
4777			uint8_t		ept = p[2];
4778
4779			/* deal with transfer completion notification */
4780			hwahc_handle_xfer_result(hwahcp, ept);
4781
4782			break;
4783		}
4784		case HWA_NOTIF_TYPE_DN_RECEIVED:
4785		{
4786			hwa_notif_dn_recvd_t	*dn_notif;
4787
4788			dn_notif = kmem_alloc(hdr->bLength, KM_NOSLEEP);
4789			(void) memcpy(dn_notif, p, hdr->bLength);
4790
4791			/* deal with device notification */
4792			hwahc_handle_dn_notif(hwahcp, dn_notif);
4793
4794			break;
4795		}
4796		case HWA_NOTIF_TYPE_BPST_ADJ:
4797			USB_DPRINTF_L3(PRINT_MASK_CBOPS,
4798			    hwahcp->hwahc_log_handle,
4799			    "hwahc_handle_notif: received BPST adjust "
4800			    "notification, bAdjustment = %d", p[2]);
4801
4802			break;
4803		default:
4804			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
4805			    hwahcp->hwahc_log_handle,
4806			    "hwahc_handle_notif: unknown notification 0x%x",
4807			    hdr->bNotifyType);
4808
4809			break;
4810		}
4811		p += hdr->bLength;
4812		len -= hdr->bLength;
4813	}
4814}
4815
4816/*
4817 * start listening on bulk-in ept for transfer result
4818 *
4819 * Dispatches a task to read the BULK IN endpoint to get the result of
4820 * last request. usb_async_req() will have system_taskq to process the tasks.
4821 */
4822int
4823hwahc_start_result_thread(hwahc_state_t *hwahcp)
4824{
4825	wusb_wa_data_t *wa_data;
4826
4827	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4828	    "hwahc_start_result_thread:");
4829
4830	ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
4831
4832	if (hwahcp->hwahc_result_thread_id != 0) {
4833		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4834		    "hwahc_start_result_thread: already started");
4835
4836		return (USB_SUCCESS);
4837	}
4838
4839	wa_data = &hwahcp->hwahc_wa_data;
4840
4841	mutex_enter(&wa_data->wa_mutex);
4842	if ((wa_data->wa_bulkin_ph != NULL) &&
4843	    (wa_data->wa_bulkin_pipe_state != WA_PIPE_STOPPED)) {
4844		mutex_exit(&wa_data->wa_mutex);
4845
4846		return (USB_INVALID_PIPE);
4847	}
4848	mutex_exit(&wa_data->wa_mutex);
4849
4850	if (wa_data->wa_bulkin_ph == NULL) {
4851		mutex_exit(&hwahcp->hwahc_mutex);
4852		if (usb_pipe_open(wa_data->wa_dip, &wa_data->wa_bulkin_ept,
4853		    &wa_data->wa_pipe_policy, USB_FLAGS_SLEEP,
4854		    &wa_data->wa_bulkin_ph) != USB_SUCCESS) {
4855			USB_DPRINTF_L2(PRINT_MASK_ATTA,
4856			    hwahcp->hwahc_log_handle,
4857			    "hwahc_start_result_thread: open pipe failed");
4858
4859
4860			mutex_enter(&hwahcp->hwahc_mutex);
4861			return (USB_FAILURE);
4862		}
4863		mutex_enter(&hwahcp->hwahc_mutex);
4864
4865		mutex_enter(&wa_data->wa_mutex);
4866		wa_data->wa_bulkin_pipe_state = WA_PIPE_STOPPED;
4867		mutex_exit(&wa_data->wa_mutex);
4868	}
4869
4870	/* kick off an asynchronous thread to handle transfer result */
4871	if (usb_async_req(hwahcp->hwahc_dip, hwahc_result_thread,
4872	    (void *)hwahcp, 0) != USB_SUCCESS) {
4873		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4874		    "hwahc_start_result_thread: failed to start result thread");
4875
4876		return (USB_FAILURE);
4877	}
4878	hwahcp->hwahc_result_thread_id = (kthread_t *)1;
4879
4880	/* pipe state is active while the result thread is on */
4881	mutex_enter(&wa_data->wa_mutex);
4882	wa_data->wa_bulkin_pipe_state = WA_PIPE_ACTIVE;
4883	mutex_exit(&wa_data->wa_mutex);
4884
4885	return (USB_SUCCESS);
4886}
4887
4888/* stop the bulk-in ept from listening */
4889static void
4890hwahc_stop_result_thread(hwahc_state_t *hwahcp)
4891{
4892	wusb_wa_data_t *wa_data;
4893
4894	ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
4895
4896	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4897	    "hwahc_stop_result_thread:");
4898
4899	if (hwahcp->hwahc_result_thread_id == 0) {
4900		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4901		    "hwahc_stop_result_thread: already stopped");
4902
4903		return;
4904	}
4905
4906	wa_data = &hwahcp->hwahc_wa_data;
4907	mutex_enter(&wa_data->wa_mutex);
4908	if ((wa_data->wa_bulkin_ph == NULL) ||
4909	    (wa_data->wa_bulkin_pipe_state != WA_PIPE_ACTIVE)) {
4910		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4911		    "hwahc_stop_result_thread: invalid pipe state");
4912
4913		mutex_exit(&wa_data->wa_mutex);
4914
4915		return;
4916	}
4917	mutex_exit(&wa_data->wa_mutex);
4918
4919	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4920	    "hwahc_stop_result_thread: reset hwa bulk-in pipe");
4921	mutex_exit(&hwahcp->hwahc_mutex);
4922	usb_pipe_reset(wa_data->wa_dip, wa_data->wa_bulkin_ph,
4923	    USB_FLAGS_SLEEP, NULL, NULL);
4924
4925	/*
4926	 * have to close pipe here to fail the bulk-in transfer
4927	 * that never timeouts
4928	 */
4929	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4930	    "hwahc_stop_result_thread: close hwa bulk-in pipe");
4931	usb_pipe_close(wa_data->wa_dip, wa_data->wa_bulkin_ph,
4932	    USB_FLAGS_SLEEP, NULL, NULL);
4933	mutex_enter(&hwahcp->hwahc_mutex);
4934
4935	mutex_enter(&wa_data->wa_mutex);
4936	wa_data->wa_bulkin_ph = NULL;
4937	wa_data->wa_bulkin_pipe_state = WA_PIPE_STOPPED;
4938	mutex_exit(&wa_data->wa_mutex);
4939
4940	while (hwahcp->hwahc_result_thread_id != 0) {
4941		/* wait the result thread to exit */
4942		cv_wait(&hwahcp->hwahc_result_thread_cv, &hwahcp->hwahc_mutex);
4943	}
4944}
4945
4946/*
4947 * keep listening for transfer result by setting timeout to 0 while the
4948 * bulk-in pipe is active
4949 * the thread would be stopped by closing bulk-in pipe or encountering
4950 * transaction error, eg, hot-removal of hwa device
4951 */
4952static void
4953hwahc_result_thread(void *arg)
4954{
4955	hwahc_state_t	*hwahcp = (hwahc_state_t *)arg;
4956	wusb_wa_data_t	*wa_data = &hwahcp->hwahc_wa_data;
4957	int		rval;
4958	uint8_t		retry = 0;
4959
4960	mutex_enter(&hwahcp->hwahc_mutex);
4961	ASSERT(hwahcp->hwahc_result_thread_id == (kthread_t *)1);
4962	hwahcp->hwahc_result_thread_id = curthread;
4963	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4964	    "hwahc_result_thread: started, thread_id=0x%p",
4965	    (void *)hwahcp->hwahc_result_thread_id);
4966
4967	/* keep polling the bulk IN endpoint to get the result */
4968	mutex_enter(&wa_data->wa_mutex);
4969	while (wa_data->wa_bulkin_pipe_state == WA_PIPE_ACTIVE) {
4970		mutex_exit(&wa_data->wa_mutex);
4971		mutex_exit(&hwahcp->hwahc_mutex);
4972
4973		if ((rval = wusb_wa_get_xfer_result(wa_data)) != USB_SUCCESS) {
4974			retry++;
4975			USB_DPRINTF_L2(PRINT_MASK_ATTA,
4976			    hwahcp->hwahc_log_handle,
4977			    "hwahc_result_thread: get xfer result failed, "
4978			    "rval = %d, retry = %d", rval, retry);
4979
4980			/* retry 3 times upon failure */
4981			if (retry >= 3) {
4982				mutex_enter(&hwahcp->hwahc_mutex);
4983				mutex_enter(&wa_data->wa_mutex);
4984
4985				break;
4986			}
4987		}
4988
4989		mutex_enter(&hwahcp->hwahc_mutex);
4990		mutex_enter(&wa_data->wa_mutex);
4991	}
4992
4993	hwahcp->hwahc_result_thread_id = 0;
4994	wa_data->wa_bulkin_pipe_state = WA_PIPE_STOPPED;
4995	mutex_exit(&wa_data->wa_mutex);
4996
4997	/* signal to the thread requesting stopping if any */
4998	cv_signal(&hwahcp->hwahc_result_thread_cv);
4999
5000	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
5001	    "hwahc_result_thread: ended");
5002
5003	mutex_exit(&hwahcp->hwahc_mutex);
5004}
5005
5006/*
5007 * nothing to do here, just check if the ept number in the transfer
5008 * completion notification is valid
5009 * the actual handling of transfer result is performed by the result thread
5010 */
5011static void
5012hwahc_handle_xfer_result(hwahc_state_t *hwahcp, uint8_t ept)
5013{
5014	usb_ep_descr_t	*epdt;
5015
5016	USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5017	    "hwahc_handle_xfer_result: result on ept %d", ept);
5018
5019	epdt = &hwahcp->hwahc_wa_data.wa_bulkin_ept;
5020
5021	/* the result should be on the bulk-in ept */
5022	if ((epdt->bEndpointAddress & USB_EP_NUM_MASK) != ept) {
5023		USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5024		    "hwahc_handle_xfer_result: ept number not match");
5025
5026		return;
5027	}
5028}
5029
5030
5031/*
5032 * Section 8.5.4.2.
5033 *	Copy the DN Notification and add it to the instance's global
5034 *	nofication list. If the worker thread is not started yet, start
5035 *	it.
5036 */
5037static void
5038hwahc_handle_dn_notif(hwahc_state_t *hwahcp, hwa_notif_dn_recvd_t *dn_notif)
5039{
5040	hwahc_dn_notif_list_t	*nlist;
5041
5042	USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5043	    "hwahc_handle_dn_notif: notif = 0x%p", (void *)dn_notif);
5044
5045	nlist = kmem_zalloc(sizeof (hwahc_dn_notif_list_t), KM_NOSLEEP);
5046
5047	mutex_enter(&hwahcp->hwahc_mutex);
5048	nlist->dn_notif = dn_notif;
5049
5050	usba_init_list(&nlist->notif_list, (usb_opaque_t)nlist,
5051	    hwahcp->hwahc_dev_data->dev_iblock_cookie);
5052
5053	/* queue the new notification to the list */
5054	usba_add_to_list(&hwahcp->hwahc_dn_notif_queue, &nlist->notif_list);
5055
5056	/* handle the notification queue with an asynchronous thread */
5057	if (hwahcp->hwahc_notif_thread_id == 0) {
5058		if (usb_async_req(hwahcp->hwahc_dip, hwahc_notif_thread,
5059		    (void *)hwahcp, 0) != USB_SUCCESS) {
5060			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
5061			    hwahcp->hwahc_log_handle,
5062			    "hwahc_handle_dn_notif: no notif thread started");
5063			mutex_exit(&hwahcp->hwahc_mutex);
5064
5065			return;
5066		}
5067		hwahcp->hwahc_notif_thread_id = (kthread_t *)1;
5068	}
5069
5070	mutex_exit(&hwahcp->hwahc_mutex);
5071}
5072
5073/* handle the notifications in the notification queue in sequence */
5074static void
5075hwahc_notif_thread(void *arg)
5076{
5077	hwahc_state_t		*hwahcp = (hwahc_state_t *)arg;
5078	hwahc_dn_notif_list_t	*nlist;
5079	hwa_notif_dn_recvd_t	*dn_notif;
5080
5081	mutex_enter(&hwahcp->hwahc_mutex);
5082	ASSERT(hwahcp->hwahc_notif_thread_id == (kthread_t *)1);
5083	hwahcp->hwahc_notif_thread_id = curthread;
5084
5085	USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5086	    "hwahc_notif_thread: started, thread_id=0x%p",
5087	    (void *)hwahcp->hwahc_notif_thread_id);
5088
5089	while (usba_list_entry_count(&hwahcp->hwahc_dn_notif_queue) != 0) {
5090		/*
5091		 * first in first out, only one notification will be handled
5092		 * at a time, so it assures no racing in attach or detach
5093		 */
5094		if ((nlist =
5095		    (hwahc_dn_notif_list_t *)usba_rm_first_pvt_from_list(
5096		    &hwahcp->hwahc_dn_notif_queue)) == NULL) {
5097
5098			continue;
5099		}
5100		dn_notif = nlist->dn_notif;
5101		mutex_exit(&hwahcp->hwahc_mutex);
5102		hwahc_handle_dn(hwahcp, dn_notif);
5103		usba_destroy_list(&nlist->notif_list);
5104		kmem_free(nlist, sizeof (hwahc_dn_notif_list_t));
5105		mutex_enter(&hwahcp->hwahc_mutex);
5106	}
5107
5108	hwahcp->hwahc_notif_thread_id = 0;
5109
5110	USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5111	    "hwahc_notif_thread: ended");
5112
5113	mutex_exit(&hwahcp->hwahc_mutex);
5114}
5115
5116/* Set the child device's active bit to 1 */
5117static void
5118hwahc_set_device_active(hwahc_state_t *hwahcp, uint8_t devaddr)
5119{
5120	wusb_dev_info_t *dev_info;
5121	wusb_hc_data_t *hc_data = &hwahcp->hwahc_hc_data;
5122	int i;
5123
5124	mutex_enter(&hc_data->hc_mutex);
5125	for (i = 1; i <= hc_data->hc_num_ports; i++) {
5126		dev_info = hc_data->hc_dev_infos[i];
5127		if ((dev_info != NULL) && (dev_info->wdev_addr == devaddr)) {
5128			dev_info->wdev_active = 1;
5129			USB_DPRINTF_L3(DPRINT_MASK_EVENTS,
5130			    hwahcp->hwahc_log_handle,
5131			    "hwahc_set_device_active:device(%p) updated ",
5132			    (void *)dev_info);
5133
5134			break;
5135		}
5136	}
5137	mutex_exit(&hc_data->hc_mutex);
5138}
5139
5140/*
5141 * handle a specific device notification
5142 * assuming the raw data in HWA DN_RECEIVED notification pkt includes
5143 * no more than one dn pkt
5144 */
5145static void
5146hwahc_handle_dn(hwahc_state_t *hwahcp, hwa_notif_dn_recvd_t *dn_notif)
5147{
5148	uint8_t			*p;
5149	size_t			len;
5150	uint8_t			dntype;
5151	int circ;
5152	wusb_hc_data_t		*hc_data = &hwahcp->hwahc_hc_data;
5153
5154	if (dn_notif->bLength < 4) {
5155		USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5156		    "hwahc_handle_dn: bLength too short %d", dn_notif->bLength);
5157		kmem_free(dn_notif, dn_notif->bLength);
5158
5159		return;
5160	}
5161
5162	p = dn_notif->notifdata;
5163	len = dn_notif->bLength - 4;
5164
5165	/*
5166	 * WUSB Errata 06.12 specifies that the raw data in the DN_RECEIVED
5167	 * notification must not include the WUSB header, but only the bType
5168	 * and Notification specific data
5169	 */
5170	if (len == 0) {
5171		USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5172		    "hwahc_handle_dn: no raw data");
5173		kmem_free(dn_notif, dn_notif->bLength);
5174
5175		return;
5176	}
5177	dntype = *p;
5178
5179	/* update the device's status bit, no matter what the DN is */
5180	hwahc_set_device_active(hwahcp, dn_notif->bSourceDeviceAddr);
5181
5182	ndi_devi_enter(hwahcp->hwahc_dip, &circ);
5183	switch (dntype) {
5184	case WUSB_DN_CONNECT:
5185		/* DN_Connect */
5186		wusb_hc_handle_dn_connect(
5187		    hc_data, hwahcp->hwahc_default_pipe,
5188		    hwahcp->hwahc_wa_data.wa_ifno, p, len,
5189		    &hwahcp->hwahc_secrt_data);
5190
5191		break;
5192	case WUSB_DN_DISCONNECT:
5193		/* DN_Disconnect */
5194		wusb_hc_handle_dn_disconnect(
5195		    hc_data, dn_notif->bSourceDeviceAddr,
5196		    p, len);
5197
5198		break;
5199	case WUSB_DN_ALIVE:
5200		/* We only send KeepAlive IE to one device at a comment */
5201		mutex_enter(&hc_data->hc_mutex);
5202		if (dn_notif->bSourceDeviceAddr ==
5203		    hc_data->hc_alive_ie.bDeviceAddress[0]) {
5204			mutex_exit(&hc_data->hc_mutex);
5205			wusb_hc_rem_ie(hc_data,
5206			    (wusb_ie_header_t *)&hc_data->hc_alive_ie);
5207			mutex_enter(&hc_data->hc_mutex);
5208		}
5209		mutex_exit(&hc_data->hc_mutex);
5210
5211		break;
5212	case WUSB_DN_EPRDY:
5213	case WUSB_DN_MASAVAILCHANGED:
5214	case WUSB_DN_REMOTEWAKEUP:
5215	case WUSB_DN_SLEEP:
5216	default:
5217		USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5218		    "hwahc_handle_dn: dn type 0x%x not supported yet",
5219		    dntype);
5220
5221		break;
5222	}
5223
5224	kmem_free(dn_notif, dn_notif->bLength);
5225	ndi_devi_exit(hwahcp->hwahc_dip, circ);
5226}
5227
5228/* exceptional callback for notification ept */
5229/* ARGSUSED */
5230static void
5231hwahc_intr_exc_cb(usb_pipe_handle_t ph, struct usb_intr_req *reqp)
5232{
5233	dev_info_t	*dip = (USBA_REQ2WRP(reqp))->wr_dip;
5234	hwahc_state_t	*hwahcp;
5235
5236	ASSERT(dip != NULL);
5237	hwahcp = ddi_get_soft_state(hwahc_statep, ddi_get_instance(dip));
5238	ASSERT(hwahcp != NULL);
5239
5240	USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5241	    "hwahc_intr_exc_cb: receive intr exception cb, cr=%d",
5242	    reqp->intr_completion_reason);
5243
5244	ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
5245
5246	mutex_enter(&hwahcp->hwahc_mutex);
5247
5248	switch (reqp->intr_completion_reason) {
5249	case USB_CR_PIPE_RESET:
5250		/* only restart nep after autoclearing */
5251		if (hwahcp->hwahc_dev_state == USB_DEV_ONLINE) {
5252			hwahcp->hwahc_wa_data.wa_intr_pipe_state =
5253			    WA_PIPE_STOPPED;
5254			mutex_exit(&hwahcp->hwahc_mutex);
5255			(void) wusb_wa_start_nep(&hwahcp->hwahc_wa_data,
5256			    USB_FLAGS_NOSLEEP);
5257			mutex_enter(&hwahcp->hwahc_mutex);
5258		}
5259
5260		break;
5261	case USB_CR_DEV_NOT_RESP:
5262	case USB_CR_STOPPED_POLLING:
5263	case USB_CR_PIPE_CLOSING:
5264	case USB_CR_UNSPECIFIED_ERR:
5265		/* never restart nep on these conditions */
5266	default:
5267		/* for all others, wait for the autoclearing PIPE_RESET cb */
5268
5269		break;
5270	}
5271
5272	usb_free_intr_req(reqp);
5273	mutex_exit(&hwahcp->hwahc_mutex);
5274}
5275
5276/*
5277 * callback function called by WA to resubmit a periodic request for
5278 * interrupt polling or isochronous transfer.
5279 */
5280static int
5281hwahc_pipe_submit_periodic_req(wusb_wa_data_t *wa_data,
5282	usba_pipe_handle_data_t *ph)
5283{
5284	hwahc_state_t *hwahcp = wa_data->wa_private_data;
5285	hwahc_pipe_private_t *pp = (hwahc_pipe_private_t *)ph->p_hcd_private;
5286	int rval;
5287
5288	mutex_enter(&hwahcp->hwahc_mutex);
5289
5290	USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5291	    "hwahc_pipe_submit_periodic_req: hwahcp=0x%p, pp=0x%p,"
5292	    " pipe state = %d", (void *)hwahcp, (void *)pp, pp->pp_state);
5293
5294	if (pp->pp_state != HWAHC_PIPE_STATE_ACTIVE) {
5295		/* pipe error or pipe closing, don't resubmit any more */
5296		USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5297		    "hwahc_pipe_submit_periodic_req: pipe not active = %d",
5298		    pp->pp_state);
5299
5300		mutex_exit(&hwahcp->hwahc_mutex);
5301
5302		return (USB_PIPE_ERROR);
5303	}
5304
5305	mutex_exit(&hwahcp->hwahc_mutex);
5306
5307	/* re-submit the original request */
5308	rval = wusb_wa_intr_xfer(wa_data, pp->pp_rp, ph,
5309	    (usb_intr_req_t *)pp->pp_client_periodic_in_reqp, 0);
5310
5311	return (rval);
5312}
5313
5314/* call HCD callback for completion handling */
5315static void
5316hwahc_rpipe_xfer_cb(dev_info_t *dip, usba_pipe_handle_data_t *ph,
5317	wusb_wa_trans_wrapper_t *wr, usb_cr_t cr)
5318{
5319	hwahc_state_t		*hwahcp;
5320	hwahc_pipe_private_t	*pp;
5321	usb_opaque_t		req;
5322	wusb_hc_data_t		*hc_data;
5323
5324	hwahcp = ddi_get_soft_state(hwahc_statep, ddi_get_instance(dip));
5325	if (hwahcp == NULL) {
5326
5327		return;
5328	}
5329
5330	hc_data = &hwahcp->hwahc_hc_data;
5331
5332	mutex_enter(&hwahcp->hwahc_mutex);
5333	USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5334	    "hwahc_rpipe_xfer_cb: ph = 0x%p, wr = 0x%p cr = 0x%x",
5335	    (void *)ph, (void *)wr, cr);
5336
5337	pp = (hwahc_pipe_private_t *)ph->p_hcd_private;
5338
5339	mutex_enter(&hc_data->hc_mutex);
5340	pp->pp_wdev->wdev_active = 1; /* this device is active on xfer */
5341	mutex_exit(&hc_data->hc_mutex);
5342
5343	switch (cr) {
5344	case USB_CR_OK:
5345		break;
5346	case USB_CR_NOT_SUPPORTED:
5347	case USB_CR_NO_RESOURCES:
5348	case USB_CR_PIPE_RESET:
5349	case USB_CR_STOPPED_POLLING:
5350		pp->pp_state = HWAHC_PIPE_STATE_IDLE;
5351		break;
5352	case USB_CR_PIPE_CLOSING:
5353		break;
5354	default:
5355		pp->pp_state = HWAHC_PIPE_STATE_ERROR;
5356
5357		break;
5358	}
5359
5360	if (wr && wr->wr_reqp) {
5361		req = wr->wr_reqp;
5362
5363		mutex_enter(&wr->wr_rp->rp_mutex);
5364		wr->wr_reqp = NULL;
5365		mutex_exit(&wr->wr_rp->rp_mutex);
5366
5367	} else { /* periodic pipe cleanup */
5368
5369		/* the original request is cleared and returned to client */
5370		req = pp->pp_client_periodic_in_reqp;
5371		pp->pp_client_periodic_in_reqp = NULL;
5372	}
5373
5374	mutex_exit(&hwahcp->hwahc_mutex);
5375
5376	USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5377	    "hwahc_rpipe_xfer_cb: call usba_hcdi_cb for req= 0x%p",
5378	    (void *)req);
5379
5380	usba_hcdi_cb(ph, req, cr);
5381}
5382
5383/* post disconnect event to child on a certain port */
5384static void
5385hwahc_disconnect_dev(dev_info_t *dip, usb_port_t port)
5386{
5387	hwahc_state_t	*hwahcp;
5388	int		circ;
5389	dev_info_t	*child_dip;
5390
5391	if ((hwahcp = ddi_get_soft_state(hwahc_statep,
5392	    ddi_get_instance(dip))) == NULL) {
5393
5394		return;
5395	}
5396
5397	ndi_devi_enter(dip, &circ);
5398	mutex_enter(&hwahcp->hwahc_mutex);
5399
5400	child_dip = hwahcp->hwahc_hc_data.hc_children_dips[port];
5401	if ((hwahcp->hwahc_dev_state == USB_DEV_ONLINE) && child_dip) {
5402		mutex_exit(&hwahcp->hwahc_mutex);
5403
5404		/* if the child driver remains attached */
5405		if (i_ddi_devi_attached(child_dip)) {
5406			hwahc_post_event(hwahcp, port,
5407			    USBA_EVENT_TAG_HOT_REMOVAL);
5408		}
5409		mutex_enter(&hwahcp->hwahc_mutex);
5410	}
5411
5412	mutex_exit(&hwahcp->hwahc_mutex);
5413	ndi_devi_exit(dip, circ);
5414}
5415
5416/* post reconect event to child on a certain port */
5417static void
5418hwahc_reconnect_dev(dev_info_t *dip, usb_port_t port)
5419{
5420	hwahc_state_t	*hwahcp;
5421	int		circ;
5422
5423	if ((hwahcp = ddi_get_soft_state(hwahc_statep,
5424	    ddi_get_instance(dip))) == NULL) {
5425
5426		return;
5427	}
5428	ndi_devi_enter(dip, &circ);
5429	mutex_enter(&hwahcp->hwahc_mutex);
5430
5431	if ((hwahcp->hwahc_dev_state == USB_DEV_ONLINE) &&
5432	    (hwahcp->hwahc_hc_data.hc_children_dips[port])) {
5433		mutex_exit(&hwahcp->hwahc_mutex);
5434		hwahc_post_event(hwahcp, port, USBA_EVENT_TAG_HOT_INSERTION);
5435		mutex_enter(&hwahcp->hwahc_mutex);
5436	}
5437
5438	mutex_exit(&hwahcp->hwahc_mutex);
5439	ndi_devi_exit(dip, circ);
5440}
5441
5442
5443/*
5444 * Device TrustTimeout timer operations:
5445 * hwahc_start_trust_timer: start the trust timer for a newly connected device
5446 * hwahc_trust_timeout_handler: timer handler
5447 * hwahc_stop_trust_timer: stop a device's trust timer
5448 */
5449static void
5450hwahc_start_trust_timer(wusb_dev_info_t *dev)
5451{
5452	if (hwahc_enable_trust_timeout == 0) {
5453
5454		return;
5455	}
5456
5457	if (dev->wdev_trust_timer == NULL) {
5458		dev->wdev_trust_timer = timeout(hwahc_trust_timeout_handler,
5459		    (void *)dev, drv_usectohz(WUSB_TRUST_TIMEOUT_US));
5460	}
5461}
5462
5463/* timeout handler for device TrustTimeout. See section 4.14 */
5464static void
5465hwahc_trust_timeout_handler(void *arg)
5466{
5467	wusb_dev_info_t *dev = (wusb_dev_info_t *)arg;
5468	usb_port_t port;
5469	uint16_t   dev_addr;
5470	wusb_hc_data_t *hc_data = dev->wdev_hc;
5471	uint8_t	retry = 3;
5472	int rval;
5473
5474	mutex_enter(&hc_data->hc_mutex);
5475
5476	dev->wdev_trust_timer = 0;
5477	dev_addr = dev->wdev_addr;
5478
5479	if (dev->wdev_active == 1) {
5480	/* device is active during the past period. Restart the timer */
5481		dev->wdev_active = 0; /* expect device DN set it to 1 */
5482	} else {
5483		/* send a KeepAlive IE to query the device */
5484		for (retry = 0; retry < 3; retry++) {
5485			mutex_exit(&hc_data->hc_mutex);
5486			rval = wusb_hc_send_keepalive_ie(hc_data,
5487			    dev_addr);
5488			mutex_enter(&hc_data->hc_mutex);
5489
5490			if (rval == USB_SUCCESS) {
5491				break;
5492			}
5493			/* retry 3 times if fail to send KeepAlive IE */
5494		}
5495
5496		if (dev->wdev_active == 0) {
5497			/* still no activity! Delete this device */
5498			if (wusb_hc_is_dev_connected(hc_data, dev->wdev_cdid,
5499			    &port)) {
5500				mutex_exit(&hc_data->hc_mutex);
5501				(void) hwahc_destroy_child(hc_data->hc_dip,
5502				    port);
5503
5504				/* the device comes to the end of its life */
5505				return;
5506			}
5507		}
5508	}
5509
5510	/* active or we received DN during query */
5511	hwahc_start_trust_timer(dev);
5512
5513	mutex_exit(&hc_data->hc_mutex);
5514}
5515
5516/* stop a child device's trust timeout handler */
5517void
5518hwahc_stop_trust_timer(wusb_dev_info_t *dev)
5519{
5520	timeout_id_t tid;
5521	wusb_hc_data_t *hc_data = dev->wdev_hc;
5522
5523	ASSERT(mutex_owned(&hc_data->hc_mutex));
5524
5525	if (hwahc_enable_trust_timeout == 0) {
5526		return;
5527	}
5528
5529	tid = dev->wdev_trust_timer;
5530
5531	dev->wdev_trust_timer = NULL;
5532	mutex_exit(&hc_data->hc_mutex);
5533
5534	if (tid != NULL) {
5535		(void) untimeout(tid);
5536	}
5537
5538	mutex_enter(&hc_data->hc_mutex);
5539}
5540
5541/* configure child device and attach child on a certain port */
5542static int
5543hwahc_create_child(dev_info_t *dip, usb_port_t port)
5544{
5545	hwahc_state_t		*hwahcp;
5546	wusb_hc_data_t		*hc_data;
5547	wusb_dev_info_t		*dev_info;
5548	usb_pipe_handle_t	ph;
5549	int			rval;
5550	dev_info_t		*child_dip;
5551	usba_device_t		*child_ud = NULL;
5552	mblk_t			*pdata = NULL;
5553	usb_cr_t		completion_reason;
5554	usb_cb_flags_t		cb_flags;
5555	size_t			size;
5556	uint8_t			address;
5557	int			user_conf_index;
5558	uint_t			config_index;
5559	int			prh_circ, rh_circ, circ;
5560	dev_info_t		*rh_dip;
5561	usb_dev_descr_t		usb_dev_descr;
5562
5563	if ((hwahcp = ddi_get_soft_state(hwahc_statep,
5564	    ddi_get_instance(dip))) == NULL) {
5565
5566		return (USB_INVALID_ARGS);
5567	}
5568
5569	rh_dip = hwahcp->hwahc_hubd->h_usba_device->usb_root_hub_dip;
5570	ndi_hold_devi(dip);	/* avoid racing with dev detach */
5571	/* exclude other threads */
5572	ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
5573	ndi_devi_enter(rh_dip, &rh_circ);
5574	ndi_devi_enter(dip, &circ);
5575
5576	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*dev_info));
5577
5578	hc_data = &hwahcp->hwahc_hc_data;
5579	mutex_enter(&hc_data->hc_mutex);
5580	dev_info = hc_data->hc_dev_infos[port];
5581
5582	/* Created in whcdi.c before authed */
5583	child_dip = hc_data->hc_children_dips[port];
5584
5585	child_ud = usba_get_usba_device(child_dip);
5586	ph = dev_info->wdev_ph;
5587
5588	mutex_exit(&hc_data->hc_mutex);
5589	/*
5590	 * HWA maintains the address space as a separate bus and
5591	 * will not occupy parent's address space
5592	 */
5593	address = child_ud->usb_addr;
5594	if (address < 0x80) {
5595		USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5596		    "hwahc_create_child: reconnecting, address = %d",
5597		    address);
5598
5599	} else {
5600		/* SetAddress(0) */
5601		if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
5602		    USB_DEV_REQ_HOST_TO_DEV,
5603		    USB_REQ_SET_ADDRESS,	/* bRequest */
5604		    0,				/* wValue */
5605		    0,				/* wIndex */
5606		    0,				/* wLength */
5607		    NULL, 0,
5608		    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
5609			char buffer[64];
5610			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
5611			    hwahcp->hwahc_log_handle,
5612			    "setting address failed (cr=%s cb_flags=%s "
5613			    "rval=%d)", usb_str_cr(completion_reason),
5614			    usb_str_cb_flags(cb_flags, buffer, sizeof (buffer)),
5615			    rval);
5616
5617			goto done;
5618		}
5619
5620		USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5621		    "set address 0 done");
5622
5623		usb_pipe_close(child_dip, ph,
5624		    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
5625
5626		child_ud->usb_addr = 0;
5627		dev_info->wdev_addr = 0;
5628		dev_info->wdev_ph = NULL;
5629
5630		/* need to be called each time dev addr is changed */
5631		if ((rval = wusb_hc_set_device_info(&hwahcp->hwahc_hc_data,
5632		    port)) != USB_SUCCESS) {
5633			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
5634			    hwahcp->hwahc_log_handle,
5635			    "update device info failed, rval = %d", rval);
5636
5637			goto done;
5638		}
5639
5640		/* new ph is stored in usba_device */
5641		if ((rval = usb_pipe_open(child_dip, NULL, NULL,
5642		    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) !=
5643		    USB_SUCCESS) {
5644			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
5645			    hwahcp->hwahc_log_handle,
5646			    "usb_pipe_open failed (%d)", rval);
5647
5648			goto done;
5649		}
5650
5651		/* provide at least 2ms time for address change, 7.3.1.3 */
5652		delay(drv_usectohz(2000));
5653
5654		/* start normal enumeration process */
5655		/*
5656		 * wusb bus address has 1:1 relationship with port number
5657		 * and wusb bus address starts from 2, so as to follow
5658		 * the convention that USB bus address 1 is reserved for
5659		 * host controller device. As such, only 126 WUSB devices
5660		 * are supported on a WUSB host
5661		 */
5662		address = port + 1;
5663		if (address >= 0x80) {
5664			USB_DPRINTF_L3(PRINT_MASK_CBOPS,
5665			    hwahcp->hwahc_log_handle,
5666			    "hwahc_create_child: address for port %d exceeds "
5667			    "0x80", port);
5668			rval = USB_FAILURE;
5669
5670			goto done;
5671		}
5672		/* Set the address of the device */
5673		if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
5674		    USB_DEV_REQ_HOST_TO_DEV,
5675		    USB_REQ_SET_ADDRESS,	/* bRequest */
5676		    address,			/* wValue */
5677		    0,				/* wIndex */
5678		    0,				/* wLength */
5679		    NULL, 0,
5680		    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
5681			char buffer[64];
5682			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
5683			    hwahcp->hwahc_log_handle,
5684			    "setting address failed (cr=%s cb_flags=%s "
5685			    "rval=%d)", usb_str_cr(completion_reason),
5686			    usb_str_cb_flags(cb_flags, buffer, sizeof (buffer)),
5687			    rval);
5688
5689			goto done;
5690		}
5691
5692		USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5693		    "set address 0x%x done", address);
5694
5695		usb_pipe_close(child_dip, ph,
5696		    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
5697
5698		child_ud->usb_addr = address;
5699		dev_info->wdev_addr = address;
5700		dev_info->wdev_ph = NULL;
5701
5702		if ((rval = wusb_hc_set_device_info(&hwahcp->hwahc_hc_data,
5703		    port)) != USB_SUCCESS) {
5704			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
5705			    hwahcp->hwahc_log_handle,
5706			    "update device info failed, rval = %d", rval);
5707
5708			goto done;
5709		}
5710
5711		/* new ph is stored in usba_device */
5712		if ((rval = usb_pipe_open(child_dip, NULL, NULL,
5713		    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) !=
5714		    USB_SUCCESS) {
5715			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
5716			    hwahcp->hwahc_log_handle,
5717			    "usb_pipe_open failed (%d)", rval);
5718
5719			goto done;
5720		}
5721
5722		/* provide at least 2ms time for address change, 7.3.1.3 */
5723		delay(drv_usectohz(2000));
5724	}
5725
5726	/* get device descriptor ignoring device reconnection */
5727	rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
5728	    USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
5729	    USB_REQ_GET_DESCR,			/* bRequest */
5730	    USB_DESCR_TYPE_SETUP_DEV,		/* wValue */
5731	    0,					/* wIndex */
5732	    512,				/* wLength */
5733	    &pdata, USB_ATTRS_SHORT_XFER_OK,
5734	    &completion_reason, &cb_flags, 0);
5735
5736	if (rval != USB_SUCCESS) {
5737		if (pdata) {
5738			freemsg(pdata);
5739			pdata = NULL;
5740		}
5741
5742		USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5743		    "hwahc_create_child: get device descriptor failed "
5744		    "(%s 0x%x %d)", usb_str_cr(completion_reason),
5745		    cb_flags, rval);
5746
5747		goto done;
5748	}
5749
5750	ASSERT(pdata != NULL);
5751	size = usb_parse_dev_descr(
5752	    pdata->b_rptr,
5753	    MBLKL(pdata),
5754	    &usb_dev_descr,
5755	    sizeof (usb_dev_descr_t));
5756	freemsg(pdata);
5757
5758	if (size < USB_DEV_DESCR_SIZE) {
5759		USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5760		    "hwahc_create_child: get device descriptor size = %lu "
5761		    "expected size = %u", size, USB_DEV_DESCR_SIZE);
5762		rval = USB_FAILURE;
5763
5764		goto done;
5765	}
5766
5767	bcopy(&usb_dev_descr, child_ud->usb_dev_descr,
5768	    sizeof (usb_dev_descr_t));
5769	child_ud->usb_n_cfgs = usb_dev_descr.bNumConfigurations;
5770
5771	if (usb_dev_descr.bNumConfigurations == 0) {
5772		USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5773		    "device descriptor:\n\t"
5774		    "l=0x%x type=0x%x USB=0x%x class=0x%x subclass=0x%x\n\t"
5775		    "protocol=0x%x maxpktsize=0x%x "
5776		    "Vid=0x%x Pid=0x%x rel=0x%x\n\t"
5777		    "Mfg=0x%x P=0x%x sn=0x%x #config=0x%x",
5778		    usb_dev_descr.bLength, usb_dev_descr.bDescriptorType,
5779		    usb_dev_descr.bcdUSB, usb_dev_descr.bDeviceClass,
5780		    usb_dev_descr.bDeviceSubClass,
5781		    usb_dev_descr.bDeviceProtocol,
5782		    usb_dev_descr.bMaxPacketSize0,
5783		    usb_dev_descr.idVendor,
5784		    usb_dev_descr.idProduct, usb_dev_descr.bcdDevice,
5785		    usb_dev_descr.iManufacturer, usb_dev_descr.iProduct,
5786		    usb_dev_descr.iSerialNumber,
5787		    usb_dev_descr.bNumConfigurations);
5788
5789		rval = USB_FAILURE;
5790
5791		goto done;
5792	}
5793
5794	/* get the device string descriptor(s) */
5795	usba_get_dev_string_descrs(child_dip, child_ud);
5796
5797	/* retrieve config cloud for all configurations */
5798	rval = hubd_get_all_device_config_cloud(hwahcp->hwahc_hubd,
5799	    child_dip, child_ud);
5800	if (rval != USB_SUCCESS) {
5801		USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5802		    "failed to get configuration descriptor(s)");
5803
5804		goto done;
5805	}
5806
5807	/* get the preferred configuration for this device */
5808	user_conf_index = hubd_select_device_configuration(hwahcp->hwahc_hubd,
5809	    port, child_dip, child_ud);
5810
5811	/* Check if the user selected configuration index is in range */
5812	if ((user_conf_index >= usb_dev_descr.bNumConfigurations) ||
5813	    (user_conf_index < 0)) {
5814		USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5815		    "Configuration index for device idVendor=%d "
5816		    "idProduct=%d is=%d, and is out of range[0..%d]",
5817		    usb_dev_descr.idVendor, usb_dev_descr.idProduct,
5818		    user_conf_index, usb_dev_descr.bNumConfigurations - 1);
5819
5820		/* treat this as user didn't specify configuration */
5821		user_conf_index = USBA_DEV_CONFIG_INDEX_UNDEFINED;
5822	}
5823
5824	if (user_conf_index == USBA_DEV_CONFIG_INDEX_UNDEFINED) {
5825		if (child_ud->usb_preferred_driver) {
5826			/*
5827			 * It is the job of the "preferred driver" to put the
5828			 * device in the desired configuration. Till then
5829			 * put the device in config index 0.
5830			 */
5831			/* h_ignore_pwr_budget = TRUE, not care the power */
5832			if ((rval = usba_hubdi_check_power_budget(dip, child_ud,
5833			    USB_DEV_DEFAULT_CONFIG_INDEX)) != USB_SUCCESS) {
5834
5835				goto done;
5836			}
5837
5838			child_dip = hubd_ready_device(hwahcp->hwahc_hubd,
5839			    child_dip, child_ud, USB_DEV_DEFAULT_CONFIG_INDEX);
5840
5841			/*
5842			 * Assign the dip before onlining to avoid race
5843			 * with busctl
5844			 */
5845			mutex_enter(&hc_data->hc_mutex);
5846			hc_data->hc_children_dips[port] = child_dip;
5847			mutex_exit(&hc_data->hc_mutex);
5848
5849			(void) usba_bind_driver(child_dip);
5850		} else {
5851			/*
5852			 * loop through all the configurations to see if we
5853			 * can find a driver for any one config. If not, set
5854			 * the device in config_index 0
5855			 */
5856			rval = USB_FAILURE;
5857			for (config_index = 0;
5858			    (config_index < usb_dev_descr.bNumConfigurations) &&
5859			    (rval != USB_SUCCESS); config_index++) {
5860
5861				child_dip = hubd_ready_device(
5862				    hwahcp->hwahc_hubd,
5863				    child_dip, child_ud, config_index);
5864
5865				/*
5866				 * Assign the dip before onlining to avoid race
5867				 * with busctl
5868				 */
5869				mutex_enter(&hc_data->hc_mutex);
5870				hc_data->hc_children_dips[port] = child_dip;
5871				mutex_exit(&hc_data->hc_mutex);
5872
5873				rval = usba_bind_driver(child_dip);
5874
5875				if (rval == USB_SUCCESS) {
5876					/* always succeed for WUSB device */
5877					if ((usba_hubdi_check_power_budget(dip,
5878					    child_ud, config_index)) !=
5879					    USB_SUCCESS) {
5880						rval = USB_FAILURE;
5881
5882						goto done;
5883					}
5884				}
5885			}
5886
5887			if (rval != USB_SUCCESS) {
5888				if ((usba_hubdi_check_power_budget(dip,
5889				    child_ud, 0)) != USB_SUCCESS) {
5890
5891					goto done;
5892				}
5893
5894				child_dip = hubd_ready_device(
5895				    hwahcp->hwahc_hubd,
5896				    child_dip, child_ud, 0);
5897				mutex_enter(&hc_data->hc_mutex);
5898				hc_data->hc_children_dips[port] = child_dip;
5899				mutex_exit(&hc_data->hc_mutex);
5900			}
5901		} /* end else loop all configs */
5902	} else {
5903		if ((usba_hubdi_check_power_budget(dip, child_ud,
5904		    (uint_t)user_conf_index)) != USB_SUCCESS) {
5905			rval = USB_FAILURE;
5906
5907			goto done;
5908		}
5909
5910		child_dip = hubd_ready_device(hwahcp->hwahc_hubd, child_dip,
5911		    child_ud, (uint_t)user_conf_index);
5912
5913		/*
5914		 * Assign the dip before onlining to avoid race
5915		 * with busctl
5916		 */
5917		mutex_enter(&hc_data->hc_mutex);
5918		hc_data->hc_children_dips[port] = child_dip;
5919		mutex_exit(&hc_data->hc_mutex);
5920
5921		(void) usba_bind_driver(child_dip);
5922
5923		rval = USB_SUCCESS;
5924	}
5925
5926	/* workaround for non response after ctrl write */
5927	usb_pipe_close(child_dip, ph,
5928	    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
5929
5930	if ((rval = usb_pipe_open(child_dip, NULL, NULL,
5931	    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) !=
5932	    USB_SUCCESS) {
5933		USB_DPRINTF_L2(PRINT_MASK_CBOPS,
5934		    hwahcp->hwahc_log_handle,
5935		    "usb_pipe_open failed (%d)", rval);
5936
5937		goto done;
5938	}
5939
5940done:
5941	_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*dev_info));
5942
5943	ndi_devi_exit(dip, circ);
5944	ndi_devi_exit(rh_dip, rh_circ);
5945	ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ);
5946
5947	(void) devfs_clean(rh_dip, NULL, 0);
5948
5949	if (rval == USB_SUCCESS) {
5950		(void) ndi_devi_online(child_dip, 0);
5951
5952		USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5953		    "hwahc_create_child: create timer for child %p",
5954		    (void *)dev_info);
5955
5956		mutex_enter(&hc_data->hc_mutex);
5957		hwahc_start_trust_timer(dev_info);
5958		mutex_exit(&hc_data->hc_mutex);
5959	}
5960
5961	ndi_rele_devi(dip);
5962
5963	return (rval);
5964}
5965
5966/* offline child on a certain port */
5967static int
5968hwahc_destroy_child(dev_info_t *dip, usb_port_t port)
5969{
5970	hwahc_state_t	*hwahcp;
5971
5972	if ((hwahcp = ddi_get_soft_state(hwahc_statep,
5973	    ddi_get_instance(dip))) == NULL) {
5974
5975		return (USB_INVALID_ARGS);
5976	}
5977
5978	hwahc_post_event(hwahcp, port, USBA_EVENT_TAG_HOT_REMOVAL);
5979
5980	USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
5981	    "hwahc_destroy_child: scheduling cleanup");
5982
5983	/* schedule cleanup thread */
5984	hubd_schedule_cleanup(hwahcp->hwahc_hubd->h_usba_device->
5985	    usb_root_hub_dip);
5986
5987	return (USB_SUCCESS);
5988}
5989
5990/*
5991 * called by cleanup thread to offline child and cleanup child resources
5992 * Child's callback functions have been called before calling this routine.
5993 *	dip - hwahc's dip
5994 */
5995static int
5996hwahc_cleanup_child(dev_info_t *dip)
5997{
5998	hwahc_state_t	*hwahcp;
5999	wusb_hc_data_t	*hc_data;
6000	usb_port_t	port;
6001
6002	if ((hwahcp = ddi_get_soft_state(hwahc_statep,
6003	    ddi_get_instance(dip))) == NULL) {
6004
6005		return (USB_INVALID_ARGS);
6006	}
6007
6008	hc_data = &hwahcp->hwahc_hc_data;
6009	mutex_enter(&hc_data->hc_mutex);
6010	for (port = 1; port <= hc_data->hc_num_ports; port++) {
6011		dev_info_t *cdip = hc_data->hc_children_dips[port];
6012
6013		if (cdip == NULL || DEVI_IS_DEVICE_REMOVED(cdip) == 0) {
6014
6015			continue;
6016		}
6017
6018		/*
6019		 * child's callback has been called and its dip has been
6020		 * marked REMOVED. Do further cleanup in hwa driver for
6021		 * this child.
6022		 */
6023		mutex_exit(&hc_data->hc_mutex);
6024		(void) hwahc_delete_child(dip, port, NDI_DEVI_REMOVE, B_TRUE);
6025		mutex_enter(&hc_data->hc_mutex);
6026	}
6027	mutex_exit(&hc_data->hc_mutex);
6028
6029	return (USB_SUCCESS);
6030}
6031
6032/* offline child and cleanup child resources */
6033static int
6034hwahc_delete_child(dev_info_t *dip, usb_port_t port, uint_t flag,
6035	boolean_t retry)
6036{
6037	hwahc_state_t	*hwahcp;
6038	dev_info_t	*child_dip;
6039	usba_device_t	*usba_device;
6040	wusb_hc_data_t	*hc_data;
6041	int		rval;
6042
6043	if ((hwahcp = ddi_get_soft_state(hwahc_statep,
6044	    ddi_get_instance(dip))) == NULL) {
6045
6046		return (USB_INVALID_ARGS);
6047	}
6048
6049	child_dip = hwahc_get_child_dip(hwahcp, port);
6050	if (child_dip == NULL) {
6051
6052		return (USB_SUCCESS);
6053	}
6054
6055	usba_device = usba_get_usba_device(child_dip);
6056	hc_data = &hwahcp->hwahc_hc_data;
6057
6058	USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
6059	    "hwahc_delete_child: port=%d, dip=0x%p usba_device=0x%p",
6060	    port, (void *)child_dip, (void *)usba_device);
6061
6062	if (usba_device) {
6063		usba_hubdi_incr_power_budget(dip, usba_device);
6064	}
6065
6066	/* remove this child's dip. If it's <DS_INITIALIZED, free it */
6067	rval = usba_destroy_child_devi(child_dip, flag);
6068
6069	if ((rval == USB_SUCCESS) && (flag & NDI_DEVI_REMOVE)) {
6070		/*
6071		 * if the child was still < DS_INITIALIZED
6072		 * then our bus_unconfig was not called and
6073		 * we have to zap the child here
6074		 */
6075		mutex_enter(&hc_data->hc_mutex);
6076		if (hc_data->hc_children_dips[port] == child_dip) {
6077			usba_device_t *ud = hc_data->hc_usba_devices[port];
6078			wusb_dev_info_t *dev_info = hc_data->hc_dev_infos[port];
6079
6080			hc_data->hc_children_dips[port] = NULL;
6081			if (ud) {
6082				mutex_exit(&hc_data->hc_mutex);
6083
6084				mutex_enter(&ud->usb_mutex);
6085				ud->usb_ref_count = 0;
6086				mutex_exit(&ud->usb_mutex);
6087
6088				usba_free_usba_device(ud);
6089				mutex_enter(&hc_data->hc_mutex);
6090				hc_data->hc_usba_devices[port] = NULL;
6091			}
6092
6093			/* free the child's wusb_dev_info data */
6094			if (dev_info) {
6095				wusb_secrt_data_t *secrt_data;
6096
6097				if (dev_info->
6098				    wdev_secrt_data.secrt_encry_descr) {
6099					secrt_data = &dev_info->wdev_secrt_data;
6100					kmem_free(secrt_data->secrt_encry_descr,
6101					    sizeof (usb_encryption_descr_t) *
6102					    secrt_data->secrt_n_encry);
6103				}
6104				if (dev_info->wdev_uwb_descr) {
6105					kmem_free(dev_info->wdev_uwb_descr,
6106					    sizeof (usb_uwb_cap_descr_t));
6107				}
6108				kmem_free(dev_info, sizeof (wusb_dev_info_t));
6109				hc_data->hc_dev_infos[port] = NULL;
6110			}
6111		}
6112		mutex_exit(&hc_data->hc_mutex);
6113	}
6114
6115	if ((rval != USB_SUCCESS) && retry) {
6116
6117		hubd_schedule_cleanup(usba_device->usb_root_hub_dip);
6118	}
6119
6120	return (rval);
6121}
6122
6123/*
6124 * Set encryption type for WUSB host, refer to WUSB 1.0/8.5.3.6
6125 * index = port number - 1
6126 */
6127int
6128hwahc_set_dev_encrypt(usb_pipe_handle_t ph, uint8_t ifc,
6129	usb_port_t index, wusb_secrt_data_t *secrt_data, uint8_t type)
6130{
6131	int16_t			value;
6132	usb_ctrl_setup_t	setup;
6133	usb_cr_t		cr;
6134	usb_cb_flags_t		cb_flags;
6135
6136	USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
6137	    "hwahc_set_dev_encrypt: device index = %d", index);
6138
6139	if (type == USB_ENC_TYPE_UNSECURE) {
6140		value = 0;
6141	} else if (type == USB_ENC_TYPE_CCM_1) {
6142		if (secrt_data == NULL) {
6143
6144			return (USB_INVALID_ARGS);
6145		}
6146
6147		value = wusb_get_ccm_encryption_value(secrt_data);
6148		if (value == -1) {
6149			USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
6150			    "hwahc_set_dev_encrypt: cannot find ccm "
6151			    "encryption type");
6152
6153			return (USB_FAILURE);
6154		}
6155		USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
6156		    "hwahc_set_dev_encrypt: ccm encryption value is %d",
6157		    value);
6158	} else {
6159		USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
6160		    "hwahc_set_dev_encrypt: unsupported encryption type %d",
6161		    type);
6162
6163		return (USB_INVALID_ARGS);
6164	}
6165
6166	setup.bmRequestType = USB_DEV_REQ_HOST_TO_DEV |
6167	    USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_RCPT_IF;
6168	setup.bRequest = USB_REQ_SET_ENCRYPTION;
6169	setup.wValue = (uint16_t)value;
6170	setup.wIndex = (index << 8) | ifc;
6171	setup.wLength = 0;
6172	setup.attrs = USB_ATTRS_NONE;
6173
6174	USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
6175	    "bmRequestType=0x%x, bRequest=0x%x, wValue=0x%x, wIndex=0x%x",
6176	    setup.bmRequestType, setup.bRequest, setup.wValue, setup.wIndex);
6177
6178	return (usb_pipe_ctrl_xfer_wait(ph, &setup, NULL,
6179	    &cr, &cb_flags, USB_FLAGS_SLEEP));
6180}
6181