xref: /illumos-gate/usr/src/uts/common/io/audio/impl/audio_ctrl.c (revision 5bbb4db2c3f208d12bf0fd11769728f9e5ba66a2)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/types.h>
27 #include <sys/list.h>
28 #include <sys/sysmacros.h>
29 #include <sys/ddi.h>
30 #include <sys/sunddi.h>
31 #include <sys/atomic.h>
32 
33 #include "audio_impl.h"
34 
35 /*
36  * Audio Control functions.
37  */
38 
39 /*
40  * Given a control structure - free all names
41  * strings allocated to it.
42  *
43  * ctrl             - The control who's names that will be free'd.
44  */
45 static void
46 audio_control_freenames(audio_ctrl_t *ctrl)
47 {
48 	int	indx;
49 
50 	if (ctrl->ctrl_name != NULL)
51 		strfree((char *)ctrl->ctrl_name);
52 	ctrl->ctrl_name = NULL;
53 
54 	for (indx = 0; indx < 64; indx++) {
55 		if (ctrl->ctrl_enum[indx] != NULL) {
56 			strfree((char *)ctrl->ctrl_enum[indx]);
57 			ctrl->ctrl_enum[indx] = NULL;
58 		}
59 	}
60 }
61 
62 /*
63  * This will allocate and register a control for my audio device.
64  *
65  * d                - The audio device the control will be attached to.
66  * desc             - Attributes about this new control
67  * read_fn          - Callback function in driver to read control
68  * write_fn         - Callback function in driver to write control.
69  * arg              - driver private context passed to read_fn/write_fn
70  *
71  * On success this will return a control structure else NULL.
72  *
73  * The value passed in for a control number in the audio_ctrl_desc_t
74  * has some special meaning. If it is less then AUDIO_CONTROL_EXTBASE
75  * then the control is assumed to be a known control. If it is
76  * AUDIO_CONTROL_EXTBASE then the framework will allocate a unique
77  * control number and replace it in the audio_ctrl_desc_t structure
78  * and this control is considered an extended driver private control.
79  * The number that is replaced in audio_ctrl_desc_t will be greater
80  * then AUDIO_CONTROL_EXTBASE.
81  *
82  */
83 audio_ctrl_t *
84 audio_dev_add_control(audio_dev_t *d, audio_ctrl_desc_t *desc,
85     audio_ctrl_rd_t read_fn, audio_ctrl_wr_t write_fn, void *arg)
86 {
87 	audio_ctrl_t *ctrl;
88 	audio_ctrl_desc_t *new_desc;
89 	char	scratch[16];
90 	const char	*name;
91 
92 	/* Verify arguments */
93 	ASSERT(d);
94 	ASSERT(desc);
95 
96 	/* We cannot deal with unnamed controls */
97 	if ((name = desc->acd_name) == NULL) {
98 		return (NULL);
99 	}
100 
101 	/*
102 	 * If this was called with a control name that was already
103 	 * added, then we do some special things. First we reuse the
104 	 * control audio_ctrl_t and as far as outside users are
105 	 * concerned the handle is reused. To users this looks like we
106 	 * are changing the controls attributes. But what we really do
107 	 * is free every thing allocated to the control and then
108 	 * reinit everything.  That way the same code can get used for
109 	 * both.
110 	 *
111 	 * We verify anything that could fail before we change the
112 	 * control or commit to any changes. If there is something bad
113 	 * return null to indicate an error but the original control
114 	 * is still usable and untouched.
115 	 */
116 	ctrl = auclnt_find_control(d, name);
117 
118 	if (ctrl == NULL) {
119 		/* Allocate a new control */
120 		ctrl = kmem_zalloc(sizeof (*ctrl), KM_SLEEP);
121 	} else {
122 		/* Re-configure an existing control */
123 		switch (desc->acd_type) {
124 		case AUDIO_CTRL_TYPE_BOOLEAN:
125 		case AUDIO_CTRL_TYPE_STEREO:
126 		case AUDIO_CTRL_TYPE_MONO:
127 		case AUDIO_CTRL_TYPE_METER:
128 		case AUDIO_CTRL_TYPE_ENUM:
129 			break;
130 		default:
131 			audio_dev_warn(d, "bad control type %d for %s "
132 			    "not replaced", desc->acd_type, desc->acd_name);
133 			return (NULL);
134 		}
135 
136 		/*
137 		 * By removing it from the list we prevent the need to lock
138 		 * and check for locks on the control itself.
139 		 * Also by doing this we can use the normal add code to do
140 		 * what it normally does below.
141 		 */
142 		rw_enter(&d->d_ctrl_lock, RW_WRITER);
143 		list_remove(&d->d_controls, ctrl);
144 		rw_exit(&d->d_ctrl_lock);
145 
146 		audio_control_freenames(ctrl);
147 		ctrl->ctrl_read_fn = NULL;
148 		ctrl->ctrl_write_fn = NULL;
149 		ctrl->ctrl_arg = NULL;
150 		ctrl->ctrl_dev = NULL;
151 		mutex_destroy(&ctrl->ctrl_lock);
152 	}
153 	new_desc = &ctrl->ctrl_des;
154 
155 	/* Fill in new control description */
156 	new_desc->acd_type = desc->acd_type;
157 	new_desc->acd_flags = desc->acd_flags;
158 	new_desc->acd_maxvalue = desc->acd_maxvalue;
159 	new_desc->acd_minvalue = desc->acd_minvalue;
160 	new_desc->acd_name = strdup(name);
161 
162 	/* Process type of control special actions, if any */
163 	switch (desc->acd_type) {
164 	case AUDIO_CTRL_TYPE_BOOLEAN:
165 	case AUDIO_CTRL_TYPE_STEREO:
166 	case AUDIO_CTRL_TYPE_MONO:
167 	case AUDIO_CTRL_TYPE_METER:
168 		break;
169 
170 	case AUDIO_CTRL_TYPE_ENUM:
171 		for (int bit = 0; bit < 64; bit++) {
172 			if (((1U << bit) & desc->acd_maxvalue) == 0)
173 				continue;
174 			name = desc->acd_enum[bit];
175 			if (name == NULL) {
176 				(void) snprintf(scratch, sizeof (scratch),
177 				    "bit%d", bit);
178 				name = scratch;
179 			}
180 			new_desc->acd_enum[bit] = strdup(name);
181 		}
182 		break;
183 	default:
184 		audio_dev_warn(d, "bad control type %d for %s",
185 		    desc->acd_type, desc->acd_name);
186 		goto ctrl_fail;
187 	}
188 
189 	ctrl->ctrl_dev = d;
190 	if (new_desc->acd_flags & AUDIO_CTRL_FLAG_READABLE) {
191 		ASSERT(read_fn);
192 		ctrl->ctrl_read_fn = read_fn;
193 		ctrl->ctrl_arg = arg;
194 	}
195 	if (new_desc->acd_flags & AUDIO_CTRL_FLAG_WRITEABLE) {
196 		ASSERT(write_fn);
197 		ctrl->ctrl_write_fn = write_fn;
198 		ctrl->ctrl_arg = arg;
199 	}
200 
201 	mutex_init(&ctrl->ctrl_lock, NULL, MUTEX_DRIVER, NULL);
202 
203 	rw_enter(&d->d_ctrl_lock, RW_WRITER);
204 	list_insert_tail(&d->d_controls, ctrl);
205 	rw_exit(&d->d_ctrl_lock);
206 
207 	return (ctrl);
208 
209 
210 ctrl_fail:
211 	if (ctrl) {
212 		audio_control_freenames(ctrl);
213 		kmem_free(ctrl, sizeof (*ctrl));
214 	}
215 	return (NULL);
216 }
217 
218 /*
219  * This will remove a control from my audio device.
220  *
221  * ctrl             - The control will be removed.
222  */
223 void
224 audio_dev_del_control(audio_ctrl_t *ctrl)
225 {
226 	audio_dev_t *d;
227 
228 	/* Verify argument */
229 	ASSERT(ctrl);
230 	d = ctrl->ctrl_dev;
231 	ASSERT(d);
232 
233 	rw_enter(&d->d_ctrl_lock, RW_WRITER);
234 	list_remove(&d->d_controls, ctrl);
235 	rw_exit(&d->d_ctrl_lock);
236 
237 	mutex_destroy(&ctrl->ctrl_lock);
238 
239 	audio_control_freenames(ctrl);
240 	kmem_free(ctrl, sizeof (*ctrl));
241 }
242 
243 int
244 audio_dev_add_soft_volume(audio_dev_t *d)
245 {
246 	audio_ctrl_desc_t	desc;
247 
248 	bzero(&desc, sizeof (desc));
249 	if (d->d_pcmvol_ctrl == NULL) {
250 		desc.acd_name = AUDIO_CTRL_ID_VOLUME;
251 		desc.acd_type = AUDIO_CTRL_TYPE_MONO;
252 		desc.acd_minvalue = 0;
253 		desc.acd_maxvalue = 100;
254 		desc.acd_flags = AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_PLAY |
255 		    AUDIO_CTRL_FLAG_PCMVOL;
256 		d->d_pcmvol_ctrl = audio_dev_add_control(d, &desc,
257 		    auimpl_get_pcmvol, auimpl_set_pcmvol, d);
258 		d->d_pcmvol = 75;
259 	}
260 	return (0);
261 }
262 
263 /*
264  * This will notify clients of need to reread control
265  * values since they have changed.
266  *
267  * There will be a routine that allows a client to register
268  * a callback.   For now we just update the serial number.
269  *
270  * d                - The device that needs updates.
271  */
272 void
273 audio_dev_update_controls(audio_dev_t *d)
274 {
275 	atomic_inc_uint(&d->d_serial);
276 }
277 
278 
279 /*
280  * This is used to read the current value of a control.
281  * Note, this will cause a callback into the driver to get the value.
282  *
283  * ctrl        - should be the valid control being read.
284  * value       - is a pointer to the place that will contain the value read.
285  *
286  * On return zero is returned on success else errno is returned.
287  *
288  */
289 int
290 audio_control_read(audio_ctrl_t *ctrl, uint64_t *value)
291 {
292 	uint64_t my_value;
293 	int ret;
294 
295 	/* Verify arguments */
296 	ASSERT(ctrl);
297 	ASSERT(value);
298 	ASSERT(ctrl->ctrl_dev);
299 
300 	if (!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_READABLE)) {
301 		return (ENXIO);
302 	}
303 
304 	ASSERT(ctrl->ctrl_read_fn);
305 
306 	if ((ret = ctrl->ctrl_read_fn(ctrl->ctrl_arg, &my_value)) != 0) {
307 		return (ret);
308 	}
309 
310 	*value = my_value;
311 
312 	return (ret);
313 }
314 
315 /*
316  * This is used to write a value to a control.
317  * Note, this will cause a callback into the driver to write the value.
318  *
319  * ctrl        - should be the valid control being written.
320  * value       - is value to set the control to.
321  *
322  * On return zero is returned on success else errno is returned.
323  *
324  */
325 int
326 audio_control_write(audio_ctrl_t *ctrl, uint64_t value)
327 {
328 	int		ret;
329 	audio_dev_t	*d = ctrl->ctrl_dev;
330 
331 	/* Verify arguments */
332 	ASSERT(ctrl);
333 	ASSERT(d);
334 
335 	if (!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_WRITEABLE)) {
336 		return (ENXIO);
337 	}
338 
339 	ASSERT(ctrl->ctrl_write_fn);
340 
341 	ret = ctrl->ctrl_write_fn(ctrl->ctrl_arg, value);
342 
343 	if (ret == 0)
344 		audio_dev_update_controls(d);
345 
346 	return (ret);
347 }
348