mixer

FreeBSD OSS mixer library implementation and a complete rewrite of mixer(8)
git clone git://git.margiolis.net/mixer.git
Log | Files | Refs | README | LICENSE

mixer_kern.diff (20929B)


      1 diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c
      2 index 38c578ba828..4d56eee6847 100644
      3 --- a/sys/dev/sound/pcm/channel.c
      4 +++ b/sys/dev/sound/pcm/channel.c
      5 @@ -1223,6 +1223,8 @@ chn_init(struct pcm_channel *c, void *devinfo, int dir, int direction)
      6  	c->volume[SND_VOL_C_MASTER][SND_CHN_T_VOL_0DB] = SND_VOL_0DB_MASTER;
      7  	c->volume[SND_VOL_C_PCM][SND_CHN_T_VOL_0DB] = chn_vol_0db_pcm;
      8  
      9 +	memset(c->muted, 0, sizeof(c->muted));
     10 +
     11  	chn_vpc_reset(c, SND_VOL_C_PCM, 1);
     12  
     13  	ret = ENODEV;
     14 @@ -1394,6 +1396,75 @@ chn_getvolume_matrix(struct pcm_channel *c, int vc, int vt)
     15  	return (c->volume[vc][vt]);
     16  }
     17  
     18 +int
     19 +chn_setmute_multi(struct pcm_channel *c, int vc, int mute)
     20 +{
     21 +	int i, ret;
     22 +
     23 +	ret = 0;
     24 +
     25 +	for (i = 0; i < SND_CHN_T_MAX; i++) {
     26 +		if ((1 << i) & SND_CHN_LEFT_MASK)
     27 +			ret |= chn_setmute_matrix(c, vc, i, mute);
     28 +		else if ((1 << i) & SND_CHN_RIGHT_MASK)
     29 +			ret |= chn_setmute_matrix(c, vc, i, mute) << 8;
     30 +		else
     31 +			ret |= chn_setmute_matrix(c, vc, i, mute) << 16;
     32 +	}
     33 +	return (ret);
     34 +}
     35 +
     36 +int
     37 +chn_setmute_matrix(struct pcm_channel *c, int vc, int vt, int mute)
     38 +{
     39 +	int i;
     40 +
     41 +	KASSERT(c != NULL && vc >= SND_VOL_C_MASTER && vc < SND_VOL_C_MAX &&
     42 +	    (vc == SND_VOL_C_MASTER || (vc & 1)) &&
     43 +	    (vt == SND_CHN_T_VOL_0DB || (vt >= SND_CHN_T_BEGIN && vt <= SND_CHN_T_END)),
     44 +	    ("%s(): invalid mute matrix c=%p vc=%d vt=%d mute=%d",
     45 +	    __func__, c, vc, vt, mute));
     46 +
     47 +	CHN_LOCKASSERT(c);
     48 +
     49 +	mute = (mute != 0);
     50 +
     51 +	c->muted[vc][vt] = mute;
     52 +
     53 +	/*
     54 +	 * Do relative calculation here and store it into class + 1
     55 +	 * to ease the job of feeder_volume.
     56 +	 */
     57 +	if (vc == SND_VOL_C_MASTER) {
     58 +		for (vc = SND_VOL_C_BEGIN; vc <= SND_VOL_C_END;
     59 +		    vc += SND_VOL_C_STEP)
     60 +			c->muted[SND_VOL_C_VAL(vc)][vt] = mute;
     61 +	} else if (vc & 1) {
     62 +		if (vt == SND_CHN_T_VOL_0DB) {
     63 +			for (i = SND_CHN_T_BEGIN; i <= SND_CHN_T_END;
     64 +			    i += SND_CHN_T_STEP) {
     65 +				c->muted[SND_VOL_C_VAL(vc)][i] = mute;
     66 +			}
     67 +		} else {
     68 +			c->muted[SND_VOL_C_VAL(vc)][vt] = mute;
     69 +		}
     70 +	}
     71 +	return (mute);
     72 +}
     73 +
     74 +int
     75 +chn_getmute_matrix(struct pcm_channel *c, int vc, int vt)
     76 +{
     77 +	KASSERT(c != NULL && vc >= SND_VOL_C_MASTER && vc < SND_VOL_C_MAX &&
     78 +	    (vt == SND_CHN_T_VOL_0DB ||
     79 +	    (vt >= SND_CHN_T_BEGIN && vt <= SND_CHN_T_END)),
     80 +	    ("%s(): invalid mute matrix c=%p vc=%d vt=%d",
     81 +	    __func__, c, vc, vt));
     82 +	CHN_LOCKASSERT(c);
     83 +
     84 +	return (c->muted[vc][vt]);
     85 +}
     86 +
     87  struct pcmchan_matrix *
     88  chn_getmatrix(struct pcm_channel *c)
     89  {
     90 diff --git a/sys/dev/sound/pcm/channel.h b/sys/dev/sound/pcm/channel.h
     91 index 34d62f4e15c..60b7b3416cc 100644
     92 --- a/sys/dev/sound/pcm/channel.h
     93 +++ b/sys/dev/sound/pcm/channel.h
     94 @@ -166,7 +166,8 @@ struct pcm_channel {
     95  	struct pcmchan_matrix matrix;
     96    	struct pcmchan_matrix matrix_scratch;
     97  
     98 -	int volume[SND_VOL_C_MAX][SND_CHN_T_VOL_MAX];
     99 +	int16_t volume[SND_VOL_C_MAX][SND_CHN_T_VOL_MAX];
    100 +  	int8_t muted[SND_VOL_C_MAX][SND_CHN_T_VOL_MAX];
    101  
    102  	void *data1, *data2;
    103  };
    104 @@ -271,6 +272,9 @@ int chn_setvolume_multi(struct pcm_channel *c, int vc, int left, int right,
    105      int center);
    106  int chn_setvolume_matrix(struct pcm_channel *c, int vc, int vt, int val);
    107  int chn_getvolume_matrix(struct pcm_channel *c, int vc, int vt);
    108 +int chn_setmute_multi(struct pcm_channel *c, int vc, int mute);
    109 +int chn_setmute_matrix(struct pcm_channel *c, int vc, int vt, int mute);
    110 +int chn_getmute_matrix(struct pcm_channel *c, int vc, int vt);
    111  void chn_vpc_reset(struct pcm_channel *c, int vc, int force);
    112  int chn_setparam(struct pcm_channel *c, uint32_t format, uint32_t speed);
    113  int chn_setspeed(struct pcm_channel *c, uint32_t speed);
    114 @@ -307,6 +311,8 @@ int chn_syncdestroy(struct pcm_channel *c);
    115  #define CHN_GETVOLUME(x, y, z)		((x)->volume[y][z])
    116  #endif
    117  
    118 +#define CHN_GETMUTE(x, y, z)		((x)->muted[y][z])
    119 +
    120  #ifdef OSSV4_EXPERIMENT
    121  int chn_getpeaks(struct pcm_channel *c, int *lpeak, int *rpeak);
    122  #endif
    123 diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c
    124 index 0593a585b0f..15f437b8627 100644
    125 --- a/sys/dev/sound/pcm/dsp.c
    126 +++ b/sys/dev/sound/pcm/dsp.c
    127 @@ -965,6 +965,7 @@ dsp_ioctl_channel(struct cdev *dev, struct pcm_channel *volch, u_long cmd,
    128  	struct snddev_info *d;
    129  	struct pcm_channel *rdch, *wrch;
    130  	int j, devtype, ret;
    131 +	int left, right, center, mute;
    132  
    133  	d = dsp_get_info(dev);
    134  	if (!PCM_REGISTERED(d) || !(dsp_get_flags(dev) & SD_F_VPC))
    135 @@ -1003,67 +1004,95 @@ dsp_ioctl_channel(struct cdev *dev, struct pcm_channel *volch, u_long cmd,
    136  	}
    137  
    138  	/* Final validation */
    139 -	if (volch != NULL) {
    140 -		CHN_LOCK(volch);
    141 -		if (!(volch->feederflags & (1 << FEEDER_VOLUME))) {
    142 -			CHN_UNLOCK(volch);
    143 -			return (-1);
    144 -		}
    145 -		if (volch->direction == PCMDIR_PLAY)
    146 -			wrch = volch;
    147 -		else
    148 -			rdch = volch;
    149 -	}
    150 -
    151 -	ret = EINVAL;
    152 +	if (volch == NULL)
    153 +		return (EINVAL);
    154  
    155 -	if (volch != NULL &&
    156 -	    ((j == SOUND_MIXER_PCM && volch->direction == PCMDIR_PLAY) ||
    157 -	    (j == SOUND_MIXER_RECLEV && volch->direction == PCMDIR_REC))) {
    158 -		if ((cmd & ~0xff) == MIXER_WRITE(0)) {
    159 -			int left, right, center;
    160 +	CHN_LOCK(volch);
    161 +	if (!(volch->feederflags & (1 << FEEDER_VOLUME))) {
    162 +		CHN_UNLOCK(volch);
    163 +		return (EINVAL);
    164 +	}
    165  
    166 +	switch (cmd & ~0xff) {
    167 +	case MIXER_WRITE(0):
    168 +		switch (j) {
    169 +		case SOUND_MIXER_MUTE:
    170 +			if (volch->direction == PCMDIR_REC) {
    171 +				chn_setmute_multi(volch, SND_VOL_C_PCM, (*(int *)arg & SOUND_MASK_RECLEV) != 0);
    172 +			} else {
    173 +				chn_setmute_multi(volch, SND_VOL_C_PCM, (*(int *)arg & SOUND_MASK_PCM) != 0);
    174 +			}
    175 +			break;
    176 +		case SOUND_MIXER_PCM:
    177 +			if (volch->direction != PCMDIR_PLAY)
    178 +				break;
    179  			left = *(int *)arg & 0x7f;
    180  			right = ((*(int *)arg) >> 8) & 0x7f;
    181  			center = (left + right) >> 1;
    182 -			chn_setvolume_multi(volch, SND_VOL_C_PCM, left, right,
    183 -			    center);
    184 -		} else if ((cmd & ~0xff) == MIXER_READ(0)) {
    185 -			*(int *)arg = CHN_GETVOLUME(volch,
    186 -				SND_VOL_C_PCM, SND_CHN_T_FL);
    187 -			*(int *)arg |= CHN_GETVOLUME(volch,
    188 -				SND_VOL_C_PCM, SND_CHN_T_FR) << 8;
    189 +			chn_setvolume_multi(volch, SND_VOL_C_PCM,
    190 +			    left, right, center);
    191 +			break;
    192 +		case SOUND_MIXER_RECLEV:
    193 +			if (volch->direction != PCMDIR_REC)
    194 +				break;
    195 +			left = *(int *)arg & 0x7f;
    196 +			right = ((*(int *)arg) >> 8) & 0x7f;
    197 +			center = (left + right) >> 1;
    198 +			chn_setvolume_multi(volch, SND_VOL_C_PCM,
    199 +			    left, right, center);
    200 +			break;
    201 +		default:
    202 +			/* ignore all other mixer writes */
    203 +			break;
    204  		}
    205 -		ret = 0;
    206 -	} else if (rdch != NULL || wrch != NULL) {
    207 +		break;
    208 +
    209 +	case MIXER_READ(0):
    210  		switch (j) {
    211 +		case SOUND_MIXER_MUTE:
    212 +			mute = CHN_GETMUTE(volch, SND_VOL_C_PCM, SND_CHN_T_FL) ||
    213 +			    CHN_GETMUTE(volch, SND_VOL_C_PCM, SND_CHN_T_FR);
    214 +			if (volch->direction == PCMDIR_REC) {
    215 +				*(int *)arg = mute << SOUND_MIXER_RECLEV;
    216 +			} else {
    217 +				*(int *)arg = mute << SOUND_MIXER_PCM;
    218 +			}
    219 +			break;
    220 +		case SOUND_MIXER_PCM:
    221 +			if (volch->direction != PCMDIR_PLAY)
    222 +				break;
    223 +			*(int *)arg = CHN_GETVOLUME(volch,
    224 +			    SND_VOL_C_PCM, SND_CHN_T_FL);
    225 +			*(int *)arg |= CHN_GETVOLUME(volch,
    226 +			    SND_VOL_C_PCM, SND_CHN_T_FR) << 8;
    227 +			break;
    228 +		case SOUND_MIXER_RECLEV:
    229 +			if (volch->direction != PCMDIR_REC)
    230 +				break;
    231 +			*(int *)arg = CHN_GETVOLUME(volch,
    232 +			    SND_VOL_C_PCM, SND_CHN_T_FL);
    233 +			*(int *)arg |= CHN_GETVOLUME(volch,
    234 +			    SND_VOL_C_PCM, SND_CHN_T_FR) << 8;
    235 +			break;
    236  		case SOUND_MIXER_DEVMASK:
    237  		case SOUND_MIXER_CAPS:
    238  		case SOUND_MIXER_STEREODEVS:
    239 -			if ((cmd & ~0xff) == MIXER_READ(0)) {
    240 -				*(int *)arg = 0;
    241 -				if (rdch != NULL)
    242 -					*(int *)arg |= SOUND_MASK_RECLEV;
    243 -				if (wrch != NULL)
    244 -					*(int *)arg |= SOUND_MASK_PCM;
    245 -			}
    246 -			ret = 0;
    247 -			break;
    248 -		case SOUND_MIXER_RECMASK:
    249 -		case SOUND_MIXER_RECSRC:
    250 -			if ((cmd & ~0xff) == MIXER_READ(0))
    251 -				*(int *)arg = 0;
    252 -			ret = 0;
    253 +			if (volch->direction == PCMDIR_REC)
    254 +				*(int *)arg = SOUND_MASK_RECLEV;
    255 +			else
    256 +				*(int *)arg = SOUND_MASK_PCM;
    257  			break;
    258  		default:
    259 +			*(int *)arg = 0;
    260  			break;
    261  		}
    262 -	}
    263 -
    264 -	if (volch != NULL)
    265 -		CHN_UNLOCK(volch);
    266 +		break;
    267  
    268 -	return (ret);
    269 +	default:
    270 +		break;
    271 +	}
    272 +	CHN_UNLOCK(volch);
    273 +	return (0);
    274  }
    275  
    276  static int
    277 @@ -2294,8 +2323,7 @@ dsp_stdclone(char *name, char *namep, char *sep, int use_sep, int *u, int *c)
    278  	size_t len;
    279  
    280  	len = strlen(namep);
    281 -
    282 -	if (bcmp(name, namep, len) != 0)
    283 +	if (strncmp(name, namep, len) != 0)
    284  		return (ENODEV);
    285  
    286  	name += len;
    287 diff --git a/sys/dev/sound/pcm/feeder_volume.c b/sys/dev/sound/pcm/feeder_volume.c
    288 index 322d7f6b2c8..2312bd89c9d 100644
    289 --- a/sys/dev/sound/pcm/feeder_volume.c
    290 +++ b/sys/dev/sound/pcm/feeder_volume.c
    291 @@ -237,10 +237,13 @@ static int
    292  feed_volume_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
    293      uint32_t count, void *source)
    294  {
    295 +	int temp_vol[SND_CHN_T_VOL_MAX];
    296  	struct feed_volume_info *info;
    297  	uint32_t j, align;
    298 -	int i, *vol, *matrix;
    299 +	int i, *matrix;
    300  	uint8_t *dst;
    301 +	const int16_t *vol;
    302 +	const int8_t *muted;
    303  
    304  	/*
    305  	 * Fetch filter data operation.
    306 @@ -251,6 +254,7 @@ feed_volume_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
    307  		return (FEEDER_FEED(f->source, c, b, count, source));
    308  
    309  	vol = c->volume[SND_VOL_C_VAL(info->volume_class)];
    310 +	muted = c->muted[SND_VOL_C_VAL(info->volume_class)];
    311  	matrix = info->matrix;
    312  
    313  	/*
    314 @@ -258,17 +262,22 @@ feed_volume_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
    315  	 */
    316  	j = 0;
    317  	i = info->channels;
    318 -	do {
    319 -		if (vol[matrix[--i]] != SND_VOL_FLAT) {
    320 +	while (i--) {
    321 +		if (vol[matrix[i]] != SND_VOL_FLAT ||
    322 +		    muted[matrix[i]] != 0) {
    323  			j = 1;
    324  			break;
    325  		}
    326 -	} while (i != 0);
    327 +	}
    328  
    329  	/* Nope, just bypass entirely. */
    330  	if (j == 0)
    331  		return (FEEDER_FEED(f->source, c, b, count, source));
    332  
    333 +	/* Check if any controls are muted. */
    334 +	for (j = 0; j != SND_CHN_T_VOL_MAX; j++)
    335 +		temp_vol[j] = muted[j] ? 0 : vol[j];
    336 +
    337  	dst = b;
    338  	align = info->bps * info->channels;
    339  
    340 @@ -281,7 +290,7 @@ feed_volume_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
    341  		if (j == 0)
    342  			break;
    343  
    344 -		info->apply(vol, matrix, info->channels, dst, j);
    345 +		info->apply(temp_vol, matrix, info->channels, dst, j);
    346  
    347  		j *= align;
    348  		dst += j;
    349 diff --git a/sys/dev/sound/pcm/mixer.c b/sys/dev/sound/pcm/mixer.c
    350 index 09b0bb8ab14..89e78b036e9 100644
    351 --- a/sys/dev/sound/pcm/mixer.c
    352 +++ b/sys/dev/sound/pcm/mixer.c
    353 @@ -51,16 +51,16 @@ struct snd_mixer {
    354  	KOBJ_FIELDS;
    355  	void *devinfo;
    356  	int busy;
    357 -	int hwvol_muted;
    358  	int hwvol_mixer;
    359  	int hwvol_step;
    360  	int type;
    361  	device_t dev;
    362 -	u_int32_t hwvol_mute_level;
    363  	u_int32_t devs;
    364 +	u_int32_t mutedevs;
    365  	u_int32_t recdevs;
    366  	u_int32_t recsrc;
    367  	u_int16_t level[32];
    368 +	u_int16_t level_muted[32];
    369  	u_int8_t parent[32];
    370  	u_int32_t child[32];
    371  	u_int8_t realdev[32];
    372 @@ -244,7 +244,7 @@ mixer_set_eq(struct snd_mixer *m, struct snddev_info *d,
    373  }
    374  
    375  static int
    376 -mixer_set(struct snd_mixer *m, u_int dev, u_int lev)
    377 +mixer_set(struct snd_mixer *m, u_int dev, u_int32_t muted, u_int lev)
    378  {
    379  	struct snddev_info *d;
    380  	u_int l, r, tl, tr;
    381 @@ -254,7 +254,7 @@ mixer_set(struct snd_mixer *m, u_int dev, u_int lev)
    382  
    383  	if (m == NULL || dev >= SOUND_MIXER_NRDEVICES ||
    384  	    (0 == (m->devs & (1 << dev))))
    385 -		return -1;
    386 +		return (-1);
    387  
    388  	l = min((lev & 0x00ff), 100);
    389  	r = min(((lev & 0xff00) >> 8), 100);
    390 @@ -262,7 +262,7 @@ mixer_set(struct snd_mixer *m, u_int dev, u_int lev)
    391  
    392  	d = device_get_softc(m->dev);
    393  	if (d == NULL)
    394 -		return -1;
    395 +		return (-1);
    396  
    397  	/* It is safe to drop this mutex due to Giant. */
    398  	if (!(d->flags & SD_F_MPSAFE) && mtx_owned(m->lock) != 0)
    399 @@ -270,6 +270,11 @@ mixer_set(struct snd_mixer *m, u_int dev, u_int lev)
    400  	else
    401  		dropmtx = 0;
    402  
    403 +	/* Allow the volume to be "changed" while muted. */
    404 +	if (muted & (1 << dev)) {
    405 +		m->level_muted[dev] = l | (r << 8);
    406 +		return (0);
    407 +	}
    408  	MIXER_SET_UNLOCK(m, dropmtx);
    409  
    410  	/* TODO: recursive handling */
    411 @@ -287,7 +292,7 @@ mixer_set(struct snd_mixer *m, u_int dev, u_int lev)
    412  		else if (realdev != SOUND_MIXER_NONE &&
    413  		    MIXER_SET(m, realdev, tl, tr) < 0) {
    414  			MIXER_SET_LOCK(m, dropmtx);
    415 -			return -1;
    416 +			return (-1);
    417  		}
    418  	} else if (child != 0) {
    419  		for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
    420 @@ -305,8 +310,8 @@ mixer_set(struct snd_mixer *m, u_int dev, u_int lev)
    421  		realdev = m->realdev[dev];
    422  		if (realdev != SOUND_MIXER_NONE &&
    423  		    MIXER_SET(m, realdev, l, r) < 0) {
    424 -				MIXER_SET_LOCK(m, dropmtx);
    425 -				return -1;
    426 +			MIXER_SET_LOCK(m, dropmtx);
    427 +			return (-1);
    428  		}
    429  	} else {
    430  		if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL))
    431 @@ -317,7 +322,7 @@ mixer_set(struct snd_mixer *m, u_int dev, u_int lev)
    432  		else if (realdev != SOUND_MIXER_NONE &&
    433  		    MIXER_SET(m, realdev, l, r) < 0) {
    434  			MIXER_SET_LOCK(m, dropmtx);
    435 -			return -1;
    436 +			return (-1);
    437  		}
    438  	}
    439  
    440 @@ -326,16 +331,42 @@ mixer_set(struct snd_mixer *m, u_int dev, u_int lev)
    441  	m->level[dev] = l | (r << 8);
    442  	m->modify_counter++;
    443  
    444 -	return 0;
    445 +	return (0);
    446  }
    447  
    448  static int
    449  mixer_get(struct snd_mixer *mixer, int dev)
    450  {
    451 -	if ((dev < SOUND_MIXER_NRDEVICES) && (mixer->devs & (1 << dev)))
    452 -		return mixer->level[dev];
    453 -	else
    454 -		return -1;
    455 +	if ((dev < SOUND_MIXER_NRDEVICES) && (mixer->devs & (1 << dev))) {
    456 +		if (mixer->mutedevs & (1 << dev))
    457 +			return (mixer->level_muted[dev]);
    458 +		else
    459 +			return (mixer->level[dev]);
    460 +	} else {
    461 +		return (-1);
    462 +	}
    463 +}
    464 +
    465 +void
    466 +mix_setmutedevs(struct snd_mixer *mixer, u_int32_t mutedevs)
    467 +{
    468 +	u_int32_t delta;
    469 +
    470 +	/* Filter out invalid values. */
    471 +	mutedevs &= mixer->devs;
    472 +	delta = (mixer->mutedevs ^ mutedevs) & mixer->devs;
    473 +	mixer->mutedevs = mutedevs;
    474 +
    475 +	for (int i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
    476 +		if (!(delta & (1 << i)))
    477 +			continue;
    478 +		if (mutedevs & (1 << i)) {
    479 +			mixer->level_muted[i] = mixer->level[i];
    480 +			mixer_set(mixer, i, 0, 0);
    481 +		} else {
    482 +			mixer_set(mixer, i, 0, mixer->level_muted[i]);
    483 +		}
    484 +	}
    485  }
    486  
    487  static int
    488 @@ -598,6 +629,12 @@ mix_getdevs(struct snd_mixer *m)
    489  	return m->devs;
    490  }
    491  
    492 +u_int32_t
    493 +mix_getmutedevs(struct snd_mixer *m)
    494 +{
    495 +	return m->mutedevs;
    496 +}
    497 +
    498  u_int32_t
    499  mix_getrecdevs(struct snd_mixer *m)
    500  {
    501 @@ -721,7 +758,7 @@ mixer_init(device_t dev, kobj_class_t cls, void *devinfo)
    502  			}
    503  		}
    504  
    505 -		mixer_set(m, i, v | (v << 8));
    506 +		mixer_set(m, i, 0, v | (v << 8));
    507  	}
    508  
    509  	mixer_setrecsrc(m, 0); /* Set default input. */
    510 @@ -799,7 +836,7 @@ mixer_uninit(device_t dev)
    511  	snd_mtxlock(m->lock);
    512  
    513  	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
    514 -		mixer_set(m, i, 0);
    515 +		mixer_set(m, i, 0, 0);
    516  
    517  	mixer_setrecsrc(m, SOUND_MASK_MIC);
    518  
    519 @@ -836,8 +873,12 @@ mixer_reinit(device_t dev)
    520  		return i;
    521  	}
    522  
    523 -	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
    524 -		mixer_set(m, i, m->level[i]);
    525 +	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
    526 +		if (m->mutedevs & (1 << i))
    527 +			mixer_set(m, i, 0, 0);
    528 +		else
    529 +			mixer_set(m, i, 0, m->level[i]);
    530 +	}
    531  
    532  	mixer_setrecsrc(m, m->recsrc);
    533  	snd_mtxunlock(m->lock);
    534 @@ -863,10 +904,8 @@ sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS)
    535  		if (dev == -1) {
    536  			snd_mtxunlock(m->lock);
    537  			return EINVAL;
    538 -		}
    539 -		else if (dev != m->hwvol_mixer) {
    540 +		} else {
    541  			m->hwvol_mixer = dev;
    542 -			m->hwvol_muted = 0;
    543  		}
    544  	}
    545  	snd_mtxunlock(m->lock);
    546 @@ -897,14 +936,7 @@ mixer_hwvol_init(device_t dev)
    547  void
    548  mixer_hwvol_mute_locked(struct snd_mixer *m)
    549  {
    550 -	if (m->hwvol_muted) {
    551 -		m->hwvol_muted = 0;
    552 -		mixer_set(m, m->hwvol_mixer, m->hwvol_mute_level);
    553 -	} else {
    554 -		m->hwvol_muted++;
    555 -		m->hwvol_mute_level = mixer_get(m, m->hwvol_mixer);
    556 -		mixer_set(m, m->hwvol_mixer, 0);
    557 -	}
    558 +	mix_setmutedevs(m, m->mutedevs ^ (1 << m->hwvol_mixer));
    559  }
    560  
    561  void
    562 @@ -925,11 +957,8 @@ mixer_hwvol_step_locked(struct snd_mixer *m, int left_step, int right_step)
    563  {
    564  	int level, left, right;
    565  
    566 -	if (m->hwvol_muted) {
    567 -		m->hwvol_muted = 0;
    568 -		level = m->hwvol_mute_level;
    569 -	} else
    570 -		level = mixer_get(m, m->hwvol_mixer);
    571 +	level = mixer_get(m, m->hwvol_mixer);
    572 +
    573  	if (level != -1) {
    574  		left = level & 0xff;
    575  		right = (level >> 8) & 0xff;
    576 @@ -943,7 +972,8 @@ mixer_hwvol_step_locked(struct snd_mixer *m, int left_step, int right_step)
    577  			right = 0;
    578  		else if (right > 100)
    579  			right = 100;
    580 -		mixer_set(m, m->hwvol_mixer, left | right << 8);
    581 +
    582 +		mixer_set(m, m->hwvol_mixer, m->mutedevs, left | right << 8);
    583  	}
    584  }
    585  
    586 @@ -976,7 +1006,7 @@ mix_set(struct snd_mixer *m, u_int dev, u_int left, u_int right)
    587  	KASSERT(m != NULL, ("NULL snd_mixer"));
    588  
    589  	snd_mtxlock(m->lock);
    590 -	ret = mixer_set(m, dev, left | (right << 8));
    591 +	ret = mixer_set(m, dev, m->mutedevs, left | (right << 8));
    592  	snd_mtxunlock(m->lock);
    593  
    594  	return ((ret != 0) ? ENXIO : 0);
    595 @@ -1304,10 +1334,18 @@ mixer_ioctl_cmd(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
    596  		goto done;
    597  	}
    598  	if ((cmd & ~0xff) == MIXER_WRITE(0)) {
    599 -		if (j == SOUND_MIXER_RECSRC)
    600 +		switch (j) {
    601 +		case SOUND_MIXER_RECSRC:
    602  			ret = mixer_setrecsrc(m, *arg_i);
    603 -		else
    604 -			ret = mixer_set(m, j, *arg_i);
    605 +			break;
    606 +		case SOUND_MIXER_MUTE:
    607 +			mix_setmutedevs(m, *arg_i);
    608 +			ret = 0;
    609 +			break;
    610 +		default:
    611 +			ret = mixer_set(m, j, m->mutedevs, *arg_i);
    612 +			break;
    613 +		}
    614  		snd_mtxunlock(m->lock);
    615  		return ((ret == 0) ? 0 : ENXIO);
    616  	}
    617 @@ -1318,6 +1356,9 @@ mixer_ioctl_cmd(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
    618  		case SOUND_MIXER_STEREODEVS:
    619  			v = mix_getdevs(m);
    620  			break;
    621 +		case SOUND_MIXER_MUTE:
    622 +			v = mix_getmutedevs(m);
    623 +			break;
    624  		case SOUND_MIXER_RECMASK:
    625  			v = mix_getrecdevs(m);
    626  			break;
    627 @@ -1326,6 +1367,7 @@ mixer_ioctl_cmd(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
    628  			break;
    629  		default:
    630  			v = mixer_get(m, j);
    631 +			break;
    632  		}
    633  		*arg_i = v;
    634  		snd_mtxunlock(m->lock);
    635 @@ -1554,5 +1596,5 @@ mix_set_locked(struct snd_mixer *m, u_int dev, int left, int right)
    636  
    637  	level = (left & 0xFF) | ((right & 0xFF) << 8);
    638  
    639 -	return (mixer_set(m, dev, level));
    640 +	return (mixer_set(m, dev, m->mutedevs, level));
    641  }
    642 diff --git a/sys/dev/sound/pcm/mixer.h b/sys/dev/sound/pcm/mixer.h
    643 index 8e11d553a3e..7857609b289 100644
    644 --- a/sys/dev/sound/pcm/mixer.h
    645 +++ b/sys/dev/sound/pcm/mixer.h
    646 @@ -60,8 +60,10 @@ device_t mix_get_dev(struct snd_mixer *m);
    647  
    648  void mix_setdevs(struct snd_mixer *m, u_int32_t v);
    649  void mix_setrecdevs(struct snd_mixer *m, u_int32_t v);
    650 +void mix_setmutedevs(struct snd_mixer *m, u_int32_t v);
    651  u_int32_t mix_getdevs(struct snd_mixer *m);
    652  u_int32_t mix_getrecdevs(struct snd_mixer *m);
    653 +u_int32_t mix_getmutedevs(struct snd_mixer *m);
    654  void mix_setparentchild(struct snd_mixer *m, u_int32_t parent, u_int32_t childs);
    655  void mix_setrealdev(struct snd_mixer *m, u_int32_t dev, u_int32_t realdev);
    656  u_int32_t mix_getparent(struct snd_mixer *m, u_int32_t dev);
    657 diff --git a/sys/dev/sound/pcm/sound.c b/sys/dev/sound/pcm/sound.c
    658 index 299e4937f8e..663ec84f93b 100644
    659 --- a/sys/dev/sound/pcm/sound.c
    660 +++ b/sys/dev/sound/pcm/sound.c
    661 @@ -1015,12 +1015,30 @@ SYSCTL_PROC(_hw_snd, OID_AUTO, clone_gc,
    662      "global clone garbage collector");
    663  #endif
    664  
    665 +static u_int8_t
    666 +pcm_mode_init(struct snddev_info *d)
    667 +{
    668 +	u_int8_t mode = 0;
    669 +
    670 +	if (d->playcount > 0)
    671 +		mode |= PCM_MODE_PLAY;
    672 +	if (d->reccount > 0)
    673 +		mode |= PCM_MODE_REC;
    674 +	if (d->mixer_dev != NULL)
    675 +		mode |= PCM_MODE_MIXER;
    676 +
    677 +	return (mode);
    678 +}
    679 +
    680  static void
    681  pcm_sysinit(device_t dev)
    682  {
    683    	struct snddev_info *d = device_get_softc(dev);
    684 +	u_int8_t mode;
    685 +
    686 +	mode = pcm_mode_init(d);
    687  
    688 -  	/* XXX: an user should be able to set this with a control tool, the
    689 +	/* XXX: a user should be able to set this with a control tool, the
    690  	   sysadmin then needs min+max sysctls for this */
    691  	SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
    692  	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
    693 @@ -1030,6 +1048,11 @@ pcm_sysinit(device_t dev)
    694  	    "bitperfect", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, d,
    695  	    sizeof(d), sysctl_dev_pcm_bitperfect, "I",
    696  	    "bit-perfect playback/recording (0=disable, 1=enable)");
    697 +	SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
    698 +	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
    699 +	    OID_AUTO, "mode", CTLFLAG_RD, NULL, mode,
    700 +	    "mode (1=mixer, 2=play, 4=rec. The values are OR'ed if more than one"
    701 +	    "mode is supported)");
    702  #ifdef SND_DEBUG
    703  	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
    704  	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
    705 @@ -1133,7 +1156,7 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
    706  	sysctl_ctx_init(&d->rec_sysctl_ctx);
    707  	d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx,
    708  	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec",
    709 -	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "record channels node");
    710 +	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "recording channels node");
    711  
    712  	if (numplay > 0 || numrec > 0)
    713  		d->flags |= SD_F_AUTOVCHAN;
    714 diff --git a/sys/dev/sound/pcm/sound.h b/sys/dev/sound/pcm/sound.h
    715 index cdae5e837cd..62787a3e689 100644
    716 --- a/sys/dev/sound/pcm/sound.h
    717 +++ b/sys/dev/sound/pcm/sound.h
    718 @@ -411,6 +411,10 @@ struct snddev_info {
    719  void	sound_oss_sysinfo(oss_sysinfo *);
    720  int	sound_oss_card_info(oss_card_info *);
    721  
    722 +#define	PCM_MODE_MIXER		0x01
    723 +#define	PCM_MODE_PLAY		0x02
    724 +#define	PCM_MODE_REC		0x04
    725 +
    726  #define PCM_LOCKOWNED(d)	mtx_owned((d)->lock)
    727  #define	PCM_LOCK(d)		mtx_lock((d)->lock)
    728  #define	PCM_UNLOCK(d)		mtx_unlock((d)->lock)