在上小节我们分析了Adndroid系统音频的框架,这么一个复杂的系统我们怎么去学习呢?我们从下往上学,先分析音频的驱动程序,看看linux系统中驱动程序是怎么编写的,他的结构是怎么样的,然后在琢磨Tinyalsa,是如何去播放,录制声音的。在该课时接下来的所有小节都会讲解linux音频驱动程序。该小节先讲解一下alsa音频驱动的框架:

在编写应用程序的时候,我们都是使用标准的open,read,write等访问驱动程序,最简单的方法就是驱动也提供与应用程序对应的open,read,write等。

一般编写驱动程序如下:
1.构造一个结构体,即file_opevations结构体
2.告诉内核,即通过register_char注册。

上面是简单的驱动程序,但是我们的alsa音频驱动也属于linux,所以他也遵循以上套路,linux的声卡驱动有两个版本:


分别为oss,alsa,但是oss是一个收费的版本,现在流行的是alsa(Advanced Linux Sound Architecture),下面是我们开发板声卡的相关设备节点:
执行ls -l /dev/snd/

从名字上看controlC0起控制作用,C0的意思代表第一个声卡,pcmC0D0c与pcmC0D0p是一对,其pcmC0D0c表示声卡0的录音(capture),pcmC0D0p表示声卡0的播放(playbaclc)。

从设备节点,我们可以猜测,一个声卡可以由多个device,一个device有播放,录音通道。

每个设备节点对应一个file_opevations结构体,但是他们的主设备号一样,则对应同一个file_opevations结构体。

我们看看代码是如何实现的,代开kennel/sound/core/sound.c:

static const struct file_operations snd_fops =
{.owner =  THIS_MODULE,.open =        snd_open,.llseek = noop_llseek,
};static int __init alsa_sound_init(void)register_chrdev(major, "alsa", &snd_fops)

可以看到snd_fops结构体比较特殊,只有一个snd_open函数,这里snd_open起到的是一个中转的作用,会根据次设备号找到更加具体的file_operations结构体。如controlC0,pcmC0D0c,pcmC0D0p等节分别有自己file_operations结构体。
进入snd_open函数如下:

static int snd_open(struct inode *inode, struct file *file)unsigned int minor = iminor(inode);mptr = snd_minors[minor];/*根据次设备号,获得一个新的file_operations结构体*/new_fops = fops_get(mptr->f_ops);/*把老的file_operations结构体换成重新获得的file_operations结构体*/replace_fops(file, new_fops);

从这里我们可以总结出既然都遵循alsa规范,如下:

事先定义好file_opevations结构体,首先是:
顶层:定义一个file_opevations结构体,只实现open。
下层:controlC0,pcmC0D0c,pcmC0D0p等节点分别有自己对应的file_opevations结构体。

这些接口确定之后,上层应用程序就可以使用确定的接口,访问声卡,那么谁访去访问硬件呢?那么显然,还有更加底层的工作:

分配设置一个snd_card结构体,这个结构体能提供参数,设置参数,传输数据等等。我们继续查看源码,在kennel/sound/core/sound.搜索snd_minors:

int snd_register_device(int type, struct snd_card *card, int dev,const struct file_operations *f_ops,void *private_data, struct device *device)snd_minors[minor] = preg;

可以知道snd_minors数组是在snd_register_device函数中被设置,我们在源码中搜索snd_register_device,看他在哪里被调用,然后一路跟随,可以知道snd_register_device被调用的过程如下,注意下面是从上到下被调用的过程:

/*sound.c*/
snd_register_device(int type, struct snd_card *card, int dev,const struct file_operations *f_ops,void *private_data, struct device *device)/*Control.c*/static int snd_ctl_dev_register(struct snd_device *device)/*Control.c*/int snd_ctl_create(struct snd_card *card)/*Init.c*/int snd_card_new(struct device *parent, int idx, const char *xid,struct module *module, int extra_size,struct snd_card **card_ret)

最后我们在snd_card_new函数中可以看到一个struct snd_card *card;结构体,如果我们在搜索snd_card_new,可以看到一大堆。

那么我们的声卡驱动应该如何编写呢?一般如下:
1.定义一个struct snd_card *card;
2.设置snd_pcm_new
3.注册snd_card_register(card)

下面我们进行流程分析:

我们注册声卡的时候,要调用snd_card_new:

