4.3.19. Audio 开发调试说明

4.3.19.1. 概述

Audio 主要实现数字音频播放( Playback)和模拟音频采集( Capture)的功能,体现消费终端常见称呼可能喇叭,麦克风,耳机各种音频设备。要实现这些音频设备功能,多数时候使用到 I2S(Inter-IC Sound) 总线,中文集成电路内置音频总线 , 是飞利浦半导体公司 ( 现为恩智浦半导体公司 ) 针对数字音频设备之间数据传输制定一种总线标准。

4.3.19.2. 特点

芯片两路全双工的 I2S 信号线,在 Master 模式下,最高速率达到 40Mbps。

  • 支持主从两种模式

  • RX 支持 1/2/4/8/16 通道音频输入。

  • TX 支持 1/2 通道音频输出。

  • 支持 8/16/32/44.1/48/64 KHz 采样率。

  • 支持 16/24/32 bit 采样深度。

TDM (Time-Division Multiplexing) 模式需要 32bit 对齐。

4.3.19.3. 功能描述

典型应用

Audio 的典型应用两个场景,一种是 playback、一种是 capture。 具体硬件应用上,全双工采用如下连接方式进行连接。区别在于一种没有控制信号,一种一组控制信号线。

 典型应用 1

是 CODEC 通过 I2S 和 SOC 信号连接,在时钟信号正确情况下, playback 通过 SD Out / DATA OUT 信号线数据给 CODEC, capture 通过 SD In / DATA In 接收到 Codec 来数据。

这种需要控制的 Codec 在市面上存在的,还有部分控制信号线的。例如下图, CODEC 和 SOC 通过 I2C 连接,传输控制信号,一般这种涉及部分路由控制或者说比较复杂的 Codec Driver。

 典型应用 2

功能原理

I2S 的传输主要涉及如下几类数据线,

  • MCLK / SYSCLK 代表时钟( Master Clock),或者说系统时钟,用于 SOC 和 CODEC 之间同步。

  • SCK / BCLK 代表串行时钟信号( Serial Clock),或者说时钟。 SCK 是模块同步信号 , 从模式外部提供 , 主模式模块内部自己产生。一个脉冲对应一位数字音频数据。

  • WS / LRCK 代表采样时钟( Word Select),也左右声道选择信号( Left Right Clock)

  • SD / DATA 代表串行数据信号( Serial),也数据线,它主要用于传输数字音频数据。如果全双工,会两根这样线,一根用于 SOC 发送(常用于 Playback),一根用于 SOC 接收(常用于 Capture)。

 信号示例

我们在 Audio 开发经常遇到几个概念

  • 采样率:每秒钟取得声音样本次数,可以理解为 ADC、 DAC 的每秒修改多少次,常见采样率是 8 / 16 / 32 / 44.1 / 48 / 64 KHz 等,它频率和 WS/LRCK 的频率基本一致的,比如我们播放采样率位 16 KHz 的音频, WS / LRCK 这个引脚应该是 16 KHz 的频率。

  • 宽:可以理解一个响度量程,位大,数字声音精度越高,声音表现更加细腻,也常有深度说法,位深等等。体现就是 SD / DATA 的一个声道里面数量, 0-15 就是有 16bit。常见深度有 16 bit , 24 bit 等等。

  • 通道数:通常理解连接麦克风、喇叭数量。比如左右声道,就两个通道。

时钟关系: Audio 的时钟比较多,这里单独说明一下几个时钟关系。上文我们采样率数值基本就是 WS / LRCK 的频率,以此我们可以得到 SCK / BCLK 的频率,它计算公式是:位宽 X 通道数 X 采样率。比如播放一个音频文件,它格式为 16bit 的位深,双通道, 16 KHz 的采样率,那么 SCK / BCLK 的频率理论应该是 16 x 2 x 16KHz = 512 KHz ,如果我们调试时候发现 SCK 不是 512 KHz,那么需要 debug , 看看 Master 侧时钟是否异常。 此时 MCLK / SYSCLK 的时钟可能多种频率,但基本如下规律, MCLK 一般是 BCLK 的 2 / 4 /8 倍, 也就是说可能是 1.024M、 2.048M、 4.096M, MCLK 一般是 WS 的 256 倍,也就是 256 x 16KHz = 1.048M , 但这个频率主要看 CODEC 和 SOC 侧设置,不一定严格满足一种,但一定其中一种,否则出来声音瑕疵的。

