aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/sound/pcm/channel.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/sound/pcm/channel.c')
-rw-r--r--sys/dev/sound/pcm/channel.c471
1 files changed, 263 insertions, 208 deletions
diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c
index 859476f212ae..4d13f20a5262 100644
--- a/sys/dev/sound/pcm/channel.c
+++ b/sys/dev/sound/pcm/channel.c
@@ -6,6 +6,10 @@
* Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org>
* Portions Copyright (c) Luigi Rizzo <luigi@FreeBSD.org> - 1997-99
* All rights reserved.
+ * Copyright (c) 2024-2025 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Christos Margiolis
+ * <christos@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -128,6 +132,7 @@ chn_vpc_proc(int reset, int db)
struct pcm_channel *c;
int i;
+ bus_topo_lock();
for (i = 0; pcm_devclass != NULL &&
i < devclass_get_maxunit(pcm_devclass); i++) {
d = devclass_get_softc(pcm_devclass, i);
@@ -146,6 +151,7 @@ chn_vpc_proc(int reset, int db)
PCM_RELEASE(d);
PCM_UNLOCK(d);
}
+ bus_topo_unlock();
}
static int
@@ -166,7 +172,7 @@ sysctl_hw_snd_vpc_0db(SYSCTL_HANDLER_ARGS)
return (0);
}
SYSCTL_PROC(_hw_snd, OID_AUTO, vpc_0db,
- CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int),
+ CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 0, sizeof(int),
sysctl_hw_snd_vpc_0db, "I",
"0db relative level");
@@ -186,7 +192,7 @@ sysctl_hw_snd_vpc_reset(SYSCTL_HANDLER_ARGS)
return (0);
}
SYSCTL_PROC(_hw_snd, OID_AUTO, vpc_reset,
- CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, 0, sizeof(int),
+ CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, 0, sizeof(int),
sysctl_hw_snd_vpc_reset, "I",
"reset volume on all channels");
@@ -309,14 +315,7 @@ chn_wakeup(struct pcm_channel *c)
if (CHN_EMPTY(c, children.busy)) {
if (SEL_WAITING(sndbuf_getsel(bs)) && chn_polltrigger(c))
selwakeuppri(sndbuf_getsel(bs), PRIBIO);
- if (c->flags & CHN_F_SLEEPING) {
- /*
- * Ok, I can just panic it right here since it is
- * quite obvious that we never allow multiple waiters
- * from userland. I'm too generous...
- */
- CHN_BROADCAST(&c->intr_cv);
- }
+ CHN_BROADCAST(&c->intr_cv);
} else {
CHN_FOREACH(ch, c, children.busy) {
CHN_LOCK(ch);
@@ -332,15 +331,13 @@ chn_sleep(struct pcm_channel *c, int timeout)
int ret;
CHN_LOCKASSERT(c);
- KASSERT((c->flags & CHN_F_SLEEPING) == 0,
- ("%s(): entered with CHN_F_SLEEPING", __func__));
if (c->flags & CHN_F_DEAD)
return (EINVAL);
- c->flags |= CHN_F_SLEEPING;
+ c->sleeping++;
ret = cv_timedwait_sig(&c->intr_cv, c->lock, timeout);
- c->flags &= ~CHN_F_SLEEPING;
+ c->sleeping--;
return ((c->flags & CHN_F_DEAD) ? EINVAL : ret);
}
@@ -416,23 +413,6 @@ chn_wrfeed(struct pcm_channel *c)
chn_wakeup(c);
}
-#if 0
-static void
-chn_wrupdate(struct pcm_channel *c)
-{
-
- CHN_LOCKASSERT(c);
- KASSERT(c->direction == PCMDIR_PLAY, ("%s(): bad channel", __func__));
-
- if ((c->flags & (CHN_F_MMAP | CHN_F_VIRTUAL)) || CHN_STOPPED(c))
- return;
- chn_dmaupdate(c);
- chn_wrfeed(c);
- /* tell the driver we've updated the primary buffer */
- chn_trigger(c, PCMTRIG_EMLDMAWR);
-}
-#endif
-
static void
chn_wrintr(struct pcm_channel *c)
{
@@ -546,22 +526,6 @@ chn_rdfeed(struct pcm_channel *c)
chn_wakeup(c);
}
-#if 0
-static void
-chn_rdupdate(struct pcm_channel *c)
-{
-
- CHN_LOCKASSERT(c);
- KASSERT(c->direction == PCMDIR_REC, ("chn_rdupdate on bad channel"));
-
- if ((c->flags & (CHN_F_MMAP | CHN_F_VIRTUAL)) || CHN_STOPPED(c))
- return;
- chn_trigger(c, PCMTRIG_EMLDMARD);
- chn_dmaupdate(c);
- chn_rdfeed(c);
-}
-#endif
-
/* read interrupt routine. Must be called with interrupts blocked. */
static void
chn_rdintr(struct pcm_channel *c)
@@ -999,29 +963,38 @@ static const struct {
{ "mulaw", NULL, NULL, AFMT_MU_LAW },
{ "u8", "8", NULL, AFMT_U8 },
{ "s8", NULL, NULL, AFMT_S8 },
+ { "ac3", NULL, NULL, AFMT_AC3 },
#if BYTE_ORDER == LITTLE_ENDIAN
{ "s16le", "s16", "16", AFMT_S16_LE },
{ "s16be", NULL, NULL, AFMT_S16_BE },
-#else
- { "s16le", NULL, NULL, AFMT_S16_LE },
- { "s16be", "s16", "16", AFMT_S16_BE },
-#endif
- { "u16le", NULL, NULL, AFMT_U16_LE },
- { "u16be", NULL, NULL, AFMT_U16_BE },
- { "s24le", NULL, NULL, AFMT_S24_LE },
+ { "s24le", "s24", "24", AFMT_S24_LE },
{ "s24be", NULL, NULL, AFMT_S24_BE },
- { "u24le", NULL, NULL, AFMT_U24_LE },
- { "u24be", NULL, NULL, AFMT_U24_BE },
-#if BYTE_ORDER == LITTLE_ENDIAN
{ "s32le", "s32", "32", AFMT_S32_LE },
{ "s32be", NULL, NULL, AFMT_S32_BE },
+ { "f32le", "f32", NULL, AFMT_F32_LE },
+ { "f32be", NULL, NULL, AFMT_F32_BE },
+ { "u16le", "u16", NULL, AFMT_U16_LE },
+ { "u16be", NULL, NULL, AFMT_U16_BE },
+ { "u24le", "u24", NULL, AFMT_U24_LE },
+ { "u24be", NULL, NULL, AFMT_U24_BE },
+ { "u32le", "u32", NULL, AFMT_U32_LE },
+ { "u32be", NULL, NULL, AFMT_U32_BE },
#else
+ { "s16le", NULL, NULL, AFMT_S16_LE },
+ { "s16be", "s16", "16", AFMT_S16_BE },
+ { "s24le", NULL, NULL, AFMT_S24_LE },
+ { "s24be", "s24", "24", AFMT_S24_BE },
{ "s32le", NULL, NULL, AFMT_S32_LE },
{ "s32be", "s32", "32", AFMT_S32_BE },
-#endif
+ { "f32le", NULL, NULL, AFMT_F32_LE },
+ { "f32be", "f32", NULL, AFMT_F32_BE },
+ { "u16le", NULL, NULL, AFMT_U16_LE },
+ { "u16be", "u16", NULL, AFMT_U16_BE },
+ { "u24le", NULL, NULL, AFMT_U24_LE },
+ { "u24be", "u24", NULL, AFMT_U24_BE },
{ "u32le", NULL, NULL, AFMT_U32_LE },
- { "u32be", NULL, NULL, AFMT_U32_BE },
- { "ac3", NULL, NULL, AFMT_AC3 },
+ { "u32be", "u32", NULL, AFMT_U32_BE },
+#endif
{ NULL, NULL, NULL, 0 }
};
@@ -1157,89 +1130,171 @@ chn_reset(struct pcm_channel *c, uint32_t fmt, uint32_t spd)
return r;
}
-int
-chn_init(struct pcm_channel *c, void *devinfo, int dir, int direction)
+static struct unrhdr *
+chn_getunr(struct snddev_info *d, int type)
+{
+ switch (type) {
+ case PCMDIR_PLAY:
+ return (d->p_unr);
+ case PCMDIR_PLAY_VIRTUAL:
+ return (d->vp_unr);
+ case PCMDIR_REC:
+ return (d->r_unr);
+ case PCMDIR_REC_VIRTUAL:
+ return (d->vr_unr);
+ default:
+ __assert_unreachable();
+ }
+
+}
+
+char *
+chn_mkname(char *buf, size_t len, struct pcm_channel *c)
+{
+ const char *str;
+
+ KASSERT(buf != NULL && len != 0,
+ ("%s(): bogus buf=%p len=%zu", __func__, buf, len));
+
+ switch (c->type) {
+ case PCMDIR_PLAY:
+ str = "play";
+ break;
+ case PCMDIR_PLAY_VIRTUAL:
+ str = "virtual_play";
+ break;
+ case PCMDIR_REC:
+ str = "record";
+ break;
+ case PCMDIR_REC_VIRTUAL:
+ str = "virtual_record";
+ break;
+ default:
+ __assert_unreachable();
+ }
+
+ snprintf(buf, len, "dsp%d.%s.%d",
+ device_get_unit(c->dev), str, c->unit);
+
+ return (buf);
+}
+
+struct pcm_channel *
+chn_init(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls,
+ int dir, void *devinfo)
{
+ struct pcm_channel *c;
struct feeder_class *fc;
struct snd_dbuf *b, *bs;
- int i, ret;
+ char buf[CHN_NAMELEN];
+ int err, i, direction, *vchanrate, *vchanformat;
- chn_lockinit(c, dir);
+ PCM_BUSYASSERT(d);
+ PCM_LOCKASSERT(d);
+
+ switch (dir) {
+ case PCMDIR_PLAY:
+ d->playcount++;
+ /* FALLTHROUGH */
+ case PCMDIR_PLAY_VIRTUAL:
+ if (dir == PCMDIR_PLAY_VIRTUAL)
+ d->pvchancount++;
+ direction = PCMDIR_PLAY;
+ vchanrate = &d->pvchanrate;
+ vchanformat = &d->pvchanformat;
+ break;
+ case PCMDIR_REC:
+ d->reccount++;
+ /* FALLTHROUGH */
+ case PCMDIR_REC_VIRTUAL:
+ if (dir == PCMDIR_REC_VIRTUAL)
+ d->rvchancount++;
+ direction = PCMDIR_REC;
+ vchanrate = &d->rvchanrate;
+ vchanformat = &d->rvchanformat;
+ break;
+ default:
+ device_printf(d->dev,
+ "%s(): invalid channel direction: %d\n",
+ __func__, dir);
+ return (NULL);
+ }
+ PCM_UNLOCK(d);
b = NULL;
bs = NULL;
+
+ c = malloc(sizeof(*c), M_DEVBUF, M_WAITOK | M_ZERO);
+ c->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO);
+ chn_lockinit(c, dir);
CHN_INIT(c, children);
CHN_INIT(c, children.busy);
- c->devinfo = NULL;
- c->feeder = NULL;
+ c->direction = direction;
+ c->type = dir;
+ c->unit = alloc_unr(chn_getunr(d, c->type));
+ c->format = SND_FORMAT(AFMT_S16_LE, 2, 0);
+ c->speed = 48000;
+ c->pid = -1;
c->latency = -1;
c->timeout = 1;
-
- ret = ENOMEM;
- b = sndbuf_create(c->dev, c->name, "primary", c);
- if (b == NULL)
- goto out;
- bs = sndbuf_create(c->dev, c->name, "secondary", c);
- if (bs == NULL)
- goto out;
-
- CHN_LOCK(c);
-
- ret = EINVAL;
- fc = feeder_getclass(NULL);
- if (fc == NULL)
- goto out;
- if (chn_addfeeder(c, fc, NULL))
- goto out;
-
- /*
- * XXX - sndbuf_setup() & sndbuf_resize() expect to be called
- * with the channel unlocked because they are also called
- * from driver methods that don't know about locking
- */
- CHN_UNLOCK(c);
- sndbuf_setup(bs, NULL, 0);
- CHN_LOCK(c);
- c->bufhard = b;
- c->bufsoft = bs;
- c->flags = 0;
- c->feederflags = 0;
- c->sm = NULL;
- c->format = SND_FORMAT(AFMT_U8, 1, 0);
- c->speed = DSP_DEFAULT_SPEED;
+ strlcpy(c->comm, CHN_COMM_UNUSED, sizeof(c->comm));
+ c->parentsnddev = d;
+ c->parentchannel = parent;
+ c->dev = d->dev;
+ c->trigger = PCMTRIG_STOP;
+ strlcpy(c->name, chn_mkname(buf, sizeof(buf), c), sizeof(c->name));
c->matrix = *feeder_matrix_id_map(SND_CHN_MATRIX_1_0);
c->matrix.id = SND_CHN_MATRIX_PCMCHANNEL;
- for (i = 0; i < SND_CHN_T_MAX; i++) {
+ for (i = 0; i < SND_CHN_T_MAX; i++)
c->volume[SND_VOL_C_MASTER][i] = SND_VOL_0DB_MASTER;
- }
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;
- memset(c->muted, 0, sizeof(c->muted));
-
+ CHN_LOCK(c);
chn_vpc_reset(c, SND_VOL_C_PCM, 1);
+ CHN_UNLOCK(c);
- ret = ENODEV;
- CHN_UNLOCK(c); /* XXX - Unlock for CHANNEL_INIT() malloc() call */
- c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, direction);
- CHN_LOCK(c);
- if (c->devinfo == NULL)
- goto out;
+ fc = feeder_getclass(NULL);
+ if (fc == NULL) {
+ device_printf(d->dev, "%s(): failed to get feeder class\n",
+ __func__);
+ goto fail;
+ }
+ if (feeder_add(c, fc, NULL)) {
+ device_printf(d->dev, "%s(): failed to add feeder\n", __func__);
+ goto fail;
+ }
- ret = ENOMEM;
- if ((sndbuf_getsize(b) == 0) && ((c->flags & CHN_F_VIRTUAL) == 0))
- goto out;
+ b = sndbuf_create(c->dev, c->name, "primary", c);
+ bs = sndbuf_create(c->dev, c->name, "secondary", c);
+ if (b == NULL || bs == NULL) {
+ device_printf(d->dev, "%s(): failed to create %s buffer\n",
+ __func__, b == NULL ? "hardware" : "software");
+ goto fail;
+ }
+ c->bufhard = b;
+ c->bufsoft = bs;
- ret = 0;
- c->direction = direction;
+ c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, direction);
+ if (c->devinfo == NULL) {
+ device_printf(d->dev, "%s(): CHANNEL_INIT() failed\n", __func__);
+ goto fail;
+ }
+
+ if ((sndbuf_getsize(b) == 0) && ((c->flags & CHN_F_VIRTUAL) == 0)) {
+ device_printf(d->dev, "%s(): hardware buffer's size is 0\n",
+ __func__);
+ goto fail;
+ }
sndbuf_setfmt(b, c->format);
sndbuf_setspd(b, c->speed);
sndbuf_setfmt(bs, c->format);
sndbuf_setspd(bs, c->speed);
+ sndbuf_setup(bs, NULL, 0);
/**
* @todo Should this be moved somewhere else? The primary buffer
@@ -1248,53 +1303,80 @@ chn_init(struct pcm_channel *c, void *devinfo, int dir, int direction)
*/
if (c->direction == PCMDIR_PLAY) {
bs->sl = sndbuf_getmaxsize(bs);
- bs->shadbuf = malloc(bs->sl, M_DEVBUF, M_NOWAIT);
- if (bs->shadbuf == NULL) {
- ret = ENOMEM;
- goto out;
- }
+ bs->shadbuf = malloc(bs->sl, M_DEVBUF, M_WAITOK);
}
-out:
- CHN_UNLOCK(c);
- if (ret) {
- if (c->devinfo) {
- if (CHANNEL_FREE(c->methods, c->devinfo))
- sndbuf_free(b);
- }
- if (bs)
- sndbuf_destroy(bs);
- if (b)
- sndbuf_destroy(b);
+ if ((c->flags & CHN_F_VIRTUAL) == 0) {
CHN_LOCK(c);
- c->flags |= CHN_F_DEAD;
- chn_lockdestroy(c);
+ err = chn_reset(c, c->format, c->speed);
+ CHN_UNLOCK(c);
+ if (err != 0)
+ goto fail;
+ }
- return ret;
+ PCM_LOCK(d);
+ CHN_INSERT_SORT_ASCEND(d, c, channels.pcm);
+ if ((c->flags & CHN_F_VIRTUAL) == 0) {
+ CHN_INSERT_SORT_ASCEND(d, c, channels.pcm.primary);
+ /* Initialize the *vchanrate/vchanformat parameters. */
+ *vchanrate = sndbuf_getspd(c->bufsoft);
+ *vchanformat = sndbuf_getfmt(c->bufsoft);
}
- return 0;
+ return (c);
+
+fail:
+ chn_kill(c);
+ PCM_LOCK(d);
+
+ return (NULL);
}
void
chn_kill(struct pcm_channel *c)
{
+ struct snddev_info *d = c->parentsnddev;
struct snd_dbuf *b = c->bufhard;
struct snd_dbuf *bs = c->bufsoft;
PCM_BUSYASSERT(c->parentsnddev);
+ PCM_LOCK(d);
+ CHN_REMOVE(d, c, channels.pcm);
+ if ((c->flags & CHN_F_VIRTUAL) == 0)
+ CHN_REMOVE(d, c, channels.pcm.primary);
+
+ switch (c->type) {
+ case PCMDIR_PLAY:
+ d->playcount--;
+ break;
+ case PCMDIR_PLAY_VIRTUAL:
+ d->pvchancount--;
+ break;
+ case PCMDIR_REC:
+ d->reccount--;
+ break;
+ case PCMDIR_REC_VIRTUAL:
+ d->rvchancount--;
+ break;
+ default:
+ __assert_unreachable();
+ }
+ PCM_UNLOCK(d);
+
if (CHN_STARTED(c)) {
CHN_LOCK(c);
chn_trigger(c, PCMTRIG_ABORT);
CHN_UNLOCK(c);
}
- while (chn_removefeeder(c) == 0)
- ;
- if (CHANNEL_FREE(c->methods, c->devinfo))
+ free_unr(chn_getunr(d, c->type), c->unit);
+ feeder_remove(c);
+ if (c->devinfo && CHANNEL_FREE(c->methods, c->devinfo))
sndbuf_free(b);
- sndbuf_destroy(bs);
- sndbuf_destroy(b);
+ if (bs)
+ sndbuf_destroy(bs);
+ if (b)
+ sndbuf_destroy(b);
CHN_LOCK(c);
c->flags |= CHN_F_DEAD;
chn_lockdestroy(c);
@@ -1327,19 +1409,6 @@ chn_release(struct pcm_channel *c)
}
int
-chn_ref(struct pcm_channel *c, int ref)
-{
- PCM_BUSYASSERT(c->parentsnddev);
- CHN_LOCKASSERT(c);
- KASSERT((c->refcount + ref) >= 0,
- ("%s(): new refcount will be negative", __func__));
-
- c->refcount += ref;
-
- return (c->refcount);
-}
-
-int
chn_setvolume_multi(struct pcm_channel *c, int vc, int left, int right,
int center)
{
@@ -1895,12 +1964,6 @@ chn_resizebuf(struct pcm_channel *c, int latency,
hblksz -= hblksz % sndbuf_getalign(b);
-#if 0
- hblksz = sndbuf_getmaxsize(b) >> 1;
- hblksz -= hblksz % sndbuf_getalign(b);
- hblkcnt = 2;
-#endif
-
CHN_UNLOCK(c);
if (chn_usefrags == 0 ||
CHANNEL_SETFRAGMENTS(c->methods, c->devinfo,
@@ -1931,14 +1994,6 @@ chn_resizebuf(struct pcm_channel *c, int latency,
if (limit > CHN_2NDBUFMAXSIZE)
limit = CHN_2NDBUFMAXSIZE;
-#if 0
- while (limit > 0 && (sblksz * sblkcnt) > limit) {
- if (sblkcnt < 4)
- break;
- sblkcnt >>= 1;
- }
-#endif
-
while ((sblksz * sblkcnt) < limit)
sblkcnt <<= 1;
@@ -2052,27 +2107,23 @@ chn_setparam(struct pcm_channel *c, uint32_t format, uint32_t speed)
int
chn_setspeed(struct pcm_channel *c, uint32_t speed)
{
- uint32_t oldformat, oldspeed, format;
+ uint32_t oldformat, oldspeed;
int ret;
-#if 0
- /* XXX force 48k */
- if (c->format & AFMT_PASSTHROUGH)
- speed = AFMT_PASSTHROUGH_RATE;
-#endif
-
oldformat = c->format;
oldspeed = c->speed;
- format = oldformat;
- ret = chn_setparam(c, format, speed);
+ if (c->speed == speed)
+ return (0);
+
+ ret = chn_setparam(c, c->format, speed);
if (ret != 0) {
if (snd_verbose > 3)
device_printf(c->dev,
"%s(): Setting speed %d failed, "
"falling back to %d\n",
__func__, speed, oldspeed);
- chn_setparam(c, c->format, oldspeed);
+ chn_setparam(c, oldformat, oldspeed);
}
return (ret);
@@ -2081,7 +2132,7 @@ chn_setspeed(struct pcm_channel *c, uint32_t speed)
int
chn_setformat(struct pcm_channel *c, uint32_t format)
{
- uint32_t oldformat, oldspeed, speed;
+ uint32_t oldformat, oldspeed;
int ret;
/* XXX force stereo */
@@ -2092,9 +2143,11 @@ chn_setformat(struct pcm_channel *c, uint32_t format)
oldformat = c->format;
oldspeed = c->speed;
- speed = oldspeed;
- ret = chn_setparam(c, format, speed);
+ if (c->format == format)
+ return (0);
+
+ ret = chn_setparam(c, format, c->speed);
if (ret != 0) {
if (snd_verbose > 3)
device_printf(c->dev,
@@ -2182,7 +2235,7 @@ chn_syncstate(struct pcm_channel *c)
else
bass = ((bass & 0x7f) + ((bass >> 8) & 0x7f)) >> 1;
- f = chn_findfeeder(c, FEEDER_EQ);
+ f = feeder_find(c, FEEDER_EQ);
if (f != NULL) {
if (FEEDER_SET(f, FEEDEQ_TREBLE, treble) != 0)
device_printf(c->dev,
@@ -2222,44 +2275,46 @@ chn_trigger(struct pcm_channel *c, int go)
if (go == c->trigger)
return (0);
+ if (snd_verbose > 3) {
+ device_printf(c->dev, "%s() %s: calling go=0x%08x , "
+ "prev=0x%08x\n", __func__, c->name, go, c->trigger);
+ }
+
+ c->trigger = go;
ret = CHANNEL_TRIGGER(c->methods, c->devinfo, go);
if (ret != 0)
return (ret);
+ CHN_UNLOCK(c);
+ PCM_LOCK(d);
+ CHN_LOCK(c);
+
+ /*
+ * Do nothing if another thread set a different trigger while we had
+ * dropped the mutex.
+ */
+ if (go != c->trigger) {
+ PCM_UNLOCK(d);
+ return (0);
+ }
+
+ /*
+ * Use the SAFE variants to prevent inserting/removing an already
+ * existing/missing element.
+ */
switch (go) {
case PCMTRIG_START:
- if (snd_verbose > 3)
- device_printf(c->dev,
- "%s() %s: calling go=0x%08x , "
- "prev=0x%08x\n", __func__, c->name, go,
- c->trigger);
- if (c->trigger != PCMTRIG_START) {
- c->trigger = go;
- CHN_UNLOCK(c);
- PCM_LOCK(d);
- CHN_INSERT_HEAD(d, c, channels.pcm.busy);
- PCM_UNLOCK(d);
- CHN_LOCK(c);
- chn_syncstate(c);
- }
+ CHN_INSERT_HEAD_SAFE(d, c, channels.pcm.busy);
+ PCM_UNLOCK(d);
+ chn_syncstate(c);
break;
case PCMTRIG_STOP:
case PCMTRIG_ABORT:
- if (snd_verbose > 3)
- device_printf(c->dev,
- "%s() %s: calling go=0x%08x , "
- "prev=0x%08x\n", __func__, c->name, go,
- c->trigger);
- if (c->trigger == PCMTRIG_START) {
- c->trigger = go;
- CHN_UNLOCK(c);
- PCM_LOCK(d);
- CHN_REMOVE(d, c, channels.pcm.busy);
- PCM_UNLOCK(d);
- CHN_LOCK(c);
- }
+ CHN_REMOVE(d, c, channels.pcm.busy);
+ PCM_UNLOCK(d);
break;
default:
+ PCM_UNLOCK(d);
break;
}
@@ -2324,7 +2379,7 @@ chn_notify(struct pcm_channel *c, u_int32_t flags)
CHN_LOCKASSERT(c);
if (CHN_EMPTY(c, children))
- return (ENODEV);
+ return (0);
err = 0;