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

Takashi Iwai tiwai at suse.de
Thu Apr 12 15:00:18 CEST 2007


At Wed, 11 Apr 2007 18:18:54 -0700 (PDT),
Tim Harvey wrote:
> 
> Greetings,
> 
> I'm setting out to write an ALSA driver for a very limited device thats behind
> a PLD.  The actual codec is a uda1380 and does 16bit MSB stereo encode/decode. 
> The way the device is driven the sampling rate is limited to 8KHz and data
> transfer to/from the device I have to handle manually.
> 
> I've read over the 'writing an ALSA driver' tutorial and have started the
> driver but have run into a few questions:
> 
>  - for the specs I've given above: 8000Hz sampling rate, 16bit stereo MSB
> samples does the following snd_pcm_hardware_t look right?:
> 
> static snd_pcm_hardware_t 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,  // is this 16bit MSB?
>         .rates =            SNDRV_PCM_RATE_8000,      // seems redundant?
>         .rate_min =         8000,
>         .rate_max =         8000,
>         .channels_min =     2,
>         .channels_max =     2,
>         .buffer_bytes_max = 32768,
>         .period_bytes_min = 4096,
>         .period_bytes_max = 32768,
>         .periods_min =      1,
>         .periods_max =      1024,
> };

Looks fine to me.  rates field shouldn't be empty, so it's not
redundant (although it might work without it).

>  - I'm a little confused at how to deal with copying data from/to the device. 
> It looks like I would implement the copy callback and copy the passed buffer to
> the device in the device specific manner I need to (playback), but I'm not
> clear where to put data that I receive from the device (capture).  I notice
> that when the hw_params callback is called a dma buffer has been created by
> ALSA in the substream->runtime struct.  I assume this is created based on hte
> buffer_bytes_max field above and I'm to copy data to this buffer?  How do I
> know where to copy it within the buffer and what data ALSA has already consumed
> from the buffer?

It's a little bit complicated right now.  If you don't need mmap mode
transfer, the thing gets easier, but it restricts application usage.

Without mmap mode, you can implement copy and silence callbacks to
copy the user-space data directly to the hardware buffer.  In this
case, the period and buffer sizes in snd_pcm_hardware have to be
identical with what the hardware can accept.  ISA GUS and emu8000_pcm
are examples.

Otherwise, you'd need an intermediate buffer.  In this case, the
transfer to the hardware is done either in the irq handler (via ack
callback) or through a dedicated workqueue in background.

>  - I might need to create an intermediate buffer between ALSA's rather large
> buffers and my hardware buffer.  Is there any driver that I could look at that
> does this?  The tutorial talks about the vxpocket driver but that code looks
> very foriegn from the rest of the ALSA drivers.

The vx driver implementation is complex and not well suitable as a
reference.   If the manual copy can be done relatively fast, you can
use helper functions in  pcm_indirect.h, as in emu10k1/emupcm.c,
rme32 and cs46xx.  They transfer the data via ack callback, which is
invoked from snd_pcm_period_elapsed().

Actually, this is a missing piece in the current ALSA design, and now
becoming a frequently asked thing.  Maybe it's good to think of a
standard framework for non-DMA type driver implementation.

>  - I'm not sure I understand what the 'period' is about.  Its clear to me that
> I need to call snd_pcm_period_elapsed() periodically from either an IRQ or
> other timed callback, but I'm not clear what the hardware pointer is or how
> that converts to 'frames' and 'periods'.

The hardware pointer (hw_ptr) is the current position of the
transfer.  Note that this is in frames unit.
Meanwhile, the application pointer (appl_ptr) is the current position
of the filled data by the app.  Thus, appl_ptr - hw_ptr gives you the
number of the rest data to be processed.

Both pointers are linear and _not_ the offset in a ring-buffer.  It
can be between 0 and runtime->boundary-1 (ca. LONG_MAX, aligned to
period_size).

However, the pointer callback returns the offset in a ring-buffer,
i.e. between 0 and (buffer_size-1).  Then the pcm core layer
recomputes the linear position.

The "frame" represents the unit, 1 frame = # channels x sample_bytes.
In your case, 1 frame corresponds to 2 channels x 16 bits = 4 bytes.

The periods is the number of periods in a ring-buffer.  In OSS, called
as "fragments".

So,
 - buffer_size = period_size * periods
 - period_bytes = period_size * bytes_per_frame
 - bytes_per_frame = channels * bytes_per_sample 
    
>  - in general, if my hardware device only allows 16bit MSB 8KHz, does ALSA
> provides a mechanism for converting data from other formats?

Yes, it's done in alsa-lib on user-space.


Takashi


More information about the Alsa-devel mailing list