1:mixer_paths.xml 文件內容
[[mixer_paths.xml 文件內容 整理中......]]
2:mixer_paths.xml 加載過程
mixer_paths.xml 的加載audio 的HAL層初始化過程中實現,以高通平台為例:
代碼路徑:hardware/qcom/audio/hal/audio_hw.c
struct audio_module HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG,
.module_api_version = AUDIO_MODULE_API_VERSION_0_1,
.hal_api_version = HARDWARE_HAL_API_VERSION,
.id = AUDIO_HARDWARE_MODULE_ID,
.name = "QCOM Audio HAL",
.author = "The Linux Foundation",
.methods = &hal_module_methods,
},
};
methods數組定義:
static struct hw_module_methods_t hal_module_methods = {
.open = adev_open,
};
指定open函數:
static int adev_open(const hw_module_t *module, const char *name,
hw_device_t **device)
{
/* Loads platform specific libraries dynamically */
adev->platform = platform_init(adev);
//指定平台 paltform_init;
}
動態加載HAL庫文件,paltform_init ,在hardware/qcom/audio/hal HAL源碼中,有若干平台的代碼,通過Android.mk 可知 以msm8953平台為例:
ifneq ($(filter msm8916 msm8909 msm8952 msm8937 thorium msm8953 msmgold sdm660,$(TARGET_BOARD_PLATFORM)),)
AUDIO_PLATFORM = msm8916
MULTIPLE_HW_VARIANTS_ENABLED := true
LOCAL_CFLAGS := -DPLATFORM_MSM8916
2.1:platform_init
AUDIO_PLATFORM = msm8916, 可知 paltform_init 是在hardware/qcom/audio/hal/msm8916/platform.c
以下是相關內容的實現:
void *platform_init(struct audio_device *adev)
{
int snd_card_num = 0;
const char *snd_card_name;
char mixer_xml_path[MAX_MIXER_XML_PATH],ffspEnable[PROPERTY_VALUE_MAX];
const char *mixer_ctl_name = "Set HPX ActiveBe";
struct mixer_ctl *ctl = NULL;
snd_card_num = audio_extn_utils_open_snd_mixer(&adev->mixer);
adev->snd_card = snd_card_num;
ALOGD("%s: Opened sound card:%d", __func__, snd_card_num);
snd_card_name = mixer_get_name(adev->mixer);
ALOGD("%s: snd_card_name: %s", __func__, snd_card_name);
my_data = calloc(1, sizeof(struct platform_data));
my_data->hw_info = hw_info_init(snd_card_name);
query_platform(snd_card_name, mixer_xml_path); //根據snd_card_name查詢平台,確定xml文件所在路徑
ALOGD("%s: mixer path file is %s", __func__,mixer_xml_path);
if (audio_extn_read_xml(adev, snd_card_num, mixer_xml_path,
MIXER_XML_PATH_AUXPCM) == -ENOSYS) {
adev->audio_route = audio_route_init(snd_card_num, mixer_xml_path);//讀取xml,根據mixer_xml的內容進行audio_route 初始化;
}
update_codec_type(snd_card_name);
update_interface(snd_card_name);
my_data->adev = adev;
//......
//......
}
2.2:query_platform 確定路徑
不同的聲卡會對應不同的xml的存放路徑;比如:
//......
/* For LE platforms */
#define MIXER_XML_PATH "/etc/mixer_paths.xml"
#define MIXER_XML_PATH_MSM8909_PM8916 "/etc/mixer_paths_msm8909_pm8916.xml"
#define MIXER_XML_PATH_MTP "/etc/mixer_paths_mtp.xml"
#define MIXER_XML_PATH_SDM439_PM8953 "/etc/mixer_paths_sdm439_pm8953.xml"
#define MIXER_XML_PATH_SKU2 "/etc/mixer_paths_qrd_sku2.xml"
#define MIXER_XML_PATH_WCD9326 "/etc/mixer_paths_wcd9326.xml"
#define MIXER_XML_PATH_WCD9335 "/etc/mixer_paths_wcd9335.xml"
#define PLATFORM_INFO_XML_PATH_EXTCODEC "/etc/audio_platform_info_extcodec.xml"
#define PLATFORM_INFO_XML_PATH_SKUSH "/etc/audio_platform_info_skush.xml"
#define PLATFORM_INFO_XML_PATH "/etc/audio_platform_info.xml"
#define MIXER_XML_PATH_WCD9326_I2S "/etc/mixer_paths_wcd9326_i2s.xml"
#define MIXER_XML_PATH_WCD9326_I2S_TDM "/etc/mixer_paths_wcd9326_i2s_tdm.xml"
#define MIXER_XML_PATH_WCD9330_I2S "/etc/mixer_paths_wcd9330_i2s.xml"
#define MIXER_XML_PATH_WCD9335_I2S "/etc/mixer_paths_wcd9335_i2s.xml"
#define MIXER_XML_PATH_SBC "/etc/mixer_paths_sbc.xml"
//......
query_platform 會根據 kernel中注冊的聲卡snd-card的name 去獲取對應的路徑; 以msm8953 為例:
可以訪問msm8953的設備注冊的設備節點得到 kernel注冊snd-card name,
msm8953_64:/ # cat /proc/asound/cards
0 [msm8953sndcardm]: msm8953-snd-car - msm8953-snd-card-mtp
msm8953-snd-card-mtp
msm8953_64:/ #
msm8953-snd-card-mtp
即是snd_card的name; 通過 query_platform 對該聲卡name的判斷可知;
mixer_xml_path 最終賦值為MIXER_XML_PATH_MTP, 對應上面的宏定義可知,它在文件系統中的路徑是 :"/etc/mixer_paths_mtp.xml"
相關的實現代碼如下:
static void query_platform(const char *snd_card_name,
char *mixer_xml_path)
{
//......
else if (!strncmp(snd_card_name, "msm8953-snd-card-mtp",
sizeof("msm8953-snd-card-mtp"))) {
strlcpy(mixer_xml_path, MIXER_XML_PATH_MTP,
sizeof(MIXER_XML_PATH_MTP));
msm_device_to_be_id = msm_device_to_be_id_internal_codec;
msm_be_id_array_len =
sizeof(msm_device_to_be_id_internal_codec) / sizeof(msm_device_to_be_id_internal_codec[0]);
//......
}
如果需要debug mixer_paths的配置,可以采用adb pull出相應路徑下的mixer_paths_mtp.xml,修改后push回原路徑重啟機器驗證;
2.3:audio_route_init 解析加載
audio_route 的初始化過程會對mixer_paths_mtp.xml 的內容做分析,並根據讀取到的內容 對audio_route 做初始化;
加載mixer ctl的配置值, audio ptch的配置;
struct audio_route *audio_route_init(unsigned int card, const char *xml_path)
{
struct config_parse_state state;
XML_Parser parser; // 定義 XML_Parser
FILE *file;
int bytes_read;
void *buf;
struct audio_route *ar;
ar = calloc(1, sizeof(struct audio_route));
ar->mixer = mixer_open(card); //open mixer
ar->mixer_path = NULL;
ar->mixer_path_size = 0;
ar->num_mixer_paths = 0;
file = fopen(xml_path, "r"); //open xml file
parser = XML_ParserCreate(NULL); //申請parser
memset(&state, 0, sizeof(state));
state.ar = ar;
XML_SetUserData(parser, &state);//設置parser UserData
XML_SetElementHandler(parser, start_tag, end_tag); //設置 start 及 end回調函數
for (;;) { //循環讀取每一個標簽
buf = XML_GetBuffer(parser, BUF_SIZE);
if (buf == NULL)
goto err_parse;
bytes_read = fread(buf, 1, BUF_SIZE, file);
if (bytes_read < 0)
goto err_parse;
if (XML_ParseBuffer(parser, bytes_read,
bytes_read == 0) == XML_STATUS_ERROR) {
ALOGE("Error in mixer xml (%s)", MIXER_XML_PATH);
goto err_parse;
} //解析讀取
if (bytes_read == 0)
break;
}
/* apply the initial mixer values, and save them so we can reset the
mixer to the original values */
audio_route_update_mixer(ar); //update mixer
save_mixer_state(ar);
XML_ParserFree(parser);
fclose(file);
return ar;
}
audio_route_init 中通過文件 io open xml文件后獲取到文件描述符;
然后通過xml解析器expat 對 xml文件中內容做讀取;
初始化設置audio_route 設置mixer;
主要的內容是在expat 的解析過程中實現;
3:xml解析器 expat 的使用
expat的開源地址: https://github.com/libexpat/libexpat
AOSP源碼中有引入這個解析器,它所在的位置:
code/external/expat/
通過上面audio_route_init 過程的分析,可知; 在包含了 expat 的頭文件#include <expat.h> 后, 使用了幾個主要的函數;
- XML_ParserCreate //創建parser
- XML_SetUserData //設置UserData
- XML_SetElementHandler //設置處理標記開始和結束的處理函數
- XML_ParseBuffer // 分析給出的緩沖區XML數據
- XML_ParserFree(parser); //釋放parser
這幾個函數完成expat 基本功能的使用;
3.1:XML_ParserCreate
expat 是圍繞parser 使用的,它是一個結構體類型,通過XML_Parser定義;
路徑:\external\expat\lib\expat.h
typedef struct XML_ParserStruct *XML_Parser;
實際是struct XML_ParserStruct類型, 它的定義:
struct XML_ParserStruct {
/* The first member must be m_userData so that the XML_GetUserData
macro works. */
void *m_userData;
void *m_handlerArg;
char *m_buffer;
const XML_Memory_Handling_Suite m_mem;
/* first character to be parsed */
const char *m_bufferPtr;
/* past last character to be parsed */
char *m_bufferEnd;
/* allocated end of m_buffer */
const char *m_bufferLim;
XML_Index m_parseEndByteIndex;
const char *m_parseEndPtr;
XML_Char *m_dataBuf;
XML_Char *m_dataBufEnd;
//......
}
在實際使用中,通過XML_ParserCreate(NULL) 獲取 一個parser 結構;
3.2:XML_SetUserData
expat 提供了處理解析xml的框架,實際使用過程要填充對應的回調函數和數據結構;
//設置 UserData, 不指定具體類型, 形參void *;
XML_SetUserData(XML_Parser parser, void *p) {
if (parser->m_handlerArg == parser->m_userData)
parser->m_handlerArg = parser->m_userData = p;
else
parser->m_userData = p;
}
在audio的mixer_paths_mtp.xml 解析中,設置parser 的 UserData為state;
state的定義如下:
struct config_parse_state {
struct audio_route *ar;
struct mixer_path *path;
int level;
};
在對parse的數據處理時,會對state做實際的填充;
3.3:XML_SetElementHandler
expat 對xml文件進行讀取時,設置處理標記開始和結束的處理函數,expat提供框架,使用者提供函數回調;它的定義:
XMLPARSEAPI(void) XML_SetElementHandler(XML_Parser parser,
XML_StartElementHandler start,
XML_EndElementHandler end);
回調函數指針的定義:
typedef void (XMLCALL *XML_StartElementHandler) (void *userData,
const XML_Char *name,
const XML_Char **atts);
typedef void (XMLCALL *XML_EndElementHandler) (void *userData,
const XML_Char *name);
在audio的mixer_paths_mtp.xml 解析中,設置的回調函數是:
XML_SetElementHandler start_tag
static void start_tag(void *data, const XML_Char *tag_name,
const XML_Char **attr)
{
const XML_Char *attr_name = NULL;
const XML_Char *attr_id = NULL;
const XML_Char *attr_value = NULL;
struct config_parse_state *state = data; // state 即是通過 XML_SetUserData 設置的UserData;
struct audio_route *ar = state->ar;
unsigned int i;
unsigned int ctl_index;
struct mixer_ctl *ctl;
long value;
unsigned int id;
struct mixer_value mixer_value;
enum mixer_ctl_type type;
/* Get name, id and value attributes (these may be empty) */
for (i = 0; attr[i]; i += 2) {
if (strcmp(attr[i], "name") == 0)
attr_name = attr[i + 1];
if (strcmp(attr[i], "id") == 0)
attr_id = attr[i + 1];
else if (strcmp(attr[i], "value") == 0)
attr_value = attr[i + 1];
}
/* Look at tags */
if (strcmp(tag_name, "path") == 0) {
if (attr_name == NULL) {
ALOGE("Unnamed path!");
} else {
if (state->level == 1) {
/* top level path: create and stash the path */
state->path = path_create(ar, (char *)attr_name);
//如果tag_name是 path, path_create創建一個 名為***(讀到的attr_name) 的mixer_path;
if (state->path == NULL)
ALOGE("path created failed, please check the path if existed");
} else {
/* nested path */
struct mixer_path *sub_path = path_get_by_name(ar, attr_name);
if (!sub_path) {
ALOGE("unable to find sub path '%s'", attr_name);
} else if (state->path != NULL) {
path_add_path(ar, state->path, sub_path);
}
}
}
}
else if (strcmp(tag_name, "ctl") == 0) {
/* Obtain the mixer ctl and value */
ctl = mixer_get_ctl_by_name(ar->mixer, attr_name);
//如果tag_name是 ctl mixer_get_ctl_by_name 獲取 名為***(讀到的attr_name) 的ctl;
switch (mixer_ctl_get_type(ctl)) {
case MIXER_CTL_TYPE_BOOL:
case MIXER_CTL_TYPE_INT:
value = strtol((char *)attr_value, NULL, 0);
break;
case MIXER_CTL_TYPE_BYTE:
value = (unsigned char) strtol((char *)attr_value, NULL, 16);
break;
case MIXER_CTL_TYPE_ENUM:
value = mixer_enum_string_to_value(ctl, (char *)attr_value);
break;
default:
value = 0;
break;
}
/* locate the mixer ctl in the list */
for (ctl_index = 0; ctl_index < ar->num_mixer_ctls; ctl_index++) {
if (ar->mixer_state[ctl_index].ctl == ctl)
break;
}
if (state->level == 1) {
/* top level ctl (initial setting) */
type = mixer_ctl_get_type(ctl);
if (is_supported_ctl_type(type)) {
/* apply the new value */
if (attr_id) {
/* set only one value */
id = atoi((char *)attr_id);
if (id < ar->mixer_state[ctl_index].num_values)
if (type == MIXER_CTL_TYPE_BYTE)
ar->mixer_state[ctl_index].new_value.bytes[id] = value;
else if (type == MIXER_CTL_TYPE_ENUM)
ar->mixer_state[ctl_index].new_value.enumerated[id] = value;
else
ar->mixer_state[ctl_index].new_value.integer[id] = value;
else
ALOGE("value id out of range for mixer ctl '%s'",
mixer_ctl_get_name(ctl));
} else {
/* set all values the same */
for (i = 0; i < ar->mixer_state[ctl_index].num_values; i++)
if (type == MIXER_CTL_TYPE_BYTE)
ar->mixer_state[ctl_index].new_value.bytes[i] = value;
else if (type == MIXER_CTL_TYPE_ENUM)
ar->mixer_state[ctl_index].new_value.enumerated[i] = value;
else
ar->mixer_state[ctl_index].new_value.integer[i] = value;
}
}
} else {
/* nested ctl (within a path) */
mixer_value.ctl_index = ctl_index;
mixer_value.value = value;
if (attr_id)
mixer_value.index = atoi((char *)attr_id);
else
mixer_value.index = -1;
if (state->path != NULL)
path_add_value(ar, state->path, &mixer_value);
}
}
done:
state->level++;
}
static void end_tag(void *data, const XML_Char *tag_name)
{
struct config_parse_state *state = data;
(void)tag_name;
state->level--;
}
不難看出,mixer_paths_mtp.xml 的解析會根據做 每一個標簽的 tag_name的不同做不同的處理;
如果tag_name是 path, path_create創建一個 名為***(讀到的attr_name) 的mixer_path;
如果tag_name是 ctl mixer_get_ctl_by_name 獲取 名為***(讀到的attr_name) 的ctl;
循環讀取判斷子標簽等;
3.4:XML_ParseBuffer
XML_ParseBuffer mixer_paths_mtp.xml 的解析的實際發起者,它的相關內容如下:
enum XML_Status XMLCALL
XML_ParseBuffer(XML_Parser parser, int len, int isFinal)
{
const char *start;
//......
start = parser->m_bufferPtr;
parser->m_positionPtr = start;
parser->m_bufferEnd += len;
parser->m_parseEndPtr = parser->m_bufferEnd;
parser->m_parseEndByteIndex += len;
parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal;
parser->m_errorCode = parser->m_processor(parser, start, parser->m_parseEndPtr, &parser->m_bufferPtr);
//......
//......
}
XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, parser->m_bufferPtr, &parser->m_position);
parser->m_positionPtr = parser->m_bufferPtr;
return result;
}
parser->m_processor 會導致doContent調用
doContent(parser, 1, parser->m_encoding, start, end, endPtr, (XML_Bool)!parser->m_parsingStatus.finalBuffer);
最終調用到 傳入的 start 回調函數,做處理
->parser->m_startElementHandler(parser->m_handlerArg, tag->name.str, (const XML_Char **)parser->m_atts);
4:mixer_paths的作用
[[mixer_paths作用 整理中......]]