0%

Linux驱动之ALSA(六)声卡创建流程

数据结构

原图

  • 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设备驱动开发-约翰-马德奥》