数据结构
原图
- snd_card: 是最顶层数据结构,通过链表挂载该sound card的所有设备snd_device
- snd_pcm: 挂在snd_card下面的一个snd_device
- snd_pcm_str: 代表playback stream和capture stream
- snd_pcm_substream: 是pcm中间层的核心,绝大部分任务都是在substream中处理,尤其是他的ops(snd_pcm_ops)字段,许多user空间的应用程序通过alsa-lib对驱动程序的请求都是由该结构中的函数处理。它的runtime字段则指向snd_pcm_runtime结构,snd_pcm_runtime记录这substream的一些重要的软件和硬件运行环境和参数
- snd_pcm_ops: 创建声卡需要提供的回调
- snd_pcm_runtime: 运行时参数,包括sample rate、channel、format等参数,以及buffer相关信息
- snd_pcm_harward: 硬件相关参数,创建声卡需要提供相关参数
HW Buffer
当app在调用snd_pcm_writei时,alsa core将app传来的数据搬到HW buffer(即DMA buffer)中,alsa driver从HW buffer中读取数据传输到硬件播放。
ALSA buffer是采用ring buffer来实现的。ring buffer有多个HW buffer组成。HW buffer一般是在alsa driver的hw_params函数中分配的一块大小为buffer size的DMA buffer.之所以采用多个HW buffer来组成ring buffer,是防止读写指针的前后位置频繁的互换(即写指针到达HW buffer边界时,就要回到HW buffer起始点)。
ring buffer = n * HW buffer.通常这个n比较大,在数据读写的过程中,很少会出现读写指针互换的情况。下图是ALSA buffer的实现以及读写指针更新的方法:
- hw_ptr_base: 是当前HW buffer在Ring buffer中的起始位置。当读指针到达HW buffer尾部时,hw_ptr_base按buffer size移动
- hw_ptr: 即HW buffer的读指针。alsa driver将数据从HW buffer中读走并送到声卡硬件时,hw_ptr就会移动到新位置
- appl_ptr: 即HW buffer的写指针。app在调用snd_pcm_write写数据,alsa core将数据copy到HW buffer后,appl_ptr就更新
- boundary: 即Ring buffer边界
- hw_ofs: 是读指针在当前HW buffer中的位置。由alsa driver的pointer()返回
- appl_ofs: 是写指针在当前HW buffer中的位置
hw_ptr的更新是通过调用snd_pcm_update_hw_ptr()完成。此函数在app写数据时会调用,也会在硬件中断时通过snd_pcm_peroid_elapsed调用
流程
原图
- 分配并设置snd_card数据结构
- 创建snd_pcm instance
- 设置snd_pcm_ops结构体成员
- 分配DMA内存
- 注册声卡
参考文献
《Linux设备驱动开发-约翰-马德奥》