当前位置: 首页 > news >正文

ALSA学习(5)——ASoC架构中的Machine

参考博客:https://blog.csdn.net/DroidPhone/article/details/7231605
(以下内容皆为原博客转载)

文章目录

  • 一、注册Platform Device
  • 二、注册Platform Driver
  • 三、初始化入口soc_probe()

一、注册Platform Device

ASoC把声卡注册为Platform Device,我们以装配有WM8994的一款Samsung的开发板SMDK为例子做说明,WM8994是一颗Wolfson生产的多功能Codec芯片。
代码的位于:/sound/soc/samsung/smdk_wm8994.c,我们关注模块的初始化函数:

static int __init smdk_audio_init(void)
{int ret;smdk_snd_device = platform_device_alloc("soc-audio", -1);if (!smdk_snd_device)return -ENOMEM;platform_set_drvdata(smdk_snd_device, &smdk);ret = platform_device_add(smdk_snd_device);if (ret)platform_device_put(smdk_snd_device);return ret;
}

由此可见,模块初始化时,注册了一个名为soc-audio的Platform设备,同时把smdk设到platform_device结构的dev.drvdata字段中,这里引出了第一个数据结构snd_soc_card的实例smdk,他的定义如下:

static struct snd_soc_dai_link smdk_dai[] = {{ /* Primary DAI i/f */.name = "WM8994 AIF1",.stream_name = "Pri_Dai",.cpu_dai_name = "samsung-i2s.0",.codec_dai_name = "wm8994-aif1",.platform_name = "samsung-audio",.codec_name = "wm8994-codec",.init = smdk_wm8994_init_paiftx,.ops = &smdk_ops,}, { /* Sec_Fifo Playback i/f */.name = "Sec_FIFO TX",.stream_name = "Sec_Dai",.cpu_dai_name = "samsung-i2s.4",.codec_dai_name = "wm8994-aif1",.platform_name = "samsung-audio",.codec_name = "wm8994-codec",.ops = &smdk_ops,},
};static struct snd_soc_card smdk = {.name = "SMDK-I2S",.owner = THIS_MODULE,.dai_link = smdk_dai,.num_links = ARRAY_SIZE(smdk_dai),
};

通过snd_soc_card结构,又引出了Machine驱动的另外两个个数据结构:
snd_soc_dai_link(实例:smdk_dai[] )
snd_soc_ops(实例:smdk_ops )
其中,snd_soc_dai_link中,指定了Platform、Codec、codec_dai、cpu_dai的名字,稍后Machine驱动将会利用这些名字去匹配已经在系统中注册的platform,codec,dai,这些注册的部件都是在另外相应的Platform驱动和Codec驱动的代码文件中定义的,这样看来,Machine驱动的设备初始化代码无非就是选择合适Platform和Codec以及dai,用他们填充以上几个数据结构,然后注册Platform设备即可。当然还要实现连接Platform和Codec的dai_link对应的ops实现,本例就是smdk_ops,它只实现了hw_params函数:smdk_hw_params

二、注册Platform Driver

按照Linux的设备模型,有platform_device,就一定会有platform_driver。ASoC的platform_driver在以下文件中定义:sound/soc/soc-core.c。

还是先从模块的入口看起:

static int __init snd_soc_init(void)
{......return platform_driver_register(&soc_driver);
}

soc_driver的定义如下:

/* ASoC platform driver */
static struct platform_driver soc_driver = {.driver		= {.name		= "soc-audio",.owner		= THIS_MODULE,.pm		= &soc_pm_ops,},.probe		= soc_probe,.remove		= soc_remove,
};

我们看到platform_driver的name字段为soc-audio,正好与platform_device中的名字相同,按照Linux的设备模型,platform总线会匹配这两个名字相同的device和driver,同时会触发soc_probe的调用,它正是整个ASoC驱动初始化的入口。

三、初始化入口soc_probe()

soc_probe函数本身很简单,它先从platform_device参数中取出snd_soc_card,然后调用snd_soc_register_card,通过snd_soc_register_card,为snd_soc_pcm_runtime数组申请内存,每一个dai_link对应snd_soc_pcm_runtime数组的一个单元,然后把snd_soc_card中的dai_link配置复制到相应的snd_soc_pcm_runtime中,最后,大部分的工作都在snd_soc_instantiate_card中实现,下面就看看snd_soc_instantiate_card做了些什么:

