summaryrefslogtreecommitdiff
path: root/sys/dev/sound
diff options
context:
space:
mode:
authorAriff Abdullah <ariff@FreeBSD.org>2006-04-04 17:43:49 +0000
committerAriff Abdullah <ariff@FreeBSD.org>2006-04-04 17:43:49 +0000
commitfa4c64ea60009d02aa4b0dbd0433bba6e5edc7cd (patch)
treeb1935f6877e4396fe959f840a506ce7c2c14b486 /sys/dev/sound
parentc8949cb9c2c120c11f969da69b47a488eac2f41a (diff)
Notes
Diffstat (limited to 'sys/dev/sound')
-rw-r--r--sys/dev/sound/pcm/dsp.c156
-rw-r--r--sys/dev/sound/pcm/mixer.c14
-rw-r--r--sys/dev/sound/pcm/sound.c676
-rw-r--r--sys/dev/sound/pcm/sound.h19
-rw-r--r--sys/dev/sound/pcm/vchan.c82
5 files changed, 513 insertions, 434 deletions
diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c
index 603a510b2310..48153350864b 100644
--- a/sys/dev/sound/pcm/dsp.c
+++ b/sys/dev/sound/pcm/dsp.c
@@ -101,7 +101,7 @@ dsp_set_flags(struct cdev *dev, u_int32_t flags)
}
/*
- * return the channels channels associated with an open device instance.
+ * return the channels associated with an open device instance.
* set the priority if the device is simplex and one direction (only) is
* specified.
* lock channels specified.
@@ -170,11 +170,18 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
struct snddev_info *d;
u_int32_t fmt;
int devtype;
- int rdref;
int error;
+ int chnum;
+
+ if (i_dev == NULL || td == NULL)
+ return ENODEV;
+
+ if ((flags & (FREAD | FWRITE)) == 0)
+ return EINVAL;
d = dsp_get_info(i_dev);
devtype = PCMDEV(i_dev);
+ chnum = -1;
/* decide default format */
switch (devtype) {
@@ -196,34 +203,24 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
case SND_DEV_DSPREC:
fmt = AFMT_U8;
- if (mode & FWRITE) {
+ if (flags & FWRITE)
return EINVAL;
- }
+ chnum = PCMCHAN(i_dev);
break;
default:
panic("impossible devtype %d", devtype);
}
- rdref = 0;
-
/* lock snddev so nobody else can monkey with it */
pcm_lock(d);
rdch = i_dev->si_drv1;
wrch = i_dev->si_drv2;
- if ((dsp_get_flags(i_dev) & SD_F_SIMPLEX) && (rdch || wrch)) {
- /* we're a simplex device and already open, no go */
- pcm_unlock(d);
- return EBUSY;
- }
-
- if (((flags & FREAD) && rdch) || ((flags & FWRITE) && wrch)) {
- /*
- * device already open in one or both directions that
- * the opener wants; we can't handle this.
- */
+ if (rdch || wrch || ((dsp_get_flags(i_dev) & SD_F_SIMPLEX) &&
+ (flags & (FREAD | FWRITE)) == (FREAD | FWRITE))) {
+ /* simplex or not, better safe than sorry. */
pcm_unlock(d);
return EBUSY;
}
@@ -237,67 +234,48 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
if (flags & FREAD) {
/* open for read */
pcm_unlock(d);
- if (devtype == SND_DEV_DSPREC)
- rdch = pcm_chnalloc(d, PCMDIR_REC, td->td_proc->p_pid, PCMCHAN(i_dev));
- else
- rdch = pcm_chnalloc(d, PCMDIR_REC, td->td_proc->p_pid, -1);
- if (!rdch) {
- /* no channel available, exit */
- return EBUSY;
- }
- /* got a channel, already locked for us */
- if (chn_reset(rdch, fmt) ||
- (fmt && chn_setspeed(rdch, DSP_DEFAULT_SPEED))) {
- pcm_chnrelease(rdch);
- pcm_lock(d);
- i_dev->si_drv1 = NULL;
- pcm_unlock(d);
- return ENODEV;
+ error = pcm_chnalloc(d, &rdch, PCMDIR_REC, td->td_proc->p_pid, chnum);
+ if (error != 0 && error != EBUSY && chnum != -1 && (flags & FWRITE))
+ error = pcm_chnalloc(d, &rdch, PCMDIR_REC, td->td_proc->p_pid, -1);
+
+ if (error == 0 && (chn_reset(rdch, fmt) ||
+ (fmt && chn_setspeed(rdch, DSP_DEFAULT_SPEED))))
+ error = ENODEV;
+
+ if (error != 0) {
+ if (rdch)
+ pcm_chnrelease(rdch);
+ return error;
}
if (flags & O_NONBLOCK)
rdch->flags |= CHN_F_NBIO;
pcm_chnref(rdch, 1);
CHN_UNLOCK(rdch);
- rdref = 1;
- /*
- * Record channel created, ref'ed and unlocked
- */
- pcm_lock(d);
+ pcm_lock(d);
}
if (flags & FWRITE) {
/* open for write */
pcm_unlock(d);
- wrch = pcm_chnalloc(d, PCMDIR_PLAY, td->td_proc->p_pid, -1);
- error = 0;
+ error = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, td->td_proc->p_pid, chnum);
+ if (error != 0 && error != EBUSY && chnum != -1 && (flags & FREAD))
+ error = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, td->td_proc->p_pid, -1);
- if (!wrch)
- error = EBUSY; /* XXX Right return code? */
- else if (chn_reset(wrch, fmt) ||
- (fmt && chn_setspeed(wrch, DSP_DEFAULT_SPEED)))
+ if (error == 0 && (chn_reset(wrch, fmt) ||
+ (fmt && chn_setspeed(wrch, DSP_DEFAULT_SPEED))))
error = ENODEV;
if (error != 0) {
- if (wrch) {
- /*
- * Free play channel
- */
+ if (wrch)
pcm_chnrelease(wrch);
- pcm_lock(d);
- i_dev->si_drv2 = NULL;
- pcm_unlock(d);
- }
- if (rdref) {
+ if (rdch) {
/*
* Lock, deref and release previously created record channel
*/
CHN_LOCK(rdch);
pcm_chnref(rdch, -1);
pcm_chnrelease(rdch);
- pcm_lock(d);
- i_dev->si_drv1 = NULL;
- pcm_unlock(d);
}
return error;
@@ -330,38 +308,11 @@ dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
wrch = i_dev->si_drv2;
pcm_unlock(d);
- refs = 0;
-
- if (rdch) {
- CHN_LOCK(rdch);
- refs += pcm_chnref(rdch, -1);
- CHN_UNLOCK(rdch);
- }
- if (wrch) {
- CHN_LOCK(wrch);
- refs += pcm_chnref(wrch, -1);
- CHN_UNLOCK(wrch);
- }
-
- /*
- * If there are no more references, release the channels.
- */
- if ((rdch || wrch) && refs == 0) {
-
- pcm_lock(d);
-
- if (pcm_getfakechan(d))
- pcm_getfakechan(d)->flags = 0;
-
- i_dev->si_drv1 = NULL;
- i_dev->si_drv2 = NULL;
-
- dsp_set_flags(i_dev, dsp_get_flags(i_dev) & ~SD_F_TRANSIENT);
-
- pcm_unlock(d);
-
+ if (rdch || wrch) {
+ refs = 0;
if (rdch) {
CHN_LOCK(rdch);
+ refs += pcm_chnref(rdch, -1);
chn_abort(rdch); /* won't sleep */
rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD);
chn_reset(rdch, 0);
@@ -369,6 +320,7 @@ dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
}
if (wrch) {
CHN_LOCK(wrch);
+ refs += pcm_chnref(wrch, -1);
/*
* XXX: Maybe the right behaviour is to abort on non_block.
* It seems that mplayer flushes the audio queue by quickly
@@ -381,6 +333,23 @@ dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
chn_reset(wrch, 0);
pcm_chnrelease(wrch);
}
+
+ pcm_lock(d);
+ if (rdch)
+ i_dev->si_drv1 = NULL;
+ if (wrch)
+ i_dev->si_drv2 = NULL;
+ /*
+ * If there are no more references, release the channels.
+ */
+ if (refs == 0 && i_dev->si_drv1 == NULL &&
+ i_dev->si_drv2 == NULL) {
+ if (pcm_getfakechan(d))
+ pcm_getfakechan(d)->flags = 0;
+ /* What is this?!? */
+ dsp_set_flags(i_dev, dsp_get_flags(i_dev) & ~SD_F_TRANSIENT);
+ }
+ pcm_unlock(d);
}
return 0;
}
@@ -445,8 +414,15 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
*/
d = dsp_get_info(i_dev);
- if (IOCGROUP(cmd) == 'M')
- return mixer_ioctl(d->mixer_dev, cmd, arg, mode, td);
+ if (IOCGROUP(cmd) == 'M') {
+ /*
+ * This is at least, a bug to bug compatible with OSS.
+ */
+ if (d->mixer_dev != NULL)
+ return mixer_ioctl(d->mixer_dev, cmd, arg, -1, td);
+ else
+ return EBADF;
+ }
getchns(i_dev, &rdch, &wrch, 0);
@@ -1162,8 +1138,8 @@ dsp_clone(void *arg, struct ucred *cred, char *name, int namelen,
struct snddev_info *pcm_dev;
struct snddev_channel *pcm_chan;
int i, unit, devtype;
- int devtypes[3] = {SND_DEV_DSP, SND_DEV_DSP16, SND_DEV_AUDIO};
- char *devnames[3] = {"dsp", "dspW", "audio"};
+ static int devtypes[3] = {SND_DEV_DSP, SND_DEV_DSP16, SND_DEV_AUDIO};
+ static char *devnames[3] = {"dsp", "dspW", "audio"};
if (*dev != NULL)
return;
diff --git a/sys/dev/sound/pcm/mixer.c b/sys/dev/sound/pcm/mixer.c
index cf8403f8a6bc..8e4ab84691d1 100644
--- a/sys/dev/sound/pcm/mixer.c
+++ b/sys/dev/sound/pcm/mixer.c
@@ -269,10 +269,14 @@ int
mixer_uninit(device_t dev)
{
int i;
+ struct snddev_info *d;
struct snd_mixer *m;
struct cdev *pdev;
+ d = device_get_softc(dev);
pdev = mixer_get_devt(dev);
+ if (d == NULL || pdev == NULL || pdev->si_drv1 == NULL)
+ return EBADF;
m = pdev->si_drv1;
snd_mtxlock(m->lock);
@@ -294,6 +298,8 @@ mixer_uninit(device_t dev)
snd_mtxfree(m->lock);
kobj_delete((kobj_t)m, M_MIXER);
+ d->mixer_dev = NULL;
+
return 0;
}
@@ -465,10 +471,16 @@ mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread
int v = -1, j = cmd & 0xff;
m = i_dev->si_drv1;
- if (mode != -1 && !m->busy)
+
+ if (m == NULL)
return EBADF;
snd_mtxlock(m->lock);
+ if (mode != -1 && !m->busy) {
+ snd_mtxunlock(m->lock);
+ return EBADF;
+ }
+
if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) {
if (j == SOUND_MIXER_RECSRC)
ret = mixer_setrecsrc(m, *arg_i);
diff --git a/sys/dev/sound/pcm/sound.c b/sys/dev/sound/pcm/sound.c
index 0311a290f25e..21381c358884 100644
--- a/sys/dev/sound/pcm/sound.c
+++ b/sys/dev/sound/pcm/sound.c
@@ -53,7 +53,7 @@ static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
struct sysctl_ctx_list *
snd_sysctl_tree(device_t dev)
{
- struct snddev_info *d = device_get_softc(dev);
+ struct snddev_info *d = device_get_softc(dev);
return &d->sysctl_tree;
}
@@ -61,7 +61,7 @@ snd_sysctl_tree(device_t dev)
struct sysctl_oid *
snd_sysctl_tree_top(device_t dev)
{
- struct snddev_info *d = device_get_softc(dev);
+ struct snddev_info *d = device_get_softc(dev);
return d->sysctl_tree_top;
}
@@ -158,50 +158,161 @@ pcm_getfakechan(struct snddev_info *d)
return d->fakechan;
}
-/* return a locked channel */
-struct pcm_channel *
-pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum)
+static int
+pcm_setvchans(struct snddev_info *d, int newcnt)
+{
+ struct snddev_channel *sce = NULL;
+ struct pcm_channel *c = NULL;
+ int err = 0, vcnt, dcnt, i;
+
+ pcm_inprog(d, 1);
+
+ if (!(d->flags & SD_F_AUTOVCHAN)) {
+ err = EINVAL;
+ goto setvchans_out;
+ }
+
+ vcnt = d->vchancount;
+ dcnt = d->playcount + d->reccount;
+
+ if (newcnt < 0 || (dcnt + newcnt) > (PCMMAXCHAN + 1)) {
+ err = E2BIG;
+ goto setvchans_out;
+ }
+
+ dcnt += vcnt;
+
+ if (newcnt > vcnt) {
+ /* add new vchans - find a parent channel first */
+ SLIST_FOREACH(sce, &d->channels, link) {
+ c = sce->channel;
+ CHN_LOCK(c);
+ if (c->direction == PCMDIR_PLAY &&
+ ((c->flags & CHN_F_HAS_VCHAN) ||
+ (vcnt == 0 &&
+ !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)))))
+ goto addok;
+ CHN_UNLOCK(c);
+ }
+ err = EBUSY;
+ goto setvchans_out;
+addok:
+ c->flags |= CHN_F_BUSY;
+ while (err == 0 && newcnt > vcnt) {
+ if (dcnt > PCMMAXCHAN) {
+ device_printf(d->dev, "%s: Maximum channel reached.\n", __func__);
+ break;
+ }
+ err = vchan_create(c);
+ if (err == 0) {
+ vcnt++;
+ dcnt++;
+ } else if (err == E2BIG && newcnt > vcnt)
+ device_printf(d->dev, "%s: err=%d Maximum channel reached.\n", __func__, err);
+ }
+ if (vcnt == 0)
+ c->flags &= ~CHN_F_BUSY;
+ CHN_UNLOCK(c);
+ } else if (newcnt < vcnt) {
+#define ORPHAN_CDEVT(cdevt) \
+ ((cdevt) == NULL || ((cdevt)->si_drv1 == NULL && \
+ (cdevt)->si_drv2 == NULL))
+ while (err == 0 && newcnt < vcnt) {
+ i = 0;
+ SLIST_FOREACH(sce, &d->channels, link) {
+ c = sce->channel;
+ CHN_LOCK(c);
+ if (c->direction == PCMDIR_PLAY &&
+ (c->flags & CHN_F_VIRTUAL) &&
+ (i++ == newcnt)) {
+ if (!(c->flags & CHN_F_BUSY) &&
+ ORPHAN_CDEVT(sce->dsp_devt) &&
+ ORPHAN_CDEVT(sce->dspW_devt) &&
+ ORPHAN_CDEVT(sce->audio_devt) &&
+ ORPHAN_CDEVT(sce->dspr_devt))
+ goto remok;
+ /*
+ * Either we're busy, or our cdev
+ * has been stolen by dsp_clone().
+ * Skip, and increase newcnt.
+ */
+ if (!(c->flags & CHN_F_BUSY))
+ device_printf(d->dev,
+ "%s: <%s> somebody steal my cdev!\n",
+ __func__, c->name);
+ newcnt++;
+ }
+ CHN_UNLOCK(c);
+ }
+ if (vcnt != newcnt)
+ err = EBUSY;
+ break;
+remok:
+ CHN_UNLOCK(c);
+ err = vchan_destroy(c);
+ if (err == 0)
+ vcnt--;
+ else
+ device_printf(d->dev,
+ "%s: WARNING: vchan_destroy() failed!",
+ __func__);
+ }
+ }
+
+setvchans_out:
+ pcm_inprog(d, -1);
+ return err;
+}
+
+/* return error status and a locked channel */
+int
+pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
+ pid_t pid, int chnum)
{
struct pcm_channel *c;
- struct snddev_channel *sce;
+ struct snddev_channel *sce;
int err;
+retry_chnalloc:
+ err = ENODEV;
/* scan for a free channel */
SLIST_FOREACH(sce, &d->channels, link) {
c = sce->channel;
CHN_LOCK(c);
- if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) {
- if (chnum == -1 || c->num == chnum) {
+ if (c->direction == direction && !(c->flags & CHN_F_BUSY)) {
+ if (chnum < 0 || sce->chan_num == chnum) {
c->flags |= CHN_F_BUSY;
c->pid = pid;
- return c;
+ *ch = c;
+ return 0;
}
}
+ if (sce->chan_num == chnum) {
+ if (c->direction != direction)
+ err = EOPNOTSUPP;
+ else if (c->flags & CHN_F_BUSY)
+ err = EBUSY;
+ else
+ err = EINVAL;
+ CHN_UNLOCK(c);
+ return err;
+ } else if (c->direction == direction && (c->flags & CHN_F_BUSY))
+ err = EBUSY;
CHN_UNLOCK(c);
}
/* no channel available */
- if (direction == PCMDIR_PLAY) {
- if ((d->vchancount > 0) && (d->vchancount < snd_maxautovchans)) {
- /* try to create a vchan */
- SLIST_FOREACH(sce, &d->channels, link) {
- c = sce->channel;
- CHN_LOCK(c);
- if ((c->flags & CHN_F_HAS_VCHAN) &&
- !SLIST_EMPTY(&c->children)) {
- err = vchan_create(c);
- CHN_UNLOCK(c);
- if (!err)
- return pcm_chnalloc(d, direction, pid, -1);
- else
- device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
- } else
- CHN_UNLOCK(c);
- }
+ if (chnum == -1 && direction == PCMDIR_PLAY && d->vchancount > 0 &&
+ d->vchancount < snd_maxautovchans &&
+ d->devcount <= PCMMAXCHAN) {
+ err = pcm_setvchans(d, d->vchancount + 1);
+ if (err == 0) {
+ chnum = -2;
+ goto retry_chnalloc;
}
}
- return NULL;
+ return err;
}
/* release a locked channel and unlock it */
@@ -245,65 +356,10 @@ pcm_inprog(struct snddev_info *d, int delta)
static void
pcm_setmaxautovchans(struct snddev_info *d, int num)
{
- struct pcm_channel *c, *ch;
- struct snddev_channel *sce;
- int err, done;
-
- /*
- * XXX WOAH... NEED SUPER CLEANUP!!!
- * Robust, yet confusing. Understanding these will
- * cause your brain spinning like a Doki Doki Dynamo.
- */
- if (num > 0 && d->vchancount == 0) {
- SLIST_FOREACH(sce, &d->channels, link) {
- c = sce->channel;
- CHN_LOCK(c);
- if ((c->direction == PCMDIR_PLAY) &&
- !(c->flags & CHN_F_BUSY) &&
- SLIST_EMPTY(&c->children)) {
- c->flags |= CHN_F_BUSY;
- err = vchan_create(c);
- if (err) {
- c->flags &= ~CHN_F_BUSY;
- device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
- }
- CHN_UNLOCK(c);
- return;
- }
- CHN_UNLOCK(c);
- }
- return;
- }
- if (num == 0 && d->vchancount > 0) {
- /*
- * XXX Keep retrying...
- */
- for (done = 0; done < 1024; done++) {
- ch = NULL;
- SLIST_FOREACH(sce, &d->channels, link) {
- c = sce->channel;
- CHN_LOCK(c);
- if (c->direction == PCMDIR_PLAY &&
- !(c->flags & CHN_F_BUSY) &&
- (c->flags & CHN_F_VIRTUAL)) {
- ch = c;
- break;
- }
- CHN_UNLOCK(c);
- }
- if (ch != NULL) {
- CHN_UNLOCK(ch);
- snd_mtxlock(d->lock);
- err = vchan_destroy(ch);
- if (err)
- device_printf(d->dev, "vchan_destroy(%s) == %d\n",
- ch->name, err);
- snd_mtxunlock(d->lock);
- } else
- return;
- }
- return;
- }
+ if (num > 0 && d->vchancount == 0)
+ pcm_setvchans(d, 1);
+ else if (num == 0 && d->vchancount > 0)
+ pcm_setvchans(d, 0);
}
#ifdef USING_DEVFS
@@ -338,18 +394,14 @@ sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
v = snd_maxautovchans;
error = sysctl_handle_int(oidp, &v, sizeof(v), req);
if (error == 0 && req->newptr != NULL) {
- if (v < 0 || v >= SND_MAXVCHANS || pcm_devclass == NULL)
- return EINVAL;
- if (v != snd_maxautovchans) {
+ if (v < 0 || v > PCMMAXCHAN)
+ return E2BIG;
+ if (pcm_devclass != NULL && v != snd_maxautovchans) {
for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
d = devclass_get_softc(pcm_devclass, i);
if (!d)
continue;
- if (d->flags & SD_F_AUTOVCHAN) {
- if (pcm_inprog(d, 1) == 1)
- pcm_setmaxautovchans(d, v);
- pcm_inprog(d, -1);
- }
+ pcm_setmaxautovchans(d, v);
}
}
snd_maxautovchans = v;
@@ -362,9 +414,11 @@ SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
struct pcm_channel *
pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
{
- struct pcm_channel *ch;
+ struct snddev_channel *sce;
+ struct pcm_channel *ch, *c;
char *dirs;
- int direction, err, *pnum;
+ uint32_t flsearch = 0;
+ int direction, err, rpnum, *pnum;
switch(dir) {
case PCMDIR_PLAY:
@@ -383,6 +437,7 @@ pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t c
dirs = "virtual";
direction = PCMDIR_PLAY;
pnum = &d->vchancount;
+ flsearch = CHN_F_VIRTUAL;
break;
default:
@@ -401,14 +456,54 @@ pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t c
}
snd_mtxlock(d->lock);
- ch->num = (*pnum)++;
+ ch->num = 0;
+ rpnum = 0;
+ SLIST_FOREACH(sce, &d->channels, link) {
+ c = sce->channel;
+ if (direction != c->direction ||
+ (c->flags & CHN_F_VIRTUAL) != flsearch)
+ continue;
+ if (ch->num == c->num)
+ ch->num++;
+ else {
+#if 0
+ device_printf(d->dev,
+ "%s: %s channel numbering screwed (Expect: %d, Got: %d)\n",
+ __func__, dirs, ch->num, c->num);
+#endif
+ goto retry_num_search;
+ }
+ rpnum++;
+ }
+ goto retry_num_search_out;
+retry_num_search:
+ rpnum = 0;
+ SLIST_FOREACH(sce, &d->channels, link) {
+ c = sce->channel;
+ if (direction != c->direction ||
+ (c->flags & CHN_F_VIRTUAL) != flsearch)
+ continue;
+ if (ch->num == c->num) {
+ ch->num++;
+ goto retry_num_search;
+ }
+ rpnum++;
+ }
+retry_num_search_out:
+ if (*pnum != rpnum) {
+ device_printf(d->dev,
+ "%s: WARNING: pnum screwed : dirs=%s, pnum=%d, rpnum=%d\n",
+ __func__, dirs, *pnum, rpnum);
+ *pnum = rpnum;
+ }
+ (*pnum)++;
snd_mtxunlock(d->lock);
ch->pid = -1;
ch->parentsnddev = d;
ch->parentchannel = parent;
ch->dev = d->dev;
- snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num);
+ snprintf(ch->name, CHN_NAMELEN, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num);
err = chn_init(ch, devinfo, dir, direction);
if (err) {
@@ -447,14 +542,16 @@ pcm_chn_destroy(struct pcm_channel *ch)
int
pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
{
- struct snddev_channel *sce, *tmp, *after;
- int device = device_get_unit(d->dev);
+ struct snddev_channel *sce, *tmp, *after;
+ unsigned rdevcount;
+ int device = device_get_unit(d->dev);
+ size_t namelen;
/*
* Note it's confusing nomenclature.
* dev_t
* device -> pcm_device
- * unit -> pcm_channel
+ * unit -> pcm_channel
* channel -> snddev_channel
* device_t
* unit -> pcm_device
@@ -467,54 +564,101 @@ pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
snd_mtxlock(d->lock);
sce->channel = ch;
- sce->chan_num= d->devcount++;
- if (SLIST_EMPTY(&d->channels)) {
+ sce->chan_num = 0;
+ rdevcount = 0;
+ after = NULL;
+ SLIST_FOREACH(tmp, &d->channels, link) {
+ if (sce->chan_num == tmp->chan_num)
+ sce->chan_num++;
+ else {
+#if 0
+ device_printf(d->dev,
+ "%s: cdev numbering screwed (Expect: %d, Got: %d)\n",
+ __func__, sce->chan_num, tmp->chan_num);
+#endif
+ goto retry_chan_num_search;
+ }
+ after = tmp;
+ rdevcount++;
+ }
+ goto retry_chan_num_search_out;
+retry_chan_num_search:
+ /*
+ * Look for possible channel numbering collision. This may not
+ * be optimized, but it will ensure that no collision occured.
+ * Can be considered cheap since none of the locking/unlocking
+ * operations involved.
+ */
+ rdevcount = 0;
+ after = NULL;
+ SLIST_FOREACH(tmp, &d->channels, link) {
+ if (sce->chan_num == tmp->chan_num) {
+ sce->chan_num++;
+ goto retry_chan_num_search;
+ }
+ if (sce->chan_num > tmp->chan_num)
+ after = tmp;
+ rdevcount++;
+ }
+retry_chan_num_search_out:
+ /*
+ * Don't overflow PCMMKMINOR / PCMMAXCHAN.
+ */
+ if (sce->chan_num > PCMMAXCHAN) {
+ snd_mtxunlock(d->lock);
+ device_printf(d->dev,
+ "%s: WARNING: sce->chan_num overflow! (%d)\n",
+ __func__, sce->chan_num);
+ free(sce, M_DEVBUF);
+ return E2BIG;
+ }
+ if (d->devcount != rdevcount) {
+ device_printf(d->dev,
+ "%s: WARNING: devcount screwed! d->devcount=%u, rdevcount=%u\n",
+ __func__, d->devcount, rdevcount);
+ d->devcount = rdevcount;
+ }
+ d->devcount++;
+ if (after == NULL) {
SLIST_INSERT_HEAD(&d->channels, sce, link);
} else {
- /*
- * Micro optimization, channel ordering:
- * hw,hw,hw,vch,vch,vch,rec
- */
- after = NULL;
- if (ch->flags & CHN_F_VIRTUAL) {
- /* virtual channel to the end */
- SLIST_FOREACH(tmp, &d->channels, link) {
- if (tmp->channel->direction == PCMDIR_REC)
- break;
- after = tmp;
- }
- } else {
- if (ch->direction == PCMDIR_REC) {
- SLIST_FOREACH(tmp, &d->channels, link) {
- after = tmp;
- }
- } else {
- SLIST_FOREACH(tmp, &d->channels, link) {
- if (tmp->channel->direction == PCMDIR_REC)
- break;
- if (!(tmp->channel->flags & CHN_F_VIRTUAL))
- after = tmp;
- }
- }
- }
- if (after == NULL) {
- SLIST_INSERT_HEAD(&d->channels, sce, link);
- } else {
- SLIST_INSERT_AFTER(after, sce, link);
+ SLIST_INSERT_AFTER(after, sce, link);
+ }
+#if 0
+ if (1) {
+ int cnum = 0;
+ SLIST_FOREACH(tmp, &d->channels, link) {
+ if (cnum != tmp->chan_num)
+ device_printf(d->dev,
+ "%s: WARNING: inconsistent cdev numbering! (Expect: %d, Got: %d)\n",
+ __func__, cnum, tmp->chan_num);
+ cnum++;
}
}
+#endif
+
+ namelen = strlen(ch->name);
+ if ((CHN_NAMELEN - namelen) > 10) { /* ":dspXX.YYY" */
+ snprintf(ch->name + namelen,
+ CHN_NAMELEN - namelen, ":dsp%d.%d",
+ device, sce->chan_num);
+ }
snd_mtxunlock(d->lock);
- sce->dsp_devt= make_dev(&dsp_cdevsw,
+
+ /*
+ * I will revisit these someday, and nuke it mercilessly..
+ */
+ sce->dsp_devt = make_dev(&dsp_cdevsw,
PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num),
UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d",
device, sce->chan_num);
- sce->dspW_devt= make_dev(&dsp_cdevsw,
+ sce->dspW_devt = make_dev(&dsp_cdevsw,
PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num),
UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d",
device, sce->chan_num);
- sce->audio_devt= make_dev(&dsp_cdevsw,
+ sce->audio_devt = make_dev(&dsp_cdevsw,
PCMMKMINOR(device, SND_DEV_AUDIO, sce->chan_num),
UID_ROOT, GID_WHEEL, 0666, "audio%d.%d",
device, sce->chan_num);
@@ -531,7 +675,7 @@ pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
int
pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
{
- struct snddev_channel *sce;
+ struct snddev_channel *sce;
#if 0
int ourlock;
@@ -573,9 +717,9 @@ gotit:
int
pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
{
- struct snddev_info *d = device_get_softc(dev);
+ struct snddev_info *d = device_get_softc(dev);
struct pcm_channel *ch;
- int err;
+ int err;
ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
if (!ch) {
@@ -590,28 +734,14 @@ pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
return err;
}
- CHN_LOCK(ch);
- if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN) &&
- ch->direction == PCMDIR_PLAY && d->vchancount == 0) {
- ch->flags |= CHN_F_BUSY;
- err = vchan_create(ch);
- if (err) {
- ch->flags &= ~CHN_F_BUSY;
- CHN_UNLOCK(ch);
- device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err);
- return err;
- }
- }
- CHN_UNLOCK(ch);
-
return err;
}
static int
pcm_killchan(device_t dev)
{
- struct snddev_info *d = device_get_softc(dev);
- struct snddev_channel *sce;
+ struct snddev_info *d = device_get_softc(dev);
+ struct snddev_channel *sce;
struct pcm_channel *ch;
int error = 0;
@@ -627,26 +757,28 @@ pcm_killchan(device_t dev)
int
pcm_setstatus(device_t dev, char *str)
{
- struct snddev_info *d = device_get_softc(dev);
+ struct snddev_info *d = device_get_softc(dev);
snd_mtxlock(d->lock);
strncpy(d->status, str, SND_STATUSLEN);
snd_mtxunlock(d->lock);
+ if (snd_maxautovchans > 0)
+ pcm_setvchans(d, 1);
return 0;
}
-u_int32_t
+uint32_t
pcm_getflags(device_t dev)
{
- struct snddev_info *d = device_get_softc(dev);
+ struct snddev_info *d = device_get_softc(dev);
return d->flags;
}
void
-pcm_setflags(device_t dev, u_int32_t val)
+pcm_setflags(device_t dev, uint32_t val)
{
- struct snddev_info *d = device_get_softc(dev);
+ struct snddev_info *d = device_get_softc(dev);
d->flags = val;
}
@@ -654,7 +786,7 @@ pcm_setflags(device_t dev, u_int32_t val)
void *
pcm_getdevinfo(device_t dev)
{
- struct snddev_info *d = device_get_softc(dev);
+ struct snddev_info *d = device_get_softc(dev);
return d->devinfo;
}
@@ -662,7 +794,7 @@ pcm_getdevinfo(device_t dev)
unsigned int
pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max)
{
- struct snddev_info *d = device_get_softc(dev);
+ struct snddev_info *d = device_get_softc(dev);
int sz, x;
sz = 0;
@@ -692,7 +824,7 @@ pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned i
int
pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
{
- struct snddev_info *d = device_get_softc(dev);
+ struct snddev_info *d = device_get_softc(dev);
if (pcm_veto_load) {
device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
@@ -720,7 +852,7 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
SLIST_INIT(&d->channels);
- if (((numplay == 0) || (numrec == 0)) && (numplay != numrec))
+ if ((numplay == 0 || numrec == 0) && numplay != numrec)
d->flags |= SD_F_SIMPLEX;
d->fakechan = fkchan_setup(dev);
@@ -739,12 +871,12 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
#endif
if (numplay > 0) {
- vchan_initsys(dev);
d->flags |= SD_F_AUTOVCHAN;
+ vchan_initsys(dev);
}
sndstat_register(dev, d->status, sndstat_prepare_pcm);
- return 0;
+ return 0;
no:
snd_mtxfree(d->lock);
return ENXIO;
@@ -753,8 +885,9 @@ no:
int
pcm_unregister(device_t dev)
{
- struct snddev_info *d = device_get_softc(dev);
- struct snddev_channel *sce;
+ struct snddev_info *d = device_get_softc(dev);
+ struct snddev_channel *sce;
+ struct pcmchan_children *pce;
struct pcm_channel *ch;
if (sndstat_acquire() != 0) {
@@ -780,7 +913,7 @@ pcm_unregister(device_t dev)
}
}
- if (mixer_uninit(dev)) {
+ if (mixer_uninit(dev) == EBUSY) {
device_printf(dev, "unregister: mixer busy\n");
snd_mtxunlock(d->lock);
sndstat_release();
@@ -788,26 +921,66 @@ pcm_unregister(device_t dev)
}
SLIST_FOREACH(sce, &d->channels, link) {
- if (sce->dsp_devt)
+ if (sce->dsp_devt) {
destroy_dev(sce->dsp_devt);
- if (sce->dspW_devt)
+ sce->dsp_devt = NULL;
+ }
+ if (sce->dspW_devt) {
destroy_dev(sce->dspW_devt);
- if (sce->audio_devt)
+ sce->dspW_devt = NULL;
+ }
+ if (sce->audio_devt) {
destroy_dev(sce->audio_devt);
- if (sce->dspr_devt)
+ sce->audio_devt = NULL;
+ }
+ if (sce->dspr_devt) {
destroy_dev(sce->dspr_devt);
+ sce->dspr_devt = NULL;
+ }
+ d->devcount--;
+ ch = sce->channel;
+ if (ch == NULL)
+ continue;
+ pce = SLIST_FIRST(&ch->children);
+ while (pce != NULL) {
+#if 0
+ device_printf(d->dev, "<%s> removing <%s>\n",
+ ch->name, (pce->channel != NULL) ?
+ pce->channel->name : "unknown");
+#endif
+ SLIST_REMOVE(&ch->children, pce, pcmchan_children, link);
+ free(pce, M_DEVBUF);
+ pce = SLIST_FIRST(&ch->children);
+ }
}
#ifdef SND_DYNSYSCTL
d->sysctl_tree_top = NULL;
sysctl_ctx_free(&d->sysctl_tree);
#endif
+
+#if 0
+ SLIST_FOREACH(sce, &d->channels, link) {
+ ch = sce->channel;
+ if (ch == NULL)
+ continue;
+ if (!SLIST_EMPTY(&ch->children))
+ device_printf(d->dev, "%s: WARNING: <%s> dangling child!\n",
+ __func__, ch->name);
+ }
+#endif
while (!SLIST_EMPTY(&d->channels))
pcm_killchan(dev);
chn_kill(d->fakechan);
fkchan_kill(d->fakechan);
+#if 0
+ device_printf(d->dev, "%s: devcount=%u, playcount=%u, "
+ "reccount=%u, vchancount=%u\n",
+ __func__, d->devcount, d->playcount, d->reccount,
+ d->vchancount);
+#endif
snd_mtxunlock(d->lock);
snd_mtxfree(d->lock);
sndstat_unregister(dev);
@@ -820,11 +993,11 @@ pcm_unregister(device_t dev)
static int
sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
{
- struct snddev_info *d;
- struct snddev_channel *sce;
+ struct snddev_info *d;
+ struct snddev_channel *sce;
struct pcm_channel *c;
struct pcm_feeder *f;
- int pc, rc, vc;
+ int pc, rc, vc;
if (verbose < 1)
return 0;
@@ -862,6 +1035,10 @@ sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
SLIST_FOREACH(sce, &d->channels, link) {
c = sce->channel;
+
+ KASSERT(c->bufhard != NULL && c->bufsoft != NULL,
+ ("hosed pcm channel setup"));
+
sbuf_printf(s, "\n\t");
/* it would be better to indent child channels */
@@ -877,32 +1054,22 @@ sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
sbuf_printf(s, ", pid %d", c->pid);
sbuf_printf(s, "\n\t");
- if (c->bufhard != NULL && c->bufsoft != NULL) {
- sbuf_printf(s, "interrupts %d, ", c->interrupts);
- if (c->direction == PCMDIR_REC)
- sbuf_printf(s, "overruns %d, hfree %d, sfree %d",
- c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft));
-#if 0
- sbuf_printf(s, "overruns %d, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
- c->xruns, 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));
-#endif
- else
- sbuf_printf(s, "underruns %d, ready %d",
- c->xruns, sndbuf_getready(c->bufsoft));
-#if 0
- sbuf_printf(s, "underruns %d, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
- c->xruns, 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));
-#endif
- sbuf_printf(s, "\n\t");
- }
+ sbuf_printf(s, "interrupts %d, ", c->interrupts);
+ if (c->direction == PCMDIR_REC)
+ sbuf_printf(s, "overruns %d, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
+ c->xruns, 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, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
+ c->xruns, 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, " -> ");
@@ -937,113 +1104,16 @@ int
sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
{
struct snddev_info *d;
- struct snddev_channel *sce;
- struct pcm_channel *c;
- int err, newcnt, cnt;
+ int err, newcnt;
- /*
- * XXX WOAH... NEED SUPER CLEANUP!!!
- * Robust, yet confusing. Understanding these will
- * cause your brain spinning like a Doki Doki Dynamo.
- */
d = oidp->oid_arg1;
- if (!(d->flags & SD_F_AUTOVCHAN)) {
- pcm_inprog(d, -1);
- return EINVAL;
- }
-
- cnt = 0;
- SLIST_FOREACH(sce, &d->channels, link) {
- c = sce->channel;
- CHN_LOCK(c);
- if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) {
- cnt++;
- if (req->newptr != NULL && c->flags & CHN_F_BUSY) {
- /* Better safe than sorry */
- CHN_UNLOCK(c);
- return EBUSY;
- }
- }
- CHN_UNLOCK(c);
- }
-
- newcnt = cnt;
-
+ newcnt = d->vchancount;
err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
- if (err == 0 && req->newptr != NULL) {
-
- if (newcnt < 0 || newcnt > SND_MAXVCHANS)
- return E2BIG;
-
- if (pcm_inprog(d, 1) != 1) {
- pcm_inprog(d, -1);
- return EINPROGRESS;
- }
+ if (err == 0 && req->newptr != NULL && d->vchancount != newcnt)
+ err = pcm_setvchans(d, newcnt);
- if (newcnt > cnt) {
- /* add new vchans - find a parent channel first */
- SLIST_FOREACH(sce, &d->channels, link) {
- c = sce->channel;
- CHN_LOCK(c);
- /* not a candidate if not a play channel */
- if (c->direction != PCMDIR_PLAY)
- goto next;
- /* not a candidate if a virtual channel */
- if (c->flags & CHN_F_VIRTUAL)
- goto next;
- /* not a candidate if it's in use */
- if (!(c->flags & CHN_F_BUSY) ||
- !(SLIST_EMPTY(&c->children)))
- /*
- * if we get here we're a nonvirtual
- * play channel, and either
- * 1) not busy
- * 2) busy with children, not directly
- * open
- *
- * thus we can add children
- */
- goto addok;
-next:
- CHN_UNLOCK(c);
- }
- pcm_inprog(d, -1);
- return EBUSY;
-addok:
- c->flags |= CHN_F_BUSY;
- while (err == 0 && newcnt > cnt) {
- err = vchan_create(c);
- if (err == 0)
- cnt++;
- }
- CHN_UNLOCK(c);
- } else if (newcnt < cnt) {
- snd_mtxlock(d->lock);
- while (err == 0 && newcnt < cnt) {
- SLIST_FOREACH(sce, &d->channels, link) {
- c = sce->channel;
- CHN_LOCK(c);
- if (c->direction == PCMDIR_PLAY &&
- (c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL)
- goto remok;
-
- CHN_UNLOCK(c);
- }
- snd_mtxunlock(d->lock);
- pcm_inprog(d, -1);
- return EINVAL;
-remok:
- CHN_UNLOCK(c);
- err = vchan_destroy(c);
- if (err == 0)
- cnt--;
- }
- snd_mtxunlock(d->lock);
- }
- pcm_inprog(d, -1);
- }
return err;
}
#endif
diff --git a/sys/dev/sound/pcm/sound.h b/sys/dev/sound/pcm/sound.h
index af539a66bf0b..7613b300c642 100644
--- a/sys/dev/sound/pcm/sound.h
+++ b/sys/dev/sound/pcm/sound.h
@@ -123,11 +123,15 @@ nomenclature:
[etc.]
*/
-#define PCMMINOR(x) (minor(x))
-#define PCMCHAN(x) ((PCMMINOR(x) & 0x00ff0000) >> 16)
-#define PCMUNIT(x) ((PCMMINOR(x) & 0x000000f0) >> 4)
-#define PCMDEV(x) (PCMMINOR(x) & 0x0000000f)
-#define PCMMKMINOR(u, d, c) ((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f))
+#define PCMMAXCHAN 0xff
+#define PCMMAXDEV 0x0f
+#define PCMMAXUNIT 0x0f
+#define PCMMINOR(x) (minor(x))
+#define PCMCHAN(x) ((PCMMINOR(x) >> 16) & PCMMAXCHAN)
+#define PCMUNIT(x) ((PCMMINOR(x) >> 4) & PCMMAXUNIT)
+#define PCMDEV(x) (PCMMINOR(x) & PCMMAXDEV)
+#define PCMMKMINOR(u, d, c) ((((c) & PCMMAXCHAN) << 16) | \
+ (((u) & PCMMAXUNIT) << 4) | ((d) & PCMMAXDEV))
#define SD_F_SIMPLEX 0x00000001
#define SD_F_AUTOVCHAN 0x00000002
@@ -156,7 +160,8 @@ nomenclature:
struct pcm_channel *fkchan_setup(device_t dev);
int fkchan_kill(struct pcm_channel *c);
-#define SND_MAXVCHANS 255
+/* XXX Flawed definition. I'll fix it someday. */
+#define SND_MAXVCHANS PCMMAXCHAN
/*
* Minor numbers for the sound driver.
@@ -207,7 +212,7 @@ struct sysctl_ctx_list *snd_sysctl_tree(device_t dev);
struct sysctl_oid *snd_sysctl_tree_top(device_t dev);
struct pcm_channel *pcm_getfakechan(struct snddev_info *d);
-struct pcm_channel *pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum);
+int pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, pid_t pid, int chnum);
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);
diff --git a/sys/dev/sound/pcm/vchan.c b/sys/dev/sound/pcm/vchan.c
index 9d6fd597c269..fba208b708b9 100644
--- a/sys/dev/sound/pcm/vchan.c
+++ b/sys/dev/sound/pcm/vchan.c
@@ -290,36 +290,50 @@ sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
d = oidp->oid_arg1;
if (!(d->flags & SD_F_AUTOVCHAN) || d->vchancount < 1)
return EINVAL;
+ if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) {
+ pcm_inprog(d, -1);
+ return EINPROGRESS;
+ }
SLIST_FOREACH(sce, &d->channels, link) {
c = sce->channel;
CHN_LOCK(c);
if (c->direction == PCMDIR_PLAY) {
if (c->flags & CHN_F_VIRTUAL) {
+ /* Sanity check */
+ if (ch != NULL && ch != c->parentchannel) {
+ CHN_UNLOCK(c);
+ pcm_inprog(d, -1);
+ return EINVAL;
+ }
if (req->newptr != NULL &&
(c->flags & CHN_F_BUSY)) {
CHN_UNLOCK(c);
+ pcm_inprog(d, -1);
return EBUSY;
}
- if (ch == NULL)
- ch = c->parentchannel;
+ } else if (c->flags & CHN_F_HAS_VCHAN) {
+ /* No way!! */
+ if (ch != NULL) {
+ CHN_UNLOCK(c);
+ pcm_inprog(d, -1);
+ return EINVAL;
+ }
+ ch = c;
+ newspd = ch->speed;
}
}
CHN_UNLOCK(c);
}
- if (ch != NULL) {
- CHN_LOCK(ch);
- newspd = ch->speed;
- CHN_UNLOCK(ch);
+ if (ch == NULL) {
+ pcm_inprog(d, -1);
+ return EINVAL;
}
err = sysctl_handle_int(oidp, &newspd, sizeof(newspd), req);
if (err == 0 && req->newptr != NULL) {
- if (ch == NULL || newspd < 1 ||
- newspd < feeder_rate_ratemin ||
- newspd > feeder_rate_ratemax)
- return EINVAL;
- if (pcm_inprog(d, 1) != 1) {
+ if (newspd < 1 || newspd < feeder_rate_ratemin ||
+ newspd > feeder_rate_ratemax) {
pcm_inprog(d, -1);
- return EINPROGRESS;
+ return EINVAL;
}
CHN_LOCK(ch);
caps = chn_getcaps(ch);
@@ -350,8 +364,8 @@ sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
}
} else
CHN_UNLOCK(ch);
- pcm_inprog(d, -1);
}
+ pcm_inprog(d, -1);
return err;
}
#endif
@@ -507,8 +521,8 @@ vchan_create(struct pcm_channel *parent)
parent->flags &= ~CHN_F_HAS_VCHAN;
CHN_UNLOCK(parent);
free(pce, M_DEVBUF);
- pcm_chn_remove(d, child);
- pcm_chn_destroy(child);
+ if (pcm_chn_remove(d, child) == 0)
+ pcm_chn_destroy(child);
CHN_LOCK(parent);
return err;
}
@@ -524,7 +538,8 @@ vchan_destroy(struct pcm_channel *c)
struct snddev_info *d = parent->parentsnddev;
struct pcmchan_children *pce;
struct snddev_channel *sce;
- int err, last;
+ uint32_t spd;
+ int err;
CHN_LOCK(parent);
if (!(parent->flags & CHN_F_BUSY)) {
@@ -546,24 +561,34 @@ vchan_destroy(struct pcm_channel *c)
gotch:
SLIST_FOREACH(sce, &d->channels, link) {
if (sce->channel == c) {
- if (sce->dsp_devt)
+ if (sce->dsp_devt) {
destroy_dev(sce->dsp_devt);
- if (sce->dspW_devt)
+ sce->dsp_devt = NULL;
+ }
+ if (sce->dspW_devt) {
destroy_dev(sce->dspW_devt);
- if (sce->audio_devt)
+ sce->dspW_devt = NULL;
+ }
+ if (sce->audio_devt) {
destroy_dev(sce->audio_devt);
- if (sce->dspr_devt)
+ sce->audio_devt = NULL;
+ }
+ if (sce->dspr_devt) {
destroy_dev(sce->dspr_devt);
+ sce->dspr_devt = NULL;
+ }
+ d->devcount--;
break;
}
}
SLIST_REMOVE(&parent->children, pce, pcmchan_children, link);
free(pce, M_DEVBUF);
- last = SLIST_EMPTY(&parent->children);
- if (last) {
- parent->flags &= ~CHN_F_BUSY;
- parent->flags &= ~CHN_F_HAS_VCHAN;
+ if (SLIST_EMPTY(&parent->children)) {
+ parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN);
+ spd = parent->speed;
+ if (chn_reset(parent, parent->format) == 0)
+ chn_setspeed(parent, spd);
}
/* remove us from our grandparent's channel list */
@@ -574,15 +599,6 @@ gotch:
if (!err)
err = pcm_chn_destroy(c);
-#if 0
- if (!err && last) {
- CHN_LOCK(parent);
- chn_reset(parent, chn_getcaps(parent)->fmtlist[0]);
- chn_setspeed(parent, chn_getcaps(parent)->minspeed);
- CHN_UNLOCK(parent);
- }
-#endif
-
return err;
}