Linux ALSA驱动之三:PCM创建流程源码分析(基于Linux 5.18)上

简介: Linux ALSA驱动之三:PCM创建流程源码分析(基于Linux 5.18)上

Linux ALSA驱动之三:PCM创建流程源码分析(基于Linux 5.18)上


1、基本概念及逻辑关系

如上图,通过上一节声卡的学习我们已经知道PCM是声卡的一个子设备,或者表示一个PCM实例。

每个声卡最多可以包含4个pcm的实例,每个pcm实例对应一个pcm设备文件。pcm实例数量的这种限制源于linux设备号所占用的位大小,如果以后使用64位的设备号,我们将可以创建更多的pcm实例。不过大多数情况下,在嵌入式设备中,一个pcm实例已经足够了。

一个pcm实例由一个playback stream和一个capture stream组成,这两个stream又分别有一个或多个substreams组成。可以用如下图来表示他们直接的逻辑关系:

当一个子流已经存在,并且已经被打开,当再次被打开的时候,会被阻塞。        

在实际的应用中,通常不会如上图这么复杂,大多数情况下是一个声卡有一个PCM实例,PCM下面有一个playback和capture,而playback和capture各自有一个substream。

PCM层有几个很重要的结构体,我们通过如下的UML图来梳理他们直接的关系。

图片地址:http://hi.csdn.net/attachment/201104/2/0_1301728746sAUd.gif

       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的一些重要的软件和硬件运行环境和参数。

2、PCM创建流程

       PCM的整个创建流程请参考如下时序图进行理解:

alsa-driver的中间层已经提供新建PCM的API:

2.1、创建PCM实例

int snd_pcm_new(struct snd_card *card, const char *id, int device,
    int playback_count, int capture_count, struct snd_pcm **rpcm)

card:表示所属的声卡。

       ID:PCM实例的ID(名字)。

       device:表示目前创建的是该声卡下的第几个PCM,第一个PCM设备从0开始计数。

       playback_count:表示该PCM播放流中将会有几个substream。

       capture_count :表示该PCM录音流中将会有几个substream。

       rpcm:返回的PCM实例。

       该函数的主要作用是创建PCM逻辑设备,创建回放子流和录制子流实例,并初始化回放子流和录制子流的PCM操作函数(数据搬运时,需要调用这些函数来驱动 codec、codec_dai、cpu_dai、dma 设备工作)。

2.2、设置PCM设备的操作函数

void snd_pcm_set_ops(struct snd_pcm *pcm, int direction,
         const struct snd_pcm_ops *ops)

pcm:上述snd_pcm_new 创建的PCM实例。

       direction:是指SNDRV_PCM_STREAM_PLAYBACK或SNDRV_PCM_STREAM_CAPTURE,即设置为播放或者录音功能。

       snd_pcm_ops:结构中的函数通常就是我们驱动要实现的函数。

2.3、定义PCM的操作函数

       以AC97驱动(linux/sound/arm/pxa2xx-ac97.c)为例,在驱动中对于PCM进行了如下设置:

static const struct snd_pcm_ops pxa2xx_ac97_pcm_ops = {
  .open   = pxa2xx_ac97_pcm_open,
  .close    = pxa2xx_ac97_pcm_close,
  .hw_params  = pxa2xx_pcm_hw_params,
  .prepare  = pxa2xx_ac97_pcm_prepare,
  .trigger  = pxa2xx_pcm_trigger,
  .pointer  = pxa2xx_pcm_pointer,
};
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pxa2xx_ac97_pcm_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pxa2xx_ac97_pcm_ops);

2.4、定义硬件参数

