Subject: Re: kern/9087: audio mmap(2) interface not entirely clear
To: None <netbsd-bugs@netbsd.org>
From: Dave Sainty <dave@dtsp.co.nz>
List: netbsd-bugs
Date: 01/16/2000 19:21:57
dave writes:

> >Number:         9087
> >Category:       kern
> >Synopsis:       audio mmap(2) interface not entirely clear
>
> >Description:
> 	This problem appears in the eso (ESS Solo-1) driver, but I think may
> 	be in others also.
> 
> 	The problem is: What size is the buffered mmap()'d area?
> 
> 	According to audio(4), this area should be the full buffer size.  But
> 	this does not appear to be enforced, and indeed it appears that
> 	sys/dev/audio.c will intruct the driver to start playing with a
> 	possibly smaller buffer at open time.
> 
> 	In the eso driver this is particularly apparent because the output
> 	buffer size is 65535 bytes, generally not divisible by the fragment
> 	size.  The mmap()'ed area is actually the buffer size rounded down to
> 	the last fragment boundary.  But I think the problem may be triggered
> 	in other drivers by using appropriate fragment sizes.

More on this...  Oh and BTW, fragment size == block size.  "Fragment"
is the ossaudio term for "block".

- The buffer size is constant (dependent on hardware).

- Memory mapped IO on any audio device where the (programmable) block
  size does not evenly divide the buffer size, will break.  ESS Solo-1
  is particularly prone to failures because the buffer size is not a
  power of 2.

- As an added complexity, ossaudio forces a block size that is a power of 2

We could:

1. Make the buffer size presented to userland be dependent on the
   block size.  This looks to be an unacceptable change of API, it
   seems like this value should be constant.

2. Force the block size to be a factor of the buffer size.  Not a
   general solution, nor a simple one.

3. Handle the last (partial) block of each complete buffer transfer as
   a special case.

4. Allow blocks to be unaligned in the buffer.  That is, allow
   wrapping at the end of the buffer to occur partway through a block,
   and continue on that same block at the start of the buffer again.

5. Round the buffer size to some value, where that value can be a
   worst case block size.  Watered down version of 2.

I assume that most hardware can support either one or the other of 3
and 4 efficiently, as this appears to be the interface provided by
ossaudio, at least in the documentation.  Perhaps someone with more
exposure to sound hardware can comment on this.  I'm not sure whether
problem may crop up with attempts to (for example) restart DMA over
the buffer in the middle of a block.

4 looks preferable to 3, I think it would be more efficient.  Hardware
implementing 4 would return AUDIO_GETOOFFS offsets not necessarily
divisible by the block size.  Does that matter?

The responsibility for starting audio is with audio.c, not the
hardware driver (audio_mmap() will start playback if it hasn't already
started, before calling mappage()).  This at least means the mappage()
functions in all the drivers I've looked at are currently correct, so
long as audio.c does the right thing (which it doesn't...).

What audio_mmap() should do is stop playback if (playback has started
&& sc_pr.end - sc_pr.start != sc_pr.bufsize), and restart it with
sc_pr.end = sc_pr.start + sc_pr.bufsize.

There is bound to be fallout from such a change in code that assumes
that (end - start) % blksize == 0, both audio.c and drivers.  For
example, from audio.c:

	offs = sc->sc_pr.outp - sc->sc_pr.start + sc->sc_pr.blksize;
	if (sc->sc_pr.start + offs >= sc->sc_pr.end)
		offs = 0;

... may no longer be valid.

Comments?  Anyone have any opinion at all about what is right here? :)

Cheers,

Dave