xref: /onnv-gate/usr/src/uts/common/io/usb/clients/hwarc/hwarc.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 /*
289430SRaymond.Chen@Sun.COM  * UWB HWA Radio Controller driver.
299430SRaymond.Chen@Sun.COM  *
309430SRaymond.Chen@Sun.COM  *
319430SRaymond.Chen@Sun.COM  * The device has four states (refer to usbai.h):
329430SRaymond.Chen@Sun.COM  *	USB_DEV_ONLINE: In action or ready for action.
339430SRaymond.Chen@Sun.COM  *	USB_DEV_DISCONNECTED: Hotplug removed, or device not present/correct on
349430SRaymond.Chen@Sun.COM  *		resume (CPR).
359430SRaymond.Chen@Sun.COM  *	USB_DEV_SUSPENDED: Device has been suspended along with the system.
369430SRaymond.Chen@Sun.COM  *	USB_DEV_PWRED_DOWN: Device has been powered down.  (Note that this
379430SRaymond.Chen@Sun.COM  *		driver supports only two power states, powered down and
389430SRaymond.Chen@Sun.COM  *		full power.)
399430SRaymond.Chen@Sun.COM  *
409430SRaymond.Chen@Sun.COM  * In order to avoid race conditions between driver entry points,
419430SRaymond.Chen@Sun.COM  * access to the device is serialized.	Race conditions are an issue in
429430SRaymond.Chen@Sun.COM  * particular between disconnect event callbacks, detach, power, open
439430SRaymond.Chen@Sun.COM  * and data transfer callbacks.  The functions hwarc_serialize/release_access
449430SRaymond.Chen@Sun.COM  * are implemented for this purpose.
459430SRaymond.Chen@Sun.COM  *
469430SRaymond.Chen@Sun.COM  * Mutexes should never be held when making calls into USBA or when
479430SRaymond.Chen@Sun.COM  * sleeping.
489430SRaymond.Chen@Sun.COM  *
499430SRaymond.Chen@Sun.COM  * pm_busy_component and pm_idle_component mark the device as busy or idle to
509430SRaymond.Chen@Sun.COM  * the system.	These functions are paired, and are called only from code
519430SRaymond.Chen@Sun.COM  * bracketed by hwarc_serialize_access and hwarc_release_access.
529430SRaymond.Chen@Sun.COM  *
539430SRaymond.Chen@Sun.COM  */
549430SRaymond.Chen@Sun.COM 
559430SRaymond.Chen@Sun.COM #define	USBDRV_MAJOR_VER	2
569430SRaymond.Chen@Sun.COM #define	USBDRV_MINOR_VER	0
579430SRaymond.Chen@Sun.COM 
589430SRaymond.Chen@Sun.COM #include <sys/strsun.h>
599430SRaymond.Chen@Sun.COM #include <sys/usb/usba.h>
609430SRaymond.Chen@Sun.COM #include <sys/usb/clients/hwarc/hwarc.h>
619430SRaymond.Chen@Sun.COM 
629430SRaymond.Chen@Sun.COM 
639430SRaymond.Chen@Sun.COM 
649430SRaymond.Chen@Sun.COM uint_t		hwarc_errlevel		= 4;
659430SRaymond.Chen@Sun.COM static uint_t	hwarc_errmask		= (uint_t)PRINT_MASK_ALL;
669430SRaymond.Chen@Sun.COM static uint_t	hwarc_instance_debug 	= (uint_t)-1;
679430SRaymond.Chen@Sun.COM 
689430SRaymond.Chen@Sun.COM static char	*name		= "hwarc";	/* Driver name, used all over */
699430SRaymond.Chen@Sun.COM 
709430SRaymond.Chen@Sun.COM 
719430SRaymond.Chen@Sun.COM /* Function Prototypes */
729430SRaymond.Chen@Sun.COM static int	hwarc_attach(dev_info_t *, ddi_attach_cmd_t);
739430SRaymond.Chen@Sun.COM static int	hwarc_detach(dev_info_t *, ddi_detach_cmd_t);
749430SRaymond.Chen@Sun.COM static int	hwarc_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
759430SRaymond.Chen@Sun.COM static int	hwarc_cleanup(dev_info_t *, hwarc_state_t *);
769430SRaymond.Chen@Sun.COM static int	hwarc_open(dev_t *, int, int, cred_t *);
779430SRaymond.Chen@Sun.COM static int	hwarc_close(dev_t, int, int, cred_t *);
789430SRaymond.Chen@Sun.COM static int 	hwarc_send_cmd(uwb_dev_handle_t, mblk_t *, uint16_t);
799430SRaymond.Chen@Sun.COM static int	hwarc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
809430SRaymond.Chen@Sun.COM static int	hwarc_disconnect_callback(dev_info_t *);
819430SRaymond.Chen@Sun.COM static int	hwarc_reconnect_callback(dev_info_t *);
829430SRaymond.Chen@Sun.COM static void	hwarc_restore_device_state(dev_info_t *, hwarc_state_t *);
839430SRaymond.Chen@Sun.COM static int	hwarc_cpr_suspend(dev_info_t *);
849430SRaymond.Chen@Sun.COM static void	hwarc_cpr_resume(dev_info_t *);
859430SRaymond.Chen@Sun.COM static int	hwarc_open_intr_pipe(hwarc_state_t *);
869430SRaymond.Chen@Sun.COM static void	hwarc_close_intr_pipe(hwarc_state_t *);
879430SRaymond.Chen@Sun.COM static void	hwarc_pm_busy_component(hwarc_state_t *);
889430SRaymond.Chen@Sun.COM static void	hwarc_pm_idle_component(hwarc_state_t *);
899430SRaymond.Chen@Sun.COM static int	hwarc_power(dev_info_t *, int, int);
909430SRaymond.Chen@Sun.COM static int	hwarc_serialize_access(hwarc_state_t *, boolean_t);
919430SRaymond.Chen@Sun.COM static void	hwarc_release_access(hwarc_state_t *);
929430SRaymond.Chen@Sun.COM static int	hwarc_reset_device(hwarc_state_t *);
939430SRaymond.Chen@Sun.COM static int	hwarc_init_phy(hwarc_state_t *);
949430SRaymond.Chen@Sun.COM 
959430SRaymond.Chen@Sun.COM 
969430SRaymond.Chen@Sun.COM static int	hwarc_start_polling(hwarc_state_t *, usb_pipe_handle_t);
979430SRaymond.Chen@Sun.COM 
989430SRaymond.Chen@Sun.COM 
999430SRaymond.Chen@Sun.COM _NOTE(SCHEME_PROTECTS_DATA("unique per call", usb_ctrl_req))
1009430SRaymond.Chen@Sun.COM _NOTE(SCHEME_PROTECTS_DATA("unique per call", usb_intr_req))
1019430SRaymond.Chen@Sun.COM _NOTE(SCHEME_PROTECTS_DATA("unique per call", buf))
1029430SRaymond.Chen@Sun.COM 
1039430SRaymond.Chen@Sun.COM /* module loading stuff */
1049430SRaymond.Chen@Sun.COM struct cb_ops hwarc_cb_ops = {
1059430SRaymond.Chen@Sun.COM 	hwarc_open,		/* open  */
1069430SRaymond.Chen@Sun.COM 	hwarc_close,		/* close */
1079430SRaymond.Chen@Sun.COM 	nodev,			/* strategy */
1089430SRaymond.Chen@Sun.COM 	nulldev,		/* print */
1099430SRaymond.Chen@Sun.COM 	nulldev,		/* dump */
1109430SRaymond.Chen@Sun.COM 	nodev,			/* read */
1119430SRaymond.Chen@Sun.COM 	nodev,			/* write */
1129430SRaymond.Chen@Sun.COM 	hwarc_ioctl,		/* ioctl */
1139430SRaymond.Chen@Sun.COM 	nulldev,		/* devmap */
1149430SRaymond.Chen@Sun.COM 	nodev,			/* mmap */
1159430SRaymond.Chen@Sun.COM 	nodev,			/* segmap */
1169430SRaymond.Chen@Sun.COM 	nochpoll,		/* poll */
1179430SRaymond.Chen@Sun.COM 	ddi_prop_op,		/* cb_prop_op */
1189430SRaymond.Chen@Sun.COM 	NULL,			/* streamtab  */
1199430SRaymond.Chen@Sun.COM 	D_MP
1209430SRaymond.Chen@Sun.COM };
1219430SRaymond.Chen@Sun.COM 
1229430SRaymond.Chen@Sun.COM static struct dev_ops hwarc_ops = {
1239430SRaymond.Chen@Sun.COM 	DEVO_REV,		/* devo_rev, */
1249430SRaymond.Chen@Sun.COM 	0,			/* refcnt  */
1259430SRaymond.Chen@Sun.COM 	hwarc_info,		/* info */
1269430SRaymond.Chen@Sun.COM 	nulldev,		/* identify */
1279430SRaymond.Chen@Sun.COM 	nulldev,		/* probe */
1289430SRaymond.Chen@Sun.COM 	hwarc_attach,		/* attach */
1299430SRaymond.Chen@Sun.COM 	hwarc_detach,		/* detach */
1309430SRaymond.Chen@Sun.COM 	nodev,			/* reset */
1319430SRaymond.Chen@Sun.COM 	&hwarc_cb_ops,		/* driver operations */
1329430SRaymond.Chen@Sun.COM 	NULL,			/* bus operations */
1339430SRaymond.Chen@Sun.COM 	hwarc_power,		/* power */
1349430SRaymond.Chen@Sun.COM 	ddi_quiesce_not_needed, /* devo_quiesce */
1359430SRaymond.Chen@Sun.COM };
1369430SRaymond.Chen@Sun.COM 
1379430SRaymond.Chen@Sun.COM static struct modldrv hwarc_modldrv =	{
1389430SRaymond.Chen@Sun.COM 	&mod_driverops,
1399430SRaymond.Chen@Sun.COM 	"USB HWA Radio Controller driver",
1409430SRaymond.Chen@Sun.COM 	&hwarc_ops
1419430SRaymond.Chen@Sun.COM };
1429430SRaymond.Chen@Sun.COM 
1439430SRaymond.Chen@Sun.COM static struct modlinkage modlinkage = {
1449430SRaymond.Chen@Sun.COM 	MODREV_1,
1459430SRaymond.Chen@Sun.COM 	&hwarc_modldrv,
1469430SRaymond.Chen@Sun.COM 	NULL
1479430SRaymond.Chen@Sun.COM };
1489430SRaymond.Chen@Sun.COM 
1499430SRaymond.Chen@Sun.COM /* Soft state structures */
1509430SRaymond.Chen@Sun.COM #define	HWARC_INITIAL_SOFT_SPACE	1
1519430SRaymond.Chen@Sun.COM static void *hwarc_statep;
1529430SRaymond.Chen@Sun.COM 
1539430SRaymond.Chen@Sun.COM 
1549430SRaymond.Chen@Sun.COM /* Module-wide initialization routine */
1559430SRaymond.Chen@Sun.COM int
_init(void)1569430SRaymond.Chen@Sun.COM _init(void)
1579430SRaymond.Chen@Sun.COM {
1589430SRaymond.Chen@Sun.COM 	int rval;
1599430SRaymond.Chen@Sun.COM 
1609430SRaymond.Chen@Sun.COM 	if ((rval = ddi_soft_state_init(&hwarc_statep,
1619430SRaymond.Chen@Sun.COM 	    sizeof (hwarc_state_t), HWARC_INITIAL_SOFT_SPACE)) != 0) {
1629430SRaymond.Chen@Sun.COM 
1639430SRaymond.Chen@Sun.COM 		return (rval);
1649430SRaymond.Chen@Sun.COM 	}
1659430SRaymond.Chen@Sun.COM 
1669430SRaymond.Chen@Sun.COM 	if ((rval = mod_install(&modlinkage)) != 0) {
1679430SRaymond.Chen@Sun.COM 		ddi_soft_state_fini(&hwarc_statep);
1689430SRaymond.Chen@Sun.COM 	}
1699430SRaymond.Chen@Sun.COM 
1709430SRaymond.Chen@Sun.COM 	return (rval);
1719430SRaymond.Chen@Sun.COM }
1729430SRaymond.Chen@Sun.COM 
1739430SRaymond.Chen@Sun.COM 
1749430SRaymond.Chen@Sun.COM /* Module-wide tear-down routine */
1759430SRaymond.Chen@Sun.COM int
_fini(void)1769430SRaymond.Chen@Sun.COM _fini(void)
1779430SRaymond.Chen@Sun.COM {
1789430SRaymond.Chen@Sun.COM 	int rval;
1799430SRaymond.Chen@Sun.COM 
1809430SRaymond.Chen@Sun.COM 	if ((rval = mod_remove(&modlinkage)) != 0) {
1819430SRaymond.Chen@Sun.COM 
1829430SRaymond.Chen@Sun.COM 		return (rval);
1839430SRaymond.Chen@Sun.COM 	}
1849430SRaymond.Chen@Sun.COM 
1859430SRaymond.Chen@Sun.COM 	ddi_soft_state_fini(&hwarc_statep);
1869430SRaymond.Chen@Sun.COM 
1879430SRaymond.Chen@Sun.COM 	return (rval);
1889430SRaymond.Chen@Sun.COM }
1899430SRaymond.Chen@Sun.COM 
1909430SRaymond.Chen@Sun.COM 
1919430SRaymond.Chen@Sun.COM int
_info(struct modinfo * modinfop)1929430SRaymond.Chen@Sun.COM _info(struct modinfo *modinfop)
1939430SRaymond.Chen@Sun.COM {
1949430SRaymond.Chen@Sun.COM 	return (mod_info(&modlinkage, modinfop));
1959430SRaymond.Chen@Sun.COM }
1969430SRaymond.Chen@Sun.COM 
1979430SRaymond.Chen@Sun.COM 
1989430SRaymond.Chen@Sun.COM /*
1999430SRaymond.Chen@Sun.COM  * hwarc_info:
2009430SRaymond.Chen@Sun.COM  *	Get minor number, soft state structure, etc.
2019430SRaymond.Chen@Sun.COM  */
2029430SRaymond.Chen@Sun.COM /*ARGSUSED*/
2039430SRaymond.Chen@Sun.COM static int
hwarc_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)2049430SRaymond.Chen@Sun.COM hwarc_info(dev_info_t *dip, ddi_info_cmd_t infocmd,
2059430SRaymond.Chen@Sun.COM 			void *arg, void **result)
2069430SRaymond.Chen@Sun.COM {
2079430SRaymond.Chen@Sun.COM 	hwarc_state_t	*hrcp;
2089430SRaymond.Chen@Sun.COM 	int error = DDI_FAILURE;
2099430SRaymond.Chen@Sun.COM 
2109430SRaymond.Chen@Sun.COM 	switch (infocmd) {
2119430SRaymond.Chen@Sun.COM 	case DDI_INFO_DEVT2DEVINFO:
2129430SRaymond.Chen@Sun.COM 		*result = NULL;
2139430SRaymond.Chen@Sun.COM 		if ((hrcp = ddi_get_soft_state(hwarc_statep,
2149430SRaymond.Chen@Sun.COM 		    getminor((dev_t)arg))) != NULL) {
2159430SRaymond.Chen@Sun.COM 			*result = hrcp->hrc_dip;
2169430SRaymond.Chen@Sun.COM 		}
2179430SRaymond.Chen@Sun.COM 		error = (*result)? DDI_SUCCESS:DDI_FAILURE;
2189430SRaymond.Chen@Sun.COM 
2199430SRaymond.Chen@Sun.COM 		break;
2209430SRaymond.Chen@Sun.COM 	case DDI_INFO_DEVT2INSTANCE:
2219430SRaymond.Chen@Sun.COM 		*result = (void *)(uintptr_t)getminor((dev_t)arg);
2229430SRaymond.Chen@Sun.COM 		error = DDI_SUCCESS;
2239430SRaymond.Chen@Sun.COM 		break;
2249430SRaymond.Chen@Sun.COM 	default:
2259430SRaymond.Chen@Sun.COM 		break;
2269430SRaymond.Chen@Sun.COM 	}
2279430SRaymond.Chen@Sun.COM 
2289430SRaymond.Chen@Sun.COM 	return (error);
2299430SRaymond.Chen@Sun.COM }
2309430SRaymond.Chen@Sun.COM 
2319430SRaymond.Chen@Sun.COM 
2329430SRaymond.Chen@Sun.COM 
2339430SRaymond.Chen@Sun.COM /*
2349430SRaymond.Chen@Sun.COM  * hwarc_init_power_mgmt:
2359430SRaymond.Chen@Sun.COM  *	Initialize power management and remote wakeup functionality.
2369430SRaymond.Chen@Sun.COM  *	No mutex is necessary in this function as it's called only by attach.
2379430SRaymond.Chen@Sun.COM  */
2389430SRaymond.Chen@Sun.COM static void
hwarc_init_power_mgmt(hwarc_state_t * hrcp)2399430SRaymond.Chen@Sun.COM hwarc_init_power_mgmt(hwarc_state_t *hrcp)
2409430SRaymond.Chen@Sun.COM {
2419430SRaymond.Chen@Sun.COM 	hwarc_power_t	*hrcpm = NULL;
2429430SRaymond.Chen@Sun.COM 	uint_t		pwr_states;
2439430SRaymond.Chen@Sun.COM 
2449430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, hrcp->hrc_log_hdl,
2459430SRaymond.Chen@Sun.COM 	    "init_power_mgmt enter");
2469430SRaymond.Chen@Sun.COM 
2479430SRaymond.Chen@Sun.COM 	/* Put online before PM init as can get power managed afterward. */
2489430SRaymond.Chen@Sun.COM 	hrcp->hrc_dev_state = USB_DEV_ONLINE;
2499430SRaymond.Chen@Sun.COM 
2509430SRaymond.Chen@Sun.COM 	/* Allocate the state structure */
2519430SRaymond.Chen@Sun.COM 	hrcpm = kmem_zalloc(sizeof (hwarc_power_t), KM_SLEEP);
2529430SRaymond.Chen@Sun.COM 
2539430SRaymond.Chen@Sun.COM 	hrcpm->hrc_state 		= hrcp;
2549430SRaymond.Chen@Sun.COM 	hrcpm->hrc_pm_capabilities 	= 0;
2559430SRaymond.Chen@Sun.COM 	hrcpm->hrc_current_power 	= USB_DEV_OS_FULL_PWR;
2569430SRaymond.Chen@Sun.COM 
2579430SRaymond.Chen@Sun.COM 
2589430SRaymond.Chen@Sun.COM 	if (usb_create_pm_components(hrcp->hrc_dip, &pwr_states) ==
2599430SRaymond.Chen@Sun.COM 	    USB_SUCCESS) {
2609430SRaymond.Chen@Sun.COM 		hrcpm->hrc_pwr_states 	= (uint8_t)pwr_states;
2619430SRaymond.Chen@Sun.COM 
2629430SRaymond.Chen@Sun.COM 		if (usb_handle_remote_wakeup(hrcp->hrc_dip,
2639430SRaymond.Chen@Sun.COM 		    USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) {
2649430SRaymond.Chen@Sun.COM 			hrcpm->hrc_wakeup_enabled = 1;
2659430SRaymond.Chen@Sun.COM 		} else {
2669430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_PM,
2679430SRaymond.Chen@Sun.COM 			    hrcp->hrc_log_hdl, "hwarc_init_power_mgmt:"
2689430SRaymond.Chen@Sun.COM 			    " remote wakeup not supported");
2699430SRaymond.Chen@Sun.COM 		}
2709430SRaymond.Chen@Sun.COM 
2719430SRaymond.Chen@Sun.COM 	} else {
2729430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_PM, hrcp->hrc_log_hdl,
2739430SRaymond.Chen@Sun.COM 		    "hwarc_init_power_mgmt:created PM components failed");
2749430SRaymond.Chen@Sun.COM 	}
2759430SRaymond.Chen@Sun.COM 	mutex_enter(&hrcp->hrc_mutex);
2769430SRaymond.Chen@Sun.COM 	hrcp->hrc_pm = hrcpm;
2779430SRaymond.Chen@Sun.COM 	hwarc_pm_busy_component(hrcp);
2789430SRaymond.Chen@Sun.COM 	mutex_exit(&hrcp->hrc_mutex);
2799430SRaymond.Chen@Sun.COM 
2809430SRaymond.Chen@Sun.COM 	(void) pm_raise_power(
2819430SRaymond.Chen@Sun.COM 	    hrcp->hrc_dip, 0, USB_DEV_OS_FULL_PWR);
2829430SRaymond.Chen@Sun.COM 
2839430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, hrcp->hrc_log_hdl,
2849430SRaymond.Chen@Sun.COM 	    "hwarc_init_power_mgmt: end");
2859430SRaymond.Chen@Sun.COM }
2869430SRaymond.Chen@Sun.COM 
2879430SRaymond.Chen@Sun.COM 
2889430SRaymond.Chen@Sun.COM /*
2899430SRaymond.Chen@Sun.COM  * hwarc_destroy_power_mgmt:
2909430SRaymond.Chen@Sun.COM  *	Shut down and destroy power management and remote wakeup functionality.
2919430SRaymond.Chen@Sun.COM  */
2929430SRaymond.Chen@Sun.COM static void
hwarc_destroy_power_mgmt(hwarc_state_t * hrcp)2939430SRaymond.Chen@Sun.COM hwarc_destroy_power_mgmt(hwarc_state_t *hrcp)
2949430SRaymond.Chen@Sun.COM {
2959430SRaymond.Chen@Sun.COM 	hwarc_power_t	*pm;
2969430SRaymond.Chen@Sun.COM 
2979430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, hrcp->hrc_log_hdl,
2989430SRaymond.Chen@Sun.COM 	    "destroy_power_mgmt enter");
2999430SRaymond.Chen@Sun.COM 
3009430SRaymond.Chen@Sun.COM 	mutex_enter(&hrcp->hrc_mutex);
3019430SRaymond.Chen@Sun.COM 
3029430SRaymond.Chen@Sun.COM 	if ((pm = hrcp->hrc_pm) == NULL) {
3039430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_PM, hrcp->hrc_log_hdl,
3049430SRaymond.Chen@Sun.COM 		    "hwarc_destroy_power_mgmt:pm not supported");
3059430SRaymond.Chen@Sun.COM 		goto done;
3069430SRaymond.Chen@Sun.COM 	}
3079430SRaymond.Chen@Sun.COM 
3089430SRaymond.Chen@Sun.COM 	if (hrcp->hrc_dev_state == USB_DEV_DISCONNECTED) {
3099430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_PM, hrcp->hrc_log_hdl,
3109430SRaymond.Chen@Sun.COM 		    "hwarc_destroy_power_mgmt:device disconnected");
3119430SRaymond.Chen@Sun.COM 		goto done;
3129430SRaymond.Chen@Sun.COM 	}
3139430SRaymond.Chen@Sun.COM 
3149430SRaymond.Chen@Sun.COM 	hwarc_pm_busy_component(hrcp);
3159430SRaymond.Chen@Sun.COM 
3169430SRaymond.Chen@Sun.COM 	mutex_exit(&hrcp->hrc_mutex);
3179430SRaymond.Chen@Sun.COM 
3189430SRaymond.Chen@Sun.COM 	if (pm->hrc_wakeup_enabled) {
3199430SRaymond.Chen@Sun.COM 
3209430SRaymond.Chen@Sun.COM 		/* First bring the device to full power */
3219430SRaymond.Chen@Sun.COM 		(void) pm_raise_power(hrcp->hrc_dip, 0, USB_DEV_OS_FULL_PWR);
3229430SRaymond.Chen@Sun.COM 		if (usb_handle_remote_wakeup(hrcp->hrc_dip,
3239430SRaymond.Chen@Sun.COM 		    USB_REMOTE_WAKEUP_DISABLE) != USB_SUCCESS) {
3249430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_PM,
3259430SRaymond.Chen@Sun.COM 			    hrcp->hrc_log_hdl, "hwarc_destroy_power_mgmt: "
3269430SRaymond.Chen@Sun.COM 			    "Error disabling rmt wakeup");
3279430SRaymond.Chen@Sun.COM 		}
3289430SRaymond.Chen@Sun.COM 
3299430SRaymond.Chen@Sun.COM 	}
3309430SRaymond.Chen@Sun.COM 
3319430SRaymond.Chen@Sun.COM 	/*
3329430SRaymond.Chen@Sun.COM 	 * Since remote wakeup is disabled now,
3339430SRaymond.Chen@Sun.COM 	 * no one can raise power
3349430SRaymond.Chen@Sun.COM 	 * and get to device once power is lowered here.
3359430SRaymond.Chen@Sun.COM 	 */
3369430SRaymond.Chen@Sun.COM 	(void) pm_lower_power(hrcp->hrc_dip, 0, USB_DEV_OS_PWR_OFF);
3379430SRaymond.Chen@Sun.COM 
3389430SRaymond.Chen@Sun.COM 	mutex_enter(&hrcp->hrc_mutex);
3399430SRaymond.Chen@Sun.COM 
3409430SRaymond.Chen@Sun.COM 	hwarc_pm_idle_component(hrcp);
3419430SRaymond.Chen@Sun.COM 
3429430SRaymond.Chen@Sun.COM done:
3439430SRaymond.Chen@Sun.COM 	if (pm) {
3449430SRaymond.Chen@Sun.COM 		kmem_free(pm, sizeof (hwarc_power_t));
3459430SRaymond.Chen@Sun.COM 
3469430SRaymond.Chen@Sun.COM 		hrcp->hrc_pm = NULL;
3479430SRaymond.Chen@Sun.COM 	}
3489430SRaymond.Chen@Sun.COM 	mutex_exit(&hrcp->hrc_mutex);
3499430SRaymond.Chen@Sun.COM }
3509430SRaymond.Chen@Sun.COM 
3519430SRaymond.Chen@Sun.COM 
3529430SRaymond.Chen@Sun.COM static void
hwarc_pm_busy_component(hwarc_state_t * hrcp)3539430SRaymond.Chen@Sun.COM hwarc_pm_busy_component(hwarc_state_t *hrcp)
3549430SRaymond.Chen@Sun.COM {
3559430SRaymond.Chen@Sun.COM 	ASSERT(mutex_owned(&hrcp->hrc_mutex));
3569430SRaymond.Chen@Sun.COM 
3579430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, hrcp->hrc_log_hdl,
3589430SRaymond.Chen@Sun.COM 	    "hwarc_pm_busy_component: enter");
3599430SRaymond.Chen@Sun.COM 	if (hrcp->hrc_pm == NULL) {
3609430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L4(PRINT_MASK_PM, hrcp->hrc_log_hdl,
3619430SRaymond.Chen@Sun.COM 		    "hwarc_pm_busy_component: pm not supported, return");
3629430SRaymond.Chen@Sun.COM 
3639430SRaymond.Chen@Sun.COM 		return;
3649430SRaymond.Chen@Sun.COM 	}
3659430SRaymond.Chen@Sun.COM 
3669430SRaymond.Chen@Sun.COM 	hrcp->hrc_pm->hrc_pm_busy++;
3679430SRaymond.Chen@Sun.COM 
3689430SRaymond.Chen@Sun.COM 	mutex_exit(&hrcp->hrc_mutex);
3699430SRaymond.Chen@Sun.COM 	if (pm_busy_component(hrcp->hrc_dip, 0) !=
3709430SRaymond.Chen@Sun.COM 	    DDI_SUCCESS) {
3719430SRaymond.Chen@Sun.COM 		mutex_enter(&hrcp->hrc_mutex);
3729430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_PM, hrcp->hrc_log_hdl,
3739430SRaymond.Chen@Sun.COM 		    "hwarc_pm_busy_component: pm busy fail, hrc_pm_busy=%d",
3749430SRaymond.Chen@Sun.COM 		    hrcp->hrc_pm->hrc_pm_busy);
3759430SRaymond.Chen@Sun.COM 
3769430SRaymond.Chen@Sun.COM 		hrcp->hrc_pm->hrc_pm_busy--;
3779430SRaymond.Chen@Sun.COM 		mutex_exit(&hrcp->hrc_mutex);
3789430SRaymond.Chen@Sun.COM 	}
3799430SRaymond.Chen@Sun.COM 	mutex_enter(&hrcp->hrc_mutex);
3809430SRaymond.Chen@Sun.COM 
3819430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, hrcp->hrc_log_hdl,
3829430SRaymond.Chen@Sun.COM 	    "hwarc_pm_busy_component: exit");
3839430SRaymond.Chen@Sun.COM }
3849430SRaymond.Chen@Sun.COM 
3859430SRaymond.Chen@Sun.COM 
3869430SRaymond.Chen@Sun.COM static void
hwarc_pm_idle_component(hwarc_state_t * hrcp)3879430SRaymond.Chen@Sun.COM hwarc_pm_idle_component(hwarc_state_t *hrcp)
3889430SRaymond.Chen@Sun.COM {
3899430SRaymond.Chen@Sun.COM 	ASSERT(mutex_owned(&hrcp->hrc_mutex));
3909430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, hrcp->hrc_log_hdl,
3919430SRaymond.Chen@Sun.COM 	    "hwarc_pm_idle_component: enter");
3929430SRaymond.Chen@Sun.COM 
3939430SRaymond.Chen@Sun.COM 	if (hrcp->hrc_pm == NULL) {
3949430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L4(PRINT_MASK_PM, hrcp->hrc_log_hdl,
3959430SRaymond.Chen@Sun.COM 		    "hwarc_pm_idle_component: pm not supported");
3969430SRaymond.Chen@Sun.COM 
3979430SRaymond.Chen@Sun.COM 		return;
3989430SRaymond.Chen@Sun.COM 	}
3999430SRaymond.Chen@Sun.COM 
4009430SRaymond.Chen@Sun.COM 	mutex_exit(&hrcp->hrc_mutex);
4019430SRaymond.Chen@Sun.COM 	if (pm_idle_component(hrcp->hrc_dip, 0) == DDI_SUCCESS) {
4029430SRaymond.Chen@Sun.COM 		mutex_enter(&hrcp->hrc_mutex);
4039430SRaymond.Chen@Sun.COM 		ASSERT(hrcp->hrc_pm->hrc_pm_busy > 0);
4049430SRaymond.Chen@Sun.COM 		hrcp->hrc_pm->hrc_pm_busy--;
4059430SRaymond.Chen@Sun.COM 		mutex_exit(&hrcp->hrc_mutex);
4069430SRaymond.Chen@Sun.COM 	}
4079430SRaymond.Chen@Sun.COM 	mutex_enter(&hrcp->hrc_mutex);
4089430SRaymond.Chen@Sun.COM 
4099430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_PM, hrcp->hrc_log_hdl,
4109430SRaymond.Chen@Sun.COM 	    "hwarc_pm_idle_component: %d", hrcp->hrc_pm->hrc_pm_busy);
4119430SRaymond.Chen@Sun.COM 
4129430SRaymond.Chen@Sun.COM }
4139430SRaymond.Chen@Sun.COM 
4149430SRaymond.Chen@Sun.COM 
4159430SRaymond.Chen@Sun.COM /*
4169430SRaymond.Chen@Sun.COM  * hwarc_pwrlvl0:
4179430SRaymond.Chen@Sun.COM  * Functions to handle power transition for OS levels 0 -> 3
4189430SRaymond.Chen@Sun.COM  */
4199430SRaymond.Chen@Sun.COM static int
hwarc_pwrlvl0(hwarc_state_t * hrcp)4209430SRaymond.Chen@Sun.COM hwarc_pwrlvl0(hwarc_state_t *hrcp)
4219430SRaymond.Chen@Sun.COM {
4229430SRaymond.Chen@Sun.COM 	int rval;
4239430SRaymond.Chen@Sun.COM 
4249430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_PM, hrcp->hrc_log_hdl,
4259430SRaymond.Chen@Sun.COM 	    "hwarc_pwrlvl0, dev_state: %x", hrcp->hrc_dev_state);
4269430SRaymond.Chen@Sun.COM 
4279430SRaymond.Chen@Sun.COM 	switch (hrcp->hrc_dev_state) {
4289430SRaymond.Chen@Sun.COM 		case USB_DEV_ONLINE:
4299430SRaymond.Chen@Sun.COM 			/* Deny the powerdown request if the device is busy */
4309430SRaymond.Chen@Sun.COM 			if (hrcp->hrc_pm->hrc_pm_busy != 0) {
4319430SRaymond.Chen@Sun.COM 				USB_DPRINTF_L2(PRINT_MASK_PM, hrcp->hrc_log_hdl,
4329430SRaymond.Chen@Sun.COM 				    "hwarc_pwrlvl0: hrc_pm_busy");
4339430SRaymond.Chen@Sun.COM 
4349430SRaymond.Chen@Sun.COM 				return (USB_FAILURE);
4359430SRaymond.Chen@Sun.COM 			}
4369430SRaymond.Chen@Sun.COM 
4379430SRaymond.Chen@Sun.COM 			/* Close the interrupt pipe */
4389430SRaymond.Chen@Sun.COM 			mutex_exit(&hrcp->hrc_mutex);
4399430SRaymond.Chen@Sun.COM 			hwarc_close_intr_pipe(hrcp);
4409430SRaymond.Chen@Sun.COM 			mutex_enter(&hrcp->hrc_mutex);
4419430SRaymond.Chen@Sun.COM 
4429430SRaymond.Chen@Sun.COM 			/* Issue USB D3 command to the device here */
4439430SRaymond.Chen@Sun.COM 			rval = usb_set_device_pwrlvl3(hrcp->hrc_dip);
4449430SRaymond.Chen@Sun.COM 			ASSERT(rval == USB_SUCCESS);
4459430SRaymond.Chen@Sun.COM 
4469430SRaymond.Chen@Sun.COM 			hrcp->hrc_dev_state = USB_DEV_PWRED_DOWN;
4479430SRaymond.Chen@Sun.COM 			hrcp->hrc_pm->hrc_current_power = USB_DEV_OS_PWR_OFF;
4489430SRaymond.Chen@Sun.COM 
4499430SRaymond.Chen@Sun.COM 		/* FALLTHRU */
4509430SRaymond.Chen@Sun.COM 		case USB_DEV_DISCONNECTED:
4519430SRaymond.Chen@Sun.COM 		case USB_DEV_SUSPENDED:
4529430SRaymond.Chen@Sun.COM 
4539430SRaymond.Chen@Sun.COM 			return (USB_SUCCESS);
4549430SRaymond.Chen@Sun.COM 
4559430SRaymond.Chen@Sun.COM 		case USB_DEV_PWRED_DOWN:
4569430SRaymond.Chen@Sun.COM 		default:
4579430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_PM, hrcp->hrc_log_hdl,
4589430SRaymond.Chen@Sun.COM 			    "hwarc_pwrlvl0: illegal dev state");
4599430SRaymond.Chen@Sun.COM 
4609430SRaymond.Chen@Sun.COM 			return (USB_FAILURE);
4619430SRaymond.Chen@Sun.COM 	}
4629430SRaymond.Chen@Sun.COM }
4639430SRaymond.Chen@Sun.COM 
4649430SRaymond.Chen@Sun.COM 
4659430SRaymond.Chen@Sun.COM /*
4669430SRaymond.Chen@Sun.COM  * hwarc_pwrlvl1:
4679430SRaymond.Chen@Sun.COM  *	Functions to handle power transition to OS levels -> 2
4689430SRaymond.Chen@Sun.COM  */
4699430SRaymond.Chen@Sun.COM static int
hwarc_pwrlvl1(hwarc_state_t * hrcp)4709430SRaymond.Chen@Sun.COM hwarc_pwrlvl1(hwarc_state_t *hrcp)
4719430SRaymond.Chen@Sun.COM {
4729430SRaymond.Chen@Sun.COM 	int	rval;
4739430SRaymond.Chen@Sun.COM 
4749430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_PM, hrcp->hrc_log_hdl, "hwarc_pwrlvl1");
4759430SRaymond.Chen@Sun.COM 
4769430SRaymond.Chen@Sun.COM 	/* Issue USB D2 command to the device here */
4779430SRaymond.Chen@Sun.COM 	rval = usb_set_device_pwrlvl2(hrcp->hrc_dip);
4789430SRaymond.Chen@Sun.COM 	ASSERT(rval == USB_SUCCESS);
4799430SRaymond.Chen@Sun.COM 
4809430SRaymond.Chen@Sun.COM 	return (USB_FAILURE);
4819430SRaymond.Chen@Sun.COM }
4829430SRaymond.Chen@Sun.COM 
4839430SRaymond.Chen@Sun.COM 
4849430SRaymond.Chen@Sun.COM /*
4859430SRaymond.Chen@Sun.COM  * hwarc_pwrlvl2:
4869430SRaymond.Chen@Sun.COM  *	Functions to handle power transition to OS levels -> 1
4879430SRaymond.Chen@Sun.COM  */
4889430SRaymond.Chen@Sun.COM static int
hwarc_pwrlvl2(hwarc_state_t * hrcp)4899430SRaymond.Chen@Sun.COM hwarc_pwrlvl2(hwarc_state_t *hrcp)
4909430SRaymond.Chen@Sun.COM {
4919430SRaymond.Chen@Sun.COM 	int	rval;
4929430SRaymond.Chen@Sun.COM 
4939430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_PM, hrcp->hrc_log_hdl,
4949430SRaymond.Chen@Sun.COM 	    "hwarc_pwrlvl2, dev_stat=%d", hrcp->hrc_dev_state);
4959430SRaymond.Chen@Sun.COM 
4969430SRaymond.Chen@Sun.COM 	/* Issue USB D1 command to the device here */
4979430SRaymond.Chen@Sun.COM 	rval = usb_set_device_pwrlvl1(hrcp->hrc_dip);
4989430SRaymond.Chen@Sun.COM 	ASSERT(rval == USB_SUCCESS);
4999430SRaymond.Chen@Sun.COM 
5009430SRaymond.Chen@Sun.COM 	return (USB_FAILURE);
5019430SRaymond.Chen@Sun.COM }
5029430SRaymond.Chen@Sun.COM 
5039430SRaymond.Chen@Sun.COM 
5049430SRaymond.Chen@Sun.COM /*
5059430SRaymond.Chen@Sun.COM  * hwarc_pwrlvl3:
5069430SRaymond.Chen@Sun.COM  *	Functions to handle power transition to OS level -> 0
5079430SRaymond.Chen@Sun.COM  */
5089430SRaymond.Chen@Sun.COM static int
hwarc_pwrlvl3(hwarc_state_t * hrcp)5099430SRaymond.Chen@Sun.COM hwarc_pwrlvl3(hwarc_state_t *hrcp)
5109430SRaymond.Chen@Sun.COM {
5119430SRaymond.Chen@Sun.COM 
5129430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_PM, hrcp->hrc_log_hdl,
5139430SRaymond.Chen@Sun.COM 	    "hwarc_pwrlvl3, dev_stat=%d", hrcp->hrc_dev_state);
5149430SRaymond.Chen@Sun.COM 
5159430SRaymond.Chen@Sun.COM 	switch (hrcp->hrc_dev_state) {
5169430SRaymond.Chen@Sun.COM 		case USB_DEV_PWRED_DOWN:
5179430SRaymond.Chen@Sun.COM 			/* Issue USB D0 command to the device here */
5189430SRaymond.Chen@Sun.COM 			(void) usb_set_device_pwrlvl0(hrcp->hrc_dip);
5199430SRaymond.Chen@Sun.COM 
5209430SRaymond.Chen@Sun.COM 			mutex_exit(&hrcp->hrc_mutex);
5219430SRaymond.Chen@Sun.COM 			if (hwarc_open_intr_pipe(hrcp) != USB_SUCCESS) {
5229430SRaymond.Chen@Sun.COM 				mutex_enter(&hrcp->hrc_mutex);
5239430SRaymond.Chen@Sun.COM 
5249430SRaymond.Chen@Sun.COM 				return (USB_FAILURE);
5259430SRaymond.Chen@Sun.COM 			}
5269430SRaymond.Chen@Sun.COM 			mutex_enter(&hrcp->hrc_mutex);
5279430SRaymond.Chen@Sun.COM 
5289430SRaymond.Chen@Sun.COM 			/* Todo: Reset device or not */
5299430SRaymond.Chen@Sun.COM 			hrcp->hrc_dev_state 		= USB_DEV_ONLINE;
5309430SRaymond.Chen@Sun.COM 			hrcp->hrc_pm->hrc_current_power = USB_DEV_OS_FULL_PWR;
5319430SRaymond.Chen@Sun.COM 
5329430SRaymond.Chen@Sun.COM 			/* FALLTHRU */
5339430SRaymond.Chen@Sun.COM 		case USB_DEV_ONLINE:
5349430SRaymond.Chen@Sun.COM 			/* we are already in full power */
5359430SRaymond.Chen@Sun.COM 			/* FALLTHRU */
5369430SRaymond.Chen@Sun.COM 		case USB_DEV_DISCONNECTED:
5379430SRaymond.Chen@Sun.COM 		case USB_DEV_SUSPENDED:
5389430SRaymond.Chen@Sun.COM 			/*
5399430SRaymond.Chen@Sun.COM 			 * PM framework tries to put us in full power
5409430SRaymond.Chen@Sun.COM 			 * during system shutdown. If we are disconnected/cpr'ed
5419430SRaymond.Chen@Sun.COM 			 * return success anyways
5429430SRaymond.Chen@Sun.COM 			 */
5439430SRaymond.Chen@Sun.COM 
5449430SRaymond.Chen@Sun.COM 			return (USB_SUCCESS);
5459430SRaymond.Chen@Sun.COM 
5469430SRaymond.Chen@Sun.COM 		default:
5479430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_PM, hrcp->hrc_log_hdl,
5489430SRaymond.Chen@Sun.COM 			    "hwarc_pwrlvl3: illegal dev state");
5499430SRaymond.Chen@Sun.COM 
5509430SRaymond.Chen@Sun.COM 			return (USB_FAILURE);
5519430SRaymond.Chen@Sun.COM 	}
5529430SRaymond.Chen@Sun.COM }
5539430SRaymond.Chen@Sun.COM 
5549430SRaymond.Chen@Sun.COM /* Init dev inst */
5559430SRaymond.Chen@Sun.COM static void
hwarc_init_devinst(dev_info_t * dip,hwarc_state_t * hrcp)5569430SRaymond.Chen@Sun.COM hwarc_init_devinst(dev_info_t *dip, hwarc_state_t *hrcp)
5579430SRaymond.Chen@Sun.COM {
5589430SRaymond.Chen@Sun.COM 	char	*devinst;
5599430SRaymond.Chen@Sun.COM 	int	devinstlen;
5609430SRaymond.Chen@Sun.COM 
5619430SRaymond.Chen@Sun.COM 	hrcp->hrc_dip	  = dip;
5629430SRaymond.Chen@Sun.COM 	hrcp->hrc_log_hdl = usb_alloc_log_hdl(dip,
5639430SRaymond.Chen@Sun.COM 	    "hwarc", &hwarc_errlevel,
5649430SRaymond.Chen@Sun.COM 	    &hwarc_errmask, &hwarc_instance_debug, 0);
5659430SRaymond.Chen@Sun.COM 
5669430SRaymond.Chen@Sun.COM 	devinst = kmem_zalloc(USB_MAXSTRINGLEN, KM_SLEEP);
5679430SRaymond.Chen@Sun.COM 	devinstlen = snprintf(devinst, USB_MAXSTRINGLEN, "%s%d: ",
5689430SRaymond.Chen@Sun.COM 	    ddi_driver_name(dip), ddi_get_instance(dip));
5699430SRaymond.Chen@Sun.COM 
5709430SRaymond.Chen@Sun.COM 	hrcp->hrc_devinst = kmem_zalloc(devinstlen + 1, KM_SLEEP);
5719430SRaymond.Chen@Sun.COM 	(void) strncpy(hrcp->hrc_devinst, devinst, devinstlen);
5729430SRaymond.Chen@Sun.COM 
5739430SRaymond.Chen@Sun.COM 	kmem_free(devinst, USB_MAXSTRINGLEN);
5749430SRaymond.Chen@Sun.COM }
5759430SRaymond.Chen@Sun.COM 
5769430SRaymond.Chen@Sun.COM /* init endpoints */
5779430SRaymond.Chen@Sun.COM static int
hwarc_init_ep(dev_info_t * dip,hwarc_state_t * hrcp)5789430SRaymond.Chen@Sun.COM hwarc_init_ep(dev_info_t *dip, hwarc_state_t *hrcp)
5799430SRaymond.Chen@Sun.COM {
5809430SRaymond.Chen@Sun.COM 	int status = USB_SUCCESS;
5819430SRaymond.Chen@Sun.COM 	usb_ep_data_t	*ep_datap;
5829430SRaymond.Chen@Sun.COM 	if ((status = usb_client_attach(dip, USBDRV_VERSION, 0)) !=
5839430SRaymond.Chen@Sun.COM 	    USB_SUCCESS) {
5849430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hrcp->hrc_log_hdl,
5859430SRaymond.Chen@Sun.COM 		    "hwarc_init_ep: usb_client_attach failed"
5869430SRaymond.Chen@Sun.COM 		    " error code = %d", status);
5879430SRaymond.Chen@Sun.COM 		goto done;
5889430SRaymond.Chen@Sun.COM 	}
5899430SRaymond.Chen@Sun.COM 
5909430SRaymond.Chen@Sun.COM 	if ((status = usb_get_dev_data(dip, &hrcp->hrc_reg, USB_PARSE_LVL_ALL,
5919430SRaymond.Chen@Sun.COM 	    0)) != USB_SUCCESS) {
5929430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hrcp->hrc_log_hdl,
5939430SRaymond.Chen@Sun.COM 		    "hwarc_init_ep: usb_get_dev_data failed"
5949430SRaymond.Chen@Sun.COM 		    "error code = %d", status);
5959430SRaymond.Chen@Sun.COM 		goto done;
5969430SRaymond.Chen@Sun.COM 	}
5979430SRaymond.Chen@Sun.COM 	hrcp->hrc_if_descr =
5989430SRaymond.Chen@Sun.COM 	    &hrcp->hrc_reg->dev_curr_cfg->cfg_if[hrcp->hrc_reg->dev_curr_if];
5999430SRaymond.Chen@Sun.COM 	hrcp->hrc_default_ph = hrcp->hrc_reg->dev_default_ph;
6009430SRaymond.Chen@Sun.COM 
6019430SRaymond.Chen@Sun.COM 	/*
6029430SRaymond.Chen@Sun.COM 	 * Get the descriptor for an intr pipe at alt 0 of current interface.
6039430SRaymond.Chen@Sun.COM 	 * This will be used later to open the pipe.
6049430SRaymond.Chen@Sun.COM 	 */
6059430SRaymond.Chen@Sun.COM 	if ((ep_datap = usb_lookup_ep_data(dip, hrcp->hrc_reg,
6069430SRaymond.Chen@Sun.COM 	    hrcp->hrc_reg->dev_curr_if, 0, 0,
6079430SRaymond.Chen@Sun.COM 	    USB_EP_ATTR_INTR, USB_EP_DIR_IN)) == NULL) {
6089430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hrcp->hrc_log_hdl,
6099430SRaymond.Chen@Sun.COM 		    "hwarc_init_ep: Error getting intr endpoint descriptor");
6109430SRaymond.Chen@Sun.COM 		status = USB_FAILURE;
6119430SRaymond.Chen@Sun.COM 		goto done;
6129430SRaymond.Chen@Sun.COM 	}
6139430SRaymond.Chen@Sun.COM 	hrcp->hrc_intr_ep_descr = ep_datap->ep_descr;
6149430SRaymond.Chen@Sun.COM 
6159430SRaymond.Chen@Sun.COM done:
6169430SRaymond.Chen@Sun.COM 
6179430SRaymond.Chen@Sun.COM 	return (status);
6189430SRaymond.Chen@Sun.COM }
6199430SRaymond.Chen@Sun.COM /* init mutex */
6209430SRaymond.Chen@Sun.COM static void
hwarc_init_mutex(hwarc_state_t * hrcp)6219430SRaymond.Chen@Sun.COM hwarc_init_mutex(hwarc_state_t *hrcp) {
6229430SRaymond.Chen@Sun.COM 	mutex_init(&hrcp->hrc_mutex, NULL, MUTEX_DRIVER,
6239430SRaymond.Chen@Sun.COM 	    hrcp->hrc_reg->dev_iblock_cookie);
6249430SRaymond.Chen@Sun.COM 
6259430SRaymond.Chen@Sun.COM 	cv_init(&hrcp->hrc_serial_cv, NULL, CV_DRIVER, NULL);
6269430SRaymond.Chen@Sun.COM 	hrcp->hrc_serial_inuse = B_FALSE;
6279430SRaymond.Chen@Sun.COM 
6289430SRaymond.Chen@Sun.COM 	hrcp->hrc_locks_initialized = B_TRUE;
6299430SRaymond.Chen@Sun.COM }
6309430SRaymond.Chen@Sun.COM /*
6319430SRaymond.Chen@Sun.COM  * hwarc_attach:
6329430SRaymond.Chen@Sun.COM  *	Attach or resume.
6339430SRaymond.Chen@Sun.COM  *
6349430SRaymond.Chen@Sun.COM  *	For attach, initialize state and device, including:
6359430SRaymond.Chen@Sun.COM  *		state variables, locks, device node
6369430SRaymond.Chen@Sun.COM  *		device registration with system
6379430SRaymond.Chen@Sun.COM  *		power management, hotplugging
6389430SRaymond.Chen@Sun.COM  *	For resume, restore device and state
6399430SRaymond.Chen@Sun.COM  */
6409430SRaymond.Chen@Sun.COM static int
hwarc_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)6419430SRaymond.Chen@Sun.COM hwarc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
6429430SRaymond.Chen@Sun.COM {
6439430SRaymond.Chen@Sun.COM 	int			instance = ddi_get_instance(dip);
6449430SRaymond.Chen@Sun.COM 	hwarc_state_t		*hrcp = NULL;
6459430SRaymond.Chen@Sun.COM 
6469430SRaymond.Chen@Sun.COM 	switch (cmd) {
6479430SRaymond.Chen@Sun.COM 		case DDI_ATTACH:
6489430SRaymond.Chen@Sun.COM 			break;
6499430SRaymond.Chen@Sun.COM 
6509430SRaymond.Chen@Sun.COM 		case DDI_RESUME:
6519430SRaymond.Chen@Sun.COM 			hwarc_cpr_resume(dip);
6529430SRaymond.Chen@Sun.COM 			/*
6539430SRaymond.Chen@Sun.COM 			 * Always return success to work around enumeration
6549430SRaymond.Chen@Sun.COM 			 * failures.This works around an issue where devices
6559430SRaymond.Chen@Sun.COM 			 * which are present before a suspend and absent upon
6569430SRaymond.Chen@Sun.COM 			 * resume could cause a system panic on resume.
6579430SRaymond.Chen@Sun.COM 			 */
6589430SRaymond.Chen@Sun.COM 
6599430SRaymond.Chen@Sun.COM 			return (DDI_SUCCESS);
6609430SRaymond.Chen@Sun.COM 		default:
6619430SRaymond.Chen@Sun.COM 			return (DDI_FAILURE);
6629430SRaymond.Chen@Sun.COM 	}
6639430SRaymond.Chen@Sun.COM 
6649430SRaymond.Chen@Sun.COM 	if (ddi_soft_state_zalloc(hwarc_statep, instance) == DDI_SUCCESS) {
6659430SRaymond.Chen@Sun.COM 		hrcp = ddi_get_soft_state(hwarc_statep, instance);
6669430SRaymond.Chen@Sun.COM 	}
6679430SRaymond.Chen@Sun.COM 	if (hrcp == NULL)  {
6689430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, NULL,
6699430SRaymond.Chen@Sun.COM 		    "hwarc_attach: get soft state failed for instance %d",
6709430SRaymond.Chen@Sun.COM 		    instance);
6719430SRaymond.Chen@Sun.COM 
6729430SRaymond.Chen@Sun.COM 		return (DDI_FAILURE);
6739430SRaymond.Chen@Sun.COM 	}
6749430SRaymond.Chen@Sun.COM 
6759430SRaymond.Chen@Sun.COM 	(void) hwarc_init_devinst(dip, hrcp);
6769430SRaymond.Chen@Sun.COM 
6779430SRaymond.Chen@Sun.COM 	if (hwarc_init_ep(dip, hrcp) != USB_SUCCESS) {
6789430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hrcp->hrc_log_hdl,
6799430SRaymond.Chen@Sun.COM 		    "attach: Error init usb data");
6809430SRaymond.Chen@Sun.COM 		goto fail;
6819430SRaymond.Chen@Sun.COM 	}
6829430SRaymond.Chen@Sun.COM 	hwarc_init_mutex(hrcp);
6839430SRaymond.Chen@Sun.COM 
6849430SRaymond.Chen@Sun.COM 	/* create minor node */
6859430SRaymond.Chen@Sun.COM 	if (ddi_create_minor_node(dip, name, S_IFCHR, instance,
6869430SRaymond.Chen@Sun.COM 	    NULL, 0) != DDI_SUCCESS) {
6879430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hrcp->hrc_log_hdl,
6889430SRaymond.Chen@Sun.COM 		    "attach: Error creating minor node");
6899430SRaymond.Chen@Sun.COM 		goto fail;
6909430SRaymond.Chen@Sun.COM 	}
6919430SRaymond.Chen@Sun.COM 
6929430SRaymond.Chen@Sun.COM 	/* initialize power management */
6939430SRaymond.Chen@Sun.COM 	hwarc_init_power_mgmt(hrcp);
6949430SRaymond.Chen@Sun.COM 
6959430SRaymond.Chen@Sun.COM 	if (usb_register_hotplug_cbs(dip, hwarc_disconnect_callback,
6969430SRaymond.Chen@Sun.COM 	    hwarc_reconnect_callback) != USB_SUCCESS) {
6979430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hrcp->hrc_log_hdl,
6989430SRaymond.Chen@Sun.COM 		    "attach: Error register hotplug cbs");
6999430SRaymond.Chen@Sun.COM 
7009430SRaymond.Chen@Sun.COM 		goto fail;
7019430SRaymond.Chen@Sun.COM 	}
7029430SRaymond.Chen@Sun.COM 
7039430SRaymond.Chen@Sun.COM 	/* register this device to uwba */
7049430SRaymond.Chen@Sun.COM 	uwb_dev_attach(dip, &hrcp->hrc_dev_hdl, 0, hwarc_send_cmd);
7059430SRaymond.Chen@Sun.COM 
7069430SRaymond.Chen@Sun.COM 	if (hwarc_open_intr_pipe(hrcp) != USB_SUCCESS) {
7079430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hrcp->hrc_log_hdl,
7089430SRaymond.Chen@Sun.COM 		    "attach: Error open intr pipe");
7099430SRaymond.Chen@Sun.COM 
7109430SRaymond.Chen@Sun.COM 		goto fail;
7119430SRaymond.Chen@Sun.COM 	}
7129430SRaymond.Chen@Sun.COM 
7139430SRaymond.Chen@Sun.COM 	/* reset device */
7149430SRaymond.Chen@Sun.COM 	if (hwarc_reset_device(hrcp) != USB_SUCCESS) {
7159430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hrcp->hrc_log_hdl,
7169430SRaymond.Chen@Sun.COM 		    "attach: Error reset deivce");
7179430SRaymond.Chen@Sun.COM 		goto fail;
7189430SRaymond.Chen@Sun.COM 	}
7199430SRaymond.Chen@Sun.COM 	/* init phy capabilities */
7209430SRaymond.Chen@Sun.COM 	if (hwarc_init_phy(hrcp) != USB_SUCCESS) {
7219430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hrcp->hrc_log_hdl,
7229430SRaymond.Chen@Sun.COM 		    "attach: Error get phy ie");
7239430SRaymond.Chen@Sun.COM 
7249430SRaymond.Chen@Sun.COM 		goto fail;
7259430SRaymond.Chen@Sun.COM 	}
7269430SRaymond.Chen@Sun.COM 	/* Report device */
7279430SRaymond.Chen@Sun.COM 	ddi_report_dev(dip);
7289430SRaymond.Chen@Sun.COM 
7299430SRaymond.Chen@Sun.COM 	mutex_enter(&hrcp->hrc_mutex);
7309430SRaymond.Chen@Sun.COM 	hwarc_pm_idle_component(hrcp);
7319430SRaymond.Chen@Sun.COM 	mutex_exit(&hrcp->hrc_mutex);
7329430SRaymond.Chen@Sun.COM 
7339430SRaymond.Chen@Sun.COM 	return (DDI_SUCCESS);
7349430SRaymond.Chen@Sun.COM 
7359430SRaymond.Chen@Sun.COM fail:
7369430SRaymond.Chen@Sun.COM 	(void) hwarc_cleanup(dip, hrcp);
7379430SRaymond.Chen@Sun.COM 
7389430SRaymond.Chen@Sun.COM 	return (DDI_FAILURE);
7399430SRaymond.Chen@Sun.COM }
7409430SRaymond.Chen@Sun.COM 
7419430SRaymond.Chen@Sun.COM 
7429430SRaymond.Chen@Sun.COM /*
7439430SRaymond.Chen@Sun.COM  * hwarc_detach:
7449430SRaymond.Chen@Sun.COM  *	detach or suspend driver instance
7459430SRaymond.Chen@Sun.COM  *
7469430SRaymond.Chen@Sun.COM  * Note: in detach, only contention threads is from pm and disconnnect.
7479430SRaymond.Chen@Sun.COM  */
7489430SRaymond.Chen@Sun.COM static int
hwarc_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)7499430SRaymond.Chen@Sun.COM hwarc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
7509430SRaymond.Chen@Sun.COM {
7519430SRaymond.Chen@Sun.COM 	int		rval	= USB_SUCCESS;
7529430SRaymond.Chen@Sun.COM 	hwarc_state_t	*hrcp	=
7539430SRaymond.Chen@Sun.COM 	    ddi_get_soft_state(hwarc_statep, ddi_get_instance(dip));
7549430SRaymond.Chen@Sun.COM 
7559430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_ATTA, hrcp->hrc_log_hdl,
7569430SRaymond.Chen@Sun.COM 	    "hwarc_detach: enter for detach, cmd = %d", cmd);
7579430SRaymond.Chen@Sun.COM 
7589430SRaymond.Chen@Sun.COM 
7599430SRaymond.Chen@Sun.COM 	switch (cmd) {
7609430SRaymond.Chen@Sun.COM 		case DDI_DETACH:
7619430SRaymond.Chen@Sun.COM 
7629430SRaymond.Chen@Sun.COM 			rval = hwarc_cleanup(dip, hrcp);
7639430SRaymond.Chen@Sun.COM 
7649430SRaymond.Chen@Sun.COM 			break;
7659430SRaymond.Chen@Sun.COM 		case DDI_SUSPEND:
7669430SRaymond.Chen@Sun.COM 
7679430SRaymond.Chen@Sun.COM 			rval = hwarc_cpr_suspend(dip);
7689430SRaymond.Chen@Sun.COM 		default:
7699430SRaymond.Chen@Sun.COM 
7709430SRaymond.Chen@Sun.COM 			break;
7719430SRaymond.Chen@Sun.COM 	}
7729430SRaymond.Chen@Sun.COM 
7739430SRaymond.Chen@Sun.COM 	return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
7749430SRaymond.Chen@Sun.COM }
7759430SRaymond.Chen@Sun.COM 
7769430SRaymond.Chen@Sun.COM 
7779430SRaymond.Chen@Sun.COM /*
7789430SRaymond.Chen@Sun.COM  * hwarc_cleanup:
7799430SRaymond.Chen@Sun.COM  *	clean up the driver state for detach
7809430SRaymond.Chen@Sun.COM  */
7819430SRaymond.Chen@Sun.COM static int
hwarc_cleanup(dev_info_t * dip,hwarc_state_t * hrcp)7829430SRaymond.Chen@Sun.COM hwarc_cleanup(dev_info_t *dip, hwarc_state_t *hrcp)
7839430SRaymond.Chen@Sun.COM {
7849430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L2(PRINT_MASK_ATTA, hrcp->hrc_log_hdl, "Cleanup: enter");
7859430SRaymond.Chen@Sun.COM 
7869430SRaymond.Chen@Sun.COM 	if (hrcp->hrc_locks_initialized) {
7879430SRaymond.Chen@Sun.COM 
7889430SRaymond.Chen@Sun.COM 		(void) uwb_stop_beacon(dip);
7899430SRaymond.Chen@Sun.COM 		/* This must be done 1st to prevent more events from coming. */
7909430SRaymond.Chen@Sun.COM 		usb_unregister_hotplug_cbs(dip);
7919430SRaymond.Chen@Sun.COM 
7929430SRaymond.Chen@Sun.COM 		hwarc_close_intr_pipe(hrcp);
7939430SRaymond.Chen@Sun.COM 
7949430SRaymond.Chen@Sun.COM 		/*
7959430SRaymond.Chen@Sun.COM 		 * At this point, no new activity can be initiated. The driver
7969430SRaymond.Chen@Sun.COM 		 * has disabled hotplug callbacks. The Solaris framework has
7979430SRaymond.Chen@Sun.COM 		 * disabled new opens on a device being detached, and does not
7989430SRaymond.Chen@Sun.COM 		 * allow detaching an open device.
7999430SRaymond.Chen@Sun.COM 		 *
8009430SRaymond.Chen@Sun.COM 		 * The following ensures that all driver activity has drained.
8019430SRaymond.Chen@Sun.COM 		 */
8029430SRaymond.Chen@Sun.COM 		mutex_enter(&hrcp->hrc_mutex);
8039430SRaymond.Chen@Sun.COM 		(void) hwarc_serialize_access(hrcp, HWARC_SER_NOSIG);
8049430SRaymond.Chen@Sun.COM 		hwarc_release_access(hrcp);
8059430SRaymond.Chen@Sun.COM 		mutex_exit(&hrcp->hrc_mutex);
8069430SRaymond.Chen@Sun.COM 
8079430SRaymond.Chen@Sun.COM 		/* All device activity has died down. */
8089430SRaymond.Chen@Sun.COM 		hwarc_destroy_power_mgmt(hrcp);
8099430SRaymond.Chen@Sun.COM 
8109430SRaymond.Chen@Sun.COM 		/* start dismantling */
8119430SRaymond.Chen@Sun.COM 		ddi_remove_minor_node(dip, NULL);
8129430SRaymond.Chen@Sun.COM 		cv_destroy(&hrcp->hrc_serial_cv);
8139430SRaymond.Chen@Sun.COM 		mutex_destroy(&hrcp->hrc_mutex);
8149430SRaymond.Chen@Sun.COM 	}
8159430SRaymond.Chen@Sun.COM 
8169430SRaymond.Chen@Sun.COM 	usb_client_detach(dip, hrcp->hrc_reg);
8179430SRaymond.Chen@Sun.COM 
8189430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_ATTA, hrcp->hrc_log_hdl, "Cleanup: end");
8199430SRaymond.Chen@Sun.COM 
8209430SRaymond.Chen@Sun.COM 	usb_free_log_hdl(hrcp->hrc_log_hdl);
8219430SRaymond.Chen@Sun.COM 
8229430SRaymond.Chen@Sun.COM 	uwb_dev_detach(hrcp->hrc_dev_hdl);
8239430SRaymond.Chen@Sun.COM 
8249430SRaymond.Chen@Sun.COM 	kmem_free(hrcp->hrc_devinst, strlen(hrcp->hrc_devinst) + 1);
8259430SRaymond.Chen@Sun.COM 
8269430SRaymond.Chen@Sun.COM 	ddi_soft_state_free(hwarc_statep, ddi_get_instance(dip));
8279430SRaymond.Chen@Sun.COM 	ddi_prop_remove_all(dip);
8289430SRaymond.Chen@Sun.COM 
8299430SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
8309430SRaymond.Chen@Sun.COM }
8319430SRaymond.Chen@Sun.COM 
8329430SRaymond.Chen@Sun.COM 
8339430SRaymond.Chen@Sun.COM 
8349430SRaymond.Chen@Sun.COM /*ARGSUSED*/
8359430SRaymond.Chen@Sun.COM static int
hwarc_open(dev_t * devp,int flag,int otyp,cred_t * cred_p)8369430SRaymond.Chen@Sun.COM hwarc_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
8379430SRaymond.Chen@Sun.COM {
8389430SRaymond.Chen@Sun.COM 	hwarc_state_t	*hrcp = NULL;
8399430SRaymond.Chen@Sun.COM 	int rval = 0;
8409430SRaymond.Chen@Sun.COM 
8419430SRaymond.Chen@Sun.COM 	hrcp = ddi_get_soft_state(hwarc_statep, getminor(*devp));
8429430SRaymond.Chen@Sun.COM 
8439430SRaymond.Chen@Sun.COM 	ASSERT(hrcp != NULL);
8449430SRaymond.Chen@Sun.COM 
8459430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_OPEN, hrcp->hrc_log_hdl, "hwarc_open: enter");
8469430SRaymond.Chen@Sun.COM 
8479430SRaymond.Chen@Sun.COM 	/*
8489430SRaymond.Chen@Sun.COM 	 * Keep it simple: one client at a time.
8499430SRaymond.Chen@Sun.COM 	 * Exclusive open only
8509430SRaymond.Chen@Sun.COM 	 */
8519430SRaymond.Chen@Sun.COM 	mutex_enter(&hrcp->hrc_mutex);
8529430SRaymond.Chen@Sun.COM 	/* exclusive open */
8539430SRaymond.Chen@Sun.COM 	if ((flag & FEXCL) && (hrcp->hrc_open_count > 0)) {
8549430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_OPEN, hrcp->hrc_log_hdl,
8559430SRaymond.Chen@Sun.COM 		    "hwarc_open failed, open count=%d", hrcp->hrc_open_count);
8569430SRaymond.Chen@Sun.COM 		mutex_exit(&hrcp->hrc_mutex);
8579430SRaymond.Chen@Sun.COM 
8589430SRaymond.Chen@Sun.COM 		return (EBUSY);
8599430SRaymond.Chen@Sun.COM 	}
8609430SRaymond.Chen@Sun.COM 
8619430SRaymond.Chen@Sun.COM 	if ((hrcp->hrc_dev_state == USB_DEV_DISCONNECTED) ||
8629430SRaymond.Chen@Sun.COM 	    (hrcp->hrc_dev_state == USB_DEV_SUSPENDED)) {
8639430SRaymond.Chen@Sun.COM 
8649430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_OPEN, hrcp->hrc_log_hdl,
8659430SRaymond.Chen@Sun.COM 		    "hwarc_open failed, dev_stat=%d", hrcp->hrc_dev_state);
8669430SRaymond.Chen@Sun.COM 		mutex_exit(&hrcp->hrc_mutex);
8679430SRaymond.Chen@Sun.COM 
8689430SRaymond.Chen@Sun.COM 		return (EIO);
8699430SRaymond.Chen@Sun.COM 	}
8709430SRaymond.Chen@Sun.COM 
8719430SRaymond.Chen@Sun.COM 	hrcp->hrc_open_count++;
8729430SRaymond.Chen@Sun.COM 
8739430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_OPEN, hrcp->hrc_log_hdl,
8749430SRaymond.Chen@Sun.COM 	    "hwarc_open, open count=%d", hrcp->hrc_open_count);
8759430SRaymond.Chen@Sun.COM 	if (hwarc_serialize_access(hrcp, HWARC_SER_SIG) == 0) {
8769430SRaymond.Chen@Sun.COM 		hrcp->hrc_open_count--;
8779430SRaymond.Chen@Sun.COM 		hwarc_release_access(hrcp);
8789430SRaymond.Chen@Sun.COM 		mutex_exit(&hrcp->hrc_mutex);
8799430SRaymond.Chen@Sun.COM 
8809430SRaymond.Chen@Sun.COM 		return (EINTR);
8819430SRaymond.Chen@Sun.COM 	}
8829430SRaymond.Chen@Sun.COM 
8839430SRaymond.Chen@Sun.COM 	hwarc_pm_busy_component(hrcp);
8849430SRaymond.Chen@Sun.COM 
8859430SRaymond.Chen@Sun.COM 	mutex_exit(&hrcp->hrc_mutex);
8869430SRaymond.Chen@Sun.COM 	(void) pm_raise_power(hrcp->hrc_dip, 0, USB_DEV_OS_FULL_PWR);
8879430SRaymond.Chen@Sun.COM 
8889430SRaymond.Chen@Sun.COM 	mutex_enter(&hrcp->hrc_mutex);
8899430SRaymond.Chen@Sun.COM 	/* Fail if device is no longer ready. */
8909430SRaymond.Chen@Sun.COM 	if (hrcp->hrc_dev_state != USB_DEV_ONLINE) {
8919430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_OPEN, hrcp->hrc_log_hdl,
8929430SRaymond.Chen@Sun.COM 		    "hwarc_open failed, dev_stat=%d", hrcp->hrc_dev_state);
8939430SRaymond.Chen@Sun.COM 		rval = EIO;
8949430SRaymond.Chen@Sun.COM 	}
8959430SRaymond.Chen@Sun.COM 	hwarc_release_access(hrcp);
8969430SRaymond.Chen@Sun.COM 	/* Device specific initialization goes here. */
8979430SRaymond.Chen@Sun.COM 	if (rval != 0) {
8989430SRaymond.Chen@Sun.COM 		hrcp->hrc_open_count--;
8999430SRaymond.Chen@Sun.COM 		hwarc_pm_idle_component(hrcp);
9009430SRaymond.Chen@Sun.COM 	}
9019430SRaymond.Chen@Sun.COM 	mutex_exit(&hrcp->hrc_mutex);
9029430SRaymond.Chen@Sun.COM 
9039430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_OPEN, hrcp->hrc_log_hdl, "hwarc_open: leave");
9049430SRaymond.Chen@Sun.COM 
9059430SRaymond.Chen@Sun.COM 	return (0);
9069430SRaymond.Chen@Sun.COM }
9079430SRaymond.Chen@Sun.COM 
9089430SRaymond.Chen@Sun.COM 
9099430SRaymond.Chen@Sun.COM /*ARGSUSED*/
9109430SRaymond.Chen@Sun.COM static int
hwarc_close(dev_t dev,int flag,int otyp,cred_t * cred_p)9119430SRaymond.Chen@Sun.COM hwarc_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
9129430SRaymond.Chen@Sun.COM {
9139430SRaymond.Chen@Sun.COM 	hwarc_state_t	*hrcp =
9149430SRaymond.Chen@Sun.COM 	    ddi_get_soft_state(hwarc_statep, getminor(dev));
9159430SRaymond.Chen@Sun.COM 
9169430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, hrcp->hrc_log_hdl,
9179430SRaymond.Chen@Sun.COM 	    "hwarc_close: enter");
9189430SRaymond.Chen@Sun.COM 
9199430SRaymond.Chen@Sun.COM 	mutex_enter(&hrcp->hrc_mutex);
9209430SRaymond.Chen@Sun.COM 	(void) hwarc_serialize_access(hrcp, HWARC_SER_NOSIG);
9219430SRaymond.Chen@Sun.COM 
9229430SRaymond.Chen@Sun.COM 	hrcp->hrc_open_count--;
9239430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_CLOSE, hrcp->hrc_log_hdl,
9249430SRaymond.Chen@Sun.COM 	    "hwarc_close: open count=%d", hrcp->hrc_open_count);
9259430SRaymond.Chen@Sun.COM 
9269430SRaymond.Chen@Sun.COM 	hwarc_release_access(hrcp);
9279430SRaymond.Chen@Sun.COM 	hwarc_pm_idle_component(hrcp);
9289430SRaymond.Chen@Sun.COM 
9299430SRaymond.Chen@Sun.COM 	mutex_exit(&hrcp->hrc_mutex);
9309430SRaymond.Chen@Sun.COM 
9319430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, hrcp->hrc_log_hdl,
9329430SRaymond.Chen@Sun.COM 	    "hwarc_close: leave");
9339430SRaymond.Chen@Sun.COM 
9349430SRaymond.Chen@Sun.COM 	return (0);
9359430SRaymond.Chen@Sun.COM }
9369430SRaymond.Chen@Sun.COM 
9379430SRaymond.Chen@Sun.COM /* Send cmd to hwarc device */
9389430SRaymond.Chen@Sun.COM int
hwarc_send_cmd(uwb_dev_handle_t uwb_dev_hdl,mblk_t * data,uint16_t data_len)9399430SRaymond.Chen@Sun.COM hwarc_send_cmd(uwb_dev_handle_t uwb_dev_hdl, mblk_t *data, uint16_t data_len)
9409430SRaymond.Chen@Sun.COM {
9419430SRaymond.Chen@Sun.COM 	usb_cb_flags_t		cb_flags;
9429430SRaymond.Chen@Sun.COM 	usb_cr_t		cr;
9439430SRaymond.Chen@Sun.COM 	usb_ctrl_setup_t 	setup;
9449430SRaymond.Chen@Sun.COM 	int 			rval;
9459430SRaymond.Chen@Sun.COM 	hwarc_state_t		*hrcp = NULL;
9469430SRaymond.Chen@Sun.COM 	dev_info_t 		*dip = uwb_get_dip(uwb_dev_hdl);
9479430SRaymond.Chen@Sun.COM 
9489430SRaymond.Chen@Sun.COM 	hrcp = ddi_get_soft_state(hwarc_statep, ddi_get_instance(dip));
9499430SRaymond.Chen@Sun.COM 
9509430SRaymond.Chen@Sun.COM 	ASSERT((hrcp != NULL) && (data != NULL));
9519430SRaymond.Chen@Sun.COM 
9529430SRaymond.Chen@Sun.COM 	setup.bmRequestType	= HWARC_SET_IF;
9539430SRaymond.Chen@Sun.COM 	setup.bRequest 		= HWA_EXEC_RC_CMD;
9549430SRaymond.Chen@Sun.COM 
9559430SRaymond.Chen@Sun.COM 	setup.wValue		= 0;
9569430SRaymond.Chen@Sun.COM 	setup.wIndex = hrcp->hrc_if_descr->if_alt->altif_descr.bInterfaceNumber;
9579430SRaymond.Chen@Sun.COM 
9589430SRaymond.Chen@Sun.COM 	setup.wLength		= data_len;
9599430SRaymond.Chen@Sun.COM 	setup.attrs		= 0;
9609430SRaymond.Chen@Sun.COM 
9619430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_DEVCTRL, hrcp->hrc_log_hdl,
9629430SRaymond.Chen@Sun.COM 	    "hwarc_send_cmd: wLength=%d, data[0], [1], [2], [3]=%d, %d, %d, %d",
9639430SRaymond.Chen@Sun.COM 	    setup.wLength, data->b_rptr[0], data->b_rptr[1], data->b_rptr[2],
9649430SRaymond.Chen@Sun.COM 	    data->b_rptr[3]);
9659430SRaymond.Chen@Sun.COM 
9669430SRaymond.Chen@Sun.COM 	if ((rval = usb_pipe_ctrl_xfer_wait(hrcp->hrc_default_ph, &setup,
9679430SRaymond.Chen@Sun.COM 	    &data, &cr, &cb_flags, 0)) != USB_SUCCESS) {
9689430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, hrcp->hrc_log_hdl,
9699430SRaymond.Chen@Sun.COM 		    "hwarc_send_cmd: fail, rval=%d, cr=%d, "
9709430SRaymond.Chen@Sun.COM 		    "cb_flags=%x", rval, cr, cb_flags);
9719430SRaymond.Chen@Sun.COM 
9729430SRaymond.Chen@Sun.COM 	}
9739430SRaymond.Chen@Sun.COM 
9749430SRaymond.Chen@Sun.COM 	freemsg(data);
9759430SRaymond.Chen@Sun.COM 
9769430SRaymond.Chen@Sun.COM 	return (rval);
9779430SRaymond.Chen@Sun.COM }
9789430SRaymond.Chen@Sun.COM 
9799430SRaymond.Chen@Sun.COM /* ioctl, call uwb ioctl  */
9809430SRaymond.Chen@Sun.COM /*ARGSUSED*/
9819430SRaymond.Chen@Sun.COM static int
hwarc_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * cred_p,int * rval_p)9829430SRaymond.Chen@Sun.COM hwarc_ioctl(dev_t dev, int cmd, intptr_t arg,
9839430SRaymond.Chen@Sun.COM 		int mode, cred_t *cred_p, int *rval_p)
9849430SRaymond.Chen@Sun.COM {
9859430SRaymond.Chen@Sun.COM 	int		rv = 0;
9869430SRaymond.Chen@Sun.COM 
9879430SRaymond.Chen@Sun.COM 	hwarc_state_t	*hrcp =
9889430SRaymond.Chen@Sun.COM 	    ddi_get_soft_state(hwarc_statep, getminor(dev));
9899430SRaymond.Chen@Sun.COM 	if (hrcp == NULL) {
9909430SRaymond.Chen@Sun.COM 
9919430SRaymond.Chen@Sun.COM 		return (ENXIO);
9929430SRaymond.Chen@Sun.COM 	}
9939430SRaymond.Chen@Sun.COM 
9949430SRaymond.Chen@Sun.COM 	if (drv_priv(cred_p) != 0) {
9959430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_ALL, hrcp->hrc_log_hdl,
9969430SRaymond.Chen@Sun.COM 		    "hwahc_wusb_ioctl: user must have SYS_DEVICE privilege,"
9979430SRaymond.Chen@Sun.COM 		    "cmd=%x", cmd);
9989430SRaymond.Chen@Sun.COM 
9999430SRaymond.Chen@Sun.COM 		return (EPERM);
10009430SRaymond.Chen@Sun.COM 	}
10019430SRaymond.Chen@Sun.COM 
10029430SRaymond.Chen@Sun.COM 
10039430SRaymond.Chen@Sun.COM 
10049430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_ALL, hrcp->hrc_log_hdl, "hwarc_ioctl: enter");
10059430SRaymond.Chen@Sun.COM 
10069430SRaymond.Chen@Sun.COM 	mutex_enter(&hrcp->hrc_mutex);
10079430SRaymond.Chen@Sun.COM 	if (hrcp->hrc_dev_state != USB_DEV_ONLINE) {
10089430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ALL, hrcp->hrc_log_hdl,
10099430SRaymond.Chen@Sun.COM 		    "ioctl: Device is not online,"
10109430SRaymond.Chen@Sun.COM 		    " dev_stat=%d", hrcp->hrc_dev_state);
10119430SRaymond.Chen@Sun.COM 		mutex_exit(&hrcp->hrc_mutex);
10129430SRaymond.Chen@Sun.COM 
10139430SRaymond.Chen@Sun.COM 		return (EFAULT);
10149430SRaymond.Chen@Sun.COM 	}
10159430SRaymond.Chen@Sun.COM 	mutex_exit(&hrcp->hrc_mutex);
10169430SRaymond.Chen@Sun.COM 
10179430SRaymond.Chen@Sun.COM 	rv = uwb_do_ioctl(hrcp->hrc_dev_hdl, cmd, arg, mode);
10189430SRaymond.Chen@Sun.COM 
10199430SRaymond.Chen@Sun.COM 	return (rv);
10209430SRaymond.Chen@Sun.COM }
10219430SRaymond.Chen@Sun.COM 
10229430SRaymond.Chen@Sun.COM 
10239430SRaymond.Chen@Sun.COM /*
10249430SRaymond.Chen@Sun.COM  * hwarc_disconnect_callback:
10259430SRaymond.Chen@Sun.COM  *	Called when device hotplug-removed.
10269430SRaymond.Chen@Sun.COM  *		Close pipes. (This does not attempt to contact device.)
10279430SRaymond.Chen@Sun.COM  *		Set state to DISCONNECTED
10289430SRaymond.Chen@Sun.COM  */
10299430SRaymond.Chen@Sun.COM static int
hwarc_disconnect_callback(dev_info_t * dip)10309430SRaymond.Chen@Sun.COM hwarc_disconnect_callback(dev_info_t *dip)
10319430SRaymond.Chen@Sun.COM {
10329430SRaymond.Chen@Sun.COM 	hwarc_state_t	*hrcp =
10339430SRaymond.Chen@Sun.COM 	    ddi_get_soft_state(hwarc_statep, ddi_get_instance(dip));
10349430SRaymond.Chen@Sun.COM 
10359430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_CB, hrcp->hrc_log_hdl, "disconnect: enter");
10369430SRaymond.Chen@Sun.COM 
10379430SRaymond.Chen@Sun.COM 	/* Disconnect the uwb device will stop beacon and save state */
10389430SRaymond.Chen@Sun.COM 	(void) uwb_dev_disconnect(dip);
10399430SRaymond.Chen@Sun.COM 	hwarc_close_intr_pipe(hrcp);
10409430SRaymond.Chen@Sun.COM 
10419430SRaymond.Chen@Sun.COM 	mutex_enter(&hrcp->hrc_mutex);
10429430SRaymond.Chen@Sun.COM 	(void) hwarc_serialize_access(hrcp, HWARC_SER_NOSIG);
10439430SRaymond.Chen@Sun.COM 
10449430SRaymond.Chen@Sun.COM 	/*
10459430SRaymond.Chen@Sun.COM 	 * Save any state of device or IO in progress required by
10469430SRaymond.Chen@Sun.COM 	 * hwarc_restore_device_state for proper device "thawing" later.
10479430SRaymond.Chen@Sun.COM 	 */
10489430SRaymond.Chen@Sun.COM 	hrcp->hrc_dev_state = USB_DEV_DISCONNECTED;
10499430SRaymond.Chen@Sun.COM 
10509430SRaymond.Chen@Sun.COM 	hwarc_release_access(hrcp);
10519430SRaymond.Chen@Sun.COM 	mutex_exit(&hrcp->hrc_mutex);
10529430SRaymond.Chen@Sun.COM 
10539430SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
10549430SRaymond.Chen@Sun.COM }
10559430SRaymond.Chen@Sun.COM 
10569430SRaymond.Chen@Sun.COM 
10579430SRaymond.Chen@Sun.COM /*
10589430SRaymond.Chen@Sun.COM  * hwarc_reconnect_callback:
10599430SRaymond.Chen@Sun.COM  *	Called with device hotplug-inserted
10609430SRaymond.Chen@Sun.COM  *		Restore state
10619430SRaymond.Chen@Sun.COM  */
10629430SRaymond.Chen@Sun.COM static int
hwarc_reconnect_callback(dev_info_t * dip)10639430SRaymond.Chen@Sun.COM hwarc_reconnect_callback(dev_info_t *dip)
10649430SRaymond.Chen@Sun.COM {
10659430SRaymond.Chen@Sun.COM 	int instance = ddi_get_instance(dip);
10669430SRaymond.Chen@Sun.COM 	hwarc_state_t	*hrcp =
10679430SRaymond.Chen@Sun.COM 	    ddi_get_soft_state(hwarc_statep, instance);
10689430SRaymond.Chen@Sun.COM 
10699430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_CB, hrcp->hrc_log_hdl, "reconnect: enter");
10709430SRaymond.Chen@Sun.COM 
10719430SRaymond.Chen@Sun.COM 	mutex_enter(&hrcp->hrc_mutex);
10729430SRaymond.Chen@Sun.COM 	(void) hwarc_serialize_access(hrcp, HWARC_SER_NOSIG);
10739430SRaymond.Chen@Sun.COM 	hwarc_restore_device_state(dip, hrcp);
10749430SRaymond.Chen@Sun.COM 	hwarc_release_access(hrcp);
10759430SRaymond.Chen@Sun.COM 	mutex_exit(&hrcp->hrc_mutex);
10769430SRaymond.Chen@Sun.COM 
10779430SRaymond.Chen@Sun.COM 	/* Reconnect the uwb device will restore uwb device state */
1078*10912SRaymond.Chen@Sun.COM 	(void) uwb_dev_reconnect(dip);
10799430SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
10809430SRaymond.Chen@Sun.COM }
10819430SRaymond.Chen@Sun.COM 
10829430SRaymond.Chen@Sun.COM 
10839430SRaymond.Chen@Sun.COM /*
10849430SRaymond.Chen@Sun.COM  * hwarc_restore_device_state:
10859430SRaymond.Chen@Sun.COM  *	Called during hotplug-reconnect and resume.
10869430SRaymond.Chen@Sun.COM  *		reenable power management
10879430SRaymond.Chen@Sun.COM  *		Verify the device is the same as before the disconnect/suspend.
10889430SRaymond.Chen@Sun.COM  *		Restore device state
10899430SRaymond.Chen@Sun.COM  *		Thaw any IO which was frozen.
10909430SRaymond.Chen@Sun.COM  *		Quiesce device.  (Other routines will activate if thawed IO.)
10919430SRaymond.Chen@Sun.COM  *		Set device online.
10929430SRaymond.Chen@Sun.COM  *		Leave device disconnected if there are problems.
10939430SRaymond.Chen@Sun.COM  */
10949430SRaymond.Chen@Sun.COM static void
hwarc_restore_device_state(dev_info_t * dip,hwarc_state_t * hrcp)10959430SRaymond.Chen@Sun.COM hwarc_restore_device_state(dev_info_t *dip, hwarc_state_t *hrcp)
10969430SRaymond.Chen@Sun.COM {
10979430SRaymond.Chen@Sun.COM 
10989430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_ATTA, hrcp->hrc_log_hdl,
10999430SRaymond.Chen@Sun.COM 	    "hwarc_restore_device_state: enter");
11009430SRaymond.Chen@Sun.COM 
11019430SRaymond.Chen@Sun.COM 	ASSERT(mutex_owned(&hrcp->hrc_mutex));
11029430SRaymond.Chen@Sun.COM 
11039430SRaymond.Chen@Sun.COM 	ASSERT((hrcp->hrc_dev_state == USB_DEV_DISCONNECTED) ||
11049430SRaymond.Chen@Sun.COM 	    (hrcp->hrc_dev_state == USB_DEV_SUSPENDED));
11059430SRaymond.Chen@Sun.COM 
11069430SRaymond.Chen@Sun.COM 	hwarc_pm_busy_component(hrcp);
11079430SRaymond.Chen@Sun.COM 
11089430SRaymond.Chen@Sun.COM 	mutex_exit(&hrcp->hrc_mutex);
11099430SRaymond.Chen@Sun.COM 
11109430SRaymond.Chen@Sun.COM 	(void) pm_raise_power(hrcp->hrc_dip, 0, USB_DEV_OS_FULL_PWR);
11119430SRaymond.Chen@Sun.COM 
11129430SRaymond.Chen@Sun.COM 	if (usb_check_same_device(dip, hrcp->hrc_log_hdl, USB_LOG_L2,
11139430SRaymond.Chen@Sun.COM 	    PRINT_MASK_ALL, USB_CHK_BASIC|USB_CHK_CFG, NULL) != USB_SUCCESS) {
11149430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hrcp->hrc_log_hdl,
11159430SRaymond.Chen@Sun.COM 		    "hwarc_restore_device_state: check same device failed");
11169430SRaymond.Chen@Sun.COM 
11179430SRaymond.Chen@Sun.COM 		goto fail;
11189430SRaymond.Chen@Sun.COM 
11199430SRaymond.Chen@Sun.COM 	}
11209430SRaymond.Chen@Sun.COM 	if (hwarc_open_intr_pipe(hrcp) != USB_SUCCESS) {
11219430SRaymond.Chen@Sun.COM 
11229430SRaymond.Chen@Sun.COM 		goto fail;
11239430SRaymond.Chen@Sun.COM 	}
11249430SRaymond.Chen@Sun.COM 	mutex_enter(&hrcp->hrc_mutex);
11259430SRaymond.Chen@Sun.COM 
11269430SRaymond.Chen@Sun.COM 	hrcp->hrc_dev_state = USB_DEV_ONLINE;
11279430SRaymond.Chen@Sun.COM 
11289430SRaymond.Chen@Sun.COM 	if (hrcp->hrc_pm && hrcp->hrc_pm->hrc_wakeup_enabled) {
11299430SRaymond.Chen@Sun.COM 
11309430SRaymond.Chen@Sun.COM 		mutex_exit(&hrcp->hrc_mutex);
11319430SRaymond.Chen@Sun.COM 		if (usb_handle_remote_wakeup(dip, USB_REMOTE_WAKEUP_ENABLE) !=
11329430SRaymond.Chen@Sun.COM 		    USB_SUCCESS) {
11339430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_ATTA, hrcp->hrc_log_hdl,
11349430SRaymond.Chen@Sun.COM 			    "hwarc_restore_device_state: "
11359430SRaymond.Chen@Sun.COM 			    "fail to enable device remote wakeup");
11369430SRaymond.Chen@Sun.COM 		}
11379430SRaymond.Chen@Sun.COM 		mutex_enter(&hrcp->hrc_mutex);
11389430SRaymond.Chen@Sun.COM 
11399430SRaymond.Chen@Sun.COM 	}
11409430SRaymond.Chen@Sun.COM 
11419430SRaymond.Chen@Sun.COM 	hwarc_pm_idle_component(hrcp);
11429430SRaymond.Chen@Sun.COM 
11439430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_ATTA, hrcp->hrc_log_hdl,
11449430SRaymond.Chen@Sun.COM 	    "hwarc_restore_device_state: end");
11459430SRaymond.Chen@Sun.COM 
11469430SRaymond.Chen@Sun.COM 	return;
11479430SRaymond.Chen@Sun.COM 
11489430SRaymond.Chen@Sun.COM fail:
11499430SRaymond.Chen@Sun.COM 	/* change the device state from suspended to disconnected */
11509430SRaymond.Chen@Sun.COM 	mutex_enter(&hrcp->hrc_mutex);
11519430SRaymond.Chen@Sun.COM 	hrcp->hrc_dev_state = USB_DEV_DISCONNECTED;
11529430SRaymond.Chen@Sun.COM 	hwarc_pm_idle_component(hrcp);
11539430SRaymond.Chen@Sun.COM }
11549430SRaymond.Chen@Sun.COM 
11559430SRaymond.Chen@Sun.COM 
11569430SRaymond.Chen@Sun.COM /*
11579430SRaymond.Chen@Sun.COM  * hwarc_cpr_suspend:
11589430SRaymond.Chen@Sun.COM  *	Clean up device.
11599430SRaymond.Chen@Sun.COM  *	Wait for any IO to finish, then close pipes.
11609430SRaymond.Chen@Sun.COM  *	Quiesce device.
11619430SRaymond.Chen@Sun.COM  */
11629430SRaymond.Chen@Sun.COM static int
hwarc_cpr_suspend(dev_info_t * dip)11639430SRaymond.Chen@Sun.COM hwarc_cpr_suspend(dev_info_t *dip)
11649430SRaymond.Chen@Sun.COM {
11659430SRaymond.Chen@Sun.COM 	hwarc_state_t	*hrcp = ddi_get_soft_state(hwarc_statep,
11669430SRaymond.Chen@Sun.COM 	    ddi_get_instance(dip));
11679430SRaymond.Chen@Sun.COM 
11689430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_PM, hrcp->hrc_log_hdl,
11699430SRaymond.Chen@Sun.COM 	    "hwarc_cpr_suspend, dev_stat=%d", hrcp->hrc_dev_state);
11709430SRaymond.Chen@Sun.COM 
11719430SRaymond.Chen@Sun.COM 	/* Disconnect the uwb device will stop beacon and save state */
11729430SRaymond.Chen@Sun.COM 	(void) uwb_dev_disconnect(dip);
11739430SRaymond.Chen@Sun.COM 
11749430SRaymond.Chen@Sun.COM 	/* Serialize to prevent races with detach, open, device access. */
11759430SRaymond.Chen@Sun.COM 	mutex_enter(&hrcp->hrc_mutex);
11769430SRaymond.Chen@Sun.COM 	(void) hwarc_serialize_access(hrcp, HWARC_SER_NOSIG);
11779430SRaymond.Chen@Sun.COM 
11789430SRaymond.Chen@Sun.COM 	hwarc_pm_busy_component(hrcp);
11799430SRaymond.Chen@Sun.COM 
11809430SRaymond.Chen@Sun.COM 	/*
11819430SRaymond.Chen@Sun.COM 	 * Set dev_state to suspended so other driver threads don't start any
11829430SRaymond.Chen@Sun.COM 	 * new I/O.  In a real driver, there may be draining of requests done
11839430SRaymond.Chen@Sun.COM 	 * afterwards, and we don't want the draining to compete with new
11849430SRaymond.Chen@Sun.COM 	 * requests being queued.
11859430SRaymond.Chen@Sun.COM 	 */
11869430SRaymond.Chen@Sun.COM 
11879430SRaymond.Chen@Sun.COM 	/* Don't suspend if the device is open. */
11889430SRaymond.Chen@Sun.COM 	if (hrcp->hrc_open_count != 0) {
11899430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_PM, hrcp->hrc_log_hdl,
11909430SRaymond.Chen@Sun.COM 		    "suspend: Device is open.  Can't suspend");
11919430SRaymond.Chen@Sun.COM 
11929430SRaymond.Chen@Sun.COM 		hwarc_release_access(hrcp);
11939430SRaymond.Chen@Sun.COM 		hwarc_pm_idle_component(hrcp);
11949430SRaymond.Chen@Sun.COM 		mutex_exit(&hrcp->hrc_mutex);
11959430SRaymond.Chen@Sun.COM 
11969430SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
11979430SRaymond.Chen@Sun.COM 	}
11989430SRaymond.Chen@Sun.COM 
11999430SRaymond.Chen@Sun.COM 	/* Access device here to clean it up. */
12009430SRaymond.Chen@Sun.COM 	mutex_exit(&hrcp->hrc_mutex);
12019430SRaymond.Chen@Sun.COM 	hwarc_close_intr_pipe(hrcp);
12029430SRaymond.Chen@Sun.COM 	mutex_enter(&hrcp->hrc_mutex);
12039430SRaymond.Chen@Sun.COM 
12049430SRaymond.Chen@Sun.COM 	hrcp->hrc_dev_state = USB_DEV_SUSPENDED;
12059430SRaymond.Chen@Sun.COM 
12069430SRaymond.Chen@Sun.COM 	/*
12079430SRaymond.Chen@Sun.COM 	 * Save any state of device required by hwarc_restore_device_state
12089430SRaymond.Chen@Sun.COM 	 * for proper device "thawing" later.
12099430SRaymond.Chen@Sun.COM 	 */
12109430SRaymond.Chen@Sun.COM 	hwarc_release_access(hrcp);
12119430SRaymond.Chen@Sun.COM 	hwarc_pm_idle_component(hrcp);
12129430SRaymond.Chen@Sun.COM 	mutex_exit(&hrcp->hrc_mutex);
12139430SRaymond.Chen@Sun.COM 
12149430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_PM, hrcp->hrc_log_hdl, "suspend: success");
12159430SRaymond.Chen@Sun.COM 
12169430SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
12179430SRaymond.Chen@Sun.COM }
12189430SRaymond.Chen@Sun.COM 
12199430SRaymond.Chen@Sun.COM 
12209430SRaymond.Chen@Sun.COM /*
12219430SRaymond.Chen@Sun.COM  * hwarc_cpr_resume:
12229430SRaymond.Chen@Sun.COM  *
12239430SRaymond.Chen@Sun.COM  *	hwarc_restore_device_state marks success by putting device back online
12249430SRaymond.Chen@Sun.COM  */
12259430SRaymond.Chen@Sun.COM static void
hwarc_cpr_resume(dev_info_t * dip)12269430SRaymond.Chen@Sun.COM hwarc_cpr_resume(dev_info_t *dip)
12279430SRaymond.Chen@Sun.COM {
12289430SRaymond.Chen@Sun.COM 	int		instance = ddi_get_instance(dip);
12299430SRaymond.Chen@Sun.COM 	hwarc_state_t	*hrcp = ddi_get_soft_state(hwarc_statep,
12309430SRaymond.Chen@Sun.COM 	    instance);
12319430SRaymond.Chen@Sun.COM 
12329430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_PM, hrcp->hrc_log_hdl,
12339430SRaymond.Chen@Sun.COM 	    "hwarc_cpr_resume, dev_stat=%d", hrcp->hrc_dev_state);
12349430SRaymond.Chen@Sun.COM 
12359430SRaymond.Chen@Sun.COM 	/*
12369430SRaymond.Chen@Sun.COM 	 * NOTE: A pm_raise_power in hwarc_restore_device_state will bring
12379430SRaymond.Chen@Sun.COM 	 * the power-up state of device into synch with the system.
12389430SRaymond.Chen@Sun.COM 	 */
12399430SRaymond.Chen@Sun.COM 	mutex_enter(&hrcp->hrc_mutex);
12409430SRaymond.Chen@Sun.COM 	hwarc_restore_device_state(dip, hrcp);
12419430SRaymond.Chen@Sun.COM 	mutex_exit(&hrcp->hrc_mutex);
12429430SRaymond.Chen@Sun.COM 
12439430SRaymond.Chen@Sun.COM 	/* Reconnect the uwb device will restore uwb device state */
1244*10912SRaymond.Chen@Sun.COM 	(void) uwb_dev_reconnect(dip);
12459430SRaymond.Chen@Sun.COM }
12469430SRaymond.Chen@Sun.COM 
12479430SRaymond.Chen@Sun.COM /*
12489430SRaymond.Chen@Sun.COM  * pipe callbacks
12499430SRaymond.Chen@Sun.COM  * --------------
12509430SRaymond.Chen@Sun.COM  *
12519430SRaymond.Chen@Sun.COM  * intr in callback for event receiving for hwarc device
12529430SRaymond.Chen@Sun.COM  */
12539430SRaymond.Chen@Sun.COM /*ARGSUSED*/
12549430SRaymond.Chen@Sun.COM void
hwarc_intr_cb(usb_pipe_handle_t pipe,usb_intr_req_t * req)12559430SRaymond.Chen@Sun.COM hwarc_intr_cb(usb_pipe_handle_t pipe, usb_intr_req_t *req)
12569430SRaymond.Chen@Sun.COM {
12579430SRaymond.Chen@Sun.COM 	hwarc_state_t *hrcp = (hwarc_state_t *)req->intr_client_private;
12589430SRaymond.Chen@Sun.COM 	uwb_dev_handle_t uwb_dev_hd;
12599430SRaymond.Chen@Sun.COM 	mblk_t	*data = req->intr_data;
12609430SRaymond.Chen@Sun.COM 	int		data_len, parse_err;
12619430SRaymond.Chen@Sun.COM 
12629430SRaymond.Chen@Sun.COM 	uwb_dev_hd = hrcp->hrc_dev_hdl;
12639430SRaymond.Chen@Sun.COM 	data_len = (data) ? MBLKL(data) : 0;
12649430SRaymond.Chen@Sun.COM 
12659430SRaymond.Chen@Sun.COM 	if (data_len == 0) {
12669430SRaymond.Chen@Sun.COM 
12679430SRaymond.Chen@Sun.COM 		return;
12689430SRaymond.Chen@Sun.COM 	}
12699430SRaymond.Chen@Sun.COM 
12709430SRaymond.Chen@Sun.COM 	/*
12719430SRaymond.Chen@Sun.COM 	 * Parse the event/notifications from the device, and cv_signal the
12729430SRaymond.Chen@Sun.COM 	 * waiting cmd
12739430SRaymond.Chen@Sun.COM 	 */
12749430SRaymond.Chen@Sun.COM 	parse_err = uwb_parse_evt_notif((uint8_t *)data->b_rptr,
12759430SRaymond.Chen@Sun.COM 	    data_len, uwb_dev_hd);
12769430SRaymond.Chen@Sun.COM 	if (parse_err != UWB_SUCCESS) {
12779430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_CB, hrcp->hrc_log_hdl,
12789430SRaymond.Chen@Sun.COM 		    "hwarc_intr_cb: parse failed, no cmd result or "
12799430SRaymond.Chen@Sun.COM 		    "notifs delivered. parse_err= %d", parse_err);
12809430SRaymond.Chen@Sun.COM 		if (data_len >= 5) {
12819430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_CB, hrcp->hrc_log_hdl,
12829430SRaymond.Chen@Sun.COM 			    "hwarc_intr_cb: erro evt len=%d"
12839430SRaymond.Chen@Sun.COM 			    " evtcode=%d %d,evt_context=%d, result-code=%d",
12849430SRaymond.Chen@Sun.COM 			    data_len, data->b_rptr[1], data->b_rptr[2],
12859430SRaymond.Chen@Sun.COM 			    data->b_rptr[3], data->b_rptr[4]);
12869430SRaymond.Chen@Sun.COM 		}
12879430SRaymond.Chen@Sun.COM 	}
12889430SRaymond.Chen@Sun.COM 
12899430SRaymond.Chen@Sun.COM 	usb_free_intr_req(req);
12909430SRaymond.Chen@Sun.COM }
12919430SRaymond.Chen@Sun.COM 
12929430SRaymond.Chen@Sun.COM /*
12939430SRaymond.Chen@Sun.COM  * pipe callbacks
12949430SRaymond.Chen@Sun.COM  * --------------
12959430SRaymond.Chen@Sun.COM  *
12969430SRaymond.Chen@Sun.COM  * intr in exception callback
12979430SRaymond.Chen@Sun.COM  */
12989430SRaymond.Chen@Sun.COM void
hwarc_intr_ex_cb(usb_pipe_handle_t pipe,usb_intr_req_t * req)12999430SRaymond.Chen@Sun.COM hwarc_intr_ex_cb(usb_pipe_handle_t pipe, usb_intr_req_t *req)
13009430SRaymond.Chen@Sun.COM {
13019430SRaymond.Chen@Sun.COM 	hwarc_state_t *hrcp = (hwarc_state_t *)req->intr_client_private;
13029430SRaymond.Chen@Sun.COM 	usb_cr_t	cr = req->intr_completion_reason;
13039430SRaymond.Chen@Sun.COM 
13049430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L4(PRINT_MASK_CB, hrcp->hrc_log_hdl,
13059430SRaymond.Chen@Sun.COM 	    "hwarc_intr_ex_cb: ph = 0x%p req = 0x%p  cr=%d flags=%x",
13069430SRaymond.Chen@Sun.COM 	    (void *)pipe, (void *)req, cr, req->intr_cb_flags);
13079430SRaymond.Chen@Sun.COM 
13089430SRaymond.Chen@Sun.COM 	usb_free_intr_req(req);
13099430SRaymond.Chen@Sun.COM 
13109430SRaymond.Chen@Sun.COM 	/* restart polling */
13119430SRaymond.Chen@Sun.COM 	if ((cr != USB_CR_PIPE_CLOSING) && (cr != USB_CR_STOPPED_POLLING) &&
13129430SRaymond.Chen@Sun.COM 	    (cr != USB_CR_FLUSHED) && (cr != USB_CR_DEV_NOT_RESP) &&
13139430SRaymond.Chen@Sun.COM 	    (cr != USB_CR_PIPE_RESET) &&
13149430SRaymond.Chen@Sun.COM 	    (hrcp->hrc_dev_state == USB_DEV_ONLINE)) {
13159430SRaymond.Chen@Sun.COM 		if (hwarc_start_polling(hrcp, hrcp->hrc_intr_ph) !=
13169430SRaymond.Chen@Sun.COM 		    USB_SUCCESS) {
13179430SRaymond.Chen@Sun.COM 			USB_DPRINTF_L2(PRINT_MASK_CB, hrcp->hrc_log_hdl,
13189430SRaymond.Chen@Sun.COM 			    "hwarc_intr_ex_cb:"
13199430SRaymond.Chen@Sun.COM 			    "Restart pollling failed.");
13209430SRaymond.Chen@Sun.COM 		}
13219430SRaymond.Chen@Sun.COM 	} else {
13229430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_CB, hrcp->hrc_log_hdl,
13239430SRaymond.Chen@Sun.COM 		    "hwarc_intr_ex_cb:"
13249430SRaymond.Chen@Sun.COM 		    "get events failed: cr=%d", cr);
13259430SRaymond.Chen@Sun.COM 	}
13269430SRaymond.Chen@Sun.COM }
13279430SRaymond.Chen@Sun.COM 
13289430SRaymond.Chen@Sun.COM /*
13299430SRaymond.Chen@Sun.COM  * start polling on the interrupt pipe for events
13309430SRaymond.Chen@Sun.COM  */
13319430SRaymond.Chen@Sun.COM int
hwarc_start_polling(hwarc_state_t * hrcp,usb_pipe_handle_t intr_ph)13329430SRaymond.Chen@Sun.COM hwarc_start_polling(hwarc_state_t *hrcp, usb_pipe_handle_t intr_ph)
13339430SRaymond.Chen@Sun.COM {
13349430SRaymond.Chen@Sun.COM 	usb_intr_req_t	*br;
13359430SRaymond.Chen@Sun.COM 	int		rval = USB_SUCCESS;
13369430SRaymond.Chen@Sun.COM 
13379430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_OPEN, hrcp->hrc_log_hdl,
13389430SRaymond.Chen@Sun.COM 	    "hwarc_start_polling");
13399430SRaymond.Chen@Sun.COM 
13409430SRaymond.Chen@Sun.COM 	br = usb_alloc_intr_req(hrcp->hrc_dip, 0, USB_FLAGS_SLEEP);
13419430SRaymond.Chen@Sun.COM 
13429430SRaymond.Chen@Sun.COM 	if (!br) {
13439430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_OPEN, hrcp->hrc_log_hdl,
13449430SRaymond.Chen@Sun.COM 		    "hwarc_start_polling: alloc req failed.");
13459430SRaymond.Chen@Sun.COM 
13469430SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
13479430SRaymond.Chen@Sun.COM 	}
13489430SRaymond.Chen@Sun.COM 	br->intr_attributes = USB_ATTRS_SHORT_XFER_OK | USB_ATTRS_AUTOCLEARING;
13499430SRaymond.Chen@Sun.COM 	br->intr_len = hrcp->hrc_intr_ep_descr.wMaxPacketSize;
13509430SRaymond.Chen@Sun.COM 	br->intr_client_private = (void *)hrcp;
13519430SRaymond.Chen@Sun.COM 
13529430SRaymond.Chen@Sun.COM 	br->intr_cb = hwarc_intr_cb;
13539430SRaymond.Chen@Sun.COM 	br->intr_exc_cb = hwarc_intr_ex_cb;
13549430SRaymond.Chen@Sun.COM 
13559430SRaymond.Chen@Sun.COM 	rval = usb_pipe_intr_xfer(intr_ph, br, USB_FLAGS_SLEEP);
13569430SRaymond.Chen@Sun.COM 
13579430SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
13589430SRaymond.Chen@Sun.COM 		usb_free_intr_req(br);
13599430SRaymond.Chen@Sun.COM 
13609430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_OPEN, hrcp->hrc_log_hdl,
13619430SRaymond.Chen@Sun.COM 		    "hwarc_start_polling: failed (%d)", rval);
13629430SRaymond.Chen@Sun.COM 	}
13639430SRaymond.Chen@Sun.COM 
13649430SRaymond.Chen@Sun.COM 	return (rval);
13659430SRaymond.Chen@Sun.COM }
13669430SRaymond.Chen@Sun.COM 
13679430SRaymond.Chen@Sun.COM /*
13689430SRaymond.Chen@Sun.COM  * hwarc_open_intr_pipe:
13699430SRaymond.Chen@Sun.COM  *	Open any pipes other than default pipe.
13709430SRaymond.Chen@Sun.COM  *	Mutex is assumed to be held.
13719430SRaymond.Chen@Sun.COM  */
13729430SRaymond.Chen@Sun.COM static int
hwarc_open_intr_pipe(hwarc_state_t * hrcp)13739430SRaymond.Chen@Sun.COM hwarc_open_intr_pipe(hwarc_state_t *hrcp)
13749430SRaymond.Chen@Sun.COM {
13759430SRaymond.Chen@Sun.COM 
13769430SRaymond.Chen@Sun.COM 	int			rval = USB_SUCCESS;
13779430SRaymond.Chen@Sun.COM 	usb_pipe_policy_t	pipe_policy;
13789430SRaymond.Chen@Sun.COM 	usb_pipe_handle_t	pipe_handle;
13799430SRaymond.Chen@Sun.COM 
13809430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_ATTA, hrcp->hrc_log_hdl, "open_pipes enter");
13819430SRaymond.Chen@Sun.COM 
13829430SRaymond.Chen@Sun.COM 	bzero(&pipe_policy, sizeof (pipe_policy));
13839430SRaymond.Chen@Sun.COM 
13849430SRaymond.Chen@Sun.COM 	/*
13859430SRaymond.Chen@Sun.COM 	 * Allow that pipes can support at least two asynchronous operations
13869430SRaymond.Chen@Sun.COM 	 * going on simultaneously.  Operations include asynchronous callbacks,
13879430SRaymond.Chen@Sun.COM 	 * resets, closures.
13889430SRaymond.Chen@Sun.COM 	 */
13899430SRaymond.Chen@Sun.COM 	pipe_policy.pp_max_async_reqs = 2;
13909430SRaymond.Chen@Sun.COM 
13919430SRaymond.Chen@Sun.COM 	if ((rval = usb_pipe_open(hrcp->hrc_dip,
13929430SRaymond.Chen@Sun.COM 	    &hrcp->hrc_intr_ep_descr, &pipe_policy,
13939430SRaymond.Chen@Sun.COM 	    USB_FLAGS_SLEEP, &pipe_handle)) != USB_SUCCESS) {
13949430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hrcp->hrc_log_hdl,
13959430SRaymond.Chen@Sun.COM 		    "hwarc_open_intr_pipe:Error opening intr pipe: status = %d",
13969430SRaymond.Chen@Sun.COM 		    rval);
13979430SRaymond.Chen@Sun.COM 		goto done;
13989430SRaymond.Chen@Sun.COM 	}
13999430SRaymond.Chen@Sun.COM 
14009430SRaymond.Chen@Sun.COM 	mutex_enter(&hrcp->hrc_mutex);
14019430SRaymond.Chen@Sun.COM 	hrcp->hrc_intr_ph = pipe_handle;
14029430SRaymond.Chen@Sun.COM 	mutex_exit(&hrcp->hrc_mutex);
14039430SRaymond.Chen@Sun.COM 
14049430SRaymond.Chen@Sun.COM 	/*
14059430SRaymond.Chen@Sun.COM 	 * At this point, polling could be started on the pipe by making an
14069430SRaymond.Chen@Sun.COM 	 * asynchronous input request on the pipe.  Allocate a request by
14079430SRaymond.Chen@Sun.COM 	 * calling usb_alloc_intr_req(9F) with a zero length, initialize
14089430SRaymond.Chen@Sun.COM 	 * attributes with USB_ATTRS_SHORT_XFER_OK | USB_ATTRS_AUTOCLEARING,
14099430SRaymond.Chen@Sun.COM 	 * initialize length to be packetsize of the endpoint, specify the
14109430SRaymond.Chen@Sun.COM 	 * callbacks.  Pass this request to usb_pipe_intr_xfer to start polling.
14119430SRaymond.Chen@Sun.COM 	 * Call usb_pipe_stop_intr_poling(9F) to stop polling.
14129430SRaymond.Chen@Sun.COM 	 */
14139430SRaymond.Chen@Sun.COM 	rval = hwarc_start_polling(hrcp, hrcp->hrc_intr_ph);
14149430SRaymond.Chen@Sun.COM 	if (rval != USB_SUCCESS) {
14159430SRaymond.Chen@Sun.COM 		hwarc_close_intr_pipe(hrcp);
14169430SRaymond.Chen@Sun.COM 
14179430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_ATTA, hrcp->hrc_log_hdl,
14189430SRaymond.Chen@Sun.COM 		    "hwarc_open_intr_pipe: Error start "
14199430SRaymond.Chen@Sun.COM 		    "polling intr pipe: rval = %d", rval);
14209430SRaymond.Chen@Sun.COM 	}
14219430SRaymond.Chen@Sun.COM 
14229430SRaymond.Chen@Sun.COM done:
14239430SRaymond.Chen@Sun.COM 
14249430SRaymond.Chen@Sun.COM 	return (rval);
14259430SRaymond.Chen@Sun.COM }
14269430SRaymond.Chen@Sun.COM 
14279430SRaymond.Chen@Sun.COM 
14289430SRaymond.Chen@Sun.COM /*
14299430SRaymond.Chen@Sun.COM  * hwarc_close_intr_pipe:
14309430SRaymond.Chen@Sun.COM  *	Close pipes. Mutex is assumed to be held.
14319430SRaymond.Chen@Sun.COM  */
14329430SRaymond.Chen@Sun.COM static void
hwarc_close_intr_pipe(hwarc_state_t * hrcp)14339430SRaymond.Chen@Sun.COM hwarc_close_intr_pipe(hwarc_state_t *hrcp)
14349430SRaymond.Chen@Sun.COM {
14359430SRaymond.Chen@Sun.COM 	usb_pipe_handle_t	pipe_handle = NULL;
14369430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_ATTA, hrcp->hrc_log_hdl, "close_pipes enter");
14379430SRaymond.Chen@Sun.COM 
14389430SRaymond.Chen@Sun.COM 	mutex_enter(&hrcp->hrc_mutex);
14399430SRaymond.Chen@Sun.COM 	if (!hrcp->hrc_intr_ph) {
14409430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hrcp->hrc_log_hdl,
14419430SRaymond.Chen@Sun.COM 		    "hwarc intr pipe not exist");
14429430SRaymond.Chen@Sun.COM 		mutex_exit(&hrcp->hrc_mutex);
14439430SRaymond.Chen@Sun.COM 		return;
14449430SRaymond.Chen@Sun.COM 	}
14459430SRaymond.Chen@Sun.COM 
14469430SRaymond.Chen@Sun.COM 	pipe_handle = hrcp->hrc_intr_ph;
14479430SRaymond.Chen@Sun.COM 	hrcp->hrc_intr_ph = NULL;
14489430SRaymond.Chen@Sun.COM 	mutex_exit(&hrcp->hrc_mutex);
14499430SRaymond.Chen@Sun.COM 
14509430SRaymond.Chen@Sun.COM 	/* Stop polling */
14519430SRaymond.Chen@Sun.COM 	usb_pipe_stop_intr_polling(pipe_handle, USB_FLAGS_SLEEP);
14529430SRaymond.Chen@Sun.COM 
14539430SRaymond.Chen@Sun.COM 	/* Close Pipe */
14549430SRaymond.Chen@Sun.COM 	usb_pipe_close(hrcp->hrc_dip, pipe_handle, USB_FLAGS_SLEEP, NULL, 0);
14559430SRaymond.Chen@Sun.COM }
14569430SRaymond.Chen@Sun.COM 
14579430SRaymond.Chen@Sun.COM /*
14589430SRaymond.Chen@Sun.COM  * hwarc_power :
14599430SRaymond.Chen@Sun.COM  *	Power entry point, the workhorse behind pm_raise_power, pm_lower_power,
14609430SRaymond.Chen@Sun.COM  *	usb_req_raise_power and usb_req_lower_power.
14619430SRaymond.Chen@Sun.COM  */
14629430SRaymond.Chen@Sun.COM /*ARGSUSED*/
14639430SRaymond.Chen@Sun.COM static int
hwarc_power(dev_info_t * dip,int comp,int level)14649430SRaymond.Chen@Sun.COM hwarc_power(dev_info_t *dip, int comp, int level)
14659430SRaymond.Chen@Sun.COM {
14669430SRaymond.Chen@Sun.COM 	hwarc_state_t	*hrcp;
14679430SRaymond.Chen@Sun.COM 	int	rval = USB_FAILURE;
14689430SRaymond.Chen@Sun.COM 
14699430SRaymond.Chen@Sun.COM 	hrcp = ddi_get_soft_state(hwarc_statep, ddi_get_instance(dip));
14709430SRaymond.Chen@Sun.COM 
14719430SRaymond.Chen@Sun.COM 	USB_DPRINTF_L3(PRINT_MASK_PM, hrcp->hrc_log_hdl,
14729430SRaymond.Chen@Sun.COM 	    "hwarc_power: level = %d", level);
14739430SRaymond.Chen@Sun.COM 
14749430SRaymond.Chen@Sun.COM 	mutex_enter(&hrcp->hrc_mutex);
14759430SRaymond.Chen@Sun.COM 
14769430SRaymond.Chen@Sun.COM 	ASSERT(hrcp->hrc_pm != NULL);
14779430SRaymond.Chen@Sun.COM 
14789430SRaymond.Chen@Sun.COM 	(void) hwarc_serialize_access(hrcp, HWARC_SER_NOSIG);
14799430SRaymond.Chen@Sun.COM 
14809430SRaymond.Chen@Sun.COM 	/*
14819430SRaymond.Chen@Sun.COM 	 * If we are disconnected/suspended, return success. Note that if we
14829430SRaymond.Chen@Sun.COM 	 * return failure, bringing down the system will hang when
14839430SRaymond.Chen@Sun.COM 	 * PM tries to power up all devices
14849430SRaymond.Chen@Sun.COM 	 */
14859430SRaymond.Chen@Sun.COM 	if ((hrcp->hrc_dev_state == USB_DEV_DISCONNECTED) ||
14869430SRaymond.Chen@Sun.COM 	    (hrcp->hrc_dev_state == USB_DEV_SUSPENDED)) {
14879430SRaymond.Chen@Sun.COM 
14889430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L3(PRINT_MASK_PM, hrcp->hrc_log_hdl,
14899430SRaymond.Chen@Sun.COM 		    "hwarc_power: disconnected/suspended "
14909430SRaymond.Chen@Sun.COM 		    "dev_state=%d", hrcp->hrc_dev_state);
14919430SRaymond.Chen@Sun.COM 		rval = USB_SUCCESS;
14929430SRaymond.Chen@Sun.COM 
14939430SRaymond.Chen@Sun.COM 		goto done;
14949430SRaymond.Chen@Sun.COM 	}
14959430SRaymond.Chen@Sun.COM 
14969430SRaymond.Chen@Sun.COM 
14979430SRaymond.Chen@Sun.COM 	/* Check if we are transitioning to a legal power level */
14989430SRaymond.Chen@Sun.COM 	if (USB_DEV_PWRSTATE_OK(hrcp->hrc_pm->hrc_pwr_states, level)) {
14999430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_PM, hrcp->hrc_log_hdl,
15009430SRaymond.Chen@Sun.COM 		    "hwarc_power: illegal power level = %d "
15019430SRaymond.Chen@Sun.COM 		    "pwr_states: %x", level, hrcp->hrc_pm->hrc_pwr_states);
15029430SRaymond.Chen@Sun.COM 
15039430SRaymond.Chen@Sun.COM 		goto done;
15049430SRaymond.Chen@Sun.COM 	}
15059430SRaymond.Chen@Sun.COM 
15069430SRaymond.Chen@Sun.COM 	switch (level) {
15079430SRaymond.Chen@Sun.COM 	case USB_DEV_OS_PWR_OFF :
15089430SRaymond.Chen@Sun.COM 		rval = hwarc_pwrlvl0(hrcp);
15099430SRaymond.Chen@Sun.COM 
15109430SRaymond.Chen@Sun.COM 		break;
15119430SRaymond.Chen@Sun.COM 	case USB_DEV_OS_PWR_1:
15129430SRaymond.Chen@Sun.COM 		rval = hwarc_pwrlvl1(hrcp);
15139430SRaymond.Chen@Sun.COM 
15149430SRaymond.Chen@Sun.COM 		break;
15159430SRaymond.Chen@Sun.COM 	case USB_DEV_OS_PWR_2:
15169430SRaymond.Chen@Sun.COM 		rval = hwarc_pwrlvl2(hrcp);
15179430SRaymond.Chen@Sun.COM 
15189430SRaymond.Chen@Sun.COM 		break;
15199430SRaymond.Chen@Sun.COM 	case USB_DEV_OS_FULL_PWR :
15209430SRaymond.Chen@Sun.COM 		rval = hwarc_pwrlvl3(hrcp);
15219430SRaymond.Chen@Sun.COM 
15229430SRaymond.Chen@Sun.COM 		break;
15239430SRaymond.Chen@Sun.COM 	}
15249430SRaymond.Chen@Sun.COM 
15259430SRaymond.Chen@Sun.COM done:
15269430SRaymond.Chen@Sun.COM 	hwarc_release_access(hrcp);
15279430SRaymond.Chen@Sun.COM 	mutex_exit(&hrcp->hrc_mutex);
15289430SRaymond.Chen@Sun.COM 
15299430SRaymond.Chen@Sun.COM 	return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
15309430SRaymond.Chen@Sun.COM }
15319430SRaymond.Chen@Sun.COM 
15329430SRaymond.Chen@Sun.COM 
15339430SRaymond.Chen@Sun.COM /*
15349430SRaymond.Chen@Sun.COM  * hwarc_serialize_access:
15359430SRaymond.Chen@Sun.COM  *    Get the serial synchronization object before returning.
15369430SRaymond.Chen@Sun.COM  *
15379430SRaymond.Chen@Sun.COM  * Arguments:
15389430SRaymond.Chen@Sun.COM  *    hrcp - Pointer to hwarc state structure
15399430SRaymond.Chen@Sun.COM  *    waitsig - Set to:
15409430SRaymond.Chen@Sun.COM  *	HWARC_SER_SIG - to wait such that a signal can interrupt
15419430SRaymond.Chen@Sun.COM  *	HWARC_SER_NOSIG - to wait such that a signal cannot interrupt
15429430SRaymond.Chen@Sun.COM  */
15439430SRaymond.Chen@Sun.COM static int
hwarc_serialize_access(hwarc_state_t * hrcp,boolean_t waitsig)15449430SRaymond.Chen@Sun.COM hwarc_serialize_access(hwarc_state_t *hrcp, boolean_t waitsig)
15459430SRaymond.Chen@Sun.COM {
15469430SRaymond.Chen@Sun.COM 	int rval = 1;
15479430SRaymond.Chen@Sun.COM 
15489430SRaymond.Chen@Sun.COM 	ASSERT(mutex_owned(&hrcp->hrc_mutex));
15499430SRaymond.Chen@Sun.COM 
15509430SRaymond.Chen@Sun.COM 	while (hrcp->hrc_serial_inuse) {
15519430SRaymond.Chen@Sun.COM 		if (waitsig == HWARC_SER_SIG) {
15529430SRaymond.Chen@Sun.COM 			rval = cv_wait_sig(&hrcp->hrc_serial_cv,
15539430SRaymond.Chen@Sun.COM 			    &hrcp->hrc_mutex);
15549430SRaymond.Chen@Sun.COM 		} else {
15559430SRaymond.Chen@Sun.COM 			cv_wait(&hrcp->hrc_serial_cv,
15569430SRaymond.Chen@Sun.COM 			    &hrcp->hrc_mutex);
15579430SRaymond.Chen@Sun.COM 		}
15589430SRaymond.Chen@Sun.COM 	}
15599430SRaymond.Chen@Sun.COM 	hrcp->hrc_serial_inuse = B_TRUE;
15609430SRaymond.Chen@Sun.COM 
15619430SRaymond.Chen@Sun.COM 	return (rval);
15629430SRaymond.Chen@Sun.COM }
15639430SRaymond.Chen@Sun.COM 
15649430SRaymond.Chen@Sun.COM 
15659430SRaymond.Chen@Sun.COM /*
15669430SRaymond.Chen@Sun.COM  * hwarc_release_access:
15679430SRaymond.Chen@Sun.COM  *    Release the serial synchronization object.
15689430SRaymond.Chen@Sun.COM  */
15699430SRaymond.Chen@Sun.COM static void
hwarc_release_access(hwarc_state_t * hrcp)15709430SRaymond.Chen@Sun.COM hwarc_release_access(hwarc_state_t *hrcp)
15719430SRaymond.Chen@Sun.COM {
15729430SRaymond.Chen@Sun.COM 	ASSERT(mutex_owned(&hrcp->hrc_mutex));
15739430SRaymond.Chen@Sun.COM 	hrcp->hrc_serial_inuse = B_FALSE;
15749430SRaymond.Chen@Sun.COM 	cv_broadcast(&hrcp->hrc_serial_cv);
15759430SRaymond.Chen@Sun.COM }
15769430SRaymond.Chen@Sun.COM 
15779430SRaymond.Chen@Sun.COM 
15789430SRaymond.Chen@Sun.COM /*
15799430SRaymond.Chen@Sun.COM  * hwarc_reset_device:
15809430SRaymond.Chen@Sun.COM  *	Reset the readio controller with uwb interfaces.
15819430SRaymond.Chen@Sun.COM  *	if the device is different.  Can block.
15829430SRaymond.Chen@Sun.COM  */
15839430SRaymond.Chen@Sun.COM static int
hwarc_reset_device(hwarc_state_t * hrcp)15849430SRaymond.Chen@Sun.COM hwarc_reset_device(hwarc_state_t *hrcp)
15859430SRaymond.Chen@Sun.COM {
15869430SRaymond.Chen@Sun.COM 	if (uwb_reset_dev(hrcp->hrc_dip) != UWB_SUCCESS) {
15879430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hrcp->hrc_log_hdl,
15889430SRaymond.Chen@Sun.COM 		    "hwarc_reset_device: uwb_reset_dev failed");
15899430SRaymond.Chen@Sun.COM 
15909430SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
15919430SRaymond.Chen@Sun.COM 	}
15929430SRaymond.Chen@Sun.COM 
15939430SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
15949430SRaymond.Chen@Sun.COM }
15959430SRaymond.Chen@Sun.COM 
15969430SRaymond.Chen@Sun.COM /*
15979430SRaymond.Chen@Sun.COM  * hwarc_init_phy
15989430SRaymond.Chen@Sun.COM  *	init the physical capabilities of the radio controller.
15999430SRaymond.Chen@Sun.COM  *	the band groups and phy rates will be initialized in the
16009430SRaymond.Chen@Sun.COM  *	uwb devices.
16019430SRaymond.Chen@Sun.COM  */
16029430SRaymond.Chen@Sun.COM static int
hwarc_init_phy(hwarc_state_t * hrcp)16039430SRaymond.Chen@Sun.COM hwarc_init_phy(hwarc_state_t *hrcp)
16049430SRaymond.Chen@Sun.COM {
16059430SRaymond.Chen@Sun.COM 	if (uwb_init_phy(hrcp->hrc_dip) != UWB_SUCCESS) {
16069430SRaymond.Chen@Sun.COM 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hrcp->hrc_log_hdl,
16079430SRaymond.Chen@Sun.COM 		    "hwarc_init_phy: uwb_init_phy failed");
16089430SRaymond.Chen@Sun.COM 
16099430SRaymond.Chen@Sun.COM 		return (USB_FAILURE);
16109430SRaymond.Chen@Sun.COM 	}
16119430SRaymond.Chen@Sun.COM 
16129430SRaymond.Chen@Sun.COM 	return (USB_SUCCESS);
16139430SRaymond.Chen@Sun.COM }
1614