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 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 * 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 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 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 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 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 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 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 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