该函数首先利用card->instantiated来判断该卡是否已经实例化,如果已经实例化则直接返回,否则遍历每一对dai_link,进行codec、platform、dai的绑定工作,下只是代码的部分选节,详细的代码请直接参考完整的代码树。

	/* bind DAIs */for (i = 0; i < card->num_links; i++)soc_bind_dai_link(card, i);

ASoC定义了三个全局的链表头变量:codec_list、dai_list、platform_list,系统中所有的Codec、DAI、Platform都在注册时连接到这三个全局链表上。soc_bind_dai_link函数逐个扫描这三个链表,根据card->dai_link[]中的名称进行匹配,匹配后把相应的codec,dai和platform实例赋值到card->rtd[]中(snd_soc_pcm_runtime)。经过这个过程后,snd_soc_pcm_runtime:(card->rtd)中保存了本Machine中使用的Codec,DAI和Platform驱动的信息。
snd_soc_instantiate_card接着初始化Codec的寄存器缓存,然后调用标准的alsa函数创建声卡实例:

	/* card bind complete so register a sound card */ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,card->owner, 0, &card->snd_card);card->snd_card->dev = card->dev;card->dapm.bias_level = SND_SOC_BIAS_OFF;card->dapm.dev = card->dev;card->dapm.card = card;list_add(&card->dapm.list, &card->dapm_list);

然后,依次调用各个子结构的probe函数:

	/* initialise the sound card only once */if (card->probe) {ret = card->probe(card);if (ret < 0)goto card_probe_error;}/* early DAI link probe */for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;order++) {for (i = 0; i < card->num_links; i++) {ret = soc_probe_dai_link(card, i, order);if (ret < 0) {pr_err("asoc: failed to instantiate card %s: %d\n",card->name, ret);goto probe_dai_err;}}}for (i = 0; i < card->num_aux_devs; i++) {ret = soc_probe_aux_dev(card, i);if (ret < 0) {pr_err("asoc: failed to add auxiliary devices %s: %d\n",card->name, ret);goto probe_aux_dev_err;}}

在上面的soc_probe_dai_link()函数中做了比较多的事情,把他展开继续讨论:

static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order)
{....../* set default power off timeout */rtd->pmdown_time = pmdown_time;/* probe the cpu_dai */if (!cpu_dai->probed &&cpu_dai->driver->probe_order == order) {if (cpu_dai->driver->probe) {ret = cpu_dai->driver->probe(cpu_dai);}cpu_dai->probed = 1;/* mark cpu_dai as probed and add to card dai list */list_add(&cpu_dai->card_list, &card->dai_dev_list);}/* probe the CODEC */if (!codec->probed &&codec->driver->probe_order == order) {ret = soc_probe_codec(card, codec);}/* probe the platform */if (!platform->probed &&platform->driver->probe_order == order) {ret = soc_probe_platform(card, platform);}/* probe the CODEC DAI */if (!codec_dai->probed && codec_dai->driver->probe_order == order) {if (codec_dai->driver->probe) {ret = codec_dai->driver->probe(codec_dai);}/* mark codec_dai as probed and add to card dai list */codec_dai->probed = 1;list_add(&codec_dai->card_list, &card->dai_dev_list);}/* complete DAI probe during last probe */if (order != SND_SOC_COMP_ORDER_LAST)return 0;ret = soc_post_component_init(card, codec, num, 0);if (ret)return ret;....../* create the pcm */ret = soc_new_pcm(rtd, num);........return 0;
}

该函数出了挨个调用了codec,dai和platform驱动的probe函数外,在最后还调用了soc_new_pcm()函数用于创建标准alsa驱动的pcm逻辑设备。现在把该函数的部分代码也贴出来:

/* create a new pcm */
int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
{......struct snd_pcm_ops *soc_pcm_ops = &rtd->ops;soc_pcm_ops->open	= soc_pcm_open;soc_pcm_ops->close	= soc_pcm_close;soc_pcm_ops->hw_params	= soc_pcm_hw_params;soc_pcm_ops->hw_free	= soc_pcm_hw_free;soc_pcm_ops->prepare	= soc_pcm_prepare;soc_pcm_ops->trigger	= soc_pcm_trigger;soc_pcm_ops->pointer	= soc_pcm_pointer;ret = snd_pcm_new(rtd->card->snd_card, new_name,num, playback, capture, &pcm);/* DAPM dai link stream work */INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);rtd->pcm = pcm;pcm->private_data = rtd;if (platform->driver->ops) {soc_pcm_ops->mmap = platform->driver->ops->mmap;soc_pcm_ops->pointer = platform->driver->ops->pointer;soc_pcm_ops->ioctl = platform->driver->ops->ioctl;soc_pcm_ops->copy = platform->driver->ops->copy;soc_pcm_ops->silence = platform->driver->ops->silence;soc_pcm_ops->ack = platform->driver->ops->ack;soc_pcm_ops->page = platform->driver->ops->page;}if (playback)snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, soc_pcm_ops);if (capture)snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, soc_pcm_ops);if (platform->driver->pcm_new) {ret = platform->driver->pcm_new(rtd);if (ret < 0) {pr_err("asoc: platform pcm constructor failed\n");return ret;}}pcm->private_free = platform->driver->pcm_free;return ret;
}

