一 声卡简介

一个音频系统由硬件和驱动这些硬件的软件组成。

在一台设备上,如果硬件完整,且驱动硬件的软件全部初始化成功后,ALSA系统就会注册一个声卡,通过这个声卡,应用程序可以控制硬件设备的链路联通,并向声卡写入数据或读取数据来完成播放与录音的音频功能。

在这里说的声卡不是配电脑时说的硬件声卡,而是Linux内核中的一个软件概念的声卡。

二 声卡目录下设备节点的作用

在注册声卡时,ALSA core会在dev/snd/下创建声卡的设备节点,在下面这张图中,可以很明显的看到有三种设备节点,分别是control设备,pcm设备,以及一个timer节点

PCM设备就是应用层直接操作的节点,应用层的tinyalsa库就是直接对这个节点进行读写来实现播放和录音的。
想知道tinyalsa怎么将数据写入PCM节点,可以看external\tinyalsa\pcm.c这个文件,在安卓的Audio HAL中,播放音乐时,就是通过调用pcm_write来将数据写下去。

int pcm_write(struct pcm *pcm, const void *data, unsigned int count)
{struct snd_xferi x;if (pcm->flags & PCM_IN)return -EINVAL;x.buf = (void*)data;x.frames = count / (pcm->config.channels *pcm_format_to_bits(pcm->config.format) / 8);for (;;) {if (!pcm->running) {int prepare_error = pcm_prepare(pcm);if (prepare_error)return prepare_error;if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x))return oops(pcm, errno, "cannot write initial data");pcm->running = 1;return 0;}if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) {    //在这里写入数据pcm->prepared = 0;pcm->running = 0;if (errno == EPIPE) {/* we failed to make our window -- try to restart if we are* allowed to do so.  Otherwise, simply allow the EPIPE error to* propagate up to the app level */pcm->underruns++;if (pcm->flags & PCM_NORESTART)return -EPIPE;continue;}return oops(pcm, errno, "cannot write stream data");}return 0;}
}

control设备用于控制声卡的内部通路开关。为了节省功耗,在移动设备上音频通路不是一直接通的,只有在需要用到时,通路才会接通,

例如下面这个例子
当需要把音频输出到Speaker时,就需要通过操作control节点,来吧I2S0的RX1接到MUX1,而当需要输出到耳机时,则可以把RX1接到MUX2。

至于timer设备,目前我暂时没有用过。

Machine驱动

Machine驱动里面描述了当前设备上的各个组件的连接关系,从platform到codec再到Speaker,Machine驱动也是一个单独的驱动,能单独的注册到ALSA中。

ALSA core会在驱动程序注册最后一步尝试去初始化声卡,这里的驱动程序是指所有的音频驱动程序,包括platform,codec,Machine。

注册声卡时,会根据Machine驱动中定义的组件连接关系,去查找所需要的组件,如果此时音频组件没有全部注册完成,则声卡会初始化失败,并返回错误码 -517,这个错误码在Linux内核的含义是需要重试,当最后的组件注册完成时,ALSA会再次尝试初始化声卡。

Machine驱动描述了platform和codec之间的连接关系,通过连接关系,来找到对应的接口。

一条简单的音频链路如图所示,CPU DAI与Codec DAI相连接,进行数据传输。

在Machine驱动中,通过 struct snd_soc_dai_link 结构体来定义连接关系,DAI全称是Digital Audio Interface,以三星的s5pv4418开发板举例,定义一个简单dai_link如下所示

static struct snd_soc_dai_link es8316_dai_link = {.name     = "ASOC-ES8316",.stream_name = "es8316 HiFi",.cpu_dai_name    = "c0055000.i2s",        /* nx_snd_i2s_driver name */.platform_name  = "nexell-pcm",      /* nx_snd_pcm_driver name */.codec_dai_name = "ES8316 HiFi", /* es8316_dai's name */.codec_name = "ES8316.0-0011",   /* es8316_i2c_driver name+ '.' + bus + '-'+ address(7bit) */.ops        = &es8316_ops,.symmetric_rates = 1,.init      = es8316_dai_init,
};

在这个定义中,可以很清楚的知道这条链路的源头DAI是c0055000.i2s,连接到codec的ES8316 HiFi这个接口上。

在dai_link描述了完整的链路信息后,就可以用这个dai_link来注册声卡,为此,需要创建一个声卡实例。

static struct snd_soc_card es8316_card = {.name         = "I2S-ES8316",  /* proc/asound/cards */.owner           = THIS_MODULE,.dai_link        = &es8316_dai_link,.num_links      = 1,.suspend_pre   = &es8316_suspend_pre,.resume_pre      = &es8316_resume_pre,.resume_post      = &es8316_resume_post,.dapm_widgets        = es8316_dapm_widgets,.num_dapm_widgets    = ARRAY_SIZE(es8316_dapm_widgets),.dapm_routes     = es8316_audio_map,.num_dapm_routes    = ARRAY_SIZE(es8316_audio_map),
};

