Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1b9b30c2 authored by Ashish Jain's avatar Ashish Jain Committed by Shiv Maliyappanahalli
Browse files

audio: Add check to confirm if routing to BT SCO is successfull

-Routing to BT-SCO can fail if the BT SOC gets disconnected or
the remote disconnects
-In such scenarios if audio is in process of routing streams to
BT SCO then those routing requests fail, hence the complete
data chain gets stalled.
-Add a mechanism to confirm with BT Codec driver if the last
routing request was successfull, if not close the stream
to avoid hang.

Change-Id: Ic78ba90ad55f30d99cdd76a6c0a0dc5398706b03
parent 81aa05d8
Loading
Loading
Loading
Loading
+162 −2
Original line number Diff line number Diff line
@@ -1485,6 +1485,48 @@ static bool force_device_switch(struct audio_usecase *usecase)
    return ret;
}

bool is_btsco_device(snd_device_t out_snd_device, snd_device_t in_snd_device)
{
   bool ret=false;
   if ((out_snd_device == SND_DEVICE_OUT_BT_SCO ||
        out_snd_device == SND_DEVICE_OUT_BT_SCO_WB) ||
        in_snd_device == SND_DEVICE_IN_BT_SCO_MIC_WB_NREC ||
        in_snd_device == SND_DEVICE_IN_BT_SCO_MIC_WB ||
        in_snd_device == SND_DEVICE_IN_BT_SCO_MIC_NREC ||
        in_snd_device == SND_DEVICE_IN_BT_SCO_MIC)
        ret = true;

   return ret;
}

bool is_a2dp_device(snd_device_t out_snd_device)
{
   bool ret=false;
   if (out_snd_device == SND_DEVICE_OUT_BT_A2DP)
        ret = true;

   return ret;
}

bool is_bt_soc_on(struct audio_device *adev)
{
    struct mixer_ctl *ctl;
    char *mixer_ctl_name = "BT SOC status";
    ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
    bool bt_soc_status = true;
    if (!ctl) {
        ALOGE("%s: Could not get ctl for mixer cmd - %s",
              __func__, mixer_ctl_name);
        /*This is to ensure we dont break targets which dont have the kernel change*/
        return true;
    }
    bt_soc_status = mixer_ctl_get_value(ctl, 0);
    ALOGD("BT SOC status: %d",bt_soc_status);
    return bt_soc_status;
}

int out_standby_l(struct audio_stream *stream);

int select_devices(struct audio_device *adev, audio_usecase_t uc_id)
{
    snd_device_t out_snd_device = SND_DEVICE_NONE;
@@ -1611,6 +1653,12 @@ int select_devices(struct audio_device *adev, audio_usecase_t uc_id)
            return 0;
    }

    if ((is_btsco_device(out_snd_device,in_snd_device) && !adev->bt_sco_on) ||
         (is_a2dp_device(out_snd_device) && !audio_extn_a2dp_is_ready())) {
          ALOGD("SCO/A2DP is selected but they are not connected/ready hence dont route");
          return 0;
    }

    ALOGD("%s: out_snd_device(%d: %s) in_snd_device(%d: %s)", __func__,
          out_snd_device, platform_get_snd_device_name(out_snd_device),
          in_snd_device,  platform_get_snd_device_name(in_snd_device));
@@ -1740,6 +1788,35 @@ int select_devices(struct audio_device *adev, audio_usecase_t uc_id)
        status = platform_switch_voice_call_usecase_route_post(adev->platform,
                                                               out_snd_device,
                                                               in_snd_device);

    if (is_btsco_device(out_snd_device, in_snd_device) || is_a2dp_device(out_snd_device)) {

         if (usecase->type == VOIP_CALL) {
             if (adev->active_input != NULL &&
                 !adev->active_input->standby) {
                 if (is_bt_soc_on(adev) == false){
                      ALOGD("BT SCO MIC disconnected while in connection");
                      if (adev->active_input->pcm != NULL)
                          pcm_stop(adev->active_input->pcm);
                 }
             }
             if ((usecase->stream.out != NULL) && (usecase->stream.out != adev->primary_output)
                  && usecase->stream.out->started) {
                  if (is_bt_soc_on(adev) == false) {
                      ALOGD("BT SCO/A2DP disconnected while in connection");
                      out_standby_l(&usecase->stream.out->stream.common);
                  }
             }
         } else if ((usecase->stream.out != NULL) &&
              !(usecase->stream.out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) &&
              usecase->stream.out->started) {
              if (is_bt_soc_on(adev) == false) {
                  ALOGD("BT SCO/A2dp disconnected while in connection");
                  out_standby_l(&usecase->stream.out->stream.common);
              }
         }
    }

    ALOGD("%s: done",__func__);

    return status;