工作方式

Audio 的工作其实涉及一连串内容,贯穿整个系统。我们需要保证每个节点正常情况下,整个 Audio 才能正常工作起来 ,有点类似摄像头的 pipeline。

 信号示例

确保硬件接线正确前提下,当 CODEC 正常上电(如果控制接口的,还需要确认能否正常控制,比如 I2C 通信正常), SOC 侧设置正确,正常接入 Alsa 框架,保证声卡能够正常启动使用。

然后我们使用 tinymix 或者 amixer 这样的 Audio 工具。详细使用可以参考本文 功能使用 部分说明。

4.3.19.4. 驱动代码

uboot 下基本没有相关配置,所以代码配置主要集中在 kernel 部分。 由于 X5 EVB 板端没有 Codec 芯片,所以这边以 40 pin 外接音频板 ( WM8960 ) 为例来说明,其余音频可以根据实际情况,参考 功能使用部分 kernel 阶段 进行调试。

kernel 下驱动代码配置、设备树

DTS 部分: 在板子对应设备树添加,比如对应设备树是 x5-evb-lp4-1_b.dts。

&i2c5 {
        wm8960:wm8960@1a{
                compatible = "wlf,wm8960";
                reg = <0x1a>;
                #sound-dai-cells = <0>;
                status = "okay";
        };
};

......

&dw_i2s1 {
        status = "okay";
        dwc-master = <1>; /*这里的 dwc-master 表示 soc 侧是 master*/
};

......

&hobot_sound_machine{

        status = "okay";
        #address-cells = <1>;
        #size-cells = <0>;
        simple-audio-card,name = "duplex-audio-i2s1";

        simple-audio-card,dai-link@0 {
                link-name = "dai-link0";
                reg = <0>;
                format = "i2s";
                bitclock-master = <&snd1_mm>;
                frame-master = <&snd1_mm>;
                snd1_mm: cpu {
                        sound-dai = <&dw_i2s1 1>;
                };
                codec {
                        sound-dai = <&wm8960>;
                };
        };

};

我们结合 alsa 框架,要确认如下代码位置。

codec 即 codec-dai 代码位置: kernel/sound/soc/codecs/xxx.c , 这部分代码比较多,它包含已经传到 linux 主线的 codec 代码。

platform 即 cpu-dai 代码位置: kernel/sound/soc/dwc/dwc-i2s.c , 这部分代码跟随平台,一般不会很多。

machine 代码位置: kernel/sound/soc/hobot , 这部分和 SOC 相关,主要串联 codec-dai 和 cpu-dai。

打开相关 codec config 开关,比如这里就要确认 codec、 machine、 platform 的相关定义。

CONFIG_SND_DESIGNWARE_I2S=y
CONFIG_SND_SOC_WM8960=m
CONFIG_SND_HOBOT_SOUND_MACHINE=m

我们建议通过 make menuconfig 的方式去 enable 需要的 codec,参考如下步骤:

主线没有的 codec , 将 codec 驱动文件增加到 kernel/sound/soc/codecs/ 目录下。

修改 sound/soc/codecs/Kconfig 以及 Makefile,将 codec 加入驱动编译。

其中, Kconfig 可以参考如下代码添加。

config SND_SOC_XXX
    tristate "XXX Audio Codec"

Makefile 可以参考如下代码添加

obj-$(CONFIG_SND_SOC_XXX)    += XXX.o

完成刚刚提到步骤,就可以主线存在的 codec 一样,通过 make menuconfig 的方式去 enable 需要的 codec 了,路径可以参考如下提示:

-> Device Drivers
    <*> Sound card support --->
        <*> Advanced Linux Sound Architecture --->
            <*> ALSA for SoC audio support --->
                    CODEC drivers --->
                        <M> XXX Audio Codec

4.3.19.5. 功能使用

kernel 阶段使用

我们使用 Audio 的时候,需要打通多个环节,我们以上提到工作方式线索,从下到上进行打通。

 连接示例

首先就是 Codec 正常注册环节,同样以 WM8960 为例(接线图上述所示),首先保证该 Codec 的 I2C 通信正常,这里主要调试 Codec 本身驱动,

如果是 SPI 通信的,可以结合 SPI 调试手册进行调试,同理, I2C 通信可以结合 I2C 调试手册进行。

