Change KASSERT() in feed_vchan16() into an explicit test and call to
panic() so that the buffer overflow just beyond this point is always caught, even when the code is not compiled with INVARIANTS. Change chn_setblocksize() buffer reallocation code to attempt to avoid the feed_vchan16() buffer overflow by attempting to always keep the bufsoft buffer at least as large as the bufhard buffer. Print a diagnositic message Danger! %s bufsoft size increasing from %d to %d after CHANNEL_SETBLOCKSIZE() if our best attempts fail. If feed_vchan16() were to be called by the interrupt handler while locks are dropped in chn_setblocksize() to increase the size bufsoft to match the size of bufhard, the panic() code in feed_vchan16() will be triggered. If the diagnostic message is printed, it is a warning that a panic is possible if the system were to see events in an "unlucky" order. Change the locking code to avoid the need for MTX_RECURSIVE mutexes. Add the MTX_DUPOK option to the channel mutexes and change the locking sequence to always lock the parent channel before its children to avoid the possibility of deadlock. Actually implement locking assertions for the channel mutexes and fix the problems found by the resulting assertion violations. Clean up the locking code in dsp_ioctl(). Allocate the channel buffers using the malloc() M_WAITOK option instead of M_NOWAIT so that buffer allocation won't fail. Drop locks across the malloc() calls. Add/modify KASSERTS() in attempt to detect problems early. Abuse layering by adding a pointer to the snd_dbuf structure that points back to the pcm_channel that owns it. This allows sndbuf_resize() to do proper locking without having to change the its API, which is used by the hardware drivers. Don't dereference a NULL pointer when setting hw.snd.maxautovchans if a hardware driver is not loaded. Noticed by Ryan Sommers <ryans at gamersimpact.com>. Tested by: Stefan Ehmann <shoesoft AT gmx.net> Tested by: matk (Mathew Kanner) Tested by: Gordon Bergling <gbergling AT 0xfce3.net>
This commit is contained in:
parent
1b8c233de3
commit
12e524a290
@ -31,13 +31,14 @@
|
||||
SND_DECLARE_FILE("$FreeBSD$");
|
||||
|
||||
struct snd_dbuf *
|
||||
sndbuf_create(device_t dev, char *drv, char *desc)
|
||||
sndbuf_create(device_t dev, char *drv, char *desc, struct pcm_channel *channel)
|
||||
{
|
||||
struct snd_dbuf *b;
|
||||
|
||||
b = malloc(sizeof(*b), M_DEVBUF, M_WAITOK | M_ZERO);
|
||||
snprintf(b->name, SNDBUF_NAMELEN, "%s:%s", drv, desc);
|
||||
b->dev = dev;
|
||||
b->channel = channel;
|
||||
|
||||
return b;
|
||||
}
|
||||
@ -113,27 +114,38 @@ sndbuf_free(struct snd_dbuf *b)
|
||||
int
|
||||
sndbuf_resize(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz)
|
||||
{
|
||||
u_int8_t *tmpbuf;
|
||||
u_int8_t *tmpbuf, *f2;
|
||||
|
||||
chn_lock(b->channel);
|
||||
if (b->maxsize == 0)
|
||||
return 0;
|
||||
goto out;
|
||||
if (blkcnt == 0)
|
||||
blkcnt = b->blkcnt;
|
||||
if (blksz == 0)
|
||||
blksz = b->blksz;
|
||||
if (blkcnt < 2 || blksz < 16 || (blkcnt * blksz > b->maxsize))
|
||||
if (blkcnt < 2 || blksz < 16 || (blkcnt * blksz > b->maxsize)) {
|
||||
chn_unlock(b->channel);
|
||||
return EINVAL;
|
||||
}
|
||||
if (blkcnt == b->blkcnt && blksz == b->blksz)
|
||||
return 0;
|
||||
goto out;
|
||||
|
||||
chn_unlock(b->channel);
|
||||
tmpbuf = malloc(blkcnt * blksz, M_DEVBUF, M_WAITOK);
|
||||
if (tmpbuf == NULL)
|
||||
return ENOMEM;
|
||||
chn_lock(b->channel);
|
||||
b->blkcnt = blkcnt;
|
||||
b->blksz = blksz;
|
||||
b->bufsize = blkcnt * blksz;
|
||||
|
||||
tmpbuf = malloc(b->bufsize, M_DEVBUF, M_NOWAIT);
|
||||
if (tmpbuf == NULL)
|
||||
return ENOMEM;
|
||||
free(b->tmpbuf, M_DEVBUF);
|
||||
f2 = b->tmpbuf;
|
||||
b->tmpbuf = tmpbuf;
|
||||
sndbuf_reset(b);
|
||||
chn_unlock(b->channel);
|
||||
free(f2, M_DEVBUF);
|
||||
return 0;
|
||||
out:
|
||||
chn_unlock(b->channel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -142,21 +154,27 @@ sndbuf_remalloc(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz)
|
||||
{
|
||||
u_int8_t *buf, *tmpbuf, *f1, *f2;
|
||||
unsigned int bufsize;
|
||||
int ret;
|
||||
|
||||
if (blkcnt < 2 || blksz < 16)
|
||||
return EINVAL;
|
||||
|
||||
bufsize = blksz * blkcnt;
|
||||
|
||||
buf = malloc(bufsize, M_DEVBUF, M_NOWAIT);
|
||||
if (buf == NULL)
|
||||
return ENOMEM;
|
||||
chn_unlock(b->channel);
|
||||
buf = malloc(bufsize, M_DEVBUF, M_WAITOK);
|
||||
if (buf == NULL) {
|
||||
ret = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
tmpbuf = malloc(bufsize, M_DEVBUF, M_NOWAIT);
|
||||
tmpbuf = malloc(bufsize, M_DEVBUF, M_WAITOK);
|
||||
if (tmpbuf == NULL) {
|
||||
free(buf, M_DEVBUF);
|
||||
return ENOMEM;
|
||||
ret = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
chn_lock(b->channel);
|
||||
|
||||
b->blkcnt = blkcnt;
|
||||
b->blksz = blksz;
|
||||
@ -167,13 +185,18 @@ sndbuf_remalloc(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz)
|
||||
b->buf = buf;
|
||||
b->tmpbuf = tmpbuf;
|
||||
|
||||
sndbuf_reset(b);
|
||||
|
||||
chn_unlock(b->channel);
|
||||
if (f1)
|
||||
free(f1, M_DEVBUF);
|
||||
if (f2)
|
||||
free(f2, M_DEVBUF);
|
||||
|
||||
sndbuf_reset(b);
|
||||
return 0;
|
||||
ret = 0;
|
||||
out:
|
||||
chn_lock(b->channel);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -53,10 +53,11 @@ struct snd_dbuf {
|
||||
bus_dma_tag_t dmatag;
|
||||
u_int32_t buf_addr;
|
||||
struct selinfo sel;
|
||||
struct pcm_channel *channel;
|
||||
char name[SNDBUF_NAMELEN];
|
||||
};
|
||||
|
||||
struct snd_dbuf *sndbuf_create(device_t dev, char *drv, char *desc);
|
||||
struct snd_dbuf *sndbuf_create(device_t dev, char *drv, char *desc, struct pcm_channel *channel);
|
||||
void sndbuf_destroy(struct snd_dbuf *b);
|
||||
|
||||
void sndbuf_dump(struct snd_dbuf *b, char *s, u_int32_t what);
|
||||
|
@ -70,9 +70,9 @@ static void
|
||||
chn_lockinit(struct pcm_channel *c, int dir)
|
||||
{
|
||||
if (dir == PCMDIR_PLAY)
|
||||
c->lock = snd_mtxcreate(c->name, "pcm play channel");
|
||||
c->lock = snd_chnmtxcreate(c->name, "pcm play channel");
|
||||
else
|
||||
c->lock = snd_mtxcreate(c->name, "pcm record channel");
|
||||
c->lock = snd_chnmtxcreate(c->name, "pcm record channel");
|
||||
}
|
||||
|
||||
static void
|
||||
@ -205,16 +205,19 @@ chn_wrfeed(struct pcm_channel *c)
|
||||
unsigned int ret, amt;
|
||||
|
||||
CHN_LOCKASSERT(c);
|
||||
DEB(
|
||||
/* DEB(
|
||||
if (c->flags & CHN_F_CLOSING) {
|
||||
sndbuf_dump(b, "b", 0x02);
|
||||
sndbuf_dump(bs, "bs", 0x02);
|
||||
})
|
||||
}) */
|
||||
|
||||
if (c->flags & CHN_F_MAPPED)
|
||||
sndbuf_acquire(bs, NULL, sndbuf_getfree(bs));
|
||||
|
||||
amt = sndbuf_getfree(b);
|
||||
KASSERT(amt <= sndbuf_getsize(bs),
|
||||
("%s(%s): amt %d > source size %d, flags 0x%x", __func__, c->name,
|
||||
amt, sndbuf_getsize(bs), c->flags));
|
||||
if (sndbuf_getready(bs) < amt)
|
||||
c->xruns++;
|
||||
|
||||
@ -746,6 +749,16 @@ chn_init(struct pcm_channel *c, void *devinfo, int dir)
|
||||
c->devinfo = NULL;
|
||||
c->feeder = NULL;
|
||||
|
||||
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)
|
||||
@ -753,21 +766,23 @@ chn_init(struct pcm_channel *c, void *devinfo, int dir)
|
||||
if (chn_addfeeder(c, fc, NULL))
|
||||
goto out;
|
||||
|
||||
ret = ENOMEM;
|
||||
b = sndbuf_create(c->dev, c->name, "primary");
|
||||
if (b == NULL)
|
||||
goto out;
|
||||
bs = sndbuf_create(c->dev, c->name, "secondary");
|
||||
if (bs == 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;
|
||||
|
||||
ret = ENODEV;
|
||||
CHN_UNLOCK(c); /* XXX - Unlock for CHANNEL_INIT() malloc() call */
|
||||
c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, dir);
|
||||
CHN_LOCK(c);
|
||||
if (c->devinfo == NULL)
|
||||
goto out;
|
||||
|
||||
@ -789,6 +804,7 @@ chn_init(struct pcm_channel *c, void *devinfo, int dir)
|
||||
|
||||
|
||||
out:
|
||||
CHN_UNLOCK(c);
|
||||
if (ret) {
|
||||
if (c->devinfo) {
|
||||
if (CHANNEL_FREE(c->methods, c->devinfo))
|
||||
@ -971,11 +987,17 @@ chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz)
|
||||
{
|
||||
struct snd_dbuf *b = c->bufhard;
|
||||
struct snd_dbuf *bs = c->bufsoft;
|
||||
int bufsz, irqhz, tmp, ret;
|
||||
int irqhz, tmp, ret, maxsize, reqblksz, tmpblksz;
|
||||
|
||||
CHN_LOCKASSERT(c);
|
||||
if (!CANCHANGE(c) || (c->flags & CHN_F_MAPPED))
|
||||
if (!CANCHANGE(c) || (c->flags & CHN_F_MAPPED)) {
|
||||
KASSERT(sndbuf_getsize(bs) == 0 ||
|
||||
sndbuf_getsize(bs) >= sndbuf_getsize(b),
|
||||
("%s(%s): bufsoft size %d < bufhard size %d", __func__,
|
||||
c->name, sndbuf_getsize(bs), sndbuf_getsize(b)));
|
||||
return EINVAL;
|
||||
}
|
||||
c->flags |= CHN_F_SETBLOCKSIZE;
|
||||
|
||||
ret = 0;
|
||||
DEB(printf("%s(%d, %d)\n", __func__, blkcnt, blksz));
|
||||
@ -1007,36 +1029,70 @@ chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz)
|
||||
c->flags |= CHN_F_HAS_SIZE;
|
||||
}
|
||||
|
||||
bufsz = blkcnt * blksz;
|
||||
|
||||
ret = sndbuf_remalloc(bs, blkcnt, blksz);
|
||||
if (ret)
|
||||
goto out;
|
||||
reqblksz = blksz;
|
||||
|
||||
/* adjust for different hw format/speed */
|
||||
irqhz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / sndbuf_getblksz(bs);
|
||||
irqhz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / blksz;
|
||||
DEB(printf("%s: soft bps %d, spd %d, irqhz == %d\n", __func__, sndbuf_getbps(bs), sndbuf_getspd(bs), irqhz));
|
||||
RANGE(irqhz, 16, 512);
|
||||
|
||||
sndbuf_setblksz(b, (sndbuf_getbps(b) * sndbuf_getspd(b)) / irqhz);
|
||||
tmpblksz = (sndbuf_getbps(b) * sndbuf_getspd(b)) / irqhz;
|
||||
|
||||
/* round down to 2^x */
|
||||
blksz = 32;
|
||||
while (blksz <= sndbuf_getblksz(b))
|
||||
while (blksz <= tmpblksz)
|
||||
blksz <<= 1;
|
||||
blksz >>= 1;
|
||||
|
||||
/* round down to fit hw buffer size */
|
||||
RANGE(blksz, 16, sndbuf_getmaxsize(b) / 2);
|
||||
if (sndbuf_getmaxsize(b) > 0)
|
||||
RANGE(blksz, 16, sndbuf_getmaxsize(b) / 2);
|
||||
else
|
||||
/* virtual channels don't appear to allocate bufhard */
|
||||
RANGE(blksz, 16, CHN_2NDBUFMAXSIZE / 2);
|
||||
DEB(printf("%s: hard blksz requested %d (maxsize %d), ", __func__, blksz, sndbuf_getmaxsize(b)));
|
||||
|
||||
/* Increase the size of bufsoft if before increasing bufhard. */
|
||||
maxsize = sndbuf_getsize(b);
|
||||
if (sndbuf_getsize(bs) > maxsize)
|
||||
maxsize = sndbuf_getsize(bs);
|
||||
if (reqblksz * blkcnt > maxsize)
|
||||
maxsize = reqblksz * blkcnt;
|
||||
if (sndbuf_getsize(bs) != maxsize || sndbuf_getblksz(bs) != reqblksz) {
|
||||
ret = sndbuf_remalloc(bs, maxsize/reqblksz, reqblksz);
|
||||
if (ret)
|
||||
goto out1;
|
||||
}
|
||||
|
||||
CHN_UNLOCK(c);
|
||||
sndbuf_setblksz(b, CHANNEL_SETBLOCKSIZE(c->methods, c->devinfo, blksz));
|
||||
CHN_LOCK(c);
|
||||
|
||||
/* Decrease the size of bufsoft after decreasing bufhard. */
|
||||
maxsize = sndbuf_getsize(b);
|
||||
if (reqblksz * blkcnt > maxsize)
|
||||
maxsize = reqblksz * blkcnt;
|
||||
if (maxsize > sndbuf_getsize(bs))
|
||||
printf("Danger! %s bufsoft size increasing from %d to %d after CHANNEL_SETBLOCKSIZE()\n",
|
||||
c->name, sndbuf_getsize(bs), maxsize);
|
||||
if (sndbuf_getsize(bs) != maxsize || sndbuf_getblksz(bs) != reqblksz) {
|
||||
ret = sndbuf_remalloc(bs, maxsize/reqblksz, reqblksz);
|
||||
if (ret)
|
||||
goto out1;
|
||||
}
|
||||
|
||||
irqhz = (sndbuf_getbps(b) * sndbuf_getspd(b)) / sndbuf_getblksz(b);
|
||||
DEB(printf("got %d, irqhz == %d\n", sndbuf_getblksz(b), irqhz));
|
||||
|
||||
chn_resetbuf(c);
|
||||
out1:
|
||||
KASSERT(sndbuf_getsize(bs) == 0 ||
|
||||
sndbuf_getsize(bs) >= sndbuf_getsize(b),
|
||||
("%s(%s): bufsoft size %d < bufhard size %d, reqblksz=%d blksz=%d maxsize=%d blkcnt=%d",
|
||||
__func__, c->name, sndbuf_getsize(bs), sndbuf_getsize(b), reqblksz,
|
||||
blksz, maxsize, blkcnt));
|
||||
out:
|
||||
c->flags &= ~CHN_F_SETBLOCKSIZE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1216,8 +1272,12 @@ chn_notify(struct pcm_channel *c, u_int32_t flags)
|
||||
struct pcm_channel *child;
|
||||
int run;
|
||||
|
||||
if (SLIST_EMPTY(&c->children))
|
||||
CHN_LOCK(c);
|
||||
|
||||
if (SLIST_EMPTY(&c->children)) {
|
||||
CHN_UNLOCK(c);
|
||||
return ENODEV;
|
||||
}
|
||||
|
||||
run = (c->flags & CHN_F_TRIGGERED)? 1 : 0;
|
||||
/*
|
||||
@ -1253,8 +1313,10 @@ chn_notify(struct pcm_channel *c, u_int32_t flags)
|
||||
blksz = sndbuf_getmaxsize(c->bufhard) / 2;
|
||||
SLIST_FOREACH(pce, &c->children, link) {
|
||||
child = pce->channel;
|
||||
CHN_LOCK(child);
|
||||
if (sndbuf_getblksz(child->bufhard) < blksz)
|
||||
blksz = sndbuf_getblksz(child->bufhard);
|
||||
CHN_UNLOCK(child);
|
||||
}
|
||||
chn_setblocksize(c, 2, blksz);
|
||||
}
|
||||
@ -1268,13 +1330,28 @@ chn_notify(struct pcm_channel *c, u_int32_t flags)
|
||||
nrun = 0;
|
||||
SLIST_FOREACH(pce, &c->children, link) {
|
||||
child = pce->channel;
|
||||
CHN_LOCK(child);
|
||||
if (child->flags & CHN_F_TRIGGERED)
|
||||
nrun = 1;
|
||||
CHN_UNLOCK(child);
|
||||
}
|
||||
if (nrun && !run)
|
||||
chn_start(c, 1);
|
||||
if (!nrun && run)
|
||||
chn_abort(c);
|
||||
}
|
||||
CHN_UNLOCK(c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
chn_lock(struct pcm_channel *c)
|
||||
{
|
||||
CHN_LOCK(c);
|
||||
}
|
||||
|
||||
void
|
||||
chn_unlock(struct pcm_channel *c)
|
||||
{
|
||||
CHN_UNLOCK(c);
|
||||
}
|
||||
|
@ -99,11 +99,13 @@ void chn_wrupdate(struct pcm_channel *c);
|
||||
void chn_rdupdate(struct pcm_channel *c);
|
||||
|
||||
int chn_notify(struct pcm_channel *c, u_int32_t flags);
|
||||
void chn_lock(struct pcm_channel *c);
|
||||
void chn_unlock(struct pcm_channel *c);
|
||||
|
||||
#ifdef USING_MUTEX
|
||||
#define CHN_LOCK(c) mtx_lock((struct mtx *)((c)->lock))
|
||||
#define CHN_UNLOCK(c) mtx_unlock((struct mtx *)((c)->lock))
|
||||
#define CHN_LOCKASSERT(c)
|
||||
#define CHN_LOCKASSERT(c) mtx_assert((struct mtx *)((c)->lock), MA_OWNED)
|
||||
#else
|
||||
#define CHN_LOCK(c)
|
||||
#define CHN_UNLOCK(c)
|
||||
@ -134,6 +136,7 @@ int fmtvalid(u_int32_t fmt, u_int32_t *fmtlist);
|
||||
#define CHN_F_MAPPED 0x00010000 /* has been mmap()ed */
|
||||
#define CHN_F_DEAD 0x00020000
|
||||
#define CHN_F_BADSETTING 0x00040000
|
||||
#define CHN_F_SETBLOCKSIZE 0x00080000
|
||||
|
||||
#define CHN_F_VIRTUAL 0x10000000 /* not backed by hardware */
|
||||
|
||||
|
@ -113,8 +113,8 @@ getchns(dev_t dev, struct pcm_channel **rdch, struct pcm_channel **wrch, u_int32
|
||||
|
||||
flags = dsp_get_flags(dev);
|
||||
d = dsp_get_info(dev);
|
||||
pcm_lock(d);
|
||||
pcm_inprog(d, 1);
|
||||
pcm_lock(d);
|
||||
KASSERT((flags & SD_F_PRIO_SET) != SD_F_PRIO_SET, \
|
||||
("getchns: read and write both prioritised"));
|
||||
|
||||
@ -159,9 +159,7 @@ relchns(dev_t dev, struct pcm_channel *rdch, struct pcm_channel *wrch, u_int32_t
|
||||
CHN_UNLOCK(wrch);
|
||||
if (rdch && rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD))
|
||||
CHN_UNLOCK(rdch);
|
||||
pcm_lock(d);
|
||||
pcm_inprog(d, -1);
|
||||
pcm_unlock(d);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -476,6 +474,11 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
|
||||
wrch = NULL;
|
||||
if (kill & 2)
|
||||
rdch = NULL;
|
||||
|
||||
if (rdch != NULL)
|
||||
CHN_LOCK(rdch);
|
||||
if (wrch != NULL)
|
||||
CHN_LOCK(wrch);
|
||||
|
||||
switch(cmd) {
|
||||
#ifdef OLDPCM_IOCTL
|
||||
@ -497,16 +500,12 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
|
||||
p->play_size = 0;
|
||||
p->rec_size = 0;
|
||||
if (wrch) {
|
||||
CHN_LOCK(wrch);
|
||||
chn_setblocksize(wrch, 2, p->play_size);
|
||||
p->play_size = sndbuf_getblksz(wrch->bufsoft);
|
||||
CHN_UNLOCK(wrch);
|
||||
}
|
||||
if (rdch) {
|
||||
CHN_LOCK(rdch);
|
||||
chn_setblocksize(rdch, 2, p->rec_size);
|
||||
p->rec_size = sndbuf_getblksz(rdch->bufsoft);
|
||||
CHN_UNLOCK(rdch);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -526,16 +525,12 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
|
||||
snd_chan_param *p = (snd_chan_param *)arg;
|
||||
|
||||
if (wrch) {
|
||||
CHN_LOCK(wrch);
|
||||
chn_setformat(wrch, p->play_format);
|
||||
chn_setspeed(wrch, p->play_rate);
|
||||
CHN_UNLOCK(wrch);
|
||||
}
|
||||
if (rdch) {
|
||||
CHN_LOCK(rdch);
|
||||
chn_setformat(rdch, p->rec_format);
|
||||
chn_setspeed(rdch, p->rec_rate);
|
||||
CHN_UNLOCK(rdch);
|
||||
}
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
@ -557,14 +552,10 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
|
||||
struct pcmchan_caps *pcaps = NULL, *rcaps = NULL;
|
||||
dev_t pdev;
|
||||
|
||||
if (rdch) {
|
||||
CHN_LOCK(rdch);
|
||||
if (rdch)
|
||||
rcaps = chn_getcaps(rdch);
|
||||
}
|
||||
if (wrch) {
|
||||
CHN_LOCK(wrch);
|
||||
if (wrch)
|
||||
pcaps = chn_getcaps(wrch);
|
||||
}
|
||||
p->rate_min = max(rcaps? rcaps->minspeed : 0,
|
||||
pcaps? pcaps->minspeed : 0);
|
||||
p->rate_max = min(rcaps? rcaps->maxspeed : 1000000,
|
||||
@ -580,10 +571,6 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
|
||||
p->mixers = 1; /* default: one mixer */
|
||||
p->inputs = pdev->si_drv1? mix_getdevs(pdev->si_drv1) : 0;
|
||||
p->left = p->right = 100;
|
||||
if (wrch)
|
||||
CHN_UNLOCK(wrch);
|
||||
if (rdch)
|
||||
CHN_UNLOCK(rdch);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -646,59 +633,42 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
|
||||
|
||||
case SNDCTL_DSP_SETBLKSIZE:
|
||||
RANGE(*arg_i, 16, 65536);
|
||||
if (wrch) {
|
||||
CHN_LOCK(wrch);
|
||||
if (wrch)
|
||||
chn_setblocksize(wrch, 2, *arg_i);
|
||||
CHN_UNLOCK(wrch);
|
||||
}
|
||||
if (rdch) {
|
||||
CHN_LOCK(rdch);
|
||||
if (rdch)
|
||||
chn_setblocksize(rdch, 2, *arg_i);
|
||||
CHN_UNLOCK(rdch);
|
||||
}
|
||||
break;
|
||||
|
||||
case SNDCTL_DSP_RESET:
|
||||
DEB(printf("dsp reset\n"));
|
||||
if (wrch) {
|
||||
CHN_LOCK(wrch);
|
||||
chn_abort(wrch);
|
||||
chn_resetbuf(wrch);
|
||||
CHN_UNLOCK(wrch);
|
||||
}
|
||||
if (rdch) {
|
||||
CHN_LOCK(rdch);
|
||||
chn_abort(rdch);
|
||||
chn_resetbuf(rdch);
|
||||
CHN_UNLOCK(rdch);
|
||||
}
|
||||
break;
|
||||
|
||||
case SNDCTL_DSP_SYNC:
|
||||
DEB(printf("dsp sync\n"));
|
||||
/* chn_sync may sleep */
|
||||
if (wrch) {
|
||||
CHN_LOCK(wrch);
|
||||
if (wrch)
|
||||
chn_sync(wrch, sndbuf_getsize(wrch->bufsoft) - 4);
|
||||
CHN_UNLOCK(wrch);
|
||||
}
|
||||
break;
|
||||
|
||||
case SNDCTL_DSP_SPEED:
|
||||
/* chn_setspeed may sleep */
|
||||
tmp = 0;
|
||||
if (wrch) {
|
||||
CHN_LOCK(wrch);
|
||||
ret = chn_setspeed(wrch, *arg_i);
|
||||
tmp = wrch->speed;
|
||||
CHN_UNLOCK(wrch);
|
||||
}
|
||||
if (rdch && ret == 0) {
|
||||
CHN_LOCK(rdch);
|
||||
ret = chn_setspeed(rdch, *arg_i);
|
||||
if (tmp == 0)
|
||||
tmp = rdch->speed;
|
||||
CHN_UNLOCK(rdch);
|
||||
}
|
||||
*arg_i = tmp;
|
||||
break;
|
||||
@ -711,17 +681,13 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
|
||||
tmp = -1;
|
||||
*arg_i = (*arg_i)? AFMT_STEREO : 0;
|
||||
if (wrch) {
|
||||
CHN_LOCK(wrch);
|
||||
ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
|
||||
tmp = (wrch->format & AFMT_STEREO)? 1 : 0;
|
||||
CHN_UNLOCK(wrch);
|
||||
}
|
||||
if (rdch && ret == 0) {
|
||||
CHN_LOCK(rdch);
|
||||
ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
|
||||
if (tmp == -1)
|
||||
tmp = (rdch->format & AFMT_STEREO)? 1 : 0;
|
||||
CHN_UNLOCK(rdch);
|
||||
}
|
||||
*arg_i = tmp;
|
||||
break;
|
||||
@ -732,22 +698,17 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
|
||||
tmp = 0;
|
||||
*arg_i = (*arg_i != 1)? AFMT_STEREO : 0;
|
||||
if (wrch) {
|
||||
CHN_LOCK(wrch);
|
||||
ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
|
||||
tmp = (wrch->format & AFMT_STEREO)? 2 : 1;
|
||||
CHN_UNLOCK(wrch);
|
||||
}
|
||||
if (rdch && ret == 0) {
|
||||
CHN_LOCK(rdch);
|
||||
ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
|
||||
if (tmp == 0)
|
||||
tmp = (rdch->format & AFMT_STEREO)? 2 : 1;
|
||||
CHN_UNLOCK(rdch);
|
||||
}
|
||||
*arg_i = tmp;
|
||||
} else {
|
||||
} else
|
||||
*arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 2 : 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case SOUND_PCM_READ_CHANNELS:
|
||||
@ -763,17 +724,13 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
|
||||
if ((*arg_i != AFMT_QUERY)) {
|
||||
tmp = 0;
|
||||
if (wrch) {
|
||||
CHN_LOCK(wrch);
|
||||
ret = chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO));
|
||||
tmp = wrch->format & ~AFMT_STEREO;
|
||||
CHN_UNLOCK(wrch);
|
||||
}
|
||||
if (rdch && ret == 0) {
|
||||
CHN_LOCK(rdch);
|
||||
ret = chn_setformat(rdch, (*arg_i) | (rdch->format & AFMT_STEREO));
|
||||
if (tmp == 0)
|
||||
tmp = rdch->format & ~AFMT_STEREO;
|
||||
CHN_UNLOCK(rdch);
|
||||
}
|
||||
*arg_i = tmp;
|
||||
} else
|
||||
@ -800,18 +757,14 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
|
||||
|
||||
DEB(printf("SNDCTL_DSP_SETFRAGMENT %d frags, %d sz\n", maxfrags, fragsz));
|
||||
if (rdch) {
|
||||
CHN_LOCK(rdch);
|
||||
ret = chn_setblocksize(rdch, maxfrags, fragsz);
|
||||
maxfrags = sndbuf_getblkcnt(rdch->bufsoft);
|
||||
fragsz = sndbuf_getblksz(rdch->bufsoft);
|
||||
CHN_UNLOCK(rdch);
|
||||
}
|
||||
if (wrch && ret == 0) {
|
||||
CHN_LOCK(wrch);
|
||||
ret = chn_setblocksize(wrch, maxfrags, fragsz);
|
||||
maxfrags = sndbuf_getblkcnt(wrch->bufsoft);
|
||||
fragsz = sndbuf_getblksz(wrch->bufsoft);
|
||||
CHN_UNLOCK(wrch);
|
||||
}
|
||||
|
||||
fragln = 0;
|
||||
@ -830,12 +783,10 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
|
||||
if (rdch) {
|
||||
struct snd_dbuf *bs = rdch->bufsoft;
|
||||
|
||||
CHN_LOCK(rdch);
|
||||
a->bytes = sndbuf_getready(bs);
|
||||
a->fragments = a->bytes / sndbuf_getblksz(bs);
|
||||
a->fragstotal = sndbuf_getblkcnt(bs);
|
||||
a->fragsize = sndbuf_getblksz(bs);
|
||||
CHN_UNLOCK(rdch);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -847,13 +798,11 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
|
||||
if (wrch) {
|
||||
struct snd_dbuf *bs = wrch->bufsoft;
|
||||
|
||||
CHN_LOCK(wrch);
|
||||
chn_wrupdate(wrch);
|
||||
a->bytes = sndbuf_getfree(bs);
|
||||
a->fragments = a->bytes / sndbuf_getblksz(bs);
|
||||
a->fragstotal = sndbuf_getblkcnt(bs);
|
||||
a->fragsize = sndbuf_getblksz(bs);
|
||||
CHN_UNLOCK(wrch);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -864,13 +813,11 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
|
||||
if (rdch) {
|
||||
struct snd_dbuf *bs = rdch->bufsoft;
|
||||
|
||||
CHN_LOCK(rdch);
|
||||
chn_rdupdate(rdch);
|
||||
a->bytes = sndbuf_gettotal(bs);
|
||||
a->blocks = sndbuf_getblocks(bs) - rdch->blocks;
|
||||
a->ptr = sndbuf_getreadyptr(bs);
|
||||
rdch->blocks = sndbuf_getblocks(bs);
|
||||
CHN_UNLOCK(rdch);
|
||||
} else
|
||||
ret = EINVAL;
|
||||
}
|
||||
@ -882,13 +829,11 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
|
||||
if (wrch) {
|
||||
struct snd_dbuf *bs = wrch->bufsoft;
|
||||
|
||||
CHN_LOCK(wrch);
|
||||
chn_wrupdate(wrch);
|
||||
a->bytes = sndbuf_gettotal(bs);
|
||||
a->blocks = sndbuf_getblocks(bs) - wrch->blocks;
|
||||
a->ptr = sndbuf_getreadyptr(bs);
|
||||
wrch->blocks = sndbuf_getblocks(bs);
|
||||
CHN_UNLOCK(wrch);
|
||||
} else
|
||||
ret = EINVAL;
|
||||
}
|
||||
@ -906,22 +851,18 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
|
||||
|
||||
case SNDCTL_DSP_SETTRIGGER:
|
||||
if (rdch) {
|
||||
CHN_LOCK(rdch);
|
||||
rdch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
|
||||
if (*arg_i & PCM_ENABLE_INPUT)
|
||||
chn_start(rdch, 1);
|
||||
else
|
||||
rdch->flags |= CHN_F_NOTRIGGER;
|
||||
CHN_UNLOCK(rdch);
|
||||
}
|
||||
if (wrch) {
|
||||
CHN_LOCK(wrch);
|
||||
wrch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
|
||||
if (*arg_i & PCM_ENABLE_OUTPUT)
|
||||
chn_start(wrch, 1);
|
||||
else
|
||||
wrch->flags |= CHN_F_NOTRIGGER;
|
||||
CHN_UNLOCK(wrch);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -938,20 +879,16 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
|
||||
struct snd_dbuf *b = wrch->bufhard;
|
||||
struct snd_dbuf *bs = wrch->bufsoft;
|
||||
|
||||
CHN_LOCK(wrch);
|
||||
chn_wrupdate(wrch);
|
||||
*arg_i = sndbuf_getready(b) + sndbuf_getready(bs);
|
||||
CHN_UNLOCK(wrch);
|
||||
} else
|
||||
ret = EINVAL;
|
||||
break;
|
||||
|
||||
case SNDCTL_DSP_POST:
|
||||
if (wrch) {
|
||||
CHN_LOCK(wrch);
|
||||
wrch->flags &= ~CHN_F_NOTRIGGER;
|
||||
chn_start(wrch, 1);
|
||||
CHN_UNLOCK(wrch);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -969,6 +906,10 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
|
||||
ret = EINVAL;
|
||||
break;
|
||||
}
|
||||
if (rdch != NULL)
|
||||
CHN_UNLOCK(rdch);
|
||||
if (wrch != NULL)
|
||||
CHN_UNLOCK(wrch);
|
||||
relchns(i_dev, rdch, wrch, 0);
|
||||
splx(s);
|
||||
return ret;
|
||||
|
@ -75,7 +75,23 @@ snd_mtxcreate(const char *desc, const char *type)
|
||||
m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
|
||||
if (m == NULL)
|
||||
return NULL;
|
||||
mtx_init(m, desc, type, MTX_DEF | MTX_RECURSE);
|
||||
mtx_init(m, desc, type, MTX_DEF);
|
||||
return m;
|
||||
#else
|
||||
return (void *)0xcafebabe;
|
||||
#endif
|
||||
}
|
||||
|
||||
void *
|
||||
snd_chnmtxcreate(const char *desc, const char *type)
|
||||
{
|
||||
#ifdef USING_MUTEX
|
||||
struct mtx *m;
|
||||
|
||||
m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
|
||||
if (m == NULL)
|
||||
return NULL;
|
||||
mtx_init(m, desc, type, MTX_DEF | MTX_DUPOK);
|
||||
return m;
|
||||
#else
|
||||
return (void *)0xcafebabe;
|
||||
@ -188,13 +204,16 @@ pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum)
|
||||
/* try to create a vchan */
|
||||
SLIST_FOREACH(sce, &d->channels, link) {
|
||||
c = sce->channel;
|
||||
CHN_LOCK(c);
|
||||
if (!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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -250,15 +269,19 @@ pcm_setmaxautovchans(struct snddev_info *d, int num)
|
||||
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)) {
|
||||
c->flags |= CHN_F_BUSY;
|
||||
err = vchan_create(c);
|
||||
if (err) {
|
||||
device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
|
||||
c->flags &= ~CHN_F_BUSY;
|
||||
}
|
||||
CHN_UNLOCK(c);
|
||||
device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
|
||||
} else
|
||||
CHN_UNLOCK(c);
|
||||
return;
|
||||
}
|
||||
CHN_UNLOCK(c);
|
||||
}
|
||||
}
|
||||
if (num == 0 && d->vchancount > 0) {
|
||||
@ -313,7 +336,7 @@ 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)
|
||||
if (v < 0 || v >= SND_MAXVCHANS || pcm_devclass == NULL)
|
||||
return EINVAL;
|
||||
if (v != snd_maxautovchans) {
|
||||
for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
|
||||
@ -529,20 +552,23 @@ pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
|
||||
err = pcm_chn_add(d, ch);
|
||||
if (err) {
|
||||
device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
|
||||
snd_mtxunlock(d->lock);
|
||||
pcm_chn_destroy(ch);
|
||||
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) {
|
||||
device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, 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;
|
||||
}
|
||||
@ -866,11 +892,13 @@ sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
|
||||
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 (c->flags & CHN_F_BUSY)
|
||||
busy++;
|
||||
}
|
||||
CHN_UNLOCK(c);
|
||||
}
|
||||
|
||||
newcnt = cnt;
|
||||
@ -888,23 +916,28 @@ sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
|
||||
/* 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)
|
||||
continue;
|
||||
goto next;
|
||||
/* not a candidate if a virtual channel */
|
||||
if (c->flags & CHN_F_VIRTUAL)
|
||||
continue;
|
||||
goto next;
|
||||
/* not a candidate if it's in use */
|
||||
if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children)))
|
||||
continue;
|
||||
/*
|
||||
* 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;
|
||||
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;
|
||||
@ -917,6 +950,7 @@ sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
|
||||
}
|
||||
if (SLIST_EMPTY(&c->children))
|
||||
c->flags &= ~CHN_F_BUSY;
|
||||
CHN_UNLOCK(c);
|
||||
} else if (newcnt < cnt) {
|
||||
if (busy > newcnt) {
|
||||
printf("cnt %d, newcnt %d, busy %d\n", cnt, newcnt, busy);
|
||||
@ -928,13 +962,17 @@ sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
|
||||
while (err == 0 && newcnt < cnt) {
|
||||
SLIST_FOREACH(sce, &d->channels, link) {
|
||||
c = sce->channel;
|
||||
CHN_LOCK(c);
|
||||
if ((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--;
|
||||
|
@ -238,6 +238,7 @@ int snd_setup_intr(device_t dev, struct resource *res, int flags,
|
||||
driver_intr_t hand, void *param, void **cookiep);
|
||||
|
||||
void *snd_mtxcreate(const char *desc, const char *type);
|
||||
void *snd_chnmtxcreate(const char *desc, const char *type);
|
||||
void snd_mtxfree(void *m);
|
||||
void snd_mtxassert(void *m);
|
||||
#define snd_mtxlock(m) mtx_lock(m)
|
||||
|
@ -77,7 +77,9 @@ feed_vchan_s16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32
|
||||
int16_t *tmp, *dst;
|
||||
unsigned int cnt;
|
||||
|
||||
KASSERT(sndbuf_getsize(src) >= count, ("bad bufsize"));
|
||||
if (sndbuf_getsize(src) < count)
|
||||
panic("feed_vchan_s16(%s): tmp buffer size %d < count %d, flags = 0x%x",
|
||||
c->name, sndbuf_getsize(src), count, c->flags);
|
||||
count &= ~1;
|
||||
bzero(b, count);
|
||||
|
||||
@ -92,12 +94,14 @@ feed_vchan_s16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32
|
||||
bzero(tmp, count);
|
||||
SLIST_FOREACH(cce, &c->children, link) {
|
||||
ch = cce->channel;
|
||||
CHN_LOCK(ch);
|
||||
if (ch->flags & CHN_F_TRIGGERED) {
|
||||
if (ch->flags & CHN_F_MAPPED)
|
||||
sndbuf_acquire(ch->bufsoft, NULL, sndbuf_getfree(ch->bufsoft));
|
||||
cnt = FEEDER_FEED(ch->feeder, ch, (u_int8_t *)tmp, count, ch->bufsoft);
|
||||
vchan_mix_s16(dst, tmp, cnt / 2);
|
||||
}
|
||||
CHN_UNLOCK(ch);
|
||||
}
|
||||
|
||||
return count;
|
||||
@ -145,13 +149,16 @@ vchan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
{
|
||||
struct vchinfo *ch = data;
|
||||
struct pcm_channel *parent = ch->parent;
|
||||
struct pcm_channel *channel = ch->channel;
|
||||
|
||||
ch->fmt = format;
|
||||
ch->bps = 1;
|
||||
ch->bps <<= (ch->fmt & AFMT_STEREO)? 1 : 0;
|
||||
ch->bps <<= (ch->fmt & AFMT_16BIT)? 1 : 0;
|
||||
ch->bps <<= (ch->fmt & AFMT_32BIT)? 2 : 0;
|
||||
CHN_UNLOCK(channel);
|
||||
chn_notify(parent, CHN_N_FORMAT);
|
||||
CHN_LOCK(channel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -160,9 +167,12 @@ vchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
{
|
||||
struct vchinfo *ch = data;
|
||||
struct pcm_channel *parent = ch->parent;
|
||||
struct pcm_channel *channel = ch->channel;
|
||||
|
||||
ch->spd = speed;
|
||||
CHN_UNLOCK(channel);
|
||||
chn_notify(parent, CHN_N_RATE);
|
||||
CHN_LOCK(channel);
|
||||
return speed;
|
||||
}
|
||||
|
||||
@ -171,14 +181,19 @@ vchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
{
|
||||
struct vchinfo *ch = data;
|
||||
struct pcm_channel *parent = ch->parent;
|
||||
/* struct pcm_channel *channel = ch->channel; */
|
||||
int prate, crate;
|
||||
|
||||
ch->blksz = blocksize;
|
||||
/* CHN_UNLOCK(channel); */
|
||||
chn_notify(parent, CHN_N_BLOCKSIZE);
|
||||
CHN_LOCK(parent);
|
||||
/* CHN_LOCK(channel); */
|
||||
|
||||
crate = ch->spd * ch->bps;
|
||||
prate = sndbuf_getspd(parent->bufhard) * sndbuf_getbps(parent->bufhard);
|
||||
blocksize = sndbuf_getblksz(parent->bufhard);
|
||||
CHN_UNLOCK(parent);
|
||||
blocksize *= prate;
|
||||
blocksize /= crate;
|
||||
|
||||
@ -190,12 +205,15 @@ vchan_trigger(kobj_t obj, void *data, int go)
|
||||
{
|
||||
struct vchinfo *ch = data;
|
||||
struct pcm_channel *parent = ch->parent;
|
||||
struct pcm_channel *channel = ch->channel;
|
||||
|
||||
if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)
|
||||
return 0;
|
||||
|
||||
ch->run = (go == PCMTRIG_START)? 1 : 0;
|
||||
CHN_UNLOCK(channel);
|
||||
chn_notify(parent, CHN_N_TRIGGER);
|
||||
CHN_LOCK(channel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -235,8 +253,11 @@ vchan_create(struct pcm_channel *parent)
|
||||
struct pcm_channel *child;
|
||||
int err, first;
|
||||
|
||||
CHN_UNLOCK(parent);
|
||||
|
||||
pce = malloc(sizeof(*pce), M_DEVBUF, M_WAITOK | M_ZERO);
|
||||
if (!pce) {
|
||||
CHN_LOCK(parent);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
@ -244,14 +265,13 @@ vchan_create(struct pcm_channel *parent)
|
||||
child = pcm_chn_create(d, parent, &vchan_class, PCMDIR_VIRTUAL, parent);
|
||||
if (!child) {
|
||||
free(pce, M_DEVBUF);
|
||||
CHN_LOCK(parent);
|
||||
return ENODEV;
|
||||
}
|
||||
|
||||
CHN_LOCK(parent);
|
||||
if (!(parent->flags & CHN_F_BUSY)) {
|
||||
CHN_UNLOCK(parent);
|
||||
if (!(parent->flags & CHN_F_BUSY))
|
||||
return EBUSY;
|
||||
}
|
||||
|
||||
first = SLIST_EMPTY(&parent->children);
|
||||
/* add us to our parent channel's children */
|
||||
@ -269,6 +289,7 @@ vchan_create(struct pcm_channel *parent)
|
||||
free(pce, M_DEVBUF);
|
||||
}
|
||||
|
||||
CHN_LOCK(parent);
|
||||
/* XXX gross ugly hack, murder death kill */
|
||||
if (first && !err) {
|
||||
err = chn_reset(parent, AFMT_STEREO | AFMT_S16_LE);
|
||||
|
Loading…
Reference in New Issue
Block a user