--- sys/dev/sound/pcm/channel.c.orig Mon Jul 9 02:23:30 2007 +++ sys/dev/sound/pcm/channel.c Thu Jul 12 12:04:19 2007 @@ -107,6 +107,80 @@ "interrupt timeout (1 - 10) seconds"); #endif +static int chn_vpc_autoreset = 0; +TUNABLE_INT("hw.snd.vpc_autoreset", &chn_vpc_autoreset); +SYSCTL_INT(_hw_snd, OID_AUTO, vpc_autoreset, CTLFLAG_RW, + &chn_vpc_autoreset, 0, "automatically reset channels volume to 0db"); + +static int chn_vol_0db_pcm = SND_VOL_0DB_PCM; + +static void +chn_vpc_proc(int reset, int db) +{ + struct snddev_info *d; + struct pcm_channel *c; + int i; + + for (i = 0; pcm_devclass != NULL && + i < devclass_get_maxunit(pcm_devclass); i++) { + d = devclass_get_softc(pcm_devclass, i); + if (!PCM_REGISTERED(d)) + continue; + pcm_lock(d); + PCM_WAIT(d); + PCM_ACQUIRE(d); + CHN_FOREACH(c, d, channels.pcm) { + CHN_LOCK(c); + CHN_SETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_VOL_0DB, db); + if (reset != 0) + chn_vpc_reset(c, SND_VOL_C_PCM, 1); + CHN_UNLOCK(c); + } + PCM_RELEASE(d); + pcm_unlock(d); + } +} + +static int +sysctl_hw_snd_vpc_0db(SYSCTL_HANDLER_ARGS) +{ + int err, val; + + val = chn_vol_0db_pcm; + err = sysctl_handle_int(oidp, &val, 0, req); + if (err != 0 || req->newptr == NULL) + return (err); + if (val < SND_VOL_0DB_MIN || val > SND_VOL_0DB_MAX) + return (EINVAL); + + chn_vol_0db_pcm = val; + chn_vpc_proc(0, val); + + return (0); +} +SYSCTL_PROC(_hw_snd, OID_AUTO, vpc_0db, CTLTYPE_INT | CTLFLAG_RW, + 0, sizeof(int), sysctl_hw_snd_vpc_0db, "I", + "0db relative level"); + +static int +sysctl_hw_snd_vpc_reset(SYSCTL_HANDLER_ARGS) +{ + int err, val; + + val = 0; + err = sysctl_handle_int(oidp, &val, 0, req); + if (err != 0 || req->newptr == NULL || val == 0) + return (err); + + chn_vol_0db_pcm = SND_VOL_0DB_PCM; + chn_vpc_proc(1, SND_VOL_0DB_PCM); + + return (0); +} +SYSCTL_PROC(_hw_snd, OID_AUTO, vpc_reset, CTLTYPE_INT | CTLFLAG_RW, + 0, sizeof(int), sysctl_hw_snd_vpc_reset, "I", + "reset volume on all channels"); + static int chn_usefrags = 0; TUNABLE_INT("hw.snd.usefrags", &chn_usefrags); static int chn_syncdelay = -1; @@ -1102,6 +1176,19 @@ c->feederflags = 0; c->sm = NULL; + /* Only Front Left/Right, for now. */ + c->matrix[0] = SND_CHN_T_FL; + c->matrix[1] = SND_CHN_T_FR; + c->matrix[2] = SND_CHN_T_MAX; + + c->volume[SND_VOL_C_MASTER][SND_CHN_T_VOL_0DB] = SND_VOL_0DB_MASTER; + c->volume[SND_VOL_C_PCM][SND_CHN_T_VOL_0DB] = chn_vol_0db_pcm; + + c->volume[SND_VOL_C_MASTER][SND_CHN_T_FL] = SND_VOL_0DB_MASTER; + c->volume[SND_VOL_C_MASTER][SND_CHN_T_FR] = SND_VOL_0DB_MASTER; + + chn_vpc_reset(c, SND_VOL_C_PCM, 1); + ret = ENODEV; CHN_UNLOCK(c); /* XXX - Unlock for CHANNEL_INIT() malloc() call */ c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, direction); @@ -1202,21 +1289,94 @@ return r; } +/* XXX Obsolete. Use *_matrix() variant instead. */ int chn_setvolume(struct pcm_channel *c, int left, int right) { + int ret; + + ret = chn_setvolume_matrix(c, SND_VOL_C_MASTER, SND_CHN_T_FL, left); + ret |= chn_setvolume_matrix(c, SND_VOL_C_MASTER, SND_CHN_T_FR, + right) << 8; + + return (ret); +} + +int +chn_setvolume_matrix(struct pcm_channel *c, snd_volume_class_t vc, + snd_channel_t vt, int val) +{ + int i; + + KASSERT(c != NULL && vc >= SND_VOL_C_MASTER && vc < SND_VOL_C_MAX && + (vc == SND_VOL_C_MASTER || (vc & 1)) && + (vt == SND_CHN_T_VOL_0DB || (vt >= SND_CHN_T_BEGIN && + vt <= SND_CHN_T_END)) && (vt != SND_CHN_T_VOL_0DB || + (val >= SND_VOL_0DB_MIN && val <= SND_VOL_0DB_MAX)), + ("%s(): invalid volume matrix c=%p vc=%d vt=%d val=%d", + __func__, c, vc, vt, val)); CHN_LOCKASSERT(c); - /* should add a feeder for volume changing if channel returns -1 */ - if (left > 100) - left = 100; - if (left < 0) - left = 0; - if (right > 100) - right = 100; - if (right < 0) - right = 0; - c->volume = left | (right << 8); - return 0; + + if (val < 0) + val = 0; + if (val > 100) + val = 100; + + c->volume[vc][vt] = val; + + /* + * Do relative calculation here and store it into class + 1 + * to ease the job of feeder_volume. + */ + if (vc == SND_VOL_C_MASTER) { + for (vc = SND_VOL_C_BEGIN; vc <= SND_VOL_C_END; + vc += SND_VOL_C_STEP) + c->volume[SND_VOL_C_VAL(vc)][vt] = + SND_VOL_CALC_VAL(c->volume, vc, vt); + } else if (vc & 1) { + if (vt == SND_CHN_T_VOL_0DB) { + for (i = 0; c->matrix[i] != SND_CHN_T_MAX; i++) { + vt = c->matrix[i]; + c->volume[SND_VOL_C_VAL(vc)][vt] = + SND_VOL_CALC_VAL(c->volume, vc, vt); + } + } else + c->volume[SND_VOL_C_VAL(vc)][vt] = + SND_VOL_CALC_VAL(c->volume, vc, vt); + } + + return (val); +} + +int +chn_getvolume_matrix(struct pcm_channel *c, snd_volume_class_t vc, + snd_channel_t vt) +{ + KASSERT(c != NULL && vc >= SND_VOL_C_MASTER && vc < SND_VOL_C_MAX && + (vt == SND_CHN_T_VOL_0DB || + (vt >= SND_CHN_T_BEGIN && vt <= SND_CHN_T_END)), + ("%s(): invalid volume matrix c=%p vc=%d vt=%d", + __func__, c, vc, vt)); + CHN_LOCKASSERT(c); + + return (c->volume[vc][vt]); +} + +void +chn_vpc_reset(struct pcm_channel *c, snd_volume_class_t vc, int force) +{ + int i; + + KASSERT(c != NULL && vc >= SND_VOL_C_BEGIN && vc <= SND_VOL_C_END, + ("%s(): invalid reset c=%p vc=%d", __func__, c, vc)); + CHN_LOCKASSERT(c); + + if (force == 0 && chn_vpc_autoreset == 0) + return; + + for (i = 0; c->matrix[i] != SND_CHN_T_MAX; i++) + CHN_SETVOLUME(c, vc, c->matrix[i], + c->volume[vc][SND_CHN_T_VOL_0DB]); } static u_int32_t @@ -1840,11 +2000,13 @@ static int chn_buildfeeder(struct pcm_channel *c) { + struct snddev_info *d; struct feeder_class *fc; struct pcm_feederdesc desc; + struct pcm_feeder *f; struct snd_mixer *m; - u_int32_t tmp[2], type, flags, hwfmt, *fmtlist; - int err; + u_int32_t tmp[2], type, flags, hwfmt, parent, *fmtlist; + int err, vol, left, right; char fmtstr[AFMTSTR_MAXSZ]; CHN_LOCKASSERT(c); @@ -1894,26 +2056,28 @@ } else return EOPNOTSUPP; - /* XXX These are too much.. */ - if (c->parentsnddev != NULL && c->parentsnddev->mixer_dev != NULL && - c->parentsnddev->mixer_dev->si_drv1 != NULL) - m = c->parentsnddev->mixer_dev->si_drv1; + d = c->parentsnddev; + + if (c->direction == PCMDIR_PLAY && (d->flags & SD_F_SOFTPCMVOL) && + d->mixer_dev != NULL) + m = d->mixer_dev->si_drv1; else m = NULL; c->feederflags &= ~(1 << FEEDER_VOLUME); - if (c->direction == PCMDIR_PLAY && !(c->flags & CHN_F_VIRTUAL) && m && - (c->parentsnddev->flags & SD_F_SOFTPCMVOL)) + if (((d->flags & SD_F_VPC) && CHN_EMPTY(c, children)) || + (!(d->flags & SD_F_VPC) && (d->flags & SD_F_SOFTPCMVOL) && + !(c->flags & CHN_F_VIRTUAL))) c->feederflags |= 1 << FEEDER_VOLUME; - if (!(c->flags & CHN_F_VIRTUAL) && c->parentsnddev && - ((c->direction == PCMDIR_PLAY && - (c->parentsnddev->flags & SD_F_PSWAPLR)) || - (c->direction == PCMDIR_REC && - (c->parentsnddev->flags & SD_F_RSWAPLR)))) + c->feederflags &= ~(1 << FEEDER_SWAPLR); + if (!(c->flags & CHN_F_VIRTUAL) && ((c->direction == PCMDIR_PLAY && + (d->flags & SD_F_PSWAPLR)) || (c->direction == PCMDIR_REC && + (d->flags & SD_F_RSWAPLR)))) c->feederflags |= 1 << FEEDER_SWAPLR; flags = c->feederflags; + fmtlist = chn_getcaps(c)->fmtlist; DEB(printf("feederflags %x\n", flags)); @@ -1927,9 +2091,11 @@ DEB(printf("find feeder type %d, ", type)); if (type == FEEDER_VOLUME || type == FEEDER_RATE) { if (c->feeder->desc->out & AFMT_32BIT) - strlcpy(fmtstr,"s32le", sizeof(fmtstr)); + strlcpy(fmtstr,"s32le", + sizeof(fmtstr)); else if (c->feeder->desc->out & AFMT_24BIT) - strlcpy(fmtstr, "s24le", sizeof(fmtstr)); + strlcpy(fmtstr, "s24le", + sizeof(fmtstr)); else { /* * 8bit doesn't provide enough headroom @@ -1937,20 +2103,23 @@ * creating too much noises. Force to * 16bit instead. */ - strlcpy(fmtstr, "s16le", sizeof(fmtstr)); + strlcpy(fmtstr, "s16le", + sizeof(fmtstr)); } if (!(c->feeder->desc->out & AFMT_8BIT) && - c->feeder->desc->out & AFMT_BIGENDIAN) + c->feeder->desc->out & AFMT_BIGENDIAN) afmtstr_swap_endian(fmtstr); - if (!(c->feeder->desc->out & (AFMT_A_LAW | AFMT_MU_LAW)) && - !(c->feeder->desc->out & AFMT_SIGNED)) + if (!(c->feeder->desc->out & AFMT_SIGNED)) afmtstr_swap_sign(fmtstr); - desc.in = afmtstr2afmt(NULL, fmtstr, AFMTSTR_MONO_RETURN); + desc.in = afmtstr2afmt(NULL, fmtstr, + AFMTSTR_MONO_RETURN); if (desc.in == 0) desc.in = AFMT_S16_LE; /* feeder_volume need stereo processing */ if (type == FEEDER_VOLUME || - c->feeder->desc->out & AFMT_STEREO) + (c->feeder->desc->out & AFMT_STEREO) || + (type != FEEDER_VOLUME && + (flags & (1 << FEEDER_VOLUME)))) desc.in |= AFMT_STEREO; desc.out = desc.in; } else if (type == FEEDER_SWAPLR) { @@ -2024,36 +2193,40 @@ sndbuf_setfmt(c->bufhard, hwfmt); - if ((flags & (1 << FEEDER_VOLUME))) { - u_int32_t parent; - int vol, left, right; - - CHN_UNLOCK(c); - vol = mix_get(m, SOUND_MIXER_PCM); - if (vol == -1) { - device_printf(c->dev, - "Soft PCM Volume: Failed to read default value\n"); - vol = 100 | (100 << 8); - } - left = vol & 0x7f; - right = (vol >> 8) & 0x7f; - parent = mix_getparent(m, SOUND_MIXER_PCM); - if (parent != SOUND_MIXER_NONE) { - vol = mix_get(m, parent); + if (flags & (1 << FEEDER_VOLUME)) { + if (m != NULL) { + CHN_UNLOCK(c); + vol = mix_get(m, SOUND_MIXER_PCM); if (vol == -1) { device_printf(c->dev, - "Soft Volume: Failed to read parent " + "Soft PCM Volume: Failed to read " "default value\n"); vol = 100 | (100 << 8); } - left = (left * (vol & 0x7f)) / 100; - right = (right * ((vol >> 8) & 0x7f)) / 100; + left = vol & 0x7f; + right = (vol >> 8) & 0x7f; + parent = mix_getparent(m, SOUND_MIXER_PCM); + if (parent != SOUND_MIXER_NONE) { + vol = mix_get(m, parent); + if (vol == -1) { + device_printf(c->dev, + "Soft Volume: Failed to read " + "parent default value\n"); + vol = 100 | (100 << 8); + } + left = (left * (vol & 0x7f)) / 100; + right = (right * ((vol >> 8) & 0x7f)) / 100; + } + CHN_LOCK(c); + CHN_SETVOLUME(c, SND_VOL_C_MASTER, SND_CHN_T_FL, left); + CHN_SETVOLUME(c, SND_VOL_C_MASTER, SND_CHN_T_FR, right); } - CHN_LOCK(c); - chn_setvolume(c, left, right); + f = chn_findfeeder(c, FEEDER_VOLUME); + if (f != NULL) + FEEDER_SET(f, FEEDVOL_CLASS, SND_VOL_C_PCM); } - return 0; + return (0); } int --- sys/dev/sound/pcm/channel.h.orig Mon Jul 9 02:23:30 2007 +++ sys/dev/sound/pcm/channel.h Thu Jul 12 12:04:19 2007 @@ -26,6 +26,8 @@ * $FreeBSD: src/sys/dev/sound/pcm/channel.h,v 1.37 2007/06/16 03:37:28 ariff Exp $ */ +#include + struct pcmchan_caps { u_int32_t minspeed, maxspeed; u_int32_t *fmtlist; @@ -63,7 +65,10 @@ struct pcm_channel *ch; }; -#define CHN_NAMELEN 32 +#define CHN_NAMELEN 32 +#define CHN_COMM_UNUSED "" +#define CHN_COMM_UNKNOWN "" + struct pcm_channel { kobj_t methods; @@ -72,7 +77,6 @@ struct pcm_feeder *feeder; u_int32_t align; - int volume; int latency; u_int32_t speed; u_int32_t format; @@ -90,6 +94,7 @@ device_t dev; int unit; char name[CHN_NAMELEN]; + char comm[MAXCOMLEN + 1]; struct mtx *lock; int trigger; /** @@ -139,9 +144,15 @@ struct { SLIST_ENTRY(pcm_channel) link; } busy; + struct { + SLIST_ENTRY(pcm_channel) link; + } opened; } pcm; } channels; + int volume[SND_VOL_C_MAX][SND_CHN_T_VOL_MAX]; + int matrix[SND_CHN_T_MAX + 1]; + void *data1, *data2; }; @@ -172,9 +183,8 @@ if (t == y) \ break; \ } \ - if (t != y) { \ + if (t != y) \ CHN_INSERT_HEAD(x, y, z); \ - } \ } while(0) #define CHN_INSERT_AFTER_SAFE(w, x, y, z) do { \ @@ -183,9 +193,8 @@ if (t == y) \ break; \ } \ - if (t != y) { \ + if (t != y) \ CHN_INSERT_AFTER(x, y, z); \ - } \ } while(0) #define CHN_REMOVE_SAFE(x, y, z) do { \ @@ -194,11 +203,27 @@ if (t == y) \ break; \ } \ - if (t == y) { \ + if (t == y) \ CHN_REMOVE(x, y, z); \ +} while(0) + +#define CHN_INSERT_SORT(w, x, y, z) do { \ + struct pcm_channel *t, *a = NULL; \ + CHN_FOREACH(t, x, z) { \ + if ((y)->unit w t->unit) \ + a = t; \ + else \ + break; \ } \ + if (a != NULL) \ + CHN_INSERT_AFTER(a, y, z); \ + else \ + CHN_INSERT_HEAD(x, y, z); \ } while(0) +#define CHN_INSERT_SORT_ASCEND(x, y, z) CHN_INSERT_SORT(>, x, y, z) +#define CHN_INSERT_SORT_DESCEND(x, y, z) CHN_INSERT_SORT(<, x, y, z) + #define CHN_UNIT(x) (snd_unit2u((x)->unit)) #define CHN_DEV(x) (snd_unit2d((x)->unit)) #define CHN_CHAN(x) (snd_unit2c((x)->unit)) @@ -228,6 +253,11 @@ int chn_setdir(struct pcm_channel *c, int dir); int chn_reset(struct pcm_channel *c, u_int32_t fmt); int chn_setvolume(struct pcm_channel *c, int left, int right); +int chn_setvolume_matrix(struct pcm_channel *c, snd_volume_class_t vc, + snd_channel_t vt, int val); +int chn_getvolume_matrix(struct pcm_channel *c, snd_volume_class_t vc, + snd_channel_t vt); +void chn_vpc_reset(struct pcm_channel *c, snd_volume_class_t vc, int force); int chn_setspeed(struct pcm_channel *c, int speed); int chn_setformat(struct pcm_channel *c, u_int32_t fmt); int chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz); @@ -252,6 +282,13 @@ int chn_getrates(struct pcm_channel *c, int **rates); int chn_syncdestroy(struct pcm_channel *c); + +#define CHN_SETVOLUME(x...) chn_setvolume_matrix(x) +#if defined(SND_DIAGNOSTIC) || defined(INVARIANTS) +#define CHN_GETVOLUME(x...) chn_getvolume_matrix(x) +#else +#define CHN_GETVOLUME(x, y, z) ((x)->volume[y][z]) +#endif #ifdef OSSV4_EXPERIMENT int chn_getpeaks(struct pcm_channel *c, int *lpeak, int *rpeak); --- sys/dev/sound/pcm/dsp.c.orig Mon Jul 9 02:23:30 2007 +++ sys/dev/sound/pcm/dsp.c Thu Jul 12 12:04:19 2007 @@ -35,12 +35,14 @@ struct dsp_cdevinfo { struct pcm_channel *rdch, *wrch; + struct pcm_channel *volch; int busy, simplex; TAILQ_ENTRY(dsp_cdevinfo) link; }; #define PCM_RDCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->rdch) #define PCM_WRCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->wrch) +#define PCM_VOLCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->volch) #define PCM_SIMPLEX(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->simplex) #define DSP_CDEVINFO_CACHESIZE 8 @@ -200,7 +202,8 @@ static void dsp_cdevinfo_alloc(struct cdev *dev, - struct pcm_channel *rdch, struct pcm_channel *wrch) + struct pcm_channel *rdch, struct pcm_channel *wrch, + struct pcm_channel *volch) { struct snddev_info *d; struct dsp_cdevinfo *cdi; @@ -209,7 +212,7 @@ d = dsp_get_info(dev); KASSERT(PCM_REGISTERED(d) && dev != NULL && dev->si_drv1 == NULL && - rdch != wrch, + ((rdch == NULL && wrch == NULL) || rdch != wrch), ("bogus %s(), what are you trying to accomplish here?", __func__)); PCM_BUSYASSERT(d); mtx_assert(d->lock, MA_OWNED); @@ -225,6 +228,7 @@ break; cdi->rdch = rdch; cdi->wrch = wrch; + cdi->volch = volch; cdi->simplex = simplex; cdi->busy = 1; TAILQ_REMOVE(&d->dsp_cdevinfo_pool, cdi, link); @@ -237,6 +241,7 @@ pcm_lock(d); cdi->rdch = rdch; cdi->wrch = wrch; + cdi->volch = volch; cdi->simplex = simplex; cdi->busy = 1; TAILQ_INSERT_TAIL(&d->dsp_cdevinfo_pool, cdi, link); @@ -254,7 +259,8 @@ d = dsp_get_info(dev); KASSERT(PCM_REGISTERED(d) && dev != NULL && dev->si_drv1 != NULL && - PCM_RDCH(dev) == NULL && PCM_WRCH(dev) == NULL, + PCM_RDCH(dev) == NULL && PCM_WRCH(dev) == NULL && + PCM_VOLCH(dev) == NULL, ("bogus %s(), what are you trying to accomplish here?", __func__)); PCM_BUSYASSERT(d); mtx_assert(d->lock, MA_OWNED); @@ -263,6 +269,7 @@ dev->si_drv1 = NULL; cdi->rdch = NULL; cdi->wrch = NULL; + cdi->volch = NULL; cdi->simplex = 0; cdi->busy = 0; @@ -340,6 +347,12 @@ DSP_CDEV_TYPE_RDWR, /* duplex read, write, or both */ }; +enum { + DSP_CDEV_VOLCTL_NONE, + DSP_CDEV_VOLCTL_READ, + DSP_CDEV_VOLCTL_WRITE, +}; + #define DSP_F_VALID(x) ((x) & (FREAD | FWRITE)) #define DSP_F_DUPLEX(x) (((x) & (FREAD | FWRITE)) == (FREAD | FWRITE)) #define DSP_F_SIMPLEX(x) (!DSP_F_DUPLEX(x)) @@ -350,30 +363,41 @@ int type; char *name; char *sep; + char *alias; int use_sep; int hw; int max; + int volctl; uint32_t fmt, spd; int query; } dsp_cdevs[] = { - { SND_DEV_DSP, "dsp", ".", 0, 0, 0, + { SND_DEV_DSP, "dsp", ".", NULL, 0, 0, 0, 0, AFMT_U8, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, - { SND_DEV_AUDIO, "audio", ".", 0, 0, 0, + { SND_DEV_AUDIO, "audio", ".", NULL, 0, 0, 0, 0, AFMT_MU_LAW, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, - { SND_DEV_DSP16, "dspW", ".", 0, 0, 0, + { SND_DEV_DSP16, "dspW", ".", NULL, 0, 0, 0, 0, AFMT_S16_LE, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, - { SND_DEV_DSPHW_PLAY, "dsp", ".p", 1, 1, SND_MAXHWCHAN, + { SND_DEV_DSPHW_PLAY, "dsp", ".p", NULL, 1, 1, SND_MAXHWCHAN, 1, AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_WRONLY }, - { SND_DEV_DSPHW_VPLAY, "dsp", ".vp", 1, 1, SND_MAXVCHANS, + { SND_DEV_DSPHW_VPLAY, "dsp", ".vp", NULL, 1, 1, SND_MAXVCHANS, 1, AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_WRONLY }, - { SND_DEV_DSPHW_REC, "dsp", ".r", 1, 1, SND_MAXHWCHAN, + { SND_DEV_DSPHW_REC, "dsp", ".r", NULL, 1, 1, SND_MAXHWCHAN, 1, AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_RDONLY }, - { SND_DEV_DSPHW_VREC, "dsp", ".vr", 1, 1, SND_MAXVCHANS, + { SND_DEV_DSPHW_VREC, "dsp", ".vr", NULL, 1, 1, SND_MAXVCHANS, 1, AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_RDONLY }, - { SND_DEV_DSPHW_CD, "dspcd", ".", 0, 0, 0, + { SND_DEV_DSPHW_CD, "dspcd", ".", NULL, 0, 0, 0, 0, AFMT_S16_LE | AFMT_STEREO, 44100, DSP_CDEV_TYPE_RDWR }, - { SND_DEV_DSP_MMAP, "dsp_mmap", ".", 0, 0, 0, - AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_RDWR }, + /* Low priority, OSSv4 aliases. */ + { SND_DEV_DSP, "dsp_ac3", ".", "dsp", 0, 0, 0, 0, + AFMT_U8, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, + { SND_DEV_DSP, "dsp_mmap", ".", "dsp", 0, 0, 0, 0, + AFMT_U8, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, + { SND_DEV_DSP, "dsp_multich", ".", "dsp", 0, 0, 0, 0, + AFMT_U8, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, + { SND_DEV_DSP, "dsp_spdifout", ".", "dsp", 0, 0, 0, 0, + AFMT_U8, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, + { SND_DEV_DSP, "dsp_spdifin", ".", "dsp", 0, 0, 0, 0, + AFMT_U8, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, }; #define DSP_FIXUP_ERROR() do { \ @@ -397,7 +421,7 @@ { struct pcm_channel *rdch, *wrch; struct snddev_info *d; - uint32_t fmt, spd, prio; + uint32_t fmt, spd, prio, volctl; int i, error, rderror, wrerror, devtype, wdevunit, rdevunit; /* Kind of impossible.. */ @@ -449,27 +473,46 @@ rdevunit = -1; fmt = 0; spd = 0; + volctl = DSP_CDEV_VOLCTL_NONE; for (i = 0; i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) { - if (devtype != dsp_cdevs[i].type) + if (devtype != dsp_cdevs[i].type || dsp_cdevs[i].alias != NULL) continue; - if (DSP_F_SIMPLEX(flags) && - ((dsp_cdevs[i].query == DSP_CDEV_TYPE_WRONLY && - DSP_F_READ(flags)) || - (dsp_cdevs[i].query == DSP_CDEV_TYPE_RDONLY && - DSP_F_WRITE(flags)))) { - /* - * simplex, opposite direction? Please be gone.. - */ - (void)snd_clone_release(i_dev); - PCM_RELEASE_QUICK(d); - PCM_GIANT_EXIT(d); - return (ENOTSUP); - } - if (dsp_cdevs[i].query == DSP_CDEV_TYPE_WRONLY) + /* + * Volume control only valid for DSPHW devices, + * and it must be opened in opposite direction be it + * simplex or duplex. Anything else will be handled + * as usual. + */ + if (dsp_cdevs[i].query == DSP_CDEV_TYPE_WRONLY) { + if (dsp_cdevs[i].volctl != 0 && + DSP_F_READ(flags)) { + volctl = DSP_CDEV_VOLCTL_WRITE; + flags &= ~FREAD; + flags |= FWRITE; + } + if (DSP_F_READ(flags)) { + (void)snd_clone_release(i_dev); + PCM_RELEASE_QUICK(d); + PCM_GIANT_EXIT(d); + return (ENOTSUP); + } wdevunit = dev2unit(i_dev); - else if (dsp_cdevs[i].query == DSP_CDEV_TYPE_RDONLY) + } else if (dsp_cdevs[i].query == DSP_CDEV_TYPE_RDONLY) { + if (dsp_cdevs[i].volctl != 0 && + DSP_F_WRITE(flags)) { + volctl = DSP_CDEV_VOLCTL_READ; + flags &= ~FWRITE; + flags |= FREAD; + } + if (DSP_F_WRITE(flags)) { + (void)snd_clone_release(i_dev); + PCM_RELEASE_QUICK(d); + PCM_GIANT_EXIT(d); + return (ENOTSUP); + } rdevunit = dev2unit(i_dev); + } fmt = dsp_cdevs[i].fmt; spd = dsp_cdevs[i].spd; break; @@ -493,12 +536,15 @@ if (DSP_F_READ(flags)) { /* open for read */ rderror = pcm_chnalloc(d, &rdch, PCMDIR_REC, - td->td_proc->p_pid, rdevunit); + td->td_proc->p_pid, td->td_proc->p_comm, rdevunit); if (rderror == 0 && (chn_reset(rdch, fmt) != 0 || (chn_setspeed(rdch, spd) != 0))) rderror = ENXIO; + if (volctl == DSP_CDEV_VOLCTL_READ) + rderror = 0; + if (rderror != 0) { if (rdch != NULL) pcm_chnrelease(rdch); @@ -509,10 +555,17 @@ return (rderror); } rdch = NULL; + } else if (volctl == DSP_CDEV_VOLCTL_READ) { + if (rdch != NULL) { + pcm_chnref(rdch, 1); + pcm_chnrelease(rdch); + } } else { if (flags & O_NONBLOCK) rdch->flags |= CHN_F_NBIO; pcm_chnref(rdch, 1); + if (volctl == DSP_CDEV_VOLCTL_NONE) + chn_vpc_reset(rdch, SND_VOL_C_PCM, 0); CHN_UNLOCK(rdch); } } @@ -520,12 +573,15 @@ if (DSP_F_WRITE(flags)) { /* open for write */ wrerror = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, - td->td_proc->p_pid, wdevunit); + td->td_proc->p_pid, td->td_proc->p_comm, wdevunit); if (wrerror == 0 && (chn_reset(wrch, fmt) != 0 || (chn_setspeed(wrch, spd) != 0))) wrerror = ENXIO; + if (volctl == DSP_CDEV_VOLCTL_WRITE) + wrerror = 0; + if (wrerror != 0) { if (wrch != NULL) pcm_chnrelease(wrch); @@ -545,27 +601,56 @@ return (wrerror); } wrch = NULL; + } else if (volctl == DSP_CDEV_VOLCTL_WRITE) { + if (wrch != NULL) { + pcm_chnref(wrch, 1); + pcm_chnrelease(wrch); + } } else { if (flags & O_NONBLOCK) wrch->flags |= CHN_F_NBIO; pcm_chnref(wrch, 1); + if (volctl == DSP_CDEV_VOLCTL_NONE) + chn_vpc_reset(wrch, SND_VOL_C_PCM, 0); CHN_UNLOCK(wrch); } } - if (rdch == NULL && wrch == NULL) { - (void)snd_clone_release(i_dev); - PCM_RELEASE_QUICK(d); - PCM_GIANT_EXIT(d); - return ((wrerror != 0) ? wrerror : rderror); - } pcm_lock(d); /* * We're done. Allocate channels information for this cdev. */ - dsp_cdevinfo_alloc(i_dev, rdch, wrch); + switch (volctl) { + case DSP_CDEV_VOLCTL_READ: + KASSERT(wrch == NULL, ("wrch=%p not null!", wrch)); + dsp_cdevinfo_alloc(i_dev, NULL, NULL, rdch); + break; + case DSP_CDEV_VOLCTL_WRITE: + KASSERT(rdch == NULL, ("rdch=%p not null!", rdch)); + dsp_cdevinfo_alloc(i_dev, NULL, NULL, wrch); + break; + case DSP_CDEV_VOLCTL_NONE: + default: + if (wrch == NULL && rdch == NULL) { + (void)snd_clone_release(i_dev); + PCM_RELEASE(d); + pcm_unlock(d); + PCM_GIANT_EXIT(d); + if (wrerror != 0) + return (wrerror); + if (rderror != 0) + return (rderror); + return (EINVAL); + } + dsp_cdevinfo_alloc(i_dev, rdch, wrch, NULL); + if (rdch != NULL) + CHN_INSERT_HEAD(d, rdch, channels.pcm.opened); + if (wrch != NULL) + CHN_INSERT_HEAD(d, wrch, channels.pcm.opened); + break; + } /* * Increase clone refcount for its automatic garbage collector. @@ -583,9 +668,9 @@ static int dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td) { - struct pcm_channel *rdch, *wrch; + struct pcm_channel *rdch, *wrch, *volch; struct snddev_info *d; - int sg_ids, refs; + int sg_ids, rdref, wdref; d = dsp_get_info(i_dev); if (!DSP_REGISTERED(d, i_dev)) @@ -595,24 +680,49 @@ pcm_lock(d); PCM_WAIT(d); + PCM_ACQUIRE(d); rdch = PCM_RDCH(i_dev); wrch = PCM_WRCH(i_dev); + volch = PCM_VOLCH(i_dev); - if (rdch || wrch) { - PCM_ACQUIRE(d); - pcm_unlock(d); + PCM_RDCH(i_dev) = NULL; + PCM_WRCH(i_dev) = NULL; + PCM_VOLCH(i_dev) = NULL; + + rdref = -1; + wdref = -1; + + if (volch != NULL) { + if (volch == rdch) + rdref--; + else if (volch == wrch) + wdref--; + else { + CHN_LOCK(volch); + pcm_chnref(volch, -1); + CHN_UNLOCK(volch); + } + } - refs = 0; - if (rdch) { + if (rdch != NULL) + CHN_REMOVE(d, rdch, channels.pcm.opened); + if (wrch != NULL) + CHN_REMOVE(d, wrch, channels.pcm.opened); + + if (rdch != NULL || wrch != NULL) { + pcm_unlock(d); + if (rdch != NULL) { /* * The channel itself need not be locked because: - * a) Adding a channel to a syncgroup happens only in dsp_ioctl(), - * which cannot run concurrently to dsp_close(). - * b) The syncmember pointer (sm) is protected by the global - * syncgroup list lock. - * c) A channel can't just disappear, invalidating pointers, - * unless it's closed/dereferenced first. + * a) Adding a channel to a syncgroup happens only + * in dsp_ioctl(), which cannot run concurrently + * to dsp_close(). + * b) The syncmember pointer (sm) is protected by + * the global syncgroup list lock. + * c) A channel can't just disappear, invalidating + * pointers, unless it's closed/dereferenced + * first. */ PCM_SG_LOCK(); sg_ids = chn_syncdestroy(rdch); @@ -621,14 +731,14 @@ free_unr(pcmsg_unrhdr, sg_ids); CHN_LOCK(rdch); - refs += pcm_chnref(rdch, -1); + pcm_chnref(rdch, rdref); chn_abort(rdch); /* won't sleep */ - rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); + rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | + CHN_F_DEAD); chn_reset(rdch, 0); pcm_chnrelease(rdch); - PCM_RDCH(i_dev) = NULL; } - if (wrch) { + if (wrch != NULL) { /* * Please see block above. */ @@ -639,33 +749,26 @@ free_unr(pcmsg_unrhdr, sg_ids); CHN_LOCK(wrch); - refs += pcm_chnref(wrch, -1); + pcm_chnref(wrch, wdref); chn_flush(wrch); /* may sleep */ - wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); + wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | + CHN_F_DEAD); chn_reset(wrch, 0); pcm_chnrelease(wrch); - PCM_WRCH(i_dev) = NULL; } - pcm_lock(d); - /* - * If there are no more references, release the channels. - */ - if (refs == 0 && PCM_RDCH(i_dev) == NULL && - PCM_WRCH(i_dev) == NULL) { - dsp_cdevinfo_free(i_dev); - /* - * Release clone busy state and unref it - * so the automatic garbage collector will - * get the hint and do the remaining cleanup - * process. - */ - (void)snd_clone_release(i_dev); - (void)snd_clone_unref(i_dev); - } - PCM_RELEASE(d); } + dsp_cdevinfo_free(i_dev); + /* + * Release clone busy state and unref it so the automatic + * garbage collector will get the hint and do the remaining + * cleanup process. + */ + (void)snd_clone_release(i_dev); + (void)snd_clone_unref(i_dev); + + PCM_RELEASE(d); pcm_unlock(d); PCM_GIANT_LEAVE(d); @@ -760,11 +863,172 @@ } static int -dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td) +dsp_get_volume_channel(struct cdev *dev, struct pcm_channel **volch) +{ + struct snddev_info *d; + struct pcm_channel *c; + int unit; + + KASSERT(dev != NULL && volch != NULL, + ("%s(): NULL query dev=%p volch=%p", __func__, dev, volch)); + + d = dsp_get_info(dev); + if (!PCM_REGISTERED(d)) { + *volch = NULL; + return (EINVAL); + } + + mtx_assert(d->lock, MA_NOTOWNED); + + *volch = NULL; + + c = PCM_VOLCH(dev); + if (c != NULL) { + if (!(c->feederflags & (1 << FEEDER_VOLUME))) + return (-1); + *volch = c; + return (0); + } + + pcm_lock(d); + PCM_WAIT(d); + PCM_ACQUIRE(d); + + unit = dev2unit(dev); + + CHN_FOREACH(c, d, channels.pcm) { + CHN_LOCK(c); + if (c->unit != unit) { + CHN_UNLOCK(c); + continue; + } + *volch = c; + pcm_chnref(c, 1); + PCM_VOLCH(dev) = c; + CHN_UNLOCK(c); + PCM_RELEASE(d); + pcm_unlock(d); + return ((c->feederflags & (1 << FEEDER_VOLUME)) ? 0 : -1); + } + + PCM_RELEASE(d); + pcm_unlock(d); + + return (EINVAL); +} + +static int +dsp_ioctl_channel(struct cdev *dev, struct pcm_channel *volch, u_long cmd, + caddr_t arg) +{ + struct snddev_info *d; + struct pcm_channel *rdch, *wrch; + int j, devtype, ret; + + d = dsp_get_info(dev); + if (!PCM_REGISTERED(d) || !(d->flags & SD_F_VPC)) + return (-1); + + mtx_assert(d->lock, MA_NOTOWNED); + + j = cmd & 0xff; + + rdch = PCM_RDCH(dev); + wrch = PCM_WRCH(dev); + + /* No specific channel, look into cache */ + if (volch == NULL) + volch = PCM_VOLCH(dev); + + /* Look harder */ + if (volch == NULL) { + if (j == SOUND_MIXER_RECLEV && rdch != NULL) + volch = rdch; + else if (j == SOUND_MIXER_PCM && wrch != NULL) + volch = wrch; + } + + devtype = PCMDEV(dev); + + /* Look super harder */ + if (volch == NULL && + (devtype == SND_DEV_DSPHW_PLAY || devtype == SND_DEV_DSPHW_VPLAY || + devtype == SND_DEV_DSPHW_REC || devtype == SND_DEV_DSPHW_VREC)) { + ret = dsp_get_volume_channel(dev, &volch); + if (ret != 0) + return (ret); + if (volch == NULL) + return (EINVAL); + } + + /* Final validation */ + if (volch != NULL) { + CHN_LOCK(volch); + if (!(volch->feederflags & (1 << FEEDER_VOLUME))) { + CHN_UNLOCK(volch); + return (-1); + } + if (volch->direction == PCMDIR_PLAY) + wrch = volch; + else + rdch = volch; + } + + ret = EINVAL; + + if (volch != NULL && + ((j == SOUND_MIXER_PCM && volch->direction == PCMDIR_PLAY) || + (j == SOUND_MIXER_RECLEV && volch->direction == PCMDIR_REC))) { + if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) { + CHN_SETVOLUME(volch, SND_VOL_C_PCM, SND_CHN_T_FL, + *(int *)arg & 0x7f); + CHN_SETVOLUME(volch, SND_VOL_C_PCM, SND_CHN_T_FR, + (*(int *)arg >> 8) & 0x7f); + } else if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) { + *(int *)arg = CHN_GETVOLUME(volch, + SND_VOL_C_PCM, SND_CHN_T_FL); + *(int *)arg |= CHN_GETVOLUME(volch, + SND_VOL_C_PCM, SND_CHN_T_FR) << 8; + } + ret = 0; + } else if (rdch != NULL || wrch != NULL) { + switch (j) { + case SOUND_MIXER_DEVMASK: + case SOUND_MIXER_CAPS: + case SOUND_MIXER_STEREODEVS: + if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) { + *(int *)arg = 0; + if (rdch != NULL) + *(int *)arg |= SOUND_MASK_RECLEV; + if (wrch != NULL) + *(int *)arg |= SOUND_MASK_PCM; + } + ret = 0; + break; + case SOUND_MIXER_RECMASK: + case SOUND_MIXER_RECSRC: + if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) + *(int *)arg = 0; + ret = 0; + break; + default: + break; + } + } + + if (volch != NULL) + CHN_UNLOCK(volch); + + return (ret); +} + +static int +dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, + struct thread *td) { struct pcm_channel *chn, *rdch, *wrch; struct snddev_info *d; - int *arg_i, ret, kill, tmp, xcmd; + int *arg_i, ret, tmp, xcmd; d = dsp_get_info(i_dev); if (!DSP_REGISTERED(d, i_dev)) @@ -775,15 +1039,15 @@ arg_i = (int *)arg; ret = 0; xcmd = 0; + chn = NULL; - /* - * this is an evil hack to allow broken apps to perform mixer ioctls - * on dsp devices. - */ if (IOCGROUP(cmd) == 'M') { - /* - * This is at least, a bug to bug compatible with OSS. - */ + ret = dsp_ioctl_channel(i_dev, PCM_VOLCH(i_dev), cmd, arg); + if (ret != -1) { + PCM_GIANT_EXIT(d); + return (ret); + } + if (d->mixer_dev != NULL) { PCM_ACQUIRE_QUICK(d); ret = mixer_ioctl_cmd(d->mixer_dev, cmd, arg, -1, td, @@ -823,23 +1087,12 @@ getchns(i_dev, &rdch, &wrch, 0); - kill = 0; - if (wrch && (wrch->flags & CHN_F_DEAD)) - kill |= 1; - if (rdch && (rdch->flags & CHN_F_DEAD)) - kill |= 2; - if (kill == 3) { - relchns(i_dev, rdch, wrch, 0); - PCM_GIANT_EXIT(d); - return (EINVAL); - } - if (kill & 1) + if (wrch != NULL && (wrch->flags & CHN_F_DEAD)) wrch = NULL; - if (kill & 2) + if (rdch != NULL && (rdch->flags & CHN_F_DEAD)) rdch = NULL; if (wrch == NULL && rdch == NULL) { - relchns(i_dev, rdch, wrch, 0); PCM_GIANT_EXIT(d); return (EINVAL); } @@ -1465,20 +1718,34 @@ * command". */ case SNDCTL_DSP_GETRECVOL: - if (xcmd == 0) + if (xcmd == 0) { xcmd = SOUND_MIXER_READ_RECLEV; + chn = rdch; + } /* FALLTHROUGH */ case SNDCTL_DSP_SETRECVOL: - if (xcmd == 0) + if (xcmd == 0) { xcmd = SOUND_MIXER_WRITE_RECLEV; + chn = rdch; + } /* FALLTHROUGH */ case SNDCTL_DSP_GETPLAYVOL: - if (xcmd == 0) + if (xcmd == 0) { xcmd = SOUND_MIXER_READ_PCM; + chn = wrch; + } /* FALLTHROUGH */ case SNDCTL_DSP_SETPLAYVOL: - if (xcmd == 0) + if (xcmd == 0) { xcmd = SOUND_MIXER_WRITE_PCM; + chn = wrch; + } + + ret = dsp_ioctl_channel(i_dev, chn, xcmd, arg); + if (ret != -1) { + PCM_GIANT_EXIT(d); + return (ret); + } if (d->mixer_dev != NULL) { PCM_ACQUIRE_QUICK(d); @@ -1487,6 +1754,7 @@ PCM_RELEASE_QUICK(d); } else ret = ENOTSUP; + break; case SNDCTL_DSP_GET_RECSRC_NAMES: @@ -1796,8 +2064,6 @@ break; } - relchns(i_dev, rdch, wrch, 0); - PCM_GIANT_LEAVE(d); return (ret); @@ -1969,7 +2235,7 @@ struct snd_clone_entry *ce; struct pcm_channel *c; int i, unit, udcmask, cunit, devtype, devhw, devcmax, tumax; - char *devname, *devsep; + char *devname, *devcmp, *devsep; KASSERT(dsp_umax >= 0 && dsp_cmax >= 0, ("Uninitialized unit!")); @@ -1988,13 +2254,16 @@ for (i = 0; unit == -1 && i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) { devtype = dsp_cdevs[i].type; - devname = dsp_cdevs[i].name; + devcmp = dsp_cdevs[i].name; devsep = dsp_cdevs[i].sep; + devname = dsp_cdevs[i].alias; + if (devname == NULL) + devname = devcmp; devhw = dsp_cdevs[i].hw; devcmax = dsp_cdevs[i].max - 1; - if (strcmp(name, devname) == 0) + if (strcmp(name, devcmp) == 0) unit = snd_unit; - else if (dsp_stdclone(name, devname, devsep, + else if (dsp_stdclone(name, devcmp, devsep, dsp_cdevs[i].use_sep, &unit, &cunit) != 0) { unit = -1; cunit = -1; @@ -2131,7 +2400,7 @@ dtype = snd_unit2d(unit); for (i = 0; i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) { - if (dtype != dsp_cdevs[i].type) + if (dtype != dsp_cdevs[i].type || dsp_cdevs[i].alias != NULL) continue; snprintf(buf, len, "%s%d%s%d", dsp_cdevs[i].name, snd_unit2u(unit), dsp_cdevs[i].sep, snd_unit2c(unit)); --- sys/dev/sound/pcm/feeder.h.orig Mon Jul 9 02:23:30 2007 +++ sys/dev/sound/pcm/feeder.h Thu Jul 12 12:04:19 2007 @@ -74,17 +74,21 @@ }; \ SYSINIT(feeder, SI_SUB_DRIVERS, SI_ORDER_ANY, feeder_register, &feeder ## _class); -#define FEEDER_ROOT 0 -#define FEEDER_FMT 1 -#define FEEDER_MIXER 2 -#define FEEDER_RATE 3 -#define FEEDER_FILTER 4 -#define FEEDER_VOLUME 5 -#define FEEDER_SWAPLR 6 -#define FEEDER_LAST 32 +enum { + FEEDER_ROOT, + FEEDER_FMT, + FEEDER_MIXER, + FEEDER_RATE, + FEEDER_FILTER, + FEEDER_VOLUME, + FEEDER_SWAPLR, + FEEDER_LAST +}; #define FEEDRATE_SRC 1 #define FEEDRATE_DST 2 + +#define FEEDVOL_CLASS 1 #define FEEDRATE_RATEMIN 1 #define FEEDRATE_RATEMAX 2016000 /* 48000 * 42 */ --- sys/dev/sound/pcm/feeder_fmt.c.orig Mon Jul 9 02:23:30 2007 +++ sys/dev/sound/pcm/feeder_fmt.c Thu Jul 12 12:04:19 2007 @@ -39,6 +39,8 @@ */ #include +#include +#include #include "feeder_if.h" SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pcm/feeder_fmt.c,v 1.23 2007/06/02 13:07:44 joel Exp $"); @@ -50,7 +52,7 @@ &feeder_fmt_stereodownmix, 1, "averaging stereo downmix"); #endif -#define FEEDFMT_RESERVOIR 8 /* 32bit stereo */ +#define FEEDFMT_RESERVOIR (PCM_32_BPS * SND_CHN_MAX) static uint8_t ulaw_to_u8_tbl[] = { 3, 7, 11, 15, 19, 23, 27, 31, @@ -374,11 +376,11 @@ * Bit conversion */ -#define FBIT_DATA(i, o, c) ((intptr_t)((((c) & 0xf) << 6) | \ - (((i) & 0x7) << 3) | ((o) & 0x7))) -#define FBIT_OUTBPS(m) ((m) & 0x7) -#define FBIT_INBPS(m) FBIT_OUTBPS((m) >> 3) -#define FBIT_CHANNELS(m) (((m) >> 6) & 0xf) +#define FBIT_DATA(i, o, c) ((intptr_t)((((c) & 0x3f) << 8) | \ + (((i) & 0xf) << 4) | ((o) & 0xf))) +#define FBIT_OUTBPS(m) ((m) & 0xf) +#define FBIT_INBPS(m) FBIT_OUTBPS((m) >> 4) +#define FBIT_CHANNELS(m) (((m) >> 8) & 0x3f) static int feed_updownbit_init(struct pcm_feeder *f) @@ -944,32 +946,31 @@ /* * Channel conversion (stereo -> mono) */ -#define FEEDER_FMT_STEREODOWNMIX(FMTBIT, FMT_INTCAST, SIGN, \ - SIGNS, ENDIAN, ENDIANS) \ +#define FEEDER_FMT_STEREODOWNMIX(FMTBIT, SIGN, SIGNS, ENDIAN, ENDIANS) \ static void \ -SIGNS##FMTBIT##ENDIANS##_stereodownmix(uint8_t *dst, uint8_t *sx, uint8_t *sy) \ +SIGNS##FMTBIT##ENDIANS##e_stereodownmix(uint8_t *dst, uint8_t *sx, uint8_t *sy) \ { \ - int32_t v; \ + intpcm_t v; \ \ - v = ((FMT_INTCAST)PCM_READ_##SIGN##FMTBIT##_##ENDIAN(sx) + \ - PCM_READ_##SIGN##FMTBIT##_##ENDIAN(sy)) >> 1; \ - PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN(dst, v); \ + v = (INTPCM##FMTBIT##_T(PCM_READ_##SIGN##FMTBIT##_##ENDIAN##E(sx)) + \ + PCM_READ_##SIGN##FMTBIT##_##ENDIAN##E(sy)) >> 1; \ + PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN##E(dst, v); \ } -FEEDER_FMT_STEREODOWNMIX(8, int32_t, S, s, NE, ne); -FEEDER_FMT_STEREODOWNMIX(16, int32_t, S, s, LE, le); -FEEDER_FMT_STEREODOWNMIX(24, int32_t, S, s, LE, le); -FEEDER_FMT_STEREODOWNMIX(32, intpcm_t, S, s, LE, le); -FEEDER_FMT_STEREODOWNMIX(16, int32_t, S, s, BE, be); -FEEDER_FMT_STEREODOWNMIX(24, int32_t, S, s, BE, be); -FEEDER_FMT_STEREODOWNMIX(32, intpcm_t, S, s, BE, be); -FEEDER_FMT_STEREODOWNMIX(8, int32_t, U, u, NE, ne); -FEEDER_FMT_STEREODOWNMIX(16, int32_t, U, u, LE, le); -FEEDER_FMT_STEREODOWNMIX(24, int32_t, U, u, LE, le); -FEEDER_FMT_STEREODOWNMIX(32, intpcm_t, U, u, LE, le); -FEEDER_FMT_STEREODOWNMIX(16, int32_t, U, u, BE, be); -FEEDER_FMT_STEREODOWNMIX(24, int32_t, U, u, BE, be); -FEEDER_FMT_STEREODOWNMIX(32, intpcm_t, U, u, BE, be); +FEEDER_FMT_STEREODOWNMIX(8, S, s, N, n) +FEEDER_FMT_STEREODOWNMIX(16, S, s, L, l) +FEEDER_FMT_STEREODOWNMIX(24, S, s, L, l) +FEEDER_FMT_STEREODOWNMIX(32, S, s, L, l) +FEEDER_FMT_STEREODOWNMIX(16, S, s, B, b) +FEEDER_FMT_STEREODOWNMIX(24, S, s, B, b) +FEEDER_FMT_STEREODOWNMIX(32, S, s, B, b) +FEEDER_FMT_STEREODOWNMIX(8, U, u, N, n) +FEEDER_FMT_STEREODOWNMIX(16, U, u, L, l) +FEEDER_FMT_STEREODOWNMIX(24, U, u, L, l) +FEEDER_FMT_STEREODOWNMIX(32, U, u, L, l) +FEEDER_FMT_STEREODOWNMIX(16, U, u, B, b) +FEEDER_FMT_STEREODOWNMIX(24, U, u, B, b) +FEEDER_FMT_STEREODOWNMIX(32, U, u, B, b) static void ulaw_stereodownmix(uint8_t *dst, uint8_t *sx, uint8_t *sy) @@ -1017,8 +1018,8 @@ { AFMT_U32_BE, PCM_32_BPS, { NULL, u32be_stereodownmix }}, }; -#define FSM_DATA(i, j) ((intptr_t)((((i) & 0x1f) << 1) | ((j) & 0x1))) -#define FSM_INFOIDX(m) (((m) >> 1) & 0x1f) +#define FSM_DATA(i, j) ((intptr_t)((((i) & 0x3f) << 1) | ((j) & 0x1))) +#define FSM_INFOIDX(m) (((m) >> 1) & 0x3f) #define FSM_FUNCIDX(m) ((m) & 0x1) static int --- sys/dev/sound/pcm/feeder_rate.c.orig Mon Jul 9 02:23:30 2007 +++ sys/dev/sound/pcm/feeder_rate.c Thu Jul 12 12:04:19 2007 @@ -75,6 +75,7 @@ */ #include +#include #include "feeder_if.h" SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pcm/feeder_rate.c,v 1.23 2007/06/16 03:37:28 ariff Exp $"); @@ -96,8 +97,8 @@ struct feed_rate_info; -typedef uint32_t (*feed_rate_converter)(struct feed_rate_info *, - uint8_t *, uint32_t); +typedef uint32_t (*feed_rate_converter)(struct feed_rate_info *, uint8_t *, + uint32_t); struct feed_rate_info { uint32_t src, dst; /* rounded source / destination rates */ @@ -181,13 +182,13 @@ 0, sizeof(int), sysctl_hw_snd_feeder_rate_round, "I", "sample rate converter rounding threshold"); -#define FEEDER_RATE_CONVERT(FMTBIT, RATE_INTCAST, SIGN, SIGNS, ENDIAN, ENDIANS) \ +#define FEEDER_RATE_CONVERT(FMTBIT, SIGN, SIGNS, ENDIAN, ENDIANS) \ static uint32_t \ -feed_convert_##SIGNS##FMTBIT##ENDIANS(struct feed_rate_info *info, \ - uint8_t *dst, uint32_t max) \ +feed_convert_##SIGNS##FMTBIT##ENDIANS##e(struct feed_rate_info *info, \ + uint8_t *dst, uint32_t max) \ { \ uint32_t ret, smpsz, ch, pos, bpos, gx, gy, alpha, d1, d2; \ - int32_t x, y; \ + intpcm_t x, y; \ int i; \ uint8_t *src, *sx, *sy; \ \ @@ -215,11 +216,12 @@ sy = src; \ i = ch; \ do { \ - x = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(sx); \ - y = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(sy); \ - x = (((RATE_INTCAST)x * d1) + \ - ((RATE_INTCAST)y * d2)) >> PCM_FXSHIFT; \ - PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN(dst, x); \ + x = PCM_READ_##SIGN##FMTBIT##_##ENDIAN##E(sx); \ + y = PCM_READ_##SIGN##FMTBIT##_##ENDIAN##E(sy); \ + x = ((INTPCM##FMTBIT##_T(x) * d1) + \ + (INTPCM##FMTBIT##_T(y) * d2)) >> \ + PCM_FXSHIFT; \ + PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN##E(dst, x); \ dst += PCM_##FMTBIT##_BPS; \ sx += PCM_##FMTBIT##_BPS; \ sy += PCM_##FMTBIT##_BPS; \ @@ -234,20 +236,20 @@ return (ret); \ } -FEEDER_RATE_CONVERT(8, int32_t, S, s, NE, ne) -FEEDER_RATE_CONVERT(16, int32_t, S, s, LE, le) -FEEDER_RATE_CONVERT(24, int32_t, S, s, LE, le) -FEEDER_RATE_CONVERT(32, intpcm_t, S, s, LE, le) -FEEDER_RATE_CONVERT(16, int32_t, S, s, BE, be) -FEEDER_RATE_CONVERT(24, int32_t, S, s, BE, be) -FEEDER_RATE_CONVERT(32, intpcm_t, S, s, BE, be) -FEEDER_RATE_CONVERT(8, int32_t, U, u, NE, ne) -FEEDER_RATE_CONVERT(16, int32_t, U, u, LE, le) -FEEDER_RATE_CONVERT(24, int32_t, U, u, LE, le) -FEEDER_RATE_CONVERT(32, intpcm_t, U, u, LE, le) -FEEDER_RATE_CONVERT(16, int32_t, U, u, BE, be) -FEEDER_RATE_CONVERT(24, int32_t, U, u, BE, be) -FEEDER_RATE_CONVERT(32, intpcm_t, U, u, BE, be) +FEEDER_RATE_CONVERT(8, S, s, N, n) +FEEDER_RATE_CONVERT(16, S, s, L, l) +FEEDER_RATE_CONVERT(24, S, s, L, l) +FEEDER_RATE_CONVERT(32, S, s, L, l) +FEEDER_RATE_CONVERT(16, S, s, B, b) +FEEDER_RATE_CONVERT(24, S, s, B, b) +FEEDER_RATE_CONVERT(32, S, s, B, b) +FEEDER_RATE_CONVERT(8, U, u, N, n) +FEEDER_RATE_CONVERT(16, U, u, L, l) +FEEDER_RATE_CONVERT(24, U, u, L, l) +FEEDER_RATE_CONVERT(32, U, u, L, l) +FEEDER_RATE_CONVERT(16, U, u, B, b) +FEEDER_RATE_CONVERT(24, U, u, B, b) +FEEDER_RATE_CONVERT(32, U, u, B, b) static void feed_speed_ratio(uint32_t src, uint32_t dst, uint32_t *gx, uint32_t *gy) @@ -432,7 +434,7 @@ static int feed_rate(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) + uint32_t count, void *source) { struct feed_rate_info *info = f->data; uint32_t i, smpsz; --- sys/dev/sound/pcm/feeder_volume.c.orig Mon Jul 9 02:23:30 2007 +++ sys/dev/sound/pcm/feeder_volume.c Thu Jul 12 12:04:19 2007 @@ -27,26 +27,25 @@ /* feeder_volume, a long 'Lost Technology' rather than a new feature. */ #include +#include #include "feeder_if.h" SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pcm/feeder_volume.c,v 1.6 2007/06/16 20:36:39 ariff Exp $"); -#define FVOL_OSS_SCALE 100 -#define FVOL_RESOLUTION PCM_FXSHIFT -#define FVOL_CLAMP(val) (((val) << FVOL_RESOLUTION) / FVOL_OSS_SCALE) -#define FVOL_LEFT(val) FVOL_CLAMP((val) & 0x7f) -#define FVOL_RIGHT(val) FVOL_LEFT((val) >> 8) -#define FVOL_MAX (1 << FVOL_RESOLUTION) -#define FVOL_CALC(sval, vval) (((sval) * (vval)) >> FVOL_RESOLUTION) - -typedef uint32_t (*feed_volume_filter)(uint8_t *, int *, uint32_t); - -#define FEEDER_VOLUME_FILTER(FMTBIT, VOL_INTCAST, SIGN, SIGNS, ENDIAN, ENDIANS) \ -static uint32_t \ -feed_volume_filter_##SIGNS##FMTBIT##ENDIANS(uint8_t *b, int *vol, \ - uint32_t count) \ +typedef int (*feed_volume_filter)(uint8_t *, int *, int, int); + +#define FVOL_CALC_8(s, v) SND_VOL_CALC_SAMPLE((int32_t)(s), v) +#define FVOL_CALC_16(s, v) SND_VOL_CALC_SAMPLE((int32_t)(s), v) +#define FVOL_CALC_24(s, v) SND_VOL_CALC_SAMPLE((int64_t)(s), v) +#define FVOL_CALC_32(s, v) SND_VOL_CALC_SAMPLE((int64_t)(s), v) + +#define FEEDER_VOLUME_FILTER(FMTBIT, SIGN, SIGNS, ENDIAN, ENDIANS) \ +static int \ +feed_volume_filter_##SIGNS##FMTBIT##ENDIANS##e(uint8_t *b, int *vol, \ + int channels, int count) \ { \ - int32_t j; \ + intpcm##FMTBIT##_t k; \ + intpcm_t j; \ int i; \ \ i = count; \ @@ -55,29 +54,30 @@ do { \ b -= PCM_##FMTBIT##_BPS; \ i -= PCM_##FMTBIT##_BPS; \ - j = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(b); \ - j = FVOL_CALC((VOL_INTCAST)j, \ - vol[(i / PCM_##FMTBIT##_BPS) & 1]); \ - PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN(b, j); \ + j = PCM_READ_##SIGN##FMTBIT##_##ENDIAN##E(b); \ + k = FVOL_CALC_##FMTBIT(j, \ + vol[(i / PCM_##FMTBIT##_BPS) % channels]); \ + j = PCM_CLAMP_##SIGN##FMTBIT(k); \ + _PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN##E(b, j); \ } while (i != 0); \ \ return (count); \ } -FEEDER_VOLUME_FILTER(8, int32_t, S, s, NE, ne) -FEEDER_VOLUME_FILTER(16, int32_t, S, s, LE, le) -FEEDER_VOLUME_FILTER(24, int32_t, S, s, LE, le) -FEEDER_VOLUME_FILTER(32, intpcm_t, S, s, LE, le) -FEEDER_VOLUME_FILTER(16, int32_t, S, s, BE, be) -FEEDER_VOLUME_FILTER(24, int32_t, S, s, BE, be) -FEEDER_VOLUME_FILTER(32, intpcm_t, S, s, BE, be) -FEEDER_VOLUME_FILTER(8, int32_t, U, u, NE, ne) -FEEDER_VOLUME_FILTER(16, int32_t, U, u, LE, le) -FEEDER_VOLUME_FILTER(24, int32_t, U, u, LE, le) -FEEDER_VOLUME_FILTER(32, intpcm_t, U, u, LE, le) -FEEDER_VOLUME_FILTER(16, int32_t, U, u, BE, be) -FEEDER_VOLUME_FILTER(24, int32_t, U, u, BE, be) -FEEDER_VOLUME_FILTER(32, intpcm_t, U, u, BE, be) +FEEDER_VOLUME_FILTER(8, S, s, N, n) +FEEDER_VOLUME_FILTER(16, S, s, L, l) +FEEDER_VOLUME_FILTER(24, S, s, L, l) +FEEDER_VOLUME_FILTER(32, S, s, L, l) +FEEDER_VOLUME_FILTER(16, S, s, B, b) +FEEDER_VOLUME_FILTER(24, S, s, B, b) +FEEDER_VOLUME_FILTER(32, S, s, B, b) +FEEDER_VOLUME_FILTER(8, U, u, N, n) +FEEDER_VOLUME_FILTER(16, U, u, L, l) +FEEDER_VOLUME_FILTER(24, U, u, L, l) +FEEDER_VOLUME_FILTER(32, U, u, L, l) +FEEDER_VOLUME_FILTER(16, U, u, B, b) +FEEDER_VOLUME_FILTER(24, U, u, B, b) +FEEDER_VOLUME_FILTER(32, U, u, B, b) struct feed_volume_info { uint32_t format; @@ -102,16 +102,19 @@ { AFMT_U32_BE, PCM_32_BPS, feed_volume_filter_u32be }, }; -#define FVOL_DATA(i, c) ((intptr_t)((((i) & 0x1f) << 4) | ((c) & 0xf))) -#define FVOL_INFOIDX(m) (((m) >> 4) & 0x1f) -#define FVOL_CHANNELS(m) ((m) & 0xf) +#define FVOL_DATA(vc, i, c) ((intptr_t)((((vc) & 0x3f) << 12) | \ + (((i) & 0x3f) << 6) | ((c) & 0x3f))) +#define FVOL_CLASS(m) (((m) >> 12) & 0x3f) +#define FVOL_INFOIDX(m) (((m) >> 6) & 0x3f) +#define FVOL_CHANNELS(m) ((m) & 0x3f) static int -feed_volume_init(struct pcm_feeder *f) +feed_volume_setup(struct pcm_feeder *f, int vc) { int i, channels; - if (f->desc->in != f->desc->out) + if (f->desc->in != f->desc->out || vc < SND_VOL_C_BEGIN || + vc > SND_VOL_C_END) return (EINVAL); /* For now, this is mandatory! */ @@ -120,34 +123,84 @@ channels = 2; - for (i = 0; i < sizeof(feed_volume_tbl) / sizeof(feed_volume_tbl[0]); + /* XXX Future interleave multichannel check yada yada.. */ + if (channels < SND_CHN_MIN || channels > SND_CHN_MAX) + return (EINVAL); + + for (i = 0; i < (sizeof(feed_volume_tbl) / sizeof(feed_volume_tbl[0])); i++) { if ((f->desc->out & ~AFMT_STEREO) == feed_volume_tbl[i].format) { - f->data = (void *)FVOL_DATA(i, channels); + f->data = (void *)FVOL_DATA(vc, i, channels); return (0); } } + return (EINVAL); +} + +static int +feed_volume_init(struct pcm_feeder *f) +{ + return (feed_volume_setup(f, SND_VOL_C_PCM)); +} + +static int +feed_volume_set(struct pcm_feeder *f, int what, int value) +{ + switch (what) { + case FEEDVOL_CLASS: + return (feed_volume_setup(f, value)); + break; + default: + break; + } + + return (EINVAL); +} + +static int +feed_volume_get(struct pcm_feeder *f, int what) +{ + switch (what) { + case FEEDVOL_CLASS: + return (FVOL_CLASS((intptr_t)f->data)); + break; + default: + break; + } + return (-1); } static int feed_volume(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) + uint32_t count, void *source) { struct feed_volume_info *info; - int vol[2]; - int k, smpsz; + int vol[SND_CHN_MAX]; + int j, k, v, smpsz, channels; + snd_volume_class_t vcv; + + channels = FVOL_CHANNELS((intptr_t)f->data); + vcv = SND_VOL_C_VAL(FVOL_CLASS((intptr_t)f->data)); + j = channels - 1; + k = 0; + + do { + KASSERT(c->matrix[j] != SND_CHN_T_MAX, + ("%s(): invalid matrix j=%d", __func__, j)); + v = c->volume[vcv][c->matrix[j]]; + if (v != SND_VOL_FLAT) + k = 1; + vol[j] = v; + } while (j-- != 0); - vol[0] = FVOL_LEFT(c->volume); - vol[1] = FVOL_RIGHT(c->volume); - - if (vol[0] == FVOL_MAX && vol[1] == FVOL_MAX) + if (k == 0) return (FEEDER_FEED(f->source, c, b, count, source)); info = &feed_volume_tbl[FVOL_INFOIDX((intptr_t)f->data)]; - smpsz = info->bps * FVOL_CHANNELS((intptr_t)f->data); + smpsz = info->bps * channels; if (count < smpsz) return (0); @@ -156,7 +209,8 @@ return (0); k -= k % smpsz; - return (info->filter(b, vol, k)); + + return (info->filter(b, vol, channels, k)); } static struct pcm_feederdesc feeder_volume_desc[] = { @@ -178,6 +232,8 @@ }; static kobj_method_t feeder_volume_methods[] = { KOBJMETHOD(feeder_init, feed_volume_init), + KOBJMETHOD(feeder_set, feed_volume_set), + KOBJMETHOD(feeder_get, feed_volume_get), KOBJMETHOD(feeder_feed, feed_volume), {0, 0} }; --- sys/dev/sound/pcm/matrix.h.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/pcm/matrix.h Thu Jul 12 12:04:19 2007 @@ -0,0 +1,99 @@ +/*- + * Copyright (c) 2007 Ariff Abdullah + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _SND_MATRIX_H_ +#define _SND_MATRIX_H_ + +typedef enum { + SND_CHN_T_FL, /* Front Left */ + SND_CHN_T_FR, /* Front Right */ + SND_CHN_T_FC, /* Front Center */ + SND_CHN_T_LF, /* Low Frequency */ + SND_CHN_T_BL, /* Back Left */ + SND_CHN_T_BR, /* Back Right */ + SND_CHN_T_FLC, /* Front Left Center */ + SND_CHN_T_FRC, /* Front Right Center */ + SND_CHN_T_BC, /* Back Center */ + SND_CHN_T_SL, /* Side Left */ + SND_CHN_T_SR, /* Side Right */ + SND_CHN_T_TC, /* Top Center */ + SND_CHN_T_TFL, /* Top Front Left */ + SND_CHN_T_TFC, /* Top Front Center */ + SND_CHN_T_TFR, /* Top Front Right */ + SND_CHN_T_TBL, /* Top Back Left */ + SND_CHN_T_TBC, /* Top Back Center */ + SND_CHN_T_TBR, /* Top Back Right */ + SND_CHN_T_MAX, + SND_CHN_T_VOL_0DB = SND_CHN_T_MAX, + SND_CHN_T_VOL_MAX +} snd_channel_t; + +#define SND_CHN_T_BEGIN SND_CHN_T_FL +#define SND_CHN_T_END SND_CHN_T_FR +#define SND_CHN_T_STEP 1 + +#define SND_CHN_MIN 1 +#define SND_CHN_MAX 2 + +/* + * Multichannel interleaved volume matrix. Each calculated value relative + * to master and 0db will be stored in each CLASS + 1 as long as + * chn_setvolume_matrix() or the equivalent CHN_SETVOLUME() macros is + * used (see channel.c). + */ +typedef enum { + SND_VOL_C_MASTER, + SND_VOL_C_PCM, + SND_VOL_C_PCM_VAL, + SND_VOL_C_MAX +} snd_volume_class_t; + +#define SND_VOL_C_BEGIN SND_VOL_C_PCM +#define SND_VOL_C_END SND_VOL_C_PCM +#define SND_VOL_C_STEP 2 + +#define SND_VOL_C_VAL(x) ((x) + 1) + +#define SND_VOL_0DB_MIN 1 +#define SND_VOL_0DB_MAX 100 + +#define SND_VOL_0DB_MASTER 100 +#define SND_VOL_0DB_PCM 45 + +#define SND_VOL_RESOLUTION 8 +#define SND_VOL_FLAT (1 << SND_VOL_RESOLUTION) + +#define SND_VOL_CALC_SAMPLE(x, y) (((x) * (y)) >> SND_VOL_RESOLUTION) + +#define SND_VOL_CALC_VAL(x, y, z) \ + (((((x)[y][z] << SND_VOL_RESOLUTION) / \ + (x)[y][SND_CHN_T_VOL_0DB]) * \ + (x)[SND_VOL_C_MASTER][z]) / \ + (x)[SND_VOL_C_MASTER][SND_CHN_T_VOL_0DB]) \ + +#endif /* !_SND_MATRIX_H_ */ --- sys/dev/sound/pcm/mixer.c.orig Mon Jul 9 02:23:30 2007 +++ sys/dev/sound/pcm/mixer.c Thu Jul 12 12:04:19 2007 @@ -32,6 +32,12 @@ MALLOC_DEFINE(M_MIXER, "mixer", "mixer"); +static int mixer_bypass = 1; +TUNABLE_INT("hw.snd.vpc_mixer_bypass", &mixer_bypass); +SYSCTL_INT(_hw_snd, OID_AUTO, vpc_mixer_bypass, CTLFLAG_RW, + &mixer_bypass, 0, + "control channel pcm/rec volume, bypassing real mixer device"); + #define MIXER_NAMELEN 16 struct snd_mixer { KOBJ_FIELDS; @@ -170,16 +176,24 @@ CHN_FOREACH(c, d, channels.pcm) { CHN_LOCK(c); if (c->direction == PCMDIR_PLAY && - (c->feederflags & (1 << FEEDER_VOLUME))) - chn_setvolume(c, left, right); + (c->feederflags & (1 << FEEDER_VOLUME))) { + CHN_SETVOLUME(c, + SND_VOL_C_MASTER, SND_CHN_T_FL, left); + CHN_SETVOLUME(c, + SND_VOL_C_MASTER, SND_CHN_T_FR, right); + } CHN_UNLOCK(c); } } else { CHN_FOREACH(c, d, channels.pcm.busy) { CHN_LOCK(c); if (c->direction == PCMDIR_PLAY && - (c->feederflags & (1 << FEEDER_VOLUME))) - chn_setvolume(c, left, right); + (c->feederflags & (1 << FEEDER_VOLUME))) { + CHN_SETVOLUME(c, + SND_VOL_C_MASTER, SND_CHN_T_FL, left); + CHN_SETVOLUME(c, + SND_VOL_C_MASTER, SND_CHN_T_FR, right); + } CHN_UNLOCK(c); } } @@ -243,7 +257,8 @@ realdev = m->realdev[i]; tl = (l * (m->level[i] & 0x00ff)) / 100; tr = (r * ((m->level[i] & 0xff00) >> 8)) / 100; - if (i == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) + if (i == SOUND_MIXER_PCM && + (d->flags & SD_F_SOFTPCMVOL)) (void)mixer_set_softpcmvol(m, d, tl, tr); else if (realdev != SOUND_MIXER_NONE) MIXER_SET(m, realdev, tl, tr); @@ -976,6 +991,112 @@ } static int +mixer_ioctl_channel(struct cdev *dev, u_long cmd, caddr_t arg, int mode, + struct thread *td, int from) +{ + struct snddev_info *d; + struct snd_mixer *m; + struct pcm_channel *c, *rdch, *wrch; + pid_t pid; + int j, ret; + + if (td == NULL || td->td_proc == NULL) + return (-1); + + m = dev->si_drv1; + d = device_get_softc(m->dev); + j = cmd & 0xff; + + switch (j) { + case SOUND_MIXER_PCM: + case SOUND_MIXER_RECLEV: + case SOUND_MIXER_DEVMASK: + case SOUND_MIXER_CAPS: + case SOUND_MIXER_STEREODEVS: + break; + default: + return (-1); + break; + } + + pid = td->td_proc->p_pid; + rdch = NULL; + wrch = NULL; + c = NULL; + ret = -1; + + /* + * This is unfair. Imagine single proc opening multiple + * instances of same direction. What we do right now + * is looking for the first matching proc/pid, and just + * that. Nothing more. Consider it done. + * + * The better approach of controlling specific channel + * pcm or rec volume is by doing mixer ioctl + * (SNDCTL_DSP_[SET|GET][PLAY|REC]VOL / SOUND_MIXER_[PCM|RECLEV] + * on its open fd, rather than cracky mixer bypassing here. + */ + CHN_FOREACH(c, d, channels.pcm.opened) { + CHN_LOCK(c); + if (c->pid != pid || + !(c->feederflags & (1 << FEEDER_VOLUME))) { + CHN_UNLOCK(c); + continue; + } + if (rdch == NULL && c->direction == PCMDIR_REC) { + rdch = c; + if (j == SOUND_MIXER_RECLEV) + goto mixer_ioctl_channel_proc; + } else if (wrch == NULL && c->direction == PCMDIR_PLAY) { + wrch = c; + if (j == SOUND_MIXER_PCM) + goto mixer_ioctl_channel_proc; + } + CHN_UNLOCK(c); + if (rdch != NULL && wrch != NULL) + break; + } + + if (rdch == NULL && wrch == NULL) + return (-1); + + if ((j == SOUND_MIXER_DEVMASK || j == SOUND_MIXER_CAPS || + j == SOUND_MIXER_STEREODEVS) && + (cmd & MIXER_READ(0)) == MIXER_READ(0)) { + snd_mtxlock(m->lock); + *(int *)arg = mix_getdevs(m); + snd_mtxunlock(m->lock); + if (rdch != NULL) + *(int *)arg |= SOUND_MASK_RECLEV; + if (wrch != NULL) + *(int *)arg |= SOUND_MASK_PCM; + ret = 0; + } + + return (ret); + +mixer_ioctl_channel_proc: + + KASSERT(c != NULL, ("%s(): NULL channel", __func__)); + CHN_LOCKASSERT(c); + + if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) { + CHN_SETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_FL, + *(int *)arg & 0x7f); + CHN_SETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_FR, + (*(int *)arg >> 8) & 0x7f); + } else if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) { + *(int *)arg = CHN_GETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_FL); + *(int *)arg |= + CHN_GETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_FR) << 8; + } + + CHN_UNLOCK(c); + + return (0); +} + +static int mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td) { @@ -992,7 +1113,15 @@ PCM_GIANT_ENTER(d); PCM_ACQUIRE_QUICK(d); - ret = mixer_ioctl_cmd(i_dev, cmd, arg, mode, td, MIXER_CMD_CDEV); + ret = -1; + + if (mixer_bypass != 0 && (d->flags & SD_F_VPC)) + ret = mixer_ioctl_channel(i_dev, cmd, arg, mode, td, + MIXER_CMD_CDEV); + + if (ret == -1) + ret = mixer_ioctl_cmd(i_dev, cmd, arg, mode, td, + MIXER_CMD_CDEV); PCM_RELEASE_QUICK(d); PCM_GIANT_LEAVE(d); @@ -1002,7 +1131,7 @@ /* * XXX Make sure you can guarantee concurrency safety before calling this - * function, be it through Giant, PCM_CV_*, etc ! + * function, be it through Giant, PCM_*, etc ! */ int mixer_ioctl_cmd(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, --- sys/dev/sound/pcm/pcm.h.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/pcm/pcm.h Thu Jul 12 12:04:19 2007 @@ -0,0 +1,374 @@ +/*- + * Copyright (c) 2007 Ariff Abdullah + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _SND_PCM_H_ +#define _SND_PCM_H_ + +#include + +/* + * Macros for reading/writing PCM sample / int values from bytes array. + * Since every process is done using signed integer (and to make our life + * less miserable), unsigned sample will be converted to its signed + * counterpart and restored during writing back. To avoid overflow, + * we truncate 32bit (and only 32bit) samples down to 24bit (see below + * for the reason), unless PCM_USE_64BIT_ARITH is defined. + */ + +/* + * Automatically turn on 64bit arithmetic on suitable archs + * (amd64 64bit, ia64, etc..) for wider 32bit samples / integer processing. + */ +#if LONG_BIT >= 64 +#undef PCM_USE_64BIT_ARITH +#define PCM_USE_64BIT_ARITH 1 +#endif + +typedef int32_t intpcm_t; + +typedef int32_t intpcm8_t; +typedef int32_t intpcm16_t; +typedef int32_t intpcm24_t; +#ifdef PCM_USE_64BIT_ARITH +typedef int64_t intpcm32_t; +#else +typedef int32_t intpcm32_t; +#endif + +/* 32bit fixed point shift */ +#define PCM_FXSHIFT 8 + +#define PCM_S8_MAX 0x7f +#define PCM_S8_MIN -0x80 +#define PCM_S16_MAX 0x7fff +#define PCM_S16_MIN -0x8000 +#define PCM_S24_MAX 0x7fffff +#define PCM_S24_MIN -0x800000 +#ifdef PCM_USE_64BIT_ARITH +#if LONG_BIT >= 64 +#define PCM_S32_MAX 0x7fffffffL +#define PCM_S32_MIN -0x80000000L +#else +#define PCM_S32_MAX 0x7fffffffLL +#define PCM_S32_MIN -0x80000000LL +#endif +#else +#define PCM_S32_MAX 0x7fffffff +#define PCM_S32_MIN (-0x7fffffff - 1) +#endif + +/* Bytes-per-sample definition */ +#define PCM_8_BPS 1 +#define PCM_16_BPS 2 +#define PCM_24_BPS 3 +#define PCM_32_BPS 4 + +#define INTPCM_T(v) ((intpcm_t)(v)) +#define INTPCM8_T(v) ((intpcm8_t)(v)) +#define INTPCM16_T(v) ((intpcm16_t)(v)) +#define INTPCM24_T(v) ((intpcm24_t)(v)) +#define INTPCM32_T(v) ((intpcm32_t)(v)) + +#if BYTE_ORDER == LITTLE_ENDIAN +#define _PCM_READ_S16_LE(b8) INTPCM_T(*((int16_t *)(b8))) +#define _PCM_READ_S32_LE(b8) INTPCM_T(*((int32_t *)(b8))) +#define _PCM_READ_S16_BE(b8) \ + INTPCM_T((b8)[1] | (((int8_t)((b8)[0])) << 8)) +#define _PCM_READ_S32_BE(b8) \ + INTPCM_T((b8)[3] | ((b8)[2] << 8) | ((b8)[1] << 16) | \ + (((int8_t)((b8)[0])) << 24)) + +#define _PCM_WRITE_S16_LE(b8, val) do { \ + *((int16_t *)(b8)) = (val); \ +} while(0) +#define _PCM_WRITE_S32_LE(b8, val) do { \ + *((int32_t *)(b8)) = (val); \ +} while(0) +#define _PCM_WRITE_S16_BE(bb8, vval) do { \ + intpcm_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[1] = val; \ + b8[0] = val >> 8; \ +} while(0) +#define _PCM_WRITE_S32_BE(bb8, vval) do { \ + intpcm_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[3] = val; \ + b8[2] = val >> 8; \ + b8[1] = val >> 16; \ + b8[0] = val >> 24; \ +} while(0) + +#define _PCM_READ_U16_LE(b8) \ + INTPCM_T((int16_t)(*((uint16_t *)(b8)) ^ 0x8000)) +#define _PCM_READ_U32_LE(b8) \ + INTPCM_T((int32_t)(*((uint32_t *)(b8)) ^ 0x80000000)) +#define _PCM_READ_U16_BE(b8) \ + INTPCM_T((b8)[1] | (((int8_t)((b8)[0] ^ 0x80)) << 8)) +#define _PCM_READ_U32_BE(b8) \ + INTPCM_T((b8)[3] | ((b8)[2] << 8) | ((b8)[1] << 16) | \ + (((int8_t)((b8)[0] ^ 0x80)) << 24)) + +#define _PCM_WRITE_U16_LE(b8, val) do { \ + *((uint16_t *)(b8)) = (val) ^ 0x8000; \ +} while(0) +#define _PCM_WRITE_U32_LE(b8, val) do { \ + *((uint32_t *)(b8)) = (val) ^ 0x80000000; \ +} while(0) +#define _PCM_WRITE_U16_BE(bb8, vval) do { \ + intpcm_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[1] = val; \ + b8[0] = (val >> 8) ^ 0x80; \ +} while(0) +#define _PCM_WRITE_U32_BE(bb8, vval) do { \ + intpcm_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[3] = val; \ + b8[2] = val >> 8; \ + b8[1] = val >> 16; \ + b8[0] = (val >> 24) ^ 0x80; \ +} while(0) +#else /* !LITTLE_ENDIAN */ +#define _PCM_READ_S16_LE(b8) \ + INTPCM_T((b8)[0] | (((int8_t)((b8)[1])) << 8)) +#define _PCM_READ_S32_LE(b8) \ + INTPCM_T((b8)[0] | ((b8)[1] << 8) | ((b8)[2] << 16) | \ + (((int8_t)((b8)[3])) << 24)) +#define _PCM_READ_S16_BE(b8) INTPCM_T(*((int16_t *)(b8))) +#define _PCM_READ_S32_BE(b8) INTPCM_T(*((int32_t *)(b8))) + +#define _PCM_WRITE_S16_LE(bb8, vval) do { \ + intpcm_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[0] = val; \ + b8[1] = val >> 8; \ +} while(0) +#define _PCM_WRITE_S32_LE(bb8, vval) do { \ + intpcm_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[0] = val; \ + b8[1] = val >> 8; \ + b8[2] = val >> 16; \ + b8[3] = val >> 24; \ +} while(0) +#define _PCM_WRITE_S16_BE(b8, val) do { \ + *((int16_t *)(b8)) = (val); \ +} while(0) +#define _PCM_WRITE_S32_BE(b8, val) do { \ + *((int32_t *)(b8)) = (val); \ +} while(0) + +#define _PCM_READ_U16_LE(b8) \ + INTPCM_T((b8)[0] | (((int8_t)((b8)[1] ^ 0x80)) << 8)) +#define _PCM_READ_U32_LE(b8) \ + INTPCM_T((b8)[0] | ((b8)[1] << 8) | ((b8)[2] << 16) | \ + (((int8_t)((b8)[3] ^ 0x80)) << 24)) +#define _PCM_READ_U16_BE(b8) \ + INTPCM_T((int16_t)(*((uint16_t *)(b8)) ^ 0x8000)) +#define _PCM_READ_U32_BE(b8) \ + INTPCM_T((int32_t)(*((uint32_t *)(b8)) ^ 0x80000000)) + +#define _PCM_WRITE_U16_LE(bb8, vval) do { \ + intpcm_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[0] = val; \ + b8[1] = (val >> 8) ^ 0x80; \ +} while(0) +#define _PCM_WRITE_U32_LE(bb8, vval) do { \ + intpcm_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[0] = val; \ + b8[1] = val >> 8; \ + b8[2] = val >> 16; \ + b8[3] = (val >> 24) ^ 0x80; \ +} while(0) +#define _PCM_WRITE_U16_BE(b8, val) do { \ + *((uint16_t *)(b8)) = (val) ^ 0x8000; \ +} while(0) +#define _PCM_WRITE_U32_BE(b8, val) do { \ + *((uint32_t *)(b8)) = (val) ^ 0x80000000; \ +} while(0) +#endif + +#define _PCM_READ_S24_LE(b8) \ + INTPCM_T((b8)[0] | ((b8)[1] << 8) | (((int8_t)((b8)[2])) << 16)) +#define _PCM_READ_S24_BE(b8) \ + INTPCM_T((b8)[2] | ((b8)[1] << 8) | (((int8_t)((b8)[0])) << 16)) + +#define _PCM_WRITE_S24_LE(bb8, vval) do { \ + intpcm_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[0] = val; \ + b8[1] = val >> 8; \ + b8[2] = val >> 16; \ +} while(0) +#define _PCM_WRITE_S24_BE(bb8, vval) do { \ + intpcm_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[2] = val; \ + b8[1] = val >> 8; \ + b8[0] = val >> 16; \ +} while(0) + +#define _PCM_READ_U24_LE(b8) \ + INTPCM_T((b8)[0] | ((b8)[1] << 8) | \ + (((int8_t)((b8)[2] ^ 0x80)) << 16)) +#define _PCM_READ_U24_BE(b8) \ + INTPCM_T((b8)[2] | ((b8)[1] << 8) | \ + (((int8_t)((b8)[0] ^ 0x80)) << 16)) + +#define _PCM_WRITE_U24_LE(bb8, vval) do { \ + intpcm_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[0] = val; \ + b8[1] = val >> 8; \ + b8[2] = (val >> 16) ^ 0x80; \ +} while(0) +#define _PCM_WRITE_U24_BE(bb8, vval) do { \ + intpcm_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[2] = val; \ + b8[1] = val >> 8; \ + b8[0] = (val >> 16) ^ 0x80; \ +} while(0) + +/* + * 8bit sample is pretty much useless since it doesn't provide + * sufficient dynamic range throughout our filtering process. + * For the sake of completeness, declare it anyway. + */ +#define _PCM_READ_S8_NE(b8) INTPCM_T(*((int8_t *)(b8))) +#define _PCM_READ_U8_NE(b8) \ + INTPCM_T((int8_t)(*((uint8_t *)(b8)) ^ 0x80)) + +#define _PCM_WRITE_S8_NE(b8, val) do { \ + *((int8_t *)(b8)) = (val); \ +} while(0) +#define _PCM_WRITE_U8_NE(b8, val) do { \ + *((uint8_t *)(b8)) = (val) ^ 0x80; \ +} while(0) + +/* + * Common macross. Use this instead of "_", unless we want + * the real sample value. + */ + +/* 8bit */ +#define PCM_READ_S8_NE(b8) _PCM_READ_S8_NE(b8) +#define PCM_READ_U8_NE(b8) _PCM_READ_U8_NE(b8) +#define PCM_WRITE_S8_NE(b8, val) _PCM_WRITE_S8_NE(b8, val) +#define PCM_WRITE_U8_NE(b8, val) _PCM_WRITE_U8_NE(b8, val) + +/* 16bit */ +#define PCM_READ_S16_LE(b8) _PCM_READ_S16_LE(b8) +#define PCM_READ_S16_BE(b8) _PCM_READ_S16_BE(b8) +#define PCM_READ_U16_LE(b8) _PCM_READ_U16_LE(b8) +#define PCM_READ_U16_BE(b8) _PCM_READ_U16_BE(b8) + +#define PCM_WRITE_S16_LE(b8, val) _PCM_WRITE_S16_LE(b8, val) +#define PCM_WRITE_S16_BE(b8, val) _PCM_WRITE_S16_BE(b8, val) +#define PCM_WRITE_U16_LE(b8, val) _PCM_WRITE_U16_LE(b8, val) +#define PCM_WRITE_U16_BE(b8, val) _PCM_WRITE_U16_BE(b8, val) + +/* 24bit */ +#define PCM_READ_S24_LE(b8) _PCM_READ_S24_LE(b8) +#define PCM_READ_S24_BE(b8) _PCM_READ_S24_BE(b8) +#define PCM_READ_U24_LE(b8) _PCM_READ_U24_LE(b8) +#define PCM_READ_U24_BE(b8) _PCM_READ_U24_BE(b8) + +#define PCM_WRITE_S24_LE(b8, val) _PCM_WRITE_S24_LE(b8, val) +#define PCM_WRITE_S24_BE(b8, val) _PCM_WRITE_S24_BE(b8, val) +#define PCM_WRITE_U24_LE(b8, val) _PCM_WRITE_U24_LE(b8, val) +#define PCM_WRITE_U24_BE(b8, val) _PCM_WRITE_U24_BE(b8, val) + +/* 32bit */ +#ifdef PCM_USE_64BIT_ARITH +#define PCM_READ_S32_LE(b8) _PCM_READ_S32_LE(b8) +#define PCM_READ_S32_BE(b8) _PCM_READ_S32_BE(b8) +#define PCM_READ_U32_LE(b8) _PCM_READ_U32_LE(b8) +#define PCM_READ_U32_BE(b8) _PCM_READ_U32_BE(b8) + +#define PCM_WRITE_S32_LE(b8, val) _PCM_WRITE_S32_LE(b8, val) +#define PCM_WRITE_S32_BE(b8, val) _PCM_WRITE_S32_BE(b8, val) +#define PCM_WRITE_U32_LE(b8, val) _PCM_WRITE_U32_LE(b8, val) +#define PCM_WRITE_U32_BE(b8, val) _PCM_WRITE_U32_BE(b8, val) +#else /* !PCM_USE_64BIT_ARITH */ +/* + * 24bit integer ?!? This is quite unfortunate, eh? Get the fact straight: + * Dynamic range for: + * 1) Human =~ 140db + * 2) 16bit = 96db (close enough) + * 3) 24bit = 144db (perfect) + * 4) 32bit = 196db (way too much) + * 5) Bugs Bunny = Gazillion!@%$Erbzzztt-EINVAL db + * Since we're not Bugs Bunny ..uh..err.. avoiding 64bit arithmetic, 24bit + * is pretty much sufficient for our signed integer processing. + */ +#define PCM_READ_S32_LE(b8) (_PCM_READ_S32_LE(b8) >> PCM_FXSHIFT) +#define PCM_READ_S32_BE(b8) (_PCM_READ_S32_BE(b8) >> PCM_FXSHIFT) +#define PCM_READ_U32_LE(b8) (_PCM_READ_U32_LE(b8) >> PCM_FXSHIFT) +#define PCM_READ_U32_BE(b8) (_PCM_READ_U32_BE(b8) >> PCM_FXSHIFT) + +#define PCM_WRITE_S32_LE(b8, val) \ + _PCM_WRITE_S32_LE(b8, (val) << PCM_FXSHIFT) +#define PCM_WRITE_S32_BE(b8, val) \ + _PCM_WRITE_S32_BE(b8, (val) << PCM_FXSHIFT) +#define PCM_WRITE_U32_LE(b8, val) \ + _PCM_WRITE_U32_LE(b8, (val) << PCM_FXSHIFT) +#define PCM_WRITE_U32_BE(b8, val) \ + _PCM_WRITE_U32_BE(b8, (val) << PCM_FXSHIFT) +#endif + +#define PCM_CLAMP_S8(val) \ + (((val) > PCM_S8_MAX) ? PCM_S8_MAX : \ + (((val) < PCM_S8_MIN) ? PCM_S8_MIN : (val))) +#define PCM_CLAMP_S16(val) \ + (((val) > PCM_S16_MAX) ? PCM_S16_MAX : \ + (((val) < PCM_S16_MIN) ? PCM_S16_MIN : (val))) +#define PCM_CLAMP_S24(val) \ + (((val) > PCM_S24_MAX) ? PCM_S24_MAX : \ + (((val) < PCM_S24_MIN) ? PCM_S24_MIN : (val))) + +#ifdef PCM_USE_64BIT_ARITH +#define PCM_CLAMP_S32(val) \ + (((val) > PCM_S32_MAX) ? PCM_S32_MAX : \ + (((val) < PCM_S32_MIN) ? PCM_S32_MIN : (val))) +#else +#define PCM_CLAMP_S32(val) \ + (((val) > PCM_S24_MAX) ? PCM_S32_MAX : \ + (((val) < PCM_S24_MIN) ? PCM_S32_MIN : \ + ((val) << PCM_FXSHIFT))) +#endif + +#define PCM_CLAMP_U8(val) PCM_CLAMP_S8(val) +#define PCM_CLAMP_U16(val) PCM_CLAMP_S16(val) +#define PCM_CLAMP_U24(val) PCM_CLAMP_S24(val) +#define PCM_CLAMP_U32(val) PCM_CLAMP_S32(val) + +#endif /* !_SND_PCM_H_ */ --- sys/dev/sound/pcm/sndstat.c.orig Mon Jul 9 02:23:30 2007 +++ sys/dev/sound/pcm/sndstat.c Thu Jul 12 12:04:19 2007 @@ -25,7 +25,7 @@ */ #include -#include +#include #include #ifdef USING_MUTEX #include @@ -354,7 +354,7 @@ int i, j; sbuf_printf(s, "FreeBSD Audio Driver (newpcm: %ubit %d/%s)\n", - (u_int)sizeof(intpcm_t) << 3, SND_DRV_VERSION, MACHINE_ARCH); + (u_int)sizeof(intpcm32_t) << 3, SND_DRV_VERSION, MACHINE_ARCH); if (SLIST_EMPTY(&sndstat_devlist)) { sbuf_printf(s, "No devices installed.\n"); sbuf_finish(s); --- sys/dev/sound/pcm/sndstat.h.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/pcm/sndstat.h Thu Jul 12 12:04:19 2007 @@ -0,0 +1,147 @@ +/*- + * Copyright (c) 2007 Ariff Abdullah + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _SND_SNDSTAT_H_ +#define _SND_SNDSTAT_H_ + +#define SNDSTAT_PREPARE_PCM_ARGS \ + struct sbuf *s, device_t dev, int verbose + +#define SNDSTAT_PREPARE_PCM_BEGIN() do { \ + struct snddev_info *d; \ + struct pcm_channel *c; \ + struct pcm_feeder *f; \ + \ + if (verbose < 1) \ + return (0); \ + \ + d = device_get_softc(dev); \ + PCM_BUSYASSERT(d); \ + \ + if (CHN_EMPTY(d, channels.pcm)) { \ + sbuf_printf(s, " (mixer only)"); \ + return (0); \ + } \ + \ + sbuf_printf(s, " (%dp:%dv/%dr:%dv channels %splex%s)", \ + d->playcount, d->pvchancount, d->reccount, d->rvchancount, \ + (d->flags & SD_F_SIMPLEX) ? "sim" : "du", \ + (device_get_unit(dev) == snd_unit) ? " default" : "") + + +#define SNDSTAT_PREPARE_PCM_END() \ + if (verbose <= 1) \ + return (0); \ + \ + CHN_FOREACH(c, d, channels.pcm) { \ + \ + KASSERT(c->bufhard != NULL && c->bufsoft != NULL, \ + ("hosed pcm channel setup")); \ + \ + sbuf_printf(s, "\n\t"); \ + \ + sbuf_printf(s, "%s[%s]: ", \ + (c->parentchannel != NULL) ? \ + c->parentchannel->name : "", c->name); \ + sbuf_printf(s, "spd %d", c->speed); \ + if (c->speed != sndbuf_getspd(c->bufhard)) \ + sbuf_printf(s, "/%d", \ + sndbuf_getspd(c->bufhard)); \ + sbuf_printf(s, ", fmt 0x%08x", c->format); \ + if (c->format != sndbuf_getfmt(c->bufhard)) \ + sbuf_printf(s, "/0x%08x", \ + sndbuf_getfmt(c->bufhard)); \ + sbuf_printf(s, ", flags 0x%08x, 0x%08x", \ + c->flags, c->feederflags); \ + if (c->pid != -1) \ + sbuf_printf(s, ", pid %d (%s)", \ + c->pid, c->comm); \ + sbuf_printf(s, "\n\t"); \ + \ + sbuf_printf(s, "interrupts %d, ", c->interrupts); \ + \ + if (c->direction == PCMDIR_REC) \ + sbuf_printf(s, \ + "overruns %d, feed %u, hfree %d, " \ + "sfree %d [b:%d/%d/%d|bs:%d/%d/%d]", \ + c->xruns, c->feedcount, \ + sndbuf_getfree(c->bufhard), \ + sndbuf_getfree(c->bufsoft), \ + sndbuf_getsize(c->bufhard), \ + sndbuf_getblksz(c->bufhard), \ + sndbuf_getblkcnt(c->bufhard), \ + sndbuf_getsize(c->bufsoft), \ + sndbuf_getblksz(c->bufsoft), \ + sndbuf_getblkcnt(c->bufsoft)); \ + else \ + sbuf_printf(s, \ + "underruns %d, feed %u, ready %d " \ + "[b:%d/%d/%d|bs:%d/%d/%d]", \ + c->xruns, c->feedcount, \ + sndbuf_getready(c->bufsoft), \ + sndbuf_getsize(c->bufhard), \ + sndbuf_getblksz(c->bufhard), \ + sndbuf_getblkcnt(c->bufhard), \ + sndbuf_getsize(c->bufsoft), \ + sndbuf_getblksz(c->bufsoft), \ + sndbuf_getblkcnt(c->bufsoft)); \ + sbuf_printf(s, "\n\t"); \ + \ + sbuf_printf(s, "{%s}", \ + (c->direction == PCMDIR_REC) ? "hardware" : \ + "userland"); \ + sbuf_printf(s, " -> "); \ + f = c->feeder; \ + while (f->source != NULL) \ + f = f->source; \ + while (f != NULL) { \ + sbuf_printf(s, "%s", f->class->name); \ + if (f->desc->type == FEEDER_FMT) \ + sbuf_printf(s, "(0x%08x -> 0x%08x)", \ + f->desc->in, f->desc->out); \ + if (f->desc->type == FEEDER_RATE) \ + sbuf_printf(s, "(%d -> %d)", \ + FEEDER_GET(f, FEEDRATE_SRC), \ + FEEDER_GET(f, FEEDRATE_DST)); \ + if (f->desc->type == FEEDER_ROOT || \ + f->desc->type == FEEDER_MIXER || \ + f->desc->type == FEEDER_VOLUME) \ + sbuf_printf(s, "(0x%08x)", \ + f->desc->out); \ + sbuf_printf(s, " -> "); \ + f = f->parent; \ + } \ + sbuf_printf(s, "{%s}", \ + (c->direction == PCMDIR_REC) ? "userland" : \ + "hardware"); \ + } \ + \ + return (0); \ +} while(0) + +#endif /* !_SND_SNDSTAT_H_ */ --- sys/dev/sound/pcm/sound.c.orig Mon Jul 9 02:23:30 2007 +++ sys/dev/sound/pcm/sound.c Thu Jul 12 12:04:19 2007 @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -66,14 +67,19 @@ static const char snd_driver_version[] = __XSTRING(SND_DRV_VERSION)"/"MACHINE_ARCH; SYSCTL_STRING(_hw_snd, OID_AUTO, version, CTLFLAG_RD, &snd_driver_version, - 0, "Driver version/arch"); + 0, "driver version/arch"); /** * @brief Unit number allocator for syncgroup IDs */ struct unrhdr *pcmsg_unrhdr = NULL; -static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose); +static int +sndstat_prepare_pcm(SNDSTAT_PREPARE_PCM_ARGS) +{ + SNDSTAT_PREPARE_PCM_BEGIN(); + SNDSTAT_PREPARE_PCM_END(); +} void * snd_mtxcreate(const char *desc, const char *type) @@ -228,6 +234,7 @@ CHN_LOCK(c); if (c->direction == direction && ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 && + c->refcount < 1 && !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) { ch = c; break; @@ -267,11 +274,12 @@ } CHN_FOREACH_SAFE(ch, c, nch, children) { CHN_LOCK(ch); - if (!(ch->flags & CHN_F_BUSY)) { + if (vcnt == 1 && c->refcount > 0) { CHN_UNLOCK(ch); - CHN_UNLOCK(c); + break; + } if (!(ch->flags & CHN_F_BUSY) && + ch->refcount < 1) { err = vchan_destroy(ch); - CHN_LOCK(c); if (err == 0) vcnt--; } else @@ -291,10 +299,10 @@ /* return error status and a locked channel */ int pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, - pid_t pid, int devunit) + pid_t pid, char *comm, int devunit) { struct pcm_channel *c; - int err, vchancount; + int err, vchancount, vchan_num; KASSERT(d != NULL && ch != NULL && (devunit == -1 || !(devunit & ~(SND_U_MASK | SND_D_MASK | SND_C_MASK))) && @@ -324,15 +332,31 @@ } } + *ch = NULL; + vchan_num = 0; + vchancount = (direction == PCMDIR_PLAY) ? d->pvchancount : + d->rvchancount; + retry_chnalloc: err = EOPNOTSUPP; /* scan for a free channel */ CHN_FOREACH(c, d, channels.pcm) { CHN_LOCK(c); + if (devunit == -1 && c->direction == direction && + (c->flags & CHN_F_VIRTUAL)) { + if (vchancount < snd_maxautovchans && + vchan_num < CHN_CHAN(c)) { + CHN_UNLOCK(c); + goto vchan_alloc; + } + vchan_num++; + } if (c->direction == direction && !(c->flags & CHN_F_BUSY) && (devunit == -1 || devunit == -2 || c->unit == devunit)) { c->flags |= CHN_F_BUSY; c->pid = pid; + strlcpy(c->comm, (comm != NULL) ? comm : + CHN_COMM_UNKNOWN, sizeof(c->comm)); *ch = c; return (0); } else if (c->unit == devunit) { @@ -353,13 +377,10 @@ if (devunit == -2) return (err); +vchan_alloc: /* no channel available */ if (devunit == -1 || snd_unit2d(devunit) == SND_DEV_DSPHW_VPLAY || snd_unit2d(devunit) == SND_DEV_DSPHW_VREC) { - if (direction == PCMDIR_PLAY) - vchancount = d->pvchancount; - else - vchancount = d->rvchancount; if (!(vchancount > 0 && vchancount < snd_maxautovchans) && (devunit == -1 || snd_unit2c(devunit) < snd_maxautovchans)) return (err); @@ -384,6 +405,7 @@ c->flags &= ~CHN_F_BUSY; c->pid = -1; + strlcpy(c->comm, CHN_COMM_UNUSED, sizeof(c->comm)); CHN_UNLOCK(c); return (0); @@ -575,6 +597,7 @@ ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO); ch->unit = udc; ch->pid = -1; + strlcpy(ch->comm, CHN_COMM_UNUSED, sizeof(ch->comm)); ch->parentsnddev = d; ch->parentchannel = parent; ch->dev = d->dev; @@ -620,46 +643,12 @@ int pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) { - struct pcm_channel *tmp, *after; - int num; - PCM_BUSYASSERT(d); snd_mtxassert(d->lock); KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY || ch->direction == PCMDIR_REC), ("Invalid pcm channel")); - after = NULL; - tmp = NULL; - num = 0; - - /* - * Look for possible device collision. - */ - CHN_FOREACH(tmp, d, channels.pcm) { - if (tmp->unit == ch->unit) { - device_printf(d->dev, "%s(): Device collision " - "old=%p new=%p devunit=0x%08x\n", - __func__, tmp, ch, ch->unit); - return (ENODEV); - } - if (CHN_DEV(tmp) < CHN_DEV(ch)) { - if (num == 0) - after = tmp; - continue; - } else if (CHN_DEV(tmp) > CHN_DEV(ch)) - break; - num++; - if (CHN_CHAN(tmp) < CHN_CHAN(ch)) - after = tmp; - else if (CHN_CHAN(tmp) > CHN_CHAN(ch)) - break; - } - - if (after != NULL) { - CHN_INSERT_AFTER(after, ch, channels.pcm); - } else { - CHN_INSERT_HEAD(d, ch, channels.pcm); - } + CHN_INSERT_SORT_ASCEND(d, ch, channels.pcm); switch (CHN_DEV(ch)) { case SND_DEV_DSPHW_PLAY: @@ -981,6 +970,7 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec) { struct snddev_info *d; + int i; if (pcm_veto_load) { device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); @@ -1010,6 +1000,10 @@ */ d->flags = 0; #endif + i = 0; + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "vpc_disabled", &i) != 0 || i == 0) + d->flags |= SD_F_VPC; d->devinfo = devinfo; d->devcount = 0; d->reccount = 0; @@ -1041,6 +1035,7 @@ CHN_INIT(d, channels.pcm); CHN_INIT(d, channels.pcm.busy); + CHN_INIT(d, channels.pcm.opened); /* XXX This is incorrect, but lets play along for now. */ if ((numplay == 0 || numrec == 0) && numplay != numrec) @@ -1224,103 +1219,6 @@ } return (0); -} - -/************************************************************************/ - -static int -sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) -{ - struct snddev_info *d; - struct pcm_channel *c; - struct pcm_feeder *f; - - if (verbose < 1) - return 0; - - d = device_get_softc(dev); - if (!d) - return ENXIO; - - PCM_BUSYASSERT(d); - - if (CHN_EMPTY(d, channels.pcm)) { - sbuf_printf(s, " (mixer only)"); - return 0; - } - - sbuf_printf(s, " (%dp:%dv/%dr:%dv channels%s%s)", - d->playcount, d->pvchancount, - d->reccount, d->rvchancount, - (d->flags & SD_F_SIMPLEX)? "" : " duplex", -#ifdef USING_DEVFS - (device_get_unit(dev) == snd_unit)? " default" : "" -#else - "" -#endif - ); - - if (verbose <= 1) - return 0; - - CHN_FOREACH(c, d, channels.pcm) { - - KASSERT(c->bufhard != NULL && c->bufsoft != NULL, - ("hosed pcm channel setup")); - - sbuf_printf(s, "\n\t"); - - /* it would be better to indent child channels */ - sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); - sbuf_printf(s, "spd %d", c->speed); - if (c->speed != sndbuf_getspd(c->bufhard)) - sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); - sbuf_printf(s, ", fmt 0x%08x", c->format); - if (c->format != sndbuf_getfmt(c->bufhard)) - sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); - sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags); - if (c->pid != -1) - sbuf_printf(s, ", pid %d", c->pid); - sbuf_printf(s, "\n\t"); - - sbuf_printf(s, "interrupts %d, ", c->interrupts); - if (c->direction == PCMDIR_REC) - sbuf_printf(s, "overruns %d, feed %u, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]", - c->xruns, c->feedcount, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft), - sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), - sndbuf_getblkcnt(c->bufhard), - sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), - sndbuf_getblkcnt(c->bufsoft)); - else - sbuf_printf(s, "underruns %d, feed %u, ready %d [b:%d/%d/%d|bs:%d/%d/%d]", - c->xruns, c->feedcount, sndbuf_getready(c->bufsoft), - sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), - sndbuf_getblkcnt(c->bufhard), - sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), - sndbuf_getblkcnt(c->bufsoft)); - sbuf_printf(s, "\n\t"); - - sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); - sbuf_printf(s, " -> "); - f = c->feeder; - while (f->source != NULL) - f = f->source; - while (f != NULL) { - sbuf_printf(s, "%s", f->class->name); - if (f->desc->type == FEEDER_FMT) - sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); - if (f->desc->type == FEEDER_RATE) - sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); - if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER || - f->desc->type == FEEDER_VOLUME) - sbuf_printf(s, "(0x%08x)", f->desc->out); - sbuf_printf(s, " -> "); - f = f->parent; - } - sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); - } - - return 0; } /************************************************************************/ --- sys/dev/sound/pcm/sound.h.orig Mon Jul 9 02:23:30 2007 +++ sys/dev/sound/pcm/sound.h Thu Jul 12 12:04:19 2007 @@ -141,6 +141,7 @@ #define SD_F_BUSY 0x00000080 #define SD_F_MPSAFE 0x00000100 #define SD_F_REGISTERED 0x00000200 +#define SD_F_VPC 0x00000400 /* volume-per-channel */ #define SD_F_PRIO_RD 0x10000000 #define SD_F_PRIO_WR 0x20000000 @@ -158,281 +159,6 @@ (((var)<(low))? (low) : ((var)>(high))? (high) : (var)) #define DSP_BUFFSIZE (8192) -/* - * Macros for reading/writing PCM sample / int values from bytes array. - * Since every process is done using signed integer (and to make our life - * less miserable), unsigned sample will be converted to its signed - * counterpart and restored during writing back. To avoid overflow, - * we truncate 32bit (and only 32bit) samples down to 24bit (see below - * for the reason), unless PCM_USE_64BIT_ARITH is defined. - */ - -/* - * Automatically turn on 64bit arithmetic on suitable archs - * (amd64 64bit, ia64, etc..) for wider 32bit samples / integer processing. - */ -#if LONG_BIT >= 64 -#undef PCM_USE_64BIT_ARITH -#define PCM_USE_64BIT_ARITH 1 -#else -#if 0 -#undef PCM_USE_64BIT_ARITH -#define PCM_USE_64BIT_ARITH 1 -#endif -#endif - -#ifdef PCM_USE_64BIT_ARITH -typedef int64_t intpcm_t; -#else -typedef int32_t intpcm_t; -#endif - -/* 32bit fixed point shift */ -#define PCM_FXSHIFT 8 - -#define PCM_S8_MAX 0x7f -#define PCM_S8_MIN -0x80 -#define PCM_S16_MAX 0x7fff -#define PCM_S16_MIN -0x8000 -#define PCM_S24_MAX 0x7fffff -#define PCM_S24_MIN -0x800000 -#ifdef PCM_USE_64BIT_ARITH -#if LONG_BIT >= 64 -#define PCM_S32_MAX 0x7fffffffL -#define PCM_S32_MIN -0x80000000L -#else -#define PCM_S32_MAX 0x7fffffffLL -#define PCM_S32_MIN -0x80000000LL -#endif -#else -#define PCM_S32_MAX 0x7fffffff -#define PCM_S32_MIN (-0x7fffffff - 1) -#endif - -/* Bytes-per-sample definition */ -#define PCM_8_BPS 1 -#define PCM_16_BPS 2 -#define PCM_24_BPS 3 -#define PCM_32_BPS 4 - -#if BYTE_ORDER == LITTLE_ENDIAN -#define PCM_READ_S16_LE(b8) *((int16_t *)(b8)) -#define _PCM_READ_S32_LE(b8) *((int32_t *)(b8)) -#define PCM_READ_S16_BE(b8) \ - ((int32_t)((b8)[1] | ((int8_t)((b8)[0])) << 8)) -#define _PCM_READ_S32_BE(b8) \ - ((int32_t)((b8)[3] | (b8)[2] << 8 | (b8)[1] << 16 | \ - ((int8_t)((b8)[0])) << 24)) - -#define PCM_WRITE_S16_LE(b8, val) *((int16_t *)(b8)) = (val) -#define _PCM_WRITE_S32_LE(b8, val) *((int32_t *)(b8)) = (val) -#define PCM_WRITE_S16_BE(bb8, vval) do { \ - int32_t val = (vval); \ - uint8_t *b8 = (bb8); \ - b8[1] = val; \ - b8[0] = val >> 8; \ - } while(0) -#define _PCM_WRITE_S32_BE(bb8, vval) do { \ - int32_t val = (vval); \ - uint8_t *b8 = (bb8); \ - b8[3] = val; \ - b8[2] = val >> 8; \ - b8[1] = val >> 16; \ - b8[0] = val >> 24; \ - } while(0) - -#define PCM_READ_U16_LE(b8) ((int16_t)(*((uint16_t *)(b8)) ^ 0x8000)) -#define _PCM_READ_U32_LE(b8) ((int32_t)(*((uint32_t *)(b8)) ^ 0x80000000)) -#define PCM_READ_U16_BE(b8) \ - ((int32_t)((b8)[1] | ((int8_t)((b8)[0] ^ 0x80)) << 8)) -#define _PCM_READ_U32_BE(b8) \ - ((int32_t)((b8)[3] | (b8)[2] << 8 | (b8)[1] << 16 | \ - ((int8_t)((b8)[0] ^ 0x80)) << 24)) - -#define PCM_WRITE_U16_LE(b8, val) *((uint16_t *)(b8)) = (val) ^ 0x8000 -#define _PCM_WRITE_U32_LE(b8, val) *((uint32_t *)(b8)) = (val) ^ 0x80000000 -#define PCM_WRITE_U16_BE(bb8, vval) do { \ - int32_t val = (vval); \ - uint8_t *b8 = (bb8); \ - b8[1] = val; \ - b8[0] = (val >> 8) ^ 0x80; \ - } while(0) -#define _PCM_WRITE_U32_BE(bb8, vval) do { \ - int32_t val = (vval); \ - uint8_t *b8 = (bb8); \ - b8[3] = val; \ - b8[2] = val >> 8; \ - b8[1] = val >> 16; \ - b8[0] = (val >> 24) ^ 0x80; \ - } while(0) -#else /* !LITTLE_ENDIAN */ -#define PCM_READ_S16_LE(b8) \ - ((int32_t)((b8)[0] | ((int8_t)((b8)[1])) << 8)) -#define _PCM_READ_S32_LE(b8) \ - ((int32_t)((b8)[0] | (b8)[1] << 8 | (b8)[2] << 16 | \ - ((int8_t)((b8)[3])) << 24)) -#define PCM_READ_S16_BE(b8) *((int16_t *)(b8)) -#define _PCM_READ_S32_BE(b8) *((int32_t *)(b8)) - -#define PCM_WRITE_S16_LE(bb8, vval) do { \ - int32_t val = (vval); \ - uint8_t *b8 = (bb8); \ - b8[0] = val; \ - b8[1] = val >> 8; \ - } while(0) -#define _PCM_WRITE_S32_LE(bb8, vval) do { \ - int32_t val = (vval); \ - uint8_t *b8 = (bb8); \ - b8[0] = val; \ - b8[1] = val >> 8; \ - b8[2] = val >> 16; \ - b8[3] = val >> 24; \ - } while(0) -#define PCM_WRITE_S16_BE(b8, val) *((int16_t *)(b8)) = (val) -#define _PCM_WRITE_S32_BE(b8, val) *((int32_t *)(b8)) = (val) - -#define PCM_READ_U16_LE(b8) \ - ((int32_t)((b8)[0] | ((int8_t)((b8)[1] ^ 0x80)) << 8)) -#define _PCM_READ_U32_LE(b8) \ - ((int32_t)((b8)[0] | (b8)[1] << 8 | (b8)[2] << 16 | \ - ((int8_t)((b8)[3] ^ 0x80)) << 24)) -#define PCM_READ_U16_BE(b8) ((int16_t)(*((uint16_t *)(b8)) ^ 0x8000)) -#define _PCM_READ_U32_BE(b8) ((int32_t)(*((uint32_t *)(b8)) ^ 0x80000000)) - -#define PCM_WRITE_U16_LE(bb8, vval) do { \ - int32_t val = (vval); \ - uint8_t *b8 = (bb8); \ - b8[0] = val; \ - b8[1] = (val >> 8) ^ 0x80; \ - } while(0) -#define _PCM_WRITE_U32_LE(bb8, vval) do { \ - int32_t val = (vval); \ - uint8_t *b8 = (bb8); \ - b8[0] = val; \ - b8[1] = val >> 8; \ - b8[2] = val >> 16; \ - b8[3] = (val >> 24) ^ 0x80; \ - } while(0) -#define PCM_WRITE_U16_BE(b8, val) *((uint16_t *)(b8)) = (val) ^ 0x8000 -#define _PCM_WRITE_U32_BE(b8, val) *((uint32_t *)(b8)) = (val) ^ 0x80000000 -#endif - -#define PCM_READ_S24_LE(b8) \ - ((int32_t)((b8)[0] | (b8)[1] << 8 | ((int8_t)((b8)[2])) << 16)) -#define PCM_READ_S24_BE(b8) \ - ((int32_t)((b8)[2] | (b8)[1] << 8 | ((int8_t)((b8)[0])) << 16)) - -#define PCM_WRITE_S24_LE(bb8, vval) do { \ - int32_t val = (vval); \ - uint8_t *b8 = (bb8); \ - b8[0] = val; \ - b8[1] = val >> 8; \ - b8[2] = val >> 16; \ - } while(0) -#define PCM_WRITE_S24_BE(bb8, vval) do { \ - int32_t val = (vval); \ - uint8_t *b8 = (bb8); \ - b8[2] = val; \ - b8[1] = val >> 8; \ - b8[0] = val >> 16; \ - } while(0) - -#define PCM_READ_U24_LE(b8) \ - ((int32_t)((b8)[0] | (b8)[1] << 8 | \ - ((int8_t)((b8)[2] ^ 0x80)) << 16)) -#define PCM_READ_U24_BE(b8) \ - ((int32_t)((b8)[2] | (b8)[1] << 8 | \ - ((int8_t)((b8)[0] ^ 0x80)) << 16)) - -#define PCM_WRITE_U24_LE(bb8, vval) do { \ - int32_t val = (vval); \ - uint8_t *b8 = (bb8); \ - b8[0] = val; \ - b8[1] = val >> 8; \ - b8[2] = (val >> 16) ^ 0x80; \ - } while(0) -#define PCM_WRITE_U24_BE(bb8, vval) do { \ - int32_t val = (vval); \ - uint8_t *b8 = (bb8); \ - b8[2] = val; \ - b8[1] = val >> 8; \ - b8[0] = (val >> 16) ^ 0x80; \ - } while(0) - -#ifdef PCM_USE_64BIT_ARITH -#define PCM_READ_S32_LE(b8) _PCM_READ_S32_LE(b8) -#define PCM_READ_S32_BE(b8) _PCM_READ_S32_BE(b8) -#define PCM_WRITE_S32_LE(b8, val) _PCM_WRITE_S32_LE(b8, val) -#define PCM_WRITE_S32_BE(b8, val) _PCM_WRITE_S32_BE(b8, val) - -#define PCM_READ_U32_LE(b8) _PCM_READ_U32_LE(b8) -#define PCM_READ_U32_BE(b8) _PCM_READ_U32_BE(b8) -#define PCM_WRITE_U32_LE(b8, val) _PCM_WRITE_U32_LE(b8, val) -#define PCM_WRITE_U32_BE(b8, val) _PCM_WRITE_U32_BE(b8, val) -#else /* !PCM_USE_64BIT_ARITH */ -/* - * 24bit integer ?!? This is quite unfortunate, eh? Get the fact straight: - * Dynamic range for: - * 1) Human =~ 140db - * 2) 16bit = 96db (close enough) - * 3) 24bit = 144db (perfect) - * 4) 32bit = 196db (way too much) - * 5) Bugs Bunny = Gazillion!@%$Erbzzztt-EINVAL db - * Since we're not Bugs Bunny ..uh..err.. avoiding 64bit arithmetic, 24bit - * is pretty much sufficient for our signed integer processing. - */ -#define PCM_READ_S32_LE(b8) (_PCM_READ_S32_LE(b8) >> PCM_FXSHIFT) -#define PCM_READ_S32_BE(b8) (_PCM_READ_S32_BE(b8) >> PCM_FXSHIFT) -#define PCM_WRITE_S32_LE(b8, val) _PCM_WRITE_S32_LE(b8, (val) << PCM_FXSHIFT) -#define PCM_WRITE_S32_BE(b8, val) _PCM_WRITE_S32_BE(b8, (val) << PCM_FXSHIFT) - -#define PCM_READ_U32_LE(b8) (_PCM_READ_U32_LE(b8) >> PCM_FXSHIFT) -#define PCM_READ_U32_BE(b8) (_PCM_READ_U32_BE(b8) >> PCM_FXSHIFT) -#define PCM_WRITE_U32_LE(b8, val) _PCM_WRITE_U32_LE(b8, (val) << PCM_FXSHIFT) -#define PCM_WRITE_U32_BE(b8, val) _PCM_WRITE_U32_BE(b8, (val) << PCM_FXSHIFT) -#endif - -/* - * 8bit sample is pretty much useless since it doesn't provide - * sufficient dynamic range throughout our filtering process. - * For the sake of completeness, declare it anyway. - */ -#define PCM_READ_S8(b8) *((int8_t *)(b8)) -#define PCM_READ_S8_NE(b8) PCM_READ_S8(b8) -#define PCM_READ_U8(b8) ((int8_t)(*((uint8_t *)(b8)) ^ 0x80)) -#define PCM_READ_U8_NE(b8) PCM_READ_U8(b8) - -#define PCM_WRITE_S8(b8, val) *((int8_t *)(b8)) = (val) -#define PCM_WRITE_S8_NE(b8, val) PCM_WRITE_S8(b8, val) -#define PCM_WRITE_U8(b8, val) *((uint8_t *)(b8)) = (val) ^ 0x80 -#define PCM_WRITE_U8_NE(b8, val) PCM_WRITE_U8(b8, val) - -#define PCM_CLAMP_S8(val) \ - (((val) > PCM_S8_MAX) ? PCM_S8_MAX : \ - (((val) < PCM_S8_MIN) ? PCM_S8_MIN : (val))) -#define PCM_CLAMP_S16(val) \ - (((val) > PCM_S16_MAX) ? PCM_S16_MAX : \ - (((val) < PCM_S16_MIN) ? PCM_S16_MIN : (val))) -#define PCM_CLAMP_S24(val) \ - (((val) > PCM_S24_MAX) ? PCM_S24_MAX : \ - (((val) < PCM_S24_MIN) ? PCM_S24_MIN : (val))) - -#ifdef PCM_USE_64BIT_ARITH -#define PCM_CLAMP_S32(val) \ - (((val) > PCM_S32_MAX) ? PCM_S32_MAX : \ - (((val) < PCM_S32_MIN) ? PCM_S32_MIN : (val))) -#else -#define PCM_CLAMP_S32(val) \ - (((val) > PCM_S24_MAX) ? PCM_S32_MAX : \ - (((val) < PCM_S24_MIN) ? PCM_S32_MIN : \ - ((val) << PCM_FXSHIFT))) -#endif - -#define PCM_CLAMP_U8(val) PCM_CLAMP_S8(val) -#define PCM_CLAMP_U16(val) PCM_CLAMP_S16(val) -#define PCM_CLAMP_U24(val) PCM_CLAMP_S24(val) -#define PCM_CLAMP_U32(val) PCM_CLAMP_S32(val) - /* make figuring out what a format is easier. got AFMT_STEREO already */ #define AFMT_32BIT (AFMT_S32_LE | AFMT_S32_BE | AFMT_U32_LE | AFMT_U32_BE) #define AFMT_24BIT (AFMT_S24_LE | AFMT_S24_BE | AFMT_U24_LE | AFMT_U24_BE) @@ -475,9 +201,18 @@ #define SND_DEV_DSPHW_CD 15 /* s16le/stereo 44100Hz CD */ -#define SND_DEV_DSP_MMAP 16 /* OSSv4 compatible /dev/dsp_mmap */ +/* + * OSSv4 compatible device. For now, it serve no purpose and + * the cloning itself will forward the request to ordinary /dev/dsp + * instead. + */ +#define SND_DEV_DSP_MMAP 16 /* /dev/dsp_mmap */ +#define SND_DEV_DSP_AC3 17 /* /dev/dsp_ac3 */ +#define SND_DEV_DSP_MULTICH 18 /* /dev/dsp_multich */ +#define SND_DEV_DSP_SPDIFOUT 19 /* /dev/dsp_spdifout */ +#define SND_DEV_DSP_SPDIFIN 20 /* /dev/dsp_spdifin */ -#define SND_DEV_LAST SND_DEV_DSP_MMAP +#define SND_DEV_LAST SND_DEV_DSP_SPDIFIN #define SND_DEV_MAX PCMMAXDEV #define DSP_DEFAULT_SPEED 8000 @@ -506,7 +241,8 @@ SYSCTL_DECL(_hw_snd); struct pcm_channel *pcm_getfakechan(struct snddev_info *d); -int pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, pid_t pid, int devunit); +int pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, + pid_t pid, char *comm, int devunit); int pcm_chnrelease(struct pcm_channel *c); int pcm_chnref(struct pcm_channel *c, int ref); int pcm_inprog(struct snddev_info *d, int delta); @@ -579,6 +315,9 @@ struct { SLIST_HEAD(, pcm_channel) head; } busy; + struct { + SLIST_HEAD(, pcm_channel) head; + } opened; } pcm; } channels; TAILQ_HEAD(dsp_cdevinfo_linkhead, dsp_cdevinfo) dsp_cdevinfo_pool; @@ -611,7 +350,7 @@ #endif /* - * For PCM_CV_[WAIT | ACQUIRE | RELEASE], be sure to surround these + * For PCM_[WAIT | ACQUIRE | RELEASE], be sure to surround these * with pcm_lock/unlock() sequence, or I'll come to gnaw upon you! */ #ifdef SND_DIAGNOSTIC --- sys/dev/sound/pcm/vchan.c.orig Mon Jul 9 02:23:30 2007 +++ sys/dev/sound/pcm/vchan.c Thu Jul 12 12:04:19 2007 @@ -28,6 +28,7 @@ /* Almost entirely rewritten to add multi-format/channels mixing support. */ #include +#include #include #include "feeder_if.h" @@ -71,32 +72,13 @@ #define vchan_valid_strformat(strfmt) \ afmtstr2afmt(vchan_supported_fmts, strfmt, AFMTSTR_STEREO_RETURN); -/* - * Need specialized WRITE macros since 32bit might involved saturation - * if calculation is done within 32bit arithmetic. - */ -#define VCHAN_PCM_WRITE_S8_NE(b8, val) PCM_WRITE_S8(b8, val) -#define VCHAN_PCM_WRITE_S16_LE(b8, val) PCM_WRITE_S16_LE(b8, val) -#define VCHAN_PCM_WRITE_S24_LE(b8, val) PCM_WRITE_S24_LE(b8, val) -#define VCHAN_PCM_WRITE_S32_LE(b8, val) _PCM_WRITE_S32_LE(b8, val) -#define VCHAN_PCM_WRITE_S16_BE(b8, val) PCM_WRITE_S16_BE(b8, val) -#define VCHAN_PCM_WRITE_S24_BE(b8, val) PCM_WRITE_S24_BE(b8, val) -#define VCHAN_PCM_WRITE_S32_BE(b8, val) _PCM_WRITE_S32_BE(b8, val) -#define VCHAN_PCM_WRITE_U8_NE(b8, val) PCM_WRITE_U8(b8, val) -#define VCHAN_PCM_WRITE_U16_LE(b8, val) PCM_WRITE_U16_LE(b8, val) -#define VCHAN_PCM_WRITE_U24_LE(b8, val) PCM_WRITE_U24_LE(b8, val) -#define VCHAN_PCM_WRITE_U32_LE(b8, val) _PCM_WRITE_U32_LE(b8, val) -#define VCHAN_PCM_WRITE_U16_BE(b8, val) PCM_WRITE_U16_BE(b8, val) -#define VCHAN_PCM_WRITE_U24_BE(b8, val) PCM_WRITE_U24_BE(b8, val) -#define VCHAN_PCM_WRITE_U32_BE(b8, val) _PCM_WRITE_U32_BE(b8, val) - -#define FEEDER_VCHAN_MIX(FMTBIT, VCHAN_INTCAST, SIGN, SIGNS, ENDIAN, ENDIANS) \ +#define FEEDER_VCHAN_MIX(FMTBIT, SIGN, SIGNS, ENDIAN, ENDIANS) \ static uint32_t \ -feed_vchan_mix_##SIGNS##FMTBIT##ENDIANS(uint8_t *to, uint8_t *tmp, \ - uint32_t count) \ +feed_vchan_mix_##SIGNS##FMTBIT##ENDIANS##e(uint8_t *to, uint8_t *tmp, \ + uint32_t count) \ { \ - int32_t x, y; \ - VCHAN_INTCAST z; \ + intpcm##FMTBIT##_t z; \ + intpcm_t x, y; \ int i; \ \ i = count; \ @@ -107,30 +89,30 @@ tmp -= PCM_##FMTBIT##_BPS; \ to -= PCM_##FMTBIT##_BPS; \ i -= PCM_##FMTBIT##_BPS; \ - x = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(tmp); \ - y = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(to); \ - z = (VCHAN_INTCAST)x + y; \ + x = PCM_READ_##SIGN##FMTBIT##_##ENDIAN##E(tmp); \ + y = PCM_READ_##SIGN##FMTBIT##_##ENDIAN##E(to); \ + z = INTPCM##FMTBIT##_T(x) + y; \ x = PCM_CLAMP_##SIGN##FMTBIT(z); \ - VCHAN_PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN(to, x); \ + _PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN##E(to, x); \ } while (i != 0); \ \ return (count); \ } -FEEDER_VCHAN_MIX(8, int32_t, S, s, NE, ne) -FEEDER_VCHAN_MIX(16, int32_t, S, s, LE, le) -FEEDER_VCHAN_MIX(24, int32_t, S, s, LE, le) -FEEDER_VCHAN_MIX(32, intpcm_t, S, s, LE, le) -FEEDER_VCHAN_MIX(16, int32_t, S, s, BE, be) -FEEDER_VCHAN_MIX(24, int32_t, S, s, BE, be) -FEEDER_VCHAN_MIX(32, intpcm_t, S, s, BE, be) -FEEDER_VCHAN_MIX(8, int32_t, U, u, NE, ne) -FEEDER_VCHAN_MIX(16, int32_t, U, u, LE, le) -FEEDER_VCHAN_MIX(24, int32_t, U, u, LE, le) -FEEDER_VCHAN_MIX(32, intpcm_t, U, u, LE, le) -FEEDER_VCHAN_MIX(16, int32_t, U, u, BE, be) -FEEDER_VCHAN_MIX(24, int32_t, U, u, BE, be) -FEEDER_VCHAN_MIX(32, intpcm_t, U, u, BE, be) +FEEDER_VCHAN_MIX(8, S, s, N, n) +FEEDER_VCHAN_MIX(16, S, s, L, l) +FEEDER_VCHAN_MIX(24, S, s, L, l) +FEEDER_VCHAN_MIX(32, S, s, L, l) +FEEDER_VCHAN_MIX(16, S, s, B, b) +FEEDER_VCHAN_MIX(24, S, s, B, b) +FEEDER_VCHAN_MIX(32, S, s, B, b) +FEEDER_VCHAN_MIX(8, U, u, N, n) +FEEDER_VCHAN_MIX(16, U, u, L, l) +FEEDER_VCHAN_MIX(24, U, u, L, l) +FEEDER_VCHAN_MIX(32, U, u, L, l) +FEEDER_VCHAN_MIX(16, U, u, B, b) +FEEDER_VCHAN_MIX(24, U, u, B, b) +FEEDER_VCHAN_MIX(32, U, u, B, b) struct feed_vchan_info { uint32_t format; @@ -222,7 +204,7 @@ CHN_FOREACH(ch, c, children.busy) { CHN_LOCK(ch); - if (!(ch->flags & CHN_F_TRIGGERED)) { + if (CHN_STOPPED(ch)) { CHN_UNLOCK(ch); continue; } @@ -304,7 +286,7 @@ CHN_FOREACH(ch, c, children.busy) { CHN_LOCK(ch); - if (!(ch->flags & CHN_F_TRIGGERED)) { + if (CHN_STOPPED(ch)) { CHN_UNLOCK(ch); continue; } @@ -449,15 +431,13 @@ switch (go) { case PCMTRIG_START: - if (otrigger != PCMTRIG_START) { + if (otrigger != PCMTRIG_START) CHN_INSERT_HEAD(p, c, children.busy); - } break; case PCMTRIG_STOP: case PCMTRIG_ABORT: - if (otrigger == PCMTRIG_START) { + if (otrigger == PCMTRIG_START) CHN_REMOVE(p, c, children.busy); - } break; default: break; @@ -512,7 +492,7 @@ */ #ifdef SND_DYNSYSCTL static int -sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS) +sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS) { struct snddev_info *d; struct pcm_channel *c, *ch = NULL; @@ -621,7 +601,7 @@ } static int -sysctl_hw_snd_vchanformat(SYSCTL_HANDLER_ARGS) +sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS) { struct snddev_info *d; struct pcm_channel *c, *ch = NULL; @@ -739,7 +719,7 @@ vchan_create(struct pcm_channel *parent, int num) { struct snddev_info *d = parent->parentsnddev; - struct pcm_channel *ch, *tmp, *after; + struct pcm_channel *ch; struct pcmchan_caps *parent_caps; uint32_t vchanfmt; int err, first, speed, r; @@ -781,20 +761,13 @@ } CHN_LOCK(parent); - /* add us to our parent channel's children */ + /* + * Add us to our parent channel's children in reverse order + * so future destruction will pick the last (biggest number) + * channel. + */ first = CHN_EMPTY(parent, children); - after = NULL; - CHN_FOREACH(tmp, parent, children) { - if (CHN_CHAN(tmp) > CHN_CHAN(ch)) - after = tmp; - else if (CHN_CHAN(tmp) < CHN_CHAN(ch)) - break; - } - if (after != NULL) { - CHN_INSERT_AFTER(after, ch, children); - } else { - CHN_INSERT_HEAD(parent, ch, children); - } + CHN_INSERT_SORT_DESCEND(parent, ch, children); parent->flags |= CHN_F_HAS_VCHAN; if (first) { @@ -941,22 +914,30 @@ int vchan_destroy(struct pcm_channel *c) { - struct pcm_channel *parent = c->parentchannel; - struct snddev_info *d = parent->parentsnddev; + struct pcm_channel *parent; + struct snddev_info *d; uint32_t spd; int err; + KASSERT(c != NULL && c->parentchannel != NULL && + c->parentsnddev != NULL, ("%s(): invalid channel=%p", + __func__, c)); + + CHN_LOCKASSERT(c); + + d = c->parentsnddev; + parent = c->parentchannel; + PCM_BUSYASSERT(d); + CHN_LOCKASSERT(parent); - CHN_LOCK(parent); - if (!(parent->flags & CHN_F_BUSY)) { - CHN_UNLOCK(parent); + CHN_UNLOCK(c); + + if (!(parent->flags & CHN_F_BUSY)) return (EBUSY); - } - if (CHN_EMPTY(parent, children)) { - CHN_UNLOCK(parent); + + if (CHN_EMPTY(parent, children)) return (EINVAL); - } /* remove us from our parent's children list */ CHN_REMOVE(parent, c, children); @@ -979,6 +960,8 @@ if (!err) err = pcm_chn_destroy(c); + CHN_LOCK(parent); + return (err); } @@ -1002,12 +985,12 @@ SYSCTL_CHILDREN(d->play_sysctl_tree), OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, - sysctl_hw_snd_vchanrate, "I", "virtual channel mixing speed/rate"); + sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate"); SYSCTL_ADD_PROC(&d->play_sysctl_ctx, SYSCTL_CHILDREN(d->play_sysctl_tree), OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW, VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, - sysctl_hw_snd_vchanformat, "A", "virtual channel format"); + sysctl_dev_pcm_vchanformat, "A", "virtual channel format"); /* Rec */ SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, SYSCTL_CHILDREN(d->rec_sysctl_tree), @@ -1018,12 +1001,12 @@ SYSCTL_CHILDREN(d->rec_sysctl_tree), OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, - sysctl_hw_snd_vchanrate, "I", "virtual channel base speed/rate"); + sysctl_dev_pcm_vchanrate, "I", "virtual channel base speed/rate"); SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, SYSCTL_CHILDREN(d->rec_sysctl_tree), OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW, VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, - sysctl_hw_snd_vchanformat, "A", "virtual channel format"); + sysctl_dev_pcm_vchanformat, "A", "virtual channel format"); #endif return (0); --- sys/dev/sound/usb/uaudio.c.orig Mon Jul 9 02:23:30 2007 +++ sys/dev/sound/usb/uaudio.c Thu Jul 12 12:04:19 2007 @@ -94,6 +94,7 @@ #include #elif defined(__FreeBSD__) #include /* XXXXX */ +#include #include #include "feeder_if.h" #endif @@ -467,7 +468,6 @@ #elif defined(__FreeBSD__) static int audio_attach_mi(device_t); static int uaudio_init_params(struct uaudio_softc * sc, struct chan *ch, int mode); -static int uaudio_sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose); /* for NetBSD compatibirity */ #define AUMODE_PLAY 0x01 @@ -4497,104 +4497,17 @@ } static int -uaudio_sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) +uaudio_sndstat_prepare_pcm(SNDSTAT_PREPARE_PCM_ARGS) { - struct snddev_info *d; - struct pcm_channel *c; - struct pcm_feeder *f; device_t pa_dev = device_get_parent(dev); struct uaudio_softc *sc = device_get_softc(pa_dev); - if (verbose < 1) - return 0; - - d = device_get_softc(dev); - if (!d) - return ENXIO; + SNDSTAT_PREPARE_PCM_BEGIN(); - PCM_BUSYASSERT(d); - - if (CHN_EMPTY(d, channels.pcm)) { - sbuf_printf(s, " (mixer only)"); - return 0; - } - - sbuf_printf(s, " (%dp:%dv/%dr:%dv channels%s%s)", - d->playcount, d->pvchancount, - d->reccount, d->rvchancount, - (d->flags & SD_F_SIMPLEX)? "" : " duplex", -#ifdef USING_DEVFS - (device_get_unit(dev) == snd_unit)? " default" : "" -#else - "" -#endif - ); - - if (sc->uaudio_sndstat_flag != 0) { + if (sc->uaudio_sndstat_flag != 0) sbuf_cat(s, sbuf_data(&(sc->uaudio_sndstat))); - } - - if (verbose <= 1) - return 0; - - CHN_FOREACH(c, d, channels.pcm) { - - KASSERT(c->bufhard != NULL && c->bufsoft != NULL, - ("hosed pcm channel setup")); - - sbuf_printf(s, "\n\t"); - /* it would be better to indent child channels */ - sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); - sbuf_printf(s, "spd %d", c->speed); - if (c->speed != sndbuf_getspd(c->bufhard)) - sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); - sbuf_printf(s, ", fmt 0x%08x", c->format); - if (c->format != sndbuf_getfmt(c->bufhard)) - sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); - sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags); - if (c->pid != -1) - sbuf_printf(s, ", pid %d", c->pid); - sbuf_printf(s, "\n\t"); - - sbuf_printf(s, "interrupts %d, ", c->interrupts); - if (c->direction == PCMDIR_REC) - sbuf_printf(s, "overruns %d, feed %u, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]", - c->xruns, c->feedcount, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft), - sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), - sndbuf_getblkcnt(c->bufhard), - sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), - sndbuf_getblkcnt(c->bufsoft)); - else - sbuf_printf(s, "underruns %d, feed %u, ready %d [b:%d/%d/%d|bs:%d/%d/%d]", - c->xruns, c->feedcount, sndbuf_getready(c->bufsoft), - sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), - sndbuf_getblkcnt(c->bufhard), - sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), - sndbuf_getblkcnt(c->bufsoft)); - sbuf_printf(s, "\n\t"); - - sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); - sbuf_printf(s, " -> "); - f = c->feeder; - while (f->source != NULL) - f = f->source; - while (f != NULL) { - sbuf_printf(s, "%s", f->class->name); - if (f->desc->type == FEEDER_FMT) - sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); - if (f->desc->type == FEEDER_RATE) - sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); - if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER || - f->desc->type == FEEDER_VOLUME) - sbuf_printf(s, "(0x%08x)", f->desc->out); - sbuf_printf(s, " -> "); - f = f->parent; - } - sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); - } - - return 0; + SNDSTAT_PREPARE_PCM_END(); } void --- sys/dev/sound/version.h.orig Mon Jul 9 02:23:30 2007 +++ sys/dev/sound/version.h Thu Jul 12 14:46:27 2007 @@ -37,6 +37,6 @@ * Last 2 decimal places reserved for daily versioning, starting * with 0. */ -#define SND_DRV_VERSION 2007061600 +#define SND_DRV_VERSION 2007071200 #endif /* !_SND_VERSION_H_ */