xref: /onnv-gate/usr/src/uts/common/io/audio/impl/audio_ctrl.c (revision 12979:99d5baececdc)
19484Sgarrett.damore@Sun.COM /*
29484Sgarrett.damore@Sun.COM  * CDDL HEADER START
39484Sgarrett.damore@Sun.COM  *
49484Sgarrett.damore@Sun.COM  * The contents of this file are subject to the terms of the
59484Sgarrett.damore@Sun.COM  * Common Development and Distribution License (the "License").
69484Sgarrett.damore@Sun.COM  * You may not use this file except in compliance with the License.
79484Sgarrett.damore@Sun.COM  *
89484Sgarrett.damore@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99484Sgarrett.damore@Sun.COM  * or http://www.opensolaris.org/os/licensing.
109484Sgarrett.damore@Sun.COM  * See the License for the specific language governing permissions
119484Sgarrett.damore@Sun.COM  * and limitations under the License.
129484Sgarrett.damore@Sun.COM  *
139484Sgarrett.damore@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
149484Sgarrett.damore@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159484Sgarrett.damore@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
169484Sgarrett.damore@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
179484Sgarrett.damore@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
189484Sgarrett.damore@Sun.COM  *
199484Sgarrett.damore@Sun.COM  * CDDL HEADER END
209484Sgarrett.damore@Sun.COM  */
219484Sgarrett.damore@Sun.COM /*
2212165Sgdamore@opensolaris.org  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
239484Sgarrett.damore@Sun.COM  */
249484Sgarrett.damore@Sun.COM 
259484Sgarrett.damore@Sun.COM #include <sys/types.h>
269484Sgarrett.damore@Sun.COM #include <sys/list.h>
279484Sgarrett.damore@Sun.COM #include <sys/sysmacros.h>
289484Sgarrett.damore@Sun.COM #include <sys/ddi.h>
299484Sgarrett.damore@Sun.COM #include <sys/sunddi.h>
3010632Sgdamore@opensolaris.org #include <sys/atomic.h>
319484Sgarrett.damore@Sun.COM 
329484Sgarrett.damore@Sun.COM #include "audio_impl.h"
339484Sgarrett.damore@Sun.COM 
349484Sgarrett.damore@Sun.COM /*
359484Sgarrett.damore@Sun.COM  * Audio Control functions.
369484Sgarrett.damore@Sun.COM  */
379484Sgarrett.damore@Sun.COM 
389484Sgarrett.damore@Sun.COM /*
399484Sgarrett.damore@Sun.COM  * Given a control structure - free all names
409484Sgarrett.damore@Sun.COM  * strings allocated to it.
419484Sgarrett.damore@Sun.COM  *
429484Sgarrett.damore@Sun.COM  * ctrl             - The control who's names that will be free'd.
439484Sgarrett.damore@Sun.COM  */
449484Sgarrett.damore@Sun.COM static void
audio_control_freenames(audio_ctrl_t * ctrl)459484Sgarrett.damore@Sun.COM audio_control_freenames(audio_ctrl_t *ctrl)
469484Sgarrett.damore@Sun.COM {
479484Sgarrett.damore@Sun.COM 	int	indx;
489484Sgarrett.damore@Sun.COM 
499484Sgarrett.damore@Sun.COM 	if (ctrl->ctrl_name != NULL)
509484Sgarrett.damore@Sun.COM 		strfree((char *)ctrl->ctrl_name);
519484Sgarrett.damore@Sun.COM 	ctrl->ctrl_name = NULL;
529484Sgarrett.damore@Sun.COM 
539484Sgarrett.damore@Sun.COM 	for (indx = 0; indx < 64; indx++) {
549484Sgarrett.damore@Sun.COM 		if (ctrl->ctrl_enum[indx] != NULL) {
559484Sgarrett.damore@Sun.COM 			strfree((char *)ctrl->ctrl_enum[indx]);
569484Sgarrett.damore@Sun.COM 			ctrl->ctrl_enum[indx] = NULL;
579484Sgarrett.damore@Sun.COM 		}
589484Sgarrett.damore@Sun.COM 	}
599484Sgarrett.damore@Sun.COM }
609484Sgarrett.damore@Sun.COM 
619484Sgarrett.damore@Sun.COM /*
629484Sgarrett.damore@Sun.COM  * This will allocate and register a control for my audio device.
639484Sgarrett.damore@Sun.COM  *
649484Sgarrett.damore@Sun.COM  * d                - The audio device the control will be attached to.
659484Sgarrett.damore@Sun.COM  * desc             - Attributes about this new control
669484Sgarrett.damore@Sun.COM  * read_fn          - Callback function in driver to read control
679484Sgarrett.damore@Sun.COM  * write_fn         - Callback function in driver to write control.
689484Sgarrett.damore@Sun.COM  * arg              - driver private context passed to read_fn/write_fn
699484Sgarrett.damore@Sun.COM  *
709484Sgarrett.damore@Sun.COM  * On success this will return a control structure else NULL.
719484Sgarrett.damore@Sun.COM  *
729484Sgarrett.damore@Sun.COM  * The value passed in for a control number in the audio_ctrl_desc_t
739484Sgarrett.damore@Sun.COM  * has some special meaning. If it is less then AUDIO_CONTROL_EXTBASE
749484Sgarrett.damore@Sun.COM  * then the control is assumed to be a known control. If it is
759484Sgarrett.damore@Sun.COM  * AUDIO_CONTROL_EXTBASE then the framework will allocate a unique
769484Sgarrett.damore@Sun.COM  * control number and replace it in the audio_ctrl_desc_t structure
779484Sgarrett.damore@Sun.COM  * and this control is considered an extended driver private control.
789484Sgarrett.damore@Sun.COM  * The number that is replaced in audio_ctrl_desc_t will be greater
799484Sgarrett.damore@Sun.COM  * then AUDIO_CONTROL_EXTBASE.
809484Sgarrett.damore@Sun.COM  *
819484Sgarrett.damore@Sun.COM  */
829484Sgarrett.damore@Sun.COM audio_ctrl_t *
audio_dev_add_control(audio_dev_t * d,audio_ctrl_desc_t * desc,audio_ctrl_rd_t read_fn,audio_ctrl_wr_t write_fn,void * arg)839484Sgarrett.damore@Sun.COM audio_dev_add_control(audio_dev_t *d, audio_ctrl_desc_t *desc,
849484Sgarrett.damore@Sun.COM     audio_ctrl_rd_t read_fn, audio_ctrl_wr_t write_fn, void *arg)
859484Sgarrett.damore@Sun.COM {
869484Sgarrett.damore@Sun.COM 	audio_ctrl_t *ctrl;
879484Sgarrett.damore@Sun.COM 	audio_ctrl_desc_t *new_desc;
889484Sgarrett.damore@Sun.COM 	char	scratch[16];
899484Sgarrett.damore@Sun.COM 	const char	*name;
909484Sgarrett.damore@Sun.COM 
919484Sgarrett.damore@Sun.COM 	/* Verify arguments */
929484Sgarrett.damore@Sun.COM 	ASSERT(d);
939484Sgarrett.damore@Sun.COM 	ASSERT(desc);
949484Sgarrett.damore@Sun.COM 
959484Sgarrett.damore@Sun.COM 	/* We cannot deal with unnamed controls */
969484Sgarrett.damore@Sun.COM 	if ((name = desc->acd_name) == NULL) {
979484Sgarrett.damore@Sun.COM 		return (NULL);
989484Sgarrett.damore@Sun.COM 	}
999484Sgarrett.damore@Sun.COM 
1009484Sgarrett.damore@Sun.COM 	/*
1019484Sgarrett.damore@Sun.COM 	 * If this was called with a control name that was already
1029484Sgarrett.damore@Sun.COM 	 * added, then we do some special things. First we reuse the
1039484Sgarrett.damore@Sun.COM 	 * control audio_ctrl_t and as far as outside users are
1049484Sgarrett.damore@Sun.COM 	 * concerned the handle is reused. To users this looks like we
1059484Sgarrett.damore@Sun.COM 	 * are changing the controls attributes. But what we really do
1069484Sgarrett.damore@Sun.COM 	 * is free every thing allocated to the control and then
1079484Sgarrett.damore@Sun.COM 	 * reinit everything.  That way the same code can get used for
1089484Sgarrett.damore@Sun.COM 	 * both.
1099484Sgarrett.damore@Sun.COM 	 *
1109484Sgarrett.damore@Sun.COM 	 * We verify anything that could fail before we change the
1119484Sgarrett.damore@Sun.COM 	 * control or commit to any changes. If there is something bad
1129484Sgarrett.damore@Sun.COM 	 * return null to indicate an error but the original control
1139484Sgarrett.damore@Sun.COM 	 * is still usable and untouched.
1149484Sgarrett.damore@Sun.COM 	 */
1159484Sgarrett.damore@Sun.COM 	ctrl = auclnt_find_control(d, name);
1169484Sgarrett.damore@Sun.COM 
1179484Sgarrett.damore@Sun.COM 	if (ctrl == NULL) {
1189484Sgarrett.damore@Sun.COM 		/* Allocate a new control */
1199484Sgarrett.damore@Sun.COM 		ctrl = kmem_zalloc(sizeof (*ctrl), KM_SLEEP);
1209484Sgarrett.damore@Sun.COM 	} else {
1219484Sgarrett.damore@Sun.COM 		/* Re-configure an existing control */
1229484Sgarrett.damore@Sun.COM 		switch (desc->acd_type) {
1239484Sgarrett.damore@Sun.COM 		case AUDIO_CTRL_TYPE_BOOLEAN:
1249484Sgarrett.damore@Sun.COM 		case AUDIO_CTRL_TYPE_STEREO:
1259484Sgarrett.damore@Sun.COM 		case AUDIO_CTRL_TYPE_MONO:
1269484Sgarrett.damore@Sun.COM 		case AUDIO_CTRL_TYPE_METER:
1279484Sgarrett.damore@Sun.COM 		case AUDIO_CTRL_TYPE_ENUM:
1289484Sgarrett.damore@Sun.COM 			break;
1299484Sgarrett.damore@Sun.COM 		default:
1309484Sgarrett.damore@Sun.COM 			audio_dev_warn(d, "bad control type %d for %s "
1319484Sgarrett.damore@Sun.COM 			    "not replaced", desc->acd_type, desc->acd_name);
1329484Sgarrett.damore@Sun.COM 			return (NULL);
1339484Sgarrett.damore@Sun.COM 		}
1349484Sgarrett.damore@Sun.COM 
1359484Sgarrett.damore@Sun.COM 		/*
1369484Sgarrett.damore@Sun.COM 		 * By removing it from the list we prevent the need to lock
1379484Sgarrett.damore@Sun.COM 		 * and check for locks on the control itself.
1389484Sgarrett.damore@Sun.COM 		 * Also by doing this we can use the normal add code to do
1399484Sgarrett.damore@Sun.COM 		 * what it normally does below.
1409484Sgarrett.damore@Sun.COM 		 */
14111936Sgdamore@opensolaris.org 		mutex_enter(&d->d_ctrl_lock);
1429484Sgarrett.damore@Sun.COM 		list_remove(&d->d_controls, ctrl);
14311936Sgdamore@opensolaris.org 		mutex_exit(&d->d_ctrl_lock);
1449484Sgarrett.damore@Sun.COM 
1459484Sgarrett.damore@Sun.COM 		audio_control_freenames(ctrl);
1469484Sgarrett.damore@Sun.COM 		ctrl->ctrl_read_fn = NULL;
1479484Sgarrett.damore@Sun.COM 		ctrl->ctrl_write_fn = NULL;
1489484Sgarrett.damore@Sun.COM 		ctrl->ctrl_arg = NULL;
1499484Sgarrett.damore@Sun.COM 		ctrl->ctrl_dev = NULL;
1509484Sgarrett.damore@Sun.COM 	}
1519484Sgarrett.damore@Sun.COM 	new_desc = &ctrl->ctrl_des;
1529484Sgarrett.damore@Sun.COM 
1539484Sgarrett.damore@Sun.COM 	/* Fill in new control description */
1549484Sgarrett.damore@Sun.COM 	new_desc->acd_type = desc->acd_type;
1559484Sgarrett.damore@Sun.COM 	new_desc->acd_flags = desc->acd_flags;
1569484Sgarrett.damore@Sun.COM 	new_desc->acd_maxvalue = desc->acd_maxvalue;
1579484Sgarrett.damore@Sun.COM 	new_desc->acd_minvalue = desc->acd_minvalue;
1589484Sgarrett.damore@Sun.COM 	new_desc->acd_name = strdup(name);
1599484Sgarrett.damore@Sun.COM 
1609484Sgarrett.damore@Sun.COM 	/* Process type of control special actions, if any */
1619484Sgarrett.damore@Sun.COM 	switch (desc->acd_type) {
1629484Sgarrett.damore@Sun.COM 	case AUDIO_CTRL_TYPE_BOOLEAN:
1639484Sgarrett.damore@Sun.COM 	case AUDIO_CTRL_TYPE_STEREO:
1649484Sgarrett.damore@Sun.COM 	case AUDIO_CTRL_TYPE_MONO:
1659484Sgarrett.damore@Sun.COM 	case AUDIO_CTRL_TYPE_METER:
1669484Sgarrett.damore@Sun.COM 		break;
1679484Sgarrett.damore@Sun.COM 
1689484Sgarrett.damore@Sun.COM 	case AUDIO_CTRL_TYPE_ENUM:
1699484Sgarrett.damore@Sun.COM 		for (int bit = 0; bit < 64; bit++) {
1709484Sgarrett.damore@Sun.COM 			if (((1U << bit) & desc->acd_maxvalue) == 0)
1719484Sgarrett.damore@Sun.COM 				continue;
1729484Sgarrett.damore@Sun.COM 			name = desc->acd_enum[bit];
1739484Sgarrett.damore@Sun.COM 			if (name == NULL) {
1749484Sgarrett.damore@Sun.COM 				(void) snprintf(scratch, sizeof (scratch),
1759484Sgarrett.damore@Sun.COM 				    "bit%d", bit);
1769484Sgarrett.damore@Sun.COM 				name = scratch;
1779484Sgarrett.damore@Sun.COM 			}
1789484Sgarrett.damore@Sun.COM 			new_desc->acd_enum[bit] = strdup(name);
1799484Sgarrett.damore@Sun.COM 		}
1809484Sgarrett.damore@Sun.COM 		break;
1819484Sgarrett.damore@Sun.COM 	default:
1829484Sgarrett.damore@Sun.COM 		audio_dev_warn(d, "bad control type %d for %s",
1839484Sgarrett.damore@Sun.COM 		    desc->acd_type, desc->acd_name);
1849484Sgarrett.damore@Sun.COM 		goto ctrl_fail;
1859484Sgarrett.damore@Sun.COM 	}
1869484Sgarrett.damore@Sun.COM 
1879484Sgarrett.damore@Sun.COM 	ctrl->ctrl_dev = d;
1889484Sgarrett.damore@Sun.COM 	if (new_desc->acd_flags & AUDIO_CTRL_FLAG_READABLE) {
1899484Sgarrett.damore@Sun.COM 		ASSERT(read_fn);
1909484Sgarrett.damore@Sun.COM 		ctrl->ctrl_read_fn = read_fn;
1919484Sgarrett.damore@Sun.COM 		ctrl->ctrl_arg = arg;
1929484Sgarrett.damore@Sun.COM 	}
1939484Sgarrett.damore@Sun.COM 	if (new_desc->acd_flags & AUDIO_CTRL_FLAG_WRITEABLE) {
1949484Sgarrett.damore@Sun.COM 		ASSERT(write_fn);
1959484Sgarrett.damore@Sun.COM 		ctrl->ctrl_write_fn = write_fn;
1969484Sgarrett.damore@Sun.COM 		ctrl->ctrl_arg = arg;
1979484Sgarrett.damore@Sun.COM 	}
1989484Sgarrett.damore@Sun.COM 
19911936Sgdamore@opensolaris.org 	mutex_enter(&d->d_ctrl_lock);
2009484Sgarrett.damore@Sun.COM 	list_insert_tail(&d->d_controls, ctrl);
20111936Sgdamore@opensolaris.org 	mutex_exit(&d->d_ctrl_lock);
2029484Sgarrett.damore@Sun.COM 
2039484Sgarrett.damore@Sun.COM 	return (ctrl);
2049484Sgarrett.damore@Sun.COM 
2059484Sgarrett.damore@Sun.COM 
2069484Sgarrett.damore@Sun.COM ctrl_fail:
2079484Sgarrett.damore@Sun.COM 	if (ctrl) {
2089484Sgarrett.damore@Sun.COM 		audio_control_freenames(ctrl);
2099484Sgarrett.damore@Sun.COM 		kmem_free(ctrl, sizeof (*ctrl));
2109484Sgarrett.damore@Sun.COM 	}
2119484Sgarrett.damore@Sun.COM 	return (NULL);
2129484Sgarrett.damore@Sun.COM }
2139484Sgarrett.damore@Sun.COM 
2149484Sgarrett.damore@Sun.COM /*
2159484Sgarrett.damore@Sun.COM  * This will remove a control from my audio device.
2169484Sgarrett.damore@Sun.COM  *
2179484Sgarrett.damore@Sun.COM  * ctrl             - The control will be removed.
2189484Sgarrett.damore@Sun.COM  */
2199484Sgarrett.damore@Sun.COM void
audio_dev_del_control(audio_ctrl_t * ctrl)2209484Sgarrett.damore@Sun.COM audio_dev_del_control(audio_ctrl_t *ctrl)
2219484Sgarrett.damore@Sun.COM {
2229484Sgarrett.damore@Sun.COM 	audio_dev_t *d;
2239484Sgarrett.damore@Sun.COM 
2249484Sgarrett.damore@Sun.COM 	/* Verify argument */
2259484Sgarrett.damore@Sun.COM 	ASSERT(ctrl);
2269484Sgarrett.damore@Sun.COM 	d = ctrl->ctrl_dev;
2279484Sgarrett.damore@Sun.COM 	ASSERT(d);
2289484Sgarrett.damore@Sun.COM 
22911936Sgdamore@opensolaris.org 	mutex_enter(&d->d_ctrl_lock);
2309484Sgarrett.damore@Sun.COM 	list_remove(&d->d_controls, ctrl);
23111936Sgdamore@opensolaris.org 	mutex_exit(&d->d_ctrl_lock);
2329484Sgarrett.damore@Sun.COM 
2339484Sgarrett.damore@Sun.COM 	audio_control_freenames(ctrl);
2349484Sgarrett.damore@Sun.COM 	kmem_free(ctrl, sizeof (*ctrl));
2359484Sgarrett.damore@Sun.COM }
2369484Sgarrett.damore@Sun.COM 
23712165Sgdamore@opensolaris.org void
audio_dev_add_soft_volume(audio_dev_t * d)2389484Sgarrett.damore@Sun.COM audio_dev_add_soft_volume(audio_dev_t *d)
2399484Sgarrett.damore@Sun.COM {
2409484Sgarrett.damore@Sun.COM 	audio_ctrl_desc_t	desc;
2419484Sgarrett.damore@Sun.COM 
2429484Sgarrett.damore@Sun.COM 	bzero(&desc, sizeof (desc));
2439484Sgarrett.damore@Sun.COM 	if (d->d_pcmvol_ctrl == NULL) {
2449484Sgarrett.damore@Sun.COM 		desc.acd_name = AUDIO_CTRL_ID_VOLUME;
2459484Sgarrett.damore@Sun.COM 		desc.acd_type = AUDIO_CTRL_TYPE_MONO;
2469484Sgarrett.damore@Sun.COM 		desc.acd_minvalue = 0;
2479484Sgarrett.damore@Sun.COM 		desc.acd_maxvalue = 100;
2489484Sgarrett.damore@Sun.COM 		desc.acd_flags = AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_PLAY |
2499484Sgarrett.damore@Sun.COM 		    AUDIO_CTRL_FLAG_PCMVOL;
2509484Sgarrett.damore@Sun.COM 		d->d_pcmvol_ctrl = audio_dev_add_control(d, &desc,
2519484Sgarrett.damore@Sun.COM 		    auimpl_get_pcmvol, auimpl_set_pcmvol, d);
2529484Sgarrett.damore@Sun.COM 		d->d_pcmvol = 75;
2539484Sgarrett.damore@Sun.COM 	}
2549484Sgarrett.damore@Sun.COM }
2559484Sgarrett.damore@Sun.COM 
2569484Sgarrett.damore@Sun.COM /*
2579484Sgarrett.damore@Sun.COM  * This will notify clients of need to reread control
2589484Sgarrett.damore@Sun.COM  * values since they have changed.
2599484Sgarrett.damore@Sun.COM  *
2609484Sgarrett.damore@Sun.COM  * There will be a routine that allows a client to register
26110632Sgdamore@opensolaris.org  * a callback.   For now we just update the serial number.
2629484Sgarrett.damore@Sun.COM  *
2639484Sgarrett.damore@Sun.COM  * d                - The device that needs updates.
2649484Sgarrett.damore@Sun.COM  */
2659484Sgarrett.damore@Sun.COM void
audio_dev_update_controls(audio_dev_t * d)2669484Sgarrett.damore@Sun.COM audio_dev_update_controls(audio_dev_t *d)
2679484Sgarrett.damore@Sun.COM {
26810632Sgdamore@opensolaris.org 	atomic_inc_uint(&d->d_serial);
2699484Sgarrett.damore@Sun.COM }
2709484Sgarrett.damore@Sun.COM 
2719484Sgarrett.damore@Sun.COM 
2729484Sgarrett.damore@Sun.COM /*
2739484Sgarrett.damore@Sun.COM  * This is used to read the current value of a control.
2749484Sgarrett.damore@Sun.COM  * Note, this will cause a callback into the driver to get the value.
2759484Sgarrett.damore@Sun.COM  *
2769484Sgarrett.damore@Sun.COM  * ctrl        - should be the valid control being read.
2779484Sgarrett.damore@Sun.COM  * value       - is a pointer to the place that will contain the value read.
2789484Sgarrett.damore@Sun.COM  *
2799484Sgarrett.damore@Sun.COM  * On return zero is returned on success else errno is returned.
2809484Sgarrett.damore@Sun.COM  *
2819484Sgarrett.damore@Sun.COM  */
2829484Sgarrett.damore@Sun.COM int
audio_control_read(audio_ctrl_t * ctrl,uint64_t * value)2839484Sgarrett.damore@Sun.COM audio_control_read(audio_ctrl_t *ctrl, uint64_t *value)
2849484Sgarrett.damore@Sun.COM {
28511936Sgdamore@opensolaris.org 	audio_dev_t	*d = ctrl->ctrl_dev;
28611936Sgdamore@opensolaris.org 	uint64_t	my_value;
28711936Sgdamore@opensolaris.org 	int		ret;
2889484Sgarrett.damore@Sun.COM 
2899484Sgarrett.damore@Sun.COM 	ASSERT(value);
29011936Sgdamore@opensolaris.org 
29111936Sgdamore@opensolaris.org 	mutex_enter(&d->d_ctrl_lock);
29211936Sgdamore@opensolaris.org 	while (d->d_suspended) {
29311936Sgdamore@opensolaris.org 		cv_wait(&d->d_ctrl_cv, &d->d_ctrl_lock);
29411936Sgdamore@opensolaris.org 	}
2959484Sgarrett.damore@Sun.COM 
2969484Sgarrett.damore@Sun.COM 	if (!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_READABLE)) {
29711936Sgdamore@opensolaris.org 		mutex_exit(&d->d_ctrl_lock);
2989484Sgarrett.damore@Sun.COM 		return (ENXIO);
2999484Sgarrett.damore@Sun.COM 	}
3009484Sgarrett.damore@Sun.COM 
3019484Sgarrett.damore@Sun.COM 	ASSERT(ctrl->ctrl_read_fn);
3029484Sgarrett.damore@Sun.COM 
30311936Sgdamore@opensolaris.org 	ret = ctrl->ctrl_read_fn(ctrl->ctrl_arg, &my_value);
30411936Sgdamore@opensolaris.org 	mutex_exit(&d->d_ctrl_lock);
30511936Sgdamore@opensolaris.org 
30611936Sgdamore@opensolaris.org 	if (ret == 0) {
30711936Sgdamore@opensolaris.org 		*value = my_value;
3089484Sgarrett.damore@Sun.COM 	}
3099484Sgarrett.damore@Sun.COM 
3109484Sgarrett.damore@Sun.COM 	return (ret);
3119484Sgarrett.damore@Sun.COM }
3129484Sgarrett.damore@Sun.COM 
3139484Sgarrett.damore@Sun.COM /*
3149484Sgarrett.damore@Sun.COM  * This is used to write a value to a control.
3159484Sgarrett.damore@Sun.COM  * Note, this will cause a callback into the driver to write the value.
3169484Sgarrett.damore@Sun.COM  *
3179484Sgarrett.damore@Sun.COM  * ctrl        - should be the valid control being written.
3189484Sgarrett.damore@Sun.COM  * value       - is value to set the control to.
3199484Sgarrett.damore@Sun.COM  *
3209484Sgarrett.damore@Sun.COM  * On return zero is returned on success else errno is returned.
3219484Sgarrett.damore@Sun.COM  *
3229484Sgarrett.damore@Sun.COM  */
3239484Sgarrett.damore@Sun.COM int
audio_control_write(audio_ctrl_t * ctrl,uint64_t value)3249484Sgarrett.damore@Sun.COM audio_control_write(audio_ctrl_t *ctrl, uint64_t value)
3259484Sgarrett.damore@Sun.COM {
3269484Sgarrett.damore@Sun.COM 	int		ret;
3279484Sgarrett.damore@Sun.COM 	audio_dev_t	*d = ctrl->ctrl_dev;
3289484Sgarrett.damore@Sun.COM 
32911936Sgdamore@opensolaris.org 	mutex_enter(&d->d_ctrl_lock);
33011936Sgdamore@opensolaris.org 	while (d->d_suspended) {
33111936Sgdamore@opensolaris.org 		cv_wait(&d->d_ctrl_cv, &d->d_ctrl_lock);
33211936Sgdamore@opensolaris.org 	}
3339484Sgarrett.damore@Sun.COM 
3349484Sgarrett.damore@Sun.COM 	if (!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_WRITEABLE)) {
33511936Sgdamore@opensolaris.org 		mutex_exit(&d->d_ctrl_lock);
3369484Sgarrett.damore@Sun.COM 		return (ENXIO);
3379484Sgarrett.damore@Sun.COM 	}
3389484Sgarrett.damore@Sun.COM 
3399484Sgarrett.damore@Sun.COM 	ASSERT(ctrl->ctrl_write_fn);
3409484Sgarrett.damore@Sun.COM 
3419484Sgarrett.damore@Sun.COM 	ret = ctrl->ctrl_write_fn(ctrl->ctrl_arg, value);
34211936Sgdamore@opensolaris.org 	if (ret == 0) {
34311936Sgdamore@opensolaris.org 		ctrl->ctrl_saved = value;
34411936Sgdamore@opensolaris.org 		ctrl->ctrl_saved_ok = B_TRUE;
34511936Sgdamore@opensolaris.org 	}
34611936Sgdamore@opensolaris.org 	mutex_exit(&d->d_ctrl_lock);
3479484Sgarrett.damore@Sun.COM 
34811936Sgdamore@opensolaris.org 	if (ret == 0) {
3499484Sgarrett.damore@Sun.COM 		audio_dev_update_controls(d);
35011936Sgdamore@opensolaris.org 	}
3519484Sgarrett.damore@Sun.COM 
3529484Sgarrett.damore@Sun.COM 	return (ret);
3539484Sgarrett.damore@Sun.COM }
35411936Sgdamore@opensolaris.org 
35511936Sgdamore@opensolaris.org /*
35611936Sgdamore@opensolaris.org  * This is used to save control values.
35711936Sgdamore@opensolaris.org  */
35811936Sgdamore@opensolaris.org int
auimpl_save_controls(audio_dev_t * d)35911936Sgdamore@opensolaris.org auimpl_save_controls(audio_dev_t *d)
36011936Sgdamore@opensolaris.org {
36111936Sgdamore@opensolaris.org 	audio_ctrl_t	*ctrl;
36211936Sgdamore@opensolaris.org 	list_t		*l;
36311936Sgdamore@opensolaris.org 	int		ret;
36411936Sgdamore@opensolaris.org 
36511936Sgdamore@opensolaris.org 	ASSERT(mutex_owned(&d->d_ctrl_lock));
36611936Sgdamore@opensolaris.org 	l = &d->d_controls;
36711936Sgdamore@opensolaris.org 
36811936Sgdamore@opensolaris.org 	for (ctrl = list_head(l); ctrl; ctrl = list_next(l, ctrl)) {
36911936Sgdamore@opensolaris.org 		if ((!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_WRITEABLE)) ||
370*12979SEdgar.Liu@Sun.COM 		    (!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_READABLE))) {
37111936Sgdamore@opensolaris.org 			continue;
37211936Sgdamore@opensolaris.org 		}
37311936Sgdamore@opensolaris.org 		ret = ctrl->ctrl_read_fn(ctrl->ctrl_arg, &ctrl->ctrl_saved);
37411936Sgdamore@opensolaris.org 		if (ret != 0) {
37511936Sgdamore@opensolaris.org 			audio_dev_warn(d,
37611936Sgdamore@opensolaris.org 			    "Unable to save value of control %s",
37711936Sgdamore@opensolaris.org 			    ctrl->ctrl_name);
37811936Sgdamore@opensolaris.org 			return (ret);
37911936Sgdamore@opensolaris.org 		} else {
38011936Sgdamore@opensolaris.org 			ctrl->ctrl_saved_ok = B_TRUE;
38111936Sgdamore@opensolaris.org 		}
38211936Sgdamore@opensolaris.org 	}
38311936Sgdamore@opensolaris.org 	return (0);
38411936Sgdamore@opensolaris.org }
38511936Sgdamore@opensolaris.org 
38611936Sgdamore@opensolaris.org int
auimpl_restore_controls(audio_dev_t * d)38711936Sgdamore@opensolaris.org auimpl_restore_controls(audio_dev_t *d)
38811936Sgdamore@opensolaris.org {
38911936Sgdamore@opensolaris.org 	audio_ctrl_t	*ctrl;
39011936Sgdamore@opensolaris.org 	list_t		*l;
39111936Sgdamore@opensolaris.org 	int		ret;
39211936Sgdamore@opensolaris.org 	int		rv = 0;
39311936Sgdamore@opensolaris.org 
39411936Sgdamore@opensolaris.org 	ASSERT(mutex_owned(&d->d_ctrl_lock));
39511936Sgdamore@opensolaris.org 	l = &d->d_controls;
39611936Sgdamore@opensolaris.org 
39711936Sgdamore@opensolaris.org 	for (ctrl = list_head(l); ctrl; ctrl = list_next(l, ctrl)) {
39811936Sgdamore@opensolaris.org 		if (!ctrl->ctrl_saved_ok) {
39911936Sgdamore@opensolaris.org 			continue;
40011936Sgdamore@opensolaris.org 		}
40111936Sgdamore@opensolaris.org 		ret = ctrl->ctrl_write_fn(ctrl->ctrl_arg, ctrl->ctrl_saved);
40211936Sgdamore@opensolaris.org 		if (ret != 0) {
40311936Sgdamore@opensolaris.org 			audio_dev_warn(d,
40411936Sgdamore@opensolaris.org 			    "Unable to restore value of control %s",
40511936Sgdamore@opensolaris.org 			    ctrl->ctrl_name);
40611936Sgdamore@opensolaris.org 			rv = ret;
40711936Sgdamore@opensolaris.org 		}
40811936Sgdamore@opensolaris.org 	}
40911936Sgdamore@opensolaris.org 	return (rv);
41011936Sgdamore@opensolaris.org }
411