audio: Add support for bluetooth voice calls
authorAndreas Schneider <asn@cryptomilk.org>
Thu, 9 Feb 2017 13:03:11 +0000 (14:03 +0100)
committerChristopher N. Hesse <raymanfx@gmail.com>
Sun, 12 Feb 2017 11:56:30 +0000 (11:56 +0000)
Change-Id: Ic63b7c09ed7401ab72824a406db333e3fbce780b

audio/audio_hw.c
audio/audio_hw.h
audio/voice.c
audio/voice.h

index 3659277de1880bc076d4c9c78ac657428a405c89..ff855d579e02560ad0f3de16564166467bc3c94c 100644 (file)
@@ -634,8 +634,6 @@ static snd_device_t get_input_snd_device(struct audio_device *adev, audio_device
         snd_device = SND_DEVICE_IN_VOICE_MIC;
         if (out_device & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
             snd_device = SND_DEVICE_IN_VOICE_HEADSET_MIC;
-        } else if (out_device & AUDIO_DEVICE_OUT_ALL_SCO) {
-            snd_device = SND_DEVICE_IN_BT_SCO_MIC;
         }
 
         if (voice_session_uses_twomic(adev->voice.session)) {
@@ -661,6 +659,17 @@ static snd_device_t get_input_snd_device(struct audio_device *adev, audio_device
                 }
             }
         }
+
+        /* BT SCO */
+        if (out_device & AUDIO_DEVICE_OUT_ALL_SCO) {
+            snd_device = SND_DEVICE_IN_VOICE_MIC;
+
+            if (out_device & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET) {
+                snd_device = SND_DEVICE_IN_BT_SCO_MIC;
+            } else if (voice_session_uses_twomic(adev->voice.session)) {
+                snd_device = SND_DEVICE_IN_VOICE_EARPIECE_MIC;
+            }
+        }
     } else if (source == AUDIO_SOURCE_CAMCORDER) {
         if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC ||
             in_device & AUDIO_DEVICE_IN_BACK_MIC) {
@@ -2726,6 +2735,11 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
         }
 #endif
         if (val != 0) {
+            bool bt_sco_active = false;
+
+            if (out->devices & AUDIO_DEVICE_OUT_ALL_SCO) {
+                bt_sco_active = true;
+            }
             out->devices = val;
 
             if (!out->standby) {
@@ -2758,6 +2772,10 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
             } else if ((adev->mode == AUDIO_MODE_IN_CALL) &&
                        adev->voice.in_call &&
                        (out == adev->primary_output)) {
+                /* Turn on bluetooth if needed */
+                if ((out->devices & AUDIO_DEVICE_OUT_ALL_SCO) && !bt_sco_active) {
+                    start_voice_session_bt_sco(adev->voice.session);
+                }
                 select_devices(adev, USECASE_VOICE_CALL);
             }
         }
@@ -3933,6 +3951,9 @@ static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)
 
     parms = str_parms_create_str(kvpairs);
 
+    /******************************************************
+     *** BT SCO
+     ******************************************************/
     ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_BT_NREC, value, sizeof(value));
     if (ret >= 0) {
         /* When set to false, HAL should disable EC and NS
@@ -3944,6 +3965,21 @@ static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)
             adev->voice.bluetooth_nrec = false;
     }
 
+    ret = str_parms_get_str(parms,
+                            AUDIO_PARAMETER_KEY_BT_SCO_WB,
+                            value,
+                            sizeof(value));
+    if (ret >= 0) {
+        /* TODO: Add support in voice calls */
+        if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) == 0) {
+            adev->voice.bluetooth_wb = true;
+            ALOGI("%s: Implement support for BT SCO wideband calls!!!",
+                  __func__);
+        } else {
+            adev->voice.bluetooth_wb = false;
+        }
+    }
+
     ret = str_parms_get_str(parms, "screen_state", value, sizeof(value));
     if (ret >= 0) {
         if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) == 0)
index 45c34075ff709ebce7e4b90341508c18d78ab618..ca90fef3b11c02c73ea387d5ccc1359235d52dd6 100644 (file)
@@ -381,6 +381,7 @@ struct voice_data {
     bool  in_call;
     float volume;
     bool  bluetooth_nrec;
+    bool  bluetooth_wb;
     void  *session;
 };
 
index 01a7b96ec273562dc868962d85d67c5c28896936..08d8baf38ee845663f60525380f1179c9a1fef91 100644 (file)
@@ -50,6 +50,14 @@ static struct pcm_config pcm_config_voicecall_wideband = {
     .format = PCM_FORMAT_S16_LE,
 };
 
+struct pcm_config pcm_config_voice_sco = {
+    .channels = 1,
+    .rate = SCO_DEFAULT_SAMPLING_RATE,
+    .period_size = SCO_PERIOD_SIZE,
+    .period_count = SCO_PERIOD_COUNT,
+    .format = PCM_FORMAT_S16_LE,
+};
+
 /* Prototypes */
 int start_voice_call(struct audio_device *adev);
 int stop_voice_call(struct audio_device *adev);
