1. 程式人生 > 其它 >Qcom平臺Audio mixer_paths.xml 解析

Qcom平臺Audio mixer_paths.xml 解析

mixer_paths.xml 檔案內容

整理中......

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

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;
//......
//......
}

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回原路徑重啟機器驗證;

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 的解析過程中實現;

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 基本功能的使用;

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 結構;

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做實際的填充;

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;

迴圈讀取判斷子標籤等;

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);

mixer_paths的作用

整理中......