[Linux Audio Driver] ACDB文件加载流程(完结篇)

0 背景

首先代码还是android7,之前两篇文章提到,拿到声卡名字以及在platform.c里面通过dlsym的方式,加载acdb_loader_init_v2函数, 今天继续分析;
代码路径:

vendor\qcom\proprietary\mm-audio\audio-acdb-util\acdb-loader\src\family-b\acdb-loader.c

int acdb_loader_init_v2(char *snd_card_name, char *cvd_version, int metaInfoKey){

{

1. 简单注解

int acdb_loader_init_v2(char *snd_card_name, char *cvd_version, int metaInfoKey)
{
	int				ret = 0;
	int				i;
	int				result = 0;
	AcdbVocProcGainDepVolTblSizeV2CmdType	vocvoltablesize;
	AcdbSizeResponseType		response;
	AcdbInitCmdType			acdb_init_cmd; //重点结构,后面分析
	AcdbGetMetaInfoSizeCmdType metaInfoSizeCmd;
	AcdbSizeResponseType metaInfoSize;

        pthread_mutex_lock(&loader_mutex); //上锁
        if (acdb_init_ref_cnt != 0) {
            acdb_init_ref_cnt++;
            ALOGD("ACDB -> already initialized, exit");
            goto done;
        }

	list_init(&aud_vol_idx_list.list); // 链表初始化,acdb id,app id相关,

	if (acdb_load_files(&acdb_init_cmd, snd_card_name) <= 0) { //acdb_load_files是加载
	                                                           //acdb文件的重点
		LOGE("ACDB -> Could not load .acdb files!\n");
		ret = -ENODEV;
		goto done;
	}

	LOGD("ACDB -> ACDB_CMD_INITIALIZE_V2\n");
	ret = acdb_ioctl(ACDB_CMD_INITIALIZE_V2,   //初始化acdb文件
		(const uint8_t *)&acdb_init_cmd, sizeof(acdb_init_cmd), NULL, 0);
    ......
	acdb_rtac_init(); //接收/发送来自QACT的数据,并将其发送/接收到RTAC驱动程序
	                  //(用于设备和QACT通信)
    ......
	acdb_loader_send_common_custom_topology(); //加载音频校准的API的数据并推送到DSP

	if (send_meta_info(metaInfoKey) < 0)
		LOGD("ACDB -> send_meta_info failed");

	current_feature_set = ACDB_VOCVOL_FID_DEFAULT;
	is_initialized = true; //bool变量,acdb加载一波之后置true

	parse_codec_type(snd_card_name); //声卡类型解析

	LOGD("ACDB -> init done!\n");
        acdb_init_ref_cnt++;

done:
        pthread_mutex_unlock(&loader_mutex); //释放锁
	return ret;
}

2. 重点分析

if (acdb_load_files(&acdb_init_cmd, snd_card_name) <= 0)

这句代码是加载acdb 的核心,我们先看一下这个变量acdb_init_cmd;

AcdbInitCmdType acdb_init_cmd;

//Structure to hold an individual ACDB file name and path.
LA.UM.5.6\vendor\qcom\proprietary\mm-audio\audcal\family-b\acdb\inc\acdb.h

struct _AcdbFileName{
   uint32_t fileNameLen;
   //< Full file path name length.
   char fileName[ACDB_FILENAME_MAX_CHARS];
     //< Array that holds the ACDB file path and name. The file size cannot
       //   exceed 256 characters, including the NULL-termiated character.
          //@newpagetable 
}

typedef struct _AcdbInitCmdType AcdbInitCmdType;
#include "acdb_begin_pack.h"


 //  Query command structure for the command ACDB_CMD_INITIALIZE_V2.

struct _AcdbInitCmdType {
   uint32_t nNoOfFiles;
  //< Number of ACDB files to read from the acdbFiles array. 
   AcdbFileName acdbFiles[20];
    //< Array of ACDB file names. A maximum of 20 ACDB files can be
         // provided at one time to be initialized. 
}

我们可以看到acdb_init_cmd是AcdbInitCmdType结构类型的,其存储了ACDB的文件名字以及
ACDB文件的位置;

接下来进去acdb_load_files函数,

vendor\qcom\proprietary\mm-audio\audio-acdb-util\acdb-loader\src\family-b\acdb-loader.c
static int acdb_load_files(AcdbInitCmdType *acdb_init_cmd, char * snd_card_name)
{
	int result = 0;

	result = get_files_from_properties(acdb_init_cmd);
	if (result > 0)
		goto done;

	result = get_files_from_device_tree(acdb_init_cmd, snd_card_name);
done:
	return result;
}

先分析get_files_from_properties函数,

vendor\qcom\proprietary\mm-audio\audio-acdb-util\acdb-loader\src\family-b\acdb-loader.c
static int get_files_from_properties(AcdbInitCmdType *acdb_init_cmd)
{
	int i = 0;
	int prop_len;
	char prop_name[24];

	for (i=0; i < MAX_ACDB_FILES; i++) {
		if (snprintf(prop_name, sizeof(prop_name), "persist.audio.calfile%d", i) < 0)
			goto done;

		prop_len = property_get(prop_name, acdb_init_cmd->acdbFiles[i].fileName, NULL);
		if (prop_len <= 0)
			goto done;

		acdb_init_cmd->acdbFiles[i].fileNameLen = 
		          strlen(acdb_init_cmd->acdbFiles[i].fileName);
		LOGD("ACDB -> Prop Load file: %s\n", acdb_init_cmd->acdbFiles[i].fileName);
	}
done:
	acdb_init_cmd->nNoOfFiles = i;
	return i;
}

这段代码很奇怪,首先这个persist.audio.calfile默认代码就没有配置(当然可能是作为调试用代码),
从我的理解来看,他会通过snprintf把"persist.audio.calfile0"存到字符数组prop_name里面去,

#define MAX_ACDB_FILES		20

这个常量最大20,所以字符最多为"persist.audio.calfile20",最大占用23个char 型位置,字符串最后补个\0的话,刚刚数组长度为24,那么其输出必然不被截断,返回值固定为24;

之后代码又通过property_get的方式,把刚才存到prop_name的值存到acdb_init_cmd->acdbFiles[i].fileName里面,而我们从刚才的重点的那个结构AcdbInitCmdType,

struct _AcdbInitCmdType {
   uint32_t nNoOfFiles;
  //< Number of ACDB files to read from the acdbFiles array. 
   AcdbFileName acdbFiles[20];
    //< Array of ACDB file names. A maximum of 20 ACDB files can be
         // provided at one time to be initialized. 
}

可以看出(从注释也行),其明明应该存储acdb 文件名字,所以这段代码很诡异,当时实际调试由于这个属性persist.audio.calfilem没有配置(从adb shell 进入设备,然后getprop persist.audio.calfilem没有返回值)可以看出,这段代码根本没有走到;

然后我们回到static int acdb_load_files(AcdbInitCmdType *acdb_init_cmd, char * snd_card_name)这个函数继续分析:

result = get_files_from_device_tree(acdb_init_cmd, snd_card_name);

先把这个 主要贴下来(只能删减写,不然这篇还写不完,发现CSDN有篇幅限制),如下:

static int get_files_from_device_tree(AcdbInitCmdType *acdb_init_cmd, char *snd_card_name)
{
	int result = 0;
	char dir_path[300];
	char board_type[64] = DEFAULT_BOARD;
	FILE *fp = NULL;

	/* Get Board type */
	fp = fopen("/sys/devices/soc0/hw_platform","r");
	if (fp == NULL)
		fp = fopen("/sys/devices/system/soc/soc0/hw_platform","r");
	if (fp == NULL)
		LOGE("ACDB -> Error: Couldn't open hw_platform\n");
	else if (fgets(board_type, sizeof(board_type), fp) == NULL)
		LOGE("ACDB -> Error: Couldn't get board type\n");
	else if (board_type[(strlen(board_type) - 1)] == '\n')
		board_type[(strlen(board_type) - 1)] = '\0';
	if (fp != NULL)
		fclose(fp);

	/* get files from form factor & soundcard independant path */
	result = get_acdb_files_in_directory(acdb_init_cmd, ACDB_BIN_PATH);
	if (result >= 0)
		LOGD("ACDB -> found %d form factor & soundcard independant files\n",
			result);
	if (result > MAX_INDEPENDANT_ACDB_FILES)
		goto done;

	/* Try board directory with soundcard name */
	if (snd_card_name != NULL) {
		result = snprintf(dir_path, sizeof(dir_path), "%s/%s/%s", 
		ACDB_BIN_PATH, board_type, snd_card_name);
		if (result < 0) {
			LOGE("ACDB -> Error: snprintf failed for snd card %s, error: %d\n", 
			snd_card_name, result);
			result = -ENODEV;
			goto done;
		}
		result = get_acdb_files_in_directory(acdb_init_cmd, dir_path);
		if (result > 0)
			goto done;
	}

	......
}

高通代码上来就给你个注释:

/* Get Board type */
fp = fopen("/sys/devices/soc0/hw_platform","r");
if (fp == NULL)
	fp = fopen("/sys/devices/system/soc/soc0/hw_platform","r");
if (fp == NULL)
	LOGE("ACDB -> Error: Couldn't open hw_platform\n");
else if (fgets(board_type, sizeof(board_type), fp) == NULL)
	LOGE("ACDB -> Error: Couldn't get board type\n");
else if (board_type[(strlen(board_type) - 1)] == '\n')
	board_type[(strlen(board_type) - 1)] = '\0';
if (fp != NULL)
	fclose(fp);

board_type,这个代码里面默认配置的是MTP:

~/LA.UM.5.6$ grep -nr "DEFAULT_BOARD" ./vendor/ ./hardware/
./vendor/qcom/proprietary/mm-audio/audio-acdb-util/acdb-loader/Android.mk:17:
libacdbloader-def += -D DEFAULT_BOARD=\"MTP\"

./vendor/qcom/proprietary/mm-audio/audio-acdb-util/acdb-loader/src/family-b/acdb-loader.c
:616:  char board_type[64] = DEFAULT_BOARD;

然后经过fopen的读取之后,我们能够获取到他的值是QRD,这玩意是高通CPU的平台信息,芯片内部
有一段空间是存储这个平台信息的,抽空俺也可以分析一波(之前有了解,但是忘了,信息好像是从
modem那边传过来的);随便找台设备,cat 一下这个节点值,我们就知道是QRD了。
在这里插入图片描述

/* get files from form factor & soundcard independant path */
result = get_acdb_files_in_directory(acdb_init_cmd, ACDB_BIN_PATH);
if (result >= 0)
	LOGD("ACDB -> found %d form factor & soundcard independant files\n",
		result);
if (result > MAX_INDEPENDANT_ACDB_FILES)
	goto done;

后面的代码几乎都是get_acdb_files_in_directory函数在跑了,

ACDB_BIN_PATH在android 7是 设备里面的/etc/acdbdata/

result = get_acdb_files_in_directory(acdb_init_cmd, ACDB_BIN_PATH);

这个代码的意思就是,先在设备里面的etc/acdbdata/目录下面找,能不能找到那八个文件?(一般是8个),找到了goto done;函数跑完,找不到,走下一个判断;

result = snprintf(dir_path, sizeof(dir_path), "%s/%s/%s", 
              ACDB_BIN_PATH, board_type, snd_card_name);

接下来看你能否在/etc/acdbdata/QRD/msm8953-sku3-snd-card下面找到这个acdb文件;之后就是依次查找: /etc/acdbdata/QRD/ -> /etc/acdbdata/MTP/msm8953-sku3-snd-card ->
/etc/acdbdata/MTP -> /etc

总之,按照这个流程在设备里面找这个参数,按代码顺序,谁先找到就加载那个位置的acdb文件。

补充: 这个acdb的文件由系统编到设备目录下;

3. 总结与引申

其实整个acdb 文件的加载流程如下图所示:

在这里插入图片描述
(备注,图片的标号略有问题,俺就懒得改了…)

那么我们知道这个有啥用呢,其实在实际研发过程中,会遇到不得不使用同一套软件而兼容不同硬件状态的情况,具体可参考下面这位博主的博客,我就不多聊了。

https://blog.csdn.net/crow_ch/article/details/103886156

感谢您的阅读,本文OVER!

原创文章 42 获赞 15 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Codeliang666/article/details/105881429