这里我们确认 3 个点,是否正常上电; I2C 地址; I2C 速率。

按照上文设备树代码进行配置,正常启动之后,我们手动加载 ko,参考如下命令:

modprobe designware_i2s i2s_ms=1
modprobe snd-soc-wm8960
modprobe snd-soc-hobot-sound-machine

我们可以对应的 I2C 总线探测设备地址,甚至可以找到地址对应设备,找到打印寄存器文件节点。

# 探测设备地址

root@buildroot:~# i2cdetect -y -r 5
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- UU -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- -- 
root@buildroot:~#
# 找到可以打印寄存器的节点

cat /sys/kernel/debug/regmap/5-001aregisters > 0x1a_registers.txt

其次我们可以检查 codec-dai 是否正常注册了,正常情况下, codec 驱动中有类似如下函数

snd_soc_register_codec
devm_snd_soc_register_component

正常情况不会报错,如果注册失败,会返回具体报错问题。一般设备树节点配置问题,这里可以结合每个 codec 驱动代码进行 debug。

然后我们需要检查 cpu-dai 部分,这里本质对 I2S 控制器进行注册检查,对应代码配置上文提到,这里主要检查我们使用 I2S 和 CODEC 配合部分

  • 确认 I2S 控制器模式, SOC 侧和 codec 侧需要确认。

  • 确认是否限制,常见是 16/24/32bit。

  • 确认传输格式,左对齐对齐等等。

  • 确认采样率是否限制,比如有些 CODEC 的 mclk 固定之后,只有固定采样率。

比如我们的 I2S 主从模式就是在 dts 中配置的。同样的,如果注册异常, kernel 中打印报错日志,根据具体情况进行判断;但如果播放或者录制时候调试,那么需要再次确认刚刚提到的 I2S 和 CODEC 配合部分,大多数情况就是这里没有设置好,导致点亮环节出现无声、卡顿、声音异常情况。

最后我们需要通过 machine 将 codec dai 和 cpu dai,这里配置涉及到 dts 、 deconfig 选择。配置完成之后我们可以系统检查声卡是否注册成功,检查是否对应的 pcm 设备了。

# 检查声卡, X5 EVB 上默认有一张 dummy 声卡

root@buildroot:/proc/asound# cat cards
 0 [guaaudio       ]: gua-audio - gua-audio
                      gua-audio
root@buildroot:/proc/asound#


# 正常注册之后,可以看到两张声卡
root@buildroot:~# cat /proc/asound/cards
 0 [guaaudio       ]: gua-audio - gua-audio
                      gua-audio
 1 [duplexaudioi2s1]: simple-card - duplex-audio-i2s1
                      duplex-audio-i2s1


## 检查 pcm 设备 , 可以看到 id 和 name 等等信息,都能和 codec 中的对上。

root@buildroot:~# cat /proc/asound/duplexaudioi2s1/pcm0c/info
card: 1
device: 0
subdevice: 0
stream: CAPTURE
id: i2s1-wm8960-hifi wm8960-hifi-0
name: i2s1-wm8960-hifi wm8960-hifi-0
subname: subdevice #0
class: 0
subclass: 0
subdevices_count: 1
subdevices_avail: 1
root@buildroot:~# cat /proc/asound/duplexaudioi2s1/pcm0p/info
card: 1
device: 0
subdevice: 0
stream: PLAYBACK
id: i2s1-wm8960-hifi wm8960-hifi-0
name: i2s1-wm8960-hifi wm8960-hifi-0
subname: subdevice #0
class: 0
subclass: 0
subdevices_count: 1
subdevices_avail: 1
root@buildroot:~#

我们常见问题是 dts 中的 machine 部分没有配置正确,导致声卡注册不上。这里我们就要确认清楚 machine 的配置。

完成上述使用配置之后, kernel 空间部分基本完成,接下来用户使用。

用户阶段使用

这里需要配合常见嵌入式音频工具 tinyalsa ,一般三个基本的, tinymix 调试通路, tinycap 实施录制, tinyplay 实施播放。

tinymix 说明:

# tinymix -h
    usage: tinymix [options] <command>
    options:
        -h, --help               : prints this help message and exits
        -v, --version            : prints this version of tinymix and exits
        -D, --card NUMBER        : specifies the card number of the mixer

    commands:
        get NAME|ID              : prints the values of a control
        set NAME|ID VALUE(S) ... : sets the value of a control
        VALUE(S): integers, percents, and relative values
                Integers: 0, 100, -100 ...
                Percents: 0%, 100% ...
                Relative values: 1+, 1-, 1%+, 2%+ ...
        controls                 : lists controls of the mixer
        contents                 : lists controls of the mixer and their contents

示例命令:

查看当前 codec 支持设置属性(每一款 Codec 对应通路一般一样,所以这里只是举例,需要根据实际情况操作,比如下文对 WM8960 的操作,就是按照 WM8960 CODEC 中设置进行的)

#:/userdata# tinymix contents

通过 ctr 设置属性:

#:/userdata# tinymix set 6 120
#:/userdata# tinymix get 6
120 (range 0->255)

通过 name 设置属性:

#:~# tinymix set "ADC4 PGA gain" 1
#:~# tinymix get "ADC4 PGA gain"
1 (range 0->31)

tinycap 说明:

#:/userdata# tinycap
Usage: tinycap {file.wav | --} [-D card] [-d device] [-c channels] [-r
rate] [-b bits] [-p period_size] [-n n_periods] [-t time_in_seconds]
Use -- for filename to send raw PCM to stdout

示例命令:

tinycap /userdata/test.wav -D 0 -d 0 -t 5

tinyplay 说明:

#:/userdata# tinyplay
usage: tinyplay file.wav [options]
options:
-D | --card <card number> The device to receive the audio
-d | --device <device number> The card to receive the audio
-p | --period-size <size> The size of the PCM's period
-n | --period-count <count> The number of PCM periods
-i | --file-type <file-type > The type of file to read (raw or wav)
-c | --channels <count> The amount of channels per frame
-r | --rate <rate> The amount of frames per second
-b | --bits <bit-count> The number of bits in one sample
-M | --mmap Use memory mapped IO to play audio

示例命令:

tinyplay [file.wav] -D 0 -d 1

确定工具之后,我们需要确认音频路由。我们首先阅读 Audio Codec 的手册,最好找到类似这样图:  WM8960 通路参考

可以形象理解 Audio 路由连接。

然后,我们结合 tinymix 命令确认路由: 我们使用的 Device 编号是 1 ,所以我们这边操作时候 -D 后需要跟 1

tinymix -D 1 contents

接着我们可以打通路由,进行 Audio 录制播放了。

录制:

tinymix -D 1 set 'Left Input Boost Mixer LINPUT1 Volume' 3
tinymix -D 1 set 'Right Input Boost Mixer RINPUT1 Volume' 3

tinymix -D 1 set 'Left Input Boost Mixer LINPUT1 Volume' 1
tinymix -D 1 set 'Right Input Boost Mixer RINPUT1 Volume' 1

tinymix -D 1 set 'Capture Volume' 40,40
tinymix -D 1 set 'ADC PCM Capture Volume' 200,200

tinymix -D 1 set 'Left Boost Mixer LINPUT1 Switch' 1
tinymix -D 1 set 'Right Boost Mixer RINPUT1 Switch' 1

tinymix -D 1 set 'Left Input Mixer Boost Switch' 1
tinymix -D 1 set 'Right Input Mixer Boost Switch' 1

tinymix -D 1 set 'Capture Switch' 1,1

tinycap /userdata/2chn_test.wav -D 1 -d 0 -c 2 -b 16 -r 48000 -p 512 -n 4 -t 5

播放:

tinymix -D  1 set 'Left Output Mixer PCM Playback Switch' 1
tinymix -D  1 set 'Right Output Mixer PCM Playback Switch' 1
tinymix -D  1 set 'Speaker DC Volume' 3
tinymix -D  1 set 'Speaker AC Volume' 3
tinymix -D  1 set 'Speaker Playback Volume' 127 , 127
tinymix -D  1 set 'Playback Volume' 255 , 255
tinymix -D  1 set 'Left Output Mixer PCM Playback Switch' 1
tinymix -D  1 set 'Right Output Mixer PCM Playback Switch' 1

tinyplay /userdata/2chn_test.wav -D 1 -d 0

4.3.19.6. 常见问题

Q1 :无声问题如何定位。

