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 */
44 static void
audio_control_freenames(audio_ctrl_t * ctrl)45 audio_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 */
82 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)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)
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
206 ctrl_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 */
219 void
audio_dev_del_control(audio_ctrl_t * ctrl)220 audio_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
237 void
audio_dev_add_soft_volume(audio_dev_t * d)238 audio_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 */
265 void
audio_dev_update_controls(audio_dev_t * d)266 audio_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 */
282 int
audio_control_read(audio_ctrl_t * ctrl,uint64_t * value)283 audio_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 */
323 int
audio_control_write(audio_ctrl_t * ctrl,uint64_t value)324 audio_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 */
358 int
auimpl_save_controls(audio_dev_t * d)359 auimpl_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
386 int
auimpl_restore_controls(audio_dev_t * d)387 auimpl_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