Merge remote-tracking branch 'origin/master'
[unleashed/lotheac.git] / usr / src / uts / common / io / audio / impl / audio_ctrl.c
blobf5fd2806397e5d110f8cf09f26315b52663494b8
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
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>
32 #include "audio_impl.h"
35 * Audio Control functions.
39 * Given a control structure - free all names
40 * strings allocated to it.
42 * ctrl - The control who's names that will be free'd.
44 static void
45 audio_control_freenames(audio_ctrl_t *ctrl)
47 int indx;
49 if (ctrl->ctrl_name != NULL)
50 strfree((char *)ctrl->ctrl_name);
51 ctrl->ctrl_name = NULL;
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;
62 * This will allocate and register a control for my audio device.
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
70 * On success this will return a control structure else NULL.
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.
82 audio_ctrl_t *
83 audio_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)
86 audio_ctrl_t *ctrl;
87 audio_ctrl_desc_t *new_desc;
88 char scratch[16];
89 const char *name;
91 /* Verify arguments */
92 ASSERT(d);
93 ASSERT(desc);
95 /* We cannot deal with unnamed controls */
96 if ((name = desc->acd_name) == NULL) {
97 return (NULL);
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.
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.
115 ctrl = auclnt_find_control(d, name);
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);
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.
141 mutex_enter(&d->d_ctrl_lock);
142 list_remove(&d->d_controls, ctrl);
143 mutex_exit(&d->d_ctrl_lock);
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;
151 new_desc = &ctrl->ctrl_des;
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);
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;
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;
178 new_desc->acd_enum[bit] = strdup(name);
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;
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;
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;
199 mutex_enter(&d->d_ctrl_lock);
200 list_insert_tail(&d->d_controls, ctrl);
201 mutex_exit(&d->d_ctrl_lock);
203 return (ctrl);
206 ctrl_fail:
207 if (ctrl) {
208 audio_control_freenames(ctrl);
209 kmem_free(ctrl, sizeof (*ctrl));
211 return (NULL);
215 * This will remove a control from my audio device.
217 * ctrl - The control will be removed.
219 void
220 audio_dev_del_control(audio_ctrl_t *ctrl)
222 audio_dev_t *d;
224 /* Verify argument */
225 ASSERT(ctrl);
226 d = ctrl->ctrl_dev;
227 ASSERT(d);
229 mutex_enter(&d->d_ctrl_lock);
230 list_remove(&d->d_controls, ctrl);
231 mutex_exit(&d->d_ctrl_lock);
233 audio_control_freenames(ctrl);
234 kmem_free(ctrl, sizeof (*ctrl));
237 void
238 audio_dev_add_soft_volume(audio_dev_t *d)
240 audio_ctrl_desc_t desc;
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;
257 * This will notify clients of need to reread control
258 * values since they have changed.
260 * There will be a routine that allows a client to register
261 * a callback. For now we just update the serial number.
263 * d - The device that needs updates.
265 void
266 audio_dev_update_controls(audio_dev_t *d)
268 atomic_inc_uint(&d->d_serial);
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.
276 * ctrl - should be the valid control being read.
277 * value - is a pointer to the place that will contain the value read.
279 * On return zero is returned on success else errno is returned.
283 audio_control_read(audio_ctrl_t *ctrl, uint64_t *value)
285 audio_dev_t *d = ctrl->ctrl_dev;
286 uint64_t my_value;
287 int ret;
289 ASSERT(value);
291 mutex_enter(&d->d_ctrl_lock);
292 while (d->d_suspended) {
293 cv_wait(&d->d_ctrl_cv, &d->d_ctrl_lock);
296 if (!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_READABLE)) {
297 mutex_exit(&d->d_ctrl_lock);
298 return (ENXIO);
301 ASSERT(ctrl->ctrl_read_fn);
303 ret = ctrl->ctrl_read_fn(ctrl->ctrl_arg, &my_value);
304 mutex_exit(&d->d_ctrl_lock);
306 if (ret == 0) {
307 *value = my_value;
310 return (ret);
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.
317 * ctrl - should be the valid control being written.
318 * value - is value to set the control to.
320 * On return zero is returned on success else errno is returned.
324 audio_control_write(audio_ctrl_t *ctrl, uint64_t value)
326 int ret;
327 audio_dev_t *d = ctrl->ctrl_dev;
329 mutex_enter(&d->d_ctrl_lock);
330 while (d->d_suspended) {
331 cv_wait(&d->d_ctrl_cv, &d->d_ctrl_lock);
334 if (!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_WRITEABLE)) {
335 mutex_exit(&d->d_ctrl_lock);
336 return (ENXIO);
339 ASSERT(ctrl->ctrl_write_fn);
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;
346 mutex_exit(&d->d_ctrl_lock);
348 if (ret == 0) {
349 audio_dev_update_controls(d);
352 return (ret);
356 * This is used to save control values.
359 auimpl_save_controls(audio_dev_t *d)
361 audio_ctrl_t *ctrl;
362 list_t *l;
363 int ret;
365 ASSERT(mutex_owned(&d->d_ctrl_lock));
366 l = &d->d_controls;
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;
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;
383 return (0);
387 auimpl_restore_controls(audio_dev_t *d)
389 audio_ctrl_t *ctrl;
390 list_t *l;
391 int ret;
392 int rv = 0;
394 ASSERT(mutex_owned(&d->d_ctrl_lock));
395 l = &d->d_controls;
397 for (ctrl = list_head(l); ctrl; ctrl = list_next(l, ctrl)) {
398 if (!ctrl->ctrl_saved_ok) {
399 continue;
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;
409 return (rv);