188447a05SGarrett D'Amore /*
288447a05SGarrett D'Amore  * CDDL HEADER START
388447a05SGarrett D'Amore  *
488447a05SGarrett D'Amore  * The contents of this file are subject to the terms of the
588447a05SGarrett D'Amore  * Common Development and Distribution License (the "License").
688447a05SGarrett D'Amore  * You may not use this file except in compliance with the License.
788447a05SGarrett D'Amore  *
888447a05SGarrett D'Amore  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
988447a05SGarrett D'Amore  * or http://www.opensolaris.org/os/licensing.
1088447a05SGarrett D'Amore  * See the License for the specific language governing permissions
1188447a05SGarrett D'Amore  * and limitations under the License.
1288447a05SGarrett D'Amore  *
1388447a05SGarrett D'Amore  * When distributing Covered Code, include this CDDL HEADER in each
1488447a05SGarrett D'Amore  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1588447a05SGarrett D'Amore  * If applicable, add the following below this CDDL HEADER, with the
1688447a05SGarrett D'Amore  * fields enclosed by brackets "[]" replaced with your own identifying
1788447a05SGarrett D'Amore  * information: Portions Copyright [yyyy] [name of copyright owner]
1888447a05SGarrett D'Amore  *
1988447a05SGarrett D'Amore  * CDDL HEADER END
2088447a05SGarrett D'Amore  */
2188447a05SGarrett D'Amore /*
222c30fa45SGarrett D'Amore  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2388447a05SGarrett D'Amore  */
2488447a05SGarrett D'Amore 
2588447a05SGarrett D'Amore #include <sys/types.h>
2688447a05SGarrett D'Amore #include <sys/list.h>
2788447a05SGarrett D'Amore #include <sys/sysmacros.h>
2888447a05SGarrett D'Amore #include <sys/ddi.h>
2988447a05SGarrett D'Amore #include <sys/sunddi.h>
30682cb104SGarrett D'Amore #include <sys/atomic.h>
3188447a05SGarrett D'Amore 
3288447a05SGarrett D'Amore #include "audio_impl.h"
3388447a05SGarrett D'Amore 
3488447a05SGarrett D'Amore /*
3588447a05SGarrett D'Amore  * Audio Control functions.
3688447a05SGarrett D'Amore  */
3788447a05SGarrett D'Amore 
3888447a05SGarrett D'Amore /*
3988447a05SGarrett D'Amore  * Given a control structure - free all names
4088447a05SGarrett D'Amore  * strings allocated to it.
4188447a05SGarrett D'Amore  *
4288447a05SGarrett D'Amore  * ctrl             - The control who's names that will be free'd.
4388447a05SGarrett D'Amore  */
4488447a05SGarrett D'Amore static void
audio_control_freenames(audio_ctrl_t * ctrl)4588447a05SGarrett D'Amore audio_control_freenames(audio_ctrl_t *ctrl)
4688447a05SGarrett D'Amore {
4788447a05SGarrett D'Amore 	int	indx;
4888447a05SGarrett D'Amore 
4988447a05SGarrett D'Amore 	if (ctrl->ctrl_name != NULL)
5088447a05SGarrett D'Amore 		strfree((char *)ctrl->ctrl_name);
5188447a05SGarrett D'Amore 	ctrl->ctrl_name = NULL;
5288447a05SGarrett D'Amore 
5388447a05SGarrett D'Amore 	for (indx = 0; indx < 64; indx++) {
5488447a05SGarrett D'Amore 		if (ctrl->ctrl_enum[indx] != NULL) {
5588447a05SGarrett D'Amore 			strfree((char *)ctrl->ctrl_enum[indx]);
5688447a05SGarrett D'Amore 			ctrl->ctrl_enum[indx] = NULL;
5788447a05SGarrett D'Amore 		}
5888447a05SGarrett D'Amore 	}
5988447a05SGarrett D'Amore }
6088447a05SGarrett D'Amore 
6188447a05SGarrett D'Amore /*
6288447a05SGarrett D'Amore  * This will allocate and register a control for my audio device.
6388447a05SGarrett D'Amore  *
6488447a05SGarrett D'Amore  * d                - The audio device the control will be attached to.
6588447a05SGarrett D'Amore  * desc             - Attributes about this new control
6688447a05SGarrett D'Amore  * read_fn          - Callback function in driver to read control
6788447a05SGarrett D'Amore  * write_fn         - Callback function in driver to write control.
6888447a05SGarrett D'Amore  * arg              - driver private context passed to read_fn/write_fn
6988447a05SGarrett D'Amore  *
7088447a05SGarrett D'Amore  * On success this will return a control structure else NULL.
7188447a05SGarrett D'Amore  *
7288447a05SGarrett D'Amore  * The value passed in for a control number in the audio_ctrl_desc_t
7388447a05SGarrett D'Amore  * has some special meaning. If it is less then AUDIO_CONTROL_EXTBASE
7488447a05SGarrett D'Amore  * then the control is assumed to be a known control. If it is
7588447a05SGarrett D'Amore  * AUDIO_CONTROL_EXTBASE then the framework will allocate a unique
7688447a05SGarrett D'Amore  * control number and replace it in the audio_ctrl_desc_t structure
7788447a05SGarrett D'Amore  * and this control is considered an extended driver private control.
7888447a05SGarrett D'Amore  * The number that is replaced in audio_ctrl_desc_t will be greater
7988447a05SGarrett D'Amore  * then AUDIO_CONTROL_EXTBASE.
8088447a05SGarrett D'Amore  *
8188447a05SGarrett D'Amore  */
8288447a05SGarrett D'Amore 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)8388447a05SGarrett D'Amore audio_dev_add_control(audio_dev_t *d, audio_ctrl_desc_t *desc,
8488447a05SGarrett D'Amore     audio_ctrl_rd_t read_fn, audio_ctrl_wr_t write_fn, void *arg)
8588447a05SGarrett D'Amore {
8688447a05SGarrett D'Amore 	audio_ctrl_t *ctrl;
8788447a05SGarrett D'Amore 	audio_ctrl_desc_t *new_desc;
8888447a05SGarrett D'Amore 	char	scratch[16];
8988447a05SGarrett D'Amore 	const char	*name;
9088447a05SGarrett D'Amore 
9188447a05SGarrett D'Amore 	/* Verify arguments */
9288447a05SGarrett D'Amore 	ASSERT(d);
9388447a05SGarrett D'Amore 	ASSERT(desc);
9488447a05SGarrett D'Amore 
9588447a05SGarrett D'Amore 	/* We cannot deal with unnamed controls */
9688447a05SGarrett D'Amore 	if ((name = desc->acd_name) == NULL) {
9788447a05SGarrett D'Amore 		return (NULL);
9888447a05SGarrett D'Amore 	}
9988447a05SGarrett D'Amore 
10088447a05SGarrett D'Amore 	/*
10188447a05SGarrett D'Amore 	 * If this was called with a control name that was already
10288447a05SGarrett D'Amore 	 * added, then we do some special things. First we reuse the
10388447a05SGarrett D'Amore 	 * control audio_ctrl_t and as far as outside users are
10488447a05SGarrett D'Amore 	 * concerned the handle is reused. To users this looks like we
10588447a05SGarrett D'Amore 	 * are changing the controls attributes. But what we really do
10688447a05SGarrett D'Amore 	 * is free every thing allocated to the control and then
10788447a05SGarrett D'Amore 	 * reinit everything.  That way the same code can get used for
10888447a05SGarrett D'Amore 	 * both.
10988447a05SGarrett D'Amore 	 *
11088447a05SGarrett D'Amore 	 * We verify anything that could fail before we change the
11188447a05SGarrett D'Amore 	 * control or commit to any changes. If there is something bad
11288447a05SGarrett D'Amore 	 * return null to indicate an error but the original control
11388447a05SGarrett D'Amore 	 * is still usable and untouched.
11488447a05SGarrett D'Amore 	 */
11588447a05SGarrett D'Amore 	ctrl = auclnt_find_control(d, name);
11688447a05SGarrett D'Amore 
11788447a05SGarrett D'Amore 	if (ctrl == NULL) {
11888447a05SGarrett D'Amore 		/* Allocate a new control */
11988447a05SGarrett D'Amore 		ctrl = kmem_zalloc(sizeof (*ctrl), KM_SLEEP);
12088447a05SGarrett D'Amore 	} else {
12188447a05SGarrett D'Amore 		/* Re-configure an existing control */
12288447a05SGarrett D'Amore 		switch (desc->acd_type) {
12388447a05SGarrett D'Amore 		case AUDIO_CTRL_TYPE_BOOLEAN:
12488447a05SGarrett D'Amore 		case AUDIO_CTRL_TYPE_STEREO:
12588447a05SGarrett D'Amore 		case AUDIO_CTRL_TYPE_MONO:
12688447a05SGarrett D'Amore 		case AUDIO_CTRL_TYPE_METER:
12788447a05SGarrett D'Amore 		case AUDIO_CTRL_TYPE_ENUM:
12888447a05SGarrett D'Amore 			break;
12988447a05SGarrett D'Amore 		default:
13088447a05SGarrett D'Amore 			audio_dev_warn(d, "bad control type %d for %s "
13188447a05SGarrett D'Amore 			    "not replaced", desc->acd_type, desc->acd_name);
13288447a05SGarrett D'Amore 			return (NULL);
13388447a05SGarrett D'Amore 		}
13488447a05SGarrett D'Amore 
13588447a05SGarrett D'Amore 		/*
13688447a05SGarrett D'Amore 		 * By removing it from the list we prevent the need to lock
13788447a05SGarrett D'Amore 		 * and check for locks on the control itself.
13888447a05SGarrett D'Amore 		 * Also by doing this we can use the normal add code to do
13988447a05SGarrett D'Amore 		 * what it normally does below.
14088447a05SGarrett D'Amore 		 */
14168c47f65SGarrett D'Amore 		mutex_enter(&d->d_ctrl_lock);
14288447a05SGarrett D'Amore 		list_remove(&d->d_controls, ctrl);
14368c47f65SGarrett D'Amore 		mutex_exit(&d->d_ctrl_lock);
14488447a05SGarrett D'Amore 
14588447a05SGarrett D'Amore 		audio_control_freenames(ctrl);
14688447a05SGarrett D'Amore 		ctrl->ctrl_read_fn = NULL;
14788447a05SGarrett D'Amore 		ctrl->ctrl_write_fn = NULL;
14888447a05SGarrett D'Amore 		ctrl->ctrl_arg = NULL;
14988447a05SGarrett D'Amore 		ctrl->ctrl_dev = NULL;
15088447a05SGarrett D'Amore 	}
15188447a05SGarrett D'Amore 	new_desc = &ctrl->ctrl_des;
15288447a05SGarrett D'Amore 
15388447a05SGarrett D'Amore 	/* Fill in new control description */
15488447a05SGarrett D'Amore 	new_desc->acd_type = desc->acd_type;
15588447a05SGarrett D'Amore 	new_desc->acd_flags = desc->acd_flags;
15688447a05SGarrett D'Amore 	new_desc->acd_maxvalue = desc->acd_maxvalue;
15788447a05SGarrett D'Amore 	new_desc->acd_minvalue = desc->acd_minvalue;
15888447a05SGarrett D'Amore 	new_desc->acd_name = strdup(name);
15988447a05SGarrett D'Amore 
16088447a05SGarrett D'Amore 	/* Process type of control special actions, if any */
16188447a05SGarrett D'Amore 	switch (desc->acd_type) {
16288447a05SGarrett D'Amore 	case AUDIO_CTRL_TYPE_BOOLEAN:
16388447a05SGarrett D'Amore 	case AUDIO_CTRL_TYPE_STEREO:
16488447a05SGarrett D'Amore 	case AUDIO_CTRL_TYPE_MONO:
16588447a05SGarrett D'Amore 	case AUDIO_CTRL_TYPE_METER:
16688447a05SGarrett D'Amore 		break;
16788447a05SGarrett D'Amore 
16888447a05SGarrett D'Amore 	case AUDIO_CTRL_TYPE_ENUM:
16988447a05SGarrett D'Amore 		for (int bit = 0; bit < 64; bit++) {
17088447a05SGarrett D'Amore 			if (((1U << bit) & desc->acd_maxvalue) == 0)
17188447a05SGarrett D'Amore 				continue;
17288447a05SGarrett D'Amore 			name = desc->acd_enum[bit];
17388447a05SGarrett D'Amore 			if (name == NULL) {
17488447a05SGarrett D'Amore 				(void) snprintf(scratch, sizeof (scratch),
17588447a05SGarrett D'Amore 				    "bit%d", bit);
17688447a05SGarrett D'Amore 				name = scratch;
17788447a05SGarrett D'Amore 			}
17888447a05SGarrett D'Amore 			new_desc->acd_enum[bit] = strdup(name);
17988447a05SGarrett D'Amore 		}
18088447a05SGarrett D'Amore 		break;
18188447a05SGarrett D'Amore 	default:
18288447a05SGarrett D'Amore 		audio_dev_warn(d, "bad control type %d for %s",
18388447a05SGarrett D'Amore 		    desc->acd_type, desc->acd_name);
18488447a05SGarrett D'Amore 		goto ctrl_fail;
18588447a05SGarrett D'Amore 	}
18688447a05SGarrett D'Amore 
18788447a05SGarrett D'Amore 	ctrl->ctrl_dev = d;
18888447a05SGarrett D'Amore 	if (new_desc->acd_flags & AUDIO_CTRL_FLAG_READABLE) {
18988447a05SGarrett D'Amore 		ASSERT(read_fn);
19088447a05SGarrett D'Amore 		ctrl->ctrl_read_fn = read_fn;
19188447a05SGarrett D'Amore 		ctrl->ctrl_arg = arg;
19288447a05SGarrett D'Amore 	}
19388447a05SGarrett D'Amore 	if (new_desc->acd_flags & AUDIO_CTRL_FLAG_WRITEABLE) {
19488447a05SGarrett D'Amore 		ASSERT(write_fn);
19588447a05SGarrett D'Amore 		ctrl->ctrl_write_fn = write_fn;
19688447a05SGarrett D'Amore 		ctrl->ctrl_arg = arg;
19788447a05SGarrett D'Amore 	}
19888447a05SGarrett D'Amore 
19968c47f65SGarrett D'Amore 	mutex_enter(&d->d_ctrl_lock);
20088447a05SGarrett D'Amore 	list_insert_tail(&d->d_controls, ctrl);
20168c47f65SGarrett D'Amore 	mutex_exit(&d->d_ctrl_lock);
20288447a05SGarrett D'Amore 
20388447a05SGarrett D'Amore 	return (ctrl);
20488447a05SGarrett D'Amore 
20588447a05SGarrett D'Amore 
20688447a05SGarrett D'Amore ctrl_fail:
20788447a05SGarrett D'Amore 	if (ctrl) {
20888447a05SGarrett D'Amore 		audio_control_freenames(ctrl);
20988447a05SGarrett D'Amore 		kmem_free(ctrl, sizeof (*ctrl));
21088447a05SGarrett D'Amore 	}
21188447a05SGarrett D'Amore 	return (NULL);
21288447a05SGarrett D'Amore }
21388447a05SGarrett D'Amore 
21488447a05SGarrett D'Amore /*
21588447a05SGarrett D'Amore  * This will remove a control from my audio device.
21688447a05SGarrett D'Amore  *
21788447a05SGarrett D'Amore  * ctrl             - The control will be removed.
21888447a05SGarrett D'Amore  */
21988447a05SGarrett D'Amore void
audio_dev_del_control(audio_ctrl_t * ctrl)22088447a05SGarrett D'Amore audio_dev_del_control(audio_ctrl_t *ctrl)
22188447a05SGarrett D'Amore {
22288447a05SGarrett D'Amore 	audio_dev_t *d;
22388447a05SGarrett D'Amore 
22488447a05SGarrett D'Amore 	/* Verify argument */
22588447a05SGarrett D'Amore 	ASSERT(ctrl);
22688447a05SGarrett D'Amore 	d = ctrl->ctrl_dev;
22788447a05SGarrett D'Amore 	ASSERT(d);
22888447a05SGarrett D'Amore 
22968c47f65SGarrett D'Amore 	mutex_enter(&d->d_ctrl_lock);
23088447a05SGarrett D'Amore 	list_remove(&d->d_controls, ctrl);
23168c47f65SGarrett D'Amore 	mutex_exit(&d->d_ctrl_lock);
23288447a05SGarrett D'Amore 
23388447a05SGarrett D'Amore 	audio_control_freenames(ctrl);
23488447a05SGarrett D'Amore 	kmem_free(ctrl, sizeof (*ctrl));
23588447a05SGarrett D'Amore }
23688447a05SGarrett D'Amore 
2372c30fa45SGarrett D'Amore void
audio_dev_add_soft_volume(audio_dev_t * d)23888447a05SGarrett D'Amore audio_dev_add_soft_volume(audio_dev_t *d)
23988447a05SGarrett D'Amore {
24088447a05SGarrett D'Amore 	audio_ctrl_desc_t	desc;
24188447a05SGarrett D'Amore 
24288447a05SGarrett D'Amore 	bzero(&desc, sizeof (desc));
24388447a05SGarrett D'Amore 	if (d->d_pcmvol_ctrl == NULL) {
24488447a05SGarrett D'Amore 		desc.acd_name = AUDIO_CTRL_ID_VOLUME;
24588447a05SGarrett D'Amore 		desc.acd_type = AUDIO_CTRL_TYPE_MONO;
24688447a05SGarrett D'Amore 		desc.acd_minvalue = 0;
24788447a05SGarrett D'Amore 		desc.acd_maxvalue = 100;
24888447a05SGarrett D'Amore 		desc.acd_flags = AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_PLAY |
24988447a05SGarrett D'Amore 		    AUDIO_CTRL_FLAG_PCMVOL;
25088447a05SGarrett D'Amore 		d->d_pcmvol_ctrl = audio_dev_add_control(d, &desc,
25188447a05SGarrett D'Amore 		    auimpl_get_pcmvol, auimpl_set_pcmvol, d);
25288447a05SGarrett D'Amore 		d->d_pcmvol = 75;
25388447a05SGarrett D'Amore 	}
25488447a05SGarrett D'Amore }
25588447a05SGarrett D'Amore 
25688447a05SGarrett D'Amore /*
25788447a05SGarrett D'Amore  * This will notify clients of need to reread control
25888447a05SGarrett D'Amore  * values since they have changed.
25988447a05SGarrett D'Amore  *
26088447a05SGarrett D'Amore  * There will be a routine that allows a client to register
261682cb104SGarrett D'Amore  * a callback.   For now we just update the serial number.
26288447a05SGarrett D'Amore  *
26388447a05SGarrett D'Amore  * d                - The device that needs updates.
26488447a05SGarrett D'Amore  */
26588447a05SGarrett D'Amore void
audio_dev_update_controls(audio_dev_t * d)26688447a05SGarrett D'Amore audio_dev_update_controls(audio_dev_t *d)
26788447a05SGarrett D'Amore {
268682cb104SGarrett D'Amore 	atomic_inc_uint(&d->d_serial);
26988447a05SGarrett D'Amore }
27088447a05SGarrett D'Amore 
27188447a05SGarrett D'Amore 
27288447a05SGarrett D'Amore /*
27388447a05SGarrett D'Amore  * This is used to read the current value of a control.
27488447a05SGarrett D'Amore  * Note, this will cause a callback into the driver to get the value.
27588447a05SGarrett D'Amore  *
27688447a05SGarrett D'Amore  * ctrl        - should be the valid control being read.
27788447a05SGarrett D'Amore  * value       - is a pointer to the place that will contain the value read.
27888447a05SGarrett D'Amore  *
27988447a05SGarrett D'Amore  * On return zero is returned on success else errno is returned.
28088447a05SGarrett D'Amore  *
28188447a05SGarrett D'Amore  */
28288447a05SGarrett D'Amore int
audio_control_read(audio_ctrl_t * ctrl,uint64_t * value)28388447a05SGarrett D'Amore audio_control_read(audio_ctrl_t *ctrl, uint64_t *value)
28488447a05SGarrett D'Amore {
28568c47f65SGarrett D'Amore 	audio_dev_t	*d = ctrl->ctrl_dev;
28668c47f65SGarrett D'Amore 	uint64_t	my_value;
28768c47f65SGarrett D'Amore 	int		ret;
28888447a05SGarrett D'Amore 
28988447a05SGarrett D'Amore 	ASSERT(value);
29068c47f65SGarrett D'Amore 
29168c47f65SGarrett D'Amore 	mutex_enter(&d->d_ctrl_lock);
29268c47f65SGarrett D'Amore 	while (d->d_suspended) {
29368c47f65SGarrett D'Amore 		cv_wait(&d->d_ctrl_cv, &d->d_ctrl_lock);
29468c47f65SGarrett D'Amore 	}
29588447a05SGarrett D'Amore 
29688447a05SGarrett D'Amore 	if (!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_READABLE)) {
29768c47f65SGarrett D'Amore 		mutex_exit(&d->d_ctrl_lock);
29888447a05SGarrett D'Amore 		return (ENXIO);
29988447a05SGarrett D'Amore 	}
30088447a05SGarrett D'Amore 
30188447a05SGarrett D'Amore 	ASSERT(ctrl->ctrl_read_fn);
30288447a05SGarrett D'Amore 
30368c47f65SGarrett D'Amore 	ret = ctrl->ctrl_read_fn(ctrl->ctrl_arg, &my_value);
30468c47f65SGarrett D'Amore 	mutex_exit(&d->d_ctrl_lock);
30588447a05SGarrett D'Amore 
30668c47f65SGarrett D'Amore 	if (ret == 0) {
30768c47f65SGarrett D'Amore 		*value = my_value;
30868c47f65SGarrett D'Amore 	}
30988447a05SGarrett D'Amore 
31088447a05SGarrett D'Amore 	return (ret);
31188447a05SGarrett D'Amore }
31288447a05SGarrett D'Amore 
31388447a05SGarrett D'Amore /*
31488447a05SGarrett D'Amore  * This is used to write a value to a control.
31588447a05SGarrett D'Amore  * Note, this will cause a callback into the driver to write the value.
31688447a05SGarrett D'Amore  *
31788447a05SGarrett D'Amore  * ctrl        - should be the valid control being written.
31888447a05SGarrett D'Amore  * value       - is value to set the control to.
31988447a05SGarrett D'Amore  *
32088447a05SGarrett D'Amore  * On return zero is returned on success else errno is returned.
32188447a05SGarrett D'Amore  *
32288447a05SGarrett D'Amore  */
32388447a05SGarrett D'Amore int
audio_control_write(audio_ctrl_t * ctrl,uint64_t value)32488447a05SGarrett D'Amore audio_control_write(audio_ctrl_t *ctrl, uint64_t value)
32588447a05SGarrett D'Amore {
32688447a05SGarrett D'Amore 	int		ret;
32788447a05SGarrett D'Amore 	audio_dev_t	*d = ctrl->ctrl_dev;
32888447a05SGarrett D'Amore 
32968c47f65SGarrett D'Amore 	mutex_enter(&d->d_ctrl_lock);
33068c47f65SGarrett D'Amore 	while (d->d_suspended) {
33168c47f65SGarrett D'Amore 		cv_wait(&d->d_ctrl_cv, &d->d_ctrl_lock);
33268c47f65SGarrett D'Amore 	}
33388447a05SGarrett D'Amore 
33488447a05SGarrett D'Amore 	if (!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_WRITEABLE)) {
33568c47f65SGarrett D'Amore 		mutex_exit(&d->d_ctrl_lock);
33688447a05SGarrett D'Amore 		return (ENXIO);
33788447a05SGarrett D'Amore 	}
33888447a05SGarrett D'Amore 
33988447a05SGarrett D'Amore 	ASSERT(ctrl->ctrl_write_fn);
34088447a05SGarrett D'Amore 
34188447a05SGarrett D'Amore 	ret = ctrl->ctrl_write_fn(ctrl->ctrl_arg, value);
34268c47f65SGarrett D'Amore 	if (ret == 0) {
34368c47f65SGarrett D'Amore 		ctrl->ctrl_saved = value;
34468c47f65SGarrett D'Amore 		ctrl->ctrl_saved_ok = B_TRUE;
34568c47f65SGarrett D'Amore 	}
34668c47f65SGarrett D'Amore 	mutex_exit(&d->d_ctrl_lock);
34788447a05SGarrett D'Amore 
34868c47f65SGarrett D'Amore 	if (ret == 0) {
34988447a05SGarrett D'Amore 		audio_dev_update_controls(d);
35068c47f65SGarrett D'Amore 	}
35188447a05SGarrett D'Amore 
35288447a05SGarrett D'Amore 	return (ret);
35388447a05SGarrett D'Amore }
35468c47f65SGarrett D'Amore 
35568c47f65SGarrett D'Amore /*
35668c47f65SGarrett D'Amore  * This is used to save control values.
35768c47f65SGarrett D'Amore  */
35868c47f65SGarrett D'Amore int
auimpl_save_controls(audio_dev_t * d)35968c47f65SGarrett D'Amore auimpl_save_controls(audio_dev_t *d)
36068c47f65SGarrett D'Amore {
36168c47f65SGarrett D'Amore 	audio_ctrl_t	*ctrl;
36268c47f65SGarrett D'Amore 	list_t		*l;
36368c47f65SGarrett D'Amore 	int		ret;
36468c47f65SGarrett D'Amore 
36568c47f65SGarrett D'Amore 	ASSERT(mutex_owned(&d->d_ctrl_lock));
36668c47f65SGarrett D'Amore 	l = &d->d_controls;
36768c47f65SGarrett D'Amore 
36868c47f65SGarrett D'Amore 	for (ctrl = list_head(l); ctrl; ctrl = list_next(l, ctrl)) {
36968c47f65SGarrett D'Amore 		if ((!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_WRITEABLE)) ||
370*4a2cf368SZhao Edgar Liu - Sun Microsystems 		    (!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_READABLE))) {
37168c47f65SGarrett D'Amore 			continue;
37268c47f65SGarrett D'Amore 		}
37368c47f65SGarrett D'Amore 		ret = ctrl->ctrl_read_fn(ctrl->ctrl_arg, &ctrl->ctrl_saved);
37468c47f65SGarrett D'Amore 		if (ret != 0) {
37568c47f65SGarrett D'Amore 			audio_dev_warn(d,
37668c47f65SGarrett D'Amore 			    "Unable to save value of control %s",
37768c47f65SGarrett D'Amore 			    ctrl->ctrl_name);
37868c47f65SGarrett D'Amore 			return (ret);
37968c47f65SGarrett D'Amore 		} else {
38068c47f65SGarrett D'Amore 			ctrl->ctrl_saved_ok = B_TRUE;
38168c47f65SGarrett D'Amore 		}
38268c47f65SGarrett D'Amore 	}
38368c47f65SGarrett D'Amore 	return (0);
38468c47f65SGarrett D'Amore }
38568c47f65SGarrett D'Amore 
38668c47f65SGarrett D'Amore int
auimpl_restore_controls(audio_dev_t * d)38768c47f65SGarrett D'Amore auimpl_restore_controls(audio_dev_t *d)
38868c47f65SGarrett D'Amore {
38968c47f65SGarrett D'Amore 	audio_ctrl_t	*ctrl;
39068c47f65SGarrett D'Amore 	list_t		*l;
39168c47f65SGarrett D'Amore 	int		ret;
39268c47f65SGarrett D'Amore 	int		rv = 0;
39368c47f65SGarrett D'Amore 
39468c47f65SGarrett D'Amore 	ASSERT(mutex_owned(&d->d_ctrl_lock));
39568c47f65SGarrett D'Amore 	l = &d->d_controls;
39668c47f65SGarrett D'Amore 
39768c47f65SGarrett D'Amore 	for (ctrl = list_head(l); ctrl; ctrl = list_next(l, ctrl)) {
39868c47f65SGarrett D'Amore 		if (!ctrl->ctrl_saved_ok) {
39968c47f65SGarrett D'Amore 			continue;
40068c47f65SGarrett D'Amore 		}
40168c47f65SGarrett D'Amore 		ret = ctrl->ctrl_write_fn(ctrl->ctrl_arg, ctrl->ctrl_saved);
40268c47f65SGarrett D'Amore 		if (ret != 0) {
40368c47f65SGarrett D'Amore 			audio_dev_warn(d,
40468c47f65SGarrett D'Amore 			    "Unable to restore value of control %s",
40568c47f65SGarrett D'Amore 			    ctrl->ctrl_name);
40668c47f65SGarrett D'Amore 			rv = ret;
40768c47f65SGarrett D'Amore 		}
40868c47f65SGarrett D'Amore 	}
40968c47f65SGarrett D'Amore 	return (rv);
41068c47f65SGarrett D'Amore }
411