ALSA Timer

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/pengfei240/article/details/79152013

0. 前言

本文主要介绍alsa-timer相关代码的分析内容。

1. 介绍

官方文档可以参见下面的链接:

ALSA Timer

官网上对Timer的介绍很简单,我根据自己的使用情况总结如下:

  • Timer被设计为使用声卡内部的定时器,但是也可以被其它定时器驱动,比如内核中的 snd_timer.ko 就是一个基于系统定时器的Timer。
  • Timer使用循环Buffer来存储信息。
  • 定时器最高精度是ns级,但实际的精度依赖于时钟源。
  • 可以使用 NONBLOCK 或 BLOCK 的模式打开定时器。
  • 如果需要查询Timer的状态,需要使用 timer_query 的接口。

我们后面主要是基于NONBLOCK模式进行分析。

2. Timer ID

Timer ID主要由以下5个字段组成:

名称 说明
class 区分声卡类别 SNDRV_TIMER_CLASS_GLOBAL 全局声卡,也就是snd_timer.ko提供的默认Timer,只能有一个
SNDRV_TIMER_CLASS_CARD Card提供的声卡
SNDRV_TIMER_CLASS_PCM PCM提供的声卡
sclass 区分声卡子类 SNDRV_TIMER_SCLASS_APPLICATION 表明是应用程序使用
card 声卡ID - -
device 区分时钟源 SNDRV_TIMER_GLOBAL_SYSTEM 全局定时器
SNDRV_TIMER_GLOBAL_HPET 高精度定时器
SNDRV_TIMER_GLOBAL_HRTIMER 高精度定时器
subdevice - 如果有必要可以用于区分 Capture / Playback

3. 用户使用

用户使用定时器比较简单,试例代码可以参见alsa-lib源码包,路径为:alsa-lib-1.1.4.1/test/timer.c

其主要的流程图如下(NONBLOCK模式):

用户流程图

4. 内核部分

初始化

ALSA的默认Timer是使用 snd_timer.ko 提供的SNDRV_TIMER_CLASS_GLOBAL定时器,该定时器使用系统定时器作为时钟源,且不属于任何声卡。

初始化流程如下图:

Timer Init

内核主要数据联系图

主要数据结构图如下:

数据结构图

5. 代码的一些分析

  • ALSA初始化时会使用结构体 snd_timer 来管理定时器本身,所有定时器由 snd_timer_list 串联在一起。具体的硬件操作由结构体 snd_timer_hardware 管理。
  • 用户空间使用 snd_timer_t 记录定时器 handle。它使用 async_handlers 链表挂载回调函数,因此,理论上一个定时器可以挂载多个回调函数(多线程中处理多条音轨可能会用到)。
  • /dev/timer 节点打开时会创建结构体 snd_timer_user ,并与文件节点绑定。在该结构体中会记录该用户的 Timer 的运行情况,如overrun计数;也会维护一个环形buffer作为消息队列,其中消息队列有 snd_timer_readsnd_timer_tread 两种格式,任选其一。
  • SNDRV_TIMER_IOCTL_SELECT 命令中会通过 snd_timer_instance_new 函数创建结构体 snd_timer_instance 。该结构体作为联系定时器和用户的桥梁,会绑定具体使用的 snd_timer 。主要负责记录用户和定时器的回调函数,并使用链表跟踪定时器需要做的进一步动作。比如,当需要发送消息给用户时,snd_timer_instance 会将 ack_list 挂载到 snd_timerack_list_headack_list_head 链表下进行消息发送,前者表明消息需要在定时器中断上下文中发送,后者表示消息需要在tasklet上下文中发送。

猜你喜欢

转载自blog.csdn.net/pengfei240/article/details/79152013