该函数首先初始化snd_soc_runtime中的snd_pcm_ops字段,也就是rtd->ops中的部分成员,例如open,close,hw_params等,紧接着调用标准alsa驱动中的创建pcm的函数snd_pcm_new()创建声卡的pcm实例,pcm的private_data字段设置为该runtime变量rtd,然后用platform驱动中的snd_pcm_ops替换部分pcm中的snd_pcm_ops字段,最后,调用platform驱动的pcm_new回调,该回调实现该platform下的dma内存申请和dma初始化等相关工作。到这里,声卡和他的pcm实例创建完成。

回到snd_soc_instantiate_card函数,完成snd_card和snd_pcm的创建后,接着对dapm和dai支持的格式做出一些初始化合设置工作后,调用了 card->late_probe(card)进行一些最后的初始化合设置工作,最后则是调用标准alsa驱动的声卡注册函数对声卡进行注册:

	if (card->late_probe) {ret = card->late_probe(card);if (ret < 0) {dev_err(card->dev, "%s late_probe() failed: %d\n",card->name, ret);goto probe_aux_dev_err;}}snd_soc_dapm_new_widgets(&card->dapm);if (card->fully_routed)list_for_each_entry(codec, &card->codec_dev_list, card_list)snd_soc_dapm_auto_nc_codec_pins(codec);ret = snd_card_register(card->snd_card);if (ret < 0) {printk(KERN_ERR "asoc: failed to register soundcard for %s\n", card->name);goto probe_aux_dev_err;}

至此,整个Machine驱动的初始化已经完成,通过各个子结构的probe调用,实际上,也完成了部分Platfrom驱动和Codec驱动的初始化工作,整个过程可以用一下的序列图表示:
基于3.0内核 soc_probe序列图
在这里插入图片描述
基于2.6.35 soc_probe序列图
在这里插入图片描述

相关文章:

ALSA学习(5)——ASoC架构中的Machine

参考博客&#xff1a;https://blog.csdn.net/DroidPhone/article/details/7231605 &#xff08;以下内容皆为原博客转载&#xff09; 文章目录 一、注册Platform Device二、注册Platform Driver三、初始化入口soc_probe() 一、注册Platform Device ASoC把声卡注册为Platform …...

LeetCode 0447.回旋镖的数量:哈希表

【LetMeFly】447.回旋镖的数量&#xff1a;哈希表 力扣题目链接&#xff1a;https://leetcode.cn/problems/number-of-boomerangs/ 给定平面上 n 对 互不相同 的点 points &#xff0c;其中 points[i] [xi, yi] 。回旋镖 是由点 (i, j, k) 表示的元组 &#xff0c;其中 i 和…...

容器相关笔记

目录 1.容器 1.什么是容器 2.java中的容器 3.容器里存放的是引用数据类型&#xff08;存对象的地址&#xff0c;不是对象本身&#xff09;&#xff0c;不能存基本数据类型 4.容器存放的两种格式 5.容器类所在的包 6.容器的分类 1.Collection&#xff0c;存放单一的类型 1.List&…...

cissp 第10章 : 物理安全要求

10.1 站点与设施设计的安全原则 物理控制是安全防护的第一条防线&#xff0c;而人员是最后一道防线。 10.1.1 安全设施计划 安全设施计划描述了组织的安全要求的轮廓&#xff0c; 并且着重强调为了提供安全性所用的方法和机制。 这样的计划通过被称为关键路径分析的过程进行开…...

聊一聊 .NET高级调试 内核模式堆泄露

