xref: /onnv-gate/usr/src/uts/common/io/usb/hwa/hwahc/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 
93 void *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 */
104 uint_t	hwahc_errmask	= (uint_t)PRINT_MASK_ALL;
105 uint_t	hwahc_errlevel	= USB_LOG_L4;
106 uint_t	hwahc_instance_debug = (uint_t)-1;
107 
108 /* bus config debug flag */
109 uint_t	hwahc_bus_config_debug = 0;
110 uint8_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  */
117 static uint8_t	dft_gtk[16];
118 static uint8_t	dft_gtkid[3];
119 
120 extern usb_log_handle_t	whcdi_log_handle;
121 
122 /*
123  * Function Prototypes
124  */
125 /* driver operations (dev_ops) entry points */
126 static int	hwahc_open(dev_t *, int, int, cred_t *);
127 static int	hwahc_close(dev_t, int, int, cred_t *);
128 static int	hwahc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
129 
130 static int	hwahc_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
131 static int	hwahc_attach(dev_info_t *, ddi_attach_cmd_t);
132 static int	hwahc_detach(dev_info_t *, ddi_detach_cmd_t);
133 static int	hwahc_power(dev_info_t *, int, int);
134 
135 /* bus_ops entry points */
136 static int	hwahc_bus_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t,
137 		void *, void *);
138 static int	hwahc_busop_get_eventcookie(dev_info_t *, dev_info_t *,
139 		char *, ddi_eventcookie_t *);
140 static 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 *);
144 static int	hwahc_busop_remove_eventcall(dev_info_t *, ddi_callback_id_t);
145 static int	hwahc_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t,
146 		void *, dev_info_t **);
147 static int	hwahc_bus_unconfig(dev_info_t *, uint_t, ddi_bus_config_op_t,
148 		void *);
149 
150 /* hotplug and power management supporting functions */
151 static int	hwahc_disconnect_event_cb(dev_info_t *dip);
152 static int	hwahc_reconnect_event_cb(dev_info_t *dip);
153 static int	hwahc_pre_suspend_event_cb(dev_info_t *dip);
154 static int	hwahc_post_resume_event_cb(dev_info_t *dip);
155 static int	hwahc_cpr_suspend(dev_info_t *);
156 static int	hwahc_cpr_resume(dev_info_t *);
157 static void	hwahc_restore_device_state(dev_info_t *, hwahc_state_t *);
158 static void	hwahc_run_callbacks(hwahc_state_t *, usba_event_t);
159 static void	hwahc_post_event(hwahc_state_t *, usb_port_t, usba_event_t);
160 
161 static int	hwahc_cleanup(dev_info_t *, hwahc_state_t *);
162 static void	hwahc_create_pm_components(dev_info_t *, hwahc_state_t *);
163 static void	hwahc_destroy_pm_components(hwahc_state_t *);
164 static void	hwahc_pm_busy_component(hwahc_state_t *);
165 static void	hwahc_pm_idle_component(hwahc_state_t *);
166 static int	hwahc_pwrlvl0(hwahc_state_t *);
167 static int	hwahc_pwrlvl1(hwahc_state_t *);
168 static int	hwahc_pwrlvl2(hwahc_state_t *);
169 static int	hwahc_pwrlvl3(hwahc_state_t *);
170 static int	hwahc_hc_channel_suspend(hwahc_state_t *);
171 
172 /* hardware initialization and deinitialization functions */
173 static int	hwahc_parse_security_data(wusb_secrt_data_t *,
174 		usb_cfg_data_t *);
175 static void	hwahc_print_secrt_data(hwahc_state_t *);
176 
177 static int	hwahc_hub_attach(hwahc_state_t *);
178 static int	hwahc_hub_detach(hwahc_state_t *);
179 
180 static int	hwahc_hc_initial_start(hwahc_state_t *);
181 static int	hwahc_hc_final_stop(hwahc_state_t *);
182 static int	hwahc_wa_start(hwahc_state_t *);
183 static void	hwahc_wa_stop(hwahc_state_t *);
184 static int	hwahc_hc_channel_start(hwahc_state_t *);
185 static int	hwahc_hc_channel_stop(hwahc_state_t *);
186 static void	hwahc_hc_data_init(hwahc_state_t *);
187 static void	hwahc_hc_data_fini(hwahc_state_t *);
188 
189 /* ioctl support */
190 static int	hwahc_cfgadm_ioctl(hwahc_state_t *, int, intptr_t, int,
191 		cred_t *, int *);
192 static int	hwahc_wusb_ioctl(hwahc_state_t *, int, intptr_t, int,
193 		cred_t *, int *);
194 
195 /* callbacks registered to USBA */
196 static void	hwahc_disconnect_dev(dev_info_t *, usb_port_t);
197 static void	hwahc_reconnect_dev(dev_info_t *, usb_port_t);
198 static int	hwahc_create_child(dev_info_t *, usb_port_t);
199 static int	hwahc_destroy_child(dev_info_t *, usb_port_t);
200 static int	hwahc_cleanup_child(dev_info_t *);
201 static int	hwahc_delete_child(dev_info_t *, usb_port_t, uint_t, boolean_t);
202 
203 /* data transfer and notification handling */
204 static void	hwahc_intr_cb(usb_pipe_handle_t, struct usb_intr_req *);
205 static void	hwahc_intr_exc_cb(usb_pipe_handle_t, struct usb_intr_req *);
206 static void	hwahc_handle_notif(hwahc_state_t *, mblk_t *);
207 static void	hwahc_handle_xfer_result(hwahc_state_t *, uint8_t);
208 static void	hwahc_stop_result_thread(hwahc_state_t *);
209 static void	hwahc_result_thread(void *);
210 static void	hwahc_handle_dn_notif(hwahc_state_t *, hwa_notif_dn_recvd_t *);
211 static void	hwahc_notif_thread(void *);
212 static void	hwahc_handle_dn(hwahc_state_t *, hwa_notif_dn_recvd_t *);
213 static void	hwahc_drain_notif_queue(hwahc_state_t *);
214 static void	hwahc_rpipe_xfer_cb(dev_info_t *, usba_pipe_handle_data_t *,
215 		wusb_wa_trans_wrapper_t *, usb_cr_t);
216 
217 static void	hwahc_trust_timeout_handler(void *arg);
218 static void	hwahc_stop_trust_timer(wusb_dev_info_t *dev);
219 
220 static int hwahc_pipe_submit_periodic_req(wusb_wa_data_t *wa_data,
221 	usba_pipe_handle_data_t *ph);
222 
223 /* hwa specific requests */
224 static int	hwahc_set_chid(hwahc_state_t *, uint8_t *);
225 
226 /* helper functions */
227 static usb_port_t hwahc_get_port_num(hwahc_state_t *, struct devctl_iocdata *);
228 static dev_info_t *hwahc_get_child_dip(hwahc_state_t *, usb_port_t);
229 
230 static 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 
248 static 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 
279 static 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 
294 static struct modldrv hwahc_modldrv =	{
295 	&mod_driverops,
296 	"WUSB hwa-hc driver",
297 	&hwahc_ops
298 };
299 
300 static struct modlinkage modlinkage = {
301 	MODREV_1,
302 	&hwahc_modldrv,
303 	NULL
304 };
305 
306 /* events from parent */
307 static 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  */
318 static 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 
332 static	ndi_event_set_t hwahc_ndi_events = {
333 	NDI_EVENTS_REV1, HWAHC_N_NDI_EVENTS, hwahc_ndi_event_defs};
334 
335 /* transfer callbacks */
336 static 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  */
347 int
_init(void)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  */
369 int
_fini(void)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 
383 int
_info(struct modinfo * modinfop)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*/
395 static int
hwahc_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)396 hwahc_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  */
438 static int
hwahc_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)439 hwahc_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 
656 fail:
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  */
686 static int
hwahc_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)687 hwahc_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  */
725 static int
hwahc_cleanup(dev_info_t * dip,hwahc_state_t * hwahcp)726 hwahc_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 
820 done:
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*/
840 static int
hwahc_open(dev_t * devp,int flag,int otyp,cred_t * cred_p)841 hwahc_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*/
885 static int
hwahc_close(dev_t dev,int flag,int otyp,cred_t * cred_p)886 hwahc_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 */
920 static usb_port_t
hwahc_get_port_num(hwahc_state_t * hwahcp,struct devctl_iocdata * dcp)921 hwahc_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 */
942 static dev_info_t *
hwahc_get_child_dip(hwahc_state_t * hwahcp,usb_port_t port)943 hwahc_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  */
974 static uint_t
hwahc_cfgadm_state(hwahc_state_t * hwahcp,usb_port_t port)975 hwahc_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 */
1027 static int
hwahc_cfgadm_ioctl(hwahc_state_t * hwahcp,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)1028 hwahc_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 */
1634 static int
hwahc_set_chid(hwahc_state_t * hwahcp,uint8_t * chid)1635 hwahc_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 */
1684 static int
hwahc_wusb_ioctl(hwahc_state_t * hwahcp,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)1685 hwahc_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 
2112 static int
hwahc_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)2113 hwahc_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 */
2141 static usb_port_t
hwahc_child_dip2port(hwahc_state_t * hwahcp,dev_info_t * dip)2142 hwahc_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  */
2163 static void
hwahc_post_attach(hwahc_state_t * hwahcp,dev_info_t * rdip,struct attachspec * as)2164 hwahc_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 
2173 static void
hwahc_post_detach(hwahc_state_t * hwahcp,dev_info_t * rdip,struct detachspec * as)2174 hwahc_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  */
2189 static int
hwahc_bus_ctl(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t op,void * arg,void * result)2190 hwahc_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  */
2317 static int
hwahc_bus_config(dev_info_t * dip,uint_t flag,ddi_bus_config_op_t op,void * arg,dev_info_t ** child)2318 hwahc_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  */
2350 static int
hwahc_bus_unconfig(dev_info_t * dip,uint_t flag,ddi_bus_config_op_t op,void * arg)2351 hwahc_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  */
2472 static int
hwahc_busop_get_eventcookie(dev_info_t * dip,dev_info_t * rdip,char * eventname,ddi_eventcookie_t * cookie)2473 hwahc_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  */
2502 static int
hwahc_busop_add_eventcall(dev_info_t * dip,dev_info_t * rdip,ddi_eventcookie_t cookie,void (* callback)(dev_info_t * dip,ddi_eventcookie_t cookie,void * arg,void * bus_impldata),void * arg,ddi_callback_id_t * cb_id)2503 hwahc_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  */
2563 static int
hwahc_busop_remove_eventcall(dev_info_t * dip,ddi_callback_id_t cb_id)2564 hwahc_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  */
2596 static void
hwahc_post_event(hwahc_state_t * hwahcp,usb_port_t port,usba_event_t type)2597 hwahc_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  */
2771 static void
hwahc_run_callbacks(hwahc_state_t * hwahcp,usba_event_t type)2772 hwahc_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  */
2798 static int
hwahc_disconnect_event_cb(dev_info_t * dip)2799 hwahc_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  */
2867 static int
hwahc_reconnect_event_cb(dev_info_t * dip)2868 hwahc_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  */
2895 static int
hwahc_pre_suspend_event_cb(dev_info_t * dip)2896 hwahc_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  */
2978 static int
hwahc_post_resume_event_cb(dev_info_t * dip)2979 hwahc_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  */
3027 static void
hwahc_restore_device_state(dev_info_t * dip,hwahc_state_t * hwahcp)3028 hwahc_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 
3198 err:
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  */
3213 static int
hwahc_cpr_suspend(dev_info_t * dip)3214 hwahc_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  */
3278 static int
hwahc_cpr_resume(dev_info_t * dip)3279 hwahc_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  */
3305 static void
hwahc_create_pm_components(dev_info_t * dip,hwahc_state_t * hwahcp)3306 hwahc_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  */
3347 static void
hwahc_destroy_pm_components(hwahc_state_t * hwahcp)3348 hwahc_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 */
3405 static void
hwahc_pm_busy_component(hwahc_state_t * hwahcp)3406 hwahc_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 */
3432 static void
hwahc_pm_idle_component(hwahc_state_t * hwahcp)3433 hwahc_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 */
3459 static int
hwahc_power(dev_info_t * dip,int comp,int level)3460 hwahc_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 	}
3512 done:
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  */
3523 static int
hwahc_pwrlvl0(hwahc_state_t * hwahcp)3524 hwahc_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  */
3571 static int
hwahc_pwrlvl1(hwahc_state_t * hwahcp)3572 hwahc_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  */
3591 static int
hwahc_pwrlvl2(hwahc_state_t * hwahcp)3592 hwahc_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  */
3612 static int
hwahc_pwrlvl3(hwahc_state_t * hwahcp)3613 hwahc_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  */
3673 static int
hwahc_hc_channel_suspend(hwahc_state_t * hwahcp)3674 hwahc_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  */
3718 static int
hwahc_parse_security_data(wusb_secrt_data_t * secrt_data,usb_cfg_data_t * cfg_data)3719 hwahc_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 */
3790 static void
hwahc_hc_data_init(hwahc_state_t * hwahcp)3791 hwahc_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 */
3846 static void
hwahc_hc_data_fini(hwahc_state_t * hwahcp)3847 hwahc_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 */
3898 static int
hwahc_hc_initial_start(hwahc_state_t * hwahcp)3899 hwahc_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 
4113 err:
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 */
4126 static int
hwahc_hc_final_stop(hwahc_state_t * hwahcp)4127 hwahc_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  */
4217 static int
hwahc_hc_channel_start(hwahc_state_t * hwahcp)4218 hwahc_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  */
4299 static int
hwahc_hc_channel_stop(hwahc_state_t * hwahcp)4300 hwahc_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 */
4386 static int
hwahc_wa_start(hwahc_state_t * hwahcp)4387 hwahc_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 */
4423 static void
hwahc_wa_stop(hwahc_state_t * hwahcp)4424 hwahc_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  */
4438 static int
hwahc_hub_attach(hwahc_state_t * hwahcp)4439 hwahc_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 
4549 fail:
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 */
4559 static int
hwahc_hub_detach(hwahc_state_t * hwahcp)4560 hwahc_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 
4590 done:
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 */
4604 static void
hwahc_print_secrt_data(hwahc_state_t * hwahcp)4605 hwahc_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 */
4639 static void
hwahc_drain_notif_queue(hwahc_state_t * hwahcp)4640 hwahc_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 */
4694 static void
hwahc_intr_cb(usb_pipe_handle_t ph,struct usb_intr_req * reqp)4695 hwahc_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 */
4732 static void
hwahc_handle_notif(hwahc_state_t * hwahcp,mblk_t * data)4733 hwahc_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  */
4822 int
hwahc_start_result_thread(hwahc_state_t * hwahcp)4823 hwahc_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 */
4889 static void
hwahc_stop_result_thread(hwahc_state_t * hwahcp)4890 hwahc_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  */
4952 static void
hwahc_result_thread(void * arg)4953 hwahc_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  */
5011 static void
hwahc_handle_xfer_result(hwahc_state_t * hwahcp,uint8_t ept)5012 hwahc_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  */
5037 static void
hwahc_handle_dn_notif(hwahc_state_t * hwahcp,hwa_notif_dn_recvd_t * dn_notif)5038 hwahc_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 */
5074 static void
hwahc_notif_thread(void * arg)5075 hwahc_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 */
5117 static void
hwahc_set_device_active(hwahc_state_t * hwahcp,uint8_t devaddr)5118 hwahc_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  */
5145 static void
hwahc_handle_dn(hwahc_state_t * hwahcp,hwa_notif_dn_recvd_t * dn_notif)5146 hwahc_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 */
5230 static void
hwahc_intr_exc_cb(usb_pipe_handle_t ph,struct usb_intr_req * reqp)5231 hwahc_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  */
5280 static int
hwahc_pipe_submit_periodic_req(wusb_wa_data_t * wa_data,usba_pipe_handle_data_t * ph)5281 hwahc_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 */
5315 static void
hwahc_rpipe_xfer_cb(dev_info_t * dip,usba_pipe_handle_data_t * ph,wusb_wa_trans_wrapper_t * wr,usb_cr_t cr)5316 hwahc_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 */
5384 static void
hwahc_disconnect_dev(dev_info_t * dip,usb_port_t port)5385 hwahc_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 */
5417 static void
hwahc_reconnect_dev(dev_info_t * dip,usb_port_t port)5418 hwahc_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  */
5449 static void
hwahc_start_trust_timer(wusb_dev_info_t * dev)5450 hwahc_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 */
5464 static void
hwahc_trust_timeout_handler(void * arg)5465 hwahc_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 */
5517 void
hwahc_stop_trust_timer(wusb_dev_info_t * dev)5518 hwahc_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 */
5542 static int
hwahc_create_child(dev_info_t * dip,usb_port_t port)5543 hwahc_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 
5940 done:
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 */
5967 static int
hwahc_destroy_child(dev_info_t * dip,usb_port_t port)5968 hwahc_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  */
5995 static int
hwahc_cleanup_child(dev_info_t * dip)5996 hwahc_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 */
6033 static int
hwahc_delete_child(dev_info_t * dip,usb_port_t port,uint_t flag,boolean_t retry)6034 hwahc_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  */
6127 int
hwahc_set_dev_encrypt(usb_pipe_handle_t ph,uint8_t ifc,usb_port_t index,wusb_secrt_data_t * secrt_data,uint8_t type)6128 hwahc_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