我们同样工作方式提到节点思路,每个进行排查。首先场景,是播放无声,还是录制无声。其次排查软件排查硬件;排查顺序一般音源顺序进行排查,比如播放,我们首先排查软件下发音源是否异常, i2s 状态是否正确, codec 寄存器状态是否正确,最后到 I2S 信号是否正确,时钟是否正确,当然方便情况下,检查音源没有异常时候,直接信号可以,这样直接排查板端状态,而不是硬件侧。录制反过来的,比如下图,我们可以看到 I2S 硬件信号正常的。

黄色代表 DATA 线

蓝色代表 BCLK

紫色代表 LRCLK

 Q1

Q2: LOG 比较少,无法定位问题,怎么办?

调整 debug log 等级

echo "8 4 1 7" > /proc/sys/kernel/printk
echo -n "file dwc-i2s.c +p" > /sys/kernel/debug/dynamic_debug/control

Q3 :如何判断声卡注册成功和 pcm 设备对应关系?

Alsa 有 procfs,挂载目录为:/proc/asound, ALSA 使用 /proc/asound 目录文件保存设备信息控制目的。调试重点关注信息介绍如下。

/proc/asound/cards : 已注册声卡列表。通过查看节点,检查当前系统注册声卡列表或者检查声卡是否注册成功。 /proc/asound/pcm : 分配的 pcm 流设备信息。通过查看节点,找到当前声卡支持设备列表。这有助于测试选择如何设置设备节点的 card/device 值。

/proc/asound/cardX/pcmY[c, p]/ : 系统声卡对应每个 pcm 流设备一个类似上面的 procfs 目录。其中 X 代表卡号,/proc/asound/cards 或者 /dev/snd 下设备节点信息确认; Y 代表设备号,/proc/asound/pcm 或者 /dev/snd 下设备节点信息确认。 c/p 分别代表 capture/playback。该目录查看 PCM 设备信息以及 status。

  • info pcm 流一般信息,比如卡号、设备号、流类型、所绑定的 codec 类型

  • hw_params pcm 流打开状态下,可以查看 pcm 的基础参数配置。比如采样率、位宽、通道数、 period_size、 buffer_size 等。 配置的 period_size、 buffer_size 可能这里打印不同。实际这里查看为准,对应硬件实际参数

    #:/proc/asound/card0/pcm0c/sub0# cat hw_params
    access: RW_INTERLEAVED
    format: S16_LE
    subformat: STD
    channels: 2
    rate: 48000 (48000/1)
    period_size: 1024
    buffer_size: 4096
    
  • sw_params pcm 流打开状态下,查看 start_threshold、 stop_threshold、 silence_threshold 等。重点关注 start_threshold、 stop_threshold 阈值 start_threshold: 该值设置大,从开始播放声音出来延时长,会导致短促声音出来 stop_threshold: 判断是否触发 xrun 的条件。当可用空间大小超过该值时,会触发 xrun。

    #:/proc/asound/card0/pcm0c/sub0# cat sw_params
    tstamp_mode: ENABLE
    period_step: 1
    avail_min: 1
    start_threshold: 1
    stop_threshold: 40960
    silence_threshold: 0
    silence_size: 0
    boundary: 4611686018427387904
    
  • status 可查看当前 substream 的状态 (running、 xrun 等 ), appl_ptr/hw_ptr 指针值。其中, hw_ptr 和 appl_ptr 可以识别底层硬件无法传输接收任何数据情况。 比如,启动测试但是中断正常触发,数据传输将会停滞。此时, hw_ptr/appl_ptr 指针持续得不到更新

Q4 : xrun 是什么问题,遇到怎么定位解决?

音频播放偶现或者连续某个时间段出现断断续续、声音类似“呲呲”或者“爆破”的杂音, 一般发生了 xrun。 xrun 丢系统性能限制不可避免,在满足使用场景需求下,允许一定率,只能通过某些方法优化达到尽量规避。如果出现 xrun 频发,无法恢复情况,就需要排查代码实现是否缺陷

可能出现 xrun 的场景

播放时,应用不断音频数据填入驱动 buffer,驱动 buffer 经过 I2S 送给 codec 播放。当应用填入了,导致驱动 buffer 为空,就触发 underrun 导致帧,可能声音异常现象

录音时, codec 转化数字信号经过 I2S 填入驱动 buffer,应用驱动 buffer 中读取音频数据。当应用读取速度赶不上写入速度,超过 stop_threshold 阈值触发 overrun