int snd_card_new(struct device *parent, int idx, const char *xid,struct module *module, int extra_size,struct snd_card **card_ret)err = snd_ctl_create(card);static struct snd_device_ops ops = {.dev_free = snd_ctl_dev_free,.dev_register =    snd_ctl_dev_register,.dev_disconnect = snd_ctl_dev_disconnect,};err = snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);

可以知道其会创建一个controlC接口,这是一个声卡最最基本的东西,如pcmC0D0c,pcmC0D0p是可以不存在的 ,但是controlC我们必须创建。
执行snd_device_new函数的时候,snd_device_ops结构体的snd_ctl_dev_register函数会被调用:

static const struct file_operations snd_ctl_f_ops =
{.owner =  THIS_MODULE,.read =        snd_ctl_read,.open =       snd_ctl_open,.release =    snd_ctl_release,.llseek =  no_llseek,.poll =      snd_ctl_poll,.unlocked_ioctl = snd_ctl_ioctl,.compat_ioctl =  snd_ctl_ioctl_compat,.fasync = snd_ctl_fasync,
};
static int snd_ctl_dev_register(struct snd_device *device)snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,&snd_ctl_f_ops, card, &card->ctl_dev);

其上的snd_ctl_f_ops结构体,就是对节点真正控制的描述符。我们知道对于声卡还有pcmC0D0c,pcmC0D0p等节点,他们是在哪里设置的呢?我们随便打开一个声卡驱动程序,如Echoaudio.c文件:

static int snd_echo_probe(struct pci_dev *pci,const struct pci_device_id *pci_id)err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,0, &card);snd_echo_new_pcm(chip)snd_pcm_new(chip->card, "PCM", 0, num_pipes_out(chip),num_analog_busses_in(chip), &pcm)static struct snd_device_ops ops = {.dev_free = snd_pcm_dev_free,.dev_register =  snd_pcm_dev_register,.dev_disconnect = snd_pcm_dev_disconnect,};snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count))snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)) snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops))

其是通过snd_pcm_new进行创建pcmC0D0c,pcmC0D0p节点,在调用snd_pcm_new之后,会导致dev_register = snd_pcm_dev_register函数被调用:

static int snd_pcm_dev_register(struct snd_device *device)case SNDRV_PCM_STREAM_PLAYBACK:devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;case SNDRV_PCM_STREAM_CAPTURE:devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;snd_register_device(devtype, pcm->card, pcm->device,&snd_pcm_f_ops[cidx], pcm,&pcm->streams[cidx].dev);

可以知道,其根据SNDRV_PCM_STREAM_PLAYBACK创建pcmC0D0p,根据SNDRV_DEVICE_TYPE_PCM_CAPTURE则创建
pcmC0D0c节点。

一个pcm,就是一个逻辑设备device,一个pcm中有两个通道stream,一个为playback,一个为capture。他们分别对应节点pcmC0D0c,pcmC0D0p。

在调用snd_register_device函数时,我们需要注册一个snd_pcm_f_ops结构体:

const struct file_operations snd_pcm_f_ops[2] = {{.owner =     THIS_MODULE,.write =       snd_pcm_write,.write_iter =        snd_pcm_writev,.open =         snd_pcm_playback_open,.release =       snd_pcm_release,.llseek =      no_llseek,.poll =          snd_pcm_playback_poll,.unlocked_ioctl =    snd_pcm_playback_ioctl,.compat_ioctl =     snd_pcm_ioctl_compat,.mmap =           snd_pcm_mmap,.fasync =     snd_pcm_fasync,.get_unmapped_area =    snd_pcm_get_unmapped_area,},{.owner =      THIS_MODULE,.read =            snd_pcm_read,.read_iter =      snd_pcm_readv,.open =          snd_pcm_capture_open,.release =        snd_pcm_release,.llseek =      no_llseek,.poll =          snd_pcm_capture_poll,.unlocked_ioctl = snd_pcm_capture_ioctl,.compat_ioctl =  snd_pcm_ioctl_compat,.mmap =           snd_pcm_mmap,.fasync =     snd_pcm_fasync,.get_unmapped_area =    snd_pcm_get_unmapped_area,}
};

可以看到有两个定义,一个对应输出,一个对应输入。

