Advanced Linux Sound Architecture - Driver: PCM - Digital Audio

Previous Next Table of Contents

## 5. PCM - Digital Audio

Digital Audio related structures and function is in include/pcm.h header file.

## 5.1 Variables and functions

Variables from snd_pcm_t structure which must be filled:

- info_flags look to SND_PCM_INFO_*
- name of pcm device (for example 'CS4231')
- playback variables for playback direction
- record variables for record direction

Variables from snd_pcm_t structure which should be filled:

- private_data should contains pointer to private data
- private_free should free private data

Variables from struct snd_stru_pcm_hardware which must be filled:

- flags
  - SND_PCM_HW_BATCH - hardware does double buffering
  - SND_PCM_HW_8BITONLY - hardware supports only 8-bit data (be careful - look to sb16.c)
  - SND_PCM_HW_16BITONLY - hardware supports only 16-bit data (be careful - look to sb16.c)
  - SND_PCM_HW_AUTODMA - hardware supports auto init dma transfer (unterminated loop)
- formats list of supported formats SND_PCM_FMT_*
  - if hardware doesn't support Mu-Law - it _must_ be emulated (converted), but it can't be listed here
- align - if transfer block must be aligned to some value - set this mask
- min_fragment - minimal fragment in 2ˆx
- min_rate - minimal rate in Hz
- max_rate - maximal rate in Hz
- max_voices - maximal number of voices
- int (*open)( snd_pcm_t *pcm )
  - pcm pointer to pcm structure
  - opens pcm direction and allocates dma buffer with snd_pcm_dma_alloc
- int (*close)( snd_pcm_t *pcm )
  - closes pcm direction and frees dma buffer with snd_pcm_dma_free
- void (*compute_rate)( snd_pcm_t *pcm )
  - computes snd_pcm_channel_t -> real_rate from snd_pcm_channel_t -> rate
- void (*prepare)( snd_pcm_t *pcm, unsigned char *buffer, unsigned int size, unsigned int offset, unsigned int count )
  - pcm pointer to pcm structure
  - buffer pointer to dma buffer
  - size used size of dma buffer
  - offset offset in bytes to dma buffer
    - this value is always zero if hardware supports auto init dma transfer mode
  - count of bytes to transfer (block size)
  - prepares pcm direction to output/input
- void (*trigger)( snd_pcm_t *pcm, int up )
  - pcm pointer to pcm structure
  - up if zero - trigger down (turn off), if nonzero - trigger up (turn on)
  - starts or stops pcm direction
- unsigned int (*pointer)( snd_pcm_t *pcm, unsigned int used_size )
  - pcm pointer to pcm structure
  - used_size - used size of dma buffer
  - returns actual byte pointer from pcm direction
- void (*dma)( snd_pcm_t *pcm, unsigned char *buffer, unsigned int offset, unsigned char *user, unsigned int count )
  - pcm pointer to pcm structure
  - buffer pointer to dma buffer
  - offset destonation/source offset in bytes to/from dma buffer
  - user pointer to buffer in user space
  - count transfer count in bytes
  - copies data from or to user space to or from kernel dma buffer
- void (*dma_move)( snd_pcm_t *pcm, unsigned char *buffer, unsigned int dest_offset, unsigned int src_offset, unsigned int count )
  - pcm pointer to pcm structure
  - buffer pointer to dma buffer
  - dest_offset destonation offset in bytes to dma buffer
  - src_offset source offset in bytes from dma buffer
  - count transfer count
  - transfer areas can never be overlapped
- void (*dma_neutral)( snd_pcm_t *pcm, unsigned char *buffer, unsigned offset, unsigned int count, unsigned char neutral_byte )
  - pcm pointer to pcm structure
  - buffer pointer to dma buffer
  - offset destonation offset in bytes to dma buffer
  - count count in bytes
  - neutral_byte - neutral byte
  - fills specified area of dma buffer with neutral byte