触发 xrun 异常使用场景

  • 音频数据来自 storage, IO 操作耗时

  • 访问 RAM disk 和读写 pcm 设备单线程运行

  • 语音进程优先级低,其他优先级任务抢占

定位 xrun

xrun 问题比较多种多样,我们尽可能问题搜集整理,以此提供丰富参考。

检查 xrun 是否由于 IO 操作耗时导致

  • 音频文件写入 ram disk 或者特定设备文件,参考命令

mkdir /data/audio_test
tinycap /data/audio_test/test.wav

tinycap /dev/null

注意,以上只能用于定位 xrun 是否由于 IO 操作耗时导致,不能作为解决 xrun 的方案

  • 优化应用程序测试

使用独立线程访问媒体存储读写 PCM 设备。

录音为例,写文件创建单独线程创建 ring buffer(ring buffer 大小自由调整 ), pcm_read 的 buffer 写入 ring buffer, fwrite 从 ring buffer 读取数据。只要 fwrite 陷入时间超过 ring buffer 的限制,就不会发生 xrun 导致数据。

依靠/proc 下配置一些功能查看信息

开启编译 xrun_debug 的 config 配置项。编译重新烧写镜像。(如果 xrun_debug 存在,表示功能处于使能状态,不需要执行一步)

CONFIG_SND_PCM_XRUN_DEBUG=y
CONFIG_SND_VERBOSE_PROCFS=y
CONFIG_SND_DEBUG=y

对应位置 /proc/asound/cardX/pcmY[c,p]/xrun_debug 比如,往 xrun_debug 写入 3 ,也就是启用基本调试堆栈功能,可以查看 PCM 流是否由于某种原因停止

# Enable basic debugging and dump stack
# Usefull to just see, if PCM stream is stopped for a reason (usually wrong audio process timing from scheduler)
echo 3 > /proc/asound/card0/pcm0p/xrun_debug

通过 ftrace 来分析问题

Ftrace 是内核故障调试性能分析工具,用于分析发生 xrun 的可能原因。检查是否长时间中断占用导致调度及时情况。

  • 开启编译 ftrace 的 config 配置项。编译重新烧写镜像。

CONFIG_FTRACE=y
CONFIG_FUNCTION_TRACER=y
CONFIG_FUNCTION_GRAPH_TRACER=y
CONFIG_STACK_TRACER=y
CONFIG_DYNAMIC_FTRACE=y
  • 生成 trace 文件

echo > /sys/kernel/debug/tracing/trace
echo 1 > /sys/kernel/debug/tracing/events/signal/enable
app_test  # 执行你的应用
killed
echo 0 > /sys/kernel/debug/tracing/events/signal/enable
cat /sys/kernel/debug/tracing/trace > /userdata/trace.txt

或者使用 trace-cmd 工具,这里一下详细代码。

#!/bin/sh
date
rm /userdata/trace.dat
echo "" > /userdata/log/usr/message
trace-cmd record -e irq -e sched_switch -e sched_wakeup -o /userdata/trace.dat &
cnt=0
while true;do
    nr=$(grep -r "audio unexpected delay count" /userdata/log/usr/message | wc -l)
    echo "nr " $nr
    if [ $nr -gt 1 ];then
    echo "find the error message,send the SIGINT Term Interrupt to the trace-cmd !"
    kill -2 $(pidof trace-cmd)
    date
    exit 0
    else
    if [ $cnt -gt 100 ];then
        date
        cnt=0
        echo "rm the trace.dat file,and restart the trace-cmd"
        kill -9 $(pidof trace-cmd)
        rm -rf /userdata/trace.dat
        trace-cmd record -e irq -e sched_switch -e sched_wakeup -o /userdata/trace.dat &
    fi
    fi
    cnt=$((cnt+1))
    sleep 1
done
  • 分析 trace 文件

分析 trace 文件方式多种。比如 google trace viewer 工具、 kernelshark 等 如果使用 google trace viewer 工具需要修改源码,回退支持 lazy 的 trace 输出,使输出的 trace 文件满足谷歌 trace viewer 的格式要求。 patch 如下:

diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index 29db703f6880..86de03221287 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -870,6 +870,10 @@ config HIST_TRIGGERS_DEBUG

       If unsure, say N.

