1139749Simp/*- 2193640Sariff * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> 3193640Sariff * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006 4193640Sariff * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org> 550724Scg * All rights reserved. 650724Scg * 750724Scg * Redistribution and use in source and binary forms, with or without 850724Scg * modification, are permitted provided that the following conditions 950724Scg * are met: 1050724Scg * 1. Redistributions of source code must retain the above copyright 1150724Scg * notice, this list of conditions and the following disclaimer. 1250724Scg * 2. Redistributions in binary form must reproduce the above copyright 1350724Scg * notice, this list of conditions and the following disclaimer in the 1450724Scg * documentation and/or other materials provided with the distribution. 1550724Scg * 1650724Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1750724Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1850724Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1950724Scg * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2050724Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2150724Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2250724Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2350724Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2450724Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2550724Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2650724Scg * SUCH DAMAGE. 2750724Scg */ 2850724Scg 29193640Sariff#ifdef HAVE_KERNEL_OPTION_HEADERS 30193640Sariff#include "opt_snd.h" 31193640Sariff#endif 32193640Sariff 3353465Scg#include <dev/sound/pcm/sound.h> 3450724Scg 35193640Sariff#include "feeder_if.h" 3670134Scg#include "mixer_if.h" 3770134Scg 3882180ScgSND_DECLARE_FILE("$FreeBSD: stable/10/sys/dev/sound/pcm/mixer.c 359887 2020-04-13 16:32:18Z hselasky $"); 3982180Scg 40227293Sedstatic MALLOC_DEFINE(M_MIXER, "mixer", "mixer"); 4170134Scg 42193640Sariffstatic int mixer_bypass = 1; 43193640SariffTUNABLE_INT("hw.snd.vpc_mixer_bypass", &mixer_bypass); 44193640SariffSYSCTL_INT(_hw_snd, OID_AUTO, vpc_mixer_bypass, CTLFLAG_RW, 45193640Sariff &mixer_bypass, 0, 46193640Sariff "control channel pcm/rec volume, bypassing real mixer device"); 47193640Sariff 4874763Scg#define MIXER_NAMELEN 16 4974763Scgstruct snd_mixer { 5074763Scg KOBJ_FIELDS; 5174763Scg void *devinfo; 52168264Sariff int busy; 5374763Scg int hwvol_muted; 5474763Scg int hwvol_mixer; 5574763Scg int hwvol_step; 56170815Sariff int type; 57150825Snetchild device_t dev; 5874763Scg u_int32_t hwvol_mute_level; 5974763Scg u_int32_t devs; 6074763Scg u_int32_t recdevs; 6174763Scg u_int32_t recsrc; 6274763Scg u_int16_t level[32]; 63162828Sariff u_int8_t parent[32]; 64162738Sariff u_int32_t child[32]; 65162828Sariff u_int8_t realdev[32]; 6674763Scg char name[MIXER_NAMELEN]; 67107285Scg struct mtx *lock; 68162588Snetchild oss_mixer_enuminfo enuminfo; 69162588Snetchild /** 70162588Snetchild * Counter is incremented when applications change any of this 71162588Snetchild * mixer's controls. A change in value indicates that persistent 72162588Snetchild * mixer applications should update their displays. 73162588Snetchild */ 74162588Snetchild int modify_counter; 7574763Scg}; 7674763Scg 7750724Scgstatic u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = { 7850724Scg [SOUND_MIXER_VOLUME] = 75, 7950724Scg [SOUND_MIXER_BASS] = 50, 8050724Scg [SOUND_MIXER_TREBLE] = 50, 8162947Stanimura [SOUND_MIXER_SYNTH] = 75, 8250724Scg [SOUND_MIXER_PCM] = 75, 8350724Scg [SOUND_MIXER_SPEAKER] = 75, 8450724Scg [SOUND_MIXER_LINE] = 75, 85359884Shselasky [SOUND_MIXER_MIC] = 25, 8650724Scg [SOUND_MIXER_CD] = 75, 87152422Sariff [SOUND_MIXER_IGAIN] = 0, 8850724Scg [SOUND_MIXER_LINE1] = 75, 8950724Scg [SOUND_MIXER_VIDEO] = 75, 90202166Smav [SOUND_MIXER_RECLEV] = 75, 9153203Scg [SOUND_MIXER_OGAIN] = 50, 9279044Scg [SOUND_MIXER_MONITOR] = 75, 9350724Scg}; 9450724Scg 9570680Sjhbstatic char* snd_mixernames[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES; 9670680Sjhb 9778362Scgstatic d_open_t mixer_open; 9878362Scgstatic d_close_t mixer_close; 99170815Sariffstatic d_ioctl_t mixer_ioctl; 10078362Scg 10178362Scgstatic struct cdevsw mixer_cdevsw = { 102126080Sphk .d_version = D_VERSION, 103111815Sphk .d_open = mixer_open, 104111815Sphk .d_close = mixer_close, 105111815Sphk .d_ioctl = mixer_ioctl, 106111815Sphk .d_name = "mixer", 10778362Scg}; 10878362Scg 109162588Snetchild/** 110162588Snetchild * Keeps a count of mixer devices; used only by OSSv4 SNDCTL_SYSINFO ioctl. 111162588Snetchild */ 112162588Snetchildint mixer_count = 0; 113162588Snetchild 114170161Sariffstatic eventhandler_tag mixer_ehtag = NULL; 11578362Scg 116130585Sphkstatic struct cdev * 11778362Scgmixer_get_devt(device_t dev) 11878362Scg{ 119124617Sphk struct snddev_info *snddev; 12078362Scg 121124617Sphk snddev = device_get_softc(dev); 12278362Scg 123124617Sphk return snddev->mixer_dev; 12478362Scg} 12578362Scg 12665390Speterstatic int 12770680Sjhbmixer_lookup(char *devname) 12870680Sjhb{ 12970680Sjhb int i; 13070680Sjhb 13170680Sjhb for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 13270680Sjhb if (strncmp(devname, snd_mixernames[i], 13370680Sjhb strlen(snd_mixernames[i])) == 0) 13470680Sjhb return i; 13570680Sjhb return -1; 13670680Sjhb} 13770680Sjhb 138170815Sariff#define MIXER_SET_UNLOCK(x, y) do { \ 139170815Sariff if ((y) != 0) \ 140170815Sariff snd_mtxunlock((x)->lock); \ 141193640Sariff} while (0) 142170815Sariff 143170815Sariff#define MIXER_SET_LOCK(x, y) do { \ 144170815Sariff if ((y) != 0) \ 145170815Sariff snd_mtxlock((x)->lock); \ 146193640Sariff} while (0) 147170815Sariff 14870680Sjhbstatic int 149170815Sariffmixer_set_softpcmvol(struct snd_mixer *m, struct snddev_info *d, 150193640Sariff u_int left, u_int right) 15165390Speter{ 152170161Sariff struct pcm_channel *c; 153170815Sariff int dropmtx, acquiremtx; 154162738Sariff 155358879Shselasky if (PCM_DETACHING(d) || !PCM_REGISTERED(d)) 156170815Sariff return (EINVAL); 157170161Sariff 158170815Sariff if (mtx_owned(m->lock)) 159170815Sariff dropmtx = 1; 160170815Sariff else 161170815Sariff dropmtx = 0; 162170815Sariff 163170815Sariff if (!(d->flags & SD_F_MPSAFE) || mtx_owned(d->lock) != 0) 164170815Sariff acquiremtx = 0; 165170815Sariff else 166170815Sariff acquiremtx = 1; 167170815Sariff 168170815Sariff /* 169170815Sariff * Be careful here. If we're coming from cdev ioctl, it is OK to 170170815Sariff * not doing locking AT ALL (except on individual channel) since 171170815Sariff * we've been heavily guarded by pcm cv, or if we're still 172170815Sariff * under Giant influence. Since we also have mix_* calls, we cannot 173170815Sariff * assume such protection and just do the lock as usuall. 174170815Sariff */ 175170815Sariff MIXER_SET_UNLOCK(m, dropmtx); 176170815Sariff MIXER_SET_LOCK(d, acquiremtx); 177170815Sariff 178193640Sariff CHN_FOREACH(c, d, channels.pcm.busy) { 179193640Sariff CHN_LOCK(c); 180193640Sariff if (c->direction == PCMDIR_PLAY && 181193640Sariff (c->feederflags & (1 << FEEDER_VOLUME))) 182193640Sariff chn_setvolume_multi(c, SND_VOL_C_MASTER, left, right, 183193640Sariff (left + right) >> 1); 184193640Sariff CHN_UNLOCK(c); 185162738Sariff } 186170161Sariff 187170815Sariff MIXER_SET_UNLOCK(d, acquiremtx); 188170815Sariff MIXER_SET_LOCK(m, dropmtx); 189170161Sariff 190170815Sariff return (0); 191162738Sariff} 192162738Sariff 193162738Sariffstatic int 194193640Sariffmixer_set_eq(struct snd_mixer *m, struct snddev_info *d, 195193640Sariff u_int dev, u_int level) 196162738Sariff{ 197193640Sariff struct pcm_channel *c; 198193640Sariff struct pcm_feeder *f; 199193640Sariff int tone, dropmtx, acquiremtx; 200193640Sariff 201193640Sariff if (dev == SOUND_MIXER_TREBLE) 202193640Sariff tone = FEEDEQ_TREBLE; 203193640Sariff else if (dev == SOUND_MIXER_BASS) 204193640Sariff tone = FEEDEQ_BASS; 205193640Sariff else 206193640Sariff return (EINVAL); 207193640Sariff 208358879Shselasky if (PCM_DETACHING(d) || !PCM_REGISTERED(d)) 209193640Sariff return (EINVAL); 210193640Sariff 211193640Sariff if (mtx_owned(m->lock)) 212193640Sariff dropmtx = 1; 213193640Sariff else 214193640Sariff dropmtx = 0; 215193640Sariff 216193640Sariff if (!(d->flags & SD_F_MPSAFE) || mtx_owned(d->lock) != 0) 217193640Sariff acquiremtx = 0; 218193640Sariff else 219193640Sariff acquiremtx = 1; 220193640Sariff 221193640Sariff /* 222193640Sariff * Be careful here. If we're coming from cdev ioctl, it is OK to 223193640Sariff * not doing locking AT ALL (except on individual channel) since 224193640Sariff * we've been heavily guarded by pcm cv, or if we're still 225193640Sariff * under Giant influence. Since we also have mix_* calls, we cannot 226193640Sariff * assume such protection and just do the lock as usuall. 227193640Sariff */ 228193640Sariff MIXER_SET_UNLOCK(m, dropmtx); 229193640Sariff MIXER_SET_LOCK(d, acquiremtx); 230193640Sariff 231193640Sariff CHN_FOREACH(c, d, channels.pcm.busy) { 232193640Sariff CHN_LOCK(c); 233193640Sariff f = chn_findfeeder(c, FEEDER_EQ); 234193640Sariff if (f != NULL) 235193640Sariff (void)FEEDER_SET(f, tone, level); 236193640Sariff CHN_UNLOCK(c); 237193640Sariff } 238193640Sariff 239193640Sariff MIXER_SET_UNLOCK(d, acquiremtx); 240193640Sariff MIXER_SET_LOCK(m, dropmtx); 241193640Sariff 242193640Sariff return (0); 243193640Sariff} 244193640Sariff 245193640Sariffstatic int 246193640Sariffmixer_set(struct snd_mixer *m, u_int dev, u_int lev) 247193640Sariff{ 248150825Snetchild struct snddev_info *d; 249193640Sariff u_int l, r, tl, tr; 250162738Sariff u_int32_t parent = SOUND_MIXER_NONE, child = 0; 251162738Sariff u_int32_t realdev; 252170815Sariff int i, dropmtx; 25370134Scg 254162738Sariff if (m == NULL || dev >= SOUND_MIXER_NRDEVICES || 255162738Sariff (0 == (m->devs & (1 << dev)))) 25670134Scg return -1; 25770134Scg 25870134Scg l = min((lev & 0x00ff), 100); 25970134Scg r = min(((lev & 0xff00) >> 8), 100); 260162738Sariff realdev = m->realdev[dev]; 26170134Scg 262162738Sariff d = device_get_softc(m->dev); 263162738Sariff if (d == NULL) 264162738Sariff return -1; 26570134Scg 266170815Sariff /* It is safe to drop this mutex due to Giant. */ 267170815Sariff if (!(d->flags & SD_F_MPSAFE) && mtx_owned(m->lock) != 0) 268170815Sariff dropmtx = 1; 269170815Sariff else 270170815Sariff dropmtx = 0; 271170815Sariff 272170815Sariff MIXER_SET_UNLOCK(m, dropmtx); 273170815Sariff 274162738Sariff /* TODO: recursive handling */ 275162738Sariff parent = m->parent[dev]; 276162738Sariff if (parent >= SOUND_MIXER_NRDEVICES) 277162738Sariff parent = SOUND_MIXER_NONE; 278162738Sariff if (parent == SOUND_MIXER_NONE) 279162738Sariff child = m->child[dev]; 280162738Sariff 281162738Sariff if (parent != SOUND_MIXER_NONE) { 282162738Sariff tl = (l * (m->level[parent] & 0x00ff)) / 100; 283162738Sariff tr = (r * ((m->level[parent] & 0xff00) >> 8)) / 100; 284162738Sariff if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) 285170815Sariff (void)mixer_set_softpcmvol(m, d, tl, tr); 286162738Sariff else if (realdev != SOUND_MIXER_NONE && 287170815Sariff MIXER_SET(m, realdev, tl, tr) < 0) { 288170815Sariff MIXER_SET_LOCK(m, dropmtx); 289162738Sariff return -1; 290170815Sariff } 291162738Sariff } else if (child != 0) { 292162738Sariff for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 293162738Sariff if (!(child & (1 << i)) || m->parent[i] != dev) 294162738Sariff continue; 295162738Sariff realdev = m->realdev[i]; 296162738Sariff tl = (l * (m->level[i] & 0x00ff)) / 100; 297162738Sariff tr = (r * ((m->level[i] & 0xff00) >> 8)) / 100; 298193640Sariff if (i == SOUND_MIXER_PCM && 299193640Sariff (d->flags & SD_F_SOFTPCMVOL)) 300170815Sariff (void)mixer_set_softpcmvol(m, d, tl, tr); 301162738Sariff else if (realdev != SOUND_MIXER_NONE) 302162738Sariff MIXER_SET(m, realdev, tl, tr); 303150825Snetchild } 304162738Sariff realdev = m->realdev[dev]; 305162738Sariff if (realdev != SOUND_MIXER_NONE && 306170815Sariff MIXER_SET(m, realdev, l, r) < 0) { 307170815Sariff MIXER_SET_LOCK(m, dropmtx); 308162738Sariff return -1; 309170815Sariff } 310150825Snetchild } else { 311162738Sariff if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) 312170815Sariff (void)mixer_set_softpcmvol(m, d, l, r); 313193640Sariff else if ((dev == SOUND_MIXER_TREBLE || 314193640Sariff dev == SOUND_MIXER_BASS) && (d->flags & SD_F_EQ)) 315193640Sariff (void)mixer_set_eq(m, d, dev, (l + r) >> 1); 316162738Sariff else if (realdev != SOUND_MIXER_NONE && 317170815Sariff MIXER_SET(m, realdev, l, r) < 0) { 318170815Sariff MIXER_SET_LOCK(m, dropmtx); 319150825Snetchild return -1; 320170815Sariff } 321150825Snetchild } 322150825Snetchild 323193640Sariff MIXER_SET_LOCK(m, dropmtx); 324193640Sariff 325162738Sariff m->level[dev] = l | (r << 8); 326336890Shselasky m->modify_counter++; 327162738Sariff 32870134Scg return 0; 32965390Speter} 33065390Speter 33165390Speterstatic int 33274763Scgmixer_get(struct snd_mixer *mixer, int dev) 33365390Speter{ 33470134Scg if ((dev < SOUND_MIXER_NRDEVICES) && (mixer->devs & (1 << dev))) 33570134Scg return mixer->level[dev]; 336170815Sariff else 337170815Sariff return -1; 33865390Speter} 33965390Speter 34065390Speterstatic int 34174763Scgmixer_setrecsrc(struct snd_mixer *mixer, u_int32_t src) 34265390Speter{ 343170815Sariff struct snddev_info *d; 344193640Sariff u_int32_t recsrc; 345170815Sariff int dropmtx; 346170815Sariff 347170815Sariff d = device_get_softc(mixer->dev); 348170815Sariff if (d == NULL) 349170815Sariff return -1; 350170815Sariff if (!(d->flags & SD_F_MPSAFE) && mtx_owned(mixer->lock) != 0) 351170815Sariff dropmtx = 1; 352170815Sariff else 353170815Sariff dropmtx = 0; 35470134Scg src &= mixer->recdevs; 35570134Scg if (src == 0) 356202166Smav src = mixer->recdevs & SOUND_MASK_MIC; 357202166Smav if (src == 0) 358202166Smav src = mixer->recdevs & SOUND_MASK_MONITOR; 359202166Smav if (src == 0) 360202166Smav src = mixer->recdevs & SOUND_MASK_LINE; 361202166Smav if (src == 0 && mixer->recdevs != 0) 362202166Smav src = (1 << (ffs(mixer->recdevs) - 1)); 363170815Sariff /* It is safe to drop this mutex due to Giant. */ 364170815Sariff MIXER_SET_UNLOCK(mixer, dropmtx); 365193640Sariff recsrc = MIXER_SETRECSRC(mixer, src); 366170815Sariff MIXER_SET_LOCK(mixer, dropmtx); 367193640Sariff 368193640Sariff mixer->recsrc = recsrc; 369193640Sariff 37065390Speter return 0; 37165390Speter} 37265390Speter 37365390Speterstatic int 37474763Scgmixer_getrecsrc(struct snd_mixer *mixer) 37565390Speter{ 37670134Scg return mixer->recsrc; 37765390Speter} 37865390Speter 379162588Snetchild/** 380162588Snetchild * @brief Retrieve the route number of the current recording device 381162588Snetchild * 382162588Snetchild * OSSv4 assigns routing numbers to recording devices, unlike the previous 383162588Snetchild * API which relied on a fixed table of device numbers and names. This 384162588Snetchild * function returns the routing number of the device currently selected 385162588Snetchild * for recording. 386162588Snetchild * 387162588Snetchild * For now, this function is kind of a goofy compatibility stub atop the 388162588Snetchild * existing sound system. (For example, in theory, the old sound system 389162588Snetchild * allows multiple recording devices to be specified via a bitmask.) 390162588Snetchild * 391162588Snetchild * @param m mixer context container thing 392162588Snetchild * 393162588Snetchild * @retval 0 success 394162588Snetchild * @retval EIDRM no recording device found (generally not possible) 395162588Snetchild * @todo Ask about error code 396162588Snetchild */ 397162588Snetchildstatic int 398162588Snetchildmixer_get_recroute(struct snd_mixer *m, int *route) 399162588Snetchild{ 400162588Snetchild int i, cnt; 401162588Snetchild 402162588Snetchild cnt = 0; 403162588Snetchild 404162588Snetchild for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 405162588Snetchild /** @todo can user set a multi-device mask? (== or &?) */ 406162588Snetchild if ((1 << i) == m->recsrc) 407162588Snetchild break; 408162588Snetchild if ((1 << i) & m->recdevs) 409162588Snetchild ++cnt; 410162588Snetchild } 411162588Snetchild 412162588Snetchild if (i == SOUND_MIXER_NRDEVICES) 413162588Snetchild return EIDRM; 414162588Snetchild 415162588Snetchild *route = cnt; 416162588Snetchild return 0; 417162588Snetchild} 418162588Snetchild 419162588Snetchild/** 420162588Snetchild * @brief Select a device for recording 421162588Snetchild * 422162588Snetchild * This function sets a recording source based on a recording device's 423162588Snetchild * routing number. Said number is translated to an old school recdev 424162588Snetchild * mask and passed over mixer_setrecsrc. 425162588Snetchild * 426162588Snetchild * @param m mixer context container thing 427162588Snetchild * 428162588Snetchild * @retval 0 success(?) 429162588Snetchild * @retval EINVAL User specified an invalid device number 430162588Snetchild * @retval otherwise error from mixer_setrecsrc 431162588Snetchild */ 432162588Snetchildstatic int 433162588Snetchildmixer_set_recroute(struct snd_mixer *m, int route) 434162588Snetchild{ 435162588Snetchild int i, cnt, ret; 436162588Snetchild 437162588Snetchild ret = 0; 438162588Snetchild cnt = 0; 439162588Snetchild 440162588Snetchild for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 441162588Snetchild if ((1 << i) & m->recdevs) { 442162588Snetchild if (route == cnt) 443162588Snetchild break; 444162588Snetchild ++cnt; 445162588Snetchild } 446162588Snetchild } 447162588Snetchild 448162588Snetchild if (i == SOUND_MIXER_NRDEVICES) 449162588Snetchild ret = EINVAL; 450162588Snetchild else 451162588Snetchild ret = mixer_setrecsrc(m, (1 << i)); 452162588Snetchild 453162588Snetchild return ret; 454162588Snetchild} 455162588Snetchild 45670134Scgvoid 45774763Scgmix_setdevs(struct snd_mixer *m, u_int32_t v) 45870134Scg{ 459162791Sariff struct snddev_info *d; 460162738Sariff int i; 461162738Sariff 462162738Sariff if (m == NULL) 463162738Sariff return; 464162791Sariff 465162791Sariff d = device_get_softc(m->dev); 466162738Sariff if (d != NULL && (d->flags & SD_F_SOFTPCMVOL)) 467150825Snetchild v |= SOUND_MASK_PCM; 468193640Sariff if (d != NULL && (d->flags & SD_F_EQ)) 469193640Sariff v |= SOUND_MASK_TREBLE | SOUND_MASK_BASS; 470162738Sariff for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 471162738Sariff if (m->parent[i] < SOUND_MIXER_NRDEVICES) 472162738Sariff v |= 1 << m->parent[i]; 473162738Sariff v |= m->child[i]; 474162738Sariff } 47570134Scg m->devs = v; 47670134Scg} 47770134Scg 478162588Snetchild/** 479162588Snetchild * @brief Record mask of available recording devices 480162588Snetchild * 481162588Snetchild * Calling functions are responsible for defining the mask of available 482162588Snetchild * recording devices. This function records that value in a structure 483162588Snetchild * used by the rest of the mixer code. 484162588Snetchild * 485162588Snetchild * This function also populates a structure used by the SNDCTL_DSP_*RECSRC* 486162588Snetchild * family of ioctls that are part of OSSV4. All recording device labels 487162588Snetchild * are concatenated in ascending order corresponding to their routing 488162588Snetchild * numbers. (Ex: a system might have 0 => 'vol', 1 => 'cd', 2 => 'line', 489162588Snetchild * etc.) For now, these labels are just the standard recording device 490162588Snetchild * names (cd, line1, etc.), but will eventually be fully dynamic and user 491162588Snetchild * controlled. 492162588Snetchild * 493162588Snetchild * @param m mixer device context container thing 494162588Snetchild * @param v mask of recording devices 495162588Snetchild */ 49670134Scgvoid 49774763Scgmix_setrecdevs(struct snd_mixer *m, u_int32_t v) 49870134Scg{ 499162588Snetchild oss_mixer_enuminfo *ei; 500162588Snetchild char *loc; 501162588Snetchild int i, nvalues, nwrote, nleft, ncopied; 502162588Snetchild 503162588Snetchild ei = &m->enuminfo; 504162588Snetchild 505162588Snetchild nvalues = 0; 506162588Snetchild nwrote = 0; 507162588Snetchild nleft = sizeof(ei->strings); 508162588Snetchild loc = ei->strings; 509162588Snetchild 510162588Snetchild for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 511162588Snetchild if ((1 << i) & v) { 512162588Snetchild ei->strindex[nvalues] = nwrote; 513162588Snetchild ncopied = strlcpy(loc, snd_mixernames[i], nleft) + 1; 514162588Snetchild /* strlcpy retval doesn't include terminator */ 515162588Snetchild 516162588Snetchild nwrote += ncopied; 517162588Snetchild nleft -= ncopied; 518162588Snetchild nvalues++; 519162588Snetchild 520162588Snetchild /* 521162588Snetchild * XXX I don't think this should ever be possible. 522162588Snetchild * Even with a move to dynamic device/channel names, 523162588Snetchild * each label is limited to ~16 characters, so that'd 524162588Snetchild * take a LOT to fill this buffer. 525162588Snetchild */ 526162588Snetchild if ((nleft <= 0) || (nvalues >= OSS_ENUM_MAXVALUE)) { 527162588Snetchild device_printf(m->dev, 528162588Snetchild "mix_setrecdevs: Not enough room to store device names--please file a bug report.\n"); 529162588Snetchild device_printf(m->dev, 530162588Snetchild "mix_setrecdevs: Please include details about your sound hardware, OS version, etc.\n"); 531162588Snetchild break; 532162588Snetchild } 533162588Snetchild 534162588Snetchild loc = &ei->strings[nwrote]; 535162588Snetchild } 536162588Snetchild } 537162588Snetchild 538162588Snetchild /* 539162588Snetchild * NB: The SNDCTL_DSP_GET_RECSRC_NAMES ioctl ignores the dev 540162588Snetchild * and ctrl fields. 541162588Snetchild */ 542162588Snetchild ei->nvalues = nvalues; 54370134Scg m->recdevs = v; 54470134Scg} 54570134Scg 546162738Sariffvoid 547162738Sariffmix_setparentchild(struct snd_mixer *m, u_int32_t parent, u_int32_t childs) 548162738Sariff{ 549162738Sariff u_int32_t mask = 0; 550162738Sariff int i; 551162738Sariff 552162738Sariff if (m == NULL || parent >= SOUND_MIXER_NRDEVICES) 553162738Sariff return; 554162738Sariff for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 555162738Sariff if (i == parent) 556162738Sariff continue; 557162738Sariff if (childs & (1 << i)) { 558162738Sariff mask |= 1 << i; 559162738Sariff if (m->parent[i] < SOUND_MIXER_NRDEVICES) 560162738Sariff m->child[m->parent[i]] &= ~(1 << i); 561162738Sariff m->parent[i] = parent; 562162738Sariff m->child[i] = 0; 563162738Sariff } 564162738Sariff } 565162738Sariff mask &= ~(1 << parent); 566162738Sariff m->child[parent] = mask; 567162738Sariff} 568162738Sariff 569162738Sariffvoid 570162738Sariffmix_setrealdev(struct snd_mixer *m, u_int32_t dev, u_int32_t realdev) 571162738Sariff{ 572162738Sariff if (m == NULL || dev >= SOUND_MIXER_NRDEVICES || 573162738Sariff !(realdev == SOUND_MIXER_NONE || realdev < SOUND_MIXER_NRDEVICES)) 574162738Sariff return; 575162738Sariff m->realdev[dev] = realdev; 576162738Sariff} 577162738Sariff 57870134Scgu_int32_t 579162738Sariffmix_getparent(struct snd_mixer *m, u_int32_t dev) 580162738Sariff{ 581162738Sariff if (m == NULL || dev >= SOUND_MIXER_NRDEVICES) 582162738Sariff return SOUND_MIXER_NONE; 583162738Sariff return m->parent[dev]; 584162738Sariff} 585162738Sariff 586162738Sariffu_int32_t 587162738Sariffmix_getchild(struct snd_mixer *m, u_int32_t dev) 588162738Sariff{ 589162738Sariff if (m == NULL || dev >= SOUND_MIXER_NRDEVICES) 590162738Sariff return 0; 591162738Sariff return m->child[dev]; 592162738Sariff} 593162738Sariff 594162738Sariffu_int32_t 59574763Scgmix_getdevs(struct snd_mixer *m) 59670134Scg{ 59770134Scg return m->devs; 59870134Scg} 59970134Scg 60070134Scgu_int32_t 60174763Scgmix_getrecdevs(struct snd_mixer *m) 60270134Scg{ 60370134Scg return m->recdevs; 60470134Scg} 60570134Scg 60670134Scgvoid * 60774763Scgmix_getdevinfo(struct snd_mixer *m) 60870134Scg{ 60970134Scg return m->devinfo; 61070134Scg} 61170134Scg 612170815Sariffstatic struct snd_mixer * 613170815Sariffmixer_obj_create(device_t dev, kobj_class_t cls, void *devinfo, 614170815Sariff int type, const char *desc) 61570134Scg{ 61674763Scg struct snd_mixer *m; 617170815Sariff int i; 61870134Scg 619170815Sariff KASSERT(dev != NULL && cls != NULL && devinfo != NULL, 620170815Sariff ("%s(): NULL data dev=%p cls=%p devinfo=%p", 621170815Sariff __func__, dev, cls, devinfo)); 622170815Sariff KASSERT(type == MIXER_TYPE_PRIMARY || type == MIXER_TYPE_SECONDARY, 623170815Sariff ("invalid mixer type=%d", type)); 624170815Sariff 625111119Simp m = (struct snd_mixer *)kobj_create(cls, M_MIXER, M_WAITOK | M_ZERO); 626170815Sariff snprintf(m->name, sizeof(m->name), "%s:mixer", 627170815Sariff device_get_nameunit(dev)); 628170815Sariff if (desc != NULL) { 629170815Sariff strlcat(m->name, ":", sizeof(m->name)); 630170815Sariff strlcat(m->name, desc, sizeof(m->name)); 631170815Sariff } 632170815Sariff m->lock = snd_mtxcreate(m->name, (type == MIXER_TYPE_PRIMARY) ? 633170815Sariff "primary pcm mixer" : "secondary pcm mixer"); 634170815Sariff m->type = type; 63570134Scg m->devinfo = devinfo; 63678362Scg m->busy = 0; 637150825Snetchild m->dev = dev; 638170815Sariff for (i = 0; i < (sizeof(m->parent) / sizeof(m->parent[0])); i++) { 639162738Sariff m->parent[i] = SOUND_MIXER_NONE; 640162738Sariff m->child[i] = 0; 641162738Sariff m->realdev[i] = i; 642162738Sariff } 64370134Scg 644170815Sariff if (MIXER_INIT(m)) { 645170815Sariff snd_mtxlock(m->lock); 646170815Sariff snd_mtxfree(m->lock); 647170815Sariff kobj_delete((kobj_t)m, M_MIXER); 648170815Sariff return (NULL); 649170815Sariff } 65070134Scg 651170815Sariff return (m); 652170815Sariff} 653170815Sariff 654170815Sariffint 655170815Sariffmixer_delete(struct snd_mixer *m) 656170815Sariff{ 657170815Sariff KASSERT(m != NULL, ("NULL snd_mixer")); 658170815Sariff KASSERT(m->type == MIXER_TYPE_SECONDARY, 659170815Sariff ("%s(): illegal mixer type=%d", __func__, m->type)); 660170815Sariff 661184610Salfred /* mixer uninit can sleep --hps */ 662170815Sariff 663170815Sariff MIXER_UNINIT(m); 664170815Sariff 665170815Sariff snd_mtxfree(m->lock); 666170815Sariff kobj_delete((kobj_t)m, M_MIXER); 667170815Sariff 668170815Sariff --mixer_count; 669170815Sariff 670170815Sariff return (0); 671170815Sariff} 672170815Sariff 673170815Sariffstruct snd_mixer * 674170815Sariffmixer_create(device_t dev, kobj_class_t cls, void *devinfo, const char *desc) 675170815Sariff{ 676170815Sariff struct snd_mixer *m; 677170815Sariff 678170815Sariff m = mixer_obj_create(dev, cls, devinfo, MIXER_TYPE_SECONDARY, desc); 679170815Sariff 680170815Sariff if (m != NULL) 681170815Sariff ++mixer_count; 682170815Sariff 683170815Sariff return (m); 684170815Sariff} 685170815Sariff 686170815Sariffint 687170815Sariffmixer_init(device_t dev, kobj_class_t cls, void *devinfo) 688170815Sariff{ 689170815Sariff struct snddev_info *snddev; 690170815Sariff struct snd_mixer *m; 691170815Sariff u_int16_t v; 692170815Sariff struct cdev *pdev; 693170815Sariff int i, unit, devunit, val; 694170815Sariff 695193640Sariff snddev = device_get_softc(dev); 696193640Sariff if (snddev == NULL) 697193640Sariff return (-1); 698193640Sariff 699193640Sariff if (resource_int_value(device_get_name(dev), 700193640Sariff device_get_unit(dev), "eq", &val) == 0 && val != 0) { 701193640Sariff snddev->flags |= SD_F_EQ; 702193640Sariff if ((val & SD_F_EQ_MASK) == val) 703193640Sariff snddev->flags |= val; 704193640Sariff else 705193640Sariff snddev->flags |= SD_F_EQ_DEFAULT; 706193640Sariff snddev->eqpreamp = 0; 707193640Sariff } 708193640Sariff 709170815Sariff m = mixer_obj_create(dev, cls, devinfo, MIXER_TYPE_PRIMARY, NULL); 710170815Sariff if (m == NULL) 711170815Sariff return (-1); 712170815Sariff 71370134Scg for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 714131105Sjosef v = snd_mixerdefaults[i]; 715131105Sjosef 716131064Sjosef if (resource_int_value(device_get_name(dev), 717131064Sjosef device_get_unit(dev), snd_mixernames[i], &val) == 0) { 718131064Sjosef if (val >= 0 && val <= 100) { 719131064Sjosef v = (u_int16_t) val; 720131064Sjosef } 721131064Sjosef } 722130792Sjosef 72370134Scg mixer_set(m, i, v | (v << 8)); 72470134Scg } 72570134Scg 726202166Smav mixer_setrecsrc(m, 0); /* Set default input. */ 72770134Scg 72878362Scg unit = device_get_unit(dev); 729170161Sariff devunit = snd_mkunit(unit, SND_DEV_CTL, 0); 730193640Sariff pdev = make_dev(&mixer_cdevsw, PCMMINOR(devunit), 73178362Scg UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit); 73278362Scg pdev->si_drv1 = m; 733124617Sphk snddev->mixer_dev = pdev; 73470134Scg 735162588Snetchild ++mixer_count; 736162588Snetchild 737162738Sariff if (bootverbose) { 738162738Sariff for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 739162738Sariff if (!(m->devs & (1 << i))) 740162738Sariff continue; 741162738Sariff if (m->realdev[i] != i) { 742162738Sariff device_printf(dev, "Mixer \"%s\" -> \"%s\":", 743162738Sariff snd_mixernames[i], 744162738Sariff (m->realdev[i] < SOUND_MIXER_NRDEVICES) ? 745162738Sariff snd_mixernames[m->realdev[i]] : "none"); 746162738Sariff } else { 747162738Sariff device_printf(dev, "Mixer \"%s\":", 748162738Sariff snd_mixernames[i]); 749162738Sariff } 750162738Sariff if (m->parent[i] < SOUND_MIXER_NRDEVICES) 751162738Sariff printf(" parent=\"%s\"", 752162738Sariff snd_mixernames[m->parent[i]]); 753162738Sariff if (m->child[i] != 0) 754162738Sariff printf(" child=0x%08x", m->child[i]); 755162738Sariff printf("\n"); 756162738Sariff } 757162738Sariff if (snddev->flags & SD_F_SOFTPCMVOL) 758162738Sariff device_printf(dev, "Soft PCM mixer ENABLED\n"); 759193640Sariff if (snddev->flags & SD_F_EQ) 760193640Sariff device_printf(dev, "EQ Treble/Bass ENABLED\n"); 761162738Sariff } 762162738Sariff 763170815Sariff return (0); 76470134Scg} 76570134Scg 76670134Scgint 76765340Scgmixer_uninit(device_t dev) 76858383Scg{ 76958383Scg int i; 770156929Sariff struct snddev_info *d; 77174763Scg struct snd_mixer *m; 772130585Sphk struct cdev *pdev; 77370134Scg 774156929Sariff d = device_get_softc(dev); 77578362Scg pdev = mixer_get_devt(dev); 776156929Sariff if (d == NULL || pdev == NULL || pdev->si_drv1 == NULL) 777156929Sariff return EBADF; 778170815Sariff 77978362Scg m = pdev->si_drv1; 780170815Sariff KASSERT(m != NULL, ("NULL snd_mixer")); 781170815Sariff KASSERT(m->type == MIXER_TYPE_PRIMARY, 782170815Sariff ("%s(): illegal mixer type=%d", __func__, m->type)); 783170815Sariff 78474763Scg snd_mtxlock(m->lock); 78570134Scg 78678362Scg if (m->busy) { 78778362Scg snd_mtxunlock(m->lock); 78878362Scg return EBUSY; 78978362Scg } 79078362Scg 791184610Salfred /* destroy dev can sleep --hps */ 792184610Salfred 793184610Salfred snd_mtxunlock(m->lock); 794184610Salfred 79578362Scg pdev->si_drv1 = NULL; 79678362Scg destroy_dev(pdev); 79778362Scg 798184610Salfred snd_mtxlock(m->lock); 799184610Salfred 80065340Scg for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 80170134Scg mixer_set(m, i, 0); 80270134Scg 80370134Scg mixer_setrecsrc(m, SOUND_MASK_MIC); 80470134Scg 805184610Salfred snd_mtxunlock(m->lock); 806184610Salfred 807184610Salfred /* mixer uninit can sleep --hps */ 808184610Salfred 80970134Scg MIXER_UNINIT(m); 81070134Scg 81174763Scg snd_mtxfree(m->lock); 81270134Scg kobj_delete((kobj_t)m, M_MIXER); 81370134Scg 814156929Sariff d->mixer_dev = NULL; 815156929Sariff 816162588Snetchild --mixer_count; 817162588Snetchild 81865340Scg return 0; 81965340Scg} 82065340Scg 82165340Scgint 82265340Scgmixer_reinit(device_t dev) 82365340Scg{ 82478362Scg struct snd_mixer *m; 825130585Sphk struct cdev *pdev; 82665340Scg int i; 82767652Scg 82878362Scg pdev = mixer_get_devt(dev); 82978362Scg m = pdev->si_drv1; 83074763Scg snd_mtxlock(m->lock); 83170134Scg 83270134Scg i = MIXER_REINIT(m); 83374763Scg if (i) { 83474763Scg snd_mtxunlock(m->lock); 83567652Scg return i; 83674763Scg } 83770134Scg 83867652Scg for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 83970134Scg mixer_set(m, i, m->level[i]); 84070134Scg 84170134Scg mixer_setrecsrc(m, m->recsrc); 84274763Scg snd_mtxunlock(m->lock); 84370134Scg 84467652Scg return 0; 84558383Scg} 84658383Scg 84770680Sjhbstatic int 84870680Sjhbsysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS) 84970680Sjhb{ 85070680Sjhb char devname[32]; 85170680Sjhb int error, dev; 85274763Scg struct snd_mixer *m; 85370618Sjhb 85470680Sjhb m = oidp->oid_arg1; 85574763Scg snd_mtxlock(m->lock); 856164614Sariff strlcpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname)); 857100654Sgreen snd_mtxunlock(m->lock); 85870680Sjhb error = sysctl_handle_string(oidp, &devname[0], sizeof(devname), req); 859100654Sgreen snd_mtxlock(m->lock); 86070680Sjhb if (error == 0 && req->newptr != NULL) { 86170680Sjhb dev = mixer_lookup(devname); 86274763Scg if (dev == -1) { 86374763Scg snd_mtxunlock(m->lock); 86470680Sjhb return EINVAL; 86574763Scg } 86670944Sjhb else if (dev != m->hwvol_mixer) { 86770680Sjhb m->hwvol_mixer = dev; 86870944Sjhb m->hwvol_muted = 0; 86970944Sjhb } 87070680Sjhb } 87174763Scg snd_mtxunlock(m->lock); 87270680Sjhb return error; 87370680Sjhb} 87470618Sjhb 87570680Sjhbint 87670944Sjhbmixer_hwvol_init(device_t dev) 87770680Sjhb{ 87874763Scg struct snd_mixer *m; 879130585Sphk struct cdev *pdev; 88070680Sjhb 88178362Scg pdev = mixer_get_devt(dev); 88278362Scg m = pdev->si_drv1; 88378362Scg 88470680Sjhb m->hwvol_mixer = SOUND_MIXER_VOLUME; 88570680Sjhb m->hwvol_step = 5; 886164614Sariff SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), 887164614Sariff SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 88870680Sjhb OID_AUTO, "hwvol_step", CTLFLAG_RW, &m->hwvol_step, 0, ""); 889164614Sariff SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 890164614Sariff SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 89170680Sjhb OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0, 892100071Smarkm sysctl_hw_snd_hwvol_mixer, "A", ""); 89370680Sjhb return 0; 89470680Sjhb} 89570680Sjhb 89670618Sjhbvoid 897246454Shselaskymixer_hwvol_mute_locked(struct snd_mixer *m) 89870618Sjhb{ 89970944Sjhb if (m->hwvol_muted) { 90070944Sjhb m->hwvol_muted = 0; 90170944Sjhb mixer_set(m, m->hwvol_mixer, m->hwvol_mute_level); 90270680Sjhb } else { 90370944Sjhb m->hwvol_muted++; 90470944Sjhb m->hwvol_mute_level = mixer_get(m, m->hwvol_mixer); 90570680Sjhb mixer_set(m, m->hwvol_mixer, 0); 90670680Sjhb } 90770618Sjhb} 90870618Sjhb 90970618Sjhbvoid 910246454Shselaskymixer_hwvol_mute(device_t dev) 91170618Sjhb{ 91274763Scg struct snd_mixer *m; 913130585Sphk struct cdev *pdev; 91470618Sjhb 91578362Scg pdev = mixer_get_devt(dev); 91678362Scg m = pdev->si_drv1; 91774763Scg snd_mtxlock(m->lock); 918246454Shselasky mixer_hwvol_mute_locked(m); 919246454Shselasky snd_mtxunlock(m->lock); 920246454Shselasky} 921246454Shselasky 922246454Shselaskyvoid 923246454Shselaskymixer_hwvol_step_locked(struct snd_mixer *m, int left_step, int right_step) 924246454Shselasky{ 925246454Shselasky int level, left, right; 926246454Shselasky 92770944Sjhb if (m->hwvol_muted) { 92870944Sjhb m->hwvol_muted = 0; 92970944Sjhb level = m->hwvol_mute_level; 93070944Sjhb } else 93170944Sjhb level = mixer_get(m, m->hwvol_mixer); 93270618Sjhb if (level != -1) { 93370618Sjhb left = level & 0xff; 934246454Shselasky right = (level >> 8) & 0xff; 93570680Sjhb left += left_step * m->hwvol_step; 93670618Sjhb if (left < 0) 93770618Sjhb left = 0; 938246454Shselasky else if (left > 100) 939246454Shselasky left = 100; 94070680Sjhb right += right_step * m->hwvol_step; 94170618Sjhb if (right < 0) 94270618Sjhb right = 0; 943246454Shselasky else if (right > 100) 944246454Shselasky right = 100; 94570680Sjhb mixer_set(m, m->hwvol_mixer, left | right << 8); 94670618Sjhb } 947246454Shselasky} 948246454Shselasky 949246454Shselaskyvoid 950246454Shselaskymixer_hwvol_step(device_t dev, int left_step, int right_step) 951246454Shselasky{ 952246454Shselasky struct snd_mixer *m; 953246454Shselasky struct cdev *pdev; 954246454Shselasky 955246454Shselasky pdev = mixer_get_devt(dev); 956246454Shselasky m = pdev->si_drv1; 957246454Shselasky snd_mtxlock(m->lock); 958246454Shselasky mixer_hwvol_step_locked(m, left_step, right_step); 95974763Scg snd_mtxunlock(m->lock); 96070618Sjhb} 96170618Sjhb 962170815Sariffint 963170815Sariffmixer_busy(struct snd_mixer *m) 964170815Sariff{ 965170815Sariff KASSERT(m != NULL, ("NULL snd_mixer")); 966170815Sariff 967170815Sariff return (m->busy); 968170815Sariff} 969170815Sariff 970170815Sariffint 971170815Sariffmix_set(struct snd_mixer *m, u_int dev, u_int left, u_int right) 972170815Sariff{ 973170815Sariff int ret; 974170815Sariff 975170815Sariff KASSERT(m != NULL, ("NULL snd_mixer")); 976170815Sariff 977170815Sariff snd_mtxlock(m->lock); 978170815Sariff ret = mixer_set(m, dev, left | (right << 8)); 979170815Sariff snd_mtxunlock(m->lock); 980170815Sariff 981170815Sariff return ((ret != 0) ? ENXIO : 0); 982170815Sariff} 983170815Sariff 984170815Sariffint 985170815Sariffmix_get(struct snd_mixer *m, u_int dev) 986170815Sariff{ 987170815Sariff int ret; 988170815Sariff 989170815Sariff KASSERT(m != NULL, ("NULL snd_mixer")); 990170815Sariff 991170815Sariff snd_mtxlock(m->lock); 992170815Sariff ret = mixer_get(m, dev); 993170815Sariff snd_mtxunlock(m->lock); 994170815Sariff 995170815Sariff return (ret); 996170815Sariff} 997170815Sariff 998170815Sariffint 999170815Sariffmix_setrecsrc(struct snd_mixer *m, u_int32_t src) 1000170815Sariff{ 1001170815Sariff int ret; 1002170815Sariff 1003170815Sariff KASSERT(m != NULL, ("NULL snd_mixer")); 1004170815Sariff 1005170815Sariff snd_mtxlock(m->lock); 1006170815Sariff ret = mixer_setrecsrc(m, src); 1007170815Sariff snd_mtxunlock(m->lock); 1008170815Sariff 1009170815Sariff return ((ret != 0) ? ENXIO : 0); 1010170815Sariff} 1011170815Sariff 1012170815Sariffu_int32_t 1013170815Sariffmix_getrecsrc(struct snd_mixer *m) 1014170815Sariff{ 1015170815Sariff u_int32_t ret; 1016170815Sariff 1017170815Sariff KASSERT(m != NULL, ("NULL snd_mixer")); 1018170815Sariff 1019170815Sariff snd_mtxlock(m->lock); 1020170815Sariff ret = mixer_getrecsrc(m); 1021170815Sariff snd_mtxunlock(m->lock); 1022170815Sariff 1023170815Sariff return (ret); 1024170815Sariff} 1025170815Sariff 1026170815Sariffint 1027170815Sariffmix_get_type(struct snd_mixer *m) 1028170815Sariff{ 1029170815Sariff KASSERT(m != NULL, ("NULL snd_mixer")); 1030170815Sariff 1031170815Sariff return (m->type); 1032170815Sariff} 1033170815Sariff 1034359887Shselaskydevice_t 1035359887Shselaskymix_get_dev(struct snd_mixer *m) 1036359887Shselasky{ 1037359887Shselasky KASSERT(m != NULL, ("NULL snd_mixer")); 1038359887Shselasky 1039359887Shselasky return (m->dev); 1040359887Shselasky} 1041359887Shselasky 104278362Scg/* ----------------------------------------------------------------------- */ 104350724Scg 104478362Scgstatic int 1045130585Sphkmixer_open(struct cdev *i_dev, int flags, int mode, struct thread *td) 104678362Scg{ 1047170815Sariff struct snddev_info *d; 104878362Scg struct snd_mixer *m; 104978362Scg 1050170815Sariff 1051170815Sariff if (i_dev == NULL || i_dev->si_drv1 == NULL) 1052170815Sariff return (EBADF); 1053170815Sariff 105482181Scg m = i_dev->si_drv1; 1055170815Sariff d = device_get_softc(m->dev); 1056358879Shselasky if (PCM_DETACHING(d) || !PCM_REGISTERED(d)) 1057170815Sariff return (EBADF); 1058170815Sariff 1059170815Sariff /* XXX Need Giant magic entry ??? */ 1060170815Sariff 106182181Scg snd_mtxlock(m->lock); 1062168247Sariff m->busy = 1; 1063170815Sariff snd_mtxunlock(m->lock); 106482181Scg 1065170815Sariff return (0); 106678362Scg} 106778362Scg 106878362Scgstatic int 1069130585Sphkmixer_close(struct cdev *i_dev, int flags, int mode, struct thread *td) 107078362Scg{ 1071170815Sariff struct snddev_info *d; 107278362Scg struct snd_mixer *m; 1073170815Sariff int ret; 107478362Scg 1075170815Sariff if (i_dev == NULL || i_dev->si_drv1 == NULL) 1076170815Sariff return (EBADF); 1077170815Sariff 107882181Scg m = i_dev->si_drv1; 1079170815Sariff d = device_get_softc(m->dev); 1080170815Sariff if (!PCM_REGISTERED(d)) 1081170815Sariff return (EBADF); 1082170815Sariff 1083170815Sariff /* XXX Need Giant magic entry ??? */ 1084170815Sariff 108582181Scg snd_mtxlock(m->lock); 1086170815Sariff ret = (m->busy == 0) ? EBADF : 0; 1087168247Sariff m->busy = 0; 1088170815Sariff snd_mtxunlock(m->lock); 108978362Scg 1090170815Sariff return (ret); 109178362Scg} 109278362Scg 1093170815Sariffstatic int 1094193640Sariffmixer_ioctl_channel(struct cdev *dev, u_long cmd, caddr_t arg, int mode, 1095193640Sariff struct thread *td, int from) 1096193640Sariff{ 1097193640Sariff struct snddev_info *d; 1098193640Sariff struct snd_mixer *m; 1099193640Sariff struct pcm_channel *c, *rdch, *wrch; 1100193640Sariff pid_t pid; 1101193640Sariff int j, ret; 1102193640Sariff 1103193640Sariff if (td == NULL || td->td_proc == NULL) 1104193640Sariff return (-1); 1105193640Sariff 1106193640Sariff m = dev->si_drv1; 1107193640Sariff d = device_get_softc(m->dev); 1108193640Sariff j = cmd & 0xff; 1109193640Sariff 1110193640Sariff switch (j) { 1111193640Sariff case SOUND_MIXER_PCM: 1112193640Sariff case SOUND_MIXER_RECLEV: 1113193640Sariff case SOUND_MIXER_DEVMASK: 1114193640Sariff case SOUND_MIXER_CAPS: 1115193640Sariff case SOUND_MIXER_STEREODEVS: 1116193640Sariff break; 1117193640Sariff default: 1118193640Sariff return (-1); 1119193640Sariff break; 1120193640Sariff } 1121193640Sariff 1122193640Sariff pid = td->td_proc->p_pid; 1123193640Sariff rdch = NULL; 1124193640Sariff wrch = NULL; 1125193640Sariff c = NULL; 1126193640Sariff ret = -1; 1127193640Sariff 1128193640Sariff /* 1129193640Sariff * This is unfair. Imagine single proc opening multiple 1130193640Sariff * instances of same direction. What we do right now 1131193640Sariff * is looking for the first matching proc/pid, and just 1132193640Sariff * that. Nothing more. Consider it done. 1133193640Sariff * 1134193640Sariff * The better approach of controlling specific channel 1135193640Sariff * pcm or rec volume is by doing mixer ioctl 1136193640Sariff * (SNDCTL_DSP_[SET|GET][PLAY|REC]VOL / SOUND_MIXER_[PCM|RECLEV] 1137193640Sariff * on its open fd, rather than cracky mixer bypassing here. 1138193640Sariff */ 1139193640Sariff CHN_FOREACH(c, d, channels.pcm.opened) { 1140193640Sariff CHN_LOCK(c); 1141193640Sariff if (c->pid != pid || 1142193640Sariff !(c->feederflags & (1 << FEEDER_VOLUME))) { 1143193640Sariff CHN_UNLOCK(c); 1144193640Sariff continue; 1145193640Sariff } 1146193640Sariff if (rdch == NULL && c->direction == PCMDIR_REC) { 1147193640Sariff rdch = c; 1148193640Sariff if (j == SOUND_MIXER_RECLEV) 1149193640Sariff goto mixer_ioctl_channel_proc; 1150193640Sariff } else if (wrch == NULL && c->direction == PCMDIR_PLAY) { 1151193640Sariff wrch = c; 1152193640Sariff if (j == SOUND_MIXER_PCM) 1153193640Sariff goto mixer_ioctl_channel_proc; 1154193640Sariff } 1155193640Sariff CHN_UNLOCK(c); 1156193640Sariff if (rdch != NULL && wrch != NULL) 1157193640Sariff break; 1158193640Sariff } 1159193640Sariff 1160193640Sariff if (rdch == NULL && wrch == NULL) 1161193640Sariff return (-1); 1162193640Sariff 1163193640Sariff if ((j == SOUND_MIXER_DEVMASK || j == SOUND_MIXER_CAPS || 1164193640Sariff j == SOUND_MIXER_STEREODEVS) && 1165202150Smav (cmd & ~0xff) == MIXER_READ(0)) { 1166193640Sariff snd_mtxlock(m->lock); 1167193640Sariff *(int *)arg = mix_getdevs(m); 1168193640Sariff snd_mtxunlock(m->lock); 1169193640Sariff if (rdch != NULL) 1170193640Sariff *(int *)arg |= SOUND_MASK_RECLEV; 1171193640Sariff if (wrch != NULL) 1172193640Sariff *(int *)arg |= SOUND_MASK_PCM; 1173193640Sariff ret = 0; 1174193640Sariff } 1175193640Sariff 1176193640Sariff return (ret); 1177193640Sariff 1178193640Sariffmixer_ioctl_channel_proc: 1179193640Sariff 1180193640Sariff KASSERT(c != NULL, ("%s(): NULL channel", __func__)); 1181193640Sariff CHN_LOCKASSERT(c); 1182193640Sariff 1183202150Smav if ((cmd & ~0xff) == MIXER_WRITE(0)) { 1184193640Sariff int left, right, center; 1185193640Sariff 1186193640Sariff left = *(int *)arg & 0x7f; 1187193640Sariff right = (*(int *)arg >> 8) & 0x7f; 1188193640Sariff center = (left + right) >> 1; 1189193640Sariff chn_setvolume_multi(c, SND_VOL_C_PCM, left, right, center); 1190202150Smav } else if ((cmd & ~0xff) == MIXER_READ(0)) { 1191193640Sariff *(int *)arg = CHN_GETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_FL); 1192193640Sariff *(int *)arg |= 1193193640Sariff CHN_GETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_FR) << 8; 1194193640Sariff } 1195193640Sariff 1196193640Sariff CHN_UNLOCK(c); 1197193640Sariff 1198193640Sariff return (0); 1199193640Sariff} 1200193640Sariff 1201193640Sariffstatic int 1202170815Sariffmixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, 1203170815Sariff struct thread *td) 1204170815Sariff{ 1205170815Sariff struct snddev_info *d; 1206170815Sariff int ret; 1207170815Sariff 1208170815Sariff if (i_dev == NULL || i_dev->si_drv1 == NULL) 1209170815Sariff return (EBADF); 1210170815Sariff 1211170815Sariff d = device_get_softc(((struct snd_mixer *)i_dev->si_drv1)->dev); 1212358879Shselasky if (PCM_DETACHING(d) || !PCM_REGISTERED(d)) 1213170815Sariff return (EBADF); 1214170815Sariff 1215170815Sariff PCM_GIANT_ENTER(d); 1216170815Sariff PCM_ACQUIRE_QUICK(d); 1217170815Sariff 1218193640Sariff ret = -1; 1219170815Sariff 1220193640Sariff if (mixer_bypass != 0 && (d->flags & SD_F_VPC)) 1221193640Sariff ret = mixer_ioctl_channel(i_dev, cmd, arg, mode, td, 1222193640Sariff MIXER_CMD_CDEV); 1223193640Sariff 1224193640Sariff if (ret == -1) 1225193640Sariff ret = mixer_ioctl_cmd(i_dev, cmd, arg, mode, td, 1226193640Sariff MIXER_CMD_CDEV); 1227193640Sariff 1228170815Sariff PCM_RELEASE_QUICK(d); 1229170815Sariff PCM_GIANT_LEAVE(d); 1230170815Sariff 1231170815Sariff return (ret); 1232170815Sariff} 1233170815Sariff 1234271193Smavstatic void 1235271193Smavmixer_mixerinfo(struct snd_mixer *m, mixer_info *mi) 1236271193Smav{ 1237271193Smav bzero((void *)mi, sizeof(*mi)); 1238271193Smav strlcpy(mi->id, m->name, sizeof(mi->id)); 1239271193Smav strlcpy(mi->name, device_get_desc(m->dev), sizeof(mi->name)); 1240271193Smav mi->modify_counter = m->modify_counter; 1241271193Smav} 1242271193Smav 1243170815Sariff/* 1244170815Sariff * XXX Make sure you can guarantee concurrency safety before calling this 1245193640Sariff * function, be it through Giant, PCM_*, etc ! 1246170815Sariff */ 124778362Scgint 1248170815Sariffmixer_ioctl_cmd(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, 1249170815Sariff struct thread *td, int from) 125078362Scg{ 125178670Scg struct snd_mixer *m; 1252202150Smav int ret = EINVAL, *arg_i = (int *)arg; 125378362Scg int v = -1, j = cmd & 0xff; 125478362Scg 1255187030Smav /* 1256187030Smav * Certain ioctls may be made on any type of device (audio, mixer, 1257187030Smav * and MIDI). Handle those special cases here. 1258187030Smav */ 1259187030Smav if (IOCGROUP(cmd) == 'X') { 1260187030Smav switch (cmd) { 1261187030Smav case SNDCTL_SYSINFO: 1262187030Smav sound_oss_sysinfo((oss_sysinfo *)arg); 1263187030Smav return (0); 1264187030Smav case SNDCTL_CARDINFO: 1265187030Smav return (sound_oss_card_info((oss_card_info *)arg)); 1266187030Smav case SNDCTL_AUDIOINFO: 1267187030Smav case SNDCTL_AUDIOINFO_EX: 1268187030Smav case SNDCTL_ENGINEINFO: 1269187030Smav return (dsp_oss_audioinfo(i_dev, (oss_audioinfo *)arg)); 1270187030Smav case SNDCTL_MIXERINFO: 1271187030Smav return (mixer_oss_mixerinfo(i_dev, (oss_mixerinfo *)arg)); 1272187030Smav } 1273187036Smav return (EINVAL); 1274187030Smav } 1275187030Smav 127678362Scg m = i_dev->si_drv1; 1277156929Sariff 1278156929Sariff if (m == NULL) 1279170815Sariff return (EBADF); 128078362Scg 128178362Scg snd_mtxlock(m->lock); 1282170815Sariff if (from == MIXER_CMD_CDEV && !m->busy) { 1283156929Sariff snd_mtxunlock(m->lock); 1284170815Sariff return (EBADF); 1285156929Sariff } 1286202150Smav switch (cmd) { 1287202150Smav case SNDCTL_DSP_GET_RECSRC_NAMES: 1288202150Smav bcopy((void *)&m->enuminfo, arg, sizeof(oss_mixer_enuminfo)); 1289202150Smav ret = 0; 1290202150Smav goto done; 1291202150Smav case SNDCTL_DSP_GET_RECSRC: 1292202150Smav ret = mixer_get_recroute(m, arg_i); 1293202150Smav goto done; 1294202150Smav case SNDCTL_DSP_SET_RECSRC: 1295202150Smav ret = mixer_set_recroute(m, *arg_i); 1296202150Smav goto done; 1297202150Smav case OSS_GETVERSION: 1298202150Smav *arg_i = SOUND_VERSION; 1299202150Smav ret = 0; 1300202150Smav goto done; 1301271193Smav case SOUND_MIXER_INFO: 1302271193Smav mixer_mixerinfo(m, (mixer_info *)arg); 1303271193Smav ret = 0; 1304271193Smav goto done; 1305202150Smav } 1306202150Smav if ((cmd & ~0xff) == MIXER_WRITE(0)) { 130778362Scg if (j == SOUND_MIXER_RECSRC) 130878362Scg ret = mixer_setrecsrc(m, *arg_i); 130978362Scg else 131078362Scg ret = mixer_set(m, j, *arg_i); 131178362Scg snd_mtxunlock(m->lock); 1312170815Sariff return ((ret == 0) ? 0 : ENXIO); 131378362Scg } 1314202150Smav if ((cmd & ~0xff) == MIXER_READ(0)) { 131578362Scg switch (j) { 1316202150Smav case SOUND_MIXER_DEVMASK: 1317202150Smav case SOUND_MIXER_CAPS: 1318202150Smav case SOUND_MIXER_STEREODEVS: 131978362Scg v = mix_getdevs(m); 132078362Scg break; 1321202150Smav case SOUND_MIXER_RECMASK: 132278362Scg v = mix_getrecdevs(m); 132378362Scg break; 1324202150Smav case SOUND_MIXER_RECSRC: 132578362Scg v = mixer_getrecsrc(m); 132678362Scg break; 132778362Scg default: 132878362Scg v = mixer_get(m, j); 132978362Scg } 133078362Scg *arg_i = v; 133178362Scg snd_mtxunlock(m->lock); 1332170815Sariff return ((v != -1) ? 0 : ENXIO); 133378362Scg } 1334202150Smavdone: 133578362Scg snd_mtxunlock(m->lock); 1336170815Sariff return (ret); 133778362Scg} 133878362Scg 133978362Scgstatic void 1340170161Sariffmixer_clone(void *arg, 1341170161Sariff#if __FreeBSD_version >= 600034 1342170161Sariff struct ucred *cred, 1343170161Sariff#endif 1344170161Sariff char *name, int namelen, struct cdev **dev) 134578362Scg{ 1346170161Sariff struct snddev_info *d; 134778362Scg 1348130640Sphk if (*dev != NULL) 134978362Scg return; 135078362Scg if (strcmp(name, "mixer") == 0) { 1351170161Sariff d = devclass_get_softc(pcm_devclass, snd_unit); 1352170815Sariff if (PCM_REGISTERED(d) && d->mixer_dev != NULL) { 1353170161Sariff *dev = d->mixer_dev; 1354144389Sphk dev_ref(*dev); 1355144389Sphk } 135678362Scg } 135778362Scg} 135878362Scg 135978362Scgstatic void 136078362Scgmixer_sysinit(void *p) 136178362Scg{ 1362170161Sariff if (mixer_ehtag != NULL) 1363170161Sariff return; 136478362Scg mixer_ehtag = EVENTHANDLER_REGISTER(dev_clone, mixer_clone, 0, 1000); 136578362Scg} 136678362Scg 136778362Scgstatic void 136878362Scgmixer_sysuninit(void *p) 136978362Scg{ 1370170161Sariff if (mixer_ehtag == NULL) 1371170161Sariff return; 1372170161Sariff EVENTHANDLER_DEREGISTER(dev_clone, mixer_ehtag); 1373170161Sariff mixer_ehtag = NULL; 137478362Scg} 137578362Scg 137678362ScgSYSINIT(mixer_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysinit, NULL); 137778362ScgSYSUNINIT(mixer_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysuninit, NULL); 137878362Scg 1379162588Snetchild/** 1380162588Snetchild * @brief Handler for SNDCTL_MIXERINFO 1381162588Snetchild * 1382162588Snetchild * This function searches for a mixer based on the numeric ID stored 1383162588Snetchild * in oss_miserinfo::dev. If set to -1, then information about the 1384162588Snetchild * current mixer handling the request is provided. Note, however, that 1385162588Snetchild * this ioctl may be made with any sound device (audio, mixer, midi). 1386162588Snetchild * 1387162588Snetchild * @note Caller must not hold any PCM device, channel, or mixer locks. 1388162588Snetchild * 1389162588Snetchild * See http://manuals.opensound.com/developer/SNDCTL_MIXERINFO.html for 1390162588Snetchild * more information. 1391162588Snetchild * 1392162588Snetchild * @param i_dev character device on which the ioctl arrived 1393162588Snetchild * @param arg user argument (oss_mixerinfo *) 1394162588Snetchild * 1395162588Snetchild * @retval EINVAL oss_mixerinfo::dev specified a bad value 1396162588Snetchild * @retval 0 success 1397162588Snetchild */ 1398162588Snetchildint 1399162588Snetchildmixer_oss_mixerinfo(struct cdev *i_dev, oss_mixerinfo *mi) 1400162588Snetchild{ 1401162588Snetchild struct snddev_info *d; 1402162588Snetchild struct snd_mixer *m; 1403170815Sariff int nmix, i; 140478362Scg 1405162588Snetchild /* 1406162588Snetchild * If probing the device handling the ioctl, make sure it's a mixer 1407162588Snetchild * device. (This ioctl is valid on audio, mixer, and midi devices.) 1408162588Snetchild */ 1409170815Sariff if (mi->dev == -1 && i_dev->si_devsw != &mixer_cdevsw) 1410170815Sariff return (EINVAL); 1411162588Snetchild 1412162606Snetchild d = NULL; 1413162588Snetchild m = NULL; 1414162588Snetchild nmix = 0; 1415162588Snetchild 1416162588Snetchild /* 1417162588Snetchild * There's a 1:1 relationship between mixers and PCM devices, so 1418162588Snetchild * begin by iterating over PCM devices and search for our mixer. 1419162588Snetchild */ 1420170235Sariff for (i = 0; pcm_devclass != NULL && 1421170235Sariff i < devclass_get_maxunit(pcm_devclass); i++) { 1422162588Snetchild d = devclass_get_softc(pcm_devclass, i); 1423358879Shselasky if (PCM_DETACHING(d) || !PCM_REGISTERED(d)) 1424162588Snetchild continue; 1425162588Snetchild 1426170815Sariff /* XXX Need Giant magic entry */ 1427170815Sariff 1428162588Snetchild /* See the note in function docblock. */ 1429193640Sariff PCM_UNLOCKASSERT(d); 1430193640Sariff PCM_LOCK(d); 1431162588Snetchild 1432170815Sariff if (d->mixer_dev != NULL && d->mixer_dev->si_drv1 != NULL && 1433170815Sariff ((mi->dev == -1 && d->mixer_dev == i_dev) || 1434170815Sariff mi->dev == nmix)) { 1435170815Sariff m = d->mixer_dev->si_drv1; 1436170815Sariff mtx_lock(m->lock); 1437162588Snetchild 1438170815Sariff /* 1439170815Sariff * At this point, the following synchronization stuff 1440170815Sariff * has happened: 1441170815Sariff * - a specific PCM device is locked. 1442170815Sariff * - a specific mixer device has been locked, so be 1443170815Sariff * sure to unlock when existing. 1444170815Sariff */ 1445170815Sariff bzero((void *)mi, sizeof(*mi)); 1446170815Sariff mi->dev = nmix; 1447170815Sariff snprintf(mi->id, sizeof(mi->id), "mixer%d", i); 1448170815Sariff strlcpy(mi->name, m->name, sizeof(mi->name)); 1449170815Sariff mi->modify_counter = m->modify_counter; 1450170815Sariff mi->card_number = i; 1451170815Sariff /* 1452170815Sariff * Currently, FreeBSD assumes 1:1 relationship between 1453170815Sariff * a pcm and mixer devices, so this is hardcoded to 0. 1454170815Sariff */ 1455170815Sariff mi->port_number = 0; 1456162588Snetchild 1457170815Sariff /** 1458170815Sariff * @todo Fill in @sa oss_mixerinfo::mixerhandle. 1459170815Sariff * @note From 4Front: "mixerhandle is an arbitrary 1460170815Sariff * string that identifies the mixer better than 1461170815Sariff * the device number (mixerinfo.dev). Device 1462170815Sariff * numbers may change depending on the order the 1463170815Sariff * drivers are loaded. However the handle should 1464170815Sariff * remain the same provided that the sound card 1465170815Sariff * is not moved to another PCI slot." 1466170815Sariff */ 1467162588Snetchild 1468170815Sariff /** 1469170815Sariff * @note 1470170815Sariff * @sa oss_mixerinfo::magic is a reserved field. 1471170815Sariff * 1472170815Sariff * @par 1473170815Sariff * From 4Front: "magic is usually 0. However some 1474170815Sariff * devices may have dedicated setup utilities and the 1475170815Sariff * magic field may contain an unique driver specific 1476170815Sariff * value (managed by [4Front])." 1477170815Sariff */ 1478162588Snetchild 1479170815Sariff mi->enabled = device_is_attached(m->dev) ? 1 : 0; 1480170815Sariff /** 1481170815Sariff * The only flag for @sa oss_mixerinfo::caps is 1482170815Sariff * currently MIXER_CAP_VIRTUAL, which I'm not sure we 1483170815Sariff * really worry about. 1484170815Sariff */ 1485170815Sariff /** 1486170815Sariff * Mixer extensions currently aren't supported, so 1487170815Sariff * leave @sa oss_mixerinfo::nrext blank for now. 1488170815Sariff */ 1489170815Sariff /** 1490170815Sariff * @todo Fill in @sa oss_mixerinfo::priority (requires 1491170815Sariff * touching drivers?) 1492170815Sariff * @note The priority field is for mixer applets to 1493170815Sariff * determine which mixer should be the default, with 0 1494170815Sariff * being least preferred and 10 being most preferred. 1495170815Sariff * From 4Front: "OSS drivers like ICH use higher 1496170815Sariff * values (10) because such chips are known to be used 1497170815Sariff * only on motherboards. Drivers for high end pro 1498170815Sariff * devices use 0 because they will never be the 1499170815Sariff * default mixer. Other devices use values 1 to 9 1500170815Sariff * depending on the estimated probability of being the 1501170815Sariff * default device. 1502170815Sariff * 1503170815Sariff * XXX Described by Hannu@4Front, but not found in 1504170815Sariff * soundcard.h. 1505231378Sed strlcpy(mi->devnode, devtoname(d->mixer_dev), 1506170815Sariff sizeof(mi->devnode)); 1507170815Sariff mi->legacy_device = i; 1508170815Sariff */ 1509170815Sariff mtx_unlock(m->lock); 1510170815Sariff } else 1511170815Sariff ++nmix; 1512162588Snetchild 1513193640Sariff PCM_UNLOCK(d); 1514162588Snetchild 1515170815Sariff if (m != NULL) 1516170815Sariff return (0); 1517170815Sariff } 1518162588Snetchild 1519170815Sariff return (EINVAL); 1520162588Snetchild} 1521184610Salfred 1522184610Salfred/* 1523184610Salfred * Allow the sound driver to use the mixer lock to protect its mixer 1524184610Salfred * data: 1525184610Salfred */ 1526184610Salfredstruct mtx * 1527184610Salfredmixer_get_lock(struct snd_mixer *m) 1528184610Salfred{ 1529184610Salfred if (m->lock == NULL) { 1530184610Salfred return (&Giant); 1531184610Salfred } 1532184610Salfred return (m->lock); 1533184610Salfred} 1534246421Shselasky 1535246421Shselaskyint 1536246421Shselaskymix_get_locked(struct snd_mixer *m, u_int dev, int *pleft, int *pright) 1537246421Shselasky{ 1538246421Shselasky int level; 1539246421Shselasky 1540246421Shselasky level = mixer_get(m, dev); 1541246421Shselasky if (level < 0) { 1542246421Shselasky *pright = *pleft = -1; 1543246421Shselasky return (-1); 1544246421Shselasky } 1545246421Shselasky 1546246421Shselasky *pleft = level & 0xFF; 1547246421Shselasky *pright = (level >> 8) & 0xFF; 1548246421Shselasky 1549246421Shselasky return (0); 1550246421Shselasky} 1551246421Shselasky 1552246421Shselaskyint 1553246421Shselaskymix_set_locked(struct snd_mixer *m, u_int dev, int left, int right) 1554246421Shselasky{ 1555246421Shselasky int level; 1556246421Shselasky 1557246421Shselasky level = (left & 0xFF) | ((right & 0xFF) << 8); 1558246421Shselasky 1559246421Shselasky return (mixer_set(m, dev, level)); 1560246421Shselasky} 1561