From owner-freebsd-bugs Thu Nov 18 18:30:24 1999 Delivered-To: freebsd-bugs@freebsd.org Received: from freefall.freebsd.org (freefall.FreeBSD.ORG [204.216.27.21]) by hub.freebsd.org (Postfix) with ESMTP id EBEA8151D2 for ; Thu, 18 Nov 1999 18:30:00 -0800 (PST) (envelope-from gnats@FreeBSD.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.9.3/8.9.2) id SAA49041; Thu, 18 Nov 1999 18:30:00 -0800 (PST) (envelope-from gnats@FreeBSD.org) Received: from smtp6.mindspring.com (smtp6.mindspring.com [207.69.200.110]) by hub.freebsd.org (Postfix) with ESMTP id 6BE0C15544 for ; Thu, 18 Nov 1999 18:20:04 -0800 (PST) (envelope-from vns@mindspring.com) Received: from mindspring.com (user-2ivebnb.dialup.mindspring.com [165.247.46.235]) by smtp6.mindspring.com (8.8.5/8.8.5) with ESMTP id VAA28340 for ; Thu, 18 Nov 1999 21:19:56 -0500 (EST) Received: (from vsilyaev@localhost) by mindspring.com (8.9.3/8.9.3) id VAA00758; Thu, 18 Nov 1999 21:19:55 -0500 (EST) (envelope-from vns) Message-Id: <199911190219.VAA00758@mindspring.com> Date: Thu, 18 Nov 1999 21:19:55 -0500 (EST) From: vns@delta.odessa.ua Reply-To: vns@delta.odessa.ua To: FreeBSD-gnats-submit@freebsd.org X-Send-Pr-Version: 3.2 Subject: kern/14990: new-pcm caveats Sender: owner-freebsd-bugs@FreeBSD.ORG Precedence: bulk X-Loop: FreeBSD.org >Number: 14990 >Category: kern >Synopsis: new-pcm caveats >Confidential: no >Severity: non-critical >Priority: medium >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Thu Nov 18 18:30:00 PST 1999 >Closed-Date: >Last-Modified: >Originator: Vladimir N.Silyaev >Release: FreeBSD 4.0-CURRENT i386 >Organization: >Environment: FreeBSD 4.0-CURRENT i386 Crystal Sound sound card pcm0: at port 0x534-0x537,0x388-0x38b,0x220-0x22f irq 7 drq 1,0 on isa0 mpg123 Version 0.59q (1999/Jan/26) software audio MPEG decoder. >Description: Some times, after changing tracks, the sound card produce white noise instead of audio clip. >How-To-Repeat: Start mpg123 program, then press ^Z key (pause the program) and then execute bg command. After some tries sound card begin to produce white noise. >Fix: This error, as say my own investigation, don't depend from soundcard type, but caused some problem, which occured when underflow condition occured in the DMA buffer. The current implementation don't work properly with this situation at all. So next patch try to solve most of the problem occured with play back in the such case of situations. After patch follow simple program, which was used for easy testing sound card driver in wide spectrum of formats and frequencies. Index: channel.c =================================================================== RCS file: /home/vns/cvs/FreeBSD//src/sys/dev/pcm/channel.c,v retrieving revision 1.5 diff -u -r1.5 channel.c --- channel.c 1999/11/15 23:57:33 1.5 +++ channel.c 1999/11/19 00:25:20 @@ -37,6 +37,7 @@ #define CANCHANGE(c) (!(c)->buffer.dl) static void chn_stintr(pcm_channel *c); +static void chn_clearbuf(pcm_channel *c, int length); /* * SOUND OUTPUT @@ -103,7 +104,7 @@ { if (ISA_DMA(&c->buffer)) { /* tell isa_dma to bounce data in/out */ - } else panic("chn_isadmabounce called on invalid channel"); + } else KASSERT(1, ("chn_isadmabounce called on invalid channel")); } static int @@ -153,27 +154,53 @@ * NOTE: when we are using auto dma in the device, rl might become * negative. */ +DEB (static int chn_updatecount=0); + void chn_dmaupdate(pcm_channel *c) { snd_dbuf *b = &c->buffer; - int delta, hwptr = chn_getptr(c); + int delta, hwptr; + DEB (int b_rl=b->rl; int b_fl=b->fl; int b_rp=b->rp; int b_fp=b->fp); + hwptr = chn_getptr(c); if (c->direction == PCMDIR_PLAY) { delta = (b->bufsize + hwptr - b->rp) % b->bufsize; b->rp = hwptr; b->rl -= delta; b->fl += delta; + DEB(if (b->rl<0) printf("OUCH!(%d) rl %d(%d) delta %d bufsize %d hwptr %d rp %d(%d)\n", chn_updatecount++, b->rl, b_rl, delta, b->bufsize, hwptr, b->rp, b_rp)); } else { delta = (b->bufsize + hwptr - b->fp) % b->bufsize; b->fp = hwptr; b->rl += delta; b->fl -= delta; + DEB(if (b->fl<0) printf("OUCH!(%d) fl %d(%d) delta %d bufsize %d hwptr %d fp %d(%d)\n", chn_updatecount++, b->fl, b_fl, delta, b->bufsize, hwptr, b->fp, b_fp)); } b->total += delta; } /* + * Check channel for underflow occured, reset DMA buffer in case of + * underflow. It must be called at spltty(). + */ +static void +chn_checkunderflow(pcm_channel *c) +{ + snd_dbuf *b = &c->buffer; + + if (b->underflow) { + DEB(printf("Clear underflow condition\n")); + b->rp = b->fp = chn_getptr(c); + b->rl = 0; + b->fl = b->bufsize; + b->underflow=0; + } else { + chn_dmaupdate(c); + } +} + +/* * Write interrupt routine. Can be called from other places (e.g. * to start a paused transfer), but with interrupts disabled. */ @@ -183,7 +210,8 @@ snd_dbuf *b = &c->buffer; int start; - if (b->dl) chn_dmadone(c); + if (b->underflow) return; /* nothing new happened */ + if (b->dl) chn_dmadone(c); /* * start another dma operation only if have ready data in the buffer, @@ -198,40 +226,31 @@ if (start) { int l; chn_dmaupdate(c); - l = min(b->rl, c->blocksize) & DMA_ALIGN_MASK; if (c->flags & CHN_F_MAPPED) l = c->blocksize; + else l = min(b->rl, c->blocksize) & DMA_ALIGN_MASK; /* * check if we need to reprogram the DMA on the sound card. - * This happens if the size has changed _and_ the new size - * is smaller, or it matches the blocksize. + * This happens if the size has changed from zero * - * 0 <= l <= blocksize - * 0 <= dl <= blocksize - * reprog if (dl == 0 || l != dl) - * was: - * l != b->dl && (b->dl == 0 || l < b->dl || l == c->blocksize) */ - if (b->dl == 0 || l != b->dl) { - /* size has changed. Stop and restart */ - DEB(printf("wrintr: bsz %d -> %d, rp %d rl %d\n", - b->dl, l, b->rp, b->rl)); - if (b->dl) chn_trigger(c, PCMTRIG_STOP); - b->dl = l; /* record new transfer size */ + if (b->dl == 0) { + /* Start DMA operation */ + b->dl = c->blocksize ; /* record new transfer size */ chn_trigger(c, PCMTRIG_START); + } else if (b->dl != l) { + /* + * we are near to underflow condition, so to prevent + * audio 'clicks' clear next 1.5*dl bytes + */ + chn_clearbuf(c, (b->dl*3)/2); } } else { /* cannot start a new dma transfer */ DEB(printf("cannot start wr-dma flags 0x%08x rp %d rl %d\n", c->flags, b->rp, b->rl)); - if (b->dl) { /* was active */ - b->dl = 0; - chn_trigger(c, PCMTRIG_STOP); -#if 0 - if (c->flags & CHN_F_WRITING) - DEB(printf("got wrint while reloading\n")); - else if (b->rl <= 0) /* XXX added 980110 lr */ - chn_resetbuf(c); -#endif + if (b->dl) { /* DMA was active */ + b->underflow = 1; /* set underflow flag */ + chn_clearbuf(c, b->bufsize); /* and clear all DMA buffer */ } } } @@ -255,9 +274,10 @@ int chn_write(pcm_channel *c, struct uio *buf) { - int a, l, w, timeout, ret = 0; + int a, l, w, timeout, ret = 0, rc; long s; snd_dbuf *b = &c->buffer; + int threshold, maxthreshold, minthreshold; if (c->flags & CHN_F_WRITING) { /* This shouldn't happen and is actually silly @@ -267,35 +287,47 @@ return EBUSY; } a = (1 << c->align) - 1; + maxthreshold=(b->dl/4+a)&(~a); + minthreshold=a; c->flags |= CHN_F_WRITING; - while ((c->smegcnt + buf->uio_resid) > a) { - s = spltty(); - chn_dmaupdate(c); - splx(s); - if (b->fl < DMA_ALIGN_THRESHOLD) { + s = spltty(); + chn_checkunderflow(c); + splx(s); + while ((buf->uio_resid+c->smegcnt) > minthreshold ) { /* Don't allow write unaligned data */ + threshold = min ((buf->uio_resid+c->smegcnt), maxthreshold); + if (b->fl < threshold) { if (c->flags & CHN_F_NBIO) break; timeout = (buf->uio_resid >= b->dl)? hz : 1; - ret = tsleep(b, PRIBIO | PCATCH, "pcmwr", timeout); - if (ret == EINTR) chn_abort(c); - if (ret == EINTR || ret == ERESTART) break; - ret = 0; - continue; - } + rc = tsleep(b, PRIBIO | PCATCH, "pcmwr", timeout); + if (rc == 0 || rc == EWOULDBLOCK) { + s = spltty(); + chn_checkunderflow(c); + splx(s); + if (b->fl < minthreshold) continue; /* write only alligned chunk of data */ + } else { +#if 0 + if (ret == EINTR) chn_abort(c); +#endif + ret=rc; + break; + } + } /* ensure we always have a whole number of samples */ - l = min(b->fl, b->bufsize - b->fp) & ~a; - if (l == 0) break; + l = min(b->fl, b->bufsize - b->fp); + KASSERT(!(l & a),("unaligned write %d, %d fl %d fp %d bufsize %d", l, a + 1, b->fl, b->fp, b->bufsize)); w = c->feeder->feed(c->feeder, c, b->buf + b->fp, l, buf); - if (w == 0) panic("no feed"); + KASSERT(w, ("chn_write: no feed")); s = spltty(); b->rl += w; b->fl -= w; b->fp = (b->fp + w) % b->bufsize; splx(s); - if (b->rl && !b->dl) chn_stintr(c); + DEB(if(1) printf("write %d bytes fp %d rl %d\n",w ,b->fp, b->rl)); + if (!b->dl) chn_stintr(c); } if ((ret == 0) && (buf->uio_resid > 0)) { l = buf->uio_resid; - if ((c->smegcnt + l) >= SMEGBUFSZ) panic("resid overflow %d", l); + KASSERT( (c->smegcnt + l) < SMEGBUFSZ, ("resid overflow %d", l)); uiomove(c->smegbuf + c->smegcnt, l, buf); c->smegcnt += l; } @@ -472,44 +504,61 @@ return 0; } +static void +chn_clearbuf(pcm_channel *c, int length) +{ +int i; +u_int16_t data, *p; + + snd_dbuf *b = &c->buffer; + /* rely on length & DMA_ALIGN_MASK == 0 */ + length&=DMA_ALIGN_MASK; + if (c->hwfmt & AFMT_SIGNED) data = 0x00; else data = 0x80; + if (c->hwfmt & AFMT_16BIT) data <<= 8; else data |= data << 8; + if (c->hwfmt & AFMT_BIGENDIAN) + data = ((data >> 8) & 0x00ff) | ((data << 8) & 0xff00); + for (i = b->fp, p=(u_int16_t*)(b->buf+b->fp) ; i < b->bufsize && length; i += 2, length-=2) + *p++ = data; + for (i = 0, p=(u_int16_t*)b->buf; i < b->bufsize && length; i += 2, length-=2) + *p++ = data; + + return; +} + void chn_resetbuf(pcm_channel *c) { snd_dbuf *b = &c->buffer; - u_int16_t data, *p; - u_int32_t i; c->smegcnt = 0; c->buffer.sample_size = 1; c->buffer.sample_size <<= (c->hwfmt & AFMT_STEREO)? 1 : 0; c->buffer.sample_size <<= (c->hwfmt & AFMT_16BIT)? 1 : 0; - /* rely on bufsize & 3 == 0 */ - if (c->hwfmt & AFMT_SIGNED) data = 0x00; else data = 0x80; - if (c->hwfmt & AFMT_16BIT) data <<= 8; else data |= data << 8; - if (c->hwfmt & AFMT_BIGENDIAN) - data = ((data >> 8) & 0x00ff) | ((data << 8) & 0xff00); - for (i = 0, p = (u_int16_t *)b->buf; i < b->bufsize; i += 2) - *p++ = data; b->rp = b->fp = 0; b->dl = b->rl = 0; + b->fl = b->bufsize; + chn_clearbuf(c, b->bufsize); b->prev_total = b->total = 0; b->prev_int_count = b->int_count = 0; b->first_poll = 1; - b->fl = b->bufsize; + b->underflow=0; } void buf_isadma(snd_dbuf *b, int go) { if (ISA_DMA(b)) { - if (go == PCMTRIG_START) isa_dmastart(b->dir | B_RAW, b->buf, - b->bufsize, b->chan); - else { + if (go == PCMTRIG_START) { + DEB(printf("buf 0x%p ISA DMA started\n", b)); + isa_dmastart(b->dir | B_RAW, b->buf, + b->bufsize, b->chan); + } else { + DEB(printf("buf 0x%p ISA DMA stopped\n", b)); isa_dmastop(b->chan); isa_dmadone(b->dir | B_RAW, b->buf, b->bufsize, b->chan); } - } else panic("buf_isadma called on invalid channel"); + } else KASSERT(1, ("buf_isadma called on invalid channel")); } int @@ -519,7 +568,7 @@ int i = b->dl? isa_dmastatus(b->chan) : b->bufsize; if (i < 0) i = 0; return b->bufsize - i; - } else panic("buf_isadmaptr called on invalid channel"); + } else KASSERT(1, ("buf_isadmaptr called on invalid channel")); return -1; } @@ -543,7 +592,7 @@ ret = tsleep((caddr_t)b, PRIBIO | PCATCH, "pcmsyn", 1); splx(s); if (ret == ERESTART || ret == EINTR) { - printf("tsleep returns %d\n", ret); + DEB(printf("chn_sync: tsleep returns %d\n", ret)); return -1; } } else break; @@ -605,18 +654,18 @@ DEB(printf("snd_flush c->flags 0x%08x\n", c->flags)); c->flags |= CHN_F_CLOSING; if (c->direction != PCMDIR_PLAY) chn_abort(c); - else while (b->dl) { + else if (b->dl) while (!b->underflow) { /* still pending output data. */ ret = tsleep((caddr_t)b, PRIBIO | PCATCH, "pcmflu", hz); - chn_dmaupdate(c); + DEB(chn_dmaupdate(c)); DEB(printf("snd_sync: now rl : fl %d : %d\n", b->rl, b->fl)); - if (ret == EINTR) { - printf("tsleep returns %d\n", ret); + if (ret == EINTR || ret == ERESTART) { + DEB(printf("chn_flush: tsleep returns %d\n", ret)); return -1; } if (ret && --count == 0) { - printf("timeout flushing dbuf_out, cnt 0x%x flags 0x%x\n", - b->rl, c->flags); + DEB(printf("chn_flush: timeout flushing dbuf_out, cnt 0x%x flags 0x%x\n",\ + b->rl, c->flags)); break; } } @@ -641,11 +690,13 @@ if ((c->flags & CHN_F_INIT) && CANCHANGE(c)) { chn_setformat(c, c->format); chn_setspeed(c, c->speed); - chn_setblocksize(c, c->blocksize); + chn_setblocksize(c, (c->flags & CHN_F_HAS_SIZE) ? c->blocksize : 0); chn_setvolume(c, (c->volume >> 8) & 0xff, c->volume & 0xff); c->flags &= ~CHN_F_INIT; return 1; } + if (CANCHANGE(c) && !(c->flags & CHN_F_HAS_SIZE) ) + chn_setblocksize(c, 0); /* Apply new block size */ return 0; } @@ -716,10 +767,10 @@ c->flags &= ~CHN_F_HAS_SIZE; if (blksz >= 2) c->flags |= CHN_F_HAS_SIZE; blksz = abs(blksz); - if (blksz < 2) blksz = (c->buffer.sample_size * c->speed) >> 2; + if (blksz < 2) blksz = c->buffer.sample_size * (c->speed / 4); /* 1/4 sec */ RANGE(blksz, 1024, c->buffer.bufsize / 4); - blksz &= ~3; - c->blocksize = c->setblocksize(c->devinfo, blksz); + blksz &= DMA_ALIGN_MASK; + c->blocksize = c->setblocksize(c->devinfo, blksz)&DMA_ALIGN_MASK; return c->blocksize; } c->blocksize = blksz; @@ -736,7 +787,14 @@ int chn_getptr(pcm_channel *c) { - return c->getptr(c->devinfo); + int hwptr; + int a = (1 << c->align) - 1; + + hwptr=c->getptr(c->devinfo); + /* don't allow unaligned values in the hwa ptr */ + hwptr &= ~a ; /* Apply channel align mask */ + hwptr &= DMA_ALIGN_MASK; /* Apply DMA align mask */ + return hwptr; } pcmchan_caps * Index: datatypes.h =================================================================== RCS file: /home/vns/cvs/FreeBSD//src/sys/dev/pcm/datatypes.h,v retrieving revision 1.4 diff -u -r1.4 datatypes.h --- datatypes.h 1999/11/15 23:57:33 1.4 +++ datatypes.h 1999/11/18 21:13:22 @ -58,7 +58,7 @@ */ struct _snd_dbuf { - char *buf; + u_int8_t *buf; int bufsize; volatile int rp, fp; /* pointers to the ready and free area */ volatile int dl; /* transfer size */ @@ -71,6 +71,7 @@ u_long prev_total; /* copy of the above when GETxPTR called */ int first_poll; bus_dmamap_t dmamap; + int underflow; }; typedef int (pcmfeed_init_t)(pcm_feeder *feeder); Index: dsp.c =================================================================== RCS file: /home/vns/cvs/FreeBSD//src/sys/dev/pcm/dsp.c,v retrieving revision 1.7 diff -u -r1.7 dsp.c --- dsp.c 1999/11/13 18:31:29 1.7 +++ dsp.c 1999/11/18 22:40:53 @ -51,8 +51,8 @@ static int getchns(snddev_info *d, int chan, pcm_channel **rdch, pcm_channel **wrch) { - if ((d->flags & SD_F_PRIO_SET) == SD_F_PRIO_SET) - panic("read and write both prioritised"); + KASSERT((d->flags & SD_F_PRIO_SET) != SD_F_PRIO_SET, \ + ("getchns: read and write both prioritised")); if (d->flags & SD_F_SIMPLEX) { *rdch = (d->flags & SD_F_PRIO_RD)? d->arec[chan] : &d->fakechan; *wrch = (d->flags & SD_F_PRIO_WR)? d->aplay[chan] : &d->fakechan; @@ -66,8 +66,8 @@ static void setchns(snddev_info *d, int chan) { - if ((d->flags & SD_F_PRIO_SET) == SD_F_PRIO_SET) - panic("read and write both prioritised"); + KASSERT((d->flags & SD_F_PRIO_SET) != SD_F_PRIO_SET, \ + ("getchns: read and write both prioritised")); d->flags |= SD_F_DIR_SET; if (d->flags & SD_F_EVILSB16) { if ((d->flags & SD_F_PRIO_RD) && (d->aplay[chan])) { @@ -159,7 +159,10 @@ chn_abort(rdch); rdch->flags &= ~(CHN_F_BUSY | CHN_F_RUNNING | CHN_F_MAPPED); } - if (wrch) wrch->flags &= ~(CHN_F_BUSY | CHN_F_RUNNING | CHN_F_MAPPED); + if (wrch) { + chn_flush(wrch); + wrch->flags &= ~(CHN_F_BUSY | CHN_F_RUNNING | CHN_F_MAPPED); + } d->aplay[chan] = NULL; d->arec[chan] = NULL; return 0; @@ -173,8 +176,8 @@ if (!(d->flags & SD_F_PRIO_SET)) d->flags |= SD_F_PRIO_RD; if (!(d->flags & SD_F_DIR_SET)) setchns(d, chan); getchns(d, chan, &rdch, &wrch); - if (!rdch || !(rdch->flags & CHN_F_BUSY)) - panic("dsp_read: non%s channel", rdch? "busy" : "existant"); + KASSERT(wrch, ("dsp_read: nonexistant channel")); + KASSERT(wrch->flags & CHN_F_BUSY, ("dsp_read: nonbusy channel")); if (rdch->flags & CHN_F_MAPPED) return EINVAL; if (!(rdch->flags & CHN_F_RUNNING)) { rdch->flags |= CHN_F_RUNNING; @@ -191,8 +194,8 @@ if (!(d->flags & SD_F_PRIO_SET)) d->flags |= SD_F_PRIO_WR; if (!(d->flags & SD_F_DIR_SET)) setchns(d, chan); getchns(d, chan, &rdch, &wrch); - if (!wrch || !(wrch->flags & CHN_F_BUSY)) - panic("dsp_write: non%s channel", wrch? "busy" : "existant"); + KASSERT(wrch, ("dsp_write: nonexistant channel")); + KASSERT(wrch->flags & CHN_F_BUSY, ("dsp_write: nonbusy channel")); if (wrch->flags & CHN_F_MAPPED) return EINVAL; if (!(wrch->flags & CHN_F_RUNNING)) { wrch->flags |= CHN_F_RUNNING; @@ -519,7 +522,7 @@ case SOUND_PCM_READ_FILTER: /* dunno what these do, don't sound important */ default: - DEB(printf("default ioctl snd%d fn 0x%08x fail\n", unit, cmd)); + DEB(printf("default ioctl chan%d fn 0x%08lx fail\n", chan, cmd)); ret = EINVAL; break; } Index: feeder.c =================================================================== RCS file: /home/vns/cvs/FreeBSD//src/sys/dev/pcm/feeder.c,v retrieving revision 1.6 diff -u -r1.6 feeder.c --- feeder.c 1999/11/15 23:57:32 1.6 +++ feeder.c 1999/11/18 22:42:48 @ -108,24 +108,21 @@ static int feed_root(pcm_feeder *feeder, pcm_channel *ch, u_int8_t *buffer, u_int32_t count, struct uio *stream) { - int ret, tmp = 0, c = 0; - if (!count) panic("feed_root: count == 0"); - count &= ~((1 << ch->align) - 1); - if (!count) panic("feed_root: aligned count == 0"); - if (ch->smegcnt > 0) { - c = min(ch->smegcnt, count); - bcopy(ch->smegbuf, buffer, c); - ch->smegcnt -= c; + int ret, c=0; + KASSERT(count, ("feed_root: count == 0")); + count &= ~((1 << ch->align) - 1); + KASSERT(count, ("feed_root: aligned count == 0")); + if (ch->smegcnt > 0) { + c = min(ch->smegcnt, count); + bcopy(ch->smegbuf, buffer, c); + ch->smegcnt -= c; + } + count = min(count, stream->uio_resid); + if (count) { + ret = uiomove(buffer, count, stream); + KASSERT(ret==0, ("feed_root: uiomove failed")); } - while ((stream->uio_resid > 0) && (c < count)) { - tmp = stream->uio_resid; - ret = uiomove(buffer + c, count - c, stream); - if (ret) panic("feed_root: uiomove failed"); - tmp -= stream->uio_resid; - c += tmp; - } - if (!c) panic("feed_root: uiomove didn't"); - return c; + return c+count; } pcm_feeder feeder_root = { "root", 0, NULL, NULL, feed_root }; Index: sound.c =================================================================== RCS file: /home/vns/cvs/FreeBSD//src/sys/dev/pcm/sound.c,v retrieving revision 1.7 diff -u -r1.7 sound.c --- sound.c 1999/11/06 05:31:47 1.7 +++ sound.c 1999/11/18 19:05:02 @ -352,7 +352,7 @@ int dev, chan; snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan); - DEB(printf("sndpoll dev 0x%04x events 0x%08x\n", i_dev, events)); + DEB(printf("sndpoll d 0x%p dev 0x%04x events 0x%08x\n", d, dev, events)); if (d == NULL) return ENXIO; Index: sound.h =================================================================== RCS file: /home/vns/cvs/FreeBSD//src/sys/dev/pcm/sound.h,v retrieving revision 1.3 diff -u -r1.3 sound.h --- sound.h 1999/09/28 21:43:35 1.3 +++ sound.h 1999/11/18 19:05:02 @ -160,7 +160,7 @@ #define BVDDB(x) if (bootverbose) x #ifndef DEB -#define DEB(x) +#define DEB(x) #endif int pcm_addchan(device_t dev, int dir, pcm_channel *templ, void *devinfo); Index: isa/mss.c =================================================================== RCS file: /home/vns/cvs/FreeBSD//src/sys/dev/pcm/isa/mss.c,v retrieving revision 1.32 diff -u -r1.32 mss.c --- mss.c 1999/11/15 17:02:32 1.32 +++ mss.c 1999/11/18 22:14:26 @ -348,7 +348,7 @@ mss->opti_offset = (rman_get_start(mss->conf_base) & ~3) + 2 - rman_get_start(mss->conf_base); - printf("mss_init: opti_offset=%d\n", mss->opti_offset); + BVDDB(printf("mss_init: opti_offset=%d\n", mss->opti_offset)); opti_wr(mss, 4, 0xd6); /* fifo empty, OPL3, audio enable, SB3.2 */ ad_write(mss, 10, 2); /* enable interrupts */ opti_wr(mss, 6, 2); /* MCIR6: mss enable, sb disable */ @@ -933,9 +933,10 @@ if (FULL_DUPLEX(mss)) ad_write(mss, 24, ~c); /* ack selectively */ else io_wr(mss, MSS_STATUS, 0); /* Clear interrupt status */ } - if (i == 10) printf("mss_intr: irq, but not from mss\n"); - else if (served == 0) { - printf("mss_intr: unexpected irq with reason %x\n", c); + if (i == 10) { + BVDDB(printf("mss_intr: irq, but not from mss\n")); + } else if (served == 0) { + BVDDB(printf("mss_intr: unexpected irq with reason %x\n", c)); /* * this should not happen... I have no idea what to do now. * maybe should do a sanity check and restart dmas ? @@ -1043,7 +1044,7 @@ u_char prev; if ((mss->bd_flags & BD_F_MCE_BIT) == 0) { - printf("--- hey, leave_MCE: MCE bit was not set!\n"); + DEB(printf("--- hey, leave_MCE: MCE bit was not set!\n")); return; } @@ -1245,8 +1246,8 @@ ad_write(mss, 9, m); if (ad_read(mss, 9) == m) break; } - if (retry == 0) printf("start dma, failed to set bit 0x%02x 0x%02x\n", - m, ad_read(mss, 9)); + if (retry == 0) BVDDB(printf("stop dma, failed to set bit 0x%02x 0x%02x\n", \ + m, ad_read(mss, 9))); return 0; } @@ -1407,7 +1408,7 @@ #if 0 reason = io_rd(mss, MSS_STATUS); if (!(reason & 1)) {/* no int, maybe a shared line ? */ - printf("intr: flag 0, mcir11 0x%02x\n", ad_read(mss, 11)); + DEB(printf("intr: flag 0, mcir11 0x%02x\n", ad_read(mss, 11))); return; } #endif @@ -1424,8 +1425,8 @@ DEB(printf("Warning: MPU interrupt\n");) mc11 |= 0x20; } - if (mc11 & masked) printf("irq reset failed, mc11 0x%02x, 0x%02x\n", - mc11, masked); + if (mc11 & masked) BVDDB(printf("irq reset failed, mc11 0x%02x, 0x%02x\n",\ + mc11, masked)); masked |= mc11; /* * the nice OPTi931 sets the IRQ line before setting the bits in @@ -1438,7 +1439,7 @@ if (--loops) goto again; else DDB(printf("intr, but mc11 not set\n");) } - if (loops == 0) printf("intr, nothing in mcir11 0x%02x\n", mc11); + if (loops == 0) BVDDB(printf("intr, nothing in mcir11 0x%02x\n", mc11)); return; } =================================================================== =================================================================== # This is a shell archive. Save it in a file, remove anything before # this line, and then unpack it by entering "sh file". Note, it may # create directories; files and directories will be owned by you and # have default permissions. # # This archive contains: # # Makefile # README # sound.c # echo x - Makefile sed 's/^X//' >Makefile << 'END-of-Makefile' XPROG= sound XSRCS= sound.c XLDADD= -lm XNOMAN= yes X#DEBUG_FLAGS= -g XCFLAGS= -Wall X X.include END-of-Makefile echo x - README sed 's/^X//' >README << 'END-of-README' XSound driver testing utility. X XThis program simple produce series of harmonic signals and silence to Xsoundcard. When program launched without arguments, it's produce X5 secs 1KHz tone, than 3 secs pause, and than again tone and pause. XWhen tone produced status changed from 'sound playing' to 'sound sync', Xand when pause status changed to 'silent'. When sound card's working Xproperly the signal must be to have right tone frequency and changing Xstatus must be synchronized within change of soundcard output. X XThis program have the following command line arguments: X-h - Print short information about command line arguments X-? - the same X-i - Catch INT signal, so you may press Ctrl-C, and sound output must X my stay the same, without any clicks or noise. X-d X Change device to 'device', default /dev/dsp X-T X Change number of tone/pause to 'periods', default 2 X-a X Change duration of tone to 'nsec', default 5 X-s X Change duration of pause to 'nsec', default 2 X-p X Change frequency of generated tone, default 1000Hz X-f X Set list of formats, delimited by coma (','), for each of X which the sound card will be tested, for example U8,STEREO|U16_LE. X Default STEREO|U16_LE X-r X Set list of rates, delimited by coma (','), for each of X which the sound card will be tested, for example 8000,11050. X Default 44100. X-b X Set the size of blocks, which will be used as arg to write routine. X X XAuthor: XVladimir N. Silyaev vns@delta.odessa.ua X END-of-README echo x - sound.c sed 's/^X//' >sound.c << 'END-of-sound.c' X/* X * sound.c X * X * Soundcard/driver testing utility X * X * Copyright by Vladimir N. Silyaev 1999 X * X * Redistribution and use in source and binary forms, with or without X * modification, are permitted provided that the following conditions are X * met: 1. Redistributions of source code must retain the above copyright X * notice, this list of conditions and the following disclaimer. 2. X * Redistributions in binary form must reproduce the above copyright notice, X * this list of conditions and the following disclaimer in the documentation X * and/or other materials provided with the distribution. X * X * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY X * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED X * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE X * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR X * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR X * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER X * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF X * SUCH DAMAGE. X * X * $Id: sound.c,v 1.7 1999/11/19 01:05:13 vsilyaev Exp $ X * X */ X X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X X#define DEFAULT_RATE 44100 X#define DEFAULT_FORMAT (AFMT_U16_LE|AFMT_STEREO) X#define DEFAULT_FREQ 1000 X#define DEFAULT_PLAY_TIME 5 X#define DEFAULT_PAUSE_TIME 3 X#define DEFAULT_NPERIODS 2 X#define DEFAULT_BLOCK_SIZE 16384 X Xtypedef struct{ X u_long *values; X size_t length; X} l_array; X Xtypedef struct { X l_array formats; X l_array rates; X char *device; X int play_freq; X int play_time; X int pause_time; X int nperiods; X int catch_sigint; X int write_block_size; X} sound_options; X Xstatic sound_options _app_options; Xstatic sound_options *app_options=&_app_options; X Xtypedef struct { X char *name; X int fd; /* File descriptor */ X snd_capabilities info; X u_long format,rate; X} sound_info ; Xstatic int sound_change_param(sound_info *snd, u_long rate, u_long format); X Xtypedef struct { X u_long format; X u_long rate; X int freq; X float f_freq; X int nsamples; X int sample_len; X int align; X float t_sample; X union { X void *data; X u_int8_t *data8; X u_int8_t (*data8_s)[2]; X u_int16_t *data16; X u_int16_t (*data16_s)[2]; X } data; X} sound_sample; X Xtypedef struct { X u_long offset; /* in samples, not in bytes */ X} sample_play; X Xstatic int Xl_array_init(l_array *array) X{ X bzero(array, sizeof(*array)); X array->values=NULL; X array->length=0; X return 0; X} X Xstatic int Xl_array_shutdown(l_array *array) X{ X free(array->values); X return 0; X} X Xstatic int Xl_array_push(l_array *array, u_long value) X{ X array->values=realloc(array->values,(array->length+1)*sizeof(*array->values)); X array->values[array->length++]=value; X return 0; X} X Xstatic int Xl_array_get(l_array *array, int index, u_long *pvalues) X{ X if (index>=array->length) return -1; X if (pvalues) *pvalues=array->values[index]; X return 0; X} X Xstatic int Xsound_options_init(sound_options *options) X{ X bzero(options, sizeof(*options)); X l_array_init(&options->formats); X l_array_init(&options->rates); X options->device=NULL; X return 0; X} X Xstatic int Xsound_options_shutdown(sound_options *options) X{ X l_array_shutdown(&options->formats); X l_array_shutdown(&options->rates); X return 0; X} X X Xstatic const char fields_sep[] = ","; X Xstatic int Xfill_rates(l_array *rates, char *str) X{ Xchar *word; Xint rate; X X while ( (word=strsep(&str, fields_sep)) != NULL ) { X rate = atoi(word); X if (rate==0) { X fprintf(stderr, "Invalid bit rate '%s'\n", word); X continue; X } X l_array_push(rates, rate); X } X return 0; X} X Xstatic const struct { Xu_long bit; Xconst char *name; X} known_formats[] = { X#define FORMAT(name) {AFMT_##name, #name} X FORMAT(U8), X FORMAT(U16_LE), X FORMAT(STEREO) X#undef FORMAT X}; X Xstatic int Xstricmp(const char *str1,const char *str2) X{ Xint ch1,ch2; X X for(;;) { X ch1=*str1++; X ch2=*str2++; X ch1=toupper(ch1); X ch2=toupper(ch2); X if (ch1!=ch2) return ch1-ch2; X if (ch1==0) return 0; X } X /* NOT REACHED */ X} X Xstatic u_long Xparse_format(const char *sformat) X{ Xchar *str_alloc=strdup(sformat); Xconst int n_known_formats=sizeof(known_formats)/sizeof(*known_formats); Xu_long format=0; Xint i; Xu_long bit; Xint nbits=0; Xchar *str,*word; X X str=str_alloc; X while ( (word=strsep(&str, "|")) != NULL) { X for (bit=0,i=0;i] [-T ] [-a ] [-s ] [-p ] [-f ] [-r ] [-b ]" X "\n", name); X exit(EX_USAGE); X} X Xstatic int Xsound_options_fill(sound_options *options, int argc, char *argv[]) X{ Xint option; X X options->play_freq=0; X options->play_time=0; X options->pause_time=0; X options->nperiods=0; X options->device=NULL; X options->catch_sigint=0; X options->write_block_size=0; X while( (option=getopt(argc, argv, "ib:d:T:a:s:p:r:f:h?")) != -1 ) { X switch(option) { X case 'i': X options->catch_sigint=1; X break; X case 'd': X options->device=strdup(optarg); X break; X case 'T': X options->nperiods=atoi(optarg); X break; X case 'a': X options->play_time=atoi(optarg); X break; X case 's': X options->pause_time=atoi(optarg); X break; X case 'p': X options->play_freq=atoi(optarg); X break; X case 'b': X options->write_block_size=atoi(optarg); X break; X case 'r': X fill_rates(&options->rates,optarg); X break; X case 'f': X fill_formats(&options->formats,optarg); X break; X case 'h': X case '?': X default: X usage(argv[0]); X /* NOT REACHED */ X } X } X if (l_array_get(&options->rates, 0, NULL)<0) X l_array_push(&options->rates, DEFAULT_RATE); X if (l_array_get(&options->formats, 0, NULL)<0) X l_array_push(&options->formats, DEFAULT_FORMAT); X if (options->play_freq<=0) { X options->play_freq=DEFAULT_FREQ; X } X if (options->play_time<=0) { X options->play_time=DEFAULT_PLAY_TIME; X } X if (options->pause_time<=0) { X options->pause_time=DEFAULT_PAUSE_TIME; X } X if (options->nperiods<=0) { X options->nperiods=DEFAULT_NPERIODS; X } X if (options->write_block_size<=0) { X options->write_block_size=DEFAULT_BLOCK_SIZE; X } X if (options->device==NULL) { X options->device=strdup("/dev/dsp"); X } X return 0; X} X Xstatic int Xfind_nearest_for_freq(int rate,int freq) X{ Xint i; Xint nsamples; Xfloat diff; Xfloat min_diff=freq; Xint min_periods=1; X X for (i=1;i<1024;i++) { X nsamples=(int)ceil((i*(float)rate)/freq); X diff=fabs(freq-rate/nsamples); X if (diffformat=format; X sample->rate=rate; X sample->freq=freq; X switch(format&~AFMT_STEREO) { X case AFMT_U8: X sample->sample_len=1; X break; X case AFMT_U16_LE: X sample->sample_len=2; X break; X default: X return -1; X } X if (format&AFMT_STEREO) sample->sample_len*=2; X X sample->align=sample->sample_len; X nperiods=find_nearest_for_freq(rate,freq); X sample->nsamples=(int)ceil((nperiods*(float)rate)/freq); X#if 0 X fprintf(stderr, "Real freq %.1f HZ\n",((float)nperiods*rate)/sample->nsamples); X#endif X sample->t_sample=(2*M_PI*nperiods)/sample->nsamples; X sample->data.data=malloc(sample->nsamples*sample->sample_len); X assert(sample->data.data); X for (i=0;insamples;i++) { X f_sample=amp*sin(sample->t_sample*i); X data8=(int)floor(f_sample*128+0.5); X data16=(int)floor(f_sample*32768+0.5); X switch(format) { X case AFMT_U8: X sample->data.data8[i]=(u_int8_t)(data8+128); X break; X case AFMT_U8|AFMT_STEREO: X sample->data.data8_s[i][0]= X sample->data.data8_s[i][1]=(u_int8_t)(data8+128); X break; X case AFMT_U16_LE: X sample->data.data16[i]=(u_int16_t)(data16+32768); X break; X case AFMT_U16_LE|AFMT_STEREO: X sample->data.data16_s[i][0]= X sample->data.data16_s[i][1]=(u_int16_t)(data16+32768); X break; X } X } X return 0; X} X X Xstatic int Xsample_shutdown(sound_sample *sample) X{ X free(sample->data.data); X sample->data.data=0; X return 0; X} X Xstatic int Xsample_play_init(sample_play *splay) X{ X bzero(splay, sizeof(*splay)); X return 0; X} X Xtypedef struct { X u_long remain; X u_int8_t *dest; X unsigned offset; X size_t bufsize; X u_long ncopy; X} sound_dest_helper; X Xstatic void Xcopy_data_helper(sound_sample *sample, sample_play *splay, sound_dest_helper *helper) X{ Xsize_t ncopy; X X ncopy=helper->remain*sample->sample_len; X memcpy(helper->dest, sample->data.data8+helper->offset, ncopy); X helper->dest+=ncopy; X helper->ncopy+=ncopy; X helper->bufsize-=helper->remain; X splay->offset+=helper->remain; X} X Xstatic u_long Xsample_play_fill_data(sound_sample *sample, sample_play *splay, void *buf, size_t bufsize) X{ Xsound_dest_helper helper; X X assert(bufsize%sample->sample_len==0); X helper.bufsize = bufsize/sample->sample_len; X helper.remain = splay->offset%sample->nsamples; X helper.dest = (u_int8_t*)buf; X helper.ncopy = 0; X if (helper.remain) { X helper.offset = helper.remain * sample->sample_len; X helper.remain = sample->nsamples - helper.remain; X if (helper.remain>helper.bufsize) helper.remain=helper.bufsize; X copy_data_helper(sample, splay, &helper); X } X while (helper.bufsize>=sample->nsamples) { X helper.offset=0; X helper.remain=sample->nsamples; X copy_data_helper(sample, splay, &helper); X } X if (helper.bufsize) { X helper.offset=0; X helper.remain=helper.bufsize; X copy_data_helper(sample, splay, &helper); X } X return helper.ncopy; X} X Xstatic int Xsample_play_shutdown(sample_play *splay) X{ X return 0; X} X Xstatic int Xsound_open(sound_info *snd) X{ Xsnd_capabilities info; Xint rc; Xint sound; X X bzero(snd, sizeof(*snd)); X snd->name=strdup(app_options->device); X sound=open(snd->name ,O_RDWR); X X if (sound<0) { X err(EX_UNAVAILABLE,snd->name); X } X X rc=ioctl(sound,AIOGCAP,&info); X if (rc<0) return rc; X #if 0 X printf("soundcard capability: rate_min %lu, rate_max %lu,\n" X "formats %08lx, \n" X "bufsize %lu,\n" X "mixers %08lx, inputs %08lx, left %u, right %u\n", X info.rate_min,info.rate_max, X info.formats, X info.bufsize, X info.mixers, info.inputs, info.left, info.right); X #endif X snd->info=info; X snd->fd=sound; X return 0; X} X Xstatic void wait_for_time(const struct timeval *stop) X{ Xstruct timeval current,remain_tv; Xstruct timespec remain; Xint rc; X X gettimeofday(¤t,NULL); X timersub(stop, ¤t, &remain_tv); X TIMEVAL_TO_TIMESPEC(&remain_tv, &remain); X /* printf("%d,%ld\n", (int)remain.tv_sec,remain.tv_nsec); */ X for (;;) { X rc=nanosleep(&remain,&remain); X if (rc!=-1 || errno!=EINTR) break; X } X return; X} X X#define SOUND_WRITE 1 X Xstatic int Xsound_play(sound_info *snd,sound_sample *sample) X{ Xu_int8_t *data,*pdata; Xsample_play play; Xstruct timeval begin,stop; Xconst int play_time=app_options->play_time,pause_time=app_options->pause_time; Xu_long nsamples,offset; Xunsigned nwrite,nwrote; Xint i; Xint rc; Xint block_size; X X block_size=app_options->write_block_size; X data=malloc(block_size); X assert(data); X nsamples=sample->rate*play_time*sample->sample_len; X for (i=0;inperiods;i++) { X sample_play_init(&play); X gettimeofday(&begin,NULL); X fprintf(stderr,"\rsound playing%20c\r", ' '); X for (offset=0;offsetblock_size) { X nwrite=block_size; X } else { X nwrite=nsamples-offset; X nwrite=((nwrite+sample->align-1)/sample->align)*sample->align; X } X sample_play_fill_data(sample,&play,data,nwrite); X assert((nwrite%sample->align)==0); X #if SOUND_WRITE X for (nwrote=0,pdata=data;nwrite;) { X rc=write(snd->fd,pdata,nwrite); X if (rc<0) { X warn("Error on writing %s",snd->name); X if (errno == EINTR) continue; X break; X } X nwrote+=rc; X pdata+=rc; X nwrite-=rc; X } X #else X nwrote=nwrite; X #endif X offset+=nwrote; X } X #if SOUND_WRITE X /* X rc=ioctl(snd->fd, SNDCTL_DSP_SYNC, NULL); X if (rc<0) warn("Error on sync at %s",snd->name); X printf(" sync done\n"); X */ X #endif X stop=begin; X stop.tv_sec+=play_time; X fprintf(stderr,"\rsound sync%20c\r", ' '); X wait_for_time(&stop); X fprintf(stderr,"\rsilent%20c\r", ' '); X stop=begin; X stop.tv_sec+=play_time+pause_time; X wait_for_time(&stop); X #if 0 X sound_change_param(snd, snd->rate, snd->format); X #endif X sample_play_shutdown(&play); X } X free(data); X return 0; X} X X Xstatic int Xsound_sync(sound_info *snd) X{ X close(snd->fd); X snd->fd=open(snd->name ,O_RDWR); X X if (snd->fd<0) { X err(EX_UNAVAILABLE,snd->name); X } X return 0; X} X Xchar * Xformat2str(u_long format, u_long rate, char *dest, size_t maxlen) X{ Xchar str[64]; Xconst char *name; Xconst char *stereo; X X switch(format&~AFMT_STEREO) { X case AFMT_U8: X name="Unsigned 8-bit"; X break; X case AFMT_U16_LE: X name="Unsigned 16-bit, Little endian"; X break; X default: X snprintf(str,sizeof(str),"Unknown [0x%08lx]", format&~AFMT_STEREO); X name=str; X break; X } X if (format&AFMT_STEREO) stereo="Stereo, "; X else stereo=""; X snprintf(dest,maxlen,"%s%s rate %lu", stereo, name, rate); X return dest; X} X Xstatic int Xsound_change_param(sound_info *snd, u_long rate, u_long format) X{ Xsnd_chan_param param; Xint rc; Xchar str[256]; Xchar str2[256]; X X rc=sound_sync(snd); X if (rc<0) return rc; X bzero(¶m,sizeof(param)); X param.play_rate=rate; X param.play_format=format; X rc=ioctl(snd->fd, AIOSFMT, ¶m); X if (rc<0) { X warn("Device %s: format %s rejected", X snd->name, format2str(format, rate, str, sizeof(str))); X return rc; X } X if (param.play_format!=format || param.play_rate!=rate) { X fprintf(stderr, "Device %s: format %s unsupported, " X "has been changed to %s\n", X snd->name, X format2str(format, rate, str, sizeof(str)), X format2str(param.play_format, param.play_rate, X str2, sizeof(str2)) X ); X return -1; X } X snd->format=format; X snd->rate=rate; X return 0; X} X Xstatic int Xsound_test(sound_info *snd) X{ Xint rate_index, format_index; Xu_long rate, format; Xint rc; Xsound_sample sample; Xchar sformat[256]; X X for(rate_index=0;l_array_get(&app_options->rates, rate_index, &rate)>=0;rate_index++) { X for(format_index=0;l_array_get(&app_options->formats, format_index, &format)>=0; format_index++) { X rc=sound_change_param(snd, rate, format); X if (rc<0) continue; X format2str(format, rate, sformat, sizeof(sformat)); X fprintf(stderr, "%s%30c\n", sformat,' '); X rc=sample_init(&sample, format, rate, app_options->play_freq); X if (rc<0) { X fprintf(stderr, "Unsupported format %s\n", sformat); X continue; X } X sound_play(snd, &sample); X sample_shutdown(&sample); X } X } X return 0; X} X X Xstatic int Xsound_close(sound_info *snd) X{ X if (snd->fd>0) close(snd->fd); X snd->fd=-1; X free(snd->name); X snd->name=NULL; X return 0; X} X Xstatic void Xsig_int_handler(int dummy) X{ X} X Xint main(int argc, char **argv) X{ Xsound_info snd; X X sound_options_init(app_options); X sound_options_fill(app_options,argc,argv); X if (app_options->catch_sigint) X signal(SIGINT,sig_int_handler); X sound_open(&snd); X sound_test(&snd); X sound_close(&snd); X sound_options_shutdown(app_options); X return 0; X} END-of-sound.c exit >Release-Note: >Audit-Trail: >Unformatted: To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-bugs" in the body of the message