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 int 243 audio_dev_add_soft_volume(audio_dev_t *d) 244 { 245 audio_ctrl_desc_t desc; 246 247 bzero(&desc, sizeof (desc)); 248 if (d->d_pcmvol_ctrl == NULL) { 249 desc.acd_name = AUDIO_CTRL_ID_VOLUME; 250 desc.acd_type = AUDIO_CTRL_TYPE_MONO; 251 desc.acd_minvalue = 0; 252 desc.acd_maxvalue = 100; 253 desc.acd_flags = AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_PLAY | 254 AUDIO_CTRL_FLAG_PCMVOL; 255 d->d_pcmvol_ctrl = audio_dev_add_control(d, &desc, 256 auimpl_get_pcmvol, auimpl_set_pcmvol, d); 257 d->d_pcmvol = 75; 258 } 259 return (0); 260 } 261 262 /* 263 * This will notify clients of need to reread control 264 * values since they have changed. 265 * 266 * There will be a routine that allows a client to register 267 * a callback. All callbacks registered to this device should 268 * get called from this routine. 269 * 270 * d - The device that needs updates. 271 */ 272 void 273 audio_dev_update_controls(audio_dev_t *d) 274 { 275 auclnt_notify_dev(d); 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