Variables from struct snd_stru_pcm_hardware which should be filled:

- private_data should contains pointer to private data for specified direction
- private_free should free private data

Functions list:

- void snd_pcm_playback_dma( snd_pcm_t *pcm, unsigned char *buffer, unsigned int offset, unsigned char *user, unsigned int count )
  - standard function for playback snd_pcm_channel_t -> hw.dma
- void snd_pcm_playback_dma_ulaw( snd_pcm_t *pcm, unsigned char *buffer, unsigned int offset, unsigned char *user, unsigned int count )
  - standard function for playback snd_pcm_channel_t -> hw.dma if hardware doesn't supports Mu-Law compression
- void snd_pcm_playback_dma_neutral( snd_pcm_t *pcm, unsigned char *buffer, unsigned int of unsigned int count, unsigned char neutral_byte )
  - standard function for playback snd_pcm_channel_t -> hw.dma_neutral
- void snd_pcm_record_dma( snd_pcm_t *pcm, unsigned char *buffer, unsigned int offset, unsigned char *user, unsigned int count )
  - standard function for record snd_pcm_channel_t -> hw.dma
- void snd_pcm_record_dma_ulaw( snd_pcm_t *pcm, unsigned char *buffer, unsigned int offset, unsigned char *user, unsigned int count )
  - standard function for record snd_pcm_channel_t -> hw.dma if hardware doesn't supports Mu-Law compression
- void snd_pcm_dma_move( snd_pcm_t *pcm, unsigned char *buffer, unsigned int dest_offset, unsigned int src_offset, unsigned int count )
  - standard function for snd_pcm_channel_t -> hw.dma_move

Functions for dma allocation:

- int snd_pcm_dma_alloc( snd_pcm_t *pcm, int direction, int dmanum, char *ident )
  - pcm pointer to pcm structure
  - direction pcm direction - SND_PCM_PLAYBACK or SND_PCM_RECORD
  - dmanum dma index
  - ident identification for dma owner
  - allocates pcm dma buffer
- int snd_pcm_dma_free( snd_pcm_t *pcm, int direction, int dmanum )
  - pcm pointer to pcm structure
  - direction pcm direction
  - dmanum dma index
  - frees pcm dma buffer

Functions for allocation and registering:

- snd_pcm_t *snd_pcm_new_device( snd_card_t *card, char *id )
  - card pointer to soundcard structure
  - id identification for pcm device (for example 'AD1848')
- int snd_pcm_free( snd_pcm_t *pcm )
  - pcm pointer to pcm structure
  - frees pcm structure and associated private data
- int snd_pcm_register( snd_pcm_t *pcm, int pcm_device )
  - pcm pointer to pcm structure
  - pcm_device number of pcm device (0-3)
  - registers pcm device
- int snd_pcm_unregister( snd_pcm_t *pcm )
  - pcm pointer to pcm structure
  - unregisters and frees pcm structure

## 5.2 Examples

