Start with the data structure driven by sound cards:
/* SOC card */struct snd_soc_card {const char * Name; struct device * dev; struct snd_card * snd_card; // create a sound card struct module * owner using snd_card_create in snd_soc_instantiate_card; struct list_head list; struct mutex; bool instantiated; int (* probe) (struct snd_soc_card * card); int (* late_probe) (struct snd_soc_card * card); int (* remove) (struct snd_soc_card * card);/* the pre and post PM functions are Used to do any PM work before and * after the codec and Dai's do any PM work. */INT (* suspend_pre) (struct snd_soc_card * card); int (* suspend_post) (struct snd_soc_card * card); int (* resume_pre) (struct snd_soc_card * card ); INT (* resume_post) (struct snd_soc_card * card);/* callbacks */INT (* set_bias_level) (struct snd_soc_card *, Enum snd_soc_bias_level level); int (* Second) (struct snd _ Soc_card *, Enum running level); long pmdown_time;/* CPU <--> codec Dai links */struct running * dai_link; int num_links; struct snd_soc_pcm_runtime * RTD; int num_rtd; /* optional codec specific configuration */struct snd_soc_codec_conf * codec_conf; int num_configs;/* optional auxiliary devices such as amplifiers or codecs with Dai link unused */struct login * aux_de V; int num_aux_devs; struct snd_soc_pcm_runtime * rtd_aux; int num_aux_rtd;/* card-specific routes and widgets. */struct snd_soc_dapm_widget * dapm_widgets; int num_dapm_widgets; struct snd_soc_dapm_route * dapm_routes; int num_dapm_routes; struct work_struct deferred_resume_work; // list of devices detected by this card/* lists of probed devices belonging to this card */struct list_head codec_dev_list; struct LIST _ Head platform_dev_list; struct list_head dai_dev_list; struct list_head widgets; struct list_head paths; struct list_head dapm_list;/* generic dapm context for the card */struct snd_soc_dapm_context dapm; # ifdef config_debug_fs struct dentry * debugfs_card_root; struct dentry * debugfs_pop_time; # endif u32 pop_time; void * drvdata ;};/* SOC machine Dai configuration, glues (glue) A codec and CPU Dai toge Ther */struct snd_soc_pcm_runtime {struct device dev; struct snd_soc_card * card; struct snd_soc_dai_link * dai_link; unsigned int complete: 1; unsigned int dev_registered: 1; /* Transport ry data-only valid if transport ry is being enforced */unsigned int rate; long pmdown_time;/* runtime devices */struct snd_pcm * PCM; struct snd_soc_codec * codec; struct snd_soc_platform * platform; struct snd_soc_dai * Codec_dai; struct snd_soc_dai * cpu_dai; struct delayed_work;}; 1) struct snd_soc_codec-implemented by a platform-independent codec driver. 2) struct snd_soc_platform-implemented by the Dai driver related to The IMX platform, mainly implementing the DMA transmission function of audio data. 3) struct snd_soc_dai_link-associate platform-related Dai with platform-independent codec.Sound-related logical devices are managed by snd_card. The first action of a sound card driver is to create an snd_card structure. 1. struct snd_card {2. int number;/* Number of Soundcard (index to snd_cards) */3. char ID [16];/* ID string of this card */4. char driver [16];/* driver name */5. char shortname [32];/* short name of this Soundcard */6. char longname [80];/* Name of this Soundcard */7. char mixername [80];/* mixer name */8. char components [128];/* card components delimited with space */9. struct module * module;/* top-level module */10. 11. void * private_data;/* private data for Soundcard private data of the sound card. You can specify the data size through parameters when creating the sound card */12. void (* private_free) (struct snd_card * card);/* callback for freeing of private data */13. struct list_head devices;/* devices records the linked list of all logical devices under the sound card */14. 15. unsigned int last_numid;/* last used numeric ID */16. struct rw_semaphore controls_rwsem;/* Controls List lock */17. rwlock_t ctl_files_rwlock;/* ctl_files list lock */18. int controls_count;/* count of all controls */19. int user_ctl_count;/* count of all user controls */20. struct list_head controls;/* all controls for this card records the linked list of all control units under the sound card */21. struct list_head ctl_files;/* Active Control Files */22. 23. struct snd_info_entry * proc_root;/* root for Soundcard specific files */24. struct snd_info_entry * proc_id;/* The card ID */25. struct proc_dir_entry * proc_root_link;/* Number link to real ID */26. 27. struct list_head files_list;/* all files associated to this card */28. struct snd_shutdown_f_ops * s_f_ops;/* file operations in the shutdown state */29. spinlock_t files_lock;/* lock the files for this card */30. int shutdown;/* this card is going down */31. int free_on_last_close;/* free in context of file_release */32. wait_queue_head_t shutdown_sleep; 33. struct device * dev;/* Device assigned to this card */34. # ifndef config_sysfs_deprecated 35. struct device * card_dev;/* cardx object for sysfs */36. # endif 37. 38. # ifdef config_pm 39. unsigned int power_state;/* power state */40. struct mutex power_lock;/* power lock */41. wait_queue_head_t power_sleep; 42. # endif 43. 44. # If defined (config_snd_mixer_oss) | defined (config_snd_mixer_oss_module) 45. struct snd_mixer_oss * mixer_oss; 46. int mixer_oss_change_count; 47. # endif 48 .};
Creates sound card functional components (logical devices), such as PCM, mixer, and Midi.
The creation of each component will eventually call snd_device_new () to generate an snd_device instance and link the instance to the snd_card devices linked list.
Generally, the Alsa-driver has provided some common component creation functions, instead of directly calling snd_device_new (), such:
PCM ---- snd_pcm_new ()
Rawmidi -- snd_rawmidi_new ()
Control -- snd_ctl_create ()
- Controlc0 --> used for sound card control, such as channel selection, sound mixing, and microphone Control
- Midic0d0 --> used to play MIDI audio
- Pcmc0d0c --> PCM device used for recording
- Pcmc0d0p --> PCM device used for playback
- Seq --> sequencer
- Timer --> Timer
Core this directory contains the intermediate layer of the Alsa driver, it is the core part of the entire ALSA driver I2C ALSA's own I2C control code SOC for the System-on-chip system's intermediate layer code soc/codecs for the SoC system's various codec code, platform independent
Each sound card can contain up to four PCM instances. Each PCM instance corresponds to a PCM device file.
ALSA has implemented a powerful PCM Middle Layer for us. In our own driver, we only need to implement some underlying functions that need to access hardware.
In most cases, a PCM instance is sufficient for an embedded device. A pcm instance consists of a playback stream and a capture stream. The two streams have one or more substreams respectively.
Relationship between several important structs In the PCM Middle Layer
Struct snd_pcm {
Struct snd_card * card;
Struct list_head list;
Int device;/* Device Number */
Unsigned int info_flags;
Unsigned short dev_class;
Unsigned short dev_subclass;
Char ID [64];
Char name [80];
Struct snd_pcm_str streams [2];
Struct mutex open_mutex;
Wait_queue_head_t open_wait;
Void * private_data;
Void (* private_free) (struct snd_pcm * PCM );
Struct device * dev;/* Actual HW device this belongs */
# If defined (config_snd_pcm_oss) | defined (config_snd_pcm_oss_module)
Struct snd_pcm_oss OSS;
# Endif
};
1 snd_pcm is an snd_device mounted under snd_card
2 snd_pcm field: streams [2]. The two elements in this array point to two snd_pcm_str structures, representing playback stream and capture stream respectively.
3. The substream field in snd_pcm_str points to the snd_pcm_substream structure.
4 snd_pcm_substream is the core of the PCM intermediate layer. Most tasks are processed in substream, especially in the OPS (snd_pcm_ops) field,
Many user space applications use ALSA-lib to process driver requests by functions in this structure. Its runtime field points to the snd_pcm_runtime structure,
Snd_pcm_runtime records important software and hardware Running Environments and parameters of the substream.
Struct snd_pcm_str {
Int stream;/* stream (Direction )*/
Struct snd_pcm * PCM;
/* -- Substreams --*/
Unsigned int substream_count;
Unsigned int substream_opened;
Struct snd_pcm_substream * substream;
# 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 */
Struct snd_info_entry * proc_xrun_debug_entry;
# Endif
# Endif
};
Struct snd_pcm_substream {
Struct snd_pcm * PCM;
Struct snd_pcm_str * pstr;
Void * private_data;/* copied from PCM-> private_data */
Int number;
Char name [32];/* substream name */
Int stream;/* stream (Direction )*/
Struct pm_qos_request_list latency_pm_qos_req;/* pm_qos Request */
Size_t buffer_bytes_max;/* Limit Ring buffer size */
Struct snd_dma_buffer dma_buffer;
Unsigned int dma_buf_id;
Size_t dma_max;
/* -- Hardware operations --*/
Struct snd_pcm_ops * OPS;
/* -- Runtime information --*/
Struct snd_pcm_runtime * runtime;
/* -- 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;
# Endif
/* MISC flags */
Unsigned int hw_opened: 1;
};
You can use the following sequence diagram to describe a new PCM call:
Sequence Diagram for creating PCM
Snd_pcm_set_opsSet the control/operation interface function to operate on the PCM. The functions in the snd_pcm_ops structure in the parameter are usually the functions we need to implement.
Snd_card_registerRegister a sound card. At this stage, all logical devices under the sound card are traversed and the registration callback function of each device is called,
For PCM, It is the snd_pcm_dev_register function mentioned in step 2. This callback function establishes the device file node used to communicate with the user space application (ALSA-Lib):/dev/snd/pcmcxxdxxp
And/dev/snd/pcmcxxdxxc (these device nodes are used by ALSA-lib and are not directly used by users ).
Snd_pcm_dev_register calls snd_register_device_for_dev, and snd_register_device_for_dev calls device_create to create a device node. Snd_register_device_for_dev has a parameter.
Snd_pcm_f_ops is a standard file system file_operations structure array, which is defined in sound/CORE/pcm_native.c and recorded in the field f_ops in snd_minors [minor.
/*
* Register Section
*/
// Used in PCM. c
Const struct file_operations snd_pcm_f_ops [2] = {
{
. Owner = this_module,
. Write = snd_pcm_write,
. Aio_write = snd_pcm_aio_write,
. Open = snd_pcm_playback_open,
. Release = snd_pcm_release,
. Llseek = no_llseek,
. Poll = snd_pcm_playback_poll,
. Unlocked_ioctl = snd_pcm_playback_ioctl,
. Compat_ioctl = snd_pcm_ioctl_compat,
. MMAP = snd_pcm_mmap,
. Fasync = snd_pcm_fasync,
. Get_unmapped_area = snd_pcm_get_unmapped_area,
},
{
. Owner = this_module,
. Read = snd_pcm_read,
. Aio_read = snd_pcm_aio_read,
. Open = snd_pcm_capture_open,
. Release = snd_pcm_release,
. Llseek = no_llseek,
. Poll = snd_pcm_capture_poll,
. Unlocked_ioctl = snd_pcm_capture_ioctl,
. Compat_ioctl = snd_pcm_ioctl_compat,
. MMAP = snd_pcm_mmap,
. Fasync = snd_pcm_fasync,
. Get_unmapped_area = snd_pcm_get_unmapped_area,
}
};
In sound/CORE/sound. C, the alsa_sound_init () function is available, including:
Register_chrdev (Major, "ALSA", & snd_fops). The major parameter is the same as the major parameter when the PCM device was created as device_create. The result is,
When the application opens the device file/dev/snd/pcmcxdxp, the open callback function of snd_fops is entered.
Static const struct file_operations snd_fops =
{
. Owner = this_module,
. Open = snd_open,
. Llseek = noop_llseek,
};
Snd_open function, which first retrieves the device number from inode, then uses the secondary device number as the index, and obtains the snd_minor structure filled when the PCM device was registered from the snd_minors global array.
And then extract the f_ops of the PCM device from the snd_minor structure, replace file-> f_op with the f_ops of the PCM device, and then directly call the f_ops-> open () of the PCM device (), then return.
Because file-> f_op has been replaced, all the read/write/IOCTL calls of the application will go to the PCM device's own callback function, that is, the callback defined in the snd_pcm_f_ops structure.
This blog is based on http://blog.csdn.net/droidphone.