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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25#include <sys/types.h>
26#include <sys/list.h>
27#include <sys/sysmacros.h>
28#include <sys/ddi.h>
29#include <sys/sunddi.h>
30#include <sys/atomic.h>
31
32#include "audio_impl.h"
33
34/*
35 * Audio Control functions.
36 */
37
38/*
39 * Given a control structure - free all names
40 * strings allocated to it.
41 *
42 * ctrl             - The control who's names that will be free'd.
43 */
44static void
45audio_control_freenames(audio_ctrl_t *ctrl)
46{
47	int	indx;
48
49	if (ctrl->ctrl_name != NULL)
50		strfree((char *)ctrl->ctrl_name);
51	ctrl->ctrl_name = NULL;
52
53	for (indx = 0; indx < 64; indx++) {
54		if (ctrl->ctrl_enum[indx] != NULL) {
55			strfree((char *)ctrl->ctrl_enum[indx]);
56			ctrl->ctrl_enum[indx] = NULL;
57		}
58	}
59}
60
61/*
62 * This will allocate and register a control for my audio device.
63 *
64 * d                - The audio device the control will be attached to.
65 * desc             - Attributes about this new control
66 * read_fn          - Callback function in driver to read control
67 * write_fn         - Callback function in driver to write control.
68 * arg              - driver private context passed to read_fn/write_fn
69 *
70 * On success this will return a control structure else NULL.
71 *
72 * The value passed in for a control number in the audio_ctrl_desc_t
73 * has some special meaning. If it is less then AUDIO_CONTROL_EXTBASE
74 * then the control is assumed to be a known control. If it is
75 * AUDIO_CONTROL_EXTBASE then the framework will allocate a unique
76 * control number and replace it in the audio_ctrl_desc_t structure
77 * and this control is considered an extended driver private control.
78 * The number that is replaced in audio_ctrl_desc_t will be greater
79 * then AUDIO_CONTROL_EXTBASE.
80 *
81 */
82audio_ctrl_t *
83audio_dev_add_control(audio_dev_t *d, audio_ctrl_desc_t *desc,
84    audio_ctrl_rd_t read_fn, audio_ctrl_wr_t write_fn, void *arg)
85{
86	audio_ctrl_t *ctrl;
87	audio_ctrl_desc_t *new_desc;
88	char	scratch[16];
89	const char	*name;
90
91	/* Verify arguments */
92	ASSERT(d);
93	ASSERT(desc);
94
95	/* We cannot deal with unnamed controls */
96	if ((name = desc->acd_name) == NULL) {
97		return (NULL);
98	}
99
100	/*
101	 * If this was called with a control name that was already
102	 * added, then we do some special things. First we reuse the
103	 * control audio_ctrl_t and as far as outside users are
104	 * concerned the handle is reused. To users this looks like we
105	 * are changing the controls attributes. But what we really do
106	 * is free every thing allocated to the control and then
107	 * reinit everything.  That way the same code can get used for
108	 * both.
109	 *
110	 * We verify anything that could fail before we change the
111	 * control or commit to any changes. If there is something bad
112	 * return null to indicate an error but the original control
113	 * is still usable and untouched.
114	 */
115	ctrl = auclnt_find_control(d, name);
116
117	if (ctrl == NULL) {
118		/* Allocate a new control */
119		ctrl = kmem_zalloc(sizeof (*ctrl), KM_SLEEP);
120	} else {
121		/* Re-configure an existing control */
122		switch (desc->acd_type) {
123		case AUDIO_CTRL_TYPE_BOOLEAN:
124		case AUDIO_CTRL_TYPE_STEREO:
125		case AUDIO_CTRL_TYPE_MONO:
126		case AUDIO_CTRL_TYPE_METER:
127		case AUDIO_CTRL_TYPE_ENUM:
128			break;
129		default:
130			audio_dev_warn(d, "bad control type %d for %s "
131			    "not replaced", desc->acd_type, desc->acd_name);
132			return (NULL);
133		}
134
135		/*
136		 * By removing it from the list we prevent the need to lock
137		 * and check for locks on the control itself.
138		 * Also by doing this we can use the normal add code to do
139		 * what it normally does below.
140		 */
141		mutex_enter(&d->d_ctrl_lock);
142		list_remove(&d->d_controls, ctrl);
143		mutex_exit(&d->d_ctrl_lock);
144
145		audio_control_freenames(ctrl);
146		ctrl->ctrl_read_fn = NULL;
147		ctrl->ctrl_write_fn = NULL;
148		ctrl->ctrl_arg = NULL;
149		ctrl->ctrl_dev = NULL;
150	}
151	new_desc = &ctrl->ctrl_des;
152
153	/* Fill in new control description */
154	new_desc->acd_type = desc->acd_type;
155	new_desc->acd_flags = desc->acd_flags;
156	new_desc->acd_maxvalue = desc->acd_maxvalue;
157	new_desc->acd_minvalue = desc->acd_minvalue;
158	new_desc->acd_name = strdup(name);
159
160	/* Process type of control special actions, if any */
161	switch (desc->acd_type) {
162	case AUDIO_CTRL_TYPE_BOOLEAN:
163	case AUDIO_CTRL_TYPE_STEREO:
164	case AUDIO_CTRL_TYPE_MONO:
165	case AUDIO_CTRL_TYPE_METER:
166		break;
167
168	case AUDIO_CTRL_TYPE_ENUM:
169		for (int bit = 0; bit < 64; bit++) {
170			if (((1U << bit) & desc->acd_maxvalue) == 0)
171				continue;
172			name = desc->acd_enum[bit];
173			if (name == NULL) {
174				(void) snprintf(scratch, sizeof (scratch),
175				    "bit%d", bit);
176				name = scratch;
177			}
178			new_desc->acd_enum[bit] = strdup(name);
179		}
180		break;
181	default:
182		audio_dev_warn(d, "bad control type %d for %s",
183		    desc->acd_type, desc->acd_name);
184		goto ctrl_fail;
185	}
186
187	ctrl->ctrl_dev = d;
188	if (new_desc->acd_flags & AUDIO_CTRL_FLAG_READABLE) {
189		ASSERT(read_fn);
190		ctrl->ctrl_read_fn = read_fn;
191		ctrl->ctrl_arg = arg;
192	}
193	if (new_desc->acd_flags & AUDIO_CTRL_FLAG_WRITEABLE) {
194		ASSERT(write_fn);
195		ctrl->ctrl_write_fn = write_fn;
196		ctrl->ctrl_arg = arg;
197	}
198
199	mutex_enter(&d->d_ctrl_lock);
200	list_insert_tail(&d->d_controls, ctrl);
201	mutex_exit(&d->d_ctrl_lock);
202
203	return (ctrl);
204
205
206ctrl_fail:
207	if (ctrl) {
208		audio_control_freenames(ctrl);
209		kmem_free(ctrl, sizeof (*ctrl));
210	}
211	return (NULL);
212}
213
214/*
215 * This will remove a control from my audio device.
216 *
217 * ctrl             - The control will be removed.
218 */
219void
220audio_dev_del_control(audio_ctrl_t *ctrl)
221{
222	audio_dev_t *d;
223
224	/* Verify argument */
225	ASSERT(ctrl);
226	d = ctrl->ctrl_dev;
227	ASSERT(d);
228
229	mutex_enter(&d->d_ctrl_lock);
230	list_remove(&d->d_controls, ctrl);
231	mutex_exit(&d->d_ctrl_lock);
232
233	audio_control_freenames(ctrl);
234	kmem_free(ctrl, sizeof (*ctrl));
235}
236
237void
238audio_dev_add_soft_volume(audio_dev_t *d)
239{
240	audio_ctrl_desc_t	desc;
241
242	bzero(&desc, sizeof (desc));
243	if (d->d_pcmvol_ctrl == NULL) {
244		desc.acd_name = AUDIO_CTRL_ID_VOLUME;
245		desc.acd_type = AUDIO_CTRL_TYPE_MONO;
246		desc.acd_minvalue = 0;
247		desc.acd_maxvalue = 100;
248		desc.acd_flags = AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_PLAY |
249		    AUDIO_CTRL_FLAG_PCMVOL;
250		d->d_pcmvol_ctrl = audio_dev_add_control(d, &desc,
251		    auimpl_get_pcmvol, auimpl_set_pcmvol, d);
252		d->d_pcmvol = 75;
253	}
254}
255
256/*
257 * This will notify clients of need to reread control
258 * values since they have changed.
259 *
260 * There will be a routine that allows a client to register
261 * a callback.   For now we just update the serial number.
262 *
263 * d                - The device that needs updates.
264 */
265void
266audio_dev_update_controls(audio_dev_t *d)
267{
268	atomic_inc_uint(&d->d_serial);
269}
270
271
272/*
273 * This is used to read the current value of a control.
274 * Note, this will cause a callback into the driver to get the value.
275 *
276 * ctrl        - should be the valid control being read.
277 * value       - is a pointer to the place that will contain the value read.
278 *
279 * On return zero is returned on success else errno is returned.
280 *
281 */
282int
283audio_control_read(audio_ctrl_t *ctrl, uint64_t *value)
284{
285	audio_dev_t	*d = ctrl->ctrl_dev;
286	uint64_t	my_value;
287	int		ret;
288
289	ASSERT(value);
290
291	mutex_enter(&d->d_ctrl_lock);
292	while (d->d_suspended) {
293		cv_wait(&d->d_ctrl_cv, &d->d_ctrl_lock);
294	}
295
296	if (!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_READABLE)) {
297		mutex_exit(&d->d_ctrl_lock);
298		return (ENXIO);
299	}
300
301	ASSERT(ctrl->ctrl_read_fn);
302
303	ret = ctrl->ctrl_read_fn(ctrl->ctrl_arg, &my_value);
304	mutex_exit(&d->d_ctrl_lock);
305
306	if (ret == 0) {
307		*value = my_value;
308	}
309
310	return (ret);
311}
312
313/*
314 * This is used to write a value to a control.
315 * Note, this will cause a callback into the driver to write the value.
316 *
317 * ctrl        - should be the valid control being written.
318 * value       - is value to set the control to.
319 *
320 * On return zero is returned on success else errno is returned.
321 *
322 */
323int
324audio_control_write(audio_ctrl_t *ctrl, uint64_t value)
325{
326	int		ret;
327	audio_dev_t	*d = ctrl->ctrl_dev;
328
329	mutex_enter(&d->d_ctrl_lock);
330	while (d->d_suspended) {
331		cv_wait(&d->d_ctrl_cv, &d->d_ctrl_lock);
332	}
333
334	if (!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_WRITEABLE)) {
335		mutex_exit(&d->d_ctrl_lock);
336		return (ENXIO);
337	}
338
339	ASSERT(ctrl->ctrl_write_fn);
340
341	ret = ctrl->ctrl_write_fn(ctrl->ctrl_arg, value);
342	if (ret == 0) {
343		ctrl->ctrl_saved = value;
344		ctrl->ctrl_saved_ok = B_TRUE;
345	}
346	mutex_exit(&d->d_ctrl_lock);
347
348	if (ret == 0) {
349		audio_dev_update_controls(d);
350	}
351
352	return (ret);
353}
354
355/*
356 * This is used to save control values.
357 */
358int
359auimpl_save_controls(audio_dev_t *d)
360{
361	audio_ctrl_t	*ctrl;
362	list_t		*l;
363	int		ret;
364
365	ASSERT(mutex_owned(&d->d_ctrl_lock));
366	l = &d->d_controls;
367
368	for (ctrl = list_head(l); ctrl; ctrl = list_next(l, ctrl)) {
369		if ((!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_WRITEABLE)) ||
370		    (!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_READABLE))) {
371			continue;
372		}
373		ret = ctrl->ctrl_read_fn(ctrl->ctrl_arg, &ctrl->ctrl_saved);
374		if (ret != 0) {
375			audio_dev_warn(d,
376			    "Unable to save value of control %s",
377			    ctrl->ctrl_name);
378			return (ret);
379		} else {
380			ctrl->ctrl_saved_ok = B_TRUE;
381		}
382	}
383	return (0);
384}
385
386int
387auimpl_restore_controls(audio_dev_t *d)
388{
389	audio_ctrl_t	*ctrl;
390	list_t		*l;
391	int		ret;
392	int		rv = 0;
393
394	ASSERT(mutex_owned(&d->d_ctrl_lock));
395	l = &d->d_controls;
396
397	for (ctrl = list_head(l); ctrl; ctrl = list_next(l, ctrl)) {
398		if (!ctrl->ctrl_saved_ok) {
399			continue;
400		}
401		ret = ctrl->ctrl_write_fn(ctrl->ctrl_arg, ctrl->ctrl_saved);
402		if (ret != 0) {
403			audio_dev_warn(d,
404			    "Unable to restore value of control %s",
405			    ctrl->ctrl_name);
406			rv = ret;
407		}
408	}
409	return (rv);
410}
411