mixer.c revision 73127
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 73127 2001-02-27 07:01:49Z cg $ 27 */ 28 29#include <dev/sound/pcm/sound.h> 30 31#include "mixer_if.h" 32 33MALLOC_DEFINE(M_MIXER, "mixer", "mixer"); 34 35static u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = { 36 [SOUND_MIXER_VOLUME] = 75, 37 [SOUND_MIXER_BASS] = 50, 38 [SOUND_MIXER_TREBLE] = 50, 39 [SOUND_MIXER_SYNTH] = 75, 40 [SOUND_MIXER_PCM] = 75, 41 [SOUND_MIXER_SPEAKER] = 75, 42 [SOUND_MIXER_LINE] = 75, 43 [SOUND_MIXER_MIC] = 0, 44 [SOUND_MIXER_CD] = 75, 45 [SOUND_MIXER_LINE1] = 75, 46 [SOUND_MIXER_VIDEO] = 75, 47 [SOUND_MIXER_RECLEV] = 0, 48 [SOUND_MIXER_OGAIN] = 50, 49}; 50 51static char* snd_mixernames[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES; 52 53#ifdef SND_DYNSYSCTL 54static int 55mixer_lookup(char *devname) 56{ 57 int i; 58 59 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 60 if (strncmp(devname, snd_mixernames[i], 61 strlen(snd_mixernames[i])) == 0) 62 return i; 63 return -1; 64} 65#endif 66 67static int 68mixer_set(snd_mixer *mixer, unsigned dev, unsigned lev) 69{ 70 unsigned l, r; 71 int v; 72 73 if ((dev >= SOUND_MIXER_NRDEVICES) || (0 == (mixer->devs & (1 << dev)))) 74 return -1; 75 76 l = min((lev & 0x00ff), 100); 77 r = min(((lev & 0xff00) >> 8), 100); 78 79 v = MIXER_SET(mixer, dev, l, r); 80 if (v < 0) 81 return -1; 82 83 mixer->level[dev] = l | (r << 8); 84 return 0; 85} 86 87static int 88mixer_get(snd_mixer *mixer, int dev) 89{ 90 if ((dev < SOUND_MIXER_NRDEVICES) && (mixer->devs & (1 << dev))) 91 return mixer->level[dev]; 92 else return -1; 93} 94 95static int 96mixer_setrecsrc(snd_mixer *mixer, u_int32_t src) 97{ 98 src &= mixer->recdevs; 99 if (src == 0) 100 src = SOUND_MASK_MIC; 101 mixer->recsrc = MIXER_SETRECSRC(mixer, src); 102 return 0; 103} 104 105static int 106mixer_getrecsrc(snd_mixer *mixer) 107{ 108 return mixer->recsrc; 109} 110 111void 112mix_setdevs(snd_mixer *m, u_int32_t v) 113{ 114 m->devs = v; 115} 116 117void 118mix_setrecdevs(snd_mixer *m, u_int32_t v) 119{ 120 m->recdevs = v; 121} 122 123u_int32_t 124mix_getdevs(snd_mixer *m) 125{ 126 return m->devs; 127} 128 129u_int32_t 130mix_getrecdevs(snd_mixer *m) 131{ 132 return m->recdevs; 133} 134 135void * 136mix_getdevinfo(snd_mixer *m) 137{ 138 return m->devinfo; 139} 140 141int 142mixer_busy(snd_mixer *m, int busy) 143{ 144 m->busy = busy; 145 return 0; 146} 147 148int 149mixer_isbusy(snd_mixer *m) 150{ 151 return m->busy; 152} 153 154int 155mixer_init(device_t dev, kobj_class_t cls, void *devinfo) 156{ 157 snddev_info *d; 158 snd_mixer *m; 159 u_int16_t v; 160 int i; 161 162 d = device_get_softc(dev); 163 m = (snd_mixer *)kobj_create(cls, M_MIXER, M_WAITOK | M_ZERO); 164 165 m->name = cls->name; 166 m->devinfo = devinfo; 167 168 if (MIXER_INIT(m)) 169 goto bad; 170 171 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 172 v = snd_mixerdefaults[i]; 173 mixer_set(m, i, v | (v << 8)); 174 } 175 176 mixer_setrecsrc(m, SOUND_MASK_MIC); 177 178 d->mixer = m; 179 180 return 0; 181 182bad: kobj_delete((kobj_t)m, M_MIXER); 183 return -1; 184} 185 186int 187mixer_uninit(device_t dev) 188{ 189 int i; 190 snddev_info *d; 191 snd_mixer *m; 192 193 d = device_get_softc(dev); 194 m = d->mixer; 195 196 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 197 mixer_set(m, i, 0); 198 199 mixer_setrecsrc(m, SOUND_MASK_MIC); 200 201 MIXER_UNINIT(m); 202 203 kobj_delete((kobj_t)m, M_MIXER); 204 d->mixer = NULL; 205 206 return 0; 207} 208 209int 210mixer_reinit(device_t dev) 211{ 212 int i; 213 snddev_info *d; 214 snd_mixer *m; 215 216 d = device_get_softc(dev); 217 m = d->mixer; 218 219 i = MIXER_REINIT(m); 220 if (i) 221 return i; 222 223 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 224 mixer_set(m, i, m->level[i]); 225 226 mixer_setrecsrc(m, m->recsrc); 227 228 return 0; 229} 230 231int 232mixer_ioctl(snddev_info *d, u_long cmd, caddr_t arg) 233{ 234 int ret, *arg_i = (int *)arg; 235 int v = -1, j = cmd & 0xff; 236 snd_mixer *m; 237 238 m = d->mixer; 239 240 if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) { 241 if (j == SOUND_MIXER_RECSRC) 242 ret = mixer_setrecsrc(m, *arg_i); 243 else 244 ret = mixer_set(m, j, *arg_i); 245 return (ret == 0)? 0 : ENXIO; 246 } 247 248 if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) { 249 switch (j) { 250 case SOUND_MIXER_DEVMASK: 251 case SOUND_MIXER_CAPS: 252 case SOUND_MIXER_STEREODEVS: 253 v = mix_getdevs(m); 254 break; 255 256 case SOUND_MIXER_RECMASK: 257 v = mix_getrecdevs(m); 258 break; 259 260 case SOUND_MIXER_RECSRC: 261 v = mixer_getrecsrc(m); 262 break; 263 264 default: 265 v = mixer_get(m, j); 266 } 267 *arg_i = v; 268 return (v != -1)? 0 : ENXIO; 269 } 270 return ENXIO; 271} 272 273#ifdef SND_DYNSYSCTL 274static int 275sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS) 276{ 277 char devname[32]; 278 int error, dev; 279 snd_mixer *m; 280 281 m = oidp->oid_arg1; 282 strncpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname)); 283 error = sysctl_handle_string(oidp, &devname[0], sizeof(devname), req); 284 if (error == 0 && req->newptr != NULL) { 285 dev = mixer_lookup(devname); 286 if (dev == -1) 287 return EINVAL; 288 else if (dev != m->hwvol_mixer) { 289 m->hwvol_mixer = dev; 290 m->hwvol_muted = 0; 291 } 292 } 293 return error; 294} 295#endif 296 297int 298mixer_hwvol_init(device_t dev) 299{ 300 snddev_info *d; 301 snd_mixer *m; 302 303 d = device_get_softc(dev); 304 m = d->mixer; 305 m->hwvol_mixer = SOUND_MIXER_VOLUME; 306 m->hwvol_step = 5; 307#ifdef SND_DYNSYSCTL 308 SYSCTL_ADD_INT(&d->sysctl_tree, SYSCTL_CHILDREN(d->sysctl_tree_top), 309 OID_AUTO, "hwvol_step", CTLFLAG_RW, &m->hwvol_step, 0, ""); 310 SYSCTL_ADD_PROC(&d->sysctl_tree, SYSCTL_CHILDREN(d->sysctl_tree_top), 311 OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0, 312 sysctl_hw_snd_hwvol_mixer, "A", "") 313#endif 314 return 0; 315} 316 317void 318mixer_hwvol_mute(device_t dev) 319{ 320 snddev_info *d; 321 snd_mixer *m; 322 323 d = device_get_softc(dev); 324 m = d->mixer; 325 if (m->hwvol_muted) { 326 m->hwvol_muted = 0; 327 mixer_set(m, m->hwvol_mixer, m->hwvol_mute_level); 328 } else { 329 m->hwvol_muted++; 330 m->hwvol_mute_level = mixer_get(m, m->hwvol_mixer); 331 mixer_set(m, m->hwvol_mixer, 0); 332 } 333} 334 335void 336mixer_hwvol_step(device_t dev, int left_step, int right_step) 337{ 338 snddev_info *d; 339 snd_mixer *m; 340 int level, left, right; 341 342 d = device_get_softc(dev); 343 m = d->mixer; 344 if (m->hwvol_muted) { 345 m->hwvol_muted = 0; 346 level = m->hwvol_mute_level; 347 } else 348 level = mixer_get(m, m->hwvol_mixer); 349 if (level != -1) { 350 left = level & 0xff; 351 right = level >> 8; 352 left += left_step * m->hwvol_step; 353 if (left < 0) 354 left = 0; 355 right += right_step * m->hwvol_step; 356 if (right < 0) 357 right = 0; 358 mixer_set(m, m->hwvol_mixer, left | right << 8); 359 } 360} 361 362/* 363 * The various mixers use a variety of bitmasks etc. The Voxware 364 * driver had a very nice technique to describe a mixer and interface 365 * to it. A table defines, for each channel, which register, bits, 366 * offset, polarity to use. This procedure creates the new value 367 * using the table and the old value. 368 */ 369 370void 371change_bits(mixer_tab *t, u_char *regval, int dev, int chn, int newval) 372{ 373 u_char mask; 374 int shift; 375 376 DEB(printf("ch_bits dev %d ch %d val %d old 0x%02x " 377 "r %d p %d bit %d off %d\n", 378 dev, chn, newval, *regval, 379 (*t)[dev][chn].regno, (*t)[dev][chn].polarity, 380 (*t)[dev][chn].nbits, (*t)[dev][chn].bitoffs ) ); 381 382 if ( (*t)[dev][chn].polarity == 1) /* reverse */ 383 newval = 100 - newval ; 384 385 mask = (1 << (*t)[dev][chn].nbits) - 1; 386 newval = (int) ((newval * mask) + 50) / 100; /* Scale it */ 387 shift = (*t)[dev][chn].bitoffs /*- (*t)[dev][LEFT_CHN].nbits + 1*/; 388 389 *regval &= ~(mask << shift); /* Filter out the previous value */ 390 *regval |= (newval & mask) << shift; /* Set the new value */ 391} 392 393