Android 10插入mic摄像头和喇叭,报错cannot open device ‘/dev/snd/pcmC0D0p‘: No such file or directory

平台:A133P安卓10
问题:插入喇叭并且插入带麦克风的摄像头后,喇叭没有声音
思路:
先从上层framework入手,logcat -d | grep audio
Android 10插入mic摄像头和喇叭,报错cannot open device ‘/dev/snd/pcmC0D0p‘: No such file or directory_第1张图片
可以发现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(&params);
    param_set_mask(&params, SNDRV_PCM_HW_PARAM_FORMAT,
                   pcm_format_to_alsa(config->format));
    param_set_mask(&params, SNDRV_PCM_HW_PARAM_SUBFORMAT,
                   SNDRV_PCM_SUBFORMAT_STD);
    param_set_min(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, config->period_size);
    param_set_int(&params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
                  pcm_format_to_bits(config->format));
    param_set_int(&params, SNDRV_PCM_HW_PARAM_FRAME_BITS,
                  pcm_format_to_bits(config->format) * config->channels);
    param_set_int(&params, SNDRV_PCM_HW_PARAM_CHANNELS,
                  config->channels);
    param_set_int(&params, SNDRV_PCM_HW_PARAM_PERIODS, config->period_count);
    param_set_int(&params, 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(&params, SNDRV_PCM_HW_PARAM_ACCESS,
                       SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
    else
        param_set_mask(&params, SNDRV_PCM_HW_PARAM_ACCESS,
                       SNDRV_PCM_ACCESS_RW_INTERLEAVED);

    if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, &params)) {
        oops(pcm, errno, "cannot set hw params");
        goto fail_close;
    }

    /* get our refined hw_params */
    config->period_size = param_get_int(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
    config->period_count = param_get_int(&params, 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

你可能感兴趣的:(LInux嵌入式,android,linux,嵌入式硬件,软件工程,java)