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/11/sys/dev/sound/pcm/mixer.c 359886 2020-04-13 16:31:46Z hselasky $"); 3982180Scg 40227293Sedstatic MALLOC_DEFINE(M_MIXER, "mixer", "mixer"); 4170134Scg 42193640Sariffstatic int mixer_bypass = 1; 43267992ShselaskySYSCTL_INT(_hw_snd, OID_AUTO, vpc_mixer_bypass, CTLFLAG_RWTUN, 44193640Sariff &mixer_bypass, 0, 45193640Sariff "control channel pcm/rec volume, bypassing real mixer device"); 46193640Sariff 4774763Scg#define MIXER_NAMELEN 16 4874763Scgstruct snd_mixer { 4974763Scg KOBJ_FIELDS; 5074763Scg void *devinfo; 51168264Sariff int busy; 5274763Scg int hwvol_muted; 5374763Scg int hwvol_mixer; 5474763Scg int hwvol_step; 55170815Sariff int type; 56150825Snetchild device_t dev; 5774763Scg u_int32_t hwvol_mute_level; 5874763Scg u_int32_t devs; 5974763Scg u_int32_t recdevs; 6074763Scg u_int32_t recsrc; 6174763Scg u_int16_t level[32]; 62162828Sariff u_int8_t parent[32]; 63162738Sariff u_int32_t child[32]; 64162828Sariff u_int8_t realdev[32]; 6574763Scg char name[MIXER_NAMELEN]; 66107285Scg struct mtx *lock; 67162588Snetchild oss_mixer_enuminfo enuminfo; 68162588Snetchild /** 69162588Snetchild * Counter is incremented when applications change any of this 70162588Snetchild * mixer's controls. A change in value indicates that persistent 71162588Snetchild * mixer applications should update their displays. 72162588Snetchild */ 73162588Snetchild int modify_counter; 7474763Scg}; 7574763Scg 7650724Scgstatic u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = { 7750724Scg [SOUND_MIXER_VOLUME] = 75, 7850724Scg [SOUND_MIXER_BASS] = 50, 7950724Scg [SOUND_MIXER_TREBLE] = 50, 8062947Stanimura [SOUND_MIXER_SYNTH] = 75, 8150724Scg [SOUND_MIXER_PCM] = 75, 8250724Scg [SOUND_MIXER_SPEAKER] = 75, 8350724Scg [SOUND_MIXER_LINE] = 75, 84359883Shselasky [SOUND_MIXER_MIC] = 25, 8550724Scg [SOUND_MIXER_CD] = 75, 86152422Sariff [SOUND_MIXER_IGAIN] = 0, 8750724Scg [SOUND_MIXER_LINE1] = 75, 8850724Scg [SOUND_MIXER_VIDEO] = 75, 89202166Smav [SOUND_MIXER_RECLEV] = 75, 9053203Scg [SOUND_MIXER_OGAIN] = 50, 9179044Scg [SOUND_MIXER_MONITOR] = 75, 9250724Scg}; 9350724Scg 9470680Sjhbstatic char* snd_mixernames[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES; 9570680Sjhb 9678362Scgstatic d_open_t mixer_open; 9778362Scgstatic d_close_t mixer_close; 98170815Sariffstatic d_ioctl_t mixer_ioctl; 9978362Scg 10078362Scgstatic struct cdevsw mixer_cdevsw = { 101126080Sphk .d_version = D_VERSION, 102111815Sphk .d_open = mixer_open, 103111815Sphk .d_close = mixer_close, 104111815Sphk .d_ioctl = mixer_ioctl, 105111815Sphk .d_name = "mixer", 10678362Scg}; 10778362Scg 108162588Snetchild/** 109162588Snetchild * Keeps a count of mixer devices; used only by OSSv4 SNDCTL_SYSINFO ioctl. 110162588Snetchild */ 111162588Snetchildint mixer_count = 0; 112162588Snetchild 113170161Sariffstatic eventhandler_tag mixer_ehtag = NULL; 11478362Scg 115130585Sphkstatic struct cdev * 11678362Scgmixer_get_devt(device_t dev) 11778362Scg{ 118124617Sphk struct snddev_info *snddev; 11978362Scg 120124617Sphk snddev = device_get_softc(dev); 12178362Scg 122124617Sphk return snddev->mixer_dev; 12378362Scg} 12478362Scg 12565390Speterstatic int 12670680Sjhbmixer_lookup(char *devname) 12770680Sjhb{ 12870680Sjhb int i; 12970680Sjhb 13070680Sjhb for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 13170680Sjhb if (strncmp(devname, snd_mixernames[i], 13270680Sjhb strlen(snd_mixernames[i])) == 0) 13370680Sjhb return i; 13470680Sjhb return -1; 13570680Sjhb} 13670680Sjhb 137170815Sariff#define MIXER_SET_UNLOCK(x, y) do { \ 138170815Sariff if ((y) != 0) \ 139170815Sariff snd_mtxunlock((x)->lock); \ 140193640Sariff} while (0) 141170815Sariff 142170815Sariff#define MIXER_SET_LOCK(x, y) do { \ 143170815Sariff if ((y) != 0) \ 144170815Sariff snd_mtxlock((x)->lock); \ 145193640Sariff} while (0) 146170815Sariff 14770680Sjhbstatic int 148170815Sariffmixer_set_softpcmvol(struct snd_mixer *m, struct snddev_info *d, 149193640Sariff u_int left, u_int right) 15065390Speter{ 151170161Sariff struct pcm_channel *c; 152170815Sariff int dropmtx, acquiremtx; 153162738Sariff 154358878Shselasky if (PCM_DETACHING(d) || !PCM_REGISTERED(d)) 155170815Sariff return (EINVAL); 156170161Sariff 157170815Sariff if (mtx_owned(m->lock)) 158170815Sariff dropmtx = 1; 159170815Sariff else 160170815Sariff dropmtx = 0; 161170815Sariff 162170815Sariff if (!(d->flags & SD_F_MPSAFE) || mtx_owned(d->lock) != 0) 163170815Sariff acquiremtx = 0; 164170815Sariff else 165170815Sariff acquiremtx = 1; 166170815Sariff 167170815Sariff /* 168170815Sariff * Be careful here. If we're coming from cdev ioctl, it is OK to 169170815Sariff * not doing locking AT ALL (except on individual channel) since 170170815Sariff * we've been heavily guarded by pcm cv, or if we're still 171170815Sariff * under Giant influence. Since we also have mix_* calls, we cannot 172170815Sariff * assume such protection and just do the lock as usuall. 173170815Sariff */ 174170815Sariff MIXER_SET_UNLOCK(m, dropmtx); 175170815Sariff MIXER_SET_LOCK(d, acquiremtx); 176170815Sariff 177193640Sariff CHN_FOREACH(c, d, channels.pcm.busy) { 178193640Sariff CHN_LOCK(c); 179193640Sariff if (c->direction == PCMDIR_PLAY && 180193640Sariff (c->feederflags & (1 << FEEDER_VOLUME))) 181193640Sariff chn_setvolume_multi(c, SND_VOL_C_MASTER, left, right, 182193640Sariff (left + right) >> 1); 183193640Sariff CHN_UNLOCK(c); 184162738Sariff } 185170161Sariff 186170815Sariff MIXER_SET_UNLOCK(d, acquiremtx); 187170815Sariff MIXER_SET_LOCK(m, dropmtx); 188170161Sariff 189170815Sariff return (0); 190162738Sariff} 191162738Sariff 192162738Sariffstatic int 193193640Sariffmixer_set_eq(struct snd_mixer *m, struct snddev_info *d, 194193640Sariff u_int dev, u_int level) 195162738Sariff{ 196193640Sariff struct pcm_channel *c; 197193640Sariff struct pcm_feeder *f; 198193640Sariff int tone, dropmtx, acquiremtx; 199193640Sariff 200193640Sariff if (dev == SOUND_MIXER_TREBLE) 201193640Sariff tone = FEEDEQ_TREBLE; 202193640Sariff else if (dev == SOUND_MIXER_BASS) 203193640Sariff tone = FEEDEQ_BASS; 204193640Sariff else 205193640Sariff return (EINVAL); 206193640Sariff 207358878Shselasky if (PCM_DETACHING(d) || !PCM_REGISTERED(d)) 208193640Sariff return (EINVAL); 209193640Sariff 210193640Sariff if (mtx_owned(m->lock)) 211193640Sariff dropmtx = 1; 212193640Sariff else 213193640Sariff dropmtx = 0; 214193640Sariff 215193640Sariff if (!(d->flags & SD_F_MPSAFE) || mtx_owned(d->lock) != 0) 216193640Sariff acquiremtx = 0; 217193640Sariff else 218193640Sariff acquiremtx = 1; 219193640Sariff 220193640Sariff /* 221193640Sariff * Be careful here. If we're coming from cdev ioctl, it is OK to 222193640Sariff * not doing locking AT ALL (except on individual channel) since 223193640Sariff * we've been heavily guarded by pcm cv, or if we're still 224193640Sariff * under Giant influence. Since we also have mix_* calls, we cannot 225193640Sariff * assume such protection and just do the lock as usuall. 226193640Sariff */ 227193640Sariff MIXER_SET_UNLOCK(m, dropmtx); 228193640Sariff MIXER_SET_LOCK(d, acquiremtx); 229193640Sariff 230193640Sariff CHN_FOREACH(c, d, channels.pcm.busy) { 231193640Sariff CHN_LOCK(c); 232193640Sariff f = chn_findfeeder(c, FEEDER_EQ); 233193640Sariff if (f != NULL) 234193640Sariff (void)FEEDER_SET(f, tone, level); 235193640Sariff CHN_UNLOCK(c); 236193640Sariff } 237193640Sariff 238193640Sariff MIXER_SET_UNLOCK(d, acquiremtx); 239193640Sariff MIXER_SET_LOCK(m, dropmtx); 240193640Sariff 241193640Sariff return (0); 242193640Sariff} 243193640Sariff 244193640Sariffstatic int 245193640Sariffmixer_set(struct snd_mixer *m, u_int dev, u_int lev) 246193640Sariff{ 247150825Snetchild struct snddev_info *d; 248193640Sariff u_int l, r, tl, tr; 249162738Sariff u_int32_t parent = SOUND_MIXER_NONE, child = 0; 250162738Sariff u_int32_t realdev; 251170815Sariff int i, dropmtx; 25270134Scg 253162738Sariff if (m == NULL || dev >= SOUND_MIXER_NRDEVICES || 254162738Sariff (0 == (m->devs & (1 << dev)))) 25570134Scg return -1; 25670134Scg 25770134Scg l = min((lev & 0x00ff), 100); 25870134Scg r = min(((lev & 0xff00) >> 8), 100); 259162738Sariff realdev = m->realdev[dev]; 26070134Scg 261162738Sariff d = device_get_softc(m->dev); 262162738Sariff if (d == NULL) 263162738Sariff return -1; 26470134Scg 265170815Sariff /* It is safe to drop this mutex due to Giant. */ 266170815Sariff if (!(d->flags & SD_F_MPSAFE) && mtx_owned(m->lock) != 0) 267170815Sariff dropmtx = 1; 268170815Sariff else 269170815Sariff dropmtx = 0; 270170815Sariff 271170815Sariff MIXER_SET_UNLOCK(m, dropmtx); 272170815Sariff 273162738Sariff /* TODO: recursive handling */ 274162738Sariff parent = m->parent[dev]; 275162738Sariff if (parent >= SOUND_MIXER_NRDEVICES) 276162738Sariff parent = SOUND_MIXER_NONE; 277162738Sariff if (parent == SOUND_MIXER_NONE) 278162738Sariff child = m->child[dev]; 279162738Sariff 280162738Sariff if (parent != SOUND_MIXER_NONE) { 281162738Sariff tl = (l * (m->level[parent] & 0x00ff)) / 100; 282162738Sariff tr = (r * ((m->level[parent] & 0xff00) >> 8)) / 100; 283162738Sariff if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) 284170815Sariff (void)mixer_set_softpcmvol(m, d, tl, tr); 285162738Sariff else if (realdev != SOUND_MIXER_NONE && 286170815Sariff MIXER_SET(m, realdev, tl, tr) < 0) { 287170815Sariff MIXER_SET_LOCK(m, dropmtx); 288162738Sariff return -1; 289170815Sariff } 290162738Sariff } else if (child != 0) { 291162738Sariff for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 292162738Sariff if (!(child & (1 << i)) || m->parent[i] != dev) 293162738Sariff continue; 294162738Sariff realdev = m->realdev[i]; 295162738Sariff tl = (l * (m->level[i] & 0x00ff)) / 100; 296162738Sariff tr = (r * ((m->level[i] & 0xff00) >> 8)) / 100; 297193640Sariff if (i == SOUND_MIXER_PCM && 298193640Sariff (d->flags & SD_F_SOFTPCMVOL)) 299170815Sariff (void)mixer_set_softpcmvol(m, d, tl, tr); 300162738Sariff else if (realdev != SOUND_MIXER_NONE) 301162738Sariff MIXER_SET(m, realdev, tl, tr); 302150825Snetchild } 303162738Sariff realdev = m->realdev[dev]; 304162738Sariff if (realdev != SOUND_MIXER_NONE && 305170815Sariff MIXER_SET(m, realdev, l, r) < 0) { 306170815Sariff MIXER_SET_LOCK(m, dropmtx); 307162738Sariff return -1; 308170815Sariff } 309150825Snetchild } else { 310162738Sariff if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) 311170815Sariff (void)mixer_set_softpcmvol(m, d, l, r); 312193640Sariff else if ((dev == SOUND_MIXER_TREBLE || 313193640Sariff dev == SOUND_MIXER_BASS) && (d->flags & SD_F_EQ)) 314193640Sariff (void)mixer_set_eq(m, d, dev, (l + r) >> 1); 315162738Sariff else if (realdev != SOUND_MIXER_NONE && 316170815Sariff MIXER_SET(m, realdev, l, r) < 0) { 317170815Sariff MIXER_SET_LOCK(m, dropmtx); 318150825Snetchild return -1; 319170815Sariff } 320150825Snetchild } 321150825Snetchild 322193640Sariff MIXER_SET_LOCK(m, dropmtx); 323193640Sariff 324162738Sariff m->level[dev] = l | (r << 8); 325336889Shselasky m->modify_counter++; 326162738Sariff 32770134Scg return 0; 32865390Speter} 32965390Speter 33065390Speterstatic int 33174763Scgmixer_get(struct snd_mixer *mixer, int dev) 33265390Speter{ 33370134Scg if ((dev < SOUND_MIXER_NRDEVICES) && (mixer->devs & (1 << dev))) 33470134Scg return mixer->level[dev]; 335170815Sariff else 336170815Sariff return -1; 33765390Speter} 33865390Speter 33965390Speterstatic int 34074763Scgmixer_setrecsrc(struct snd_mixer *mixer, u_int32_t src) 34165390Speter{ 342170815Sariff struct snddev_info *d; 343193640Sariff u_int32_t recsrc; 344170815Sariff int dropmtx; 345170815Sariff 346170815Sariff d = device_get_softc(mixer->dev); 347170815Sariff if (d == NULL) 348170815Sariff return -1; 349170815Sariff if (!(d->flags & SD_F_MPSAFE) && mtx_owned(mixer->lock) != 0) 350170815Sariff dropmtx = 1; 351170815Sariff else 352170815Sariff dropmtx = 0; 35370134Scg src &= mixer->recdevs; 35470134Scg if (src == 0) 355202166Smav src = mixer->recdevs & SOUND_MASK_MIC; 356202166Smav if (src == 0) 357202166Smav src = mixer->recdevs & SOUND_MASK_MONITOR; 358202166Smav if (src == 0) 359202166Smav src = mixer->recdevs & SOUND_MASK_LINE; 360202166Smav if (src == 0 && mixer->recdevs != 0) 361202166Smav src = (1 << (ffs(mixer->recdevs) - 1)); 362170815Sariff /* It is safe to drop this mutex due to Giant. */ 363170815Sariff MIXER_SET_UNLOCK(mixer, dropmtx); 364193640Sariff recsrc = MIXER_SETRECSRC(mixer, src); 365170815Sariff MIXER_SET_LOCK(mixer, dropmtx); 366193640Sariff 367193640Sariff mixer->recsrc = recsrc; 368193640Sariff 36965390Speter return 0; 37065390Speter} 37165390Speter 37265390Speterstatic int 37374763Scgmixer_getrecsrc(struct snd_mixer *mixer) 37465390Speter{ 37570134Scg return mixer->recsrc; 37665390Speter} 37765390Speter 378162588Snetchild/** 379162588Snetchild * @brief Retrieve the route number of the current recording device 380162588Snetchild * 381162588Snetchild * OSSv4 assigns routing numbers to recording devices, unlike the previous 382162588Snetchild * API which relied on a fixed table of device numbers and names. This 383162588Snetchild * function returns the routing number of the device currently selected 384162588Snetchild * for recording. 385162588Snetchild * 386162588Snetchild * For now, this function is kind of a goofy compatibility stub atop the 387162588Snetchild * existing sound system. (For example, in theory, the old sound system 388162588Snetchild * allows multiple recording devices to be specified via a bitmask.) 389162588Snetchild * 390162588Snetchild * @param m mixer context container thing 391162588Snetchild * 392162588Snetchild * @retval 0 success 393162588Snetchild * @retval EIDRM no recording device found (generally not possible) 394162588Snetchild * @todo Ask about error code 395162588Snetchild */ 396162588Snetchildstatic int 397162588Snetchildmixer_get_recroute(struct snd_mixer *m, int *route) 398162588Snetchild{ 399162588Snetchild int i, cnt; 400162588Snetchild 401162588Snetchild cnt = 0; 402162588Snetchild 403162588Snetchild for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 404162588Snetchild /** @todo can user set a multi-device mask? (== or &?) */ 405162588Snetchild if ((1 << i) == m->recsrc) 406162588Snetchild break; 407162588Snetchild if ((1 << i) & m->recdevs) 408162588Snetchild ++cnt; 409162588Snetchild } 410162588Snetchild 411162588Snetchild if (i == SOUND_MIXER_NRDEVICES) 412162588Snetchild return EIDRM; 413162588Snetchild 414162588Snetchild *route = cnt; 415162588Snetchild return 0; 416162588Snetchild} 417162588Snetchild 418162588Snetchild/** 419162588Snetchild * @brief Select a device for recording 420162588Snetchild * 421162588Snetchild * This function sets a recording source based on a recording device's 422162588Snetchild * routing number. Said number is translated to an old school recdev 423162588Snetchild * mask and passed over mixer_setrecsrc. 424162588Snetchild * 425162588Snetchild * @param m mixer context container thing 426162588Snetchild * 427162588Snetchild * @retval 0 success(?) 428162588Snetchild * @retval EINVAL User specified an invalid device number 429162588Snetchild * @retval otherwise error from mixer_setrecsrc 430162588Snetchild */ 431162588Snetchildstatic int 432162588Snetchildmixer_set_recroute(struct snd_mixer *m, int route) 433162588Snetchild{ 434162588Snetchild int i, cnt, ret; 435162588Snetchild 436162588Snetchild ret = 0; 437162588Snetchild cnt = 0; 438162588Snetchild 439162588Snetchild for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 440162588Snetchild if ((1 << i) & m->recdevs) { 441162588Snetchild if (route == cnt) 442162588Snetchild break; 443162588Snetchild ++cnt; 444162588Snetchild } 445162588Snetchild } 446162588Snetchild 447162588Snetchild if (i == SOUND_MIXER_NRDEVICES) 448162588Snetchild ret = EINVAL; 449162588Snetchild else 450162588Snetchild ret = mixer_setrecsrc(m, (1 << i)); 451162588Snetchild 452162588Snetchild return ret; 453162588Snetchild} 454162588Snetchild 45570134Scgvoid 45674763Scgmix_setdevs(struct snd_mixer *m, u_int32_t v) 45770134Scg{ 458162791Sariff struct snddev_info *d; 459162738Sariff int i; 460162738Sariff 461162738Sariff if (m == NULL) 462162738Sariff return; 463162791Sariff 464162791Sariff d = device_get_softc(m->dev); 465162738Sariff if (d != NULL && (d->flags & SD_F_SOFTPCMVOL)) 466150825Snetchild v |= SOUND_MASK_PCM; 467193640Sariff if (d != NULL && (d->flags & SD_F_EQ)) 468193640Sariff v |= SOUND_MASK_TREBLE | SOUND_MASK_BASS; 469162738Sariff for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 470162738Sariff if (m->parent[i] < SOUND_MIXER_NRDEVICES) 471162738Sariff v |= 1 << m->parent[i]; 472162738Sariff v |= m->child[i]; 473162738Sariff } 47470134Scg m->devs = v; 47570134Scg} 47670134Scg 477162588Snetchild/** 478162588Snetchild * @brief Record mask of available recording devices 479162588Snetchild * 480162588Snetchild * Calling functions are responsible for defining the mask of available 481162588Snetchild * recording devices. This function records that value in a structure 482162588Snetchild * used by the rest of the mixer code. 483162588Snetchild * 484162588Snetchild * This function also populates a structure used by the SNDCTL_DSP_*RECSRC* 485162588Snetchild * family of ioctls that are part of OSSV4. All recording device labels 486162588Snetchild * are concatenated in ascending order corresponding to their routing 487162588Snetchild * numbers. (Ex: a system might have 0 => 'vol', 1 => 'cd', 2 => 'line', 488162588Snetchild * etc.) For now, these labels are just the standard recording device 489162588Snetchild * names (cd, line1, etc.), but will eventually be fully dynamic and user 490162588Snetchild * controlled. 491162588Snetchild * 492162588Snetchild * @param m mixer device context container thing 493162588Snetchild * @param v mask of recording devices 494162588Snetchild */ 49570134Scgvoid 49674763Scgmix_setrecdevs(struct snd_mixer *m, u_int32_t v) 49770134Scg{ 498162588Snetchild oss_mixer_enuminfo *ei; 499162588Snetchild char *loc; 500162588Snetchild int i, nvalues, nwrote, nleft, ncopied; 501162588Snetchild 502162588Snetchild ei = &m->enuminfo; 503162588Snetchild 504162588Snetchild nvalues = 0; 505162588Snetchild nwrote = 0; 506162588Snetchild nleft = sizeof(ei->strings); 507162588Snetchild loc = ei->strings; 508162588Snetchild 509162588Snetchild for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 510162588Snetchild if ((1 << i) & v) { 511162588Snetchild ei->strindex[nvalues] = nwrote; 512162588Snetchild ncopied = strlcpy(loc, snd_mixernames[i], nleft) + 1; 513162588Snetchild /* strlcpy retval doesn't include terminator */ 514162588Snetchild 515162588Snetchild nwrote += ncopied; 516162588Snetchild nleft -= ncopied; 517162588Snetchild nvalues++; 518162588Snetchild 519162588Snetchild /* 520162588Snetchild * XXX I don't think this should ever be possible. 521162588Snetchild * Even with a move to dynamic device/channel names, 522162588Snetchild * each label is limited to ~16 characters, so that'd 523162588Snetchild * take a LOT to fill this buffer. 524162588Snetchild */ 525162588Snetchild if ((nleft <= 0) || (nvalues >= OSS_ENUM_MAXVALUE)) { 526162588Snetchild device_printf(m->dev, 527162588Snetchild "mix_setrecdevs: Not enough room to store device names--please file a bug report.\n"); 528162588Snetchild device_printf(m->dev, 529162588Snetchild "mix_setrecdevs: Please include details about your sound hardware, OS version, etc.\n"); 530162588Snetchild break; 531162588Snetchild } 532162588Snetchild 533162588Snetchild loc = &ei->strings[nwrote]; 534162588Snetchild } 535162588Snetchild } 536162588Snetchild 537162588Snetchild /* 538162588Snetchild * NB: The SNDCTL_DSP_GET_RECSRC_NAMES ioctl ignores the dev 539162588Snetchild * and ctrl fields. 540162588Snetchild */ 541162588Snetchild ei->nvalues = nvalues; 54270134Scg m->recdevs = v; 54370134Scg} 54470134Scg 545162738Sariffvoid 546162738Sariffmix_setparentchild(struct snd_mixer *m, u_int32_t parent, u_int32_t childs) 547162738Sariff{ 548162738Sariff u_int32_t mask = 0; 549162738Sariff int i; 550162738Sariff 551162738Sariff if (m == NULL || parent >= SOUND_MIXER_NRDEVICES) 552162738Sariff return; 553162738Sariff for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 554162738Sariff if (i == parent) 555162738Sariff continue; 556162738Sariff if (childs & (1 << i)) { 557162738Sariff mask |= 1 << i; 558162738Sariff if (m->parent[i] < SOUND_MIXER_NRDEVICES) 559162738Sariff m->child[m->parent[i]] &= ~(1 << i); 560162738Sariff m->parent[i] = parent; 561162738Sariff m->child[i] = 0; 562162738Sariff } 563162738Sariff } 564162738Sariff mask &= ~(1 << parent); 565162738Sariff m->child[parent] = mask; 566162738Sariff} 567162738Sariff 568162738Sariffvoid 569162738Sariffmix_setrealdev(struct snd_mixer *m, u_int32_t dev, u_int32_t realdev) 570162738Sariff{ 571162738Sariff if (m == NULL || dev >= SOUND_MIXER_NRDEVICES || 572162738Sariff !(realdev == SOUND_MIXER_NONE || realdev < SOUND_MIXER_NRDEVICES)) 573162738Sariff return; 574162738Sariff m->realdev[dev] = realdev; 575162738Sariff} 576162738Sariff 57770134Scgu_int32_t 578162738Sariffmix_getparent(struct snd_mixer *m, u_int32_t dev) 579162738Sariff{ 580162738Sariff if (m == NULL || dev >= SOUND_MIXER_NRDEVICES) 581162738Sariff return SOUND_MIXER_NONE; 582162738Sariff return m->parent[dev]; 583162738Sariff} 584162738Sariff 585162738Sariffu_int32_t 586162738Sariffmix_getchild(struct snd_mixer *m, u_int32_t dev) 587162738Sariff{ 588162738Sariff if (m == NULL || dev >= SOUND_MIXER_NRDEVICES) 589162738Sariff return 0; 590162738Sariff return m->child[dev]; 591162738Sariff} 592162738Sariff 593162738Sariffu_int32_t 59474763Scgmix_getdevs(struct snd_mixer *m) 59570134Scg{ 59670134Scg return m->devs; 59770134Scg} 59870134Scg 59970134Scgu_int32_t 60074763Scgmix_getrecdevs(struct snd_mixer *m) 60170134Scg{ 60270134Scg return m->recdevs; 60370134Scg} 60470134Scg 60570134Scgvoid * 60674763Scgmix_getdevinfo(struct snd_mixer *m) 60770134Scg{ 60870134Scg return m->devinfo; 60970134Scg} 61070134Scg 611170815Sariffstatic struct snd_mixer * 612170815Sariffmixer_obj_create(device_t dev, kobj_class_t cls, void *devinfo, 613170815Sariff int type, const char *desc) 61470134Scg{ 61574763Scg struct snd_mixer *m; 616170815Sariff int i; 61770134Scg 618170815Sariff KASSERT(dev != NULL && cls != NULL && devinfo != NULL, 619170815Sariff ("%s(): NULL data dev=%p cls=%p devinfo=%p", 620170815Sariff __func__, dev, cls, devinfo)); 621170815Sariff KASSERT(type == MIXER_TYPE_PRIMARY || type == MIXER_TYPE_SECONDARY, 622170815Sariff ("invalid mixer type=%d", type)); 623170815Sariff 624111119Simp m = (struct snd_mixer *)kobj_create(cls, M_MIXER, M_WAITOK | M_ZERO); 625170815Sariff snprintf(m->name, sizeof(m->name), "%s:mixer", 626170815Sariff device_get_nameunit(dev)); 627170815Sariff if (desc != NULL) { 628170815Sariff strlcat(m->name, ":", sizeof(m->name)); 629170815Sariff strlcat(m->name, desc, sizeof(m->name)); 630170815Sariff } 631170815Sariff m->lock = snd_mtxcreate(m->name, (type == MIXER_TYPE_PRIMARY) ? 632170815Sariff "primary pcm mixer" : "secondary pcm mixer"); 633170815Sariff m->type = type; 63470134Scg m->devinfo = devinfo; 63578362Scg m->busy = 0; 636150825Snetchild m->dev = dev; 637170815Sariff for (i = 0; i < (sizeof(m->parent) / sizeof(m->parent[0])); i++) { 638162738Sariff m->parent[i] = SOUND_MIXER_NONE; 639162738Sariff m->child[i] = 0; 640162738Sariff m->realdev[i] = i; 641162738Sariff } 64270134Scg 643170815Sariff if (MIXER_INIT(m)) { 644170815Sariff snd_mtxlock(m->lock); 645170815Sariff snd_mtxfree(m->lock); 646170815Sariff kobj_delete((kobj_t)m, M_MIXER); 647170815Sariff return (NULL); 648170815Sariff } 64970134Scg 650170815Sariff return (m); 651170815Sariff} 652170815Sariff 653170815Sariffint 654170815Sariffmixer_delete(struct snd_mixer *m) 655170815Sariff{ 656170815Sariff KASSERT(m != NULL, ("NULL snd_mixer")); 657170815Sariff KASSERT(m->type == MIXER_TYPE_SECONDARY, 658170815Sariff ("%s(): illegal mixer type=%d", __func__, m->type)); 659170815Sariff 660184610Salfred /* mixer uninit can sleep --hps */ 661170815Sariff 662170815Sariff MIXER_UNINIT(m); 663170815Sariff 664170815Sariff snd_mtxfree(m->lock); 665170815Sariff kobj_delete((kobj_t)m, M_MIXER); 666170815Sariff 667170815Sariff --mixer_count; 668170815Sariff 669170815Sariff return (0); 670170815Sariff} 671170815Sariff 672170815Sariffstruct snd_mixer * 673170815Sariffmixer_create(device_t dev, kobj_class_t cls, void *devinfo, const char *desc) 674170815Sariff{ 675170815Sariff struct snd_mixer *m; 676170815Sariff 677170815Sariff m = mixer_obj_create(dev, cls, devinfo, MIXER_TYPE_SECONDARY, desc); 678170815Sariff 679170815Sariff if (m != NULL) 680170815Sariff ++mixer_count; 681170815Sariff 682170815Sariff return (m); 683170815Sariff} 684170815Sariff 685170815Sariffint 686170815Sariffmixer_init(device_t dev, kobj_class_t cls, void *devinfo) 687170815Sariff{ 688170815Sariff struct snddev_info *snddev; 689170815Sariff struct snd_mixer *m; 690170815Sariff u_int16_t v; 691170815Sariff struct cdev *pdev; 692170815Sariff int i, unit, devunit, val; 693170815Sariff 694193640Sariff snddev = device_get_softc(dev); 695193640Sariff if (snddev == NULL) 696193640Sariff return (-1); 697193640Sariff 698193640Sariff if (resource_int_value(device_get_name(dev), 699193640Sariff device_get_unit(dev), "eq", &val) == 0 && val != 0) { 700193640Sariff snddev->flags |= SD_F_EQ; 701193640Sariff if ((val & SD_F_EQ_MASK) == val) 702193640Sariff snddev->flags |= val; 703193640Sariff else 704193640Sariff snddev->flags |= SD_F_EQ_DEFAULT; 705193640Sariff snddev->eqpreamp = 0; 706193640Sariff } 707193640Sariff 708170815Sariff m = mixer_obj_create(dev, cls, devinfo, MIXER_TYPE_PRIMARY, NULL); 709170815Sariff if (m == NULL) 710170815Sariff return (-1); 711170815Sariff 71270134Scg for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 713131105Sjosef v = snd_mixerdefaults[i]; 714131105Sjosef 715131064Sjosef if (resource_int_value(device_get_name(dev), 716131064Sjosef device_get_unit(dev), snd_mixernames[i], &val) == 0) { 717131064Sjosef if (val >= 0 && val <= 100) { 718131064Sjosef v = (u_int16_t) val; 719131064Sjosef } 720131064Sjosef } 721130792Sjosef 72270134Scg mixer_set(m, i, v | (v << 8)); 72370134Scg } 72470134Scg 725202166Smav mixer_setrecsrc(m, 0); /* Set default input. */ 72670134Scg 72778362Scg unit = device_get_unit(dev); 728170161Sariff devunit = snd_mkunit(unit, SND_DEV_CTL, 0); 729193640Sariff pdev = make_dev(&mixer_cdevsw, PCMMINOR(devunit), 73078362Scg UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit); 73178362Scg pdev->si_drv1 = m; 732124617Sphk snddev->mixer_dev = pdev; 73370134Scg 734162588Snetchild ++mixer_count; 735162588Snetchild 736162738Sariff if (bootverbose) { 737162738Sariff for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 738162738Sariff if (!(m->devs & (1 << i))) 739162738Sariff continue; 740162738Sariff if (m->realdev[i] != i) { 741162738Sariff device_printf(dev, "Mixer \"%s\" -> \"%s\":", 742162738Sariff snd_mixernames[i], 743162738Sariff (m->realdev[i] < SOUND_MIXER_NRDEVICES) ? 744162738Sariff snd_mixernames[m->realdev[i]] : "none"); 745162738Sariff } else { 746162738Sariff device_printf(dev, "Mixer \"%s\":", 747162738Sariff snd_mixernames[i]); 748162738Sariff } 749162738Sariff if (m->parent[i] < SOUND_MIXER_NRDEVICES) 750162738Sariff printf(" parent=\"%s\"", 751162738Sariff snd_mixernames[m->parent[i]]); 752162738Sariff if (m->child[i] != 0) 753162738Sariff printf(" child=0x%08x", m->child[i]); 754162738Sariff printf("\n"); 755162738Sariff } 756162738Sariff if (snddev->flags & SD_F_SOFTPCMVOL) 757162738Sariff device_printf(dev, "Soft PCM mixer ENABLED\n"); 758193640Sariff if (snddev->flags & SD_F_EQ) 759193640Sariff device_printf(dev, "EQ Treble/Bass ENABLED\n"); 760162738Sariff } 761162738Sariff 762170815Sariff return (0); 76370134Scg} 76470134Scg 76570134Scgint 76665340Scgmixer_uninit(device_t dev) 76758383Scg{ 76858383Scg int i; 769156929Sariff struct snddev_info *d; 77074763Scg struct snd_mixer *m; 771130585Sphk struct cdev *pdev; 77270134Scg 773156929Sariff d = device_get_softc(dev); 77478362Scg pdev = mixer_get_devt(dev); 775156929Sariff if (d == NULL || pdev == NULL || pdev->si_drv1 == NULL) 776156929Sariff return EBADF; 777170815Sariff 77878362Scg m = pdev->si_drv1; 779170815Sariff KASSERT(m != NULL, ("NULL snd_mixer")); 780170815Sariff KASSERT(m->type == MIXER_TYPE_PRIMARY, 781170815Sariff ("%s(): illegal mixer type=%d", __func__, m->type)); 782170815Sariff 78374763Scg snd_mtxlock(m->lock); 78470134Scg 78578362Scg if (m->busy) { 78678362Scg snd_mtxunlock(m->lock); 78778362Scg return EBUSY; 78878362Scg } 78978362Scg 790184610Salfred /* destroy dev can sleep --hps */ 791184610Salfred 792184610Salfred snd_mtxunlock(m->lock); 793184610Salfred 79478362Scg pdev->si_drv1 = NULL; 79578362Scg destroy_dev(pdev); 79678362Scg 797184610Salfred snd_mtxlock(m->lock); 798184610Salfred 79965340Scg for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 80070134Scg mixer_set(m, i, 0); 80170134Scg 80270134Scg mixer_setrecsrc(m, SOUND_MASK_MIC); 80370134Scg 804184610Salfred snd_mtxunlock(m->lock); 805184610Salfred 806184610Salfred /* mixer uninit can sleep --hps */ 807184610Salfred 80870134Scg MIXER_UNINIT(m); 80970134Scg 81074763Scg snd_mtxfree(m->lock); 81170134Scg kobj_delete((kobj_t)m, M_MIXER); 81270134Scg 813156929Sariff d->mixer_dev = NULL; 814156929Sariff 815162588Snetchild --mixer_count; 816162588Snetchild 81765340Scg return 0; 81865340Scg} 81965340Scg 82065340Scgint 82165340Scgmixer_reinit(device_t dev) 82265340Scg{ 82378362Scg struct snd_mixer *m; 824130585Sphk struct cdev *pdev; 82565340Scg int i; 82667652Scg 82778362Scg pdev = mixer_get_devt(dev); 82878362Scg m = pdev->si_drv1; 82974763Scg snd_mtxlock(m->lock); 83070134Scg 83170134Scg i = MIXER_REINIT(m); 83274763Scg if (i) { 83374763Scg snd_mtxunlock(m->lock); 83467652Scg return i; 83574763Scg } 83670134Scg 83767652Scg for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 83870134Scg mixer_set(m, i, m->level[i]); 83970134Scg 84070134Scg mixer_setrecsrc(m, m->recsrc); 84174763Scg snd_mtxunlock(m->lock); 84270134Scg 84367652Scg return 0; 84458383Scg} 84558383Scg 84670680Sjhbstatic int 84770680Sjhbsysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS) 84870680Sjhb{ 84970680Sjhb char devname[32]; 85070680Sjhb int error, dev; 85174763Scg struct snd_mixer *m; 85270618Sjhb 85370680Sjhb m = oidp->oid_arg1; 85474763Scg snd_mtxlock(m->lock); 855164614Sariff strlcpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname)); 856100654Sgreen snd_mtxunlock(m->lock); 85770680Sjhb error = sysctl_handle_string(oidp, &devname[0], sizeof(devname), req); 858100654Sgreen snd_mtxlock(m->lock); 85970680Sjhb if (error == 0 && req->newptr != NULL) { 86070680Sjhb dev = mixer_lookup(devname); 86174763Scg if (dev == -1) { 86274763Scg snd_mtxunlock(m->lock); 86370680Sjhb return EINVAL; 86474763Scg } 86570944Sjhb else if (dev != m->hwvol_mixer) { 86670680Sjhb m->hwvol_mixer = dev; 86770944Sjhb m->hwvol_muted = 0; 86870944Sjhb } 86970680Sjhb } 87074763Scg snd_mtxunlock(m->lock); 87170680Sjhb return error; 87270680Sjhb} 87370618Sjhb 87470680Sjhbint 87570944Sjhbmixer_hwvol_init(device_t dev) 87670680Sjhb{ 87774763Scg struct snd_mixer *m; 878130585Sphk struct cdev *pdev; 87970680Sjhb 88078362Scg pdev = mixer_get_devt(dev); 88178362Scg m = pdev->si_drv1; 88278362Scg 88370680Sjhb m->hwvol_mixer = SOUND_MIXER_VOLUME; 88470680Sjhb m->hwvol_step = 5; 885164614Sariff SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), 886164614Sariff SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 887280442Shselasky OID_AUTO, "hwvol_step", CTLFLAG_RWTUN, &m->hwvol_step, 0, ""); 888164614Sariff SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 889164614Sariff SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 890280442Shselasky OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RWTUN, m, 0, 891100071Smarkm sysctl_hw_snd_hwvol_mixer, "A", ""); 89270680Sjhb return 0; 89370680Sjhb} 89470680Sjhb 89570618Sjhbvoid 896246454Shselaskymixer_hwvol_mute_locked(struct snd_mixer *m) 89770618Sjhb{ 89870944Sjhb if (m->hwvol_muted) { 89970944Sjhb m->hwvol_muted = 0; 90070944Sjhb mixer_set(m, m->hwvol_mixer, m->hwvol_mute_level); 90170680Sjhb } else { 90270944Sjhb m->hwvol_muted++; 90370944Sjhb m->hwvol_mute_level = mixer_get(m, m->hwvol_mixer); 90470680Sjhb mixer_set(m, m->hwvol_mixer, 0); 90570680Sjhb } 90670618Sjhb} 90770618Sjhb 90870618Sjhbvoid 909246454Shselaskymixer_hwvol_mute(device_t dev) 91070618Sjhb{ 91174763Scg struct snd_mixer *m; 912130585Sphk struct cdev *pdev; 91370618Sjhb 91478362Scg pdev = mixer_get_devt(dev); 91578362Scg m = pdev->si_drv1; 91674763Scg snd_mtxlock(m->lock); 917246454Shselasky mixer_hwvol_mute_locked(m); 918246454Shselasky snd_mtxunlock(m->lock); 919246454Shselasky} 920246454Shselasky 921246454Shselaskyvoid 922246454Shselaskymixer_hwvol_step_locked(struct snd_mixer *m, int left_step, int right_step) 923246454Shselasky{ 924246454Shselasky int level, left, right; 925246454Shselasky 92670944Sjhb if (m->hwvol_muted) { 92770944Sjhb m->hwvol_muted = 0; 92870944Sjhb level = m->hwvol_mute_level; 92970944Sjhb } else 93070944Sjhb level = mixer_get(m, m->hwvol_mixer); 93170618Sjhb if (level != -1) { 93270618Sjhb left = level & 0xff; 933246454Shselasky right = (level >> 8) & 0xff; 93470680Sjhb left += left_step * m->hwvol_step; 93570618Sjhb if (left < 0) 93670618Sjhb left = 0; 937246454Shselasky else if (left > 100) 938246454Shselasky left = 100; 93970680Sjhb right += right_step * m->hwvol_step; 94070618Sjhb if (right < 0) 94170618Sjhb right = 0; 942246454Shselasky else if (right > 100) 943246454Shselasky right = 100; 94470680Sjhb mixer_set(m, m->hwvol_mixer, left | right << 8); 94570618Sjhb } 946246454Shselasky} 947246454Shselasky 948246454Shselaskyvoid 949246454Shselaskymixer_hwvol_step(device_t dev, int left_step, int right_step) 950246454Shselasky{ 951246454Shselasky struct snd_mixer *m; 952246454Shselasky struct cdev *pdev; 953246454Shselasky 954246454Shselasky pdev = mixer_get_devt(dev); 955246454Shselasky m = pdev->si_drv1; 956246454Shselasky snd_mtxlock(m->lock); 957246454Shselasky mixer_hwvol_step_locked(m, left_step, right_step); 95874763Scg snd_mtxunlock(m->lock); 95970618Sjhb} 96070618Sjhb 961170815Sariffint 962170815Sariffmixer_busy(struct snd_mixer *m) 963170815Sariff{ 964170815Sariff KASSERT(m != NULL, ("NULL snd_mixer")); 965170815Sariff 966170815Sariff return (m->busy); 967170815Sariff} 968170815Sariff 969170815Sariffint 970170815Sariffmix_set(struct snd_mixer *m, u_int dev, u_int left, u_int right) 971170815Sariff{ 972170815Sariff int ret; 973170815Sariff 974170815Sariff KASSERT(m != NULL, ("NULL snd_mixer")); 975170815Sariff 976170815Sariff snd_mtxlock(m->lock); 977170815Sariff ret = mixer_set(m, dev, left | (right << 8)); 978170815Sariff snd_mtxunlock(m->lock); 979170815Sariff 980170815Sariff return ((ret != 0) ? ENXIO : 0); 981170815Sariff} 982170815Sariff 983170815Sariffint 984170815Sariffmix_get(struct snd_mixer *m, u_int dev) 985170815Sariff{ 986170815Sariff int ret; 987170815Sariff 988170815Sariff KASSERT(m != NULL, ("NULL snd_mixer")); 989170815Sariff 990170815Sariff snd_mtxlock(m->lock); 991170815Sariff ret = mixer_get(m, dev); 992170815Sariff snd_mtxunlock(m->lock); 993170815Sariff 994170815Sariff return (ret); 995170815Sariff} 996170815Sariff 997170815Sariffint 998170815Sariffmix_setrecsrc(struct snd_mixer *m, u_int32_t src) 999170815Sariff{ 1000170815Sariff int ret; 1001170815Sariff 1002170815Sariff KASSERT(m != NULL, ("NULL snd_mixer")); 1003170815Sariff 1004170815Sariff snd_mtxlock(m->lock); 1005170815Sariff ret = mixer_setrecsrc(m, src); 1006170815Sariff snd_mtxunlock(m->lock); 1007170815Sariff 1008170815Sariff return ((ret != 0) ? ENXIO : 0); 1009170815Sariff} 1010170815Sariff 1011170815Sariffu_int32_t 1012170815Sariffmix_getrecsrc(struct snd_mixer *m) 1013170815Sariff{ 1014170815Sariff u_int32_t ret; 1015170815Sariff 1016170815Sariff KASSERT(m != NULL, ("NULL snd_mixer")); 1017170815Sariff 1018170815Sariff snd_mtxlock(m->lock); 1019170815Sariff ret = mixer_getrecsrc(m); 1020170815Sariff snd_mtxunlock(m->lock); 1021170815Sariff 1022170815Sariff return (ret); 1023170815Sariff} 1024170815Sariff 1025170815Sariffint 1026170815Sariffmix_get_type(struct snd_mixer *m) 1027170815Sariff{ 1028170815Sariff KASSERT(m != NULL, ("NULL snd_mixer")); 1029170815Sariff 1030170815Sariff return (m->type); 1031170815Sariff} 1032170815Sariff 1033359886Shselaskydevice_t 1034359886Shselaskymix_get_dev(struct snd_mixer *m) 1035359886Shselasky{ 1036359886Shselasky KASSERT(m != NULL, ("NULL snd_mixer")); 1037359886Shselasky 1038359886Shselasky return (m->dev); 1039359886Shselasky} 1040359886Shselasky 104178362Scg/* ----------------------------------------------------------------------- */ 104250724Scg 104378362Scgstatic int 1044130585Sphkmixer_open(struct cdev *i_dev, int flags, int mode, struct thread *td) 104578362Scg{ 1046170815Sariff struct snddev_info *d; 104778362Scg struct snd_mixer *m; 104878362Scg 1049170815Sariff 1050170815Sariff if (i_dev == NULL || i_dev->si_drv1 == NULL) 1051170815Sariff return (EBADF); 1052170815Sariff 105382181Scg m = i_dev->si_drv1; 1054170815Sariff d = device_get_softc(m->dev); 1055358878Shselasky if (PCM_DETACHING(d) || !PCM_REGISTERED(d)) 1056170815Sariff return (EBADF); 1057170815Sariff 1058170815Sariff /* XXX Need Giant magic entry ??? */ 1059170815Sariff 106082181Scg snd_mtxlock(m->lock); 1061168247Sariff m->busy = 1; 1062170815Sariff snd_mtxunlock(m->lock); 106382181Scg 1064170815Sariff return (0); 106578362Scg} 106678362Scg 106778362Scgstatic int 1068130585Sphkmixer_close(struct cdev *i_dev, int flags, int mode, struct thread *td) 106978362Scg{ 1070170815Sariff struct snddev_info *d; 107178362Scg struct snd_mixer *m; 1072170815Sariff int ret; 107378362Scg 1074170815Sariff if (i_dev == NULL || i_dev->si_drv1 == NULL) 1075170815Sariff return (EBADF); 1076170815Sariff 107782181Scg m = i_dev->si_drv1; 1078170815Sariff d = device_get_softc(m->dev); 1079170815Sariff if (!PCM_REGISTERED(d)) 1080170815Sariff return (EBADF); 1081170815Sariff 1082170815Sariff /* XXX Need Giant magic entry ??? */ 1083170815Sariff 108482181Scg snd_mtxlock(m->lock); 1085170815Sariff ret = (m->busy == 0) ? EBADF : 0; 1086168247Sariff m->busy = 0; 1087170815Sariff snd_mtxunlock(m->lock); 108878362Scg 1089170815Sariff return (ret); 109078362Scg} 109178362Scg 1092170815Sariffstatic int 1093193640Sariffmixer_ioctl_channel(struct cdev *dev, u_long cmd, caddr_t arg, int mode, 1094193640Sariff struct thread *td, int from) 1095193640Sariff{ 1096193640Sariff struct snddev_info *d; 1097193640Sariff struct snd_mixer *m; 1098193640Sariff struct pcm_channel *c, *rdch, *wrch; 1099193640Sariff pid_t pid; 1100193640Sariff int j, ret; 1101193640Sariff 1102193640Sariff if (td == NULL || td->td_proc == NULL) 1103193640Sariff return (-1); 1104193640Sariff 1105193640Sariff m = dev->si_drv1; 1106193640Sariff d = device_get_softc(m->dev); 1107193640Sariff j = cmd & 0xff; 1108193640Sariff 1109193640Sariff switch (j) { 1110193640Sariff case SOUND_MIXER_PCM: 1111193640Sariff case SOUND_MIXER_RECLEV: 1112193640Sariff case SOUND_MIXER_DEVMASK: 1113193640Sariff case SOUND_MIXER_CAPS: 1114193640Sariff case SOUND_MIXER_STEREODEVS: 1115193640Sariff break; 1116193640Sariff default: 1117193640Sariff return (-1); 1118193640Sariff break; 1119193640Sariff } 1120193640Sariff 1121193640Sariff pid = td->td_proc->p_pid; 1122193640Sariff rdch = NULL; 1123193640Sariff wrch = NULL; 1124193640Sariff c = NULL; 1125193640Sariff ret = -1; 1126193640Sariff 1127193640Sariff /* 1128193640Sariff * This is unfair. Imagine single proc opening multiple 1129193640Sariff * instances of same direction. What we do right now 1130193640Sariff * is looking for the first matching proc/pid, and just 1131193640Sariff * that. Nothing more. Consider it done. 1132193640Sariff * 1133193640Sariff * The better approach of controlling specific channel 1134193640Sariff * pcm or rec volume is by doing mixer ioctl 1135193640Sariff * (SNDCTL_DSP_[SET|GET][PLAY|REC]VOL / SOUND_MIXER_[PCM|RECLEV] 1136193640Sariff * on its open fd, rather than cracky mixer bypassing here. 1137193640Sariff */ 1138193640Sariff CHN_FOREACH(c, d, channels.pcm.opened) { 1139193640Sariff CHN_LOCK(c); 1140193640Sariff if (c->pid != pid || 1141193640Sariff !(c->feederflags & (1 << FEEDER_VOLUME))) { 1142193640Sariff CHN_UNLOCK(c); 1143193640Sariff continue; 1144193640Sariff } 1145193640Sariff if (rdch == NULL && c->direction == PCMDIR_REC) { 1146193640Sariff rdch = c; 1147193640Sariff if (j == SOUND_MIXER_RECLEV) 1148193640Sariff goto mixer_ioctl_channel_proc; 1149193640Sariff } else if (wrch == NULL && c->direction == PCMDIR_PLAY) { 1150193640Sariff wrch = c; 1151193640Sariff if (j == SOUND_MIXER_PCM) 1152193640Sariff goto mixer_ioctl_channel_proc; 1153193640Sariff } 1154193640Sariff CHN_UNLOCK(c); 1155193640Sariff if (rdch != NULL && wrch != NULL) 1156193640Sariff break; 1157193640Sariff } 1158193640Sariff 1159193640Sariff if (rdch == NULL && wrch == NULL) 1160193640Sariff return (-1); 1161193640Sariff 1162193640Sariff if ((j == SOUND_MIXER_DEVMASK || j == SOUND_MIXER_CAPS || 1163193640Sariff j == SOUND_MIXER_STEREODEVS) && 1164202150Smav (cmd & ~0xff) == MIXER_READ(0)) { 1165193640Sariff snd_mtxlock(m->lock); 1166193640Sariff *(int *)arg = mix_getdevs(m); 1167193640Sariff snd_mtxunlock(m->lock); 1168193640Sariff if (rdch != NULL) 1169193640Sariff *(int *)arg |= SOUND_MASK_RECLEV; 1170193640Sariff if (wrch != NULL) 1171193640Sariff *(int *)arg |= SOUND_MASK_PCM; 1172193640Sariff ret = 0; 1173193640Sariff } 1174193640Sariff 1175193640Sariff return (ret); 1176193640Sariff 1177193640Sariffmixer_ioctl_channel_proc: 1178193640Sariff 1179193640Sariff KASSERT(c != NULL, ("%s(): NULL channel", __func__)); 1180193640Sariff CHN_LOCKASSERT(c); 1181193640Sariff 1182202150Smav if ((cmd & ~0xff) == MIXER_WRITE(0)) { 1183193640Sariff int left, right, center; 1184193640Sariff 1185193640Sariff left = *(int *)arg & 0x7f; 1186193640Sariff right = (*(int *)arg >> 8) & 0x7f; 1187193640Sariff center = (left + right) >> 1; 1188193640Sariff chn_setvolume_multi(c, SND_VOL_C_PCM, left, right, center); 1189202150Smav } else if ((cmd & ~0xff) == MIXER_READ(0)) { 1190193640Sariff *(int *)arg = CHN_GETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_FL); 1191193640Sariff *(int *)arg |= 1192193640Sariff CHN_GETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_FR) << 8; 1193193640Sariff } 1194193640Sariff 1195193640Sariff CHN_UNLOCK(c); 1196193640Sariff 1197193640Sariff return (0); 1198193640Sariff} 1199193640Sariff 1200193640Sariffstatic int 1201170815Sariffmixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, 1202170815Sariff struct thread *td) 1203170815Sariff{ 1204170815Sariff struct snddev_info *d; 1205170815Sariff int ret; 1206170815Sariff 1207170815Sariff if (i_dev == NULL || i_dev->si_drv1 == NULL) 1208170815Sariff return (EBADF); 1209170815Sariff 1210170815Sariff d = device_get_softc(((struct snd_mixer *)i_dev->si_drv1)->dev); 1211358878Shselasky if (PCM_DETACHING(d) || !PCM_REGISTERED(d)) 1212170815Sariff return (EBADF); 1213170815Sariff 1214170815Sariff PCM_GIANT_ENTER(d); 1215170815Sariff PCM_ACQUIRE_QUICK(d); 1216170815Sariff 1217193640Sariff ret = -1; 1218170815Sariff 1219193640Sariff if (mixer_bypass != 0 && (d->flags & SD_F_VPC)) 1220193640Sariff ret = mixer_ioctl_channel(i_dev, cmd, arg, mode, td, 1221193640Sariff MIXER_CMD_CDEV); 1222193640Sariff 1223193640Sariff if (ret == -1) 1224193640Sariff ret = mixer_ioctl_cmd(i_dev, cmd, arg, mode, td, 1225193640Sariff MIXER_CMD_CDEV); 1226193640Sariff 1227170815Sariff PCM_RELEASE_QUICK(d); 1228170815Sariff PCM_GIANT_LEAVE(d); 1229170815Sariff 1230170815Sariff return (ret); 1231170815Sariff} 1232170815Sariff 1233269228Smavstatic void 1234269228Smavmixer_mixerinfo(struct snd_mixer *m, mixer_info *mi) 1235269228Smav{ 1236269228Smav bzero((void *)mi, sizeof(*mi)); 1237269228Smav strlcpy(mi->id, m->name, sizeof(mi->id)); 1238269228Smav strlcpy(mi->name, device_get_desc(m->dev), sizeof(mi->name)); 1239269228Smav mi->modify_counter = m->modify_counter; 1240269228Smav} 1241269228Smav 1242170815Sariff/* 1243170815Sariff * XXX Make sure you can guarantee concurrency safety before calling this 1244193640Sariff * function, be it through Giant, PCM_*, etc ! 1245170815Sariff */ 124678362Scgint 1247170815Sariffmixer_ioctl_cmd(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, 1248170815Sariff struct thread *td, int from) 124978362Scg{ 125078670Scg struct snd_mixer *m; 1251202150Smav int ret = EINVAL, *arg_i = (int *)arg; 125278362Scg int v = -1, j = cmd & 0xff; 125378362Scg 1254187030Smav /* 1255187030Smav * Certain ioctls may be made on any type of device (audio, mixer, 1256187030Smav * and MIDI). Handle those special cases here. 1257187030Smav */ 1258187030Smav if (IOCGROUP(cmd) == 'X') { 1259187030Smav switch (cmd) { 1260187030Smav case SNDCTL_SYSINFO: 1261187030Smav sound_oss_sysinfo((oss_sysinfo *)arg); 1262187030Smav return (0); 1263187030Smav case SNDCTL_CARDINFO: 1264187030Smav return (sound_oss_card_info((oss_card_info *)arg)); 1265187030Smav case SNDCTL_AUDIOINFO: 1266187030Smav case SNDCTL_AUDIOINFO_EX: 1267187030Smav case SNDCTL_ENGINEINFO: 1268187030Smav return (dsp_oss_audioinfo(i_dev, (oss_audioinfo *)arg)); 1269187030Smav case SNDCTL_MIXERINFO: 1270187030Smav return (mixer_oss_mixerinfo(i_dev, (oss_mixerinfo *)arg)); 1271187030Smav } 1272187036Smav return (EINVAL); 1273187030Smav } 1274187030Smav 127578362Scg m = i_dev->si_drv1; 1276156929Sariff 1277156929Sariff if (m == NULL) 1278170815Sariff return (EBADF); 127978362Scg 128078362Scg snd_mtxlock(m->lock); 1281170815Sariff if (from == MIXER_CMD_CDEV && !m->busy) { 1282156929Sariff snd_mtxunlock(m->lock); 1283170815Sariff return (EBADF); 1284156929Sariff } 1285202150Smav switch (cmd) { 1286202150Smav case SNDCTL_DSP_GET_RECSRC_NAMES: 1287202150Smav bcopy((void *)&m->enuminfo, arg, sizeof(oss_mixer_enuminfo)); 1288202150Smav ret = 0; 1289202150Smav goto done; 1290202150Smav case SNDCTL_DSP_GET_RECSRC: 1291202150Smav ret = mixer_get_recroute(m, arg_i); 1292202150Smav goto done; 1293202150Smav case SNDCTL_DSP_SET_RECSRC: 1294202150Smav ret = mixer_set_recroute(m, *arg_i); 1295202150Smav goto done; 1296202150Smav case OSS_GETVERSION: 1297202150Smav *arg_i = SOUND_VERSION; 1298202150Smav ret = 0; 1299202150Smav goto done; 1300269228Smav case SOUND_MIXER_INFO: 1301269228Smav mixer_mixerinfo(m, (mixer_info *)arg); 1302269228Smav ret = 0; 1303269228Smav goto done; 1304202150Smav } 1305202150Smav if ((cmd & ~0xff) == MIXER_WRITE(0)) { 130678362Scg if (j == SOUND_MIXER_RECSRC) 130778362Scg ret = mixer_setrecsrc(m, *arg_i); 130878362Scg else 130978362Scg ret = mixer_set(m, j, *arg_i); 131078362Scg snd_mtxunlock(m->lock); 1311170815Sariff return ((ret == 0) ? 0 : ENXIO); 131278362Scg } 1313202150Smav if ((cmd & ~0xff) == MIXER_READ(0)) { 131478362Scg switch (j) { 1315202150Smav case SOUND_MIXER_DEVMASK: 1316202150Smav case SOUND_MIXER_CAPS: 1317202150Smav case SOUND_MIXER_STEREODEVS: 131878362Scg v = mix_getdevs(m); 131978362Scg break; 1320202150Smav case SOUND_MIXER_RECMASK: 132178362Scg v = mix_getrecdevs(m); 132278362Scg break; 1323202150Smav case SOUND_MIXER_RECSRC: 132478362Scg v = mixer_getrecsrc(m); 132578362Scg break; 132678362Scg default: 132778362Scg v = mixer_get(m, j); 132878362Scg } 132978362Scg *arg_i = v; 133078362Scg snd_mtxunlock(m->lock); 1331170815Sariff return ((v != -1) ? 0 : ENXIO); 133278362Scg } 1333202150Smavdone: 133478362Scg snd_mtxunlock(m->lock); 1335170815Sariff return (ret); 133678362Scg} 133778362Scg 133878362Scgstatic void 1339170161Sariffmixer_clone(void *arg, 1340170161Sariff struct ucred *cred, 1341170161Sariff char *name, int namelen, struct cdev **dev) 134278362Scg{ 1343170161Sariff struct snddev_info *d; 134478362Scg 1345130640Sphk if (*dev != NULL) 134678362Scg return; 134778362Scg if (strcmp(name, "mixer") == 0) { 1348170161Sariff d = devclass_get_softc(pcm_devclass, snd_unit); 1349170815Sariff if (PCM_REGISTERED(d) && d->mixer_dev != NULL) { 1350170161Sariff *dev = d->mixer_dev; 1351144389Sphk dev_ref(*dev); 1352144389Sphk } 135378362Scg } 135478362Scg} 135578362Scg 135678362Scgstatic void 135778362Scgmixer_sysinit(void *p) 135878362Scg{ 1359170161Sariff if (mixer_ehtag != NULL) 1360170161Sariff return; 136178362Scg mixer_ehtag = EVENTHANDLER_REGISTER(dev_clone, mixer_clone, 0, 1000); 136278362Scg} 136378362Scg 136478362Scgstatic void 136578362Scgmixer_sysuninit(void *p) 136678362Scg{ 1367170161Sariff if (mixer_ehtag == NULL) 1368170161Sariff return; 1369170161Sariff EVENTHANDLER_DEREGISTER(dev_clone, mixer_ehtag); 1370170161Sariff mixer_ehtag = NULL; 137178362Scg} 137278362Scg 137378362ScgSYSINIT(mixer_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysinit, NULL); 137478362ScgSYSUNINIT(mixer_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysuninit, NULL); 137578362Scg 1376162588Snetchild/** 1377162588Snetchild * @brief Handler for SNDCTL_MIXERINFO 1378162588Snetchild * 1379162588Snetchild * This function searches for a mixer based on the numeric ID stored 1380162588Snetchild * in oss_miserinfo::dev. If set to -1, then information about the 1381162588Snetchild * current mixer handling the request is provided. Note, however, that 1382162588Snetchild * this ioctl may be made with any sound device (audio, mixer, midi). 1383162588Snetchild * 1384162588Snetchild * @note Caller must not hold any PCM device, channel, or mixer locks. 1385162588Snetchild * 1386162588Snetchild * See http://manuals.opensound.com/developer/SNDCTL_MIXERINFO.html for 1387162588Snetchild * more information. 1388162588Snetchild * 1389162588Snetchild * @param i_dev character device on which the ioctl arrived 1390162588Snetchild * @param arg user argument (oss_mixerinfo *) 1391162588Snetchild * 1392162588Snetchild * @retval EINVAL oss_mixerinfo::dev specified a bad value 1393162588Snetchild * @retval 0 success 1394162588Snetchild */ 1395162588Snetchildint 1396162588Snetchildmixer_oss_mixerinfo(struct cdev *i_dev, oss_mixerinfo *mi) 1397162588Snetchild{ 1398162588Snetchild struct snddev_info *d; 1399162588Snetchild struct snd_mixer *m; 1400170815Sariff int nmix, i; 140178362Scg 1402162588Snetchild /* 1403162588Snetchild * If probing the device handling the ioctl, make sure it's a mixer 1404162588Snetchild * device. (This ioctl is valid on audio, mixer, and midi devices.) 1405162588Snetchild */ 1406170815Sariff if (mi->dev == -1 && i_dev->si_devsw != &mixer_cdevsw) 1407170815Sariff return (EINVAL); 1408162588Snetchild 1409162606Snetchild d = NULL; 1410162588Snetchild m = NULL; 1411162588Snetchild nmix = 0; 1412162588Snetchild 1413162588Snetchild /* 1414162588Snetchild * There's a 1:1 relationship between mixers and PCM devices, so 1415162588Snetchild * begin by iterating over PCM devices and search for our mixer. 1416162588Snetchild */ 1417170235Sariff for (i = 0; pcm_devclass != NULL && 1418170235Sariff i < devclass_get_maxunit(pcm_devclass); i++) { 1419162588Snetchild d = devclass_get_softc(pcm_devclass, i); 1420358878Shselasky if (PCM_DETACHING(d) || !PCM_REGISTERED(d)) 1421162588Snetchild continue; 1422162588Snetchild 1423170815Sariff /* XXX Need Giant magic entry */ 1424170815Sariff 1425162588Snetchild /* See the note in function docblock. */ 1426193640Sariff PCM_UNLOCKASSERT(d); 1427193640Sariff PCM_LOCK(d); 1428162588Snetchild 1429170815Sariff if (d->mixer_dev != NULL && d->mixer_dev->si_drv1 != NULL && 1430170815Sariff ((mi->dev == -1 && d->mixer_dev == i_dev) || 1431170815Sariff mi->dev == nmix)) { 1432170815Sariff m = d->mixer_dev->si_drv1; 1433170815Sariff mtx_lock(m->lock); 1434162588Snetchild 1435170815Sariff /* 1436170815Sariff * At this point, the following synchronization stuff 1437170815Sariff * has happened: 1438170815Sariff * - a specific PCM device is locked. 1439170815Sariff * - a specific mixer device has been locked, so be 1440170815Sariff * sure to unlock when existing. 1441170815Sariff */ 1442170815Sariff bzero((void *)mi, sizeof(*mi)); 1443170815Sariff mi->dev = nmix; 1444170815Sariff snprintf(mi->id, sizeof(mi->id), "mixer%d", i); 1445170815Sariff strlcpy(mi->name, m->name, sizeof(mi->name)); 1446170815Sariff mi->modify_counter = m->modify_counter; 1447170815Sariff mi->card_number = i; 1448170815Sariff /* 1449170815Sariff * Currently, FreeBSD assumes 1:1 relationship between 1450170815Sariff * a pcm and mixer devices, so this is hardcoded to 0. 1451170815Sariff */ 1452170815Sariff mi->port_number = 0; 1453162588Snetchild 1454170815Sariff /** 1455170815Sariff * @todo Fill in @sa oss_mixerinfo::mixerhandle. 1456170815Sariff * @note From 4Front: "mixerhandle is an arbitrary 1457170815Sariff * string that identifies the mixer better than 1458170815Sariff * the device number (mixerinfo.dev). Device 1459170815Sariff * numbers may change depending on the order the 1460170815Sariff * drivers are loaded. However the handle should 1461170815Sariff * remain the same provided that the sound card 1462170815Sariff * is not moved to another PCI slot." 1463170815Sariff */ 1464162588Snetchild 1465170815Sariff /** 1466170815Sariff * @note 1467170815Sariff * @sa oss_mixerinfo::magic is a reserved field. 1468170815Sariff * 1469170815Sariff * @par 1470170815Sariff * From 4Front: "magic is usually 0. However some 1471170815Sariff * devices may have dedicated setup utilities and the 1472170815Sariff * magic field may contain an unique driver specific 1473170815Sariff * value (managed by [4Front])." 1474170815Sariff */ 1475162588Snetchild 1476170815Sariff mi->enabled = device_is_attached(m->dev) ? 1 : 0; 1477170815Sariff /** 1478170815Sariff * The only flag for @sa oss_mixerinfo::caps is 1479170815Sariff * currently MIXER_CAP_VIRTUAL, which I'm not sure we 1480170815Sariff * really worry about. 1481170815Sariff */ 1482170815Sariff /** 1483170815Sariff * Mixer extensions currently aren't supported, so 1484170815Sariff * leave @sa oss_mixerinfo::nrext blank for now. 1485170815Sariff */ 1486170815Sariff /** 1487170815Sariff * @todo Fill in @sa oss_mixerinfo::priority (requires 1488170815Sariff * touching drivers?) 1489170815Sariff * @note The priority field is for mixer applets to 1490170815Sariff * determine which mixer should be the default, with 0 1491170815Sariff * being least preferred and 10 being most preferred. 1492170815Sariff * From 4Front: "OSS drivers like ICH use higher 1493170815Sariff * values (10) because such chips are known to be used 1494170815Sariff * only on motherboards. Drivers for high end pro 1495170815Sariff * devices use 0 because they will never be the 1496170815Sariff * default mixer. Other devices use values 1 to 9 1497170815Sariff * depending on the estimated probability of being the 1498170815Sariff * default device. 1499170815Sariff * 1500170815Sariff * XXX Described by Hannu@4Front, but not found in 1501170815Sariff * soundcard.h. 1502231378Sed strlcpy(mi->devnode, devtoname(d->mixer_dev), 1503170815Sariff sizeof(mi->devnode)); 1504170815Sariff mi->legacy_device = i; 1505170815Sariff */ 1506170815Sariff mtx_unlock(m->lock); 1507170815Sariff } else 1508170815Sariff ++nmix; 1509162588Snetchild 1510193640Sariff PCM_UNLOCK(d); 1511162588Snetchild 1512170815Sariff if (m != NULL) 1513170815Sariff return (0); 1514170815Sariff } 1515162588Snetchild 1516170815Sariff return (EINVAL); 1517162588Snetchild} 1518184610Salfred 1519184610Salfred/* 1520184610Salfred * Allow the sound driver to use the mixer lock to protect its mixer 1521184610Salfred * data: 1522184610Salfred */ 1523184610Salfredstruct mtx * 1524184610Salfredmixer_get_lock(struct snd_mixer *m) 1525184610Salfred{ 1526184610Salfred if (m->lock == NULL) { 1527184610Salfred return (&Giant); 1528184610Salfred } 1529184610Salfred return (m->lock); 1530184610Salfred} 1531246421Shselasky 1532246421Shselaskyint 1533246421Shselaskymix_get_locked(struct snd_mixer *m, u_int dev, int *pleft, int *pright) 1534246421Shselasky{ 1535246421Shselasky int level; 1536246421Shselasky 1537246421Shselasky level = mixer_get(m, dev); 1538246421Shselasky if (level < 0) { 1539246421Shselasky *pright = *pleft = -1; 1540246421Shselasky return (-1); 1541246421Shselasky } 1542246421Shselasky 1543246421Shselasky *pleft = level & 0xFF; 1544246421Shselasky *pright = (level >> 8) & 0xFF; 1545246421Shselasky 1546246421Shselasky return (0); 1547246421Shselasky} 1548246421Shselasky 1549246421Shselaskyint 1550246421Shselaskymix_set_locked(struct snd_mixer *m, u_int dev, int left, int right) 1551246421Shselasky{ 1552246421Shselasky int level; 1553246421Shselasky 1554246421Shselasky level = (left & 0xFF) | ((right & 0xFF) << 8); 1555246421Shselasky 1556246421Shselasky return (mixer_set(m, dev, level)); 1557246421Shselasky} 1558