@@ -113,6 +121,70 @@ void prepare_voice_session(struct voice_session *session,
     }
 }
 
+/*
+ * This must be called with the hw device mutex locked, OK to hold other
+ * mutexes.
+ */
+static void stop_voice_session_bt_sco(struct voice_session *session) {
+    ALOGV("%s: Closing SCO PCMs", __func__);
+
+    if (session->pcm_sco_rx != NULL) {
+        pcm_stop(session->pcm_sco_rx);
+        pcm_close(session->pcm_sco_rx);
+        session->pcm_sco_rx = NULL;
+    }
+
+    if (session->pcm_sco_tx != NULL) {
+        pcm_stop(session->pcm_sco_tx);
+        pcm_close(session->pcm_sco_tx);
+        session->pcm_sco_tx = NULL;
+    }
+}
+
+/* must be called with the hw device mutex locked, OK to hold other mutexes */
+void start_voice_session_bt_sco(struct voice_session *session)
+{
+    if (session->pcm_sco_rx != NULL || session->pcm_sco_tx != NULL) {
+        ALOGW("%s: SCO PCMs already open!\n", __func__);
+        return;
+    }
+
+    ALOGV("%s: Opening SCO PCMs", __func__);
+
+    /* TODO: Add wideband support */
+
+    session->pcm_sco_rx = pcm_open(SOUND_CARD,
+                                   SOUND_PLAYBACK_SCO_DEVICE,
+                                   PCM_OUT|PCM_MONOTONIC,
+                                   &pcm_config_voice_sco);
+    if (session->pcm_sco_rx != NULL && !pcm_is_ready(session->pcm_sco_rx)) {
+        ALOGE("%s: cannot open PCM SCO RX stream: %s",
+              __func__, pcm_get_error(session->pcm_sco_rx));
+        goto err_sco_rx;
+    }
+
+    session->pcm_sco_tx = pcm_open(SOUND_CARD,
+                                   SOUND_CAPTURE_SCO_DEVICE,
+                                   PCM_IN|PCM_MONOTONIC,
+                                   &pcm_config_voice_sco);
+    if (session->pcm_sco_tx && !pcm_is_ready(session->pcm_sco_tx)) {
+        ALOGE("%s: cannot open PCM SCO TX stream: %s",
+              __func__, pcm_get_error(session->pcm_sco_tx));
+        goto err_sco_tx;
+    }
+
+    pcm_start(session->pcm_sco_rx);
+    pcm_start(session->pcm_sco_tx);
+
+    return;
+
+err_sco_tx:
+    pcm_close(session->pcm_sco_tx);
+    session->pcm_sco_tx = NULL;
+err_sco_rx:
+    pcm_close(session->pcm_sco_rx);
+    session->pcm_sco_rx = NULL;
+}
 /*
  * This function must be called with hw device mutex locked, OK to hold other
  * mutexes
@@ -170,7 +242,9 @@ int start_voice_session(struct voice_session *session)
     pcm_start(session->pcm_voice_rx);
     pcm_start(session->pcm_voice_tx);
 
-    /* TODO: handle SCO */
+    if (session->out_device & AUDIO_DEVICE_OUT_ALL_SCO) {
+        start_voice_session_bt_sco(session);
+    }
 
     if (session->two_mic_control) {
         ALOGV("%s: enabling two mic control", __func__);
@@ -209,7 +283,10 @@ void stop_voice_session(struct voice_session *session)
         status++;
     }
 
-    /* TODO: handle SCO */
+    if (session->out_device & AUDIO_DEVICE_OUT_ALL_SCO) {
+        stop_voice_session_bt_sco(session);
+    }
+
 
     session->out_device = AUDIO_DEVICE_NONE;
 
index 10f0fef8890dfeb5d602672472769f454b0bd4df..c89a3727e96a21c239cd0b4b2537d80cdf87fb86 100644 (file)
@@ -25,6 +25,9 @@ struct voice_session {
     struct pcm *pcm_voice_rx;
     struct pcm *pcm_voice_tx;
 
+    struct pcm *pcm_sco_rx;
+    struct pcm *pcm_sco_tx;
+
     bool wb_amr;
     bool two_mic_control;
     bool two_mic_disabled;
@@ -41,6 +44,8 @@ void set_voice_session_volume(struct voice_session *session, float volume);
 void set_voice_session_audio_path(struct voice_session *session);
 void set_voice_session_mic_mute(struct voice_session *session, bool state);
 
+void start_voice_session_bt_sco(struct voice_session *session);
+
 bool voice_session_uses_twomic(struct voice_session *session);
 bool voice_session_uses_wideband(struct voice_session *session);