The creation framework for PCM device has been written before, so let's look at how PCM device is created.
A SND_PCM type of PCM device is created when the snd_pcm_new is called.
struct SND_PCM {
struct Snd_card *card;//pcm device says the sound card is mounted
struct List_head list;//A card may have multiple PCM DEVICE,PCM device lists
int device; /* Index of device number *///PCM device
unsigned int info_flags;
unsigned short dev_class;
unsigned short dev_subclass;
Char id[64];
Char name[80];
struct SND_PCM_STR STREAMS[2];//PCM playback and capture stream
struct Mutex Open_mutex;
wait_queue_head_t open_wait;
void *private_data;//private_data generally for chip-specific information
void (*private_free) (struct SND_PCM *pcm);//used to release Private_data
BOOL internal; /* PCM is for internal use only */
BOOL Nonatomic; /* Whole PCM operations is in non-atomic context */
#if defined (config_snd_pcm_oss) | | Defined (config_snd_pcm_oss_module)
struct Snd_pcm_oss oss;
#endif
};
Let's take a look at some of the actions in Snd_pcm_new:
static int _snd_pcm_new (struct snd_card *card, const char *id, int device,
int playback_count, int capture_count, BOOL internal,
struct SND_PCM **rpcm)
{
struct SND_PCM *pcm;
int err;
static struct Snd_device_ops Ops = {//PCM device operation function
. Dev_free = Snd_pcm_dev_free,
. dev_register =snd_pcm_dev_register,//in PCM device Rigister call
. Dev_disconnect = Snd_pcm_dev_disconnect,
};
if (snd_bug_on (!card))
Return-enxio;
if (RPCM)
*RPCM = NULL;
PCM = Kzalloc (sizeof (*PCM), gfp_kernel);//Create space for PCM device
if (!PCM)
Return-enomem;
Pcm->card = card;//to save the sound card in PCM device
Pcm->device = device;
Pcm->internal = internal;
Mutex_init (&pcm->open_mutex);
Init_waitqueue_head (&pcm->open_wait);
Init_list_head (&pcm->list);
if (ID)
strlcpy (pcm->id, id, sizeof (pcm->id));
if (err = Snd_pcm_new_stream (PCM, Sndrv_pcm_stream_playback, Playback_count)) < 0) {//Create PLAYBACK stream for PCM device
Snd_pcm_free (PCM);
return err;
}
if (err = Snd_pcm_new_stream (PCM, Sndrv_pcm_stream_capture, Capture_count)) < 0) {//Create CAPTURE stream for PCM device
Snd_pcm_free (PCM);
return err;
}
if (err = snd_device_new (Card, SNDRV_DEV_PCM, PCM, &ops)) < 0) {//Attach the PCM device to the card.
Snd_pcm_free (PCM);
return err;
}
if (RPCM)
*RPCM = PCM;
return 0;
}
A PCM device has a playback, capture stream, created by Snd_pcm_new_stream.
Playback and capture stream are a SND_PCM_STR structure,
struct SND_PCM_STR {
int stream;/* stream (direction) *///is playback stream or capture stream
struct SND_PCM * pcm;//the current PCM device
/*-substreams--*/
unsigned the number of int substream_count;//substream.
Unsigned int substream_opened;//The number of substream already open. Each open +1,close-1.
struct Snd_pcm_substream *substream;//playback stream or capture stream's substream linked list
#if defined (CONFIG_SND_PCM _oss) | | Defined (config_snd_pcm_oss_module)
/*--oss things--*/
struct snd_pcm_oss_stream oss;
#endif
#ifdef config_snd_verbose_procfs
struct snd_info_entry *proc_root;
struct Snd_info_entry *proc_info_entry;
#ifdef config_snd_pcm_xrun_debug
unsigned int xrun_debug;/* 0 = disabled, 1 = verbose, 2 = stacktrace */
Str UCT Snd_info_entry *proc_xrun_debug_entry;
#endif
#endif
struct Snd_kcontrol *chmap_kctl;/* channel-mapping controls */
struct device Dev;//stream Device Structure
};
Substream Structural body Snd_pcm_substream:
struct Snd_pcm_substream {
struct SND_PCM *PCM;//PCM Device
struct SND_PCM_STR *pstr;//playback stream or Substream
void *private_data;/* copied from pcm->private_data *///private_data are usually the same as the private data of PCM device.
int number;//Index of the current Substream
Char name[32];/* substream name */
int stream;/* Stream (direction) *///is playback or capture Substream
struct Pm_qos_request latency_pm_qos_req; /* Pm_qos Request */
size_t buffer_bytes_max;/* limit Ring buffer size *///the largest buffer size
struct Snd_dma_buffer DMA_BUFFER;//DMA Buffer
size_t Dma_max;
/*--Hardware Operations--* *
const struct Snd_pcm_ops *ops;//substream operation function
/*-Runtime information--* *
struct Snd_pcm_runtime *runtime;//runtime Information
/*--Timer section--*/
struct Snd_timer *timer;/* Timer */
Unsigned timer_running:1;/* time is running */
/*-Next Substream--* *
struct Snd_pcm_substream *next;
/*--linked Substreams--* *
struct List_head link_list;/* Linked list member */
struct Snd_pcm_group self_group;/* fake group for non linked Substream (with Substream lock inside) */
struct Snd_pcm_group *group;/* pointer to current group */
/*--Assigned files--* *
void *file;
int ref_count;
atomic_t Mmap_count;
unsigned int f_flags;
void (*pcm_release) (struct snd_pcm_substream *);
struct PID *pid;
#if defined (config_snd_pcm_oss) | | Defined (config_snd_pcm_oss_module)
/*--OSS things--* *
struct Snd_pcm_oss_substream oss;
#endif
#ifdef CONFIG_SND_VERBOSE_PROCFS
struct Snd_info_entry *proc_root;
struct Snd_info_entry *proc_info_entry;
struct Snd_info_entry *proc_hw_params_entry;
struct Snd_info_entry *proc_sw_params_entry;
struct Snd_info_entry *proc_status_entry;
struct Snd_info_entry *proc_prealloc_entry;
struct Snd_info_entry *proc_prealloc_max_entry;
#ifdef Config_snd_pcm_xrun_debug
struct Snd_info_entry *proc_xrun_injection_entry;
#endif
#endif/* CONFIG_SND_VERBOSE_PROCFS */
/* Misc Flags */
unsigned int hw_opened:1;
};
/**
* Snd_pcm_new_stream-create a new PCM stream
* @pcm: The PCM instance
* @stream: The stream direction, sndrv_pcm_stream_xxx
* @substream_count: The number of Substreams
*/
int Snd_pcm_new_stream (struct SND_PCM *pcm, int stream, int substream_count)
{
int idx, err;
struct SND_PCM_STR *pstr = &pcm->streams[stream];//Current playback stream or capture stream
struct Snd_pcm_substream *substream, *prev;
#if is_enabled (Config_snd_pcm_oss)
Mutex_init (&pstr->oss.setup_mutex);
#endif
Pstr->stream = stream;
PSTR->PCM = PCM;
Pstr->substream_count = Substream_count;
if (!substream_count)
return 0;
Snd_device_initialize (&pstr->dev, Pcm->card);
Pstr->dev.groups = pcm_dev_attr_groups;
Dev_set_name (&pstr->dev, "pcmc%id%i%c", Pcm->card->number, pcm->device, stream = = SNDRV_PCM_STREAM_ PLAYBACK? ' P ': ' C ');//playback Stream's name
if (!pcm->internal) {
Err = Snd_pcm_stream_proc_init (PSTR);//The playback stream is hung to the/proc directory, which we can view through cat.
if (Err < 0) {
Pcm_err (PCM, "Error in snd_pcm_stream_proc_init\n");
return err;
}
}
prev = NULL;
for (idx = 0, prev = NULL; idx < substream_count; idx++) {//A playback stream can have multiple substream, create Substream
Substrea m = Kzalloc (sizeof (*substream), Gfp_kernel);
if (!substream)
Return-enomem;
SUBSTREAM->PCM = PCM;
Substream->pstr = pstr;
Substream->number = idx;
Substream->stream = stream;
sprintf (Substream->name, "Subdevice #%i", IDX);
Substream->buffer_bytes_max = Uint_max; The
if (prev = = NULL)//links Substream to the Substream member of the playback stream.
Pstr->substream = Substream;
Else
Prev->next = substream;
if (!pcm->internal) {
Err = Snd_pcm_substream_proc_init (substream);//The Substream is hung under/proc.
if (Err < 0) {
Pcm_err (PCM, "Error in snd_pcm_stream_proc_init\n");
if (prev = = NULL)
Pstr->substream = NULL;
Else
Prev->next = NULL;
Kfree (Substream);
return err;
}
}
Substream->group = &substream->self_group;
Spin_lock_init (&substream->self_group.lock);
Mutex_init (&substream->self_group.mutex);
Init_list_head (&substream->self_group.substreams);
List_add_tail (&substream->link_list, &substream->self_group.substreams);
Atomic_set (&substream->mmap_count, 0);
prev = Substream;
}
return 0;
}
ALSA driver--PCM Device creation process