上篇的分析到audio_route,现在接个力,也算是7月的作业,再不交这个月就落下了。
audio_route_apply_and_update_path分两个过程,从函数名字都能看出来,一个是apply,一个是updata:
audio_route_apply_path
/* Apply an audio route path by name */
int audio_route_apply_path(struct audio_route *ar, const char *name)
{
struct mixer_path *path;
...
path = path_get_by_name(ar, name);
...
path_apply(ar, path);
return 0;
}
path是一个struct:
struct mixer_path {
char *name;
unsigned int size;
unsigned int length;
struct mixer_setting *setting;
};
内含struct mixer_setting:
struct mixer_setting {
unsigned int ctl_index;
unsigned int num_values;
unsigned int type;
union ctl_values value;
};
好吧,还套了个ctl_values:
union ctl_values {
int *enumerated;
long *integer;
void *ptr;
unsigned char *bytes;
};
背景知识先来这么多,先继续看代码
path_get_by_name(ar, name)这个调用返回了一个mixer_path指针。
static struct mixer_path *path_get_by_name(struct audio_route *ar,
const char *name)
{
unsigned int i;
for (i = 0; i < ar->num_mixer_paths; i++)
if (strcmp(ar->mixer_path[i].name, name) == 0)
return &ar->mixer_path[i];
return NULL;
}
看到代码简单,老夫长舒一口气!
无论怎么样我们还是需要去查证两个参数,ar和name.
name简单一些:
//audio_hw.c
strlcpy(mixer_path, use_case_table[usecase->id], MIXER_PATH_MAX_LENGTH);
...
audio_route_apply_and_update_path(adev->audio_route, mixer_path);//name的源头
mixer_path来自use_case_table[usecase->id],列举几个常见的(我常见的<(__)>):
const char * const use_case_table[AUDIO_USECASE_MAX] = {
...
[USECASE_AUDIO_PLAYBACK_DEEP_BUFFER] = "deep-buffer-playback",
[USECASE_AUDIO_PLAYBACK_LOW_LATENCY] = "low-latency-playback",
[USECASE_AUDIO_RECORD] = "audio-record",
[USECASE_AUDIO_PLAYBACK_FM] = "play-fm",
[USECASE_AUDIO_HFP_SCO_UPLINK] = "hfp-sco",
[USECASE_VOICE_CALL] = "voice-call",
...
}
播放录音,电话,甚至FM都包括在其间了。从概念上来讲,这个就是一个使用场景,从代码上来讲,就是一个字符串而已。。。
再去看复杂一些的第一个参数ar。
首先,从audio_hw.c来看,ar = adev->audio_route, adev 来自 out_set_parameters函数的out->dev。
而out:
struct stream_out *out = (struct stream_out *)stream;//stream类型audio_stream
简单做个小结就是ar即:
stream->dev->audio_route
stream一路追溯上去,来自:
audio_hw.c
adev_open_output_stream函数。完全没找到ar被赋值,被构造的地方!郁闷。。。
前面说到path_get_by_name,完全不知道ar的num_mixer_paths哪来的?
一咬牙一跺脚,来个不要脸的方法,看看num_mixer_paths在哪赋值的,好家伙,又找出来个线索:
//audio_route.c
audio_route_init
start_tag
path_create
num_mixer_paths赋值
还偷偷发现,audio_route_init里用到了XML_Parser。一阵阵窃喜)——)十有八九,mixer_path就是在这个过程中被解析的。
我们现在搜索下/在哪调用的吧:
小插曲,碰到一个问题:
./msm8974/platform.c./msm8916/platform.c./msm8960/platform.c
我们的平台是msm8996,audio hal用的到底是哪个platform.c?
只好打开audio/audio/hal/Android.mk看看了
...
#filter的结果不为空(TARGET_BOARD_PLATFORM为msm 8996)
ifneq ($(filter msm8974 msm8226 msm8610 apq8084 msm8994 msm8992 msm8996 msm8996_gvmq,$(TARGET_BOARD_PLATFORM)),)//注意逗号后面,第二个参数是空的
AUDIO_PLATFORM = msm8974
...
LOCAL_SRC_FILES := \
audio_hw.c \
voice.c \
platform_info.c \
$(AUDIO_PLATFORM)/platform.c
...
好的,我知道了,用的就是./msm8974/platform.c
随着时间一点点过去,我又捋出来这么一个过程:
AudioFlinger::loadHwModule_l
...
audio_hw/adev_open//刚才百思不得其解的adev原来是这个时候初始化的
...
adev->platform = platform_init(adev);
嗯,qcom
void *platform_init(struct audio_device *adev)
{
...
int retry_num = 0, snd_card_num = 0, key = 0;
...
//MAX_SND_CARD为8.
while (snd_card_num < MAX_SND_CARD) {
//external/tinyalsa/mixer.c
//打开/dev/snd/controlC(snd_card_num)节点,关联到mixer上
adev->mixer = mixer_open(snd_card_num);
...
//取到mixer的名字赋值给snd_card_name
//本例中是apq8096-adp-agave-snd-card
snd_card_name = strdup(mixer_get_name(adev->mixer));
...
//hw_info.c
my_data->hw_info = hw_info_init(snd_card_name);
//本例中is_i2s_ext_modem:0
//i2s外置?
if (platform_is_i2s_ext_modem(snd_card_name, my_data)) {
adev->audio_route = audio_route_init(snd_card_num,
MIXER_XML_PATH_I2S);
} else {
//(翻译一下这里的一段注释)
//从声卡名称中获取编解码器内部名称,并动态形成混音器路径文件名.
//这是将来挑选任何基于编解码器名称的混音器文件的通用方法,无需更改代码。
//此代码假设混合器文件的格式为mixer_paths_internalcodecname.xml。
//如果此动态读取混音器文件无法打开,则它将回退到默认混音器文件,即mixer_paths.xml.
//这样做是为了保持向后兼容性,但不是强制性的,只要混合器文件按照上述假设命名即可。
if (platform_is_guardian(snd_card_name, my_data)) {
//平台是监护人?护卫者?
ALOGD("%s:%s test", __func__, MIXER_XML_PATH_GUARDIAN);
}
//snd_internal_name=“apq8096” 剩下的tmp="adp-agave-snd-card"
snd_internal_name = strtok_r(snd_card_name, "-", &tmp);
if (snd_internal_name != NULL)
//snd_internal_name=“adp” tmp="agave-snd-card"
snd_internal_name = strtok_r(NULL, "-", &tmp);
if (snd_internal_name != NULL) {
//mixer_xml_file = "/vendor/etc/mixer_paths"
strlcpy(mixer_xml_file, MIXER_XML_BASE_STRING,
MIXER_PATH_MAX_LENGTH);
//strcat, 连接字符串
//mixer_xml_file = "/vendor/etc/mixer_paths_"
strlcat(mixer_xml_file, MIXER_FILE_DELIMITER,
MIXER_PATH_MAX_LENGTH);
//mixer_xml_file = "/vendor/etc/mixer_paths_adp"
strlcat(mixer_xml_file, snd_internal_name,
MIXER_PATH_MAX_LENGTH);
//mixer_xml_file = "/vendor/etc/mixer_paths_adp.xml"
strlcat(mixer_xml_file, MIXER_FILE_EXT,
MIXER_PATH_MAX_LENGTH);
} else {
strlcpy(mixer_xml_file, MIXER_XML_DEFAULT_PATH,
MIXER_PATH_MAX_LENGTH);
}
//access()检查该进程是否将被允许读,写或测试存在的文件
if (F_OK == access(mixer_xml_file, 0)) {
ALOGD("%s: Loading mixer file: %s", __func__, mixer_xml_file);
if (audio_extn_read_xml(adev, snd_card_num, mixer_xml_file,
MIXER_XML_PATH_AUXPCM) == -ENOSYS)
adev->audio_route = audio_route_init(snd_card_num,
mixer_xml_file);
} else {
//由于mixer_paths_adp.xml文件并不存在,所以还是用默认的xml文件
ALOGD("%s: Loading default mixer file", __func__);
//AUXPCM_BT_ENABLED没定义,所以这个方法直接等同于-ENOSYS
if(audio_extn_read_xml(adev, snd_card_num, MIXER_XML_DEFAULT_PATH,
MIXER_XML_PATH_AUXPCM) == -ENOSYS)
//终于要给audio_route赋值了。。。前文找了半天啊。
//在audio_route_init中完成了对mixer_paths.xml的解析
adev->audio_route = audio_route_init(snd_card_num,
MIXER_XML_DEFAULT_PATH);
}
}
...
adev->snd_card = snd_card_num;
break;
}
retry_num = 0;
snd_card_num++;
mixer_close(adev->mixer);
...
//my_data初始化
my_data->adev = adev;
my_data->fluence_in_spkr_mode = false;
...
//高通的双mic降噪相关的一些设置项,涉及不到的就可以暂时不看了
property_get("ro.qc.sdk.audio.fluencetype", my_data->fluence_cap, "");
...
//acdb处理
my_data->acdb_handle = dlopen(LIB_ACDB_LOADER, RTLD_NOW);
...
platform_acdb_init(my_data);
...
//底下的就是看着眼熟,不知道干嘛用的了
}
终于可以回到audio_route_apply_path
第一步:path = path_get_by_name(ar, name);
ar对应着mixer_paths.xml,path_get_by_name,name即前文分析到的usecase,比如录音时候的"audio_record".
取到的结果对应mixer_paths.xml中的:
<path name="audio-record">
<ctl name="MultiMedia1 Mixer SEC_MI2S_TX" value="1" />
</path>
简单做个解释:
-
MultiMedia1:audio-record这个usecase对应的FE(front end) PCM.
-
SEC_MI2S_TX : device连接的BE(back end) DAI.
-
Mixer:表示 DSP 路由功能
-
value:1 表示连接,0 表示断开连接
这里有个很专业的文章可供参考(可能看完底下这个,你会发现我这篇文章没有任何存在的价值了!):
这个ctl的意思是把MultiMedia1 PCM与SEC_MI2S_TX这个DAI连接起来。
第二步:path_apply(ar, path);
将第一步中取到的path传给path_apply
static int path_apply(struct audio_route *ar, struct mixer_path *path)
{
unsigned int i;
unsigned int ctl_index;
struct mixer_ctl *ctl;
enum mixer_ctl_type type;
ALOGD("Apply path: %s", path->name != NULL ? path->name : "none");
for (i = 0; i < path->length; i++) {
ctl_index = path->setting[i].ctl_index;
ctl = index_to_ctl(ar, ctl_index);
type = mixer_ctl_get_type(ctl);
if (!is_supported_ctl_type(type))
continue;
size_t value_sz = sizeof_ctl_type(type);
//通过memcpy直接将path->setting[i].value放入ar->mixer_state[ctl_index].new_value
memcpy(ar->mixer_state[ctl_index].new_value.ptr, path->setting[i].value.ptr,
path->setting[i].num_values * value_sz);
}
return 0;
}
前面apply的过程到这里算勉强结束了,接下来该update了。
audio_route_update_path
/*
* Operates on the specified path .. controls will be updated in the
* order listed in the XML file
*/
static int audio_route_update_path(struct audio_route *ar, const char *name, bool reverse)
{
struct mixer_path *path;
int32_t i, end;
unsigned int j;
...
path = path_get_by_name(ar, name);//再取一遍
...
i = reverse ? (path->length - 1) : 0;//前面传的false,所以这里i = 0;
end = reverse ? -1 : (int32_t)path->length;//这里等于path的长度
while (i != end) {
unsigned int ctl_index;
enum mixer_ctl_type type;
ctl_index = path->setting[i].ctl_index;
struct mixer_state * ms = &ar->mixer_state[ctl_index];
type = mixer_ctl_get_type(ms->ctl);
if (!is_supported_ctl_type(type)) {
continue;
}
size_t value_sz = sizeof_ctl_type(type);
/* if any value has changed, update the mixer */
for (j = 0; j < ms->num_values; j++) {
//用memcpy的方式更新ar->mixer_state中对应的值。
}
...
}