[alsa-devel] Questions about writing a new ALSA driver for a very limitted device

Takashi Iwai tiwai at suse.de
Tue Apr 24 15:13:15 CEST 2007

At Thu, 19 Apr 2007 14:30:29 -0700 (PDT),
Tim Harvey wrote:
> --- Takashi Iwai <tiwai at suse.de> wrote:
> > The "period" defines the frequency to update the status, usually via
> > the invokation of interrupts.  The "period_size" defines the frame
> > sizes corresponding to the "period time".  This term corresponds to
> > the "fragment size" on OSS.  On major sound hardwares, a ring-buffer
> > is divided to several parts and an irq is issued on each boundary.
> > The period_size defines the size of this chunk.
> > 
> > On some hardwares, the irq is controlled on the basis of a timer.  In
> > this case, the period is defined as the timer frequency to invoke an
> > irq.
> > 
> > 
> My system has a hardware buffer of 4096 bytes (so I will use an intermediate
> buffer ) and is fixed at 8KHz 16bit stereo.  The 4K hw buffer would accommodate
> 128ms of data which is pretty small.  Therefore I'll choose to use a 32K
> intermediate buffer which will accommodate about 1sec of audio data.  My IRQ is
> timer based so I will need to decide upon a reasonable period.  As the period
> seems to dictate the ability for the middle layer to see the status of the
> buffer I'll use 4 periods allowing a 250ms granularity in my intermediate
> buffer.
> I'm assuming that the snd_pcm_hardware struct has a min AND max period_bytes
> and periods because the framesize can vary if your hardware supports multiple
> configurations.  As mine only supports 8Khz 16bit stereo, my min/max values
> should all be the same correct?
> So using the above info my struct should look like:
> static snd_pcm_hardware_t snd_my_playback_hw = {
>         .info = (SNDRV_PCM_INFO_MMAP |
>                  SNDRV_PCM_INFO_INTERLEAVED |
>                  SNDRV_PCM_INFO_BLOCK_TRANSFER |
>                  SNDRV_PCM_INFO_MMAP_VALID),
>         .formats =          SNDRV_PCM_FMTBIT_U16_BE,
>         .rates =            SNDRV_PCM_RATE_8000,
>         .rate_min =         8000,
>         .rate_max =         8000,
>         .channels_min =     2,
>         .channels_max =     2,
>         .buffer_bytes_max = 32768,
>         .period_bytes_min = 8192,
>         .period_bytes_max = 8192,
>         .periods_min =      4,
>         .periods_max =      4,
> };
> Does this seem reasonable?

Should be OK, but maybe more finer periods would be better, I guess.
But this depends on the irq source you can use.

> Because I'm using a 32K intermediate buffer I'll preallocate it with
> snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
>   snd_dma_continuous_data(GFP_KERNEL), 0, 32768);
> I want to make sure I'm implementing things properly for full duplex
> capability.  I'm creating the pcm device via:
> snd_pcm_new(card, "my dev", 0, 1 /* play streams */, 1 /* capt streams */,
> &pcm)
> If I understand this preallocation a 32K DMAable buffer will be allocated by
> ALSA for each stream my device can play/capture (= 2 streams, 1 playback and 1
> capture).  I can obtain the buffer pre-allocated from the runtime data in the
> prepare callback and use that buffer as my intermediate buffer
> correct? 

If you call snd_pcm_lib_malloc_pages() in hw_params callback (and
snd_pcm_lib_free_pages() in hw_free callback).  Otherwise, the
pre-allocated buffer won't be assigned to the stream at all.

> I
> don't have a great grasp of the 'prepare' callback yet, but because thats the
> first time you've got access to valid format/buffer info I'll assume that each
> time 'prepare' is called your PCM buffer/hardware/stream should be initialized.
> If I understand correctly the pointer callback should return the num of frames
> processed in the 'intermediate buffer'.  Because this is a virtual ring buffer,
> I guess I should simply return the offset in the buffer (divided by my
> bytes_per_frame which is 4) that I last pulled data from?

Exactly.  It's an offset between 0 and (buffer_size-1).

> I've looked at the example dealing with the hw interrupt and period_elapsed in
> http://www.alsa-project.org/~iwai/writing-an-alsa-driver/x773.htm.  The example
> doesn't specify where it was getting runtime pointer from so I am assuming that
> I'll need to keep a pointer to each (playback and capture) substream so that I
> can call period_elapsed appropriately for each if they are concurrent?



More information about the Alsa-devel mailing list