Linux: ASoC 声卡硬件参数的设置过程简析

文章目录

  • 1. 前言
  • 2. ASoC 声卡设备硬件参数
    • 2.1 将 DAI、Machine 平台的硬件参数添加到声卡
    • 2.2 打开 PCM 流时将声卡硬件参数配置到 PCM 流
    • 2.3 应用程序对 PCM 流参数进行修改调整

1. 前言

限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。

2. ASoC 声卡设备硬件参数

ASoC(ALSA System-on-Chip) 声卡驱动框架如下图所示:
在这里插入图片描述
整个声卡驱动由 CPU DAI 驱动CODEC DAI 驱动Machine 驱动(胶水粘合驱动)3 者拼接构成,所以声卡硬件参数,自然也同时受这 3 者的共同约束。

声卡的硬件参数管理过程包括以下两步:

1. 将 DAI (CPU 侧 + CODEC 侧)、Machine 平台的 声卡硬件参数 添加到声卡
2. 打开 PCM 流时,将 声卡的硬件参数 配置到 PCM 流
3. 应用程序在硬件参数允许的范围内,对 PCM 流参数进行修改调整

2.1 将 DAI、Machine 平台的硬件参数添加到声卡

声卡硬件参数主要包括:通道数,数据格式(S16_LE,S32_LE,...),采样率,period 大小,FIFO 大小 等。这些声卡硬件参数来自于 DAI 接口驱动、Machine 平台驱动。这里以 CPU 侧 DAI 驱动硬件参数为例,来剖析声卡硬件参数的添加过程。如:

static struct snd_soc_dai_driver sun4i_i2s_dai = {.probe = sun4i_i2s_dai_probe,.capture = {.stream_name = "Capture",.channels_min = 2,.channels_max = 2,.rates = SNDRV_PCM_RATE_8000_192000,.formats = SNDRV_PCM_FMTBIT_S16_LE,},.playback = {.stream_name = "Playback",.channels_min = 2,.channels_max = 2,.rates = SNDRV_PCM_RATE_8000_192000,.formats = SNDRV_PCM_FMTBIT_S16_LE,},.ops = &sun4i_i2s_dai_ops,.symmetric_rates = 1,
};

上面的示例代码定义了 SoC CPU 一侧 DAI 接口 I2S 支持的通道数、数据格式、采样率,然后通过接口 snd_soc_register_component() ,将声卡 DAI 组件注册到系统,即添加到声卡组件对象(struct snd_soc_component)全局列表 component_list

devm_snd_soc_register_component(&pdev->dev,&sun4i_i2s_component,&sun4i_i2s_dai, 1)snd_soc_register_component()struct snd_soc_component *component;...component = kzalloc(sizeof(*component), GFP_KERNEL);...ret = snd_soc_component_initialize(component, component_driver, dev);...snd_soc_component_add(component)...list_add(&component->list, &component_list); /* (1) 添加到声卡组件全局列表 @component_list */

然后在注册声卡时,将声卡组件对象(struct snd_soc_component)添加到声卡(声卡组件对象(struct snd_soc_component)作为声卡的一部分),这一过程也将包含在 DAI 驱动内的 DAI 硬件参数添加到了声卡:

snd_soc_register_card()snd_soc_instantiate_card()soc_bind_dai_link()static int soc_bind_dai_link(struct snd_soc_card *card,struct snd_soc_dai_link *dai_link)
{.../* 集成 CPU 一侧 DAI (驱动) 到声卡 */rtd->cpu_dai = snd_soc_find_dai(&cpu_dai_component);snd_soc_rtdcom_add(rtd, rtd->cpu_dai->component);.../* Find CODEC from registered CODECs */codec_dais = rtd->codec_dais;for (i = 0; i < rtd->num_codecs; i++) { /* 集成 CODEC 一侧 DAI (驱动) 到声卡 */codec_dais[i] = snd_soc_find_dai(&codecs[i]); // 查找 DAI...snd_soc_rtdcom_add(rtd, codec_dais[i]->component); // 添加 DAI 到声卡(包括 DAI 携带的硬件参数)}...
}struct snd_soc_dai *snd_soc_find_dai(const struct snd_soc_dai_link_component *dlc)
{.../* Find CPU DAI from registered DAIs*/list_for_each_entry(component, &component_list, list) {// 匹配 DAI...list_for_each_entry(dai, &component->dai_list, list) {...return dai; // 返回匹配的 DAI}}...
}static int snd_soc_rtdcom_add(struct snd_soc_pcm_runtime *rtd,struct snd_soc_component *component)
{...list_add_tail(&new_rtdcom->list, &rtd->component_list);...
}

2.2 打开 PCM 流时将声卡硬件参数配置到 PCM 流

open("/dev/snd/pcmC%dD%dp", ...)...snd_pcm_playback_open() / snd_pcm_capture_open()snd_pcm_open()snd_pcm_open_file()snd_pcm_open_substream()struct snd_pcm_runtime {...struct snd_pcm_hardware hw;struct snd_pcm_hw_constraints hw_constraints;...
};int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,struct file *file,struct snd_pcm_substream **rsubstream)
{...// 将 DAI 等的硬件参数 配置到 PCM 流if ((err = substream->ops->open(substream)) < 0) // ASoC 架构下: soc_pcm_open() goto error;...// 将 参数 转换为 约束参数形式err = snd_pcm_hw_constraints_complete(substream);...
}// 将 DAI 等的硬件参数 配置到 PCM 流
soc_pcm_open() // ASoC 架构下soc_pcm_init_runtime_hw()static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream)
{struct snd_pcm_runtime *runtime = substream->runtime;struct snd_pcm_hardware *hw = &runtime->hw;...struct snd_soc_pcm_stream *codec_stream;struct snd_soc_pcm_stream *cpu_stream;...// CPU DAIif (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)cpu_stream = &cpu_dai_drv->playback; // &sun4i_i2s_dai.playbackelsecpu_stream = &cpu_dai_drv->capture; // &sun4i_i2s_dai.capture.../* 配置声卡 DAI 的参数到 PCM 流 */hw->channels_min = max(chan_min, cpu_stream->channels_min);hw->channels_max = min(chan_max, cpu_stream->channels_max);if (hw->formats)hw->formats &= formats & cpu_stream->formats;elsehw->formats = formats & cpu_stream->formats;...
}int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
{struct snd_pcm_runtime *runtime = substream->runtime;struct snd_pcm_hardware *hw = &runtime->hw;......err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_ACCESS, mask);...err = snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT, hw->formats);...err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS,hw->channels_min, hw->channels_max);...err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIODS,hw->periods_min, hw->periods_max);...err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,hw->period_bytes_min, hw->buffer_bytes_max);...
}int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,unsigned int min, unsigned int max)
{struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints;struct snd_interval t;t.min = min;t.max = max;t.openmin = t.openmax = 0;t.integer = 0;return snd_interval_refine(constrs_interval(constrs, var), &t);
}

2.3 应用程序对 PCM 流参数进行修改调整

2.2 小节中,在 open("/dev/snd/pcmC%dD%dp", ...) 打开 PCM 流时,设置了 PCM 的默认硬件参数,但这并不一定符合用户的预期,譬如采样率,通道数,数据格式等,这时候用户空间可以通过 ioctl(fd, SNDRV_PCM_IOCTL_HW_PARAMS, &hwparams) 来进行调整:

sys_ioctl()...snd_pcm_ioctl()snd_pcm_common_ioctl()snd_pcm_hw_params_user()snd_pcm_hw_params()static int snd_pcm_hw_params_user(struct snd_pcm_substream *substream,struct snd_pcm_hw_params __user * _params)
{struct snd_pcm_hw_params *params;.../* 复制用户空间参数到内核空间 */params = memdup_user(_params, sizeof(*params));.../* 按用户请求参数 @_params,对当前设置的参数做可能的调整 */err = snd_pcm_hw_params(substream, params);.../* 修改后的参数,从 @_params 返回用户空间 */if (copy_to_user(_params, params, sizeof(*params)))err = -EFAULT;
end:kfree(params);return err;
}static int snd_pcm_hw_params(struct snd_pcm_substream *substream,struct snd_pcm_hw_params *params)
{struct snd_pcm_runtime *runtime;...params->rmask = ~0U;err = snd_pcm_hw_refine(substream, params); /* 对 所有 硬件参数,按用户请求参数 @params,做可能的调整 */.../* 更新 用户参数 @params 到 PCM 流 */runtime->access = params_access(params);runtime->format = params_format(params);runtime->subformat = params_subformat(params);runtime->channels = params_channels(params);runtime->rate = params_rate(params);runtime->period_size = params_period_size(params);runtime->periods = params_periods(params);runtime->buffer_size = params_buffer_size(params);runtime->info = params->info;runtime->rate_num = params->rate_num;runtime->rate_den = params->rate_den;...return 0;_error:...
}int snd_pcm_hw_refine(struct snd_pcm_substream *substream,struct snd_pcm_hw_params *params)
{.../* 对所有 掩码类型参数 做可能的调整 */err = constrain_mask_params(substream, params);.../* 对所有 区间类型参数 做可能的调整 */err = constrain_interval_params(substream, params);.../* 对 所有类型(掩码类 + 区间类) 的所有参数 按 约束规则 做可能的调整 */err = constrain_params_by_rules(substream, params);...
}

