17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5b0fe7b8fSlg  * Common Development and Distribution License (the "License").
6b0fe7b8fSlg  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22be529ebcSRaymond Chen  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate /*
2788447a05SGarrett D'Amore  * Audio Streams Interface Driver:
2888447a05SGarrett D'Amore  *
2988447a05SGarrett D'Amore  * usb_as is responsible for (1) Processing audio data messages during
3088447a05SGarrett D'Amore  * play and record and management of isoc pipe, (2) Selecting correct
3188447a05SGarrett D'Amore  * alternate that matches a set of parameters and management of control pipe.
3288447a05SGarrett D'Amore  * This driver is opened by usb_ac and interacts with usb_ac synchronously
3388447a05SGarrett D'Amore  * using ioctls. If the processing involves an async USBA command, the ioctl
3488447a05SGarrett D'Amore  * returns after completion of the command.
357c478bd9Sstevel@tonic-gate  *
3688447a05SGarrett D'Amore  * Note: When there is a play/record, usb_as calls framework routines
3788447a05SGarrett D'Amore  * directly for data (play) or sends data to mixer (record).
387c478bd9Sstevel@tonic-gate  *
3988447a05SGarrett D'Amore  * Serialization: A competing thread can't be allowed to interfere with
4088447a05SGarrett D'Amore  * (1) pipe, (2) streams state.
417c478bd9Sstevel@tonic-gate  * So we need some kind of serialization among the asynchronous
427c478bd9Sstevel@tonic-gate  * threads that can run in the driver. The serialization is mostly
437c478bd9Sstevel@tonic-gate  * needed to avoid races among open/close/events/power entry points
447c478bd9Sstevel@tonic-gate  * etc. Once a routine grabs access, if checks if the resource (pipe or
457c478bd9Sstevel@tonic-gate  * stream or dev state) is still accessible. If so, it proceeds with
467c478bd9Sstevel@tonic-gate  * its job and until it completes, no other thread requiring the same
477c478bd9Sstevel@tonic-gate  * resource can run.
487c478bd9Sstevel@tonic-gate  *
497c478bd9Sstevel@tonic-gate  * PM Model in usb_as: Raise power during attach and lower power in detach.
507c478bd9Sstevel@tonic-gate  * If device is not fully powered, synchronous raise power in wsrv entry points.
517c478bd9Sstevel@tonic-gate  */
527c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usbai_version.h>
537c478bd9Sstevel@tonic-gate #include <sys/usb/usba.h>
5488447a05SGarrett D'Amore #include <sys/ddi.h>
5588447a05SGarrett D'Amore #include <sys/sunddi.h>
567c478bd9Sstevel@tonic-gate 
57e272e4c8SBinzi Cao - Sun Microsystems - Beijing China #include <sys/audio/audio_driver.h>
587c478bd9Sstevel@tonic-gate 
597c478bd9Sstevel@tonic-gate #include <sys/usb/clients/audio/usb_audio.h>
607c478bd9Sstevel@tonic-gate #include <sys/usb/clients/audio/usb_mixer.h>
617c478bd9Sstevel@tonic-gate #include <sys/usb/clients/audio/usb_as/usb_as.h>
62e272e4c8SBinzi Cao - Sun Microsystems - Beijing China #include <sys/usb/clients/audio/usb_ac/usb_ac.h>
637c478bd9Sstevel@tonic-gate 
6488447a05SGarrett D'Amore 
657c478bd9Sstevel@tonic-gate /* debug support */
664610e4a0Sfrits uint_t	usb_as_errlevel	= USB_LOG_L4;
674610e4a0Sfrits uint_t	usb_as_errmask	= (uint_t)-1;
684610e4a0Sfrits uint_t	usb_as_instance_debug = (uint_t)-1;
697c478bd9Sstevel@tonic-gate 
707c478bd9Sstevel@tonic-gate /*
717c478bd9Sstevel@tonic-gate  * Module linkage routines for the kernel
727c478bd9Sstevel@tonic-gate  */
737c478bd9Sstevel@tonic-gate static int	usb_as_attach(dev_info_t *, ddi_attach_cmd_t);
747c478bd9Sstevel@tonic-gate static int	usb_as_detach(dev_info_t *, ddi_detach_cmd_t);
757c478bd9Sstevel@tonic-gate static int	usb_as_power(dev_info_t *, int, int);
767c478bd9Sstevel@tonic-gate static int	usb_as_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
777c478bd9Sstevel@tonic-gate 
7888447a05SGarrett D'Amore static int usb_as_open(dev_t *, int, int, cred_t *);
7988447a05SGarrett D'Amore static int usb_as_close(dev_t, int, int, cred_t *);
8088447a05SGarrett D'Amore 
817c478bd9Sstevel@tonic-gate 
827c478bd9Sstevel@tonic-gate /* support functions */
837c478bd9Sstevel@tonic-gate static void	usb_as_cleanup(dev_info_t *, usb_as_state_t *);
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate static int	usb_as_handle_descriptors(usb_as_state_t *);
867c478bd9Sstevel@tonic-gate static void	usb_as_prepare_registration_data(usb_as_state_t *);
87*64391892SAlbert Lee static int	usb_as_valid_format(usb_as_state_t *, uint_t);
887c478bd9Sstevel@tonic-gate static void	usb_as_free_alts(usb_as_state_t *);
897c478bd9Sstevel@tonic-gate 
907c478bd9Sstevel@tonic-gate static void	usb_as_create_pm_components(dev_info_t *, usb_as_state_t *);
917c478bd9Sstevel@tonic-gate static int	usb_as_disconnect_event_cb(dev_info_t *);
927c478bd9Sstevel@tonic-gate static int	usb_as_reconnect_event_cb(dev_info_t *);
937c478bd9Sstevel@tonic-gate static int	usb_as_cpr_suspend(dev_info_t *);
947c478bd9Sstevel@tonic-gate static void	usb_as_cpr_resume(dev_info_t *);
957c478bd9Sstevel@tonic-gate 
9688447a05SGarrett D'Amore static int	usb_as_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
977c478bd9Sstevel@tonic-gate 
987c478bd9Sstevel@tonic-gate static int	usb_as_pwrlvl0(usb_as_state_t *);
997c478bd9Sstevel@tonic-gate static int	usb_as_pwrlvl1(usb_as_state_t *);
1007c478bd9Sstevel@tonic-gate static int	usb_as_pwrlvl2(usb_as_state_t *);
1017c478bd9Sstevel@tonic-gate static int	usb_as_pwrlvl3(usb_as_state_t *);
1027c478bd9Sstevel@tonic-gate static void	usb_as_pm_busy_component(usb_as_state_t *);
1037c478bd9Sstevel@tonic-gate static void	usb_as_pm_idle_component(usb_as_state_t *);
1047c478bd9Sstevel@tonic-gate 
1057c478bd9Sstevel@tonic-gate static void	usb_as_restore_device_state(dev_info_t *, usb_as_state_t *);
10688447a05SGarrett D'Amore static int	usb_as_setup(usb_as_state_t *);
10788447a05SGarrett D'Amore static void	usb_as_teardown(usb_as_state_t *);
10888447a05SGarrett D'Amore static int	usb_as_start_play(usb_as_state_t *, usb_audio_play_req_t *);
1097c478bd9Sstevel@tonic-gate static void	usb_as_continue_play(usb_as_state_t *);
11088447a05SGarrett D'Amore static void	usb_as_pause_play(usb_as_state_t *);
1117c478bd9Sstevel@tonic-gate 
11288447a05SGarrett D'Amore static int	usb_as_set_format(usb_as_state_t *, usb_audio_formats_t *);
11388447a05SGarrett D'Amore static int	usb_as_set_sample_freq(usb_as_state_t *, int);
1147c478bd9Sstevel@tonic-gate static int	usb_as_send_ctrl_cmd(usb_as_state_t *, uchar_t, uchar_t,
1157c478bd9Sstevel@tonic-gate 			ushort_t, ushort_t, ushort_t, mblk_t *, boolean_t);
1167c478bd9Sstevel@tonic-gate 
117e272e4c8SBinzi Cao - Sun Microsystems - Beijing China static int	usb_as_start_record(usb_as_state_t *, void *);
11888447a05SGarrett D'Amore static int	usb_as_stop_record(usb_as_state_t *);
1197c478bd9Sstevel@tonic-gate static void	usb_as_play_cb(usb_pipe_handle_t, usb_isoc_req_t *);
1207c478bd9Sstevel@tonic-gate static void	usb_as_record_cb(usb_pipe_handle_t, usb_isoc_req_t *);
1217c478bd9Sstevel@tonic-gate static void	usb_as_play_exc_cb(usb_pipe_handle_t, usb_isoc_req_t  *);
1227c478bd9Sstevel@tonic-gate static void	usb_as_record_exc_cb(usb_pipe_handle_t, usb_isoc_req_t	*);
123*64391892SAlbert Lee static int	usb_as_get_pktsize(usb_as_state_t *, usb_frame_number_t);
12488447a05SGarrett D'Amore static void	usb_as_handle_shutdown(usb_as_state_t *);
12588447a05SGarrett D'Amore static int	usb_as_play_isoc_data(usb_as_state_t *,
12688447a05SGarrett D'Amore 			usb_audio_play_req_t *);
1277c478bd9Sstevel@tonic-gate 
1287c478bd9Sstevel@tonic-gate /* anchor for soft state structures */
1297c478bd9Sstevel@tonic-gate static void	*usb_as_statep;
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate 
1327c478bd9Sstevel@tonic-gate /*
1337c478bd9Sstevel@tonic-gate  * DDI Structures
1347c478bd9Sstevel@tonic-gate  */
1357c478bd9Sstevel@tonic-gate 
1367c478bd9Sstevel@tonic-gate /* Entry points structure */
1377c478bd9Sstevel@tonic-gate static struct cb_ops usb_as_cb_ops = {
13888447a05SGarrett D'Amore 	usb_as_open,		/* cb_open */
13988447a05SGarrett D'Amore 	usb_as_close,		/* cb_close */
1407c478bd9Sstevel@tonic-gate 	nodev,			/* cb_strategy */
1417c478bd9Sstevel@tonic-gate 	nodev,			/* cb_print */
1427c478bd9Sstevel@tonic-gate 	nodev,			/* cb_dump */
1437c478bd9Sstevel@tonic-gate 	nodev,			/* cb_read */
1447c478bd9Sstevel@tonic-gate 	nodev,			/* cb_write */
14588447a05SGarrett D'Amore 	usb_as_ioctl,		/* cb_ioctl */
1467c478bd9Sstevel@tonic-gate 	nodev,			/* cb_devmap */
1477c478bd9Sstevel@tonic-gate 	nodev,			/* cb_mmap */
1487c478bd9Sstevel@tonic-gate 	nodev,			/* cb_segmap */
1497c478bd9Sstevel@tonic-gate 	nochpoll,		/* cb_chpoll */
1507c478bd9Sstevel@tonic-gate 	ddi_prop_op,		/* cb_prop_op */
15188447a05SGarrett D'Amore 	NULL,			/* cb_str */
15288447a05SGarrett D'Amore 	D_MP | D_64BIT,		/* cb_flag */
1537c478bd9Sstevel@tonic-gate 	CB_REV,			/* cb_rev */
1547c478bd9Sstevel@tonic-gate 	nodev,			/* cb_aread */
1557c478bd9Sstevel@tonic-gate 	nodev,			/* cb_arwite */
1567c478bd9Sstevel@tonic-gate };
1577c478bd9Sstevel@tonic-gate 
1587c478bd9Sstevel@tonic-gate /* Device operations structure */
1597c478bd9Sstevel@tonic-gate static struct dev_ops usb_as_dev_ops = {
1607c478bd9Sstevel@tonic-gate 	DEVO_REV,		/* devo_rev */
1617c478bd9Sstevel@tonic-gate 	0,			/* devo_refcnt */
1627c478bd9Sstevel@tonic-gate 	usb_as_getinfo,		/* devo_getinfo */
1637c478bd9Sstevel@tonic-gate 	nulldev,		/* devo_identify - obsolete */
1647c478bd9Sstevel@tonic-gate 	nulldev,		/* devo_probe - not needed */
1657c478bd9Sstevel@tonic-gate 	usb_as_attach,		/* devo_attach */
1667c478bd9Sstevel@tonic-gate 	usb_as_detach,		/* devo_detach */
1677c478bd9Sstevel@tonic-gate 	nodev,			/* devo_reset */
1687c478bd9Sstevel@tonic-gate 	&usb_as_cb_ops,		/* devi_cb_ops */
1697c478bd9Sstevel@tonic-gate 	NULL,			/* devo_busb_as_ops */
17019397407SSherry Moore 	usb_as_power,		/* devo_power */
171be529ebcSRaymond Chen 	ddi_quiesce_not_needed,	/* devo_quiesce */
1727c478bd9Sstevel@tonic-gate };
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate /* Linkage structure for loadable drivers */
1757c478bd9Sstevel@tonic-gate static struct modldrv usb_as_modldrv = {
1767c478bd9Sstevel@tonic-gate 	&mod_driverops,			/* drv_modops */
17777e51571Sgongtian zhao - Sun Microsystems - Beijing China 	"USB Audio Streaming Driver",	/* drv_linkinfo */
1787c478bd9Sstevel@tonic-gate 	&usb_as_dev_ops			/* drv_dev_ops */
1797c478bd9Sstevel@tonic-gate };
1807c478bd9Sstevel@tonic-gate 
1817c478bd9Sstevel@tonic-gate /* Module linkage structure */
1827c478bd9Sstevel@tonic-gate static struct modlinkage usb_as_modlinkage = {
1837c478bd9Sstevel@tonic-gate 	MODREV_1,			/* ml_rev */
1847c478bd9Sstevel@tonic-gate 	(void *)&usb_as_modldrv,	/* ml_linkage */
1857c478bd9Sstevel@tonic-gate 	NULL				/* NULL terminates the list */
1867c478bd9Sstevel@tonic-gate };
1877c478bd9Sstevel@tonic-gate 
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate static usb_event_t usb_as_events = {
1907c478bd9Sstevel@tonic-gate 	usb_as_disconnect_event_cb,
1917c478bd9Sstevel@tonic-gate 	usb_as_reconnect_event_cb,
1927c478bd9Sstevel@tonic-gate 	NULL, NULL
1937c478bd9Sstevel@tonic-gate };
1947c478bd9Sstevel@tonic-gate 
1957c478bd9Sstevel@tonic-gate /*
1967c478bd9Sstevel@tonic-gate  * Mixer registration Management
1977c478bd9Sstevel@tonic-gate  *	use defaults as much as possible
1987c478bd9Sstevel@tonic-gate  */
1997c478bd9Sstevel@tonic-gate 
200e272e4c8SBinzi Cao - Sun Microsystems - Beijing China _NOTE(SCHEME_PROTECTS_DATA("unique per call", mblk_t))
201e272e4c8SBinzi Cao - Sun Microsystems - Beijing China _NOTE(SCHEME_PROTECTS_DATA("unique per call", usb_isoc_req_t))
202e272e4c8SBinzi Cao - Sun Microsystems - Beijing China _NOTE(SCHEME_PROTECTS_DATA("unique per call", usb_isoc_pkt_descr))
2037c478bd9Sstevel@tonic-gate 
2047c478bd9Sstevel@tonic-gate int
_init(void)2057c478bd9Sstevel@tonic-gate _init(void)
2067c478bd9Sstevel@tonic-gate {
2077c478bd9Sstevel@tonic-gate 	int rval;
2087c478bd9Sstevel@tonic-gate 
2097c478bd9Sstevel@tonic-gate 	/* initialize the soft state */
2107c478bd9Sstevel@tonic-gate 	if ((rval = ddi_soft_state_init(&usb_as_statep,
2117c478bd9Sstevel@tonic-gate 	    sizeof (usb_as_state_t), 1)) != DDI_SUCCESS) {
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate 		return (rval);
2147c478bd9Sstevel@tonic-gate 	}
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate 	if ((rval = mod_install(&usb_as_modlinkage)) != 0) {
2177c478bd9Sstevel@tonic-gate 		ddi_soft_state_fini(&usb_as_statep);
2187c478bd9Sstevel@tonic-gate 	}
2197c478bd9Sstevel@tonic-gate 
2207c478bd9Sstevel@tonic-gate 	return (rval);
2217c478bd9Sstevel@tonic-gate }
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate 
2247c478bd9Sstevel@tonic-gate int
_fini(void)2257c478bd9Sstevel@tonic-gate _fini(void)
2267c478bd9Sstevel@tonic-gate {
2277c478bd9Sstevel@tonic-gate 	int rval;
2287c478bd9Sstevel@tonic-gate 
2297c478bd9Sstevel@tonic-gate 	if ((rval = mod_remove(&usb_as_modlinkage)) == 0) {
2307c478bd9Sstevel@tonic-gate 		/* Free the soft state internal structures */
2317c478bd9Sstevel@tonic-gate 		ddi_soft_state_fini(&usb_as_statep);
2327c478bd9Sstevel@tonic-gate 	}
2337c478bd9Sstevel@tonic-gate 
2347c478bd9Sstevel@tonic-gate 	return (rval);
2357c478bd9Sstevel@tonic-gate }
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate 
2387c478bd9Sstevel@tonic-gate int
_info(struct modinfo * modinfop)2397c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
2407c478bd9Sstevel@tonic-gate {
2417c478bd9Sstevel@tonic-gate 	return (mod_info(&usb_as_modlinkage, modinfop));
2427c478bd9Sstevel@tonic-gate }
2437c478bd9Sstevel@tonic-gate 
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate /*ARGSUSED*/
2467c478bd9Sstevel@tonic-gate static int
usb_as_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)2477c478bd9Sstevel@tonic-gate usb_as_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd,
2487c478bd9Sstevel@tonic-gate 			void *arg, void **result)
2497c478bd9Sstevel@tonic-gate {
2507c478bd9Sstevel@tonic-gate 	usb_as_state_t	*uasp = NULL;
2517c478bd9Sstevel@tonic-gate 	int		error = DDI_FAILURE;
2527c478bd9Sstevel@tonic-gate 	int		instance = USB_AS_MINOR_TO_INSTANCE(
253112116d8Sfb 	    getminor((dev_t)arg));
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate 	switch (infocmd) {
2567c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate 		if ((uasp = ddi_get_soft_state(usb_as_statep,
2597c478bd9Sstevel@tonic-gate 		    instance)) != NULL) {
2607c478bd9Sstevel@tonic-gate 			*result = uasp->usb_as_dip;
2617c478bd9Sstevel@tonic-gate 			if (*result != NULL) {
2627c478bd9Sstevel@tonic-gate 				error = DDI_SUCCESS;
2637c478bd9Sstevel@tonic-gate 			}
2647c478bd9Sstevel@tonic-gate 		} else {
2657c478bd9Sstevel@tonic-gate 			*result = NULL;
2667c478bd9Sstevel@tonic-gate 		}
2677c478bd9Sstevel@tonic-gate 		break;
2687c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
2697c478bd9Sstevel@tonic-gate 		*result = (void *)(uintptr_t)instance;
2707c478bd9Sstevel@tonic-gate 		error = DDI_SUCCESS;
2717c478bd9Sstevel@tonic-gate 		break;
2727c478bd9Sstevel@tonic-gate 	default:
2737c478bd9Sstevel@tonic-gate 		break;
2747c478bd9Sstevel@tonic-gate 	}
2757c478bd9Sstevel@tonic-gate 
2767c478bd9Sstevel@tonic-gate 	return (error);
2777c478bd9Sstevel@tonic-gate }
2787c478bd9Sstevel@tonic-gate 
2797c478bd9Sstevel@tonic-gate 
2807c478bd9Sstevel@tonic-gate static int
usb_as_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)2817c478bd9Sstevel@tonic-gate usb_as_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2827c478bd9Sstevel@tonic-gate {
2837c478bd9Sstevel@tonic-gate 	int			instance = ddi_get_instance(dip);
2847c478bd9Sstevel@tonic-gate 	usb_as_state_t		*uasp;
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate 	switch (cmd) {
2877c478bd9Sstevel@tonic-gate 		case DDI_ATTACH:
2887c478bd9Sstevel@tonic-gate 
2897c478bd9Sstevel@tonic-gate 			break;
2907c478bd9Sstevel@tonic-gate 		case DDI_RESUME:
2917c478bd9Sstevel@tonic-gate 			usb_as_cpr_resume(dip);
2927c478bd9Sstevel@tonic-gate 
2937c478bd9Sstevel@tonic-gate 			return (DDI_SUCCESS);
2947c478bd9Sstevel@tonic-gate 		default:
2957c478bd9Sstevel@tonic-gate 
2967c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
2977c478bd9Sstevel@tonic-gate 	}
2987c478bd9Sstevel@tonic-gate 
2997c478bd9Sstevel@tonic-gate 	/*
3007c478bd9Sstevel@tonic-gate 	 * Allocate soft state information.
3017c478bd9Sstevel@tonic-gate 	 */
3027c478bd9Sstevel@tonic-gate 	if (ddi_soft_state_zalloc(usb_as_statep, instance) != DDI_SUCCESS) {
3037c478bd9Sstevel@tonic-gate 
3047c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
3057c478bd9Sstevel@tonic-gate 	}
3067c478bd9Sstevel@tonic-gate 
3077c478bd9Sstevel@tonic-gate 	/*
3087c478bd9Sstevel@tonic-gate 	 * get soft state space and initialize
3097c478bd9Sstevel@tonic-gate 	 */
3107c478bd9Sstevel@tonic-gate 	uasp = (usb_as_state_t *)ddi_get_soft_state(usb_as_statep, instance);
3117c478bd9Sstevel@tonic-gate 	if (uasp == NULL) {
3127c478bd9Sstevel@tonic-gate 
3137c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
3147c478bd9Sstevel@tonic-gate 	}
3157c478bd9Sstevel@tonic-gate 
3167c478bd9Sstevel@tonic-gate 	uasp->usb_as_log_handle = usb_alloc_log_hdl(dip, "as",
317112116d8Sfb 	    &usb_as_errlevel,
318112116d8Sfb 	    &usb_as_errmask, &usb_as_instance_debug, 0);
3197c478bd9Sstevel@tonic-gate 
3207c478bd9Sstevel@tonic-gate 	uasp->usb_as_instance = instance;
3217c478bd9Sstevel@tonic-gate 	uasp->usb_as_dip = dip;
3227c478bd9Sstevel@tonic-gate 
32388447a05SGarrett D'Amore 	(void) snprintf(uasp->dstr, sizeof (uasp->dstr), "%s#%d",
32488447a05SGarrett D'Amore 	    ddi_driver_name(dip), instance);
32588447a05SGarrett D'Amore 
3267c478bd9Sstevel@tonic-gate 	if (usb_client_attach(dip, USBDRV_VERSION, 0) != USB_SUCCESS) {
3277c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ATTA, uasp->usb_as_log_handle,
3287c478bd9Sstevel@tonic-gate 		    "usb_client_attach failed");
3297c478bd9Sstevel@tonic-gate 
3307c478bd9Sstevel@tonic-gate 		usb_free_log_hdl(uasp->usb_as_log_handle);
3317c478bd9Sstevel@tonic-gate 		ddi_soft_state_free(usb_as_statep, uasp->usb_as_instance);
3327c478bd9Sstevel@tonic-gate 
3337c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
3347c478bd9Sstevel@tonic-gate 	}
3357c478bd9Sstevel@tonic-gate 
3367c478bd9Sstevel@tonic-gate 	if (usb_get_dev_data(dip, &uasp->usb_as_dev_data,
3377c478bd9Sstevel@tonic-gate 	    USB_PARSE_LVL_IF, 0) != USB_SUCCESS) {
3387c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ATTA, uasp->usb_as_log_handle,
3397c478bd9Sstevel@tonic-gate 		    "usb_get_dev_data failed");
3407c478bd9Sstevel@tonic-gate 		usb_client_detach(dip, NULL);
3417c478bd9Sstevel@tonic-gate 		usb_free_log_hdl(uasp->usb_as_log_handle);
3427c478bd9Sstevel@tonic-gate 		ddi_soft_state_free(usb_as_statep, uasp->usb_as_instance);
3437c478bd9Sstevel@tonic-gate 
3447c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
3457c478bd9Sstevel@tonic-gate 	}
3467c478bd9Sstevel@tonic-gate 
3477c478bd9Sstevel@tonic-gate 	/* initialize mutex */
3487c478bd9Sstevel@tonic-gate 	mutex_init(&uasp->usb_as_mutex, NULL, MUTEX_DRIVER,
349112116d8Sfb 	    uasp->usb_as_dev_data->dev_iblock_cookie);
350e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 
351e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 	cv_init(&uasp->usb_as_pipe_cv, NULL, CV_DRIVER, NULL);
352e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 
3537c478bd9Sstevel@tonic-gate 	uasp->usb_as_ser_acc = usb_init_serialization(dip,
3547c478bd9Sstevel@tonic-gate 	    USB_INIT_SER_CHECK_SAME_THREAD);
3557c478bd9Sstevel@tonic-gate 
3567c478bd9Sstevel@tonic-gate 	uasp->usb_as_default_ph = uasp->usb_as_dev_data->dev_default_ph;
3577c478bd9Sstevel@tonic-gate 	uasp->usb_as_isoc_pp.pp_max_async_reqs = 1;
3587c478bd9Sstevel@tonic-gate 
3597c478bd9Sstevel@tonic-gate 	/* parse all descriptors */
3607c478bd9Sstevel@tonic-gate 	if (usb_as_handle_descriptors(uasp) != USB_SUCCESS) {
3617c478bd9Sstevel@tonic-gate 
3627c478bd9Sstevel@tonic-gate 		goto fail;
3637c478bd9Sstevel@tonic-gate 	}
3647c478bd9Sstevel@tonic-gate 
3657c478bd9Sstevel@tonic-gate 	usb_free_descr_tree(dip, uasp->usb_as_dev_data);
3667c478bd9Sstevel@tonic-gate 
3677c478bd9Sstevel@tonic-gate 	if ((ddi_create_minor_node(dip, "usb_as", S_IFCHR,
3687c478bd9Sstevel@tonic-gate 	    USB_AS_CONSTRUCT_MINOR(instance),
3697c478bd9Sstevel@tonic-gate 	    NULL, 0)) != DDI_SUCCESS) {
370d291d9f2Sfrits 		USB_DPRINTF_L2(PRINT_MASK_ATTA, uasp->usb_as_log_handle,
3717c478bd9Sstevel@tonic-gate 		    "usb_as_attach: couldn't create minor node");
3727c478bd9Sstevel@tonic-gate 
3737c478bd9Sstevel@tonic-gate 		goto fail;
3747c478bd9Sstevel@tonic-gate 	}
3757c478bd9Sstevel@tonic-gate 
3767c478bd9Sstevel@tonic-gate 	/* we are online */
3777c478bd9Sstevel@tonic-gate 	uasp->usb_as_dev_state = USB_DEV_ONLINE;
3787c478bd9Sstevel@tonic-gate 
3797c478bd9Sstevel@tonic-gate 	/* create components to power manage this device */
3807c478bd9Sstevel@tonic-gate 	usb_as_create_pm_components(dip, uasp);
3817c478bd9Sstevel@tonic-gate 
3827c478bd9Sstevel@tonic-gate 	/* Register for events */
3837c478bd9Sstevel@tonic-gate 	if (usb_register_event_cbs(dip, &usb_as_events, 0) != USB_SUCCESS) {
384d291d9f2Sfrits 		USB_DPRINTF_L2(PRINT_MASK_ATTA, uasp->usb_as_log_handle,
3857c478bd9Sstevel@tonic-gate 		    "usb_as_attach: couldn't register for events");
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate 		goto fail;
3887c478bd9Sstevel@tonic-gate 	}
3897c478bd9Sstevel@tonic-gate 
3907c478bd9Sstevel@tonic-gate 	/* report device */
3917c478bd9Sstevel@tonic-gate 	ddi_report_dev(dip);
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, uasp->usb_as_log_handle,
3947c478bd9Sstevel@tonic-gate 	    "usb_as_attach: End");
3957c478bd9Sstevel@tonic-gate 
3967c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
3977c478bd9Sstevel@tonic-gate 
3987c478bd9Sstevel@tonic-gate fail:
3997c478bd9Sstevel@tonic-gate 	if (uasp) {
400d291d9f2Sfrits 		USB_DPRINTF_L2(PRINT_MASK_ATTA, uasp->usb_as_log_handle,
4017c478bd9Sstevel@tonic-gate 		    "attach failed");
4027c478bd9Sstevel@tonic-gate 		usb_as_cleanup(dip, uasp);
4037c478bd9Sstevel@tonic-gate 	}
4047c478bd9Sstevel@tonic-gate 
4057c478bd9Sstevel@tonic-gate 	return (DDI_FAILURE);
4067c478bd9Sstevel@tonic-gate }
4077c478bd9Sstevel@tonic-gate 
4087c478bd9Sstevel@tonic-gate 
4097c478bd9Sstevel@tonic-gate /*ARGSUSED*/
4107c478bd9Sstevel@tonic-gate static int
usb_as_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)4117c478bd9Sstevel@tonic-gate usb_as_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
4127c478bd9Sstevel@tonic-gate {
4137c478bd9Sstevel@tonic-gate 	int instance = ddi_get_instance(dip);
4147c478bd9Sstevel@tonic-gate 	usb_as_state_t	*uasp;
4157c478bd9Sstevel@tonic-gate 	int rval;
4167c478bd9Sstevel@tonic-gate 
4177c478bd9Sstevel@tonic-gate 	uasp = ddi_get_soft_state(usb_as_statep, instance);
4187c478bd9Sstevel@tonic-gate 
4197c478bd9Sstevel@tonic-gate 	switch (cmd) {
4207c478bd9Sstevel@tonic-gate 	case DDI_DETACH:
4217c478bd9Sstevel@tonic-gate 		usb_as_cleanup(dip, uasp);
4227c478bd9Sstevel@tonic-gate 
4237c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
4247c478bd9Sstevel@tonic-gate 	case DDI_SUSPEND:
4257c478bd9Sstevel@tonic-gate 		rval = usb_as_cpr_suspend(dip);
4267c478bd9Sstevel@tonic-gate 
4277c478bd9Sstevel@tonic-gate 		return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
4287c478bd9Sstevel@tonic-gate 	default:
4297c478bd9Sstevel@tonic-gate 
4307c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
4317c478bd9Sstevel@tonic-gate 	}
4327c478bd9Sstevel@tonic-gate }
4337c478bd9Sstevel@tonic-gate 
4347c478bd9Sstevel@tonic-gate 
4357c478bd9Sstevel@tonic-gate static void
usb_as_cleanup(dev_info_t * dip,usb_as_state_t * uasp)4367c478bd9Sstevel@tonic-gate usb_as_cleanup(dev_info_t *dip, usb_as_state_t *uasp)
4377c478bd9Sstevel@tonic-gate {
4387c478bd9Sstevel@tonic-gate 	usb_as_power_t	*uaspm;
4397c478bd9Sstevel@tonic-gate 
4407c478bd9Sstevel@tonic-gate 	if (uasp == NULL) {
4417c478bd9Sstevel@tonic-gate 
4427c478bd9Sstevel@tonic-gate 		return;
4437c478bd9Sstevel@tonic-gate 	}
4447c478bd9Sstevel@tonic-gate 
4457c478bd9Sstevel@tonic-gate 	uaspm = uasp->usb_as_pm;
4467c478bd9Sstevel@tonic-gate 
4477c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, uasp->usb_as_log_handle,
448112116d8Sfb 	    "usb_as_cleanup: uaspm=0x%p", (void *)uaspm);
4497c478bd9Sstevel@tonic-gate 
4506918308bSyz 	if (uasp->usb_as_isoc_ph) {
4516918308bSyz 		usb_pipe_close(dip, uasp->usb_as_isoc_ph,
4526918308bSyz 		    USB_FLAGS_SLEEP, NULL, NULL);
4536918308bSyz 	}
4547c478bd9Sstevel@tonic-gate 	/*
4557c478bd9Sstevel@tonic-gate 	 * Disable the event callbacks first, after this point, event
4567c478bd9Sstevel@tonic-gate 	 * callbacks will never get called. Note we shouldn't hold
4577c478bd9Sstevel@tonic-gate 	 * mutex while unregistering events because there may be a
4587c478bd9Sstevel@tonic-gate 	 * competing event callback thread. Event callbacks are done
4597c478bd9Sstevel@tonic-gate 	 * with ndi mutex held and this can cause a potential deadlock.
4607c478bd9Sstevel@tonic-gate 	 */
4617c478bd9Sstevel@tonic-gate 	usb_unregister_event_cbs(dip, &usb_as_events);
4627c478bd9Sstevel@tonic-gate 
4637c478bd9Sstevel@tonic-gate 	mutex_enter(&uasp->usb_as_mutex);
4647c478bd9Sstevel@tonic-gate 
4657c478bd9Sstevel@tonic-gate 	if (uaspm && (uasp->usb_as_dev_state != USB_DEV_DISCONNECTED)) {
4667c478bd9Sstevel@tonic-gate 		if (uaspm->aspm_wakeup_enabled) {
4677c478bd9Sstevel@tonic-gate 			mutex_exit(&uasp->usb_as_mutex);
4687c478bd9Sstevel@tonic-gate 
4697c478bd9Sstevel@tonic-gate 			/*
4707c478bd9Sstevel@tonic-gate 			 * We need to raise power first because
4717c478bd9Sstevel@tonic-gate 			 * we need to send down a command to disable
4727c478bd9Sstevel@tonic-gate 			 * remote wakeup
4737c478bd9Sstevel@tonic-gate 			 */
4747c478bd9Sstevel@tonic-gate 			usb_as_pm_busy_component(uasp);
4757c478bd9Sstevel@tonic-gate 			(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
4767c478bd9Sstevel@tonic-gate 
4777c478bd9Sstevel@tonic-gate 			if (usb_handle_remote_wakeup(dip,
4787c478bd9Sstevel@tonic-gate 			    USB_REMOTE_WAKEUP_DISABLE)) {
4797c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(PRINT_MASK_ALL,
4807c478bd9Sstevel@tonic-gate 				    uasp->usb_as_log_handle,
4817c478bd9Sstevel@tonic-gate 				    "disable remote wake up failed");
4827c478bd9Sstevel@tonic-gate 			}
4837c478bd9Sstevel@tonic-gate 			usb_as_pm_idle_component(uasp);
4847c478bd9Sstevel@tonic-gate 		} else {
4857c478bd9Sstevel@tonic-gate 			mutex_exit(&uasp->usb_as_mutex);
4867c478bd9Sstevel@tonic-gate 		}
4877c478bd9Sstevel@tonic-gate 
4887c478bd9Sstevel@tonic-gate 		(void) pm_lower_power(dip, 0, USB_DEV_OS_PWR_OFF);
4897c478bd9Sstevel@tonic-gate 
4907c478bd9Sstevel@tonic-gate 		mutex_enter(&uasp->usb_as_mutex);
4917c478bd9Sstevel@tonic-gate 	}
4927c478bd9Sstevel@tonic-gate 
4937c478bd9Sstevel@tonic-gate 	if (uaspm) {
4947c478bd9Sstevel@tonic-gate 		kmem_free(uaspm, sizeof (usb_as_power_t));
4957c478bd9Sstevel@tonic-gate 		uasp->usb_as_pm = NULL;
4967c478bd9Sstevel@tonic-gate 	}
4977c478bd9Sstevel@tonic-gate 
4987c478bd9Sstevel@tonic-gate 	usb_client_detach(dip, uasp->usb_as_dev_data);
4997c478bd9Sstevel@tonic-gate 
5007c478bd9Sstevel@tonic-gate 	usb_as_free_alts(uasp);
5017c478bd9Sstevel@tonic-gate 
5027c478bd9Sstevel@tonic-gate 	mutex_exit(&uasp->usb_as_mutex);
5037c478bd9Sstevel@tonic-gate 	mutex_destroy(&uasp->usb_as_mutex);
5047c478bd9Sstevel@tonic-gate 
5057c478bd9Sstevel@tonic-gate 	usb_fini_serialization(uasp->usb_as_ser_acc);
5067c478bd9Sstevel@tonic-gate 
5077c478bd9Sstevel@tonic-gate 	ddi_remove_minor_node(dip, NULL);
5087c478bd9Sstevel@tonic-gate 	usb_free_log_hdl(uasp->usb_as_log_handle);
5097c478bd9Sstevel@tonic-gate 	ddi_soft_state_free(usb_as_statep, uasp->usb_as_instance);
5107c478bd9Sstevel@tonic-gate 
5117c478bd9Sstevel@tonic-gate 	ddi_prop_remove_all(dip);
5127c478bd9Sstevel@tonic-gate }
5137c478bd9Sstevel@tonic-gate 
5147c478bd9Sstevel@tonic-gate 
5157c478bd9Sstevel@tonic-gate /*
5167c478bd9Sstevel@tonic-gate  * usb_as_open:
5177c478bd9Sstevel@tonic-gate  *	Open entry point for plumbing only
5187c478bd9Sstevel@tonic-gate  */
5197c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5207c478bd9Sstevel@tonic-gate static int
usb_as_open(dev_t * devp,int flag,int otyp,cred_t * credp)52188447a05SGarrett D'Amore usb_as_open(dev_t *devp, int flag, int otyp, cred_t *credp)
5227c478bd9Sstevel@tonic-gate {
52388447a05SGarrett D'Amore 	int		inst = USB_AS_MINOR_TO_INSTANCE(getminor(*devp));
52488447a05SGarrett D'Amore 	usb_as_state_t	*uasp = ddi_get_soft_state(usb_as_statep, inst);
52588447a05SGarrett D'Amore 
5267c478bd9Sstevel@tonic-gate 	if (uasp == NULL) {
5277c478bd9Sstevel@tonic-gate 
5287c478bd9Sstevel@tonic-gate 		return (ENXIO);
5297c478bd9Sstevel@tonic-gate 	}
5307c478bd9Sstevel@tonic-gate 
5317c478bd9Sstevel@tonic-gate 	/* Do mux plumbing stuff */
5321cdd1aafSBinzi Cao - Sun Microsystems - Beijing China 	USB_DPRINTF_L4(PRINT_MASK_OPEN, uasp->usb_as_log_handle,
5331cdd1aafSBinzi Cao - Sun Microsystems - Beijing China 	    "usb_as_open: start");
53488447a05SGarrett D'Amore 
53588447a05SGarrett D'Amore 	mutex_enter(&uasp->usb_as_mutex);
5367c478bd9Sstevel@tonic-gate 
53788447a05SGarrett D'Amore 	if (uasp->usb_as_flag == USB_AS_OPEN || credp != kcred) {
5381cdd1aafSBinzi Cao - Sun Microsystems - Beijing China 		USB_DPRINTF_L2(PRINT_MASK_OPEN, uasp->usb_as_log_handle,
5391cdd1aafSBinzi Cao - Sun Microsystems - Beijing China 		    "usb_as_open:multiple opens or opens from userspace"
5401cdd1aafSBinzi Cao - Sun Microsystems - Beijing China 		    " not supported");
5411cdd1aafSBinzi Cao - Sun Microsystems - Beijing China 
54288447a05SGarrett D'Amore 		mutex_exit(&uasp->usb_as_mutex);
5437c478bd9Sstevel@tonic-gate 
5447c478bd9Sstevel@tonic-gate 		return (ENXIO);
5457c478bd9Sstevel@tonic-gate 	}
5467c478bd9Sstevel@tonic-gate 
5477c478bd9Sstevel@tonic-gate 	/* fail open on a disconnected device */
5487c478bd9Sstevel@tonic-gate 	if (uasp->usb_as_dev_state == USB_DEV_DISCONNECTED) {
5491cdd1aafSBinzi Cao - Sun Microsystems - Beijing China 		USB_DPRINTF_L2(PRINT_MASK_OPEN, uasp->usb_as_log_handle,
5501cdd1aafSBinzi Cao - Sun Microsystems - Beijing China 		    "usb_as_open: disconnected");
5517c478bd9Sstevel@tonic-gate 		mutex_exit(&uasp->usb_as_mutex);
5527c478bd9Sstevel@tonic-gate 
5537c478bd9Sstevel@tonic-gate 		return (ENODEV);
5547c478bd9Sstevel@tonic-gate 	}
5557c478bd9Sstevel@tonic-gate 
55688447a05SGarrett D'Amore 	/* Initialize state */
55788447a05SGarrett D'Amore 	uasp->usb_as_flag = USB_AS_OPEN;
5587c478bd9Sstevel@tonic-gate 	mutex_exit(&uasp->usb_as_mutex);
5597c478bd9Sstevel@tonic-gate 
5607c478bd9Sstevel@tonic-gate 	/*
5617c478bd9Sstevel@tonic-gate 	 * go to full power, and remain pm_busy till close
5627c478bd9Sstevel@tonic-gate 	 */
5637c478bd9Sstevel@tonic-gate 	usb_as_pm_busy_component(uasp);
5647c478bd9Sstevel@tonic-gate 	(void) pm_raise_power(uasp->usb_as_dip, 0, USB_DEV_OS_FULL_PWR);
5657c478bd9Sstevel@tonic-gate 
5661cdd1aafSBinzi Cao - Sun Microsystems - Beijing China 	USB_DPRINTF_L4(PRINT_MASK_OPEN, uasp->usb_as_log_handle,
5671cdd1aafSBinzi Cao - Sun Microsystems - Beijing China 	    "usb_as_open:done");
5687c478bd9Sstevel@tonic-gate 
5697c478bd9Sstevel@tonic-gate 	return (0);
5707c478bd9Sstevel@tonic-gate }
5717c478bd9Sstevel@tonic-gate 
5727c478bd9Sstevel@tonic-gate 
5737c478bd9Sstevel@tonic-gate /*
5747c478bd9Sstevel@tonic-gate  * usb_as_close:
5757c478bd9Sstevel@tonic-gate  *	Close entry point for plumbing
5767c478bd9Sstevel@tonic-gate  */
5777c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5787c478bd9Sstevel@tonic-gate static int
usb_as_close(dev_t dev,int flag,int otyp,cred_t * credp)57988447a05SGarrett D'Amore usb_as_close(dev_t dev, int flag, int otyp, cred_t *credp)
5807c478bd9Sstevel@tonic-gate {
58188447a05SGarrett D'Amore 	int		inst = USB_AS_MINOR_TO_INSTANCE(getminor(dev));
58288447a05SGarrett D'Amore 	usb_as_state_t	*uasp = ddi_get_soft_state(usb_as_statep, inst);
5837c478bd9Sstevel@tonic-gate 
5847c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, uasp->usb_as_log_handle,
58588447a05SGarrett D'Amore 	    "usb_as_close: inst=%d", inst);
5867c478bd9Sstevel@tonic-gate 
5877c478bd9Sstevel@tonic-gate 	mutex_enter(&uasp->usb_as_mutex);
58888447a05SGarrett D'Amore 	uasp->usb_as_flag = USB_AS_DISMANTLING;
5897c478bd9Sstevel@tonic-gate 	mutex_exit(&uasp->usb_as_mutex);
5907c478bd9Sstevel@tonic-gate 
5917c478bd9Sstevel@tonic-gate 	/*
5927c478bd9Sstevel@tonic-gate 	 * Avoid races with other routines.
5937c478bd9Sstevel@tonic-gate 	 * For example, if a control transfer is going on, wait
5947c478bd9Sstevel@tonic-gate 	 * for that to be completed
5957c478bd9Sstevel@tonic-gate 	 * At this point default pipe cannot be open.
5967c478bd9Sstevel@tonic-gate 	 */
5977c478bd9Sstevel@tonic-gate 	(void) usb_serialize_access(uasp->usb_as_ser_acc, USB_WAIT, 0);
5987c478bd9Sstevel@tonic-gate 
5997c478bd9Sstevel@tonic-gate 	usb_release_access(uasp->usb_as_ser_acc);
6007c478bd9Sstevel@tonic-gate 
6017c478bd9Sstevel@tonic-gate 	/* we can now power down */
6027c478bd9Sstevel@tonic-gate 	usb_as_pm_idle_component(uasp);
6037c478bd9Sstevel@tonic-gate 
6047c478bd9Sstevel@tonic-gate 	mutex_enter(&uasp->usb_as_mutex);
60588447a05SGarrett D'Amore 	uasp->usb_as_flag = 0;
6067c478bd9Sstevel@tonic-gate 	mutex_exit(&uasp->usb_as_mutex);
6077c478bd9Sstevel@tonic-gate 
60888447a05SGarrett D'Amore 	return (0);
6097c478bd9Sstevel@tonic-gate }
6107c478bd9Sstevel@tonic-gate 
6117c478bd9Sstevel@tonic-gate 
6127c478bd9Sstevel@tonic-gate /*
61388447a05SGarrett D'Amore  *
6147c478bd9Sstevel@tonic-gate  */
61588447a05SGarrett D'Amore /*ARGSUSED*/
6167c478bd9Sstevel@tonic-gate static int
usb_as_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)61788447a05SGarrett D'Amore usb_as_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
61888447a05SGarrett D'Amore     int *rvalp)
6197c478bd9Sstevel@tonic-gate {
62088447a05SGarrett D'Amore 	int		inst = USB_AS_MINOR_TO_INSTANCE(getminor(dev));
62188447a05SGarrett D'Amore 	usb_as_state_t	*uasp = ddi_get_soft_state(usb_as_statep, inst);
62288447a05SGarrett D'Amore 	int		rv = USB_SUCCESS;
6237c478bd9Sstevel@tonic-gate 
6247c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, uasp->usb_as_log_handle,
62588447a05SGarrett D'Amore 	    "usb_as_ioctl: Begin inst=%d, cmd=0x%x, arg=0x%p",
62688447a05SGarrett D'Amore 	    inst, cmd, (void *)arg);
6277c478bd9Sstevel@tonic-gate 
62888447a05SGarrett D'Amore 	if (!(mode & FKIOCTL)) {
6297c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ALL, uasp->usb_as_log_handle,
63088447a05SGarrett D'Amore 		    "usb_as_ioctl: inst=%d, user space not supported", inst);
63188447a05SGarrett D'Amore 		return (ENXIO);
6327c478bd9Sstevel@tonic-gate 	}
6337c478bd9Sstevel@tonic-gate 
63488447a05SGarrett D'Amore 	mutex_enter(&uasp->usb_as_mutex);
6357c478bd9Sstevel@tonic-gate 
63688447a05SGarrett D'Amore 	switch (cmd) {
63788447a05SGarrett D'Amore 	case USB_AUDIO_MIXER_REGISTRATION:
63888447a05SGarrett D'Amore 		USB_DPRINTF_L4(PRINT_MASK_ALL, uasp->usb_as_log_handle,
63988447a05SGarrett D'Amore 		    "usb_as_ioctl(mixer reg): inst=%d", inst);
6407c478bd9Sstevel@tonic-gate 
64188447a05SGarrett D'Amore 		/*
64288447a05SGarrett D'Amore 		 * Copy the usb_as_reg structure to the structure
64388447a05SGarrett D'Amore 		 * that usb_ac passed. Note that this is a structure
64488447a05SGarrett D'Amore 		 * assignment and not a pointer assignment!
64588447a05SGarrett D'Amore 		 */
64688447a05SGarrett D'Amore 		*(usb_as_registration_t *)arg = uasp->usb_as_reg;
6477c478bd9Sstevel@tonic-gate 
64888447a05SGarrett D'Amore 		break;
64988447a05SGarrett D'Amore 	case USB_AUDIO_SET_FORMAT:
65088447a05SGarrett D'Amore 		rv = usb_as_set_format(uasp, (usb_audio_formats_t *)arg);
65188447a05SGarrett D'Amore 		break;
65288447a05SGarrett D'Amore 	case USB_AUDIO_SET_SAMPLE_FREQ:
65388447a05SGarrett D'Amore 		rv = usb_as_set_sample_freq(uasp, *(int *)arg);
65488447a05SGarrett D'Amore 		break;
65588447a05SGarrett D'Amore 	case USB_AUDIO_SETUP:
65688447a05SGarrett D'Amore 		rv = usb_as_setup(uasp);
65788447a05SGarrett D'Amore 		break;
65888447a05SGarrett D'Amore 	case USB_AUDIO_TEARDOWN:
65988447a05SGarrett D'Amore 		usb_as_teardown(uasp);
66088447a05SGarrett D'Amore 		break;
66188447a05SGarrett D'Amore 	case USB_AUDIO_START_PLAY:
66288447a05SGarrett D'Amore 		rv = usb_as_start_play(uasp, (usb_audio_play_req_t *)arg);
66388447a05SGarrett D'Amore 		break;
66488447a05SGarrett D'Amore 	case USB_AUDIO_STOP_PLAY:
66588447a05SGarrett D'Amore 	case USB_AUDIO_PAUSE_PLAY:
66688447a05SGarrett D'Amore 		usb_as_pause_play(uasp);
66788447a05SGarrett D'Amore 		break;
66888447a05SGarrett D'Amore 	case USB_AUDIO_START_RECORD:
669e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 		rv = usb_as_start_record(uasp, (void *)arg);
67088447a05SGarrett D'Amore 		break;
67188447a05SGarrett D'Amore 	case USB_AUDIO_STOP_RECORD:
67288447a05SGarrett D'Amore 		rv = usb_as_stop_record(uasp);
67388447a05SGarrett D'Amore 		break;
67488447a05SGarrett D'Amore 	default:
67588447a05SGarrett D'Amore 		USB_DPRINTF_L2(PRINT_MASK_ALL, uasp->usb_as_log_handle,
67688447a05SGarrett D'Amore 		    "usb_as_ioctl: unknown IOCTL, cmd=%d", cmd);
67788447a05SGarrett D'Amore 		break;
6787c478bd9Sstevel@tonic-gate 	}
6797c478bd9Sstevel@tonic-gate 
6807c478bd9Sstevel@tonic-gate 	mutex_exit(&uasp->usb_as_mutex);
6817c478bd9Sstevel@tonic-gate 
68288447a05SGarrett D'Amore 	return (rv == USB_SUCCESS ? 0 : ENXIO);
6837c478bd9Sstevel@tonic-gate }
6847c478bd9Sstevel@tonic-gate 
6857c478bd9Sstevel@tonic-gate 
6867c478bd9Sstevel@tonic-gate /*
6877c478bd9Sstevel@tonic-gate  * usb_as_set_sample_freq:
6887c478bd9Sstevel@tonic-gate  *	Sets the sample freq by sending a control command to interface
6897c478bd9Sstevel@tonic-gate  *	Although not required for continuous sample rate devices, some
6907c478bd9Sstevel@tonic-gate  *	devices such as plantronics devices do need this.
6917c478bd9Sstevel@tonic-gate  *	On the other hand, the TI chip which does not support continuous
6927c478bd9Sstevel@tonic-gate  *	sample rate stalls on this request
6937c478bd9Sstevel@tonic-gate  *	Therefore, we ignore errors and carry on regardless
6947c478bd9Sstevel@tonic-gate  */
6957c478bd9Sstevel@tonic-gate static int
usb_as_set_sample_freq(usb_as_state_t * uasp,int freq)69688447a05SGarrett D'Amore usb_as_set_sample_freq(usb_as_state_t *uasp, int freq)
6977c478bd9Sstevel@tonic-gate {
69888447a05SGarrett D'Amore 	int	alt, ep;
6997c478bd9Sstevel@tonic-gate 	mblk_t	*data;
7007c478bd9Sstevel@tonic-gate 	int	rval = USB_FAILURE;
7017c478bd9Sstevel@tonic-gate 	boolean_t ignore_errors;
7027c478bd9Sstevel@tonic-gate 
7037c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uasp->usb_as_mutex));
7047c478bd9Sstevel@tonic-gate 
7057c478bd9Sstevel@tonic-gate 	alt = uasp->usb_as_alternate;
7067c478bd9Sstevel@tonic-gate 
707*64391892SAlbert Lee 	uasp->usb_as_curr_sr = freq;
708*64391892SAlbert Lee 
7097c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, uasp->usb_as_log_handle,
71088447a05SGarrett D'Amore 	    "usb_as_set_sample_freq: inst=%d cont_sr=%d freq=%d",
71188447a05SGarrett D'Amore 	    ddi_get_instance(uasp->usb_as_dip),
71288447a05SGarrett D'Amore 	    uasp->usb_as_alts[alt].alt_continuous_sr, freq);
7137c478bd9Sstevel@tonic-gate 
7147c478bd9Sstevel@tonic-gate 	ignore_errors = B_TRUE;
7157c478bd9Sstevel@tonic-gate 
7167c478bd9Sstevel@tonic-gate 	ep = uasp->usb_as_alts[alt].alt_ep->bEndpointAddress;
7177c478bd9Sstevel@tonic-gate 
7187c478bd9Sstevel@tonic-gate 	data = allocb(4, BPRI_HI);
7197c478bd9Sstevel@tonic-gate 	if (data) {
7207c478bd9Sstevel@tonic-gate 		*(data->b_wptr++) = (char)freq;
7217c478bd9Sstevel@tonic-gate 		*(data->b_wptr++) = (char)(freq >> 8);
7227c478bd9Sstevel@tonic-gate 		*(data->b_wptr++) = (char)(freq >> 16);
7237c478bd9Sstevel@tonic-gate 
7247c478bd9Sstevel@tonic-gate 		mutex_exit(&uasp->usb_as_mutex);
7257c478bd9Sstevel@tonic-gate 
7267c478bd9Sstevel@tonic-gate 		if ((rval = usb_as_send_ctrl_cmd(uasp,
7277c478bd9Sstevel@tonic-gate 		    USB_DEV_REQ_HOST_TO_DEV |
7287c478bd9Sstevel@tonic-gate 		    USB_DEV_REQ_TYPE_CLASS |
7297c478bd9Sstevel@tonic-gate 		    USB_DEV_REQ_RCPT_EP,		/* bmRequestType */
7307c478bd9Sstevel@tonic-gate 		    USB_AUDIO_SET_CUR,			/* bRequest */
7317c478bd9Sstevel@tonic-gate 		    USB_AUDIO_SAMPLING_FREQ_CONTROL << 8, /* wValue */
7327c478bd9Sstevel@tonic-gate 		    ep,					/* wIndex */
7337c478bd9Sstevel@tonic-gate 		    3,					/* wLength */
7347c478bd9Sstevel@tonic-gate 		    data,
7357c478bd9Sstevel@tonic-gate 		    ignore_errors)) != USB_SUCCESS) {
7367c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ALL, uasp->usb_as_log_handle,
7377c478bd9Sstevel@tonic-gate 			    "usb_as_set_sample_freq: set sample freq failed");
7387c478bd9Sstevel@tonic-gate 		}
7397c478bd9Sstevel@tonic-gate 		mutex_enter(&uasp->usb_as_mutex);
7407c478bd9Sstevel@tonic-gate 	}
74188447a05SGarrett D'Amore 	freemsg(data);
7427c478bd9Sstevel@tonic-gate 
7437c478bd9Sstevel@tonic-gate 	return (rval);
7447c478bd9Sstevel@tonic-gate }
7457c478bd9Sstevel@tonic-gate 
7467c478bd9Sstevel@tonic-gate 
7477c478bd9Sstevel@tonic-gate /*
7487c478bd9Sstevel@tonic-gate  * usb_as_set_format:
7497c478bd9Sstevel@tonic-gate  *	Matches channel, encoding and precision and find out
75088447a05SGarrett D'Amore  *	the right alternate. Sets alternate interface and returns it.
7517c478bd9Sstevel@tonic-gate  */
7527c478bd9Sstevel@tonic-gate static int
usb_as_set_format(usb_as_state_t * uasp,usb_audio_formats_t * format)75388447a05SGarrett D'Amore usb_as_set_format(usb_as_state_t *uasp, usb_audio_formats_t *format)
7547c478bd9Sstevel@tonic-gate {
7557c478bd9Sstevel@tonic-gate 	int		n;
7567c478bd9Sstevel@tonic-gate 	usb_as_registration_t *reg;
7577c478bd9Sstevel@tonic-gate 	int		alt, rval;
7587c478bd9Sstevel@tonic-gate 	uint_t		interface;
7597c478bd9Sstevel@tonic-gate 
7607c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uasp->usb_as_mutex));
7617c478bd9Sstevel@tonic-gate 
7627c478bd9Sstevel@tonic-gate 	if (uasp->usb_as_request_count) {
7637c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ALL, uasp->usb_as_log_handle,
76488447a05SGarrett D'Amore 		    "usb_as_set_format: failing inst=%d, rq_cnt=%d",
76588447a05SGarrett D'Amore 		    ddi_get_instance(uasp->usb_as_dip),
76688447a05SGarrett D'Amore 		    uasp->usb_as_request_count);
7677c478bd9Sstevel@tonic-gate 
7687c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
7697c478bd9Sstevel@tonic-gate 	}
7707c478bd9Sstevel@tonic-gate 
7717c478bd9Sstevel@tonic-gate 	reg = &uasp->usb_as_reg;
7727c478bd9Sstevel@tonic-gate 	interface = uasp->usb_as_ifno;
7737c478bd9Sstevel@tonic-gate 
77488447a05SGarrett D'Amore 	uasp->usb_as_curr_format = *format;
7757c478bd9Sstevel@tonic-gate 
7767c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, uasp->usb_as_log_handle,
77788447a05SGarrett D'Amore 	    "usb_as_set_format: inst=%d, reg=0x%p, format=0x%p",
77888447a05SGarrett D'Amore 	    ddi_get_instance(uasp->usb_as_dip), (void *)reg, (void *)format);
7797c478bd9Sstevel@tonic-gate 
7807c478bd9Sstevel@tonic-gate 	for (n = 0; n < reg->reg_n_formats; n++) {
7817c478bd9Sstevel@tonic-gate 		if ((format->fmt_chns == reg->reg_formats[n].fmt_chns) &&
7827c478bd9Sstevel@tonic-gate 		    (format->fmt_precision == reg->reg_formats[n].
7837c478bd9Sstevel@tonic-gate 		    fmt_precision) && (format->fmt_encoding ==
7847c478bd9Sstevel@tonic-gate 		    reg->reg_formats[n].fmt_encoding)) {
785*64391892SAlbert Lee 			int i;
786*64391892SAlbert Lee 			int n_srs = reg->reg_formats[n].fmt_n_srs;
787*64391892SAlbert Lee 			uint_t *srs = reg->reg_formats[n].fmt_srs;
788*64391892SAlbert Lee 
789*64391892SAlbert Lee 			/* match sample rate */
790*64391892SAlbert Lee 			for (i = 0; i < n_srs; i++) {
791*64391892SAlbert Lee 				if (format->fmt_srs[0] == srs[i]) {
792*64391892SAlbert Lee 
793*64391892SAlbert Lee 					break;
794*64391892SAlbert Lee 				}
795*64391892SAlbert Lee 			}
796*64391892SAlbert Lee 
797*64391892SAlbert Lee 			if (i == n_srs) {
798*64391892SAlbert Lee 
799*64391892SAlbert Lee 				continue;
800*64391892SAlbert Lee 			}
801*64391892SAlbert Lee 
8027c478bd9Sstevel@tonic-gate 			/*
8037c478bd9Sstevel@tonic-gate 			 * Found the alternate
8047c478bd9Sstevel@tonic-gate 			 */
8057c478bd9Sstevel@tonic-gate 			uasp->usb_as_alternate = alt =
806112116d8Sfb 			    reg->reg_formats[n].fmt_alt;
8077c478bd9Sstevel@tonic-gate 			break;
8087c478bd9Sstevel@tonic-gate 		}
8097c478bd9Sstevel@tonic-gate 	}
8107c478bd9Sstevel@tonic-gate 
81188447a05SGarrett D'Amore 	if (n >= reg->reg_n_formats) {
8127c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ALL, uasp->usb_as_log_handle,
8137c478bd9Sstevel@tonic-gate 		    "usb_as_set_format: Didn't find a matching alt");
8147c478bd9Sstevel@tonic-gate 
8157c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
8167c478bd9Sstevel@tonic-gate 	}
8177c478bd9Sstevel@tonic-gate 
8187c478bd9Sstevel@tonic-gate 
8197c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_ALL, uasp->usb_as_log_handle,
8207c478bd9Sstevel@tonic-gate 	    "usb_as_set_format: interface=%d alternate=%d",
8217c478bd9Sstevel@tonic-gate 	    interface, alt);
8227c478bd9Sstevel@tonic-gate 
8237c478bd9Sstevel@tonic-gate 	mutex_exit(&uasp->usb_as_mutex);
8247c478bd9Sstevel@tonic-gate 
82588447a05SGarrett D'Amore 	rval = usb_as_send_ctrl_cmd(uasp,
8267c478bd9Sstevel@tonic-gate 					/* bmRequestType */
8277c478bd9Sstevel@tonic-gate 	    USB_DEV_REQ_HOST_TO_DEV | USB_DEV_REQ_RCPT_IF,
8287c478bd9Sstevel@tonic-gate 	    USB_REQ_SET_IF,		/* bRequest */
8297c478bd9Sstevel@tonic-gate 	    alt,			/* wValue */
8307c478bd9Sstevel@tonic-gate 	    interface,			/* wIndex */
8317c478bd9Sstevel@tonic-gate 	    0,				/* wLength */
83288447a05SGarrett D'Amore 	    NULL, B_FALSE);
83388447a05SGarrett D'Amore 
83488447a05SGarrett D'Amore 	mutex_enter(&uasp->usb_as_mutex);
83588447a05SGarrett D'Amore 
83688447a05SGarrett D'Amore 	if (rval != USB_SUCCESS) {
8377c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ALL, uasp->usb_as_log_handle,
8387c478bd9Sstevel@tonic-gate 		    "usb_as_set_format: set_alternate failed");
83988447a05SGarrett D'Amore 	} else {
84088447a05SGarrett D'Amore 		format->fmt_alt = (uchar_t)alt;
8417c478bd9Sstevel@tonic-gate 	}
8427c478bd9Sstevel@tonic-gate 
8437c478bd9Sstevel@tonic-gate 	return (rval);
8447c478bd9Sstevel@tonic-gate }
8457c478bd9Sstevel@tonic-gate 
8467c478bd9Sstevel@tonic-gate 
8477c478bd9Sstevel@tonic-gate /*
8487c478bd9Sstevel@tonic-gate  * usb_as_setup:
8497c478bd9Sstevel@tonic-gate  *	Open isoc pipe. Will hang around till bandwidth
8507c478bd9Sstevel@tonic-gate  *	is available.
8517c478bd9Sstevel@tonic-gate  */
8527c478bd9Sstevel@tonic-gate static int
usb_as_setup(usb_as_state_t * uasp)85388447a05SGarrett D'Amore usb_as_setup(usb_as_state_t *uasp)
8547c478bd9Sstevel@tonic-gate {
8557c478bd9Sstevel@tonic-gate 	int alt = uasp->usb_as_alternate;
8567c478bd9Sstevel@tonic-gate 	usb_ep_descr_t *ep = (usb_ep_descr_t *)uasp->usb_as_alts[alt].alt_ep;
8577c478bd9Sstevel@tonic-gate 	int rval;
8587c478bd9Sstevel@tonic-gate 
859e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 
8607c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uasp->usb_as_mutex));
8617c478bd9Sstevel@tonic-gate 
8627c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, uasp->usb_as_log_handle,
86388447a05SGarrett D'Amore 	    "usb_as_setup: Begin usb_as_setup, inst=%d",
86488447a05SGarrett D'Amore 	    ddi_get_instance(uasp->usb_as_dip));
8657c478bd9Sstevel@tonic-gate 
8667c478bd9Sstevel@tonic-gate 
8677c478bd9Sstevel@tonic-gate 	/* Set record packet size to max packet size */
868e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 	if (uasp->usb_as_alts[alt].alt_mode == USB_AUDIO_RECORD) {
8697c478bd9Sstevel@tonic-gate 		uasp->usb_as_record_pkt_size = ep->wMaxPacketSize;
8707c478bd9Sstevel@tonic-gate 	} else {
8717c478bd9Sstevel@tonic-gate 		uasp->usb_as_record_pkt_size = 0;
8727c478bd9Sstevel@tonic-gate 	}
8737c478bd9Sstevel@tonic-gate 
874e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 	if (uasp->usb_as_isoc_ph != NULL) {
875e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 		while (uasp->usb_as_request_count) {
876e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 			cv_wait(&uasp->usb_as_pipe_cv,
877e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 			    &uasp->usb_as_mutex);
878e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 		}
879e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 
880e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 		/* close the isoc pipe which is opened before */
881e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 		mutex_exit(&uasp->usb_as_mutex);
882e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 		usb_pipe_close(uasp->usb_as_dip, uasp->usb_as_isoc_ph,
883e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 		    USB_FLAGS_SLEEP, NULL, (usb_opaque_t)NULL);
884e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 
885e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 		mutex_enter(&uasp->usb_as_mutex);
886e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 		uasp->usb_as_isoc_ph = NULL;
887e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 	}
888e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 
889e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 	ASSERT(uasp->usb_as_request_count == 0);
8907c478bd9Sstevel@tonic-gate 	mutex_exit(&uasp->usb_as_mutex);
8917c478bd9Sstevel@tonic-gate 
8927c478bd9Sstevel@tonic-gate 	/* open isoc pipe, may fail if there is no bandwidth  */
8937c478bd9Sstevel@tonic-gate 	rval = usb_pipe_open(uasp->usb_as_dip, ep, &uasp->usb_as_isoc_pp,
89488447a05SGarrett D'Amore 	    USB_FLAGS_SLEEP, &uasp->usb_as_isoc_ph);
8957c478bd9Sstevel@tonic-gate 
8967c478bd9Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
8977c478bd9Sstevel@tonic-gate 		switch (rval) {
8987c478bd9Sstevel@tonic-gate 		case USB_NO_BANDWIDTH:
8997c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L0(PRINT_MASK_ALL, uasp->usb_as_log_handle,
9007c478bd9Sstevel@tonic-gate 			    "no bandwidth available");
9017c478bd9Sstevel@tonic-gate 			break;
9027c478bd9Sstevel@tonic-gate 		case USB_NOT_SUPPORTED:
9037c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L0(PRINT_MASK_ALL, uasp->usb_as_log_handle,
9047c478bd9Sstevel@tonic-gate 			    "Operating a full/high speed audio device on a "
9057c478bd9Sstevel@tonic-gate 			    "high speed port is not supported");
9067c478bd9Sstevel@tonic-gate 			break;
9077c478bd9Sstevel@tonic-gate 		default:
9087c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ALL,
9097c478bd9Sstevel@tonic-gate 			    uasp->usb_as_log_handle,
9107c478bd9Sstevel@tonic-gate 			    "usb_as_setup: isoc pipe open failed (%d)",
9117c478bd9Sstevel@tonic-gate 			    rval);
9127c478bd9Sstevel@tonic-gate 		}
9137c478bd9Sstevel@tonic-gate 
9147c478bd9Sstevel@tonic-gate 		mutex_enter(&uasp->usb_as_mutex);
9157c478bd9Sstevel@tonic-gate 
9167c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
9177c478bd9Sstevel@tonic-gate 	}
9187c478bd9Sstevel@tonic-gate 
9197c478bd9Sstevel@tonic-gate 	(void) usb_pipe_set_private(uasp->usb_as_isoc_ph, (usb_opaque_t)uasp);
9207c478bd9Sstevel@tonic-gate 
9217c478bd9Sstevel@tonic-gate 	mutex_enter(&uasp->usb_as_mutex);
9227c478bd9Sstevel@tonic-gate 	uasp->usb_as_audio_state = USB_AS_IDLE;
9237c478bd9Sstevel@tonic-gate 	uasp->usb_as_setup_cnt++;
9247c478bd9Sstevel@tonic-gate 
9257c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, uasp->usb_as_log_handle,
9267c478bd9Sstevel@tonic-gate 	    "usb_as_setup: End");
9277c478bd9Sstevel@tonic-gate 
9287c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
9297c478bd9Sstevel@tonic-gate }
9307c478bd9Sstevel@tonic-gate 
9317c478bd9Sstevel@tonic-gate 
9327c478bd9Sstevel@tonic-gate /*
9337c478bd9Sstevel@tonic-gate  * usb_as_teardown
9347c478bd9Sstevel@tonic-gate  *
9357c478bd9Sstevel@tonic-gate  */
9367c478bd9Sstevel@tonic-gate static void
usb_as_teardown(usb_as_state_t * uasp)93788447a05SGarrett D'Amore usb_as_teardown(usb_as_state_t *uasp)
9387c478bd9Sstevel@tonic-gate {
9397c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, uasp->usb_as_log_handle,
94088447a05SGarrett D'Amore 	    "usb_as_teardown: Begin inst=%d",
94188447a05SGarrett D'Amore 	    ddi_get_instance(uasp->usb_as_dip));
94288447a05SGarrett D'Amore 
9437c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uasp->usb_as_mutex));
9447c478bd9Sstevel@tonic-gate 
9457c478bd9Sstevel@tonic-gate 	uasp->usb_as_audio_state = USB_AS_IDLE;
9467c478bd9Sstevel@tonic-gate 
947e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 	ASSERT(uasp->usb_as_isoc_ph);
948e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 	/* reset setup flag */
949e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 	uasp->usb_as_setup_cnt--;
9507c478bd9Sstevel@tonic-gate 
9517c478bd9Sstevel@tonic-gate 
9527c478bd9Sstevel@tonic-gate 	ASSERT(uasp->usb_as_setup_cnt == 0);
9537c478bd9Sstevel@tonic-gate 
9547c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, uasp->usb_as_log_handle,
9557c478bd9Sstevel@tonic-gate 	    "usb_as_teardown: End");
9567c478bd9Sstevel@tonic-gate }
9577c478bd9Sstevel@tonic-gate 
9587c478bd9Sstevel@tonic-gate 
9597c478bd9Sstevel@tonic-gate /*
96088447a05SGarrett D'Amore  * usb_as_start_play
9617c478bd9Sstevel@tonic-gate  */
9627c478bd9Sstevel@tonic-gate static int
usb_as_start_play(usb_as_state_t * uasp,usb_audio_play_req_t * play_req)96388447a05SGarrett D'Amore usb_as_start_play(usb_as_state_t *uasp, usb_audio_play_req_t *play_req)
9647c478bd9Sstevel@tonic-gate {
9657c478bd9Sstevel@tonic-gate 	int		n_requests;
9667c478bd9Sstevel@tonic-gate 	int		rval = USB_FAILURE;
9677c478bd9Sstevel@tonic-gate 
9687c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, uasp->usb_as_log_handle,
96988447a05SGarrett D'Amore 	    "usb_as_start_play: Begin inst=%d, req_cnt=%d",
97088447a05SGarrett D'Amore 	    ddi_get_instance(uasp->usb_as_dip), uasp->usb_as_request_count);
9717c478bd9Sstevel@tonic-gate 
9727c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uasp->usb_as_mutex));
9737c478bd9Sstevel@tonic-gate 
9747c478bd9Sstevel@tonic-gate 	uasp->usb_as_request_samples = play_req->up_samples;
9757c478bd9Sstevel@tonic-gate 	uasp->usb_as_ahdl = play_req->up_handle;
9767c478bd9Sstevel@tonic-gate 	uasp->usb_as_audio_state = USB_AS_ACTIVE;
9777c478bd9Sstevel@tonic-gate 
9787c478bd9Sstevel@tonic-gate 	if ((uasp->usb_as_request_count >= USB_AS_MAX_REQUEST_COUNT) ||
9797c478bd9Sstevel@tonic-gate 	    (uasp->usb_as_audio_state == USB_AS_IDLE) ||
9807c478bd9Sstevel@tonic-gate 	    (uasp->usb_as_audio_state == USB_AS_PLAY_PAUSED)) {
9817c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ALL, uasp->usb_as_log_handle,
9827c478bd9Sstevel@tonic-gate 		    "nothing to do or paused or idle (%d)",
9837c478bd9Sstevel@tonic-gate 		    uasp->usb_as_audio_state);
9847c478bd9Sstevel@tonic-gate 		rval = USB_SUCCESS;
9857c478bd9Sstevel@tonic-gate 	} else {
9867c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ALL, uasp->usb_as_log_handle,
9877c478bd9Sstevel@tonic-gate 		    "usb_as_start_play: samples=%d requestcount=%d ",
98888447a05SGarrett D'Amore 		    uasp->usb_as_request_samples, uasp->usb_as_request_count);
9897c478bd9Sstevel@tonic-gate 
9907c478bd9Sstevel@tonic-gate 		/* queue up as many requests as allowed */
9917c478bd9Sstevel@tonic-gate 		for (n_requests = uasp->usb_as_request_count;
9927c478bd9Sstevel@tonic-gate 		    n_requests < USB_AS_MAX_REQUEST_COUNT; n_requests++) {
99388447a05SGarrett D'Amore 			if ((rval = usb_as_play_isoc_data(uasp, play_req)) !=
9947c478bd9Sstevel@tonic-gate 			    USB_SUCCESS) {
9957c478bd9Sstevel@tonic-gate 				break;
9967c478bd9Sstevel@tonic-gate 			}
9977c478bd9Sstevel@tonic-gate 		}
9987c478bd9Sstevel@tonic-gate 	}
9997c478bd9Sstevel@tonic-gate 
10007c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, uasp->usb_as_log_handle,
10017c478bd9Sstevel@tonic-gate 	    "usb_as_start_play: End");
10027c478bd9Sstevel@tonic-gate 
10037c478bd9Sstevel@tonic-gate 	return (rval);
10047c478bd9Sstevel@tonic-gate }
10057c478bd9Sstevel@tonic-gate 
10067c478bd9Sstevel@tonic-gate 
10077c478bd9Sstevel@tonic-gate /*
10087c478bd9Sstevel@tonic-gate  * usb_as_continue_play:
10097c478bd9Sstevel@tonic-gate  *	this function is called from the play callbacks
10107c478bd9Sstevel@tonic-gate  */
10117c478bd9Sstevel@tonic-gate static void
usb_as_continue_play(usb_as_state_t * uasp)10127c478bd9Sstevel@tonic-gate usb_as_continue_play(usb_as_state_t *uasp)
10137c478bd9Sstevel@tonic-gate {
10147c478bd9Sstevel@tonic-gate 	int		n_requests;
10157c478bd9Sstevel@tonic-gate 
10167c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, uasp->usb_as_log_handle,
10177c478bd9Sstevel@tonic-gate 	    "usb_as_contine_play: Begin req_cnt=%d",
10187c478bd9Sstevel@tonic-gate 	    uasp->usb_as_request_count);
10197c478bd9Sstevel@tonic-gate 
10207c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uasp->usb_as_mutex));
10217c478bd9Sstevel@tonic-gate 
10227c478bd9Sstevel@tonic-gate 	if (uasp->usb_as_dev_state == USB_DEV_DISCONNECTED) {
102388447a05SGarrett D'Amore 		usb_as_handle_shutdown(uasp);
10247c478bd9Sstevel@tonic-gate 
10257c478bd9Sstevel@tonic-gate 		return;
10267c478bd9Sstevel@tonic-gate 	}
10277c478bd9Sstevel@tonic-gate 
10287c478bd9Sstevel@tonic-gate 	if ((uasp->usb_as_request_count >= USB_AS_MAX_REQUEST_COUNT) ||
10297c478bd9Sstevel@tonic-gate 	    (uasp->usb_as_audio_state == USB_AS_IDLE) ||
10307c478bd9Sstevel@tonic-gate 	    (uasp->usb_as_audio_state == USB_AS_PLAY_PAUSED)) {
10317c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ALL, uasp->usb_as_log_handle,
10327c478bd9Sstevel@tonic-gate 		    "usb_as_continue_play: nothing to do (audio_state=%d)",
10337c478bd9Sstevel@tonic-gate 		    uasp->usb_as_audio_state);
10347c478bd9Sstevel@tonic-gate 	} else {
10357c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ALL, uasp->usb_as_log_handle,
10367c478bd9Sstevel@tonic-gate 		    "usb_as_continue_play: samples=%d requestcount=%d ",
103788447a05SGarrett D'Amore 		    uasp->usb_as_request_samples, uasp->usb_as_request_count);
10387c478bd9Sstevel@tonic-gate 
10397c478bd9Sstevel@tonic-gate 		/* queue up as many requests as allowed */
10407c478bd9Sstevel@tonic-gate 		for (n_requests = uasp->usb_as_request_count;
10417c478bd9Sstevel@tonic-gate 		    n_requests < USB_AS_MAX_REQUEST_COUNT; n_requests++) {
10427c478bd9Sstevel@tonic-gate 			if (usb_as_play_isoc_data(uasp, NULL) !=
10437c478bd9Sstevel@tonic-gate 			    USB_SUCCESS) {
10447c478bd9Sstevel@tonic-gate 
10457c478bd9Sstevel@tonic-gate 				break;
10467c478bd9Sstevel@tonic-gate 			}
10477c478bd9Sstevel@tonic-gate 		}
10487c478bd9Sstevel@tonic-gate 	}
10497c478bd9Sstevel@tonic-gate 
10507c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, uasp->usb_as_log_handle,
10517c478bd9Sstevel@tonic-gate 	    "usb_as_continue_play: End");
10527c478bd9Sstevel@tonic-gate }
10537c478bd9Sstevel@tonic-gate 
10547c478bd9Sstevel@tonic-gate 
10557c478bd9Sstevel@tonic-gate static void
usb_as_handle_shutdown(usb_as_state_t * uasp)105688447a05SGarrett D'Amore usb_as_handle_shutdown(usb_as_state_t *uasp)
10577c478bd9Sstevel@tonic-gate {
1058e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 	void	*ahdl;
10597c478bd9Sstevel@tonic-gate 
10607c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, uasp->usb_as_log_handle,
106188447a05SGarrett D'Amore 	    "usb_as_handle_shutdown, inst=%d",
106288447a05SGarrett D'Amore 	    ddi_get_instance(uasp->usb_as_dip));
10637c478bd9Sstevel@tonic-gate 
10647c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L2(PRINT_MASK_ALL, uasp->usb_as_log_handle,
10657c478bd9Sstevel@tonic-gate 	    "usb_as_handle_shutdown: am_play_shutdown");
10667c478bd9Sstevel@tonic-gate 
10677c478bd9Sstevel@tonic-gate 	uasp->usb_as_audio_state = USB_AS_IDLE;
10687c478bd9Sstevel@tonic-gate 	uasp->usb_as_pkt_count = 0;
10697c478bd9Sstevel@tonic-gate 	ahdl = uasp->usb_as_ahdl;
10707c478bd9Sstevel@tonic-gate 
10717c478bd9Sstevel@tonic-gate 	mutex_exit(&uasp->usb_as_mutex);
1072e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 	usb_ac_stop_play(ahdl, NULL);
10737c478bd9Sstevel@tonic-gate 	mutex_enter(&uasp->usb_as_mutex);
10747c478bd9Sstevel@tonic-gate }
10757c478bd9Sstevel@tonic-gate 
10767c478bd9Sstevel@tonic-gate 
10777c478bd9Sstevel@tonic-gate static int
usb_as_play_isoc_data(usb_as_state_t * uasp,usb_audio_play_req_t * play_req)107888447a05SGarrett D'Amore usb_as_play_isoc_data(usb_as_state_t *uasp, usb_audio_play_req_t *play_req)
10797c478bd9Sstevel@tonic-gate {
10807c478bd9Sstevel@tonic-gate 	int		rval = USB_FAILURE;
10817c478bd9Sstevel@tonic-gate 
10827c478bd9Sstevel@tonic-gate 	usb_isoc_req_t *isoc_req = NULL;
10837c478bd9Sstevel@tonic-gate 	usb_audio_formats_t *format = &uasp->usb_as_curr_format;
10847c478bd9Sstevel@tonic-gate 	mblk_t		*data = NULL;
1085e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 	void *	ahdl = uasp->usb_as_ahdl;
10867c478bd9Sstevel@tonic-gate 	int		precision;
10877c478bd9Sstevel@tonic-gate 	int		pkt, frame, n, n_pkts, count;
10887c478bd9Sstevel@tonic-gate 	size_t		bufsize;
10897c478bd9Sstevel@tonic-gate 	int		pkt_len[USB_AS_N_FRAMES];
10907c478bd9Sstevel@tonic-gate 
10917c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uasp->usb_as_mutex));
10927c478bd9Sstevel@tonic-gate 
1093*64391892SAlbert Lee 	precision = format->fmt_precision >> 3;
10947c478bd9Sstevel@tonic-gate 
10957c478bd9Sstevel@tonic-gate 	frame = uasp->usb_as_pkt_count;
10967c478bd9Sstevel@tonic-gate 
10977c478bd9Sstevel@tonic-gate 	/*
10987c478bd9Sstevel@tonic-gate 	 * calculate total bufsize by determining the pkt size for
10997c478bd9Sstevel@tonic-gate 	 * each frame
11007c478bd9Sstevel@tonic-gate 	 */
11017c478bd9Sstevel@tonic-gate 	for (bufsize = pkt = 0; pkt < USB_AS_N_FRAMES; pkt++) {
1102*64391892SAlbert Lee 		pkt_len[pkt] = usb_as_get_pktsize(uasp, frame++);
11037c478bd9Sstevel@tonic-gate 		bufsize += pkt_len[pkt];
11047c478bd9Sstevel@tonic-gate 	}
11057c478bd9Sstevel@tonic-gate 
11067c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, uasp->usb_as_log_handle,
110788447a05SGarrett D'Amore 	    "usb_as_play_isoc_data: Begin bufsize=0x%lx, inst=%d", bufsize,
110888447a05SGarrett D'Amore 	    ddi_get_instance(uasp->usb_as_dip));
11097c478bd9Sstevel@tonic-gate 
11107c478bd9Sstevel@tonic-gate 	mutex_exit(&uasp->usb_as_mutex);
11117c478bd9Sstevel@tonic-gate 
11127c478bd9Sstevel@tonic-gate 	if ((data = allocb(bufsize, BPRI_HI)) == NULL) {
11137c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ALL, uasp->usb_as_log_handle,
11147c478bd9Sstevel@tonic-gate 		    "usb_as_play_isoc_data: allocb failed");
11157c478bd9Sstevel@tonic-gate 		mutex_enter(&uasp->usb_as_mutex);
11167c478bd9Sstevel@tonic-gate 
11177c478bd9Sstevel@tonic-gate 		goto done;
11187c478bd9Sstevel@tonic-gate 	}
11197c478bd9Sstevel@tonic-gate 
112088447a05SGarrett D'Amore 	/*
1121e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 	 * restriction of Boomer: cannot call usb_ac_get_audio() in the context
112288447a05SGarrett D'Amore 	 * of start so we play a fragment of silence at first
112388447a05SGarrett D'Amore 	 */
112488447a05SGarrett D'Amore 	if (play_req != NULL) {
112588447a05SGarrett D'Amore 		bzero(data->b_wptr, bufsize);
112688447a05SGarrett D'Amore 		count = bufsize / precision;
112788447a05SGarrett D'Amore 
1128e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 	} else if ((count = usb_ac_get_audio(ahdl, (void *)data->b_wptr,
112988447a05SGarrett D'Amore 	    bufsize / precision)) == 0) {
11307c478bd9Sstevel@tonic-gate 		mutex_enter(&uasp->usb_as_mutex);
11317c478bd9Sstevel@tonic-gate 		if (uasp->usb_as_request_count == 0) {
113288447a05SGarrett D'Amore 			usb_as_handle_shutdown(uasp);
11337c478bd9Sstevel@tonic-gate 
11347c478bd9Sstevel@tonic-gate 			/* Don't return failure for 0 bytes of data sent */
113588447a05SGarrett D'Amore 			if (play_req) {
11367c478bd9Sstevel@tonic-gate 				/*
11377c478bd9Sstevel@tonic-gate 				 * Since we set rval to SUCCESS
11387c478bd9Sstevel@tonic-gate 				 * we treat it as a special case
11397c478bd9Sstevel@tonic-gate 				 * and free data here
11407c478bd9Sstevel@tonic-gate 				 */
11417c478bd9Sstevel@tonic-gate 				rval = USB_SUCCESS;
11427c478bd9Sstevel@tonic-gate 				freemsg(data);
11437c478bd9Sstevel@tonic-gate 				data = NULL;
11447c478bd9Sstevel@tonic-gate 
11457c478bd9Sstevel@tonic-gate 				goto done;
11467c478bd9Sstevel@tonic-gate 			}
11477c478bd9Sstevel@tonic-gate 		} else {
11487c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ALL,
11497c478bd9Sstevel@tonic-gate 			    uasp->usb_as_log_handle,
11507c478bd9Sstevel@tonic-gate 			    "usb_as_play_isoc_data: no audio bytes, "
11517c478bd9Sstevel@tonic-gate 			    "rcnt=0x%x ", uasp->usb_as_request_count);
11527c478bd9Sstevel@tonic-gate 		}
11537c478bd9Sstevel@tonic-gate 		rval = USB_FAILURE;
11547c478bd9Sstevel@tonic-gate 
11557c478bd9Sstevel@tonic-gate 		goto done;
11567c478bd9Sstevel@tonic-gate 	}
11577c478bd9Sstevel@tonic-gate 
11587c478bd9Sstevel@tonic-gate 	bufsize = n = count * precision;
11597c478bd9Sstevel@tonic-gate 	data->b_wptr += n;
11607c478bd9Sstevel@tonic-gate 
11617c478bd9Sstevel@tonic-gate 	/* calculate how many frames we can actually fill */
11627c478bd9Sstevel@tonic-gate 	for (n_pkts = 0; (n_pkts < USB_AS_N_FRAMES) && (n > 0); n_pkts++) {
11637c478bd9Sstevel@tonic-gate 		if (n < pkt_len[n_pkts]) {
11647c478bd9Sstevel@tonic-gate 			pkt_len[n_pkts] = n;
11657c478bd9Sstevel@tonic-gate 		}
11667c478bd9Sstevel@tonic-gate 		n -= pkt_len[n_pkts];
11677c478bd9Sstevel@tonic-gate 	}
11687c478bd9Sstevel@tonic-gate 
11697c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, uasp->usb_as_log_handle,
11707c478bd9Sstevel@tonic-gate 	    "usb_as_play_isoc_data: n_pkts=%d, bufsize=%ld, n=%d",
11717c478bd9Sstevel@tonic-gate 	    n_pkts, bufsize, count * precision);
11727c478bd9Sstevel@tonic-gate 
11737c478bd9Sstevel@tonic-gate 	/* allocate an isoc request packet */
11747c478bd9Sstevel@tonic-gate 	if ((isoc_req = usb_alloc_isoc_req(uasp->usb_as_dip,
11757c478bd9Sstevel@tonic-gate 	    n_pkts, 0, 0)) == NULL) {
11767c478bd9Sstevel@tonic-gate 		mutex_enter(&uasp->usb_as_mutex);
11777c478bd9Sstevel@tonic-gate 
11787c478bd9Sstevel@tonic-gate 		goto done;
11797c478bd9Sstevel@tonic-gate 	}
11807c478bd9Sstevel@tonic-gate 
11817c478bd9Sstevel@tonic-gate 
11827c478bd9Sstevel@tonic-gate 
11837c478bd9Sstevel@tonic-gate 	/* initialize the packet descriptor */
11847c478bd9Sstevel@tonic-gate 	for (pkt = 0; pkt < n_pkts; pkt++) {
11857c478bd9Sstevel@tonic-gate 		isoc_req->isoc_pkt_descr[pkt].isoc_pkt_length =
1186112116d8Sfb 		    pkt_len[pkt];
11877c478bd9Sstevel@tonic-gate 	}
11887c478bd9Sstevel@tonic-gate 
11897c478bd9Sstevel@tonic-gate 	isoc_req->isoc_data		= data;
11907c478bd9Sstevel@tonic-gate 	isoc_req->isoc_pkts_count	= (ushort_t)n_pkts;
11917c478bd9Sstevel@tonic-gate 	isoc_req->isoc_attributes	= USB_ATTRS_ISOC_XFER_ASAP |
1192112116d8Sfb 	    USB_ATTRS_AUTOCLEARING;
11937c478bd9Sstevel@tonic-gate 	isoc_req->isoc_cb		= usb_as_play_cb;
11947c478bd9Sstevel@tonic-gate 	isoc_req->isoc_exc_cb		= usb_as_play_exc_cb;
11957c478bd9Sstevel@tonic-gate 	isoc_req->isoc_client_private	= (usb_opaque_t)uasp;
11967c478bd9Sstevel@tonic-gate 
11977c478bd9Sstevel@tonic-gate 	mutex_enter(&uasp->usb_as_mutex);
11987c478bd9Sstevel@tonic-gate 
11997c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_ALL, uasp->usb_as_log_handle,
12007c478bd9Sstevel@tonic-gate 	    "usb_as_play_isoc_data: rq=0x%p data=0x%p cnt=0x%x "
1201112116d8Sfb 	    "pkt=0x%p rqcnt=%d ", (void *)isoc_req, (void *)data, count,
1202112116d8Sfb 	    (void *)isoc_req->isoc_pkt_descr, uasp->usb_as_request_count);
12037c478bd9Sstevel@tonic-gate 
12047c478bd9Sstevel@tonic-gate 	ASSERT(isoc_req->isoc_data != NULL);
12057c478bd9Sstevel@tonic-gate 
12067c478bd9Sstevel@tonic-gate 	uasp->usb_as_send_debug_count++;
12077c478bd9Sstevel@tonic-gate 	uasp->usb_as_request_count++;
12087c478bd9Sstevel@tonic-gate 	uasp->usb_as_pkt_count += n_pkts;
12097c478bd9Sstevel@tonic-gate 	mutex_exit(&uasp->usb_as_mutex);
12107c478bd9Sstevel@tonic-gate 
12117c478bd9Sstevel@tonic-gate 	if ((rval = usb_pipe_isoc_xfer(uasp->usb_as_isoc_ph,
12127c478bd9Sstevel@tonic-gate 	    isoc_req, 0)) != USB_SUCCESS) {
12137c478bd9Sstevel@tonic-gate 
12147c478bd9Sstevel@tonic-gate 		mutex_enter(&uasp->usb_as_mutex);
12157c478bd9Sstevel@tonic-gate 		uasp->usb_as_request_count--;
1216e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 		cv_signal(&uasp->usb_as_pipe_cv);
12177c478bd9Sstevel@tonic-gate 		uasp->usb_as_send_debug_count--;
12187c478bd9Sstevel@tonic-gate 		uasp->usb_as_pkt_count -= n_pkts;
12197c478bd9Sstevel@tonic-gate 
12207c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ALL, uasp->usb_as_log_handle,
12217c478bd9Sstevel@tonic-gate 		    "usb_as_play_isoc_data: rval=%d", rval);
12227c478bd9Sstevel@tonic-gate 
12237c478bd9Sstevel@tonic-gate 		rval = USB_FAILURE;
12247c478bd9Sstevel@tonic-gate 
12257c478bd9Sstevel@tonic-gate 	} else {
12267c478bd9Sstevel@tonic-gate 		mutex_enter(&uasp->usb_as_mutex);
12277c478bd9Sstevel@tonic-gate 
12287c478bd9Sstevel@tonic-gate 		data = NULL;
12297c478bd9Sstevel@tonic-gate 		isoc_req = NULL;
12307c478bd9Sstevel@tonic-gate 	}
12317c478bd9Sstevel@tonic-gate 
12327c478bd9Sstevel@tonic-gate done:
12337c478bd9Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
12347c478bd9Sstevel@tonic-gate 		freemsg(data);
12357c478bd9Sstevel@tonic-gate 		if (isoc_req) {
12367c478bd9Sstevel@tonic-gate 			isoc_req->isoc_data = NULL;
12377c478bd9Sstevel@tonic-gate 			usb_free_isoc_req(isoc_req);
12387c478bd9Sstevel@tonic-gate 		}
12397c478bd9Sstevel@tonic-gate 	}
12407c478bd9Sstevel@tonic-gate 
12414610e4a0Sfrits 	USB_DPRINTF_L4(PRINT_MASK_ALL, uasp->usb_as_log_handle,
12427c478bd9Sstevel@tonic-gate 	    "usb_as_play_isoc_data: SEND CNT=%d, RCV COUNT=%d",
12437c478bd9Sstevel@tonic-gate 	    uasp->usb_as_send_debug_count, uasp->usb_as_rcv_debug_count);
12447c478bd9Sstevel@tonic-gate 
12457c478bd9Sstevel@tonic-gate 	return (rval);
12467c478bd9Sstevel@tonic-gate }
12477c478bd9Sstevel@tonic-gate 
12487c478bd9Sstevel@tonic-gate 
12497c478bd9Sstevel@tonic-gate static void
usb_as_pause_play(usb_as_state_t * uasp)125088447a05SGarrett D'Amore usb_as_pause_play(usb_as_state_t *uasp)
12517c478bd9Sstevel@tonic-gate {
12527c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uasp->usb_as_mutex));
125388447a05SGarrett D'Amore 
125488447a05SGarrett D'Amore 	/* this will stop the isoc request in the play callback */
12557c478bd9Sstevel@tonic-gate 	uasp->usb_as_audio_state = USB_AS_PLAY_PAUSED;
12567c478bd9Sstevel@tonic-gate }
12577c478bd9Sstevel@tonic-gate 
12587c478bd9Sstevel@tonic-gate 
12597c478bd9Sstevel@tonic-gate /*ARGSUSED*/
12607c478bd9Sstevel@tonic-gate static void
usb_as_play_cb(usb_pipe_handle_t ph,usb_isoc_req_t * isoc_req)12617c478bd9Sstevel@tonic-gate usb_as_play_cb(usb_pipe_handle_t ph, usb_isoc_req_t *isoc_req)
12627c478bd9Sstevel@tonic-gate {
12637c478bd9Sstevel@tonic-gate 	usb_as_state_t *uasp = (usb_as_state_t *)
1264112116d8Sfb 	    (isoc_req->isoc_client_private);
12657c478bd9Sstevel@tonic-gate 	int i;
12667c478bd9Sstevel@tonic-gate 
12677c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_CB, uasp->usb_as_log_handle,
12687c478bd9Sstevel@tonic-gate 	    "usb_as_play_cb: Begin ph=0x%p, isoc_req=0x%p",
1269112116d8Sfb 	    (void *)ph, (void *)isoc_req);
12707c478bd9Sstevel@tonic-gate 
12717c478bd9Sstevel@tonic-gate 	ASSERT((isoc_req->isoc_cb_flags & USB_CB_INTR_CONTEXT) != 0);
12727c478bd9Sstevel@tonic-gate 
12737c478bd9Sstevel@tonic-gate 	for (i = 0; i < isoc_req->isoc_pkts_count; i++) {
12747c478bd9Sstevel@tonic-gate 		if (isoc_req->isoc_pkt_descr[i].isoc_pkt_status !=
12757c478bd9Sstevel@tonic-gate 		    USB_CR_OK) {
12767c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_CB, uasp->usb_as_log_handle,
12777c478bd9Sstevel@tonic-gate 			    "usb_as_play_cb: \tpkt%d: len=%d status=%s", i,
12787c478bd9Sstevel@tonic-gate 			    isoc_req->isoc_pkt_descr[i].isoc_pkt_length,
12797c478bd9Sstevel@tonic-gate 			    usb_str_cr(isoc_req->
12807c478bd9Sstevel@tonic-gate 			    isoc_pkt_descr[i].isoc_pkt_status));
12817c478bd9Sstevel@tonic-gate 		}
12827c478bd9Sstevel@tonic-gate 	}
12837c478bd9Sstevel@tonic-gate 
12847c478bd9Sstevel@tonic-gate 	mutex_enter(&uasp->usb_as_mutex);
12857c478bd9Sstevel@tonic-gate 	if (isoc_req->isoc_error_count) {
12867c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_CB, uasp->usb_as_log_handle,
12877c478bd9Sstevel@tonic-gate 		    "usb_as_play_cb: error_count = %d",
12887c478bd9Sstevel@tonic-gate 		    isoc_req->isoc_error_count);
12897c478bd9Sstevel@tonic-gate 	}
12907c478bd9Sstevel@tonic-gate 
12917c478bd9Sstevel@tonic-gate 	usb_free_isoc_req(isoc_req);
12927c478bd9Sstevel@tonic-gate 	uasp->usb_as_request_count--;
1293e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 	cv_signal(&uasp->usb_as_pipe_cv);
12947c478bd9Sstevel@tonic-gate 	uasp->usb_as_rcv_debug_count++;
12957c478bd9Sstevel@tonic-gate 	usb_as_continue_play(uasp);
12967c478bd9Sstevel@tonic-gate 
12974610e4a0Sfrits 	USB_DPRINTF_L4(PRINT_MASK_CB, uasp->usb_as_log_handle,
12987c478bd9Sstevel@tonic-gate 	    "usb_as_play_cb: SEND CNT=%d, RCV COUNT=%d",
12997c478bd9Sstevel@tonic-gate 	    uasp->usb_as_send_debug_count, uasp->usb_as_rcv_debug_count);
13007c478bd9Sstevel@tonic-gate 
13017c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_CB, uasp->usb_as_log_handle,
13027c478bd9Sstevel@tonic-gate 	    "usb_as_play_cb: End, req_cnt=%d", uasp->usb_as_request_count);
13037c478bd9Sstevel@tonic-gate 
13047c478bd9Sstevel@tonic-gate 	mutex_exit(&uasp->usb_as_mutex);
13057c478bd9Sstevel@tonic-gate }
13067c478bd9Sstevel@tonic-gate 
13077c478bd9Sstevel@tonic-gate 
13087c478bd9Sstevel@tonic-gate static void
usb_as_play_exc_cb(usb_pipe_handle_t ph,usb_isoc_req_t * isoc_req)13097c478bd9Sstevel@tonic-gate usb_as_play_exc_cb(usb_pipe_handle_t ph, usb_isoc_req_t *isoc_req)
13107c478bd9Sstevel@tonic-gate {
13117c478bd9Sstevel@tonic-gate 	int i;
13127c478bd9Sstevel@tonic-gate 	usb_as_state_t	*uasp = (usb_as_state_t *)
1313112116d8Sfb 	    (isoc_req->isoc_client_private);
13147c478bd9Sstevel@tonic-gate 	usb_cr_t	cr = isoc_req->isoc_completion_reason;
13157c478bd9Sstevel@tonic-gate 	usb_cb_flags_t	cb_flags = isoc_req->isoc_cb_flags;
13167c478bd9Sstevel@tonic-gate 
13177c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L2(PRINT_MASK_ALL, uasp->usb_as_log_handle,
13187c478bd9Sstevel@tonic-gate 	    "usb_as_play_exc_cb: ph=0x%p, rq=0x%p data=0x%p pkts=0x%x "
1319112116d8Sfb 	    "cr=%d, cb_flag=0x%x", (void *)ph, (void *)isoc_req,
1320112116d8Sfb 	    (void *)isoc_req->isoc_data, isoc_req->isoc_pkts_count,
1321112116d8Sfb 	    cr, cb_flags);
13227c478bd9Sstevel@tonic-gate 
13237c478bd9Sstevel@tonic-gate 	ASSERT((isoc_req->isoc_cb_flags & USB_CB_INTR_CONTEXT) == 0);
13247c478bd9Sstevel@tonic-gate 
13257c478bd9Sstevel@tonic-gate 	for (i = 0; i < isoc_req->isoc_pkts_count; i++) {
13267c478bd9Sstevel@tonic-gate 		if (isoc_req->isoc_pkt_descr[i].isoc_pkt_status ==
13277c478bd9Sstevel@tonic-gate 		    USB_CR_OK) {
13287c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ALL,
13297c478bd9Sstevel@tonic-gate 			    uasp->usb_as_log_handle,
13307c478bd9Sstevel@tonic-gate 			    "usb_as_play_exc_cb: \tpkt%d: len=%d status=%d",
13317c478bd9Sstevel@tonic-gate 			    i,
13327c478bd9Sstevel@tonic-gate 			    isoc_req->isoc_pkt_descr[i].isoc_pkt_length,
13337c478bd9Sstevel@tonic-gate 			    isoc_req->isoc_pkt_descr[i].isoc_pkt_status);
13347c478bd9Sstevel@tonic-gate 		}
13357c478bd9Sstevel@tonic-gate 	}
13367c478bd9Sstevel@tonic-gate 
13377c478bd9Sstevel@tonic-gate 	usb_free_isoc_req(isoc_req);
13387c478bd9Sstevel@tonic-gate 
13397c478bd9Sstevel@tonic-gate 	mutex_enter(&uasp->usb_as_mutex);
13407c478bd9Sstevel@tonic-gate 	uasp->usb_as_rcv_debug_count++;
13417c478bd9Sstevel@tonic-gate 	uasp->usb_as_request_count--;
1342e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 	cv_signal(&uasp->usb_as_pipe_cv);
134388447a05SGarrett D'Amore 	usb_as_handle_shutdown(uasp);
13447c478bd9Sstevel@tonic-gate 
13457c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L2(PRINT_MASK_ALL, uasp->usb_as_log_handle,
13467c478bd9Sstevel@tonic-gate 	    "usb_as_play_exc_cb: SEND CNT=%d, RCV COUNT=%d",
13477c478bd9Sstevel@tonic-gate 	    uasp->usb_as_send_debug_count, uasp->usb_as_rcv_debug_count);
13487c478bd9Sstevel@tonic-gate 
13497c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L2(PRINT_MASK_ALL, uasp->usb_as_log_handle,
13507c478bd9Sstevel@tonic-gate 	    "usb_as_play_exc_cb: End request_count=%d",
13517c478bd9Sstevel@tonic-gate 	    uasp->usb_as_request_count);
13527c478bd9Sstevel@tonic-gate 
13537c478bd9Sstevel@tonic-gate 	mutex_exit(&uasp->usb_as_mutex);
13547c478bd9Sstevel@tonic-gate }
13557c478bd9Sstevel@tonic-gate 
13567c478bd9Sstevel@tonic-gate 
13577c478bd9Sstevel@tonic-gate /*
13587c478bd9Sstevel@tonic-gate  * usb_as_start_record
13597c478bd9Sstevel@tonic-gate  */
13607c478bd9Sstevel@tonic-gate static int
usb_as_start_record(usb_as_state_t * uasp,void * ahdl)1361e272e4c8SBinzi Cao - Sun Microsystems - Beijing China usb_as_start_record(usb_as_state_t *uasp, void * ahdl)
13627c478bd9Sstevel@tonic-gate {
13637c478bd9Sstevel@tonic-gate 	int		rval = USB_FAILURE;
13647c478bd9Sstevel@tonic-gate 	usb_isoc_req_t *isoc_req;
13657c478bd9Sstevel@tonic-gate 	ushort_t	record_pkt_size = uasp->usb_as_record_pkt_size;
13667c478bd9Sstevel@tonic-gate 	ushort_t	n_pkt = 1, pkt;
13677c478bd9Sstevel@tonic-gate 
13687c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, uasp->usb_as_log_handle,
136988447a05SGarrett D'Amore 	    "usb_as_start_record: inst=%d",
137088447a05SGarrett D'Amore 	    ddi_get_instance(uasp->usb_as_dip));
13717c478bd9Sstevel@tonic-gate 
13727c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uasp->usb_as_mutex));
13737c478bd9Sstevel@tonic-gate 
13747c478bd9Sstevel@tonic-gate 	/*
13757c478bd9Sstevel@tonic-gate 	 * A start_record should not happen when stop polling is
13767c478bd9Sstevel@tonic-gate 	 * happening
13777c478bd9Sstevel@tonic-gate 	 */
13787c478bd9Sstevel@tonic-gate 	ASSERT(uasp->usb_as_audio_state != USB_AS_STOP_POLLING_STARTED);
13797c478bd9Sstevel@tonic-gate 
13807c478bd9Sstevel@tonic-gate 	if (uasp->usb_as_audio_state == USB_AS_IDLE) {
13817c478bd9Sstevel@tonic-gate 
138288447a05SGarrett D'Amore 		uasp->usb_as_ahdl = ahdl;
13837c478bd9Sstevel@tonic-gate 		uasp->usb_as_audio_state = USB_AS_ACTIVE;
13847c478bd9Sstevel@tonic-gate 		mutex_exit(&uasp->usb_as_mutex);
13857c478bd9Sstevel@tonic-gate 
13867c478bd9Sstevel@tonic-gate 		if ((isoc_req = usb_alloc_isoc_req(uasp->usb_as_dip, n_pkt,
13877c478bd9Sstevel@tonic-gate 		    n_pkt * record_pkt_size, 0)) != NULL) {
13887c478bd9Sstevel@tonic-gate 			/* Initialize the packet descriptor */
13897c478bd9Sstevel@tonic-gate 			for (pkt = 0; pkt < n_pkt; pkt++) {
13907c478bd9Sstevel@tonic-gate 				isoc_req->isoc_pkt_descr[pkt].
13917c478bd9Sstevel@tonic-gate 				    isoc_pkt_length = record_pkt_size;
13927c478bd9Sstevel@tonic-gate 			}
13937c478bd9Sstevel@tonic-gate 
13947c478bd9Sstevel@tonic-gate 			isoc_req->isoc_pkts_count = n_pkt;
13957c478bd9Sstevel@tonic-gate 			isoc_req->isoc_pkts_length = record_pkt_size;
13967c478bd9Sstevel@tonic-gate 			isoc_req->isoc_attributes = USB_ATTRS_ISOC_XFER_ASAP |
13977c478bd9Sstevel@tonic-gate 			    USB_ATTRS_SHORT_XFER_OK | USB_ATTRS_AUTOCLEARING;
13987c478bd9Sstevel@tonic-gate 			isoc_req->isoc_cb = usb_as_record_cb;
13997c478bd9Sstevel@tonic-gate 			isoc_req->isoc_exc_cb = usb_as_record_exc_cb;
14007c478bd9Sstevel@tonic-gate 			isoc_req->isoc_client_private = (usb_opaque_t)uasp;
14017c478bd9Sstevel@tonic-gate 
14027c478bd9Sstevel@tonic-gate 			rval = usb_pipe_isoc_xfer(uasp->usb_as_isoc_ph,
14037c478bd9Sstevel@tonic-gate 			    isoc_req, 0);
14047c478bd9Sstevel@tonic-gate 
14057c478bd9Sstevel@tonic-gate 		} else {
14067c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ALL, uasp->usb_as_log_handle,
14077c478bd9Sstevel@tonic-gate 			    "usb_as_start_record: Isoc req allocation failed");
14087c478bd9Sstevel@tonic-gate 		}
14097c478bd9Sstevel@tonic-gate 
14107c478bd9Sstevel@tonic-gate 		mutex_enter(&uasp->usb_as_mutex);
14117c478bd9Sstevel@tonic-gate 
14127c478bd9Sstevel@tonic-gate 	} else {
14137c478bd9Sstevel@tonic-gate 
14147c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ALL, uasp->usb_as_log_handle,
14157c478bd9Sstevel@tonic-gate 		    "usb_as_start_record: Record in progress");
14167c478bd9Sstevel@tonic-gate 
14177c478bd9Sstevel@tonic-gate 		rval = USB_SUCCESS;
14187c478bd9Sstevel@tonic-gate 	}
14197c478bd9Sstevel@tonic-gate 
14207c478bd9Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
14217c478bd9Sstevel@tonic-gate 		uasp->usb_as_audio_state = USB_AS_IDLE;
14227c478bd9Sstevel@tonic-gate 		if (isoc_req) {
14237c478bd9Sstevel@tonic-gate 			usb_free_isoc_req(isoc_req);
14247c478bd9Sstevel@tonic-gate 			isoc_req = NULL;
14257c478bd9Sstevel@tonic-gate 		}
14267c478bd9Sstevel@tonic-gate 	}
14277c478bd9Sstevel@tonic-gate 
14287c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, uasp->usb_as_log_handle,
14297c478bd9Sstevel@tonic-gate 	    "usb_as_start_record: rval=%d", rval);
14307c478bd9Sstevel@tonic-gate 
14317c478bd9Sstevel@tonic-gate 	return (rval);
14327c478bd9Sstevel@tonic-gate }
14337c478bd9Sstevel@tonic-gate 
14347c478bd9Sstevel@tonic-gate 
14357c478bd9Sstevel@tonic-gate static int
usb_as_stop_record(usb_as_state_t * uasp)143688447a05SGarrett D'Amore usb_as_stop_record(usb_as_state_t *uasp)
14377c478bd9Sstevel@tonic-gate {
14387c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, uasp->usb_as_log_handle,
14397c478bd9Sstevel@tonic-gate 	    "usb_as_stop_record: ");
14407c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uasp->usb_as_mutex));
14417c478bd9Sstevel@tonic-gate 
14427c478bd9Sstevel@tonic-gate 	/* if we are disconnected, the pipe will be closed anyways */
144388447a05SGarrett D'Amore 	if (uasp->usb_as_dev_state == USB_DEV_DISCONNECTED)
14447c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
14457c478bd9Sstevel@tonic-gate 
14467c478bd9Sstevel@tonic-gate 	switch (uasp->usb_as_audio_state) {
14477c478bd9Sstevel@tonic-gate 	case USB_AS_ACTIVE:
14487c478bd9Sstevel@tonic-gate 		mutex_exit(&uasp->usb_as_mutex);
14497c478bd9Sstevel@tonic-gate 
14507c478bd9Sstevel@tonic-gate 		/*
14517c478bd9Sstevel@tonic-gate 		 * Stop polling. When the completion reason indicate that
14527c478bd9Sstevel@tonic-gate 		 * polling is over, return response message up.
14537c478bd9Sstevel@tonic-gate 		 */
14547c478bd9Sstevel@tonic-gate 		usb_pipe_stop_isoc_polling(uasp->usb_as_isoc_ph,
14557c478bd9Sstevel@tonic-gate 		    USB_FLAGS_SLEEP);
14567c478bd9Sstevel@tonic-gate 		mutex_enter(&uasp->usb_as_mutex);
14577c478bd9Sstevel@tonic-gate 
14587c478bd9Sstevel@tonic-gate 		break;
14597c478bd9Sstevel@tonic-gate 	case USB_AS_STOP_POLLING_STARTED:
14607c478bd9Sstevel@tonic-gate 		/* A stop polling in progress, wait for completion and reply */
14617c478bd9Sstevel@tonic-gate 		break;
14627c478bd9Sstevel@tonic-gate 	default:
146388447a05SGarrett D'Amore 		break;
14647c478bd9Sstevel@tonic-gate 	}
14657c478bd9Sstevel@tonic-gate 
14667c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
14677c478bd9Sstevel@tonic-gate }
14687c478bd9Sstevel@tonic-gate 
14697c478bd9Sstevel@tonic-gate 
14707c478bd9Sstevel@tonic-gate static void
usb_as_record_exc_cb(usb_pipe_handle_t ph,usb_isoc_req_t * isoc_req)14717c478bd9Sstevel@tonic-gate usb_as_record_exc_cb(usb_pipe_handle_t ph, usb_isoc_req_t *isoc_req)
14727c478bd9Sstevel@tonic-gate {
14737c478bd9Sstevel@tonic-gate 	usb_as_state_t	*uasp = (usb_as_state_t *)
1474112116d8Sfb 	    (isoc_req->isoc_client_private);
14757c478bd9Sstevel@tonic-gate 	usb_cr_t	completion_reason;
14767c478bd9Sstevel@tonic-gate 	int		rval;
14777c478bd9Sstevel@tonic-gate 
14787c478bd9Sstevel@tonic-gate 	completion_reason = isoc_req->isoc_completion_reason;
14797c478bd9Sstevel@tonic-gate 
14807c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, uasp->usb_as_log_handle,
14817c478bd9Sstevel@tonic-gate 	    "usb_as_record_exc_cb: ph=0x%p, isoc_req=0x%p, cr=%d",
1482112116d8Sfb 	    (void *)ph, (void *)isoc_req, completion_reason);
14837c478bd9Sstevel@tonic-gate 
14847c478bd9Sstevel@tonic-gate 	ASSERT((isoc_req->isoc_cb_flags & USB_CB_INTR_CONTEXT) == 0);
14857c478bd9Sstevel@tonic-gate 
14867c478bd9Sstevel@tonic-gate 	switch (completion_reason) {
14877c478bd9Sstevel@tonic-gate 	case USB_CR_STOPPED_POLLING:
14887c478bd9Sstevel@tonic-gate 	case USB_CR_PIPE_CLOSING:
14897c478bd9Sstevel@tonic-gate 	case USB_CR_PIPE_RESET:
14907c478bd9Sstevel@tonic-gate 
14917c478bd9Sstevel@tonic-gate 		break;
14927c478bd9Sstevel@tonic-gate 	case USB_CR_NO_RESOURCES:
14937c478bd9Sstevel@tonic-gate 		/*
14947c478bd9Sstevel@tonic-gate 		 * keep the show going: Since we have the original
14957c478bd9Sstevel@tonic-gate 		 * request, we just resubmit it
14967c478bd9Sstevel@tonic-gate 		 */
14977c478bd9Sstevel@tonic-gate 		rval = usb_pipe_isoc_xfer(uasp->usb_as_isoc_ph, isoc_req, 0);
14987c478bd9Sstevel@tonic-gate 
14997c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_ALL, uasp->usb_as_log_handle,
15007c478bd9Sstevel@tonic-gate 		    "usb_as_record_exc_cb: restart record rval=%d", rval);
15017c478bd9Sstevel@tonic-gate 
15027c478bd9Sstevel@tonic-gate 		return;
15037c478bd9Sstevel@tonic-gate 	default:
15047c478bd9Sstevel@tonic-gate 
15057c478bd9Sstevel@tonic-gate 		mutex_enter(&uasp->usb_as_mutex);
15067c478bd9Sstevel@tonic-gate 
15077c478bd9Sstevel@tonic-gate 		/* Do not start if one is already in progress */
15087c478bd9Sstevel@tonic-gate 		if (uasp->usb_as_audio_state != USB_AS_STOP_POLLING_STARTED) {
15097c478bd9Sstevel@tonic-gate 			uasp->usb_as_audio_state = USB_AS_STOP_POLLING_STARTED;
15107c478bd9Sstevel@tonic-gate 
15117c478bd9Sstevel@tonic-gate 			mutex_exit(&uasp->usb_as_mutex);
15127c478bd9Sstevel@tonic-gate 			(void) usb_pipe_stop_isoc_polling(ph,
15137c478bd9Sstevel@tonic-gate 			    USB_FLAGS_NOSLEEP);
15147c478bd9Sstevel@tonic-gate 
15157c478bd9Sstevel@tonic-gate 			return;
15167c478bd9Sstevel@tonic-gate 		} else {
15177c478bd9Sstevel@tonic-gate 			mutex_exit(&uasp->usb_as_mutex);
15187c478bd9Sstevel@tonic-gate 		}
15197c478bd9Sstevel@tonic-gate 
15207c478bd9Sstevel@tonic-gate 		break;
15217c478bd9Sstevel@tonic-gate 	}
15227c478bd9Sstevel@tonic-gate 	usb_free_isoc_req(isoc_req);
15237c478bd9Sstevel@tonic-gate 
15247c478bd9Sstevel@tonic-gate 	mutex_enter(&uasp->usb_as_mutex);
15257c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_ALL, uasp->usb_as_log_handle,
15267c478bd9Sstevel@tonic-gate 	    "usb_as_record_exc_cb: state=%d cr=0x%x",
15277c478bd9Sstevel@tonic-gate 	    uasp->usb_as_audio_state, completion_reason);
15287c478bd9Sstevel@tonic-gate 
15297c478bd9Sstevel@tonic-gate 	uasp->usb_as_audio_state = USB_AS_IDLE;
15307c478bd9Sstevel@tonic-gate 	mutex_exit(&uasp->usb_as_mutex);
15317c478bd9Sstevel@tonic-gate }
15327c478bd9Sstevel@tonic-gate 
15337c478bd9Sstevel@tonic-gate 
15347c478bd9Sstevel@tonic-gate /*ARGSUSED*/
15357c478bd9Sstevel@tonic-gate static void
usb_as_record_cb(usb_pipe_handle_t ph,usb_isoc_req_t * isoc_req)15367c478bd9Sstevel@tonic-gate usb_as_record_cb(usb_pipe_handle_t ph, usb_isoc_req_t *isoc_req)
15377c478bd9Sstevel@tonic-gate {
15387c478bd9Sstevel@tonic-gate 	usb_as_state_t *uasp = (usb_as_state_t *)isoc_req->isoc_client_private;
15397c478bd9Sstevel@tonic-gate 	int		i, offset, sz;
1540e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 	void *	ahdl;
15417c478bd9Sstevel@tonic-gate 	usb_audio_formats_t *format = &uasp->usb_as_curr_format;
15427c478bd9Sstevel@tonic-gate 	int		precision;
15437c478bd9Sstevel@tonic-gate 
15447c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_CB, uasp->usb_as_log_handle,
15457c478bd9Sstevel@tonic-gate 	    "usb_as_record_cb: rq=0x%p data=0x%p pkts=0x%x",
1546112116d8Sfb 	    (void *)isoc_req, (void *)isoc_req->isoc_data,
1547112116d8Sfb 	    isoc_req->isoc_pkts_count);
15487c478bd9Sstevel@tonic-gate 
15497c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_CB, uasp->usb_as_log_handle,
15507c478bd9Sstevel@tonic-gate 	    "\tfno=%" PRId64 ", n_pkts=%u, flag=0x%x, data=0x%p, cnt=%d",
15517c478bd9Sstevel@tonic-gate 	    isoc_req->isoc_frame_no, isoc_req->isoc_pkts_count,
1552112116d8Sfb 	    isoc_req->isoc_attributes, (void *)isoc_req->isoc_data,
15537c478bd9Sstevel@tonic-gate 	    isoc_req->isoc_error_count);
15547c478bd9Sstevel@tonic-gate 
15557c478bd9Sstevel@tonic-gate 	ASSERT((isoc_req->isoc_cb_flags & USB_CB_INTR_CONTEXT) != 0);
15567c478bd9Sstevel@tonic-gate 
15577c478bd9Sstevel@tonic-gate 	mutex_enter(&uasp->usb_as_mutex);
15587c478bd9Sstevel@tonic-gate 	ahdl = uasp->usb_as_ahdl;
15597c478bd9Sstevel@tonic-gate 	sz = uasp->usb_as_record_pkt_size;
1560*64391892SAlbert Lee 	precision = format->fmt_precision >> 3;
15617c478bd9Sstevel@tonic-gate 
15627c478bd9Sstevel@tonic-gate 	if (uasp->usb_as_audio_state != USB_AS_IDLE) {
15637c478bd9Sstevel@tonic-gate 		for (offset = i = 0; i < isoc_req->isoc_pkts_count; i++) {
15647c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(PRINT_MASK_CB, uasp->usb_as_log_handle,
15657c478bd9Sstevel@tonic-gate 			    "\tpkt%d: "
15667c478bd9Sstevel@tonic-gate 			    "offset=%d pktsize=%d len=%d status=%d resid=%d",
15677c478bd9Sstevel@tonic-gate 			    i, offset, sz,
15687c478bd9Sstevel@tonic-gate 			    isoc_req->isoc_pkt_descr[i].isoc_pkt_length,
15697c478bd9Sstevel@tonic-gate 			    isoc_req->isoc_pkt_descr[i].isoc_pkt_status,
15707c478bd9Sstevel@tonic-gate 			    isoc_req->isoc_pkt_descr[i].isoc_pkt_actual_length);
15717c478bd9Sstevel@tonic-gate 
15727c478bd9Sstevel@tonic-gate 			if (isoc_req->isoc_pkt_descr[i].isoc_pkt_status !=
15737c478bd9Sstevel@tonic-gate 			    USB_CR_OK) {
15747c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(PRINT_MASK_CB,
15757c478bd9Sstevel@tonic-gate 				    uasp->usb_as_log_handle,
15767c478bd9Sstevel@tonic-gate 				    "record: pkt=%d offset=0x%x status=%s",
15777c478bd9Sstevel@tonic-gate 				    i, offset, usb_str_cr(isoc_req->
15787c478bd9Sstevel@tonic-gate 				    isoc_pkt_descr[i].isoc_pkt_status));
15797c478bd9Sstevel@tonic-gate 			}
15807c478bd9Sstevel@tonic-gate 			mutex_exit(&uasp->usb_as_mutex);
15817c478bd9Sstevel@tonic-gate 
1582e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 			usb_ac_send_audio(ahdl,
15837c478bd9Sstevel@tonic-gate 			    isoc_req->isoc_data->b_rptr + offset,
158488447a05SGarrett D'Amore 			    isoc_req->isoc_pkt_descr[i].isoc_pkt_actual_length /
15857c478bd9Sstevel@tonic-gate 			    precision);
15867c478bd9Sstevel@tonic-gate 
15877c478bd9Sstevel@tonic-gate 			mutex_enter(&uasp->usb_as_mutex);
15887c478bd9Sstevel@tonic-gate 			offset += isoc_req->isoc_pkt_descr[i].isoc_pkt_length;
15897c478bd9Sstevel@tonic-gate 		}
15907c478bd9Sstevel@tonic-gate 	}
15917c478bd9Sstevel@tonic-gate 
15927c478bd9Sstevel@tonic-gate 	mutex_exit(&uasp->usb_as_mutex);
15937c478bd9Sstevel@tonic-gate 
15947c478bd9Sstevel@tonic-gate 	usb_free_isoc_req(isoc_req);
15957c478bd9Sstevel@tonic-gate }
15967c478bd9Sstevel@tonic-gate 
15977c478bd9Sstevel@tonic-gate /*
15987c478bd9Sstevel@tonic-gate  * Since the int_rate is 1000, we have to do special arithmetic for
15997c478bd9Sstevel@tonic-gate  * sample rates not multiple of 1K. For example,
16007c478bd9Sstevel@tonic-gate  * if the sample rate is 48000(i.e multiple of 1K), we can send 48000/1000
16017c478bd9Sstevel@tonic-gate  * = 48 samples every packet per channel. Since we have to support sample
16027c478bd9Sstevel@tonic-gate  * rate like 11025, 22050 and 44100, we will have some extra samples
16037c478bd9Sstevel@tonic-gate  * at the end that we need to spread among the 1000 cycles. So if we make
16047c478bd9Sstevel@tonic-gate  * the pktsize as below for these sample rates, at the end of 1000 cycles,
16057c478bd9Sstevel@tonic-gate  * we will be able to send all the data in the correct rate:
16067c478bd9Sstevel@tonic-gate  *
16077c478bd9Sstevel@tonic-gate  * 11025: 39 samples of 11, 1 of 12
16087c478bd9Sstevel@tonic-gate  * 22050: 19 samples of 22, 1 of 23
16097c478bd9Sstevel@tonic-gate  * 44100: 9 samples of 44, 1 of 45
16107c478bd9Sstevel@tonic-gate  *
16117c478bd9Sstevel@tonic-gate  * frameno is a simple counter maintained in the soft state structure.
16127c478bd9Sstevel@tonic-gate  * So the pkt size is:
16137c478bd9Sstevel@tonic-gate  * pkt_size =  ((frameno %  cycle) ?  pkt : (pkt + extra));
16147c478bd9Sstevel@tonic-gate  *
16157c478bd9Sstevel@tonic-gate  */
16167c478bd9Sstevel@tonic-gate 
16177c478bd9Sstevel@tonic-gate static int
usb_as_get_pktsize(usb_as_state_t * uasp,usb_frame_number_t frameno)1618*64391892SAlbert Lee usb_as_get_pktsize(usb_as_state_t *uasp, usb_frame_number_t frameno)
16197c478bd9Sstevel@tonic-gate {
1620*64391892SAlbert Lee 	static uint_t	sr = 0;
1621*64391892SAlbert Lee 	static ushort_t	pkt, cycle;
1622*64391892SAlbert Lee 	static int	extra;
16237c478bd9Sstevel@tonic-gate 	int	pkt_size = 0;
1624*64391892SAlbert Lee 	usb_audio_formats_t *format = &uasp->usb_as_curr_format;
1625*64391892SAlbert Lee 
1626*64391892SAlbert Lee 	if (sr != uasp->usb_as_curr_sr) {
1627*64391892SAlbert Lee 		/* calculate once */
1628*64391892SAlbert Lee 		sr = uasp->usb_as_curr_sr;
1629*64391892SAlbert Lee 		pkt = (sr + 500) / 1000;
1630*64391892SAlbert Lee 		extra = sr % 1000;
1631*64391892SAlbert Lee 
1632*64391892SAlbert Lee 		if (extra == 0) {
1633*64391892SAlbert Lee 			/* sample rate is a multiple of 1000 */
1634*64391892SAlbert Lee 			cycle = 1000;
1635*64391892SAlbert Lee 		} else {
1636*64391892SAlbert Lee 			/* find a common divisor of 1000 and extra */
1637*64391892SAlbert Lee 			int m = 1000;
1638*64391892SAlbert Lee 			int n = extra;
1639*64391892SAlbert Lee 
1640*64391892SAlbert Lee 			while (m != n) {
1641*64391892SAlbert Lee 				if (m > n) {
1642*64391892SAlbert Lee 					m = m - n;
1643*64391892SAlbert Lee 				} else {
1644*64391892SAlbert Lee 					n = n - m;
1645*64391892SAlbert Lee 				}
1646*64391892SAlbert Lee 			}
1647*64391892SAlbert Lee 			cycle = (1000 / n);
1648*64391892SAlbert Lee 			extra = ((extra >= 500) ? (extra - 1000) : extra) / n;
16497c478bd9Sstevel@tonic-gate 		}
16507c478bd9Sstevel@tonic-gate 	}
1651*64391892SAlbert Lee 	pkt_size = (((frameno + 1) % cycle) ?
1652*64391892SAlbert Lee 	    pkt : (pkt + extra));
1653*64391892SAlbert Lee 	pkt_size *= (format->fmt_precision >> 3)
1654*64391892SAlbert Lee 	    * format->fmt_chns;
16557c478bd9Sstevel@tonic-gate 
16567c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, uasp->usb_as_log_handle,
16577c478bd9Sstevel@tonic-gate 	    "usb_as_get_pktsize: %d", pkt_size);
16587c478bd9Sstevel@tonic-gate 
16597c478bd9Sstevel@tonic-gate 	return (pkt_size);
16607c478bd9Sstevel@tonic-gate }
16617c478bd9Sstevel@tonic-gate 
16627c478bd9Sstevel@tonic-gate 
16637c478bd9Sstevel@tonic-gate /*
16647c478bd9Sstevel@tonic-gate  * usb_as_send_ctrl_cmd:
16657c478bd9Sstevel@tonic-gate  *	Opens the pipe; sends a control command down
16667c478bd9Sstevel@tonic-gate  */
16677c478bd9Sstevel@tonic-gate static int
usb_as_send_ctrl_cmd(usb_as_state_t * uasp,uchar_t bmRequestType,uchar_t bRequest,ushort_t wValue,ushort_t wIndex,ushort_t wLength,mblk_t * data,boolean_t ignore_errors)16687c478bd9Sstevel@tonic-gate usb_as_send_ctrl_cmd(usb_as_state_t *uasp,
16697c478bd9Sstevel@tonic-gate 	uchar_t	bmRequestType, uchar_t bRequest,
16707c478bd9Sstevel@tonic-gate 	ushort_t wValue, ushort_t wIndex, ushort_t wLength,
16717c478bd9Sstevel@tonic-gate 	mblk_t	*data, boolean_t ignore_errors)
16727c478bd9Sstevel@tonic-gate {
167388447a05SGarrett D'Amore 	usb_ctrl_setup_t setup;
167488447a05SGarrett D'Amore 	usb_cr_t cr;
167588447a05SGarrett D'Amore 	usb_cb_flags_t cf;
16767c478bd9Sstevel@tonic-gate 
16777c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, uasp->usb_as_log_handle,
16787c478bd9Sstevel@tonic-gate 	    "usb_as_send_ctrl_cmd: Begin bmRequestType=%d,\n\t"
16797c478bd9Sstevel@tonic-gate 	    "bRequest=%d, wValue=%d, wIndex=%d, wLength=%d, data=0x%p",
1680112116d8Sfb 	    bmRequestType, bRequest, wValue, wIndex, wLength, (void *)data);
16817c478bd9Sstevel@tonic-gate 
168288447a05SGarrett D'Amore 	setup.bmRequestType	= bmRequestType & ~USB_DEV_REQ_DEV_TO_HOST;
168388447a05SGarrett D'Amore 	setup.bRequest		= bRequest;
168488447a05SGarrett D'Amore 	setup.wValue		= wValue;
168588447a05SGarrett D'Amore 	setup.wIndex		= wIndex;
168688447a05SGarrett D'Amore 	setup.wLength		= wLength;
168788447a05SGarrett D'Amore 	setup.attrs		= 0;
16887c478bd9Sstevel@tonic-gate 
168988447a05SGarrett D'Amore 	if (usb_pipe_ctrl_xfer_wait(uasp->usb_as_default_ph, &setup, &data,
169088447a05SGarrett D'Amore 	    &cr, &cf, 0) != USB_SUCCESS) {
16917c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ALL, uasp->usb_as_log_handle,
169288447a05SGarrett D'Amore 		    "usb_as_send_ctrl_cmd: usba xfer failed (req=%d), "
169388447a05SGarrett D'Amore 		    "completion reason: 0x%x, completion flags: 0x%x",
169488447a05SGarrett D'Amore 		    bRequest, cr, cf);
16957c478bd9Sstevel@tonic-gate 
169688447a05SGarrett D'Amore 		return (ignore_errors ? USB_SUCCESS: USB_FAILURE);
16977c478bd9Sstevel@tonic-gate 	}
16987c478bd9Sstevel@tonic-gate 
16997c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
17007c478bd9Sstevel@tonic-gate }
17017c478bd9Sstevel@tonic-gate 
17027c478bd9Sstevel@tonic-gate 
17037c478bd9Sstevel@tonic-gate /*
17047c478bd9Sstevel@tonic-gate  * Power management
17057c478bd9Sstevel@tonic-gate  */
17067c478bd9Sstevel@tonic-gate 
17077c478bd9Sstevel@tonic-gate /*ARGSUSED*/
17087c478bd9Sstevel@tonic-gate static void
usb_as_create_pm_components(dev_info_t * dip,usb_as_state_t * uasp)17097c478bd9Sstevel@tonic-gate usb_as_create_pm_components(dev_info_t *dip, usb_as_state_t *uasp)
17107c478bd9Sstevel@tonic-gate {
17117c478bd9Sstevel@tonic-gate 	usb_as_power_t	*uaspm;
17127c478bd9Sstevel@tonic-gate 	uint_t		pwr_states;
17137c478bd9Sstevel@tonic-gate 
17147c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_PM, uasp->usb_as_log_handle,
17157c478bd9Sstevel@tonic-gate 	    "usb_as_create_pm_components: begin");
17167c478bd9Sstevel@tonic-gate 
17177c478bd9Sstevel@tonic-gate 	/* Allocate the state structure */
17187c478bd9Sstevel@tonic-gate 	uaspm = kmem_zalloc(sizeof (usb_as_power_t), KM_SLEEP);
17197c478bd9Sstevel@tonic-gate 	uasp->usb_as_pm = uaspm;
17207c478bd9Sstevel@tonic-gate 	uaspm->aspm_state = uasp;
17217c478bd9Sstevel@tonic-gate 	uaspm->aspm_capabilities = 0;
17227c478bd9Sstevel@tonic-gate 	uaspm->aspm_current_power = USB_DEV_OS_FULL_PWR;
17237c478bd9Sstevel@tonic-gate 
17247c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_PM, uasp->usb_as_log_handle,
17257c478bd9Sstevel@tonic-gate 	    "usb_as_pm_components: remote Wakeup enabled");
17267c478bd9Sstevel@tonic-gate 	if (usb_create_pm_components(dip, &pwr_states) ==
17277c478bd9Sstevel@tonic-gate 	    USB_SUCCESS) {
17287c478bd9Sstevel@tonic-gate 		if (usb_handle_remote_wakeup(dip,
17297c478bd9Sstevel@tonic-gate 		    USB_REMOTE_WAKEUP_ENABLE) != USB_SUCCESS) {
17307c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_PM,
17317c478bd9Sstevel@tonic-gate 			    uasp->usb_as_log_handle,
17327c478bd9Sstevel@tonic-gate 			    "enable remote wakeup failed");
17337c478bd9Sstevel@tonic-gate 		} else {
17347c478bd9Sstevel@tonic-gate 			uaspm->aspm_wakeup_enabled = 1;
17357c478bd9Sstevel@tonic-gate 		}
17367c478bd9Sstevel@tonic-gate 		uaspm->aspm_pwr_states = (uint8_t)pwr_states;
17377c478bd9Sstevel@tonic-gate 		(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
17387c478bd9Sstevel@tonic-gate 	}
17397c478bd9Sstevel@tonic-gate 
17407c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_PM, uasp->usb_as_log_handle,
17417c478bd9Sstevel@tonic-gate 	    "usb_as_create_pm_components: end");
17427c478bd9Sstevel@tonic-gate }
17437c478bd9Sstevel@tonic-gate 
17447c478bd9Sstevel@tonic-gate 
17457c478bd9Sstevel@tonic-gate /*
17467c478bd9Sstevel@tonic-gate  * usb_as_power:
17477c478bd9Sstevel@tonic-gate  *	power entry point
17487c478bd9Sstevel@tonic-gate  */
17497c478bd9Sstevel@tonic-gate static int
usb_as_power(dev_info_t * dip,int comp,int level)17507c478bd9Sstevel@tonic-gate usb_as_power(dev_info_t *dip, int comp, int level)
17517c478bd9Sstevel@tonic-gate {
17527c478bd9Sstevel@tonic-gate 	int		instance = ddi_get_instance(dip);
17537c478bd9Sstevel@tonic-gate 	usb_as_state_t	*uasp;
17547c478bd9Sstevel@tonic-gate 	usb_as_power_t	*uaspm;
17557c478bd9Sstevel@tonic-gate 	int		retval = USB_FAILURE;
17567c478bd9Sstevel@tonic-gate 
17577c478bd9Sstevel@tonic-gate 	uasp = ddi_get_soft_state(usb_as_statep, instance);
17587c478bd9Sstevel@tonic-gate 
17597c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_PM, uasp->usb_as_log_handle,
17607c478bd9Sstevel@tonic-gate 	    "usb_as_power: comp=%d level=%d", comp, level);
17617c478bd9Sstevel@tonic-gate 
17627c478bd9Sstevel@tonic-gate 	(void) usb_serialize_access(uasp->usb_as_ser_acc, USB_WAIT, 0);
17637c478bd9Sstevel@tonic-gate 
17647c478bd9Sstevel@tonic-gate 	mutex_enter(&uasp->usb_as_mutex);
17657c478bd9Sstevel@tonic-gate 	uaspm = uasp->usb_as_pm;
17667c478bd9Sstevel@tonic-gate 
17677c478bd9Sstevel@tonic-gate 	if (USB_DEV_PWRSTATE_OK(uaspm->aspm_pwr_states, level)) {
17687c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_PM, uasp->usb_as_log_handle,
17697c478bd9Sstevel@tonic-gate 		    "usb_as_power: illegal level=%d pwr_states=%d",
1770112116d8Sfb 		    level, uaspm->aspm_pwr_states);
17717c478bd9Sstevel@tonic-gate 
17727c478bd9Sstevel@tonic-gate 		goto done;
17737c478bd9Sstevel@tonic-gate 	}
17747c478bd9Sstevel@tonic-gate 
17757c478bd9Sstevel@tonic-gate 	switch (level) {
17767c478bd9Sstevel@tonic-gate 	case USB_DEV_OS_PWR_OFF:
17777c478bd9Sstevel@tonic-gate 		retval = usb_as_pwrlvl0(uasp);
17787c478bd9Sstevel@tonic-gate 		break;
17797c478bd9Sstevel@tonic-gate 	case USB_DEV_OS_PWR_1:
17807c478bd9Sstevel@tonic-gate 		retval = usb_as_pwrlvl1(uasp);
17817c478bd9Sstevel@tonic-gate 		break;
17827c478bd9Sstevel@tonic-gate 	case USB_DEV_OS_PWR_2:
17837c478bd9Sstevel@tonic-gate 		retval = usb_as_pwrlvl2(uasp);
17847c478bd9Sstevel@tonic-gate 		break;
17857c478bd9Sstevel@tonic-gate 	case USB_DEV_OS_FULL_PWR:
17867c478bd9Sstevel@tonic-gate 		retval = usb_as_pwrlvl3(uasp);
17877c478bd9Sstevel@tonic-gate 		break;
17887c478bd9Sstevel@tonic-gate 	default:
17897c478bd9Sstevel@tonic-gate 		retval = USB_FAILURE;
17907c478bd9Sstevel@tonic-gate 		break;
17917c478bd9Sstevel@tonic-gate 	}
17927c478bd9Sstevel@tonic-gate 
17937c478bd9Sstevel@tonic-gate done:
17947c478bd9Sstevel@tonic-gate 
17957c478bd9Sstevel@tonic-gate 	usb_release_access(uasp->usb_as_ser_acc);
17967c478bd9Sstevel@tonic-gate 	mutex_exit(&uasp->usb_as_mutex);
17977c478bd9Sstevel@tonic-gate 
17987c478bd9Sstevel@tonic-gate 	return ((retval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
17997c478bd9Sstevel@tonic-gate }
18007c478bd9Sstevel@tonic-gate 
18017c478bd9Sstevel@tonic-gate 
18027c478bd9Sstevel@tonic-gate /*
18037c478bd9Sstevel@tonic-gate  * functions to handle power transition for various levels
18047c478bd9Sstevel@tonic-gate  * These functions act as place holders to issue USB commands
18057c478bd9Sstevel@tonic-gate  * to the devices to change their power levels
18067c478bd9Sstevel@tonic-gate  * Level 0 = Device is powered off
18077c478bd9Sstevel@tonic-gate  * Level 3 = Device if full powered
18087c478bd9Sstevel@tonic-gate  * Level 1,2 = Intermediate power level of the device as implemented
18097c478bd9Sstevel@tonic-gate  *	by the hardware.
18107c478bd9Sstevel@tonic-gate  * Note that Level 0 is OS power-off and Level 3 is OS full-power.
18117c478bd9Sstevel@tonic-gate  */
18127c478bd9Sstevel@tonic-gate static int
usb_as_pwrlvl0(usb_as_state_t * uasp)18137c478bd9Sstevel@tonic-gate usb_as_pwrlvl0(usb_as_state_t *uasp)
18147c478bd9Sstevel@tonic-gate {
18157c478bd9Sstevel@tonic-gate 	usb_as_power_t	*uaspm;
18167c478bd9Sstevel@tonic-gate 	int		rval;
18177c478bd9Sstevel@tonic-gate 
18187c478bd9Sstevel@tonic-gate 	uaspm = uasp->usb_as_pm;
18197c478bd9Sstevel@tonic-gate 
18207c478bd9Sstevel@tonic-gate 	switch (uasp->usb_as_dev_state) {
18217c478bd9Sstevel@tonic-gate 	case USB_DEV_ONLINE:
18227c478bd9Sstevel@tonic-gate 		/* Deny the powerdown request if the device is busy */
18237c478bd9Sstevel@tonic-gate 		if (uaspm->aspm_pm_busy != 0) {
18247c478bd9Sstevel@tonic-gate 
18257c478bd9Sstevel@tonic-gate 			return (USB_FAILURE);
18267c478bd9Sstevel@tonic-gate 		}
18277c478bd9Sstevel@tonic-gate 
18287c478bd9Sstevel@tonic-gate 		if (uasp->usb_as_audio_state != USB_AS_IDLE) {
18297c478bd9Sstevel@tonic-gate 
18307c478bd9Sstevel@tonic-gate 			return (USB_FAILURE);
18317c478bd9Sstevel@tonic-gate 		}
18327c478bd9Sstevel@tonic-gate 
18337c478bd9Sstevel@tonic-gate 		/* Issue USB D3 command to the device here */
18347c478bd9Sstevel@tonic-gate 		rval = usb_set_device_pwrlvl3(uasp->usb_as_dip);
18357c478bd9Sstevel@tonic-gate 		ASSERT(rval == USB_SUCCESS);
18367c478bd9Sstevel@tonic-gate 
18377c478bd9Sstevel@tonic-gate 		uasp->usb_as_dev_state = USB_DEV_PWRED_DOWN;
18387c478bd9Sstevel@tonic-gate 		uaspm->aspm_current_power = USB_DEV_OS_PWR_OFF;
18397c478bd9Sstevel@tonic-gate 
18407c478bd9Sstevel@tonic-gate 		/* FALLTHRU */
18417c478bd9Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
18427c478bd9Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
18437c478bd9Sstevel@tonic-gate 		/* allow a disconnected/cpr'ed device to go to low power */
18447c478bd9Sstevel@tonic-gate 
18457c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
18467c478bd9Sstevel@tonic-gate 	case USB_DEV_PWRED_DOWN:
18477c478bd9Sstevel@tonic-gate 	default:
18487c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_PM, uasp->usb_as_log_handle,
18497c478bd9Sstevel@tonic-gate 		    "usb_as_pwrlvl0: Illegal dev_state");
18507c478bd9Sstevel@tonic-gate 
18517c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
18527c478bd9Sstevel@tonic-gate 	}
18537c478bd9Sstevel@tonic-gate }
18547c478bd9Sstevel@tonic-gate 
18557c478bd9Sstevel@tonic-gate 
18567c478bd9Sstevel@tonic-gate /* ARGSUSED */
18577c478bd9Sstevel@tonic-gate static int
usb_as_pwrlvl1(usb_as_state_t * uasp)18587c478bd9Sstevel@tonic-gate usb_as_pwrlvl1(usb_as_state_t *uasp)
18597c478bd9Sstevel@tonic-gate {
18607c478bd9Sstevel@tonic-gate 	int		rval;
18617c478bd9Sstevel@tonic-gate 
18627c478bd9Sstevel@tonic-gate 	/* Issue USB D2 command to the device here */
18637c478bd9Sstevel@tonic-gate 	rval = usb_set_device_pwrlvl2(uasp->usb_as_dip);
18647c478bd9Sstevel@tonic-gate 	ASSERT(rval == USB_SUCCESS);
18657c478bd9Sstevel@tonic-gate 
18667c478bd9Sstevel@tonic-gate 	return (USB_FAILURE);
18677c478bd9Sstevel@tonic-gate }
18687c478bd9Sstevel@tonic-gate 
18697c478bd9Sstevel@tonic-gate 
18707c478bd9Sstevel@tonic-gate /* ARGSUSED */
18717c478bd9Sstevel@tonic-gate static int
usb_as_pwrlvl2(usb_as_state_t * uasp)18727c478bd9Sstevel@tonic-gate usb_as_pwrlvl2(usb_as_state_t *uasp)
18737c478bd9Sstevel@tonic-gate {
18747c478bd9Sstevel@tonic-gate 	int		rval;
18757c478bd9Sstevel@tonic-gate 
18767c478bd9Sstevel@tonic-gate 	rval = usb_set_device_pwrlvl1(uasp->usb_as_dip);
18777c478bd9Sstevel@tonic-gate 	ASSERT(rval == USB_SUCCESS);
18787c478bd9Sstevel@tonic-gate 
18797c478bd9Sstevel@tonic-gate 	return (USB_FAILURE);
18807c478bd9Sstevel@tonic-gate }
18817c478bd9Sstevel@tonic-gate 
18827c478bd9Sstevel@tonic-gate 
18837c478bd9Sstevel@tonic-gate static int
usb_as_pwrlvl3(usb_as_state_t * uasp)18847c478bd9Sstevel@tonic-gate usb_as_pwrlvl3(usb_as_state_t *uasp)
18857c478bd9Sstevel@tonic-gate {
18867c478bd9Sstevel@tonic-gate 	usb_as_power_t	*uaspm;
18877c478bd9Sstevel@tonic-gate 	int		rval;
18887c478bd9Sstevel@tonic-gate 
18897c478bd9Sstevel@tonic-gate 	uaspm = uasp->usb_as_pm;
18907c478bd9Sstevel@tonic-gate 
18917c478bd9Sstevel@tonic-gate 	switch (uasp->usb_as_dev_state) {
18927c478bd9Sstevel@tonic-gate 	case USB_DEV_PWRED_DOWN:
18937c478bd9Sstevel@tonic-gate 
18947c478bd9Sstevel@tonic-gate 		/* Issue USB D0 command to the device here */
18957c478bd9Sstevel@tonic-gate 		rval = usb_set_device_pwrlvl0(uasp->usb_as_dip);
18967c478bd9Sstevel@tonic-gate 		ASSERT(rval == USB_SUCCESS);
18977c478bd9Sstevel@tonic-gate 
18987c478bd9Sstevel@tonic-gate 		uasp->usb_as_dev_state = USB_DEV_ONLINE;
18997c478bd9Sstevel@tonic-gate 		uaspm->aspm_current_power = USB_DEV_OS_FULL_PWR;
19007c478bd9Sstevel@tonic-gate 
19017c478bd9Sstevel@tonic-gate 		/* FALLTHRU */
19027c478bd9Sstevel@tonic-gate 	case USB_DEV_ONLINE:
19037c478bd9Sstevel@tonic-gate 		/* we are already in full power */
19047c478bd9Sstevel@tonic-gate 
19057c478bd9Sstevel@tonic-gate 		/* fall thru */
19067c478bd9Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
19077c478bd9Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
19087c478bd9Sstevel@tonic-gate 		/* allow power change on a disconnected/cpr'ed device */
19097c478bd9Sstevel@tonic-gate 
19107c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
19117c478bd9Sstevel@tonic-gate 	default:
19127c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_PM, uasp->usb_as_log_handle,
19137c478bd9Sstevel@tonic-gate 		    "usb_as_pwrlvl3: Illegal dev_state");
19147c478bd9Sstevel@tonic-gate 
19157c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
19167c478bd9Sstevel@tonic-gate 	}
19177c478bd9Sstevel@tonic-gate }
19187c478bd9Sstevel@tonic-gate 
19197c478bd9Sstevel@tonic-gate 
19207c478bd9Sstevel@tonic-gate /*
19217c478bd9Sstevel@tonic-gate  * Descriptor Management
19227c478bd9Sstevel@tonic-gate  *
19237c478bd9Sstevel@tonic-gate  * usb_as_handle_descriptors:
19247c478bd9Sstevel@tonic-gate  *	read and parse all descriptors and build up usb_as_alts list
19257c478bd9Sstevel@tonic-gate  *
19267c478bd9Sstevel@tonic-gate  *	the order is as follows:
19277c478bd9Sstevel@tonic-gate  *	    interface, general, format, endpoint, CV endpoint
19287c478bd9Sstevel@tonic-gate  */
19297c478bd9Sstevel@tonic-gate static int
usb_as_handle_descriptors(usb_as_state_t * uasp)19307c478bd9Sstevel@tonic-gate usb_as_handle_descriptors(usb_as_state_t *uasp)
19317c478bd9Sstevel@tonic-gate {
19327c478bd9Sstevel@tonic-gate 	usb_client_dev_data_t		*dev_data = uasp->usb_as_dev_data;
19337c478bd9Sstevel@tonic-gate 	int				interface = dev_data->dev_curr_if;
19347c478bd9Sstevel@tonic-gate 	uint_t				alternate;
19357c478bd9Sstevel@tonic-gate 	uint_t				n_alternates;
1936*64391892SAlbert Lee 	int				len, i, j, n, n_srs, sr, index;
19377c478bd9Sstevel@tonic-gate 	int				rval = USB_SUCCESS;
19387c478bd9Sstevel@tonic-gate 	usb_if_descr_t			*if_descr;
19397c478bd9Sstevel@tonic-gate 	usb_audio_as_if_descr_t 	*general;
19407c478bd9Sstevel@tonic-gate 	usb_audio_type1_format_descr_t	*format;
1941*64391892SAlbert Lee 	uint_t				*sample_rates;
19427c478bd9Sstevel@tonic-gate 	usb_ep_descr_t			*ep;
19437c478bd9Sstevel@tonic-gate 	usb_audio_as_isoc_ep_descr_t	*cs_ep;
19447c478bd9Sstevel@tonic-gate 	usb_if_data_t			*if_data;
19457c478bd9Sstevel@tonic-gate 	usb_alt_if_data_t		*altif_data;
19467c478bd9Sstevel@tonic-gate 	usb_ep_data_t			*ep_data;
19477c478bd9Sstevel@tonic-gate 
19487c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, uasp->usb_as_log_handle,
1949112116d8Sfb 	    "usb_as_handle_descriptors: cfg=%ld interface=%d",
1950112116d8Sfb 	    (long)(dev_data->dev_curr_cfg - &dev_data->dev_cfg[0]),
19517c478bd9Sstevel@tonic-gate 	    dev_data->dev_curr_if);
19527c478bd9Sstevel@tonic-gate 
19537c478bd9Sstevel@tonic-gate 	if_data = &dev_data->dev_curr_cfg->cfg_if[dev_data->dev_curr_if];
19547c478bd9Sstevel@tonic-gate 	uasp->usb_as_ifno = interface;
19557c478bd9Sstevel@tonic-gate 
19567c478bd9Sstevel@tonic-gate 	/*
19577c478bd9Sstevel@tonic-gate 	 * find the number of alternates for this interface
19587c478bd9Sstevel@tonic-gate 	 * and allocate an array to store the descriptors for
19597c478bd9Sstevel@tonic-gate 	 * each alternate
19607c478bd9Sstevel@tonic-gate 	 */
19617c478bd9Sstevel@tonic-gate 	uasp->usb_as_n_alternates = n_alternates = if_data->if_n_alt;
19627c478bd9Sstevel@tonic-gate 	uasp->usb_as_alts = kmem_zalloc((n_alternates) *
1963112116d8Sfb 	    sizeof (usb_as_alt_descr_t), KM_SLEEP);
19647c478bd9Sstevel@tonic-gate 
19657c478bd9Sstevel@tonic-gate 	/*
19667c478bd9Sstevel@tonic-gate 	 * for each alternate read descriptors
19677c478bd9Sstevel@tonic-gate 	 */
19687c478bd9Sstevel@tonic-gate 	for (alternate = 0; alternate < n_alternates; alternate++) {
19697c478bd9Sstevel@tonic-gate 		altif_data = &if_data->if_alt[alternate];
19707c478bd9Sstevel@tonic-gate 
19717c478bd9Sstevel@tonic-gate 		uasp->usb_as_alts[alternate].alt_if =
1972112116d8Sfb 		    kmem_zalloc(sizeof (usb_if_descr_t), KM_SLEEP);
19737c478bd9Sstevel@tonic-gate 		if_descr = &altif_data->altif_descr;
19747c478bd9Sstevel@tonic-gate 
19757c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_ATTA, uasp->usb_as_log_handle,
19767c478bd9Sstevel@tonic-gate 		    "interface (%d.%d):\n\t"
19777c478bd9Sstevel@tonic-gate 		    "l = 0x%x type = 0x%x n = 0x%x alt = 0x%x #ep = 0x%x\n\t"
19787c478bd9Sstevel@tonic-gate 		    "iclass = 0x%x subclass = 0x%x proto = 0x%x string = 0x%x",
19797c478bd9Sstevel@tonic-gate 		    interface, alternate,
19807c478bd9Sstevel@tonic-gate 		    if_descr->bLength, if_descr->bDescriptorType,
19817c478bd9Sstevel@tonic-gate 		    if_descr->bInterfaceNumber, if_descr->bAlternateSetting,
19827c478bd9Sstevel@tonic-gate 		    if_descr->bNumEndpoints, if_descr->bInterfaceClass,
19837c478bd9Sstevel@tonic-gate 		    if_descr->bInterfaceSubClass,
19847c478bd9Sstevel@tonic-gate 		    if_descr->bInterfaceProtocol, if_descr->iInterface);
19857c478bd9Sstevel@tonic-gate 
19867c478bd9Sstevel@tonic-gate 		*(uasp->usb_as_alts[alternate].alt_if) = *if_descr;
19877c478bd9Sstevel@tonic-gate 
19887c478bd9Sstevel@tonic-gate 		/* read the general descriptor */
19897c478bd9Sstevel@tonic-gate 		index = 0;
19907c478bd9Sstevel@tonic-gate 
19917c478bd9Sstevel@tonic-gate 		if (altif_data->altif_cvs == NULL) {
19927c478bd9Sstevel@tonic-gate 
19937c478bd9Sstevel@tonic-gate 			continue;
19947c478bd9Sstevel@tonic-gate 		}
19957c478bd9Sstevel@tonic-gate 
19967c478bd9Sstevel@tonic-gate 		general = kmem_zalloc(sizeof (*general), KM_SLEEP);
19977c478bd9Sstevel@tonic-gate 
19987c478bd9Sstevel@tonic-gate 		len = usb_parse_data(AS_IF_DESCR_FORMAT,
1999112116d8Sfb 		    altif_data->altif_cvs[index].cvs_buf,
2000112116d8Sfb 		    altif_data->altif_cvs[index].cvs_buf_len,
2001112116d8Sfb 		    (void *)general, sizeof (*general));
20027c478bd9Sstevel@tonic-gate 
20037c478bd9Sstevel@tonic-gate 		/* is this a sane header descriptor */
20047c478bd9Sstevel@tonic-gate 		if (!((len >= AS_IF_DESCR_SIZE) &&
20057c478bd9Sstevel@tonic-gate 		    (general->bDescriptorType == USB_AUDIO_CS_INTERFACE) &&
20067c478bd9Sstevel@tonic-gate 		    (general->bDescriptorSubType == USB_AUDIO_AS_GENERAL))) {
20077c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ATTA,
20087c478bd9Sstevel@tonic-gate 			    uasp->usb_as_log_handle,
20097c478bd9Sstevel@tonic-gate 			    "invalid general cs interface descr");
20107c478bd9Sstevel@tonic-gate 
20117c478bd9Sstevel@tonic-gate 			kmem_free(general, sizeof (*general));
20127c478bd9Sstevel@tonic-gate 
20137c478bd9Sstevel@tonic-gate 			continue;
20147c478bd9Sstevel@tonic-gate 		}
20157c478bd9Sstevel@tonic-gate 
20167c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_ATTA, uasp->usb_as_log_handle,
20177c478bd9Sstevel@tonic-gate 		    "general (%d.%d): type=0x%x subtype=0x%x termlink=0x%x\n\t"
20187c478bd9Sstevel@tonic-gate 		    "delay=0x%x format=0x%x",
20197c478bd9Sstevel@tonic-gate 		    interface, alternate,
20207c478bd9Sstevel@tonic-gate 		    general->bDescriptorType, general->bDescriptorSubType,
20217c478bd9Sstevel@tonic-gate 		    general->bTerminalLink, general->bDelay,
20227c478bd9Sstevel@tonic-gate 		    general->wFormatTag);
20237c478bd9Sstevel@tonic-gate 
20247c478bd9Sstevel@tonic-gate 		uasp->usb_as_alts[alternate].alt_general = general;
20257c478bd9Sstevel@tonic-gate 
20267c478bd9Sstevel@tonic-gate 		/*
20277c478bd9Sstevel@tonic-gate 		 * there should be one format descriptor of unknown size.
20287c478bd9Sstevel@tonic-gate 		 * the format descriptor contains just bytes, no need to
20297c478bd9Sstevel@tonic-gate 		 * parse
20307c478bd9Sstevel@tonic-gate 		 */
20317c478bd9Sstevel@tonic-gate 		index++;
20327c478bd9Sstevel@tonic-gate 		len = altif_data->altif_cvs[index].cvs_buf_len;
20337c478bd9Sstevel@tonic-gate 		format = kmem_zalloc(len, KM_SLEEP);
20347c478bd9Sstevel@tonic-gate 		bcopy(altif_data->altif_cvs[index].cvs_buf, format, len);
20357c478bd9Sstevel@tonic-gate 
20367c478bd9Sstevel@tonic-gate 		/* is this a sane format descriptor */
20377c478bd9Sstevel@tonic-gate 		if (!((format->blength >= AUDIO_TYPE1_FORMAT_SIZE) &&
20387c478bd9Sstevel@tonic-gate 		    format->bDescriptorSubType == USB_AUDIO_AS_FORMAT_TYPE)) {
20397c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ATTA,
20407c478bd9Sstevel@tonic-gate 			    uasp->usb_as_log_handle,
20417c478bd9Sstevel@tonic-gate 			    "invalid format cs interface descr");
20427c478bd9Sstevel@tonic-gate 
20437c478bd9Sstevel@tonic-gate 			kmem_free(format, len);
20447c478bd9Sstevel@tonic-gate 
20457c478bd9Sstevel@tonic-gate 			continue;
20467c478bd9Sstevel@tonic-gate 		}
20477c478bd9Sstevel@tonic-gate 
20487c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_ATTA, uasp->usb_as_log_handle,
20497c478bd9Sstevel@tonic-gate 		    "format (%d.%d): len = %d "
20507c478bd9Sstevel@tonic-gate 		    "type = 0x%x subtype = 0x%x format = 0x%x\n\t"
20517c478bd9Sstevel@tonic-gate 		    "#channels = 0x%x subframe = 0x%x resolution = 0x%x\n\t"
20527c478bd9Sstevel@tonic-gate 		    "sample freq type = 0x%x",
20537c478bd9Sstevel@tonic-gate 		    interface, alternate, len,
20547c478bd9Sstevel@tonic-gate 		    format->bDescriptorType,
20557c478bd9Sstevel@tonic-gate 		    format->bDescriptorSubType,
20567c478bd9Sstevel@tonic-gate 		    format->bFormatType,
20577c478bd9Sstevel@tonic-gate 		    format->bNrChannels,
20587c478bd9Sstevel@tonic-gate 		    format->bSubFrameSize,
20597c478bd9Sstevel@tonic-gate 		    format->bBitResolution,
20607c478bd9Sstevel@tonic-gate 		    format->bSamFreqType);
20617c478bd9Sstevel@tonic-gate 
20627c478bd9Sstevel@tonic-gate 		if (format->bSamFreqType == 0) {
20637c478bd9Sstevel@tonic-gate 			/* continuous sample rate limits */
20647c478bd9Sstevel@tonic-gate 			n_srs = 2;
20657c478bd9Sstevel@tonic-gate 			uasp->usb_as_alts[alternate].alt_continuous_sr++;
20667c478bd9Sstevel@tonic-gate 		} else {
20677c478bd9Sstevel@tonic-gate 			n_srs = format->bSamFreqType;
20687c478bd9Sstevel@tonic-gate 		}
20697c478bd9Sstevel@tonic-gate 
2070*64391892SAlbert Lee 		sample_rates =
2071112116d8Sfb 		    kmem_zalloc(n_srs * (sizeof (uint_t)), KM_SLEEP);
20727c478bd9Sstevel@tonic-gate 
20737c478bd9Sstevel@tonic-gate 		/* go thru all sample rates (3 bytes) each */
2074*64391892SAlbert Lee 		for (i = 0, j = 0, n = 0; n < n_srs; i += 3, n++) {
2075*64391892SAlbert Lee 			sr = (format->bSamFreqs[i+2] << 16) |
2076*64391892SAlbert Lee 			    (format->bSamFreqs[i+1] << 8) |
2077*64391892SAlbert Lee 			    format->bSamFreqs[i];
20787c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(PRINT_MASK_ATTA,
20797c478bd9Sstevel@tonic-gate 			    uasp->usb_as_log_handle,
20807c478bd9Sstevel@tonic-gate 			    "sr = %d", sr);
2081*64391892SAlbert Lee 			sample_rates[n] = sr;
2082*64391892SAlbert Lee 			if (sr != 0) {
2083*64391892SAlbert Lee 				j++;
2084*64391892SAlbert Lee 			}
2085*64391892SAlbert Lee 		}
2086*64391892SAlbert Lee 
2087*64391892SAlbert Lee 		if (j == 0) {
2088*64391892SAlbert Lee 			USB_DPRINTF_L2(PRINT_MASK_ATTA,
2089*64391892SAlbert Lee 			    uasp->usb_as_log_handle,
2090*64391892SAlbert Lee 			    "format cs interface descr has no valid rates");
2091*64391892SAlbert Lee 
2092*64391892SAlbert Lee 			kmem_free(format, len);
2093*64391892SAlbert Lee 			kmem_free(sample_rates, n_srs * (sizeof (uint_t)));
20947c478bd9Sstevel@tonic-gate 
2095*64391892SAlbert Lee 			continue;
20967c478bd9Sstevel@tonic-gate 		}
20977c478bd9Sstevel@tonic-gate 
2098*64391892SAlbert Lee 		uasp->usb_as_alts[alternate].alt_format_len = (uchar_t)len;
2099*64391892SAlbert Lee 
2100*64391892SAlbert Lee 		uasp->usb_as_alts[alternate].alt_format = format;
2101*64391892SAlbert Lee 
2102*64391892SAlbert Lee 		uasp->usb_as_alts[alternate].alt_n_sample_rates =
2103*64391892SAlbert Lee 		    (uchar_t)n_srs;
2104*64391892SAlbert Lee 
2105*64391892SAlbert Lee 		uasp->usb_as_alts[alternate].alt_sample_rates =
2106*64391892SAlbert Lee 		    sample_rates;
2107*64391892SAlbert Lee 
21087c478bd9Sstevel@tonic-gate 		if ((ep_data = usb_lookup_ep_data(uasp->usb_as_dip,
21097c478bd9Sstevel@tonic-gate 		    dev_data, interface, alternate, 0,
21107c478bd9Sstevel@tonic-gate 		    USB_EP_ATTR_ISOCH, USB_EP_DIR_IN)) == NULL) {
21117c478bd9Sstevel@tonic-gate 			if ((ep_data = usb_lookup_ep_data(uasp->usb_as_dip,
21127c478bd9Sstevel@tonic-gate 			    dev_data, interface, alternate, 0,
21137c478bd9Sstevel@tonic-gate 			    USB_EP_ATTR_ISOCH, USB_EP_DIR_OUT)) == NULL) {
21147c478bd9Sstevel@tonic-gate 
21157c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(PRINT_MASK_ATTA,
21167c478bd9Sstevel@tonic-gate 				    uasp->usb_as_log_handle,
21177c478bd9Sstevel@tonic-gate 				    "no endpoint descriptor found");
21187c478bd9Sstevel@tonic-gate 
21197c478bd9Sstevel@tonic-gate 				continue;
21207c478bd9Sstevel@tonic-gate 			}
21217c478bd9Sstevel@tonic-gate 		}
21227c478bd9Sstevel@tonic-gate 		ep = &ep_data->ep_descr;
21237c478bd9Sstevel@tonic-gate 
21247c478bd9Sstevel@tonic-gate 		uasp->usb_as_alts[alternate].alt_ep =
21257c478bd9Sstevel@tonic-gate 		    kmem_zalloc(sizeof (usb_ep_descr_t), KM_SLEEP);
21267c478bd9Sstevel@tonic-gate 		*(uasp->usb_as_alts[alternate].alt_ep) = *ep;
21277c478bd9Sstevel@tonic-gate 
21287c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ATTA, uasp->usb_as_log_handle,
21297c478bd9Sstevel@tonic-gate 		    "endpoint (%d.%d):\n\t"
21307c478bd9Sstevel@tonic-gate 		    "len = 0x%x type = 0x%x add = 0x%x "
21317c478bd9Sstevel@tonic-gate 		    "attr = 0x%x mps = 0x%x\n\t"
21327c478bd9Sstevel@tonic-gate 		    "int = 0x%x",
21337c478bd9Sstevel@tonic-gate 		    interface, alternate,
21347c478bd9Sstevel@tonic-gate 		    ep->bLength, ep->bDescriptorType, ep->bEndpointAddress,
21357c478bd9Sstevel@tonic-gate 		    ep->bmAttributes, ep->wMaxPacketSize, ep->bInterval);
21367c478bd9Sstevel@tonic-gate 
21377c478bd9Sstevel@tonic-gate 		uasp->usb_as_alts[alternate].alt_mode  =
21387c478bd9Sstevel@tonic-gate 		    (ep->bEndpointAddress & USB_EP_DIR_IN) ?
2139e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 		    USB_AUDIO_RECORD : USB_AUDIO_PLAY;
21407c478bd9Sstevel@tonic-gate 
21417c478bd9Sstevel@tonic-gate 		if (ep_data->ep_n_cvs == 0) {
21427c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ATTA,
21437c478bd9Sstevel@tonic-gate 			    uasp->usb_as_log_handle,
21447c478bd9Sstevel@tonic-gate 			    "no cv ep descriptor");
21457c478bd9Sstevel@tonic-gate 
21467c478bd9Sstevel@tonic-gate 			continue;
21477c478bd9Sstevel@tonic-gate 		}
21487c478bd9Sstevel@tonic-gate 
21497c478bd9Sstevel@tonic-gate 		cs_ep = kmem_zalloc(sizeof (*cs_ep), KM_SLEEP);
21507c478bd9Sstevel@tonic-gate 		len = usb_parse_data(AS_ISOC_EP_DESCR_FORMAT,
2151112116d8Sfb 		    ep_data->ep_cvs[0].cvs_buf,
2152112116d8Sfb 		    ep_data->ep_cvs[0].cvs_buf_len,
2153112116d8Sfb 		    (void *)cs_ep, sizeof (*cs_ep));
21547c478bd9Sstevel@tonic-gate 
21557c478bd9Sstevel@tonic-gate 		if ((len < AS_ISOC_EP_DESCR_SIZE) ||
21567c478bd9Sstevel@tonic-gate 		    (cs_ep->bDescriptorType != USB_AUDIO_CS_ENDPOINT)) {
21577c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ATTA,
21587c478bd9Sstevel@tonic-gate 			    uasp->usb_as_log_handle,
21597c478bd9Sstevel@tonic-gate 			    "cs endpoint descriptor invalid (%d)", len);
21607c478bd9Sstevel@tonic-gate 			kmem_free(cs_ep, sizeof (*cs_ep));
21617c478bd9Sstevel@tonic-gate 
21627c478bd9Sstevel@tonic-gate 			continue;
21637c478bd9Sstevel@tonic-gate 		}
21647c478bd9Sstevel@tonic-gate 
21657c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ATTA, uasp->usb_as_log_handle,
21667c478bd9Sstevel@tonic-gate 		    "cs isoc endpoint (%d.%d):\n\t"
21677c478bd9Sstevel@tonic-gate 		    "type=0x%x sub=0x%x attr=0x%x units=0x%x delay=%x",
21687c478bd9Sstevel@tonic-gate 		    interface, alternate,
21697c478bd9Sstevel@tonic-gate 		    cs_ep->bDescriptorType,
21707c478bd9Sstevel@tonic-gate 		    cs_ep->bDescriptorSubType,
21717c478bd9Sstevel@tonic-gate 		    cs_ep->bmAttributes,
21727c478bd9Sstevel@tonic-gate 		    cs_ep->bLockDelayUnits,
21734610e4a0Sfrits 		    cs_ep->wLockDelay);
21747c478bd9Sstevel@tonic-gate 
21757c478bd9Sstevel@tonic-gate 		uasp->usb_as_alts[alternate].alt_cs_ep = cs_ep;
21767c478bd9Sstevel@tonic-gate 
21777c478bd9Sstevel@tonic-gate 		/* we are done */
21787c478bd9Sstevel@tonic-gate 		uasp->usb_as_alts[alternate].alt_valid++;
21797c478bd9Sstevel@tonic-gate 	}
21807c478bd9Sstevel@tonic-gate 
21817c478bd9Sstevel@tonic-gate done:
21827c478bd9Sstevel@tonic-gate 	usb_as_prepare_registration_data(uasp);
21837c478bd9Sstevel@tonic-gate 
21847c478bd9Sstevel@tonic-gate 	return (rval);
21857c478bd9Sstevel@tonic-gate }
21867c478bd9Sstevel@tonic-gate 
21877c478bd9Sstevel@tonic-gate 
21887c478bd9Sstevel@tonic-gate /*
21897c478bd9Sstevel@tonic-gate  * usb_as_free_alts:
21907c478bd9Sstevel@tonic-gate  *	cleanup alternate list and deallocate all descriptors
21917c478bd9Sstevel@tonic-gate  */
21927c478bd9Sstevel@tonic-gate static void
usb_as_free_alts(usb_as_state_t * uasp)21937c478bd9Sstevel@tonic-gate usb_as_free_alts(usb_as_state_t *uasp)
21947c478bd9Sstevel@tonic-gate {
21957c478bd9Sstevel@tonic-gate 	int	alt;
21967c478bd9Sstevel@tonic-gate 	usb_as_alt_descr_t *altp;
21977c478bd9Sstevel@tonic-gate 
21987c478bd9Sstevel@tonic-gate 	if (uasp->usb_as_alts) {
21997c478bd9Sstevel@tonic-gate 		for (alt = 0; alt < uasp->usb_as_n_alternates; alt++) {
22007c478bd9Sstevel@tonic-gate 			altp = &uasp->usb_as_alts[alt];
22017c478bd9Sstevel@tonic-gate 			if (altp) {
22027c478bd9Sstevel@tonic-gate 				if (altp->alt_sample_rates) {
22037c478bd9Sstevel@tonic-gate 					kmem_free(altp->alt_sample_rates,
22047c478bd9Sstevel@tonic-gate 					    altp->alt_n_sample_rates *
22057c478bd9Sstevel@tonic-gate 					    sizeof (uint_t));
22067c478bd9Sstevel@tonic-gate 				}
22077c478bd9Sstevel@tonic-gate 				if (altp->alt_if) {
22087c478bd9Sstevel@tonic-gate 					kmem_free(altp->alt_if,
22097c478bd9Sstevel@tonic-gate 					    sizeof (usb_if_descr_t));
22107c478bd9Sstevel@tonic-gate 				}
22117c478bd9Sstevel@tonic-gate 				if (altp->alt_general) {
22127c478bd9Sstevel@tonic-gate 					kmem_free(altp->alt_general,
22137c478bd9Sstevel@tonic-gate 					    sizeof (usb_audio_as_if_descr_t));
22147c478bd9Sstevel@tonic-gate 				}
22157c478bd9Sstevel@tonic-gate 				if (altp->alt_format) {
22167c478bd9Sstevel@tonic-gate 					kmem_free(altp->alt_format,
22177c478bd9Sstevel@tonic-gate 					    altp->alt_format_len);
22187c478bd9Sstevel@tonic-gate 				}
22197c478bd9Sstevel@tonic-gate 				if (altp->alt_ep) {
22207c478bd9Sstevel@tonic-gate 					kmem_free(altp->alt_ep,
22217c478bd9Sstevel@tonic-gate 					    sizeof (usb_ep_descr_t));
22227c478bd9Sstevel@tonic-gate 				}
22237c478bd9Sstevel@tonic-gate 				if (altp->alt_cs_ep) {
22247c478bd9Sstevel@tonic-gate 					kmem_free(altp->alt_cs_ep,
22257c478bd9Sstevel@tonic-gate 					    sizeof (*altp->alt_cs_ep));
22267c478bd9Sstevel@tonic-gate 				}
22277c478bd9Sstevel@tonic-gate 			}
22287c478bd9Sstevel@tonic-gate 		}
22297c478bd9Sstevel@tonic-gate 		kmem_free(uasp->usb_as_alts, (uasp->usb_as_n_alternates) *
22307c478bd9Sstevel@tonic-gate 		    sizeof (usb_as_alt_descr_t));
22317c478bd9Sstevel@tonic-gate 	}
22327c478bd9Sstevel@tonic-gate }
22337c478bd9Sstevel@tonic-gate 
22347c478bd9Sstevel@tonic-gate 
22357c478bd9Sstevel@tonic-gate /*
22367c478bd9Sstevel@tonic-gate  * usb_as_prepare_registration_data
22377c478bd9Sstevel@tonic-gate  */
22387c478bd9Sstevel@tonic-gate static void
usb_as_prepare_registration_data(usb_as_state_t * uasp)22397c478bd9Sstevel@tonic-gate usb_as_prepare_registration_data(usb_as_state_t   *uasp)
22407c478bd9Sstevel@tonic-gate {
22417c478bd9Sstevel@tonic-gate 	usb_as_registration_t *reg = &uasp->usb_as_reg;
22427c478bd9Sstevel@tonic-gate 	usb_audio_type1_format_descr_t	*format;
22437c478bd9Sstevel@tonic-gate 	uchar_t n_alternates = uasp->usb_as_n_alternates;
2244e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 	int alt, n;
22457c478bd9Sstevel@tonic-gate 
22467c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, uasp->usb_as_log_handle,
22477c478bd9Sstevel@tonic-gate 	    "usb_as_prepare_registration_data:");
22487c478bd9Sstevel@tonic-gate 
22497c478bd9Sstevel@tonic-gate 	/* there has to be at least two alternates, ie 0 and 1	*/
22507c478bd9Sstevel@tonic-gate 	if (n_alternates < 2) {
22517c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ATTA, uasp->usb_as_log_handle,
22527c478bd9Sstevel@tonic-gate 		    "not enough alternates %d", n_alternates);
22537c478bd9Sstevel@tonic-gate 
22547c478bd9Sstevel@tonic-gate 		return;
22557c478bd9Sstevel@tonic-gate 	}
22567c478bd9Sstevel@tonic-gate 
22577c478bd9Sstevel@tonic-gate 	reg->reg_ifno = uasp->usb_as_ifno;
22587c478bd9Sstevel@tonic-gate 
22597c478bd9Sstevel@tonic-gate 	/* all endpoints need to have the same direction */
2260*64391892SAlbert Lee 	for (alt = 1; alt < n_alternates; alt++) {
22617c478bd9Sstevel@tonic-gate 		if (!uasp->usb_as_alts[alt].alt_valid) {
22627c478bd9Sstevel@tonic-gate 			continue;
22637c478bd9Sstevel@tonic-gate 		}
2264*64391892SAlbert Lee 		if (reg->reg_mode && uasp->usb_as_alts[alt].alt_mode !=
22657c478bd9Sstevel@tonic-gate 		    reg->reg_mode) {
22667c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ATTA, uasp->usb_as_log_handle,
22677c478bd9Sstevel@tonic-gate 			    "alternates have different direction");
22687c478bd9Sstevel@tonic-gate 
22697c478bd9Sstevel@tonic-gate 			return;
22707c478bd9Sstevel@tonic-gate 		}
2271*64391892SAlbert Lee 		reg->reg_mode = uasp->usb_as_alts[alt].alt_mode;
22727c478bd9Sstevel@tonic-gate 	}
22737c478bd9Sstevel@tonic-gate 
22747c478bd9Sstevel@tonic-gate 	/*
22757c478bd9Sstevel@tonic-gate 	 * we assume that alternate 0 is not interesting (no bandwidth),
22767c478bd9Sstevel@tonic-gate 	 * we check all formats and use the formats that we can support
22777c478bd9Sstevel@tonic-gate 	 */
22787c478bd9Sstevel@tonic-gate 	for (alt = 1, n = 0; alt < n_alternates; alt++) {
22797c478bd9Sstevel@tonic-gate 		if (!uasp->usb_as_alts[alt].alt_valid) {
22807c478bd9Sstevel@tonic-gate 			continue;
22817c478bd9Sstevel@tonic-gate 		}
22827c478bd9Sstevel@tonic-gate 
22837c478bd9Sstevel@tonic-gate 		format = uasp->usb_as_alts[alt].alt_format;
22847c478bd9Sstevel@tonic-gate 		if (uasp->usb_as_alts[alt].alt_valid &&
22857c478bd9Sstevel@tonic-gate 		    (n < USB_AS_N_FORMATS) &&
2286*64391892SAlbert Lee 		    (usb_as_valid_format(uasp, alt) == USB_SUCCESS)) {
22877c478bd9Sstevel@tonic-gate 			reg->reg_formats[n].fmt_termlink =
22887c478bd9Sstevel@tonic-gate 			    uasp->usb_as_alts[alt].alt_general->
22897c478bd9Sstevel@tonic-gate 			    bTerminalLink;
22907c478bd9Sstevel@tonic-gate 			reg->reg_formats[n].fmt_alt = (uchar_t)alt;
22917c478bd9Sstevel@tonic-gate 			reg->reg_formats[n].fmt_chns =
2292112116d8Sfb 			    format->bNrChannels;
22937c478bd9Sstevel@tonic-gate 			reg->reg_formats[n].fmt_precision =
2294112116d8Sfb 			    format->bBitResolution;
2295*64391892SAlbert Lee 			reg->reg_formats[n].fmt_encoding =
2296e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 			    format->bFormatType;
2297*64391892SAlbert Lee 			reg->reg_formats[n].fmt_n_srs =
2298*64391892SAlbert Lee 			    uasp->usb_as_alts[alt].alt_n_sample_rates;
2299*64391892SAlbert Lee 			reg->reg_formats[n++].fmt_srs =
2300*64391892SAlbert Lee 			    uasp->usb_as_alts[alt].alt_sample_rates;
23017c478bd9Sstevel@tonic-gate 		}
23027c478bd9Sstevel@tonic-gate 	}
23037c478bd9Sstevel@tonic-gate 
23047c478bd9Sstevel@tonic-gate 	reg->reg_n_formats = (uchar_t)n;
23057c478bd9Sstevel@tonic-gate 
23067c478bd9Sstevel@tonic-gate 	if (n == 0) {
23077c478bd9Sstevel@tonic-gate 		/* no valid formats */
23087c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ATTA, uasp->usb_as_log_handle,
23097c478bd9Sstevel@tonic-gate 		    "zero valid formats");
23107c478bd9Sstevel@tonic-gate 
23117c478bd9Sstevel@tonic-gate 		return;
23127c478bd9Sstevel@tonic-gate 	}
23137c478bd9Sstevel@tonic-gate 
23147c478bd9Sstevel@tonic-gate 	/* dump what we have so far */
23157c478bd9Sstevel@tonic-gate 	for (n = 0; n < reg->reg_n_formats; n++) {
23167c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_ATTA, uasp->usb_as_log_handle,
2317e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 		    "regformats[%d]: termlink = %d, alt=%d chns=%d"
2318e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 		    " prec=%d enc=%d", n,
2319e272e4c8SBinzi Cao - Sun Microsystems - Beijing China 		    reg->reg_formats[n].fmt_termlink,
23207c478bd9Sstevel@tonic-gate 		    reg->reg_formats[n].fmt_alt,
23217c478bd9Sstevel@tonic-gate 		    reg->reg_formats[n].fmt_chns,
23227c478bd9Sstevel@tonic-gate 		    reg->reg_formats[n].fmt_precision,
23237c478bd9Sstevel@tonic-gate 		    reg->reg_formats[n].fmt_encoding);
23247c478bd9Sstevel@tonic-gate 	}
23257c478bd9Sstevel@tonic-gate 
23267c478bd9Sstevel@tonic-gate 	reg->reg_valid++;
23277c478bd9Sstevel@tonic-gate }
23287c478bd9Sstevel@tonic-gate 
23297c478bd9Sstevel@tonic-gate 
23307c478bd9Sstevel@tonic-gate /*
23317c478bd9Sstevel@tonic-gate  * usb_as_valid_format:
23327c478bd9Sstevel@tonic-gate  *	check if this format can be supported
23337c478bd9Sstevel@tonic-gate  */
23347c478bd9Sstevel@tonic-gate static int
usb_as_valid_format(usb_as_state_t * uasp,uint_t alternate)2335*64391892SAlbert Lee usb_as_valid_format(usb_as_state_t *uasp, uint_t alternate)
23367c478bd9Sstevel@tonic-gate {
23377c478bd9Sstevel@tonic-gate 	usb_as_alt_descr_t *alt_descr = &uasp->usb_as_alts[alternate];
23387c478bd9Sstevel@tonic-gate 	usb_audio_type1_format_descr_t	*format = alt_descr->alt_format;
23397c478bd9Sstevel@tonic-gate 
23407c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_PM, uasp->usb_as_log_handle,
23417c478bd9Sstevel@tonic-gate 	    "usb_as_valid_format: %d %d %d %d %d",
23427c478bd9Sstevel@tonic-gate 	    format->bNrChannels, format->bSubFrameSize,
23437c478bd9Sstevel@tonic-gate 	    format->bBitResolution, format->bSamFreqType,
23447c478bd9Sstevel@tonic-gate 	    format->bFormatType);
23457c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_PM, uasp->usb_as_log_handle,
2346*64391892SAlbert Lee 	    "alt=%d", alternate);
23477c478bd9Sstevel@tonic-gate 
23487c478bd9Sstevel@tonic-gate 	switch (format->bNrChannels) {
2349*64391892SAlbert Lee 	case 0:
23507c478bd9Sstevel@tonic-gate 
23517c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
2352*64391892SAlbert Lee 	default:
2353*64391892SAlbert Lee 
2354*64391892SAlbert Lee 		break;
23557c478bd9Sstevel@tonic-gate 	}
23567c478bd9Sstevel@tonic-gate 
23577c478bd9Sstevel@tonic-gate 	switch (format->bSubFrameSize) {
23587c478bd9Sstevel@tonic-gate 	case 1:
23597c478bd9Sstevel@tonic-gate 	case 2:
23607c478bd9Sstevel@tonic-gate 		break;
23617c478bd9Sstevel@tonic-gate 	default:
23627c478bd9Sstevel@tonic-gate 
23637c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
23647c478bd9Sstevel@tonic-gate 	}
23657c478bd9Sstevel@tonic-gate 
23667c478bd9Sstevel@tonic-gate 	switch (format->bBitResolution) {
2367*64391892SAlbert Lee 	case USB_AUDIO_PRECISION_8:
2368*64391892SAlbert Lee 	case USB_AUDIO_PRECISION_16:
2369*64391892SAlbert Lee 	case USB_AUDIO_PRECISION_24:
2370*64391892SAlbert Lee 	case USB_AUDIO_PRECISION_32:
23717c478bd9Sstevel@tonic-gate 		break;
23727c478bd9Sstevel@tonic-gate 	default:
23737c478bd9Sstevel@tonic-gate 
23747c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
23757c478bd9Sstevel@tonic-gate 	}
23767c478bd9Sstevel@tonic-gate 
23777c478bd9Sstevel@tonic-gate 	switch (format->bFormatType) {
23787c478bd9Sstevel@tonic-gate 	case USB_AUDIO_FORMAT_TYPE1_PCM:
23797c478bd9Sstevel@tonic-gate 		break;
23807c478bd9Sstevel@tonic-gate 	default:
23817c478bd9Sstevel@tonic-gate 
23827c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
23837c478bd9Sstevel@tonic-gate 	}
23847c478bd9Sstevel@tonic-gate 
23857c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
23867c478bd9Sstevel@tonic-gate }
23877c478bd9Sstevel@tonic-gate 
23887c478bd9Sstevel@tonic-gate 
23897c478bd9Sstevel@tonic-gate 
23907c478bd9Sstevel@tonic-gate 
23917c478bd9Sstevel@tonic-gate /*
23927c478bd9Sstevel@tonic-gate  * Event Management
23937c478bd9Sstevel@tonic-gate  *
23947c478bd9Sstevel@tonic-gate  * usb_as_disconnect_event_cb:
23957c478bd9Sstevel@tonic-gate  *	The device has been disconnected.
23967c478bd9Sstevel@tonic-gate  */
23977c478bd9Sstevel@tonic-gate static int
usb_as_disconnect_event_cb(dev_info_t * dip)23987c478bd9Sstevel@tonic-gate usb_as_disconnect_event_cb(dev_info_t *dip)
23997c478bd9Sstevel@tonic-gate {
24007c478bd9Sstevel@tonic-gate 	usb_as_state_t *uasp = (usb_as_state_t *)ddi_get_soft_state(
24017c478bd9Sstevel@tonic-gate 	    usb_as_statep, ddi_get_instance(dip));
24027c478bd9Sstevel@tonic-gate 
24037c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_EVENTS, uasp->usb_as_log_handle,
2404112116d8Sfb 	    "usb_as_disconnect_event_cb: dip=0x%p", (void *)dip);
24057c478bd9Sstevel@tonic-gate 
24067c478bd9Sstevel@tonic-gate 	(void) usb_serialize_access(uasp->usb_as_ser_acc, USB_WAIT, 0);
24077c478bd9Sstevel@tonic-gate 
24087c478bd9Sstevel@tonic-gate 	mutex_enter(&uasp->usb_as_mutex);
24097c478bd9Sstevel@tonic-gate 	uasp->usb_as_dev_state = USB_DEV_DISCONNECTED;
24107c478bd9Sstevel@tonic-gate 	mutex_exit(&uasp->usb_as_mutex);
24117c478bd9Sstevel@tonic-gate 
24127c478bd9Sstevel@tonic-gate 	usb_release_access(uasp->usb_as_ser_acc);
24137c478bd9Sstevel@tonic-gate 
24147c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
24157c478bd9Sstevel@tonic-gate }
24167c478bd9Sstevel@tonic-gate 
24177c478bd9Sstevel@tonic-gate 
24187c478bd9Sstevel@tonic-gate /*
24197c478bd9Sstevel@tonic-gate  * usb_as_cpr_suspend:
24207c478bd9Sstevel@tonic-gate  */
24217c478bd9Sstevel@tonic-gate static int
usb_as_cpr_suspend(dev_info_t * dip)24227c478bd9Sstevel@tonic-gate usb_as_cpr_suspend(dev_info_t *dip)
24237c478bd9Sstevel@tonic-gate {
24247c478bd9Sstevel@tonic-gate 	usb_as_state_t *uasp = (usb_as_state_t *)ddi_get_soft_state(
24257c478bd9Sstevel@tonic-gate 	    usb_as_statep, ddi_get_instance(dip));
24267c478bd9Sstevel@tonic-gate 
24277c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_EVENTS, uasp->usb_as_log_handle,
24287c478bd9Sstevel@tonic-gate 	    "usb_as_cpr_suspend: Begin");
24297c478bd9Sstevel@tonic-gate 
24307c478bd9Sstevel@tonic-gate 	(void) usb_serialize_access(uasp->usb_as_ser_acc, USB_WAIT, 0);
24317c478bd9Sstevel@tonic-gate 
24327c478bd9Sstevel@tonic-gate 	mutex_enter(&uasp->usb_as_mutex);
24337c478bd9Sstevel@tonic-gate 	uasp->usb_as_dev_state = USB_DEV_SUSPENDED;
24347c478bd9Sstevel@tonic-gate 	mutex_exit(&uasp->usb_as_mutex);
24357c478bd9Sstevel@tonic-gate 
24367c478bd9Sstevel@tonic-gate 	usb_release_access(uasp->usb_as_ser_acc);
24377c478bd9Sstevel@tonic-gate 
24387c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, uasp->usb_as_log_handle,
24397c478bd9Sstevel@tonic-gate 	    "usb_as_cpr_suspend: End");
24407c478bd9Sstevel@tonic-gate 
24417c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
24427c478bd9Sstevel@tonic-gate }
24437c478bd9Sstevel@tonic-gate 
24447c478bd9Sstevel@tonic-gate 
24457c478bd9Sstevel@tonic-gate /*
24467c478bd9Sstevel@tonic-gate  * usb_as_reconnect_event_cb:
24477c478bd9Sstevel@tonic-gate  *	The device was disconnected but this instance not detached, probably
24487c478bd9Sstevel@tonic-gate  *	because the device was busy.
24497c478bd9Sstevel@tonic-gate  *	if the same device, continue with restoring state
24507c478bd9Sstevel@tonic-gate  */
24517c478bd9Sstevel@tonic-gate static int
usb_as_reconnect_event_cb(dev_info_t * dip)24527c478bd9Sstevel@tonic-gate usb_as_reconnect_event_cb(dev_info_t *dip)
24537c478bd9Sstevel@tonic-gate {
24547c478bd9Sstevel@tonic-gate 	usb_as_state_t *uasp = (usb_as_state_t *)ddi_get_soft_state(
24557c478bd9Sstevel@tonic-gate 	    usb_as_statep, ddi_get_instance(dip));
24567c478bd9Sstevel@tonic-gate 
24577c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_EVENTS, uasp->usb_as_log_handle,
2458112116d8Sfb 	    "usb_as_reconnect_event_cb: dip=0x%p", (void *)dip);
24597c478bd9Sstevel@tonic-gate 
24607c478bd9Sstevel@tonic-gate 	(void) usb_serialize_access(uasp->usb_as_ser_acc, USB_WAIT, 0);
24617c478bd9Sstevel@tonic-gate 
24627c478bd9Sstevel@tonic-gate 	mutex_enter(&uasp->usb_as_mutex);
24637c478bd9Sstevel@tonic-gate 	usb_as_restore_device_state(dip, uasp);
24647c478bd9Sstevel@tonic-gate 	mutex_exit(&uasp->usb_as_mutex);
24657c478bd9Sstevel@tonic-gate 
24667c478bd9Sstevel@tonic-gate 	usb_release_access(uasp->usb_as_ser_acc);
24677c478bd9Sstevel@tonic-gate 
24687c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
24697c478bd9Sstevel@tonic-gate }
24707c478bd9Sstevel@tonic-gate 
24717c478bd9Sstevel@tonic-gate 
24727c478bd9Sstevel@tonic-gate /*
24737c478bd9Sstevel@tonic-gate  * usb_as_cpr_resume:
24747c478bd9Sstevel@tonic-gate  *	recover this device from suspended state
24757c478bd9Sstevel@tonic-gate  */
24767c478bd9Sstevel@tonic-gate static void
usb_as_cpr_resume(dev_info_t * dip)24777c478bd9Sstevel@tonic-gate usb_as_cpr_resume(dev_info_t *dip)
24787c478bd9Sstevel@tonic-gate {
24797c478bd9Sstevel@tonic-gate 	usb_as_state_t *uasp = (usb_as_state_t *)ddi_get_soft_state(
24807c478bd9Sstevel@tonic-gate 	    usb_as_statep, ddi_get_instance(dip));
24817c478bd9Sstevel@tonic-gate 
24827c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_EVENTS, uasp->usb_as_log_handle,
2483112116d8Sfb 	    "usb_as_cpr_resume: dip=0x%p", (void *)dip);
24847c478bd9Sstevel@tonic-gate 
24857c478bd9Sstevel@tonic-gate 	(void) usb_serialize_access(uasp->usb_as_ser_acc, USB_WAIT, 0);
24867c478bd9Sstevel@tonic-gate 
24877c478bd9Sstevel@tonic-gate 	mutex_enter(&uasp->usb_as_mutex);
24887c478bd9Sstevel@tonic-gate 	usb_as_restore_device_state(dip, uasp);
24897c478bd9Sstevel@tonic-gate 	mutex_exit(&uasp->usb_as_mutex);
24907c478bd9Sstevel@tonic-gate 
24917c478bd9Sstevel@tonic-gate 	usb_release_access(uasp->usb_as_ser_acc);
24927c478bd9Sstevel@tonic-gate }
24937c478bd9Sstevel@tonic-gate 
24947c478bd9Sstevel@tonic-gate 
24957c478bd9Sstevel@tonic-gate /*
24967c478bd9Sstevel@tonic-gate  * usb_as_restore_device_state:
24977c478bd9Sstevel@tonic-gate  *	Set original configuration of the device
24987c478bd9Sstevel@tonic-gate  *	enable wrq - this starts new transactions on the control pipe
24997c478bd9Sstevel@tonic-gate  */
25007c478bd9Sstevel@tonic-gate static void
usb_as_restore_device_state(dev_info_t * dip,usb_as_state_t * uasp)25017c478bd9Sstevel@tonic-gate usb_as_restore_device_state(dev_info_t *dip, usb_as_state_t *uasp)
25027c478bd9Sstevel@tonic-gate {
25037c478bd9Sstevel@tonic-gate 	usb_as_power_t	*uaspm;
25047c478bd9Sstevel@tonic-gate 
25057c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, uasp->usb_as_log_handle,
25067c478bd9Sstevel@tonic-gate 	    "usb_as_restore_device_state:");
25077c478bd9Sstevel@tonic-gate 
25087c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uasp->usb_as_mutex));
25097c478bd9Sstevel@tonic-gate 
25107c478bd9Sstevel@tonic-gate 	uaspm = uasp->usb_as_pm;
25117c478bd9Sstevel@tonic-gate 
25127c478bd9Sstevel@tonic-gate 	/* Check if we are talking to the same device */
25137c478bd9Sstevel@tonic-gate 	mutex_exit(&uasp->usb_as_mutex);
25147c478bd9Sstevel@tonic-gate 	usb_as_pm_busy_component(uasp);
25157c478bd9Sstevel@tonic-gate 	(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
25167c478bd9Sstevel@tonic-gate 
25177c478bd9Sstevel@tonic-gate 	if (usb_check_same_device(dip, uasp->usb_as_log_handle, USB_LOG_L0,
25187c478bd9Sstevel@tonic-gate 	    PRINT_MASK_ALL, USB_CHK_BASIC|USB_CHK_CFG, NULL) != USB_SUCCESS) {
25197c478bd9Sstevel@tonic-gate 		usb_as_pm_idle_component(uasp);
25207c478bd9Sstevel@tonic-gate 
25217c478bd9Sstevel@tonic-gate 		/* change the device state from suspended to disconnected */
25227c478bd9Sstevel@tonic-gate 		mutex_enter(&uasp->usb_as_mutex);
25237c478bd9Sstevel@tonic-gate 		uasp->usb_as_dev_state = USB_DEV_DISCONNECTED;
25247c478bd9Sstevel@tonic-gate 
25257c478bd9Sstevel@tonic-gate 		return;
25267c478bd9Sstevel@tonic-gate 	}
25277c478bd9Sstevel@tonic-gate 	mutex_enter(&uasp->usb_as_mutex);
25287c478bd9Sstevel@tonic-gate 
25297c478bd9Sstevel@tonic-gate 	if (uaspm) {
25307c478bd9Sstevel@tonic-gate 		if (uaspm->aspm_wakeup_enabled) {
25317c478bd9Sstevel@tonic-gate 			mutex_exit(&uasp->usb_as_mutex);
25327c478bd9Sstevel@tonic-gate 			if (usb_handle_remote_wakeup(uasp->usb_as_dip,
25337c478bd9Sstevel@tonic-gate 			    USB_REMOTE_WAKEUP_ENABLE)) {
25347c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(PRINT_MASK_ALL,
25357c478bd9Sstevel@tonic-gate 				    uasp->usb_as_log_handle,
25367c478bd9Sstevel@tonic-gate 				    "enable remote wake up failed");
25377c478bd9Sstevel@tonic-gate 			}
25387c478bd9Sstevel@tonic-gate 			mutex_enter(&uasp->usb_as_mutex);
25397c478bd9Sstevel@tonic-gate 		}
25407c478bd9Sstevel@tonic-gate 	}
25417c478bd9Sstevel@tonic-gate 	uasp->usb_as_dev_state = USB_DEV_ONLINE;
25427c478bd9Sstevel@tonic-gate 
25437c478bd9Sstevel@tonic-gate 	mutex_exit(&uasp->usb_as_mutex);
25447c478bd9Sstevel@tonic-gate 	usb_as_pm_idle_component(uasp);
25457c478bd9Sstevel@tonic-gate 	mutex_enter(&uasp->usb_as_mutex);
25467c478bd9Sstevel@tonic-gate }
25477c478bd9Sstevel@tonic-gate 
25487c478bd9Sstevel@tonic-gate 
25497c478bd9Sstevel@tonic-gate static void
usb_as_pm_busy_component(usb_as_state_t * usb_as_statep)25507c478bd9Sstevel@tonic-gate usb_as_pm_busy_component(usb_as_state_t *usb_as_statep)
25517c478bd9Sstevel@tonic-gate {
25527c478bd9Sstevel@tonic-gate 	ASSERT(!mutex_owned(&usb_as_statep->usb_as_mutex));
25537c478bd9Sstevel@tonic-gate 
25547c478bd9Sstevel@tonic-gate 	if (usb_as_statep->usb_as_pm != NULL) {
25557c478bd9Sstevel@tonic-gate 		mutex_enter(&usb_as_statep->usb_as_mutex);
25567c478bd9Sstevel@tonic-gate 		usb_as_statep->usb_as_pm->aspm_pm_busy++;
25577c478bd9Sstevel@tonic-gate 
25587c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_PM, usb_as_statep->usb_as_log_handle,
25597c478bd9Sstevel@tonic-gate 		    "usb_as_pm_busy_component: %d",
25607c478bd9Sstevel@tonic-gate 		    usb_as_statep->usb_as_pm->aspm_pm_busy);
25617c478bd9Sstevel@tonic-gate 
25627c478bd9Sstevel@tonic-gate 		mutex_exit(&usb_as_statep->usb_as_mutex);
25637c478bd9Sstevel@tonic-gate 
25647c478bd9Sstevel@tonic-gate 		if (pm_busy_component(usb_as_statep->usb_as_dip, 0) !=
25657c478bd9Sstevel@tonic-gate 		    DDI_SUCCESS) {
25667c478bd9Sstevel@tonic-gate 			mutex_enter(&usb_as_statep->usb_as_mutex);
25677c478bd9Sstevel@tonic-gate 			usb_as_statep->usb_as_pm->aspm_pm_busy--;
25687c478bd9Sstevel@tonic-gate 
25697c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_PM,
25707c478bd9Sstevel@tonic-gate 			    usb_as_statep->usb_as_log_handle,
25717c478bd9Sstevel@tonic-gate 			    "usb_as_pm_busy_component failed: %d",
25727c478bd9Sstevel@tonic-gate 			    usb_as_statep->usb_as_pm->aspm_pm_busy);
25737c478bd9Sstevel@tonic-gate 
25747c478bd9Sstevel@tonic-gate 			mutex_exit(&usb_as_statep->usb_as_mutex);
25757c478bd9Sstevel@tonic-gate 		}
25767c478bd9Sstevel@tonic-gate 	}
25777c478bd9Sstevel@tonic-gate }
25787c478bd9Sstevel@tonic-gate 
25797c478bd9Sstevel@tonic-gate 
25807c478bd9Sstevel@tonic-gate static void
usb_as_pm_idle_component(usb_as_state_t * usb_as_statep)25817c478bd9Sstevel@tonic-gate usb_as_pm_idle_component(usb_as_state_t *usb_as_statep)
25827c478bd9Sstevel@tonic-gate {
25837c478bd9Sstevel@tonic-gate 	ASSERT(!mutex_owned(&usb_as_statep->usb_as_mutex));
25847c478bd9Sstevel@tonic-gate 
25857c478bd9Sstevel@tonic-gate 	if (usb_as_statep->usb_as_pm != NULL) {
25867c478bd9Sstevel@tonic-gate 		if (pm_idle_component(usb_as_statep->usb_as_dip, 0) ==
25877c478bd9Sstevel@tonic-gate 		    DDI_SUCCESS) {
25887c478bd9Sstevel@tonic-gate 			mutex_enter(&usb_as_statep->usb_as_mutex);
25897c478bd9Sstevel@tonic-gate 			ASSERT(usb_as_statep->usb_as_pm->aspm_pm_busy > 0);
25907c478bd9Sstevel@tonic-gate 			usb_as_statep->usb_as_pm->aspm_pm_busy--;
25917c478bd9Sstevel@tonic-gate 
25927c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L4(PRINT_MASK_PM,
25937c478bd9Sstevel@tonic-gate 			    usb_as_statep->usb_as_log_handle,
25947c478bd9Sstevel@tonic-gate 			    "usb_as_pm_idle_component: %d",
25957c478bd9Sstevel@tonic-gate 			    usb_as_statep->usb_as_pm->aspm_pm_busy);
25967c478bd9Sstevel@tonic-gate 
25977c478bd9Sstevel@tonic-gate 			mutex_exit(&usb_as_statep->usb_as_mutex);
25987c478bd9Sstevel@tonic-gate 		}
25997c478bd9Sstevel@tonic-gate 	}
26007c478bd9Sstevel@tonic-gate }
2601