summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorariff <ariff@FreeBSD.org>2005-11-14 18:21:23 +0000
committerariff <ariff@FreeBSD.org>2005-11-14 18:21:23 +0000
commit06d7edacc70faac12e5b594a976b58aca1438543 (patch)
treec2dc552cd4b80cf69c1384321e1cfd7bab2b39b0
parent23cbfb6404aad05209d7d6d35f8b2ad124c13cbf (diff)
downloadFreeBSD-src-06d7edacc70faac12e5b594a976b58aca1438543.zip
FreeBSD-src-06d7edacc70faac12e5b594a976b58aca1438543.tar.gz
From luigi:
This one simply tries to simplify the logic to select the buffer sizes. I am not sure it is necessary but the code seems a bit more readable to me. And at least i have tried to document how the buffer sizes are computed. Thanks to luigi for deciphering one of the most cryptic part of sound driver. Submitted by: luigi Approved by: netchild (mentor)
-rw-r--r--sys/dev/sound/pcm/channel.c106
1 files changed, 71 insertions, 35 deletions
diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c
index e6a09a0..0da40db 100644
--- a/sys/dev/sound/pcm/channel.c
+++ b/sys/dev/sound/pcm/channel.c
@@ -1065,12 +1065,50 @@ chn_setformat(struct pcm_channel *c, u_int32_t fmt)
return r;
}
+/*
+ * given a bufsz value, round it to a power of 2 in the min-max range
+ * XXX only works if min and max are powers of 2
+ */
+static int
+round_bufsz(int bufsz, int min, int max)
+{
+ int tmp = min * 2;
+
+ KASSERT( min & (min-1) == 0, ("min %d must be power of 2\n", min));
+ KASSERT( max & (max-1) == 0, ("max %d must be power of 2\n", max));
+ while (tmp <= bufsz)
+ tmp <<= 1;
+ tmp >>= 1;
+ if (tmp > max)
+ tmp = max;
+ return tmp;
+}
+
+/*
+ * set the channel's blocksize both for soft and hard buffers.
+ *
+ * blksz should be a power of 2 between 2**4 and 2**16 -- it is useful
+ * that it has the same value for both bufsoft and bufhard.
+ * blksz == -1 computes values according to a target irq rate.
+ * blksz == 0 reuses previous values if available, otherwise
+ * behaves as for -1
+ *
+ * blkcnt is set by the user, between 2 and (2**17)/blksz for bufsoft,
+ * but should be a power of 2 for bufhard to simplify life to low
+ * level drivers.
+ * Note, for the rec channel a large blkcnt is ok,
+ * but for the play channel we want blksz as small as possible to keep
+ * the delay small, because routines in the write path always try to
+ * keep bufhard full.
+ *
+ * Unless we have good reason to, use the values suggested by the caller.
+ */
int
chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz)
{
struct snd_dbuf *b = c->bufhard;
struct snd_dbuf *bs = c->bufsoft;
- int irqhz, tmp, ret, maxsize, reqblksz, tmpblksz;
+ int irqhz, ret, maxsz, maxsize, reqblksz;
CHN_LOCKASSERT(c);
if (!CANCHANGE(c) || (c->flags & CHN_F_MAPPED)) {
@@ -1084,27 +1122,30 @@ chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz)
ret = 0;
DEB(printf("%s(%d, %d)\n", __func__, blkcnt, blksz));
- if (blksz == 0 || blksz == -1) {
- if (blksz == -1)
+ if (blksz == 0 || blksz == -1) { /* let the driver choose values */
+ if (blksz == -1) /* delete previous values */
c->flags &= ~CHN_F_HAS_SIZE;
- if (!(c->flags & CHN_F_HAS_SIZE)) {
- blksz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / chn_targetirqrate;
- tmp = 32;
- while (tmp <= blksz)
- tmp <<= 1;
- tmp >>= 1;
- blksz = tmp;
+ if (!(c->flags & CHN_F_HAS_SIZE)) { /* no previous value */
+ /*
+ * compute a base blksz according to the target irq
+ * rate, then round to a suitable power of 2
+ * in the range 16.. 2^17/2.
+ * Finally compute a suitable blkcnt.
+ */
+ blksz = round_bufsz( (sndbuf_getbps(bs) *
+ sndbuf_getspd(bs)) / chn_targetirqrate,
+ 16, CHN_2NDBUFMAXSIZE / 2);
blkcnt = CHN_2NDBUFMAXSIZE / blksz;
-
- RANGE(blksz, 16, CHN_2NDBUFMAXSIZE / 2);
- RANGE(blkcnt, 2, CHN_2NDBUFMAXSIZE / blksz);
- DEB(printf("%s: defaulting to (%d, %d)\n", __func__, blkcnt, blksz));
- } else {
+ } else { /* use previously defined value */
blkcnt = sndbuf_getblkcnt(bs);
blksz = sndbuf_getblksz(bs);
- DEB(printf("%s: updating (%d, %d)\n", __func__, blkcnt, blksz));
}
} else {
+ /*
+ * use supplied values if reasonable. Note that here we
+ * might have blksz which is not a power of 2 if the
+ * ioctl() to compute it allows such values.
+ */
ret = EINVAL;
if ((blksz < 16) || (blkcnt < 2) || (blkcnt * blksz > CHN_2NDBUFMAXSIZE))
goto out;
@@ -1119,25 +1160,23 @@ chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz)
reqblksz -= reqblksz % sndbuf_getbps(bs);
/* adjust for different hw format/speed */
+ /*
+ * Now compute the approx irq rate for the given (soft) blksz,
+ * reduce to the acceptable range and compute a corresponding blksz
+ * for the hard buffer. Then set the channel's blocksize and
+ * corresponding hardbuf value. The number of blocks used should
+ * be set by the device-specific routine. In fact, even the
+ * call to sndbuf_setblksz() should not be here! XXX
+ */
+
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);
- tmpblksz = (sndbuf_getbps(b) * sndbuf_getspd(b)) / irqhz;
-
- /* round down to 2^x */
- blksz = 32;
- while (blksz <= tmpblksz)
- blksz <<= 1;
- blksz >>= 1;
-
- /* round down to fit hw buffer size */
- 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)));
+ maxsz = sndbuf_getmaxsize(b);
+ if (maxsz == 0) /* virtual channels don't appear to allocate bufhard */
+ maxsz = CHN_2NDBUFMAXSIZE;
+ blksz = round_bufsz( (sndbuf_getbps(b) * sndbuf_getspd(b)) / irqhz,
+ 16, maxsz / 2);
/* Increase the size of bufsoft if before increasing bufhard. */
maxsize = sndbuf_getsize(b);
@@ -1168,9 +1207,6 @@ chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz)
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 ||
OpenPOWER on IntegriCloud