static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
  .info     = SNDRV_PCM_INFO_MMAP |
          SNDRV_PCM_INFO_MMAP_VALID |
          SNDRV_PCM_INFO_INTERLEAVED |
          SNDRV_PCM_INFO_PAUSE |
          SNDRV_PCM_INFO_RESUME,
  .formats    = SNDRV_PCM_FMTBIT_S16_LE |
          SNDRV_PCM_FMTBIT_S24_LE |
          SNDRV_PCM_FMTBIT_S32_LE,
  .period_bytes_min = 32,
  .period_bytes_max = 8192 - 32,
  .periods_min    = 1,
  .periods_max    = 256,
  .buffer_bytes_max = 128 * 1024,
  .fifo_size    = 32,
};
int pxa2xx_pcm_open(struct snd_pcm_substream *substream)
{
  struct snd_soc_pcm_runtime *rtd = substream->private_data;
  struct snd_pcm_runtime *runtime = substream->runtime;
  struct snd_dmaengine_dai_dma_data *dma_params;
  int ret;
  runtime->hw = pxa2xx_pcm_hardware;
  dma_params = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
  if (!dma_params)
    return 0;
  /*
   * For mysterious reasons (and despite what the manual says)
   * playback samples are lost if the DMA count is not a multiple
   * of the DMA burst size.  Let's add a rule to enforce that.
   */
  ret = snd_pcm_hw_constraint_step(runtime, 0,
    SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
  if (ret)
    return ret;
  ret = snd_pcm_hw_constraint_step(runtime, 0,
    SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
  if (ret)
    return ret;
  ret = snd_pcm_hw_constraint_integer(runtime,
              SNDRV_PCM_HW_PARAM_PERIODS);
  if (ret < 0)
    return ret;
  return snd_dmaengine_pcm_open(
    substream, dma_request_slave_channel(asoc_rtd_to_cpu(rtd, 0)->dev,
                 dma_params->chan_name));
}


相关文章
|
20天前
|
存储 Oracle 安全
服务器数据恢复—LINUX系统删除/格式化的数据恢复流程
Linux操作系统是世界上流行的操作系统之一,被广泛用于服务器、个人电脑、移动设备和嵌入式系统。Linux系统下数据被误删除或者误格式化的问题非常普遍。下面北亚企安数据恢复工程师简单聊一下基于linux的文件系统(EXT2/EXT3/EXT4/Reiserfs/Xfs) 下删除或者格式化的数据恢复流程和可行性。
|
2月前
|
监控 安全 Java
linux服务器上启动framework应用程序流程
【10月更文挑战第17天】在Linux服务器上启动Framework应用程序需经过准备工作、部署、启动、监控及访问五个步骤。首先确保服务器满足系统要求并安装依赖项;接着上传应用文件,编译构建,配置参数;然后通过脚本、命令行或系统服务启动应用;启动后检查日志,监控性能;最后确认访问地址,验证应用运行状态。具体操作应参照应用文档。
|
2月前
|
监控 Java Linux
linux服务器上启动framework应用程序流程
【10月更文挑战第18天】在 Linux 服务器上启动框架应用程序的流程包括:准备工作(确保访问权限、上传部署文件、了解启动要求)、检查依赖项、配置环境变量、切换到应用程序目录、启动应用程序、监控启动过程以及验证应用程序是否正常运行。具体步骤可能因应用程序类型和框架而异。
|
4月前
|
Java Linux API
Linux设备驱动开发详解2
Linux设备驱动开发详解
60 6
|
4月前
|
消息中间件 算法 Unix
Linux设备驱动开发详解1
Linux设备驱动开发详解
61 5
|
3月前
|
Linux API
Linux里的高精度时间计时器(HPET)驱动 【ChatGPT】
Linux里的高精度时间计时器(HPET)驱动 【ChatGPT】
|
Linux Windows
LINUX编译alsa
LINUX编译alsa
145 0
|
1月前
|
Linux 网络安全 数据安全/隐私保护
Linux 超级强大的十六进制 dump 工具:XXD 命令,我教你应该如何使用!
在 Linux 系统中,xxd 命令是一个强大的十六进制 dump 工具,可以将文件或数据以十六进制和 ASCII 字符形式显示,帮助用户深入了解和分析数据。本文详细介绍了 xxd 命令的基本用法、高级功能及实际应用案例,包括查看文件内容、指定输出格式、写入文件、数据比较、数据提取、数据转换和数据加密解密等。通过掌握这些技巧,用户可以更高效地处理各种数据问题。
107 8
|
1月前
|
监控 Linux
如何检查 Linux 内存使用量是否耗尽?这 5 个命令堪称绝了!
本文介绍了在Linux系统中检查内存使用情况的5个常用命令:`free`、`top`、`vmstat`、`pidstat` 和 `/proc/meminfo` 文件,帮助用户准确监控内存状态,确保系统稳定运行。
377 6
|
1月前
|
Linux
在 Linux 系统中,“cd”命令用于切换当前工作目录
在 Linux 系统中,“cd”命令用于切换当前工作目录。本文详细介绍了“cd”命令的基本用法和常见技巧,包括使用“.”、“..”、“~”、绝对路径和相对路径,以及快速切换到上一次工作目录等。此外,还探讨了高级技巧,如使用通配符、结合其他命令、在脚本中使用,以及实际应用案例,帮助读者提高工作效率。
86 3