一&#xff1a;背景 1. 讲故事 前几天有位朋友找到我&#xff0c;说他的机器内存在不断的上涨&#xff0c;但在任务管理器中查不出是哪个进程吃的内存&#xff0c;特别奇怪&#xff0c;截图如下&#xff1a; 在我的分析旅程中都是用户态模式的内存泄漏&#xff0c;像上图中的…...

海外代理IP在游戏中有什么作用?

随着科技的飞速发展&#xff0c;手机和电脑等电子产品已成为互联网连接万物的重要工具&#xff0c;深度融入我们的日常生活&#xff0c;我们借助互联网完成工作、休闲和购物等任务&#xff0c;以求提升生活质量。 不仅如此&#xff0c;网络游戏也是人们心中最爱&#xff0c;它…...

高防ip适合防御网站和游戏类的攻击吗?

​  作为站长&#xff0c;要学会并承受得住网站外来攻击的压力&#xff0c;尤其是所属为 DDoS 攻击高发行业的网站类业务及游戏行业&#xff0c;是很容易被竞争对手或者一些伪黑客爱好者盯上的。 加上&#xff0c;有些站长并没有提前了解&#xff0c;就盲目进军了这两个行业&…...

HTML5和JS实现明媚月色效果

HTML5和JS实现明媚月色效果 先给出效果图&#xff1a; 源码如下&#xff1a; <!DOCTYPE html> <html> <head><title>明媚月光效果</title><style>body {margin: 0;overflow: hidden;background-color: #000; /* 添加一个深色背景以便看到…...

Django5+DRF序列化

概述 本教程将介绍如何创建一个简单的粘贴板代码高亮 Web API。在此过程中&#xff0c;它将介绍构成 REST 框架的各种组件&#xff0c;让你全面了解所有组件是如何组合在一起的。 本教程相当深入&#xff0c;因此在开始学习之前&#xff0c;你可能需要先吃一块饼干&#xff0…...

什么是编译程序和解释程序

一、编译程序 1、编译器接收源代码作为输入&#xff0c;它会一次性地将整个源代码程序转换成目标代码&#xff08;通常是机器语言或汇编语言&#xff09;&#xff0c;这个过程包括词法分析、语法分析、语义分析、优化以及最终的目标代码生成。2、编译后的目标代码是一个独立的…...

文档审阅批注的合并和对比

#创作灵感# 最近在改论文&#xff0c;Feedback返回的时候&#xff0c;把之前的批注都删了&#xff0c;这就增加了工作量&#xff0c;看起来不方便&#xff0c;所以就需要将删掉的批注全部复原。 那在原来的文档重新在修改一遍&#xff0c;工作量还是很大的&#xff0c;所以这里…...

广义零样本学习综述的笔记

1 Title A Review of Generalized Zero-Shot Learning Methods&#xff08;Farhad Pourpanah; Moloud Abdar; Yuxuan Luo; Xinlei Zhou; Ran Wang; Chee Peng Lim&#xff09;【IEEE Transactions on Pattern Analysis and Machine Intelligence 2022】 2 conclusion Generali…...

java每日一题——输出9x9乘法表(答案及编程思路)

前言&#xff1a; 打好基础&#xff0c;daydayup! 题目&#xff1a;输出下图9x9乘法表 编程思路&#xff1a;java只能输出行&#xff0c;不能输出列&#xff0c;所以考虑好每一行输出的内容即可 public class demo {public static void main(String[] args) {for (int i 1; i…...

Android 车联网——基础简介(一)

传统的车载功能单一,无太多娱乐性,而随着智能化时代的发展,车载系统也被赋予了在系统中预装 Android 应用的能力,基于Android平台的车载信息娱乐系统 —— Android AutoMotive 应运而生。 一、AutoMotive简介 Android Automotive OS 车载操作系统,是一个基本 Android 平台…...

自动驾驶货车编队行驶系统功能规范

货车编队行驶功能规范 Truck Platooning Functional Specification 目录 1 概述... 7 1.1 目的... 7 1.2 范围... 7 1.3 术语及缩写... 7 1.4 参考法规标准... 8 2 功能规范... 9 2.1 功能描述... 9 2.1.1 功能用途…...

javafx

JavaFX JavaFX简介 JavaFX是一个用于创建富客户端应用程序的图形用户界面&#xff08;GUI&#xff09;框架。它是Java平台的一部分&#xff0c;从Java 8开始成为Java的标准库。 JavaFX提供了丰富的图形和多媒体功能&#xff0c;使开发人员能够创建具有吸引力和交互性的应用程…...

