Subject: Re: audio(9) question
To: D. <jmcneill@invisible.yi.org>
From: Nathan J. Williams <nathanw@MIT.EDU>
List: tech-kern
Date: 06/25/2001 13:37:28
Jared "D." McNeill <jmcneill@invisible.yi.org> writes:

> > > My question is, once I get to the end of this buffer, how do I load more
> > > data to it? Simply reading from the beginning again makes the sound loop.
> > 
> > The higher-level audio code calls trigger_output with a callback
> > routine (audio_pint()) that will be called after each "blksize" chunk
> > is transferred to the device. The audio_pint() routine takes care of
> > keeping the circular buffer filled.
> 
> How does it know when the data has been transfered to the device? 
> 
> > Note that normally the size of the buffer (end - start) is larger than
> > blksize, so that audio_pint() will get several chances to do refilling
> > before the device gets to the end of the buffer and wraps around.
> 
> Hrm, I keep getting the same data over and over again. Mind having a
> look at the code and telling me if you see anything overly wrong?
> 
> http://invisible.yi.org/~jmcneill/netbsd/esl-20010625.tar.gz

As they come to me...

Looking at the esl_setup() routine:

        /*
         * XXX: This is just sick. We know what IRQ we're using from
         *      pcmcia_intr_establish, so why not use it instead?
         */
        reg = ESS_IRQ_CTRL_MASK | 0x20 | ESS_IRQ_CTRL_INTRA;
        esl_write_x_reg(sc, ESS_XCMD_IRQ_CTRL, reg);

Just to clear up what's going on, the ESS x88/1x88 chips are made to
be wired up directly to the ISA bus, and have four pins designed to be
attached to the ISA IRQ 5, 7, 9, and 10 lines. This register controls
which of those pins is used to signal interrupts. Since, in your case,
the chip is on a PCMCIA card, and PCMCIA cards only have one IRQ line
(which goes to the PCMCIA controller, not directly to the ISA bus),
the designers wired the IRQA line to the PCMCIA IRQ line, which is why
you have to program it this way. The "actual" ISA IRQ that this shows
up as is controlled by the PCMCIA controller, and is handles over in
pcmcia_intr_establish().

Your FIFO setup in esl_trigger_output() looks okay. 

However, I see trouble in esl_intr():

                if (pos >= sc->sc_esl.sc_dmaend) {
                        pos = sc->sc_esl.sc_dmastart;
                        (*sc->sc_esl.intr)(sc->sc_esl.arg);
                }

You're manually wrapping the circular buffer, which is the right thing
to do for FIFO mode, but you're only calling the audio_pint() callback
after finishing the buffer. You're supposed to call it after
transferring blksize bytes. The easiest approach would probably be to
keep another counter in sc->sc_esl for "bytes transferred in current
block", maybe sc_esl.sc_blkpos, Then, esl_intr() would look more like:

                sc->sc_esl.sc_blkpos += (ESS_FIFO_SIZE / 2);
                if (sc->sc_esl.sc_blkpos > sc->sc_esl.sc_blksize) {
                        (*sc->sc_esl.intr)(sc->sc_esl.arg);
                        sc->sc_esl.sc_blkpos -= sc->sc_esl.sc_blksize;
                }
                        
                pos += (ESS_FIFO_SIZE / 2);             
                if (pos >= sc->sc_esl.sc_dmaend)
                        pos = sc->sc_esl.sc_dmastart;
                sc->sc_esl.sc_dmaaddr = pos;


Try this and see if it helps. I'm also curious what blksize and buffer
size the audio system ends up using...

        - Nathan