@@ -1799,6 +1876,14 @@ int start_input_stream(struct stream_in *in)
        goto error_config;
    }

    if (audio_is_bluetooth_sco_device(in->device)) {
        if (!adev->bt_sco_on) {
            ALOGE("%s: SCO profile is not ready, return error", __func__);
            ret = -EIO;
            goto error_config;
        }
    }

    /* Check if source matches incall recording usecase criteria */
    ret = voice_check_and_set_incall_rec_usecase(adev, in);
    if (ret)
@@ -2197,7 +2282,7 @@ static int stop_output_stream(struct stream_out *out)

    list_remove(&uc_info->list);
    free(uc_info);

    out->started = 0;
    if (is_offload_usecase(out->usecase) &&
        (audio_extn_passthru_is_passthrough_stream(out))) {
        ALOGV("Disable passthrough , reset mixer to pcm");
@@ -2259,6 +2344,20 @@ int start_output_stream(struct stream_out *out)
            }
        }
    }
    if (out->devices & AUDIO_DEVICE_OUT_ALL_SCO) {
        if (!adev->bt_sco_on) {
            if (out->devices & AUDIO_DEVICE_OUT_SPEAKER) {
                //combo usecase just by pass a2dp
                ALOGW("%s: SCO is not connected, route it to speaker", __func__);
                out->devices = AUDIO_DEVICE_OUT_SPEAKER;
            } else {
                ALOGE("%s: SCO profile is not ready, return error", __func__);
                ret = -EAGAIN;
                goto error_config;
            }
        }
    }

    out->pcm_device_id = platform_get_pcm_device_id(out->usecase, PCM_PLAYBACK);
    if (out->pcm_device_id < 0) {
        ALOGE("%s: Invalid PCM device id(%d) for the usecase(%d)",
@@ -2647,6 +2746,7 @@ static int out_standby(struct audio_stream *stream)
        out->standby = true;
        if (out->usecase == USECASE_COMPRESS_VOIP_CALL) {
            voice_extn_compress_voip_close_output_stream(stream);
            out->started = 0;
            pthread_mutex_unlock(&adev->lock);
            pthread_mutex_unlock(&out->lock);
            ALOGD("VOIP output entered standby");
@@ -2696,6 +2796,53 @@ static int out_on_error(struct audio_stream *stream)
    return 0;
}

/*
 *standby implementation without locks, assumes that the callee already
 *has taken adev and out lock.
 */
int out_standby_l(struct audio_stream *stream)
{
    struct stream_out *out = (struct stream_out *)stream;
    struct audio_device *adev = out->dev;

    ALOGD("%s: enter: stream (%p) usecase(%d: %s)", __func__,
          stream, out->usecase, use_case_table[out->usecase]);

    if (!out->standby) {
        if (adev->adm_deregister_stream)
            adev->adm_deregister_stream(adev->adm_data, out->handle);

        if (is_offload_usecase(out->usecase))
            stop_compressed_output_l(out);

        out->standby = true;
        if (out->usecase == USECASE_COMPRESS_VOIP_CALL) {
            voice_extn_compress_voip_close_output_stream(stream);
            out->started = 0;
            ALOGD("VOIP output entered standby");
            return 0;
        } else if (!is_offload_usecase(out->usecase)) {
            if (out->pcm) {
                pcm_close(out->pcm);
                out->pcm = NULL;
            }
        } else {
            ALOGD("copl(%p):standby", out);
            out->send_next_track_params = false;
            out->is_compr_metadata_avail = false;
            out->gapless_mdata.encoder_delay = 0;
            out->gapless_mdata.encoder_padding = 0;
            if (out->compr != NULL) {
                compress_close(out->compr);
                out->compr = NULL;
            }
        }
        stop_output_stream(out);
    }
    ALOGD("%s: exit", __func__);
    return 0;
}

static int out_dump(const struct audio_stream *stream __unused,
                    int fd __unused)
{
@@ -3203,7 +3350,7 @@ static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
            out->standby = true;
            goto exit;
        }

        out->started = 1;
        if (last_known_cal_step != -1) {
            ALOGD("%s: retry previous failed cal level set", __func__);
            audio_hw_send_gain_dep_calibration(last_known_cal_step);
@@ -3372,6 +3519,7 @@ exit:
        if (out->usecase == USECASE_COMPRESS_VOIP_CALL) {
            pthread_mutex_lock(&adev->lock);
            voice_extn_compress_voip_close_output_stream(&out->stream.common);
            out->started = 0;
            pthread_mutex_unlock(&adev->lock);
            out->standby = true;
        }
@@ -4075,6 +4223,7 @@ int adev_open_output_stream(struct audio_hw_device *dev,
    out->bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
    out->non_blocking = 0;
    out->convert_buffer = NULL;
    out->started = 0;

    if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL &&
        (flags & AUDIO_OUTPUT_FLAG_DIRECT)) {
@@ -4575,6 +4724,7 @@ void adev_close_output_stream(struct audio_hw_device *dev __unused,
    if (out->usecase == USECASE_COMPRESS_VOIP_CALL) {
        pthread_mutex_lock(&adev->lock);
        ret = voice_extn_compress_voip_close_output_stream(&stream->common);
        out->started = 0;
        pthread_mutex_unlock(&adev->lock);
        if(ret != 0)
            ALOGE("%s: Compress voip output cannot be closed, error:%d",
@@ -4622,6 +4772,15 @@ static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)
    if (!parms)
        goto error;

    ret = str_parms_get_str(parms, "BT_SCO", value, sizeof(value));
    if (ret >= 0) {
        /* When set to false, HAL should disable EC and NS */
        if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) == 0)
            adev->bt_sco_on = true;
        else
            adev->bt_sco_on = false;
    }

    pthread_mutex_lock(&adev->lock);
    status = voice_set_parameters(adev, parms);
    if (status != 0)
@@ -5355,6 +5514,7 @@ static int adev_open(const hw_module_t *module, const char *name,
    adev->bluetooth_nrec = true;
    adev->acdb_settings = TTY_MODE_OFF;
    adev->allow_afe_proxy_usage = true;
    adev->bt_sco_on = false;
    /* adev->cur_hdmi_channels = 0;  by calloc() */
    adev->snd_dev_ref_cnt = calloc(SND_DEVICE_MAX, sizeof(int));
    voice_init(adev);
+2 −0
Original line number Diff line number Diff line
@@ -269,6 +269,7 @@ struct stream_out {

    struct audio_out_channel_map_param channel_map_param; /* input channel map */
    audio_offload_info_t info;
    int started;
    qahwi_stream_out_t qahwi_out;
};

@@ -437,6 +438,7 @@ struct audio_device {
    bool asrc_mode_enabled;
    qahwi_device_t qahwi_dev;
    bool vr_audio_mode_enabled;
    bool bt_sco_on;
};

int select_devices(struct audio_device *adev,
+21 −0
Original line number Diff line number Diff line
@@ -340,6 +340,13 @@ static int voip_start_call(struct audio_device *adev,

        select_devices(adev, USECASE_COMPRESS_VOIP_CALL);

        if (uc_info->in_snd_device == SND_DEVICE_NONE &&
            uc_info->out_snd_device == SND_DEVICE_NONE) {
            ALOGD("No valid input output device return");
            ret = -EIO;
            goto error_start_voip;
        }

        pcm_dev_rx_id = platform_get_pcm_device_id(uc_info->id, PCM_PLAYBACK);
        pcm_dev_tx_id = platform_get_pcm_device_id(uc_info->id, PCM_CAPTURE);

@@ -541,6 +548,14 @@ int voice_extn_compress_voip_start_output_stream(struct stream_out *out)
        goto error;
    }

    if (out->devices & AUDIO_DEVICE_OUT_ALL_SCO) {
         if (!adev->bt_sco_on) {
             ALOGE("%s: SCO profile is not ready, return error", __func__);
             ret = -EAGAIN;
             goto error;
         }
    }

    if (!voip_data.out_stream_count)
        ret = voice_extn_compress_voip_open_output_stream(out);

@@ -575,6 +590,12 @@ int voice_extn_compress_voip_start_input_stream(struct stream_in *in)
        goto error;
    }

    if (audio_is_bluetooth_sco_device(in->device) && !adev->bt_sco_on) {
        ret = -EIO;
        ALOGE("%s SCO is not ready return error %d", __func__,ret);
        goto error;
    }

    if (!voip_data.in_stream_count)
        ret = voice_extn_compress_voip_open_input_stream(in);