08.音频系统:第003课_Linux音频驱动程序:第001节_alsa音频驱动框架相关推荐

  1. 08.音频系统:第003课_Linux音频驱动程序:第002节_ASoC音频驱动框架

    通过上小节alsa音频驱动框架的分析,知道如果要去写一个声卡驱动,我们需要分配,设置,注册snd_card结构体: 定义一个struct snd_card *card; snd_card_new // ...

  2. 08.音频系统:第003课_Linux音频驱动程序:第003节_RK3399声卡驱动移植_combine

    该小节我们讲解一下开发板RK3399声卡rt5651的移植,主要分为4个部分,platfrom,codec,machine,dts(设备树). 首先我们从设备树开始讲起,当然在讲解之前,我们先来体验下 ...

  3. 08.音频系统:第003课_Linux音频驱动程序:第005节_DAPM_widget_route_path

    DAPM是Dynamic Audio PowerManagement的缩写,译过来就是动态音频电源管理的意思.DAPM是为了使基于linux的移动设备上的音频驱动子系统,在任何时候都工作在最小功耗状态 ...

  4. 08.音频系统:第003课_Linux音频驱动程序:第003节_耳麦拔插事件调用流程分析

    在前面的小节中,我们编写了一个驱动程序,模拟耳机的插拔事件,其可以上报耳机的拔插事件,并且修改了android的源代码,可以根据耳机的拔插事件,在状态栏上现实或者消除耳麦的图标,这节视频我们讲解耳麦插 ...

  5. Linux ALSA 音频系统:物理链路篇

    原址 1. Overview 硬件平台及软件版本: Kernel - 3.4.5 SoC - Samsung exynos CODEC - WM8994 Machine - goni_wm8994 U ...

  6. (一)Linux ALSA 音频系统:物理链路篇

    物理链路篇 转自:https://me.csdn.net/zyuanyun Linux ALSA 音频系统:物理链路篇 Linux ALSA 音频系统:物理链路篇 原创 zyuanyun 最后发布于2 ...

  7. 逐步攻略:使用Matlab音频系统工具箱创建自己的VST插件,让音乐编程在Nashville崭新绽放

    第一部分:引言与Matlab音频系统工具箱概览 尊敬的读者,欢迎阅读这篇文章.我作为一个热爱音乐和编程的人,十分激动地在这里和大家分享我最近的一次学习经验.正如标题所述,我们将探索一种崭新的方式来创建 ...

  8. Linux ALSA音频系统:platform,machine,codec

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/weixin_41965270/arti ...

  9. 08.音频系统:第002节_Android音频系统框架简述

    该小节我们来讲解Android音频系统框架,了解了框架之后,我们才能更加容易的去查看以及分析源码,有了框架才不会遗失方向. 下面是一个大框图,该小节我们将围绕下面的图示进行讲解: 以前总提到,写应用程 ...

最新文章

  1. Celery - 一个懂得 异步任务 , 定时任务 , 周期任务 的芹菜
  2. tensorflow 1.0 学习:模型的保存与恢复(Saver)
  3. Tensorflow2.0开启,从此忘记1.*版本
  4. 大家整齐的qipaifans朗读声忽然乱了
  5. linux中create命令详解,linux中 pmap 命令详解
  6. ad19做直插封装 ipc_AD19的IPC封装向导使用
  7. 联想台式机usb驱动_windows安装系列教程—驱动安装
  8. html菜鸟教程zoom,插件 jQuery.panzoom 中文API文档
  9. 《6色荧光笔学习法》——从如何做笔记到高效学习和“战略性思考”
  10. ps怎么把模糊的图片变清楚
  11. 迷你世界怎么显示服务器未连接,打开迷你世界提示网络异常或者连接不上
  12. 无法启动程序,.dll不是有效的Win32应用程序
  13. 使用springboot jpa完成复杂的分页查询
  14. 手把手教你做个AR涂涂乐
  15. 手机游戏的分析,设计,思考
  16. 基于关联规则的气象服务智能推荐
  17. 深入理解深度学习——语境词嵌入(Contextual Word Embedding)
  18. cmd 更新 pip版本指令
  19. java好学么_java是什么好学吗?难不难学?
  20. 慕课Java第三季学习及笔记整理

热门文章

  1. 思想的芦苇——把过程改进注入人文服务的思想以提升其价值
  2. 我的新书 asp net开发技巧精讲
  3. android 定时推送提醒,使用workManager实现每日定时推送通知
  4. 前端常用得CSS代码分享
  5. OSCHINA开源中国
  6. php jq实现抽奖,php 实现抽奖功能
  7. 区块链+游戏资产所有权,将如何激活游戏经济的发展?
  8. 杜甫写的有关风雨的古诗有哪些
  9. 冯扬文:船用燃料油价格大涨对我省航运企业的影响
  10. 用python制作文字特效