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 2010 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 mutex_enter(&d->d_ctrl_lock); 143 list_remove(&d->d_controls, ctrl); 144 mutex_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 } 152 new_desc = &ctrl->ctrl_des; 153 154 /* Fill in new control description */ 155 new_desc->acd_type = desc->acd_type; 156 new_desc->acd_flags = desc->acd_flags; 157 new_desc->acd_maxvalue = desc->acd_maxvalue; 158 new_desc->acd_minvalue = desc->acd_minvalue; 159 new_desc->acd_name = strdup(name); 160 161 /* Process type of control special actions, if any */ 162 switch (desc->acd_type) { 163 case AUDIO_CTRL_TYPE_BOOLEAN: 164 case AUDIO_CTRL_TYPE_STEREO: 165 case AUDIO_CTRL_TYPE_MONO: 166 case AUDIO_CTRL_TYPE_METER: 167 break; 168 169 case AUDIO_CTRL_TYPE_ENUM: 170 for (int bit = 0; bit < 64; bit++) { 171 if (((1U << bit) & desc->acd_maxvalue) == 0) 172 continue; 173 name = desc->acd_enum[bit]; 174 if (name == NULL) { 175 (void) snprintf(scratch, sizeof (scratch), 176 "bit%d", bit); 177 name = scratch; 178 } 179 new_desc->acd_enum[bit] = strdup(name); 180 } 181 break; 182 default: 183 audio_dev_warn(d, "bad control type %d for %s", 184 desc->acd_type, desc->acd_name); 185 goto ctrl_fail; 186 } 187 188 ctrl->ctrl_dev = d; 189 if (new_desc->acd_flags & AUDIO_CTRL_FLAG_READABLE) { 190 ASSERT(read_fn); 191 ctrl->ctrl_read_fn = read_fn; 192 ctrl->ctrl_arg = arg; 193 } 194 if (new_desc->acd_flags & AUDIO_CTRL_FLAG_WRITEABLE) { 195 ASSERT(write_fn); 196 ctrl->ctrl_write_fn = write_fn; 197 ctrl->ctrl_arg = arg; 198 } 199 200 mutex_enter(&d->d_ctrl_lock); 201 list_insert_tail(&d->d_controls, ctrl); 202 mutex_exit(&d->d_ctrl_lock); 203 204 return (ctrl); 205 206 207 ctrl_fail: 208 if (ctrl) { 209 audio_control_freenames(ctrl); 210 kmem_free(ctrl, sizeof (*ctrl)); 211 } 212 return (NULL); 213 } 214 215 /* 216 * This will remove a control from my audio device. 217 * 218 * ctrl - The control will be removed. 219 */ 220 void 221 audio_dev_del_control(audio_ctrl_t *ctrl) 222 { 223 audio_dev_t *d; 224 225 /* Verify argument */ 226 ASSERT(ctrl); 227 d = ctrl->ctrl_dev; 228 ASSERT(d); 229 230 mutex_enter(&d->d_ctrl_lock); 231 list_remove(&d->d_controls, ctrl); 232 mutex_exit(&d->d_ctrl_lock); 233 234 audio_control_freenames(ctrl); 235 kmem_free(ctrl, sizeof (*ctrl)); 236 } 237 238 int 239 audio_dev_add_soft_volume(audio_dev_t *d) 240 { 241 audio_ctrl_desc_t desc; 242 243 bzero(&desc, sizeof (desc)); 244 if (d->d_pcmvol_ctrl == NULL) { 245 desc.acd_name = AUDIO_CTRL_ID_VOLUME; 246 desc.acd_type = AUDIO_CTRL_TYPE_MONO; 247 desc.acd_minvalue = 0; 248 desc.acd_maxvalue = 100; 249 desc.acd_flags = AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_PLAY | 250 AUDIO_CTRL_FLAG_PCMVOL; 251 d->d_pcmvol_ctrl = audio_dev_add_control(d, &desc, 252 auimpl_get_pcmvol, auimpl_set_pcmvol, d); 253 d->d_pcmvol = 75; 254 } 255 return (0); 256 } 257 258 /* 259 * This will notify clients of need to reread control 260 * values since they have changed. 261 * 262 * There will be a routine that allows a client to register 263 * a callback. For now we just update the serial number. 264 * 265 * d - The device that needs updates. 266 */ 267 void 268 audio_dev_update_controls(audio_dev_t *d) 269 { 270 atomic_inc_uint(&d->d_serial); 271 } 272 273 274 /* 275 * This is used to read the current value of a control. 276 * Note, this will cause a callback into the driver to get the value. 277 * 278 * ctrl - should be the valid control being read. 279 * value - is a pointer to the place that will contain the value read. 280 * 281 * On return zero is returned on success else errno is returned. 282 * 283 */ 284 int 285 audio_control_read(audio_ctrl_t *ctrl, uint64_t *value) 286 { 287 audio_dev_t *d = ctrl->ctrl_dev; 288 uint64_t my_value; 289 int ret; 290 291 ASSERT(value); 292 293 mutex_enter(&d->d_ctrl_lock); 294 while (d->d_suspended) { 295 cv_wait(&d->d_ctrl_cv, &d->d_ctrl_lock); 296 } 297 298 if (!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_READABLE)) { 299 mutex_exit(&d->d_ctrl_lock); 300 return (ENXIO); 301 } 302 303 ASSERT(ctrl->ctrl_read_fn); 304 305 ret = ctrl->ctrl_read_fn(ctrl->ctrl_arg, &my_value); 306 mutex_exit(&d->d_ctrl_lock); 307 308 if (ret == 0) { 309 *value = my_value; 310 } 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 mutex_enter(&d->d_ctrl_lock); 332 while (d->d_suspended) { 333 cv_wait(&d->d_ctrl_cv, &d->d_ctrl_lock); 334 } 335 336 if (!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_WRITEABLE)) { 337 mutex_exit(&d->d_ctrl_lock); 338 return (ENXIO); 339 } 340 341 ASSERT(ctrl->ctrl_write_fn); 342 343 ret = ctrl->ctrl_write_fn(ctrl->ctrl_arg, value); 344 if (ret == 0) { 345 ctrl->ctrl_saved = value; 346 ctrl->ctrl_saved_ok = B_TRUE; 347 } 348 mutex_exit(&d->d_ctrl_lock); 349 350 if (ret == 0) { 351 audio_dev_update_controls(d); 352 } 353 354 return (ret); 355 } 356 357 /* 358 * This is used to save control values. 359 */ 360 int 361 auimpl_save_controls(audio_dev_t *d) 362 { 363 audio_ctrl_t *ctrl; 364 list_t *l; 365 int ret; 366 367 ASSERT(mutex_owned(&d->d_ctrl_lock)); 368 l = &d->d_controls; 369 370 for (ctrl = list_head(l); ctrl; ctrl = list_next(l, ctrl)) { 371 if ((!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_WRITEABLE)) || 372 (!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_WRITEABLE))) { 373 continue; 374 } 375 ret = ctrl->ctrl_read_fn(ctrl->ctrl_arg, &ctrl->ctrl_saved); 376 if (ret != 0) { 377 audio_dev_warn(d, 378 "Unable to save value of control %s", 379 ctrl->ctrl_name); 380 return (ret); 381 } else { 382 ctrl->ctrl_saved_ok = B_TRUE; 383 } 384 } 385 return (0); 386 } 387 388 int 389 auimpl_restore_controls(audio_dev_t *d) 390 { 391 audio_ctrl_t *ctrl; 392 list_t *l; 393 int ret; 394 int rv = 0; 395 396 ASSERT(mutex_owned(&d->d_ctrl_lock)); 397 l = &d->d_controls; 398 399 for (ctrl = list_head(l); ctrl; ctrl = list_next(l, ctrl)) { 400 if (!ctrl->ctrl_saved_ok) { 401 continue; 402 } 403 ret = ctrl->ctrl_write_fn(ctrl->ctrl_arg, ctrl->ctrl_saved); 404 if (ret != 0) { 405 audio_dev_warn(d, 406 "Unable to restore value of control %s", 407 ctrl->ctrl_name); 408 rv = ret; 409 } 410 } 411 return (rv); 412 } 413