mixer.c revision 74763
1/* 2 * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: head/sys/dev/sound/pcm/mixer.c 74763 2001-03-24 23:10:29Z cg $ 27 */ 28 29#include <dev/sound/pcm/sound.h> 30 31#include "mixer_if.h" 32 33MALLOC_DEFINE(M_MIXER, "mixer", "mixer"); 34 35#define MIXER_NAMELEN 16 36struct snd_mixer { 37 KOBJ_FIELDS; 38 const char *type; 39 void *devinfo; 40 int busy; 41 int hwvol_muted; 42 int hwvol_mixer; 43 int hwvol_step; 44 u_int32_t hwvol_mute_level; 45 u_int32_t devs; 46 u_int32_t recdevs; 47 u_int32_t recsrc; 48 u_int16_t level[32]; 49 char name[MIXER_NAMELEN]; 50 void *lock; 51}; 52 53static u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = { 54 [SOUND_MIXER_VOLUME] = 75, 55 [SOUND_MIXER_BASS] = 50, 56 [SOUND_MIXER_TREBLE] = 50, 57 [SOUND_MIXER_SYNTH] = 75, 58 [SOUND_MIXER_PCM] = 75, 59 [SOUND_MIXER_SPEAKER] = 75, 60 [SOUND_MIXER_LINE] = 75, 61 [SOUND_MIXER_MIC] = 0, 62 [SOUND_MIXER_CD] = 75, 63 [SOUND_MIXER_LINE1] = 75, 64 [SOUND_MIXER_VIDEO] = 75, 65 [SOUND_MIXER_RECLEV] = 0, 66 [SOUND_MIXER_OGAIN] = 50, 67}; 68 69static char* snd_mixernames[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES; 70 71#ifdef SND_DYNSYSCTL 72static int 73mixer_lookup(char *devname) 74{ 75 int i; 76 77 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 78 if (strncmp(devname, snd_mixernames[i], 79 strlen(snd_mixernames[i])) == 0) 80 return i; 81 return -1; 82} 83#endif 84 85static int 86mixer_set(struct snd_mixer *mixer, unsigned dev, unsigned lev) 87{ 88 unsigned l, r; 89 int v; 90 91 if ((dev >= SOUND_MIXER_NRDEVICES) || (0 == (mixer->devs & (1 << dev)))) 92 return -1; 93 94 l = min((lev & 0x00ff), 100); 95 r = min(((lev & 0xff00) >> 8), 100); 96 97 v = MIXER_SET(mixer, dev, l, r); 98 if (v < 0) 99 return -1; 100 101 mixer->level[dev] = l | (r << 8); 102 return 0; 103} 104 105static int 106mixer_get(struct snd_mixer *mixer, int dev) 107{ 108 if ((dev < SOUND_MIXER_NRDEVICES) && (mixer->devs & (1 << dev))) 109 return mixer->level[dev]; 110 else return -1; 111} 112 113static int 114mixer_setrecsrc(struct snd_mixer *mixer, u_int32_t src) 115{ 116 src &= mixer->recdevs; 117 if (src == 0) 118 src = SOUND_MASK_MIC; 119 mixer->recsrc = MIXER_SETRECSRC(mixer, src); 120 return 0; 121} 122 123static int 124mixer_getrecsrc(struct snd_mixer *mixer) 125{ 126 return mixer->recsrc; 127} 128 129void 130mix_setdevs(struct snd_mixer *m, u_int32_t v) 131{ 132 m->devs = v; 133} 134 135void 136mix_setrecdevs(struct snd_mixer *m, u_int32_t v) 137{ 138 m->recdevs = v; 139} 140 141u_int32_t 142mix_getdevs(struct snd_mixer *m) 143{ 144 return m->devs; 145} 146 147u_int32_t 148mix_getrecdevs(struct snd_mixer *m) 149{ 150 return m->recdevs; 151} 152 153void * 154mix_getdevinfo(struct snd_mixer *m) 155{ 156 return m->devinfo; 157} 158 159int 160mixer_busy(struct snd_mixer *m, int busy) 161{ 162 m->busy = busy; 163 return 0; 164} 165 166int 167mixer_isbusy(struct snd_mixer *m) 168{ 169 return m->busy; 170} 171 172int 173mixer_init(device_t dev, kobj_class_t cls, void *devinfo) 174{ 175 struct snddev_info *d; 176 struct snd_mixer *m; 177 u_int16_t v; 178 int i; 179 180 m = (struct snd_mixer *)kobj_create(cls, M_MIXER, M_WAITOK | M_ZERO); 181 snprintf(m->name, MIXER_NAMELEN, "%s:mixer", device_get_nameunit(dev)); 182 m->lock = snd_mtxcreate(m->name); 183 m->type = cls->name; 184 m->devinfo = devinfo; 185 186 if (MIXER_INIT(m)) 187 goto bad; 188 189 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 190 v = snd_mixerdefaults[i]; 191 mixer_set(m, i, v | (v << 8)); 192 } 193 194 mixer_setrecsrc(m, SOUND_MASK_MIC); 195 196 d = device_get_softc(dev); 197 d->mixer = m; 198 199 return 0; 200 201bad: 202 snd_mtxlock(m->lock); 203 snd_mtxfree(m->lock); 204 kobj_delete((kobj_t)m, M_MIXER); 205 return -1; 206} 207 208int 209mixer_uninit(device_t dev) 210{ 211 int i; 212 struct snddev_info *d; 213 struct snd_mixer *m; 214 215 d = device_get_softc(dev); 216 m = d->mixer; 217 snd_mtxlock(m->lock); 218 219 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 220 mixer_set(m, i, 0); 221 222 mixer_setrecsrc(m, SOUND_MASK_MIC); 223 224 MIXER_UNINIT(m); 225 226 snd_mtxfree(m->lock); 227 kobj_delete((kobj_t)m, M_MIXER); 228 d->mixer = NULL; 229 230 return 0; 231} 232 233int 234mixer_reinit(device_t dev) 235{ 236 int i; 237 struct snddev_info *d; 238 struct snd_mixer *m; 239 240 d = device_get_softc(dev); 241 m = d->mixer; 242 snd_mtxlock(m->lock); 243 244 i = MIXER_REINIT(m); 245 if (i) { 246 snd_mtxunlock(m->lock); 247 return i; 248 } 249 250 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 251 mixer_set(m, i, m->level[i]); 252 253 mixer_setrecsrc(m, m->recsrc); 254 snd_mtxunlock(m->lock); 255 256 return 0; 257} 258 259int 260mixer_ioctl(struct snddev_info *d, u_long cmd, caddr_t arg) 261{ 262 int ret, *arg_i = (int *)arg; 263 int v = -1, j = cmd & 0xff; 264 struct snd_mixer *m; 265 266 m = d->mixer; 267 268 snd_mtxlock(m->lock); 269 if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) { 270 if (j == SOUND_MIXER_RECSRC) 271 ret = mixer_setrecsrc(m, *arg_i); 272 else 273 ret = mixer_set(m, j, *arg_i); 274 snd_mtxunlock(m->lock); 275 return (ret == 0)? 0 : ENXIO; 276 } 277 278 if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) { 279 switch (j) { 280 case SOUND_MIXER_DEVMASK: 281 case SOUND_MIXER_CAPS: 282 case SOUND_MIXER_STEREODEVS: 283 v = mix_getdevs(m); 284 break; 285 286 case SOUND_MIXER_RECMASK: 287 v = mix_getrecdevs(m); 288 break; 289 290 case SOUND_MIXER_RECSRC: 291 v = mixer_getrecsrc(m); 292 break; 293 294 default: 295 v = mixer_get(m, j); 296 } 297 *arg_i = v; 298 snd_mtxunlock(m->lock); 299 return (v != -1)? 0 : ENXIO; 300 } 301 snd_mtxunlock(m->lock); 302 return ENXIO; 303} 304 305#ifdef SND_DYNSYSCTL 306static int 307sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS) 308{ 309 char devname[32]; 310 int error, dev; 311 struct snd_mixer *m; 312 313 m = oidp->oid_arg1; 314 snd_mtxlock(m->lock); 315 strncpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname)); 316 error = sysctl_handle_string(oidp, &devname[0], sizeof(devname), req); 317 if (error == 0 && req->newptr != NULL) { 318 dev = mixer_lookup(devname); 319 if (dev == -1) { 320 snd_mtxunlock(m->lock); 321 return EINVAL; 322 } 323 else if (dev != m->hwvol_mixer) { 324 m->hwvol_mixer = dev; 325 m->hwvol_muted = 0; 326 } 327 } 328 snd_mtxunlock(m->lock); 329 return error; 330} 331#endif 332 333int 334mixer_hwvol_init(device_t dev) 335{ 336 struct snddev_info *d; 337 struct snd_mixer *m; 338 339 d = device_get_softc(dev); 340 m = d->mixer; 341 snd_mtxlock(m->lock); 342 m->hwvol_mixer = SOUND_MIXER_VOLUME; 343 m->hwvol_step = 5; 344#ifdef SND_DYNSYSCTL 345 SYSCTL_ADD_INT(&d->sysctl_tree, SYSCTL_CHILDREN(d->sysctl_tree_top), 346 OID_AUTO, "hwvol_step", CTLFLAG_RW, &m->hwvol_step, 0, ""); 347 SYSCTL_ADD_PROC(&d->sysctl_tree, SYSCTL_CHILDREN(d->sysctl_tree_top), 348 OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0, 349 sysctl_hw_snd_hwvol_mixer, "A", "") 350#endif 351 snd_mtxunlock(m->lock); 352 return 0; 353} 354 355void 356mixer_hwvol_mute(device_t dev) 357{ 358 struct snddev_info *d; 359 struct snd_mixer *m; 360 361 d = device_get_softc(dev); 362 m = d->mixer; 363 snd_mtxlock(m->lock); 364 if (m->hwvol_muted) { 365 m->hwvol_muted = 0; 366 mixer_set(m, m->hwvol_mixer, m->hwvol_mute_level); 367 } else { 368 m->hwvol_muted++; 369 m->hwvol_mute_level = mixer_get(m, m->hwvol_mixer); 370 mixer_set(m, m->hwvol_mixer, 0); 371 } 372 snd_mtxunlock(m->lock); 373} 374 375void 376mixer_hwvol_step(device_t dev, int left_step, int right_step) 377{ 378 struct snddev_info *d; 379 struct snd_mixer *m; 380 int level, left, right; 381 382 d = device_get_softc(dev); 383 m = d->mixer; 384 snd_mtxlock(m->lock); 385 if (m->hwvol_muted) { 386 m->hwvol_muted = 0; 387 level = m->hwvol_mute_level; 388 } else 389 level = mixer_get(m, m->hwvol_mixer); 390 if (level != -1) { 391 left = level & 0xff; 392 right = level >> 8; 393 left += left_step * m->hwvol_step; 394 if (left < 0) 395 left = 0; 396 right += right_step * m->hwvol_step; 397 if (right < 0) 398 right = 0; 399 mixer_set(m, m->hwvol_mixer, left | right << 8); 400 } 401 snd_mtxunlock(m->lock); 402} 403 404 405