> `

>     static struct snd_stru_pcm_hardware snd_es1688_playback = {
>       NULL,                         /* private data */
>       NULL,                         /* private_free */
>       SND_PCM_HW_AUTODMA,           /* flags */
>       SND_PCM_FMT_U8 | SND_PCM_FMT_S16_LE,  /* formats */
>       0,                            /* align value */
>       6,                            /* minimal fragment */
>       4000,                         /* min. rate */
>       48000,                        /* max. rate */
>       2,                            /* max. voices */
>       snd_es1688_playback_open,
>       snd_es1688_playback_close,
>       snd_es1688_playback_compute_rate,
>       snd_es1688_playback_prepare,
>       snd_es1688_playback_trigger,
>       snd_es1688_playback_pointer,
>       snd_pcm_playback_dma_ulaw,
>       snd_pcm_dma_move,
>       snd_pcm_playback_dma_neutral
>     };

>     static struct snd_stru_pcm_hardware snd_es1688_record = {
>       NULL,                         /* private data */
>       NULL,                         /* private free */
>       SND_PCM_HW_AUTODMA,           /* flags */
>       SND_PCM_FMT_U8 | SND_PCM_FMT_S16_LE,  /* formats */
>       0,                            /* align value */
>       6,                            /* minimal fragment */
>       4000,                         /* min. rate */
>       48000,                        /* max. rate */
>       2,                            /* max. voices */
>       snd_es1688_record_open,
>       snd_es1688_record_close,
>       snd_es1688_record_compute_rate,
>       snd_es1688_record_prepare,
>       snd_es1688_record_trigger,
>       snd_es1688_record_pointer,
>       snd_pcm_record_dma_ulaw,
>       snd_pcm_dma_move,
>       NULL
>     };

>     static void snd_es1688_free( void *private_data )
>     {
>       snd_free( private_data, sizeof( es1688_t ) );
>     }

>     snd_pcm_t *snd_es1688_new_device( snd_card_t *card,
>                                       unsigned short port,
>                                       unsigned short mpu_port,
>                                       unsigned short irqnum,
>                                       unsigned short mpu_irqnum,
>                                       unsigned short dma8num,
>                                       unsigned short hardware )
>     {
>       snd_pcm_t *pcm;
>       es1688_t *codec;

>       pcm = snd_pcm_new_device( card, "ESx688" );
>       if ( !pcm ) return NULL;
>       codec = (es1688_t *)snd_malloc( sizeof( es1688_t ) );
>       if ( !codec ) return NULL;
>       memset( codec, 0, sizeof( es1688_t ) );
>       snd_spin_prepare( codec, reg );
>       snd_spin_prepare( codec, mixer );
>       codec -> pcm = pcm;
>       codec -> card = pcm -> card;
>       codec -> port = port;
>       codec -> mpu_port = mpu_port;
>       codec -> irqnum = irqnum;
>       codec -> irq = pcm -> card -> irqs[ irqnum ] -> irq;
>       codec -> mpu_irqnum = mpu_irqnum;
>       if ( mpu_irqnum != SND_IRQ_DISABLE )
>         codec -> mpu_irq = pcm -> card -> irqs[ mpu_irqnum ] -> irq;
>       codec -> dma8num = dma8num;
>       codec -> dma8 = pcm -> card -> dmas[ dma8num ] -> dma;
>       codec -> hardware = hardware;
>       memcpy( &pcm -> playback.hw, &snd_es1688_playback, sizeof( snd_es1688_playback ) );
>       memcpy( &pcm -> record.hw, &snd_es1688_record, sizeof( snd_es1688_record ) );
>       pcm -> private_data = codec;
>       pcm -> private_free = snd_es1688_free;
>       pcm -> info_flags = SND_PCM_INFO_CODEC | SND_PCM_INFO_MMAP |
>                           SND_PCM_INFO_PLAYBACK | SND_PCM_INFO_RECORD;
>       sprintf( pcm -> name, "ES%s688 rev %i", codec -> hardware == ES1688_HW_688 ?"" : "1", codec -> version & 0x0f );
>       if ( snd_es1688_probe( pcm ) < 0 ) {
>         snd_pcm_free( pcm );
>         return NULL;
>       }
>       return pcm;
>     }

> `

PCM device registering:

> `

>       snd_card_t *card;
>       snd_pcm_t *pcm;

>       pcm = snd_es1688_new_device( card, port, mpu_port, irqnum, mpu_irqnum, dma8num, ES1688_HW_AUTO );
>       if ( !pcm ) return NULL;
>       ...
>       if ( snd_pcm_register( pcm, 0 ) ) {
>         ... unregister already registered devices ...
>         snd_pcm_free( pcm );
>         snd_card_free( card );
>         return -ENXIO;
>       }
>       ...
>       snd_pcm_unregister( pcm );

> `

Previous Next Table of Contents