以声卡驱动的数据结构为切入点分析:
/* SoC card */
struct snd_soc_card {
const char *name;
struct device *dev;
struct snd_card *snd_card; //在snd_soc_instantiate_card中利用snd_card_create创建声卡
struct module *owner;
struct list_head list;
struct mutex 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 (*set_bias_level_post)(struct snd_soc_card *,
enum snd_soc_bias_level level);
long pmdown_time;
/* CPU <--> Codec DAI links */
struct snd_soc_dai_link *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 snd_soc_aux_dev *aux_dev;
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;
//属于这个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(胶水) a codec and cpu DAI together */
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;
/* Symmetry data - only valid if symmetry 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 delayed_work;
};
1) struct snd_soc_codec - 由与平台无关的codec驱动实现。
2) struct snd_soc_platform - 由与imx平台相关的DAI驱动实现,主要实现了音频数据的DMA传输功能。
3) struct snd_soc_dai_link - 将平台相关的DAI与平台无关的codec联系起来。
与声音相关的逻辑设备都是在snd_card的管理之下,声卡驱动的第一个动作通常就是创建一个snd_card结构体。
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 声卡的私有数据,可以在创建声卡时通过参数指定数据的大小 */
12. void (*private_free) (struct snd_card *card); /* callback for freeing of private data */
13. struct list_head devices; /* devices 记录该声卡下所有逻辑设备的链表 */
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 记录该声卡下所有的控制单元的链表 */
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. };
每一种部件的创建最终会调用snd_device_new()来生成一个snd_device实例,并把该实例链接到snd_card的devices链表中。
通常,alsa-driver的已经提供了一些常用的部件的创建函数,而不必直接调用snd_device_new(),比如:
PCM ---- snd_pcm_new()
RAWMIDI -- snd_rawmidi_new()
CONTROL -- snd_ctl_create()
每个声卡最多可以包含4个pcm的实例,每个pcm实例对应一个pcm设备文件。
ALSA已经为我们实现了功能强劲的PCM中间层,自己的驱动中只要实现一些底层的需要访问硬件的函数即可。
pcm中间层的几个重要的结构体的关系图
新建一个pcm可以用下面一张新建pcm的调用的序列图进行描述:
新建pcm的序列图
snd_pcm_set_ops 设置操作该pcm的控制/操作接口函数,参数中的snd_pcm_ops结构中的函数通常就是我们驱动要实现的函数