暂时只需要知道dai_link这个成员的含义。
有了声卡实例之后,可以用snd_soc_register_card来注册声卡,这个函数只需要传入一个声卡实例,这里省略了大量对平台的gpio,memory,clock进行初始化的代码,只对ALSA注册声卡进行分析

int snd_soc_register_card(struct snd_soc_card *card)
{int i, j, ret;if (!card->name || !card->dev)return -EINVAL;for (i = 0; i < card->num_links; i++) {struct snd_soc_dai_link *link = &card->dai_link[i];ret = snd_soc_init_multicodec(card, link);if (ret) {dev_err(card->dev, "ASoC: failed to init multicodec\n");return ret;}for (j = 0; j < link->num_codecs; j++) {    //有很多种定义dai_link的形式,这个循环内是对dai_link的数据进行检查以及标准化/** Codec must be specified by 1 of name or OF node,* not both or neither.*/if (!!link->codecs[j].name ==!!link->codecs[j].of_node) {dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n",link->name);return -EINVAL;}/* Codec DAI name must be specified */if (!link->codecs[j].dai_name) {dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n",link->name);return -EINVAL;}}/** Platform may be specified by either name or OF node, but* can be left unspecified, and a dummy platform will be used.*/if (link->platform_name && link->platform_of_node) {dev_err(card->dev,"ASoC: Both platform name/of_node are set for %s\n",link->name);return -EINVAL;}/** CPU device may be specified by either name or OF node, but* can be left unspecified, and will be matched based on DAI* name alone..*/if (link->cpu_name && link->cpu_of_node) {dev_err(card->dev,"ASoC: Neither/both cpu name/of_node are set for %s\n",link->name);return -EINVAL;}/** At least one of CPU DAI name or CPU device name/node must be* specified*/if (!link->cpu_dai_name &&!(link->cpu_name || link->cpu_of_node)) {dev_err(card->dev,"ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n",link->name);return -EINVAL;}}dev_set_drvdata(card->dev, card);snd_soc_initialize_card_lists(card);    //对声卡结构体内的几个链表头进行初始化card->rtd = devm_kzalloc(card->dev,              //根据 dai_link 创建rtdsizeof(struct snd_soc_pcm_runtime) *(card->num_links + card->num_aux_devs),GFP_KERNEL);if (card->rtd == NULL)return -ENOMEM;card->num_rtd = 0;card->rtd_aux = &card->rtd[card->num_links];for (i = 0; i < card->num_links; i++) {     card->rtd[i].card = card;card->rtd[i].dai_link = &card->dai_link[i];card->rtd[i].codec_dais = devm_kzalloc(card->dev,sizeof(struct snd_soc_dai *) *(card->rtd[i].dai_link->num_codecs),GFP_KERNEL);if (card->rtd[i].codec_dais == NULL)return -ENOMEM;}for (i = 0; i < card->num_aux_devs; i++)card->rtd_aux[i].card = card;INIT_LIST_HEAD(&card->dapm_dirty);INIT_LIST_HEAD(&card->dobj_list);card->instantiated = 0;mutex_init(&card->mutex);mutex_init(&card->dapm_mutex);ret = snd_soc_instantiate_card(card);   //尝试去初始化声卡,这里是有可能会失败的,因为可能有其他的组件还没有初始化完成if (ret != 0)return ret;/* deactivate pins to sleep state */for (i = 0; i < card->num_rtd; i++) {struct snd_soc_pcm_runtime *rtd = &card->rtd[i];struct snd_soc_dai *cpu_dai = rtd->cpu_dai;int j;for (j = 0; j < rtd->num_codecs; j++) {struct snd_soc_dai *codec_dai = rtd->codec_dais[j];if (!codec_dai->active)pinctrl_pm_select_sleep_state(codec_dai->dev);}if (!cpu_dai->active)pinctrl_pm_select_sleep_state(cpu_dai->dev);}return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_register_card);

比较核心的是在snd_soc_instantiate_card,在这里才是真正的对声卡进行初始化,会依次调用dai_link上所有组建的初始化函数,来对系统中的所有组件进行初始化,这个函数非常复杂,需要在看动platform和codec驱动,以及dapm之后回来看才能看懂。

安卓声卡驱动:2.Machine驱动与声卡相关推荐

  1. Linux 音频驱动(四) ASoC音频驱动之Machine驱动

    目录 1. 基本介绍 2. 源码分析 2.1. Machine数据结构 struct snd_soc_dai_link 3. 声卡 3.1. 数据结构struct snd_soc_card 3.2. ...

  2. linux音频驱动修复工具,Linux声卡驱动(4)——音频驱动实战

    一.应用测试工具的使用 1.在external/tinyalsa下有以C语言实现的alsa的测试程序,编译后生成tinypcminfo tinyplay tinycap tinymix 四个elf格式 ...

  3. Linux ALSA声卡驱动之五:Machine 以及ALSA声卡的注册

    ALSA声卡驱动: 1.Linux ALSA声卡驱动之一:ALSA架构简介和ASOC架构简介 2.Linux ALSA声卡驱动之二:Platform 3. Linux ALSA声卡驱动之三:Platf ...

  4. beast软件linux用法,声卡驱动设置 - 黑苹果驱动高手篇 MultiBeast用法进阶_Linux教程_Linux公社-Linux系统门户网站...

    声卡驱动设置 Audio自然是声卡驱动设置项,这个选项包括众多板载声卡驱动,选择之前最好仔细阅读描述文件,并选择正确的声卡型号. ▲ Realtek ALC8xx 项(包括ALC8xxHDA和Appl ...

  5. cp210x驱动怎么安装_电脑声卡驱动怎么安装,教你三步搞定声卡驱动

    电脑声卡驱动怎么安装呢?声卡是电脑重要的硬件之一,电脑有声卡才能播放音乐声音,那么如果电脑没有安装声卡驱动的话,那电脑就不能发出声音了,那么如何解决呢?为此小编就给大家带来的是电脑声卡驱动安装教程. ...

  6. win7(windows7旗舰版)声卡High Definition Audio驱动 (安装失败)解决方法

    win7(windows7旗舰版)声卡High Definition Audio驱动 (安装失败)解决方案 前几天装了一下windows7体验一下,结果声卡驱动安装有问题,这电脑没声音我可没法活啊. ...

  7. x200装linux驱动下载,ubuntu 安装X200声卡驱动

    ubuntu 安装X200声卡驱动 标签:x200 ubuntu 声卡驱动 驱动 linux驱动 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任 ...

  8. win10怎么更新显卡驱动_解决win10系统声卡驱动故障没有声音怎么办?

    win10系统更新后电脑没有声音怎么办?这个可能是系统更新的时候没有安装好导致的,如何解决win10升级后没有声音的问题呢?经过小编网上收集整理,给大家分享一下win10声卡故障的解决方法,喜欢的小伙 ...

  9. win7(windows7旗舰版)声卡High Definition Audio驱动(安装失败)解决方案(Thinkpad)

    申精, Definition, 声卡, 旗舰, Audio 你看到我这篇文章时候估计你快被windows7无法安装声卡驱动而崩溃了,呵呵,别急,这篇文章是你的救星,本人已经试验多台电脑,完美解决win ...

最新文章

  1. DoubleViewPager
  2. mysql快捷创建数据库_mysql数据库快速入门(1)
  3. java double 运算精度问题_关于java中Double类型的运算精度问题
  4. 近似求PI (15 分)
  5. 华为云云原生数据库GaussDB加速创新,企业核心数据上云信赖之选
  6. qomo linux最新版本,“珠峰”Qomo Linux新版本将迎来重大变革
  7. [转载] 数组快速排序python_python实现快速排序
  8. 3D视觉(二)四元数简要说明
  9. android数码管字体,matplotlib绘图时显示额外的“figure”浮窗
  10. 【休憩时的练手】—— 制作简易的网易云音乐播放器
  11. navicat 绿化版
  12. 三级等保成标配,互联网医院安全架构报告发布
  13. java常见异常思维导图_Java知识思维导图
  14. AutoCAD2020中文版软件下载和安装教程|兼容WIN10
  15. 常用C++库及测试程序
  16. 大数据培训课程内容介绍
  17. layui-table表格根据条件更换表格背景颜色,高亮显示
  18. Android P 9.0 MTK平台 增加以太网静态IP功能
  19. MFC ShowWindow参数
  20. 关不上的窗徘徊在爱与痛的边缘

热门文章

  1. 微信小程序查看通讯录 联系人
  2. 郑州大学计算机研究生毕业就业,21考研择校:郑州大学就业率和薪酬情况统计!...
  3. mysql查询并更新_MySQL如何实现更新查询?
  4. 第十三周 项目2 输入班级的人数及成绩三种方法(1和2)
  5. AG9321电路设计原理图|USB-C转HDMI双口正反插带PD3.0充电电路设计资料
  6. 使用SQL语句UPDATE更新表数据
  7. 腾讯一面直接扑街,等我回头继续再战
  8. Java实现布隆过滤器
  9. 开发一个投票项目的JAVA服务端-SSM项目
  10. Eclipse代码提示设置