以聲卡驅動的數據結構爲切入點分析:
/* 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. };
創建聲卡的功能部件(邏輯設備),例如PCM,Mixer,MIDI等。
每一種部件的創建最終會調用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()
- controlC0 --> 用於聲卡的控制,例如通道選擇,混音,麥克風的控制等
- midiC0D0 --> 用於播放midi音頻
- pcmC0D0c --〉 用於錄音的pcm設備
- pcmC0D0p --〉 用於播放的pcm設備
- seq --〉 音序器
- timer --〉 定時器
每個聲卡最多可以包含4個pcm的實例,每個pcm實例對應一個pcm設備文件。
ALSA已經爲我們實現了功能強勁的PCM中間層,自己的驅動中只要實現一些底層的需要訪問硬件的函數即可。大多數情況下,在嵌入式設備中,一個pcm實例已經足夠了。一個pcm實例由一個playback stream和一個capture stream組成,這兩個stream又分別有一個或多個substreams組成。
pcm中間層的幾個重要的結構體的關係圖
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 to */
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
struct snd_pcm_oss oss;
#endif
};
1 snd_pcm是掛在snd_card下面的一個snd_device
2 snd_pcm中的字段:streams[2],該數組中的兩個元素指向兩個snd_pcm_str結構,分別代表playback stream和capture stream。
3 snd_pcm_str中的substream字段,指向snd_pcm_substream結構
4 snd_pcm_substream是pcm中間層的核心,絕大部分任務都是在substream中處理,尤其是他的ops(snd_pcm_ops)字段,
許多user空間的應用程序通過alsa-lib對驅動程序的請求都是由該結構中的函數處理。它的runtime字段則指向snd_pcm_runtime結構,
snd_pcm_runtime記錄這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;
};
新建一個pcm可以用下面一張新建pcm的調用的序列圖進行描述:
新建pcm的序列圖
snd_pcm_set_ops 設置操作該pcm的控制/操作接口函數,參數中的snd_pcm_ops結構中的函數通常就是我們驅動要實現的函數snd_card_register 註冊聲卡,在這個階段會遍歷聲卡下的所有邏輯設備,並且調用各設備的註冊回調函數,
對於pcm,就是第二步提到的snd_pcm_dev_register函數,該回調函數建立了和用戶空間應用程序(alsa-lib)通信所用的設備文件節點:/dev/snd/pcmCxxDxxp
和/dev/snd/pcmCxxDxxc (這些設備節點由alsa-lib使用,用戶不直接使用)。
snd_pcm_dev_register調用snd_register_device_for_dev,snd_register_device_for_dev調用device_create創建設備節點。snd_register_device_for_dev有個參數
snd_pcm_f_ops,它是一個標準的文件系統file_operations結構數組,它的定義在sound/core/pcm_native.c中:並被記錄在snd_minors[minor]中的字段f_ops中。
/*
* 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,
}
};
在sound/core/sound.c中有alsa_sound_init()函數,其中有:
register_chrdev(major, "alsa", &snd_fops),參數major與之前創建pcm設備是device_create時的major是同一個,這樣的結果是,
當應用程序open設備文件/dev/snd/pcmCxDxp時,會進入snd_fops的open回調函數。
static const struct file_operations snd_fops =
{
.owner = THIS_MODULE,
.open = snd_open,
.llseek = noop_llseek,
};
snd_open函數,它首先從inode中取出此設備號,然後以次設備號爲索引,從snd_minors全局數組中取出當初註冊pcm設備時填充的snd_minor結構
,然後從snd_minor結構中取出pcm設備的f_ops,並且把file->f_op替換爲pcm設備的f_ops,緊接着直接調用pcm設備的f_ops->open(),然後返回。
因爲file->f_op已經被替換,以後,應用程序的所有read/write/ioctl調用都會進入pcm設備自己的回調函數中,也就是snd_pcm_f_ops結構中定義的回調。
本博內容均由http://blog.csdn.net/droidphone原創