这列只看 constrain_interval_params() 的调整参数的过程,其它的类似:

static int constrain_interval_params(struct snd_pcm_substream *substream,struct snd_pcm_hw_params *params)
{// 这里的 ,正是前面 2.2 分析 PCM 打开过程中, snd_pcm_hw_constraint_minmax() 设置的struct snd_pcm_hw_constraints *constrs =&substream->runtime->hw_constraints;struct snd_interval *i;unsigned int k;...for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) {i = hw_param_interval(params, k);.../* 对区间类型参数做可能的调整 */changed = snd_interval_refine(i, constrs_interval(constrs, k));...}return 0;
}

到此,对声卡硬件参数管理设置过程分析已毕。对应硬件参数,声卡还有软件参数,对这部分感兴趣的读者,可自行阅读相关源码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/70685.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ansible使用学习

一、查询手册 1、官网 ansible官网地址&#xff1a;https://docs.ansible.com 模块查看路径&#xff1a;https://docs.ansible.com/ansible/latest/collections/ansible/builtin/index.html#plugins-in-ansible-builtin 2、命令 ansible-doc -s command二、相关脚本 1、服务…

jmap使用

常用命令 jmap -heap PID jmap -histo PID | head -20 jmap -dump:formatb,fileheap_dump.hprof PID jmap 是 Java 开发工具包&#xff08;JDK&#xff09;提供的一个命令行工具&#xff0c;用于生成 Java 进程的内存映射信息。它可以帮助开发者分析 Java 堆内存的使用情况…

基于 SpringBoot 和 Vue 的智能腰带健康监测数据可视化平台开发(文末联系,整套资料提供)

基于 SpringBoot 和 Vue 的智能腰带健康监测数据可视化平台开发 一、系统介绍 随着人们生活水平的提高和健康意识的增强&#xff0c;智能健康监测设备越来越受到关注。智能腰带作为一种新型的健康监测设备&#xff0c;能够实时采集用户的腰部健康数据&#xff0c;如姿势、运动…

docker离线安装及部署各类中间件(x86系统架构)

前言&#xff1a;此文主要针对需要在x86内网服务器搭建系统的情况 一、docker离线安装 1、下载docker镜像 https://download.docker.com/linux/static/stable/x86_64/ 版本&#xff1a;docker-23.0.6.tgz 2、将docker-23.0.6.tgz 文件上传到服务器上面&#xff0c;这里放在…

从零到一:我的元宵灯谜小程序诞生记

缘起&#xff1a;一碗汤圆引发的灵感 去年元宵节&#xff0c;我正捧着热腾腾的汤圆刷朋友圈&#xff0c;满屏都是"转发锦鲤求灯谜答案"的动态。看着大家对着手机手忙脚乱地切换浏览器查答案&#xff0c;我突然拍案而起&#xff1a;为什么不做一个能即时猜灯谜的微信…

CSS3+动画

浏览器内核以及其前缀 css标准中各个属性都要经历从草案到推荐的过程&#xff0c;css3中的属性进展都不一样&#xff0c;浏览器厂商在标准尚未明确的情况下提前支持会有风险&#xff0c;浏览器厂商对新属性的支持情况也不同&#xff0c;所有会加厂商前缀加以区分。如果某个属性…

2025.2.8——二、Confusion1 SSTI模板注入|Jinja2模板

题目来源&#xff1a;攻防世界 Confusion1 目录 一、打开靶机&#xff0c;整理信息 二、解题思路 step 1&#xff1a;查看网页源码信息 step 2&#xff1a;模板注入 step 3&#xff1a;构造payload&#xff0c;验证漏洞 step 4&#xff1a;已确认为SSTI漏洞中的Jinjia2…

数字电路-基础逻辑门实验

基础逻辑门是数字电路设计的核心元件&#xff0c;它们执行的是基本的逻辑运算。通过这些基本运算&#xff0c;可以构建出更为复杂的逻辑功能。常见的基础逻辑门包括与门&#xff08;AND&#xff09;、或门&#xff08;OR&#xff09;、非门&#xff08;NOT&#xff09;、异或门…

HC32功能复用说明

目录 引脚有哪些功能如何选择功能代码 引脚有哪些功能 数据手册中&#xff0c;每一个引脚功能有至多64个&#xff0c;对应列Func0~Func63 其中&#xff0c;Func0 ~Func31在《表 2-1 引脚功能表》中列出 Func32~Func63在《表 2-2 Func32~63 表》中列出。 Func32~Func63中的功…