玩转贝启科技BQ3588C开源鸿蒙系统开发板 —— 编译构建及此过程中的踩坑填坑(3)

接前一篇文章&#xff1a;玩转贝启科技BQ3588C开源鸿蒙系统开发板 —— 编译构建及此过程中的踩坑填坑&#xff08;2&#xff09; 上一篇文章结束时在等待提示的各依赖包下载安装后的编译结果&#xff0c;但是很遗憾&#xff0c;编译并没有最终完成&#xff0c;既未成功也没有失…...

SQL ORDER BY 关键字

ORDER BY 关键字用于对结果集进行排序。 SQL ORDER BY 关键字 ORDER BY 关键字用于对结果集按照一个列或者多个列进行排序。 ORDER BY 关键字默认按照升序对记录进行排序。如果需要按照降序对记录进行排序&#xff0c;您可以使用 DESC 关键字。 SQL ORDER BY 语法 SELECT …...

多线程-生产者消费者模型

一、基本信息 1、场景介绍&#xff1a;厨师和吃货的例子&#xff0c;吃货吃桌子上的面条&#xff0c;吃完让厨师做&#xff0c;厨师做完面条放桌子上&#xff0c;让吃货吃&#xff0c;厨师如果发现桌子上有面条&#xff0c;就不做&#xff0c;吃货发现桌子上没有面条就不吃。 …...

解压命令之一 gzip

文章目录 解压命令之一 gzip更多信息 解压命令之一 gzip gzip用于对后缀为gz文件进行解压&#xff1a; $ gzip -d data.gz这个命令将解压examplefile.gz&#xff0c;并且在当前目录下生成一个名为data的解压后的文件。 但特别需要留意的是&#xff0c;这个操作会删除源文件&…...

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇&#xff0c;在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下&#xff1a; 【Note】&#xff1a;如果你已经完成安装等操作&#xff0c;可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作&#xff0c;重…...

(二)原型模式

原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作

一、上下文切换 即使单核CPU也可以进行多线程执行代码&#xff0c;CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短&#xff0c;所以CPU会不断地切换线程执行&#xff0c;从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系&#xff0c;以下是深入解析&#xff1a; 门铃FIFO溢出的本质 在RapidIO系统中&#xff0c;门铃消息FIFO是硬件控制器内部的缓冲区&#xff0c;用于临时存储接收到的门铃消息&#xff08;Doorbell Message&#xff09;。…...

鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南

1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发&#xff0c;使用DevEco Studio作为开发工具&#xff0c;采用Java语言实现&#xff0c;包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...

Typeerror: cannot read properties of undefined (reading ‘XXX‘)

最近需要在离线机器上运行软件&#xff0c;所以得把软件用docker打包起来&#xff0c;大部分功能都没问题&#xff0c;出了一个奇怪的事情。同样的代码&#xff0c;在本机上用vscode可以运行起来&#xff0c;但是打包之后在docker里出现了问题。使用的是dialog组件&#xff0c;…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式

今天是关于AI如何在教学中增强学生的学习体验&#xff0c;我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育&#xff0c;这并非炒作&#xff0c;而是已经发生的巨大变革。教育机构和教育者不能忽视它&#xff0c;试图简单地禁止学生使…...

AxureRP-Pro-Beta-Setup_114413.exe (6.0.0.2887)

Name&#xff1a;3ddown Serial&#xff1a;FiCGEezgdGoYILo8U/2MFyCWj0jZoJc/sziRRj2/ENvtEq7w1RH97k5MWctqVHA 注册用户名&#xff1a;Axure 序列号&#xff1a;8t3Yk/zu4cX601/seX6wBZgYRVj/lkC2PICCdO4sFKCCLx8mcCnccoylVb40lP...

CppCon 2015 学习:Reactive Stream Processing in Industrial IoT using DDS and Rx

“Reactive Stream Processing in Industrial IoT using DDS and Rx” 是指在工业物联网&#xff08;IIoT&#xff09;场景中&#xff0c;结合 DDS&#xff08;Data Distribution Service&#xff09; 和 Rx&#xff08;Reactive Extensions&#xff09; 技术&#xff0c;实现 …...

HTML中各种标签的作用

一、HTML文件主要标签结构及说明 1. <&#xff01;DOCTYPE html> 作用&#xff1a;声明文档类型&#xff0c;告知浏览器这是 HTML5 文档。 必须&#xff1a;是。 2. <html lang“zh”>. </html> 作用&#xff1a;包裹整个网页内容&#xff0c;lang"z…...