From 7522e673fc9e0653b30d7022a86522af78036f8d Mon Sep 17 00:00:00 2001
From: Matthias Ringwald <matthias@ringwald.ch>
Date: Thu, 28 Jul 2016 17:19:13 +0200
Subject: [PATCH]  hfp_hf: send codec_event(CVSD) if no codec negotation
 happens. hfp_ag: use T2/T1 connection settings. only accept SCO connections
 in hfp_hf

---
 src/classic/hfp.c    | 36 +++++++++++++++--------
 src/classic/hfp_ag.c | 69 +++++++++++++++++---------------------------
 src/classic/hfp_hf.c | 50 +++++++++++++++++++++++---------
 3 files changed, 88 insertions(+), 67 deletions(-)

diff --git a/src/classic/hfp.c b/src/classic/hfp.c
index c056f3940..0c4335e4b 100644
--- a/src/classic/hfp.c
+++ b/src/classic/hfp.c
@@ -318,7 +318,7 @@ void hfp_reset_context_flags(hfp_connection_t * hfp_connection){
 
     // establish codecs hfp_connection
     hfp_connection->suggested_codec = 0;
-    hfp_connection->negotiated_codec = HFP_CODEC_CVSD;
+    hfp_connection->negotiated_codec = 0;
     hfp_connection->codec_confirmed = 0;
 
     hfp_connection->establish_audio_connection = 0; 
@@ -495,14 +495,22 @@ static void hfp_handle_failed_sco_connection(uint8_t status){
             sco_establishment_active->link_setting = HFP_LINK_SETTINGS_D1;
             break;                    
         case HFP_LINK_SETTINGS_S2:
-        case HFP_LINK_SETTINGS_S3:
-        case HFP_LINK_SETTINGS_S4:
             sco_establishment_active->link_setting = HFP_LINK_SETTINGS_S1;
             break;
-        case HFP_LINK_SETTINGS_T1:
-        case HFP_LINK_SETTINGS_T2:
+        case HFP_LINK_SETTINGS_S3:
+            sco_establishment_active->link_setting = HFP_LINK_SETTINGS_S2;
+            break;
+        case HFP_LINK_SETTINGS_S4:
             sco_establishment_active->link_setting = HFP_LINK_SETTINGS_S3;
             break;
+        case HFP_LINK_SETTINGS_T1:
+            log_info("T1 failed, fallback to CVSD - D1");
+            sco_establishment_active->negotiated_codec = HFP_CODEC_CVSD;
+            sco_establishment_active->link_setting = HFP_LINK_SETTINGS_D1;
+            break;
+        case HFP_LINK_SETTINGS_T2:
+            sco_establishment_active->link_setting = HFP_LINK_SETTINGS_T1;
+            break;
     }
     sco_establishment_active->establish_audio_connection = 1;
     sco_establishment_active = 0;
@@ -520,12 +528,16 @@ void hfp_handle_hci_event(uint8_t packet_type, uint16_t channel, uint8_t *packet
     switch (hci_event_packet_get_type(packet)) {
         case HCI_EVENT_CONNECTION_REQUEST:
             // printf("hfp HCI_EVENT_CONNECTION_REQUEST\n");
-            
-            hci_event_connection_request_get_bd_addr(packet, event_addr);
-            hfp_connection = provide_hfp_connection_context_for_bd_addr(event_addr);
-            
-            if (!hfp_connection) break;
-            hfp_connection->ag_establish_SCO = 1;
+            switch(hci_event_connection_request_get_link_type(packet)){
+                case 0: //  SCO
+                case 2: // eSCO
+                    hci_event_connection_request_get_bd_addr(packet, event_addr);
+                    hfp_connection = get_hfp_connection_context_for_bd_addr(event_addr);
+                    if (!hfp_connection) break;
+                    hfp_connection->ag_establish_SCO = 1;
+                default:
+                    break;                    
+            }            
             break;
         
         case RFCOMM_EVENT_INCOMING_CONNECTION:
@@ -1400,7 +1412,7 @@ void hfp_setup_synchronous_connection(hfp_connection_t * hfp_connection){
     sco_establishment_active = hfp_connection;
     uint16_t sco_voice_setting = hci_get_sco_voice_setting();
     if (hfp_connection->negotiated_codec == HFP_CODEC_MSBC){
-        sco_voice_setting = 0x0003; // Transparent data
+        sco_voice_setting = 0x0043; // Transparent data
     }
     hci_send_cmd(&hci_setup_synchronous_connection, hfp_connection->acl_handle, 8000, 8000, hfp_link_settings[setting].max_latency,
         sco_voice_setting, hfp_link_settings[setting].retransmission_effort, hfp_link_settings[setting].packet_types); // all types 0x003f, only 2-ev3 0x380
diff --git a/src/classic/hfp_ag.c b/src/classic/hfp_ag.c
index 14c8a66a2..efb0222c2 100644
--- a/src/classic/hfp_ag.c
+++ b/src/classic/hfp_ag.c
@@ -536,6 +536,29 @@ static uint8_t hfp_ag_suggest_codec(hfp_connection_t *hfp_connection){
     return HFP_CODEC_CVSD;
 }
 
+static void hfp_init_link_settings(hfp_connection_t * hfp_connection){
+    // determine highest possible link setting
+    hfp_connection->link_setting = HFP_LINK_SETTINGS_D1;
+    switch (hfp_connection->negotiated_codec){
+        default:
+        case HFP_CODEC_CVSD:
+            if (hci_remote_esco_supported(hfp_connection->acl_handle)){
+                hfp_connection->link_setting = HFP_LINK_SETTINGS_S3;
+                if ((hfp_connection->remote_supported_features & (1<<HFP_HFSF_ESCO_S4))
+                &&  (hfp_supported_features             & (1<<HFP_AGSF_ESCO_S4))){
+                    hfp_connection->link_setting = HFP_LINK_SETTINGS_S4;
+                }
+            }
+            break;
+        case HFP_CODEC_MSBC:
+            if (hci_remote_esco_supported(hfp_connection->acl_handle)){
+                hfp_connection->link_setting = HFP_LINK_SETTINGS_T2;
+            }
+            break;
+    }
+    log_info("hfp_init_link_settings: %u", hfp_connection->link_setting);
+}
+
 static int codecs_exchange_state_machine(hfp_connection_t * hfp_connection){
     /* events ( == commands):
         HFP_CMD_AVAILABLE_CODECS == received AT+BAC with list of codecs
@@ -599,6 +622,8 @@ static int codecs_exchange_state_machine(hfp_connection_t * hfp_connection){
             log_info("hfp: codec confirmed: %s", hfp_connection->negotiated_codec == HFP_CODEC_MSBC ? "mSBC" : "CVSD");
             hfp_emit_codec_event(hfp_callback, 0, hfp_connection->negotiated_codec);
             hfp_ag_ok(hfp_connection->rfcomm_cid);           
+            // now, pick link settings
+            hfp_init_link_settings(hfp_connection);
             return 1; 
         default:
             break;
@@ -606,24 +631,10 @@ static int codecs_exchange_state_machine(hfp_connection_t * hfp_connection){
     return 0;
 }
 
-static void hfp_init_link_settings(hfp_connection_t * hfp_connection){
-    // determine highest possible link setting
-    hfp_connection->link_setting = HFP_LINK_SETTINGS_D1;
-    if (hci_remote_esco_supported(hfp_connection->acl_handle)){
-        hfp_connection->link_setting = HFP_LINK_SETTINGS_S3;
-        if ((hfp_connection->remote_supported_features & (1<<HFP_HFSF_ESCO_S4))
-        &&  (hfp_supported_features             & (1<<HFP_AGSF_ESCO_S4))){
-            hfp_connection->link_setting = HFP_LINK_SETTINGS_S4;
-        }
-    }
-}
-
 static void hfp_ag_slc_established(hfp_connection_t * hfp_connection){
     hfp_connection->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED;
     hfp_emit_connection_event(hfp_callback, HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED, 0, hfp_connection->acl_handle, hfp_connection->remote_addr);
     
-    hfp_init_link_settings(hfp_connection);
-
     // if active call exist, set per-hfp_connection state active, too (when audio is on)
     if (hfp_gsm_call_status() == HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT){
         hfp_connection->call_state = HFP_CALL_W4_AUDIO_CONNECTION_FOR_ACTIVE;
@@ -1626,34 +1637,6 @@ static void hfp_run_for_context(hfp_connection_t *hfp_connection){
 
     if (!hfp_connection->rfcomm_cid) return;
 
-    if (hfp_connection->ag_establish_SCO && hci_can_send_command_packet_now()){
-        // remote supported feature eSCO is set if link type is eSCO
-        // eSCO: S4 - max latency == transmission interval = 0x000c == 12 ms, 
-        uint16_t max_latency;
-        uint8_t  retransmission_effort;
-        uint16_t packet_types;
-        
-        if (hci_remote_esco_supported(hfp_connection->acl_handle)){
-            max_latency = 0x000c;
-            retransmission_effort = 0x02;
-            packet_types = 0x388;
-        } else {
-            max_latency = 0xffff;
-            retransmission_effort = 0xff;
-            packet_types = 0x003f;
-        }
-        
-        uint16_t sco_voice_setting = hci_get_sco_voice_setting();
-        if (hfp_connection->negotiated_codec == HFP_CODEC_MSBC){
-            sco_voice_setting = 0x0003; // Transparent data
-        }
-        
-        log_info("HFP: sending hci_accept_connection_request, sco_voice_setting %02x", sco_voice_setting);
-        hci_send_cmd(&hci_accept_synchronous_connection, hfp_connection->remote_addr, 8000, 8000, max_latency, 
-                        sco_voice_setting, retransmission_effort, packet_types);
-        return;
-    }
-
     if (!rfcomm_can_send_packet_now(hfp_connection->rfcomm_cid)) {
         log_info("hfp_run_for_context: request can send for 0x%02x", hfp_connection->rfcomm_cid);
         rfcomm_request_can_send_now_event(hfp_connection->rfcomm_cid);
@@ -2134,6 +2117,8 @@ static void hfp_ag_setup_audio_connection(hfp_connection_t * hfp_connection){
         hfp_connection->negotiated_codec = HFP_CODEC_CVSD;
         hfp_connection->codecs_state = HFP_CODECS_EXCHANGED;
         hfp_emit_codec_event(hfp_callback, 0, hfp_connection->negotiated_codec);
+        // now, pick link settings
+        hfp_init_link_settings(hfp_connection);
     } 
 
     switch (hfp_connection->codecs_state){
diff --git a/src/classic/hfp_hf.c b/src/classic/hfp_hf.c
index 7706587b8..8bdcf5c4c 100644
--- a/src/classic/hfp_hf.c
+++ b/src/classic/hfp_hf.c
@@ -588,6 +588,42 @@ static int call_setup_state_machine(hfp_connection_t * hfp_connection){
 static void hfp_run_for_context(hfp_connection_t * hfp_connection){
     if (!hfp_connection) return;
     if (!hfp_connection->rfcomm_cid) return;
+
+    if (hfp_connection->ag_establish_SCO && hci_can_send_command_packet_now()){
+
+        // notify about codec selection if not done already
+        if (hfp_connection->negotiated_codec == 0){
+            hfp_connection->negotiated_codec = HFP_CODEC_CVSD;
+            hfp_emit_codec_event(hfp_callback, 0, hfp_connection->negotiated_codec);
+        }
+
+        // remote supported feature eSCO is set if link type is eSCO
+        // eSCO: S4 - max latency == transmission interval = 0x000c == 12 ms, 
+        uint16_t max_latency;
+        uint8_t  retransmission_effort;
+        uint16_t packet_types;
+        
+        if (hci_remote_esco_supported(hfp_connection->acl_handle)){
+            max_latency = 0x000c;
+            retransmission_effort = 0x02;
+            packet_types = 0x388;
+        } else {
+            max_latency = 0xffff;
+            retransmission_effort = 0xff;
+            packet_types = 0x003f;
+        }
+        
+        uint16_t sco_voice_setting = hci_get_sco_voice_setting();
+        if (hfp_connection->negotiated_codec == HFP_CODEC_MSBC){
+            sco_voice_setting = 0x0043; // Transparent data
+        }
+        
+        log_info("HFP: sending hci_accept_connection_request, sco_voice_setting %02x", sco_voice_setting);
+        hci_send_cmd(&hci_accept_synchronous_connection, hfp_connection->remote_addr, 8000, 8000, max_latency, 
+                        sco_voice_setting, retransmission_effort, packet_types);
+        return;
+    }
+
     if (!rfcomm_can_send_packet_now(hfp_connection->rfcomm_cid)) return;
     
     int done = hfp_hf_run_for_context_service_level_connection(hfp_connection);
@@ -824,23 +860,11 @@ static void hfp_run_for_context(hfp_connection_t * hfp_connection){
     }
 }
 
-static void hfp_init_link_settings(hfp_connection_t * hfp_connection){
-    // determine highest possible link setting
-    hfp_connection->link_setting = HFP_LINK_SETTINGS_D1;
-    if (hci_remote_esco_supported(hfp_connection->acl_handle)){
-        hfp_connection->link_setting = HFP_LINK_SETTINGS_S3;
-        if ((hfp_supported_features             & (1<<HFP_HFSF_ESCO_S4))
-        &&  (hfp_connection->remote_supported_features & (1<<HFP_AGSF_ESCO_S4))){
-            hfp_connection->link_setting = HFP_LINK_SETTINGS_S4;
-        }
-    }
-}
-
 static void hfp_ag_slc_established(hfp_connection_t * hfp_connection){
     hfp_connection->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED;
 
     hfp_emit_connection_event(hfp_callback, HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED, 0, hfp_connection->acl_handle, hfp_connection->remote_addr);
-    hfp_init_link_settings(hfp_connection);
+
     // restore volume settings
     hfp_connection->speaker_gain = hfp_hf_speaker_gain;
     hfp_connection->send_speaker_gain = 1;