1:FFBM测试
1.1:测试应用界面
由UI内容可知,audio相关的测试项有4个,分别是 speaker测试,已经2个mic到speaker的回环,以及一个耳机mic的回环;
它在ui中的配置是在:vendor/qcom/proprietary/commonsys/fastmmi/res/config/mmi.xml中
<!-- default config for UI mode -->
<modules>
<module name="SPEAKER"
lib_name="mmi_audio.so"
enable="1"
automation="0"
display_name="loudspeaker"
layout="layout_handset.xml"
parameter="type:play;tc:1;volume:50;duration:4;file:/system/etc/mmi/qualsound.wav;"/>
<module name="SPEAKER LOOP"
lib_name="mmi_audio.so"
enable="1"
automation="0"
display_name="audio_loudspeaker_play"
layout="layout_loudspeaker.xml"
parameter="type:loopback;tc:227;volume:30;duration:3;file:/mnt/vendor/persist/FTM_AP/speaker_record.wav;tc_play:1;"/>
<!-- Loopback for headset -->
<module name="HEADSET LOOP"
lib_name="mmi_audio.so"
enable="1"
automation="0"
display_name="audio_headset_loopback"
layout="layout_headset.xml"
parameter="type:loopback;tc:225;volume:45;duration:3;file:/mnt/vendor/persist/FTM_AP/headset_record.wav;tc_play:1;"/>
<!-- BACK MIC -->
<module name="NOISE MIC"
lib_name="mmi_audio.so"
enable="1"
automation="0"
display_name="audio_noise_mic"
layout="layout_primary_mic.xml"
parameter="type:mic;tc:226;volume:45;duration:3;file:/mnt/vendor/persist/FTM_AP/ftm_pcm_record.wav;tc_play:1;"/>
具体FCT相关的内容不做分析,只关注通过UI点击后,给测试命令传递的参数; 即parameter 后的参数,最终使用也是mm-audio-ftm 可执行程序;
1.2:FCT命令mm-audio-ftm
以下是常用的几个测试命令;
#测试扬声器
mm-audio-ftm -c /vendor/etc/ftm_test_config -tc 1 -d 10 -v 60 -file /system/etc/mmi/qualsound.wav
#测试耳机
headset:
left:
mm-audio-ftm -c /vendor/etc/ftm_test_config -tc 3 -d 10 -v 60 -file /system/etc/mmi/qualsound.wav
right:
mm-audio-ftm -c /vendor/etc/ftm_test_config -tc 4 -d 10 -v 60 -file /system/etc/mmi/qualsound.wav
#测试回环
mm-audio-ftm -c /vendor/etc/ftm_test_config -tc 225 -d 10 -v 50
mm-audio-ftm -c /vendor/etc/ftm_test_config -tc 226 -v 20 -d 5
mm-audio-ftm -c /vendor/etc/ftm_test_config -tc 227 -v 20
-c 后跟的参数,是配置文件,指定打开关闭一个通路所执行的配置命令集;
-tc 后跟的参数,是配置文件中标识对应测试项的 id;
-v 后跟的参数,配置测试过程中的音量设置;
-d 后跟的参数,是配置测试延时的时间,以s为单位;
-file 后跟的参数,配置测试过程中使用的音频文件的路径;
1.3:用例配置实例
1.3.1:Speaker
以/vendor/etc/ftm_test_config 中 tc 1 Left Speaker测试用例为例:
tc 1
#Left Speaker
!Playback
Rxdevice:0
enable
RX_CDC_DMA_RX_1 Channels:One
RX_MACRO RX2 MUX:AIF2_PB
RX INT2_1 MIX1 INP0:RX2
AUX_RDAC Switch:1
RX_CDC_DMA_RX_1 Audio Mixer MultiMedia1:1
disable
RX_MACRO RX2 MUX:ZERO
RX INT2_1 MIX1 INP0:ZERO
AUX_RDAC Switch:0
RX_CDC_DMA_RX_1 Audio Mixer MultiMedia1:0
1.3.2:Loop back
以/vendor/etc/ftm_test_config 中 tc 225 Headset loop back测试用例为例:
tc 225
#AMIC2 to HPH_LR AFE loopback
!AfeLoop
Txdevice:31
Rxdevice:30
enable
RX_MACRO RX0 MUX:AIF1_PB
RX_MACRO RX1 MUX:AIF1_PB
RX_CDC_DMA_RX_0 Channels:Two
RX INT0_1 MIX1 INP0:RX0
RX INT1_1 MIX1 INP0:RX1
RX INT0 DEM MUX:CLSH_DSM_OUT
RX INT1 DEM MUX:CLSH_DSM_OUT
RX_COMP1 Switch:1
RX_COMP2 Switch:1
HPHL_RDAC Switch:1
HPHR_RDAC Switch:1
HPHL_COMP Switch:1
HPHR_COMP Switch:1
RX_RX0 Digital Volume:62
RX_RX1 Digital Volume:62
TX DEC0 MUX:SWR_MIC
TX SMIC MUX0:SWR_MIC4
TX_CDC_DMA_TX_3 Channels:One
TX_AIF1_CAP Mixer DEC0:1
ADC2_MIXER Switch:1
ADC2 MUX:INP2
ADC2 Volume:62
RX_CDC_DMA_RX_0_DL_HL Switch:1
RX_CDC_DMA_RX_0 Port Mixer TX_CDC_DMA_TX_3:1
disable
RX_MACRO RX0 MUX:ZERO
RX_MACRO RX1 MUX:ZERO
RX INT0_1 MIX1 INP0:ZERO
RX INT1_1 MIX1 INP0:ZERO
RX INT0 DEM MUX:NORMAL_DSM_OUT
RX INT1 DEM MUX:NORMAL_DSM_OUT
RX_COMP1 Switch:0
RX_COMP2 Switch:0
HPHL_RDAC Switch:0
HPHR_RDAC Switch:0
HPHL_COMP Switch:0
HPHR_COMP Switch:0
RX_RX0 Digital Volume:0
RX_RX1 Digital Volume:0
TX DEC0 MUX:MSM_DMIC
TX SMIC MUX0:ZERO
TX_AIF1_CAP Mixer DEC0:0
ADC2_MIXER Switch:0
ADC2 MUX:ZERO
ADC2 Volume:62
RX_CDC_DMA_RX_0_DL_HL Switch:0
RX_CDC_DMA_RX_0 Port Mixer TX_CDC_DMA_TX_3:0
测试过程是以可执行命令mm-audio-ftm 进入执行的,下面对执行过程的细节进一步分析;
2:mm-audio-ftm分析
文件:
- code/vendor/qcom/proprietary/mm-audio/ftm_audio_main.c
- code/vendor/qcom/proprietary/mm-audio/ftm_audio_dispatch.c
2.1:main函数
(以上面测试命令为例, 其他测试情况忽略,仅分析主要流程)
int main(int argc, char *argv[])
{
//.....
char filename[100], *pfilename = NULL;
int codec = 0, total_test = INT_MAX, result, test_case;
int volume = 0, duration = 3, fl = 300, fh = 4000, count = 0;
int tx_volume = 0, rx_volume = 0;
//.....
#ifdef MSM8960_ALSA
FILE *fp;
FILE *fp_config;
char soundCardInfo[200];
char soundCardName[200];
char config_path[200] = {0};
g_config_test = 0;
if((fp = fopen("/proc/asound/cards","r")) == NULL) {
printf("Cannot open /proc/asound/cards file to get sound card info\n");
return -1;
} else { //跳过/proc/asound/cards 声卡name的第一行 获取到 snd card name
if((fgets(soundCardInfo, sizeof(soundCardInfo), fp) == NULL)) {
printf("/proc/asound/cards is empty. Abort\n");
return -1;
}
while((fgets(soundCardInfo, sizeof(soundCardInfo), fp) != NULL)) {
printf("SoundCardInfo %s", soundCardInfo);
sscanf(soundCardInfo, "%s", soundCardName);
printf("\nsoundCardName %s\n", soundCardName);
snprintf(config_path, sizeof(config_path), "%s_%s",
"/vendor/etc/ftm_test_config", soundCardName); //指定的这个config文件就用这个测试
//......
} else if ((fp_config = fopen("/vendor/etc/ftm_test_config","r")) != NULL) {
printf("Use the codec ftm_test_config file\n");
codec = CODEC_CONFIG_SUPPORT;
fclose(fp_config);
fp_config = NULL;
/* use default config file( ftm_test_config) as last resort. */
snprintf(config_path, sizeof(config_path), "/vendor/etc/ftm_test_config");
break;
} else {
//......
}
fclose(fp);
fp = NULL;
}
if (argc == 1) {
//...... 忽略
}else if(argc == 3) {
//...... 忽略
} else if (argc >= 5) { //有5个参数的通过文件指定的,采用的是这种
count = argc;
fp = NULL;
int i = 1;
test_case = -1;
/* Support only for codecs with config.txt support */
if (codec != CODEC_CONFIG_SUPPORT) {
printf("Command to enter: mm-audio-ftm -tc <tc number>\n");
return -1;
}
while((i + 1) < count) {
if (!strncmp(argv[i], "-tc", 3)) {
i++;
test_case = atoi(argv[i++]);
printf("\n Test case number %d", test_case);
} else if (!strncmp(argv[i], "-c", 2)) { // "-c 指定了 config文件 路径"
i++;
printf("\n Conf file name %s", argv[i]);
if (!(fp = fopen(argv[i++],"rb"))) {
printf("\n Failed to open file");
return -1;
}
g_config_test = 1;
} else if (!strncmp(argv[i], "-v", 2)) { // "-v" 配置 音量volume
i++;
volume = atoi(argv[i++]);
if (volume < 0 || volume > 100) // 范围0 - 100
volume = DEFAULT_VOLUME;
printf("\n Volume level set to %d", volume);
} else if (!strncmp(argv[i], "-d", 2)) { // "-d" 配置duration 测试时长
i++;
duration = atoi(argv[i++]);
printf("\n duration set to %d", duration);
} else if (!strncmp(argv[i], "-fl", 3)) { // "-fl" 配置频率
i++;
fl = atoi(argv[i++]);
printf("\n Frequency set to %d", fl);
} else if (!strncmp(argv[i], "-fh", 3)) {
i++;
fh = atoi(argv[i++]);
printf("\n Frequency set to %d", fh);
} else if (!strncmp(argv[i], "-tv", 3)) { //"-tv" 配置 tx_volume
i++;
tx_volume = atoi(argv[i++]);
printf("\n tx volume set to %d", tx_volume);
} else if (!strncmp(argv[i], "-rv", 3)) {
i++;
rx_volume = atoi(argv[i++]);
printf("\n rx volume set to %d", rx_volume); //"-rv" 配置 x_volume
//......
} else if (!strncmp(argv[i], "-file", 5)) { // "-file" 指定音频文件
uint32 len;
i++;
len = strnlen(argv[i], sizeof(filename));
//......
}
}
//......
result = execute_test_case(test_case, codec, fp, volume,
fl, fh, duration, pfilename, tx_volume, rx_volume,
tone_analysis, freq_tolerance);
g_config_test = 0;
if (!result) {
printf("test %d success\n", test_case);
ret = 0;
} else {
printf("test %d failure (%d)\n", test_case, result);
ret = -1;
}
if (fp) {
fclose(fp);
}
return ret;
//......
}
从上分析可知,获取传入的参数配置后,通过execute_test_case 执行具体的测试过程
2.2:execute_test_case 测试用例执行过程
int execute_test_case(int test_case, int codec, FILE *fp, int vol, int fl,
int fh, int duration, char *file_name, int tx_volume, int rx_volume,
int tone_analysis, int freq_tolerance)
{
//......
fprintf(stderr, "size of ftm_tc_devices_tabla = %d\n",
sizeof(ftm_tc_devices_tabla)/sizeof(ftm_tc_devices_tabla[0]));
fprintf(stderr, "size of ftm_tc_devices_sitar = %d\n",
sizeof(ftm_tc_devices_sitar)/sizeof(ftm_tc_devices_sitar[0]));
fprintf(stderr, "size of ftm_tc_devices_taiko = %d\n",
sizeof(ftm_tc_devices_taiko)/sizeof(ftm_tc_devices_taiko[0]));
//......
g_devicecapture = g_deviceplayback = -1;
if (fp){
paraminfo = ¶ms;
pthread_mutex_lock(¶ms.lock);
params.duration = duration;
params.volume = vol;
params.enable = 1;
params.fp = fp;
params.fl = fl;
params.fh = fh;
params.test_case = test_case; //test_case赋值给 params.test_case 后面test_case不是代替test id了
params.filename = file_name;
params.type_of_test = -1; //params.type_of_test 指测试设备类型
params.tx_volume = tx_volume;
params.rx_volume = rx_volume;
params.tone_analysis = tone_analysis;
params.freq_tolerance = freq_tolerance;
printf("\n Call parse function to enable"); //指定 enable为1后 执行parse,
parse(¶ms); //实际执行config文件中对应 test case中的 enable ctrl序列设置,详见parse分析
pthread_mutex_unlock(¶ms.lock);
//......
test_case = params.type_of_test; //test_case 变为type_of_test 指测试设备类型,经过parse 函数,
//更加配置文件中每个配置项的"!" 决定PATH 的具体类型
//类型一般定义为:
/*
#define PATH_NULL 0
#define PATH_RX 1
#define PATH_TX 2
#define PATH_AFE_LB 3
#define PATH_ADIE_LB 4
#define PATH_FM 5
#define PATH_EXT_LB 6
*/
ftm_tc_devices[test_case].path_type = params.type_of_test;
fseek(params.fp, 0 ,SEEK_SET);
}
switch (ftm_tc_devices[test_case].path_type) //在parse过程中 会对该test case的种类做出解析,Playback,Capture,LooP等
{
case PATH_RX: //对应"Playback",详见parse分析
result = test_ftm_tone_play_comm(test_case, paraminfo); //对应 实例log中 enable ctl序列和disable ctl序列中间的部分
break;
case PATH_TX: //对应"Capture",详见parse分析
result = test_ftm_pcm_record_comm(test_case, paraminfo);
break;
case PATH_FM: //对应"FM",详见parse分析
result = test_ftm_pcm_fm_comm(test_case, paraminfo);
break;
case PATH_AFE_LB: //对应"AfeLoop",详见parse分析
result = test_ftm_afe_loopback_comm(test_case, paraminfo);
break;
case PATH_ADIE_LB: //对应"CodecLoop",详见parse分析
result = test_ftm_adie_loopback_comm(test_case, paraminfo);
break;
case PATH_EXT_LB:
result = test_ftm_ext_loopback_comm(test_case, paraminfo);
break;
}
if (!fp) {
mxr = mixer_open(SND_CARD_NUM);
if (!mxr){
printf("error: failed to open mixer\n");
return -1;
}
ftm_tc_devices[test_case].enable(mxr, 0);
mixer_close(mxr);
taiko_i2s = FALSE;
} else {
printf("\n Call parse function to disable");
pthread_mutex_lock(¶ms.lock);
params.enable = 0;
parse(¶ms); //指定 enable为0后 执行parse, 实际执行 config文件中对应 test case中的 disable ctrl序列设置,详见parse分析
pthread_mutex_unlock(¶ms.lock);
}
return result;
}
2.3:parse 解析过程
static int parse(struct test_params *commands)
{
FILE *fp;
char *p, array[100], *pmode;
char tc[10];
char en[] = "enable";
char dis[] = "disable";
int len = 0, found = 0, len2 = 0;
struct mixer *mxr = NULL;
struct mixer_ctl *ctl = 0;
int ret, vol_found = 0;
char *temp;
int params, sublen;
//......
fp = commands->fp;
snprintf(tc, sizeof(tc), "tc %d", commands->test_case);
pmode = (commands->enable)?en:dis;
/* Find the test case */
while((p = fgets(array, sizeof(array), fp))) {
len = strnlen(p,sizeof(array));
p[len-1] = '\0';
len--;
if (!strncmp(p, tc, sizeof(tc))) { //根据tc 标号 在config文件中查找配置
/* Differentiate between tc 2 and tc 28 */
len2 = strnlen(tc, sizeof(tc));
if (len == len2)
found = 1;
break;
}
}
if (!found) {
printf("\n Invalid Testcase %s", tc);
return -1;
}
printf("\nFound %s %d seq %s", tc, found, pmode); //对应上面实例执行中输出的 "Found tc 1 1 seq enable"
strlcpy(tc, "tc", sizeof(tc));
/* 下面开始查找 enable or disable 命令序列*/
found = 0;
commands->type_of_test = -1;
while((p = fgets(array, sizeof(array), fp))) {
len = strnlen(p,sizeof(array));
p[len-1] = '\0';
len--;
/* Comment added print comment */
if (!strncmp(&p[0], "#", 1)) { //读到字符串 以"#" 开始,直接打印, 对应log中第二行的"#Left Speaker"
printf("\n %s", p);
} else if (strstr(p, "Rxdevice")) { //如果是"Rxdevice"
temp = NULL;
if ((temp = strstr(p,":"))) {
sublen = temp - p;
len = len - sublen - 1;
temp++;
temp[len] = '\0';
if (*temp >= '0' || *temp <= '9')
g_deviceplayback = atoi(temp); // 获取 "Rxdevice" 后的 0 - 9 的编号
else
g_deviceplayback = -1;
}
} else if (strstr(p, "Txdevice")) { //如果是"Txdevice"
temp = NULL;
if ((temp = strstr(p,":"))) {
sublen = temp - p;
len = len - sublen - 1;
temp++;
temp[len] = '\0';
if (*temp >= '0' || *temp <= '9')
g_devicecapture = atoi(temp); // 获取 "Txdevice" 后的 0 - 9 的编号
else
g_devicecapture = -1;
}
} else if (strstr(p, tc) && !strstr(p, ":")) { // 如果是 test case 本身,直接返回
break;
} else if (!strncmp(&p[0], "!", 1)) { //读到字符串 以"!" 开始, 判断其类型
if (!strncmp(&p[1], "Playback", len)) {
commands->type_of_test = PATH_RX; //如果是 "Playback";test type 设置为PATH_RX
} else if (!strncmp(&p[1], "Capture", len)) {
commands->type_of_test = PATH_TX; //如果是 "Capture";test type 设置为PATH_TX
} else if (!strncmp(&p[1], "AfeLoop", len)) {
commands->type_of_test = PATH_AFE_LB; //如果是 "AfeLoop";test type 设置为PATH_AFE_LB
} else if (!strncmp(&p[1], "CodecLoop", len)) {
commands->type_of_test = PATH_ADIE_LB; //如果是 "CodecLoop";test type 设置为PATH_ADIE_LB
} else if (!strncmp(&p[1], "FM", len)) {
commands->type_of_test = PATH_FM; //如果是 "FM";test type 设置为PATH_FM
} else
commands->type_of_test = -1;
} else if (strstr(p, pmode)) { //在config文件汇总 test case命令配置下 找到了 enable或disable的配置
found = 1;
break;
}
}
if (!found || commands->type_of_test == -1) {
printf("\n Sequence for %s not found", pmode);
return -1;
} else {
printf("\n Sequence for %s found", pmode); //对应前面实例 log中 "Sequence for enable found"
} // "Sequence for disable found"
pmode = (!commands->enable)?en:dis;
mxr = mixer_open("/dev/snd/controlC0"); //打开声卡的 Conctrol设备节点
//......
while((p = fgets(array, sizeof(array), fp))) { //获取一行配置
len = strnlen(p,sizeof(array));
p[len-1] = '\0';
len--;
if ( ((strstr(p, tc) || strstr(p, pmode)) && (!strstr(p, ":"))) ) { //去除非ctrl设置项
break;
} else {
if (len) {
char ctlname[100];
char ctlval[100];
temp = strstr(p,":");
if (temp) {
sublen = temp - p;
memcpy(ctlname, p, sublen);
ctlname[sublen] = '\0';
ctl = get_ctl(mxr, ctlname); //获取 ':' 之前的内容未 ctlname
if (!ctl) {
printf("%s Failed to get %s\n", ctlname);
break;
}
sublen = len - sublen;
sublen--;
temp++;
memcpy(ctlval, temp, sublen); //获取 ':' 之后的内容未 value
ctlval[sublen] = '\0';
int val = -1;
while(sublen > 0) {
if (*temp == ' ') {
temp++;
sublen--;
}else if (*temp >= '0' && *temp <= '9') {
val = 1; // value 是 数字, 通过mixer_ctl_set 设置
break;
} else {
val = 0; // 其他 mixer_ctl_select 设置
break;
}
}
if (val < 0) {
printf("\n In valid param for val");
return -EINVAL;
} else if (!val) {
printf("\n Set %s %s", ctlname, ctlval);
ret = mixer_ctl_select(ctl, ctlval); //设置ctl的值为 value
} else {
val = atoi(temp);
if (strstr(ctlname, "Volume")) {
val = commands->volume;
}
printf("\n Set %s %d", ctlname, val);
ret = mixer_ctl_set(ctl, val); //设置ctl的值为 value
}
}
}
}
}
mixer_close(mxr);
return ret;
}
3:测试实例过程分析
3.1:测试Speaker过程分析
3.1.1:测试过程
用mm-audio-ftm 执行测试, Left Speaker测试用例测试过程输出有以下内容:
bengal:/ # mm-audio-ftm -c /vendor/etc/ftm_test_config -tc 1 -d 10 -v 60 -file /system/etc/mmi/qualsound.wav
SoundCardInfo bengal-idp-snd-card
soundCardName bengal-idp-snd-card
Use the codec ftm_test_config file
Conf file name /vendor/etc/ftm_test_config
Test case number 1
duration set to 10
Volume level set to 60
size of ftm_tc_devices_tabla = 45
size of ftm_tc_devices_sitar = 28
size of ftm_tc_devices_taiko = 43
File name is /system/etc/mmi/qualsound.wav
Call parse function to enable
Found tc 1 1 seq enable
#Left Speaker
Sequence for enable found
Set RX_CDC_DMA_RX_1 Channels One
Set RX_MACRO RX2 MUX AIF2_PB
Set RX INT2_1 MIX1 INP0 RX2
Set AUX_RDAC Switch 1
Set RX_CDC_DMA_RX_1 Audio Mixer MultiMedia1 1
ftm_audio_set_path: 8875
Playback of file /system/etc/mmi/qualsound.wavFTM: audio_ftm_pcm_parse_header:256 - hdr.fmt_sz 16 hdr.bits_per_sample = 16hdr.audio_format = 1
FTM: audio_ftm_pcm_play_attach:362 -
audio_ftm_pcm_play_attach width 16 channels 2 sample rate 44100ftm_audio_start_file_play: call attach res = 0
audio_ftm_hw_open: flags 0 rate 44100 channels 2 period size 960
audio_ftm_hw_iocontrol: capturedevice 0 playback 0FTM: aud_ftm_play_pcm:163 -
Start PlaybackFTM: aud_ftm_play_pcm:168 - EOF file reached
FTM: aud_ftm_play_pcm:186 -
Done with fileFTM: aud_ftm_play_pcm:194 -
Close the fileFTM: audio_ftm_hw_iocontrol:1020 -
PCM Close calledaud_ftm_common_stop: 9019
Call parse function to disable
Found tc 1 1 seq disable
#Left Speaker
Sequence for disable found
Set RX_MACRO RX2 MUX ZERO
Set RX INT2_1 MIX1 INP0 ZERO
Set AUX_RDAC Switch 0
Set RX_CDC_DMA_RX_1 Audio Mixer MultiMedia1 0test 1 success
bengal:/ #
通过前面内容的分析,可知支持main函数后,携带用户配置参数;
通过parse解析后,在测试speaker的场景下,执行了配置文件中对audio kcontrol序列的enable序列的设置,同时指定了type_of_test = PATH_RX;对照上面的execute_test_case流程分析,在parse后通过 switch (ftm_tc_devices[test_case].path_type) 判断后,紧接着根据类型是PATH_RX,执行的是test_ftm_tone_play_comm; 等待设定的延时时间后,设置 params.enable = 0;通过parse(¶ms); 再进行一次参数解析,最后执行对audio kcontrol序列的disable序列的设置; 其中speaker播放测试的内容如下:
3.1.2:test_ftm_tone_play_comm
static int test_ftm_tone_play_comm(uint32 path, struct test_params *params)
{
ftm_audio_pkt_type cmd;
ftm_audio_response_type *pkt = NULL;
int result = 0;
struct mixer *mxr = 0;
//......
if (!ftm_audio_set_path(path)) {
result = 1;
goto done;
}
if (!params) {
if (!ftm_audio_start_tone(300, 2000)) { //如果没有设置音频格式,播放一个固定频率的声音
result = 2;
goto done;
}
} else { //有设置音频格式
if (params->filename) { //如果指定文件名,则直接播放这个文件
printf("\n Playback of file %s", params->filename);
ftm_audio_start_file_play(params); //播放设置的频率的声音
} else if (!ftm_audio_start_tone(params->fl, params->fh)) {
result = 2;
goto done;
}
}
//......
if (!params)
sleep(3);
else {
if (params && !params->filename)
sleep(params->duration);
}
if (!params || (params && !params->filename)) {
printf("\n Stop the tone now");
if (!ftm_audio_stop_tone()) {
result = 5;
goto done;
}
} else if (params && params->filename) {
ftm_audio_stop_file_play();
}
done:
return result;
}
3.1.3:测试时序图
3.2:测试Headset Loopback
测试回环的过程与单独的播放是不同的,主要是对对应场景下的设置。
3.2.1:测试过程
用mm-audio-ftm 执行测试,Headset Loopback测试用例测试过程输出有以下内容:
bengal:/ # mm-audio-ftm -c /vendor/etc/ftm_test_config -tc 225 -d 10 -v 50
SoundCardInfo bengal-idp-snd-card
soundCardName bengal-idp-snd-card
Use the codec ftm_test_config file
Conf file name /vendor/etc/ftm_test_config
Test case number 225
duration set to 10
size of ftm_tc_devices_tabla = 45
size of ftm_tc_devices_sitar = 28
size of ftm_tc_devices_taiko = 43
Volume level set to 50
Call parse function to enable
Found tc 225 1 seq enable
#AMIC2 to HPH_LR AFE loopback
Sequence for enable found
Set RX_MACRO RX0 MUX AIF1_PB
Set RX_MACRO RX1 MUX AIF1_PB
Set RX_CDC_DMA_RX_0 Channels Two
Set RX INT0_1 MIX1 INP0 RX0
Set RX INT1_1 MIX1 INP0 RX1
Set RX INT0 DEM MUX CLSH_DSM_OUT
Set RX INT1 DEM MUX CLSH_DSM_OUT
Set RX_COMP1 Switch 1
Set RX_COMP2 Switch 1
Set HPHL_RDAC Switch 1
Set HPHR_RDAC Switch 1
Set HPHL_COMP Switch 1
Set HPHR_COMP Switch 1
Set RX_RX0 Digital Volume 62
Set RX_RX1 Digital Volume 62
Set TX DEC0 MUX SWR_MIC
Set TX SMIC MUX0 SWR_MIC4
Set TX_CDC_DMA_TX_3 Channels One
Set TX_AIF1_CAP Mixer DEC0 1
Set ADC2_MIXER Switch 1
Set ADC2 MUX INP2
Set ADC2 Volume 10
Set RX_CDC_DMA_RX_0_DL_HL Switch 1
Set RX_CDC_DMA_RX_0 Port Mixer TX_CDC_DMA_TX_3 1
Set Path to device: 3
ftm_audio_set_path: 8875
AFE loopback to status: 1
ftm_audio_start_dsp_loopback: cdevice 31 pdevice 30
audio_ftm_hw_iocontrol: capturedevice 31 playback 30audio_ftm_hw_loopback_en(1.1)
play_loopback
rec_loopback
AFE loopback to status: 0
FTM: audio_ftm_hw_iocontrol:1023 -
PCM Close not calledaudio_ftm_hw_loopback_en(0.1)
aud_ftm_common_stop: 9019
Call parse function to disable
Found tc 225 1 seq disable
#AMIC2 to HPH_LR AFE loopback
Sequence for disable found
Set RX_MACRO RX0 MUX ZERO
Set RX_MACRO RX1 MUX ZERO
Set RX INT0_1 MIX1 INP0 ZERO
Set RX INT1_1 MIX1 INP0 ZERO
Set RX INT0 DEM MUX NORMAL_DSM_OUT
Set RX INT1 DEM MUX NORMAL_DSM_OUT
Set RX_COMP1 Switch 0
Set RX_COMP2 Switch 0
Set HPHL_RDAC Switch 0
Set HPHR_RDAC Switch 0
Set HPHL_COMP Switch 0
Set HPHR_COMP Switch 0
Set RX_RX0 Digital Volume 0
Set RX_RX1 Digital Volume 0
Set TX DEC0 MUX MSM_DMIC
Set TX SMIC MUX0 ZERO
Set TX_AIF1_CAP Mixer DEC0 0
Set ADC2_MIXER Switch 0
Set ADC2 MUX ZERO
Set ADC2 Volume 12
Set RX_CDC_DMA_RX_0_DL_HL Switch 0
Set RX_CDC_DMA_RX_0 Port Mixer TX_CDC_DMA_TX_3 0test 225 success
bengal:/ #
通过前面内容的分析,可知支持main函数后,携带用户配置参数;
通过parse解析后,在测试speaker的场景下,执行了配置文件中对audio kcontrol序列的enable序列的设置,同时指定了type_of_test = PATH_AFE_LB;对照上面的execute_test_case流程分析,在parse后通过 switch (ftm_tc_devices[test_case].path_type) 判断后,紧接着根据类型是PATH_RX,执行的是test_ftm_afe_loopback_comm; 等待设定的延时时间后,设置 params.enable = 0;通过parse(¶ms); 再进行一次参数解析,最后执行对audio kcontrol序列的disable序列的设置; 其中headset loopback测试的内容如下:
3.2.2:test_ftm_afe_loopback_comm
test_ftm_afe_loopback_comm(test_case, paraminfo);
int test_ftm_afe_loopback_comm(uint32 path, struct test_params *params)
{
ftm_audio_pkt_type cmd;
struct mixer *mxr = 0;
int result = 0;
cmd.composite_header.ftm_hdr.cmd_id=FTM_AUDIO_SET_PATH; //设置cmd Id, 为FTM_AUDIO_SET_PATH;
cmd.audio_params.device=path; //test case
ftm_audio_dispatch((PACKED void *)&cmd, FALSE); //发送cmd FALSE是指非DIAG
cmd.composite_header.ftm_hdr.cmd_id=FTM_AUDIO_DSP_LOOPBACK; //设置cmd Id, 为FTM_AUDIO_DSP_LOOPBACK;
cmd.audio_params.on_off = ON; //设置ON
ftm_audio_dispatch((PACKED void *)&cmd, FALSE); //发送cmd FALSE是指非DIAG
if (!params) {
//......
}
//......
else
sleep(params->duration); //有设置执行时间(睡眠等待)sleep(params->duration);
//没有设置,按main函数中的初始化值duration是3 约3s;
cmd.audio_params.on_off = OFF; //设置OFF
ftm_audio_dispatch((PACKED void *)&cmd, FALSE); //送cmd FALSE是指非DIAG
sleep(1);
done:
return result;
}
其中一些参数的结构定义:
test_params
struct test_params {
FILE *fp;
int test_case;
int enable;
int volume;
int fl;
int fh;
int duration;
char *filename;
int type_of_test;
int tx_volume;
int rx_volume;
int tone_analysis;
int freq_tolerance;
pthread_mutex_t lock;
};
struct test_params params;
ftm_audio_pkt_type
ftm_audio_pkt_type:
- ftm_audio_composite_cmd_header_type
- ftm_audio_diagpkt_subsys_header_type
- ftm_audio_cmd_header_type
- ftm_audio_params
ftm_audio_pkt_type
typedef struct
{
ftm_audio_composite_cmd_header_type composite_header;
ftm_audio_params audio_params;
}__attribute__((packed)) ftm_audio_pkt_type;
ftm_audio_composite_cmd_header_type
typedef struct
{
ftm_audio_diagpkt_subsys_header_type diag_hdr;
ftm_audio_cmd_header_type ftm_hdr;
}__attribute__((packed)) ftm_audio_composite_cmd_header_type;
ftm_audio_diagpkt_subsys_header_type
typedef struct
{
diagpkt_cmd_code_type cmd_code;
diagpkt_subsys_id_type subsys_id;
diagpkt_subsys_cmd_code_type subsys_cmd_code;
}__attribute__((packed)) ftm_audio_diagpkt_subsys_header_type;
ftm_audio_cmd_header_type
typedef struct
{
uint16 cmd_id; /* command id (required) */
uint16 cmd_data_len; /* request pkt data length, excluding the diag and ftm headers
(optional, set to 0 if not used)
*/
uint16 cmd_rsp_pkt_size; /* rsp pkt size, size of response pkt if different then req pkt
(optional, set to 0 if not used)
*/
}__attribute__((packed)) ftm_audio_cmd_header_type;
ftm_audio_params
typedef union
{
boolean on_off;
uint16 device;
uint8 volume;
uint16 num_pcm_buffers;
sint15 mic_gain_offset;
ftm_audio_tone_type tone_params;
ftm_audio_pcm_req_type pcm_req;
}__attribute__((packed)) ftm_audio_params;
能够设置的type 枚举:
typedef enum
{
FTM_AUDIO_SET_PATH,
FTM_AUDIO_SET_VOLUME,
FTM_AUDIO_DSP_LOOPBACK,
FTM_AUDIO_PCM_LOOPBACK,
FTM_AUDIO_TONES_PLAY,
FTM_AUDIO_TONES_STOP,
FTM_AUDIO_NS_CONTROL,
FTM_AUDIO_PCM_ENABLE,
FTM_AUDIO_GET_PCM_SAMPLES,
FTM_AUDIO_PCM_DISABLE,
FTM_MIC_GAIN_OFFSET,
FTM_AUDIO_MP3_PLAY,
FTM_AUDIO_EXT_LOOPBACK
} ftm_audio_subcmd_type;
输命令的是ftm_audio_dispatch
3.2.3:ftm_audio_dispatch
ftm_audio_dispatch
主要的FTM 音频命令处理程序。
这个函数可以被认为是 FTM 音频功能的 main()。
它从 FTM 主调度程序(PC FTM 工具 ->
DIAG -> PC USB -> Phone USB -> DIAG -> FTM -> 此处)并参与各种
音频系统驱动程序执行对应于 FTM 音频的操作命令发送。
@param cmd_ptr FTM 音频 DIAG 数据包结构。
@return DIAG 响应数据包。
PACKED void * ftm_audio_dispatch( PACKED void * request, DALBOOL is_diag)
{
ftm_audio_pkt_type * cmd_ptr = (ftm_audio_pkt_type *)request;
char result;
ftm_audio_response_type * new_pkt=NULL;
AUDIO_FTM_OUTPUT_PATH_ENUM_T x;
result=FTM_AUDIO_COMMAND_SUCCESS;
switch( cmd_ptr->composite_header.ftm_hdr.cmd_id )
{
case FTM_AUDIO_SET_PATH:
printf("\n Set Path to device: %d\n", cmd_ptr->audio_params.device); //对应 回环测试中"Set Path to device: 3"
//test type PATH设置PATH_AFE_LB即3
//......
if( ftm_audio_set_path(cmd_ptr->audio_params.device) != TRUE)
//......
break;
case FTM_AUDIO_SET_VOLUME:
//......
break;
case FTM_AUDIO_PCM_LOOPBACK:
//......
break;
case FTM_AUDIO_TONES_PLAY:
//......
break;
case FTM_AUDIO_TONES_STOP:
//......
break;
case FTM_AUDIO_DSP_LOOPBACK:
printf("\n AFE loopback to status: %d\n",cmd_ptr->audio_params.on_off);
if (g_curr_device <= 0) {
printf("\n In correct device");
break;
}
if(( cmd_ptr->audio_params.on_off == ON ) && (!ftm_audio_checkif_active_session())
&& (ftm_tc_devices[g_curr_device].path_type == PATH_AFE_LB))
{
ftm_audio_start_dsp_loopback(); //设置回环通路打开
}
else if(( cmd_ptr->audio_params.on_off == OFF ) && (ftm_audio_checkif_active_session()))
{
ftm_audio_stop_dsp_loopback(); //设置回环通路关闭
}
break;
//......
default:
printf("%s: unknown command (%d)\n",__func__,cmd_ptr->composite_header.ftm_hdr.cmd_id);
result=FTM_AUDIO_COMMAND_FAILURE;
break;
}
if (!is_diag)
return NULL;
//......
}
ftm_audio_set_path
static DALBOOL ftm_audio_set_path(
word test_case
)
{
//......
printf("%s: %d\n", __func__, __LINE__); //对应log:ftm_audio_set_path: 8875
mxr = mixer_open(SND_CARD_NUM);
//......
if (!g_config_test) {
if (ftm_tc_devices[test_case].path_type == PATH_RX) {
if (g_curr_rx_device >= 0)
ftm_tc_devices[g_curr_rx_device].enable(mxr, 0); //执行enable回调
}
//......
if (g_config_test || !ftm_tc_devices[test_case].enable(mxr, 1)) {
switch(ftm_tc_devices[test_case].path_type)
{
case PATH_RX:
g_curr_rx_device = test_case;
break;
case PATH_TX:
g_curr_tx_device = test_case;
break;
case PATH_AFE_LB:
g_curr_afe_lb_device = test_case;
break;
//......
}
} //......
}
g_curr_device = test_case;
//......
}
ftm_audio_start_dsp_loopback
static DALBOOL ftm_audio_start_dsp_loopback()
{
AUDIO_FTM_STS_T res;
AUD_FTM_DISPATCH_PARAM_T ftm_param;
memset(&ftm_param,0,sizeof(ftm_param));
ftm_param.afe_lp_param.path.inpath=g_current_path.iPath;
ftm_param.afe_lp_param.path.outpath=g_current_path.oPath;
ftm_param.afe_lp_param.gain=(uint16)g_volume_level;
ftm_param.afe_lp_param.channel=g_current_path.channel;
ftm_param.afe_lp_param.capturedevice=g_devicecapture;
ftm_param.afe_lp_param.playbackdevice=g_deviceplayback;
printf("\n %s: cdevice %d pdevice %d", __func__,
g_devicecapture, g_deviceplayback);
res=Audio_FTM_Attach( //调用Audio_FTM_Attach
AUDIO_FTM_METHOD_AFE_LOOPBACK, //指定为AUDIO_FTM_METHOD_AFE_LOOPBACK
&ftm_param,
&g_active_ftm_driver_attach_hdl);
//......
return aud_ftm_common_start(); //开始
}
ftm_audio_stop_dsp_loopback
static DALBOOL ftm_audio_stop_dsp_loopback()
{
return aud_ftm_common_stop();
}
Audio_FTM_Attach
AUDIO_FTM_STS_T
Audio_FTM_Attach
(
AUDIO_FTM_METHOD_ENUM_T nMethod,
void *pParam,
AUDIO_FTM_DRIVER_HANDLE_T * pDriverHdl
)
{
uint8 i;
AUDIO_FTM_STS_T ret;
nCurrent_ftm_method=nMethod;
if(g_pFncPtrTbl != NULL)
return AUDIO_FTM_ERROR;
for(i=0; i<AUDIO_FTM_METHOD_MAX;i++)
{
if(i<ARRAYLEN(g_IntfaceTable))
if(nCurrent_ftm_method == g_IntfaceTable[i].method_id)
break;
}
if(i >= AUDIO_FTM_METHOD_MAX)
return AUDIO_FTM_ERR_INVALID_PARAM;
g_pFncPtrTbl=&g_IntfaceTable[i].intf;
ret=g_pFncPtrTbl->Attach(pParam,pDriverHdl);
return ret;
}
aud_ftm_common_start
static DALBOOL aud_ftm_common_start()
{
AUDIO_FTM_STS_T res;
res=Audio_FTM_Open(
g_active_ftm_driver_attach_hdl,
AUD_FTM_ACCESS_MODE_CTRL,
AUD_FTM_SHAREMODE_EXCLU,
&g_active_ftm_driver_open_hdl);
if((res != AUDIO_FTM_SUCCESS) || (g_active_ftm_driver_open_hdl == NULL)) {
g_active_ftm_driver_open_hdl=NULL;
printf("%s: open failed\n",__func__);
return FALSE;
}
res=Audio_FTM_IOControl(
g_active_ftm_driver_open_hdl,
IOCTL_AUDIO_FTM_START,
NULL);
if(res != AUDIO_FTM_SUCCESS)
{
printf("%s: ioctl failed\n",__func__);
return FALSE;
}
AddRef(&g_total_ftm_instances);
return TRUE;
}
aud_ftm_common_stop
static DALBOOL aud_ftm_common_stop()
{
AUDIO_FTM_STS_T res;
res=Audio_FTM_IOControl(
g_active_ftm_driver_open_hdl,
IOCTL_AUDIO_FTM_STOP,
NULL);
if(res != AUDIO_FTM_SUCCESS)
{
printf("%s: stop failed\n",__func__);
return FALSE;
}
printf("%s: %d\n",__func__,__LINE__);
res=Audio_FTM_Close(g_active_ftm_driver_open_hdl);
if(res != AUDIO_FTM_SUCCESS)
{
return FALSE;
}
g_active_ftm_driver_open_hdl = NULL;
res=Audio_FTM_Detach(g_active_ftm_driver_attach_hdl);
if(res != AUDIO_FTM_SUCCESS)
{
printf("%s: detach failed\n",__func__);
return FALSE;
}
g_active_ftm_driver_attach_hdl = NULL;
Release(&g_total_ftm_instances);
return TRUE;
}