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 2009 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 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 rw_enter(&d->d_ctrl_lock, RW_WRITER); 142 list_remove(&d->d_controls, ctrl); 143 rw_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 mutex_destroy(&ctrl->ctrl_lock); 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_init(&ctrl->ctrl_lock, NULL, MUTEX_DRIVER, NULL); 201 202 rw_enter(&d->d_ctrl_lock, RW_WRITER); 203 list_insert_tail(&d->d_controls, ctrl); 204 rw_exit(&d->d_ctrl_lock); 205 206 return (ctrl); 207 208 209 ctrl_fail: 210 if (ctrl) { 211 audio_control_freenames(ctrl); 212 kmem_free(ctrl, sizeof (*ctrl)); 213 } 214 return (NULL); 215 } 216 217 /* 218 * This will remove a control from my audio device. 219 * 220 * ctrl - The control will be removed. 221 */ 222 void 223 audio_dev_del_control(audio_ctrl_t *ctrl) 224 { 225 audio_dev_t *d; 226 227 /* Verify argument */ 228 ASSERT(ctrl); 229 d = ctrl->ctrl_dev; 230 ASSERT(d); 231 232 rw_enter(&d->d_ctrl_lock, RW_WRITER); 233 list_remove(&d->d_controls, ctrl); 234 rw_exit(&d->d_ctrl_lock); 235 236 mutex_destroy(&ctrl->ctrl_lock); 237 238 audio_control_freenames(ctrl); 239 kmem_free(ctrl, sizeof (*ctrl)); 240 } 241 242 static int 243 auimpl_set_pcmvol(void *arg, uint64_t val) 244 { 245 audio_dev_t *d = arg; 246 list_t *l = &d->d_clients; 247 audio_client_t *c; 248 249 if (val > 100) { 250 return (EINVAL); 251 } 252 rw_enter(&d->d_clnt_lock, RW_WRITER); 253 d->d_pcmvol = val & 0xff; 254 for (c = list_head(l); c; c = list_next(l, c)) { 255 auimpl_set_gain_master(&c->c_ostream, (uint8_t)val); 256 } 257 rw_exit(&d->d_clnt_lock); 258 return (0); 259 } 260 261 static int 262 auimpl_get_pcmvol(void *arg, uint64_t *val) 263 { 264 audio_dev_t *d = arg; 265 266 *val = d->d_pcmvol; 267 return (0); 268 } 269 270 int 271 audio_dev_add_soft_volume(audio_dev_t *d) 272 { 273 audio_ctrl_desc_t desc; 274 275 bzero(&desc, sizeof (desc)); 276 if (d->d_pcmvol_ctrl == NULL) { 277 desc.acd_name = AUDIO_CTRL_ID_VOLUME; 278 desc.acd_type = AUDIO_CTRL_TYPE_MONO; 279 desc.acd_minvalue = 0; 280 desc.acd_maxvalue = 100; 281 desc.acd_flags = AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_PLAY | 282 AUDIO_CTRL_FLAG_PCMVOL; 283 d->d_pcmvol_ctrl = audio_dev_add_control(d, &desc, 284 auimpl_get_pcmvol, auimpl_set_pcmvol, d); 285 d->d_pcmvol = 75; 286 } 287 return (0); 288 } 289 290 /* 291 * This will notify clients of need to reread control 292 * values since they have changed. 293 * 294 * There will be a routine that allows a client to register 295 * a callback. All callbacks registered to this device should 296 * get called from this routine. 297 * 298 * d - The device that needs updates. 299 */ 300 void 301 audio_dev_update_controls(audio_dev_t *d) 302 { 303 auclnt_notify_dev(d); 304 } 305 306 307 /* 308 * This is used to read the current value of a control. 309 * Note, this will cause a callback into the driver to get the value. 310 * 311 * ctrl - should be the valid control being read. 312 * value - is a pointer to the place that will contain the value read. 313 * 314 * On return zero is returned on success else errno is returned. 315 * 316 */ 317 int 318 audio_control_read(audio_ctrl_t *ctrl, uint64_t *value) 319 { 320 uint64_t my_value; 321 int ret; 322 323 /* Verify arguments */ 324 ASSERT(ctrl); 325 ASSERT(value); 326 ASSERT(ctrl->ctrl_dev); 327 328 if (!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_READABLE)) { 329 return (ENXIO); 330 } 331 332 ASSERT(ctrl->ctrl_read_fn); 333 334 if ((ret = ctrl->ctrl_read_fn(ctrl->ctrl_arg, &my_value)) != 0) { 335 return (ret); 336 } 337 338 *value = my_value; 339 340 return (ret); 341 } 342 343 /* 344 * This is used to write a value to a control. 345 * Note, this will cause a callback into the driver to write the value. 346 * 347 * ctrl - should be the valid control being written. 348 * value - is value to set the control to. 349 * 350 * On return zero is returned on success else errno is returned. 351 * 352 */ 353 int 354 audio_control_write(audio_ctrl_t *ctrl, uint64_t value) 355 { 356 int ret; 357 audio_dev_t *d = ctrl->ctrl_dev; 358 359 /* Verify arguments */ 360 ASSERT(ctrl); 361 ASSERT(d); 362 363 if (!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_WRITEABLE)) { 364 return (ENXIO); 365 } 366 367 ASSERT(ctrl->ctrl_write_fn); 368 369 ret = ctrl->ctrl_write_fn(ctrl->ctrl_arg, value); 370 371 if (ret == 0) 372 audio_dev_update_controls(d); 373 374 return (ret); 375 } 376