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 #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 rw_enter(&d->d_ctrl_lock, RW_WRITER); 143 list_remove(&d->d_controls, ctrl); 144 rw_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 mutex_destroy(&ctrl->ctrl_lock); 152 } 153 new_desc = &ctrl->ctrl_des; 154 155 /* Fill in new control description */ 156 new_desc->acd_type = desc->acd_type; 157 new_desc->acd_flags = desc->acd_flags; 158 new_desc->acd_maxvalue = desc->acd_maxvalue; 159 new_desc->acd_minvalue = desc->acd_minvalue; 160 new_desc->acd_name = strdup(name); 161 162 /* Process type of control special actions, if any */ 163 switch (desc->acd_type) { 164 case AUDIO_CTRL_TYPE_BOOLEAN: 165 case AUDIO_CTRL_TYPE_STEREO: 166 case AUDIO_CTRL_TYPE_MONO: 167 case AUDIO_CTRL_TYPE_METER: 168 break; 169 170 case AUDIO_CTRL_TYPE_ENUM: 171 for (int bit = 0; bit < 64; bit++) { 172 if (((1U << bit) & desc->acd_maxvalue) == 0) 173 continue; 174 name = desc->acd_enum[bit]; 175 if (name == NULL) { 176 (void) snprintf(scratch, sizeof (scratch), 177 "bit%d", bit); 178 name = scratch; 179 } 180 new_desc->acd_enum[bit] = strdup(name); 181 } 182 break; 183 default: 184 audio_dev_warn(d, "bad control type %d for %s", 185 desc->acd_type, desc->acd_name); 186 goto ctrl_fail; 187 } 188 189 ctrl->ctrl_dev = d; 190 if (new_desc->acd_flags & AUDIO_CTRL_FLAG_READABLE) { 191 ASSERT(read_fn); 192 ctrl->ctrl_read_fn = read_fn; 193 ctrl->ctrl_arg = arg; 194 } 195 if (new_desc->acd_flags & AUDIO_CTRL_FLAG_WRITEABLE) { 196 ASSERT(write_fn); 197 ctrl->ctrl_write_fn = write_fn; 198 ctrl->ctrl_arg = arg; 199 } 200 201 mutex_init(&ctrl->ctrl_lock, NULL, MUTEX_DRIVER, NULL); 202 203 rw_enter(&d->d_ctrl_lock, RW_WRITER); 204 list_insert_tail(&d->d_controls, ctrl); 205 rw_exit(&d->d_ctrl_lock); 206 207 return (ctrl); 208 209 210 ctrl_fail: 211 if (ctrl) { 212 audio_control_freenames(ctrl); 213 kmem_free(ctrl, sizeof (*ctrl)); 214 } 215 return (NULL); 216 } 217 218 /* 219 * This will remove a control from my audio device. 220 * 221 * ctrl - The control will be removed. 222 */ 223 void 224 audio_dev_del_control(audio_ctrl_t *ctrl) 225 { 226 audio_dev_t *d; 227 228 /* Verify argument */ 229 ASSERT(ctrl); 230 d = ctrl->ctrl_dev; 231 ASSERT(d); 232 233 rw_enter(&d->d_ctrl_lock, RW_WRITER); 234 list_remove(&d->d_controls, ctrl); 235 rw_exit(&d->d_ctrl_lock); 236 237 mutex_destroy(&ctrl->ctrl_lock); 238 239 audio_control_freenames(ctrl); 240 kmem_free(ctrl, sizeof (*ctrl)); 241 } 242 243 int 244 audio_dev_add_soft_volume(audio_dev_t *d) 245 { 246 audio_ctrl_desc_t desc; 247 248 bzero(&desc, sizeof (desc)); 249 if (d->d_pcmvol_ctrl == NULL) { 250 desc.acd_name = AUDIO_CTRL_ID_VOLUME; 251 desc.acd_type = AUDIO_CTRL_TYPE_MONO; 252 desc.acd_minvalue = 0; 253 desc.acd_maxvalue = 100; 254 desc.acd_flags = AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_PLAY | 255 AUDIO_CTRL_FLAG_PCMVOL; 256 d->d_pcmvol_ctrl = audio_dev_add_control(d, &desc, 257 auimpl_get_pcmvol, auimpl_set_pcmvol, d); 258 d->d_pcmvol = 75; 259 } 260 return (0); 261 } 262 263 /* 264 * This will notify clients of need to reread control 265 * values since they have changed. 266 * 267 * There will be a routine that allows a client to register 268 * a callback. For now we just update the serial number. 269 * 270 * d - The device that needs updates. 271 */ 272 void 273 audio_dev_update_controls(audio_dev_t *d) 274 { 275 atomic_inc_uint(&d->d_serial); 276 } 277 278 279 /* 280 * This is used to read the current value of a control. 281 * Note, this will cause a callback into the driver to get the value. 282 * 283 * ctrl - should be the valid control being read. 284 * value - is a pointer to the place that will contain the value read. 285 * 286 * On return zero is returned on success else errno is returned. 287 * 288 */ 289 int 290 audio_control_read(audio_ctrl_t *ctrl, uint64_t *value) 291 { 292 uint64_t my_value; 293 int ret; 294 295 /* Verify arguments */ 296 ASSERT(ctrl); 297 ASSERT(value); 298 ASSERT(ctrl->ctrl_dev); 299 300 if (!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_READABLE)) { 301 return (ENXIO); 302 } 303 304 ASSERT(ctrl->ctrl_read_fn); 305 306 if ((ret = ctrl->ctrl_read_fn(ctrl->ctrl_arg, &my_value)) != 0) { 307 return (ret); 308 } 309 310 *value = my_value; 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 /* Verify arguments */ 332 ASSERT(ctrl); 333 ASSERT(d); 334 335 if (!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_WRITEABLE)) { 336 return (ENXIO); 337 } 338 339 ASSERT(ctrl->ctrl_write_fn); 340 341 ret = ctrl->ctrl_write_fn(ctrl->ctrl_arg, value); 342 343 if (ret == 0) 344 audio_dev_update_controls(d); 345 346 return (ret); 347 } 348