[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.7.6$ grep -nr "DEFAULT_BOARD" ./vendor/ ./hardware/
./vendor/qcom/proprietary/mm-audio-cal/audio-acdb-util/acdb-loader/Android.mk:17:
 libacdbloader-def += -D DEFAULT_BOARD=\"MTP\"

然後經過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!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章