Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 19 Jul 1997 11:45:06 -0700
From:      Amancio Hasty <hasty@rah.star-gate.com>
To:        Luigi Rizzo <luigi@iet.unipi.it>
Cc:        hackers@FreeBSD.ORG, multimedia@FreeBSD.ORG
Subject:   Re: dma handling in the sound driver 
Message-ID:  <199707191845.LAA17575@rah.star-gate.com>
In-Reply-To: Your message of "Sat, 19 Jul 1997 16:37:50 %2B0200." <199707191437.QAA01053@prova.iet.unipi.it> 

next in thread | previous in thread | raw e-mail | index | archive | help
Lets take a case example the dma algorithm  from the current 
sound driver for the SB16

dmabuf splits a pool of continous memory into N buffers where each
buffer can hold upto a 1/2 second. auto dma is chosen.
In the case of playing sun style au files recorded at 8khz , the size
of a buffer is 4096 , 1/2 seconds worth. The dma auto init pool size 
is taken from a sound driver constant which is usually a multiple of
4k for our example the dma auto init buffer is 32k.

A high level audio write routine whose buffer count usually is a 
bigger than 32k , breaks up the the write request in 4096 bytes buffers.

The sound card loops on its auto init dma buffer as well as the sound
driver's dma buffer routines.


	Cheers,
	Amancio
>From The Desk Of Luigi Rizzo :
> I have planned to rewrite the dma buffer handling routines for
> the sound driver as follows.
> 
> To me this seems reasonably simple and efficient. Can you please
> have a look at this high-level description of the problem (including some
> pseudo-code, very close to the actual implementation) and tell if this
> looks reasonable ?
> 
> (this description will also go into the source files, so hopefully
> people will not have a hard time in understanding how it works...)
> 
> 	Thanks
> 	Luigi
> 
> -------
> 
> 
> The main problem with the DMA in the sound driver is to avoid that the
> flow of data from/to the codec is interrupted, resulting in missing
> samples or clicks. The problem is dealt with at several levels.
> 
> 1) within the sound card, there is some amount of buffering (e.g. a
>    FIFO, or on-board memory, etc.) so as to overcome occasional
>    periods when the user process is slow.
> 
> 2) some amount of buffering must be supplied in the driver as well,
>    for those boards (many) which do not have enough buffering.
> 
> Even in presence of buffering, there might be problems when the
> current DMA operation terminates and a new one is restarted. In fact:
> - if we use a single DMA buffer, we need the time to refill it before
>   starting the next op. The largest the buffer (and the size of the
>   block being transferred), the longest is the idle time;
> - with two DMA buffers, we can refill one buffer while the other one
>   is in use by the DMA engine. We can still have troubles if we start
>   a long refill near the end of operation of DMA on the other buffer,
>   but this problem can be minimized (but not avoided; if we are late,
>   we are late, no matter how many buffers we have!)
> 
> In our implementation, we use a single memory block structured as
> three logical, variable-size, buffers: one in use by the dma engine,
> the next one ready for use by the dma (already filled up), the last
> one free for refills.
> 
>             dp,dl         rp,rl           fp,fl
>     +-------+-------------+---------------+------+
>     | free  | used by dma | ready for use | free |
>     +-------+-------------+---------------+------+
> 
> Both the "ready" and "free" areas can wrap around the end of the
> buffer. The "dma" are
> 
> Three pointers (dp, rp, fp) and three length (dl, rl, fl) are used for
> the purpose.
> 
> At initialization:
>     dp = rp = fp = 0 ;	/*  beginning of buffer		*/
>     dl = 0 ;		/* meaning no dma activity)	*/
>     rl = 0 ;		/* meaning no data ready	*/
>     fl = bufsize ;
> 
> Upon dma_interrupt(), we try to use the next buffer if possible:
> 
>     s=splhigh();
>     fl += dl ;
>     dp = rp ;
>     dl = 0 ;
>     if (rl > 0) {
> 	dl = min(rl, bufsize - rp ) ; /* do not wrap */
> 	rl -= dl ;
> 	rp += dl ;
> 	if (rp == bufsize) rp = 0;
>         /*
>          * now try to avoid too small dma transfers in the near future. 
>          * This can happen if I let rp start too close to the end of
>          * the buffer. If this happens, and have enough data, try to
>          * split the available block in two approx. equal parts.
>          */
>         if (bufsize - rp < MIN_DMA_SIZE && bufsize - dp > 2*MIN_DMA_SIZE) {
>             dl = (bufsize - dp) / 2; 
>             rl += (rp - (dp + dl) ) ;
>             rp = dp + dl ;
>         }
> 	restart_dma();
>     }
>     splx(s)
> 
> Upon user_write() for n bytes we copy data into the free buffer and
> then extend the ready block. Instead of doing all the copy at once, we
> start with small pieces in order to minimize the chance of a
> starvation. The size of the smallest chunk is chosen in a way that the
> execution time of uiomove is still dominated by the constant part.
> 
>     bsz = 64 ; /* min block size */
>     while (n>0) {
> 	int l = min (n, bsz);
> 	l = min (l, fl);
> 	l = min (l, bufsize - fp );
> 	uiomove(buf, .. , l);
> 	s=splhigh();
> 	rl += l ;
> 	fl -= l ;
> 	fp += l ;
> 	if (fp == bufsize) fp = 0 ;
> 	if (rl == l) {
> 	    dl = rl ;
> 	    restart_dma();
> 	}
> 	splx(s);
> 	if (fl == 0) { /* buffer full, must sleep */
> 	    tsleep( ... );
> 	    /* handle errors etc. */
> 	}
> 	bsz = min(bufsize, bsz*2);
>     }
> 
> ====================================================================
> Luigi Rizzo                     Dip. di Ingegneria dell'Informazione
> email: luigi@iet.unipi.it       Universita' di Pisa
> tel: +39-50-568533              via Diotisalvi 2, 56126 PISA (Italy)
> fax: +39-50-568522              http://www.iet.unipi.it/~luigi/
> ====================================================================





Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199707191845.LAA17575>