平台:A133P安卓10
问题:插入喇叭并且插入带麦克风的摄像头后,喇叭没有声音
思路:
先从上层framework入手,logcat -d | grep audio
可以发现07-10 11:58:21.486 1906 8810 E audio_hw_primary: cannot open pcm_out driver: cannot open device '/dev/snd/pcmC0D0p': No such file or directory
根据报错信息可以知道,audio驱动打开pcmC0D0p失败。
根据日志提供的TAG可以知道,audio_hw_primary在hardware层。
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -790,7 +790,7 @@ int start_output_stream(struct sunxi_stream_out *out)
adev->platform);
get_platform_snd_card_config(&out->card, &out->port, &out->config,
platform_device, adev->platform);
-
+ ALOGD("lijie debug get_platform_snd_card_config ,out->card = %d,out->port = %d\n",out->card, out->port);
update_debug_flag();
print_sunxi_stream_out(out);
@@ -898,6 +898,7 @@ int start_output_stream(struct sunxi_stream_out *out)
}
open:
+ ALOGE("lijie out->card=%d, out->port=%d\n",out->card, out->port);
out->pcm = pcm_open(out->card, out->port, PCM_OUT | PCM_MONOTONIC,
&out->config);
if (!pcm_is_ready(out->pcm)) {
定位到hardware后可以找的int start_output_stream(struct sunxi_stream_out *out)
会去调用open函数。
open:
ALOGE("lijie out->card=%d, out->port=%d\n",out->card, out->port);
out->pcm = pcm_open(out->card, out->port, PCM_OUT | PCM_MONOTONIC,
&out->config);
if (!pcm_is_ready(out->pcm)) {
ALOGE("cannot open pcm_out driver: %s", pcm_get_error(out->pcm));
pcm_close(out->pcm);
adev->active_output = NULL;
return -ENOMEM;
}
return ret;
其实可以发现open函数如果打开一个节点失败比如pcmc0d0p打开时候后,就直接return了,并不会继续打开pcmc1d0p。。。等等其他设备。我们可以在这里加一个逻辑,当open失败的时候就继续打开其他节点。接着我们继续看pcm_open做了什么事情。
struct pcm *pcm_open(unsigned int card, unsigned int device,
unsigned int flags, struct pcm_config *config)
{
struct pcm *pcm;
struct snd_pcm_info info;
struct snd_pcm_hw_params params;
struct snd_pcm_sw_params sparams;
char fn[256];
int rc;
if (!config) {
return &bad_pcm; /* TODO: could support default config here */
}
pcm = calloc(1, sizeof(struct pcm));
if (!pcm)
return &bad_pcm; /* TODO: could support default config here */
pcm->config = *config;
snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device,
flags & PCM_IN ? 'c' : 'p');
pcm->flags = flags;
pcm->fd = open(fn, O_RDWR|O_NONBLOCK);
if (pcm->fd < 0) {
oops(pcm, errno, "cannot open device '%s'", fn);
return pcm;
}
if (fcntl(pcm->fd, F_SETFL, fcntl(pcm->fd, F_GETFL) &
~O_NONBLOCK) < 0) {
oops(pcm, errno, "failed to reset blocking mode '%s'", fn);
goto fail_close;
}
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_INFO, &info)) {
oops(pcm, errno, "cannot get info");
goto fail_close;
}
pcm->subdevice = info.subdevice;
param_init(¶ms);
param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_FORMAT,
pcm_format_to_alsa(config->format));
param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_SUBFORMAT,
SNDRV_PCM_SUBFORMAT_STD);
param_set_min(¶ms, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, config->period_size);
param_set_int(¶ms, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
pcm_format_to_bits(config->format));
param_set_int(¶ms, SNDRV_PCM_HW_PARAM_FRAME_BITS,
pcm_format_to_bits(config->format) * config->channels);
param_set_int(¶ms, SNDRV_PCM_HW_PARAM_CHANNELS,
config->channels);
param_set_int(¶ms, SNDRV_PCM_HW_PARAM_PERIODS, config->period_count);
param_set_int(¶ms, SNDRV_PCM_HW_PARAM_RATE, config->rate);
if (flags & PCM_NOIRQ) {
if (!(flags & PCM_MMAP)) {
oops(pcm, EINVAL, "noirq only currently supported with mmap().");
goto fail_close;
}
params.flags |= SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP;
pcm->noirq_frames_per_msec = config->rate / 1000;
}
if (flags & PCM_MMAP)
param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_ACCESS,
SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
else
param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_ACCESS,
SNDRV_PCM_ACCESS_RW_INTERLEAVED);
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, ¶ms)) {
oops(pcm, errno, "cannot set hw params");
goto fail_close;
}
/* get our refined hw_params */
config->period_size = param_get_int(¶ms, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
config->period_count = param_get_int(¶ms, SNDRV_PCM_HW_PARAM_PERIODS);
pcm->buffer_size = config->period_count * config->period_size;
if (flags & PCM_MMAP) {
pcm->mmap_buffer = mmap(NULL, pcm_frames_to_bytes(pcm, pcm->buffer_size),
PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, pcm->fd, 0);
if (pcm->mmap_buffer == MAP_FAILED) {
oops(pcm, errno, "failed to mmap buffer %d bytes\n",
pcm_frames_to_bytes(pcm, pcm->buffer_size));
goto fail_close;
}
}
memset(&sparams, 0, sizeof(sparams));
sparams.tstamp_mode = SNDRV_PCM_TSTAMP_ENABLE;
sparams.period_step = 1;
if (!config->start_threshold) {
if (pcm->flags & PCM_IN)
pcm->config.start_threshold = sparams.start_threshold = 1;
else
pcm->config.start_threshold = sparams.start_threshold =
config->period_count * config->period_size / 2;
} else
sparams.start_threshold = config->start_threshold;
/* pick a high stop threshold - todo: does this need further tuning */
if (!config->stop_threshold) {
if (pcm->flags & PCM_IN)
pcm->config.stop_threshold = sparams.stop_threshold =
config->period_count * config->period_size * 10;
else
pcm->config.stop_threshold = sparams.stop_threshold =
config->period_count * config->period_size;
}
else
sparams.stop_threshold = config->stop_threshold;
if (!pcm->config.avail_min) {
if (pcm->flags & PCM_MMAP)
pcm->config.avail_min = sparams.avail_min = pcm->config.period_size;
else
pcm->config.avail_min = sparams.avail_min = 1;
} else
sparams.avail_min = config->avail_min;
sparams.xfer_align = config->period_size / 2; /* needed for old kernels */
sparams.silence_threshold = config->silence_threshold;
sparams.silence_size = config->silence_size;
pcm->boundary = sparams.boundary = pcm->buffer_size;
while (pcm->boundary * 2 <= INT_MAX - pcm->buffer_size)
pcm->boundary *= 2;
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams)) {
oops(pcm, errno, "cannot set sw params");
goto fail;
}
rc = pcm_hw_mmap_status(pcm);
if (rc < 0) {
oops(pcm, errno, "mmap status failed");
goto fail;
}
#ifdef SNDRV_PCM_IOCTL_TTSTAMP
if (pcm->flags & PCM_MONOTONIC) {
int arg = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC;
rc = ioctl(pcm->fd, SNDRV_PCM_IOCTL_TTSTAMP, &arg);
if (rc < 0) {
oops(pcm, errno, "cannot set timestamp type");
goto fail;
}
}
#endif
pcm->underruns = 0;
return pcm;
fail:
if (flags & PCM_MMAP)
munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size));
fail_close:
close(pcm->fd);
pcm->fd = -1;
return pcm;
}
pcm_open主要是根据上层调用开辟内存空间,open设备,初始化设备。
pcm_open里面不太好改,然后我们回到audio_hw.c继续看看怎么改:
改法一:
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -790,7 +790,7 @@ int start_output_stream(struct sunxi_stream_out *out)
adev->platform);
get_platform_snd_card_config(&out->card, &out->port, &out->config,
platform_device, adev->platform);
-
+ ALOGD("lijie debug get_platform_snd_card_config ,out->card = %d,out->port = %d\n",out->card, out->port);
update_debug_flag();
print_sunxi_stream_out(out);
@@ -898,11 +898,25 @@ int start_output_stream(struct sunxi_stream_out *out)
}
open:
+ ALOGE("lijie out->card=%d, out->port=%d\n",out->card, out->port);
out->pcm = pcm_open(out->card, out->port, PCM_OUT | PCM_MONOTONIC,
&out->config);
if (!pcm_is_ready(out->pcm)) {
ALOGE("cannot open pcm_out driver: %s", pcm_get_error(out->pcm));
pcm_close(out->pcm);
+ int flag = false;
+ for(int i = 0; i < 64 ; i++){
+ out->pcm = pcm_open(i out->port, PCM_OUT | PCM_MONOTONIC,
+ &out->config);
+ if(pcm_is_ready(out->pcm)){
+ flag = true;
+ break;
+ }
+ pcm_close(out->pcm);
+ }
+ if(!flag){
+ return ret;
+ }
adev->active_output = NULL;
return -ENOMEM;
}
当打开一个设备失败后会继续打开其他设备,但是这样改了之后不行,我怀疑是pcm_open里面传入的参数有些没有修改,我们只修改的card编号但是其他的信息对不上。继续看前面的代码:
int start_output_stream(struct sunxi_stream_out *out)
{
ALOGD("start_output_stream");
int ret = 0;
struct sunxi_audio_device *adev = out->dev;
int platform_device;
/* audio dump data init */
init_dump_flags(true, &out->dd_write_out);
adev->active_output = out;
select_devices(adev, true, false);
platform_device = get_platform_device(adev->mode, adev->out_devices,
adev->platform);
get_platform_snd_card_config(&out->card, &out->port, &out->config,
platform_device, adev->platform);
ALOGD("lijie debug get_platform_snd_card_config ,out->card = %d,out->port = %d\n",out->card, out->port);
update_debug_flag();
print_sunxi_stream_out(out);
可以发现这个函数前面调用了get_platform_snd_card_config
这个函数,进去看看这个函数干什么的?
int get_platform_snd_card_config(int *card, int *port,
struct pcm_config *pcm_conf,
int platform_device,
const struct platform *platform)
{
struct platform_info *info = platform->info;
struct pdev_profile *profile = NULL;
char dev_name[50];
unsigned int i;
pdev2str(dev_name, platform_device);
/* find platform_device profile */
ALOGE("lijie ARRAY_SIZE(info->profiles) = %d\n",ARRAY_SIZE(info->profiles));
ALOGE("lijie dev_name[] = %s\n",dev_name);
for (i = 0; i < ARRAY_SIZE(info->profiles); i++) {
profile = info->profiles[i];
if (profile) {
if (strstr(profile->devices, dev_name)) {
ALOGE("lijie profile->devices = %s\n",profile->devices);
break;
}
}
}
if (!profile) {
ALOGE("can't get snd_card_config.");
return -1;
}
/* get snd_card_config */
*card = profile->frontend.card;
*port = profile->frontend.port;
pcm_conf->rate = profile->frontend.rate;
pcm_conf->channels = profile->frontend.channels;
pcm_conf->period_count = profile->frontend.period_count;
pcm_conf->period_size = profile->frontend.period_size;
return 0;
}
/* select SPK snd card id as default mixer card id.
* NOTE: There may be multiple snd card in the future.
*/
这个函数就是选择一个默认的声卡,并且给pcm初始化,所以我们可以在这里加一个逻辑用于重新选择card。代码如下:
--- a/hal/platform.c
+++ b/hal/platform.c
@@ -1179,10 +1179,13 @@ int get_platform_snd_card_config(int *card, int *port,
pdev2str(dev_name, platform_device);
/* find platform_device profile */
+ ALOGE("lijie ARRAY_SIZE(info->profiles) = %d\n",ARRAY_SIZE(info->profiles));
+ ALOGE("lijie dev_name[] = %s\n",dev_name);
for (i = 0; i < ARRAY_SIZE(info->profiles); i++) {
profile = info->profiles[i];
if (profile) {
if (strstr(profile->devices, dev_name)) {
+ ALOGE("lijie profile->devices = %s\n",profile->devices);
break;
}
}
@@ -1193,6 +1196,17 @@ int get_platform_snd_card_config(int *card, int *port,
return -1;
}
+ char filePath[128] = {0};
+ for(i = 0; i < 64; i++){
+ memset(filePath,0,sizeof(filePath));
+ sprintf(filePath,"/dev/snd/pcmC%dD0p",i);
+ if(!access(filePath,0)){
+ break;
+ }
+ }
+ if(i != 64){
+ profile->frontend.card = i;
+ }
/* get snd_card_config */
*card = profile->frontend.card;
*port = profile->frontend.port;
烧录固件一切正常。
参考资料:https://blog.csdn.net/Ciellee/article/details/100997885
https://blog.csdn.net/ansondroider/article/details/107485454