ALSA(二), makefile, Autotools, premake

http://antkillerfarm.github.io/

从Gstreamer到ALSA(续)

4.SOC_SINGLE类宏

这里对SOC_SINGLE类的宏,详细说明一下,因为只有这些宏才是真正需要驱动开发者添加或修改的。

例如如下的代码:

SOC_DOUBLE_R_TLV("Digital Playback Volume", JZCODEC_DACLVOL, JZCODEC_DACRVOL,
         0, 255, 0, jzcodec_tlv),

使用amixer命令观察该代码的效果:

Simple mixer control 'Digital',0
  Capabilities: pvolume
  Playback channels: Front Left - Front Right
  Limits: Playback 0 - 255
  Mono:
  Front Left: Playback 255 [100%] [0.00dB]
  Front Right: Playback 255 [100%] [0.00dB]

从中可以看出snd_kcontrol_new的name成员的名字是有特定含义的,不能随便定。

有的音频芯片,如TAS5086,它的音量寄存器用0xFF表示静音,而用0x0表示最大音量,这种情况下需要将SOC_SINGLE类宏的invert参数设为1。

5.SOC_SINGLE_TLV类宏

除了SOC_SINGLE类宏之外,还有SOC_SINGLE_TLV类宏。后者和前者的主要区别在于:用TLV数组完成音量到寄存器值之间的转换。

例如如下代码:

static const DECLARE_TLV_DB_SCALE(tas5086_dac_tlv, -10350, 50, 1);

-10350是音量的最小值,表示-103.5dB。50是相邻的两个寄存器值之间的音量差(即步长),这里表示0.5dB。也就是说这个宏设定的音量值的单位都是0.01dB。

ASOC对多Codec的支持

音频设计方案

ASOC在Linux 3.X时代最大的改进就是添加了对多codec的支持。这里我们首先描述一下多Codec在音频设备设计中的意义,以及它的一些实现方案。

这里写图片描述

上图是实现方案A,它的特点是:

1.1个MCU通过2个Codec,间接连接了4个Speaker。在当前的音频设计中,音箱的扬声器个数越来越多,但是单个Codec所能提供的声道数量有限。当扬声器个数超过这一数量限制时,就需要采用多Codec方案了。

2.两个Codec有独立的I2C控制通道。这里的“独立”是逻辑意义上的。在物理上,两个Codec可挂在同一条I2C总线上,以不同的从机地址加以区分。

3.两个Codec共享I2S输入。

这里写图片描述

上图是实现方案B,它和方案A的区别为:两个Codec有独立的I2S输入。方案B根据用途,又可分为两种子方案:

1.方案B1:两个I2S的输入相同。

2.方案B2:两个I2S的输入不同。

方案A的代码实现

1.首先讨论I2C控制的实现,这里以TAS5731M芯片为例。

在board初始化阶段,为两个codec各自生成一个I2C设备。

static struct i2c_board_info __initdata rtl819xd_i2c_devices[] = {
    {
        I2C_BOARD_INFO("tas5731", 0x1a), //TAS5731M Master
    },
    {
        I2C_BOARD_INFO("tas5731", 0x1b), //TAS5731M Sub
    },
};

2.方案A由于共享I2S,因此在驱动角度,必须将两个codec驱动整合到一个card之中。

相应的machine代码为:

static struct snd_soc_dai_link rtl819xd_jzcodec_dai[] = {
    {
        .name = "JZCODEC_MASTER",
        .stream_name = "JZCODEC_MASTER",
        .cpu_dai_name = "rtl8197d-i2s",
        .codec_name = "tas5731.0-001a",
        .codec_dai_name = "tas5731_master",
        .init = rtl819xd_jzcodec_jzcodec_init,
        .ops = &rtl819xd_jzcodec_ops,
        .platform_name  = "rtl819x-pcm-audio",
    },
    {
        .name = "JZCODEC_SUB",
        .stream_name = "JZCODEC_SUB",
        .cpu_dai_name = "rtl8197d-i2s",
        .codec_name = "tas5731.0-001b",
        .codec_dai_name = "tas5731_sub",
        .init = rtl819xd_jzcodec_jzcodec_init,
        .ops = &rtl819xd_jzcodec_ops,
    }
};

