赞
踩
佛沟 笔记
module_init(hello_init); ///每个ko文件的最后都必须有module_init和module_exit
module_exit(hello_exit);
CSV即Comma Separate Values,这种文件格式经常用来作为不同程序之间的数据交互的格式。
音频log中常用的关键字:
Events----各种事件
keycode----按键 keycode=键值大全; down =true(按下) down =false(松开)
AUDIO_DEVICE_OUT/IN
AUDIO_ROUTE-----查看播放通路
DeviceName----查看音频播放设备
shutdown----重启关键字
subsys-restart: subsystem_shutdown(): [kworker/u17:2:437]: Shutting down modem
qcom:
snd_devices------播放设备
screenoff(screen off)-------锁屏关键字
stop try start standby by lockScreenMsg ON when screenoff
1. 24950 10-24 08:58:24.808423 963 3018 D AudioService: Mits getDeviceForStream: stream2 device:2
2. 25004 10-24 08:58:24.909459 519 5008 V APM_AudioPolicyManager: setStreamVolumeIndex() stream 8, device 00000008, index 10
09-22 17:51:09.532 1915 1915 D AS.AudioService: checkLimitedMusicVolumeForSpeaker mMaxMusicVolumeForSpeaker = 150index = 11 (音量level)
3. 25632 12-20 09:38:31.613 689 689 D AudioManager: setWiredDeviceConnectionState: type = 8, state = 1, address, name = (有线耳机插入/拔出log)
4. 19560 12-15 04:47:13.363570 626 3572 D APM_AudioPolicyManager: setDeviceConnectionStateInt() device: 0x10, state 1, address 88:C6:26:DA:71:D6 name Jaybird X3 (蓝牙耳机连接/断开)
5. APM_AudioPolicyManager: [MTK_APM_Input]getInputForAttr() source 0 ····· 音频通路
这个source=0代表是record mode, 要等于7才是voip mode.
6. D APM_AudioPolicyManager: [MTK_APM_Route]setPhoneState() state 1/0 电话接通,挂断log
7. wcd_mbhc_report_plug: 检查耳机插入拔出以及阻抗
wcd_mbhc_report_plug,compute impedance,zl = 20,zr = 22
wcd_mbhc_report_plug: Reporting insertion 3(3)
wcd_mbhc_report_plug: Reporting removal 3(0)
项目有标准过不了的风险,所以需要兼容HAC方案设计。
标准的具体细节,标准的要求是什么
--------------------------------------
DAI: Digital Audio Interfaces 数字音频接口
APR:Asynchronous Packet Router 异步数据包路由
LPASS:Low Power Audio Subsystem 低功耗音频子系统
ACDB:Audio Calibration Database 音频校准数据库
ADSP:Audio Digital Signal Processor 音频数字信号处理器
MBHC:Multibutton Headset Control 多按键耳机控制
HLOS:高级操作系统
POPP: Per-Object post-Processing
COPP: Common-Object post-Processing xx后处理模块(PP)
APSS: Applications Processor Subsystem 主CPU处理器
--------------------------------------
FAILED: out/soong/build.ninja(报错用以下方法解决)
Linux扩展Swap分区:
首先先建立一个分区:
sudo dd if=/dev/zero of=/home/swap bs=1024 count=2048000
(报错:dd: failed to open '/swapfile': Text file busy ----->> 运行 sudo swapoff -a)
分区变成swap分区:
sudo /sbin/mkswap /home/swap
使用这个swap分区,使其成为有效状态
sudo /sbin/swapon /home/swap
free -m命令查看一下内存和swap分区大小
设置扩展的swap分区为自动挂载
vim /etc/fstab
最后一行加上:
/home/swap swap swap defaults 0 0
--------------------------------------
make bootimage: 编译内核
增加PA驱动后内核编译报错 fatal error: 'dsp/msm_audio_ion.h' file not found\n"
fix:makefile 文件写错
KO编译方式
cd .repo/manifests
grep -rn "audio-kernel"
确定仓的映射;audio-kernel仓的位置(vendor/qcom/opensource/audio-kernel or kernel/msm-5.4/techpack/audio)
/vendor/qcom/opensource/audio-hal/primary-hal/hal/Android.mk
LOCAL_MODULE := audio.primary.$(TARGET_BOARD_PLATFORM)
hal层编译后so包
--------------------------------------
git clean -dxf :清除未追踪的文件
文件夹里有个.gitnore文件,控制忽略哪些文件或者文件夹,vscode对应目录文件夹变成灰色
--------------------------------------
topo-id 和 port-id 怎么确认:
port-id:文件/kernel/msm-5.4/techpack/audio/include/dsp/apr_audio-v2.h 或
/kernel/msm-5.4/sound/soc/qcom/qdsp6/q6afe.c 中 AFE_PORT_ID_XXXXX 对应的值,例如
AFE_PORT_ID_SECONDARY_MI2S_RX
AFE_PORT_ID_SECONDARY_MI2S_TX
--------------------------------------
XXXX项目驱动合入:
device/qcom/holi/init.target.rc BoardConfig.mk holi.mk
kernel/msm-5.4/techpack/audio/asoc/codecs/aw882xx/aw882xx.c ....
kernel/msm-5.4/techpack/audio/Makefile.am asoc/Kbuild asoc/codecs/Kbuild config/holiauto.conf config/holiautoconf.h
vendor/qcom/opensource/audio-hal/primary-hal/configs/holi/holi.mk
在q6afe.c中,我们一般看afe_port_param_data_v2结构体,有这个就认为是V2
-----------------------------------------------
mixer_paths_qrd.xml 编译后内容被修改?原因 opensource/audio-hal/primary-hal/configs/holi/holi.mk 将 opensource/audio-hal/primary-hal/configs/common/base/mixer_paths_base.xml 替换到 holi/mixer_paths_qrd.xml
修改 /configs/holi/holi.mk
mixer_paths_xxxx.xml :确定使用那个xml #define MIXER_XML_PATH 定义
-------------------------------------
新建分支:git branch branch_name(分支名称)
切换分支:git checkout branch_name
创建并切换到分支:git checkout -b branch_name
将branch_name分支合并到 当前分支: git merge branch_name
patch打包及应用:
标准patch git diff > patch
git apply patch
git apply –check 检查补丁能否干净完整合入
git专用Patch git format-patch HEAD^ #生成最近的#次commit的patch
git format-patch 你的commit对应的id
-------------------------------------------------
1.查看当前的声卡:
cat /proc/asound/cards
2.查看当前有哪些进程占用了pcm设备节点
lsof |grep pcm
3.查看有哪些音频设备节点
ls /dev/snd/
音频设备的命名规则为 [device type]C[card index]D[device index][capture/playback],即名字中含有4部分的信息:
comprC0D11 comprC0D7 pcmC0D10c pcmC0D15p pcmC0D20c pcmC0D2c pcmC0D33p pcmC0D38c pcmC0D5p timer
comprC0D24 controlC0 pcmC0D12c pcmC0D16c pcmC0D21c pcmC0D2p pcmC0D34c pcmC0D39p pcmC0D6c
comprC0D25 hwC0D1000 pcmC0D12p pcmC0D17c pcmC0D22c pcmC0D30c pcmC0D34p pcmC0D3c pcmC0D80c
......
[device type]
设备类型,通常只有control/pcm/compr。从上图可以看到声卡会管理很多设备,PCM设备只是其中的一种设备。
[card index]
声卡的id,代表第几块声卡。通常都是0,代表第一块声卡。手机上通常都只有一块声卡。
[device index]
设备的id,代表这个设备是声卡上的第几个设备。设备的ID只和驱动中配置的DAI link的次序有关。如果驱动没有改变,那么这些ID就是固定的。
[capture/playback]
只有PCM设备才有这部分,只有c和p两种。c代表capture,说明这是一个提供录音的设备,p代表palyback,说明这是一个提供播放的设备。
2.查看pcm设备列表:
cat /proc/asound/pcm
xxxx# cat proc/asound/pcm
00-00: MultiMedia1 (*) : : playback 1 : capture 1
00-01: MultiMedia2 (*) : : playback 1 : capture 1
00-02: VoiceMMode1 (*) : : playback 1 : capture 1
00-03: VoIP (*) : : playback 1 : capture 1
00-04: MultiMedia3 (*) : : playback 1
00-05: AFE-PROXY RX msm-stub-rx-5 : : playback 1
00-06: AFE-PROXY TX msm-stub-tx-6 : : capture 1
......
前面2位数字指的card index:00,后面2位是device index:01
tinypcminfo用于查看pcm通道的相关信息:
tinypcminfo -D 0 -d 0
--------------------------------------------------
adb remount报错: Cannot use remount when a checkpoint is in progress
# in device
vdc checkpoint commitChanges
# outside device
adb shell vdc checkpoint commitChanges
安装adb后安装fastboot 驱动:https://link.zhihu.com/?target=https%3A//dl.google.com/android/repository/usb_driver_r13-windows.zip
https://zhuanlan.zhihu.com/p/366904302
--------------------------------------------------
单编adsp :编译vendor/build_nonhlos-xxx.sh, 选择对应的编号
单编报错:ERROR: qaic builder: couldn't find qaic exe for your host.
locate libffi.so
sudo ln -s /usr/lib/x86_64-linux-gnu/libffi.so.6.0.4 /usr/lib/x86_64-linux-gnu/libffi.so.6
或者
sudo ln -sf /usr/lib/x86_64-linux-gnu/libffi.so.7 /usr/lib/x86_64-linux-gnu/libffi.so.6
1. awinic 算法库文件编译到代码中去:找另外一个py,保证算法地址对就OK
2. 单编adsp时 ./vendor/huaqin/build/build_nonhlos-holi.sh 5 | tee build_adsp.log
3. 编译后结果为 adsp.mbn | grep -rn -a "Awinic" adsp.mbn 检查算法是否编译成功 -a 不忽略二进制文件
grep -rn "androidboot.serialno=NFNA1F0156" 查看log中设备SN号
gcc编译流程:
先预处理为.i文件: gcc -E add.c -o add.i
再编译为汇编文件: gcc -S add.i -o add.s
再汇编为二进制的.o文件: gcc -c add.s -o add.o
1 .o文件是源码编译出的二进制文件
2 .a文件实质上就是*.o文件打了个包。一般把它叫做 静态库文件
3 .so文件不仅是.o*文件打了一个包,它是一个ELF格式的文件,即linux的可执行文件
--------------------------------------------------
播放:playback
deep_buffer:铃声、视频等对 时延要求不高 的放音场景
----Android开发中最常用的播放模式,边加载边播放,由 CPU进行解码 ,再送入AudioFlinger进行混音播放
low_latency:按键音、触摸音、游戏背景音等低延时的放音场景
mutil_channel:多媒体播放。和hdmi相关
compress_offload :mp3、flac、aac等格式的音源播放场景,这种音源不需要软件解码,直接把数据送到 硬件解码器(aDSP),由硬件解码器(aDSP)进行解码,可以降低CPU的负载
录制:
record:普通录音场景
compress_record: A recording mode where encoded packets are received by the APSS directly from the aDSP
查看调用通路log:audio_hw_primary: enable_audio_route: apply mixer and update path:
--------------------------------------------------
mixer_paths_xxx.xml:
在
target/vendor/qcom/opensource/audio-hal/primary-hal/hal/xxx(msm8974)/platform.c
(target/hardware/qcom/audio / hal/msm8974/platform.c)
target/vendor/qcom/opensource/audio-hal/primary-hal/hal/audio_hw.c
中调用
<path name="speaker"> ---- use case 和 devices
<ctl name="PRI_MI2S_RX Channels" value="Two" /> ----tinymix 控件
<ctl name="aw_dev_0_switch" value="Enable" />
<ctl name="aw_dev_1_switch" value="Enable" />
<ctl name="aw_dev_1_prof" value="Music" />
</path>
ctl name -----audio devices --> 在 platform.c 中定义
path name -----usecase --> 在 audio_hw.c 中定义
--------------------------------------------------
检查dailink是否加载上,与代码比较相同即可
cat sys/kernel/debug/asoc/dais
cat sys/kernel/debug/asoc/components
检查设备树I2C的加载
cat sys/firmware/devicetree/base/soc/i2c@4c90000/aw-cali-mode
-- aw_attr
xxd sys/firmware/devicetree/base/soc/i2c@4c90000/sound-channel
-- xxd用来检查数值,以16进制显示
--------------------------------------------------
Linux 常用的文件系统有三个:procfs、sysfs、debugfs
procfs:该文件系统主要用来反馈内核的信息,包括系统中所有的中断、进程信息都可以在这里查看。挂载在 /proc/...
sysfs:该文件系统主要是和驱动强相关,会反馈所有的驱动信息,以目录形式显示。挂载在 /sys/...
debugfs:该文件系统挂载在 /sys/kernel/debug/... ,主要用来 debug
sys/bus/msm_subsys/devices:高通子系统
包含subsystem0 ~ 5 共6个子系统
--> crash_count
崩溃次数
--> firmware_name
firmware名称,0~5 分别是adsp、cdsp、
--> name
子系统名称,0~5 分别是adsp、cdsp、
--> power
暂不清楚
--> restart_level
system、related:
--> state
--> subsystem -> ../../../../../bus/msm_subsys
--> system_debug
--> uevent
--> waiting_for_supplier
--> wakeup
-----------------
DRE:Dynamic range enhancement
The compander or DRE (Dynamic range enhancement) provides Hi-Fi audio quality for headphone and line-output
PAs. Compander support is available on MSM8998 with WCD9335 and WCD934x. The DRE controller is located in
the codec (WCD9335/WCD934x)
compander 或者 DRE(动态范围增强)为耳机和模拟PA提供Hi-Fi 高质量音频。DRE 控制器位于codec中
The mixer commands below will enable the DRE in stereo speaker path and setup the Headset for the Playback.
adb shell
tinymix 'HPHL_COMP Switch'
tinymix 'HPHR_COMP Switch' ---读取当前状态,是否OFF
tinymix 'HPHL_COMP Switch' 1
tinymix 'HPHR_COMP Switch' 1 ---写入,打开DRE
tinymix 'HPHL_COMP Switch'
tinymix 'HPHR_COMP Switch' ---读取当前状态,是否ON
或 COMP1 Switch tinymix | grep "COMP"
KBA-170413105901_REV_3_How_to_Enable_Compander_on_WCD9335_WCD934x.pdf
------------------------------------
1、什么是SELinux:
SELinux(security enhanced linux)安全增强型Linux系统,它是一个linux内核模块,也是Linux的一个安全子系统。
Selinux的主要作用就是最大限度地减小系统中服务进程可访问的资源(最小权限原则)
sestatus / getenforce: 检查SELinux的状态
--Enforcing SELinux处于“Enforcing”模式,正在强制执行系统的安全策略;
--Permissive SELinux处于“Permissive”模式,只会记录安全事件,而不会强制执行任何策略;
--Disabled SELinux处于“Disabled”模式,已经被禁用,不会提供任何安全保护
setenforce: 设置SELinux的状态
------------------------------------
linux 信号系统:
http://akaedu.github.io/book/ch33.html
binder是一种进程间通信机制,基于开源的 OpenBinder 实现
确认audioHal启动情况,log标签:audio_hw_primary; 关键字:adev_open
09-17 15:10:13.533 30607 30615 D audio_hw_primary: adev_open: enter
09-17 15:10:14.346 30607 30615 D audio_hw_primary: adev_open: exit time 813.25 ms
这表示audiohal启动成功了
TimeCheck:
android audioserver里面用于binder调用超时检测有个TimeCheck机制,对于audioserver binder调用不能超过5s,如果超过5s就会产生一个abort的log
实现路径在frameworks\av\media\libmedia\TimeCheck.cpp
TimeCheck线程属于audioserver进程,在每次Binder通信时创建一个TimeCheck的对象,同时创建守护线程,在每次调用结束之后销毁
1. audiohal进程被杀 ---SIGNAL 35
audioserver的调用超时之后TimeCheck线程先向audio hal的进程发送DEBUGGER_SIGNAL的信号,触发debuggerd_signal_handler信号,fork子进程crash_dump32产生dump信息,同时该信号触发tombstoned进程将相关dump信息写入到/data/tombstones/tombstone_xx文件。(sigqueue)
08:25:31.244 11489 11568 I AudioFlinger: [createRecord:2049]
08:25:36.250 11489 11522 I TimeCheck: requesting tombstone for pid: 11490
中间5s超时
2. audioserver进程自杀 ----SIGNAL 6
在给audio hal进程发生DEBUGGER_SIGNAL的信号之后,再等待1s,然后使用LOG_ALWAYS_FATAL断言函数触发系统调用abort(),发送SIGABRT信号,终止程序,触发debuggerd_signal_handler信号,fork子进程crash_dump32产生dump信息,同时该信号触发tombstoned进程将相关dump信息写入到/data/tombstones/tombstone_xx文件。
LOG_ALWAYS_FATAL实现:
08:25:36.250 11489 11522 I TimeCheck: requesting tombstone for pid: 11490
08:25:37.250 11489 11522 F TimeCheck: TimeCheck timeout for IAudioFlinger command 2
中间延时1s
TimeCheck 一般伴随着Tombstone
signal 11:段错误,当应用程序试图对无权访问的内存地址进行读写操作时,就会触发这个错误
例如 1.指针错误 2.内存错误 3.堆栈溢出 4.资源不足
aw882xx_codec_probe: 未注册
排查方式:
1. 检查dai_link配置
cat sys/kernel/debug/asoc/codecs或sys/kernel/debug/asoc/components
2. 确认声卡是否注册成功
失败 → 检索ASoC,分析相关报错
成功 → 确认所修改的dai_link是否有添加到声卡
确认方式:分析machine_probe函数,确认所修改的dai_link是否有添加到total_links。
如下示例,修改的dai_link为msm_mi2s_be_dai_links,而machine_probe中读取”qcom, mi2s-audio-intf”不为0时才会添加msm_mi2s_be_dai_links。
----------------------------------------
moto手机切高通口,连接高通工具
adb reboot bootloader
fastboot oem qcom-on
fastboot reboot ---出高通口
----------------------------------------
getevent: 检查按键、耳机、触摸等外部事件
----------------------------------------
getprop 查看属性
adb pull product/etc/build.prop .
[ro.audio.monitorRotation]: [true]
[ro.audio.monitorWindowRotation]: [true]
旋转声道属性
adb push D:\Project\Fogo\build.prop product/etc/
logcat |grep set_parameter:
检查旋转角度
----------------------------------------
module_platform_driver(holi_asoc_machine_driver);
#define module_platform_driver(__platform_driver) \
module_driver(__platform_driver, platform_driver_register, platform_driver_unregister)
#define platform_driver_register(drv) \
__platform_driver_register(drv, THIS_MODULE)
extern int __platform_driver_register(struct platform_driver *,
struct module *);
extern void platform_driver_unregister(struct platform_driver *);
----------------------------------------
module_init(fn) ----/kernel/msm-5.4/include/linux/module.h
__initcall(fn) ----/kernel/msm-5.4/include/linux/init.h
device_initcall(fn) ----/kernel/msm-5.4/include/linux/init.h
__define_initcall(fn, 6) ----/kernel/msm-5.4/include/linux/init.h
___define_initcall(fn, id, .initcall##id) ----/kernel/msm-5.4/include/linux/init.h
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(#__sec ".init"))) = fn; ----/kernel/msm-5.4/include/linux/init.h
即module_init(aw882xx_i2c_init)展开为:
static initcall_t __initcall_aw882xx_i2c_init6 __used __attribute__((__section__(".initcall6.init"))) = aw882xx_i2c_init
所以这里的意思就是:定义一个名为 __initcall_aw882xx_i2c_init6 的函数指针变量,并初始化为 aw882xx_i2c_init (指向 aw882xx_i2c_init);并且该函数指针变量存放于 .initcall6.init 代码段中。
函数指针的调用:do_pre_smp_initcalls ----/kernel/msm-5.4/init/main.c
static void __init do_pre_smp_initcalls(void)
{
initcall_entry_t *fn;
trace_initcall_level("early");
for (fn = __initcall_start; fn < __initcall0_start; fn++)
do_one_initcall(initcall_from_entry(fn));
}
initcall_t :函数指针类型
typedef int (*initcall_t)(void);
__attribute__((__section__("section-name"))): 使用atttribute 来声明一个 section 属性,是在程序编译时,将一个函数或变量放到指定的段,即 section 中
-----------------------------------------
音频log tag
audio HAL层: audio_hw_primary /vendor/qcom/opensource/audio-hal/primary-hal/hal/audio_hw.c
SoundTrigger HAL: soundtrigger /vendor/qcom/opensource/audio-hal/primary-hal/hal/audio_extn/soundtrigger.c
audio_route audio_route /system/media/audio_route/audio_route.c
ACDB loader ACDB-LOADER /vendor/qcom/proprietary/mm-audio-cal/audio-acdb-util/acdb-loader/src/acdb-loader.c
-----------------------------------------
HIDL:
是用于指定 HAL 和其用户之间的接口的一种接口描述语言 (IDL)。HIDL 允许指定类型和方法调用(会汇集到接口和软件包中),目的是为了解耦硬件与上层软件,摆脱传统的HAL需要依赖上层与底层硬件
SoundTrigger:
应用场景主要有 语音助手(VIA) 或者 唤醒词(HOTWORD),识别后会触发相应的events给到上层。它不负责处理后续音频数据,应用层应该通过AudioRecord的方式来读取后续的音频数据
-----------------------------------------
音频kernel树:
/kernel/msm-5.4/techpack/audio/
--->asoc
--->dsp
--->ipc
--->soc
--->include
-->asoc
-->dsp
-->ipc
-->soc
--->config
------------------------------------------
在线抓取AP log:
adb root
adb logcat -c
adb logcat -v time > xxx/logcat.log
------------------------------------------
dts/dtsi 文件解析:
设备树(dt:device tree)是linux内核采用的 参数表示和传递技术,在 系统引导启动阶段 进行设备初始化的时候,将设备树中描述的 硬件信息传递给操作系统
dts/dtsi源文件 ----> dtc编译器 ----> dtb二进制文件
dts文件在内核源码中的存放位置: arch/arm/boot/dts
vendor/qcom/proprietary/devicetree/qcom/holi.dtsi
----
[label:] <node-name> [@<unit-address>]{
[property]
[child nodes]
[child nodes]
......
};
[]:表示该项可以省略,<>:表示不可省略;
[label:]:label是标签名,为了方便访问节点,后面可以直接通过&label来访问该节点。
node-name:节点名称。根节点的名称必须是/
[@unit-address]:unit-address是设备地址,如cpu node就是0、1这种,reg node就是0x12010000这种;
----
static inline int of_property_read_u32(const struct device_node *np,
const char *propname,
u32 *out_value)
{
return of_property_read_u32_array(np, propname, out_value, 1);
}
根据 设备节点 device_node 查找字符串
------------------------------------------
dai_link 的配置
SND_SOC_DAILINK_DEFS 用于定义 compoment,而 SND_SOC_DAILINK_REG 则是引用 compoment
SND_SOC_DAILINK_DEFS(pri_mi2s_rx,
DAILINK_COMP_ARRAY(COMP_CPU("msm-dai-q6-mi2s.0")),
DAILINK_COMP_ARRAY(COMP_CODEC("aw882xx_smartpa_0", "aw882xx-aif-0"),
COMP_CODEC("aw882xx_smartpa_1", "aw882xx-aif-1")),
DAILINK_COMP_ARRAY(COMP_PLATFORM("msm-pcm-routing")));
----
#define DAILINK_COMP_ARRAY(param...) param
#define COMP_CPU(_dai) { .dai_name = _dai, }
#define COMP_CODEC(_name, _dai) { .name = _name, .dai_name = _dai, }
#define COMP_PLATFORM(_name) { .name = _name }
----
#define SND_SOC_DAILINK_DEFS(name, cpu, codec, platform...) \
SND_SOC_DAILINK_DEF(name##_cpus, cpu); \
SND_SOC_DAILINK_DEF(name##_codecs, codec); \
SND_SOC_DAILINK_DEF(name##_platforms, platform)
----
#define SND_SOC_DAILINK_DEF(name, def...) \
static struct snd_soc_dai_link_component name[] = { def }
----
struct snd_soc_dai_link_component {
const char *name;
struct device_node *of_node;
const char *dai_name;
};
----
DAILINK 定义:
SND_SOC_DAILINK_DEFS(pri_mi2s_rx,
DAILINK_COMP_ARRAY(COMP_CPU("msm-dai-q6-mi2s.0")),
DAILINK_COMP_ARRAY(COMP_CODEC("aw882xx_smartpa_0", "aw882xx-aif-0"),
COMP_CODEC("aw882xx_smartpa_1", "aw882xx-aif-1")),
DAILINK_COMP_ARRAY(COMP_PLATFORM("msm-pcm-routing")));
->
SND_SOC_DAILINK_DEFS(pri_mi2s_rx,
COMP_CPU("msm-dai-q6-mi2s.0"),
COMP_CODEC("aw882xx_smartpa_0", "aw882xx-aif-0"),
COMP_CODEC("aw882xx_smartpa_1", "aw882xx-aif-1"),
COMP_PLATFORM("msm-pcm-routing"));
->
SND_SOC_DAILINK_DEFS(pri_mi2s_rx,
{ .dai_name = "msm-dai-q6-mi2s.0", },
{ .name = "aw882xx_smartpa_0", .dai_name = "aw882xx-aif-0", },
{ .name = "aw882xx_smartpa_1", .dai_name = "aw882xx-aif-1", },
{ .name = "msm-pcm-routing" });
原定义:
SND_SOC_DAILINK_DEFS(name, cpu, codec, platform...)
->
static struct snd_soc_dai_link_component name##_cpus[] = { cpu };
static struct snd_soc_dai_link_component name##_codecs[] = { codec };
static struct snd_soc_dai_link_component name##_platforms[] = { platform }
故DAILINK定义可变形为:
static struct snd_soc_dai_link_component pri_mi2s_rx_cpus[] = { .dai_name = "msm-dai-q6-mi2s.0", };
static struct snd_soc_dai_link_component pri_mi2s_rx_codecs[] = { .name = "aw882xx_smartpa_0", .dai_name = "aw882xx-aif-0", };
static struct snd_soc_dai_link_component pri_mi2s_rx_codecs[] = { .name = "aw882xx_smartpa_1", .dai_name = "aw882xx-aif-1", };
static struct snd_soc_dai_link_component pri_mi2s_rx_platforms[] = { .name = "msm-pcm-routing" };
--------------------------------------------
#define SND_SOC_DAILINK_REG1(name) SND_SOC_DAILINK_REG3(name##_cpus, name##_codecs, name##_platforms)
#define SND_SOC_DAILINK_REG2(cpu, codec) SND_SOC_DAILINK_REG3(cpu, codec, null_dailink_component)
#define SND_SOC_DAILINK_REG3(cpu, codec, platform) \
.cpus = cpu, \
.num_cpus = ARRAY_SIZE(cpu), \
.codecs = codec, \
.num_codecs = ARRAY_SIZE(codec), \
.platforms = platform, \
.num_platforms = ARRAY_SIZE(platform)
#define SND_SOC_DAILINK_REGx(_1, _2, _3, func, ...) func
#define SND_SOC_DAILINK_REG(...) \
SND_SOC_DAILINK_REGx(__VA_ARGS__, \
SND_SOC_DAILINK_REG3, \
SND_SOC_DAILINK_REG2, \
SND_SOC_DAILINK_REG1)(__VA_ARGS__)
------------------------------------------
__VA_ARGS__ 的用法:
自定义打印时,用到可变参数,用...即可表示可变参数,如下:
##__VA_ARGS__前面加上##的作用是:当可变参数的个数为0时,这里的##可以把把前面多余的","去掉,否则会编译出错。
#include <stdio.h>
#define LOG1(...) printf(__VA_ARGS__) //...表示可变参数,__VA_ARGS__就是将...的值复制到这里
int main(int argc, char** argv)
{
char *str = "test __VA_ARGS__";
int num = 10086;
LOG1("this is test __VA_ARGS__\r\n");
LOG1("this is test __VA_ARGS__:%s, %d\r\n", str, num);
return 0;
}
打印结果:
this is test __VA_ARGS__
this is test __VA_ARGS__:test __VA_ARGS__, 10086
------------------------------------------
----------------------------------------------------------------------------------------------------------------------------------------|
SND_SOC_DAILINK_DEFS(pri_mi2s_rx, |
DAILINK_COMP_ARRAY(COMP_CPU("msm-dai-q6-mi2s.0")), |
DAILINK_COMP_ARRAY(COMP_CODEC("aw882xx_smartpa_0", "aw882xx-aif-0"), |
COMP_CODEC("aw882xx_smartpa_1", "aw882xx-aif-1")), |
DAILINK_COMP_ARRAY(COMP_PLATFORM("msm-pcm-routing"))); |
|
展开为: |
static struct snd_soc_dai_link_component pri_mi2s_rx_cpus[] = { .dai_name = "msm-dai-q6-mi2s.0", }; |
static struct snd_soc_dai_link_component pri_mi2s_rx_codecs[] = { .name = "aw882xx_smartpa_0", .dai_name = "aw882xx-aif-0", }; |
static struct snd_soc_dai_link_component pri_mi2s_rx_codecs[] = { .name = "aw882xx_smartpa_1", .dai_name = "aw882xx-aif-1", }; |
static struct snd_soc_dai_link_component pri_mi2s_rx_platforms[] = { .name = "msm-pcm-routing" }; |
|
----------------------------------------------------------------------------------------------------------------------------------------|-
|
SND_SOC_DAILINK_REG(pri_mi2s_rx); //在 kernel/msm-5.4/techpack/audio/asoc/holi.c 中有引用 |
struct snd_soc_dai_link msm_mi2s_be_dai_links[] |
传入一个参数时,展开为: |
.cpus = pri_mi2s_rx_cpus, |
.num_cpus = ARRAY_SIZE(pri_mi2s_rx_cpus), |
.codecs = pri_mi2s_rx_codecs, |
.num_codecs = ARRAY_SIZE(pri_mi2s_rx_codecs), |
.platforms = pri_mi2s_rx_platforms, |
.num_platforms = ARRAY_SIZE(pri_mi2s_rx_platforms) |
----------------------------------------------------------------------------------------------------------------------------------------|
struct snd_soc_dai_link {
/* config - must be set by machine driver */
const char *name; /* Codec name */
const char *stream_name; /* Stream name */
/*
* You MAY specify the link's CPU-side device, either by device name,
* or by DT/OF node, but not both. If this information is omitted,
* the CPU-side DAI is matched using .cpu_dai_name only, which hence
* must be globally unique. These fields are currently typically used
* only for codec to codec links, or systems using device tree.
*/
/*
* You MAY specify the DAI name of the CPU DAI. If this information is
* omitted, the CPU-side DAI is matched using .cpu_name/.cpu_of_node
* only, which only works well when that device exposes a single DAI.
*/
struct snd_soc_dai_link_component *cpus;
unsigned int num_cpus;
/*
* You MUST specify the link's codec, either by device name, or by
* DT/OF node, but not both.
*/
/* You MUST specify the DAI name within the codec */
struct snd_soc_dai_link_component *codecs;
unsigned int num_codecs;
/*
* You MAY specify the link's platform/PCM/DMA driver, either by
* device name, or by DT/OF node, but not both. Some forms of link
* do not need a platform. In such case, platforms are not mandatory.
*/
struct snd_soc_dai_link_component *platforms;
unsigned int num_platforms;
int id; /* optional ID for machine driver link identification */
const struct snd_soc_pcm_stream *params;
unsigned int num_params;
unsigned int dai_fmt; /* format to set on init */
enum snd_soc_dpcm_trigger trigger[2]; /* trigger type for DPCM */
/* codec/machine specific init - e.g. add machine controls */
int (*init)(struct snd_soc_pcm_runtime *rtd);
/* optional hw_params re-writing for BE and FE sync */
int (*be_hw_params_fixup)(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params);
/* machine stream operations */
const struct snd_soc_ops *ops;
const struct snd_soc_compr_ops *compr_ops;
/* Mark this pcm with non atomic ops */
bool nonatomic;
/* For unidirectional dai links */
unsigned int playback_only:1;
unsigned int capture_only:1;
/* Keep DAI active over suspend */
unsigned int ignore_suspend:1;
/* Symmetry requirements */
unsigned int symmetric_rates:1;
unsigned int symmetric_channels:1;
unsigned int symmetric_samplebits:1;
/* Do not create a PCM for this DAI link (Backend link) */
unsigned int no_pcm:1;
/* This DAI link can route to other DAI links at runtime (Frontend)*/
unsigned int dynamic:1;
#ifdef CONFIG_AUDIO_QGKI
/* This DAI link can be reconfigured at runtime (Backend) */
unsigned int dynamic_be:1;
#endif
/*
* This DAI can support no host IO (no pcm data is
* copied to from host)
*/
unsigned int no_host_mode:2;
/* DPCM capture and Playback support */
unsigned int dpcm_capture:1;
unsigned int dpcm_playback:1;
/* DPCM used FE & BE merged format */
unsigned int dpcm_merged_format:1;
/* DPCM used FE & BE merged channel */
unsigned int dpcm_merged_chan:1;
/* DPCM used FE & BE merged rate */
unsigned int dpcm_merged_rate:1;
/* pmdown_time is ignored at stop */
unsigned int ignore_pmdown_time:1;
/* Do not create a PCM for this DAI link (Backend link) */
unsigned int ignore:1;
struct list_head list; /* DAI link list of the soc card */
struct snd_soc_dobj dobj; /* For topology */
#ifdef CONFIG_AUDIO_QGKI
/* this value determines what all ops can be started asynchronously */
enum snd_soc_async_ops async_ops;
#endif
};
-----------------------------------------------------------
设备树of操作函数:
1. 查找节点函数 of_find_node_by_path
inline struct device_node *of_find_node_by_path(const char *path)
path:带有全路径(/sys/firmware/devicetree/base)路径下的节点名,可以使用节点的别名
返回值: 找到的节点,如果为 NULL 表示查找失败
如:struct device_node *of_aliases = of_find_node_by_path("/aliases");
2. 提取属性值函数 of_find_property
property *of_find_property(const struct device_node *np,const char *name,int *lenp)查找指定属性
np:设备节点;name: 属性名字;lenp:属性值的字节数
返回值: 找到的属性
如:if (of_find_property(dev->of_node, "qcom,cdc-micbias4-mv", NULL))
qcom,cdc-micbias4-mv = <1800>;
3. 读取字符串属性函数 of_property_read_string
int of_property_read_string(struct device_node *np,
const char *propname,
const char **out_string)
np:设备节点; proname:要读取的属性名字;out_string:读取到的字符串值
返回值: 0,读取成功,负值,读取失败
如:ret = of_property_read_string(np, "aw-cali-mode", &cali_mode_str);
aw-cali-mode = "aw_attr";
4. of_property_read_u32/u16/u8/u64 :用于读取只有一个整形值的属性
5. of_property_count_elems_of_size : 用于获取属性中元素的数量
rc = of_property_count_elems_of_size(node, "qcom,soc", sizeof(int));
6. of_property_read_u32/u16/u8/u64_array :读取属性中 u32/u16/u8/u64 类型的数组数据
Linux 内核使用 device_node 结构体来描述一个节点:
struct device_node {
const char *name;
phandle phandle;
const char *full_name;
struct fwnode_handle fwnode;
struct property *properties;
struct property *deadprops; /* removed properties */
struct device_node *parent;
struct device_node *child;
struct device_node *sibling;
#if defined(CONFIG_OF_KOBJ)
struct kobject kobj;
#endif
unsigned long _flags;
void *data;
#if defined(CONFIG_SPARC)
unsigned int unique_id;
struct of_irq_controller *irq_trans;
#endif
};
内核中使用结构体 property 表示属性:
struct property {
char *name;
int length;
void *value;
struct property *next;
#if defined(CONFIG_OF_DYNAMIC) || defined(CONFIG_SPARC)
unsigned long _flags;
#endif
#if defined(CONFIG_OF_PROMTREE)
unsigned int unique_id;
#endif
#if defined(CONFIG_OF_KOBJ)
struct bin_attribute attr;
#endif
};
#if defined(CONFIG_SPARC)
struct of_irq_controller;
#endif
-----------------------------------------------------------
audio_policy.conf 或者 (7.0版本以上)/vendor/etc/audio_policy_configuration.xml
生成音频设备管理对象
adb shell dumpsys media.audio_policy
在系统中生成的声卡设备及路由策略内容
-----------------------------------------------------------
AUDIO_OUTPUT_FLAG:
定义在:system/media/audio/include/system/audio.h
打印在 AudioTrack 或者 AudioFlinger
/* the audio output flags serve two purposes:
* - when an AudioTrack is created they indicate a "wish" to be connected to an
* output stream with attributes corresponding to the specified flags
* - when present in an output profile descriptor listed for a particular audio
* hardware module, they indicate that an output stream can be opened that
* supports the attributes indicated by the flags.
* the audio policy manager will try to match the flags in the request
* (when getOuput() is called) to an available output stream.
*/
typedef enum {
AUDIO_OUTPUT_FLAG_NONE = 0x0, // no attributes
AUDIO_OUTPUT_FLAG_DIRECT = 0x1, // this output directly connects a track to one output stream: no software mixer
AUDIO_OUTPUT_FLAG_PRIMARY = 0x2, // this output is the primary output of the device. It is unique and must be present.
// It is opened by default and receives routing, audio mode and volume controls related to voice calls.
AUDIO_OUTPUT_FLAG_FAST = 0x4, // output supports "fast tracks", defined elsewhere
AUDIO_OUTPUT_FLAG_DEEP_BUFFER = 0x8, // use deep audio buffers
AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD = 0x10, // offload playback of compressed streams to hardware codec
AUDIO_OUTPUT_FLAG_NON_BLOCKING = 0x20, // use non-blocking write
AUDIO_OUTPUT_FLAG_HW_AV_SYNC = 0x40 // output uses a hardware A/V synchronization source
} audio_output_flags_t;
#define AUDIO_OUTPUT_FLAG_LIST_DEF(V) \
V(AUDIO_OUTPUT_FLAG_NONE, 0x0) \
V(AUDIO_OUTPUT_FLAG_DIRECT, 0x1) \
·····
#define AUDIO_DEFINE_ENUM_SYMBOL_V(symbol, value) symbol = value,
typedef enum {
AUDIO_OUTPUT_FLAG_LIST_DEF(AUDIO_DEFINE_ENUM_SYMBOL_V)
} audio_output_flags_t;
等价于:
typedef enum {
AUDIO_DEFINE_ENUM_SYMBOL_V(AUDIO_OUTPUT_FLAG_NONE, 0x0) \
AUDIO_DEFINE_ENUM_SYMBOL_V(AUDIO_OUTPUT_FLAG_DIRECT, 0x1) \ // 效果同上,也不知道为啥要写这么复杂
·····
} audio_output_flags_t;
-----------------------------------------------------------
音频焦点(audio focus)
为了避免多个应用在同时请求音频播放的时候发生冲突,Android 平台使用了 音频焦点 这一概念来 协调音频播放
——-- 即只有获得音频焦点的应用才可以播放音频
抢占式的机制:没有优先级概念,一般情况下后一个申请者都能从前一个申请者的手中获取AudioFocus(除过通话)
/frameworks/base/media/java/android/media/AudioManager.java
OnAudioFocusChangeListener 音频焦点监听器
focusChange 主要参数
0、AUDIOFOCUS_NONE:
1、AUDIOFOCUS_GAIN:你已经获得音频焦点;
2、AUDIOFOCUS_GAIN_TRANSIENT:用于指示短暂请求音频焦点,例如系统通知
3、AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:用于指示短暂请求音频焦点,同时允许其他声音降低音量播放
4、AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:独家,排他性,暂停所有媒体申请,不应答抢占要求
5、AUDIOFOCUS_LOSS:将失去音频焦点,持续时间未知,必须终止所有的音频播放。
6、AUDIOFOCUS_LOSS_TRANSIENT:临时失去了音频焦点,但是在不久就会再返回来。须暂停所有的音频播放,但保留播放资源。
7、AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:临时失去了音频焦点,但允许安静的播放音频(低音量),而不是完全的终止音频播放。
-----------------------------------------------------------
Linux内核中链表的完整构建包括 虚节点 和 宿主结构体 两个部分。虚节点里只存放用于和其他链表沟通串联的前趋后继指针 prev 和 next,宿主结构体里存放实际的数据;
虚节点作为宿主结构体的成员“寄生”在宿主里,在链表相关操作的过程中代表宿主进行相关操作
//宿主结构体
struct audio_device_info {
//虚节点,作为宿主结构体的成员存在;
struct listnode list;
//用于实际数据存储的其他成员
audio_devices_t type;
char address[AUDIO_DEVICE_MAX_ADDRESS_LEN];
};
//Linux里链表结构体只定义了指针段的 前驱指针 prev 和 后继指针 next,没有数据段,只做为链表串联时的对接机构,不存储实际数据,称为 虚节点
struct listnode {
struct listnode *next;
struct listnode *prev;
};
根据 虚节点找到宿主结构体 从而访问其内的数据,方法:node_to_item
用虚节点减去虚节点相对于宿主结构开头的偏移量即可得到宿主结构体
node_to_item(node, struct action, alist)替换为 (struct action *) (((char*) (node)) - offsetof(struct action, alist))
offsetof求得的是alist在action中的偏移量,(char*) (node)减去offsetof求得的是node对应的Action地址(node减去自己在Action中的偏移量,即为node所在的Action首地址)
#define node_to_item(node, struct audio_device_info, list) \
(struct audio_device_info *) (((char*) (node)) - offsetof(struct audio_device_info, list))
offsetof:能够求出 指定成员 相对于 结构体起始地址 的偏移量(单位:字节byte)
//遍历链表
#define list_for_each(item, list) \
for (item = (list)->next; item != list; item = item->next)
等价于:
for (node = (devices)->next; node != devices; node = node->next){
item = node_to_item(node, struct audio_device_info, list);
if (item != NULL)
device_type |= item->type;
}
//http://t.csdnimg.cn/JTnZk
//遍历结构体,从虚节点找到宿主结构体,进而确定其结构体内部数据
audio_devices_t get_device_types(struct listnode *devices)
{
struct listnode *node; // 虚节点
struct audio_device_info *item = NULL; // 宿主结构体
audio_devices_t device_type = AUDIO_DEVICE_NONE;
if (devices == NULL)
return AUDIO_DEVICE_NONE;
list_for_each (node, devices) {
item = node_to_item(node, struct audio_device_info, list);
if (item != NULL)
device_type |= item->type;
}
return device_type;
}
--------------------------------------------------------
/kernel/msm-5.4/include/sound/control.h
struct snd_kcontrol_new {
snd_ctl_elem_iface_t iface; /* interface identifier */
unsigned int device; /* device/client number */
unsigned int subdevice; /* subdevice (substream) number */
const unsigned char *name; /* ASCII name of item */
unsigned int index; /* index of item */
unsigned int access; /* access rights */
unsigned int count; /* count of same elements */
snd_kcontrol_info_t *info;
snd_kcontrol_get_t *get;
snd_kcontrol_put_t *put;
union {
snd_kcontrol_tlv_rw_t *c;
const unsigned int *p;
} tlv;
unsigned long private_value;
};
/kernel/msm-5.4/include/sound/soc.h
/* enumerated kcontrol */
struct soc_enum {
int reg;
unsigned char shift_l;
unsigned char shift_r;
unsigned int items;
unsigned int mask;
const char * const *texts;
const unsigned int *values;
unsigned int autodisable:1;
struct snd_soc_dobj dobj;
};
#define SOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = snd_soc_info_enum_ext, \
.get = xhandler_get, .put = xhandler_put, \
.private_value = (unsigned long)&xenum }
#define SOC_ENUM_SINGLE_EXT(xitems, xtexts) \
{ .items = xitems, .texts = xtexts }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。