xref: /onnv-gate/usr/src/uts/common/io/usb/hwa/hwahc/hwahc.c (revision 10912:bb04b6e33d44)
19430SRaymond.Chen@Sun.COM /*
29430SRaymond.Chen@Sun.COM  * CDDL HEADER START
39430SRaymond.Chen@Sun.COM  *
49430SRaymond.Chen@Sun.COM  * The contents of this file are subject to the terms of the
59430SRaymond.Chen@Sun.COM  * Common Development and Distribution License (the "License").
69430SRaymond.Chen@Sun.COM  * You may not use this file except in compliance with the License.
79430SRaymond.Chen@Sun.COM  *
89430SRaymond.Chen@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99430SRaymond.Chen@Sun.COM  * or http://www.opensolaris.org/os/licensing.
109430SRaymond.Chen@Sun.COM  * See the License for the specific language governing permissions
119430SRaymond.Chen@Sun.COM  * and limitations under the License.
129430SRaymond.Chen@Sun.COM  *
139430SRaymond.Chen@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
149430SRaymond.Chen@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159430SRaymond.Chen@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
169430SRaymond.Chen@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
179430SRaymond.Chen@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
189430SRaymond.Chen@Sun.COM  *
199430SRaymond.Chen@Sun.COM  * CDDL HEADER END
209430SRaymond.Chen@Sun.COM  */
219430SRaymond.Chen@Sun.COM /*
229430SRaymond.Chen@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
239430SRaymond.Chen@Sun.COM  * Use is subject to license terms.
249430SRaymond.Chen@Sun.COM  */
259430SRaymond.Chen@Sun.COM 
269430SRaymond.Chen@Sun.COM /*
279430SRaymond.Chen@Sun.COM  * The Data Transfer Interface driver for Host Wire Adapter device
289430SRaymond.Chen@Sun.COM  *
299430SRaymond.Chen@Sun.COM  * HWA device has two interfaces, one is the data transfer interface,
309430SRaymond.Chen@Sun.COM  * another is the radio control interface. This driver (hwahc) is only
319430SRaymond.Chen@Sun.COM  * for data transfer interface support, but it depends on the radio
329430SRaymond.Chen@Sun.COM  * control interface driver (hwarc) to work. That means the hwarc
339430SRaymond.Chen@Sun.COM  * driver must be loaded while the hwahc is working. This is now
349430SRaymond.Chen@Sun.COM  * ensured by holding hwarc open until hwahc detaches or powers down.
359430SRaymond.Chen@Sun.COM  *
369430SRaymond.Chen@Sun.COM  * The data transfer interface has three endpoints besides the default
379430SRaymond.Chen@Sun.COM  * control endpoint which is shared between the two interfaces. The
389430SRaymond.Chen@Sun.COM  * three endpoints are:
399430SRaymond.Chen@Sun.COM  *
409430SRaymond.Chen@Sun.COM  * - notification endpoint (intr in type, for asynchronous event
419430SRaymond.Chen@Sun.COM  * notifications and transfer status notifications)
429430SRaymond.Chen@Sun.COM  *
439430SRaymond.Chen@Sun.COM  * - data transfer OUT endpoint (bulk out type, for sending transfer
449430SRaymond.Chen@Sun.COM  * requests and transfer data from the host to the HWA device)
459430SRaymond.Chen@Sun.COM  *
469430SRaymond.Chen@Sun.COM  * - data transfer IN endpoint (bulk in type, for returning transfer
479430SRaymond.Chen@Sun.COM  * status and transfer data from the HWA device to the host)
489430SRaymond.Chen@Sun.COM  *
499430SRaymond.Chen@Sun.COM  * The HWA device is a USB 2.0 device, so it supports the standard USB
509430SRaymond.Chen@Sun.COM  * requests defined in chapter 9 of USB 2.0 specification as other USB
519430SRaymond.Chen@Sun.COM  * client devices. But its most important functionality is to work as
529430SRaymond.Chen@Sun.COM  * a wireless USB host. This means the hwahc driver needs to supply
539430SRaymond.Chen@Sun.COM  * host controller functionalities, which include children hotplug
549430SRaymond.Chen@Sun.COM  * support and data transfer support to children device endpoints.
559430SRaymond.Chen@Sun.COM  *
569430SRaymond.Chen@Sun.COM  * So hwahc driver is implemented as a nexus driver and it follows the
579430SRaymond.Chen@Sun.COM  * event mechanism in existing USBA framework to support children
589430SRaymond.Chen@Sun.COM  * hotplug events.
599430SRaymond.Chen@Sun.COM  *
609430SRaymond.Chen@Sun.COM  * The hwahc driver works as the root-hub on wireless USB bus. And it
619430SRaymond.Chen@Sun.COM  * relays data transfers to/from wireless bus to the USB bus where ehci/
629430SRaymond.Chen@Sun.COM  * ohci/uhci works as the root-hub. This makes a bus cascading topology.
639430SRaymond.Chen@Sun.COM  *
649430SRaymond.Chen@Sun.COM  * The data transfer to/from wireless device endpoints is implemented by
659430SRaymond.Chen@Sun.COM  * remote pipe (rpipe) mechanism. The rpipe descriptor on the HWA defines
669430SRaymond.Chen@Sun.COM  * the attributes of a wireless USB transfer, such as the transfer type,
679430SRaymond.Chen@Sun.COM  * the target device address, the target endpoint address and the max
689430SRaymond.Chen@Sun.COM  * packet size. And the transfer requests through data transfer OUT
699430SRaymond.Chen@Sun.COM  * endpoint will take a certain rpipe as the transfer target, thus
709430SRaymond.Chen@Sun.COM  * fulfills the data transfer across buses. Refer to chapter 8 of WUSB
719430SRaymond.Chen@Sun.COM  * 1.0 specification for details of this.
729430SRaymond.Chen@Sun.COM  */
739430SRaymond.Chen@Sun.COM 
749430SRaymond.Chen@Sun.COM #define	USBDRV_MAJOR_VER	2
759430SRaymond.Chen@Sun.COM #define	USBDRV_MINOR_VER	0
769430SRaymond.Chen@Sun.COM 
779430SRaymond.Chen@Sun.COM #include <sys/usb/hwa/hwahc/hwahc.h>
789430SRaymond.Chen@Sun.COM #include <sys/usb/hwa/hwahc/hwahc_util.h>
799430SRaymond.Chen@Sun.COM #include <sys/usb/usba/wa.h>
809430SRaymond.Chen@Sun.COM #include <sys/usb/usba/wusba.h>
819430SRaymond.Chen@Sun.COM #include <sys/usb/usba/whcdi.h>
829430SRaymond.Chen@Sun.COM #include <sys/usb/usba.h>
839430SRaymond.Chen@Sun.COM #include <sys/usb/usba/usba_impl.h>
849430SRaymond.Chen@Sun.COM #include <sys/usb/usba/usba_devdb.h>	/* for usba_devdb_refresh */
859430SRaymond.Chen@Sun.COM #include <sys/usb/hubd/hubdvar.h>
869430SRaymond.Chen@Sun.COM #include <sys/usb/hubd/hubd_impl.h>	/* for hubd_ioctl_data_t */
879430SRaymond.Chen@Sun.COM #include <sys/strsubr.h>	/* for allocb_wait */
889430SRaymond.Chen@Sun.COM #include <sys/strsun.h>		/* for MBLKL macro */
899430SRaymond.Chen@Sun.COM #include <sys/fs/dv_node.h>	/* for devfs_clean */
909430SRaymond.Chen@Sun.COM #include <sys/uwb/uwbai.h>	/* for uwb ioctls */
919430SRaymond.Chen@Sun.COM #include <sys/random.h>
929430SRaymond.Chen@Sun.COM 
939430SRaymond.Chen@Sun.COM void *hwahc_statep;
949430SRaymond.Chen@Sun.COM 
959430SRaymond.Chen@Sun.COM /* number of instances */
969430SRaymond.Chen@Sun.COM #define	HWAHC_INSTS	1
979430SRaymond.Chen@Sun.COM 
989430SRaymond.Chen@Sun.COM /* default value for set number DNTS slots request */
999430SRaymond.Chen@Sun.COM #define	HWAHC_DEFAULT_DNTS_INTERVAL	2 /* ms */
1009430SRaymond.Chen@Sun.COM #define	HWAHC_DEFAULT_DNTS_SLOT_NUM	4
1019430SRaymond.Chen@Sun.COM 
1029430SRaymond.Chen@Sun.COM 
1039430SRaymond.Chen@Sun.COM /* debug support */
1049430SRaymond.Chen@Sun.COM uint_t	hwahc_errmask	= (uint_t)PRINT_MASK_ALL;
1059430SRaymond.Chen@Sun.COM uint_t	hwahc_errlevel	= USB_LOG_L4;
1069430SRaymond.Chen@Sun.COM uint_t	hwahc_instance_debug = (uint_t)-1;
1079430SRaymond.Chen@Sun.COM 
1089430SRaymond.Chen@Sun.COM /* bus config debug flag */
1099430SRaymond.Chen@Sun.COM uint_t	hwahc_bus_config_debug = 0;
1109430SRaymond.Chen@Sun.COM uint8_t	hwahc_enable_trust_timeout = 1;
1119430SRaymond.Chen@Sun.COM 
1129430SRaymond.Chen@Sun.COM 
1139430SRaymond.Chen@Sun.COM /*
1149430SRaymond.Chen@Sun.COM  * Use the default GTK for the whole life of HWA driver.
1159430SRaymond.Chen@Sun.COM  * Not so compatible with WUSB spec.
1169430SRaymond.Chen@Sun.COM  */
1179430SRaymond.Chen@Sun.COM static uint8_t	dft_gtk[16];
1189430SRaymond.Chen@Sun.COM static uint8_t	dft_gtkid[3];
1199430SRaymond.Chen@Sun.COM 
1209430SRaymond.Chen@Sun.COM extern usb_log_handle_t	whcdi_log_handle;
1219430SRaymond.Chen@Sun.COM 
1229430SRaymond.Chen@Sun.COM /*
1239430SRaymond.Chen@Sun.COM  * Function Prototypes
1249430SRaymond.Chen@Sun.COM  */
1259430SRaymond.Chen@Sun.COM /* driver operations (dev_ops) entry points */
1269430SRaymond.Chen@Sun.COM static int	hwahc_open(dev_t *, int, int, cred_t *);
1279430SRaymond.Chen@Sun.COM static int	hwahc_close(dev_t, int, int, cred_t *);
1289430SRaymond.Chen@Sun.COM static int	hwahc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
1299430SRaymond.Chen@Sun.COM 
1309430SRaymond.Chen@Sun.COM static int	hwahc_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
1319430SRaymond.Chen@Sun.COM static int	hwahc_attach(dev_info_t *, ddi_attach_cmd_t);
1329430SRaymond.Chen@Sun.COM static int	hwahc_detach(dev_info_t *, ddi_detach_cmd_t);
1339430SRaymond.Chen@Sun.COM static int	hwahc_power(dev_info_t *, int, int);
1349430SRaymond.Chen@Sun.COM 
1359430SRaymond.Chen@Sun.COM /* bus_ops entry points */
1369430SRaymond.Chen@Sun.COM static int	hwahc_bus_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t,
1379430SRaymond.Chen@Sun.COM 		void *, void *);
1389430SRaymond.Chen@Sun.COM static int	hwahc_busop_get_eventcookie(dev_info_t *, dev_info_t *,
1399430SRaymond.Chen@Sun.COM 		char *, ddi_eventcookie_t *);
1409430SRaymond.Chen@Sun.COM static int	hwahc_busop_add_eventcall(
1419430SRaymond.Chen@Sun.COM 		dev_info_t *, dev_info_t *, ddi_eventcookie_t,
1429430SRaymond.Chen@Sun.COM 		void (*)(dev_info_t *, ddi_eventcookie_t, void *, void *),
1439430SRaymond.Chen@Sun.COM 		void *, ddi_callback_id_t *);
1449430SRaymond.Chen@Sun.COM static int	hwahc_busop_remove_eventcall(dev_info_t *, ddi_callback_id_t);
1459430SRaymond.Chen@Sun.COM static int	hwahc_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t,
1469430SRaymond.Chen@Sun.COM 		void *, dev_info_t **);
1479430SRaymond.Chen@Sun.COM static int	hwahc_bus_unconfig(dev_info_t *, uint_t, ddi_bus_config_op_t,
1489430SRaymond.Chen@Sun.COM 		void *);
1499430SRaymond.Chen@Sun.COM 
1509430SRaymond.Chen@Sun.COM /* hotplug and power management supporting functions */
1519430SRaymond.Chen@Sun.COM static int	hwahc_disconnect_event_cb(dev_info_t *dip);
1529430SRaymond.Chen@Sun.COM static int	hwahc_reconnect_event_cb(dev_info_t *dip);
1539430SRaymond.Chen@Sun.COM static int	hwahc_pre_suspend_event_cb(dev_info_t *dip);
1549430SRaymond.Chen@Sun.COM static int	hwahc_post_resume_event_cb(dev_info_t *dip);
1559430SRaymond.Chen@Sun.COM static int	hwahc_cpr_suspend(dev_info_t *);
1569430SRaymond.Chen@Sun.COM static int	hwahc_cpr_resume(dev_info_t *);
1579430SRaymond.Chen@Sun.COM static void	hwahc_restore_device_state(dev_info_t *, hwahc_state_t *);
1589430SRaymond.Chen@Sun.COM static void	hwahc_run_callbacks(hwahc_state_t *, usba_event_t);
1599430SRaymond.Chen@Sun.COM static void	hwahc_post_event(hwahc_state_t *, usb_port_t, usba_event_t);
1609430SRaymond.Chen@Sun.COM 
1619430SRaymond.Chen@Sun.COM static int	hwahc_cleanup(dev_info_t *, hwahc_state_t *);
1629430SRaymond.Chen@Sun.COM static void	hwahc_create_pm_components(dev_info_t *, hwahc_state_t *);
1639430SRaymond.Chen@Sun.COM static void	hwahc_destroy_pm_components(hwahc_state_t *);
1649430SRaymond.Chen@Sun.COM static void	hwahc_pm_busy_component(hwahc_state_t *);
1659430SRaymond.Chen@Sun.COM static void	hwahc_pm_idle_component(hwahc_state_t *);
1669430SRaymond.Chen@Sun.COM static int	hwahc_pwrlvl0(hwahc_state_t *);
1679430SRaymond.Chen@Sun.COM static int	hwahc_pwrlvl1(hwahc_state_t *);
1689430SRaymond.Chen@Sun.COM static int	hwahc_pwrlvl2(hwahc_state_t *);
1699430SRaymond.Chen@Sun.COM static int	hwahc_pwrlvl3(hwahc_state_t *);
1709430SRaymond.Chen@Sun.COM static int	hwahc_hc_channel_suspend(hwahc_state_t *);
1719430SRaymond.Chen@Sun.COM 
1729430SRaymond.Chen@Sun.COM /* hardware initialization and deinitialization functions */
1739430SRaymond.Chen@Sun.COM static int	hwahc_parse_security_data(wusb_secrt_data_t *,
1749430SRaymond.Chen@Sun.COM 		usb_cfg_data_t *);
1759430SRaymond.Chen@Sun.COM static void	hwahc_print_secrt_data(hwahc_state_t *);
1769430SRaymond.Chen@Sun.COM 
1779430SRaymond.Chen@Sun.COM static int	hwahc_hub_attach(hwahc_state_t *);
1789430SRaymond.Chen@Sun.COM static int	hwahc_hub_detach(hwahc_state_t *);
1799430SRaymond.Chen@Sun.COM 
1809430SRaymond.Chen@Sun.COM static int	hwahc_hc_initial_start(hwahc_state_t *);
1819430SRaymond.Chen@Sun.COM static int	hwahc_hc_final_stop(hwahc_state_t *);
1829430SRaymond.Chen@Sun.COM static int	hwahc_wa_start(hwahc_state_t *);
1839430SRaymond.Chen@Sun.COM static void	hwahc_wa_stop(hwahc_state_t *);
1849430SRaymond.Chen@Sun.COM static int	hwahc_hc_channel_start(hwahc_state_t *);
1859430SRaymond.Chen@Sun.COM static int	hwahc_hc_channel_stop(hwahc_state_t *);
1869430SRaymond.Chen@Sun.COM static void	hwahc_hc_data_init(hwahc_state_t *);
1879430SRaymond.Chen@Sun.COM static void	hwahc_hc_data_fini(hwahc_state_t *);
1889430SRaymond.Chen@Sun.COM 
1899430SRaymond.Chen@Sun.COM /* ioctl support */
1909430SRaymond.Chen@Sun.COM static int	hwahc_cfgadm_ioctl(hwahc_state_t *, int, intptr_t, int,
1919430SRaymond.Chen@Sun.COM 		cred_t *, int *);
1929430SRaymond.Chen@Sun.COM static int	hwahc_wusb_ioctl(hwahc_state_t *, int, intptr_t, int,
1939430SRaymond.Chen@Sun.COM 		cred_t *, int *);
1949430SRaymond.Chen@Sun.COM 
1959430SRaymond.Chen@Sun.COM /* callbacks registered to USBA */
1969430SRaymond.Chen@Sun.COM static void	hwahc_disconnect_dev(dev_info_t *, usb_port_t);
1979430SRaymond.Chen@Sun.COM static void	hwahc_reconnect_dev(dev_info_t *, usb_port_t);
1989430SRaymond.Chen@Sun.COM static int	hwahc_create_child(dev_info_t *, usb_port_t);
1999430SRaymond.Chen@Sun.COM static int	hwahc_destroy_child(dev_info_t *, usb_port_t);
2009430SRaymond.Chen@Sun.COM static int	hwahc_cleanup_child(dev_info_t *);
2019430SRaymond.Chen@Sun.COM static int	hwahc_delete_child(dev_info_t *, usb_port_t, uint_t, boolean_t);
2029430SRaymond.Chen@Sun.COM 
2039430SRaymond.Chen@Sun.COM /* data transfer and notification handling */
2049430SRaymond.Chen@Sun.COM static void	hwahc_intr_cb(usb_pipe_handle_t, struct usb_intr_req *);
2059430SRaymond.Chen@Sun.COM static void	hwahc_intr_exc_cb(usb_pipe_handle_t, struct usb_intr_req *);
2069430SRaymond.Chen@Sun.COM static void	hwahc_handle_notif(hwahc_state_t *, mblk_t *);
2079430SRaymond.Chen@Sun.COM static void	hwahc_handle_xfer_result(hwahc_state_t *, uint8_t);
2089430SRaymond.Chen@Sun.COM static void	hwahc_stop_result_thread(hwahc_state_t *);
2099430SRaymond.Chen@Sun.COM static void	hwahc_result_thread(void *);
2109430SRaymond.Chen@Sun.COM static void	hwahc_handle_dn_notif(hwahc_state_t *, hwa_notif_dn_recvd_t *);
2119430SRaymond.Chen@Sun.COM static void	hwahc_notif_thread(void *);
2129430SRaymond.Chen@Sun.COM static void	hwahc_handle_dn(hwahc_state_t *, hwa_notif_dn_recvd_t *);
2139430SRaymond.Chen@Sun.COM static void	hwahc_drain_notif_queue(hwahc_state_t *);
2149430SRaymond.Chen@Sun.COM static void	hwahc_rpipe_xfer_cb(dev_info_t *, usba_pipe_handle_data_t *,
2159430SRaymond.Chen@Sun.COM 		wusb_wa_trans_wrapper_t *, usb_cr_t);
2169430SRaymond.Chen@Sun.COM 
2179430SRaymond.Chen@Sun.COM static void	hwahc_trust_timeout_handler(void *arg);
2189430SRaymond.Chen@Sun.COM static void	hwahc_stop_trust_timer(wusb_dev_info_t *dev);
2199430SRaymond.Chen@Sun.COM 
2209430SRaymond.Chen@Sun.COM static int hwahc_pipe_submit_periodic_req(wusb_wa_data_t *wa_data,
2219430SRaymond.Chen@Sun.COM 	usba_pipe_handle_data_t *ph);
2229430SRaymond.Chen@Sun.COM 
2239430SRaymond.Chen@Sun.COM /* hwa specific requests */
2249430SRaymond.Chen@Sun.COM static int	hwahc_set_chid(hwahc_state_t *, uint8_t *);
2259430SRaymond.Chen@Sun.COM 
2269430SRaymond.Chen@Sun.COM /* helper functions */
2279430SRaymond.Chen@Sun.COM static usb_port_t hwahc_get_port_num(hwahc_state_t *, struct devctl_iocdata *);
2289430SRaymond.Chen@Sun.COM static dev_info_t *hwahc_get_child_dip(hwahc_state_t *, usb_port_t);
2299430SRaymond.Chen@Sun.COM 
2309430SRaymond.Chen@Sun.COM static struct cb_ops hwahc_cb_ops = {
2319430SRaymond.Chen@Sun.COM 	hwahc_open,			/* Open */
2329430SRaymond.Chen@Sun.COM 	hwahc_close,			/* Close */
2339430SRaymond.Chen@Sun.COM 	nodev,				/* Strategy */
2349430SRaymond.Chen@Sun.COM 	nodev,				/* Print */
2359430SRaymond.Chen@Sun.COM 	nodev,				/* Dump */
2369430SRaymond.Chen@Sun.COM 	nodev,				/* Read */
2379430SRaymond.Chen@Sun.COM 	nodev,				/* Write */
2389430SRaymond.Chen@Sun.COM 	hwahc_ioctl,			/* Ioctl */
2399430SRaymond.Chen@Sun.COM 	nodev,				/* Devmap */
2409430SRaymond.Chen@Sun.COM 	nodev,				/* Mmap */
2419430SRaymond.Chen@Sun.COM 	nodev,				/* Segmap */
2429430SRaymond.Chen@Sun.COM 	nochpoll,			/* Poll */
2439430SRaymond.Chen@Sun.COM 	ddi_prop_op,			/* cb_prop_op */
2449430SRaymond.Chen@Sun.COM 	NULL,				/* Streamtab */
2459430SRaymond.Chen@Sun.COM 	D_MP				/* Driver compatibility flag */
2469430SRaymond.Chen@Sun.COM };
2479430SRaymond.Chen@Sun.COM 
2489430SRaymond.Chen@Sun.COM static struct bus_ops hwahc_busops = {
2499430SRaymond.Chen@Sun.COM 	BUSO_REV,
2509430SRaymond.Chen@Sun.COM 	nullbusmap,			/* bus_map */
2519430SRaymond.Chen@Sun.COM 	NULL,				/* bus_get_intrspec */
2529430SRaymond.Chen@Sun.COM 	NULL,				/* bus_add_intrspec */
2539430SRaymond.Chen@Sun.COM 	NULL,				/* bus_remove_intrspec */
2549430SRaymond.Chen@Sun.COM 	NULL,				/* bus_map_fault */
2559430SRaymond.Chen@Sun.COM 	ddi_dma_map,			/* bus_dma_map */
2569430SRaymond.Chen@Sun.COM 	ddi_dma_allochdl,
2579430SRaymond.Chen@Sun.COM 	ddi_dma_freehdl,
2589430SRaymond.Chen@Sun.COM 	ddi_dma_bindhdl,
2599430SRaymond.Chen@Sun.COM 	ddi_dma_unbindhdl,
2609430SRaymond.Chen@Sun.COM 	ddi_dma_flush,
2619430SRaymond.Chen@Sun.COM 	ddi_dma_win,
2629430SRaymond.Chen@Sun.COM 	ddi_dma_mctl,			/* bus_dma_ctl */
2639430SRaymond.Chen@Sun.COM 	hwahc_bus_ctl,			/* bus_ctl */
2649430SRaymond.Chen@Sun.COM 	ddi_bus_prop_op,		/* bus_prop_op */
2659430SRaymond.Chen@Sun.COM 	hwahc_busop_get_eventcookie,	/* bus_get_eventcookie */
2669430SRaymond.Chen@Sun.COM 	hwahc_busop_add_eventcall,	/* bus_add_eventcall */
2679430SRaymond.Chen@Sun.COM 	hwahc_busop_remove_eventcall,	/* bus_remove_eventcall */
2689430SRaymond.Chen@Sun.COM 	NULL,				/* bus_post_event */
2699430SRaymond.Chen@Sun.COM 	NULL,				/* bus_intr_ctl */
2709430SRaymond.Chen@Sun.COM 	hwahc_bus_config,		/* bus_config */
2719430SRaymond.Chen@Sun.COM 	hwahc_bus_unconfig,		/* bus_unconfig */
2729430SRaymond.Chen@Sun.COM 	NULL,				/* bus_fm_init */
2739430SRaymond.Chen@Sun.COM 	NULL,				/* bus_fm_fini */
2749430SRaymond.Chen@Sun.COM 	NULL,				/* bus_fm_access_enter */
2759430SRaymond.Chen@Sun.COM 	NULL,				/* bus_fm_access_exit */
2769430SRaymond.Chen@Sun.COM 	NULL,				/* bus_power */
2779430SRaymond.Chen@Sun.COM };
2789430SRaymond.Chen@Sun.COM 
2799430SRaymond.Chen@Sun.COM static struct dev_ops hwahc_ops = {
2809430SRaymond.Chen@Sun.COM 	DEVO_REV,			/* Devo_rev */
2819430SRaymond.Chen@Sun.COM 	0,				/* Refcnt */
2829430SRaymond.Chen@Sun.COM 	hwahc_info,			/* Info */
2839430SRaymond.Chen@Sun.COM 	nulldev,			/* Identify */
2849430SRaymond.Chen@Sun.COM 	nulldev,			/* Probe */
2859430SRaymond.Chen@Sun.COM 	hwahc_attach,			/* Attach */
2869430SRaymond.Chen@Sun.COM 	hwahc_detach,			/* Detach */
2879430SRaymond.Chen@Sun.COM 	nodev,				/* Reset */
2889430SRaymond.Chen@Sun.COM 	&hwahc_cb_ops,			/* Driver operations */
2899430SRaymond.Chen@Sun.COM 	&hwahc_busops,			/* Bus operations */
2909430SRaymond.Chen@Sun.COM 	hwahc_power,			/* Power */
2919430SRaymond.Chen@Sun.COM 	ddi_quiesce_not_needed,		/* devo_quiesce */
2929430SRaymond.Chen@Sun.COM };
2939430SRaymond.Chen@Sun.COM 
2949430SRaymond.Chen@Sun.COM static struct modldrv hwahc_modldrv =	{
2959430SRaymond.Chen@Sun.COM 	&mod_driverops,
2969430SRaymond.Chen@Sun.COM 	"WUSB hwa-hc driver",
2979430SRaymond.Chen@Sun.COM 	&hwahc_ops
2989430SRaymond.Chen@Sun.COM };
2999430SRaymond.Chen@Sun.COM 
3009430SRaymond.Chen@Sun.COM static struct modlinkage modlinkage = {
3019430SRaymond.Chen@Sun.COM 	MODREV_1,
3029430SRaymond.Chen@Sun.COM 	&hwahc_modldrv,
3039430SRaymond.Chen@Sun.COM 	NULL
3049430SRaymond.Chen@Sun.COM };
3059430SRaymond.Chen@Sun.COM 
3069430SRaymond.Chen@Sun.COM /* events from parent */
3079430SRaymond.Chen@Sun.COM static usb_event_t hwahc_events = {
3089430SRaymond.Chen@Sun.COM 	hwahc_disconnect_event_cb,
3099430SRaymond.Chen@Sun.COM 	hwahc_reconnect_event_cb,
3109430SRaymond.Chen@Sun.COM 	hwahc_pre_suspend_event_cb,
3119430SRaymond.Chen@Sun.COM 	hwahc_post_resume_event_cb
3129430SRaymond.Chen@Sun.COM };
3139430SRaymond.Chen@Sun.COM 
3149430SRaymond.Chen@Sun.COM /*
3159430SRaymond.Chen@Sun.COM  * events support for children
3169430SRaymond.Chen@Sun.COM  * A map tween USBA_EVENTs and DDI_EVENTs.
3179430SRaymond.Chen@Sun.COM  */
3189430SRaymond.Chen@Sun.COM static ndi_event_definition_t hwahc_ndi_event_defs[] = {
3199430SRaymond.Chen@Sun.COM 	{USBA_EVENT_TAG_HOT_REMOVAL, DDI_DEVI_REMOVE_EVENT, EPL_KERNEL,
3209430SRaymond.Chen@Sun.COM 						NDI_EVENT_POST_TO_ALL},
3219430SRaymond.Chen@Sun.COM 	{USBA_EVENT_TAG_HOT_INSERTION, DDI_DEVI_INSERT_EVENT, EPL_KERNEL,
3229430SRaymond.Chen@Sun.COM 						NDI_EVENT_POST_TO_ALL},
3239430SRaymond.Chen@Sun.COM 	{USBA_EVENT_TAG_POST_RESUME, USBA_POST_RESUME_EVENT, EPL_KERNEL,
3249430SRaymond.Chen@Sun.COM 						NDI_EVENT_POST_TO_ALL},
3259430SRaymond.Chen@Sun.COM 	{USBA_EVENT_TAG_PRE_SUSPEND, USBA_PRE_SUSPEND_EVENT, EPL_KERNEL,
3269430SRaymond.Chen@Sun.COM 						NDI_EVENT_POST_TO_ALL}
3279430SRaymond.Chen@Sun.COM };
3289430SRaymond.Chen@Sun.COM 
3299430SRaymond.Chen@Sun.COM #define	HWAHC_N_NDI_EVENTS \
3309430SRaymond.Chen@Sun.COM 	(sizeof (hwahc_ndi_event_defs) / sizeof (ndi_event_definition_t))
3319430SRaymond.Chen@Sun.COM 
3329430SRaymond.Chen@Sun.COM static	ndi_event_set_t hwahc_ndi_events = {
3339430SRaymond.Chen@Sun.COM 	NDI_EVENTS_REV1, HWAHC_N_NDI_EVENTS, hwahc_ndi_event_defs};
3349430SRaymond.Chen@Sun.COM 
3359430SRaymond.Chen@Sun.COM /* transfer callbacks */
3369430SRaymond.Chen@Sun.COM static wusb_wa_cb_t hwahc_cbs = {
3379430SRaymond.Chen@Sun.COM 	hwahc_pipe_submit_periodic_req,
3389430SRaymond.Chen@Sun.COM 	hwahc_intr_cb,
3399430SRaymond.Chen@Sun.COM 	hwahc_intr_exc_cb,
3409430SRaymond.Chen@Sun.COM 	hwahc_rpipe_xfer_cb
3419430SRaymond.Chen@Sun.COM };
3429430SRaymond.Chen@Sun.COM 
3439430SRaymond.Chen@Sun.COM 
3449430SRaymond.Chen@Sun.COM /*
3459430SRaymond.Chen@Sun.COM  * Module-wide initialization routine.
3469430SRaymond.Chen@Sun.COM  */
3479430SRaymond.Chen@Sun.COM int
_init(void)3489430SRaymond.Chen@Sun.COM _init(void)
3499430SRaymond.Chen@Sun.COM {
3509430SRaymond.Chen@Sun.COM 	int rval;
3519430SRaymond.Chen@Sun.COM 
3529430SRaymond.Chen@Sun.COM 	if ((rval = ddi_soft_state_init(&hwahc_statep, sizeof (hwahc_state_t),
3539430SRaymond.Chen@Sun.COM 	    HWAHC_INSTS)) != 0) {
3549430SRaymond.Chen@Sun.COM 
3559430SRaymond.Chen@Sun.COM 		return (rval);
3569430SRaymond.Chen@Sun.COM 	}
3579430SRaymond.Chen@Sun.COM 
3589430SRaymond.Chen@Sun.COM 	if ((rval = mod_install(&modlinkage)) != 0) {
3599430SRaymond.Chen@Sun.COM 		ddi_soft_state_fini(&hwahc_statep);
3609430SRaymond.Chen@Sun.COM 	}
3619430SRaymond.Chen@Sun.COM 
3629430SRaymond.Chen@Sun.COM 	return (rval);
3639430SRaymond.Chen@Sun.COM }
3649430SRaymond.Chen@Sun.COM 
3659430SRaymond.Chen@Sun.COM 
3669430SRaymond.Chen@Sun.COM /*
3679430SRaymond.Chen@Sun.COM  * Module-wide tear-down routine.
3689430SRaymond.Chen@Sun.COM  */
3699430SRaymond.Chen@Sun.COM int
_fini(void)3709430SRaymond.Chen@Sun.COM _fini(void)
3719430SRaymond.Chen@Sun.COM {
3729430SRaymond.Chen@Sun.COM 	int rval;
3739430SRaymond.Chen@Sun.COM 
3749430SRaymond.Chen@Sun.COM 	if ((rval = mod_remove(&modlinkage)) == 0) {
3759430SRaymond.Chen@Sun.COM 		/* Release per module resources */
3769430SRaymond.Chen@Sun.COM 		ddi_soft_state_fini(&hwahc_statep);
3779430SRaymond.Chen@Sun.COM 	}
3789430SRaymond.Chen@Sun.COM 
3799430SRaymond.Chen@Sun.COM 	return (rval);
3809430SRaymond.Chen@Sun.COM }
3819430SRaymond.Chen@Sun.COM 
3829430SRaymond.Chen@Sun.COM 
3839430SRaymond.Chen@Sun.COM int
_info(struct modinfo * modinfop)3849430SRaymond.Chen@Sun.COM _info(struct modinfo *modinfop)
3859430SRaymond.Chen@Sun.COM {
3869430SRaymond.Chen@Sun.COM 	return (mod_info(&modlinkage, modinfop));
3879430SRaymond.Chen@Sun.COM }
3889430SRaymond.Chen@Sun.COM 
3899430SRaymond.Chen@Sun.COM 
3909430SRaymond.Chen@Sun.COM /*
3919430SRaymond.Chen@Sun.COM  * hwahc_info:
3929430SRaymond.Chen@Sun.COM  *	Get minor number, instance number, etc.
3939430SRaymond.Chen@Sun.COM  */
3949430SRaymond.Chen@Sun.COM /*ARGSUSED*/
3959430SRaymond.Chen@Sun.COM static int
hwahc_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)3969430SRaymond.Chen@Sun.COM hwahc_info(dev_info_t *dip, ddi_info_cmd_t infocmd,
3979430SRaymond.Chen@Sun.COM 	void *arg, void **result)
3989430SRaymond.Chen@Sun.COM {
3999430SRaymond.Chen@Sun.COM 	hwahc_state_t	*hwahcp;
4009430SRaymond.Chen@Sun.COM 	int error = DDI_FAILURE;
4019430SRaymond.Chen@Sun.COM 	int instance = HWAHC_MINOR_TO_INSTANCE(getminor((dev_t)arg));
4029430SRaymond.Chen@Sun.COM 
4039430SRaymond.Chen@Sun.COM 	switch (infocmd) {
4049430SRaymond.Chen@Sun.COM 	case DDI_INFO_DEVT2DEVINFO:
4059430SRaymond.Chen@Sun.COM 		if ((hwahcp = ddi_get_soft_state(hwahc_statep,
4069430SRaymond.Chen@Sun.COM 		    instance)) != NULL) {
4079430SRaymond.Chen@Sun.COM 			*result = hwahcp->hwahc_dip;
4089430SRaymond.Chen@Sun.COM 			if (*result != NULL) {
4099430SRaymond.Chen@Sun.COM 				error = DDI_SUCCESS;
4109430SRaymond.Chen@Sun.COM 			}
4119430SRaymond.Chen@Sun.COM 		} else {
4129430SRaymond.Chen@Sun.COM 			*result = NULL;
4139430SRaymond.Chen@Sun.COM 		}
4149430SRaymond.Chen@Sun.COM 		break;
4159430SRaymond.Chen@Sun.COM 	case DDI_INFO_DEVT2INSTANCE:
4169430SRaymond.Chen@Sun.COM 		*result = (void *)(uintptr_t)instance;
4179430SRaymond.Chen@Sun.COM 		error = DDI_SUCCESS;
4189430SRaymond.Chen@Sun.COM 		break;
4199430SRaymond.Chen@Sun.COM 	default:
4209430SRaymond.Chen@Sun.COM 		break;
4219430SRaymond.Chen@Sun.COM 	}
4229430SRaymond.Chen@Sun.COM 
4239430SRaymond.Chen@Sun.COM 	return (error);
4249430SRaymond.Chen@Sun.COM }
4259430SRaymond.Chen@Sun.COM 
4269430SRaymond.Chen@Sun.COM 
4279430SRaymond.Chen@Sun.COM /*
4289430SRaymond.Chen@Sun.COM  * hwahc_attach:
4299430SRaymond.Chen@Sun.COM  *	Attach or resume.
4309430SRaymond.Chen@Sun.COM  *
4319430SRaymond.Chen@Sun.COM  *	For attach, initialize state and device, including:
4329430SRaymond.Chen@Sun.COM  *		state variables, locks, device node,
4339430SRaymond.Chen@Sun.COM  *		resource initialization, event registration,
4349430SRaymond.Chen@Sun.COM  *		device registration with system
4359430SRaymond.Chen@Sun.COM  *		power management, hotplugging
4369430SRaymond.Chen@Sun.COM  *	For resume, restore device and state
4379430SRaymond.Chen@Sun.COM  */
4389430SRaymond.Chen@Sun.COM static int
hwahc_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)4399430SRaymond.Chen@Sun.COM hwahc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
4409430SRaymond.Chen@Sun.COM {
4419430SRaymond.Chen@Sun.COM 	int				instance = ddi_get_instance(dip);
4429430SRaymond.Chen@Sun.COM 	hwahc_state_t			*hwahcp = NULL;
4439430SRaymond.Chen@Sun.COM 	usb_client_dev_data_t		*dev_data;
4449430SRaymond.Chen@Sun.COM 	struct usb_cfg_data		*cfg_data;
4459430SRaymond.Chen@Sun.COM 	usba_hcdi_register_args_t	hcdi_args;
4469430SRaymond.Chen@Sun.COM 	int				rval;
4479430SRaymond.Chen@Sun.COM 	char *pathname;
4489430SRaymond.Chen@Sun.COM 
4499430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_ATTA, NULL, "hwahc_attach: cmd=%d", cmd);
4509430SRaymond.Chen@Sun.COM 
4519430SRaymond.Chen@Sun.COM 	switch (cmd) {
4529430SRaymond.Chen@Sun.COM 	case DDI_ATTACH:
4539430SRaymond.Chen@Sun.COM 		break;
4549430SRaymond.Chen@Sun.COM 	case DDI_RESUME:
455*10912SRaymond.Chen@Sun.COM 		(void) hwahc_cpr_resume(dip);
4569430SRaymond.Chen@Sun.COM 
4579430SRaymond.Chen@Sun.COM 		return (DDI_SUCCESS);
4589430SRaymond.Chen@Sun.COM 	default:
4599430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, NULL,
4609430SRaymond.Chen@Sun.COM 		    "hwahc_attach: failed");
4619430SRaymond.Chen@Sun.COM 
4629430SRaymond.Chen@Sun.COM 		return (DDI_FAILURE);
4639430SRaymond.Chen@Sun.COM 	}
4649430SRaymond.Chen@Sun.COM 
4659430SRaymond.Chen@Sun.COM 	/*
4669430SRaymond.Chen@Sun.COM 	 * Allocate soft state information.
4679430SRaymond.Chen@Sun.COM 	 */
4689430SRaymond.Chen@Sun.COM 	rval = ddi_soft_state_zalloc(hwahc_statep, instance);
4699430SRaymond.Chen@Sun.COM 	if (rval != DDI_SUCCESS) {
4709430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, NULL,
4719430SRaymond.Chen@Sun.COM 		    "hwahc_attach: cannot allocate soft state for instance %d",
4729430SRaymond.Chen@Sun.COM 		    instance);
4739430SRaymond.Chen@Sun.COM 
4749430SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
4759430SRaymond.Chen@Sun.COM 	}
4769430SRaymond.Chen@Sun.COM 
4779430SRaymond.Chen@Sun.COM 	hwahcp = ddi_get_soft_state(hwahc_statep, instance);
4789430SRaymond.Chen@Sun.COM 	if (hwahcp == NULL) {
4799430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, NULL,
4809430SRaymond.Chen@Sun.COM 		    "hwahc_attach: get soft state failed for instance %d",
4819430SRaymond.Chen@Sun.COM 		    instance);
4829430SRaymond.Chen@Sun.COM 
4839430SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
4849430SRaymond.Chen@Sun.COM 	}
4859430SRaymond.Chen@Sun.COM 
4869430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_log_handle = usb_alloc_log_hdl(dip, "hwahc",
4879430SRaymond.Chen@Sun.COM 	    &hwahc_errlevel, &hwahc_errmask, &hwahc_instance_debug, 0);
4889430SRaymond.Chen@Sun.COM 
4899430SRaymond.Chen@Sun.COM 	/* initialize hc state */
4909430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_INIT_STATE;
4919430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_dip = dip;
4929430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_instance = instance;
4939430SRaymond.Chen@Sun.COM 
4949430SRaymond.Chen@Sun.COM 	/* register with USBA as client driver */
4959430SRaymond.Chen@Sun.COM 	if (usb_client_attach(dip, USBDRV_VERSION, 0) != USB_SUCCESS) {
4969430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
4979430SRaymond.Chen@Sun.COM 		    "hwahc_attach: client attach failed");
4989430SRaymond.Chen@Sun.COM 
4999430SRaymond.Chen@Sun.COM 		goto fail;
5009430SRaymond.Chen@Sun.COM 	}
5019430SRaymond.Chen@Sun.COM 
5029430SRaymond.Chen@Sun.COM 	if (usb_get_dev_data(dip, &dev_data, USB_PARSE_LVL_IF, 0) !=
5039430SRaymond.Chen@Sun.COM 	    USB_SUCCESS) {
5049430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
5059430SRaymond.Chen@Sun.COM 		    "hwahc_attach: cannot get dev_data");
5069430SRaymond.Chen@Sun.COM 
5079430SRaymond.Chen@Sun.COM 		goto fail;
5089430SRaymond.Chen@Sun.COM 	}
5099430SRaymond.Chen@Sun.COM 
5109430SRaymond.Chen@Sun.COM 	/* initialize mutex and cv */
5119430SRaymond.Chen@Sun.COM 	mutex_init(&hwahcp->hwahc_mutex, NULL, MUTEX_DRIVER,
5129430SRaymond.Chen@Sun.COM 	    dev_data->dev_iblock_cookie);
5139430SRaymond.Chen@Sun.COM 	cv_init(&hwahcp->hwahc_result_thread_cv, NULL, CV_DRIVER, NULL);
5149430SRaymond.Chen@Sun.COM 
5159430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_flags |= HWAHC_LOCK_INITED;
5169430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_dev_data = dev_data;
5179430SRaymond.Chen@Sun.COM 
5189430SRaymond.Chen@Sun.COM 	/* initialize data transfer function related structure */
5199430SRaymond.Chen@Sun.COM 	if (wusb_wa_data_init(dip, &hwahcp->hwahc_wa_data, &hwahc_cbs,
5209430SRaymond.Chen@Sun.COM 	    dev_data, PRINT_MASK_ATTA,
5219430SRaymond.Chen@Sun.COM 	    hwahcp->hwahc_log_handle) != USB_SUCCESS) {
5229430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
5239430SRaymond.Chen@Sun.COM 		    "hwahc_attach: init wa data failed");
5249430SRaymond.Chen@Sun.COM 
5259430SRaymond.Chen@Sun.COM 		goto fail;
5269430SRaymond.Chen@Sun.COM 	}
5279430SRaymond.Chen@Sun.COM 
5289430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_flags |= HWAHC_WA_INITED;
5299430SRaymond.Chen@Sun.COM 	cfg_data = dev_data->dev_curr_cfg;
5309430SRaymond.Chen@Sun.COM 
5319430SRaymond.Chen@Sun.COM 	/* parse the security descrs from the configuration descr cloud */
5329430SRaymond.Chen@Sun.COM 	if (hwahc_parse_security_data(&hwahcp->hwahc_secrt_data, cfg_data) !=
5339430SRaymond.Chen@Sun.COM 	    USB_SUCCESS) {
5349430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
5359430SRaymond.Chen@Sun.COM 		    "hwahc_attach: parse security descrs failed");
5369430SRaymond.Chen@Sun.COM 
5379430SRaymond.Chen@Sun.COM 		goto fail;
5389430SRaymond.Chen@Sun.COM 	}
5399430SRaymond.Chen@Sun.COM 
5409430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_default_pipe = dev_data->dev_default_ph;
5419430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_wa_data.wa_private_data = (void *)hwahcp;
5429430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_wa_data.wa_default_pipe = hwahcp->hwahc_default_pipe;
5439430SRaymond.Chen@Sun.COM 
5449430SRaymond.Chen@Sun.COM 	usb_free_descr_tree(dip, dev_data);
5459430SRaymond.Chen@Sun.COM 
5469430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_dev_state = USB_DEV_ONLINE;
5479430SRaymond.Chen@Sun.COM 
5489430SRaymond.Chen@Sun.COM 	/* now create components to power manage this device */
5499430SRaymond.Chen@Sun.COM 	hwahc_create_pm_components(dip, hwahcp);
5509430SRaymond.Chen@Sun.COM 
5519430SRaymond.Chen@Sun.COM 	/*
5529430SRaymond.Chen@Sun.COM 	 * Event definition and registration
5539430SRaymond.Chen@Sun.COM 	 *
5549430SRaymond.Chen@Sun.COM 	 * allocate a new NDI event handle as a nexus driver
5559430SRaymond.Chen@Sun.COM 	 */
5569430SRaymond.Chen@Sun.COM 	(void) ndi_event_alloc_hdl(dip, 0, &hwahcp->hwahc_ndi_event_hdl,
5579430SRaymond.Chen@Sun.COM 	    NDI_SLEEP);
5589430SRaymond.Chen@Sun.COM 
5599430SRaymond.Chen@Sun.COM 	/*
5609430SRaymond.Chen@Sun.COM 	 * bind our NDI events with the event handle,
5619430SRaymond.Chen@Sun.COM 	 * i.e. Define the events set we're to support as a nexus driver.
5629430SRaymond.Chen@Sun.COM 	 *
5639430SRaymond.Chen@Sun.COM 	 * These events will be used by bus_ops functions to register callbacks.
5649430SRaymond.Chen@Sun.COM 	 */
5659430SRaymond.Chen@Sun.COM 	if (ndi_event_bind_set(hwahcp->hwahc_ndi_event_hdl, &hwahc_ndi_events,
5669430SRaymond.Chen@Sun.COM 	    NDI_SLEEP)) {
5679430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
5689430SRaymond.Chen@Sun.COM 		    "hwahc_attach: binding event set failed");
5699430SRaymond.Chen@Sun.COM 
5709430SRaymond.Chen@Sun.COM 		goto fail;
5719430SRaymond.Chen@Sun.COM 	}
5729430SRaymond.Chen@Sun.COM 
5739430SRaymond.Chen@Sun.COM 
5749430SRaymond.Chen@Sun.COM 	/*
5759430SRaymond.Chen@Sun.COM 	 * Register USB events to USBA(the parent) to get callbacks as a
5769430SRaymond.Chen@Sun.COM 	 * child of (root) hub
5779430SRaymond.Chen@Sun.COM 	 */
5789430SRaymond.Chen@Sun.COM 	if (usb_register_event_cbs(dip, &hwahc_events, 0) != USB_SUCCESS) {
5799430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
5809430SRaymond.Chen@Sun.COM 		    "hwahc_attach: register_events failed");
5819430SRaymond.Chen@Sun.COM 
5829430SRaymond.Chen@Sun.COM 		goto fail;
5839430SRaymond.Chen@Sun.COM 	}
5849430SRaymond.Chen@Sun.COM 
5859430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_flags |= HWAHC_EVENTS_REGISTERED;
5869430SRaymond.Chen@Sun.COM 
5879430SRaymond.Chen@Sun.COM 	/* create minor nodes */
5889430SRaymond.Chen@Sun.COM 	if (ddi_create_minor_node(dip, "hwahc", S_IFCHR,
5899430SRaymond.Chen@Sun.COM 	    instance << HWAHC_MINOR_INSTANCE_SHIFT,
5909430SRaymond.Chen@Sun.COM 	    DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
5919430SRaymond.Chen@Sun.COM 
5929430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
5939430SRaymond.Chen@Sun.COM 		    "hwahc_attach: cannot create minor node");
5949430SRaymond.Chen@Sun.COM 
5959430SRaymond.Chen@Sun.COM 		goto fail;
5969430SRaymond.Chen@Sun.COM 	}
5979430SRaymond.Chen@Sun.COM 
5989430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_flags |= HWAHC_MINOR_NODE_CREATED;
5999430SRaymond.Chen@Sun.COM 
6009430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_hcdi_ops = hwahc_alloc_hcdi_ops(hwahcp);
6019430SRaymond.Chen@Sun.COM 
6029430SRaymond.Chen@Sun.COM 	/* register this hc instance with usba HCD interface */
6039430SRaymond.Chen@Sun.COM 	hcdi_args.usba_hcdi_register_version = HCDI_REGISTER_VERSION;
6049430SRaymond.Chen@Sun.COM 	hcdi_args.usba_hcdi_register_dip = dip;
6059430SRaymond.Chen@Sun.COM 	hcdi_args.usba_hcdi_register_ops = hwahcp->hwahc_hcdi_ops;
6069430SRaymond.Chen@Sun.COM 
6079430SRaymond.Chen@Sun.COM 	/* use parent dma attr here */
6089430SRaymond.Chen@Sun.COM 	hcdi_args.usba_hcdi_register_dma_attr = usba_get_hc_dma_attr(dip);
6099430SRaymond.Chen@Sun.COM 	hcdi_args.usba_hcdi_register_iblock_cookie = NULL;
6109430SRaymond.Chen@Sun.COM 
6119430SRaymond.Chen@Sun.COM 	if (usba_hcdi_register(&hcdi_args, 0) != USB_SUCCESS) {
6129430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
6139430SRaymond.Chen@Sun.COM 		    "hwahc_attach: usba_hcdi_register failed");
6149430SRaymond.Chen@Sun.COM 
6159430SRaymond.Chen@Sun.COM 		goto fail;
6169430SRaymond.Chen@Sun.COM 	}
6179430SRaymond.Chen@Sun.COM 
6189430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_flags |= HWAHC_HCDI_REGISTERED;
6199430SRaymond.Chen@Sun.COM 
6209430SRaymond.Chen@Sun.COM 	/* create hub minor node and register to usba HUBD interface */
6219430SRaymond.Chen@Sun.COM 	if (hwahc_hub_attach(hwahcp) != USB_SUCCESS) {
6229430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
6239430SRaymond.Chen@Sun.COM 		    "hwahc_attach: hub attach failed");
6249430SRaymond.Chen@Sun.COM 
6259430SRaymond.Chen@Sun.COM 		goto fail;
6269430SRaymond.Chen@Sun.COM 	}
6279430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_flags |= HWAHC_HUBREG;
6289430SRaymond.Chen@Sun.COM 
6299430SRaymond.Chen@Sun.COM 	/* intialize WUSB host function related structure */
6309430SRaymond.Chen@Sun.COM 	hwahc_hc_data_init(hwahcp);
6319430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_flags |= HWAHC_HC_INITED;
6329430SRaymond.Chen@Sun.COM 
6339430SRaymond.Chen@Sun.COM 	/* can be combined with wusb_wa_data_init() */
6349430SRaymond.Chen@Sun.COM 	if (hwahc_wa_start(hwahcp) != USB_SUCCESS) {
6359430SRaymond.Chen@Sun.COM 
6369430SRaymond.Chen@Sun.COM 		goto fail;
6379430SRaymond.Chen@Sun.COM 	}
6389430SRaymond.Chen@Sun.COM 
6399430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_flags |= HWAHC_WA_STARTED;
6409430SRaymond.Chen@Sun.COM 
6419430SRaymond.Chen@Sun.COM 	/* report this dev */
6429430SRaymond.Chen@Sun.COM 	ddi_report_dev(dip);
6439430SRaymond.Chen@Sun.COM 
6449430SRaymond.Chen@Sun.COM 	hwahc_pm_idle_component(hwahcp);
6459430SRaymond.Chen@Sun.COM 
6469430SRaymond.Chen@Sun.COM 	mutex_enter(&(hwahcp->hwahc_mutex));
6479430SRaymond.Chen@Sun.COM 	hwahc_print_secrt_data(hwahcp);
6489430SRaymond.Chen@Sun.COM 	mutex_exit(&(hwahcp->hwahc_mutex));
6499430SRaymond.Chen@Sun.COM 
6509430SRaymond.Chen@Sun.COM 	if (uwb_dev_online(dip) != USB_SUCCESS) {
6519430SRaymond.Chen@Sun.COM 		goto fail;
6529430SRaymond.Chen@Sun.COM 	}
6539430SRaymond.Chen@Sun.COM 
6549430SRaymond.Chen@Sun.COM 	return (DDI_SUCCESS);
6559430SRaymond.Chen@Sun.COM 
6569430SRaymond.Chen@Sun.COM fail:
6579430SRaymond.Chen@Sun.COM 	pathname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
6589430SRaymond.Chen@Sun.COM 
6599430SRaymond.Chen@Sun.COM 	/* log this message to usba_debug_buf */
6609430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
6619430SRaymond.Chen@Sun.COM 	    "cannot attach %s", ddi_pathname(dip, pathname));
6629430SRaymond.Chen@Sun.COM 
6639430SRaymond.Chen@Sun.COM 	kmem_free(pathname, MAXPATHLEN);
6649430SRaymond.Chen@Sun.COM 
6659430SRaymond.Chen@Sun.COM 	if (hwahcp) {
6669430SRaymond.Chen@Sun.COM 		hwahc_pm_idle_component(hwahcp);
6679430SRaymond.Chen@Sun.COM 
6689430SRaymond.Chen@Sun.COM 		rval = hwahc_cleanup(dip, hwahcp);
6699430SRaymond.Chen@Sun.COM 		if (rval != USB_SUCCESS) {
6709430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_ATTA,
6719430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
6729430SRaymond.Chen@Sun.COM 			    "failure to complete cleanup after attach failure");
6739430SRaymond.Chen@Sun.COM 		}
6749430SRaymond.Chen@Sun.COM 	}
6759430SRaymond.Chen@Sun.COM 
6769430SRaymond.Chen@Sun.COM 	return (DDI_FAILURE);
6779430SRaymond.Chen@Sun.COM }
6789430SRaymond.Chen@Sun.COM 
6799430SRaymond.Chen@Sun.COM 
6809430SRaymond.Chen@Sun.COM /*
6819430SRaymond.Chen@Sun.COM  * hwahc_detach:
6829430SRaymond.Chen@Sun.COM  *	detach or suspend driver instance
6839430SRaymond.Chen@Sun.COM  *
6849430SRaymond.Chen@Sun.COM  * Note: in detach, only contention threads is from pm and disconnnect.
6859430SRaymond.Chen@Sun.COM  */
6869430SRaymond.Chen@Sun.COM static int
hwahc_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)6879430SRaymond.Chen@Sun.COM hwahc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
6889430SRaymond.Chen@Sun.COM {
6899430SRaymond.Chen@Sun.COM 	int		instance = ddi_get_instance(dip);
6909430SRaymond.Chen@Sun.COM 	hwahc_state_t	*hwahcp = ddi_get_soft_state(hwahc_statep, instance);
6919430SRaymond.Chen@Sun.COM 	int		rval = DDI_FAILURE;
6929430SRaymond.Chen@Sun.COM 
6939430SRaymond.Chen@Sun.COM 
6949430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
6959430SRaymond.Chen@Sun.COM 	    "hwahc_detach: cmd = %d", cmd);
6969430SRaymond.Chen@Sun.COM 
6979430SRaymond.Chen@Sun.COM 	switch (cmd) {
6989430SRaymond.Chen@Sun.COM 	case DDI_DETACH:
6999430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
7009430SRaymond.Chen@Sun.COM 		    "offline uwb device for dip: 0x%p", (void *)dip);
7019430SRaymond.Chen@Sun.COM 		/* offline the hwarc interface */
7029430SRaymond.Chen@Sun.COM 		(void) uwb_dev_offline(dip);
7039430SRaymond.Chen@Sun.COM 		if (hwahcp) {
7049430SRaymond.Chen@Sun.COM 			rval = hwahc_cleanup(dip, hwahcp);
7059430SRaymond.Chen@Sun.COM 		}
7069430SRaymond.Chen@Sun.COM 
7079430SRaymond.Chen@Sun.COM 		break;
7089430SRaymond.Chen@Sun.COM 	case DDI_SUSPEND:
7099430SRaymond.Chen@Sun.COM 		rval = hwahc_cpr_suspend(dip);
7109430SRaymond.Chen@Sun.COM 
7119430SRaymond.Chen@Sun.COM 		break;
7129430SRaymond.Chen@Sun.COM 	default:
7139430SRaymond.Chen@Sun.COM 
7149430SRaymond.Chen@Sun.COM 		break;
7159430SRaymond.Chen@Sun.COM 	}
7169430SRaymond.Chen@Sun.COM 
7179430SRaymond.Chen@Sun.COM 	return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
7189430SRaymond.Chen@Sun.COM }
7199430SRaymond.Chen@Sun.COM 
7209430SRaymond.Chen@Sun.COM 
7219430SRaymond.Chen@Sun.COM /*
7229430SRaymond.Chen@Sun.COM  * hwahc_cleanup:
7239430SRaymond.Chen@Sun.COM  *	clean up on attach failure or detach
7249430SRaymond.Chen@Sun.COM  */
7259430SRaymond.Chen@Sun.COM static int
hwahc_cleanup(dev_info_t * dip,hwahc_state_t * hwahcp)7269430SRaymond.Chen@Sun.COM hwahc_cleanup(dev_info_t *dip, hwahc_state_t *hwahcp)
7279430SRaymond.Chen@Sun.COM {
7289430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
7299430SRaymond.Chen@Sun.COM 	    "hwahc_cleanup: start");
7309430SRaymond.Chen@Sun.COM 
7319430SRaymond.Chen@Sun.COM 	if ((hwahcp->hwahc_flags & HWAHC_LOCK_INITED) == 0) {
7329430SRaymond.Chen@Sun.COM 
7339430SRaymond.Chen@Sun.COM 		goto done;
7349430SRaymond.Chen@Sun.COM 	}
7359430SRaymond.Chen@Sun.COM 
7369430SRaymond.Chen@Sun.COM 	/*
7379430SRaymond.Chen@Sun.COM 	 * deallocate events, if events are still registered
7389430SRaymond.Chen@Sun.COM 	 * (ie. children still attached) then we have to fail the detach
7399430SRaymond.Chen@Sun.COM 	 */
7409430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_ndi_event_hdl &&
7419430SRaymond.Chen@Sun.COM 	    (ndi_event_free_hdl(hwahcp->hwahc_ndi_event_hdl) != NDI_SUCCESS)) {
7429430SRaymond.Chen@Sun.COM 
7439430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
7449430SRaymond.Chen@Sun.COM 		    "hwahc_cleanup: ndi_event_free_hdl failed");
7459430SRaymond.Chen@Sun.COM 
7469430SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
7479430SRaymond.Chen@Sun.COM 	}
7489430SRaymond.Chen@Sun.COM 
7499430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_flags & HWAHC_EVENTS_REGISTERED) {
7509430SRaymond.Chen@Sun.COM 		/* unregister events */
7519430SRaymond.Chen@Sun.COM 		usb_unregister_event_cbs(dip, &hwahc_events);
7529430SRaymond.Chen@Sun.COM 	}
7539430SRaymond.Chen@Sun.COM 
7549430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_flags & HWAHC_HCDI_REGISTERED) {
7559430SRaymond.Chen@Sun.COM 		/* unregister the instance with usba HCD interface */
7569430SRaymond.Chen@Sun.COM 		usba_hcdi_unregister(hwahcp->hwahc_dip);
7579430SRaymond.Chen@Sun.COM 	}
7589430SRaymond.Chen@Sun.COM 
7599430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
7609430SRaymond.Chen@Sun.COM 
7619430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_hw_state != HWAHC_HW_STOPPED) {
7629430SRaymond.Chen@Sun.COM 		/* stop the hw if it is enabled */
7639430SRaymond.Chen@Sun.COM 		(void) hwahc_hc_final_stop(hwahcp);
7649430SRaymond.Chen@Sun.COM 	}
7659430SRaymond.Chen@Sun.COM 
7669430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_flags & HWAHC_WA_STARTED) {
7679430SRaymond.Chen@Sun.COM 		/* can be combined with wusb_wa_data_fini() */
7689797SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
7699430SRaymond.Chen@Sun.COM 		hwahc_wa_stop(hwahcp);
7709797SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
7719430SRaymond.Chen@Sun.COM 	}
7729430SRaymond.Chen@Sun.COM 
7739430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_flags & HWAHC_HC_INITED) {
7749430SRaymond.Chen@Sun.COM 		/* deinitialize the WUSB host function related structure */
7759430SRaymond.Chen@Sun.COM 		hwahc_hc_data_fini(hwahcp);
7769430SRaymond.Chen@Sun.COM 	}
7779430SRaymond.Chen@Sun.COM 
7789430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
7799430SRaymond.Chen@Sun.COM 
7809430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_pm) {
7819430SRaymond.Chen@Sun.COM 		/* destroy power management components */
7829430SRaymond.Chen@Sun.COM 		hwahc_destroy_pm_components(hwahcp);
7839430SRaymond.Chen@Sun.COM 	}
7849430SRaymond.Chen@Sun.COM 
7859430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_flags & HWAHC_HUBREG) {
7869430SRaymond.Chen@Sun.COM 		/* unregister the instance from usba HUBD interface */
7879430SRaymond.Chen@Sun.COM 		if (hwahc_hub_detach(hwahcp) != USB_SUCCESS) {
7889430SRaymond.Chen@Sun.COM 
7899430SRaymond.Chen@Sun.COM 			return (USB_FAILURE);
7909430SRaymond.Chen@Sun.COM 		}
7919430SRaymond.Chen@Sun.COM 	}
7929430SRaymond.Chen@Sun.COM 
7939430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_hcdi_ops) {
7949430SRaymond.Chen@Sun.COM 		usba_free_hcdi_ops(hwahcp->hwahc_hcdi_ops);
7959430SRaymond.Chen@Sun.COM 	}
7969430SRaymond.Chen@Sun.COM 
7979430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
7989430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_secrt_data.secrt_encry_descr) {
7999430SRaymond.Chen@Sun.COM 		/* free security descrs */
8009430SRaymond.Chen@Sun.COM 		kmem_free(hwahcp->hwahc_secrt_data.secrt_encry_descr,
8019430SRaymond.Chen@Sun.COM 		    sizeof (usb_encryption_descr_t) *
8029430SRaymond.Chen@Sun.COM 		    hwahcp->hwahc_secrt_data.secrt_n_encry);
8039430SRaymond.Chen@Sun.COM 	}
8049430SRaymond.Chen@Sun.COM 
8059430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_flags & HWAHC_WA_INITED) {
8069430SRaymond.Chen@Sun.COM 		/* deinitialize data transfer function related structure */
8079430SRaymond.Chen@Sun.COM 		wusb_wa_data_fini(&hwahcp->hwahc_wa_data);
8089430SRaymond.Chen@Sun.COM 	}
8099430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
8109430SRaymond.Chen@Sun.COM 
8119430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_flags & HWAHC_MINOR_NODE_CREATED) {
8129430SRaymond.Chen@Sun.COM 		/* remove all the minor nodes */
8139430SRaymond.Chen@Sun.COM 		ddi_remove_minor_node(dip, NULL);
8149430SRaymond.Chen@Sun.COM 	}
8159430SRaymond.Chen@Sun.COM 
8169430SRaymond.Chen@Sun.COM 	/* destroy mutex and cv */
8179430SRaymond.Chen@Sun.COM 	mutex_destroy(&hwahcp->hwahc_mutex);
8189430SRaymond.Chen@Sun.COM 	cv_destroy(&hwahcp->hwahc_result_thread_cv);
8199430SRaymond.Chen@Sun.COM 
8209430SRaymond.Chen@Sun.COM done:
8219430SRaymond.Chen@Sun.COM 	/* unregister the client driver from usba */
8229430SRaymond.Chen@Sun.COM 	usb_client_detach(dip, hwahcp->hwahc_dev_data);
8239430SRaymond.Chen@Sun.COM 
8249430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
8259430SRaymond.Chen@Sun.COM 	    "hwahc_cleanup: end");
8269430SRaymond.Chen@Sun.COM 
8279430SRaymond.Chen@Sun.COM 	usb_free_log_hdl(hwahcp->hwahc_log_handle);
8289430SRaymond.Chen@Sun.COM 
8299430SRaymond.Chen@Sun.COM 	/* remove all properties created */
8309430SRaymond.Chen@Sun.COM 	ddi_prop_remove_all(dip);
8319430SRaymond.Chen@Sun.COM 
8329430SRaymond.Chen@Sun.COM 	/* free the soft state information */
8339430SRaymond.Chen@Sun.COM 	ddi_soft_state_free(hwahc_statep, ddi_get_instance(dip));
8349430SRaymond.Chen@Sun.COM 
8359430SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
8369430SRaymond.Chen@Sun.COM }
8379430SRaymond.Chen@Sun.COM 
8389430SRaymond.Chen@Sun.COM 
8399430SRaymond.Chen@Sun.COM /*ARGSUSED*/
8409430SRaymond.Chen@Sun.COM static int
hwahc_open(dev_t * devp,int flag,int otyp,cred_t * cred_p)8419430SRaymond.Chen@Sun.COM hwahc_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
8429430SRaymond.Chen@Sun.COM {
8439430SRaymond.Chen@Sun.COM 	hwahc_state_t	*hwahcp;
8449430SRaymond.Chen@Sun.COM 
8459430SRaymond.Chen@Sun.COM 	if ((hwahcp = ddi_get_soft_state(hwahc_statep,
8469430SRaymond.Chen@Sun.COM 	    HWAHC_MINOR_TO_INSTANCE(getminor(*devp)))) == NULL) {
8479430SRaymond.Chen@Sun.COM 
8489430SRaymond.Chen@Sun.COM 		return (ENXIO);
8499430SRaymond.Chen@Sun.COM 	}
8509430SRaymond.Chen@Sun.COM 
8519430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_OPEN, hwahcp->hwahc_log_handle,
8529430SRaymond.Chen@Sun.COM 	    "hwahc_open: start");
8539430SRaymond.Chen@Sun.COM 
8549430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
8559430SRaymond.Chen@Sun.COM 	/* exclusive open */
8569430SRaymond.Chen@Sun.COM 	if ((flag & FEXCL) && (hwahcp->hwahc_open_count > 0)) {
8579430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
8589430SRaymond.Chen@Sun.COM 
8599430SRaymond.Chen@Sun.COM 		return (EBUSY);
8609430SRaymond.Chen@Sun.COM 	}
8619430SRaymond.Chen@Sun.COM 
8629430SRaymond.Chen@Sun.COM 	if ((hwahcp->hwahc_dev_state == USB_DEV_DISCONNECTED) ||
8639430SRaymond.Chen@Sun.COM 	    (hwahcp->hwahc_dev_state == USB_DEV_SUSPENDED)) {
8649430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
8659430SRaymond.Chen@Sun.COM 
8669430SRaymond.Chen@Sun.COM 		return (EIO);
8679430SRaymond.Chen@Sun.COM 	}
8689430SRaymond.Chen@Sun.COM 
8699430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_open_count++;
8709430SRaymond.Chen@Sun.COM 
8719430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
8729430SRaymond.Chen@Sun.COM 
8739430SRaymond.Chen@Sun.COM 	/* raise to full power and keep it until close */
8749430SRaymond.Chen@Sun.COM 	hwahc_pm_busy_component(hwahcp);
8759430SRaymond.Chen@Sun.COM 	(void) pm_raise_power(hwahcp->hwahc_dip, 0, USB_DEV_OS_FULL_PWR);
8769430SRaymond.Chen@Sun.COM 
8779430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_OPEN, hwahcp->hwahc_log_handle,
8789430SRaymond.Chen@Sun.COM 	    "hwahc_open: end");
8799430SRaymond.Chen@Sun.COM 
8809430SRaymond.Chen@Sun.COM 	return (0);
8819430SRaymond.Chen@Sun.COM }
8829430SRaymond.Chen@Sun.COM 
8839430SRaymond.Chen@Sun.COM 
8849430SRaymond.Chen@Sun.COM /*ARGSUSED*/
8859430SRaymond.Chen@Sun.COM static int
hwahc_close(dev_t dev,int flag,int otyp,cred_t * cred_p)8869430SRaymond.Chen@Sun.COM hwahc_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
8879430SRaymond.Chen@Sun.COM {
8889430SRaymond.Chen@Sun.COM 	hwahc_state_t	*hwahcp;
8899430SRaymond.Chen@Sun.COM 
8909430SRaymond.Chen@Sun.COM 	if ((hwahcp = ddi_get_soft_state(hwahc_statep,
8919430SRaymond.Chen@Sun.COM 	    HWAHC_MINOR_TO_INSTANCE(getminor(dev)))) == NULL) {
8929430SRaymond.Chen@Sun.COM 
8939430SRaymond.Chen@Sun.COM 		return (ENXIO);
8949430SRaymond.Chen@Sun.COM 	}
8959430SRaymond.Chen@Sun.COM 
8969430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, hwahcp->hwahc_log_handle,
8979430SRaymond.Chen@Sun.COM 	    "hwahc_close: start");
8989430SRaymond.Chen@Sun.COM 
8999430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
9009430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_open_count == 0) {
9019430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_CLOSE, hwahcp->hwahc_log_handle,
9029430SRaymond.Chen@Sun.COM 		    "hwahc_close: already closed");
9039430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
9049430SRaymond.Chen@Sun.COM 
9059430SRaymond.Chen@Sun.COM 		return (EINVAL);
9069430SRaymond.Chen@Sun.COM 	}
9079430SRaymond.Chen@Sun.COM 
9089430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_open_count--;
9099430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
9109430SRaymond.Chen@Sun.COM 
9119430SRaymond.Chen@Sun.COM 	hwahc_pm_idle_component(hwahcp);
9129430SRaymond.Chen@Sun.COM 
9139430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, hwahcp->hwahc_log_handle,
9149430SRaymond.Chen@Sun.COM 	    "hwahc_close: end");
9159430SRaymond.Chen@Sun.COM 
9169430SRaymond.Chen@Sun.COM 	return (0);
9179430SRaymond.Chen@Sun.COM }
9189430SRaymond.Chen@Sun.COM 
9199430SRaymond.Chen@Sun.COM /* retrieve port number from devctl data */
9209430SRaymond.Chen@Sun.COM static usb_port_t
hwahc_get_port_num(hwahc_state_t * hwahcp,struct devctl_iocdata * dcp)9219430SRaymond.Chen@Sun.COM hwahc_get_port_num(hwahc_state_t *hwahcp, struct devctl_iocdata *dcp)
9229430SRaymond.Chen@Sun.COM {
9239430SRaymond.Chen@Sun.COM 	int32_t port;
9249430SRaymond.Chen@Sun.COM 
9259430SRaymond.Chen@Sun.COM 	ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
9269430SRaymond.Chen@Sun.COM 
9279430SRaymond.Chen@Sun.COM 	/* Get which port to operate on.  */
9289430SRaymond.Chen@Sun.COM 	if (nvlist_lookup_int32(ndi_dc_get_ap_data(dcp), "port", &port) != 0) {
9299430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
9309430SRaymond.Chen@Sun.COM 		    "hwahc_get_port_num: port lookup failed");
9319430SRaymond.Chen@Sun.COM 		port = 0;
9329430SRaymond.Chen@Sun.COM 	}
9339430SRaymond.Chen@Sun.COM 
9349430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_CBOPS,  hwahcp->hwahc_log_handle,
9359430SRaymond.Chen@Sun.COM 	    "hwahc_get_port_num: hwahcp=0x%p, port=%d", (void *)hwahcp,
9369430SRaymond.Chen@Sun.COM 	    port);
9379430SRaymond.Chen@Sun.COM 
9389430SRaymond.Chen@Sun.COM 	return ((usb_port_t)port);
9399430SRaymond.Chen@Sun.COM }
9409430SRaymond.Chen@Sun.COM 
9419430SRaymond.Chen@Sun.COM /* return the child dip on a certain port */
9429430SRaymond.Chen@Sun.COM static dev_info_t *
hwahc_get_child_dip(hwahc_state_t * hwahcp,usb_port_t port)9439430SRaymond.Chen@Sun.COM hwahc_get_child_dip(hwahc_state_t *hwahcp, usb_port_t port)
9449430SRaymond.Chen@Sun.COM {
9459430SRaymond.Chen@Sun.COM 	wusb_hc_data_t		*hc_data;
9469430SRaymond.Chen@Sun.COM 	dev_info_t		*child_dip;
9479430SRaymond.Chen@Sun.COM 
9489430SRaymond.Chen@Sun.COM 	hc_data = &hwahcp->hwahc_hc_data;
9499430SRaymond.Chen@Sun.COM 
9509430SRaymond.Chen@Sun.COM 	/* check port range to prevent an illegal number */
9519430SRaymond.Chen@Sun.COM 	if (port > hc_data->hc_num_ports) {
9529430SRaymond.Chen@Sun.COM 		return (NULL);
9539430SRaymond.Chen@Sun.COM 	}
9549430SRaymond.Chen@Sun.COM 
9559430SRaymond.Chen@Sun.COM 	mutex_enter(&hc_data->hc_mutex);
9569430SRaymond.Chen@Sun.COM 	child_dip = hc_data->hc_children_dips[port];
9579430SRaymond.Chen@Sun.COM 	mutex_exit(&hc_data->hc_mutex);
9589430SRaymond.Chen@Sun.COM 
9599430SRaymond.Chen@Sun.COM 	return (child_dip);
9609430SRaymond.Chen@Sun.COM }
9619430SRaymond.Chen@Sun.COM 
9629430SRaymond.Chen@Sun.COM /*
9639430SRaymond.Chen@Sun.COM  * hwahc_cfgadm_state:
9649430SRaymond.Chen@Sun.COM  *
9659430SRaymond.Chen@Sun.COM  *	child_dip list		child_state		cfgadm_state
9669430SRaymond.Chen@Sun.COM  *	--------------		----------		------------
9679430SRaymond.Chen@Sun.COM  *	!= NULL			connected		configured or
9689430SRaymond.Chen@Sun.COM  *							unconfigured
9699430SRaymond.Chen@Sun.COM  *	!= NULL			not connected		disconnect but
9709430SRaymond.Chen@Sun.COM  *							busy/still referenced
9719430SRaymond.Chen@Sun.COM  *	NULL			connected		logically disconnected
9729430SRaymond.Chen@Sun.COM  *	NULL			not connected		empty
9739430SRaymond.Chen@Sun.COM  */
9749430SRaymond.Chen@Sun.COM static uint_t
hwahc_cfgadm_state(hwahc_state_t * hwahcp,usb_port_t port)9759430SRaymond.Chen@Sun.COM hwahc_cfgadm_state(hwahc_state_t *hwahcp, usb_port_t port)
9769430SRaymond.Chen@Sun.COM {
9779430SRaymond.Chen@Sun.COM 	uint_t		state;
9789430SRaymond.Chen@Sun.COM 	dev_info_t	*child_dip = hwahc_get_child_dip(hwahcp, port);
9799430SRaymond.Chen@Sun.COM 	wusb_hc_data_t	*hc_data = &hwahcp->hwahc_hc_data;
9809430SRaymond.Chen@Sun.COM 	wusb_dev_info_t	*dev_info;
9819430SRaymond.Chen@Sun.COM 
9829430SRaymond.Chen@Sun.COM 	if (child_dip == NULL) {
9839430SRaymond.Chen@Sun.COM 
9849430SRaymond.Chen@Sun.COM 		return (HWAHC_CFGADM_INVALID);
9859430SRaymond.Chen@Sun.COM 	}
9869430SRaymond.Chen@Sun.COM 
9879430SRaymond.Chen@Sun.COM 	mutex_enter(&hc_data->hc_mutex);
9889430SRaymond.Chen@Sun.COM 	dev_info = hc_data->hc_dev_infos[port];
9899430SRaymond.Chen@Sun.COM 	if (dev_info) {
9909430SRaymond.Chen@Sun.COM 		if (dev_info->wdev_state == WUSB_STATE_CONFIGURED) {
9919430SRaymond.Chen@Sun.COM 			if (child_dip &&
9929430SRaymond.Chen@Sun.COM 			    (DEVI_IS_DEVICE_OFFLINE(child_dip) ||
9939430SRaymond.Chen@Sun.COM 			    !i_ddi_devi_attached(child_dip))) {
9949430SRaymond.Chen@Sun.COM 				state = HWAHC_CFGADM_UNCONFIGURED;
9959430SRaymond.Chen@Sun.COM 			} else if (!child_dip) {
9969430SRaymond.Chen@Sun.COM 				state = HWAHC_CFGADM_UNCONFIGURED;
9979430SRaymond.Chen@Sun.COM 			} else {
9989430SRaymond.Chen@Sun.COM 				state = HWAHC_CFGADM_CONFIGURED;
9999430SRaymond.Chen@Sun.COM 			}
10009430SRaymond.Chen@Sun.COM 		} else if (dev_info->wdev_state == WUSB_STATE_UNCONNTED) {
10019430SRaymond.Chen@Sun.COM 			if (child_dip) {
10029430SRaymond.Chen@Sun.COM 				state = HWAHC_CFGADM_STILL_REFERENCED;
10039430SRaymond.Chen@Sun.COM 			} else {
10049430SRaymond.Chen@Sun.COM 				state = HWAHC_CFGADM_DISCONNECTED;
10059430SRaymond.Chen@Sun.COM 			}
10069430SRaymond.Chen@Sun.COM 		} else {
10079430SRaymond.Chen@Sun.COM 			if (child_dip) {
10089430SRaymond.Chen@Sun.COM 				state = HWAHC_CFGADM_STILL_REFERENCED;
10099430SRaymond.Chen@Sun.COM 			} else {
10109430SRaymond.Chen@Sun.COM 				state = HWAHC_CFGADM_UNCONFIGURED;
10119430SRaymond.Chen@Sun.COM 			}
10129430SRaymond.Chen@Sun.COM 		}
10139430SRaymond.Chen@Sun.COM 	} else {
10149430SRaymond.Chen@Sun.COM 		state = HWAHC_CFGADM_EMPTY;
10159430SRaymond.Chen@Sun.COM 	}
10169430SRaymond.Chen@Sun.COM 	mutex_exit(&hc_data->hc_mutex);
10179430SRaymond.Chen@Sun.COM 
10189430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_CBOPS,  hwahcp->hwahc_log_handle,
10199430SRaymond.Chen@Sun.COM 	    "hwahc_cfgadm_state: hwahcp=0x%p, port=%d state=0x%x",
10209430SRaymond.Chen@Sun.COM 	    (void *) hwahcp, port, state);
10219430SRaymond.Chen@Sun.COM 
10229430SRaymond.Chen@Sun.COM 	return (state);
10239430SRaymond.Chen@Sun.COM }
10249430SRaymond.Chen@Sun.COM 
10259430SRaymond.Chen@Sun.COM /* cfgadm ioctl support, now only implements list function */
10269430SRaymond.Chen@Sun.COM /* ARGSUSED */
10279430SRaymond.Chen@Sun.COM static int
hwahc_cfgadm_ioctl(hwahc_state_t * hwahcp,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)10289430SRaymond.Chen@Sun.COM hwahc_cfgadm_ioctl(hwahc_state_t *hwahcp, int cmd, intptr_t arg,
10299430SRaymond.Chen@Sun.COM 	int mode, cred_t *credp, int *rvalp)
10309430SRaymond.Chen@Sun.COM {
10319430SRaymond.Chen@Sun.COM 	dev_info_t		*rh_dip;
10329430SRaymond.Chen@Sun.COM 	dev_info_t		*child_dip;
10339430SRaymond.Chen@Sun.COM 	struct devctl_iocdata	*dcp = NULL;
10349430SRaymond.Chen@Sun.COM 	usb_port_t		port = 0;
10359430SRaymond.Chen@Sun.COM 	devctl_ap_state_t	ap_state;
10369430SRaymond.Chen@Sun.COM 	int			circ, rh_circ, prh_circ;
10379430SRaymond.Chen@Sun.COM 	int			rv = 0;
10389430SRaymond.Chen@Sun.COM 	char			*msg;
10399430SRaymond.Chen@Sun.COM 
10409430SRaymond.Chen@Sun.COM 	/* read devctl ioctl data */
10419430SRaymond.Chen@Sun.COM 	if ((cmd != DEVCTL_AP_CONTROL) &&
10429430SRaymond.Chen@Sun.COM 	    (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)) {
10439430SRaymond.Chen@Sun.COM 
10449430SRaymond.Chen@Sun.COM 		return (EFAULT);
10459430SRaymond.Chen@Sun.COM 	}
10469430SRaymond.Chen@Sun.COM 
10479430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
10489430SRaymond.Chen@Sun.COM 
10499430SRaymond.Chen@Sun.COM 	rh_dip = hwahcp->hwahc_hubd->h_usba_device->usb_root_hub_dip;
10509430SRaymond.Chen@Sun.COM 
10519430SRaymond.Chen@Sun.COM 	switch (cmd) {
10529430SRaymond.Chen@Sun.COM 	case DEVCTL_AP_DISCONNECT:
10539430SRaymond.Chen@Sun.COM 	case DEVCTL_AP_UNCONFIGURE:
10549430SRaymond.Chen@Sun.COM 	case DEVCTL_AP_CONFIGURE:
10559430SRaymond.Chen@Sun.COM 		if (hwahcp->hwahc_dev_state == USB_DEV_DISCONNECTED) {
10569430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_ATTA,
10579430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
10589430SRaymond.Chen@Sun.COM 			    "hwahc_cfgadm_ioctl: dev already gone");
10599430SRaymond.Chen@Sun.COM 			mutex_exit(&hwahcp->hwahc_mutex);
10609430SRaymond.Chen@Sun.COM 			if (dcp) {
10619430SRaymond.Chen@Sun.COM 				ndi_dc_freehdl(dcp);
10629430SRaymond.Chen@Sun.COM 			}
10639430SRaymond.Chen@Sun.COM 
10649430SRaymond.Chen@Sun.COM 			return (EIO);
10659430SRaymond.Chen@Sun.COM 		}
10669430SRaymond.Chen@Sun.COM 		/* FALLTHROUGH */
10679430SRaymond.Chen@Sun.COM 	case DEVCTL_AP_GETSTATE:
10689430SRaymond.Chen@Sun.COM 		if ((port = hwahc_get_port_num(hwahcp, dcp)) == 0) {
10699430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_ATTA,
10709430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
10719430SRaymond.Chen@Sun.COM 			    "hwahc_cfgadm_ioctl: bad port");
10729430SRaymond.Chen@Sun.COM 			mutex_exit(&hwahcp->hwahc_mutex);
10739430SRaymond.Chen@Sun.COM 			if (dcp) {
10749430SRaymond.Chen@Sun.COM 				ndi_dc_freehdl(dcp);
10759430SRaymond.Chen@Sun.COM 			}
10769430SRaymond.Chen@Sun.COM 
10779430SRaymond.Chen@Sun.COM 			return (EINVAL);
10789430SRaymond.Chen@Sun.COM 		}
10799430SRaymond.Chen@Sun.COM 		break;
10809430SRaymond.Chen@Sun.COM 	case DEVCTL_AP_CONTROL:
10819430SRaymond.Chen@Sun.COM 
10829430SRaymond.Chen@Sun.COM 		break;
10839430SRaymond.Chen@Sun.COM 	default:
10849430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
10859430SRaymond.Chen@Sun.COM 		if (dcp) {
10869430SRaymond.Chen@Sun.COM 			ndi_dc_freehdl(dcp);
10879430SRaymond.Chen@Sun.COM 		}
10889430SRaymond.Chen@Sun.COM 
10899430SRaymond.Chen@Sun.COM 		return (ENOTTY);
10909430SRaymond.Chen@Sun.COM 	}
10919430SRaymond.Chen@Sun.COM 
10929430SRaymond.Chen@Sun.COM 	/* should not happen, just in case */
10939430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_dev_state == USB_DEV_SUSPENDED) {
10949430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
10959430SRaymond.Chen@Sun.COM 		if (dcp) {
10969430SRaymond.Chen@Sun.COM 			ndi_dc_freehdl(dcp);
10979430SRaymond.Chen@Sun.COM 		}
10989430SRaymond.Chen@Sun.COM 
10999430SRaymond.Chen@Sun.COM 		return (EIO);
11009430SRaymond.Chen@Sun.COM 	}
11019430SRaymond.Chen@Sun.COM 
11029430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
11039430SRaymond.Chen@Sun.COM 
11049430SRaymond.Chen@Sun.COM 	ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
11059430SRaymond.Chen@Sun.COM 	ndi_devi_enter(rh_dip, &rh_circ);
11069430SRaymond.Chen@Sun.COM 	ndi_devi_enter(hwahcp->hwahc_dip, &circ);
11079430SRaymond.Chen@Sun.COM 
11089430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
11099430SRaymond.Chen@Sun.COM 
11109430SRaymond.Chen@Sun.COM 	switch (cmd) {
11119430SRaymond.Chen@Sun.COM 	case DEVCTL_AP_DISCONNECT:
11129430SRaymond.Chen@Sun.COM 		/* TODO: not supported now */
11139430SRaymond.Chen@Sun.COM 		rv = EIO;
11149430SRaymond.Chen@Sun.COM 		break;
11159430SRaymond.Chen@Sun.COM 	case DEVCTL_AP_UNCONFIGURE:
11169430SRaymond.Chen@Sun.COM 		/* TODO: not supported now */
11179430SRaymond.Chen@Sun.COM 		rv = EIO;
11189430SRaymond.Chen@Sun.COM 		break;
11199430SRaymond.Chen@Sun.COM 	case DEVCTL_AP_CONFIGURE:
11209430SRaymond.Chen@Sun.COM 		/* TODO: not supported now */
11219430SRaymond.Chen@Sun.COM 		rv = EIO;
11229430SRaymond.Chen@Sun.COM 		break;
11239430SRaymond.Chen@Sun.COM 	case DEVCTL_AP_GETSTATE:
11249430SRaymond.Chen@Sun.COM 		switch (hwahc_cfgadm_state(hwahcp, port)) {
11259430SRaymond.Chen@Sun.COM 		case HWAHC_CFGADM_DISCONNECTED:
11269430SRaymond.Chen@Sun.COM 			/* port previously 'disconnected' by cfgadm */
11279430SRaymond.Chen@Sun.COM 			ap_state.ap_rstate = AP_RSTATE_DISCONNECTED;
11289430SRaymond.Chen@Sun.COM 			ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED;
11299430SRaymond.Chen@Sun.COM 			ap_state.ap_condition = AP_COND_OK;
11309430SRaymond.Chen@Sun.COM 
11319430SRaymond.Chen@Sun.COM 			break;
11329430SRaymond.Chen@Sun.COM 		case HWAHC_CFGADM_UNCONFIGURED:
11339430SRaymond.Chen@Sun.COM 			ap_state.ap_rstate = AP_RSTATE_CONNECTED;
11349430SRaymond.Chen@Sun.COM 			ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED;
11359430SRaymond.Chen@Sun.COM 			ap_state.ap_condition = AP_COND_OK;
11369430SRaymond.Chen@Sun.COM 
11379430SRaymond.Chen@Sun.COM 			break;
11389430SRaymond.Chen@Sun.COM 		case HWAHC_CFGADM_CONFIGURED:
11399430SRaymond.Chen@Sun.COM 			ap_state.ap_rstate = AP_RSTATE_CONNECTED;
11409430SRaymond.Chen@Sun.COM 			ap_state.ap_ostate = AP_OSTATE_CONFIGURED;
11419430SRaymond.Chen@Sun.COM 			ap_state.ap_condition = AP_COND_OK;
11429430SRaymond.Chen@Sun.COM 
11439430SRaymond.Chen@Sun.COM 			break;
11449430SRaymond.Chen@Sun.COM 		case HWAHC_CFGADM_STILL_REFERENCED:
11459430SRaymond.Chen@Sun.COM 			ap_state.ap_rstate = AP_RSTATE_EMPTY;
11469430SRaymond.Chen@Sun.COM 			ap_state.ap_ostate = AP_OSTATE_CONFIGURED;
11479430SRaymond.Chen@Sun.COM 			ap_state.ap_condition = AP_COND_UNUSABLE;
11489430SRaymond.Chen@Sun.COM 
11499430SRaymond.Chen@Sun.COM 			break;
11509430SRaymond.Chen@Sun.COM 		case HWAHC_CFGADM_EMPTY:
11519430SRaymond.Chen@Sun.COM 		default:
11529430SRaymond.Chen@Sun.COM 			ap_state.ap_rstate = AP_RSTATE_EMPTY;
11539430SRaymond.Chen@Sun.COM 			ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED;
11549430SRaymond.Chen@Sun.COM 			ap_state.ap_condition = AP_COND_OK;
11559430SRaymond.Chen@Sun.COM 
11569430SRaymond.Chen@Sun.COM 			break;
11579430SRaymond.Chen@Sun.COM 		}
11589430SRaymond.Chen@Sun.COM 
11599430SRaymond.Chen@Sun.COM 		ap_state.ap_last_change = (time_t)-1;
11609430SRaymond.Chen@Sun.COM 		ap_state.ap_error_code = 0;
11619430SRaymond.Chen@Sun.COM 		ap_state.ap_in_transition = 0;
11629430SRaymond.Chen@Sun.COM 
11639430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
11649430SRaymond.Chen@Sun.COM 		    "DEVCTL_AP_GETSTATE: "
11659430SRaymond.Chen@Sun.COM 		    "ostate=0x%x, rstate=0x%x, condition=0x%x",
11669430SRaymond.Chen@Sun.COM 		    ap_state.ap_ostate,
11679430SRaymond.Chen@Sun.COM 		    ap_state.ap_rstate, ap_state.ap_condition);
11689430SRaymond.Chen@Sun.COM 
11699430SRaymond.Chen@Sun.COM 		/* copy the return-AP-state information to the user space */
11709430SRaymond.Chen@Sun.COM 		if (ndi_dc_return_ap_state(&ap_state, dcp) != NDI_SUCCESS) {
11719430SRaymond.Chen@Sun.COM 			rv = EFAULT;
11729430SRaymond.Chen@Sun.COM 		}
11739430SRaymond.Chen@Sun.COM 
11749430SRaymond.Chen@Sun.COM 		break;
11759430SRaymond.Chen@Sun.COM 	case DEVCTL_AP_CONTROL:
11769430SRaymond.Chen@Sun.COM 	{
11779430SRaymond.Chen@Sun.COM 		/*
11789430SRaymond.Chen@Sun.COM 		 * Generic devctl for hardware-specific functionality.
11799430SRaymond.Chen@Sun.COM 		 * For list of sub-commands see hubd_impl.h
11809430SRaymond.Chen@Sun.COM 		 */
11819430SRaymond.Chen@Sun.COM 		hubd_ioctl_data_t	ioc;	/* for 64 byte copies */
11829430SRaymond.Chen@Sun.COM 
11839430SRaymond.Chen@Sun.COM 		/* copy user ioctl data in first */
11849430SRaymond.Chen@Sun.COM #ifdef _MULTI_DATAMODEL
11859430SRaymond.Chen@Sun.COM 		if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
11869430SRaymond.Chen@Sun.COM 			hubd_ioctl_data_32_t ioc32;
11879430SRaymond.Chen@Sun.COM 
11889430SRaymond.Chen@Sun.COM 			if (ddi_copyin((void *)arg, (void *)&ioc32,
11899430SRaymond.Chen@Sun.COM 			    sizeof (ioc32), mode) != 0) {
11909430SRaymond.Chen@Sun.COM 				rv = EFAULT;
11919430SRaymond.Chen@Sun.COM 
11929430SRaymond.Chen@Sun.COM 				break;
11939430SRaymond.Chen@Sun.COM 			}
11949430SRaymond.Chen@Sun.COM 			ioc.cmd		= (uint_t)ioc32.cmd;
11959430SRaymond.Chen@Sun.COM 			ioc.port	= (uint_t)ioc32.port;
11969430SRaymond.Chen@Sun.COM 			ioc.get_size	= (uint_t)ioc32.get_size;
11979430SRaymond.Chen@Sun.COM 			ioc.buf		= (caddr_t)(uintptr_t)ioc32.buf;
11989430SRaymond.Chen@Sun.COM 			ioc.bufsiz	= (uint_t)ioc32.bufsiz;
11999430SRaymond.Chen@Sun.COM 			ioc.misc_arg	= (uint_t)ioc32.misc_arg;
12009430SRaymond.Chen@Sun.COM 		} else
12019430SRaymond.Chen@Sun.COM #endif /* _MULTI_DATAMODEL */
12029430SRaymond.Chen@Sun.COM 		if (ddi_copyin((void *)arg, (void *)&ioc, sizeof (ioc),
12039430SRaymond.Chen@Sun.COM 		    mode) != 0) {
12049430SRaymond.Chen@Sun.COM 			rv = EFAULT;
12059430SRaymond.Chen@Sun.COM 
12069430SRaymond.Chen@Sun.COM 			break;
12079430SRaymond.Chen@Sun.COM 		}
12089430SRaymond.Chen@Sun.COM 
12099430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
12109430SRaymond.Chen@Sun.COM 		    "DEVCTL_AP_CONTROL: ioc: cmd=0x%x port=%d get_size=%d"
12119430SRaymond.Chen@Sun.COM 		    "\n\tbuf=0x%p, bufsiz=%d,  misc_arg=%d", ioc.cmd,
12129430SRaymond.Chen@Sun.COM 		    ioc.port, ioc.get_size, (void *) ioc.buf, ioc.bufsiz,
12139430SRaymond.Chen@Sun.COM 		    ioc.misc_arg);
12149430SRaymond.Chen@Sun.COM 
12159430SRaymond.Chen@Sun.COM 		/*
12169430SRaymond.Chen@Sun.COM 		 * To avoid BE/LE and 32/64 issues, a get_size always
12179430SRaymond.Chen@Sun.COM 		 * returns a 32-bit number.
12189430SRaymond.Chen@Sun.COM 		 */
12199430SRaymond.Chen@Sun.COM 		if (ioc.get_size != 0 && ioc.bufsiz != (sizeof (uint32_t))) {
12209430SRaymond.Chen@Sun.COM 			rv = EINVAL;
12219430SRaymond.Chen@Sun.COM 
12229430SRaymond.Chen@Sun.COM 			break;
12239430SRaymond.Chen@Sun.COM 		}
12249430SRaymond.Chen@Sun.COM 
12259430SRaymond.Chen@Sun.COM 		switch (ioc.cmd) {
12269430SRaymond.Chen@Sun.COM 		case USB_DESCR_TYPE_DEV:
12279430SRaymond.Chen@Sun.COM 			msg = "DEVCTL_AP_CONTROL: GET_DEVICE_DESC";
12289430SRaymond.Chen@Sun.COM 			if (ioc.get_size) {
12299430SRaymond.Chen@Sun.COM 				/* uint32 so this works 32/64 */
12309430SRaymond.Chen@Sun.COM 				uint32_t size = sizeof (usb_dev_descr_t);
12319430SRaymond.Chen@Sun.COM 
12329430SRaymond.Chen@Sun.COM 				if (ddi_copyout((void *)&size, ioc.buf,
12339430SRaymond.Chen@Sun.COM 				    ioc.bufsiz, mode) != 0) {
12349430SRaymond.Chen@Sun.COM 					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
12359430SRaymond.Chen@Sun.COM 					    hwahcp->hwahc_log_handle,
12369430SRaymond.Chen@Sun.COM 					    "%s: get_size copyout failed", msg);
12379430SRaymond.Chen@Sun.COM 					rv = EIO;
12389430SRaymond.Chen@Sun.COM 
12399430SRaymond.Chen@Sun.COM 					break;
12409430SRaymond.Chen@Sun.COM 				}
12419430SRaymond.Chen@Sun.COM 			} else {	/* send out the actual descr */
12429430SRaymond.Chen@Sun.COM 				usb_dev_descr_t *dev_descrp;
12439430SRaymond.Chen@Sun.COM 
12449430SRaymond.Chen@Sun.COM 				/* check child_dip */
12459430SRaymond.Chen@Sun.COM 				if ((child_dip = hwahc_get_child_dip(hwahcp,
12469430SRaymond.Chen@Sun.COM 				    ioc.port)) == NULL) {
12479430SRaymond.Chen@Sun.COM 					rv = EINVAL;
12489430SRaymond.Chen@Sun.COM 
12499430SRaymond.Chen@Sun.COM 					break;
12509430SRaymond.Chen@Sun.COM 				}
12519430SRaymond.Chen@Sun.COM 
12529430SRaymond.Chen@Sun.COM 				dev_descrp = usb_get_dev_descr(child_dip);
12539430SRaymond.Chen@Sun.COM 				if (ioc.bufsiz != sizeof (*dev_descrp)) {
12549430SRaymond.Chen@Sun.COM 					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
12559430SRaymond.Chen@Sun.COM 					    hwahcp->hwahc_log_handle,
12569430SRaymond.Chen@Sun.COM 					    "%s: bufsize passed (%d) != sizeof "
12579430SRaymond.Chen@Sun.COM 					    "usba_device_descr_t (%d)", msg,
12589430SRaymond.Chen@Sun.COM 					    ioc.bufsiz, dev_descrp->bLength);
12599430SRaymond.Chen@Sun.COM 					rv = EINVAL;
12609430SRaymond.Chen@Sun.COM 
12619430SRaymond.Chen@Sun.COM 					break;
12629430SRaymond.Chen@Sun.COM 				}
12639430SRaymond.Chen@Sun.COM 
12649430SRaymond.Chen@Sun.COM 				if (ddi_copyout((void *)dev_descrp,
12659430SRaymond.Chen@Sun.COM 				    ioc.buf, ioc.bufsiz, mode) != 0) {
12669430SRaymond.Chen@Sun.COM 					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
12679430SRaymond.Chen@Sun.COM 					    hwahcp->hwahc_log_handle,
12689430SRaymond.Chen@Sun.COM 					    "%s: copyout failed.", msg);
12699430SRaymond.Chen@Sun.COM 					rv = EIO;
12709430SRaymond.Chen@Sun.COM 
12719430SRaymond.Chen@Sun.COM 					break;
12729430SRaymond.Chen@Sun.COM 				}
12739430SRaymond.Chen@Sun.COM 			}
12749430SRaymond.Chen@Sun.COM 			break;
12759430SRaymond.Chen@Sun.COM 		case USB_DESCR_TYPE_CFG:
12769430SRaymond.Chen@Sun.COM 		{
12779430SRaymond.Chen@Sun.COM 			usba_device_t	*child_ud = NULL;
12789430SRaymond.Chen@Sun.COM 			uint32_t	idx = ioc.misc_arg;
12799430SRaymond.Chen@Sun.COM 			uint32_t	cfg_len = 0;
12809430SRaymond.Chen@Sun.COM 
12819430SRaymond.Chen@Sun.COM 			if ((child_dip =
12829430SRaymond.Chen@Sun.COM 			    hwahc_get_child_dip(hwahcp, ioc.port)) == NULL) {
12839430SRaymond.Chen@Sun.COM 				rv = EINVAL;
12849430SRaymond.Chen@Sun.COM 
12859430SRaymond.Chen@Sun.COM 				break;
12869430SRaymond.Chen@Sun.COM 			}
12879430SRaymond.Chen@Sun.COM 			child_ud = usba_get_usba_device(child_dip);
12889430SRaymond.Chen@Sun.COM 			cfg_len = (uint32_t)child_ud->usb_cfg_array_len[idx];
12899430SRaymond.Chen@Sun.COM 
12909430SRaymond.Chen@Sun.COM 			msg = "DEVCTL_AP_CONTROL: GET_CONFIG_DESC";
12919430SRaymond.Chen@Sun.COM 			if (ioc.get_size) {
12929430SRaymond.Chen@Sun.COM 				if (ddi_copyout((void *)&cfg_len, ioc.buf,
12939430SRaymond.Chen@Sun.COM 				    ioc.bufsiz, mode) != 0) {
12949430SRaymond.Chen@Sun.COM 					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
12959430SRaymond.Chen@Sun.COM 					    hwahcp->hwahc_log_handle,
12969430SRaymond.Chen@Sun.COM 					    "%s: get_size copyout failed", msg);
12979430SRaymond.Chen@Sun.COM 					rv = EIO;
12989430SRaymond.Chen@Sun.COM 
12999430SRaymond.Chen@Sun.COM 					break;
13009430SRaymond.Chen@Sun.COM 				}
13019430SRaymond.Chen@Sun.COM 			} else {	/* send out the actual descr */
13029430SRaymond.Chen@Sun.COM 				uchar_t *cfg_descr =
13039430SRaymond.Chen@Sun.COM 				    child_ud->usb_cfg_array[idx];
13049430SRaymond.Chen@Sun.COM 
13059430SRaymond.Chen@Sun.COM 				if (ioc.bufsiz != cfg_len) {
13069430SRaymond.Chen@Sun.COM 					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
13079430SRaymond.Chen@Sun.COM 					    hwahcp->hwahc_log_handle,
13089430SRaymond.Chen@Sun.COM 					    "%s: bufsize passed (%d) != size "
13099430SRaymond.Chen@Sun.COM 					    "of cfg_descr (%d)", msg,
13109430SRaymond.Chen@Sun.COM 					    ioc.bufsiz, cfg_len);
13119430SRaymond.Chen@Sun.COM 					rv = EINVAL;
13129430SRaymond.Chen@Sun.COM 
13139430SRaymond.Chen@Sun.COM 					break;
13149430SRaymond.Chen@Sun.COM 				}
13159430SRaymond.Chen@Sun.COM 
13169430SRaymond.Chen@Sun.COM 				if (ddi_copyout((void *)cfg_descr,
13179430SRaymond.Chen@Sun.COM 				    ioc.buf, ioc.bufsiz, mode) != 0) {
13189430SRaymond.Chen@Sun.COM 					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
13199430SRaymond.Chen@Sun.COM 					    hwahcp->hwahc_log_handle,
13209430SRaymond.Chen@Sun.COM 					    "%s: copyout failed.", msg);
13219430SRaymond.Chen@Sun.COM 					rv = EIO;
13229430SRaymond.Chen@Sun.COM 
13239430SRaymond.Chen@Sun.COM 					break;
13249430SRaymond.Chen@Sun.COM 				}
13259430SRaymond.Chen@Sun.COM 			}
13269430SRaymond.Chen@Sun.COM 			break;
13279430SRaymond.Chen@Sun.COM 		}
13289430SRaymond.Chen@Sun.COM 		case USB_DESCR_TYPE_STRING:
13299430SRaymond.Chen@Sun.COM 		{
13309430SRaymond.Chen@Sun.COM 			char		*str;
13319430SRaymond.Chen@Sun.COM 			uint32_t	size;
13329430SRaymond.Chen@Sun.COM 			usba_device_t	*usba_device;
13339430SRaymond.Chen@Sun.COM 
13349430SRaymond.Chen@Sun.COM 			msg = "DEVCTL_AP_CONTROL: GET_STRING_DESCR";
13359430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L4(PRINT_MASK_CBOPS,
13369430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
13379430SRaymond.Chen@Sun.COM 			    "%s: string request: %d", msg, ioc.misc_arg);
13389430SRaymond.Chen@Sun.COM 
13399430SRaymond.Chen@Sun.COM 			/* recheck */
13409430SRaymond.Chen@Sun.COM 			if ((child_dip =
13419430SRaymond.Chen@Sun.COM 			    hwahc_get_child_dip(hwahcp, ioc.port)) == NULL) {
13429430SRaymond.Chen@Sun.COM 				rv = EINVAL;
13439430SRaymond.Chen@Sun.COM 
13449430SRaymond.Chen@Sun.COM 				break;
13459430SRaymond.Chen@Sun.COM 			}
13469430SRaymond.Chen@Sun.COM 			usba_device = usba_get_usba_device(child_dip);
13479430SRaymond.Chen@Sun.COM 
13489430SRaymond.Chen@Sun.COM 			switch (ioc.misc_arg) {
13499430SRaymond.Chen@Sun.COM 			case HUBD_MFG_STR:
13509430SRaymond.Chen@Sun.COM 				str = usba_device->usb_mfg_str;
13519430SRaymond.Chen@Sun.COM 
13529430SRaymond.Chen@Sun.COM 				break;
13539430SRaymond.Chen@Sun.COM 			case HUBD_PRODUCT_STR:
13549430SRaymond.Chen@Sun.COM 				str = usba_device->usb_product_str;
13559430SRaymond.Chen@Sun.COM 
13569430SRaymond.Chen@Sun.COM 				break;
13579430SRaymond.Chen@Sun.COM 			case HUBD_SERIALNO_STR:
13589430SRaymond.Chen@Sun.COM 				str = usba_device->usb_serialno_str;
13599430SRaymond.Chen@Sun.COM 
13609430SRaymond.Chen@Sun.COM 				break;
13619430SRaymond.Chen@Sun.COM 			case HUBD_CFG_DESCR_STR:
13629430SRaymond.Chen@Sun.COM 				mutex_enter(&usba_device->usb_mutex);
13639430SRaymond.Chen@Sun.COM 				str = usba_device->usb_cfg_str_descr[
13649430SRaymond.Chen@Sun.COM 				    usba_device->usb_active_cfg_ndx];
13659430SRaymond.Chen@Sun.COM 				mutex_exit(&usba_device->usb_mutex);
13669430SRaymond.Chen@Sun.COM 
13679430SRaymond.Chen@Sun.COM 				break;
13689430SRaymond.Chen@Sun.COM 			default:
13699430SRaymond.Chen@Sun.COM 				USB_DPRINTF_L2(PRINT_MASK_CBOPS,
13709430SRaymond.Chen@Sun.COM 				    hwahcp->hwahc_log_handle,
13719430SRaymond.Chen@Sun.COM 				    "%s: Invalid string request", msg);
13729430SRaymond.Chen@Sun.COM 				rv = EINVAL;
13739430SRaymond.Chen@Sun.COM 
13749430SRaymond.Chen@Sun.COM 				break;
13759430SRaymond.Chen@Sun.COM 			} /* end of switch */
13769430SRaymond.Chen@Sun.COM 
13779430SRaymond.Chen@Sun.COM 			if (rv != 0) {
13789430SRaymond.Chen@Sun.COM 
13799430SRaymond.Chen@Sun.COM 				break;
13809430SRaymond.Chen@Sun.COM 			}
13819430SRaymond.Chen@Sun.COM 
13829430SRaymond.Chen@Sun.COM 			size = (str != NULL) ? strlen(str) + 1 : 0;
13839430SRaymond.Chen@Sun.COM 			if (ioc.get_size) {
13849430SRaymond.Chen@Sun.COM 				if (ddi_copyout((void *)&size, ioc.buf,
13859430SRaymond.Chen@Sun.COM 				    ioc.bufsiz, mode) != 0) {
13869430SRaymond.Chen@Sun.COM 					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
13879430SRaymond.Chen@Sun.COM 					    hwahcp->hwahc_log_handle,
13889430SRaymond.Chen@Sun.COM 					    "%s: copyout of size failed.", msg);
13899430SRaymond.Chen@Sun.COM 					rv = EIO;
13909430SRaymond.Chen@Sun.COM 
13919430SRaymond.Chen@Sun.COM 					break;
13929430SRaymond.Chen@Sun.COM 				}
13939430SRaymond.Chen@Sun.COM 			} else {
13949430SRaymond.Chen@Sun.COM 				if (size == 0) {
13959430SRaymond.Chen@Sun.COM 					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
13969430SRaymond.Chen@Sun.COM 					    hwahcp->hwahc_log_handle,
13979430SRaymond.Chen@Sun.COM 					    "%s: String is NULL", msg);
13989430SRaymond.Chen@Sun.COM 					rv = EINVAL;
13999430SRaymond.Chen@Sun.COM 
14009430SRaymond.Chen@Sun.COM 					break;
14019430SRaymond.Chen@Sun.COM 				}
14029430SRaymond.Chen@Sun.COM 
14039430SRaymond.Chen@Sun.COM 				if (ioc.bufsiz != size) {
14049430SRaymond.Chen@Sun.COM 					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
14059430SRaymond.Chen@Sun.COM 					    hwahcp->hwahc_log_handle,
14069430SRaymond.Chen@Sun.COM 					    "%s: string buf size wrong", msg);
14079430SRaymond.Chen@Sun.COM 					rv = EINVAL;
14089430SRaymond.Chen@Sun.COM 
14099430SRaymond.Chen@Sun.COM 					break;
14109430SRaymond.Chen@Sun.COM 				}
14119430SRaymond.Chen@Sun.COM 
14129430SRaymond.Chen@Sun.COM 				if (ddi_copyout((void *)str, ioc.buf,
14139430SRaymond.Chen@Sun.COM 				    ioc.bufsiz, mode) != 0) {
14149430SRaymond.Chen@Sun.COM 					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
14159430SRaymond.Chen@Sun.COM 					    hwahcp->hwahc_log_handle,
14169430SRaymond.Chen@Sun.COM 					    "%s: copyout failed.", msg);
14179430SRaymond.Chen@Sun.COM 					rv = EIO;
14189430SRaymond.Chen@Sun.COM 
14199430SRaymond.Chen@Sun.COM 					break;
14209430SRaymond.Chen@Sun.COM 				}
14219430SRaymond.Chen@Sun.COM 			}
14229430SRaymond.Chen@Sun.COM 			break;
14239430SRaymond.Chen@Sun.COM 		}
14249430SRaymond.Chen@Sun.COM 		case HUBD_GET_CFGADM_NAME:
14259430SRaymond.Chen@Sun.COM 		{
14269430SRaymond.Chen@Sun.COM 			uint32_t   name_len;
14279430SRaymond.Chen@Sun.COM 			const char *name;
14289430SRaymond.Chen@Sun.COM 
14299430SRaymond.Chen@Sun.COM 			/* recheck */
14309430SRaymond.Chen@Sun.COM 			if ((child_dip =
14319430SRaymond.Chen@Sun.COM 			    hwahc_get_child_dip(hwahcp, ioc.port)) == NULL) {
14329430SRaymond.Chen@Sun.COM 				rv = EINVAL;
14339430SRaymond.Chen@Sun.COM 
14349430SRaymond.Chen@Sun.COM 				break;
14359430SRaymond.Chen@Sun.COM 			}
14369430SRaymond.Chen@Sun.COM 			name = ddi_node_name(child_dip);
14379430SRaymond.Chen@Sun.COM 			if (name == NULL) {
14389430SRaymond.Chen@Sun.COM 				name = "unsupported";
14399430SRaymond.Chen@Sun.COM 			}
14409430SRaymond.Chen@Sun.COM 			name_len = strlen(name) + 1;
14419430SRaymond.Chen@Sun.COM 
14429430SRaymond.Chen@Sun.COM 			msg = "DEVCTL_AP_CONTROL: HUBD_GET_CFGADM_NAME";
14439430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L4(PRINT_MASK_CBOPS,
14449430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
14459430SRaymond.Chen@Sun.COM 			    "%s: name=%s name_len=%d", msg, name, name_len);
14469430SRaymond.Chen@Sun.COM 
14479430SRaymond.Chen@Sun.COM 			if (ioc.get_size) {
14489430SRaymond.Chen@Sun.COM 				if (ddi_copyout((void *)&name_len,
14499430SRaymond.Chen@Sun.COM 				    ioc.buf, ioc.bufsiz, mode) != 0) {
14509430SRaymond.Chen@Sun.COM 					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
14519430SRaymond.Chen@Sun.COM 					    hwahcp->hwahc_log_handle,
14529430SRaymond.Chen@Sun.COM 					    "%s: copyout of size failed", msg);
14539430SRaymond.Chen@Sun.COM 					rv = EIO;
14549430SRaymond.Chen@Sun.COM 
14559430SRaymond.Chen@Sun.COM 					break;
14569430SRaymond.Chen@Sun.COM 				}
14579430SRaymond.Chen@Sun.COM 			} else {
14589430SRaymond.Chen@Sun.COM 				if (ioc.bufsiz != name_len) {
14599430SRaymond.Chen@Sun.COM 					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
14609430SRaymond.Chen@Sun.COM 					    hwahcp->hwahc_log_handle,
14619430SRaymond.Chen@Sun.COM 					    "%s: string buf length wrong", msg);
14629430SRaymond.Chen@Sun.COM 					rv = EINVAL;
14639430SRaymond.Chen@Sun.COM 
14649430SRaymond.Chen@Sun.COM 					break;
14659430SRaymond.Chen@Sun.COM 				}
14669430SRaymond.Chen@Sun.COM 
14679430SRaymond.Chen@Sun.COM 				if (ddi_copyout((void *)name, ioc.buf,
14689430SRaymond.Chen@Sun.COM 				    ioc.bufsiz, mode) != 0) {
14699430SRaymond.Chen@Sun.COM 					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
14709430SRaymond.Chen@Sun.COM 					    hwahcp->hwahc_log_handle,
14719430SRaymond.Chen@Sun.COM 					    "%s: copyout failed.", msg);
14729430SRaymond.Chen@Sun.COM 					rv = EIO;
14739430SRaymond.Chen@Sun.COM 
14749430SRaymond.Chen@Sun.COM 					break;
14759430SRaymond.Chen@Sun.COM 				}
14769430SRaymond.Chen@Sun.COM 			}
14779430SRaymond.Chen@Sun.COM 
14789430SRaymond.Chen@Sun.COM 			break;
14799430SRaymond.Chen@Sun.COM 		}
14809430SRaymond.Chen@Sun.COM 
14819430SRaymond.Chen@Sun.COM 		/*
14829430SRaymond.Chen@Sun.COM 		 * Return the config index for the currently-configured
14839430SRaymond.Chen@Sun.COM 		 * configuration.
14849430SRaymond.Chen@Sun.COM 		 */
14859430SRaymond.Chen@Sun.COM 		case HUBD_GET_CURRENT_CONFIG:
14869430SRaymond.Chen@Sun.COM 		{
14879430SRaymond.Chen@Sun.COM 			uint_t		config_index;
14889430SRaymond.Chen@Sun.COM 			uint32_t	size = sizeof (config_index);
14899430SRaymond.Chen@Sun.COM 			usba_device_t	*usba_device;
14909430SRaymond.Chen@Sun.COM 
14919430SRaymond.Chen@Sun.COM 			msg = "DEVCTL_AP_CONTROL: GET_CURRENT_CONFIG";
14929430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L4(PRINT_MASK_CBOPS,
14939430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle, "%s", msg);
14949430SRaymond.Chen@Sun.COM 
14959430SRaymond.Chen@Sun.COM 			/*
14969430SRaymond.Chen@Sun.COM 			 * Return the config index for the configuration
14979430SRaymond.Chen@Sun.COM 			 * currently in use.
14989430SRaymond.Chen@Sun.COM 			 * Recheck if child_dip exists
14999430SRaymond.Chen@Sun.COM 			 */
15009430SRaymond.Chen@Sun.COM 			if ((child_dip =
15019430SRaymond.Chen@Sun.COM 			    hwahc_get_child_dip(hwahcp, ioc.port)) == NULL) {
15029430SRaymond.Chen@Sun.COM 				rv = EINVAL;
15039430SRaymond.Chen@Sun.COM 
15049430SRaymond.Chen@Sun.COM 				break;
15059430SRaymond.Chen@Sun.COM 			}
15069430SRaymond.Chen@Sun.COM 
15079430SRaymond.Chen@Sun.COM 			usba_device = usba_get_usba_device(child_dip);
15089430SRaymond.Chen@Sun.COM 			mutex_enter(&usba_device->usb_mutex);
15099430SRaymond.Chen@Sun.COM 			config_index = usba_device->usb_active_cfg_ndx;
15109430SRaymond.Chen@Sun.COM 			mutex_exit(&usba_device->usb_mutex);
15119430SRaymond.Chen@Sun.COM 
15129430SRaymond.Chen@Sun.COM 			if (ioc.get_size) {
15139430SRaymond.Chen@Sun.COM 				if (ddi_copyout((void *)&size,
15149430SRaymond.Chen@Sun.COM 				    ioc.buf, ioc.bufsiz, mode) != 0) {
15159430SRaymond.Chen@Sun.COM 					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
15169430SRaymond.Chen@Sun.COM 					    hwahcp->hwahc_log_handle,
15179430SRaymond.Chen@Sun.COM 					    "%s: copyout of size failed.", msg);
15189430SRaymond.Chen@Sun.COM 					rv = EIO;
15199430SRaymond.Chen@Sun.COM 
15209430SRaymond.Chen@Sun.COM 					break;
15219430SRaymond.Chen@Sun.COM 				}
15229430SRaymond.Chen@Sun.COM 			} else {
15239430SRaymond.Chen@Sun.COM 				if (ioc.bufsiz != size) {
15249430SRaymond.Chen@Sun.COM 					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
15259430SRaymond.Chen@Sun.COM 					    hwahcp->hwahc_log_handle,
15269430SRaymond.Chen@Sun.COM 					    "%s: buffer size wrong", msg);
15279430SRaymond.Chen@Sun.COM 					rv = EINVAL;
15289430SRaymond.Chen@Sun.COM 
15299430SRaymond.Chen@Sun.COM 					break;
15309430SRaymond.Chen@Sun.COM 				}
15319430SRaymond.Chen@Sun.COM 				if (ddi_copyout((void *)&config_index,
15329430SRaymond.Chen@Sun.COM 				    ioc.buf, ioc.bufsiz, mode) != 0) {
15339430SRaymond.Chen@Sun.COM 					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
15349430SRaymond.Chen@Sun.COM 					    hwahcp->hwahc_log_handle,
15359430SRaymond.Chen@Sun.COM 					    "%s: copyout failed", msg);
15369430SRaymond.Chen@Sun.COM 					rv = EIO;
15379430SRaymond.Chen@Sun.COM 				}
15389430SRaymond.Chen@Sun.COM 			}
15399430SRaymond.Chen@Sun.COM 
15409430SRaymond.Chen@Sun.COM 			break;
15419430SRaymond.Chen@Sun.COM 		}
15429430SRaymond.Chen@Sun.COM 		case HUBD_GET_DEVICE_PATH:
15439430SRaymond.Chen@Sun.COM 		{
15449430SRaymond.Chen@Sun.COM 			char		*path;
15459430SRaymond.Chen@Sun.COM 			uint32_t	size;
15469430SRaymond.Chen@Sun.COM 
15479430SRaymond.Chen@Sun.COM 			msg = "DEVCTL_AP_CONTROL: GET_DEVICE_PATH";
15489430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L4(PRINT_MASK_CBOPS,
15499430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle, "%s", msg);
15509430SRaymond.Chen@Sun.COM 
15519430SRaymond.Chen@Sun.COM 			/* Recheck if child_dip exists */
15529430SRaymond.Chen@Sun.COM 			if ((child_dip =
15539430SRaymond.Chen@Sun.COM 			    hwahc_get_child_dip(hwahcp, ioc.port)) == NULL) {
15549430SRaymond.Chen@Sun.COM 				rv = EINVAL;
15559430SRaymond.Chen@Sun.COM 
15569430SRaymond.Chen@Sun.COM 				break;
15579430SRaymond.Chen@Sun.COM 			}
15589430SRaymond.Chen@Sun.COM 
15599430SRaymond.Chen@Sun.COM 			/* ddi_pathname doesn't supply /devices, so we do. */
15609430SRaymond.Chen@Sun.COM 			path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
15619430SRaymond.Chen@Sun.COM 			(void) strcpy(path, "/devices");
15629430SRaymond.Chen@Sun.COM 			(void) ddi_pathname(child_dip, path + strlen(path));
15639430SRaymond.Chen@Sun.COM 			size = strlen(path) + 1;
15649430SRaymond.Chen@Sun.COM 
15659430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L4(PRINT_MASK_CBOPS,
15669430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
15679430SRaymond.Chen@Sun.COM 			    "%s: device path=%s  size=%d", msg, path, size);
15689430SRaymond.Chen@Sun.COM 
15699430SRaymond.Chen@Sun.COM 			if (ioc.get_size) {
15709430SRaymond.Chen@Sun.COM 				if (ddi_copyout((void *)&size,
15719430SRaymond.Chen@Sun.COM 				    ioc.buf, ioc.bufsiz, mode) != 0) {
15729430SRaymond.Chen@Sun.COM 
15739430SRaymond.Chen@Sun.COM 					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
15749430SRaymond.Chen@Sun.COM 					    hwahcp->hwahc_log_handle,
15759430SRaymond.Chen@Sun.COM 					    "%s: copyout of size failed.", msg);
15769430SRaymond.Chen@Sun.COM 					rv = EIO;
15779430SRaymond.Chen@Sun.COM 				}
15789430SRaymond.Chen@Sun.COM 			} else {
15799430SRaymond.Chen@Sun.COM 				if (ioc.bufsiz != size) {
15809430SRaymond.Chen@Sun.COM 					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
15819430SRaymond.Chen@Sun.COM 					    hwahcp->hwahc_log_handle,
15829430SRaymond.Chen@Sun.COM 					    "%s: buffer wrong size.", msg);
15839430SRaymond.Chen@Sun.COM 					rv = EINVAL;
15849430SRaymond.Chen@Sun.COM 				} else if (ddi_copyout((void *)path,
15859430SRaymond.Chen@Sun.COM 				    ioc.buf, ioc.bufsiz, mode) != 0) {
15869430SRaymond.Chen@Sun.COM 					USB_DPRINTF_L2(PRINT_MASK_CBOPS,
15879430SRaymond.Chen@Sun.COM 					    hwahcp->hwahc_log_handle,
15889430SRaymond.Chen@Sun.COM 					    "%s: copyout failed.", msg);
15899430SRaymond.Chen@Sun.COM 					rv = EIO;
15909430SRaymond.Chen@Sun.COM 				}
15919430SRaymond.Chen@Sun.COM 			}
15929430SRaymond.Chen@Sun.COM 			kmem_free(path, MAXPATHLEN);
15939430SRaymond.Chen@Sun.COM 
15949430SRaymond.Chen@Sun.COM 			break;
15959430SRaymond.Chen@Sun.COM 		}
15969430SRaymond.Chen@Sun.COM 		case HUBD_REFRESH_DEVDB:
15979430SRaymond.Chen@Sun.COM 			msg = "DEVCTL_AP_CONTROL: HUBD_REFRESH_DEVDB";
15989430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L4(PRINT_MASK_CBOPS,
15999430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle, "%s", msg);
16009430SRaymond.Chen@Sun.COM 
16019430SRaymond.Chen@Sun.COM 			if ((rv = usba_devdb_refresh()) != USB_SUCCESS) {
16029430SRaymond.Chen@Sun.COM 				USB_DPRINTF_L2(PRINT_MASK_CBOPS,
16039430SRaymond.Chen@Sun.COM 				    hwahcp->hwahc_log_handle,
16049430SRaymond.Chen@Sun.COM 				    "%s: Failed: %d", msg, rv);
16059430SRaymond.Chen@Sun.COM 				rv = EIO;
16069430SRaymond.Chen@Sun.COM 			}
16079430SRaymond.Chen@Sun.COM 
16089430SRaymond.Chen@Sun.COM 			break;
16099430SRaymond.Chen@Sun.COM 		default:
16109430SRaymond.Chen@Sun.COM 			rv = ENOTSUP;
16119430SRaymond.Chen@Sun.COM 		}	/* end switch */
16129430SRaymond.Chen@Sun.COM 
16139430SRaymond.Chen@Sun.COM 		break;
16149430SRaymond.Chen@Sun.COM 	}
16159430SRaymond.Chen@Sun.COM 
16169430SRaymond.Chen@Sun.COM 	default:
16179430SRaymond.Chen@Sun.COM 		rv = ENOTTY;
16189430SRaymond.Chen@Sun.COM 	}
16199430SRaymond.Chen@Sun.COM 
16209430SRaymond.Chen@Sun.COM 	if (dcp) {
16219430SRaymond.Chen@Sun.COM 		ndi_dc_freehdl(dcp);
16229430SRaymond.Chen@Sun.COM 	}
16239430SRaymond.Chen@Sun.COM 
16249430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
16259430SRaymond.Chen@Sun.COM 
16269430SRaymond.Chen@Sun.COM 	ndi_devi_exit(hwahcp->hwahc_dip, circ);
16279430SRaymond.Chen@Sun.COM 	ndi_devi_exit(rh_dip, rh_circ);
16289430SRaymond.Chen@Sun.COM 	ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ);
16299430SRaymond.Chen@Sun.COM 
16309430SRaymond.Chen@Sun.COM 	return (rv);
16319430SRaymond.Chen@Sun.COM }
16329430SRaymond.Chen@Sun.COM 
16339430SRaymond.Chen@Sun.COM /* update CHID for the hc driver, return 0 on success */
16349430SRaymond.Chen@Sun.COM static int
hwahc_set_chid(hwahc_state_t * hwahcp,uint8_t * chid)16359430SRaymond.Chen@Sun.COM hwahc_set_chid(hwahc_state_t *hwahcp, uint8_t *chid)
16369430SRaymond.Chen@Sun.COM {
16379430SRaymond.Chen@Sun.COM 	wusb_hc_data_t	*hc_data = &hwahcp->hwahc_hc_data;
16389430SRaymond.Chen@Sun.COM 
16399430SRaymond.Chen@Sun.COM 	ASSERT(!mutex_owned(&hc_data->hc_mutex));
16409430SRaymond.Chen@Sun.COM 
16419430SRaymond.Chen@Sun.COM 	/* same as the old CHID, return success */
16429430SRaymond.Chen@Sun.COM 	if (memcmp(chid, hc_data->hc_chid, 16) == 0) {
16439430SRaymond.Chen@Sun.COM 
16449430SRaymond.Chen@Sun.COM 		return (0);
16459430SRaymond.Chen@Sun.COM 	}
16469430SRaymond.Chen@Sun.COM 
16479430SRaymond.Chen@Sun.COM 	/*
16489430SRaymond.Chen@Sun.COM 	 * stop hw from working before updating CHID
16499430SRaymond.Chen@Sun.COM 	 * this may not be necessary but so far we don't know
16509430SRaymond.Chen@Sun.COM 	 * other ways to do it safely
16519430SRaymond.Chen@Sun.COM 	 */
16529430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_hw_state == HWAHC_HW_STARTED) {
16539430SRaymond.Chen@Sun.COM 		/* use final_stop to fully stop the hwa */
16549430SRaymond.Chen@Sun.COM 		if (hwahc_hc_final_stop(hwahcp) != USB_SUCCESS) {
16559430SRaymond.Chen@Sun.COM 
16569430SRaymond.Chen@Sun.COM 			return (EIO);
16579430SRaymond.Chen@Sun.COM 		}
16589430SRaymond.Chen@Sun.COM 
16599430SRaymond.Chen@Sun.COM 		mutex_enter(&hc_data->hc_mutex);
16609430SRaymond.Chen@Sun.COM 		(void) memcpy(hc_data->hc_chid, chid, 16);
16619430SRaymond.Chen@Sun.COM 		mutex_exit(&hc_data->hc_mutex);
16629430SRaymond.Chen@Sun.COM 
16639430SRaymond.Chen@Sun.COM 		/* restart the host */
16649430SRaymond.Chen@Sun.COM 		if (hwahc_hc_initial_start(hwahcp) != USB_SUCCESS) {
16659430SRaymond.Chen@Sun.COM 
16669430SRaymond.Chen@Sun.COM 			return (EIO);
16679430SRaymond.Chen@Sun.COM 		}
16689430SRaymond.Chen@Sun.COM 
16699430SRaymond.Chen@Sun.COM 		return (0);
16709430SRaymond.Chen@Sun.COM 	}
16719430SRaymond.Chen@Sun.COM 
16729430SRaymond.Chen@Sun.COM 	/* hc is stopped or partially stopped, simply update */
16739430SRaymond.Chen@Sun.COM 	mutex_enter(&hc_data->hc_mutex);
16749430SRaymond.Chen@Sun.COM 	(void) memcpy(hc_data->hc_chid, chid, 16);
16759430SRaymond.Chen@Sun.COM 	mutex_exit(&hc_data->hc_mutex);
16769430SRaymond.Chen@Sun.COM 
16779430SRaymond.Chen@Sun.COM 	return (0);
16789430SRaymond.Chen@Sun.COM }
16799430SRaymond.Chen@Sun.COM 
16809430SRaymond.Chen@Sun.COM /*
16819430SRaymond.Chen@Sun.COM  * wusbadm ioctl support
16829430SRaymond.Chen@Sun.COM  */
16839430SRaymond.Chen@Sun.COM /* ARGSUSED */
16849430SRaymond.Chen@Sun.COM static int
hwahc_wusb_ioctl(hwahc_state_t * hwahcp,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)16859430SRaymond.Chen@Sun.COM hwahc_wusb_ioctl(hwahc_state_t *hwahcp, int cmd, intptr_t arg,
16869430SRaymond.Chen@Sun.COM 	int mode, cred_t *credp, int *rvalp)
16879430SRaymond.Chen@Sun.COM {
16889430SRaymond.Chen@Sun.COM 	int		rv = 0;
16899430SRaymond.Chen@Sun.COM 	wusb_hc_data_t	*hc_data = &hwahcp->hwahc_hc_data;
16909430SRaymond.Chen@Sun.COM 
16919430SRaymond.Chen@Sun.COM 	if (drv_priv(credp) != 0) {
16929430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
16939430SRaymond.Chen@Sun.COM 		    "hwahc_wusb_ioctl: user must have SYS_DEVICE privilege,"
16949430SRaymond.Chen@Sun.COM 		    "cmd=%x", cmd);
16959430SRaymond.Chen@Sun.COM 
16969430SRaymond.Chen@Sun.COM 		return (EPERM);
16979430SRaymond.Chen@Sun.COM 	}
16989430SRaymond.Chen@Sun.COM 
16999430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
17009430SRaymond.Chen@Sun.COM 
17019430SRaymond.Chen@Sun.COM 	switch (cmd) {
17029430SRaymond.Chen@Sun.COM 	case WUSB_HC_GET_DSTATE: /* Get device state: wusbadm list */
17039430SRaymond.Chen@Sun.COM 	{
17049430SRaymond.Chen@Sun.COM 		wusb_hc_get_dstate_t	state;
17059430SRaymond.Chen@Sun.COM 		usb_port_t		port = 0;
17069430SRaymond.Chen@Sun.COM 
17079430SRaymond.Chen@Sun.COM 		if (ddi_copyin((void *)arg, (void *)&state, sizeof (state),
17089430SRaymond.Chen@Sun.COM 		    mode) != 0) {
17099430SRaymond.Chen@Sun.COM 			rv = EFAULT;
17109430SRaymond.Chen@Sun.COM 
17119430SRaymond.Chen@Sun.COM 			break;
17129430SRaymond.Chen@Sun.COM 		}
17139430SRaymond.Chen@Sun.COM 
17149430SRaymond.Chen@Sun.COM 		mutex_enter(&hc_data->hc_mutex);
17159430SRaymond.Chen@Sun.COM 
17169430SRaymond.Chen@Sun.COM 		if (wusb_hc_is_dev_connected(hc_data, &state.cdid[0], &port)) {
17179430SRaymond.Chen@Sun.COM 			state.state = hc_data->hc_dev_infos[port]->wdev_state;
17189430SRaymond.Chen@Sun.COM 		} else {
17199430SRaymond.Chen@Sun.COM 			/* cdid not found */
17209430SRaymond.Chen@Sun.COM 			state.state = WUSB_STATE_UNCONNTED;
17219430SRaymond.Chen@Sun.COM 		}
17229430SRaymond.Chen@Sun.COM 
17239430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
17249430SRaymond.Chen@Sun.COM 		    "hwahc_wusb_ioctl: hc_data=%p, port = %d, state=%d",
17259430SRaymond.Chen@Sun.COM 		    (void *) hc_data, port, state.state);
17269430SRaymond.Chen@Sun.COM 
17279430SRaymond.Chen@Sun.COM 		mutex_exit(&hc_data->hc_mutex);
17289430SRaymond.Chen@Sun.COM 
17299430SRaymond.Chen@Sun.COM 		if (state.state == WUSB_STATE_CONFIGURED) {
17309430SRaymond.Chen@Sun.COM 			/* Get the bind device node name of this child */
17319430SRaymond.Chen@Sun.COM 			(void) memset(state.nodename, 0, MAX_USB_NODENAME);
17329430SRaymond.Chen@Sun.COM 			(void) snprintf(state.nodename, MAX_USB_NODENAME, "%s",
17339430SRaymond.Chen@Sun.COM 			    ddi_node_name(hwahc_get_child_dip(hwahcp, port)));
17349430SRaymond.Chen@Sun.COM 
17359430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L3(PRINT_MASK_CBOPS,
17369430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
17379430SRaymond.Chen@Sun.COM 			    "WUSB_HC_GET_DSTATE: nodename %s", state.nodename);
17389430SRaymond.Chen@Sun.COM 		}
17399430SRaymond.Chen@Sun.COM 
17409430SRaymond.Chen@Sun.COM 		if (ddi_copyout((void *)&state, (void *)arg,
17419430SRaymond.Chen@Sun.COM 		    sizeof (state), mode) != 0) {
17429430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
17439430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
17449430SRaymond.Chen@Sun.COM 			    "WUSB_HC_GET_DSTATE: copyout failed");
17459430SRaymond.Chen@Sun.COM 			rv = EIO;
17469430SRaymond.Chen@Sun.COM 		}
17479430SRaymond.Chen@Sun.COM 
17489430SRaymond.Chen@Sun.COM 		break;
17499430SRaymond.Chen@Sun.COM 	}
17509430SRaymond.Chen@Sun.COM 
17519430SRaymond.Chen@Sun.COM 	case WUSB_HC_GET_MAC_ADDR: /* Get host MAC addr */
17529430SRaymond.Chen@Sun.COM 	{
17539430SRaymond.Chen@Sun.COM 		uint8_t		mac_addr[6];
17549430SRaymond.Chen@Sun.COM 
17559430SRaymond.Chen@Sun.COM 		bzero(mac_addr, 6);
17569430SRaymond.Chen@Sun.COM 
17579430SRaymond.Chen@Sun.COM 		/*
17589430SRaymond.Chen@Sun.COM 		 * get UWB 48-bit mac address
17599430SRaymond.Chen@Sun.COM 		 * Section 8.6.2.2.
17609430SRaymond.Chen@Sun.COM 		 */
17619430SRaymond.Chen@Sun.COM 		if (uwb_get_mac_addr(hwahcp->hwahc_dip, mac_addr) !=
17629430SRaymond.Chen@Sun.COM 		    USB_SUCCESS) {
17639430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
17649430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
17659430SRaymond.Chen@Sun.COM 			    "WUSB_HC_GET_MAC_ADDR: get mac failed");
17669430SRaymond.Chen@Sun.COM 			rv = EIO;
17679430SRaymond.Chen@Sun.COM 
17689430SRaymond.Chen@Sun.COM 			break;
17699430SRaymond.Chen@Sun.COM 		}
17709430SRaymond.Chen@Sun.COM 
17719430SRaymond.Chen@Sun.COM 		if (ddi_copyout((void *)mac_addr, (void *)arg,
17729430SRaymond.Chen@Sun.COM 		    6, mode) != 0) {
17739430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
17749430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
17759430SRaymond.Chen@Sun.COM 			    "WUSB_HC_GET_MAC_ADDR: copyout failed");
17769430SRaymond.Chen@Sun.COM 			rv = EIO;
17779430SRaymond.Chen@Sun.COM 		}
17789430SRaymond.Chen@Sun.COM 
17799430SRaymond.Chen@Sun.COM 		break;
17809430SRaymond.Chen@Sun.COM 	}
17819430SRaymond.Chen@Sun.COM 	case WUSB_HC_ADD_CC:
17829430SRaymond.Chen@Sun.COM 	{
17839430SRaymond.Chen@Sun.COM 	/*
17849430SRaymond.Chen@Sun.COM 	 * add a new device CC to host's list: wusbadm associate
17859430SRaymond.Chen@Sun.COM 	 * Or, the application can pass in a fake CC with only CHID set
17869430SRaymond.Chen@Sun.COM 	 * to set the host's CHID.
17879430SRaymond.Chen@Sun.COM 	 */
17889430SRaymond.Chen@Sun.COM 		wusb_hc_cc_list_t	*cc_list;
17899430SRaymond.Chen@Sun.COM 
17909430SRaymond.Chen@Sun.COM 		cc_list = kmem_zalloc(sizeof (wusb_hc_cc_list_t), KM_SLEEP);
17919430SRaymond.Chen@Sun.COM 
17929430SRaymond.Chen@Sun.COM 		if (ddi_copyin((void *)arg, (void *)&cc_list->cc,
17939430SRaymond.Chen@Sun.COM 		    sizeof (wusb_cc_t), mode) != 0) {
17949430SRaymond.Chen@Sun.COM 			rv = EFAULT;
17959430SRaymond.Chen@Sun.COM 			kmem_free(cc_list, sizeof (wusb_hc_cc_list_t));
17969430SRaymond.Chen@Sun.COM 
17979430SRaymond.Chen@Sun.COM 			break;
17989430SRaymond.Chen@Sun.COM 		}
17999430SRaymond.Chen@Sun.COM 
18009430SRaymond.Chen@Sun.COM 		/* update CHID only when cc list is empty */
18019430SRaymond.Chen@Sun.COM 		mutex_enter(&hc_data->hc_mutex);
18029430SRaymond.Chen@Sun.COM 		if (hc_data->hc_cc_list == NULL) {
18039430SRaymond.Chen@Sun.COM 			mutex_exit(&hc_data->hc_mutex);
18049430SRaymond.Chen@Sun.COM 
18059430SRaymond.Chen@Sun.COM 			if ((rv = hwahc_set_chid(hwahcp,
18069430SRaymond.Chen@Sun.COM 			    cc_list->cc.CHID)) != 0) {
18079430SRaymond.Chen@Sun.COM 				kmem_free(cc_list, sizeof (wusb_hc_cc_list_t));
18089430SRaymond.Chen@Sun.COM 
18099430SRaymond.Chen@Sun.COM 				break;
18109430SRaymond.Chen@Sun.COM 			}
18119430SRaymond.Chen@Sun.COM 
18129430SRaymond.Chen@Sun.COM 			mutex_enter(&hc_data->hc_mutex);
18139430SRaymond.Chen@Sun.COM 		} else {
18149430SRaymond.Chen@Sun.COM 			/* fail if the CHID in the new CC does not match */
18159430SRaymond.Chen@Sun.COM 			if (memcmp(cc_list->cc.CHID, hc_data->hc_chid,
18169430SRaymond.Chen@Sun.COM 			    16) != 0) {
18179430SRaymond.Chen@Sun.COM 				rv = EINVAL;
18189430SRaymond.Chen@Sun.COM 				kmem_free(cc_list, sizeof (wusb_hc_cc_list_t));
18199430SRaymond.Chen@Sun.COM 
18209430SRaymond.Chen@Sun.COM 				mutex_exit(&hc_data->hc_mutex);
18219430SRaymond.Chen@Sun.COM 				break;
18229430SRaymond.Chen@Sun.COM 			}
18239430SRaymond.Chen@Sun.COM 		}
18249430SRaymond.Chen@Sun.COM 		cc_list->next = NULL;
18259430SRaymond.Chen@Sun.COM 
18269430SRaymond.Chen@Sun.COM 		wusb_hc_add_cc(&hc_data->hc_cc_list, cc_list);
18279430SRaymond.Chen@Sun.COM 		mutex_exit(&hc_data->hc_mutex);
18289430SRaymond.Chen@Sun.COM 
18299430SRaymond.Chen@Sun.COM 		break;
18309430SRaymond.Chen@Sun.COM 	}
18319430SRaymond.Chen@Sun.COM 	case WUSB_HC_REM_CC:
18329430SRaymond.Chen@Sun.COM 	{
18339430SRaymond.Chen@Sun.COM 		wusb_cc_t	cc;
18349430SRaymond.Chen@Sun.COM 		usb_port_t	port;
18359430SRaymond.Chen@Sun.COM 
18369430SRaymond.Chen@Sun.COM 		if (ddi_copyin((void *)arg, (void *)&cc, sizeof (wusb_cc_t),
18379430SRaymond.Chen@Sun.COM 		    mode) != 0) {
18389430SRaymond.Chen@Sun.COM 			rv = EFAULT;
18399430SRaymond.Chen@Sun.COM 
18409430SRaymond.Chen@Sun.COM 			break;
18419430SRaymond.Chen@Sun.COM 		}
18429430SRaymond.Chen@Sun.COM 
18439430SRaymond.Chen@Sun.COM 		/* check if the CHID in the CC matches */
18449430SRaymond.Chen@Sun.COM 		if (memcmp(cc.CHID, hc_data->hc_chid, 16) != 0) {
18459430SRaymond.Chen@Sun.COM 			rv = EINVAL;
18469430SRaymond.Chen@Sun.COM 
18479430SRaymond.Chen@Sun.COM 			break;
18489430SRaymond.Chen@Sun.COM 		}
18499430SRaymond.Chen@Sun.COM 
18509430SRaymond.Chen@Sun.COM 		/* if the device is connected, disconnect it first */
18519430SRaymond.Chen@Sun.COM 		mutex_enter(&hc_data->hc_mutex);
18529430SRaymond.Chen@Sun.COM 		if (wusb_hc_is_dev_connected(hc_data, cc.CDID, &port)) {
18539430SRaymond.Chen@Sun.COM 			mutex_exit(&hc_data->hc_mutex);
18549430SRaymond.Chen@Sun.COM 			mutex_exit(&hwahcp->hwahc_mutex);
18559430SRaymond.Chen@Sun.COM 			/*
18569430SRaymond.Chen@Sun.COM 			 * clean up host side state, device not
18579430SRaymond.Chen@Sun.COM 			 * really disconnected. But user can safely remove
18589430SRaymond.Chen@Sun.COM 			 * the device now.
18599430SRaymond.Chen@Sun.COM 			 */
18609572SRaymond.Chen@Sun.COM 			(void) hwahc_destroy_child(hc_data->hc_dip, port);
18619430SRaymond.Chen@Sun.COM 			mutex_enter(&hwahcp->hwahc_mutex);
18629430SRaymond.Chen@Sun.COM 			mutex_enter(&hc_data->hc_mutex);
18639430SRaymond.Chen@Sun.COM 		}
18649430SRaymond.Chen@Sun.COM 
18659430SRaymond.Chen@Sun.COM 		wusb_hc_rem_cc(&hc_data->hc_cc_list, &cc);
18669430SRaymond.Chen@Sun.COM 		mutex_exit(&hc_data->hc_mutex);
18679430SRaymond.Chen@Sun.COM 
18689430SRaymond.Chen@Sun.COM 		break;
18699430SRaymond.Chen@Sun.COM 	}
18709430SRaymond.Chen@Sun.COM 	case WUSB_HC_SET_CHANNEL: /* for debug purpose */
18719430SRaymond.Chen@Sun.COM 	{
18729430SRaymond.Chen@Sun.COM 		uint8_t	channel;
18739430SRaymond.Chen@Sun.COM 
18749430SRaymond.Chen@Sun.COM 		channel = (uint8_t)arg;
18759430SRaymond.Chen@Sun.COM 
18769430SRaymond.Chen@Sun.COM 		if (hwahcp->hwahc_hc_data.hc_channel == channel) {
18779430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
18789430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
18799430SRaymond.Chen@Sun.COM 			    "WUSB_HC_SET_CHANNEL ioctl: same as existing");
18809430SRaymond.Chen@Sun.COM 
18819430SRaymond.Chen@Sun.COM 			break;
18829430SRaymond.Chen@Sun.COM 		}
18839430SRaymond.Chen@Sun.COM 
18849430SRaymond.Chen@Sun.COM 		if (hwahcp->hwahc_hw_state != HWAHC_HW_STOPPED) {
18859430SRaymond.Chen@Sun.COM 			/* beacon is already started, stop it first */
18869430SRaymond.Chen@Sun.COM 			if (uwb_stop_beacon(hwahcp->hwahc_dip) != USB_SUCCESS) {
18879430SRaymond.Chen@Sun.COM 				USB_DPRINTF_L2(PRINT_MASK_CBOPS,
18889430SRaymond.Chen@Sun.COM 				    hwahcp->hwahc_log_handle,
18899430SRaymond.Chen@Sun.COM 				    "WUSB_HC_SET_CHANNEL ioctl: "
18909430SRaymond.Chen@Sun.COM 				    "stop beacon failed");
18919430SRaymond.Chen@Sun.COM 				rv = EIO;
18929430SRaymond.Chen@Sun.COM 
18939430SRaymond.Chen@Sun.COM 				break;
18949430SRaymond.Chen@Sun.COM 			}
18959430SRaymond.Chen@Sun.COM 			/* update channel number */
18969430SRaymond.Chen@Sun.COM 			hwahcp->hwahc_hc_data.hc_channel = channel;
18979430SRaymond.Chen@Sun.COM 			/* restart beacon on the new channel */
18989430SRaymond.Chen@Sun.COM 			if (uwb_start_beacon(hwahcp->hwahc_dip,
18999430SRaymond.Chen@Sun.COM 			    channel) != USB_SUCCESS) {
19009430SRaymond.Chen@Sun.COM 				USB_DPRINTF_L2(PRINT_MASK_CBOPS,
19019430SRaymond.Chen@Sun.COM 				    hwahcp->hwahc_log_handle,
19029430SRaymond.Chen@Sun.COM 				    "WUSB_HC_SET_CHANNEL ioctl: "
19039430SRaymond.Chen@Sun.COM 				    "restart beacon failed");
19049430SRaymond.Chen@Sun.COM 				rv = EIO;
19059430SRaymond.Chen@Sun.COM 			}
19069430SRaymond.Chen@Sun.COM 
19079430SRaymond.Chen@Sun.COM 			break;
19089430SRaymond.Chen@Sun.COM 		}
19099430SRaymond.Chen@Sun.COM 
19109430SRaymond.Chen@Sun.COM 		/* beacon is not started, simply update channel number */
19119430SRaymond.Chen@Sun.COM 		hwahcp->hwahc_hc_data.hc_channel = channel;
19129430SRaymond.Chen@Sun.COM 
19139430SRaymond.Chen@Sun.COM 		break;
19149430SRaymond.Chen@Sun.COM 	}
19159430SRaymond.Chen@Sun.COM 	case WUSB_HC_START:
19169430SRaymond.Chen@Sun.COM 	{
19179430SRaymond.Chen@Sun.COM 		int	flag;
19189430SRaymond.Chen@Sun.COM 
19199430SRaymond.Chen@Sun.COM 
19209430SRaymond.Chen@Sun.COM 		flag = (int)arg;
19219430SRaymond.Chen@Sun.COM 
19229430SRaymond.Chen@Sun.COM 		if (hwahcp->hwahc_hw_state == HWAHC_HW_STARTED) {
19239430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
19249430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
19259430SRaymond.Chen@Sun.COM 			    "WUSB_HC_START ioctl: already started");
19269430SRaymond.Chen@Sun.COM 
19279430SRaymond.Chen@Sun.COM 			break;
19289430SRaymond.Chen@Sun.COM 		}
19299430SRaymond.Chen@Sun.COM 
19309430SRaymond.Chen@Sun.COM 		/*
19319430SRaymond.Chen@Sun.COM 		 * now we start hc only when the cc list is not NULL
19329430SRaymond.Chen@Sun.COM 		 * this limitation may be removed if we support
19339430SRaymond.Chen@Sun.COM 		 * numeric association, but CHID needs to be set
19349430SRaymond.Chen@Sun.COM 		 * in advance for the hc to work
19359430SRaymond.Chen@Sun.COM 		 */
19369430SRaymond.Chen@Sun.COM 		mutex_enter(&hc_data->hc_mutex);
19379430SRaymond.Chen@Sun.COM 		if (hc_data->hc_cc_list == NULL) {
19389430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
19399430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
19409430SRaymond.Chen@Sun.COM 			    "WUSB_HC_START ioctl: cc list not inited");
19419430SRaymond.Chen@Sun.COM 			rv = EINVAL;
19429430SRaymond.Chen@Sun.COM 
19439430SRaymond.Chen@Sun.COM 			mutex_exit(&hc_data->hc_mutex);
19449430SRaymond.Chen@Sun.COM 
19459430SRaymond.Chen@Sun.COM 			break;
19469430SRaymond.Chen@Sun.COM 		}
19479430SRaymond.Chen@Sun.COM 		mutex_exit(&hc_data->hc_mutex);
19489430SRaymond.Chen@Sun.COM 
19499430SRaymond.Chen@Sun.COM 		/* cannot be both */
19509430SRaymond.Chen@Sun.COM 		if ((flag & WUSB_HC_INITIAL_START) && (flag &
19519430SRaymond.Chen@Sun.COM 		    WUSB_HC_CHANNEL_START)) {
19529430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
19539430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
19549430SRaymond.Chen@Sun.COM 			    "WUSB_HC_START ioctl: flag cannot coexist");
19559430SRaymond.Chen@Sun.COM 			rv = EINVAL;
19569430SRaymond.Chen@Sun.COM 
19579430SRaymond.Chen@Sun.COM 			break;
19589430SRaymond.Chen@Sun.COM 		}
19599430SRaymond.Chen@Sun.COM 
19609430SRaymond.Chen@Sun.COM 		/*
19619430SRaymond.Chen@Sun.COM 		 * init Mac layer 16-bit dev addr. it is important for
19629430SRaymond.Chen@Sun.COM 		 * authentication. It'd be better to let UWB provide
19639430SRaymond.Chen@Sun.COM 		 * this address.
19649430SRaymond.Chen@Sun.COM 		 */
19659430SRaymond.Chen@Sun.COM 		mutex_enter(&hc_data->hc_mutex);
19669430SRaymond.Chen@Sun.COM 		if (hc_data->hc_addr == 0) {
19679430SRaymond.Chen@Sun.COM 			uint16_t dev_addr = HWAHC_DEV_ADDR_BASE +
19689430SRaymond.Chen@Sun.COM 			    ddi_get_instance(hwahcp->hwahc_dip);
19699430SRaymond.Chen@Sun.COM 
19709430SRaymond.Chen@Sun.COM 			mutex_exit(&hc_data->hc_mutex);
19719430SRaymond.Chen@Sun.COM 			/* set UWB 16-bit dev address */
19729430SRaymond.Chen@Sun.COM 			if (uwb_set_dev_addr(hwahcp->hwahc_dip,
19739430SRaymond.Chen@Sun.COM 			    dev_addr) != USB_SUCCESS) {
19749430SRaymond.Chen@Sun.COM 				USB_DPRINTF_L2(PRINT_MASK_CBOPS,
19759430SRaymond.Chen@Sun.COM 				    hwahcp->hwahc_log_handle,
19769430SRaymond.Chen@Sun.COM 				    "WUSB_HC_START ioctl: set dev addr failed");
19779430SRaymond.Chen@Sun.COM 				rv = EIO;
19789430SRaymond.Chen@Sun.COM 
19799430SRaymond.Chen@Sun.COM 				break;
19809430SRaymond.Chen@Sun.COM 			}
19819430SRaymond.Chen@Sun.COM 
19829430SRaymond.Chen@Sun.COM 			/* verify the dev addr is set correctly */
19839430SRaymond.Chen@Sun.COM 			if (uwb_get_dev_addr(hwahcp->hwahc_dip,
19849430SRaymond.Chen@Sun.COM 			    &dev_addr) != USB_SUCCESS) {
19859430SRaymond.Chen@Sun.COM 				USB_DPRINTF_L2(PRINT_MASK_CBOPS,
19869430SRaymond.Chen@Sun.COM 				    hwahcp->hwahc_log_handle,
19879430SRaymond.Chen@Sun.COM 				    "WUSB_HC_START ioctl: get dev addr failed");
19889430SRaymond.Chen@Sun.COM 				rv = EIO;
19899430SRaymond.Chen@Sun.COM 
19909430SRaymond.Chen@Sun.COM 				break;
19919430SRaymond.Chen@Sun.COM 			}
19929430SRaymond.Chen@Sun.COM 
19939430SRaymond.Chen@Sun.COM 			mutex_enter(&hc_data->hc_mutex);
19949430SRaymond.Chen@Sun.COM 			hc_data->hc_addr = dev_addr;
19959430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
19969430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
19979430SRaymond.Chen@Sun.COM 			    "host dev addr = 0x%x", dev_addr);
19989430SRaymond.Chen@Sun.COM 		}
19999430SRaymond.Chen@Sun.COM 		mutex_exit(&hc_data->hc_mutex);
20009430SRaymond.Chen@Sun.COM 
20019430SRaymond.Chen@Sun.COM 		/* start functions of wusb host */
20029430SRaymond.Chen@Sun.COM 		if ((flag & WUSB_HC_INITIAL_START) &&
20039430SRaymond.Chen@Sun.COM 		    (hwahcp->hwahc_hw_state == HWAHC_HW_STOPPED)) {
20049430SRaymond.Chen@Sun.COM 			if (hwahc_hc_initial_start(hwahcp) != USB_SUCCESS) {
20059430SRaymond.Chen@Sun.COM 				rv = EIO;
20069430SRaymond.Chen@Sun.COM 			}
20079430SRaymond.Chen@Sun.COM 		} else if ((flag & WUSB_HC_CHANNEL_START) &&
20089430SRaymond.Chen@Sun.COM 		    (hwahcp->hwahc_hw_state == HWAHC_HW_CH_STOPPED)) {
20099430SRaymond.Chen@Sun.COM 			if (hwahc_hc_channel_start(hwahcp) != USB_SUCCESS) {
20109430SRaymond.Chen@Sun.COM 				rv = EIO;
20119430SRaymond.Chen@Sun.COM 			}
20129430SRaymond.Chen@Sun.COM 		} else {
20139430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
20149430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
20159430SRaymond.Chen@Sun.COM 			    "WUSB_HC_START ioctl: unknown flag (%d) or "
20169430SRaymond.Chen@Sun.COM 			    "state (%d)", flag, hwahcp->hwahc_hw_state);
20179430SRaymond.Chen@Sun.COM 			rv = EINVAL;
20189430SRaymond.Chen@Sun.COM 		}
20199430SRaymond.Chen@Sun.COM 
20209430SRaymond.Chen@Sun.COM 		break;
20219430SRaymond.Chen@Sun.COM 	}
20229430SRaymond.Chen@Sun.COM 	case WUSB_HC_STOP:
20239430SRaymond.Chen@Sun.COM 	{
20249430SRaymond.Chen@Sun.COM 		int	flag;
20259430SRaymond.Chen@Sun.COM 
20269430SRaymond.Chen@Sun.COM 		flag = (int)arg;
20279430SRaymond.Chen@Sun.COM 
20289430SRaymond.Chen@Sun.COM 		/* cannot be both */
20299430SRaymond.Chen@Sun.COM 		if ((flag & WUSB_HC_FINAL_STOP) && (flag &
20309430SRaymond.Chen@Sun.COM 		    WUSB_HC_CHANNEL_STOP)) {
20319430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
20329430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
20339430SRaymond.Chen@Sun.COM 			    "WUSB_HC_STOP ioctl: flag cannot coexist");
20349430SRaymond.Chen@Sun.COM 			rv = EINVAL;
20359430SRaymond.Chen@Sun.COM 
20369430SRaymond.Chen@Sun.COM 			break;
20379430SRaymond.Chen@Sun.COM 		}
20389430SRaymond.Chen@Sun.COM 
20399430SRaymond.Chen@Sun.COM 		if (flag & WUSB_HC_FINAL_STOP) {
20409430SRaymond.Chen@Sun.COM 			if (hwahc_hc_final_stop(hwahcp) != USB_SUCCESS) {
20419430SRaymond.Chen@Sun.COM 				rv = EIO;
20429430SRaymond.Chen@Sun.COM 			}
20439430SRaymond.Chen@Sun.COM 		} else if (flag & WUSB_HC_CHANNEL_STOP) {
20449430SRaymond.Chen@Sun.COM 			if (hwahc_hc_channel_stop(hwahcp) != USB_SUCCESS) {
20459430SRaymond.Chen@Sun.COM 				rv = EIO;
20469430SRaymond.Chen@Sun.COM 			}
20479430SRaymond.Chen@Sun.COM 		} else {
20489430SRaymond.Chen@Sun.COM 			/* must be one of the STOP flag */
20499430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
20509430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
20519430SRaymond.Chen@Sun.COM 			    "WUSB_HC_STOP ioctl: invalid flag = %d", flag);
20529430SRaymond.Chen@Sun.COM 			rv = EINVAL;
20539430SRaymond.Chen@Sun.COM 		}
20549430SRaymond.Chen@Sun.COM 
20559430SRaymond.Chen@Sun.COM 		/* REM_ALL_CC flag is optional */
20569430SRaymond.Chen@Sun.COM 		if ((rv == 0) && (flag & WUSB_HC_REM_ALL_CC)) {
20579430SRaymond.Chen@Sun.COM 			mutex_enter(&hc_data->hc_mutex);
20589430SRaymond.Chen@Sun.COM 			if (hc_data->hc_cc_list) {
20599430SRaymond.Chen@Sun.COM 				wusb_hc_free_cc_list(hc_data->hc_cc_list);
20609430SRaymond.Chen@Sun.COM 				hc_data->hc_cc_list = NULL;
20619430SRaymond.Chen@Sun.COM 			}
20629430SRaymond.Chen@Sun.COM 			mutex_exit(&hc_data->hc_mutex);
20639430SRaymond.Chen@Sun.COM 		}
20649430SRaymond.Chen@Sun.COM 
20659430SRaymond.Chen@Sun.COM 		break;
20669430SRaymond.Chen@Sun.COM 	}
20679430SRaymond.Chen@Sun.COM 	case WUSB_HC_GET_HSTATE:
20689430SRaymond.Chen@Sun.COM 	{
20699430SRaymond.Chen@Sun.COM 		int	state;
20709430SRaymond.Chen@Sun.COM 
20719430SRaymond.Chen@Sun.COM 		if (hwahcp->hwahc_dev_state == USB_DEV_DISCONNECTED) {
20729430SRaymond.Chen@Sun.COM 			state = WUSB_HC_DISCONNTED;
20739430SRaymond.Chen@Sun.COM 		} else {
20749430SRaymond.Chen@Sun.COM 			switch (hwahcp->hwahc_hw_state) {
20759430SRaymond.Chen@Sun.COM 			case HWAHC_HW_STOPPED:
20769430SRaymond.Chen@Sun.COM 				state = WUSB_HC_STOPPED;
20779430SRaymond.Chen@Sun.COM 				break;
20789430SRaymond.Chen@Sun.COM 			case HWAHC_HW_STARTED:
20799430SRaymond.Chen@Sun.COM 				state = WUSB_HC_STARTED;
20809430SRaymond.Chen@Sun.COM 				break;
20819430SRaymond.Chen@Sun.COM 			case HWAHC_HW_CH_STOPPED:
20829430SRaymond.Chen@Sun.COM 				/*
20839430SRaymond.Chen@Sun.COM 				 * app can mark the hwa as disabled
20849430SRaymond.Chen@Sun.COM 				 * for this state
20859430SRaymond.Chen@Sun.COM 				 */
20869430SRaymond.Chen@Sun.COM 				state = WUSB_HC_CH_STOPPED;
20879430SRaymond.Chen@Sun.COM 				break;
20889430SRaymond.Chen@Sun.COM 			}
20899430SRaymond.Chen@Sun.COM 		}
20909430SRaymond.Chen@Sun.COM 
20919430SRaymond.Chen@Sun.COM 		if (ddi_copyout((void *)&state, (void *)arg,
20929430SRaymond.Chen@Sun.COM 		    sizeof (int), mode) != 0) {
20939430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
20949430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
20959430SRaymond.Chen@Sun.COM 			    "WUSB_HC_GET_HSTATE: copyout failed");
20969430SRaymond.Chen@Sun.COM 			rv = EIO;
20979430SRaymond.Chen@Sun.COM 		}
20989430SRaymond.Chen@Sun.COM 
20999430SRaymond.Chen@Sun.COM 		break;
21009430SRaymond.Chen@Sun.COM 	}
21019430SRaymond.Chen@Sun.COM 	default:
21029430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
21039430SRaymond.Chen@Sun.COM 		    "hwahc_ioctl: unsupported command");
21049430SRaymond.Chen@Sun.COM 
21059430SRaymond.Chen@Sun.COM 		rv = ENOTSUP;
21069430SRaymond.Chen@Sun.COM 	}
21079430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
21089430SRaymond.Chen@Sun.COM 
21099430SRaymond.Chen@Sun.COM 	return (rv);
21109430SRaymond.Chen@Sun.COM }
21119430SRaymond.Chen@Sun.COM 
21129430SRaymond.Chen@Sun.COM static int
hwahc_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)21139430SRaymond.Chen@Sun.COM hwahc_ioctl(dev_t dev, int cmd, intptr_t arg,
21149430SRaymond.Chen@Sun.COM 	int mode, cred_t *credp, int *rvalp)
21159430SRaymond.Chen@Sun.COM {
21169430SRaymond.Chen@Sun.COM 	hwahc_state_t	*hwahcp;
21179430SRaymond.Chen@Sun.COM 	int		rval;
21189430SRaymond.Chen@Sun.COM 
21199430SRaymond.Chen@Sun.COM 	if ((hwahcp = ddi_get_soft_state(hwahc_statep,
21209430SRaymond.Chen@Sun.COM 	    HWAHC_MINOR_TO_INSTANCE(getminor(dev)))) == NULL) {
21219430SRaymond.Chen@Sun.COM 
21229430SRaymond.Chen@Sun.COM 		return (ENXIO);
21239430SRaymond.Chen@Sun.COM 	}
21249430SRaymond.Chen@Sun.COM 
21259430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
21269430SRaymond.Chen@Sun.COM 	    "hwahc_ioctl: cmd=%x, arg=%lx, mode=%x, cred=%p, rval=%p dev=0x%lx",
21279430SRaymond.Chen@Sun.COM 	    cmd, arg, mode, (void *) credp, (void *) rvalp, dev);
21289430SRaymond.Chen@Sun.COM 
21299430SRaymond.Chen@Sun.COM 	if (IS_DEVCTL(cmd)) {
21309430SRaymond.Chen@Sun.COM 		/* for cfgadm cmd support */
21319430SRaymond.Chen@Sun.COM 		rval = hwahc_cfgadm_ioctl(hwahcp, cmd, arg, mode, credp, rvalp);
21329430SRaymond.Chen@Sun.COM 	} else {
21339430SRaymond.Chen@Sun.COM 		/* for wusbadm cmd support */
21349430SRaymond.Chen@Sun.COM 		rval = hwahc_wusb_ioctl(hwahcp, cmd, arg, mode, credp, rvalp);
21359430SRaymond.Chen@Sun.COM 	}
21369430SRaymond.Chen@Sun.COM 
21379430SRaymond.Chen@Sun.COM 	return (rval);
21389430SRaymond.Chen@Sun.COM }
21399430SRaymond.Chen@Sun.COM 
21409430SRaymond.Chen@Sun.COM /* return the port number corresponding the child dip */
21419430SRaymond.Chen@Sun.COM static usb_port_t
hwahc_child_dip2port(hwahc_state_t * hwahcp,dev_info_t * dip)21429430SRaymond.Chen@Sun.COM hwahc_child_dip2port(hwahc_state_t *hwahcp, dev_info_t *dip)
21439430SRaymond.Chen@Sun.COM {
21449430SRaymond.Chen@Sun.COM 	usb_port_t	port;
21459430SRaymond.Chen@Sun.COM 	wusb_hc_data_t	*hc_data = &hwahcp->hwahc_hc_data;
21469430SRaymond.Chen@Sun.COM 
21479430SRaymond.Chen@Sun.COM 	mutex_enter(&hc_data->hc_mutex);
21489430SRaymond.Chen@Sun.COM 	for (port = 1; port <= hc_data->hc_num_ports; port++) {
21499430SRaymond.Chen@Sun.COM 		if (hc_data->hc_children_dips[port] == dip) {
21509430SRaymond.Chen@Sun.COM 
21519430SRaymond.Chen@Sun.COM 			break;
21529430SRaymond.Chen@Sun.COM 		}
21539430SRaymond.Chen@Sun.COM 	}
21549430SRaymond.Chen@Sun.COM 	ASSERT(port <= hc_data->hc_num_ports);
21559430SRaymond.Chen@Sun.COM 	mutex_exit(&hc_data->hc_mutex);
21569430SRaymond.Chen@Sun.COM 
21579430SRaymond.Chen@Sun.COM 	return (port);
21589430SRaymond.Chen@Sun.COM }
21599430SRaymond.Chen@Sun.COM 
21609430SRaymond.Chen@Sun.COM /*
21619430SRaymond.Chen@Sun.COM  * child post attach/detach notification
21629430SRaymond.Chen@Sun.COM  */
21639430SRaymond.Chen@Sun.COM static void
hwahc_post_attach(hwahc_state_t * hwahcp,dev_info_t * rdip,struct attachspec * as)21649430SRaymond.Chen@Sun.COM hwahc_post_attach(hwahc_state_t *hwahcp, dev_info_t *rdip,
21659430SRaymond.Chen@Sun.COM 	struct attachspec *as)
21669430SRaymond.Chen@Sun.COM {
21679430SRaymond.Chen@Sun.COM 	/* we don't need additional process for post-attach now */
21689430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
21699430SRaymond.Chen@Sun.COM 	    "hwahc_post_attach: rdip = 0x%p result = %d", (void *) rdip,
21709430SRaymond.Chen@Sun.COM 	    as->result);
21719430SRaymond.Chen@Sun.COM }
21729430SRaymond.Chen@Sun.COM 
21739430SRaymond.Chen@Sun.COM static void
hwahc_post_detach(hwahc_state_t * hwahcp,dev_info_t * rdip,struct detachspec * as)21749430SRaymond.Chen@Sun.COM hwahc_post_detach(hwahc_state_t *hwahcp, dev_info_t *rdip,
21759430SRaymond.Chen@Sun.COM 	struct detachspec *as)
21769430SRaymond.Chen@Sun.COM {
21779430SRaymond.Chen@Sun.COM 	/* we don't need additional process for post-detach now */
21789430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
21799430SRaymond.Chen@Sun.COM 	    "hwahc_post_detach: rdip = 0x%p result = %d", (void *) rdip,
21809430SRaymond.Chen@Sun.COM 	    as->result);
21819430SRaymond.Chen@Sun.COM }
21829430SRaymond.Chen@Sun.COM 
21839430SRaymond.Chen@Sun.COM /*
21849430SRaymond.Chen@Sun.COM  * bus ctl support.
21859430SRaymond.Chen@Sun.COM  * To support different operations, such as a PreAttach preparation,
21869430SRaymond.Chen@Sun.COM  * PostAttach operations. HWA only process the interested operations.
21879430SRaymond.Chen@Sun.COM  * Other general ones are processed by usba_bus_ctl().
21889430SRaymond.Chen@Sun.COM  */
21899430SRaymond.Chen@Sun.COM static int
hwahc_bus_ctl(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t op,void * arg,void * result)21909430SRaymond.Chen@Sun.COM hwahc_bus_ctl(dev_info_t *dip, /* dip could be the parent */
21919430SRaymond.Chen@Sun.COM 	dev_info_t	*rdip, /* rdip is the dev node to be operated */
21929430SRaymond.Chen@Sun.COM 	ddi_ctl_enum_t	op,
21939430SRaymond.Chen@Sun.COM 	void		*arg,
21949430SRaymond.Chen@Sun.COM 	void		*result)
21959430SRaymond.Chen@Sun.COM {
21969430SRaymond.Chen@Sun.COM 	usba_device_t *usba_device = usba_get_usba_device(rdip);
21979430SRaymond.Chen@Sun.COM 	dev_info_t *hubdip = usba_device->usb_root_hub_dip;
21989430SRaymond.Chen@Sun.COM 	hwahc_state_t *hwahcp;
21999430SRaymond.Chen@Sun.COM 	struct attachspec *as;
22009430SRaymond.Chen@Sun.COM 	struct detachspec *ds;
22019430SRaymond.Chen@Sun.COM 
22029430SRaymond.Chen@Sun.COM 	if ((hwahcp = ddi_get_soft_state(hwahc_statep,
22039430SRaymond.Chen@Sun.COM 	    ddi_get_instance(dip))) == NULL) {
22049430SRaymond.Chen@Sun.COM 
22059430SRaymond.Chen@Sun.COM 		return (DDI_FAILURE);
22069430SRaymond.Chen@Sun.COM 	}
22079430SRaymond.Chen@Sun.COM 
22089430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L2(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
22099430SRaymond.Chen@Sun.COM 	    "hwahc_bus_ctl:\n\t"
22109430SRaymond.Chen@Sun.COM 	    "dip = 0x%p, rdip = 0x%p, op = 0x%x, arg = 0x%p",
22119430SRaymond.Chen@Sun.COM 	    (void *) dip, (void *) rdip, op, (void *) arg);
22129430SRaymond.Chen@Sun.COM 
22139430SRaymond.Chen@Sun.COM 	switch (op) {
22149430SRaymond.Chen@Sun.COM 	case DDI_CTLOPS_ATTACH:
22159430SRaymond.Chen@Sun.COM 		as = (struct attachspec *)arg;
22169430SRaymond.Chen@Sun.COM 
22179430SRaymond.Chen@Sun.COM 		switch (as->when) {
22189430SRaymond.Chen@Sun.COM 		case DDI_PRE :
22199430SRaymond.Chen@Sun.COM 			/* nothing to do basically */
22209430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_EVENTS,
22219430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
22229430SRaymond.Chen@Sun.COM 			    "DDI_PRE DDI_CTLOPS_ATTACH");
22239430SRaymond.Chen@Sun.COM 			break;
22249430SRaymond.Chen@Sun.COM 		case DDI_POST :
22259430SRaymond.Chen@Sun.COM 			hwahc_post_attach(hwahcp, rdip,
22269430SRaymond.Chen@Sun.COM 			    (struct attachspec *)arg);
22279430SRaymond.Chen@Sun.COM 			break;
22289430SRaymond.Chen@Sun.COM 		}
22299430SRaymond.Chen@Sun.COM 
22309430SRaymond.Chen@Sun.COM 		break;
22319430SRaymond.Chen@Sun.COM 	case DDI_CTLOPS_DETACH:
22329430SRaymond.Chen@Sun.COM 		ds = (struct detachspec *)arg;
22339430SRaymond.Chen@Sun.COM 
22349430SRaymond.Chen@Sun.COM 		switch (ds->when) {
22359430SRaymond.Chen@Sun.COM 		case DDI_PRE :
22369430SRaymond.Chen@Sun.COM 			/* nothing to do basically */
22379430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_EVENTS,
22389430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
22399430SRaymond.Chen@Sun.COM 			    "DDI_PRE DDI_CTLOPS_DETACH");
22409430SRaymond.Chen@Sun.COM 			break;
22419430SRaymond.Chen@Sun.COM 		case DDI_POST :
22429430SRaymond.Chen@Sun.COM 			hwahc_post_detach(hwahcp, rdip,
22439430SRaymond.Chen@Sun.COM 			    (struct detachspec *)arg);
22449430SRaymond.Chen@Sun.COM 			break;
22459430SRaymond.Chen@Sun.COM 		}
22469430SRaymond.Chen@Sun.COM 
22479430SRaymond.Chen@Sun.COM 		break;
22489430SRaymond.Chen@Sun.COM 	case DDI_CTLOPS_REPORTDEV: /* the workhorse behind ddi_report_dev */
22499430SRaymond.Chen@Sun.COM 	{
22509430SRaymond.Chen@Sun.COM 		char *name, compat_name[64];
22519430SRaymond.Chen@Sun.COM 
22529430SRaymond.Chen@Sun.COM 		if (usb_owns_device(rdip)) {
22539430SRaymond.Chen@Sun.COM 			(void) snprintf(compat_name,
22549430SRaymond.Chen@Sun.COM 			    sizeof (compat_name),
22559430SRaymond.Chen@Sun.COM 			    "usb%x,%x",
22569430SRaymond.Chen@Sun.COM 			    usba_device->usb_dev_descr->idVendor,
22579430SRaymond.Chen@Sun.COM 			    usba_device->usb_dev_descr->idProduct);
22589430SRaymond.Chen@Sun.COM 		} else if (usba_owns_ia(rdip)) {
22599430SRaymond.Chen@Sun.COM 			(void) snprintf(compat_name,
22609430SRaymond.Chen@Sun.COM 			    sizeof (compat_name),
22619430SRaymond.Chen@Sun.COM 			    "usbia%x,%x.config%x.%x",
22629430SRaymond.Chen@Sun.COM 			    usba_device->usb_dev_descr->idVendor,
22639430SRaymond.Chen@Sun.COM 			    usba_device->usb_dev_descr->idProduct,
22649430SRaymond.Chen@Sun.COM 			    usba_device->usb_cfg_value,
22659430SRaymond.Chen@Sun.COM 			    usb_get_if_number(rdip));
22669430SRaymond.Chen@Sun.COM 		} else {
22679430SRaymond.Chen@Sun.COM 			(void) snprintf(compat_name,
22689430SRaymond.Chen@Sun.COM 			    sizeof (compat_name),
22699430SRaymond.Chen@Sun.COM 			    "usbif%x,%x.config%x.%x",
22709430SRaymond.Chen@Sun.COM 			    usba_device->usb_dev_descr->idVendor,
22719430SRaymond.Chen@Sun.COM 			    usba_device->usb_dev_descr->idProduct,
22729430SRaymond.Chen@Sun.COM 			    usba_device->usb_cfg_value,
22739430SRaymond.Chen@Sun.COM 			    usb_get_if_number(rdip));
22749430SRaymond.Chen@Sun.COM 		}
22759430SRaymond.Chen@Sun.COM 
22769430SRaymond.Chen@Sun.COM 		cmn_err(CE_CONT,
22779430SRaymond.Chen@Sun.COM 		    "?USB %x.%x %s (%s) operating wirelessly with "
22789430SRaymond.Chen@Sun.COM 		    "HWA device: "
22799430SRaymond.Chen@Sun.COM 		    "%s@%s, %s%d at bus address %d\n",
22809430SRaymond.Chen@Sun.COM 		    (usba_device->usb_dev_descr->bcdUSB & 0xff00) >> 8,
22819430SRaymond.Chen@Sun.COM 		    usba_device->usb_dev_descr->bcdUSB & 0xff,
22829430SRaymond.Chen@Sun.COM 		    (usb_owns_device(rdip) ? "device" :
22839430SRaymond.Chen@Sun.COM 		    ((usba_owns_ia(rdip) ? "interface-association" :
22849430SRaymond.Chen@Sun.COM 		    "interface"))),
22859430SRaymond.Chen@Sun.COM 		    compat_name,
22869430SRaymond.Chen@Sun.COM 		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
22879430SRaymond.Chen@Sun.COM 		    ddi_driver_name(rdip),
22889430SRaymond.Chen@Sun.COM 		    ddi_get_instance(rdip), usba_device->usb_addr);
22899430SRaymond.Chen@Sun.COM 
22909430SRaymond.Chen@Sun.COM 		name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
22919430SRaymond.Chen@Sun.COM 		(void) usba_get_mfg_prod_sn_str(rdip, name, MAXNAMELEN);
22929430SRaymond.Chen@Sun.COM 		if (name[0] != '\0') {
22939430SRaymond.Chen@Sun.COM 			cmn_err(CE_CONT, "?\t%s\n", name);
22949430SRaymond.Chen@Sun.COM 		}
22959430SRaymond.Chen@Sun.COM 		kmem_free(name, MAXNAMELEN);
22969430SRaymond.Chen@Sun.COM 
22979430SRaymond.Chen@Sun.COM 		break;
22989430SRaymond.Chen@Sun.COM 	}
22999430SRaymond.Chen@Sun.COM 	default:
23009430SRaymond.Chen@Sun.COM 		/* pass to usba to handle */
23019430SRaymond.Chen@Sun.COM 		return (usba_bus_ctl(hubdip, rdip, op, arg, result));
23029430SRaymond.Chen@Sun.COM 	}
23039430SRaymond.Chen@Sun.COM 
23049430SRaymond.Chen@Sun.COM 	return (DDI_SUCCESS);
23059430SRaymond.Chen@Sun.COM }
23069430SRaymond.Chen@Sun.COM 
23079430SRaymond.Chen@Sun.COM /*
23089430SRaymond.Chen@Sun.COM  * bus enumeration entry points
23099430SRaymond.Chen@Sun.COM  *  Configures the named device(BUS_CONFIG_ONE) or all devices under
23109430SRaymond.Chen@Sun.COM  *  the nexus(BUS_CONFIG_ALL). Drives devinfo state to DS_READY,i.e.device
23119430SRaymond.Chen@Sun.COM  *  is fully operational.
23129430SRaymond.Chen@Sun.COM  *
23139430SRaymond.Chen@Sun.COM  *  This operation is driven from devfs(reading /devices), devctl, libdevinfo;
23149430SRaymond.Chen@Sun.COM  *  or from within the kernel to attach a boot device or layered underlying
23159430SRaymond.Chen@Sun.COM  *  driver.
23169430SRaymond.Chen@Sun.COM  */
23179430SRaymond.Chen@Sun.COM static int
hwahc_bus_config(dev_info_t * dip,uint_t flag,ddi_bus_config_op_t op,void * arg,dev_info_t ** child)23189430SRaymond.Chen@Sun.COM hwahc_bus_config(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
23199430SRaymond.Chen@Sun.COM 	void *arg, dev_info_t **child)
23209430SRaymond.Chen@Sun.COM {
23219430SRaymond.Chen@Sun.COM 	hwahc_state_t	*hwahcp;
23229430SRaymond.Chen@Sun.COM 	int		rval, circ;
23239430SRaymond.Chen@Sun.COM 
23249430SRaymond.Chen@Sun.COM 	if ((hwahcp = ddi_get_soft_state(hwahc_statep,
23259430SRaymond.Chen@Sun.COM 	    ddi_get_instance(dip))) == NULL) {
23269430SRaymond.Chen@Sun.COM 
23279430SRaymond.Chen@Sun.COM 		return (NDI_FAILURE);
23289430SRaymond.Chen@Sun.COM 	}
23299430SRaymond.Chen@Sun.COM 
23309430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
23319430SRaymond.Chen@Sun.COM 	    "hwahc_bus_config: op=%d", op);
23329430SRaymond.Chen@Sun.COM 
23339430SRaymond.Chen@Sun.COM 	if (hwahc_bus_config_debug) {
23349430SRaymond.Chen@Sun.COM 		flag |= NDI_DEVI_DEBUG;
23359430SRaymond.Chen@Sun.COM 	}
23369430SRaymond.Chen@Sun.COM 
23379430SRaymond.Chen@Sun.COM 	ndi_devi_enter(dip, &circ);
23389430SRaymond.Chen@Sun.COM 	rval = ndi_busop_bus_config(dip, flag, op, arg, child, 0);
23399430SRaymond.Chen@Sun.COM 	ndi_devi_exit(dip, circ);
23409430SRaymond.Chen@Sun.COM 
23419430SRaymond.Chen@Sun.COM 	return (rval);
23429430SRaymond.Chen@Sun.COM }
23439430SRaymond.Chen@Sun.COM 
23449430SRaymond.Chen@Sun.COM /*
23459430SRaymond.Chen@Sun.COM  * Unconfigures the named device or all devices under the nexus. The
23469430SRaymond.Chen@Sun.COM  * devinfo state is not DS_READY anymore.
23479430SRaymond.Chen@Sun.COM  * This operations is driven by modunload, devctl or DR branch removal or
23489430SRaymond.Chen@Sun.COM  * rem_drv(1M).
23499430SRaymond.Chen@Sun.COM  */
23509430SRaymond.Chen@Sun.COM static int
hwahc_bus_unconfig(dev_info_t * dip,uint_t flag,ddi_bus_config_op_t op,void * arg)23519430SRaymond.Chen@Sun.COM hwahc_bus_unconfig(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
23529430SRaymond.Chen@Sun.COM 	void *arg)
23539430SRaymond.Chen@Sun.COM {
23549430SRaymond.Chen@Sun.COM 	hwahc_state_t	*hwahcp;
23559430SRaymond.Chen@Sun.COM 	wusb_hc_data_t	*hc_data;
23569430SRaymond.Chen@Sun.COM 	dev_info_t	*cdip;
23579430SRaymond.Chen@Sun.COM 	usb_port_t	port;
23589430SRaymond.Chen@Sun.COM 	int		rval, circ;
23599430SRaymond.Chen@Sun.COM 
23609430SRaymond.Chen@Sun.COM 	if ((hwahcp = ddi_get_soft_state(hwahc_statep,
23619430SRaymond.Chen@Sun.COM 	    ddi_get_instance(dip))) == NULL) {
23629430SRaymond.Chen@Sun.COM 
23639430SRaymond.Chen@Sun.COM 		return (NDI_FAILURE);
23649430SRaymond.Chen@Sun.COM 	}
23659430SRaymond.Chen@Sun.COM 
23669430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
23679430SRaymond.Chen@Sun.COM 	    "hwahc_bus_unconfig: op=%d", op);
23689430SRaymond.Chen@Sun.COM 
23699430SRaymond.Chen@Sun.COM 	if (hwahc_bus_config_debug) {
23709430SRaymond.Chen@Sun.COM 		flag |= NDI_DEVI_DEBUG;
23719430SRaymond.Chen@Sun.COM 	}
23729430SRaymond.Chen@Sun.COM 
23739430SRaymond.Chen@Sun.COM 	if ((op == BUS_UNCONFIG_ALL) && (flag & NDI_AUTODETACH) == 0) {
23749430SRaymond.Chen@Sun.COM 		flag |= NDI_DEVI_REMOVE;
23759430SRaymond.Chen@Sun.COM 	}
23769430SRaymond.Chen@Sun.COM 
23779430SRaymond.Chen@Sun.COM 	/* serialize access */
23789430SRaymond.Chen@Sun.COM 	ndi_devi_enter(dip, &circ);
23799430SRaymond.Chen@Sun.COM 
23809430SRaymond.Chen@Sun.COM 	/* unconfig children, detach them */
23819430SRaymond.Chen@Sun.COM 	rval = ndi_busop_bus_unconfig(dip, flag, op, arg);
23829430SRaymond.Chen@Sun.COM 
23839430SRaymond.Chen@Sun.COM 	/* logically zap children's list */
23849430SRaymond.Chen@Sun.COM 	hc_data = &hwahcp->hwahc_hc_data;
23859430SRaymond.Chen@Sun.COM 
23869430SRaymond.Chen@Sun.COM 	mutex_enter(&hc_data->hc_mutex);
23879430SRaymond.Chen@Sun.COM 	for (port = 1; port <= hc_data->hc_num_ports; port++) {
23889430SRaymond.Chen@Sun.COM 		hc_data->hc_children_state[port] |= WUSB_CHILD_ZAP;
23899430SRaymond.Chen@Sun.COM 	}
23909430SRaymond.Chen@Sun.COM 	mutex_exit(&hc_data->hc_mutex);
23919430SRaymond.Chen@Sun.COM 
23929430SRaymond.Chen@Sun.COM 	/* fill in what's left */
23939430SRaymond.Chen@Sun.COM 	for (cdip = ddi_get_child(dip); cdip;
23949430SRaymond.Chen@Sun.COM 	    cdip = ddi_get_next_sibling(cdip)) {
23959430SRaymond.Chen@Sun.COM 		usba_device_t *usba_device = usba_get_usba_device(cdip);
23969430SRaymond.Chen@Sun.COM 
23979430SRaymond.Chen@Sun.COM 		if (usba_device == NULL) {
23989430SRaymond.Chen@Sun.COM 
23999430SRaymond.Chen@Sun.COM 			continue;
24009430SRaymond.Chen@Sun.COM 		}
24019430SRaymond.Chen@Sun.COM 		mutex_enter(&hc_data->hc_mutex);
24029430SRaymond.Chen@Sun.COM 		port = usba_device->usb_port;
24039430SRaymond.Chen@Sun.COM 		hc_data->hc_children_dips[port] = cdip;
24049430SRaymond.Chen@Sun.COM 		hc_data->hc_children_state[port] &= ~WUSB_CHILD_ZAP;
24059430SRaymond.Chen@Sun.COM 		mutex_exit(&hc_data->hc_mutex);
24069430SRaymond.Chen@Sun.COM 	}
24079430SRaymond.Chen@Sun.COM 
24089430SRaymond.Chen@Sun.COM 	/* physically zap the children we didn't find */
24099430SRaymond.Chen@Sun.COM 	mutex_enter(&hc_data->hc_mutex);
24109430SRaymond.Chen@Sun.COM 	for (port = 1; port <= hc_data->hc_num_ports; port++) {
24119430SRaymond.Chen@Sun.COM 		if (hc_data->hc_children_state[port] & WUSB_CHILD_ZAP) {
24129430SRaymond.Chen@Sun.COM 			wusb_dev_info_t		*dev_info;
24139430SRaymond.Chen@Sun.COM 			wusb_secrt_data_t	*csecrt_data;
24149797SRaymond.Chen@Sun.COM 			usba_device_t		*child_ud;
24159430SRaymond.Chen@Sun.COM 
24169430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L3(PRINT_MASK_EVENTS,
24179430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
24189430SRaymond.Chen@Sun.COM 			    "hwahc_bus_unconfig: physically zap port %d", port);
24199430SRaymond.Chen@Sun.COM 
24209797SRaymond.Chen@Sun.COM 			child_ud = hc_data->hc_usba_devices[port];
24219797SRaymond.Chen@Sun.COM 			mutex_exit(&hc_data->hc_mutex);
24229430SRaymond.Chen@Sun.COM 			/* zap the dip and usba_device structure as well */
24239797SRaymond.Chen@Sun.COM 			usba_free_usba_device(child_ud);
24249797SRaymond.Chen@Sun.COM 			mutex_enter(&hc_data->hc_mutex);
24259430SRaymond.Chen@Sun.COM 			hc_data->hc_usba_devices[port] = NULL;
24269430SRaymond.Chen@Sun.COM 
24279430SRaymond.Chen@Sun.COM 			/* dip freed in usba_destroy_child_devi */
24289430SRaymond.Chen@Sun.COM 			hc_data->hc_children_dips[port] = NULL;
24299430SRaymond.Chen@Sun.COM 			hc_data->hc_children_state[port] &= ~WUSB_CHILD_ZAP;
24309430SRaymond.Chen@Sun.COM 
24319430SRaymond.Chen@Sun.COM 			/* free hc_dev_infos[port] */
24329430SRaymond.Chen@Sun.COM 			dev_info = hc_data->hc_dev_infos[port];
24339430SRaymond.Chen@Sun.COM 			if (dev_info == NULL) {
24349430SRaymond.Chen@Sun.COM 
24359430SRaymond.Chen@Sun.COM 				continue;
24369430SRaymond.Chen@Sun.COM 			}
24379430SRaymond.Chen@Sun.COM 
24389430SRaymond.Chen@Sun.COM 			/* stop the device's trust timer before deallocate it */
24399430SRaymond.Chen@Sun.COM 			hwahc_stop_trust_timer(dev_info);
24409430SRaymond.Chen@Sun.COM 
24419430SRaymond.Chen@Sun.COM 			if (dev_info->wdev_secrt_data.secrt_encry_descr) {
24429430SRaymond.Chen@Sun.COM 				csecrt_data = &dev_info->wdev_secrt_data;
24439430SRaymond.Chen@Sun.COM 				kmem_free(csecrt_data->secrt_encry_descr,
24449430SRaymond.Chen@Sun.COM 				    sizeof (usb_encryption_descr_t) *
24459430SRaymond.Chen@Sun.COM 				    csecrt_data->secrt_n_encry);
24469430SRaymond.Chen@Sun.COM 			}
24479430SRaymond.Chen@Sun.COM 			if (dev_info->wdev_uwb_descr) {
24489430SRaymond.Chen@Sun.COM 				kmem_free(dev_info->wdev_uwb_descr,
24499430SRaymond.Chen@Sun.COM 				    sizeof (usb_uwb_cap_descr_t));
24509430SRaymond.Chen@Sun.COM 			}
24519430SRaymond.Chen@Sun.COM 			kmem_free(dev_info, sizeof (wusb_dev_info_t));
24529430SRaymond.Chen@Sun.COM 			hc_data->hc_dev_infos[port] = NULL;
24539430SRaymond.Chen@Sun.COM 		}
24549430SRaymond.Chen@Sun.COM 	}
24559430SRaymond.Chen@Sun.COM 	mutex_exit(&hc_data->hc_mutex);
24569430SRaymond.Chen@Sun.COM 
24579430SRaymond.Chen@Sun.COM 	ndi_devi_exit(dip, circ);
24589430SRaymond.Chen@Sun.COM 
24599430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
24609430SRaymond.Chen@Sun.COM 	    "hwahc_bus_unconfig: rval=%d", rval);
24619430SRaymond.Chen@Sun.COM 
24629430SRaymond.Chen@Sun.COM 	return (rval);
24639430SRaymond.Chen@Sun.COM }
24649430SRaymond.Chen@Sun.COM 
24659430SRaymond.Chen@Sun.COM /*
24669430SRaymond.Chen@Sun.COM  * busctl event support
24679430SRaymond.Chen@Sun.COM  *
24689430SRaymond.Chen@Sun.COM  * Called by ndi_busop_get_eventcookie(). Return a event cookie
24699430SRaymond.Chen@Sun.COM  * associated with one event name.
24709430SRaymond.Chen@Sun.COM  * The eventname should be the one we defined in hwahc_ndi_event_defs
24719430SRaymond.Chen@Sun.COM  */
24729430SRaymond.Chen@Sun.COM static int
hwahc_busop_get_eventcookie(dev_info_t * dip,dev_info_t * rdip,char * eventname,ddi_eventcookie_t * cookie)24739430SRaymond.Chen@Sun.COM hwahc_busop_get_eventcookie(dev_info_t *dip,
24749430SRaymond.Chen@Sun.COM 	dev_info_t	*rdip,
24759430SRaymond.Chen@Sun.COM 	char		*eventname,
24769430SRaymond.Chen@Sun.COM 	ddi_eventcookie_t *cookie)
24779430SRaymond.Chen@Sun.COM {
24789430SRaymond.Chen@Sun.COM 	hwahc_state_t	*hwahcp;
24799430SRaymond.Chen@Sun.COM 
24809430SRaymond.Chen@Sun.COM 	if ((hwahcp = ddi_get_soft_state(hwahc_statep,
24819430SRaymond.Chen@Sun.COM 	    ddi_get_instance(dip))) == NULL) {
24829430SRaymond.Chen@Sun.COM 
24839430SRaymond.Chen@Sun.COM 		return (NDI_FAILURE);
24849430SRaymond.Chen@Sun.COM 	}
24859430SRaymond.Chen@Sun.COM 
24869430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
24879430SRaymond.Chen@Sun.COM 	    "hwahc_busop_get_eventcookie: dip=0x%p, rdip=0x%p, "
24889430SRaymond.Chen@Sun.COM 	    "event=%s", (void *)dip, (void *)rdip, eventname);
24899430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
24909430SRaymond.Chen@Sun.COM 	    "(dip=%s%d, rdip=%s%d)",
24919430SRaymond.Chen@Sun.COM 	    ddi_driver_name(dip), ddi_get_instance(dip),
24929430SRaymond.Chen@Sun.COM 	    ddi_driver_name(rdip), ddi_get_instance(rdip));
24939430SRaymond.Chen@Sun.COM 
24949430SRaymond.Chen@Sun.COM 	/* return event cookie, iblock cookie, and level */
24959430SRaymond.Chen@Sun.COM 	return (ndi_event_retrieve_cookie(hwahcp->hwahc_ndi_event_hdl,
24969430SRaymond.Chen@Sun.COM 	    rdip, eventname, cookie, NDI_EVENT_NOPASS));
24979430SRaymond.Chen@Sun.COM }
24989430SRaymond.Chen@Sun.COM 
24999430SRaymond.Chen@Sun.COM /*
25009430SRaymond.Chen@Sun.COM  * Add event handler for a given event cookie
25019430SRaymond.Chen@Sun.COM  */
25029430SRaymond.Chen@Sun.COM 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)25039430SRaymond.Chen@Sun.COM hwahc_busop_add_eventcall(dev_info_t *dip,
25049430SRaymond.Chen@Sun.COM 	dev_info_t	*rdip,
25059430SRaymond.Chen@Sun.COM 	ddi_eventcookie_t cookie,
25069430SRaymond.Chen@Sun.COM 	void		(*callback)(dev_info_t *dip,
25079430SRaymond.Chen@Sun.COM 			ddi_eventcookie_t cookie, void *arg,
25089430SRaymond.Chen@Sun.COM 			void *bus_impldata),
25099430SRaymond.Chen@Sun.COM 	void *arg, ddi_callback_id_t *cb_id)
25109430SRaymond.Chen@Sun.COM {
25119430SRaymond.Chen@Sun.COM 	hwahc_state_t	*hwahcp;
25129430SRaymond.Chen@Sun.COM 	usb_port_t	port;
25139430SRaymond.Chen@Sun.COM 
25149430SRaymond.Chen@Sun.COM 	if ((hwahcp = ddi_get_soft_state(hwahc_statep,
25159430SRaymond.Chen@Sun.COM 	    ddi_get_instance(dip))) == NULL) {
25169430SRaymond.Chen@Sun.COM 
25179430SRaymond.Chen@Sun.COM 		return (NDI_FAILURE);
25189430SRaymond.Chen@Sun.COM 	}
25199430SRaymond.Chen@Sun.COM 
25209430SRaymond.Chen@Sun.COM 	port = hwahc_child_dip2port(hwahcp, rdip);
25219430SRaymond.Chen@Sun.COM 
25229430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
25239430SRaymond.Chen@Sun.COM 
25249430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
25259430SRaymond.Chen@Sun.COM 	    "hwahc_busop_add_eventcall: dip=0x%p, rdip=0x%p "
25269430SRaymond.Chen@Sun.COM 	    "cookie=0x%p, cb=0x%p, arg=0x%p",
25279430SRaymond.Chen@Sun.COM 	    (void *)dip, (void *)rdip, (void *)cookie, (void *)callback, arg);
25289430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
25299430SRaymond.Chen@Sun.COM 	    "(dip=%s%d, rdip=%s%d, event=%s)",
25309430SRaymond.Chen@Sun.COM 	    ddi_driver_name(dip), ddi_get_instance(dip),
25319430SRaymond.Chen@Sun.COM 	    ddi_driver_name(rdip), ddi_get_instance(rdip),
25329430SRaymond.Chen@Sun.COM 	    ndi_event_cookie_to_name(hwahcp->hwahc_ndi_event_hdl, cookie));
25339430SRaymond.Chen@Sun.COM 
25349430SRaymond.Chen@Sun.COM 	/* Set flag on children registering events */
25359430SRaymond.Chen@Sun.COM 	switch (ndi_event_cookie_to_tag(hwahcp->hwahc_ndi_event_hdl, cookie)) {
25369430SRaymond.Chen@Sun.COM 	case USBA_EVENT_TAG_HOT_REMOVAL:
25379430SRaymond.Chen@Sun.COM 		hwahcp->hwahc_child_events[port] |=
25389430SRaymond.Chen@Sun.COM 		    HWAHC_CHILD_EVENT_DISCONNECT;
25399430SRaymond.Chen@Sun.COM 
25409430SRaymond.Chen@Sun.COM 		break;
25419430SRaymond.Chen@Sun.COM 	case USBA_EVENT_TAG_PRE_SUSPEND:
25429430SRaymond.Chen@Sun.COM 		hwahcp->hwahc_child_events[port] |=
25439430SRaymond.Chen@Sun.COM 		    HWAHC_CHILD_EVENT_PRESUSPEND;
25449430SRaymond.Chen@Sun.COM 
25459430SRaymond.Chen@Sun.COM 		break;
25469430SRaymond.Chen@Sun.COM 	default:
25479430SRaymond.Chen@Sun.COM 
25489430SRaymond.Chen@Sun.COM 		break;
25499430SRaymond.Chen@Sun.COM 	}
25509430SRaymond.Chen@Sun.COM 
25519430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
25529430SRaymond.Chen@Sun.COM 
25539430SRaymond.Chen@Sun.COM 	/* add callback to our event set */
25549430SRaymond.Chen@Sun.COM 	return (ndi_event_add_callback(hwahcp->hwahc_ndi_event_hdl,
25559430SRaymond.Chen@Sun.COM 	    rdip, cookie, callback, arg, NDI_SLEEP, cb_id));
25569430SRaymond.Chen@Sun.COM 
25579430SRaymond.Chen@Sun.COM }
25589430SRaymond.Chen@Sun.COM 
25599430SRaymond.Chen@Sun.COM 
25609430SRaymond.Chen@Sun.COM /*
25619430SRaymond.Chen@Sun.COM  * Remove a callback previously added by bus_add_eventcall()
25629430SRaymond.Chen@Sun.COM  */
25639430SRaymond.Chen@Sun.COM static int
hwahc_busop_remove_eventcall(dev_info_t * dip,ddi_callback_id_t cb_id)25649430SRaymond.Chen@Sun.COM hwahc_busop_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id)
25659430SRaymond.Chen@Sun.COM {
25669430SRaymond.Chen@Sun.COM 	hwahc_state_t	*hwahcp;
25679430SRaymond.Chen@Sun.COM 	ndi_event_callbacks_t *id = (ndi_event_callbacks_t *)cb_id;
25689430SRaymond.Chen@Sun.COM 
25699430SRaymond.Chen@Sun.COM 	if ((hwahcp = ddi_get_soft_state(hwahc_statep,
25709430SRaymond.Chen@Sun.COM 	    ddi_get_instance(dip))) == NULL) {
25719430SRaymond.Chen@Sun.COM 
25729430SRaymond.Chen@Sun.COM 		return (NDI_FAILURE);
25739430SRaymond.Chen@Sun.COM 	}
25749430SRaymond.Chen@Sun.COM 
25759430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
25769430SRaymond.Chen@Sun.COM 	    "hwahc_busop_remove_eventcall: dip=0x%p, rdip=0x%p "
25779430SRaymond.Chen@Sun.COM 	    "cookie=0x%p", (void *)dip, (void *) id->ndi_evtcb_dip,
25789430SRaymond.Chen@Sun.COM 	    (void *)id->ndi_evtcb_cookie);
25799430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
25809430SRaymond.Chen@Sun.COM 	    "(dip=%s%d, rdip=%s%d, event=%s)",
25819430SRaymond.Chen@Sun.COM 	    ddi_driver_name(dip), ddi_get_instance(dip),
25829430SRaymond.Chen@Sun.COM 	    ddi_driver_name(id->ndi_evtcb_dip),
25839430SRaymond.Chen@Sun.COM 	    ddi_get_instance(id->ndi_evtcb_dip),
25849430SRaymond.Chen@Sun.COM 	    ndi_event_cookie_to_name(hwahcp->hwahc_ndi_event_hdl,
25859430SRaymond.Chen@Sun.COM 	    id->ndi_evtcb_cookie));
25869430SRaymond.Chen@Sun.COM 
25879430SRaymond.Chen@Sun.COM 	/* remove event registration from our event set */
25889430SRaymond.Chen@Sun.COM 	return (ndi_event_remove_callback(hwahcp->hwahc_ndi_event_hdl, cb_id));
25899430SRaymond.Chen@Sun.COM }
25909430SRaymond.Chen@Sun.COM 
25919430SRaymond.Chen@Sun.COM /*
25929430SRaymond.Chen@Sun.COM  * hwahc_post_event
25939430SRaymond.Chen@Sun.COM  *	post event to a single child on the port depending on the type, i.e.
25949430SRaymond.Chen@Sun.COM  *	to invoke the child's registered callback.
25959430SRaymond.Chen@Sun.COM  */
25969430SRaymond.Chen@Sun.COM static void
hwahc_post_event(hwahc_state_t * hwahcp,usb_port_t port,usba_event_t type)25979430SRaymond.Chen@Sun.COM hwahc_post_event(hwahc_state_t *hwahcp, usb_port_t port, usba_event_t type)
25989430SRaymond.Chen@Sun.COM {
25999430SRaymond.Chen@Sun.COM 	int		rval;
26009430SRaymond.Chen@Sun.COM 	dev_info_t	*dip;
26019430SRaymond.Chen@Sun.COM 	usba_device_t	*usba_device;
26029430SRaymond.Chen@Sun.COM 	ddi_eventcookie_t cookie, rm_cookie, suspend_cookie;
26039430SRaymond.Chen@Sun.COM 	wusb_hc_data_t	*hc_data = &hwahcp->hwahc_hc_data;
26049430SRaymond.Chen@Sun.COM 
26059430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
26069430SRaymond.Chen@Sun.COM 	    "hwahc_post_event: port=%d event=%s", port,
26079430SRaymond.Chen@Sun.COM 	    ndi_event_tag_to_name(hwahcp->hwahc_ndi_event_hdl, type));
26089430SRaymond.Chen@Sun.COM 
26099430SRaymond.Chen@Sun.COM 	cookie = ndi_event_tag_to_cookie(hwahcp->hwahc_ndi_event_hdl, type);
26109430SRaymond.Chen@Sun.COM 	rm_cookie = ndi_event_tag_to_cookie(hwahcp->hwahc_ndi_event_hdl,
26119430SRaymond.Chen@Sun.COM 	    USBA_EVENT_TAG_HOT_REMOVAL);
26129430SRaymond.Chen@Sun.COM 	suspend_cookie = ndi_event_tag_to_cookie(hwahcp->hwahc_ndi_event_hdl,
26139430SRaymond.Chen@Sun.COM 	    USBA_EVENT_TAG_PRE_SUSPEND);
26149430SRaymond.Chen@Sun.COM 
26159430SRaymond.Chen@Sun.COM 	/*
26169430SRaymond.Chen@Sun.COM 	 * Hotplug daemon may be attaching a driver that may be registering
26179430SRaymond.Chen@Sun.COM 	 * event callbacks. So it already has got the device tree lock and
26189430SRaymond.Chen@Sun.COM 	 * event handle mutex. So to prevent a deadlock while posting events,
26199430SRaymond.Chen@Sun.COM 	 * we grab and release the locks in the same order.
26209430SRaymond.Chen@Sun.COM 	 */
26219430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
26229430SRaymond.Chen@Sun.COM 	dip = hwahcp->hwahc_hc_data.hc_children_dips[port];
26239430SRaymond.Chen@Sun.COM 	usba_device = hwahcp->hwahc_hc_data.hc_usba_devices[port];
26249430SRaymond.Chen@Sun.COM 	mutex_exit((&hwahcp->hwahc_mutex));
26259430SRaymond.Chen@Sun.COM 
26269430SRaymond.Chen@Sun.COM 	switch (type) {
26279430SRaymond.Chen@Sun.COM 	case USBA_EVENT_TAG_HOT_REMOVAL:
26289430SRaymond.Chen@Sun.COM 		/* stop this device's timer to prevent its further process */
26299430SRaymond.Chen@Sun.COM 		mutex_enter(&hc_data->hc_mutex);
26309430SRaymond.Chen@Sun.COM 
26319430SRaymond.Chen@Sun.COM 		hwahc_stop_trust_timer(hc_data->hc_dev_infos[port]);
26329430SRaymond.Chen@Sun.COM 		mutex_exit(&hc_data->hc_mutex);
26339430SRaymond.Chen@Sun.COM 
26349430SRaymond.Chen@Sun.COM 		/* Clear the registered event flag */
26359430SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
26369430SRaymond.Chen@Sun.COM 		hwahcp->hwahc_child_events[port] &=
26379430SRaymond.Chen@Sun.COM 		    ~HWAHC_CHILD_EVENT_DISCONNECT;
26389430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
26399430SRaymond.Chen@Sun.COM 
26409430SRaymond.Chen@Sun.COM 		(void) ndi_event_do_callback(hwahcp->hwahc_ndi_event_hdl,
26419430SRaymond.Chen@Sun.COM 		    dip, cookie, NULL);
26429430SRaymond.Chen@Sun.COM 		usba_persistent_pipe_close(usba_device);
26439430SRaymond.Chen@Sun.COM 
26449430SRaymond.Chen@Sun.COM 		/*
26459430SRaymond.Chen@Sun.COM 		 * Mark the dip for deletion only after the driver has
26469430SRaymond.Chen@Sun.COM 		 * seen the disconnect event to prevent cleanup thread
26479430SRaymond.Chen@Sun.COM 		 * from stepping in between.
26489430SRaymond.Chen@Sun.COM 		 */
26499430SRaymond.Chen@Sun.COM #ifndef __lock_lint
26509430SRaymond.Chen@Sun.COM 		mutex_enter(&DEVI(dip)->devi_lock);
26519430SRaymond.Chen@Sun.COM 		DEVI_SET_DEVICE_REMOVED(dip);
26529430SRaymond.Chen@Sun.COM 		mutex_exit(&DEVI(dip)->devi_lock);
26539430SRaymond.Chen@Sun.COM #endif
26549430SRaymond.Chen@Sun.COM 
26559430SRaymond.Chen@Sun.COM 		break;
26569430SRaymond.Chen@Sun.COM 	case USBA_EVENT_TAG_PRE_SUSPEND:
26579430SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
26589430SRaymond.Chen@Sun.COM 		hwahcp->hwahc_child_events[port] &=
26599430SRaymond.Chen@Sun.COM 		    ~HWAHC_CHILD_EVENT_PRESUSPEND;
26609430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
26619430SRaymond.Chen@Sun.COM 
26629430SRaymond.Chen@Sun.COM 		(void) ndi_event_do_callback(hwahcp->hwahc_ndi_event_hdl,
26639430SRaymond.Chen@Sun.COM 		    dip, cookie, NULL);
26649430SRaymond.Chen@Sun.COM 		/*
26659430SRaymond.Chen@Sun.COM 		 * persistent pipe close for this event is taken care by the
26669430SRaymond.Chen@Sun.COM 		 * caller after verfying that all children can suspend
26679430SRaymond.Chen@Sun.COM 		 */
26689430SRaymond.Chen@Sun.COM 
26699430SRaymond.Chen@Sun.COM 		break;
26709430SRaymond.Chen@Sun.COM 	case USBA_EVENT_TAG_HOT_INSERTION:
26719430SRaymond.Chen@Sun.COM 		/*
26729430SRaymond.Chen@Sun.COM 		 * Check if this child has missed the disconnect event before
26739430SRaymond.Chen@Sun.COM 		 * it registered for event callbacks
26749430SRaymond.Chen@Sun.COM 		 */
26759430SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
26769430SRaymond.Chen@Sun.COM 		if (hwahcp->hwahc_child_events[port] &
26779430SRaymond.Chen@Sun.COM 		    HWAHC_CHILD_EVENT_DISCONNECT) {
26789430SRaymond.Chen@Sun.COM 			/* clear the flag and post disconnect event */
26799430SRaymond.Chen@Sun.COM 			hwahcp->hwahc_child_events[port] &=
26809430SRaymond.Chen@Sun.COM 			    ~HWAHC_CHILD_EVENT_DISCONNECT;
26819430SRaymond.Chen@Sun.COM 			mutex_exit(&hwahcp->hwahc_mutex);
26829430SRaymond.Chen@Sun.COM 
26839430SRaymond.Chen@Sun.COM 			(void) ndi_event_do_callback(
26849430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_ndi_event_hdl,
26859430SRaymond.Chen@Sun.COM 			    dip, rm_cookie, NULL);
26869430SRaymond.Chen@Sun.COM 			usba_persistent_pipe_close(usba_device);
26879430SRaymond.Chen@Sun.COM 			mutex_enter(&hwahcp->hwahc_mutex);
26889430SRaymond.Chen@Sun.COM 		}
26899430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
26909430SRaymond.Chen@Sun.COM 
26919430SRaymond.Chen@Sun.COM 		/*
26929430SRaymond.Chen@Sun.COM 		 * Mark the dip as reinserted to prevent cleanup thread
26939430SRaymond.Chen@Sun.COM 		 * from stepping in.
26949430SRaymond.Chen@Sun.COM 		 */
26959430SRaymond.Chen@Sun.COM #ifndef __lock_lint
26969430SRaymond.Chen@Sun.COM 		mutex_enter(&(DEVI(dip)->devi_lock));
26979430SRaymond.Chen@Sun.COM 		DEVI_SET_DEVICE_REINSERTED(dip);
26989430SRaymond.Chen@Sun.COM 		mutex_exit(&(DEVI(dip)->devi_lock));
26999430SRaymond.Chen@Sun.COM #endif
27009430SRaymond.Chen@Sun.COM 
27019430SRaymond.Chen@Sun.COM 		rval = usba_persistent_pipe_open(usba_device);
27029430SRaymond.Chen@Sun.COM 		if (rval != USB_SUCCESS) {
27039430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_EVENTS,
27049430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
27059430SRaymond.Chen@Sun.COM 			    "failed to reopen all pipes on reconnect");
27069430SRaymond.Chen@Sun.COM 		}
27079430SRaymond.Chen@Sun.COM 
27089430SRaymond.Chen@Sun.COM 		(void) ndi_event_do_callback(hwahcp->hwahc_ndi_event_hdl,
27099430SRaymond.Chen@Sun.COM 		    dip, cookie, NULL);
27109430SRaymond.Chen@Sun.COM 
27119430SRaymond.Chen@Sun.COM 		/*
27129430SRaymond.Chen@Sun.COM 		 * We might see a connect event only if hotplug thread for
27139430SRaymond.Chen@Sun.COM 		 * disconnect event don't run in time.
27149430SRaymond.Chen@Sun.COM 		 * Set the flag again, so we don't miss posting a
27159430SRaymond.Chen@Sun.COM 		 * disconnect event.
27169430SRaymond.Chen@Sun.COM 		 */
27179430SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
27189430SRaymond.Chen@Sun.COM 		hwahcp->hwahc_child_events[port] |=
27199430SRaymond.Chen@Sun.COM 		    HWAHC_CHILD_EVENT_DISCONNECT;
27209430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
27219430SRaymond.Chen@Sun.COM 
27229430SRaymond.Chen@Sun.COM 		break;
27239430SRaymond.Chen@Sun.COM 	case USBA_EVENT_TAG_POST_RESUME:
27249430SRaymond.Chen@Sun.COM 		/*
27259430SRaymond.Chen@Sun.COM 		 * Check if this child has missed the pre-suspend event before
27269430SRaymond.Chen@Sun.COM 		 * it registered for event callbacks
27279430SRaymond.Chen@Sun.COM 		 */
27289430SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
27299430SRaymond.Chen@Sun.COM 		if (hwahcp->hwahc_child_events[port] &
27309430SRaymond.Chen@Sun.COM 		    HWAHC_CHILD_EVENT_PRESUSPEND) {
27319430SRaymond.Chen@Sun.COM 			/* clear the flag and post pre_suspend event */
27329430SRaymond.Chen@Sun.COM 			hwahcp->hwahc_child_events[port] &=
27339430SRaymond.Chen@Sun.COM 			    ~HWAHC_CHILD_EVENT_PRESUSPEND;
27349430SRaymond.Chen@Sun.COM 			mutex_exit(&hwahcp->hwahc_mutex);
27359430SRaymond.Chen@Sun.COM 			(void) ndi_event_do_callback(
27369430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_ndi_event_hdl,
27379430SRaymond.Chen@Sun.COM 			    dip, suspend_cookie, NULL);
27389430SRaymond.Chen@Sun.COM 			mutex_enter(&hwahcp->hwahc_mutex);
27399430SRaymond.Chen@Sun.COM 		}
27409430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
27419430SRaymond.Chen@Sun.COM 
27429430SRaymond.Chen@Sun.COM 		mutex_enter(&usba_device->usb_mutex);
27439430SRaymond.Chen@Sun.COM 		usba_device->usb_no_cpr = 0;
27449430SRaymond.Chen@Sun.COM 		mutex_exit(&usba_device->usb_mutex);
27459430SRaymond.Chen@Sun.COM 
27469430SRaymond.Chen@Sun.COM 		/*
27479430SRaymond.Chen@Sun.COM 		 * Since the pipe has already been opened by whub
27489430SRaymond.Chen@Sun.COM 		 * at DDI_RESUME time, there is no need for a
27499430SRaymond.Chen@Sun.COM 		 * persistent pipe open
27509430SRaymond.Chen@Sun.COM 		 */
27519430SRaymond.Chen@Sun.COM 		(void) ndi_event_do_callback(hwahcp->hwahc_ndi_event_hdl,
27529430SRaymond.Chen@Sun.COM 		    dip, cookie, NULL);
27539430SRaymond.Chen@Sun.COM 
27549430SRaymond.Chen@Sun.COM 		/*
27559430SRaymond.Chen@Sun.COM 		 * Set the flag again, so we don't miss posting a
27569430SRaymond.Chen@Sun.COM 		 * pre-suspend event. This enforces a tighter
27579430SRaymond.Chen@Sun.COM 		 * dev_state model.
27589430SRaymond.Chen@Sun.COM 		 */
27599430SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
27609430SRaymond.Chen@Sun.COM 		hwahcp->hwahc_child_events[port] |=
27619430SRaymond.Chen@Sun.COM 		    HWAHC_CHILD_EVENT_PRESUSPEND;
27629430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
27639430SRaymond.Chen@Sun.COM 		break;
27649430SRaymond.Chen@Sun.COM 	}
27659430SRaymond.Chen@Sun.COM }
27669430SRaymond.Chen@Sun.COM 
27679430SRaymond.Chen@Sun.COM /*
27689430SRaymond.Chen@Sun.COM  * hwahc_run_callbacks:
27699430SRaymond.Chen@Sun.COM  *	Send an event to all children
27709430SRaymond.Chen@Sun.COM  */
27719430SRaymond.Chen@Sun.COM static void
hwahc_run_callbacks(hwahc_state_t * hwahcp,usba_event_t type)27729430SRaymond.Chen@Sun.COM hwahc_run_callbacks(hwahc_state_t *hwahcp, usba_event_t type)
27739430SRaymond.Chen@Sun.COM {
27749430SRaymond.Chen@Sun.COM 	usb_port_t	port;
27759430SRaymond.Chen@Sun.COM 	wusb_hc_data_t	*hc_data = &hwahcp->hwahc_hc_data;
27769430SRaymond.Chen@Sun.COM 
27779430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
27789430SRaymond.Chen@Sun.COM 	    "hwahc_run_callbacks:");
27799430SRaymond.Chen@Sun.COM 
27809430SRaymond.Chen@Sun.COM 	mutex_enter(&hc_data->hc_mutex);
27819430SRaymond.Chen@Sun.COM 	for (port = 1; port <= hc_data->hc_num_ports; port++) {
27829430SRaymond.Chen@Sun.COM 		if (hc_data->hc_children_dips[port]) {
27839430SRaymond.Chen@Sun.COM 			mutex_exit(&hc_data->hc_mutex);
27849430SRaymond.Chen@Sun.COM 			hwahc_post_event(hwahcp, port, type);
27859430SRaymond.Chen@Sun.COM 			mutex_enter(&hc_data->hc_mutex);
27869430SRaymond.Chen@Sun.COM 		}
27879430SRaymond.Chen@Sun.COM 	}
27889430SRaymond.Chen@Sun.COM 	mutex_exit(&hc_data->hc_mutex);
27899430SRaymond.Chen@Sun.COM }
27909430SRaymond.Chen@Sun.COM 
27919430SRaymond.Chen@Sun.COM /*
27929430SRaymond.Chen@Sun.COM  * hwahc_disconnect_event_cb:
27939430SRaymond.Chen@Sun.COM  *	Called when hwa device hotplug-removed.
27949430SRaymond.Chen@Sun.COM  *		Close pipes
27959430SRaymond.Chen@Sun.COM  *		Post event to child
27969430SRaymond.Chen@Sun.COM  *		Set state to DISCONNECTED
27979430SRaymond.Chen@Sun.COM  */
27989430SRaymond.Chen@Sun.COM static int
hwahc_disconnect_event_cb(dev_info_t * dip)27999430SRaymond.Chen@Sun.COM hwahc_disconnect_event_cb(dev_info_t *dip)
28009430SRaymond.Chen@Sun.COM {
28019430SRaymond.Chen@Sun.COM 	int		instance = ddi_get_instance(dip);
28029430SRaymond.Chen@Sun.COM 	hwahc_state_t	*hwahcp;
28039430SRaymond.Chen@Sun.COM 	int		circ;
28049430SRaymond.Chen@Sun.COM 
28059430SRaymond.Chen@Sun.COM 	if ((hwahcp = ddi_get_soft_state(hwahc_statep, instance)) == NULL) {
28069430SRaymond.Chen@Sun.COM 
28079430SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
28089430SRaymond.Chen@Sun.COM 	}
28099430SRaymond.Chen@Sun.COM 
28109430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
28119430SRaymond.Chen@Sun.COM 	    "hwahc_disconnect_event_cb: dip = 0x%p", (void *)dip);
28129430SRaymond.Chen@Sun.COM 
28139430SRaymond.Chen@Sun.COM 	ndi_devi_enter(dip, &circ);
28149430SRaymond.Chen@Sun.COM 
28159430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
28169430SRaymond.Chen@Sun.COM 
28179430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
28189430SRaymond.Chen@Sun.COM 	    "hwahc_disconnect_event_cb: devstate= %d hw-state=%d",
28199430SRaymond.Chen@Sun.COM 	    hwahcp->hwahc_dev_state, hwahcp->hwahc_hw_state);
28209430SRaymond.Chen@Sun.COM 	switch (hwahcp->hwahc_dev_state) {
28219430SRaymond.Chen@Sun.COM 	case USB_DEV_ONLINE:
28229430SRaymond.Chen@Sun.COM 	case USB_DEV_PWRED_DOWN:
28239430SRaymond.Chen@Sun.COM 		hwahcp->hwahc_dev_state = USB_DEV_DISCONNECTED;
28249430SRaymond.Chen@Sun.COM 
28259430SRaymond.Chen@Sun.COM 		if (hwahcp->hwahc_hw_state != HWAHC_HW_STOPPED) {
28269430SRaymond.Chen@Sun.COM 			mutex_exit(&hwahcp->hwahc_mutex);
28279430SRaymond.Chen@Sun.COM 			wusb_wa_stop_nep(&hwahcp->hwahc_wa_data);
28289430SRaymond.Chen@Sun.COM 			mutex_enter(&hwahcp->hwahc_mutex);
28299430SRaymond.Chen@Sun.COM 			hwahc_stop_result_thread(hwahcp);
28309430SRaymond.Chen@Sun.COM 			hwahc_drain_notif_queue(hwahcp);
28319430SRaymond.Chen@Sun.COM 		}
28329430SRaymond.Chen@Sun.COM 		/* FALLTHROUGH */
28339430SRaymond.Chen@Sun.COM 	case USB_DEV_SUSPENDED:
28349430SRaymond.Chen@Sun.COM 		/* remain in this state */
28359430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
28369430SRaymond.Chen@Sun.COM 		hwahc_run_callbacks(hwahcp, USBA_EVENT_TAG_HOT_REMOVAL);
28379430SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
28389430SRaymond.Chen@Sun.COM 
28399430SRaymond.Chen@Sun.COM 		hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_INIT_STATE;
28409430SRaymond.Chen@Sun.COM 
28419430SRaymond.Chen@Sun.COM 		break;
28429430SRaymond.Chen@Sun.COM 	case USB_DEV_DISCONNECTED:
28439430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
28449430SRaymond.Chen@Sun.COM 		    "hwahc_disconnect_event_cb: already disconnected");
28459430SRaymond.Chen@Sun.COM 
28469430SRaymond.Chen@Sun.COM 		break;
28479430SRaymond.Chen@Sun.COM 	default:
28489430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
28499430SRaymond.Chen@Sun.COM 		    "hwahc_disconnect_event_cb: illegal devstate=%d",
28509430SRaymond.Chen@Sun.COM 		    hwahcp->hwahc_dev_state);
28519430SRaymond.Chen@Sun.COM 
28529430SRaymond.Chen@Sun.COM 		break;
28539430SRaymond.Chen@Sun.COM 	}
28549430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
28559430SRaymond.Chen@Sun.COM 
28569430SRaymond.Chen@Sun.COM 	ndi_devi_exit(dip, circ);
28579430SRaymond.Chen@Sun.COM 
28589430SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
28599430SRaymond.Chen@Sun.COM }
28609430SRaymond.Chen@Sun.COM 
28619430SRaymond.Chen@Sun.COM 
28629430SRaymond.Chen@Sun.COM /*
28639430SRaymond.Chen@Sun.COM  * hwahc_reconnect_event_cb:
28649430SRaymond.Chen@Sun.COM  *	Called with device hotplug-inserted
28659430SRaymond.Chen@Sun.COM  *		Restore state
28669430SRaymond.Chen@Sun.COM  */
28679430SRaymond.Chen@Sun.COM static int
hwahc_reconnect_event_cb(dev_info_t * dip)28689430SRaymond.Chen@Sun.COM hwahc_reconnect_event_cb(dev_info_t *dip)
28699430SRaymond.Chen@Sun.COM {
28709430SRaymond.Chen@Sun.COM 	int		instance = ddi_get_instance(dip);
28719430SRaymond.Chen@Sun.COM 	hwahc_state_t	*hwahcp;
28729430SRaymond.Chen@Sun.COM 	int		circ;
28739430SRaymond.Chen@Sun.COM 
28749430SRaymond.Chen@Sun.COM 
28759430SRaymond.Chen@Sun.COM 	if ((hwahcp = ddi_get_soft_state(hwahc_statep, instance)) == NULL) {
28769430SRaymond.Chen@Sun.COM 
28779430SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
28789430SRaymond.Chen@Sun.COM 	}
28799430SRaymond.Chen@Sun.COM 
28809430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
28819430SRaymond.Chen@Sun.COM 	    "hwahc_reconnect_event_cb: dip = 0x%p", (void *)dip);
28829430SRaymond.Chen@Sun.COM 
28839430SRaymond.Chen@Sun.COM 	ndi_devi_enter(dip, &circ);
28849430SRaymond.Chen@Sun.COM 	hwahc_restore_device_state(dip, hwahcp);
28859430SRaymond.Chen@Sun.COM 	ndi_devi_exit(dip, circ);
28869430SRaymond.Chen@Sun.COM 
28879430SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
28889430SRaymond.Chen@Sun.COM }
28899430SRaymond.Chen@Sun.COM 
28909430SRaymond.Chen@Sun.COM 
28919430SRaymond.Chen@Sun.COM /*
28929430SRaymond.Chen@Sun.COM  * hwahc_pre_suspend_event_cb:
28939430SRaymond.Chen@Sun.COM  *	Called before HWA device suspend
28949430SRaymond.Chen@Sun.COM  */
28959430SRaymond.Chen@Sun.COM static int
hwahc_pre_suspend_event_cb(dev_info_t * dip)28969430SRaymond.Chen@Sun.COM hwahc_pre_suspend_event_cb(dev_info_t *dip)
28979430SRaymond.Chen@Sun.COM {
28989430SRaymond.Chen@Sun.COM 	int		instance = ddi_get_instance(dip);
28999430SRaymond.Chen@Sun.COM 	hwahc_state_t	*hwahcp;
29009430SRaymond.Chen@Sun.COM 	int		circ;
29019430SRaymond.Chen@Sun.COM 
29029430SRaymond.Chen@Sun.COM 	if ((hwahcp = ddi_get_soft_state(hwahc_statep, instance)) == NULL) {
29039430SRaymond.Chen@Sun.COM 
29049430SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
29059430SRaymond.Chen@Sun.COM 	}
29069430SRaymond.Chen@Sun.COM 
29079430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
29089430SRaymond.Chen@Sun.COM 	    "hwahc_pre_suspend_event_cb: dip = 0x%p", (void *)dip);
29099430SRaymond.Chen@Sun.COM 
29109430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
29119430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
29129430SRaymond.Chen@Sun.COM 	    "hwahc_pre_suspend_event_cb: start, hw state = %d, softstate = %d",
29139430SRaymond.Chen@Sun.COM 	    hwahcp->hwahc_hw_state, hwahcp->hwahc_hc_soft_state);
29149430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
29159430SRaymond.Chen@Sun.COM 
29169430SRaymond.Chen@Sun.COM 	/* keep PM out till we see a cpr resume */
29179430SRaymond.Chen@Sun.COM 	(void) hwahc_pm_busy_component(hwahcp);
29189430SRaymond.Chen@Sun.COM 	(void) pm_raise_power(hwahcp->hwahc_dip, 0, USB_DEV_OS_FULL_PWR);
29199430SRaymond.Chen@Sun.COM 
29209430SRaymond.Chen@Sun.COM 	ndi_devi_enter(dip, &circ);
29219430SRaymond.Chen@Sun.COM 	hwahc_run_callbacks(hwahcp, USBA_EVENT_TAG_PRE_SUSPEND);
29229430SRaymond.Chen@Sun.COM 	ndi_devi_exit(dip, circ);
29239430SRaymond.Chen@Sun.COM 
29249430SRaymond.Chen@Sun.COM 	/*
29259430SRaymond.Chen@Sun.COM 	 * rc driver is always suspended first, that fails the hc suspend.
29269430SRaymond.Chen@Sun.COM 	 * need to suspend hc before rc is suspended, so move the suspend
29279430SRaymond.Chen@Sun.COM 	 * operations here
29289430SRaymond.Chen@Sun.COM 	 */
29299430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
29309430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_dev_state != USB_DEV_ONLINE) {
29319430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
29329430SRaymond.Chen@Sun.COM 		    "hwahc_pre_suspend_event_cb: dev_state = %d",
29339430SRaymond.Chen@Sun.COM 		    hwahcp->hwahc_dev_state);
29349430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
29359430SRaymond.Chen@Sun.COM 
29369430SRaymond.Chen@Sun.COM 		return (USB_SUCCESS);
29379430SRaymond.Chen@Sun.COM 	}
29389430SRaymond.Chen@Sun.COM 
29399430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_hw_state == HWAHC_HW_STARTED) {
29409430SRaymond.Chen@Sun.COM 		/*
29419430SRaymond.Chen@Sun.COM 		 * notify children the host is going to stop
29429430SRaymond.Chen@Sun.COM 		 */
29439430SRaymond.Chen@Sun.COM 		(void) hwahc_hc_channel_suspend(hwahcp);
29449430SRaymond.Chen@Sun.COM 	}
29459430SRaymond.Chen@Sun.COM 
29469430SRaymond.Chen@Sun.COM 	/* stop the hc from functioning */
29479430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_hw_state != HWAHC_HW_STOPPED) {
29489430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
29499430SRaymond.Chen@Sun.COM 		wusb_wa_stop_nep(&hwahcp->hwahc_wa_data);
29509430SRaymond.Chen@Sun.COM 
29519430SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
29529430SRaymond.Chen@Sun.COM 		hwahc_stop_result_thread(hwahcp);
29539430SRaymond.Chen@Sun.COM 		hwahc_drain_notif_queue(hwahcp);
29549430SRaymond.Chen@Sun.COM 
29559430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
29569430SRaymond.Chen@Sun.COM 		(void) wusb_wa_disable(&hwahcp->hwahc_wa_data,
29579430SRaymond.Chen@Sun.COM 		    hwahcp->hwahc_default_pipe);
29589430SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
29599430SRaymond.Chen@Sun.COM 
29609430SRaymond.Chen@Sun.COM 	}
29619430SRaymond.Chen@Sun.COM 
29629430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
29639430SRaymond.Chen@Sun.COM 	    "hwahc_pre_suspend_event_cb: end, devstate=%d "
29649430SRaymond.Chen@Sun.COM 	    "hwstate=%d softstate = %d",
29659430SRaymond.Chen@Sun.COM 	    hwahcp->hwahc_dev_state, hwahcp->hwahc_hw_state,
29669430SRaymond.Chen@Sun.COM 	    hwahcp->hwahc_hc_soft_state);
29679430SRaymond.Chen@Sun.COM 
29689430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
29699430SRaymond.Chen@Sun.COM 
29709430SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
29719430SRaymond.Chen@Sun.COM }
29729430SRaymond.Chen@Sun.COM 
29739430SRaymond.Chen@Sun.COM 
29749430SRaymond.Chen@Sun.COM /*
29759430SRaymond.Chen@Sun.COM  * hwahc_post_resume_event_cb:
29769430SRaymond.Chen@Sun.COM  *	Call after HWA device resume
29779430SRaymond.Chen@Sun.COM  */
29789430SRaymond.Chen@Sun.COM static int
hwahc_post_resume_event_cb(dev_info_t * dip)29799430SRaymond.Chen@Sun.COM hwahc_post_resume_event_cb(dev_info_t *dip)
29809430SRaymond.Chen@Sun.COM {
29819430SRaymond.Chen@Sun.COM 	int		instance = ddi_get_instance(dip);
29829430SRaymond.Chen@Sun.COM 	hwahc_state_t	*hwahcp;
29839430SRaymond.Chen@Sun.COM 	int		circ;
29849430SRaymond.Chen@Sun.COM 
29859430SRaymond.Chen@Sun.COM 	if ((hwahcp = ddi_get_soft_state(hwahc_statep, instance)) == NULL) {
29869430SRaymond.Chen@Sun.COM 
29879430SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
29889430SRaymond.Chen@Sun.COM 	}
29899430SRaymond.Chen@Sun.COM 
29909430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_EVENTS, hwahcp->hwahc_log_handle,
29919430SRaymond.Chen@Sun.COM 	    "hwahc_post_resume_event_cb: dip = 0x%p", (void *)dip);
29929430SRaymond.Chen@Sun.COM 
29939430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
29949430SRaymond.Chen@Sun.COM 
29959430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
29969430SRaymond.Chen@Sun.COM 	    "hwahc_post_resume_event_cb: start, hw state = %d, softstate = %d",
29979430SRaymond.Chen@Sun.COM 	    hwahcp->hwahc_hw_state, hwahcp->hwahc_hc_soft_state);
29989430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
29999430SRaymond.Chen@Sun.COM 
30009430SRaymond.Chen@Sun.COM 	ndi_devi_enter(dip, &circ);
30019430SRaymond.Chen@Sun.COM 
30029430SRaymond.Chen@Sun.COM 	/* need to place hc restore here to make sure rc has resumed */
30039430SRaymond.Chen@Sun.COM 	hwahc_restore_device_state(dip, hwahcp);
30049430SRaymond.Chen@Sun.COM 
30059430SRaymond.Chen@Sun.COM 	hwahc_run_callbacks(hwahcp, USBA_EVENT_TAG_POST_RESUME);
30069430SRaymond.Chen@Sun.COM 
30079430SRaymond.Chen@Sun.COM 	ndi_devi_exit(dip, circ);
30089430SRaymond.Chen@Sun.COM 
30099430SRaymond.Chen@Sun.COM 	/* enable PM */
30109430SRaymond.Chen@Sun.COM 	(void) hwahc_pm_idle_component(hwahcp);
30119430SRaymond.Chen@Sun.COM 
30129430SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
30139430SRaymond.Chen@Sun.COM }
30149430SRaymond.Chen@Sun.COM 
30159430SRaymond.Chen@Sun.COM 
30169430SRaymond.Chen@Sun.COM /*
30179430SRaymond.Chen@Sun.COM  * hwahc_restore_device_state:
30189430SRaymond.Chen@Sun.COM  *	Called during hotplug-reconnect and resume.
30199430SRaymond.Chen@Sun.COM  *		re-enable power management
30209430SRaymond.Chen@Sun.COM  *		Verify the device is the same as before the disconnect/suspend.
30219430SRaymond.Chen@Sun.COM  *		Restore device state
30229430SRaymond.Chen@Sun.COM  *		Thaw any IO which was frozen.
30239430SRaymond.Chen@Sun.COM  *		Quiesce device.  (Other routines will activate if thawed IO.)
30249430SRaymond.Chen@Sun.COM  *		Set device online.
30259430SRaymond.Chen@Sun.COM  *		Leave device disconnected if there are problems.
30269430SRaymond.Chen@Sun.COM  */
30279430SRaymond.Chen@Sun.COM static void
hwahc_restore_device_state(dev_info_t * dip,hwahc_state_t * hwahcp)30289430SRaymond.Chen@Sun.COM hwahc_restore_device_state(dev_info_t *dip, hwahc_state_t *hwahcp)
30299430SRaymond.Chen@Sun.COM {
30309430SRaymond.Chen@Sun.COM 	int	rval;
30319430SRaymond.Chen@Sun.COM 	int	old_hw_state;
30329430SRaymond.Chen@Sun.COM 
30339430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
30349430SRaymond.Chen@Sun.COM 	    "hwahc_restore_device_state: dip = 0x%p", (void *)dip);
30359430SRaymond.Chen@Sun.COM 
30369430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
30379430SRaymond.Chen@Sun.COM 
30389430SRaymond.Chen@Sun.COM 	ASSERT((hwahcp->hwahc_dev_state == USB_DEV_DISCONNECTED) ||
30399430SRaymond.Chen@Sun.COM 	    (hwahcp->hwahc_dev_state == USB_DEV_SUSPENDED));
30409430SRaymond.Chen@Sun.COM 
30419430SRaymond.Chen@Sun.COM 	/* raise power */
30429430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
30439430SRaymond.Chen@Sun.COM 	hwahc_pm_busy_component(hwahcp);
30449430SRaymond.Chen@Sun.COM 	(void) pm_raise_power(hwahcp->hwahc_dip, 0, USB_DEV_OS_FULL_PWR);
30459430SRaymond.Chen@Sun.COM 
30469430SRaymond.Chen@Sun.COM 	/*
30479430SRaymond.Chen@Sun.COM 	 * Check if we are talking to the same device
30489430SRaymond.Chen@Sun.COM 	 * Some host controllers may see all devices disconnected
30499430SRaymond.Chen@Sun.COM 	 * when they just resume. This may be a cause of not
30509430SRaymond.Chen@Sun.COM 	 * finding the same device.
30519430SRaymond.Chen@Sun.COM 	 *
30529430SRaymond.Chen@Sun.COM 	 * Some HWA devices need to download firmware when it is
30539430SRaymond.Chen@Sun.COM 	 * powered on. Before the firmware is downloaded, the device
30549430SRaymond.Chen@Sun.COM 	 * will look differently.
30559430SRaymond.Chen@Sun.COM 	 */
30569430SRaymond.Chen@Sun.COM 	if (usb_check_same_device(dip, hwahcp->hwahc_log_handle,
30579430SRaymond.Chen@Sun.COM 	    USB_LOG_L0, PRINT_MASK_ALL,
30589430SRaymond.Chen@Sun.COM 	    USB_CHK_BASIC | USB_CHK_SERIAL | USB_CHK_VIDPID, NULL) !=
30599430SRaymond.Chen@Sun.COM 	    USB_SUCCESS) {
30609430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
30619430SRaymond.Chen@Sun.COM 		    "hwahc_restore_device_state: not the same device");
30629430SRaymond.Chen@Sun.COM 		/* change the device state from suspended to disconnected */
30639430SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
30649430SRaymond.Chen@Sun.COM 		hwahcp->hwahc_dev_state = USB_DEV_DISCONNECTED;
30659430SRaymond.Chen@Sun.COM 		hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_ERROR_STATE;
30669430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
30679430SRaymond.Chen@Sun.COM 		hwahc_pm_idle_component(hwahcp);
30689430SRaymond.Chen@Sun.COM 
30699430SRaymond.Chen@Sun.COM 		return;
30709430SRaymond.Chen@Sun.COM 	}
30719430SRaymond.Chen@Sun.COM 
30729430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
30739430SRaymond.Chen@Sun.COM 	    "hwahc_restore_device_state: Hwahc has been reconnected but"
30749430SRaymond.Chen@Sun.COM 	    " data may have been lost");
30759430SRaymond.Chen@Sun.COM 
30769430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
30779430SRaymond.Chen@Sun.COM 
30789430SRaymond.Chen@Sun.COM 	/* reinitialize the hw */
30799430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_dev_state = USB_DEV_ONLINE;
30809430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_INIT_STATE;
30819430SRaymond.Chen@Sun.COM 
30829430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_hw_state == HWAHC_HW_STOPPED) {
30839430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
30849430SRaymond.Chen@Sun.COM 		/* no need to start hc */
30859430SRaymond.Chen@Sun.COM 		hwahc_pm_idle_component(hwahcp);
30869430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
30879430SRaymond.Chen@Sun.COM 		    "hwahc_restore_device_state: stopped hwa");
30889430SRaymond.Chen@Sun.COM 
30899430SRaymond.Chen@Sun.COM 		return;
30909430SRaymond.Chen@Sun.COM 	}
30919430SRaymond.Chen@Sun.COM 
30929430SRaymond.Chen@Sun.COM 
30939430SRaymond.Chen@Sun.COM 	rval = wusb_hc_set_cluster_id(&hwahcp->hwahc_hc_data,
30949430SRaymond.Chen@Sun.COM 	    hwahcp->hwahc_hc_data.hc_cluster_id);
30959430SRaymond.Chen@Sun.COM 
30969430SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
30979430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
30989430SRaymond.Chen@Sun.COM 		    "hwahc_restore_device_state: set cluster id fails");
30999430SRaymond.Chen@Sun.COM 
31009430SRaymond.Chen@Sun.COM 		goto err;
31019430SRaymond.Chen@Sun.COM 	}
31029430SRaymond.Chen@Sun.COM 
31039430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_hw_state == HWAHC_HW_STARTED) {
31049430SRaymond.Chen@Sun.COM 		old_hw_state = hwahcp->hwahc_hw_state;
31059430SRaymond.Chen@Sun.COM 		hwahcp->hwahc_hw_state = HWAHC_HW_CH_STOPPED;
31069430SRaymond.Chen@Sun.COM 		rval = hwahc_hc_channel_start(hwahcp);
31079430SRaymond.Chen@Sun.COM 		if (rval != USB_SUCCESS) {
31089430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_ATTA,
31099430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
31109430SRaymond.Chen@Sun.COM 			    "hwahc_restore_device_state: start hc fails");
31119430SRaymond.Chen@Sun.COM 			hwahcp->hwahc_hw_state = old_hw_state;
31129430SRaymond.Chen@Sun.COM 
31139430SRaymond.Chen@Sun.COM 			goto err;
31149430SRaymond.Chen@Sun.COM 		}
31159430SRaymond.Chen@Sun.COM 		hwahcp->hwahc_hw_state = old_hw_state;
31169430SRaymond.Chen@Sun.COM 	}
31179430SRaymond.Chen@Sun.COM 
31189430SRaymond.Chen@Sun.COM 	rval = wusb_hc_set_num_dnts(&hwahcp->hwahc_hc_data,
31199430SRaymond.Chen@Sun.COM 	    HWAHC_DEFAULT_DNTS_INTERVAL, HWAHC_DEFAULT_DNTS_SLOT_NUM);
31209430SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
31219430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
31229430SRaymond.Chen@Sun.COM 		    "hwahc_restore_device_state: set num dnts fails");
31239430SRaymond.Chen@Sun.COM 
31249430SRaymond.Chen@Sun.COM 		goto err;
31259430SRaymond.Chen@Sun.COM 	}
31269430SRaymond.Chen@Sun.COM 
31279430SRaymond.Chen@Sun.COM 	/* set default GTK */
31289430SRaymond.Chen@Sun.COM 	rval = wusb_hc_set_gtk(&hwahcp->hwahc_hc_data, dft_gtk, dft_gtkid);
31299430SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
31309430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
31319430SRaymond.Chen@Sun.COM 		    "hwahc_restore_device_state: set gtk fails");
31329430SRaymond.Chen@Sun.COM 
31339430SRaymond.Chen@Sun.COM 		goto err;
31349430SRaymond.Chen@Sun.COM 	}
31359430SRaymond.Chen@Sun.COM 
31369430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
31379430SRaymond.Chen@Sun.COM 
31389430SRaymond.Chen@Sun.COM 	rval = wusb_wa_enable(&hwahcp->hwahc_wa_data,
31399430SRaymond.Chen@Sun.COM 	    hwahcp->hwahc_default_pipe);
31409430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
31419430SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
31429430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
31439430SRaymond.Chen@Sun.COM 		    "hwahc_restore_device_state: enable wa fails");
31449430SRaymond.Chen@Sun.COM 
31459430SRaymond.Chen@Sun.COM 		goto err;
31469430SRaymond.Chen@Sun.COM 	}
31479430SRaymond.Chen@Sun.COM 
31489430SRaymond.Chen@Sun.COM 	/*
31499430SRaymond.Chen@Sun.COM 	 * This is a workaround, sometimes the ioctl and reconnect will
31509430SRaymond.Chen@Sun.COM 	 * happen at the sametime, so the ioctl will start nep which makes
31519430SRaymond.Chen@Sun.COM 	 * the below sart nep fail. Need more work to do to avoid such
31529430SRaymond.Chen@Sun.COM 	 * issues
31539430SRaymond.Chen@Sun.COM 	 */
31549430SRaymond.Chen@Sun.COM 	(void) wusb_wa_stop_nep(&hwahcp->hwahc_wa_data);
31559430SRaymond.Chen@Sun.COM 
31569430SRaymond.Chen@Sun.COM 	rval = wusb_wa_start_nep(&hwahcp->hwahc_wa_data, USB_FLAGS_SLEEP);
31579430SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
31589430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
31599430SRaymond.Chen@Sun.COM 		    "hwahc_restore_device_state: start notifep fails rval =%d",
31609430SRaymond.Chen@Sun.COM 		    rval);
31619430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
31629430SRaymond.Chen@Sun.COM 		(void) wusb_wa_disable(&hwahcp->hwahc_wa_data,
31639430SRaymond.Chen@Sun.COM 		    hwahcp->hwahc_default_pipe);
31649430SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
31659430SRaymond.Chen@Sun.COM 
31669430SRaymond.Chen@Sun.COM 		goto err;
31679430SRaymond.Chen@Sun.COM 	}
31689430SRaymond.Chen@Sun.COM 
31699430SRaymond.Chen@Sun.COM 	/* Handle transfer results on bulk-in ep */
31709430SRaymond.Chen@Sun.COM 	rval = hwahc_start_result_thread(hwahcp);
31719430SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
31729430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
31739430SRaymond.Chen@Sun.COM 		    "hwahc_restore_device_state: start result thread fails");
31749430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
31759430SRaymond.Chen@Sun.COM 		wusb_wa_stop_nep(&hwahcp->hwahc_wa_data);
31769430SRaymond.Chen@Sun.COM 		(void) wusb_wa_disable(&hwahcp->hwahc_wa_data,
31779430SRaymond.Chen@Sun.COM 		    hwahcp->hwahc_default_pipe);
31789430SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
31799430SRaymond.Chen@Sun.COM 
31809430SRaymond.Chen@Sun.COM 		goto err;
31819430SRaymond.Chen@Sun.COM 	}
31829430SRaymond.Chen@Sun.COM 
31839430SRaymond.Chen@Sun.COM 	/* if the device had remote wakeup earlier, enable it again */
31849430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_pm && hwahcp->hwahc_pm->hwahc_wakeup_enabled) {
31859430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
31869430SRaymond.Chen@Sun.COM 		(void) usb_handle_remote_wakeup(hwahcp->hwahc_dip,
31879430SRaymond.Chen@Sun.COM 		    USB_REMOTE_WAKEUP_ENABLE);
31889430SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
31899430SRaymond.Chen@Sun.COM 	}
31909430SRaymond.Chen@Sun.COM 
31919430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_hw_state = HWAHC_HW_STARTED;
31929430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_OPERATIONAL_STATE;
31939430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
31949430SRaymond.Chen@Sun.COM 	hwahc_pm_idle_component(hwahcp);
31959430SRaymond.Chen@Sun.COM 
31969430SRaymond.Chen@Sun.COM 	return;
31979430SRaymond.Chen@Sun.COM 
31989430SRaymond.Chen@Sun.COM err:
31999430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_hw_state = HWAHC_HW_STOPPED;
32009430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
32019430SRaymond.Chen@Sun.COM 	hwahc_pm_idle_component(hwahcp);
32029430SRaymond.Chen@Sun.COM }
32039430SRaymond.Chen@Sun.COM 
32049430SRaymond.Chen@Sun.COM 
32059430SRaymond.Chen@Sun.COM /*
32069430SRaymond.Chen@Sun.COM  * hwahc_cpr_suspend:
32079430SRaymond.Chen@Sun.COM  *	Clean up device.
32089430SRaymond.Chen@Sun.COM  *	Wait for any IO to finish, then close pipes.
32099430SRaymond.Chen@Sun.COM  *	Quiesce device.
32109430SRaymond.Chen@Sun.COM  * due to the dependency on hwarc, the actual suspend operations are
32119430SRaymond.Chen@Sun.COM  * moved to hwahc_pre_suspend_event_cb function.
32129430SRaymond.Chen@Sun.COM  */
32139430SRaymond.Chen@Sun.COM static int
hwahc_cpr_suspend(dev_info_t * dip)32149430SRaymond.Chen@Sun.COM hwahc_cpr_suspend(dev_info_t *dip)
32159430SRaymond.Chen@Sun.COM {
32169430SRaymond.Chen@Sun.COM 	int		instance = ddi_get_instance(dip);
32179430SRaymond.Chen@Sun.COM 	hwahc_state_t	*hwahcp = ddi_get_soft_state(hwahc_statep, instance);
32189430SRaymond.Chen@Sun.COM 
32199430SRaymond.Chen@Sun.COM 	if (hwahcp == NULL) {
32209430SRaymond.Chen@Sun.COM 
32219430SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
32229430SRaymond.Chen@Sun.COM 	}
32239430SRaymond.Chen@Sun.COM 
32249430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
32259430SRaymond.Chen@Sun.COM 	    "hwahc_cpr_suspend: start");
32269430SRaymond.Chen@Sun.COM 
32279430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
32289430SRaymond.Chen@Sun.COM 
32299430SRaymond.Chen@Sun.COM 	/* Don't suspend if the device is open. */
32309430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_open_count > 0) {
32319430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
32329430SRaymond.Chen@Sun.COM 		    "hwahc_cpr_suspend: Device is open, cannot suspend");
32339430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
32349430SRaymond.Chen@Sun.COM 
32359430SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
32369430SRaymond.Chen@Sun.COM 	}
32379430SRaymond.Chen@Sun.COM 
32389430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
32399430SRaymond.Chen@Sun.COM 	/* raise power */
32409430SRaymond.Chen@Sun.COM 	hwahc_pm_busy_component(hwahcp);
32419430SRaymond.Chen@Sun.COM 	(void) pm_raise_power(hwahcp->hwahc_dip, 0, USB_DEV_OS_FULL_PWR);
32429430SRaymond.Chen@Sun.COM 
32439430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
32449430SRaymond.Chen@Sun.COM 	switch (hwahcp->hwahc_dev_state) {
32459430SRaymond.Chen@Sun.COM 	case USB_DEV_ONLINE:
32469430SRaymond.Chen@Sun.COM 	/* real suspend operations put in pre_suspend function */
32479430SRaymond.Chen@Sun.COM 		/* FALLTHRU */
32489430SRaymond.Chen@Sun.COM 	case USB_DEV_DISCONNECTED:
32499430SRaymond.Chen@Sun.COM 	case USB_DEV_PWRED_DOWN:
32509430SRaymond.Chen@Sun.COM 		hwahcp->hwahc_dev_state = USB_DEV_SUSPENDED;
32519430SRaymond.Chen@Sun.COM 		hwahcp->hwahc_hw_state = HWAHC_HW_CH_SUSPEND;
32529430SRaymond.Chen@Sun.COM 
32539430SRaymond.Chen@Sun.COM 		break;
32549430SRaymond.Chen@Sun.COM 	case USB_DEV_SUSPENDED:
32559430SRaymond.Chen@Sun.COM 	default:
32569430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
32579430SRaymond.Chen@Sun.COM 		    "hwahc_cpr_suspend: illegal dev state=%d",
32589430SRaymond.Chen@Sun.COM 		    hwahcp->hwahc_dev_state);
32599430SRaymond.Chen@Sun.COM 
32609430SRaymond.Chen@Sun.COM 		break;
32619430SRaymond.Chen@Sun.COM 	}
32629430SRaymond.Chen@Sun.COM 
32639430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
32649430SRaymond.Chen@Sun.COM 	hwahc_pm_idle_component(hwahcp);
32659430SRaymond.Chen@Sun.COM 
32669430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
32679430SRaymond.Chen@Sun.COM 	    "hwahc_cpr_suspend: end");
32689430SRaymond.Chen@Sun.COM 
32699430SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
32709430SRaymond.Chen@Sun.COM }
32719430SRaymond.Chen@Sun.COM 
32729430SRaymond.Chen@Sun.COM 
32739430SRaymond.Chen@Sun.COM /*
32749430SRaymond.Chen@Sun.COM  * hwahc_cpr_resume:
32759430SRaymond.Chen@Sun.COM  *
32769430SRaymond.Chen@Sun.COM  *	hwahc_restore_device_state marks success by putting device back online
32779430SRaymond.Chen@Sun.COM  */
32789430SRaymond.Chen@Sun.COM static int
hwahc_cpr_resume(dev_info_t * dip)32799430SRaymond.Chen@Sun.COM hwahc_cpr_resume(dev_info_t *dip)
32809430SRaymond.Chen@Sun.COM {
32819430SRaymond.Chen@Sun.COM 	int		instance = ddi_get_instance(dip);
32829430SRaymond.Chen@Sun.COM 	hwahc_state_t	*hwahcp = ddi_get_soft_state(hwahc_statep, instance);
32839430SRaymond.Chen@Sun.COM 
32849430SRaymond.Chen@Sun.COM 	if (hwahcp == NULL) {
32859430SRaymond.Chen@Sun.COM 
32869430SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
32879430SRaymond.Chen@Sun.COM 	}
32889430SRaymond.Chen@Sun.COM 
32899430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
32909430SRaymond.Chen@Sun.COM 	    "hwahc_cpr_resume: hw state = %d, softstate = %d",
32919430SRaymond.Chen@Sun.COM 	    hwahcp->hwahc_hw_state, hwahcp->hwahc_hc_soft_state);
32929430SRaymond.Chen@Sun.COM 
32939430SRaymond.Chen@Sun.COM 	/*
32949430SRaymond.Chen@Sun.COM 	 * rc is always resumed after hc. restoring hc before rc would fail.
32959430SRaymond.Chen@Sun.COM 	 * move the restoring operations to hwahc_post_resume_event_cb.
32969430SRaymond.Chen@Sun.COM 	 */
32979430SRaymond.Chen@Sun.COM 
32989430SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
32999430SRaymond.Chen@Sun.COM }
33009430SRaymond.Chen@Sun.COM 
33019430SRaymond.Chen@Sun.COM /*
33029430SRaymond.Chen@Sun.COM  * hwahc_create_pm_components:
33039430SRaymond.Chen@Sun.COM  *	Create power managements components
33049430SRaymond.Chen@Sun.COM  */
33059430SRaymond.Chen@Sun.COM static void
hwahc_create_pm_components(dev_info_t * dip,hwahc_state_t * hwahcp)33069430SRaymond.Chen@Sun.COM hwahc_create_pm_components(dev_info_t *dip, hwahc_state_t *hwahcp)
33079430SRaymond.Chen@Sun.COM {
33089430SRaymond.Chen@Sun.COM 	hwahc_power_t	*hwahcpm;
33099430SRaymond.Chen@Sun.COM 	uint_t		pwr_states;
33109430SRaymond.Chen@Sun.COM 
33119430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
33129430SRaymond.Chen@Sun.COM 	    "hwahc_create_pm_components: Begin");
33139430SRaymond.Chen@Sun.COM 
33149430SRaymond.Chen@Sun.COM 	/* Allocate the state structure */
33159430SRaymond.Chen@Sun.COM 	hwahcpm = kmem_zalloc(sizeof (hwahc_power_t), KM_SLEEP);
33169430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_pm = hwahcpm;
33179430SRaymond.Chen@Sun.COM 	hwahcpm->hwahc_state = hwahcp;
33189430SRaymond.Chen@Sun.COM 	hwahcpm->hwahc_pm_capabilities = 0;
33199430SRaymond.Chen@Sun.COM 	hwahcpm->hwahc_current_power = USB_DEV_OS_FULL_PWR;
33209430SRaymond.Chen@Sun.COM 
33219430SRaymond.Chen@Sun.COM 	if (usb_create_pm_components(dip, &pwr_states) == USB_SUCCESS) {
33229430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
33239430SRaymond.Chen@Sun.COM 		    "hwahc_create_pm_components: created PM components");
33249430SRaymond.Chen@Sun.COM 
33259430SRaymond.Chen@Sun.COM 		if (usb_handle_remote_wakeup(dip,
33269430SRaymond.Chen@Sun.COM 		    USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) {
33279430SRaymond.Chen@Sun.COM 			hwahcpm->hwahc_wakeup_enabled = 1;
33289430SRaymond.Chen@Sun.COM 		}
33299430SRaymond.Chen@Sun.COM 		hwahcpm->hwahc_pwr_states = (uint8_t)pwr_states;
33309430SRaymond.Chen@Sun.COM 		/* make device busy till end of attach */
33319430SRaymond.Chen@Sun.COM 		hwahc_pm_busy_component(hwahcp);
33329430SRaymond.Chen@Sun.COM 		(void) pm_raise_power(hwahcp->hwahc_dip, 0,
33339430SRaymond.Chen@Sun.COM 		    USB_DEV_OS_FULL_PWR);
33349430SRaymond.Chen@Sun.COM 	} else {
33359430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
33369430SRaymond.Chen@Sun.COM 		    "hwahc_create_pm_components: failed");
33379430SRaymond.Chen@Sun.COM 	}
33389430SRaymond.Chen@Sun.COM 
33399430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
33409430SRaymond.Chen@Sun.COM 	    "hwahc_create_pm_components: End");
33419430SRaymond.Chen@Sun.COM }
33429430SRaymond.Chen@Sun.COM 
33439430SRaymond.Chen@Sun.COM /*
33449430SRaymond.Chen@Sun.COM  * hwahc_destroy_pm_components:
33459430SRaymond.Chen@Sun.COM  *	Shut down and destroy power management and remote wakeup functionality
33469430SRaymond.Chen@Sun.COM  */
33479430SRaymond.Chen@Sun.COM static void
hwahc_destroy_pm_components(hwahc_state_t * hwahcp)33489430SRaymond.Chen@Sun.COM hwahc_destroy_pm_components(hwahc_state_t *hwahcp)
33499430SRaymond.Chen@Sun.COM {
33509430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
33519430SRaymond.Chen@Sun.COM 	    "hwahc_destroy_pm_components: Begin");
33529430SRaymond.Chen@Sun.COM 
33539430SRaymond.Chen@Sun.COM 	ASSERT(!mutex_owned(&hwahcp->hwahc_mutex));
33549430SRaymond.Chen@Sun.COM 
33559430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
33569430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_pm && (hwahcp->hwahc_dev_state !=
33579430SRaymond.Chen@Sun.COM 	    USB_DEV_DISCONNECTED)) {
33589430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
33599430SRaymond.Chen@Sun.COM 		hwahc_pm_busy_component(hwahcp);
33609430SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
33619430SRaymond.Chen@Sun.COM 
33629430SRaymond.Chen@Sun.COM 		if (hwahcp->hwahc_pm->hwahc_wakeup_enabled) {
33639430SRaymond.Chen@Sun.COM 			int rval;
33649430SRaymond.Chen@Sun.COM 
33659430SRaymond.Chen@Sun.COM 			mutex_exit(&hwahcp->hwahc_mutex);
33669430SRaymond.Chen@Sun.COM 			(void) pm_raise_power(hwahcp->hwahc_dip, 0,
33679430SRaymond.Chen@Sun.COM 			    USB_DEV_OS_FULL_PWR);
33689430SRaymond.Chen@Sun.COM 
33699430SRaymond.Chen@Sun.COM 			if ((rval = usb_handle_remote_wakeup(
33709430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_dip,
33719430SRaymond.Chen@Sun.COM 			    USB_REMOTE_WAKEUP_DISABLE)) !=
33729430SRaymond.Chen@Sun.COM 			    USB_SUCCESS) {
33739430SRaymond.Chen@Sun.COM 				USB_DPRINTF_L3(PRINT_MASK_PM,
33749430SRaymond.Chen@Sun.COM 				    hwahcp->hwahc_log_handle,
33759430SRaymond.Chen@Sun.COM 				    "hwahc_destroy_pm_components: "
33769430SRaymond.Chen@Sun.COM 				    "Error disabling rmt wakeup: rval = %d",
33779430SRaymond.Chen@Sun.COM 				    rval);
33789430SRaymond.Chen@Sun.COM 			}
33799430SRaymond.Chen@Sun.COM 		} else {
33809430SRaymond.Chen@Sun.COM 			mutex_exit(&hwahcp->hwahc_mutex);
33819430SRaymond.Chen@Sun.COM 		}
33829430SRaymond.Chen@Sun.COM 
33839430SRaymond.Chen@Sun.COM 		/*
33849430SRaymond.Chen@Sun.COM 		 * Since remote wakeup is disabled now,
33859430SRaymond.Chen@Sun.COM 		 * no one can raise power and get to device
33869430SRaymond.Chen@Sun.COM 		 * once power is lowered here.
33879430SRaymond.Chen@Sun.COM 		 */
33889430SRaymond.Chen@Sun.COM 		(void) pm_lower_power(hwahcp->hwahc_dip, 0, USB_DEV_OS_PWR_OFF);
33899430SRaymond.Chen@Sun.COM 
33909430SRaymond.Chen@Sun.COM 		hwahc_pm_idle_component(hwahcp);
33919430SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
33929430SRaymond.Chen@Sun.COM 	}
33939430SRaymond.Chen@Sun.COM 
33949430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_pm) {
33959430SRaymond.Chen@Sun.COM 		kmem_free(hwahcp->hwahc_pm, sizeof (hwahc_power_t));
33969430SRaymond.Chen@Sun.COM 		hwahcp->hwahc_pm = NULL;
33979430SRaymond.Chen@Sun.COM 	}
33989430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
33999430SRaymond.Chen@Sun.COM 
34009430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
34019430SRaymond.Chen@Sun.COM 	    "hwahc_destroy_pm_components: End");
34029430SRaymond.Chen@Sun.COM }
34039430SRaymond.Chen@Sun.COM 
34049430SRaymond.Chen@Sun.COM /* mark component busy */
34059430SRaymond.Chen@Sun.COM static void
hwahc_pm_busy_component(hwahc_state_t * hwahcp)34069430SRaymond.Chen@Sun.COM hwahc_pm_busy_component(hwahc_state_t *hwahcp)
34079430SRaymond.Chen@Sun.COM {
34089430SRaymond.Chen@Sun.COM 	ASSERT(!mutex_owned(&hwahcp->hwahc_mutex));
34099430SRaymond.Chen@Sun.COM 
34109430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_pm != NULL) {
34119430SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
34129430SRaymond.Chen@Sun.COM 		hwahcp->hwahc_pm->hwahc_pm_busy++;
34139430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
34149430SRaymond.Chen@Sun.COM 		    "hwahc_pm_busy_component: %d",
34159430SRaymond.Chen@Sun.COM 		    hwahcp->hwahc_pm->hwahc_pm_busy);
34169430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
34179430SRaymond.Chen@Sun.COM 
34189430SRaymond.Chen@Sun.COM 		if (pm_busy_component(hwahcp->hwahc_dip, 0) !=
34199430SRaymond.Chen@Sun.COM 		    DDI_SUCCESS) {
34209430SRaymond.Chen@Sun.COM 			mutex_enter(&hwahcp->hwahc_mutex);
34219430SRaymond.Chen@Sun.COM 			hwahcp->hwahc_pm->hwahc_pm_busy--;
34229430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_PM,
34239430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
34249430SRaymond.Chen@Sun.COM 			    "hwahc_pm_busy_component failed: %d",
34259430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_pm->hwahc_pm_busy);
34269430SRaymond.Chen@Sun.COM 			mutex_exit(&hwahcp->hwahc_mutex);
34279430SRaymond.Chen@Sun.COM 		}
34289430SRaymond.Chen@Sun.COM 	}
34299430SRaymond.Chen@Sun.COM }
34309430SRaymond.Chen@Sun.COM 
34319430SRaymond.Chen@Sun.COM /* mark component idle */
34329430SRaymond.Chen@Sun.COM static void
hwahc_pm_idle_component(hwahc_state_t * hwahcp)34339430SRaymond.Chen@Sun.COM hwahc_pm_idle_component(hwahc_state_t *hwahcp)
34349430SRaymond.Chen@Sun.COM {
34359430SRaymond.Chen@Sun.COM 	ASSERT(!mutex_owned(&hwahcp->hwahc_mutex));
34369430SRaymond.Chen@Sun.COM 
34379430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_pm != NULL) {
34389430SRaymond.Chen@Sun.COM 
34399430SRaymond.Chen@Sun.COM 		if (pm_idle_component(hwahcp->hwahc_dip, 0) ==
34409430SRaymond.Chen@Sun.COM 		    DDI_SUCCESS) {
34419430SRaymond.Chen@Sun.COM 			mutex_enter(&hwahcp->hwahc_mutex);
34429430SRaymond.Chen@Sun.COM 			ASSERT(hwahcp->hwahc_pm->hwahc_pm_busy > 0);
34439430SRaymond.Chen@Sun.COM 			hwahcp->hwahc_pm->hwahc_pm_busy--;
34449430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L4(PRINT_MASK_PM,
34459430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
34469430SRaymond.Chen@Sun.COM 			    "hwahc_pm_idle_component: %d",
34479430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_pm->hwahc_pm_busy);
34489430SRaymond.Chen@Sun.COM 			mutex_exit(&hwahcp->hwahc_mutex);
34499430SRaymond.Chen@Sun.COM 		}
34509430SRaymond.Chen@Sun.COM 	}
34519430SRaymond.Chen@Sun.COM }
34529430SRaymond.Chen@Sun.COM 
34539430SRaymond.Chen@Sun.COM /*
34549430SRaymond.Chen@Sun.COM  * hwahc_power :
34559430SRaymond.Chen@Sun.COM  *	Power entry point, the workhorse behind pm_raise_power, pm_lower_power,
34569430SRaymond.Chen@Sun.COM  *	usb_req_raise_power and usb_req_lower_power.
34579430SRaymond.Chen@Sun.COM  */
34589430SRaymond.Chen@Sun.COM /* ARGSUSED */
34599430SRaymond.Chen@Sun.COM static int
hwahc_power(dev_info_t * dip,int comp,int level)34609430SRaymond.Chen@Sun.COM hwahc_power(dev_info_t *dip, int comp, int level)
34619430SRaymond.Chen@Sun.COM {
34629430SRaymond.Chen@Sun.COM 	hwahc_state_t	*hwahcp;
34639430SRaymond.Chen@Sun.COM 	hwahc_power_t	*pm;
34649430SRaymond.Chen@Sun.COM 	int		rval = USB_FAILURE;
34659430SRaymond.Chen@Sun.COM 
34669430SRaymond.Chen@Sun.COM 	hwahcp = ddi_get_soft_state(hwahc_statep, ddi_get_instance(dip));
34679430SRaymond.Chen@Sun.COM 
34689430SRaymond.Chen@Sun.COM 	if (hwahcp == NULL) {
34699430SRaymond.Chen@Sun.COM 
34709430SRaymond.Chen@Sun.COM 		return (DDI_FAILURE);
34719430SRaymond.Chen@Sun.COM 	}
34729430SRaymond.Chen@Sun.COM 
34739430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
34749430SRaymond.Chen@Sun.COM 	    "hwahc_power: dip = 0x%p", (void *)dip);
34759430SRaymond.Chen@Sun.COM 
34769430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
34779430SRaymond.Chen@Sun.COM 
34789430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_pm == NULL) {
34799430SRaymond.Chen@Sun.COM 
34809430SRaymond.Chen@Sun.COM 		goto done;
34819430SRaymond.Chen@Sun.COM 	}
34829430SRaymond.Chen@Sun.COM 
34839430SRaymond.Chen@Sun.COM 	pm = hwahcp->hwahc_pm;
34849430SRaymond.Chen@Sun.COM 
34859430SRaymond.Chen@Sun.COM 	/* Check if we are transitioning to a legal power level */
34869430SRaymond.Chen@Sun.COM 	if (USB_DEV_PWRSTATE_OK(pm->hwahc_pwr_states, level)) {
34879430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
34889430SRaymond.Chen@Sun.COM 		    "hwahc_power: illegal power level = %d "
34899430SRaymond.Chen@Sun.COM 		    "pwr_states: %x", level, pm->hwahc_pwr_states);
34909430SRaymond.Chen@Sun.COM 
34919430SRaymond.Chen@Sun.COM 		goto done;
34929430SRaymond.Chen@Sun.COM 	}
34939430SRaymond.Chen@Sun.COM 
34949430SRaymond.Chen@Sun.COM 	switch (level) {
34959430SRaymond.Chen@Sun.COM 	case USB_DEV_OS_PWR_OFF :
34969430SRaymond.Chen@Sun.COM 		rval = hwahc_pwrlvl0(hwahcp);
34979430SRaymond.Chen@Sun.COM 
34989430SRaymond.Chen@Sun.COM 		break;
34999430SRaymond.Chen@Sun.COM 	case USB_DEV_OS_PWR_1:
35009430SRaymond.Chen@Sun.COM 		rval = hwahc_pwrlvl1(hwahcp);
35019430SRaymond.Chen@Sun.COM 
35029430SRaymond.Chen@Sun.COM 		break;
35039430SRaymond.Chen@Sun.COM 	case USB_DEV_OS_PWR_2:
35049430SRaymond.Chen@Sun.COM 		rval = hwahc_pwrlvl2(hwahcp);
35059430SRaymond.Chen@Sun.COM 
35069430SRaymond.Chen@Sun.COM 		break;
35079430SRaymond.Chen@Sun.COM 	case USB_DEV_OS_FULL_PWR :
35089430SRaymond.Chen@Sun.COM 		rval = hwahc_pwrlvl3(hwahcp);
35099430SRaymond.Chen@Sun.COM 
35109430SRaymond.Chen@Sun.COM 		break;
35119430SRaymond.Chen@Sun.COM 	}
35129430SRaymond.Chen@Sun.COM done:
35139430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
35149430SRaymond.Chen@Sun.COM 
35159430SRaymond.Chen@Sun.COM 	return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
35169430SRaymond.Chen@Sun.COM }
35179430SRaymond.Chen@Sun.COM 
35189430SRaymond.Chen@Sun.COM /*
35199430SRaymond.Chen@Sun.COM  * hwahc_pwrlvl0:
35209430SRaymond.Chen@Sun.COM  * Functions to handle power transition for OS levels 0 -> 3
35219430SRaymond.Chen@Sun.COM  *	OS 0 <--> USB D3, no or minimal power
35229430SRaymond.Chen@Sun.COM  */
35239430SRaymond.Chen@Sun.COM static int
hwahc_pwrlvl0(hwahc_state_t * hwahcp)35249430SRaymond.Chen@Sun.COM hwahc_pwrlvl0(hwahc_state_t *hwahcp)
35259430SRaymond.Chen@Sun.COM {
35269430SRaymond.Chen@Sun.COM 	int	rval;
35279430SRaymond.Chen@Sun.COM 
35289430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
35299430SRaymond.Chen@Sun.COM 	    "hwahc_pwrlvl0: %d", hwahcp->hwahc_pm->hwahc_pm_busy);
35309430SRaymond.Chen@Sun.COM 
35319430SRaymond.Chen@Sun.COM 	switch (hwahcp->hwahc_dev_state) {
35329430SRaymond.Chen@Sun.COM 	case USB_DEV_ONLINE:
35339430SRaymond.Chen@Sun.COM 		/* Deny the powerdown request if the device is busy */
35349430SRaymond.Chen@Sun.COM 		if (hwahcp->hwahc_pm->hwahc_pm_busy != 0) {
35359430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_PM,
35369430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
35379430SRaymond.Chen@Sun.COM 			    "hwahc_pwrlvl0: hwahc_pm is busy");
35389430SRaymond.Chen@Sun.COM 
35399430SRaymond.Chen@Sun.COM 			return (USB_FAILURE);
35409430SRaymond.Chen@Sun.COM 		}
35419430SRaymond.Chen@Sun.COM 		/*
35429430SRaymond.Chen@Sun.COM 		 * only when final_stop gets called, we allow the system
35439430SRaymond.Chen@Sun.COM 		 * to do PM on us. At this moment, we don't need to do
35449430SRaymond.Chen@Sun.COM 		 * more operations other than those in final_stop.
35459430SRaymond.Chen@Sun.COM 		 */
35469430SRaymond.Chen@Sun.COM 
35479430SRaymond.Chen@Sun.COM 		/* Issue USB D3 command to the device here */
35489430SRaymond.Chen@Sun.COM 		rval = usb_set_device_pwrlvl3(hwahcp->hwahc_dip);
35499430SRaymond.Chen@Sun.COM 		ASSERT(rval == USB_SUCCESS);
35509430SRaymond.Chen@Sun.COM 
35519430SRaymond.Chen@Sun.COM 		hwahcp->hwahc_dev_state = USB_DEV_PWRED_DOWN;
35529430SRaymond.Chen@Sun.COM 
35539430SRaymond.Chen@Sun.COM 		hwahcp->hwahc_pm->hwahc_current_power = USB_DEV_OS_PWR_OFF;
35549430SRaymond.Chen@Sun.COM 
35559430SRaymond.Chen@Sun.COM 		break;
35569430SRaymond.Chen@Sun.COM 	case USB_DEV_DISCONNECTED:
35579430SRaymond.Chen@Sun.COM 	case USB_DEV_SUSPENDED:
35589430SRaymond.Chen@Sun.COM 	case USB_DEV_PWRED_DOWN:
35599430SRaymond.Chen@Sun.COM 	default:
35609430SRaymond.Chen@Sun.COM 		break;
35619430SRaymond.Chen@Sun.COM 	}
35629430SRaymond.Chen@Sun.COM 
35639430SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
35649430SRaymond.Chen@Sun.COM }
35659430SRaymond.Chen@Sun.COM 
35669430SRaymond.Chen@Sun.COM /*
35679430SRaymond.Chen@Sun.COM  * hwahc_pwrlvl1:
35689430SRaymond.Chen@Sun.COM  *	Functions to handle power transition to OS levels -> 2
35699430SRaymond.Chen@Sun.COM  *	OS level 1 <--> D2
35709430SRaymond.Chen@Sun.COM  */
35719430SRaymond.Chen@Sun.COM static int
hwahc_pwrlvl1(hwahc_state_t * hwahcp)35729430SRaymond.Chen@Sun.COM hwahc_pwrlvl1(hwahc_state_t *hwahcp)
35739430SRaymond.Chen@Sun.COM {
35749430SRaymond.Chen@Sun.COM 	int	rval;
35759430SRaymond.Chen@Sun.COM 
35769430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
35779430SRaymond.Chen@Sun.COM 	    "hwahc_pwrlvl1:");
35789430SRaymond.Chen@Sun.COM 
35799430SRaymond.Chen@Sun.COM 	/* Issue USB D2 command to the device here */
35809430SRaymond.Chen@Sun.COM 	rval = usb_set_device_pwrlvl2(hwahcp->hwahc_dip);
35819430SRaymond.Chen@Sun.COM 	ASSERT(rval == USB_SUCCESS);
35829430SRaymond.Chen@Sun.COM 
35839430SRaymond.Chen@Sun.COM 	return (USB_FAILURE);
35849430SRaymond.Chen@Sun.COM }
35859430SRaymond.Chen@Sun.COM 
35869430SRaymond.Chen@Sun.COM /*
35879430SRaymond.Chen@Sun.COM  * hwahc_pwrlvl2:
35889430SRaymond.Chen@Sun.COM  *	Functions to handle power transition to OS levels -> 1
35899430SRaymond.Chen@Sun.COM  *	OS leve 2 <--> D1
35909430SRaymond.Chen@Sun.COM  */
35919430SRaymond.Chen@Sun.COM static int
hwahc_pwrlvl2(hwahc_state_t * hwahcp)35929430SRaymond.Chen@Sun.COM hwahc_pwrlvl2(hwahc_state_t *hwahcp)
35939430SRaymond.Chen@Sun.COM {
35949430SRaymond.Chen@Sun.COM 	int	rval;
35959430SRaymond.Chen@Sun.COM 
35969430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
35979430SRaymond.Chen@Sun.COM 	    "hwahc_pwrlvl2:");
35989430SRaymond.Chen@Sun.COM 
35999430SRaymond.Chen@Sun.COM 	/* Issue USB D1 command to the device here */
36009430SRaymond.Chen@Sun.COM 	rval = usb_set_device_pwrlvl1(hwahcp->hwahc_dip);
36019430SRaymond.Chen@Sun.COM 	ASSERT(rval == USB_SUCCESS);
36029430SRaymond.Chen@Sun.COM 
36039430SRaymond.Chen@Sun.COM 	return (USB_FAILURE);
36049430SRaymond.Chen@Sun.COM }
36059430SRaymond.Chen@Sun.COM 
36069430SRaymond.Chen@Sun.COM 
36079430SRaymond.Chen@Sun.COM /*
36089430SRaymond.Chen@Sun.COM  * hwahc_pwrlvl3:
36099430SRaymond.Chen@Sun.COM  *	Functions to handle power transition to OS level -> 0
36109430SRaymond.Chen@Sun.COM  *	OS level 3 <--> D0 (full power)
36119430SRaymond.Chen@Sun.COM  */
36129430SRaymond.Chen@Sun.COM static int
hwahc_pwrlvl3(hwahc_state_t * hwahcp)36139430SRaymond.Chen@Sun.COM hwahc_pwrlvl3(hwahc_state_t *hwahcp)
36149430SRaymond.Chen@Sun.COM {
36159430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
36169430SRaymond.Chen@Sun.COM 	    "hwahc_pwrlvl3: %d", hwahcp->hwahc_pm->hwahc_pm_busy);
36179430SRaymond.Chen@Sun.COM 
36189430SRaymond.Chen@Sun.COM 	ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
36199430SRaymond.Chen@Sun.COM 
36209430SRaymond.Chen@Sun.COM 	switch (hwahcp->hwahc_dev_state) {
36219430SRaymond.Chen@Sun.COM 	case USB_DEV_PWRED_DOWN:
36229430SRaymond.Chen@Sun.COM 		/* Issue USB D0 command to the device here */
36239430SRaymond.Chen@Sun.COM 		(void) usb_set_device_pwrlvl0(hwahcp->hwahc_dip);
36249430SRaymond.Chen@Sun.COM 
36259430SRaymond.Chen@Sun.COM 		/*
36269430SRaymond.Chen@Sun.COM 		 * Due to our current PM policy, it's not possible
36279430SRaymond.Chen@Sun.COM 		 * for hwa to be in USB_DEV_PWRED_DOWN between
36289430SRaymond.Chen@Sun.COM 		 * initial_start and final_stop. If it's PWRED_DOWN,
36299430SRaymond.Chen@Sun.COM 		 * it should not start. We don't need to resume
36309430SRaymond.Chen@Sun.COM 		 * soft or hardware state in this case.
36319430SRaymond.Chen@Sun.COM 		 */
36329430SRaymond.Chen@Sun.COM 		if (hwahcp->hwahc_hw_state == HWAHC_HW_STOPPED) {
36339430SRaymond.Chen@Sun.COM 			/* no need to start hc */
36349430SRaymond.Chen@Sun.COM 			hwahcp->hwahc_dev_state = USB_DEV_ONLINE;
36359430SRaymond.Chen@Sun.COM 			hwahcp->hwahc_pm->hwahc_current_power =
36369430SRaymond.Chen@Sun.COM 			    USB_DEV_OS_FULL_PWR;
36379430SRaymond.Chen@Sun.COM 
36389430SRaymond.Chen@Sun.COM 			return (USB_SUCCESS);
36399430SRaymond.Chen@Sun.COM 		}
36409430SRaymond.Chen@Sun.COM 
36419430SRaymond.Chen@Sun.COM 		hwahcp->hwahc_pm->hwahc_current_power = USB_DEV_OS_FULL_PWR;
36429430SRaymond.Chen@Sun.COM 
36439430SRaymond.Chen@Sun.COM 		/* FALLTHRU */
36449430SRaymond.Chen@Sun.COM 	case USB_DEV_ONLINE:
36459430SRaymond.Chen@Sun.COM 		/* we are already in full power */
36469430SRaymond.Chen@Sun.COM 		/* FALLTHRU */
36479430SRaymond.Chen@Sun.COM 	case USB_DEV_DISCONNECTED:
36489430SRaymond.Chen@Sun.COM 	case USB_DEV_SUSPENDED:
36499430SRaymond.Chen@Sun.COM 		/*
36509430SRaymond.Chen@Sun.COM 		 * PM framework tries to put you in full power
36519430SRaymond.Chen@Sun.COM 		 * during system shutdown. If we are disconnected
36529430SRaymond.Chen@Sun.COM 		 * return success. Also, we should not change state
36539430SRaymond.Chen@Sun.COM 		 * when we are disconnected or suspended or about to
36549430SRaymond.Chen@Sun.COM 		 * transition to that state
36559430SRaymond.Chen@Sun.COM 		 */
36569430SRaymond.Chen@Sun.COM 
36579430SRaymond.Chen@Sun.COM 		return (USB_SUCCESS);
36589430SRaymond.Chen@Sun.COM 	default:
36599430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
36609430SRaymond.Chen@Sun.COM 		    "hwahc_pwrlvl3: illegal dev_state=%d",
36619430SRaymond.Chen@Sun.COM 		    hwahcp->hwahc_dev_state);
36629430SRaymond.Chen@Sun.COM 
36639430SRaymond.Chen@Sun.COM 
36649430SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
36659430SRaymond.Chen@Sun.COM 	}
36669430SRaymond.Chen@Sun.COM }
36679430SRaymond.Chen@Sun.COM 
36689430SRaymond.Chen@Sun.COM /*
36699430SRaymond.Chen@Sun.COM  * Host power management: stop channel
36709430SRaymond.Chen@Sun.COM  * 	See Section 4.16.2.1 for details
36719430SRaymond.Chen@Sun.COM  *	See Section 8.1.0 for HWA suspend/resume
36729430SRaymond.Chen@Sun.COM  */
36739430SRaymond.Chen@Sun.COM static int
hwahc_hc_channel_suspend(hwahc_state_t * hwahcp)36749430SRaymond.Chen@Sun.COM hwahc_hc_channel_suspend(hwahc_state_t *hwahcp)
36759430SRaymond.Chen@Sun.COM {
36769430SRaymond.Chen@Sun.COM 	int			rval;
36779430SRaymond.Chen@Sun.COM 
36789430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
36799430SRaymond.Chen@Sun.COM 	    "hwahc_hc_channel_suspend:");
36809430SRaymond.Chen@Sun.COM 
36819430SRaymond.Chen@Sun.COM 	ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
36829430SRaymond.Chen@Sun.COM 
36839430SRaymond.Chen@Sun.COM 	/* no need to suspend if host hw was not started */
36849430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_hw_state != HWAHC_HW_STARTED) {
36859430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
36869430SRaymond.Chen@Sun.COM 		    "hwahc_hc_channel_suspend: hw already stopped");
36879430SRaymond.Chen@Sun.COM 
36889430SRaymond.Chen@Sun.COM 		return (USB_SUCCESS);
36899430SRaymond.Chen@Sun.COM 	}
36909430SRaymond.Chen@Sun.COM 
36919430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_hw_state == HWAHC_HW_CH_SUSPEND) {
36929430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
36939430SRaymond.Chen@Sun.COM 		    "hwahc_hc_channel_suspend: already suspended");
36949430SRaymond.Chen@Sun.COM 
36959430SRaymond.Chen@Sun.COM 		return (USB_SUCCESS);
36969430SRaymond.Chen@Sun.COM 	}
36979430SRaymond.Chen@Sun.COM 
36989797SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
36999430SRaymond.Chen@Sun.COM 	/* suspend host, refer to WUSB 1.0 spec 8.5.3.14 */
37009430SRaymond.Chen@Sun.COM 	rval = wusb_hc_stop_ch(&hwahcp->hwahc_hc_data, 10000); /* 10ms */
37019797SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
37029430SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
37039430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
37049430SRaymond.Chen@Sun.COM 		    "hwahc_hc_channel_suspend: wusb channel stop fails");
37059430SRaymond.Chen@Sun.COM 
37069430SRaymond.Chen@Sun.COM 		return (rval);
37079430SRaymond.Chen@Sun.COM 	}
37089430SRaymond.Chen@Sun.COM 
37099430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_hw_state = HWAHC_HW_CH_SUSPEND;
37109430SRaymond.Chen@Sun.COM 
37119430SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
37129430SRaymond.Chen@Sun.COM }
37139430SRaymond.Chen@Sun.COM 
37149430SRaymond.Chen@Sun.COM /*
37159430SRaymond.Chen@Sun.COM  * Parse security descriptors, see T.8-43
37169430SRaymond.Chen@Sun.COM  * 	put result in secrt_data
37179430SRaymond.Chen@Sun.COM  */
37189430SRaymond.Chen@Sun.COM static int
hwahc_parse_security_data(wusb_secrt_data_t * secrt_data,usb_cfg_data_t * cfg_data)37199430SRaymond.Chen@Sun.COM hwahc_parse_security_data(wusb_secrt_data_t *secrt_data,
37209430SRaymond.Chen@Sun.COM 	usb_cfg_data_t *cfg_data)
37219430SRaymond.Chen@Sun.COM {
37229430SRaymond.Chen@Sun.COM 	int		i, j;
37239430SRaymond.Chen@Sun.COM 	usb_cvs_data_t	*cvs_data;
37249430SRaymond.Chen@Sun.COM 	size_t		count, len;
37259430SRaymond.Chen@Sun.COM 
37269430SRaymond.Chen@Sun.COM 	if ((secrt_data == NULL) || (cfg_data == NULL)) {
37279430SRaymond.Chen@Sun.COM 		return (USB_INVALID_ARGS);
37289430SRaymond.Chen@Sun.COM 	}
37299430SRaymond.Chen@Sun.COM 
37309430SRaymond.Chen@Sun.COM 	for (i = 0; i < cfg_data->cfg_n_cvs; i++) {
37319430SRaymond.Chen@Sun.COM 		cvs_data = &cfg_data->cfg_cvs[i];
37329430SRaymond.Chen@Sun.COM 		if (cvs_data == NULL) {
37339430SRaymond.Chen@Sun.COM 			continue;
37349430SRaymond.Chen@Sun.COM 		}
37359430SRaymond.Chen@Sun.COM 		if (cvs_data->cvs_buf[1] == USB_DESCR_TYPE_SECURITY) {
37369430SRaymond.Chen@Sun.COM 			count = usb_parse_data("ccsc",
37379430SRaymond.Chen@Sun.COM 			    cvs_data->cvs_buf, cvs_data->cvs_buf_len,
37389430SRaymond.Chen@Sun.COM 			    (void *)&secrt_data->secrt_descr,
37399430SRaymond.Chen@Sun.COM 			    (size_t)USB_SECURITY_DESCR_SIZE);
37409430SRaymond.Chen@Sun.COM 			if (count != USB_SECURITY_DESCR_SIZE) {
37419430SRaymond.Chen@Sun.COM 
37429430SRaymond.Chen@Sun.COM 				return (USB_FAILURE);
37439430SRaymond.Chen@Sun.COM 			} else {
37449430SRaymond.Chen@Sun.COM 				secrt_data->secrt_n_encry =
37459430SRaymond.Chen@Sun.COM 				    secrt_data->secrt_descr.bNumEncryptionTypes;
37469430SRaymond.Chen@Sun.COM 				len = sizeof (usb_encryption_descr_t) *
37479430SRaymond.Chen@Sun.COM 				    secrt_data->secrt_n_encry;
37489430SRaymond.Chen@Sun.COM 
37499430SRaymond.Chen@Sun.COM 				secrt_data->secrt_encry_descr =
37509430SRaymond.Chen@Sun.COM 				    (usb_encryption_descr_t *)kmem_alloc(len,
37519430SRaymond.Chen@Sun.COM 				    KM_SLEEP);
37529430SRaymond.Chen@Sun.COM 
37539430SRaymond.Chen@Sun.COM 				for (j = 0; j < secrt_data->secrt_n_encry;
37549430SRaymond.Chen@Sun.COM 				    j++) {
37559430SRaymond.Chen@Sun.COM 					cvs_data =
37569430SRaymond.Chen@Sun.COM 					    &cfg_data->cfg_cvs[i + j + 1];
37579430SRaymond.Chen@Sun.COM 					if (cvs_data->cvs_buf[1] !=
37589430SRaymond.Chen@Sun.COM 					    USB_DESCR_TYPE_ENCRYPTION) {
37599430SRaymond.Chen@Sun.COM 						kmem_free(secrt_data->
37609430SRaymond.Chen@Sun.COM 						    secrt_encry_descr, len);
37619430SRaymond.Chen@Sun.COM 
37629430SRaymond.Chen@Sun.COM 						return (USB_FAILURE);
37639430SRaymond.Chen@Sun.COM 					}
37649430SRaymond.Chen@Sun.COM 
37659430SRaymond.Chen@Sun.COM 					/* Table 7-34 */
37669430SRaymond.Chen@Sun.COM 					count = usb_parse_data("ccccc",
37679430SRaymond.Chen@Sun.COM 					    cvs_data->cvs_buf,
37689430SRaymond.Chen@Sun.COM 					    cvs_data->cvs_buf_len,
37699430SRaymond.Chen@Sun.COM 					    (void *)&secrt_data->
37709430SRaymond.Chen@Sun.COM 					    secrt_encry_descr[j],
37719430SRaymond.Chen@Sun.COM 					    USB_ENCRYPTION_DESCR_SIZE);
37729430SRaymond.Chen@Sun.COM 					if (count !=
37739430SRaymond.Chen@Sun.COM 					    USB_ENCRYPTION_DESCR_SIZE) {
37749430SRaymond.Chen@Sun.COM 						kmem_free(secrt_data->
37759430SRaymond.Chen@Sun.COM 						    secrt_encry_descr, len);
37769430SRaymond.Chen@Sun.COM 
37779430SRaymond.Chen@Sun.COM 						return (USB_FAILURE);
37789430SRaymond.Chen@Sun.COM 
37799430SRaymond.Chen@Sun.COM 					}
37809430SRaymond.Chen@Sun.COM 				}
37819430SRaymond.Chen@Sun.COM 				return (USB_SUCCESS);
37829430SRaymond.Chen@Sun.COM 			}
37839430SRaymond.Chen@Sun.COM 		}
37849430SRaymond.Chen@Sun.COM 	}
37859430SRaymond.Chen@Sun.COM 
37869430SRaymond.Chen@Sun.COM 	return (USB_FAILURE);
37879430SRaymond.Chen@Sun.COM }
37889430SRaymond.Chen@Sun.COM 
37899430SRaymond.Chen@Sun.COM /* initialize wusb_hc_data_t structure */
37909430SRaymond.Chen@Sun.COM static void
hwahc_hc_data_init(hwahc_state_t * hwahcp)37919430SRaymond.Chen@Sun.COM hwahc_hc_data_init(hwahc_state_t *hwahcp)
37929430SRaymond.Chen@Sun.COM {
37939430SRaymond.Chen@Sun.COM 	wusb_hc_data_t	*hc_data = &hwahcp->hwahc_hc_data;
37949430SRaymond.Chen@Sun.COM 
37959430SRaymond.Chen@Sun.COM 	hc_data->hc_dip = hwahcp->hwahc_dip;
37969430SRaymond.Chen@Sun.COM 	hc_data->hc_private_data = (void *)hwahcp;
37979430SRaymond.Chen@Sun.COM 
37989430SRaymond.Chen@Sun.COM 	(void) memset(hc_data->hc_chid, 0, sizeof (hc_data->hc_chid));
37999430SRaymond.Chen@Sun.COM 
38009430SRaymond.Chen@Sun.COM 	hc_data->hc_num_mmcies = hwahcp->hwahc_wa_data.wa_descr.bNumMMCIEs;
38019430SRaymond.Chen@Sun.COM 
38029430SRaymond.Chen@Sun.COM 	ASSERT(hc_data->hc_num_mmcies != 0);
38039430SRaymond.Chen@Sun.COM 
38049430SRaymond.Chen@Sun.COM 	hc_data->hc_mmcie_list = kmem_zalloc((hc_data->hc_num_mmcies *
38059430SRaymond.Chen@Sun.COM 	    sizeof (wusb_ie_header_t *)), KM_SLEEP);
38069430SRaymond.Chen@Sun.COM 
38079430SRaymond.Chen@Sun.COM 	/* initialize frequently used IE */
38089430SRaymond.Chen@Sun.COM 	hc_data->hc_alive_ie.bIEIdentifier = WUSB_IE_DEV_KEEPALIVE;
38099430SRaymond.Chen@Sun.COM 
38109430SRaymond.Chen@Sun.COM 	/* register callbacks */
38119430SRaymond.Chen@Sun.COM 	hc_data->disconnect_dev = hwahc_disconnect_dev;
38129430SRaymond.Chen@Sun.COM 	hc_data->reconnect_dev = hwahc_reconnect_dev;
38139430SRaymond.Chen@Sun.COM 	hc_data->create_child = hwahc_create_child;
38149430SRaymond.Chen@Sun.COM 	hc_data->destroy_child = hwahc_destroy_child;
38159430SRaymond.Chen@Sun.COM 
38169430SRaymond.Chen@Sun.COM 	/* HWA HC operation functions */
38179430SRaymond.Chen@Sun.COM 	hc_data->set_encrypt = hwahc_set_encrypt;
38189430SRaymond.Chen@Sun.COM 	hc_data->set_ptk = hwahc_set_ptk;
38199430SRaymond.Chen@Sun.COM 	hc_data->set_gtk = hwahc_set_gtk;
38209430SRaymond.Chen@Sun.COM 	hc_data->set_device_info = hwahc_set_device_info;
38219430SRaymond.Chen@Sun.COM 	hc_data->set_cluster_id = hwahc_set_cluster_id;
38229430SRaymond.Chen@Sun.COM 	hc_data->set_stream_idx = hwahc_set_stream_idx;
38239430SRaymond.Chen@Sun.COM 	hc_data->set_wusb_mas = hwahc_set_wusb_mas;
38249430SRaymond.Chen@Sun.COM 	hc_data->add_mmc_ie = hwahc_add_mmc_ie;
38259430SRaymond.Chen@Sun.COM 	hc_data->rem_mmc_ie = hwahc_remove_mmc_ie;
38269430SRaymond.Chen@Sun.COM 	hc_data->stop_ch = hwahc_stop_ch;
38279430SRaymond.Chen@Sun.COM 	hc_data->set_num_dnts = hwahc_set_num_dnts;
38289430SRaymond.Chen@Sun.COM 	hc_data->get_time = hwahc_get_time;
38299430SRaymond.Chen@Sun.COM 
38309430SRaymond.Chen@Sun.COM 	hc_data->hc_num_ports = hwahcp->hwahc_wa_data.wa_descr.bNumPorts;
38319430SRaymond.Chen@Sun.COM 
38329430SRaymond.Chen@Sun.COM 	hc_data->hc_cd_list_length = (sizeof (dev_info_t **)) *
38339430SRaymond.Chen@Sun.COM 	    (hc_data->hc_num_ports + 1);
38349430SRaymond.Chen@Sun.COM 
38359430SRaymond.Chen@Sun.COM 	hc_data->hc_children_dips = (dev_info_t **)kmem_zalloc(
38369430SRaymond.Chen@Sun.COM 	    hc_data->hc_cd_list_length, KM_SLEEP);
38379430SRaymond.Chen@Sun.COM 	hc_data->hc_usba_devices = (usba_device_t **)kmem_zalloc(
38389430SRaymond.Chen@Sun.COM 	    hc_data->hc_cd_list_length, KM_SLEEP);
38399430SRaymond.Chen@Sun.COM 	hc_data->hc_dev_infos = (wusb_dev_info_t **)kmem_zalloc(
38409430SRaymond.Chen@Sun.COM 	    hc_data->hc_cd_list_length, KM_SLEEP);
38419430SRaymond.Chen@Sun.COM 
38429430SRaymond.Chen@Sun.COM 	mutex_init(&hc_data->hc_mutex, NULL, MUTEX_DRIVER, NULL);
38439430SRaymond.Chen@Sun.COM }
38449430SRaymond.Chen@Sun.COM 
38459430SRaymond.Chen@Sun.COM /* deinitialize wusb_hc_data_t structure */
38469430SRaymond.Chen@Sun.COM static void
hwahc_hc_data_fini(hwahc_state_t * hwahcp)38479430SRaymond.Chen@Sun.COM hwahc_hc_data_fini(hwahc_state_t *hwahcp)
38489430SRaymond.Chen@Sun.COM {
38499430SRaymond.Chen@Sun.COM 	int			i;
38509430SRaymond.Chen@Sun.COM 	wusb_hc_data_t		*hc_data = &hwahcp->hwahc_hc_data;
38519430SRaymond.Chen@Sun.COM 	wusb_ie_header_t	*hdr;
38529430SRaymond.Chen@Sun.COM 
38539430SRaymond.Chen@Sun.COM #ifdef DEBUG
38549430SRaymond.Chen@Sun.COM 	usb_port_t	port;
38559430SRaymond.Chen@Sun.COM #endif
38569430SRaymond.Chen@Sun.COM 
38579430SRaymond.Chen@Sun.COM 	if (hc_data->hc_mmcie_list) {
38589430SRaymond.Chen@Sun.COM 		/* Free all recorded IEs except statically allocated IEs */
38599430SRaymond.Chen@Sun.COM 		for (i = 0; i < hc_data->hc_num_mmcies; i++) {
38609430SRaymond.Chen@Sun.COM 			if (hc_data->hc_mmcie_list[i] != NULL) {
38619430SRaymond.Chen@Sun.COM 				hdr = hc_data->hc_mmcie_list[i];
38629430SRaymond.Chen@Sun.COM 				if ((hdr->bIEIdentifier !=
38639430SRaymond.Chen@Sun.COM 				    WUSB_IE_DEV_KEEPALIVE)) {
38649430SRaymond.Chen@Sun.COM 					kmem_free(hdr, hdr->bLength);
38659430SRaymond.Chen@Sun.COM 				}
38669430SRaymond.Chen@Sun.COM 				hc_data->hc_mmcie_list[i] = NULL;
38679430SRaymond.Chen@Sun.COM 			}
38689430SRaymond.Chen@Sun.COM 		}
38699430SRaymond.Chen@Sun.COM 
38709430SRaymond.Chen@Sun.COM 		kmem_free(hc_data->hc_mmcie_list,
38719430SRaymond.Chen@Sun.COM 		    hc_data->hc_num_mmcies * sizeof (wusb_ie_header_t *));
38729430SRaymond.Chen@Sun.COM 	}
38739430SRaymond.Chen@Sun.COM 
38749430SRaymond.Chen@Sun.COM 	if (hc_data->hc_cluster_id) {
38759430SRaymond.Chen@Sun.COM 		wusb_hc_free_cluster_id(hc_data->hc_cluster_id);
38769430SRaymond.Chen@Sun.COM 	}
38779430SRaymond.Chen@Sun.COM 
38789430SRaymond.Chen@Sun.COM 	if (hc_data->hc_cc_list) {
38799430SRaymond.Chen@Sun.COM 		wusb_hc_free_cc_list(hc_data->hc_cc_list);
38809430SRaymond.Chen@Sun.COM 	}
38819430SRaymond.Chen@Sun.COM 
38829430SRaymond.Chen@Sun.COM #ifdef DEBUG
38839430SRaymond.Chen@Sun.COM 	for (port = 1; port <= hc_data->hc_num_ports; port++) {
38849430SRaymond.Chen@Sun.COM 		ASSERT(hc_data->hc_usba_devices[port] == NULL);
38859430SRaymond.Chen@Sun.COM 		ASSERT(hc_data->hc_children_dips[port] == NULL);
38869430SRaymond.Chen@Sun.COM 		ASSERT(hc_data->hc_dev_infos[port] == NULL);
38879430SRaymond.Chen@Sun.COM 	}
38889430SRaymond.Chen@Sun.COM #endif
38899430SRaymond.Chen@Sun.COM 
38909430SRaymond.Chen@Sun.COM 	kmem_free(hc_data->hc_children_dips, hc_data->hc_cd_list_length);
38919430SRaymond.Chen@Sun.COM 	kmem_free(hc_data->hc_usba_devices, hc_data->hc_cd_list_length);
38929430SRaymond.Chen@Sun.COM 	kmem_free(hc_data->hc_dev_infos, hc_data->hc_cd_list_length);
38939430SRaymond.Chen@Sun.COM 
38949430SRaymond.Chen@Sun.COM 	mutex_destroy(&hc_data->hc_mutex);
38959430SRaymond.Chen@Sun.COM }
38969430SRaymond.Chen@Sun.COM 
38979430SRaymond.Chen@Sun.COM /* fully start the HWA hw */
38989430SRaymond.Chen@Sun.COM static int
hwahc_hc_initial_start(hwahc_state_t * hwahcp)38999430SRaymond.Chen@Sun.COM hwahc_hc_initial_start(hwahc_state_t *hwahcp)
39009430SRaymond.Chen@Sun.COM {
39019430SRaymond.Chen@Sun.COM 	uint8_t	stream_idx;
39029430SRaymond.Chen@Sun.COM 	uint8_t	mas[WUSB_SET_WUSB_MAS_LEN];
39039430SRaymond.Chen@Sun.COM 	int	rval;
39049430SRaymond.Chen@Sun.COM 	uint8_t	cluster_id = 0;
39059430SRaymond.Chen@Sun.COM 
39069430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
39079430SRaymond.Chen@Sun.COM 	    "hwahc_hc_initial_start:");
39089430SRaymond.Chen@Sun.COM 
39099430SRaymond.Chen@Sun.COM 	ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
39109430SRaymond.Chen@Sun.COM 
39119430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_dev_state != USB_DEV_ONLINE) {
39129430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
39139430SRaymond.Chen@Sun.COM 		    "hwahc_hc_initial_start: invalid dev state = %d",
39149430SRaymond.Chen@Sun.COM 		    hwahcp->hwahc_dev_state);
39159430SRaymond.Chen@Sun.COM 
39169430SRaymond.Chen@Sun.COM 		return (USB_INVALID_REQUEST);
39179430SRaymond.Chen@Sun.COM 	}
39189430SRaymond.Chen@Sun.COM 
39199430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_hw_state != HWAHC_HW_STOPPED) {
39209430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
39219430SRaymond.Chen@Sun.COM 		    "hwahc_hc_initial_start: invalid hw state");
39229430SRaymond.Chen@Sun.COM 
39239430SRaymond.Chen@Sun.COM 		return (USB_INVALID_REQUEST);
39249430SRaymond.Chen@Sun.COM 	}
39259430SRaymond.Chen@Sun.COM 
39269430SRaymond.Chen@Sun.COM 	/*
39279430SRaymond.Chen@Sun.COM 	 * start beacon of radio layer
39289430SRaymond.Chen@Sun.COM 	 * We're not sure if previouse channel is occupied or not. So, let
39299430SRaymond.Chen@Sun.COM 	 * UWB allocates a free channel for this hwa. Then we can start
39309430SRaymond.Chen@Sun.COM 	 * beacon.
39319430SRaymond.Chen@Sun.COM 	 */
39329430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_hc_data.hc_channel =
39339430SRaymond.Chen@Sun.COM 	    uwb_allocate_channel(hwahcp->hwahc_dip);
39349430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_hc_data.hc_channel  == 0) {
39359430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA,
39369430SRaymond.Chen@Sun.COM 		    hwahcp->hwahc_log_handle,
39379430SRaymond.Chen@Sun.COM 		    "wusb_hc_initial_start: channel = %d",
39389430SRaymond.Chen@Sun.COM 		    hwahcp->hwahc_hc_data.hc_channel);
39399430SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
39409430SRaymond.Chen@Sun.COM 	}
39419430SRaymond.Chen@Sun.COM 
39429430SRaymond.Chen@Sun.COM 	if ((rval = uwb_start_beacon(hwahcp->hwahc_dip,
39439430SRaymond.Chen@Sun.COM 	    hwahcp->hwahc_hc_data.hc_channel)) != USB_SUCCESS) {
39449430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA,
39459430SRaymond.Chen@Sun.COM 		    hwahcp->hwahc_log_handle,
39469430SRaymond.Chen@Sun.COM 		    "wusb_hc_initial_start: start uwb beacon failed");
39479430SRaymond.Chen@Sun.COM 
39489430SRaymond.Chen@Sun.COM 		return (rval);
39499430SRaymond.Chen@Sun.COM 	}
39509430SRaymond.Chen@Sun.COM 
39519797SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
39529430SRaymond.Chen@Sun.COM 	/* reset wire adapter */
39539430SRaymond.Chen@Sun.COM 	rval = wusb_wa_reset(&hwahcp->hwahc_wa_data,
39549430SRaymond.Chen@Sun.COM 	    hwahcp->hwahc_default_pipe);
39559430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
39569430SRaymond.Chen@Sun.COM 	if (rval != SUCCESS) {
39579430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
39589430SRaymond.Chen@Sun.COM 		    "hwahc_hc_initial_start: reset wa fails");
39599430SRaymond.Chen@Sun.COM 
39609430SRaymond.Chen@Sun.COM 		goto err;
39619430SRaymond.Chen@Sun.COM 	}
39629430SRaymond.Chen@Sun.COM 
39639430SRaymond.Chen@Sun.COM 	/* reuse the old cluster id or assign one */
39649430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_hc_data.hc_cluster_id) {
39659430SRaymond.Chen@Sun.COM 		cluster_id = hwahcp->hwahc_hc_data.hc_cluster_id;
39669430SRaymond.Chen@Sun.COM 	} else {
39679430SRaymond.Chen@Sun.COM 		cluster_id = wusb_hc_get_cluster_id();
39689430SRaymond.Chen@Sun.COM 		if (cluster_id == 0) {
39699430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_ATTA,
39709430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
39719430SRaymond.Chen@Sun.COM 			    "hwahc_hc_initial_start: cannot get cluster id");
39729430SRaymond.Chen@Sun.COM 			rval = USB_NO_RESOURCES;
39739430SRaymond.Chen@Sun.COM 
39749430SRaymond.Chen@Sun.COM 			goto err;
39759430SRaymond.Chen@Sun.COM 		}
39769430SRaymond.Chen@Sun.COM 	}
39779430SRaymond.Chen@Sun.COM 
39789797SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
39799430SRaymond.Chen@Sun.COM 	/* set cluster id for the wusb channel */
39809430SRaymond.Chen@Sun.COM 	rval = wusb_hc_set_cluster_id(&hwahcp->hwahc_hc_data, cluster_id);
39819430SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
39829430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
39839430SRaymond.Chen@Sun.COM 		    "hwahc_hc_initial_start: set cluster id %d fails",
39849430SRaymond.Chen@Sun.COM 		    cluster_id);
39859797SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
39869430SRaymond.Chen@Sun.COM 
39879430SRaymond.Chen@Sun.COM 		goto err;
39889430SRaymond.Chen@Sun.COM 	}
39899430SRaymond.Chen@Sun.COM 
39909430SRaymond.Chen@Sun.COM 	/* UWB should be responsible for assigning stream index */
39919430SRaymond.Chen@Sun.COM 	stream_idx = 1;
39929430SRaymond.Chen@Sun.COM 
39939430SRaymond.Chen@Sun.COM 	rval = wusb_hc_set_stream_idx(&hwahcp->hwahc_hc_data, stream_idx);
39949430SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
39959430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
39969430SRaymond.Chen@Sun.COM 		    "hwahc_hc_initial_start: set stream idx %d fails",
39979430SRaymond.Chen@Sun.COM 		    stream_idx);
39989797SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
39999430SRaymond.Chen@Sun.COM 
40009430SRaymond.Chen@Sun.COM 		goto err;
40019430SRaymond.Chen@Sun.COM 	}
40029430SRaymond.Chen@Sun.COM 
40039430SRaymond.Chen@Sun.COM 	/* set dnts slot */
40049430SRaymond.Chen@Sun.COM 	rval = wusb_hc_set_num_dnts(&hwahcp->hwahc_hc_data,
40059430SRaymond.Chen@Sun.COM 	    HWAHC_DEFAULT_DNTS_INTERVAL, HWAHC_DEFAULT_DNTS_SLOT_NUM);
40069430SRaymond.Chen@Sun.COM 
40079430SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
40089430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
40099430SRaymond.Chen@Sun.COM 		    "hwahc_hc_initial_start: set num dnts fails");
40109797SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
40119430SRaymond.Chen@Sun.COM 
40129430SRaymond.Chen@Sun.COM 		goto err;
40139430SRaymond.Chen@Sun.COM 	}
40149430SRaymond.Chen@Sun.COM 
40159430SRaymond.Chen@Sun.COM 	/* set host info IE */
40169430SRaymond.Chen@Sun.COM 	rval = wusb_hc_add_host_info(&hwahcp->hwahc_hc_data, stream_idx);
40179430SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
40189430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
40199430SRaymond.Chen@Sun.COM 		    "hwahc_hc_initial_start: add hostinfo ie fails");
40209797SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
40219430SRaymond.Chen@Sun.COM 
40229430SRaymond.Chen@Sun.COM 		goto err;
40239430SRaymond.Chen@Sun.COM 	}
40249430SRaymond.Chen@Sun.COM 
40259430SRaymond.Chen@Sun.COM 	/* reserve MAS slots for the host, need a way to assign */
40269430SRaymond.Chen@Sun.COM 	(void) memset(mas, 0xff, WUSB_SET_WUSB_MAS_LEN);
40279430SRaymond.Chen@Sun.COM 	mas[0] = 0xf0;	/* the first 4 slots are for beacons */
40289430SRaymond.Chen@Sun.COM 	rval = wusb_hc_set_wusb_mas(&hwahcp->hwahc_hc_data, mas);
40299797SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
40309430SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
40319430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
40329430SRaymond.Chen@Sun.COM 		    "hwahc_hc_initial_start: set wusb mas fails");
40339430SRaymond.Chen@Sun.COM 
40349430SRaymond.Chen@Sun.COM 		goto err;
40359430SRaymond.Chen@Sun.COM 	}
40369430SRaymond.Chen@Sun.COM 
40379430SRaymond.Chen@Sun.COM 	/* record the available MAS slots */
40389430SRaymond.Chen@Sun.COM 	(void) memcpy(hwahcp->hwahc_hc_data.hc_mas, mas, WUSB_SET_WUSB_MAS_LEN);
40399430SRaymond.Chen@Sun.COM 
40409430SRaymond.Chen@Sun.COM 	/* Set initial GTK/TKID to random values */
4041*10912SRaymond.Chen@Sun.COM 	(void) random_get_pseudo_bytes(dft_gtk, 16);
4042*10912SRaymond.Chen@Sun.COM 	(void) random_get_pseudo_bytes(dft_gtkid, 3);
40439430SRaymond.Chen@Sun.COM 
40449430SRaymond.Chen@Sun.COM 	/* set default GTK, need a way to dynamically compute it */
40459797SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
40469430SRaymond.Chen@Sun.COM 	rval = wusb_hc_set_gtk(&hwahcp->hwahc_hc_data, dft_gtk, dft_gtkid);
40479430SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
40489430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
40499430SRaymond.Chen@Sun.COM 		    "hwahc_hc_initial_start: set gtk fails");
40509797SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
40519430SRaymond.Chen@Sun.COM 
40529430SRaymond.Chen@Sun.COM 		goto err;
40539430SRaymond.Chen@Sun.COM 	}
40549430SRaymond.Chen@Sun.COM 
40559430SRaymond.Chen@Sun.COM 	/* enable wire adapter */
40569430SRaymond.Chen@Sun.COM 	rval = wusb_wa_enable(&hwahcp->hwahc_wa_data,
40579430SRaymond.Chen@Sun.COM 	    hwahcp->hwahc_default_pipe);
40589430SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
40599430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
40609430SRaymond.Chen@Sun.COM 		    "hwahc_hc_initial_start: enable wa fails");
40619797SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
40629430SRaymond.Chen@Sun.COM 
40639430SRaymond.Chen@Sun.COM 		goto err;
40649430SRaymond.Chen@Sun.COM 	}
40659430SRaymond.Chen@Sun.COM 
40669430SRaymond.Chen@Sun.COM 	/* Start Notification endpoint */
40679430SRaymond.Chen@Sun.COM 	rval = wusb_wa_start_nep(&hwahcp->hwahc_wa_data, USB_FLAGS_SLEEP);
40689430SRaymond.Chen@Sun.COM 
40699430SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
40709430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
40719430SRaymond.Chen@Sun.COM 		    "hwahc_hc_initial_start: start notification ep fails");
40729430SRaymond.Chen@Sun.COM 		(void) wusb_wa_disable(&hwahcp->hwahc_wa_data,
40739430SRaymond.Chen@Sun.COM 		    hwahcp->hwahc_default_pipe);
40749430SRaymond.Chen@Sun.COM 
40759430SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
40769430SRaymond.Chen@Sun.COM 
40779430SRaymond.Chen@Sun.COM 		goto err;
40789430SRaymond.Chen@Sun.COM 	}
40799430SRaymond.Chen@Sun.COM 
40809430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
40819430SRaymond.Chen@Sun.COM 
40829430SRaymond.Chen@Sun.COM 	/*
40839430SRaymond.Chen@Sun.COM 	 * Handle transfer results on bulk-in ep
40849430SRaymond.Chen@Sun.COM 	 * The bulk-in ep needs to be polled no matter the completion
40859430SRaymond.Chen@Sun.COM 	 * notification is received or not to avoid miss result.
40869430SRaymond.Chen@Sun.COM 	 */
40879430SRaymond.Chen@Sun.COM 	rval = hwahc_start_result_thread(hwahcp);
40889430SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
40899430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
40909430SRaymond.Chen@Sun.COM 		    "hwahc_hc_initial_start: start result thread fails, "
40919430SRaymond.Chen@Sun.COM 		    "rval = %d", rval);
40929430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
40939430SRaymond.Chen@Sun.COM 		wusb_wa_stop_nep(&hwahcp->hwahc_wa_data);
40949430SRaymond.Chen@Sun.COM 		(void) wusb_wa_disable(&hwahcp->hwahc_wa_data,
40959430SRaymond.Chen@Sun.COM 		    hwahcp->hwahc_default_pipe);
40969430SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
40979430SRaymond.Chen@Sun.COM 
40989430SRaymond.Chen@Sun.COM 		goto err;
40999430SRaymond.Chen@Sun.COM 	}
41009430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
41019430SRaymond.Chen@Sun.COM 	    "hwahc_hc_initial_start: start result thread success");
41029430SRaymond.Chen@Sun.COM 
41039430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_hw_state = HWAHC_HW_STARTED;
41049430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_OPERATIONAL_STATE;
41059430SRaymond.Chen@Sun.COM 
41069430SRaymond.Chen@Sun.COM 	/* Don't do PM on an active beacon hwa until explicitly stopped */
41079430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
41089430SRaymond.Chen@Sun.COM 	hwahc_pm_busy_component(hwahcp);
41099430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
41109430SRaymond.Chen@Sun.COM 
41119430SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
41129430SRaymond.Chen@Sun.COM 
41139430SRaymond.Chen@Sun.COM err:
41149430SRaymond.Chen@Sun.COM 	if (cluster_id != 0) {
41159430SRaymond.Chen@Sun.COM 		wusb_hc_free_cluster_id(cluster_id);
41169430SRaymond.Chen@Sun.COM 	}
41179430SRaymond.Chen@Sun.COM 
41189797SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
41199430SRaymond.Chen@Sun.COM 	(void) uwb_stop_beacon(hwahcp->hwahc_dip);
41209797SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
41219430SRaymond.Chen@Sun.COM 
41229430SRaymond.Chen@Sun.COM 	return (rval);
41239430SRaymond.Chen@Sun.COM }
41249430SRaymond.Chen@Sun.COM 
41259430SRaymond.Chen@Sun.COM /* entirely stop the HWA from working */
41269430SRaymond.Chen@Sun.COM static int
hwahc_hc_final_stop(hwahc_state_t * hwahcp)41279430SRaymond.Chen@Sun.COM hwahc_hc_final_stop(hwahc_state_t *hwahcp)
41289430SRaymond.Chen@Sun.COM {
41299430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
41309430SRaymond.Chen@Sun.COM 	    "hwahc_hc_final_stop:");
41319430SRaymond.Chen@Sun.COM 
41329430SRaymond.Chen@Sun.COM 	ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
41339430SRaymond.Chen@Sun.COM 
41349430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_hw_state == HWAHC_HW_STOPPED) {
41359430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
41369430SRaymond.Chen@Sun.COM 		    "hwahc_hc_final_stop: already stopped");
41379430SRaymond.Chen@Sun.COM 
41389430SRaymond.Chen@Sun.COM 		return (USB_SUCCESS);
41399430SRaymond.Chen@Sun.COM 	}
41409430SRaymond.Chen@Sun.COM 
41419430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_dev_state == USB_DEV_SUSPENDED) {
41429430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
41439430SRaymond.Chen@Sun.COM 		    "hwahc_hc_final_stop: invalid dev state = %d",
41449430SRaymond.Chen@Sun.COM 		    hwahcp->hwahc_dev_state);
41459430SRaymond.Chen@Sun.COM 
41469430SRaymond.Chen@Sun.COM 		return (USB_INVALID_REQUEST);
41479430SRaymond.Chen@Sun.COM 	}
41489430SRaymond.Chen@Sun.COM 
41499430SRaymond.Chen@Sun.COM 	/* might have been powered down before detaching */
41509430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
41519430SRaymond.Chen@Sun.COM 	(void) pm_raise_power(hwahcp->hwahc_dip, 0, USB_DEV_OS_FULL_PWR);
41529430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
41539430SRaymond.Chen@Sun.COM 
41549430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_dev_state != USB_DEV_DISCONNECTED) {
41559430SRaymond.Chen@Sun.COM 		/* notify children the host is going to stop */
41569430SRaymond.Chen@Sun.COM 		(void) hwahc_hc_channel_suspend(hwahcp);
41579430SRaymond.Chen@Sun.COM 
41589430SRaymond.Chen@Sun.COM 		/* release mutex here to avoid deadlock with exc_cb */
41599430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
41609430SRaymond.Chen@Sun.COM 
41619430SRaymond.Chen@Sun.COM 		/* stop notification endpoint */
41629430SRaymond.Chen@Sun.COM 		wusb_wa_stop_nep(&hwahcp->hwahc_wa_data);
41639430SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
41649430SRaymond.Chen@Sun.COM 
41659430SRaymond.Chen@Sun.COM 		/* stop bulk-in ept from listening result */
41669430SRaymond.Chen@Sun.COM 		hwahc_stop_result_thread(hwahcp);
41679430SRaymond.Chen@Sun.COM 
41689430SRaymond.Chen@Sun.COM 		/* drain the device notifications */
41699430SRaymond.Chen@Sun.COM 		hwahc_drain_notif_queue(hwahcp);
41709430SRaymond.Chen@Sun.COM 
41719430SRaymond.Chen@Sun.COM 		/* disable wire adapter */
41729430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
41739430SRaymond.Chen@Sun.COM 		(void) wusb_wa_disable(&hwahcp->hwahc_wa_data,
41749430SRaymond.Chen@Sun.COM 		    hwahcp->hwahc_default_pipe);
41759430SRaymond.Chen@Sun.COM 
41769430SRaymond.Chen@Sun.COM 		/* stop beaconing. Not necessary to unreserve mas */
41779430SRaymond.Chen@Sun.COM 		(void) uwb_stop_beacon(hwahcp->hwahc_dip);
41789430SRaymond.Chen@Sun.COM 
41799430SRaymond.Chen@Sun.COM 		wusb_hc_rem_host_info(&hwahcp->hwahc_hc_data);
41809430SRaymond.Chen@Sun.COM 
41819430SRaymond.Chen@Sun.COM 		/* Manually remove all connected children */
41829430SRaymond.Chen@Sun.COM 		hwahc_run_callbacks(hwahcp, USBA_EVENT_TAG_HOT_REMOVAL);
41839430SRaymond.Chen@Sun.COM 
41849430SRaymond.Chen@Sun.COM 		/* delete all the children */
41859430SRaymond.Chen@Sun.COM 		(void) hwahc_cleanup_child(hwahcp->hwahc_dip);
41869797SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
41879430SRaymond.Chen@Sun.COM 	}
41889430SRaymond.Chen@Sun.COM 
41899430SRaymond.Chen@Sun.COM 	/*
41909430SRaymond.Chen@Sun.COM 	 * we make it busy at hwahc_hc_initial_start(). This idle operation
41919430SRaymond.Chen@Sun.COM 	 * is to match that busy operation.
41929430SRaymond.Chen@Sun.COM 	 * All other busy/idle operations should have been matched.
41939430SRaymond.Chen@Sun.COM 	 */
41949430SRaymond.Chen@Sun.COM 	if ((hwahcp->hwahc_hw_state == HWAHC_HW_STARTED) &&
41959430SRaymond.Chen@Sun.COM 	    (hwahcp->hwahc_hc_soft_state == HWAHC_CTRL_OPERATIONAL_STATE)) {
41969430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
41979430SRaymond.Chen@Sun.COM 		    "hwahc_hc_final_stop: pm_busy=%d",
41989430SRaymond.Chen@Sun.COM 		    hwahcp->hwahc_pm->hwahc_pm_busy);
41999430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
42009430SRaymond.Chen@Sun.COM 		hwahc_pm_idle_component(hwahcp);
42019430SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
42029430SRaymond.Chen@Sun.COM 	}
42039430SRaymond.Chen@Sun.COM 
42049430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_hw_state = HWAHC_HW_STOPPED;
42059430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_hc_soft_state == HWAHC_CTRL_OPERATIONAL_STATE) {
42069430SRaymond.Chen@Sun.COM 		hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_INIT_STATE;
42079430SRaymond.Chen@Sun.COM 	}
42089430SRaymond.Chen@Sun.COM 
42099430SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
42109430SRaymond.Chen@Sun.COM }
42119430SRaymond.Chen@Sun.COM 
42129430SRaymond.Chen@Sun.COM /*
42139430SRaymond.Chen@Sun.COM  * init WUSB channel, this is only part of the full hw start operations
42149430SRaymond.Chen@Sun.COM  * including setting wusb channel stream idx, wusb MAS slots reservation
42159430SRaymond.Chen@Sun.COM  * and adding host info IE
42169430SRaymond.Chen@Sun.COM  */
42179430SRaymond.Chen@Sun.COM static int
hwahc_hc_channel_start(hwahc_state_t * hwahcp)42189430SRaymond.Chen@Sun.COM hwahc_hc_channel_start(hwahc_state_t *hwahcp)
42199430SRaymond.Chen@Sun.COM {
42209430SRaymond.Chen@Sun.COM 	uint8_t			stream_idx;
42219430SRaymond.Chen@Sun.COM 	uint8_t			mas[WUSB_SET_WUSB_MAS_LEN];
42229430SRaymond.Chen@Sun.COM 	int			rval;
42239430SRaymond.Chen@Sun.COM 
42249430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
42259430SRaymond.Chen@Sun.COM 	    "hwahc_hc_channel_start:");
42269430SRaymond.Chen@Sun.COM 
42279430SRaymond.Chen@Sun.COM 	ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
42289430SRaymond.Chen@Sun.COM 
42299430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_dev_state != USB_DEV_ONLINE) {
42309430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
42319430SRaymond.Chen@Sun.COM 		    "hwahc_hc_channel_start: invalid dev_state = %d",
42329430SRaymond.Chen@Sun.COM 		    hwahcp->hwahc_dev_state);
42339430SRaymond.Chen@Sun.COM 
42349430SRaymond.Chen@Sun.COM 		return (USB_INVALID_REQUEST);
42359430SRaymond.Chen@Sun.COM 	}
42369430SRaymond.Chen@Sun.COM 
42379430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_hw_state != HWAHC_HW_CH_STOPPED) {
42389430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
42399430SRaymond.Chen@Sun.COM 		    "hwahc_hc_channel_start: invalid hw state");
42409430SRaymond.Chen@Sun.COM 
42419430SRaymond.Chen@Sun.COM 		return (USB_INVALID_REQUEST);
42429430SRaymond.Chen@Sun.COM 	}
42439430SRaymond.Chen@Sun.COM 
42449430SRaymond.Chen@Sun.COM 	/* set stream idx */
42459430SRaymond.Chen@Sun.COM 	stream_idx = 1;
42469430SRaymond.Chen@Sun.COM 
42479797SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
42489430SRaymond.Chen@Sun.COM 	rval = wusb_hc_set_stream_idx(&hwahcp->hwahc_hc_data, stream_idx);
42499430SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
42509430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
42519430SRaymond.Chen@Sun.COM 		    "hwahc_hc_channel_start: set stream idx %d fails",
42529430SRaymond.Chen@Sun.COM 		    stream_idx);
42539797SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
42549430SRaymond.Chen@Sun.COM 
42559430SRaymond.Chen@Sun.COM 		return (rval);
42569430SRaymond.Chen@Sun.COM 	}
42579430SRaymond.Chen@Sun.COM 
42589430SRaymond.Chen@Sun.COM 	/* reserve MAS slots for the host. Should be allocated by UWB */
42599430SRaymond.Chen@Sun.COM 	(void) memset(mas, 0xff, WUSB_SET_WUSB_MAS_LEN);
42609430SRaymond.Chen@Sun.COM 	mas[0] = 0xf0;	/* for beacons */
42619430SRaymond.Chen@Sun.COM 	rval = wusb_hc_set_wusb_mas(&hwahcp->hwahc_hc_data, mas);
42629430SRaymond.Chen@Sun.COM 
42639430SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
42649430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
42659430SRaymond.Chen@Sun.COM 		    "hwahc_hc_channel_start: set wusb mas fails");
42669797SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
42679430SRaymond.Chen@Sun.COM 
42689430SRaymond.Chen@Sun.COM 		return (rval);
42699430SRaymond.Chen@Sun.COM 	}
42709430SRaymond.Chen@Sun.COM 	(void) memcpy(hwahcp->hwahc_hc_data.hc_mas, mas, WUSB_SET_WUSB_MAS_LEN);
42719430SRaymond.Chen@Sun.COM 
42729430SRaymond.Chen@Sun.COM 	/* set host info IE */
42739430SRaymond.Chen@Sun.COM 	rval = wusb_hc_add_host_info(&hwahcp->hwahc_hc_data, stream_idx);
42749430SRaymond.Chen@Sun.COM 
42759430SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
42769430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
42779430SRaymond.Chen@Sun.COM 		    "hwahc_hc_channel_start: add hostinfo ie fails");
42789797SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
42799430SRaymond.Chen@Sun.COM 
42809430SRaymond.Chen@Sun.COM 		return (rval);
42819430SRaymond.Chen@Sun.COM 	}
42829430SRaymond.Chen@Sun.COM 
42839797SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
42849430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_hw_state = HWAHC_HW_STARTED;
42859430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_OPERATIONAL_STATE;
42869430SRaymond.Chen@Sun.COM 
42879430SRaymond.Chen@Sun.COM 	/* do not PM this device, once we're ready to accept DN */
42889430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
42899430SRaymond.Chen@Sun.COM 	hwahc_pm_busy_component(hwahcp);
42909430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
42919430SRaymond.Chen@Sun.COM 
42929430SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
42939430SRaymond.Chen@Sun.COM }
42949430SRaymond.Chen@Sun.COM 
42959430SRaymond.Chen@Sun.COM /*
42969430SRaymond.Chen@Sun.COM  * stop WUSB channel, this only stops part of the hw function
42979430SRaymond.Chen@Sun.COM  * it mainly unreserve the MAS slots and remove the host info IE
42989430SRaymond.Chen@Sun.COM  */
42999430SRaymond.Chen@Sun.COM static int
hwahc_hc_channel_stop(hwahc_state_t * hwahcp)43009430SRaymond.Chen@Sun.COM hwahc_hc_channel_stop(hwahc_state_t *hwahcp)
43019430SRaymond.Chen@Sun.COM {
43029430SRaymond.Chen@Sun.COM 	uint8_t			stream_idx;
43039430SRaymond.Chen@Sun.COM 	uint8_t			mas[WUSB_SET_WUSB_MAS_LEN];
43049430SRaymond.Chen@Sun.COM 	int			rval;
43059430SRaymond.Chen@Sun.COM 
43069430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
43079430SRaymond.Chen@Sun.COM 	    "hwahc_hc_channel_stop:");
43089430SRaymond.Chen@Sun.COM 
43099430SRaymond.Chen@Sun.COM 	ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
43109430SRaymond.Chen@Sun.COM 
43119430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_dev_state != USB_DEV_ONLINE) {
43129430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
43139430SRaymond.Chen@Sun.COM 		    "hwahc_hc_channel_stop: invalid dev state %d",
43149430SRaymond.Chen@Sun.COM 		    hwahcp->hwahc_dev_state);
43159430SRaymond.Chen@Sun.COM 
43169430SRaymond.Chen@Sun.COM 		return (USB_INVALID_REQUEST);
43179430SRaymond.Chen@Sun.COM 	}
43189430SRaymond.Chen@Sun.COM 
43199430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_hw_state == HWAHC_HW_CH_STOPPED) {
43209430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
43219430SRaymond.Chen@Sun.COM 		    "hwahc_hc_channel_stop: already partially stopped");
43229430SRaymond.Chen@Sun.COM 
43239430SRaymond.Chen@Sun.COM 		return (USB_SUCCESS);
43249430SRaymond.Chen@Sun.COM 	}
43259430SRaymond.Chen@Sun.COM 
43269430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_hw_state == HWAHC_HW_STOPPED) {
43279430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
43289430SRaymond.Chen@Sun.COM 		    "hwahc_hc_channel_stop: already stopped, invalid state");
43299430SRaymond.Chen@Sun.COM 
43309430SRaymond.Chen@Sun.COM 		return (USB_INVALID_REQUEST);
43319430SRaymond.Chen@Sun.COM 	}
43329430SRaymond.Chen@Sun.COM 
43339430SRaymond.Chen@Sun.COM 	/* send host disconect IE so that the children know to disconnect */
43349430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
43359430SRaymond.Chen@Sun.COM 	rval = wusb_hc_send_host_disconnect(&hwahcp->hwahc_hc_data);
43369430SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
43379430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
43389430SRaymond.Chen@Sun.COM 		    "hwahc_hc_channel_stop: send host disconnect ie fails");
43399430SRaymond.Chen@Sun.COM 
43409797SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
43419797SRaymond.Chen@Sun.COM 
43429430SRaymond.Chen@Sun.COM 		return (rval);
43439430SRaymond.Chen@Sun.COM 	}
43449430SRaymond.Chen@Sun.COM 
43459430SRaymond.Chen@Sun.COM 	/* remove host info IE */
43469430SRaymond.Chen@Sun.COM 	wusb_hc_rem_host_info(&hwahcp->hwahc_hc_data);
43479430SRaymond.Chen@Sun.COM 
43489430SRaymond.Chen@Sun.COM 	/* unset stream idx */
43499430SRaymond.Chen@Sun.COM 	stream_idx = 0;
43509430SRaymond.Chen@Sun.COM 
43519430SRaymond.Chen@Sun.COM 	rval = wusb_hc_set_stream_idx(&hwahcp->hwahc_hc_data, stream_idx);
43529430SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
43539430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
43549430SRaymond.Chen@Sun.COM 		    "hwahc_hc_channel_stop: set stream idx 0 fails");
43559797SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
43569430SRaymond.Chen@Sun.COM 
43579430SRaymond.Chen@Sun.COM 		return (rval);
43589430SRaymond.Chen@Sun.COM 	}
43599430SRaymond.Chen@Sun.COM 
43609430SRaymond.Chen@Sun.COM 	/* unreserve MAS slots */
43619430SRaymond.Chen@Sun.COM 	(void) memset(mas, 0, WUSB_SET_WUSB_MAS_LEN);
43629430SRaymond.Chen@Sun.COM 	rval = wusb_hc_set_wusb_mas(&hwahcp->hwahc_hc_data, mas);
43639430SRaymond.Chen@Sun.COM 
43649430SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
43659430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
43669430SRaymond.Chen@Sun.COM 		    "hwahc_hc_channel_stop: set null wusb mas fails");
43679797SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
43689430SRaymond.Chen@Sun.COM 
43699430SRaymond.Chen@Sun.COM 		return (rval);
43709430SRaymond.Chen@Sun.COM 	}
43719797SRaymond.Chen@Sun.COM 
43729797SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
43739430SRaymond.Chen@Sun.COM 	(void) memcpy(hwahcp->hwahc_hc_data.hc_mas, mas, WUSB_SET_WUSB_MAS_LEN);
43749430SRaymond.Chen@Sun.COM 
43759430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_hw_state = HWAHC_HW_CH_STOPPED;
43769430SRaymond.Chen@Sun.COM 
43779430SRaymond.Chen@Sun.COM 	/* Channel is stopped, can be PM'ed */
43789430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
43799430SRaymond.Chen@Sun.COM 	hwahc_pm_idle_component(hwahcp);
43809430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
43819430SRaymond.Chen@Sun.COM 
43829430SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
43839430SRaymond.Chen@Sun.COM }
43849430SRaymond.Chen@Sun.COM 
43859430SRaymond.Chen@Sun.COM /* initialize data transfer related resources */
43869430SRaymond.Chen@Sun.COM static int
hwahc_wa_start(hwahc_state_t * hwahcp)43879430SRaymond.Chen@Sun.COM hwahc_wa_start(hwahc_state_t *hwahcp)
43889430SRaymond.Chen@Sun.COM {
43899430SRaymond.Chen@Sun.COM 	int	rval;
43909430SRaymond.Chen@Sun.COM 
43919430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
43929430SRaymond.Chen@Sun.COM 	    "hwahc_wa_start:");
43939430SRaymond.Chen@Sun.COM 
43949430SRaymond.Chen@Sun.COM 	/* get all rpipe descrs */
43959430SRaymond.Chen@Sun.COM 	if ((rval = wusb_wa_get_rpipe_descrs(&hwahcp->hwahc_wa_data,
43969430SRaymond.Chen@Sun.COM 	    hwahcp->hwahc_default_pipe, PRINT_MASK_ATTA,
43979430SRaymond.Chen@Sun.COM 	    hwahcp->hwahc_log_handle)) != USB_SUCCESS) {
43989430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
43999430SRaymond.Chen@Sun.COM 		    "hwahc_wa_start: get rpipe descrs fails, rval=%d", rval);
44009430SRaymond.Chen@Sun.COM 
44019430SRaymond.Chen@Sun.COM 		return (rval);
44029430SRaymond.Chen@Sun.COM 	}
44039430SRaymond.Chen@Sun.COM 
44049430SRaymond.Chen@Sun.COM 	/* open all data transfer epts */
44059430SRaymond.Chen@Sun.COM 	if ((rval = wusb_wa_open_pipes(&hwahcp->hwahc_wa_data)) !=
44069430SRaymond.Chen@Sun.COM 	    USB_SUCCESS) {
44079430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
44089430SRaymond.Chen@Sun.COM 		    "hwahc_wa_start: open pipes fails, rval=%d", rval);
44099430SRaymond.Chen@Sun.COM 		(void) wusb_wa_disable(&hwahcp->hwahc_wa_data,
44109430SRaymond.Chen@Sun.COM 		    hwahcp->hwahc_default_pipe);
44119430SRaymond.Chen@Sun.COM 
44129430SRaymond.Chen@Sun.COM 		return (rval);
44139430SRaymond.Chen@Sun.COM 	}
44149430SRaymond.Chen@Sun.COM 
44159430SRaymond.Chen@Sun.COM 	/* init notification list */
44169430SRaymond.Chen@Sun.COM 	usba_init_list(&hwahcp->hwahc_dn_notif_queue, NULL,
44179430SRaymond.Chen@Sun.COM 	    hwahcp->hwahc_dev_data->dev_iblock_cookie);
44189430SRaymond.Chen@Sun.COM 
44199430SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
44209430SRaymond.Chen@Sun.COM }
44219430SRaymond.Chen@Sun.COM 
44229430SRaymond.Chen@Sun.COM /* deinitialize data transfer related resources */
44239430SRaymond.Chen@Sun.COM static void
hwahc_wa_stop(hwahc_state_t * hwahcp)44249430SRaymond.Chen@Sun.COM hwahc_wa_stop(hwahc_state_t *hwahcp)
44259430SRaymond.Chen@Sun.COM {
44269430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
44279430SRaymond.Chen@Sun.COM 	    "hwahc_wa_stop:");
44289430SRaymond.Chen@Sun.COM 
44299430SRaymond.Chen@Sun.COM 	usba_destroy_list(&hwahcp->hwahc_dn_notif_queue);
44309430SRaymond.Chen@Sun.COM 	wusb_wa_close_pipes(&hwahcp->hwahc_wa_data);
44319430SRaymond.Chen@Sun.COM }
44329430SRaymond.Chen@Sun.COM 
44339430SRaymond.Chen@Sun.COM /*
44349430SRaymond.Chen@Sun.COM  * HUBD related initialization
44359430SRaymond.Chen@Sun.COM  * To mimic standard hub attach process to create a fake "root hub"
44369430SRaymond.Chen@Sun.COM  * for HWA
44379430SRaymond.Chen@Sun.COM  */
44389430SRaymond.Chen@Sun.COM static int
hwahc_hub_attach(hwahc_state_t * hwahcp)44399430SRaymond.Chen@Sun.COM hwahc_hub_attach(hwahc_state_t *hwahcp)
44409430SRaymond.Chen@Sun.COM {
44419430SRaymond.Chen@Sun.COM 	hubd_t		*hubd = NULL;
44429430SRaymond.Chen@Sun.COM 	dev_info_t	*dip = hwahcp->hwahc_dip;
44439430SRaymond.Chen@Sun.COM 	int		instance = ddi_get_instance(dip);
44449430SRaymond.Chen@Sun.COM 	int		i;
44459430SRaymond.Chen@Sun.COM 	int		rval;
44469430SRaymond.Chen@Sun.COM 
44479430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
44489430SRaymond.Chen@Sun.COM 	    "hwahc_hub_attach:");
44499430SRaymond.Chen@Sun.COM 
44509430SRaymond.Chen@Sun.COM 	if (ndi_prop_create_boolean(DDI_DEV_T_NONE, dip,
44519430SRaymond.Chen@Sun.COM 	    "wire-adapter") != NDI_SUCCESS) {
44529430SRaymond.Chen@Sun.COM 
44539430SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
44549430SRaymond.Chen@Sun.COM 	}
44559430SRaymond.Chen@Sun.COM 
44569430SRaymond.Chen@Sun.COM 	/* allocate hubd structure */
44579430SRaymond.Chen@Sun.COM 	hubd = hwahcp->hwahc_hubd = kmem_zalloc(sizeof (hubd_t), KM_SLEEP);
44589430SRaymond.Chen@Sun.COM 
44599430SRaymond.Chen@Sun.COM 	hubd->h_log_handle = usb_alloc_log_hdl(dip, "husb", &hubd_errlevel,
44609430SRaymond.Chen@Sun.COM 	    &hubd_errmask, &hubd_instance_debug, 0);
44619430SRaymond.Chen@Sun.COM 	hubd->h_usba_device = usba_get_usba_device(dip);
44629430SRaymond.Chen@Sun.COM 	hubd->h_usba_device->usb_is_wa = TRUE;
44639430SRaymond.Chen@Sun.COM 	hubd->h_dip = dip;
44649430SRaymond.Chen@Sun.COM 	hubd->h_instance = instance;
44659430SRaymond.Chen@Sun.COM 	hubd->h_ignore_pwr_budget = B_TRUE;
44669430SRaymond.Chen@Sun.COM 	hubd->h_cleanup_child = hwahc_cleanup_child;
44679430SRaymond.Chen@Sun.COM 
44689430SRaymond.Chen@Sun.COM 	mutex_enter(&hubd->h_usba_device->usb_mutex);
44699430SRaymond.Chen@Sun.COM 	hubd->h_usba_device->usb_root_hubd = hubd;
44709430SRaymond.Chen@Sun.COM 	mutex_exit(&hubd->h_usba_device->usb_mutex);
44719430SRaymond.Chen@Sun.COM 
44729430SRaymond.Chen@Sun.COM 	if (usb_get_dev_data(dip, &hubd->h_dev_data,
44739430SRaymond.Chen@Sun.COM 	    USB_PARSE_LVL_IF, 0) != USB_SUCCESS) {
44749430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
44759430SRaymond.Chen@Sun.COM 		    "cannot get dev_data");
44769430SRaymond.Chen@Sun.COM 
44779430SRaymond.Chen@Sun.COM 		goto fail;
44789430SRaymond.Chen@Sun.COM 	}
44799430SRaymond.Chen@Sun.COM 
44809430SRaymond.Chen@Sun.COM 	/* init hubd mutex */
44819430SRaymond.Chen@Sun.COM 	mutex_init(HUBD_MUTEX(hubd), NULL, MUTEX_DRIVER,
44829430SRaymond.Chen@Sun.COM 	    hubd->h_dev_data->dev_iblock_cookie);
44839430SRaymond.Chen@Sun.COM 
44849430SRaymond.Chen@Sun.COM 	usb_free_descr_tree(dip, hubd->h_dev_data);
44859430SRaymond.Chen@Sun.COM 
44869430SRaymond.Chen@Sun.COM 	hubd->h_init_state |= HUBD_LOCKS_DONE;
44879430SRaymond.Chen@Sun.COM 
44889430SRaymond.Chen@Sun.COM 	/* register the instance to usba HUBDI */
44899430SRaymond.Chen@Sun.COM 	rval = usba_hubdi_register(dip, 0);
44909430SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
44919430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
44929430SRaymond.Chen@Sun.COM 		    "usba_hubdi_register failed");
44939430SRaymond.Chen@Sun.COM 
44949430SRaymond.Chen@Sun.COM 		goto fail;
44959430SRaymond.Chen@Sun.COM 	}
44969430SRaymond.Chen@Sun.COM 
44979430SRaymond.Chen@Sun.COM 	mutex_enter(HUBD_MUTEX(hubd));
44989430SRaymond.Chen@Sun.COM 	hubd->h_init_state |= HUBD_HUBDI_REGISTERED;
44999430SRaymond.Chen@Sun.COM 
45009430SRaymond.Chen@Sun.COM 	hubd->h_ancestry_str = (char *)kmem_zalloc(HUBD_APID_NAMELEN,
45019430SRaymond.Chen@Sun.COM 	    KM_SLEEP);
45029430SRaymond.Chen@Sun.COM 	hubd_get_ancestry_str(hubd);
45039430SRaymond.Chen@Sun.COM 
45049430SRaymond.Chen@Sun.COM 	/* create cfgadm minor nodes */
45059430SRaymond.Chen@Sun.COM 	for (i = 1; i <= hwahcp->hwahc_wa_data.wa_descr.bNumPorts; i++) {
45069430SRaymond.Chen@Sun.COM 		char ap_name[HUBD_APID_NAMELEN];
45079430SRaymond.Chen@Sun.COM 
45089430SRaymond.Chen@Sun.COM 		(void) snprintf(ap_name, HUBD_APID_NAMELEN, "%s%d",
45099430SRaymond.Chen@Sun.COM 		    hubd->h_ancestry_str, i);
45109430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(DPRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
45119430SRaymond.Chen@Sun.COM 		    "ap_name=%s", ap_name);
45129430SRaymond.Chen@Sun.COM 
45139430SRaymond.Chen@Sun.COM 		if (ddi_create_minor_node(dip, ap_name, S_IFCHR,
45149430SRaymond.Chen@Sun.COM 		    (instance << HWAHC_MINOR_INSTANCE_SHIFT) | i,
45159430SRaymond.Chen@Sun.COM 		    DDI_NT_USB_ATTACHMENT_POINT, 0) != DDI_SUCCESS) {
45169430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
45179430SRaymond.Chen@Sun.COM 			    "cannot create attachment point node (%d)",
45189430SRaymond.Chen@Sun.COM 			    instance);
45199430SRaymond.Chen@Sun.COM 			mutex_exit(HUBD_MUTEX(hubd));
45209430SRaymond.Chen@Sun.COM 
45219430SRaymond.Chen@Sun.COM 			goto fail;
45229430SRaymond.Chen@Sun.COM 		}
45239430SRaymond.Chen@Sun.COM 	}
45249430SRaymond.Chen@Sun.COM 	i = hwahcp->hwahc_wa_data.wa_descr.bNumPorts;
45259430SRaymond.Chen@Sun.COM 	mutex_exit(HUBD_MUTEX(hubd));
45269430SRaymond.Chen@Sun.COM 
45279430SRaymond.Chen@Sun.COM 	/* create hubd minor node */
45289430SRaymond.Chen@Sun.COM 	if (ddi_create_minor_node(dip, "hubd", S_IFCHR,
45299430SRaymond.Chen@Sun.COM 	    instance << HWAHC_MINOR_INSTANCE_SHIFT,
45309430SRaymond.Chen@Sun.COM 	    DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
45319430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
45329430SRaymond.Chen@Sun.COM 		    "cannot create devctl minor node (%d)", instance);
45339430SRaymond.Chen@Sun.COM 
45349430SRaymond.Chen@Sun.COM 		goto fail;
45359430SRaymond.Chen@Sun.COM 	}
45369430SRaymond.Chen@Sun.COM 
45379430SRaymond.Chen@Sun.COM 	mutex_enter(HUBD_MUTEX(hubd));
45389430SRaymond.Chen@Sun.COM 	hubd->h_init_state |= HUBD_MINOR_NODE_CREATED;
45399430SRaymond.Chen@Sun.COM 	mutex_exit(HUBD_MUTEX(hubd));
45409430SRaymond.Chen@Sun.COM 
45419430SRaymond.Chen@Sun.COM 	if (ndi_prop_update_int(DDI_DEV_T_NONE, dip,
45429430SRaymond.Chen@Sun.COM 	    "usb-port-count", i) != DDI_PROP_SUCCESS) {
45439430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
45449430SRaymond.Chen@Sun.COM 		    "usb-port-count update failed");
45459430SRaymond.Chen@Sun.COM 	}
45469430SRaymond.Chen@Sun.COM 
45479430SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
45489430SRaymond.Chen@Sun.COM 
45499430SRaymond.Chen@Sun.COM fail:
45509430SRaymond.Chen@Sun.COM 	if (hwahc_hub_detach(hwahcp) != USB_SUCCESS) {
45519430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
45529430SRaymond.Chen@Sun.COM 		    "fail to cleanup after hub attach failure");
45539430SRaymond.Chen@Sun.COM 	}
45549430SRaymond.Chen@Sun.COM 
45559430SRaymond.Chen@Sun.COM 	return (USB_FAILURE);
45569430SRaymond.Chen@Sun.COM }
45579430SRaymond.Chen@Sun.COM 
45589430SRaymond.Chen@Sun.COM /* HUBD related deinitialization */
45599430SRaymond.Chen@Sun.COM static int
hwahc_hub_detach(hwahc_state_t * hwahcp)45609430SRaymond.Chen@Sun.COM hwahc_hub_detach(hwahc_state_t *hwahcp)
45619430SRaymond.Chen@Sun.COM {
45629430SRaymond.Chen@Sun.COM 	hubd_t		*hubd = hwahcp->hwahc_hubd;
45639430SRaymond.Chen@Sun.COM 	dev_info_t	*dip = hwahcp->hwahc_dip;
45649430SRaymond.Chen@Sun.COM 
45659430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
45669430SRaymond.Chen@Sun.COM 	    "hwahc_hub_detach:");
45679430SRaymond.Chen@Sun.COM 
45689430SRaymond.Chen@Sun.COM 	if ((hubd->h_init_state & HUBD_LOCKS_DONE) == 0) {
45699430SRaymond.Chen@Sun.COM 		goto done;
45709430SRaymond.Chen@Sun.COM 	}
45719430SRaymond.Chen@Sun.COM 
45729430SRaymond.Chen@Sun.COM 	if (hubd->h_init_state & HUBD_MINOR_NODE_CREATED) {
45739430SRaymond.Chen@Sun.COM 		/* remove minor nodes */
45749430SRaymond.Chen@Sun.COM 		ddi_remove_minor_node(dip, NULL);
45759430SRaymond.Chen@Sun.COM 	}
45769430SRaymond.Chen@Sun.COM 
45779430SRaymond.Chen@Sun.COM 	if (hubd->h_init_state & HUBD_HUBDI_REGISTERED) {
45789430SRaymond.Chen@Sun.COM 		/* unregister with usba HUBDI */
45799430SRaymond.Chen@Sun.COM 		(void) usba_hubdi_unregister(dip);
45809430SRaymond.Chen@Sun.COM 	}
45819430SRaymond.Chen@Sun.COM 
45829430SRaymond.Chen@Sun.COM 	if (hubd->h_init_state & HUBD_LOCKS_DONE) {
45839430SRaymond.Chen@Sun.COM 		mutex_destroy(HUBD_MUTEX(hubd));
45849430SRaymond.Chen@Sun.COM 	}
45859430SRaymond.Chen@Sun.COM 
45869430SRaymond.Chen@Sun.COM 	if (hubd->h_ancestry_str) {
45879430SRaymond.Chen@Sun.COM 		kmem_free(hubd->h_ancestry_str, HUBD_APID_NAMELEN);
45889430SRaymond.Chen@Sun.COM 	}
45899430SRaymond.Chen@Sun.COM 
45909430SRaymond.Chen@Sun.COM done:
45919430SRaymond.Chen@Sun.COM 	if (hubd->h_dev_data) {
45929430SRaymond.Chen@Sun.COM 		/* unregister client from usba */
45939430SRaymond.Chen@Sun.COM 		usb_client_detach(dip, hubd->h_dev_data);
45949430SRaymond.Chen@Sun.COM 	}
45959430SRaymond.Chen@Sun.COM 
45969430SRaymond.Chen@Sun.COM 	usb_free_log_hdl(hubd->h_log_handle);
45979430SRaymond.Chen@Sun.COM 	kmem_free(hubd, sizeof (hubd_t));
45989430SRaymond.Chen@Sun.COM 	ddi_prop_remove_all(dip);
45999430SRaymond.Chen@Sun.COM 
46009430SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
46019430SRaymond.Chen@Sun.COM }
46029430SRaymond.Chen@Sun.COM 
46039430SRaymond.Chen@Sun.COM /* print security descrs */
46049430SRaymond.Chen@Sun.COM static void
hwahc_print_secrt_data(hwahc_state_t * hwahcp)46059430SRaymond.Chen@Sun.COM hwahc_print_secrt_data(hwahc_state_t *hwahcp)
46069430SRaymond.Chen@Sun.COM {
46079430SRaymond.Chen@Sun.COM 	int			i;
46089430SRaymond.Chen@Sun.COM 	wusb_secrt_data_t	*secrt_data = &hwahcp->hwahc_secrt_data;
46099430SRaymond.Chen@Sun.COM 
46109430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
46119430SRaymond.Chen@Sun.COM 	    "The Host Wire Adapter security descriptor:");
46129430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
46139430SRaymond.Chen@Sun.COM 	    "bLength = 0x%x\t\t bDescriptorType = 0x%x",
46149430SRaymond.Chen@Sun.COM 	    secrt_data->secrt_descr.bLength,
46159430SRaymond.Chen@Sun.COM 	    secrt_data->secrt_descr.bDescriptorType);
46169430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
46179430SRaymond.Chen@Sun.COM 	    "wTotalLength = 0x%x\t bNumEncryptionTypes = 0x%x",
46189430SRaymond.Chen@Sun.COM 	    secrt_data->secrt_descr.wTotalLength,
46199430SRaymond.Chen@Sun.COM 	    secrt_data->secrt_descr.bNumEncryptionTypes);
46209430SRaymond.Chen@Sun.COM 
46219430SRaymond.Chen@Sun.COM 	for (i = 0; i < secrt_data->secrt_n_encry; i++) {
46229430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
46239430SRaymond.Chen@Sun.COM 		    "The Host Wire Adapter encryption descriptor %d:", i + 1);
46249430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
46259430SRaymond.Chen@Sun.COM 		    "bLength = 0x%x\t\t bDescriptorType = 0x%x",
46269430SRaymond.Chen@Sun.COM 		    secrt_data->secrt_encry_descr[i].bLength,
46279430SRaymond.Chen@Sun.COM 		    secrt_data->secrt_encry_descr[i].bDescriptorType);
46289430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
46299430SRaymond.Chen@Sun.COM 		    "bEncryptionType = 0x%x\t bEncryptionValue = 0x%x",
46309430SRaymond.Chen@Sun.COM 		    secrt_data->secrt_encry_descr[i].bEncryptionType,
46319430SRaymond.Chen@Sun.COM 		    secrt_data->secrt_encry_descr[i].bEncryptionValue);
46329430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
46339430SRaymond.Chen@Sun.COM 		    "bAuthKeyIndex = 0x%x",
46349430SRaymond.Chen@Sun.COM 		    secrt_data->secrt_encry_descr[i].bAuthKeyIndex);
46359430SRaymond.Chen@Sun.COM 	}
46369430SRaymond.Chen@Sun.COM }
46379430SRaymond.Chen@Sun.COM 
46389430SRaymond.Chen@Sun.COM /* drain device notifications */
46399430SRaymond.Chen@Sun.COM static void
hwahc_drain_notif_queue(hwahc_state_t * hwahcp)46409430SRaymond.Chen@Sun.COM hwahc_drain_notif_queue(hwahc_state_t *hwahcp)
46419430SRaymond.Chen@Sun.COM {
46429430SRaymond.Chen@Sun.COM 	int	i;
46439430SRaymond.Chen@Sun.COM 
46449430SRaymond.Chen@Sun.COM 	ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
46459430SRaymond.Chen@Sun.COM 
46469430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
46479430SRaymond.Chen@Sun.COM 	    "hwahc_drain_notif_queue: started");
46489430SRaymond.Chen@Sun.COM 
46499430SRaymond.Chen@Sun.COM 	if ((hwahcp->hwahc_notif_thread_id == NULL) &&
46509430SRaymond.Chen@Sun.COM 	    (usba_list_entry_count(&hwahcp->hwahc_dn_notif_queue) != 0)) {
46519430SRaymond.Chen@Sun.COM 		/* kick off a notif thread to drain the queue */
46529430SRaymond.Chen@Sun.COM 		if (usb_async_req(hwahcp->hwahc_dip, hwahc_notif_thread,
46539430SRaymond.Chen@Sun.COM 		    (void *)hwahcp, 0) != USB_SUCCESS) {
46549430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
46559430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
46569430SRaymond.Chen@Sun.COM 			    "hwahc_drain_notif_queue: no notif thread started");
46579430SRaymond.Chen@Sun.COM 		} else {
46589430SRaymond.Chen@Sun.COM 			hwahcp->hwahc_notif_thread_id = (kthread_t *)1;
46599430SRaymond.Chen@Sun.COM 		}
46609430SRaymond.Chen@Sun.COM 	}
46619430SRaymond.Chen@Sun.COM 
46629430SRaymond.Chen@Sun.COM 	for (i = 0; i < HWAHC_NOTIF_DRAIN_TIMEOUT; i++) {
46639430SRaymond.Chen@Sun.COM 		/* loop until the queue is completed or it timeouts */
46649430SRaymond.Chen@Sun.COM 		if ((hwahcp->hwahc_notif_thread_id == NULL) &&
46659430SRaymond.Chen@Sun.COM 		    (usba_list_entry_count(&hwahcp->hwahc_dn_notif_queue) ==
46669430SRaymond.Chen@Sun.COM 		    0)) {
46679430SRaymond.Chen@Sun.COM 
46689430SRaymond.Chen@Sun.COM 			break;
46699430SRaymond.Chen@Sun.COM 		}
46709430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
46719430SRaymond.Chen@Sun.COM 		delay(drv_usectohz(1000000));
46729430SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
46739430SRaymond.Chen@Sun.COM 	}
46749430SRaymond.Chen@Sun.COM 
46759430SRaymond.Chen@Sun.COM 	/* cleanup the queue if not completed */
46769430SRaymond.Chen@Sun.COM 	while (usba_list_entry_count(&hwahcp->hwahc_dn_notif_queue) != 0) {
46779430SRaymond.Chen@Sun.COM 		hwahc_dn_notif_list_t	*nlist;
46789430SRaymond.Chen@Sun.COM 
46799430SRaymond.Chen@Sun.COM 		nlist = (hwahc_dn_notif_list_t *)usba_rm_first_pvt_from_list(
46809430SRaymond.Chen@Sun.COM 		    &hwahcp->hwahc_dn_notif_queue);
46819430SRaymond.Chen@Sun.COM 		ASSERT(nlist != NULL);
46829430SRaymond.Chen@Sun.COM 		ASSERT(nlist->dn_notif != NULL);
46839430SRaymond.Chen@Sun.COM 		usba_destroy_list(&nlist->notif_list);
46849430SRaymond.Chen@Sun.COM 		kmem_free(nlist->dn_notif, nlist->dn_notif->bLength);
46859430SRaymond.Chen@Sun.COM 		kmem_free(nlist, sizeof (hwahc_dn_notif_list_t));
46869430SRaymond.Chen@Sun.COM 	}
46879430SRaymond.Chen@Sun.COM 
46889430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
46899430SRaymond.Chen@Sun.COM 	    "hwahc_drain_notif_queue: ended");
46909430SRaymond.Chen@Sun.COM }
46919430SRaymond.Chen@Sun.COM 
46929430SRaymond.Chen@Sun.COM 
46939430SRaymond.Chen@Sun.COM /* normal callback for notification ept */
46949430SRaymond.Chen@Sun.COM static void
hwahc_intr_cb(usb_pipe_handle_t ph,struct usb_intr_req * reqp)46959430SRaymond.Chen@Sun.COM hwahc_intr_cb(usb_pipe_handle_t ph, struct usb_intr_req *reqp)
46969430SRaymond.Chen@Sun.COM {
46979430SRaymond.Chen@Sun.COM 	dev_info_t		*dip = (USBA_REQ2WRP(reqp))->wr_dip;
46989430SRaymond.Chen@Sun.COM 	hwahc_state_t		*hwahcp;
46999430SRaymond.Chen@Sun.COM 	mblk_t			*data = reqp->intr_data;
47009430SRaymond.Chen@Sun.COM 
47019430SRaymond.Chen@Sun.COM 	ASSERT(dip != NULL);
47029430SRaymond.Chen@Sun.COM 	hwahcp = ddi_get_soft_state(hwahc_statep, ddi_get_instance(dip));
47039430SRaymond.Chen@Sun.COM 	ASSERT(hwahcp != NULL);
47049430SRaymond.Chen@Sun.COM 
47059430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
47069430SRaymond.Chen@Sun.COM 	    "hwahc_intr_cb: ph = 0x%p reqp = 0x%p", (void *)ph,
47079430SRaymond.Chen@Sun.COM 	    (void *)reqp);
47089430SRaymond.Chen@Sun.COM 
47099430SRaymond.Chen@Sun.COM 	ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
47109430SRaymond.Chen@Sun.COM 
47119430SRaymond.Chen@Sun.COM 	if (data == NULL) {
47129430SRaymond.Chen@Sun.COM 		usb_free_intr_req(reqp);
47139430SRaymond.Chen@Sun.COM 
47149430SRaymond.Chen@Sun.COM 		return;
47159430SRaymond.Chen@Sun.COM 	}
47169430SRaymond.Chen@Sun.COM 
47179430SRaymond.Chen@Sun.COM 	/* handle the notification */
47189430SRaymond.Chen@Sun.COM 	hwahc_handle_notif(hwahcp, data);
47199430SRaymond.Chen@Sun.COM 
47209430SRaymond.Chen@Sun.COM 	usb_free_intr_req(reqp);
47219430SRaymond.Chen@Sun.COM }
47229430SRaymond.Chen@Sun.COM 
47239430SRaymond.Chen@Sun.COM /*
47249430SRaymond.Chen@Sun.COM  * See Section 8.3.3.3 for Transfer Notification format and
47259430SRaymond.Chen@Sun.COM  * Section 8.5.4 for HWA specific notifications.
47269430SRaymond.Chen@Sun.COM  *	Three kinds of Notifications:
47279430SRaymond.Chen@Sun.COM  *		- Transfer Completion
47289430SRaymond.Chen@Sun.COM  *		- DN Received
47299430SRaymond.Chen@Sun.COM  *		- BPST ADJ
47309430SRaymond.Chen@Sun.COM  */
47319430SRaymond.Chen@Sun.COM /* handle the notification according to notification type */
47329430SRaymond.Chen@Sun.COM static void
hwahc_handle_notif(hwahc_state_t * hwahcp,mblk_t * data)47339430SRaymond.Chen@Sun.COM hwahc_handle_notif(hwahc_state_t *hwahcp, mblk_t *data)
47349430SRaymond.Chen@Sun.COM {
47359430SRaymond.Chen@Sun.COM 	int			len;
47369430SRaymond.Chen@Sun.COM 	uint8_t			*p;
47379430SRaymond.Chen@Sun.COM 	wa_notif_header_t	*hdr;
47389430SRaymond.Chen@Sun.COM 
47399430SRaymond.Chen@Sun.COM 	if (data == NULL) {
47409430SRaymond.Chen@Sun.COM 
47419430SRaymond.Chen@Sun.COM 		return;
47429430SRaymond.Chen@Sun.COM 	}
47439430SRaymond.Chen@Sun.COM 
47449430SRaymond.Chen@Sun.COM 	len = MBLKL(data);
47459430SRaymond.Chen@Sun.COM 	p = data->b_rptr;
47469430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
47479430SRaymond.Chen@Sun.COM 	    "hwahc_handle_notif: data len = %d", len);
47489430SRaymond.Chen@Sun.COM 
47499430SRaymond.Chen@Sun.COM 	/*
47509430SRaymond.Chen@Sun.COM 	 * according to WUSB 1.0/8.1.2, multiple notifications might be sent
47519430SRaymond.Chen@Sun.COM 	 * at a time, need to parse one by one
47529430SRaymond.Chen@Sun.COM 	 */
47539430SRaymond.Chen@Sun.COM 	while (len > 0) {
47549430SRaymond.Chen@Sun.COM 		if (len < 2) {
47559430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
47569430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
47579430SRaymond.Chen@Sun.COM 			    "hwahc_handle_notif: short packet len = %d",
47589430SRaymond.Chen@Sun.COM 			    len);
47599430SRaymond.Chen@Sun.COM 
47609430SRaymond.Chen@Sun.COM 			break;
47619430SRaymond.Chen@Sun.COM 		}
47629430SRaymond.Chen@Sun.COM 
47639430SRaymond.Chen@Sun.COM 		hdr = (wa_notif_header_t *)p;
47649430SRaymond.Chen@Sun.COM 		if (len < hdr->bLength) {
47659430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
47669430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
47679430SRaymond.Chen@Sun.COM 			    "hwahc_handle_notif: length not match, "
47689430SRaymond.Chen@Sun.COM 			    "hdr length = %d, actual length = %d",
47699430SRaymond.Chen@Sun.COM 			    hdr->bLength, len);
47709430SRaymond.Chen@Sun.COM 
47719430SRaymond.Chen@Sun.COM 			break;
47729430SRaymond.Chen@Sun.COM 		}
47739430SRaymond.Chen@Sun.COM 
47749430SRaymond.Chen@Sun.COM 		switch (hdr->bNotifyType) {
47759430SRaymond.Chen@Sun.COM 		case WA_NOTIF_TYPE_TRANSFER:
47769430SRaymond.Chen@Sun.COM 		{
47779430SRaymond.Chen@Sun.COM 			uint8_t		ept = p[2];
47789430SRaymond.Chen@Sun.COM 
47799430SRaymond.Chen@Sun.COM 			/* deal with transfer completion notification */
47809430SRaymond.Chen@Sun.COM 			hwahc_handle_xfer_result(hwahcp, ept);
47819430SRaymond.Chen@Sun.COM 
47829430SRaymond.Chen@Sun.COM 			break;
47839430SRaymond.Chen@Sun.COM 		}
47849430SRaymond.Chen@Sun.COM 		case HWA_NOTIF_TYPE_DN_RECEIVED:
47859430SRaymond.Chen@Sun.COM 		{
47869430SRaymond.Chen@Sun.COM 			hwa_notif_dn_recvd_t	*dn_notif;
47879430SRaymond.Chen@Sun.COM 
47889430SRaymond.Chen@Sun.COM 			dn_notif = kmem_alloc(hdr->bLength, KM_NOSLEEP);
47899430SRaymond.Chen@Sun.COM 			(void) memcpy(dn_notif, p, hdr->bLength);
47909430SRaymond.Chen@Sun.COM 
47919430SRaymond.Chen@Sun.COM 			/* deal with device notification */
47929430SRaymond.Chen@Sun.COM 			hwahc_handle_dn_notif(hwahcp, dn_notif);
47939430SRaymond.Chen@Sun.COM 
47949430SRaymond.Chen@Sun.COM 			break;
47959430SRaymond.Chen@Sun.COM 		}
47969430SRaymond.Chen@Sun.COM 		case HWA_NOTIF_TYPE_BPST_ADJ:
47979430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L3(PRINT_MASK_CBOPS,
47989430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
47999430SRaymond.Chen@Sun.COM 			    "hwahc_handle_notif: received BPST adjust "
48009430SRaymond.Chen@Sun.COM 			    "notification, bAdjustment = %d", p[2]);
48019430SRaymond.Chen@Sun.COM 
48029430SRaymond.Chen@Sun.COM 			break;
48039430SRaymond.Chen@Sun.COM 		default:
48049430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
48059430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
48069430SRaymond.Chen@Sun.COM 			    "hwahc_handle_notif: unknown notification 0x%x",
48079430SRaymond.Chen@Sun.COM 			    hdr->bNotifyType);
48089430SRaymond.Chen@Sun.COM 
48099430SRaymond.Chen@Sun.COM 			break;
48109430SRaymond.Chen@Sun.COM 		}
48119430SRaymond.Chen@Sun.COM 		p += hdr->bLength;
48129430SRaymond.Chen@Sun.COM 		len -= hdr->bLength;
48139430SRaymond.Chen@Sun.COM 	}
48149430SRaymond.Chen@Sun.COM }
48159430SRaymond.Chen@Sun.COM 
48169430SRaymond.Chen@Sun.COM /*
48179430SRaymond.Chen@Sun.COM  * start listening on bulk-in ept for transfer result
48189430SRaymond.Chen@Sun.COM  *
48199430SRaymond.Chen@Sun.COM  * Dispatches a task to read the BULK IN endpoint to get the result of
48209430SRaymond.Chen@Sun.COM  * last request. usb_async_req() will have system_taskq to process the tasks.
48219430SRaymond.Chen@Sun.COM  */
48229430SRaymond.Chen@Sun.COM int
hwahc_start_result_thread(hwahc_state_t * hwahcp)48239430SRaymond.Chen@Sun.COM hwahc_start_result_thread(hwahc_state_t *hwahcp)
48249430SRaymond.Chen@Sun.COM {
48259430SRaymond.Chen@Sun.COM 	wusb_wa_data_t *wa_data;
48269430SRaymond.Chen@Sun.COM 
48279430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
48289430SRaymond.Chen@Sun.COM 	    "hwahc_start_result_thread:");
48299430SRaymond.Chen@Sun.COM 
48309430SRaymond.Chen@Sun.COM 	ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
48319430SRaymond.Chen@Sun.COM 
48329430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_result_thread_id != 0) {
48339430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
48349430SRaymond.Chen@Sun.COM 		    "hwahc_start_result_thread: already started");
48359430SRaymond.Chen@Sun.COM 
48369430SRaymond.Chen@Sun.COM 		return (USB_SUCCESS);
48379430SRaymond.Chen@Sun.COM 	}
48389430SRaymond.Chen@Sun.COM 
48399430SRaymond.Chen@Sun.COM 	wa_data = &hwahcp->hwahc_wa_data;
48409430SRaymond.Chen@Sun.COM 
48419430SRaymond.Chen@Sun.COM 	mutex_enter(&wa_data->wa_mutex);
48429430SRaymond.Chen@Sun.COM 	if ((wa_data->wa_bulkin_ph != NULL) &&
48439430SRaymond.Chen@Sun.COM 	    (wa_data->wa_bulkin_pipe_state != WA_PIPE_STOPPED)) {
48449430SRaymond.Chen@Sun.COM 		mutex_exit(&wa_data->wa_mutex);
48459430SRaymond.Chen@Sun.COM 
48469430SRaymond.Chen@Sun.COM 		return (USB_INVALID_PIPE);
48479430SRaymond.Chen@Sun.COM 	}
48489430SRaymond.Chen@Sun.COM 	mutex_exit(&wa_data->wa_mutex);
48499430SRaymond.Chen@Sun.COM 
48509430SRaymond.Chen@Sun.COM 	if (wa_data->wa_bulkin_ph == NULL) {
48519430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
48529430SRaymond.Chen@Sun.COM 		if (usb_pipe_open(wa_data->wa_dip, &wa_data->wa_bulkin_ept,
48539430SRaymond.Chen@Sun.COM 		    &wa_data->wa_pipe_policy, USB_FLAGS_SLEEP,
48549430SRaymond.Chen@Sun.COM 		    &wa_data->wa_bulkin_ph) != USB_SUCCESS) {
48559430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_ATTA,
48569430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
48579430SRaymond.Chen@Sun.COM 			    "hwahc_start_result_thread: open pipe failed");
48589430SRaymond.Chen@Sun.COM 
48599430SRaymond.Chen@Sun.COM 
48609430SRaymond.Chen@Sun.COM 			mutex_enter(&hwahcp->hwahc_mutex);
48619430SRaymond.Chen@Sun.COM 			return (USB_FAILURE);
48629430SRaymond.Chen@Sun.COM 		}
48639430SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
48649430SRaymond.Chen@Sun.COM 
48659430SRaymond.Chen@Sun.COM 		mutex_enter(&wa_data->wa_mutex);
48669430SRaymond.Chen@Sun.COM 		wa_data->wa_bulkin_pipe_state = WA_PIPE_STOPPED;
48679430SRaymond.Chen@Sun.COM 		mutex_exit(&wa_data->wa_mutex);
48689430SRaymond.Chen@Sun.COM 	}
48699430SRaymond.Chen@Sun.COM 
48709430SRaymond.Chen@Sun.COM 	/* kick off an asynchronous thread to handle transfer result */
48719430SRaymond.Chen@Sun.COM 	if (usb_async_req(hwahcp->hwahc_dip, hwahc_result_thread,
48729430SRaymond.Chen@Sun.COM 	    (void *)hwahcp, 0) != USB_SUCCESS) {
48739430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
48749430SRaymond.Chen@Sun.COM 		    "hwahc_start_result_thread: failed to start result thread");
48759430SRaymond.Chen@Sun.COM 
48769430SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
48779430SRaymond.Chen@Sun.COM 	}
48789430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_result_thread_id = (kthread_t *)1;
48799430SRaymond.Chen@Sun.COM 
48809430SRaymond.Chen@Sun.COM 	/* pipe state is active while the result thread is on */
48819430SRaymond.Chen@Sun.COM 	mutex_enter(&wa_data->wa_mutex);
48829430SRaymond.Chen@Sun.COM 	wa_data->wa_bulkin_pipe_state = WA_PIPE_ACTIVE;
48839430SRaymond.Chen@Sun.COM 	mutex_exit(&wa_data->wa_mutex);
48849430SRaymond.Chen@Sun.COM 
48859430SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
48869430SRaymond.Chen@Sun.COM }
48879430SRaymond.Chen@Sun.COM 
48889430SRaymond.Chen@Sun.COM /* stop the bulk-in ept from listening */
48899430SRaymond.Chen@Sun.COM static void
hwahc_stop_result_thread(hwahc_state_t * hwahcp)48909430SRaymond.Chen@Sun.COM hwahc_stop_result_thread(hwahc_state_t *hwahcp)
48919430SRaymond.Chen@Sun.COM {
48929430SRaymond.Chen@Sun.COM 	wusb_wa_data_t *wa_data;
48939430SRaymond.Chen@Sun.COM 
48949430SRaymond.Chen@Sun.COM 	ASSERT(mutex_owned(&hwahcp->hwahc_mutex));
48959430SRaymond.Chen@Sun.COM 
48969430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
48979430SRaymond.Chen@Sun.COM 	    "hwahc_stop_result_thread:");
48989430SRaymond.Chen@Sun.COM 
48999430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_result_thread_id == 0) {
49009430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
49019430SRaymond.Chen@Sun.COM 		    "hwahc_stop_result_thread: already stopped");
49029430SRaymond.Chen@Sun.COM 
49039430SRaymond.Chen@Sun.COM 		return;
49049430SRaymond.Chen@Sun.COM 	}
49059430SRaymond.Chen@Sun.COM 
49069430SRaymond.Chen@Sun.COM 	wa_data = &hwahcp->hwahc_wa_data;
49079430SRaymond.Chen@Sun.COM 	mutex_enter(&wa_data->wa_mutex);
49089430SRaymond.Chen@Sun.COM 	if ((wa_data->wa_bulkin_ph == NULL) ||
49099430SRaymond.Chen@Sun.COM 	    (wa_data->wa_bulkin_pipe_state != WA_PIPE_ACTIVE)) {
49109430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
49119430SRaymond.Chen@Sun.COM 		    "hwahc_stop_result_thread: invalid pipe state");
49129430SRaymond.Chen@Sun.COM 
49139430SRaymond.Chen@Sun.COM 		mutex_exit(&wa_data->wa_mutex);
49149430SRaymond.Chen@Sun.COM 
49159430SRaymond.Chen@Sun.COM 		return;
49169430SRaymond.Chen@Sun.COM 	}
49179430SRaymond.Chen@Sun.COM 	mutex_exit(&wa_data->wa_mutex);
49189430SRaymond.Chen@Sun.COM 
49199430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
49209430SRaymond.Chen@Sun.COM 	    "hwahc_stop_result_thread: reset hwa bulk-in pipe");
49219797SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
49229430SRaymond.Chen@Sun.COM 	usb_pipe_reset(wa_data->wa_dip, wa_data->wa_bulkin_ph,
49239430SRaymond.Chen@Sun.COM 	    USB_FLAGS_SLEEP, NULL, NULL);
49249430SRaymond.Chen@Sun.COM 
49259430SRaymond.Chen@Sun.COM 	/*
49269430SRaymond.Chen@Sun.COM 	 * have to close pipe here to fail the bulk-in transfer
49279430SRaymond.Chen@Sun.COM 	 * that never timeouts
49289430SRaymond.Chen@Sun.COM 	 */
49299430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
49309430SRaymond.Chen@Sun.COM 	    "hwahc_stop_result_thread: close hwa bulk-in pipe");
49319430SRaymond.Chen@Sun.COM 	usb_pipe_close(wa_data->wa_dip, wa_data->wa_bulkin_ph,
49329430SRaymond.Chen@Sun.COM 	    USB_FLAGS_SLEEP, NULL, NULL);
49339797SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
49349430SRaymond.Chen@Sun.COM 
49359430SRaymond.Chen@Sun.COM 	mutex_enter(&wa_data->wa_mutex);
49369430SRaymond.Chen@Sun.COM 	wa_data->wa_bulkin_ph = NULL;
49379430SRaymond.Chen@Sun.COM 	wa_data->wa_bulkin_pipe_state = WA_PIPE_STOPPED;
49389430SRaymond.Chen@Sun.COM 	mutex_exit(&wa_data->wa_mutex);
49399430SRaymond.Chen@Sun.COM 
49409430SRaymond.Chen@Sun.COM 	while (hwahcp->hwahc_result_thread_id != 0) {
49419430SRaymond.Chen@Sun.COM 		/* wait the result thread to exit */
49429430SRaymond.Chen@Sun.COM 		cv_wait(&hwahcp->hwahc_result_thread_cv, &hwahcp->hwahc_mutex);
49439430SRaymond.Chen@Sun.COM 	}
49449430SRaymond.Chen@Sun.COM }
49459430SRaymond.Chen@Sun.COM 
49469430SRaymond.Chen@Sun.COM /*
49479430SRaymond.Chen@Sun.COM  * keep listening for transfer result by setting timeout to 0 while the
49489430SRaymond.Chen@Sun.COM  * bulk-in pipe is active
49499430SRaymond.Chen@Sun.COM  * the thread would be stopped by closing bulk-in pipe or encountering
49509430SRaymond.Chen@Sun.COM  * transaction error, eg, hot-removal of hwa device
49519430SRaymond.Chen@Sun.COM  */
49529430SRaymond.Chen@Sun.COM static void
hwahc_result_thread(void * arg)49539430SRaymond.Chen@Sun.COM hwahc_result_thread(void *arg)
49549430SRaymond.Chen@Sun.COM {
49559430SRaymond.Chen@Sun.COM 	hwahc_state_t	*hwahcp = (hwahc_state_t *)arg;
49569430SRaymond.Chen@Sun.COM 	wusb_wa_data_t	*wa_data = &hwahcp->hwahc_wa_data;
49579430SRaymond.Chen@Sun.COM 	int		rval;
49589430SRaymond.Chen@Sun.COM 	uint8_t		retry = 0;
49599430SRaymond.Chen@Sun.COM 
49609430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
49619430SRaymond.Chen@Sun.COM 	ASSERT(hwahcp->hwahc_result_thread_id == (kthread_t *)1);
49629430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_result_thread_id = curthread;
49639430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
49649430SRaymond.Chen@Sun.COM 	    "hwahc_result_thread: started, thread_id=0x%p",
49659430SRaymond.Chen@Sun.COM 	    (void *)hwahcp->hwahc_result_thread_id);
49669430SRaymond.Chen@Sun.COM 
49679430SRaymond.Chen@Sun.COM 	/* keep polling the bulk IN endpoint to get the result */
49689430SRaymond.Chen@Sun.COM 	mutex_enter(&wa_data->wa_mutex);
49699430SRaymond.Chen@Sun.COM 	while (wa_data->wa_bulkin_pipe_state == WA_PIPE_ACTIVE) {
49709430SRaymond.Chen@Sun.COM 		mutex_exit(&wa_data->wa_mutex);
49719430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
49729430SRaymond.Chen@Sun.COM 
49739430SRaymond.Chen@Sun.COM 		if ((rval = wusb_wa_get_xfer_result(wa_data)) != USB_SUCCESS) {
49749430SRaymond.Chen@Sun.COM 			retry++;
49759430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_ATTA,
49769430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
49779430SRaymond.Chen@Sun.COM 			    "hwahc_result_thread: get xfer result failed, "
49789430SRaymond.Chen@Sun.COM 			    "rval = %d, retry = %d", rval, retry);
49799430SRaymond.Chen@Sun.COM 
49809430SRaymond.Chen@Sun.COM 			/* retry 3 times upon failure */
49819430SRaymond.Chen@Sun.COM 			if (retry >= 3) {
49829430SRaymond.Chen@Sun.COM 				mutex_enter(&hwahcp->hwahc_mutex);
49839430SRaymond.Chen@Sun.COM 				mutex_enter(&wa_data->wa_mutex);
49849430SRaymond.Chen@Sun.COM 
49859430SRaymond.Chen@Sun.COM 				break;
49869430SRaymond.Chen@Sun.COM 			}
49879430SRaymond.Chen@Sun.COM 		}
49889430SRaymond.Chen@Sun.COM 
49899430SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
49909430SRaymond.Chen@Sun.COM 		mutex_enter(&wa_data->wa_mutex);
49919430SRaymond.Chen@Sun.COM 	}
49929430SRaymond.Chen@Sun.COM 
49939430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_result_thread_id = 0;
49949430SRaymond.Chen@Sun.COM 	wa_data->wa_bulkin_pipe_state = WA_PIPE_STOPPED;
49959430SRaymond.Chen@Sun.COM 	mutex_exit(&wa_data->wa_mutex);
49969430SRaymond.Chen@Sun.COM 
49979430SRaymond.Chen@Sun.COM 	/* signal to the thread requesting stopping if any */
49989430SRaymond.Chen@Sun.COM 	cv_signal(&hwahcp->hwahc_result_thread_cv);
49999430SRaymond.Chen@Sun.COM 
50009430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
50019430SRaymond.Chen@Sun.COM 	    "hwahc_result_thread: ended");
50029430SRaymond.Chen@Sun.COM 
50039430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
50049430SRaymond.Chen@Sun.COM }
50059430SRaymond.Chen@Sun.COM 
50069430SRaymond.Chen@Sun.COM /*
50079430SRaymond.Chen@Sun.COM  * nothing to do here, just check if the ept number in the transfer
50089430SRaymond.Chen@Sun.COM  * completion notification is valid
50099430SRaymond.Chen@Sun.COM  * the actual handling of transfer result is performed by the result thread
50109430SRaymond.Chen@Sun.COM  */
50119430SRaymond.Chen@Sun.COM static void
hwahc_handle_xfer_result(hwahc_state_t * hwahcp,uint8_t ept)50129430SRaymond.Chen@Sun.COM hwahc_handle_xfer_result(hwahc_state_t *hwahcp, uint8_t ept)
50139430SRaymond.Chen@Sun.COM {
50149430SRaymond.Chen@Sun.COM 	usb_ep_descr_t	*epdt;
50159430SRaymond.Chen@Sun.COM 
50169430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
50179430SRaymond.Chen@Sun.COM 	    "hwahc_handle_xfer_result: result on ept %d", ept);
50189430SRaymond.Chen@Sun.COM 
50199430SRaymond.Chen@Sun.COM 	epdt = &hwahcp->hwahc_wa_data.wa_bulkin_ept;
50209430SRaymond.Chen@Sun.COM 
50219430SRaymond.Chen@Sun.COM 	/* the result should be on the bulk-in ept */
50229430SRaymond.Chen@Sun.COM 	if ((epdt->bEndpointAddress & USB_EP_NUM_MASK) != ept) {
50239430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
50249430SRaymond.Chen@Sun.COM 		    "hwahc_handle_xfer_result: ept number not match");
50259430SRaymond.Chen@Sun.COM 
50269430SRaymond.Chen@Sun.COM 		return;
50279430SRaymond.Chen@Sun.COM 	}
50289430SRaymond.Chen@Sun.COM }
50299430SRaymond.Chen@Sun.COM 
50309430SRaymond.Chen@Sun.COM 
50319430SRaymond.Chen@Sun.COM /*
50329430SRaymond.Chen@Sun.COM  * Section 8.5.4.2.
50339430SRaymond.Chen@Sun.COM  *	Copy the DN Notification and add it to the instance's global
50349430SRaymond.Chen@Sun.COM  *	nofication list. If the worker thread is not started yet, start
50359430SRaymond.Chen@Sun.COM  *	it.
50369430SRaymond.Chen@Sun.COM  */
50379430SRaymond.Chen@Sun.COM static void
hwahc_handle_dn_notif(hwahc_state_t * hwahcp,hwa_notif_dn_recvd_t * dn_notif)50389430SRaymond.Chen@Sun.COM hwahc_handle_dn_notif(hwahc_state_t *hwahcp, hwa_notif_dn_recvd_t *dn_notif)
50399430SRaymond.Chen@Sun.COM {
50409430SRaymond.Chen@Sun.COM 	hwahc_dn_notif_list_t	*nlist;
50419430SRaymond.Chen@Sun.COM 
50429430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
50439430SRaymond.Chen@Sun.COM 	    "hwahc_handle_dn_notif: notif = 0x%p", (void *)dn_notif);
50449430SRaymond.Chen@Sun.COM 
50459430SRaymond.Chen@Sun.COM 	nlist = kmem_zalloc(sizeof (hwahc_dn_notif_list_t), KM_NOSLEEP);
50469430SRaymond.Chen@Sun.COM 
50479430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
50489430SRaymond.Chen@Sun.COM 	nlist->dn_notif = dn_notif;
50499430SRaymond.Chen@Sun.COM 
50509430SRaymond.Chen@Sun.COM 	usba_init_list(&nlist->notif_list, (usb_opaque_t)nlist,
50519430SRaymond.Chen@Sun.COM 	    hwahcp->hwahc_dev_data->dev_iblock_cookie);
50529430SRaymond.Chen@Sun.COM 
50539430SRaymond.Chen@Sun.COM 	/* queue the new notification to the list */
50549430SRaymond.Chen@Sun.COM 	usba_add_to_list(&hwahcp->hwahc_dn_notif_queue, &nlist->notif_list);
50559430SRaymond.Chen@Sun.COM 
50569430SRaymond.Chen@Sun.COM 	/* handle the notification queue with an asynchronous thread */
50579430SRaymond.Chen@Sun.COM 	if (hwahcp->hwahc_notif_thread_id == 0) {
50589430SRaymond.Chen@Sun.COM 		if (usb_async_req(hwahcp->hwahc_dip, hwahc_notif_thread,
50599430SRaymond.Chen@Sun.COM 		    (void *)hwahcp, 0) != USB_SUCCESS) {
50609430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
50619430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
50629430SRaymond.Chen@Sun.COM 			    "hwahc_handle_dn_notif: no notif thread started");
50639430SRaymond.Chen@Sun.COM 			mutex_exit(&hwahcp->hwahc_mutex);
50649430SRaymond.Chen@Sun.COM 
50659430SRaymond.Chen@Sun.COM 			return;
50669430SRaymond.Chen@Sun.COM 		}
50679430SRaymond.Chen@Sun.COM 		hwahcp->hwahc_notif_thread_id = (kthread_t *)1;
50689430SRaymond.Chen@Sun.COM 	}
50699430SRaymond.Chen@Sun.COM 
50709430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
50719430SRaymond.Chen@Sun.COM }
50729430SRaymond.Chen@Sun.COM 
50739430SRaymond.Chen@Sun.COM /* handle the notifications in the notification queue in sequence */
50749430SRaymond.Chen@Sun.COM static void
hwahc_notif_thread(void * arg)50759430SRaymond.Chen@Sun.COM hwahc_notif_thread(void *arg)
50769430SRaymond.Chen@Sun.COM {
50779430SRaymond.Chen@Sun.COM 	hwahc_state_t		*hwahcp = (hwahc_state_t *)arg;
50789430SRaymond.Chen@Sun.COM 	hwahc_dn_notif_list_t	*nlist;
50799430SRaymond.Chen@Sun.COM 	hwa_notif_dn_recvd_t	*dn_notif;
50809430SRaymond.Chen@Sun.COM 
50819430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
50829430SRaymond.Chen@Sun.COM 	ASSERT(hwahcp->hwahc_notif_thread_id == (kthread_t *)1);
50839430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_notif_thread_id = curthread;
50849430SRaymond.Chen@Sun.COM 
50859430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
50869430SRaymond.Chen@Sun.COM 	    "hwahc_notif_thread: started, thread_id=0x%p",
50879430SRaymond.Chen@Sun.COM 	    (void *)hwahcp->hwahc_notif_thread_id);
50889430SRaymond.Chen@Sun.COM 
50899430SRaymond.Chen@Sun.COM 	while (usba_list_entry_count(&hwahcp->hwahc_dn_notif_queue) != 0) {
50909430SRaymond.Chen@Sun.COM 		/*
50919430SRaymond.Chen@Sun.COM 		 * first in first out, only one notification will be handled
50929430SRaymond.Chen@Sun.COM 		 * at a time, so it assures no racing in attach or detach
50939430SRaymond.Chen@Sun.COM 		 */
50949430SRaymond.Chen@Sun.COM 		if ((nlist =
50959430SRaymond.Chen@Sun.COM 		    (hwahc_dn_notif_list_t *)usba_rm_first_pvt_from_list(
50969430SRaymond.Chen@Sun.COM 		    &hwahcp->hwahc_dn_notif_queue)) == NULL) {
50979430SRaymond.Chen@Sun.COM 
50989430SRaymond.Chen@Sun.COM 			continue;
50999430SRaymond.Chen@Sun.COM 		}
51009430SRaymond.Chen@Sun.COM 		dn_notif = nlist->dn_notif;
51019430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
51029430SRaymond.Chen@Sun.COM 		hwahc_handle_dn(hwahcp, dn_notif);
51039430SRaymond.Chen@Sun.COM 		usba_destroy_list(&nlist->notif_list);
51049430SRaymond.Chen@Sun.COM 		kmem_free(nlist, sizeof (hwahc_dn_notif_list_t));
51059430SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
51069430SRaymond.Chen@Sun.COM 	}
51079430SRaymond.Chen@Sun.COM 
51089430SRaymond.Chen@Sun.COM 	hwahcp->hwahc_notif_thread_id = 0;
51099430SRaymond.Chen@Sun.COM 
51109430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
51119430SRaymond.Chen@Sun.COM 	    "hwahc_notif_thread: ended");
51129430SRaymond.Chen@Sun.COM 
51139430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
51149430SRaymond.Chen@Sun.COM }
51159430SRaymond.Chen@Sun.COM 
51169430SRaymond.Chen@Sun.COM /* Set the child device's active bit to 1 */
51179430SRaymond.Chen@Sun.COM static void
hwahc_set_device_active(hwahc_state_t * hwahcp,uint8_t devaddr)51189430SRaymond.Chen@Sun.COM hwahc_set_device_active(hwahc_state_t *hwahcp, uint8_t devaddr)
51199430SRaymond.Chen@Sun.COM {
51209430SRaymond.Chen@Sun.COM 	wusb_dev_info_t *dev_info;
51219430SRaymond.Chen@Sun.COM 	wusb_hc_data_t *hc_data = &hwahcp->hwahc_hc_data;
51229430SRaymond.Chen@Sun.COM 	int i;
51239430SRaymond.Chen@Sun.COM 
51249430SRaymond.Chen@Sun.COM 	mutex_enter(&hc_data->hc_mutex);
51259430SRaymond.Chen@Sun.COM 	for (i = 1; i <= hc_data->hc_num_ports; i++) {
51269430SRaymond.Chen@Sun.COM 		dev_info = hc_data->hc_dev_infos[i];
51279430SRaymond.Chen@Sun.COM 		if ((dev_info != NULL) && (dev_info->wdev_addr == devaddr)) {
51289430SRaymond.Chen@Sun.COM 			dev_info->wdev_active = 1;
51299430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L3(DPRINT_MASK_EVENTS,
51309430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
51319430SRaymond.Chen@Sun.COM 			    "hwahc_set_device_active:device(%p) updated ",
51329430SRaymond.Chen@Sun.COM 			    (void *)dev_info);
51339430SRaymond.Chen@Sun.COM 
51349430SRaymond.Chen@Sun.COM 			break;
51359430SRaymond.Chen@Sun.COM 		}
51369430SRaymond.Chen@Sun.COM 	}
51379430SRaymond.Chen@Sun.COM 	mutex_exit(&hc_data->hc_mutex);
51389430SRaymond.Chen@Sun.COM }
51399430SRaymond.Chen@Sun.COM 
51409430SRaymond.Chen@Sun.COM /*
51419430SRaymond.Chen@Sun.COM  * handle a specific device notification
51429430SRaymond.Chen@Sun.COM  * assuming the raw data in HWA DN_RECEIVED notification pkt includes
51439430SRaymond.Chen@Sun.COM  * no more than one dn pkt
51449430SRaymond.Chen@Sun.COM  */
51459430SRaymond.Chen@Sun.COM static void
hwahc_handle_dn(hwahc_state_t * hwahcp,hwa_notif_dn_recvd_t * dn_notif)51469430SRaymond.Chen@Sun.COM hwahc_handle_dn(hwahc_state_t *hwahcp, hwa_notif_dn_recvd_t *dn_notif)
51479430SRaymond.Chen@Sun.COM {
51489430SRaymond.Chen@Sun.COM 	uint8_t			*p;
51499430SRaymond.Chen@Sun.COM 	size_t			len;
51509430SRaymond.Chen@Sun.COM 	uint8_t			dntype;
51519430SRaymond.Chen@Sun.COM 	int circ;
51529430SRaymond.Chen@Sun.COM 	wusb_hc_data_t		*hc_data = &hwahcp->hwahc_hc_data;
51539430SRaymond.Chen@Sun.COM 
51549430SRaymond.Chen@Sun.COM 	if (dn_notif->bLength < 4) {
51559430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
51569430SRaymond.Chen@Sun.COM 		    "hwahc_handle_dn: bLength too short %d", dn_notif->bLength);
51579430SRaymond.Chen@Sun.COM 		kmem_free(dn_notif, dn_notif->bLength);
51589430SRaymond.Chen@Sun.COM 
51599430SRaymond.Chen@Sun.COM 		return;
51609430SRaymond.Chen@Sun.COM 	}
51619430SRaymond.Chen@Sun.COM 
51629430SRaymond.Chen@Sun.COM 	p = dn_notif->notifdata;
51639430SRaymond.Chen@Sun.COM 	len = dn_notif->bLength - 4;
51649430SRaymond.Chen@Sun.COM 
51659430SRaymond.Chen@Sun.COM 	/*
51669430SRaymond.Chen@Sun.COM 	 * WUSB Errata 06.12 specifies that the raw data in the DN_RECEIVED
51679430SRaymond.Chen@Sun.COM 	 * notification must not include the WUSB header, but only the bType
51689430SRaymond.Chen@Sun.COM 	 * and Notification specific data
51699430SRaymond.Chen@Sun.COM 	 */
51709430SRaymond.Chen@Sun.COM 	if (len == 0) {
51719430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
51729430SRaymond.Chen@Sun.COM 		    "hwahc_handle_dn: no raw data");
51739430SRaymond.Chen@Sun.COM 		kmem_free(dn_notif, dn_notif->bLength);
51749430SRaymond.Chen@Sun.COM 
51759430SRaymond.Chen@Sun.COM 		return;
51769430SRaymond.Chen@Sun.COM 	}
51779430SRaymond.Chen@Sun.COM 	dntype = *p;
51789430SRaymond.Chen@Sun.COM 
51799430SRaymond.Chen@Sun.COM 	/* update the device's status bit, no matter what the DN is */
51809430SRaymond.Chen@Sun.COM 	hwahc_set_device_active(hwahcp, dn_notif->bSourceDeviceAddr);
51819430SRaymond.Chen@Sun.COM 
51829430SRaymond.Chen@Sun.COM 	ndi_devi_enter(hwahcp->hwahc_dip, &circ);
51839430SRaymond.Chen@Sun.COM 	switch (dntype) {
51849430SRaymond.Chen@Sun.COM 	case WUSB_DN_CONNECT:
51859430SRaymond.Chen@Sun.COM 		/* DN_Connect */
51869430SRaymond.Chen@Sun.COM 		wusb_hc_handle_dn_connect(
51879430SRaymond.Chen@Sun.COM 		    hc_data, hwahcp->hwahc_default_pipe,
51889430SRaymond.Chen@Sun.COM 		    hwahcp->hwahc_wa_data.wa_ifno, p, len,
51899430SRaymond.Chen@Sun.COM 		    &hwahcp->hwahc_secrt_data);
51909430SRaymond.Chen@Sun.COM 
51919430SRaymond.Chen@Sun.COM 		break;
51929430SRaymond.Chen@Sun.COM 	case WUSB_DN_DISCONNECT:
51939430SRaymond.Chen@Sun.COM 		/* DN_Disconnect */
51949430SRaymond.Chen@Sun.COM 		wusb_hc_handle_dn_disconnect(
51959430SRaymond.Chen@Sun.COM 		    hc_data, dn_notif->bSourceDeviceAddr,
51969430SRaymond.Chen@Sun.COM 		    p, len);
51979430SRaymond.Chen@Sun.COM 
51989430SRaymond.Chen@Sun.COM 		break;
51999430SRaymond.Chen@Sun.COM 	case WUSB_DN_ALIVE:
52009430SRaymond.Chen@Sun.COM 		/* We only send KeepAlive IE to one device at a comment */
52019430SRaymond.Chen@Sun.COM 		mutex_enter(&hc_data->hc_mutex);
52029430SRaymond.Chen@Sun.COM 		if (dn_notif->bSourceDeviceAddr ==
52039430SRaymond.Chen@Sun.COM 		    hc_data->hc_alive_ie.bDeviceAddress[0]) {
52049797SRaymond.Chen@Sun.COM 			mutex_exit(&hc_data->hc_mutex);
52059430SRaymond.Chen@Sun.COM 			wusb_hc_rem_ie(hc_data,
52069430SRaymond.Chen@Sun.COM 			    (wusb_ie_header_t *)&hc_data->hc_alive_ie);
52079797SRaymond.Chen@Sun.COM 			mutex_enter(&hc_data->hc_mutex);
52089430SRaymond.Chen@Sun.COM 		}
52099430SRaymond.Chen@Sun.COM 		mutex_exit(&hc_data->hc_mutex);
52109430SRaymond.Chen@Sun.COM 
52119430SRaymond.Chen@Sun.COM 		break;
52129430SRaymond.Chen@Sun.COM 	case WUSB_DN_EPRDY:
52139430SRaymond.Chen@Sun.COM 	case WUSB_DN_MASAVAILCHANGED:
52149430SRaymond.Chen@Sun.COM 	case WUSB_DN_REMOTEWAKEUP:
52159430SRaymond.Chen@Sun.COM 	case WUSB_DN_SLEEP:
52169430SRaymond.Chen@Sun.COM 	default:
52179430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
52189430SRaymond.Chen@Sun.COM 		    "hwahc_handle_dn: dn type 0x%x not supported yet",
52199430SRaymond.Chen@Sun.COM 		    dntype);
52209430SRaymond.Chen@Sun.COM 
52219430SRaymond.Chen@Sun.COM 		break;
52229430SRaymond.Chen@Sun.COM 	}
52239430SRaymond.Chen@Sun.COM 
52249430SRaymond.Chen@Sun.COM 	kmem_free(dn_notif, dn_notif->bLength);
52259430SRaymond.Chen@Sun.COM 	ndi_devi_exit(hwahcp->hwahc_dip, circ);
52269430SRaymond.Chen@Sun.COM }
52279430SRaymond.Chen@Sun.COM 
52289430SRaymond.Chen@Sun.COM /* exceptional callback for notification ept */
52299430SRaymond.Chen@Sun.COM /* ARGSUSED */
52309430SRaymond.Chen@Sun.COM static void
hwahc_intr_exc_cb(usb_pipe_handle_t ph,struct usb_intr_req * reqp)52319430SRaymond.Chen@Sun.COM hwahc_intr_exc_cb(usb_pipe_handle_t ph, struct usb_intr_req *reqp)
52329430SRaymond.Chen@Sun.COM {
52339430SRaymond.Chen@Sun.COM 	dev_info_t	*dip = (USBA_REQ2WRP(reqp))->wr_dip;
52349430SRaymond.Chen@Sun.COM 	hwahc_state_t	*hwahcp;
52359430SRaymond.Chen@Sun.COM 
52369430SRaymond.Chen@Sun.COM 	ASSERT(dip != NULL);
52379430SRaymond.Chen@Sun.COM 	hwahcp = ddi_get_soft_state(hwahc_statep, ddi_get_instance(dip));
52389430SRaymond.Chen@Sun.COM 	ASSERT(hwahcp != NULL);
52399430SRaymond.Chen@Sun.COM 
52409430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
52419430SRaymond.Chen@Sun.COM 	    "hwahc_intr_exc_cb: receive intr exception cb, cr=%d",
52429430SRaymond.Chen@Sun.COM 	    reqp->intr_completion_reason);
52439430SRaymond.Chen@Sun.COM 
52449430SRaymond.Chen@Sun.COM 	ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
52459430SRaymond.Chen@Sun.COM 
52469430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
52479430SRaymond.Chen@Sun.COM 
52489430SRaymond.Chen@Sun.COM 	switch (reqp->intr_completion_reason) {
52499430SRaymond.Chen@Sun.COM 	case USB_CR_PIPE_RESET:
52509430SRaymond.Chen@Sun.COM 		/* only restart nep after autoclearing */
52519430SRaymond.Chen@Sun.COM 		if (hwahcp->hwahc_dev_state == USB_DEV_ONLINE) {
52529430SRaymond.Chen@Sun.COM 			hwahcp->hwahc_wa_data.wa_intr_pipe_state =
52539430SRaymond.Chen@Sun.COM 			    WA_PIPE_STOPPED;
52549430SRaymond.Chen@Sun.COM 			mutex_exit(&hwahcp->hwahc_mutex);
52559430SRaymond.Chen@Sun.COM 			(void) wusb_wa_start_nep(&hwahcp->hwahc_wa_data,
52569430SRaymond.Chen@Sun.COM 			    USB_FLAGS_NOSLEEP);
52579430SRaymond.Chen@Sun.COM 			mutex_enter(&hwahcp->hwahc_mutex);
52589430SRaymond.Chen@Sun.COM 		}
52599430SRaymond.Chen@Sun.COM 
52609430SRaymond.Chen@Sun.COM 		break;
52619430SRaymond.Chen@Sun.COM 	case USB_CR_DEV_NOT_RESP:
52629430SRaymond.Chen@Sun.COM 	case USB_CR_STOPPED_POLLING:
52639430SRaymond.Chen@Sun.COM 	case USB_CR_PIPE_CLOSING:
52649430SRaymond.Chen@Sun.COM 	case USB_CR_UNSPECIFIED_ERR:
52659430SRaymond.Chen@Sun.COM 		/* never restart nep on these conditions */
52669430SRaymond.Chen@Sun.COM 	default:
52679430SRaymond.Chen@Sun.COM 		/* for all others, wait for the autoclearing PIPE_RESET cb */
52689430SRaymond.Chen@Sun.COM 
52699430SRaymond.Chen@Sun.COM 		break;
52709430SRaymond.Chen@Sun.COM 	}
52719430SRaymond.Chen@Sun.COM 
52729430SRaymond.Chen@Sun.COM 	usb_free_intr_req(reqp);
52739430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
52749430SRaymond.Chen@Sun.COM }
52759430SRaymond.Chen@Sun.COM 
52769430SRaymond.Chen@Sun.COM /*
52779430SRaymond.Chen@Sun.COM  * callback function called by WA to resubmit a periodic request for
52789430SRaymond.Chen@Sun.COM  * interrupt polling or isochronous transfer.
52799430SRaymond.Chen@Sun.COM  */
52809430SRaymond.Chen@Sun.COM static int
hwahc_pipe_submit_periodic_req(wusb_wa_data_t * wa_data,usba_pipe_handle_data_t * ph)52819430SRaymond.Chen@Sun.COM hwahc_pipe_submit_periodic_req(wusb_wa_data_t *wa_data,
52829430SRaymond.Chen@Sun.COM 	usba_pipe_handle_data_t *ph)
52839430SRaymond.Chen@Sun.COM {
52849430SRaymond.Chen@Sun.COM 	hwahc_state_t *hwahcp = wa_data->wa_private_data;
52859430SRaymond.Chen@Sun.COM 	hwahc_pipe_private_t *pp = (hwahc_pipe_private_t *)ph->p_hcd_private;
52869430SRaymond.Chen@Sun.COM 	int rval;
52879430SRaymond.Chen@Sun.COM 
52889430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
52899430SRaymond.Chen@Sun.COM 
52909430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
52919430SRaymond.Chen@Sun.COM 	    "hwahc_pipe_submit_periodic_req: hwahcp=0x%p, pp=0x%p,"
52929430SRaymond.Chen@Sun.COM 	    " pipe state = %d", (void *)hwahcp, (void *)pp, pp->pp_state);
52939430SRaymond.Chen@Sun.COM 
52949430SRaymond.Chen@Sun.COM 	if (pp->pp_state != HWAHC_PIPE_STATE_ACTIVE) {
52959430SRaymond.Chen@Sun.COM 		/* pipe error or pipe closing, don't resubmit any more */
52969430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
52979430SRaymond.Chen@Sun.COM 		    "hwahc_pipe_submit_periodic_req: pipe not active = %d",
52989430SRaymond.Chen@Sun.COM 		    pp->pp_state);
52999430SRaymond.Chen@Sun.COM 
53009430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
53019430SRaymond.Chen@Sun.COM 
53029430SRaymond.Chen@Sun.COM 		return (USB_PIPE_ERROR);
53039430SRaymond.Chen@Sun.COM 	}
53049430SRaymond.Chen@Sun.COM 
53059430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
53069430SRaymond.Chen@Sun.COM 
53079430SRaymond.Chen@Sun.COM 	/* re-submit the original request */
53089430SRaymond.Chen@Sun.COM 	rval = wusb_wa_intr_xfer(wa_data, pp->pp_rp, ph,
53099430SRaymond.Chen@Sun.COM 	    (usb_intr_req_t *)pp->pp_client_periodic_in_reqp, 0);
53109430SRaymond.Chen@Sun.COM 
53119430SRaymond.Chen@Sun.COM 	return (rval);
53129430SRaymond.Chen@Sun.COM }
53139430SRaymond.Chen@Sun.COM 
53149430SRaymond.Chen@Sun.COM /* call HCD callback for completion handling */
53159430SRaymond.Chen@Sun.COM 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)53169430SRaymond.Chen@Sun.COM hwahc_rpipe_xfer_cb(dev_info_t *dip, usba_pipe_handle_data_t *ph,
53179430SRaymond.Chen@Sun.COM 	wusb_wa_trans_wrapper_t *wr, usb_cr_t cr)
53189430SRaymond.Chen@Sun.COM {
53199430SRaymond.Chen@Sun.COM 	hwahc_state_t		*hwahcp;
53209430SRaymond.Chen@Sun.COM 	hwahc_pipe_private_t	*pp;
53219430SRaymond.Chen@Sun.COM 	usb_opaque_t		req;
53229430SRaymond.Chen@Sun.COM 	wusb_hc_data_t		*hc_data;
53239430SRaymond.Chen@Sun.COM 
53249430SRaymond.Chen@Sun.COM 	hwahcp = ddi_get_soft_state(hwahc_statep, ddi_get_instance(dip));
53259430SRaymond.Chen@Sun.COM 	if (hwahcp == NULL) {
53269430SRaymond.Chen@Sun.COM 
53279430SRaymond.Chen@Sun.COM 		return;
53289430SRaymond.Chen@Sun.COM 	}
53299430SRaymond.Chen@Sun.COM 
53309430SRaymond.Chen@Sun.COM 	hc_data = &hwahcp->hwahc_hc_data;
53319430SRaymond.Chen@Sun.COM 
53329430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
53339430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
53349430SRaymond.Chen@Sun.COM 	    "hwahc_rpipe_xfer_cb: ph = 0x%p, wr = 0x%p cr = 0x%x",
53359430SRaymond.Chen@Sun.COM 	    (void *)ph, (void *)wr, cr);
53369430SRaymond.Chen@Sun.COM 
53379430SRaymond.Chen@Sun.COM 	pp = (hwahc_pipe_private_t *)ph->p_hcd_private;
53389430SRaymond.Chen@Sun.COM 
53399430SRaymond.Chen@Sun.COM 	mutex_enter(&hc_data->hc_mutex);
53409430SRaymond.Chen@Sun.COM 	pp->pp_wdev->wdev_active = 1; /* this device is active on xfer */
53419430SRaymond.Chen@Sun.COM 	mutex_exit(&hc_data->hc_mutex);
53429430SRaymond.Chen@Sun.COM 
53439430SRaymond.Chen@Sun.COM 	switch (cr) {
53449430SRaymond.Chen@Sun.COM 	case USB_CR_OK:
53459430SRaymond.Chen@Sun.COM 		break;
53469430SRaymond.Chen@Sun.COM 	case USB_CR_NOT_SUPPORTED:
53479430SRaymond.Chen@Sun.COM 	case USB_CR_NO_RESOURCES:
53489430SRaymond.Chen@Sun.COM 	case USB_CR_PIPE_RESET:
53499430SRaymond.Chen@Sun.COM 	case USB_CR_STOPPED_POLLING:
53509430SRaymond.Chen@Sun.COM 		pp->pp_state = HWAHC_PIPE_STATE_IDLE;
53519430SRaymond.Chen@Sun.COM 		break;
53529430SRaymond.Chen@Sun.COM 	case USB_CR_PIPE_CLOSING:
53539430SRaymond.Chen@Sun.COM 		break;
53549430SRaymond.Chen@Sun.COM 	default:
53559430SRaymond.Chen@Sun.COM 		pp->pp_state = HWAHC_PIPE_STATE_ERROR;
53569430SRaymond.Chen@Sun.COM 
53579430SRaymond.Chen@Sun.COM 		break;
53589430SRaymond.Chen@Sun.COM 	}
53599430SRaymond.Chen@Sun.COM 
53609430SRaymond.Chen@Sun.COM 	if (wr && wr->wr_reqp) {
53619430SRaymond.Chen@Sun.COM 		req = wr->wr_reqp;
53629430SRaymond.Chen@Sun.COM 
53639430SRaymond.Chen@Sun.COM 		mutex_enter(&wr->wr_rp->rp_mutex);
53649430SRaymond.Chen@Sun.COM 		wr->wr_reqp = NULL;
53659430SRaymond.Chen@Sun.COM 		mutex_exit(&wr->wr_rp->rp_mutex);
53669430SRaymond.Chen@Sun.COM 
53679430SRaymond.Chen@Sun.COM 	} else { /* periodic pipe cleanup */
53689430SRaymond.Chen@Sun.COM 
53699430SRaymond.Chen@Sun.COM 		/* the original request is cleared and returned to client */
53709430SRaymond.Chen@Sun.COM 		req = pp->pp_client_periodic_in_reqp;
53719430SRaymond.Chen@Sun.COM 		pp->pp_client_periodic_in_reqp = NULL;
53729430SRaymond.Chen@Sun.COM 	}
53739430SRaymond.Chen@Sun.COM 
53749430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
53759430SRaymond.Chen@Sun.COM 
53769430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
53779430SRaymond.Chen@Sun.COM 	    "hwahc_rpipe_xfer_cb: call usba_hcdi_cb for req= 0x%p",
53789430SRaymond.Chen@Sun.COM 	    (void *)req);
53799430SRaymond.Chen@Sun.COM 
53809430SRaymond.Chen@Sun.COM 	usba_hcdi_cb(ph, req, cr);
53819430SRaymond.Chen@Sun.COM }
53829430SRaymond.Chen@Sun.COM 
53839430SRaymond.Chen@Sun.COM /* post disconnect event to child on a certain port */
53849430SRaymond.Chen@Sun.COM static void
hwahc_disconnect_dev(dev_info_t * dip,usb_port_t port)53859430SRaymond.Chen@Sun.COM hwahc_disconnect_dev(dev_info_t *dip, usb_port_t port)
53869430SRaymond.Chen@Sun.COM {
53879430SRaymond.Chen@Sun.COM 	hwahc_state_t	*hwahcp;
53889430SRaymond.Chen@Sun.COM 	int		circ;
53899430SRaymond.Chen@Sun.COM 	dev_info_t	*child_dip;
53909430SRaymond.Chen@Sun.COM 
53919430SRaymond.Chen@Sun.COM 	if ((hwahcp = ddi_get_soft_state(hwahc_statep,
53929430SRaymond.Chen@Sun.COM 	    ddi_get_instance(dip))) == NULL) {
53939430SRaymond.Chen@Sun.COM 
53949430SRaymond.Chen@Sun.COM 		return;
53959430SRaymond.Chen@Sun.COM 	}
53969430SRaymond.Chen@Sun.COM 
53979430SRaymond.Chen@Sun.COM 	ndi_devi_enter(dip, &circ);
53989430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
53999430SRaymond.Chen@Sun.COM 
54009430SRaymond.Chen@Sun.COM 	child_dip = hwahcp->hwahc_hc_data.hc_children_dips[port];
54019430SRaymond.Chen@Sun.COM 	if ((hwahcp->hwahc_dev_state == USB_DEV_ONLINE) && child_dip) {
54029430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
54039430SRaymond.Chen@Sun.COM 
54049430SRaymond.Chen@Sun.COM 		/* if the child driver remains attached */
54059430SRaymond.Chen@Sun.COM 		if (i_ddi_devi_attached(child_dip)) {
54069430SRaymond.Chen@Sun.COM 			hwahc_post_event(hwahcp, port,
54079430SRaymond.Chen@Sun.COM 			    USBA_EVENT_TAG_HOT_REMOVAL);
54089430SRaymond.Chen@Sun.COM 		}
54099430SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
54109430SRaymond.Chen@Sun.COM 	}
54119430SRaymond.Chen@Sun.COM 
54129430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
54139430SRaymond.Chen@Sun.COM 	ndi_devi_exit(dip, circ);
54149430SRaymond.Chen@Sun.COM }
54159430SRaymond.Chen@Sun.COM 
54169430SRaymond.Chen@Sun.COM /* post reconect event to child on a certain port */
54179430SRaymond.Chen@Sun.COM static void
hwahc_reconnect_dev(dev_info_t * dip,usb_port_t port)54189430SRaymond.Chen@Sun.COM hwahc_reconnect_dev(dev_info_t *dip, usb_port_t port)
54199430SRaymond.Chen@Sun.COM {
54209430SRaymond.Chen@Sun.COM 	hwahc_state_t	*hwahcp;
54219430SRaymond.Chen@Sun.COM 	int		circ;
54229430SRaymond.Chen@Sun.COM 
54239430SRaymond.Chen@Sun.COM 	if ((hwahcp = ddi_get_soft_state(hwahc_statep,
54249430SRaymond.Chen@Sun.COM 	    ddi_get_instance(dip))) == NULL) {
54259430SRaymond.Chen@Sun.COM 
54269430SRaymond.Chen@Sun.COM 		return;
54279430SRaymond.Chen@Sun.COM 	}
54289430SRaymond.Chen@Sun.COM 	ndi_devi_enter(dip, &circ);
54299430SRaymond.Chen@Sun.COM 	mutex_enter(&hwahcp->hwahc_mutex);
54309430SRaymond.Chen@Sun.COM 
54319430SRaymond.Chen@Sun.COM 	if ((hwahcp->hwahc_dev_state == USB_DEV_ONLINE) &&
54329430SRaymond.Chen@Sun.COM 	    (hwahcp->hwahc_hc_data.hc_children_dips[port])) {
54339430SRaymond.Chen@Sun.COM 		mutex_exit(&hwahcp->hwahc_mutex);
54349430SRaymond.Chen@Sun.COM 		hwahc_post_event(hwahcp, port, USBA_EVENT_TAG_HOT_INSERTION);
54359430SRaymond.Chen@Sun.COM 		mutex_enter(&hwahcp->hwahc_mutex);
54369430SRaymond.Chen@Sun.COM 	}
54379430SRaymond.Chen@Sun.COM 
54389430SRaymond.Chen@Sun.COM 	mutex_exit(&hwahcp->hwahc_mutex);
54399430SRaymond.Chen@Sun.COM 	ndi_devi_exit(dip, circ);
54409430SRaymond.Chen@Sun.COM }
54419430SRaymond.Chen@Sun.COM 
54429430SRaymond.Chen@Sun.COM 
54439430SRaymond.Chen@Sun.COM /*
54449430SRaymond.Chen@Sun.COM  * Device TrustTimeout timer operations:
54459430SRaymond.Chen@Sun.COM  * hwahc_start_trust_timer: start the trust timer for a newly connected device
54469430SRaymond.Chen@Sun.COM  * hwahc_trust_timeout_handler: timer handler
54479430SRaymond.Chen@Sun.COM  * hwahc_stop_trust_timer: stop a device's trust timer
54489430SRaymond.Chen@Sun.COM  */
54499430SRaymond.Chen@Sun.COM static void
hwahc_start_trust_timer(wusb_dev_info_t * dev)54509430SRaymond.Chen@Sun.COM hwahc_start_trust_timer(wusb_dev_info_t *dev)
54519430SRaymond.Chen@Sun.COM {
54529430SRaymond.Chen@Sun.COM 	if (hwahc_enable_trust_timeout == 0) {
54539430SRaymond.Chen@Sun.COM 
54549430SRaymond.Chen@Sun.COM 		return;
54559430SRaymond.Chen@Sun.COM 	}
54569430SRaymond.Chen@Sun.COM 
54579430SRaymond.Chen@Sun.COM 	if (dev->wdev_trust_timer == NULL) {
54589430SRaymond.Chen@Sun.COM 		dev->wdev_trust_timer = timeout(hwahc_trust_timeout_handler,
54599430SRaymond.Chen@Sun.COM 		    (void *)dev, drv_usectohz(WUSB_TRUST_TIMEOUT_US));
54609430SRaymond.Chen@Sun.COM 	}
54619430SRaymond.Chen@Sun.COM }
54629430SRaymond.Chen@Sun.COM 
54639430SRaymond.Chen@Sun.COM /* timeout handler for device TrustTimeout. See section 4.14 */
54649430SRaymond.Chen@Sun.COM static void
hwahc_trust_timeout_handler(void * arg)54659430SRaymond.Chen@Sun.COM hwahc_trust_timeout_handler(void *arg)
54669430SRaymond.Chen@Sun.COM {
54679430SRaymond.Chen@Sun.COM 	wusb_dev_info_t *dev = (wusb_dev_info_t *)arg;
54689430SRaymond.Chen@Sun.COM 	usb_port_t port;
54699797SRaymond.Chen@Sun.COM 	uint16_t   dev_addr;
54709430SRaymond.Chen@Sun.COM 	wusb_hc_data_t *hc_data = dev->wdev_hc;
54719430SRaymond.Chen@Sun.COM 	uint8_t	retry = 3;
54729797SRaymond.Chen@Sun.COM 	int rval;
54739430SRaymond.Chen@Sun.COM 
54749430SRaymond.Chen@Sun.COM 	mutex_enter(&hc_data->hc_mutex);
54759430SRaymond.Chen@Sun.COM 
54769430SRaymond.Chen@Sun.COM 	dev->wdev_trust_timer = 0;
54779797SRaymond.Chen@Sun.COM 	dev_addr = dev->wdev_addr;
54789430SRaymond.Chen@Sun.COM 
54799430SRaymond.Chen@Sun.COM 	if (dev->wdev_active == 1) {
54809430SRaymond.Chen@Sun.COM 	/* device is active during the past period. Restart the timer */
54819430SRaymond.Chen@Sun.COM 		dev->wdev_active = 0; /* expect device DN set it to 1 */
54829430SRaymond.Chen@Sun.COM 	} else {
54839430SRaymond.Chen@Sun.COM 		/* send a KeepAlive IE to query the device */
54849430SRaymond.Chen@Sun.COM 		for (retry = 0; retry < 3; retry++) {
54859797SRaymond.Chen@Sun.COM 			mutex_exit(&hc_data->hc_mutex);
54869797SRaymond.Chen@Sun.COM 			rval = wusb_hc_send_keepalive_ie(hc_data,
54879797SRaymond.Chen@Sun.COM 			    dev_addr);
54889797SRaymond.Chen@Sun.COM 			mutex_enter(&hc_data->hc_mutex);
54899797SRaymond.Chen@Sun.COM 
54909797SRaymond.Chen@Sun.COM 			if (rval == USB_SUCCESS) {
54919430SRaymond.Chen@Sun.COM 				break;
54929430SRaymond.Chen@Sun.COM 			}
54939430SRaymond.Chen@Sun.COM 			/* retry 3 times if fail to send KeepAlive IE */
54949430SRaymond.Chen@Sun.COM 		}
54959430SRaymond.Chen@Sun.COM 
54969430SRaymond.Chen@Sun.COM 		if (dev->wdev_active == 0) {
54979430SRaymond.Chen@Sun.COM 			/* still no activity! Delete this device */
54989430SRaymond.Chen@Sun.COM 			if (wusb_hc_is_dev_connected(hc_data, dev->wdev_cdid,
54999430SRaymond.Chen@Sun.COM 			    &port)) {
55009430SRaymond.Chen@Sun.COM 				mutex_exit(&hc_data->hc_mutex);
55019572SRaymond.Chen@Sun.COM 				(void) hwahc_destroy_child(hc_data->hc_dip,
55029572SRaymond.Chen@Sun.COM 				    port);
55039430SRaymond.Chen@Sun.COM 
55049430SRaymond.Chen@Sun.COM 				/* the device comes to the end of its life */
55059430SRaymond.Chen@Sun.COM 				return;
55069430SRaymond.Chen@Sun.COM 			}
55079430SRaymond.Chen@Sun.COM 		}
55089430SRaymond.Chen@Sun.COM 	}
55099430SRaymond.Chen@Sun.COM 
55109430SRaymond.Chen@Sun.COM 	/* active or we received DN during query */
55119430SRaymond.Chen@Sun.COM 	hwahc_start_trust_timer(dev);
55129430SRaymond.Chen@Sun.COM 
55139430SRaymond.Chen@Sun.COM 	mutex_exit(&hc_data->hc_mutex);
55149430SRaymond.Chen@Sun.COM }
55159430SRaymond.Chen@Sun.COM 
55169430SRaymond.Chen@Sun.COM /* stop a child device's trust timeout handler */
55179430SRaymond.Chen@Sun.COM void
hwahc_stop_trust_timer(wusb_dev_info_t * dev)55189430SRaymond.Chen@Sun.COM hwahc_stop_trust_timer(wusb_dev_info_t *dev)
55199430SRaymond.Chen@Sun.COM {
55209430SRaymond.Chen@Sun.COM 	timeout_id_t tid;
55219430SRaymond.Chen@Sun.COM 	wusb_hc_data_t *hc_data = dev->wdev_hc;
55229430SRaymond.Chen@Sun.COM 
55239430SRaymond.Chen@Sun.COM 	ASSERT(mutex_owned(&hc_data->hc_mutex));
55249430SRaymond.Chen@Sun.COM 
55259430SRaymond.Chen@Sun.COM 	if (hwahc_enable_trust_timeout == 0) {
55269430SRaymond.Chen@Sun.COM 		return;
55279430SRaymond.Chen@Sun.COM 	}
55289430SRaymond.Chen@Sun.COM 
55299430SRaymond.Chen@Sun.COM 	tid = dev->wdev_trust_timer;
55309430SRaymond.Chen@Sun.COM 
55319430SRaymond.Chen@Sun.COM 	dev->wdev_trust_timer = NULL;
55329430SRaymond.Chen@Sun.COM 	mutex_exit(&hc_data->hc_mutex);
55339430SRaymond.Chen@Sun.COM 
55349430SRaymond.Chen@Sun.COM 	if (tid != NULL) {
55359430SRaymond.Chen@Sun.COM 		(void) untimeout(tid);
55369430SRaymond.Chen@Sun.COM 	}
55379430SRaymond.Chen@Sun.COM 
55389430SRaymond.Chen@Sun.COM 	mutex_enter(&hc_data->hc_mutex);
55399430SRaymond.Chen@Sun.COM }
55409430SRaymond.Chen@Sun.COM 
55419430SRaymond.Chen@Sun.COM /* configure child device and attach child on a certain port */
55429430SRaymond.Chen@Sun.COM static int
hwahc_create_child(dev_info_t * dip,usb_port_t port)55439430SRaymond.Chen@Sun.COM hwahc_create_child(dev_info_t *dip, usb_port_t port)
55449430SRaymond.Chen@Sun.COM {
55459430SRaymond.Chen@Sun.COM 	hwahc_state_t		*hwahcp;
55469430SRaymond.Chen@Sun.COM 	wusb_hc_data_t		*hc_data;
55479430SRaymond.Chen@Sun.COM 	wusb_dev_info_t		*dev_info;
55489430SRaymond.Chen@Sun.COM 	usb_pipe_handle_t	ph;
55499430SRaymond.Chen@Sun.COM 	int			rval;
55509430SRaymond.Chen@Sun.COM 	dev_info_t		*child_dip;
55519430SRaymond.Chen@Sun.COM 	usba_device_t		*child_ud = NULL;
55529430SRaymond.Chen@Sun.COM 	mblk_t			*pdata = NULL;
55539430SRaymond.Chen@Sun.COM 	usb_cr_t		completion_reason;
55549430SRaymond.Chen@Sun.COM 	usb_cb_flags_t		cb_flags;
55559430SRaymond.Chen@Sun.COM 	size_t			size;
55569430SRaymond.Chen@Sun.COM 	uint8_t			address;
55579430SRaymond.Chen@Sun.COM 	int			user_conf_index;
55589430SRaymond.Chen@Sun.COM 	uint_t			config_index;
55599430SRaymond.Chen@Sun.COM 	int			prh_circ, rh_circ, circ;
55609430SRaymond.Chen@Sun.COM 	dev_info_t		*rh_dip;
55619430SRaymond.Chen@Sun.COM 	usb_dev_descr_t		usb_dev_descr;
55629430SRaymond.Chen@Sun.COM 
55639430SRaymond.Chen@Sun.COM 	if ((hwahcp = ddi_get_soft_state(hwahc_statep,
55649430SRaymond.Chen@Sun.COM 	    ddi_get_instance(dip))) == NULL) {
55659430SRaymond.Chen@Sun.COM 
55669430SRaymond.Chen@Sun.COM 		return (USB_INVALID_ARGS);
55679430SRaymond.Chen@Sun.COM 	}
55689430SRaymond.Chen@Sun.COM 
55699430SRaymond.Chen@Sun.COM 	rh_dip = hwahcp->hwahc_hubd->h_usba_device->usb_root_hub_dip;
55709430SRaymond.Chen@Sun.COM 	ndi_hold_devi(dip);	/* avoid racing with dev detach */
55719430SRaymond.Chen@Sun.COM 	/* exclude other threads */
55729430SRaymond.Chen@Sun.COM 	ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
55739430SRaymond.Chen@Sun.COM 	ndi_devi_enter(rh_dip, &rh_circ);
55749430SRaymond.Chen@Sun.COM 	ndi_devi_enter(dip, &circ);
55759430SRaymond.Chen@Sun.COM 
55769430SRaymond.Chen@Sun.COM 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*dev_info));
55779430SRaymond.Chen@Sun.COM 
55789430SRaymond.Chen@Sun.COM 	hc_data = &hwahcp->hwahc_hc_data;
55799430SRaymond.Chen@Sun.COM 	mutex_enter(&hc_data->hc_mutex);
55809430SRaymond.Chen@Sun.COM 	dev_info = hc_data->hc_dev_infos[port];
55819430SRaymond.Chen@Sun.COM 
55829430SRaymond.Chen@Sun.COM 	/* Created in whcdi.c before authed */
55839430SRaymond.Chen@Sun.COM 	child_dip = hc_data->hc_children_dips[port];
55849430SRaymond.Chen@Sun.COM 
55859430SRaymond.Chen@Sun.COM 	child_ud = usba_get_usba_device(child_dip);
55869430SRaymond.Chen@Sun.COM 	ph = dev_info->wdev_ph;
55879430SRaymond.Chen@Sun.COM 
55889430SRaymond.Chen@Sun.COM 	mutex_exit(&hc_data->hc_mutex);
55899430SRaymond.Chen@Sun.COM 	/*
55909430SRaymond.Chen@Sun.COM 	 * HWA maintains the address space as a separate bus and
55919430SRaymond.Chen@Sun.COM 	 * will not occupy parent's address space
55929430SRaymond.Chen@Sun.COM 	 */
55939430SRaymond.Chen@Sun.COM 	address = child_ud->usb_addr;
55949430SRaymond.Chen@Sun.COM 	if (address < 0x80) {
55959430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
55969430SRaymond.Chen@Sun.COM 		    "hwahc_create_child: reconnecting, address = %d",
55979430SRaymond.Chen@Sun.COM 		    address);
55989430SRaymond.Chen@Sun.COM 
55999430SRaymond.Chen@Sun.COM 	} else {
56009430SRaymond.Chen@Sun.COM 		/* SetAddress(0) */
56019430SRaymond.Chen@Sun.COM 		if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
56029430SRaymond.Chen@Sun.COM 		    USB_DEV_REQ_HOST_TO_DEV,
56039430SRaymond.Chen@Sun.COM 		    USB_REQ_SET_ADDRESS,	/* bRequest */
56049430SRaymond.Chen@Sun.COM 		    0,				/* wValue */
56059430SRaymond.Chen@Sun.COM 		    0,				/* wIndex */
56069430SRaymond.Chen@Sun.COM 		    0,				/* wLength */
56079430SRaymond.Chen@Sun.COM 		    NULL, 0,
56089430SRaymond.Chen@Sun.COM 		    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
56099430SRaymond.Chen@Sun.COM 			char buffer[64];
56109430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
56119430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
56129430SRaymond.Chen@Sun.COM 			    "setting address failed (cr=%s cb_flags=%s "
56139430SRaymond.Chen@Sun.COM 			    "rval=%d)", usb_str_cr(completion_reason),
56149430SRaymond.Chen@Sun.COM 			    usb_str_cb_flags(cb_flags, buffer, sizeof (buffer)),
56159430SRaymond.Chen@Sun.COM 			    rval);
56169430SRaymond.Chen@Sun.COM 
56179430SRaymond.Chen@Sun.COM 			goto done;
56189430SRaymond.Chen@Sun.COM 		}
56199430SRaymond.Chen@Sun.COM 
56209430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
56219430SRaymond.Chen@Sun.COM 		    "set address 0 done");
56229430SRaymond.Chen@Sun.COM 
56239430SRaymond.Chen@Sun.COM 		usb_pipe_close(child_dip, ph,
56249430SRaymond.Chen@Sun.COM 		    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
56259430SRaymond.Chen@Sun.COM 
56269430SRaymond.Chen@Sun.COM 		child_ud->usb_addr = 0;
56279430SRaymond.Chen@Sun.COM 		dev_info->wdev_addr = 0;
56289430SRaymond.Chen@Sun.COM 		dev_info->wdev_ph = NULL;
56299430SRaymond.Chen@Sun.COM 
56309430SRaymond.Chen@Sun.COM 		/* need to be called each time dev addr is changed */
56319430SRaymond.Chen@Sun.COM 		if ((rval = wusb_hc_set_device_info(&hwahcp->hwahc_hc_data,
56329430SRaymond.Chen@Sun.COM 		    port)) != USB_SUCCESS) {
56339430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
56349430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
56359430SRaymond.Chen@Sun.COM 			    "update device info failed, rval = %d", rval);
56369430SRaymond.Chen@Sun.COM 
56379430SRaymond.Chen@Sun.COM 			goto done;
56389430SRaymond.Chen@Sun.COM 		}
56399430SRaymond.Chen@Sun.COM 
56409430SRaymond.Chen@Sun.COM 		/* new ph is stored in usba_device */
56419430SRaymond.Chen@Sun.COM 		if ((rval = usb_pipe_open(child_dip, NULL, NULL,
56429430SRaymond.Chen@Sun.COM 		    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) !=
56439430SRaymond.Chen@Sun.COM 		    USB_SUCCESS) {
56449430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
56459430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
56469430SRaymond.Chen@Sun.COM 			    "usb_pipe_open failed (%d)", rval);
56479430SRaymond.Chen@Sun.COM 
56489430SRaymond.Chen@Sun.COM 			goto done;
56499430SRaymond.Chen@Sun.COM 		}
56509430SRaymond.Chen@Sun.COM 
56519430SRaymond.Chen@Sun.COM 		/* provide at least 2ms time for address change, 7.3.1.3 */
56529430SRaymond.Chen@Sun.COM 		delay(drv_usectohz(2000));
56539430SRaymond.Chen@Sun.COM 
56549430SRaymond.Chen@Sun.COM 		/* start normal enumeration process */
56559430SRaymond.Chen@Sun.COM 		/*
56569430SRaymond.Chen@Sun.COM 		 * wusb bus address has 1:1 relationship with port number
56579430SRaymond.Chen@Sun.COM 		 * and wusb bus address starts from 2, so as to follow
56589430SRaymond.Chen@Sun.COM 		 * the convention that USB bus address 1 is reserved for
56599430SRaymond.Chen@Sun.COM 		 * host controller device. As such, only 126 WUSB devices
56609430SRaymond.Chen@Sun.COM 		 * are supported on a WUSB host
56619430SRaymond.Chen@Sun.COM 		 */
56629430SRaymond.Chen@Sun.COM 		address = port + 1;
56639430SRaymond.Chen@Sun.COM 		if (address >= 0x80) {
56649430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L3(PRINT_MASK_CBOPS,
56659430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
56669430SRaymond.Chen@Sun.COM 			    "hwahc_create_child: address for port %d exceeds "
56679430SRaymond.Chen@Sun.COM 			    "0x80", port);
56689430SRaymond.Chen@Sun.COM 			rval = USB_FAILURE;
56699430SRaymond.Chen@Sun.COM 
56709430SRaymond.Chen@Sun.COM 			goto done;
56719430SRaymond.Chen@Sun.COM 		}
56729430SRaymond.Chen@Sun.COM 		/* Set the address of the device */
56739430SRaymond.Chen@Sun.COM 		if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
56749430SRaymond.Chen@Sun.COM 		    USB_DEV_REQ_HOST_TO_DEV,
56759430SRaymond.Chen@Sun.COM 		    USB_REQ_SET_ADDRESS,	/* bRequest */
56769430SRaymond.Chen@Sun.COM 		    address,			/* wValue */
56779430SRaymond.Chen@Sun.COM 		    0,				/* wIndex */
56789430SRaymond.Chen@Sun.COM 		    0,				/* wLength */
56799430SRaymond.Chen@Sun.COM 		    NULL, 0,
56809430SRaymond.Chen@Sun.COM 		    &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
56819430SRaymond.Chen@Sun.COM 			char buffer[64];
56829430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
56839430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
56849430SRaymond.Chen@Sun.COM 			    "setting address failed (cr=%s cb_flags=%s "
56859430SRaymond.Chen@Sun.COM 			    "rval=%d)", usb_str_cr(completion_reason),
56869430SRaymond.Chen@Sun.COM 			    usb_str_cb_flags(cb_flags, buffer, sizeof (buffer)),
56879430SRaymond.Chen@Sun.COM 			    rval);
56889430SRaymond.Chen@Sun.COM 
56899430SRaymond.Chen@Sun.COM 			goto done;
56909430SRaymond.Chen@Sun.COM 		}
56919430SRaymond.Chen@Sun.COM 
56929430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
56939430SRaymond.Chen@Sun.COM 		    "set address 0x%x done", address);
56949430SRaymond.Chen@Sun.COM 
56959430SRaymond.Chen@Sun.COM 		usb_pipe_close(child_dip, ph,
56969430SRaymond.Chen@Sun.COM 		    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
56979430SRaymond.Chen@Sun.COM 
56989430SRaymond.Chen@Sun.COM 		child_ud->usb_addr = address;
56999430SRaymond.Chen@Sun.COM 		dev_info->wdev_addr = address;
57009430SRaymond.Chen@Sun.COM 		dev_info->wdev_ph = NULL;
57019430SRaymond.Chen@Sun.COM 
57029430SRaymond.Chen@Sun.COM 		if ((rval = wusb_hc_set_device_info(&hwahcp->hwahc_hc_data,
57039430SRaymond.Chen@Sun.COM 		    port)) != USB_SUCCESS) {
57049430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
57059430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
57069430SRaymond.Chen@Sun.COM 			    "update device info failed, rval = %d", rval);
57079430SRaymond.Chen@Sun.COM 
57089430SRaymond.Chen@Sun.COM 			goto done;
57099430SRaymond.Chen@Sun.COM 		}
57109430SRaymond.Chen@Sun.COM 
57119430SRaymond.Chen@Sun.COM 		/* new ph is stored in usba_device */
57129430SRaymond.Chen@Sun.COM 		if ((rval = usb_pipe_open(child_dip, NULL, NULL,
57139430SRaymond.Chen@Sun.COM 		    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) !=
57149430SRaymond.Chen@Sun.COM 		    USB_SUCCESS) {
57159430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_CBOPS,
57169430SRaymond.Chen@Sun.COM 			    hwahcp->hwahc_log_handle,
57179430SRaymond.Chen@Sun.COM 			    "usb_pipe_open failed (%d)", rval);
57189430SRaymond.Chen@Sun.COM 
57199430SRaymond.Chen@Sun.COM 			goto done;
57209430SRaymond.Chen@Sun.COM 		}
57219430SRaymond.Chen@Sun.COM 
57229430SRaymond.Chen@Sun.COM 		/* provide at least 2ms time for address change, 7.3.1.3 */
57239430SRaymond.Chen@Sun.COM 		delay(drv_usectohz(2000));
57249430SRaymond.Chen@Sun.COM 	}
57259430SRaymond.Chen@Sun.COM 
57269430SRaymond.Chen@Sun.COM 	/* get device descriptor ignoring device reconnection */
57279430SRaymond.Chen@Sun.COM 	rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
57289430SRaymond.Chen@Sun.COM 	    USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
57299430SRaymond.Chen@Sun.COM 	    USB_REQ_GET_DESCR,			/* bRequest */
57309430SRaymond.Chen@Sun.COM 	    USB_DESCR_TYPE_SETUP_DEV,		/* wValue */
57319430SRaymond.Chen@Sun.COM 	    0,					/* wIndex */
57329430SRaymond.Chen@Sun.COM 	    512,				/* wLength */
57339430SRaymond.Chen@Sun.COM 	    &pdata, USB_ATTRS_SHORT_XFER_OK,
57349430SRaymond.Chen@Sun.COM 	    &completion_reason, &cb_flags, 0);
57359430SRaymond.Chen@Sun.COM 
57369430SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
57379430SRaymond.Chen@Sun.COM 		if (pdata) {
57389430SRaymond.Chen@Sun.COM 			freemsg(pdata);
57399430SRaymond.Chen@Sun.COM 			pdata = NULL;
57409430SRaymond.Chen@Sun.COM 		}
57419430SRaymond.Chen@Sun.COM 
57429430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
57439430SRaymond.Chen@Sun.COM 		    "hwahc_create_child: get device descriptor failed "
57449430SRaymond.Chen@Sun.COM 		    "(%s 0x%x %d)", usb_str_cr(completion_reason),
57459430SRaymond.Chen@Sun.COM 		    cb_flags, rval);
57469430SRaymond.Chen@Sun.COM 
57479430SRaymond.Chen@Sun.COM 		goto done;
57489430SRaymond.Chen@Sun.COM 	}
57499430SRaymond.Chen@Sun.COM 
57509430SRaymond.Chen@Sun.COM 	ASSERT(pdata != NULL);
57519430SRaymond.Chen@Sun.COM 	size = usb_parse_dev_descr(
57529430SRaymond.Chen@Sun.COM 	    pdata->b_rptr,
57539430SRaymond.Chen@Sun.COM 	    MBLKL(pdata),
57549430SRaymond.Chen@Sun.COM 	    &usb_dev_descr,
57559430SRaymond.Chen@Sun.COM 	    sizeof (usb_dev_descr_t));
57569430SRaymond.Chen@Sun.COM 	freemsg(pdata);
57579430SRaymond.Chen@Sun.COM 
57589430SRaymond.Chen@Sun.COM 	if (size < USB_DEV_DESCR_SIZE) {
57599430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
57609430SRaymond.Chen@Sun.COM 		    "hwahc_create_child: get device descriptor size = %lu "
57619430SRaymond.Chen@Sun.COM 		    "expected size = %u", size, USB_DEV_DESCR_SIZE);
57629430SRaymond.Chen@Sun.COM 		rval = USB_FAILURE;
57639430SRaymond.Chen@Sun.COM 
57649430SRaymond.Chen@Sun.COM 		goto done;
57659430SRaymond.Chen@Sun.COM 	}
57669430SRaymond.Chen@Sun.COM 
57679430SRaymond.Chen@Sun.COM 	bcopy(&usb_dev_descr, child_ud->usb_dev_descr,
57689430SRaymond.Chen@Sun.COM 	    sizeof (usb_dev_descr_t));
57699430SRaymond.Chen@Sun.COM 	child_ud->usb_n_cfgs = usb_dev_descr.bNumConfigurations;
57709430SRaymond.Chen@Sun.COM 
57719430SRaymond.Chen@Sun.COM 	if (usb_dev_descr.bNumConfigurations == 0) {
57729430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
57739430SRaymond.Chen@Sun.COM 		    "device descriptor:\n\t"
57749430SRaymond.Chen@Sun.COM 		    "l=0x%x type=0x%x USB=0x%x class=0x%x subclass=0x%x\n\t"
57759430SRaymond.Chen@Sun.COM 		    "protocol=0x%x maxpktsize=0x%x "
57769430SRaymond.Chen@Sun.COM 		    "Vid=0x%x Pid=0x%x rel=0x%x\n\t"
57779430SRaymond.Chen@Sun.COM 		    "Mfg=0x%x P=0x%x sn=0x%x #config=0x%x",
57789430SRaymond.Chen@Sun.COM 		    usb_dev_descr.bLength, usb_dev_descr.bDescriptorType,
57799430SRaymond.Chen@Sun.COM 		    usb_dev_descr.bcdUSB, usb_dev_descr.bDeviceClass,
57809430SRaymond.Chen@Sun.COM 		    usb_dev_descr.bDeviceSubClass,
57819430SRaymond.Chen@Sun.COM 		    usb_dev_descr.bDeviceProtocol,
57829430SRaymond.Chen@Sun.COM 		    usb_dev_descr.bMaxPacketSize0,
57839430SRaymond.Chen@Sun.COM 		    usb_dev_descr.idVendor,
57849430SRaymond.Chen@Sun.COM 		    usb_dev_descr.idProduct, usb_dev_descr.bcdDevice,
57859430SRaymond.Chen@Sun.COM 		    usb_dev_descr.iManufacturer, usb_dev_descr.iProduct,
57869430SRaymond.Chen@Sun.COM 		    usb_dev_descr.iSerialNumber,
57879430SRaymond.Chen@Sun.COM 		    usb_dev_descr.bNumConfigurations);
57889430SRaymond.Chen@Sun.COM 
57899430SRaymond.Chen@Sun.COM 		rval = USB_FAILURE;
57909430SRaymond.Chen@Sun.COM 
57919430SRaymond.Chen@Sun.COM 		goto done;
57929430SRaymond.Chen@Sun.COM 	}
57939430SRaymond.Chen@Sun.COM 
57949430SRaymond.Chen@Sun.COM 	/* get the device string descriptor(s) */
57959430SRaymond.Chen@Sun.COM 	usba_get_dev_string_descrs(child_dip, child_ud);
57969430SRaymond.Chen@Sun.COM 
57979430SRaymond.Chen@Sun.COM 	/* retrieve config cloud for all configurations */
57989430SRaymond.Chen@Sun.COM 	rval = hubd_get_all_device_config_cloud(hwahcp->hwahc_hubd,
57999430SRaymond.Chen@Sun.COM 	    child_dip, child_ud);
58009430SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
58019430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
58029430SRaymond.Chen@Sun.COM 		    "failed to get configuration descriptor(s)");
58039430SRaymond.Chen@Sun.COM 
58049430SRaymond.Chen@Sun.COM 		goto done;
58059430SRaymond.Chen@Sun.COM 	}
58069430SRaymond.Chen@Sun.COM 
58079430SRaymond.Chen@Sun.COM 	/* get the preferred configuration for this device */
58089430SRaymond.Chen@Sun.COM 	user_conf_index = hubd_select_device_configuration(hwahcp->hwahc_hubd,
58099430SRaymond.Chen@Sun.COM 	    port, child_dip, child_ud);
58109430SRaymond.Chen@Sun.COM 
58119430SRaymond.Chen@Sun.COM 	/* Check if the user selected configuration index is in range */
58129430SRaymond.Chen@Sun.COM 	if ((user_conf_index >= usb_dev_descr.bNumConfigurations) ||
58139430SRaymond.Chen@Sun.COM 	    (user_conf_index < 0)) {
58149430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
58159430SRaymond.Chen@Sun.COM 		    "Configuration index for device idVendor=%d "
58169430SRaymond.Chen@Sun.COM 		    "idProduct=%d is=%d, and is out of range[0..%d]",
58179430SRaymond.Chen@Sun.COM 		    usb_dev_descr.idVendor, usb_dev_descr.idProduct,
58189430SRaymond.Chen@Sun.COM 		    user_conf_index, usb_dev_descr.bNumConfigurations - 1);
58199430SRaymond.Chen@Sun.COM 
58209430SRaymond.Chen@Sun.COM 		/* treat this as user didn't specify configuration */
58219430SRaymond.Chen@Sun.COM 		user_conf_index = USBA_DEV_CONFIG_INDEX_UNDEFINED;
58229430SRaymond.Chen@Sun.COM 	}
58239430SRaymond.Chen@Sun.COM 
58249430SRaymond.Chen@Sun.COM 	if (user_conf_index == USBA_DEV_CONFIG_INDEX_UNDEFINED) {
58259430SRaymond.Chen@Sun.COM 		if (child_ud->usb_preferred_driver) {
58269430SRaymond.Chen@Sun.COM 			/*
58279430SRaymond.Chen@Sun.COM 			 * It is the job of the "preferred driver" to put the
58289430SRaymond.Chen@Sun.COM 			 * device in the desired configuration. Till then
58299430SRaymond.Chen@Sun.COM 			 * put the device in config index 0.
58309430SRaymond.Chen@Sun.COM 			 */
58319430SRaymond.Chen@Sun.COM 			/* h_ignore_pwr_budget = TRUE, not care the power */
58329430SRaymond.Chen@Sun.COM 			if ((rval = usba_hubdi_check_power_budget(dip, child_ud,
58339430SRaymond.Chen@Sun.COM 			    USB_DEV_DEFAULT_CONFIG_INDEX)) != USB_SUCCESS) {
58349430SRaymond.Chen@Sun.COM 
58359430SRaymond.Chen@Sun.COM 				goto done;
58369430SRaymond.Chen@Sun.COM 			}
58379430SRaymond.Chen@Sun.COM 
58389430SRaymond.Chen@Sun.COM 			child_dip = hubd_ready_device(hwahcp->hwahc_hubd,
58399430SRaymond.Chen@Sun.COM 			    child_dip, child_ud, USB_DEV_DEFAULT_CONFIG_INDEX);
58409430SRaymond.Chen@Sun.COM 
58419430SRaymond.Chen@Sun.COM 			/*
58429430SRaymond.Chen@Sun.COM 			 * Assign the dip before onlining to avoid race
58439430SRaymond.Chen@Sun.COM 			 * with busctl
58449430SRaymond.Chen@Sun.COM 			 */
58459430SRaymond.Chen@Sun.COM 			mutex_enter(&hc_data->hc_mutex);
58469430SRaymond.Chen@Sun.COM 			hc_data->hc_children_dips[port] = child_dip;
58479430SRaymond.Chen@Sun.COM 			mutex_exit(&hc_data->hc_mutex);
58489430SRaymond.Chen@Sun.COM 
58499430SRaymond.Chen@Sun.COM 			(void) usba_bind_driver(child_dip);
58509430SRaymond.Chen@Sun.COM 		} else {
58519430SRaymond.Chen@Sun.COM 			/*
58529430SRaymond.Chen@Sun.COM 			 * loop through all the configurations to see if we
58539430SRaymond.Chen@Sun.COM 			 * can find a driver for any one config. If not, set
58549430SRaymond.Chen@Sun.COM 			 * the device in config_index 0
58559430SRaymond.Chen@Sun.COM 			 */
58569430SRaymond.Chen@Sun.COM 			rval = USB_FAILURE;
58579430SRaymond.Chen@Sun.COM 			for (config_index = 0;
58589430SRaymond.Chen@Sun.COM 			    (config_index < usb_dev_descr.bNumConfigurations) &&
58599430SRaymond.Chen@Sun.COM 			    (rval != USB_SUCCESS); config_index++) {
58609430SRaymond.Chen@Sun.COM 
58619430SRaymond.Chen@Sun.COM 				child_dip = hubd_ready_device(
58629430SRaymond.Chen@Sun.COM 				    hwahcp->hwahc_hubd,
58639430SRaymond.Chen@Sun.COM 				    child_dip, child_ud, config_index);
58649430SRaymond.Chen@Sun.COM 
58659430SRaymond.Chen@Sun.COM 				/*
58669430SRaymond.Chen@Sun.COM 				 * Assign the dip before onlining to avoid race
58679430SRaymond.Chen@Sun.COM 				 * with busctl
58689430SRaymond.Chen@Sun.COM 				 */
58699430SRaymond.Chen@Sun.COM 				mutex_enter(&hc_data->hc_mutex);
58709430SRaymond.Chen@Sun.COM 				hc_data->hc_children_dips[port] = child_dip;
58719430SRaymond.Chen@Sun.COM 				mutex_exit(&hc_data->hc_mutex);
58729430SRaymond.Chen@Sun.COM 
58739430SRaymond.Chen@Sun.COM 				rval = usba_bind_driver(child_dip);
58749430SRaymond.Chen@Sun.COM 
58759430SRaymond.Chen@Sun.COM 				if (rval == USB_SUCCESS) {
58769430SRaymond.Chen@Sun.COM 					/* always succeed for WUSB device */
58779430SRaymond.Chen@Sun.COM 					if ((usba_hubdi_check_power_budget(dip,
58789430SRaymond.Chen@Sun.COM 					    child_ud, config_index)) !=
58799430SRaymond.Chen@Sun.COM 					    USB_SUCCESS) {
58809430SRaymond.Chen@Sun.COM 						rval = USB_FAILURE;
58819430SRaymond.Chen@Sun.COM 
58829430SRaymond.Chen@Sun.COM 						goto done;
58839430SRaymond.Chen@Sun.COM 					}
58849430SRaymond.Chen@Sun.COM 				}
58859430SRaymond.Chen@Sun.COM 			}
58869430SRaymond.Chen@Sun.COM 
58879430SRaymond.Chen@Sun.COM 			if (rval != USB_SUCCESS) {
58889430SRaymond.Chen@Sun.COM 				if ((usba_hubdi_check_power_budget(dip,
58899430SRaymond.Chen@Sun.COM 				    child_ud, 0)) != USB_SUCCESS) {
58909430SRaymond.Chen@Sun.COM 
58919430SRaymond.Chen@Sun.COM 					goto done;
58929430SRaymond.Chen@Sun.COM 				}
58939430SRaymond.Chen@Sun.COM 
58949430SRaymond.Chen@Sun.COM 				child_dip = hubd_ready_device(
58959430SRaymond.Chen@Sun.COM 				    hwahcp->hwahc_hubd,
58969430SRaymond.Chen@Sun.COM 				    child_dip, child_ud, 0);
58979430SRaymond.Chen@Sun.COM 				mutex_enter(&hc_data->hc_mutex);
58989430SRaymond.Chen@Sun.COM 				hc_data->hc_children_dips[port] = child_dip;
58999430SRaymond.Chen@Sun.COM 				mutex_exit(&hc_data->hc_mutex);
59009430SRaymond.Chen@Sun.COM 			}
59019430SRaymond.Chen@Sun.COM 		} /* end else loop all configs */
59029430SRaymond.Chen@Sun.COM 	} else {
59039430SRaymond.Chen@Sun.COM 		if ((usba_hubdi_check_power_budget(dip, child_ud,
59049430SRaymond.Chen@Sun.COM 		    (uint_t)user_conf_index)) != USB_SUCCESS) {
59059430SRaymond.Chen@Sun.COM 			rval = USB_FAILURE;
59069430SRaymond.Chen@Sun.COM 
59079430SRaymond.Chen@Sun.COM 			goto done;
59089430SRaymond.Chen@Sun.COM 		}
59099430SRaymond.Chen@Sun.COM 
59109430SRaymond.Chen@Sun.COM 		child_dip = hubd_ready_device(hwahcp->hwahc_hubd, child_dip,
59119430SRaymond.Chen@Sun.COM 		    child_ud, (uint_t)user_conf_index);
59129430SRaymond.Chen@Sun.COM 
59139430SRaymond.Chen@Sun.COM 		/*
59149430SRaymond.Chen@Sun.COM 		 * Assign the dip before onlining to avoid race
59159430SRaymond.Chen@Sun.COM 		 * with busctl
59169430SRaymond.Chen@Sun.COM 		 */
59179430SRaymond.Chen@Sun.COM 		mutex_enter(&hc_data->hc_mutex);
59189430SRaymond.Chen@Sun.COM 		hc_data->hc_children_dips[port] = child_dip;
59199430SRaymond.Chen@Sun.COM 		mutex_exit(&hc_data->hc_mutex);
59209430SRaymond.Chen@Sun.COM 
59219430SRaymond.Chen@Sun.COM 		(void) usba_bind_driver(child_dip);
59229430SRaymond.Chen@Sun.COM 
59239430SRaymond.Chen@Sun.COM 		rval = USB_SUCCESS;
59249430SRaymond.Chen@Sun.COM 	}
59259430SRaymond.Chen@Sun.COM 
59269430SRaymond.Chen@Sun.COM 	/* workaround for non response after ctrl write */
59279430SRaymond.Chen@Sun.COM 	usb_pipe_close(child_dip, ph,
59289430SRaymond.Chen@Sun.COM 	    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
59299430SRaymond.Chen@Sun.COM 
59309430SRaymond.Chen@Sun.COM 	if ((rval = usb_pipe_open(child_dip, NULL, NULL,
59319430SRaymond.Chen@Sun.COM 	    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) !=
59329430SRaymond.Chen@Sun.COM 	    USB_SUCCESS) {
59339430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_CBOPS,
59349430SRaymond.Chen@Sun.COM 		    hwahcp->hwahc_log_handle,
59359430SRaymond.Chen@Sun.COM 		    "usb_pipe_open failed (%d)", rval);
59369430SRaymond.Chen@Sun.COM 
59379430SRaymond.Chen@Sun.COM 		goto done;
59389430SRaymond.Chen@Sun.COM 	}
59399430SRaymond.Chen@Sun.COM 
59409430SRaymond.Chen@Sun.COM done:
59419430SRaymond.Chen@Sun.COM 	_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*dev_info));
59429430SRaymond.Chen@Sun.COM 
59439430SRaymond.Chen@Sun.COM 	ndi_devi_exit(dip, circ);
59449430SRaymond.Chen@Sun.COM 	ndi_devi_exit(rh_dip, rh_circ);
59459430SRaymond.Chen@Sun.COM 	ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ);
59469430SRaymond.Chen@Sun.COM 
59479430SRaymond.Chen@Sun.COM 	(void) devfs_clean(rh_dip, NULL, 0);
59489430SRaymond.Chen@Sun.COM 
59499430SRaymond.Chen@Sun.COM 	if (rval == USB_SUCCESS) {
59509430SRaymond.Chen@Sun.COM 		(void) ndi_devi_online(child_dip, 0);
59519430SRaymond.Chen@Sun.COM 
59529430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
59539430SRaymond.Chen@Sun.COM 		    "hwahc_create_child: create timer for child %p",
59549430SRaymond.Chen@Sun.COM 		    (void *)dev_info);
59559430SRaymond.Chen@Sun.COM 
59569430SRaymond.Chen@Sun.COM 		mutex_enter(&hc_data->hc_mutex);
59579430SRaymond.Chen@Sun.COM 		hwahc_start_trust_timer(dev_info);
59589430SRaymond.Chen@Sun.COM 		mutex_exit(&hc_data->hc_mutex);
59599430SRaymond.Chen@Sun.COM 	}
59609430SRaymond.Chen@Sun.COM 
59619430SRaymond.Chen@Sun.COM 	ndi_rele_devi(dip);
59629430SRaymond.Chen@Sun.COM 
59639430SRaymond.Chen@Sun.COM 	return (rval);
59649430SRaymond.Chen@Sun.COM }
59659430SRaymond.Chen@Sun.COM 
59669430SRaymond.Chen@Sun.COM /* offline child on a certain port */
59679430SRaymond.Chen@Sun.COM static int
hwahc_destroy_child(dev_info_t * dip,usb_port_t port)59689430SRaymond.Chen@Sun.COM hwahc_destroy_child(dev_info_t *dip, usb_port_t port)
59699430SRaymond.Chen@Sun.COM {
59709430SRaymond.Chen@Sun.COM 	hwahc_state_t	*hwahcp;
59719430SRaymond.Chen@Sun.COM 
59729430SRaymond.Chen@Sun.COM 	if ((hwahcp = ddi_get_soft_state(hwahc_statep,
59739430SRaymond.Chen@Sun.COM 	    ddi_get_instance(dip))) == NULL) {
59749430SRaymond.Chen@Sun.COM 
59759430SRaymond.Chen@Sun.COM 		return (USB_INVALID_ARGS);
59769430SRaymond.Chen@Sun.COM 	}
59779430SRaymond.Chen@Sun.COM 
59789430SRaymond.Chen@Sun.COM 	hwahc_post_event(hwahcp, port, USBA_EVENT_TAG_HOT_REMOVAL);
59799430SRaymond.Chen@Sun.COM 
59809430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
59819430SRaymond.Chen@Sun.COM 	    "hwahc_destroy_child: scheduling cleanup");
59829430SRaymond.Chen@Sun.COM 
59839430SRaymond.Chen@Sun.COM 	/* schedule cleanup thread */
59849430SRaymond.Chen@Sun.COM 	hubd_schedule_cleanup(hwahcp->hwahc_hubd->h_usba_device->
59859430SRaymond.Chen@Sun.COM 	    usb_root_hub_dip);
59869430SRaymond.Chen@Sun.COM 
59879430SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
59889430SRaymond.Chen@Sun.COM }
59899430SRaymond.Chen@Sun.COM 
59909430SRaymond.Chen@Sun.COM /*
59919430SRaymond.Chen@Sun.COM  * called by cleanup thread to offline child and cleanup child resources
59929430SRaymond.Chen@Sun.COM  * Child's callback functions have been called before calling this routine.
59939430SRaymond.Chen@Sun.COM  *	dip - hwahc's dip
59949430SRaymond.Chen@Sun.COM  */
59959430SRaymond.Chen@Sun.COM static int
hwahc_cleanup_child(dev_info_t * dip)59969430SRaymond.Chen@Sun.COM hwahc_cleanup_child(dev_info_t *dip)
59979430SRaymond.Chen@Sun.COM {
59989430SRaymond.Chen@Sun.COM 	hwahc_state_t	*hwahcp;
59999430SRaymond.Chen@Sun.COM 	wusb_hc_data_t	*hc_data;
60009430SRaymond.Chen@Sun.COM 	usb_port_t	port;
60019430SRaymond.Chen@Sun.COM 
60029430SRaymond.Chen@Sun.COM 	if ((hwahcp = ddi_get_soft_state(hwahc_statep,
60039430SRaymond.Chen@Sun.COM 	    ddi_get_instance(dip))) == NULL) {
60049430SRaymond.Chen@Sun.COM 
60059430SRaymond.Chen@Sun.COM 		return (USB_INVALID_ARGS);
60069430SRaymond.Chen@Sun.COM 	}
60079430SRaymond.Chen@Sun.COM 
60089430SRaymond.Chen@Sun.COM 	hc_data = &hwahcp->hwahc_hc_data;
60099430SRaymond.Chen@Sun.COM 	mutex_enter(&hc_data->hc_mutex);
60109430SRaymond.Chen@Sun.COM 	for (port = 1; port <= hc_data->hc_num_ports; port++) {
60119430SRaymond.Chen@Sun.COM 		dev_info_t *cdip = hc_data->hc_children_dips[port];
60129430SRaymond.Chen@Sun.COM 
60139430SRaymond.Chen@Sun.COM 		if (cdip == NULL || DEVI_IS_DEVICE_REMOVED(cdip) == 0) {
60149430SRaymond.Chen@Sun.COM 
60159430SRaymond.Chen@Sun.COM 			continue;
60169430SRaymond.Chen@Sun.COM 		}
60179430SRaymond.Chen@Sun.COM 
60189430SRaymond.Chen@Sun.COM 		/*
60199430SRaymond.Chen@Sun.COM 		 * child's callback has been called and its dip has been
60209430SRaymond.Chen@Sun.COM 		 * marked REMOVED. Do further cleanup in hwa driver for
60219430SRaymond.Chen@Sun.COM 		 * this child.
60229430SRaymond.Chen@Sun.COM 		 */
60239430SRaymond.Chen@Sun.COM 		mutex_exit(&hc_data->hc_mutex);
60249430SRaymond.Chen@Sun.COM 		(void) hwahc_delete_child(dip, port, NDI_DEVI_REMOVE, B_TRUE);
60259430SRaymond.Chen@Sun.COM 		mutex_enter(&hc_data->hc_mutex);
60269430SRaymond.Chen@Sun.COM 	}
60279430SRaymond.Chen@Sun.COM 	mutex_exit(&hc_data->hc_mutex);
60289430SRaymond.Chen@Sun.COM 
60299430SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
60309430SRaymond.Chen@Sun.COM }
60319430SRaymond.Chen@Sun.COM 
60329430SRaymond.Chen@Sun.COM /* offline child and cleanup child resources */
60339430SRaymond.Chen@Sun.COM static int
hwahc_delete_child(dev_info_t * dip,usb_port_t port,uint_t flag,boolean_t retry)60349430SRaymond.Chen@Sun.COM hwahc_delete_child(dev_info_t *dip, usb_port_t port, uint_t flag,
60359430SRaymond.Chen@Sun.COM 	boolean_t retry)
60369430SRaymond.Chen@Sun.COM {
60379430SRaymond.Chen@Sun.COM 	hwahc_state_t	*hwahcp;
60389430SRaymond.Chen@Sun.COM 	dev_info_t	*child_dip;
60399430SRaymond.Chen@Sun.COM 	usba_device_t	*usba_device;
60409430SRaymond.Chen@Sun.COM 	wusb_hc_data_t	*hc_data;
60419430SRaymond.Chen@Sun.COM 	int		rval;
60429430SRaymond.Chen@Sun.COM 
60439430SRaymond.Chen@Sun.COM 	if ((hwahcp = ddi_get_soft_state(hwahc_statep,
60449430SRaymond.Chen@Sun.COM 	    ddi_get_instance(dip))) == NULL) {
60459430SRaymond.Chen@Sun.COM 
60469430SRaymond.Chen@Sun.COM 		return (USB_INVALID_ARGS);
60479430SRaymond.Chen@Sun.COM 	}
60489430SRaymond.Chen@Sun.COM 
60499430SRaymond.Chen@Sun.COM 	child_dip = hwahc_get_child_dip(hwahcp, port);
60509430SRaymond.Chen@Sun.COM 	if (child_dip == NULL) {
60519430SRaymond.Chen@Sun.COM 
60529430SRaymond.Chen@Sun.COM 		return (USB_SUCCESS);
60539430SRaymond.Chen@Sun.COM 	}
60549430SRaymond.Chen@Sun.COM 
60559430SRaymond.Chen@Sun.COM 	usba_device = usba_get_usba_device(child_dip);
60569430SRaymond.Chen@Sun.COM 	hc_data = &hwahcp->hwahc_hc_data;
60579430SRaymond.Chen@Sun.COM 
60589430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_CBOPS, hwahcp->hwahc_log_handle,
60599430SRaymond.Chen@Sun.COM 	    "hwahc_delete_child: port=%d, dip=0x%p usba_device=0x%p",
60609430SRaymond.Chen@Sun.COM 	    port, (void *)child_dip, (void *)usba_device);
60619430SRaymond.Chen@Sun.COM 
60629430SRaymond.Chen@Sun.COM 	if (usba_device) {
60639430SRaymond.Chen@Sun.COM 		usba_hubdi_incr_power_budget(dip, usba_device);
60649430SRaymond.Chen@Sun.COM 	}
60659430SRaymond.Chen@Sun.COM 
60669430SRaymond.Chen@Sun.COM 	/* remove this child's dip. If it's <DS_INITIALIZED, free it */
60679430SRaymond.Chen@Sun.COM 	rval = usba_destroy_child_devi(child_dip, flag);
60689430SRaymond.Chen@Sun.COM 
60699430SRaymond.Chen@Sun.COM 	if ((rval == USB_SUCCESS) && (flag & NDI_DEVI_REMOVE)) {
60709430SRaymond.Chen@Sun.COM 		/*
60719430SRaymond.Chen@Sun.COM 		 * if the child was still < DS_INITIALIZED
60729430SRaymond.Chen@Sun.COM 		 * then our bus_unconfig was not called and
60739430SRaymond.Chen@Sun.COM 		 * we have to zap the child here
60749430SRaymond.Chen@Sun.COM 		 */
60759430SRaymond.Chen@Sun.COM 		mutex_enter(&hc_data->hc_mutex);
60769430SRaymond.Chen@Sun.COM 		if (hc_data->hc_children_dips[port] == child_dip) {
60779430SRaymond.Chen@Sun.COM 			usba_device_t *ud = hc_data->hc_usba_devices[port];
60789430SRaymond.Chen@Sun.COM 			wusb_dev_info_t *dev_info = hc_data->hc_dev_infos[port];
60799430SRaymond.Chen@Sun.COM 
60809430SRaymond.Chen@Sun.COM 			hc_data->hc_children_dips[port] = NULL;
60819430SRaymond.Chen@Sun.COM 			if (ud) {
60829430SRaymond.Chen@Sun.COM 				mutex_exit(&hc_data->hc_mutex);
60839430SRaymond.Chen@Sun.COM 
60849430SRaymond.Chen@Sun.COM 				mutex_enter(&ud->usb_mutex);
60859430SRaymond.Chen@Sun.COM 				ud->usb_ref_count = 0;
60869430SRaymond.Chen@Sun.COM 				mutex_exit(&ud->usb_mutex);
60879430SRaymond.Chen@Sun.COM 
60889430SRaymond.Chen@Sun.COM 				usba_free_usba_device(ud);
60899430SRaymond.Chen@Sun.COM 				mutex_enter(&hc_data->hc_mutex);
60909430SRaymond.Chen@Sun.COM 				hc_data->hc_usba_devices[port] = NULL;
60919430SRaymond.Chen@Sun.COM 			}
60929430SRaymond.Chen@Sun.COM 
60939430SRaymond.Chen@Sun.COM 			/* free the child's wusb_dev_info data */
60949430SRaymond.Chen@Sun.COM 			if (dev_info) {
60959430SRaymond.Chen@Sun.COM 				wusb_secrt_data_t *secrt_data;
60969430SRaymond.Chen@Sun.COM 
60979430SRaymond.Chen@Sun.COM 				if (dev_info->
60989430SRaymond.Chen@Sun.COM 				    wdev_secrt_data.secrt_encry_descr) {
60999430SRaymond.Chen@Sun.COM 					secrt_data = &dev_info->wdev_secrt_data;
61009430SRaymond.Chen@Sun.COM 					kmem_free(secrt_data->secrt_encry_descr,
61019430SRaymond.Chen@Sun.COM 					    sizeof (usb_encryption_descr_t) *
61029430SRaymond.Chen@Sun.COM 					    secrt_data->secrt_n_encry);
61039430SRaymond.Chen@Sun.COM 				}
61049430SRaymond.Chen@Sun.COM 				if (dev_info->wdev_uwb_descr) {
61059430SRaymond.Chen@Sun.COM 					kmem_free(dev_info->wdev_uwb_descr,
61069430SRaymond.Chen@Sun.COM 					    sizeof (usb_uwb_cap_descr_t));
61079430SRaymond.Chen@Sun.COM 				}
61089430SRaymond.Chen@Sun.COM 				kmem_free(dev_info, sizeof (wusb_dev_info_t));
61099430SRaymond.Chen@Sun.COM 				hc_data->hc_dev_infos[port] = NULL;
61109430SRaymond.Chen@Sun.COM 			}
61119430SRaymond.Chen@Sun.COM 		}
61129430SRaymond.Chen@Sun.COM 		mutex_exit(&hc_data->hc_mutex);
61139430SRaymond.Chen@Sun.COM 	}
61149430SRaymond.Chen@Sun.COM 
61159430SRaymond.Chen@Sun.COM 	if ((rval != USB_SUCCESS) && retry) {
61169430SRaymond.Chen@Sun.COM 
61179430SRaymond.Chen@Sun.COM 		hubd_schedule_cleanup(usba_device->usb_root_hub_dip);
61189430SRaymond.Chen@Sun.COM 	}
61199430SRaymond.Chen@Sun.COM 
61209430SRaymond.Chen@Sun.COM 	return (rval);
61219430SRaymond.Chen@Sun.COM }
61229430SRaymond.Chen@Sun.COM 
61239430SRaymond.Chen@Sun.COM /*
61249430SRaymond.Chen@Sun.COM  * Set encryption type for WUSB host, refer to WUSB 1.0/8.5.3.6
61259430SRaymond.Chen@Sun.COM  * index = port number - 1
61269430SRaymond.Chen@Sun.COM  */
61279430SRaymond.Chen@Sun.COM 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)61289430SRaymond.Chen@Sun.COM hwahc_set_dev_encrypt(usb_pipe_handle_t ph, uint8_t ifc,
61299430SRaymond.Chen@Sun.COM 	usb_port_t index, wusb_secrt_data_t *secrt_data, uint8_t type)
61309430SRaymond.Chen@Sun.COM {
61319430SRaymond.Chen@Sun.COM 	int16_t			value;
61329430SRaymond.Chen@Sun.COM 	usb_ctrl_setup_t	setup;
61339430SRaymond.Chen@Sun.COM 	usb_cr_t		cr;
61349430SRaymond.Chen@Sun.COM 	usb_cb_flags_t		cb_flags;
61359430SRaymond.Chen@Sun.COM 
61369430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
61379430SRaymond.Chen@Sun.COM 	    "hwahc_set_dev_encrypt: device index = %d", index);
61389430SRaymond.Chen@Sun.COM 
61399430SRaymond.Chen@Sun.COM 	if (type == USB_ENC_TYPE_UNSECURE) {
61409430SRaymond.Chen@Sun.COM 		value = 0;
61419430SRaymond.Chen@Sun.COM 	} else if (type == USB_ENC_TYPE_CCM_1) {
61429430SRaymond.Chen@Sun.COM 		if (secrt_data == NULL) {
61439430SRaymond.Chen@Sun.COM 
61449430SRaymond.Chen@Sun.COM 			return (USB_INVALID_ARGS);
61459430SRaymond.Chen@Sun.COM 		}
61469430SRaymond.Chen@Sun.COM 
61479430SRaymond.Chen@Sun.COM 		value = wusb_get_ccm_encryption_value(secrt_data);
61489430SRaymond.Chen@Sun.COM 		if (value == -1) {
61499430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
61509430SRaymond.Chen@Sun.COM 			    "hwahc_set_dev_encrypt: cannot find ccm "
61519430SRaymond.Chen@Sun.COM 			    "encryption type");
61529430SRaymond.Chen@Sun.COM 
61539430SRaymond.Chen@Sun.COM 			return (USB_FAILURE);
61549430SRaymond.Chen@Sun.COM 		}
61559430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
61569430SRaymond.Chen@Sun.COM 		    "hwahc_set_dev_encrypt: ccm encryption value is %d",
61579430SRaymond.Chen@Sun.COM 		    value);
61589430SRaymond.Chen@Sun.COM 	} else {
61599430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
61609430SRaymond.Chen@Sun.COM 		    "hwahc_set_dev_encrypt: unsupported encryption type %d",
61619430SRaymond.Chen@Sun.COM 		    type);
61629430SRaymond.Chen@Sun.COM 
61639430SRaymond.Chen@Sun.COM 		return (USB_INVALID_ARGS);
61649430SRaymond.Chen@Sun.COM 	}
61659430SRaymond.Chen@Sun.COM 
61669430SRaymond.Chen@Sun.COM 	setup.bmRequestType = USB_DEV_REQ_HOST_TO_DEV |
61679430SRaymond.Chen@Sun.COM 	    USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_RCPT_IF;
61689430SRaymond.Chen@Sun.COM 	setup.bRequest = USB_REQ_SET_ENCRYPTION;
61699430SRaymond.Chen@Sun.COM 	setup.wValue = (uint16_t)value;
61709430SRaymond.Chen@Sun.COM 	setup.wIndex = (index << 8) | ifc;
61719430SRaymond.Chen@Sun.COM 	setup.wLength = 0;
61729430SRaymond.Chen@Sun.COM 	setup.attrs = USB_ATTRS_NONE;
61739430SRaymond.Chen@Sun.COM 
61749430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
61759430SRaymond.Chen@Sun.COM 	    "bmRequestType=0x%x, bRequest=0x%x, wValue=0x%x, wIndex=0x%x",
61769430SRaymond.Chen@Sun.COM 	    setup.bmRequestType, setup.bRequest, setup.wValue, setup.wIndex);
61779430SRaymond.Chen@Sun.COM 
61789430SRaymond.Chen@Sun.COM 	return (usb_pipe_ctrl_xfer_wait(ph, &setup, NULL,
61799430SRaymond.Chen@Sun.COM 	    &cr, &cb_flags, USB_FLAGS_SLEEP));
61809430SRaymond.Chen@Sun.COM }
6181