mixer.c revision 168247
1139749Simp/*- 2119853Scg * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> 350724Scg * All rights reserved. 450724Scg * 550724Scg * Redistribution and use in source and binary forms, with or without 650724Scg * modification, are permitted provided that the following conditions 750724Scg * are met: 850724Scg * 1. Redistributions of source code must retain the above copyright 950724Scg * notice, this list of conditions and the following disclaimer. 1050724Scg * 2. Redistributions in binary form must reproduce the above copyright 1150724Scg * notice, this list of conditions and the following disclaimer in the 1250724Scg * documentation and/or other materials provided with the distribution. 1350724Scg * 1450724Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1550724Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1650724Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1750724Scg * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1850724Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1950724Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2050724Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2150724Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2250724Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2350724Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2450724Scg * SUCH DAMAGE. 2550724Scg */ 2650724Scg 2753465Scg#include <dev/sound/pcm/sound.h> 2850724Scg 2970134Scg#include "mixer_if.h" 3070134Scg 3182180ScgSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/mixer.c 168247 2007-04-02 03:46:25Z ariff $"); 3282180Scg 3370134ScgMALLOC_DEFINE(M_MIXER, "mixer", "mixer"); 3470134Scg 3574763Scg#define MIXER_NAMELEN 16 3674763Scgstruct snd_mixer { 3774763Scg KOBJ_FIELDS; 3874763Scg const char *type; 3974763Scg void *devinfo; 40168247Sariff int busy:1; 4174763Scg int hwvol_muted; 4274763Scg int hwvol_mixer; 4374763Scg int hwvol_step; 44150825Snetchild device_t dev; 4574763Scg u_int32_t hwvol_mute_level; 4674763Scg u_int32_t devs; 4774763Scg u_int32_t recdevs; 4874763Scg u_int32_t recsrc; 4974763Scg u_int16_t level[32]; 50162828Sariff u_int8_t parent[32]; 51162738Sariff u_int32_t child[32]; 52162828Sariff u_int8_t realdev[32]; 5374763Scg char name[MIXER_NAMELEN]; 54107285Scg struct mtx *lock; 55162588Snetchild oss_mixer_enuminfo enuminfo; 56162588Snetchild /** 57162588Snetchild * Counter is incremented when applications change any of this 58162588Snetchild * mixer's controls. A change in value indicates that persistent 59162588Snetchild * mixer applications should update their displays. 60162588Snetchild */ 61162588Snetchild int modify_counter; 6274763Scg}; 6374763Scg 6450724Scgstatic u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = { 6550724Scg [SOUND_MIXER_VOLUME] = 75, 6650724Scg [SOUND_MIXER_BASS] = 50, 6750724Scg [SOUND_MIXER_TREBLE] = 50, 6862947Stanimura [SOUND_MIXER_SYNTH] = 75, 6950724Scg [SOUND_MIXER_PCM] = 75, 7050724Scg [SOUND_MIXER_SPEAKER] = 75, 7150724Scg [SOUND_MIXER_LINE] = 75, 7250724Scg [SOUND_MIXER_MIC] = 0, 7350724Scg [SOUND_MIXER_CD] = 75, 74152422Sariff [SOUND_MIXER_IGAIN] = 0, 7550724Scg [SOUND_MIXER_LINE1] = 75, 7650724Scg [SOUND_MIXER_VIDEO] = 75, 7750724Scg [SOUND_MIXER_RECLEV] = 0, 7853203Scg [SOUND_MIXER_OGAIN] = 50, 7979044Scg [SOUND_MIXER_MONITOR] = 75, 8050724Scg}; 8150724Scg 8270680Sjhbstatic char* snd_mixernames[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES; 8370680Sjhb 8478362Scgstatic d_open_t mixer_open; 8578362Scgstatic d_close_t mixer_close; 8678362Scg 8778362Scgstatic struct cdevsw mixer_cdevsw = { 88126080Sphk .d_version = D_VERSION, 89168247Sariff .d_flags = D_NEEDGIANT, 90111815Sphk .d_open = mixer_open, 91111815Sphk .d_close = mixer_close, 92111815Sphk .d_ioctl = mixer_ioctl, 93111815Sphk .d_name = "mixer", 9478362Scg}; 9578362Scg 96162588Snetchild/** 97162588Snetchild * Keeps a count of mixer devices; used only by OSSv4 SNDCTL_SYSINFO ioctl. 98162588Snetchild */ 99162588Snetchildint mixer_count = 0; 100162588Snetchild 10178670Scg#ifdef USING_DEVFS 10278362Scgstatic eventhandler_tag mixer_ehtag; 10378670Scg#endif 10478362Scg 105130585Sphkstatic struct cdev * 10678362Scgmixer_get_devt(device_t dev) 10778362Scg{ 108124617Sphk struct snddev_info *snddev; 10978362Scg 110124617Sphk snddev = device_get_softc(dev); 11178362Scg 112124617Sphk return snddev->mixer_dev; 11378362Scg} 11478362Scg 11573127Scg#ifdef SND_DYNSYSCTL 11665390Speterstatic int 11770680Sjhbmixer_lookup(char *devname) 11870680Sjhb{ 11970680Sjhb int i; 12070680Sjhb 12170680Sjhb for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 12270680Sjhb if (strncmp(devname, snd_mixernames[i], 12370680Sjhb strlen(snd_mixernames[i])) == 0) 12470680Sjhb return i; 12570680Sjhb return -1; 12670680Sjhb} 12773127Scg#endif 12870680Sjhb 12970680Sjhbstatic int 130162738Sariffmixer_set_softpcmvol(struct snd_mixer *mixer, struct snddev_info *d, 131162738Sariff unsigned left, unsigned right) 13265390Speter{ 133162738Sariff struct snddev_channel *sce; 134162738Sariff struct pcm_channel *ch; 135162738Sariff#ifdef USING_MUTEX 136162738Sariff int locked = (mixer->lock && mtx_owned((struct mtx *)(mixer->lock))) ? 1 : 0; 137162738Sariff 138162738Sariff if (locked) 139162738Sariff snd_mtxunlock(mixer->lock); 140162738Sariff#endif 141162738Sariff SLIST_FOREACH(sce, &d->channels, link) { 142162738Sariff ch = sce->channel; 143162738Sariff CHN_LOCK(ch); 144162738Sariff if (ch->direction == PCMDIR_PLAY && 145162738Sariff (ch->feederflags & (1 << FEEDER_VOLUME))) 146162738Sariff chn_setvolume(ch, left, right); 147162738Sariff CHN_UNLOCK(ch); 148162738Sariff } 149162738Sariff#ifdef USING_MUTEX 150162738Sariff if (locked) 151162738Sariff snd_mtxlock(mixer->lock); 152162738Sariff#endif 153162738Sariff return 0; 154162738Sariff} 155162738Sariff 156162738Sariffstatic int 157162738Sariffmixer_set(struct snd_mixer *m, unsigned dev, unsigned lev) 158162738Sariff{ 159150825Snetchild struct snddev_info *d; 160162738Sariff unsigned l, r, tl, tr; 161162738Sariff u_int32_t parent = SOUND_MIXER_NONE, child = 0; 162162738Sariff u_int32_t realdev; 163162738Sariff int i; 16470134Scg 165162738Sariff if (m == NULL || dev >= SOUND_MIXER_NRDEVICES || 166162738Sariff (0 == (m->devs & (1 << dev)))) 16770134Scg return -1; 16870134Scg 16970134Scg l = min((lev & 0x00ff), 100); 17070134Scg r = min(((lev & 0xff00) >> 8), 100); 171162738Sariff realdev = m->realdev[dev]; 17270134Scg 173162738Sariff d = device_get_softc(m->dev); 174162738Sariff if (d == NULL) 175162738Sariff return -1; 17670134Scg 177162738Sariff /* TODO: recursive handling */ 178162738Sariff parent = m->parent[dev]; 179162738Sariff if (parent >= SOUND_MIXER_NRDEVICES) 180162738Sariff parent = SOUND_MIXER_NONE; 181162738Sariff if (parent == SOUND_MIXER_NONE) 182162738Sariff child = m->child[dev]; 183162738Sariff 184162738Sariff if (parent != SOUND_MIXER_NONE) { 185162738Sariff tl = (l * (m->level[parent] & 0x00ff)) / 100; 186162738Sariff tr = (r * ((m->level[parent] & 0xff00) >> 8)) / 100; 187162738Sariff if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) 188162738Sariff mixer_set_softpcmvol(m, d, tl, tr); 189162738Sariff else if (realdev != SOUND_MIXER_NONE && 190162738Sariff MIXER_SET(m, realdev, tl, tr) < 0) 191162738Sariff return -1; 192162738Sariff } else if (child != 0) { 193162738Sariff for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 194162738Sariff if (!(child & (1 << i)) || m->parent[i] != dev) 195162738Sariff continue; 196162738Sariff realdev = m->realdev[i]; 197162738Sariff tl = (l * (m->level[i] & 0x00ff)) / 100; 198162738Sariff tr = (r * ((m->level[i] & 0xff00) >> 8)) / 100; 199162738Sariff if (i == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) 200162738Sariff mixer_set_softpcmvol(m, d, tl, tr); 201162738Sariff else if (realdev != SOUND_MIXER_NONE) 202162738Sariff MIXER_SET(m, realdev, tl, tr); 203150825Snetchild } 204162738Sariff realdev = m->realdev[dev]; 205162738Sariff if (realdev != SOUND_MIXER_NONE && 206162738Sariff MIXER_SET(m, realdev, l, r) < 0) 207162738Sariff return -1; 208150825Snetchild } else { 209162738Sariff if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) 210162738Sariff mixer_set_softpcmvol(m, d, l, r); 211162738Sariff else if (realdev != SOUND_MIXER_NONE && 212162738Sariff MIXER_SET(m, realdev, l, r) < 0) 213150825Snetchild return -1; 214150825Snetchild } 215150825Snetchild 216162738Sariff m->level[dev] = l | (r << 8); 217162738Sariff 21870134Scg return 0; 21965390Speter} 22065390Speter 22165390Speterstatic int 22274763Scgmixer_get(struct snd_mixer *mixer, int dev) 22365390Speter{ 22470134Scg if ((dev < SOUND_MIXER_NRDEVICES) && (mixer->devs & (1 << dev))) 22570134Scg return mixer->level[dev]; 22665390Speter else return -1; 22765390Speter} 22865390Speter 22965390Speterstatic int 23074763Scgmixer_setrecsrc(struct snd_mixer *mixer, u_int32_t src) 23165390Speter{ 23270134Scg src &= mixer->recdevs; 23370134Scg if (src == 0) 23470134Scg src = SOUND_MASK_MIC; 23570134Scg mixer->recsrc = MIXER_SETRECSRC(mixer, src); 23665390Speter return 0; 23765390Speter} 23865390Speter 23965390Speterstatic int 24074763Scgmixer_getrecsrc(struct snd_mixer *mixer) 24165390Speter{ 24270134Scg return mixer->recsrc; 24365390Speter} 24465390Speter 245162588Snetchild/** 246162588Snetchild * @brief Retrieve the route number of the current recording device 247162588Snetchild * 248162588Snetchild * OSSv4 assigns routing numbers to recording devices, unlike the previous 249162588Snetchild * API which relied on a fixed table of device numbers and names. This 250162588Snetchild * function returns the routing number of the device currently selected 251162588Snetchild * for recording. 252162588Snetchild * 253162588Snetchild * For now, this function is kind of a goofy compatibility stub atop the 254162588Snetchild * existing sound system. (For example, in theory, the old sound system 255162588Snetchild * allows multiple recording devices to be specified via a bitmask.) 256162588Snetchild * 257162588Snetchild * @param m mixer context container thing 258162588Snetchild * 259162588Snetchild * @retval 0 success 260162588Snetchild * @retval EIDRM no recording device found (generally not possible) 261162588Snetchild * @todo Ask about error code 262162588Snetchild */ 263162588Snetchildstatic int 264162588Snetchildmixer_get_recroute(struct snd_mixer *m, int *route) 265162588Snetchild{ 266162588Snetchild int i, cnt; 267162588Snetchild 268162588Snetchild cnt = 0; 269162588Snetchild 270162588Snetchild for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 271162588Snetchild /** @todo can user set a multi-device mask? (== or &?) */ 272162588Snetchild if ((1 << i) == m->recsrc) 273162588Snetchild break; 274162588Snetchild if ((1 << i) & m->recdevs) 275162588Snetchild ++cnt; 276162588Snetchild } 277162588Snetchild 278162588Snetchild if (i == SOUND_MIXER_NRDEVICES) 279162588Snetchild return EIDRM; 280162588Snetchild 281162588Snetchild *route = cnt; 282162588Snetchild return 0; 283162588Snetchild} 284162588Snetchild 285162588Snetchild/** 286162588Snetchild * @brief Select a device for recording 287162588Snetchild * 288162588Snetchild * This function sets a recording source based on a recording device's 289162588Snetchild * routing number. Said number is translated to an old school recdev 290162588Snetchild * mask and passed over mixer_setrecsrc. 291162588Snetchild * 292162588Snetchild * @param m mixer context container thing 293162588Snetchild * 294162588Snetchild * @retval 0 success(?) 295162588Snetchild * @retval EINVAL User specified an invalid device number 296162588Snetchild * @retval otherwise error from mixer_setrecsrc 297162588Snetchild */ 298162588Snetchildstatic int 299162588Snetchildmixer_set_recroute(struct snd_mixer *m, int route) 300162588Snetchild{ 301162588Snetchild int i, cnt, ret; 302162588Snetchild 303162588Snetchild ret = 0; 304162588Snetchild cnt = 0; 305162588Snetchild 306162588Snetchild for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 307162588Snetchild if ((1 << i) & m->recdevs) { 308162588Snetchild if (route == cnt) 309162588Snetchild break; 310162588Snetchild ++cnt; 311162588Snetchild } 312162588Snetchild } 313162588Snetchild 314162588Snetchild if (i == SOUND_MIXER_NRDEVICES) 315162588Snetchild ret = EINVAL; 316162588Snetchild else 317162588Snetchild ret = mixer_setrecsrc(m, (1 << i)); 318162588Snetchild 319162588Snetchild return ret; 320162588Snetchild} 321162588Snetchild 32270134Scgvoid 32374763Scgmix_setdevs(struct snd_mixer *m, u_int32_t v) 32470134Scg{ 325162791Sariff struct snddev_info *d; 326162738Sariff int i; 327162738Sariff 328162738Sariff if (m == NULL) 329162738Sariff return; 330162791Sariff 331162791Sariff d = device_get_softc(m->dev); 332162738Sariff if (d != NULL && (d->flags & SD_F_SOFTPCMVOL)) 333150825Snetchild v |= SOUND_MASK_PCM; 334162738Sariff for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 335162738Sariff if (m->parent[i] < SOUND_MIXER_NRDEVICES) 336162738Sariff v |= 1 << m->parent[i]; 337162738Sariff v |= m->child[i]; 338162738Sariff } 33970134Scg m->devs = v; 34070134Scg} 34170134Scg 342162588Snetchild/** 343162588Snetchild * @brief Record mask of available recording devices 344162588Snetchild * 345162588Snetchild * Calling functions are responsible for defining the mask of available 346162588Snetchild * recording devices. This function records that value in a structure 347162588Snetchild * used by the rest of the mixer code. 348162588Snetchild * 349162588Snetchild * This function also populates a structure used by the SNDCTL_DSP_*RECSRC* 350162588Snetchild * family of ioctls that are part of OSSV4. All recording device labels 351162588Snetchild * are concatenated in ascending order corresponding to their routing 352162588Snetchild * numbers. (Ex: a system might have 0 => 'vol', 1 => 'cd', 2 => 'line', 353162588Snetchild * etc.) For now, these labels are just the standard recording device 354162588Snetchild * names (cd, line1, etc.), but will eventually be fully dynamic and user 355162588Snetchild * controlled. 356162588Snetchild * 357162588Snetchild * @param m mixer device context container thing 358162588Snetchild * @param v mask of recording devices 359162588Snetchild */ 36070134Scgvoid 36174763Scgmix_setrecdevs(struct snd_mixer *m, u_int32_t v) 36270134Scg{ 363162588Snetchild oss_mixer_enuminfo *ei; 364162588Snetchild char *loc; 365162588Snetchild int i, nvalues, nwrote, nleft, ncopied; 366162588Snetchild 367162588Snetchild ei = &m->enuminfo; 368162588Snetchild 369162588Snetchild nvalues = 0; 370162588Snetchild nwrote = 0; 371162588Snetchild nleft = sizeof(ei->strings); 372162588Snetchild loc = ei->strings; 373162588Snetchild 374162588Snetchild for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 375162588Snetchild if ((1 << i) & v) { 376162588Snetchild ei->strindex[nvalues] = nwrote; 377162588Snetchild ncopied = strlcpy(loc, snd_mixernames[i], nleft) + 1; 378162588Snetchild /* strlcpy retval doesn't include terminator */ 379162588Snetchild 380162588Snetchild nwrote += ncopied; 381162588Snetchild nleft -= ncopied; 382162588Snetchild nvalues++; 383162588Snetchild 384162588Snetchild /* 385162588Snetchild * XXX I don't think this should ever be possible. 386162588Snetchild * Even with a move to dynamic device/channel names, 387162588Snetchild * each label is limited to ~16 characters, so that'd 388162588Snetchild * take a LOT to fill this buffer. 389162588Snetchild */ 390162588Snetchild if ((nleft <= 0) || (nvalues >= OSS_ENUM_MAXVALUE)) { 391162588Snetchild device_printf(m->dev, 392162588Snetchild "mix_setrecdevs: Not enough room to store device names--please file a bug report.\n"); 393162588Snetchild device_printf(m->dev, 394162588Snetchild "mix_setrecdevs: Please include details about your sound hardware, OS version, etc.\n"); 395162588Snetchild break; 396162588Snetchild } 397162588Snetchild 398162588Snetchild loc = &ei->strings[nwrote]; 399162588Snetchild } 400162588Snetchild } 401162588Snetchild 402162588Snetchild /* 403162588Snetchild * NB: The SNDCTL_DSP_GET_RECSRC_NAMES ioctl ignores the dev 404162588Snetchild * and ctrl fields. 405162588Snetchild */ 406162588Snetchild ei->nvalues = nvalues; 40770134Scg m->recdevs = v; 40870134Scg} 40970134Scg 410162738Sariffvoid 411162738Sariffmix_setparentchild(struct snd_mixer *m, u_int32_t parent, u_int32_t childs) 412162738Sariff{ 413162738Sariff u_int32_t mask = 0; 414162738Sariff int i; 415162738Sariff 416162738Sariff if (m == NULL || parent >= SOUND_MIXER_NRDEVICES) 417162738Sariff return; 418162738Sariff for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 419162738Sariff if (i == parent) 420162738Sariff continue; 421162738Sariff if (childs & (1 << i)) { 422162738Sariff mask |= 1 << i; 423162738Sariff if (m->parent[i] < SOUND_MIXER_NRDEVICES) 424162738Sariff m->child[m->parent[i]] &= ~(1 << i); 425162738Sariff m->parent[i] = parent; 426162738Sariff m->child[i] = 0; 427162738Sariff } 428162738Sariff } 429162738Sariff mask &= ~(1 << parent); 430162738Sariff m->child[parent] = mask; 431162738Sariff} 432162738Sariff 433162738Sariffvoid 434162738Sariffmix_setrealdev(struct snd_mixer *m, u_int32_t dev, u_int32_t realdev) 435162738Sariff{ 436162738Sariff if (m == NULL || dev >= SOUND_MIXER_NRDEVICES || 437162738Sariff !(realdev == SOUND_MIXER_NONE || realdev < SOUND_MIXER_NRDEVICES)) 438162738Sariff return; 439162738Sariff m->realdev[dev] = realdev; 440162738Sariff} 441162738Sariff 44270134Scgu_int32_t 443162738Sariffmix_getparent(struct snd_mixer *m, u_int32_t dev) 444162738Sariff{ 445162738Sariff if (m == NULL || dev >= SOUND_MIXER_NRDEVICES) 446162738Sariff return SOUND_MIXER_NONE; 447162738Sariff return m->parent[dev]; 448162738Sariff} 449162738Sariff 450162738Sariffu_int32_t 451162738Sariffmix_getchild(struct snd_mixer *m, u_int32_t dev) 452162738Sariff{ 453162738Sariff if (m == NULL || dev >= SOUND_MIXER_NRDEVICES) 454162738Sariff return 0; 455162738Sariff return m->child[dev]; 456162738Sariff} 457162738Sariff 458162738Sariffu_int32_t 45974763Scgmix_getdevs(struct snd_mixer *m) 46070134Scg{ 46170134Scg return m->devs; 46270134Scg} 46370134Scg 46470134Scgu_int32_t 46574763Scgmix_getrecdevs(struct snd_mixer *m) 46670134Scg{ 46770134Scg return m->recdevs; 46870134Scg} 46970134Scg 47070134Scgvoid * 47174763Scgmix_getdevinfo(struct snd_mixer *m) 47270134Scg{ 47370134Scg return m->devinfo; 47470134Scg} 47570134Scg 47650724Scgint 47770134Scgmixer_init(device_t dev, kobj_class_t cls, void *devinfo) 47870134Scg{ 479124617Sphk struct snddev_info *snddev; 48074763Scg struct snd_mixer *m; 48170134Scg u_int16_t v; 482130585Sphk struct cdev *pdev; 483130792Sjosef int i, unit, val; 48470134Scg 485111119Simp m = (struct snd_mixer *)kobj_create(cls, M_MIXER, M_WAITOK | M_ZERO); 48674763Scg snprintf(m->name, MIXER_NAMELEN, "%s:mixer", device_get_nameunit(dev)); 48793816Sjhb m->lock = snd_mtxcreate(m->name, "pcm mixer"); 48874763Scg m->type = cls->name; 48970134Scg m->devinfo = devinfo; 49078362Scg m->busy = 0; 491150825Snetchild m->dev = dev; 492162738Sariff for (i = 0; i < 32; i++) { 493162738Sariff m->parent[i] = SOUND_MIXER_NONE; 494162738Sariff m->child[i] = 0; 495162738Sariff m->realdev[i] = i; 496162738Sariff } 49770134Scg 49870134Scg if (MIXER_INIT(m)) 49970134Scg goto bad; 50070134Scg 50170134Scg for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 502131105Sjosef v = snd_mixerdefaults[i]; 503131105Sjosef 504131064Sjosef if (resource_int_value(device_get_name(dev), 505131064Sjosef device_get_unit(dev), snd_mixernames[i], &val) == 0) { 506131064Sjosef if (val >= 0 && val <= 100) { 507131064Sjosef v = (u_int16_t) val; 508131064Sjosef } 509131064Sjosef } 510130792Sjosef 51170134Scg mixer_set(m, i, v | (v << 8)); 51270134Scg } 51370134Scg 51470134Scg mixer_setrecsrc(m, SOUND_MASK_MIC); 51570134Scg 51678362Scg unit = device_get_unit(dev); 51778362Scg pdev = make_dev(&mixer_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0), 51878362Scg UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit); 51978362Scg pdev->si_drv1 = m; 520124617Sphk snddev = device_get_softc(dev); 521124617Sphk snddev->mixer_dev = pdev; 52270134Scg 523162588Snetchild ++mixer_count; 524162588Snetchild 525162738Sariff if (bootverbose) { 526162738Sariff for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 527162738Sariff if (!(m->devs & (1 << i))) 528162738Sariff continue; 529162738Sariff if (m->realdev[i] != i) { 530162738Sariff device_printf(dev, "Mixer \"%s\" -> \"%s\":", 531162738Sariff snd_mixernames[i], 532162738Sariff (m->realdev[i] < SOUND_MIXER_NRDEVICES) ? 533162738Sariff snd_mixernames[m->realdev[i]] : "none"); 534162738Sariff } else { 535162738Sariff device_printf(dev, "Mixer \"%s\":", 536162738Sariff snd_mixernames[i]); 537162738Sariff } 538162738Sariff if (m->parent[i] < SOUND_MIXER_NRDEVICES) 539162738Sariff printf(" parent=\"%s\"", 540162738Sariff snd_mixernames[m->parent[i]]); 541162738Sariff if (m->child[i] != 0) 542162738Sariff printf(" child=0x%08x", m->child[i]); 543162738Sariff printf("\n"); 544162738Sariff } 545162738Sariff if (snddev->flags & SD_F_SOFTPCMVOL) 546162738Sariff device_printf(dev, "Soft PCM mixer ENABLED\n"); 547162738Sariff } 548162738Sariff 54970134Scg return 0; 55070134Scg 55174763Scgbad: 55274763Scg snd_mtxlock(m->lock); 55374763Scg snd_mtxfree(m->lock); 55474763Scg kobj_delete((kobj_t)m, M_MIXER); 55570134Scg return -1; 55670134Scg} 55770134Scg 55870134Scgint 55965340Scgmixer_uninit(device_t dev) 56058383Scg{ 56158383Scg int i; 562156929Sariff struct snddev_info *d; 56374763Scg struct snd_mixer *m; 564130585Sphk struct cdev *pdev; 56570134Scg 566156929Sariff d = device_get_softc(dev); 56778362Scg pdev = mixer_get_devt(dev); 568156929Sariff if (d == NULL || pdev == NULL || pdev->si_drv1 == NULL) 569156929Sariff return EBADF; 57078362Scg m = pdev->si_drv1; 57174763Scg snd_mtxlock(m->lock); 57270134Scg 57378362Scg if (m->busy) { 57478362Scg snd_mtxunlock(m->lock); 57578362Scg return EBUSY; 57678362Scg } 57778362Scg 57878362Scg pdev->si_drv1 = NULL; 57978362Scg destroy_dev(pdev); 58078362Scg 58165340Scg for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 58270134Scg mixer_set(m, i, 0); 58370134Scg 58470134Scg mixer_setrecsrc(m, SOUND_MASK_MIC); 58570134Scg 58670134Scg MIXER_UNINIT(m); 58770134Scg 58874763Scg snd_mtxfree(m->lock); 58970134Scg kobj_delete((kobj_t)m, M_MIXER); 59070134Scg 591156929Sariff d->mixer_dev = NULL; 592156929Sariff 593162588Snetchild --mixer_count; 594162588Snetchild 59565340Scg return 0; 59665340Scg} 59765340Scg 59865340Scgint 59965340Scgmixer_reinit(device_t dev) 60065340Scg{ 60178362Scg struct snd_mixer *m; 602130585Sphk struct cdev *pdev; 60365340Scg int i; 60467652Scg 60578362Scg pdev = mixer_get_devt(dev); 60678362Scg m = pdev->si_drv1; 60774763Scg snd_mtxlock(m->lock); 60870134Scg 60970134Scg i = MIXER_REINIT(m); 61074763Scg if (i) { 61174763Scg snd_mtxunlock(m->lock); 61267652Scg return i; 61374763Scg } 61470134Scg 61567652Scg for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 61670134Scg mixer_set(m, i, m->level[i]); 61770134Scg 61870134Scg mixer_setrecsrc(m, m->recsrc); 61974763Scg snd_mtxunlock(m->lock); 62070134Scg 62167652Scg return 0; 62258383Scg} 62358383Scg 62473127Scg#ifdef SND_DYNSYSCTL 62570680Sjhbstatic int 62670680Sjhbsysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS) 62770680Sjhb{ 62870680Sjhb char devname[32]; 62970680Sjhb int error, dev; 63074763Scg struct snd_mixer *m; 63170618Sjhb 63270680Sjhb m = oidp->oid_arg1; 63374763Scg snd_mtxlock(m->lock); 634164614Sariff strlcpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname)); 635100654Sgreen snd_mtxunlock(m->lock); 63670680Sjhb error = sysctl_handle_string(oidp, &devname[0], sizeof(devname), req); 637100654Sgreen snd_mtxlock(m->lock); 63870680Sjhb if (error == 0 && req->newptr != NULL) { 63970680Sjhb dev = mixer_lookup(devname); 64074763Scg if (dev == -1) { 64174763Scg snd_mtxunlock(m->lock); 64270680Sjhb return EINVAL; 64374763Scg } 64470944Sjhb else if (dev != m->hwvol_mixer) { 64570680Sjhb m->hwvol_mixer = dev; 64670944Sjhb m->hwvol_muted = 0; 64770944Sjhb } 64870680Sjhb } 64974763Scg snd_mtxunlock(m->lock); 65070680Sjhb return error; 65170680Sjhb} 65273127Scg#endif 65370618Sjhb 65470680Sjhbint 65570944Sjhbmixer_hwvol_init(device_t dev) 65670680Sjhb{ 65774763Scg struct snd_mixer *m; 658130585Sphk struct cdev *pdev; 65970680Sjhb 66078362Scg pdev = mixer_get_devt(dev); 66178362Scg m = pdev->si_drv1; 66278362Scg 66370680Sjhb m->hwvol_mixer = SOUND_MIXER_VOLUME; 66470680Sjhb m->hwvol_step = 5; 66573127Scg#ifdef SND_DYNSYSCTL 666164614Sariff SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), 667164614Sariff SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 66870680Sjhb OID_AUTO, "hwvol_step", CTLFLAG_RW, &m->hwvol_step, 0, ""); 669164614Sariff SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 670164614Sariff SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 67170680Sjhb OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0, 672100071Smarkm sysctl_hw_snd_hwvol_mixer, "A", ""); 67373127Scg#endif 67470680Sjhb return 0; 67570680Sjhb} 67670680Sjhb 67770618Sjhbvoid 67870944Sjhbmixer_hwvol_mute(device_t dev) 67970618Sjhb{ 68074763Scg struct snd_mixer *m; 681130585Sphk struct cdev *pdev; 68270618Sjhb 68378362Scg pdev = mixer_get_devt(dev); 68478362Scg m = pdev->si_drv1; 68574763Scg snd_mtxlock(m->lock); 68670944Sjhb if (m->hwvol_muted) { 68770944Sjhb m->hwvol_muted = 0; 68870944Sjhb mixer_set(m, m->hwvol_mixer, m->hwvol_mute_level); 68970680Sjhb } else { 69070944Sjhb m->hwvol_muted++; 69170944Sjhb m->hwvol_mute_level = mixer_get(m, m->hwvol_mixer); 69270680Sjhb mixer_set(m, m->hwvol_mixer, 0); 69370680Sjhb } 69474763Scg snd_mtxunlock(m->lock); 69570618Sjhb} 69670618Sjhb 69770618Sjhbvoid 69870944Sjhbmixer_hwvol_step(device_t dev, int left_step, int right_step) 69970618Sjhb{ 70074763Scg struct snd_mixer *m; 70170618Sjhb int level, left, right; 702130585Sphk struct cdev *pdev; 70370618Sjhb 70478362Scg pdev = mixer_get_devt(dev); 70578362Scg m = pdev->si_drv1; 70674763Scg snd_mtxlock(m->lock); 70770944Sjhb if (m->hwvol_muted) { 70870944Sjhb m->hwvol_muted = 0; 70970944Sjhb level = m->hwvol_mute_level; 71070944Sjhb } else 71170944Sjhb level = mixer_get(m, m->hwvol_mixer); 71270618Sjhb if (level != -1) { 71370618Sjhb left = level & 0xff; 71470618Sjhb right = level >> 8; 71570680Sjhb left += left_step * m->hwvol_step; 71670618Sjhb if (left < 0) 71770618Sjhb left = 0; 71870680Sjhb right += right_step * m->hwvol_step; 71970618Sjhb if (right < 0) 72070618Sjhb right = 0; 72170680Sjhb mixer_set(m, m->hwvol_mixer, left | right << 8); 72270618Sjhb } 72374763Scg snd_mtxunlock(m->lock); 72470618Sjhb} 72570618Sjhb 72678362Scg/* ----------------------------------------------------------------------- */ 72750724Scg 72878362Scgstatic int 729130585Sphkmixer_open(struct cdev *i_dev, int flags, int mode, struct thread *td) 73078362Scg{ 73178362Scg struct snd_mixer *m; 73278362Scg 73382181Scg m = i_dev->si_drv1; 73482181Scg snd_mtxlock(m->lock); 73578362Scg 736168247Sariff m->busy = 1; 73782181Scg 73882181Scg snd_mtxunlock(m->lock); 73978362Scg return 0; 74078362Scg} 74178362Scg 74278362Scgstatic int 743130585Sphkmixer_close(struct cdev *i_dev, int flags, int mode, struct thread *td) 74478362Scg{ 74578362Scg struct snd_mixer *m; 74678362Scg 74782181Scg m = i_dev->si_drv1; 74882181Scg snd_mtxlock(m->lock); 74982181Scg 75078670Scg if (!m->busy) { 75182181Scg snd_mtxunlock(m->lock); 75278362Scg return EBADF; 75378670Scg } 754168247Sariff m->busy = 0; 75578362Scg 75682181Scg snd_mtxunlock(m->lock); 75778362Scg return 0; 75878362Scg} 75978362Scg 76078362Scgint 761130585Sphkmixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td) 76278362Scg{ 76378670Scg struct snd_mixer *m; 76478362Scg int ret, *arg_i = (int *)arg; 76578362Scg int v = -1, j = cmd & 0xff; 76678362Scg 76778362Scg m = i_dev->si_drv1; 768156929Sariff 769156929Sariff if (m == NULL) 77078362Scg return EBADF; 77178362Scg 77278362Scg snd_mtxlock(m->lock); 773156929Sariff if (mode != -1 && !m->busy) { 774156929Sariff snd_mtxunlock(m->lock); 775156929Sariff return EBADF; 776156929Sariff } 777156929Sariff 778162588Snetchild if (cmd == SNDCTL_MIXERINFO) { 779162588Snetchild snd_mtxunlock(m->lock); 780162588Snetchild return mixer_oss_mixerinfo(i_dev, (oss_mixerinfo *)arg); 781162588Snetchild } 782162588Snetchild 78378362Scg if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) { 78478362Scg if (j == SOUND_MIXER_RECSRC) 78578362Scg ret = mixer_setrecsrc(m, *arg_i); 78678362Scg else 78778362Scg ret = mixer_set(m, j, *arg_i); 78878362Scg snd_mtxunlock(m->lock); 78978362Scg return (ret == 0)? 0 : ENXIO; 79078362Scg } 79178362Scg 79278362Scg if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) { 79378362Scg switch (j) { 79478362Scg case SOUND_MIXER_DEVMASK: 79578362Scg case SOUND_MIXER_CAPS: 79678362Scg case SOUND_MIXER_STEREODEVS: 79778362Scg v = mix_getdevs(m); 79878362Scg break; 79978362Scg 80078362Scg case SOUND_MIXER_RECMASK: 80178362Scg v = mix_getrecdevs(m); 80278362Scg break; 80378362Scg 80478362Scg case SOUND_MIXER_RECSRC: 80578362Scg v = mixer_getrecsrc(m); 80678362Scg break; 80778362Scg 80878362Scg default: 80978362Scg v = mixer_get(m, j); 81078362Scg } 81178362Scg *arg_i = v; 81278362Scg snd_mtxunlock(m->lock); 81378362Scg return (v != -1)? 0 : ENXIO; 81478362Scg } 815162588Snetchild 816162588Snetchild ret = 0; 817162588Snetchild 818162588Snetchild switch (cmd) { 819162588Snetchild /** @todo Double check return values, error codes. */ 820162588Snetchild case SNDCTL_SYSINFO: 821162588Snetchild sound_oss_sysinfo((oss_sysinfo *)arg); 822162588Snetchild break; 823162588Snetchild case SNDCTL_AUDIOINFO: 824162588Snetchild ret = dsp_oss_audioinfo(i_dev, (oss_audioinfo *)arg); 825162588Snetchild break; 826162588Snetchild case SNDCTL_DSP_GET_RECSRC_NAMES: 827162588Snetchild bcopy((void *)&m->enuminfo, arg, sizeof(oss_mixer_enuminfo)); 828162588Snetchild break; 829162588Snetchild case SNDCTL_DSP_GET_RECSRC: 830162588Snetchild ret = mixer_get_recroute(m, arg_i); 831162588Snetchild break; 832162588Snetchild case SNDCTL_DSP_SET_RECSRC: 833162588Snetchild ret = mixer_set_recroute(m, *arg_i); 834162588Snetchild break; 835164613Snetchild case OSS_GETVERSION: 836164613Snetchild *arg_i = SOUND_VERSION; 837164613Snetchild break; 838162588Snetchild default: 839162588Snetchild ret = ENXIO; 840162588Snetchild } 841162588Snetchild 84278362Scg snd_mtxunlock(m->lock); 843162588Snetchild return ret; 84478362Scg} 84578362Scg 84678670Scg#ifdef USING_DEVFS 84778362Scgstatic void 848148868Srwatsonmixer_clone(void *arg, struct ucred *cred, char *name, int namelen, 849148868Srwatson struct cdev **dev) 85078362Scg{ 851124617Sphk struct snddev_info *sd; 85278362Scg 853130640Sphk if (*dev != NULL) 85478362Scg return; 85578362Scg if (strcmp(name, "mixer") == 0) { 856124617Sphk sd = devclass_get_softc(pcm_devclass, snd_unit); 857152005Sariff if (sd != NULL && sd->mixer_dev != NULL) { 858124617Sphk *dev = sd->mixer_dev; 859144389Sphk dev_ref(*dev); 860144389Sphk } 86178362Scg } 86278362Scg} 86378362Scg 86478362Scgstatic void 86578362Scgmixer_sysinit(void *p) 86678362Scg{ 86778362Scg mixer_ehtag = EVENTHANDLER_REGISTER(dev_clone, mixer_clone, 0, 1000); 86878362Scg} 86978362Scg 87078362Scgstatic void 87178362Scgmixer_sysuninit(void *p) 87278362Scg{ 87378362Scg if (mixer_ehtag != NULL) 87478362Scg EVENTHANDLER_DEREGISTER(dev_clone, mixer_ehtag); 87578362Scg} 87678362Scg 87778362ScgSYSINIT(mixer_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysinit, NULL); 87878362ScgSYSUNINIT(mixer_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysuninit, NULL); 87978670Scg#endif 88078362Scg 881162588Snetchild/** 882162588Snetchild * @brief Handler for SNDCTL_MIXERINFO 883162588Snetchild * 884162588Snetchild * This function searches for a mixer based on the numeric ID stored 885162588Snetchild * in oss_miserinfo::dev. If set to -1, then information about the 886162588Snetchild * current mixer handling the request is provided. Note, however, that 887162588Snetchild * this ioctl may be made with any sound device (audio, mixer, midi). 888162588Snetchild * 889162588Snetchild * @note Caller must not hold any PCM device, channel, or mixer locks. 890162588Snetchild * 891162588Snetchild * See http://manuals.opensound.com/developer/SNDCTL_MIXERINFO.html for 892162588Snetchild * more information. 893162588Snetchild * 894162588Snetchild * @param i_dev character device on which the ioctl arrived 895162588Snetchild * @param arg user argument (oss_mixerinfo *) 896162588Snetchild * 897162588Snetchild * @retval EINVAL oss_mixerinfo::dev specified a bad value 898162588Snetchild * @retval 0 success 899162588Snetchild */ 900162588Snetchildint 901162588Snetchildmixer_oss_mixerinfo(struct cdev *i_dev, oss_mixerinfo *mi) 902162588Snetchild{ 903162588Snetchild struct snddev_info *d; 904162588Snetchild struct snd_mixer *m; 905162588Snetchild struct cdev *t_cdev; 906162588Snetchild int nmix, ret, pcmunit, i; 90778362Scg 908162588Snetchild /* 909162588Snetchild * If probing the device handling the ioctl, make sure it's a mixer 910162588Snetchild * device. (This ioctl is valid on audio, mixer, and midi devices.) 911162588Snetchild */ 912162588Snetchild if ((mi->dev == -1) && (i_dev->si_devsw != &mixer_cdevsw)) 913162588Snetchild return EINVAL; 914162588Snetchild 915162606Snetchild d = NULL; 916162588Snetchild m = NULL; 917162588Snetchild t_cdev = NULL; 918162588Snetchild nmix = 0; 919162588Snetchild ret = 0; 920162588Snetchild pcmunit = -1; /* pcmX */ 921162588Snetchild 922162588Snetchild /* 923162588Snetchild * There's a 1:1 relationship between mixers and PCM devices, so 924162588Snetchild * begin by iterating over PCM devices and search for our mixer. 925162588Snetchild */ 926162588Snetchild for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { 927162588Snetchild d = devclass_get_softc(pcm_devclass, i); 928162588Snetchild if (d == NULL) 929162588Snetchild continue; 930162588Snetchild 931162588Snetchild /* See the note in function docblock. */ 932162588Snetchild mtx_assert(d->lock, MA_NOTOWNED); 933162588Snetchild pcm_inprog(d, 1); 934162588Snetchild pcm_lock(d); 935162588Snetchild 936162588Snetchild if (d->mixer_dev != NULL) { 937162588Snetchild if (((mi->dev == -1) && (d->mixer_dev == i_dev)) || (mi->dev == nmix)) { 938162588Snetchild t_cdev = d->mixer_dev; 939162588Snetchild pcmunit = i; 940162588Snetchild break; 941162588Snetchild } 942162588Snetchild ++nmix; 943162588Snetchild } 944162588Snetchild 945162588Snetchild pcm_unlock(d); 946162588Snetchild pcm_inprog(d, -1); 947162588Snetchild } 948162588Snetchild 949162588Snetchild /* 950162588Snetchild * If t_cdev is NULL, then search was exhausted and device wasn't 951162588Snetchild * found. No locks are held, so just return. 952162588Snetchild */ 953162588Snetchild if (t_cdev == NULL) 954162588Snetchild return EINVAL; 955162588Snetchild 956162588Snetchild m = t_cdev->si_drv1; 957162588Snetchild mtx_lock(m->lock); 958162588Snetchild 959162588Snetchild /* 960162588Snetchild * At this point, the following synchronization stuff has happened: 961162588Snetchild * - a specific PCM device is locked and its "in progress 962162588Snetchild * operations" counter has been incremented, so be sure to unlock 963162588Snetchild * and decrement when exiting; 964162588Snetchild * - a specific mixer device has been locked, so be sure to unlock 965162588Snetchild * when existing. 966162588Snetchild */ 967162588Snetchild 968162588Snetchild bzero((void *)mi, sizeof(*mi)); 969162588Snetchild 970162588Snetchild mi->dev = nmix; 971162588Snetchild snprintf(mi->id, sizeof(mi->id), "mixer%d", dev2unit(t_cdev)); 972162588Snetchild strlcpy(mi->name, m->name, sizeof(mi->name)); 973162588Snetchild mi->modify_counter = m->modify_counter; 974162588Snetchild mi->card_number = pcmunit; 975162588Snetchild /* 976162588Snetchild * Currently, FreeBSD assumes 1:1 relationship between a pcm and 977162588Snetchild * mixer devices, so this is hardcoded to 0. 978162588Snetchild */ 979162588Snetchild mi->port_number = 0; 980162588Snetchild 981162588Snetchild /** 982162588Snetchild * @todo Fill in @sa oss_mixerinfo::mixerhandle. 983162588Snetchild * @note From 4Front: "mixerhandle is an arbitrary string that 984162588Snetchild * identifies the mixer better than the device number 985162588Snetchild * (mixerinfo.dev). Device numbers may change depending on 986162588Snetchild * the order the drivers are loaded. However the handle 987162588Snetchild * should remain the same provided that the sound card is 988162588Snetchild * not moved to another PCI slot." 989162588Snetchild */ 990162588Snetchild 991162588Snetchild /** 992162588Snetchild * @note 993162588Snetchild * @sa oss_mixerinfo::magic is a reserved field. 994162588Snetchild * 995162588Snetchild * @par 996162588Snetchild * From 4Front: "magic is usually 0. However some devices may have 997162588Snetchild * dedicated setup utilities and the magic field may contain an 998162588Snetchild * unique driver specific value (managed by [4Front])." 999162588Snetchild */ 1000162588Snetchild 1001162588Snetchild mi->enabled = device_is_attached(m->dev) ? 1 : 0; 1002162588Snetchild /** 1003162588Snetchild * The only flag for @sa oss_mixerinfo::caps is currently 1004162588Snetchild * MIXER_CAP_VIRTUAL, which I'm not sure we really worry about. 1005162588Snetchild */ 1006162588Snetchild /** 1007162588Snetchild * Mixer extensions currently aren't supported, so leave 1008162588Snetchild * @sa oss_mixerinfo::nrext blank for now. 1009162588Snetchild */ 1010162588Snetchild /** 1011162588Snetchild * @todo Fill in @sa oss_mixerinfo::priority (requires touching 1012162588Snetchild * drivers?) 1013162588Snetchild * @note The priority field is for mixer applets to determine which 1014162588Snetchild * mixer should be the default, with 0 being least preferred and 10 1015162588Snetchild * being most preferred. From 4Front: "OSS drivers like ICH use 1016162588Snetchild * higher values (10) because such chips are known to be used only 1017162588Snetchild * on motherboards. Drivers for high end pro devices use 0 because 1018162588Snetchild * they will never be the default mixer. Other devices use values 1 1019162588Snetchild * to 9 depending on the estimated probability of being the default 1020162588Snetchild * device. 1021162588Snetchild * 1022162588Snetchild * XXX Described by Hannu@4Front, but not found in soundcard.h. 1023162588Snetchild strlcpy(mi->devnode, t_cdev->si_name, sizeof(mi->devnode)); 1024162588Snetchild mi->legacy_device = pcmunit; 1025162588Snetchild */ 1026162588Snetchild 1027162588Snetchild mtx_unlock(m->lock); 1028162588Snetchild pcm_unlock(d); 1029162588Snetchild pcm_inprog(d, -1); 1030162588Snetchild 1031162588Snetchild return ret; 1032162588Snetchild} 1033