相应的codec代码为:

static struct snd_soc_dai_driver tas5731_dai[] = {
    {
        .name = "tas5731_master",
        .id = 0,
        .playback = {
            .stream_name    = "Playback",
            .channels_min   = 2,
            .channels_max   = 6,
            .rates      = TAS5731_PCM_RATES,
            .formats    = TAS5731_PCM_FORMATS,
        },
        .ops = &tas5731_dai_ops,
    },
    {
        .name = "tas5731_sub",
        .id = 1,
        .playback = {
            .stream_name    = "Playback",
            .channels_min   = 2,
            .channels_max   = 6,
            .rates      = TAS5731_PCM_RATES,
            .formats    = TAS5731_PCM_FORMATS,
        },
        .ops = &tas5731_dai_ops,
    }
};

这里对代码的要点做一个解释:

1)codec_name的命名规则。codec_name一般以X.Y-Z的形式命名。

其中X为codec的I2C driver名称。注意这里的名称和I2C_BOARD_INFO中定义的名称不同,后者是根据i2c_device_id中定义的名称来命名的。

Y表示I2C总线号。如果MCU有超过一条I2C总线的话,需要使用总线号加以区分。

Z表示Codec设备的从机地址。

2)由于两个Codec共享I2S输入,因此只需要其中一个Codec定义platform_name即可。

方案B1的代码实现

和方案A的差别在于:两个Codec都需要定义自己的platform_name。

方案B2的代码实现

这种情况用两个独立的card来解决就可以了。

makefile

简单的规则可以查看《GNU makefile指南》一文,Goerge Foot写的。地址就不贴了,中文版基本到处都是。但是该文只是入门级的,最专业的还是GNU官方的手册:

http://www.gnu.org/software/make/manual/html_node/index.html

应该说make的语法,除了规则依赖之外,大多数与bash相同。但是make也有一些内置函数,它们的用法就不在bash的范畴了,比如call函数。碰到这样的情况,可以查阅以下网页,以快速定位帮助文档的位置:

http://www.gnu.org/software/make/manual/html_node/Quick-Reference.html

Autotools

概述

autotools是GNU提供的一系列自动配置工具的集合,其主要包括autoconf、automake、pkg-config和libtool等工具。

Makefile虽然规则简单,但手工书写makefile却不是一件容易的事。我这里的不容易指的是:编写符合规范的makefile不容易。如果只是那种自己平时demo的话,可能还是直接makefile更简单。毕竟autotools包含那么多工具,每个工具的规则都不尽相同,学习门槛比makefile高多了。如果针对小工程,还不一定有直接makefile来的快。

那么autotools的优点在哪里呢?

1.针对大工程时,手工编写量较小。比如需要添加的源文件和头文件,直接autoscan就出来了,不用一个一个的写。

2.可以检查编译环境。这个手工写,难度太大,我是不会写的。

3.可移植好。由于第2点的存在,autotools生成的makefile的可移植性非常好。这个优点在本地编译的时候,体现的并不十分明显,但如果交叉编译的话,就很突出了。

参考文档:

https://autotools.io/index.html

这个网站基本上每个工具都讲到了,非常值得一看。

autoconf&automake

这两个工具是整个autotools工具集的核心,使用的流程也比较复杂。教程中最经典的是:

http://www.ibm.com/developerworks/cn/linux/l-makefile/index.html

但是,这个教程比较老了,有的地方的处理并不符合当前的版本,以下针对这些新老版本的差异,做一个说明。

1.首先给出新版本(2016.2)的示例代码:

https://github.com/antkillerfarm/antkillerfarm_crazy/tree/master/gtk_browser/autotool

2.configure.in在新版本中改叫configure.ac。其中,AM_INIT_AUTOMAKE宏的格式已经和老的不同了。AC_OUTPUT宏虽然还存在,但是部分功能分解给AC_CONFIG_FILES宏。

3.拷贝depcomp和complie文件的过程,现在可以通过命令自动完成,无须手动拷贝。

4.运行automake之前,需要先用autoheader生成config.h文件。

从整个流程来说,autotools相比Cmake无疑复杂的多了。但实际使用中,由于这几个步骤都是流程化的,简化起来也比较容易。因此新版本提供了autoreconf命令,用来一站式生成所需的编译配置文件。其示例如下:

autoreconf -fi

pkg-config

一般来说,如果库的头文件不在/usr/include目录中,那么在编译的时候需要用-I参数指定其路径。由于同一个库在不同系统上可能位于不同的目录下,用户安装库的时候也可以将库安装在不同的目录下,所以即使使用同一个库,由于库的路径的不同,造成了用-I参数指定的头文件的路径和在连接时使用-L参数指定lib库的路径都可能不同,其结果就是造成了编译命令界面的不统一。可能由于编译,连接的不一致,造成同一份程序从一台机器copy到另一台机器时就可能会出现问题。

pkg-config就是用来解决编译连接界面不统一问题的一个工具。

它的基本思想:pkg-config是通过库提供的一个.pc文件获得库的各种必要信息的,包括版本信息、编译和连接需要的参数等。需要的时候可以通过pkg-config提供的参数(–cflags, –libs),将所需信息提取出来供编译和连接使用。这样,不管库文件安装在哪,通过库对应的.pc文件就可以准确定位,可以使用相同的编译和连接命令,使得编译和连接界面统一。

参见:

http://www.mike.org.cn/articles/description-configure-pkg-config-pkg_config_path-of-the-relations-between/

autoconf&automake与pkg-config的协同工作

由于pkg-config是一系列Glib派生项目(如Gstreamer、GTK)所用的配置工具。因此,如何让autoconf&automake与pkg-config协同工作就成为了一个很重要的课题。多数autotools教程由于只是helloworld型的,并没有涉及到这方面的内容。

这里仍然使用上一节的示例代码,进行讲解。

协同工作的关键在于PKG_CHECK_MODULES宏,这个宏的语法为:

PKG_CHECK_MODULES(prefix, list-of-modules, action-if-found, action-if-not-found)

该宏被执行后,会生成prefix_CFLAGS和prefix_LIBS两个全局变量。它们在Makefile.am中会用到。

这里的AC_SUBST宏,主要是针对老版本的pkg-config所做的修改,在pkg-config v0.24以后的版本中,加不加都无所谓。

这方面更复杂的例子,可以查看Totem项目的代码,该项目复杂度中等。如果这个例子还不够用的话,那就直接查看GTK项目的代码吧。我相信绝大多数的人,都不可能开发出一个比它更大的项目来。

CMake和pkg-config的协同工作

Cmake虽然主要用于Qt项目,但用于GTK项目,实际上也没有什么问题。

Cmake使用的pkg_check_modules宏,和上面的PKG_CHECK_MODULES宏,从原理来说,是类似的。这里不再赘述。

参考文档:

http://francesco-cek.com/cmake-and-gtk-3-the-easy-way/

示例代码:

https://github.com/antkillerfarm/antkillerfarm_crazy/tree/master/gtk_browser/cmake

Autotools细节汇总

1.dnl vs #

dnl#在autoconf中,都是注释开始的标志。区别在于,dnl注释的东西,不会出现在最终的configure脚本中。

2.设置debug版本

CFLAGS的默认设置是-g -O2,可用以下命令修改之:

./configure CFLAGS="-g -O0"

或者也可以用./configure --help查看支持的编译选项。

比如生成静态链接库,可使用以下命令:

./configure --enable-shared=no --enable-static=yes

3.设定so文件的导出符号表

libtool --mode=link gcc -o libfoo.la foo.lo -export-symbols=foo.sym

premake

premake是一个轻量级的构建系统。这里所谓的轻量级是和Autotools、CMake相比。

Autotools在Unix平台的功能最为全面。CMake添加了对Windows平台的支持,但在Unix平台上,仍不得不借助部分Autotools工具的功能,比如pkg-config。

这两个重量级工具,都有很多重量级的使用者,一般不必担心功能不够使的情况。它们的缺点是,使用了特殊的DSL(Domain Specific Language),用户需要花费额外的学习时间来学习这些DSL。

premake使用lua语言编写,语法比CMake更简单。它的官网是:

http://premake.github.io/

premake的缺点在于,它基本上是个人作品,全职开发人员太少,导致功能有限,目前尚无特大项目使用该系统,其功能和可靠性受到质疑。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值