+config OLD_TRACE
+   bool "old ftrace"
+   default y
+
 endif # FTRACE

 endif # TRACING_SUPPORT
diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c
index bc24ae8e3613..e0df7a412c95 100644
--- a/kernel/trace/trace_output.c
+++ b/kernel/trace/trace_output.c
@@ -482,16 +482,23 @@ int trace_print_lat_fmt(struct trace_seq *s, struct trace_entry *entry)
    hardirq              ? 'h' :
    softirq              ? 's' :
                   '.' ;
-
+#ifndef CONFIG_OLD_TRACE
    trace_seq_printf(s, "%c%c%c%c",
         irqs_off, need_resched, need_resched_lazy,
         hardsoft_irq);
-
+#else
+   trace_seq_printf(s, "%c%c%c",
+            irqs_off, need_resched,
+            hardsoft_irq);
+#endif
    if (entry->preempt_count)
    trace_seq_printf(s, "%x", entry->preempt_count);
    else
    trace_seq_putc(s, '.');

+
+
+#ifndef CONFIG_OLD_TRACE
    if (entry->preempt_lazy_count)
    trace_seq_printf(s, "%x", entry->preempt_lazy_count);
    else
@@ -501,7 +508,7 @@ int trace_print_lat_fmt(struct trace_seq *s, struct trace_entry *entry)
    trace_seq_printf(s, "%x", entry->migrate_disable);
    else
    trace_seq_putc(s, '.');
-
+#endif
    return !trace_seq_has_overflowed(s);
 }

关于 ftrace 的生成使用,更参考官方文档描述

https://www.kernel.org/doc/html/latest/trace/ftrace.html

https://perfetto.dev/docs/data-sources/cpu-scheduling

https://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git/

https://kernelshark.org/Documentation.html#graph-info-line\

总的来讲,面对 xrun 问题一些大方向可以排查。

  • 提高线程优先级 ( 设置实时线程 + 优先权值 )

  • 调大 period_size,改变 DMA 传输数据

  • 异步实现 I/O 读写和 ALSA 设备读写

Q5 : 打开设备节点报错相关问题:

  • 出现无法打开指定 PCM 设备提示,没有找到这个文件或者目录

Unable to open PCM device cannot open device (4) for card (1):
    No such file or directory

驱动加载失败,导致 /dev/snd 下没有生成设备节点;设置的 card/device 值错误导致不到对应设备节点 其中,/dev/snd 下没有生成设备节点可能原因:

原因一, I2S/codec 驱动加载失败

原因二, DTS 中驱动节点的 status 属性设置“ okay”状态,导致驱动加载不会执行任何操作。

  • 出现无法打开 PCM 设备提示

Unable to open PCM device ()

检查测试设置参数值,是否超过 I2S 和 codec 驱动对于 snd_soc_dai_driver 定义的 rate、 format、 channel 最值范围交集; 检查测试设置的 period_size/period_count, 是否超过驱动定义有效范围。

  • 打开设备节点卡住,应用任何 log 日志打印 ALSA 框架允许单个设备同一时刻只能打开一次。检查应用实现上,是否存在上次执行退出情况下,又启动应用

  • pcm_read/pcm_write 返回异常值为 -5 调整 ALSA 框架 log 打印等级,检查是否由于中断异常或者硬件链接异常导致

  • pcm_read/pcm_write 返回异常值为 -32 一般发生了 xrun

  • pcm_read 返回异常值为 -16 正常录音情况 (X5 做 slave) 下,时钟突然断掉或者硬件链接中断发生异常

  • 录制数据为 0 或者播放没有声音

    1 ,示波器测量 I2S 的时钟线频率是否正常, data 线是否数据输出。

    2 ,如果时钟频率符合预期或者某个时钟信号持续保持低电平情况,检查 I2S

    寄存器配置是否符合预期。比如主从模式、时钟比值状态

    3 ,如果以上没有问题,就需要检查 codec 芯片是否正常工作。播放一个正弦波,测量 codec 侧模拟信号

    输出,如果这里没有输出,确认 codec 的时钟比值配置和 X5 端是否匹配,以及当前 X5 端的时钟比值是否在 codec 可支持范围。

    4 ,如果 codec 的输出能量准确信号,检查功放芯片是否正常工作。确认功放芯片管脚幅值是否正常

  • 录制 / 播放噪声