数据库管理-第293期 奇怪的sys.user$授权+(20250210)

数据库管理293期 2025-02-10 数据库管理-第293期 奇怪的sys.user$授权&#xff08;20250210&#xff09;1 清空shared pool2 SR反馈总结 数据库管理-第293期 奇怪的sys.user$授权&#xff08;20250210&#xff09; 作者&#xff1a;胖头鱼的鱼缸&#xff08;尹海文&#xff09…

AutoMQ 如何实现没有写性能劣化的极致冷读效率

前言 追赶读&#xff08;Catch-up Read&#xff0c;冷读&#xff09;是消息和流系统常见和重要的场景。 削峰填谷&#xff1a;对于消息来说&#xff0c;消息通常用作业务间的解耦和削峰填谷。削峰填谷要求消息队列能将上游发送的数据堆积住&#xff0c;让下游在容量范围内消费…

【大模型】本地部署DeepSeek-R1:8b大模型及搭建Open-WebUI交互页面

本地部署DeepSeek-R1:8b大模型 一、摘要及版本选择说明1.1 摘要1.2 版本选择 二、下载并安装Ollama三、运行DeepSeek-R1:8b大模型四、安装Open WebUI增强交互体验五、关闭Ollama开机自动启动六、DeepSeek大模型启停步骤 一、摘要及版本选择说明 1.1 摘要 作为一名对 AI 和生成…

DeepSeek大模型的发展的十问十答

DeepSeek大模型是由杭州深度求索人工智能基础技术研究有限公司开发的一款基于Transformer架构的大型语言模型&#xff0c;具体介绍如下&#xff1a; 1. 架构基础 Transformer架构&#xff1a;DeepSeek大模型基于Transformer架构&#xff0c;该架构由Google在2017年提出&#xf…

Avnet RFSoC基于maltab得5G 毫米波 开发工具箱

使用 MATLAB 连接到 AMD Zynq™ RFSoC 评估板。使用 RF 附加卡执行 OTA 测试。使用 HDL Coder 部署算法 版本要求&#xff1a; 大于 2023b 需要以下支持包之一&#xff1a; 适用于 Xilinx 基于 Zynq 的无线电&#xff08;R2023b 及更早版本&#xff09;的通信工具箱支持包适…

计算机毕业设计Python+Spark知识图谱医生推荐系统 医生门诊预测系统 医生数据分析 医生可视化 医疗数据分析 医生爬虫 大数据毕业设计 机器学习

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

Vue事件处理 - 绑定事件

Vue 渐进式JavaScript 框架 基于Vue2的学习笔记 - Vue事件处理 - 绑定事件及事件处理 目录 事件处理 绑定方式 函数表达式 绑定函数名 输入框绑定事件 拿到输入框的值 传值加事件源 事件第三种写法 总结 事件处理 绑定方式 函数表达式 在按钮上使用函数表达式绑定事…

World of Warcraft [CLASSIC] 80 Four Horsemen (Naxxramas)

纳克萨玛斯 天启四骑士 Four Horsemen 图一&#xff1a;10人同生共死 图二&#xff1a;25人同生共死站位 图三&#xff0c;不做同生共死&#xff0c;做永恒者&#xff0c;击杀白马分布图&#xff0c;主要是不熟练乱跑&#xff0c;容易导致减员失败 永恒者&#xff0c;玩家无一…

DeepSeek与AI提示语设计的全面指南

当人人都会用AI时&#xff0c;你如何用得更好更出彩&#xff1f;本文全面介绍了DeepSeek的功能与使用方法&#xff0c;并深入探讨了AI提示语设计的核心技巧与进阶策略。通过精准的任务定义、提示语优化和人机协作&#xff0c;用户可以从AI的基础使用逐步进阶到创新应用&#xf…

HarmonyOS Next 方舟字节码文件格式介绍

在开发中&#xff0c;可读的编程语言要编译成二进制的字节码格式才能被机器识别。在HarmonyOS Next开发中&#xff0c;arkts会编译成方舟字节码。方舟字节码长什么样呢&#xff1f;我们以一个demo编译出的abc文件&#xff1a; 二进制就是长这样&#xff0c;怎么去理解呢&…

TCP/IP 协议图解 | TCP 协议详解 | IP 协议详解

注&#xff1a;本文为 “TCP/IP 协议” 相关文章合辑。 未整理去重。 TCP/IP 协议图解 退休的汤姆 于 2021-07-01 16:14:25 发布 TCP/IP 协议简介 TCP/IP 协议包含了一系列的协议&#xff0c;也叫 TCP/IP 协议族&#xff08;TCP/IP Protocol Suite&#xff0c;或 TCP/IP Pr…