From 12f314e4b8a030177e5891a178f7dea7393d6f2e Mon Sep 17 00:00:00 2001
From: Milanka Ringwald <mila@ringwald.ch>
Date: Thu, 16 Jul 2015 17:26:34 +0200
Subject: [PATCH] hfp parse indicators

---
 src/hfp.c              |   6 +-
 src/hfp.h              |  50 ++++++++++++-
 src/hfp_ag.c           |   2 +-
 src/hfp_hf.c           | 164 +++++++++++++++++++++++++++++++----------
 test/pts/hfp_hf_test.c |   1 +
 5 files changed, 180 insertions(+), 43 deletions(-)

diff --git a/src/hfp.c b/src/hfp.c
index 5f054f0e8..097c1438f 100644
--- a/src/hfp.c
+++ b/src/hfp.c
@@ -68,6 +68,8 @@ int send_str_over_rfcomm(uint16_t cid, char * command){
     int err = rfcomm_send_internal(cid, (uint8_t*) command, strlen(command));
     if (err){
         printf("rfcomm_send_internal -> error 0X%02x", err);
+    } else {
+        printf("Sent   %s", command);
     }
     return err;
 }
@@ -135,6 +137,8 @@ static hfp_connection_t * create_hfp_connection_context(){
     if (!context) return NULL;
     // init state
     context->state = HFP_IDLE;
+    context->line_size = 0;
+    
     context->negotiated_codec = HFP_Codec_CSVD;
     context->remote_supported_features = 0;
     context->remote_indicators_update_enabled = 0;
@@ -396,5 +400,3 @@ void hfp_set_codec(hfp_connection_t * context, uint8_t *packet, uint16_t size){
     }
     printf("Negotiated Codec 0x%02x\n", context->negotiated_codec);
 }
-
-
diff --git a/src/hfp.h b/src/hfp.h
index e98e7e0bf..3332d34ef 100644
--- a/src/hfp.h
+++ b/src/hfp.h
@@ -88,7 +88,7 @@ extern "C" {
 */
 #define HFP_Supported_Features "+BRSF"
 #define HFP_Available_Codecs "+BAC"
-#define HFP_Codec_Indicator "+CIND"
+#define HFP_Indicator "+CIND"
 #define HFP_Enable_Indicator_Status_Update "+CMER"
 #define HFP_Support_Call_Hold_And_Multiparty_Services "+CHLD"
 #define HFP_Generic_Status_Indicator "+BIND"
@@ -103,16 +103,33 @@ typedef enum {
     HFP_SDP_QUERY_RFCOMM_CHANNEL,
     HFP_W4_SDP_QUERY_COMPLETE,
     HFP_W4_RFCOMM_CONNECTED,
+    
     HFP_EXCHANGE_SUPPORTED_FEATURES,
+    HFP_W4_EXCHANGE_SUPPORTED_FEATURES,
+    
     HFP_NOTIFY_ON_CODECS,
+    HFP_W4_NOTIFY_ON_CODECS,
+    
     HFP_RETRIEVE_INDICATORS,
+    HFP_W4_RETRIEVE_INDICATORS,
+    
     HFP_RETRIEVE_INDICATORS_STATUS,
+    HFP_W4_RETRIEVE_INDICATORS_STATUS,
+    
     HFP_ENABLE_INDICATORS_STATUS_UPDATE,
+    HFP_W4_ENABLE_INDICATORS_STATUS_UPDATE,
+    
     HFP_RETRIEVE_CAN_HOLD_CALL,
+    HFP_W4_RETRIEVE_CAN_HOLD_CALL,
     
     HFP_LIST_GENERIC_STATUS_INDICATORS,
+    HFP_W4_LIST_GENERIC_STATUS_INDICATORS,
+    
     HFP_RETRIEVE_GENERIC_STATUS_INDICATORS,
+    HFP_W4_RETRIEVE_GENERIC_STATUS_INDICATORS,
+    
     HFP_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS,
+    HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS,
     
     HFP_ACTIVE,
     HFP_W2_DISCONNECT_RFCOMM,
@@ -120,12 +137,43 @@ typedef enum {
     HFP_W4_CONNECTION_ESTABLISHED_TO_SHUTDOWN
 } hfp_state_t;
 
+typedef enum {
+    HFP_AG_SERVICE,   /*    <value>=0 implies no service. No Home/Roam network available.
+                            <value>=1 implies presence of service. Home/Roam network available.
+                       */
+
+    HFP_AG_CALL,      /*    <value>=0 means there are no calls in progress
+                            <value>=1 means at least one call is in progress
+                       */
+
+    HFP_AG_CALLSETUP, /*    <value>=0 means not currently in call set up.
+                            <value>=1 means an incoming call process ongoing.
+                            <value>=2 means an outgoing call set up is ongoing.
+                            <value>=3 means remote party being alerted in an outgoing call.
+                       */
+    HFP_AG_CALLHELD,   /*   0 = No calls held
+                            1 = Call is placed on hold or active/held calls swapped
+                                    (The AG has both an active AND a held call) 
+                            2 = Call on hold, no active call
+                        */
+    HFP_AG_SIGNAL,      /*  ranges from 0 to 5, Signal Strength indicator */
+    HFP_AG_ROAM,        /*  <value>=0 means roaming is not active
+                            <value>=1 means a roaming is active
+                        */
+    HFP_AG_BATTCHG      /* ranges from 0 to 5, Battery Charge */
+
+} hfp_ag_indicators_t;
+
 typedef void (*hfp_callback_t)(uint8_t * event, uint16_t event_size);
 
+
 typedef struct hfp_connection {
     linked_item_t    item;
     hfp_state_t state;
 
+    uint32_t line_size;
+    uint8_t  line_buffer[20];
+
     bd_addr_t remote_addr;
     uint16_t con_handle;
     uint16_t rfcomm_channel_nr;
diff --git a/src/hfp_ag.c b/src/hfp_ag.c
index df5206ae8..a6055ae0f 100644
--- a/src/hfp_ag.c
+++ b/src/hfp_ag.c
@@ -95,7 +95,7 @@ static void hfp_run(hfp_connection_t * connection){
 }
 
 hfp_connection_t * hfp_handle_rfcomm_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
-    hfp_connection_t * context = get_hfp_connection_context_for_rfcomm_cid(channel);
+    hfp_connection_t * context = provide_hfp_connection_context_for_rfcomm_cid(channel);
     if (!context) return NULL;
     while (size > 0 && (packet[0] == '\n' || packet[0] == '\r')){
         size--;
diff --git a/src/hfp_hf.c b/src/hfp_hf.c
index 717d7b5f9..daea028e7 100644
--- a/src/hfp_hf.c
+++ b/src/hfp_hf.c
@@ -98,6 +98,7 @@ static int store_bit(uint32_t bitmap, int position, uint8_t value){
 int hfp_hs_exchange_supported_features_cmd(uint16_t cid){
     char buffer[20];
     sprintf(buffer, "AT%s=%d\r\n", HFP_Supported_Features, hfp_supported_features);
+    // printf("exchange_supported_features %s\n", buffer);
     return send_str_over_rfcomm(cid, buffer);
 }
 
@@ -105,31 +106,36 @@ int hfp_hs_retrieve_codec_cmd(uint16_t cid){
     char buffer[30];
     int buffer_offset = sprintf(buffer, "AT%s=", HFP_Available_Codecs);
     join(buffer, sizeof(buffer), buffer_offset, hfp_codecs, hfp_codecs_nr, 1);
+    // printf("retrieve_codec %s\n", buffer);
     return send_str_over_rfcomm(cid, buffer);
 }
 
 
 int hfp_hs_retrieve_indicators_cmd(uint16_t cid){
     char buffer[20];
-    sprintf(buffer, "AT%s=?\r\n", HFP_Codec_Indicator);
+    sprintf(buffer, "AT%s=?\r\n", HFP_Indicator);
+    // printf("retrieve_indicators %s\n", buffer);
     return send_str_over_rfcomm(cid, buffer);
 }
 
 int hfp_hs_retrieve_indicators_status_cmd(uint16_t cid){
     char buffer[20];
-    sprintf(buffer, "AT%s?\r\n", HFP_Codec_Indicator);
+    sprintf(buffer, "AT%s?\r\n", HFP_Indicator);
+    // printf("retrieve_indicators_status %s\n", buffer);
     return send_str_over_rfcomm(cid, buffer);
 }
 
 int hfp_hs_toggle_indicator_status_update_cmd(uint16_t cid){
     char buffer[20];
     sprintf(buffer, "AT%s\r\n", HFP_Enable_Indicator_Status_Update);
+    // printf("toggle_indicator_status_update %s\n", buffer);
     return send_str_over_rfcomm(cid, buffer);
 }
 
 int hfp_hs_retrieve_can_hold_call_cmd(uint16_t cid){
     char buffer[20];
     sprintf(buffer, "AT%s\r\n", HFP_Support_Call_Hold_And_Multiparty_Services);
+    // printf("retrieve_can_hold_call %s\n", buffer);
     return send_str_over_rfcomm(cid, buffer);
 }
 
@@ -138,18 +144,21 @@ int hfp_hs_list_supported_generic_status_indicators_cmd(uint16_t cid){
     char buffer[30];
     int buffer_offset = sprintf(buffer, "AT%s=", HFP_Generic_Status_Indicator); 
     join(buffer, sizeof(buffer), buffer_offset, hfp_indicators, hfp_indicators_nr, 2); 
+    // printf("list_supported_generic_status_indicators %s\n", buffer);
     return send_str_over_rfcomm(cid, buffer);
 }
 
 int hfp_hs_retrieve_supported_generic_status_indicators_cmd(uint16_t cid){
     char buffer[20];
     sprintf(buffer, "AT%s=?\r\rn", HFP_Generic_Status_Indicator); 
+    // printf("retrieve_supported_generic_status_indicators %s\n", buffer);
     return send_str_over_rfcomm(cid, buffer);
 }
 
 int hfp_hs_list_initital_supported_generic_status_indicators_cmd(uint16_t cid){
     char buffer[20];
     sprintf(buffer, "AT%s?\r\n", HFP_Generic_Status_Indicator);
+    // printf("list_initital_supported_generic_status_indicators %s\n", buffer);
     return send_str_over_rfcomm(cid, buffer);
 }
 
@@ -168,7 +177,6 @@ int has_hf_indicators_feature(hfp_connection_t * connection){
     return get_bit(hfp_supported_features,8) && get_bit(connection->remote_supported_features,10);
 }
 
-
 static void hfp_run_for_context(hfp_connection_t * connection){
     if (!connection) return;
     if (!rfcomm_can_send_packet_now(connection->rfcomm_cid)) return;
@@ -176,66 +184,120 @@ static void hfp_run_for_context(hfp_connection_t * connection){
     int err = 0;
     switch (connection->state){
         case HFP_EXCHANGE_SUPPORTED_FEATURES:
-            log_info("HFP_EXCHANGE_SUPPORTED_FEATURES 0x%02x", connection->rfcomm_cid);
             err = hfp_hs_exchange_supported_features_cmd(connection->rfcomm_cid);
+            if (!err) connection->state = HFP_W4_EXCHANGE_SUPPORTED_FEATURES;
             break;
         case HFP_NOTIFY_ON_CODECS:
-            log_info("HFP_NOTIFY_ON_CODECS 0x%02x", connection->rfcomm_cid);
             err = hfp_hs_retrieve_codec_cmd(connection->rfcomm_cid);
+            if (!err) connection->state = HFP_W4_NOTIFY_ON_CODECS;
             break;
         case HFP_RETRIEVE_INDICATORS:
-            log_info("HFP_RETRIEVE_INDICATORS 0x%02x", connection->rfcomm_cid);
             err = hfp_hs_retrieve_indicators_cmd(connection->rfcomm_cid);
+            if (!err) connection->state = HFP_W4_RETRIEVE_INDICATORS;
             break;
         case HFP_RETRIEVE_INDICATORS_STATUS:
-            log_info("HFP_RETRIEVE_INDICATORS_STATUS 0x%02x", connection->rfcomm_cid);
             err = hfp_hs_retrieve_indicators_cmd(connection->rfcomm_cid);
+            if (!err) connection->state = HFP_W4_EXCHANGE_SUPPORTED_FEATURES;
             break;
         case HFP_ENABLE_INDICATORS_STATUS_UPDATE:
-            err = 0;
             if (connection->remote_indicators_update_enabled == 0){
-                log_info("HFP_ENABLE_INDICATORS_STATUS_UPDATE 0x%02x", connection->rfcomm_cid);
                 err = hfp_hs_toggle_indicator_status_update_cmd(connection->rfcomm_cid);
+                if (!err) connection->state = HFP_W4_ENABLE_INDICATORS_STATUS_UPDATE;
             }
             break;
         case HFP_RETRIEVE_CAN_HOLD_CALL:
-            log_info("HFP_RETRIEVE_CAN_HOLD_CALL 0x%02x", connection->rfcomm_cid);
             err = hfp_hs_retrieve_can_hold_call_cmd(connection->rfcomm_cid);
+            if (!err) connection->state = HFP_W4_RETRIEVE_CAN_HOLD_CALL;
             break;
         case HFP_LIST_GENERIC_STATUS_INDICATORS:
-            log_info("HFP_LIST_GENERIC_STATUS_INDICATORS 0x%02x", connection->rfcomm_cid);
             err = hfp_hs_list_supported_generic_status_indicators_cmd(connection->rfcomm_cid);
+            if (!err) connection->state = HFP_W4_LIST_GENERIC_STATUS_INDICATORS;
             break;
         case HFP_RETRIEVE_GENERIC_STATUS_INDICATORS:
-            log_info("HFP_RETRIEVE_GENERIC_STATUS_INDICATORS 0x%02x", connection->rfcomm_cid);
             err = hfp_hs_retrieve_supported_generic_status_indicators_cmd(connection->rfcomm_cid);
+            if (!err) connection->state = HFP_W4_RETRIEVE_GENERIC_STATUS_INDICATORS;
             break;
         case HFP_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS:
-            log_info("HFP_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS 0x%02x", connection->rfcomm_cid);
             err = hfp_hs_list_initital_supported_generic_status_indicators_cmd(connection->rfcomm_cid);
+            if (!err) connection->state = HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS;
             break;
         default:
             break;
     }
 }
 
-hfp_connection_t * hfp_handle_rfcomm_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
-    hfp_connection_t * context = provide_hfp_connection_context_for_rfcomm_cid(channel);
-    int offset = 0;
+void hfp_parse_indicators(hfp_connection_t * context, uint8_t *packet, uint16_t size){
+    uint8_t parse_indicator = 0;
+    uint8_t parse_range = 0;
+    uint8_t parse_max_range = 0;
+    uint8_t byte;
+    char indicator[10];
+    char min_range[3];
+    char max_range[3];
 
-    if (!context) return NULL;
-    while (size > 0 && (packet[0] == '\n' || packet[0] == '\r')){
-        size--;
-        packet++;
+    int i;
+    int pos = 0;
+
+    while ( pos < size ){
+        while (!parse_indicator && pos < size){
+            byte = packet[pos++];
+            if (byte == '"'){
+                parse_indicator = 1;
+                break;
+            }
+        }
+        i = 0;
+        while (parse_indicator && pos < size){
+            byte = packet[pos++];
+            if (byte == '"'){
+                parse_indicator = 0;
+                indicator[i] = 0;
+                break;
+            } 
+            indicator[i++] = byte;
+        }
+
+        while (!parse_range && pos < size){
+            byte = packet[pos++];
+            if (byte == '('){
+                parse_range = 1;
+            }
+        }
+        i = 0;
+        while (parse_range && pos < size){
+            byte = packet[pos++];
+            if (byte == ')'){
+                parse_range = 0;
+                parse_max_range = 0;
+                max_range[i] = 0;
+                printf("Indicator %s, min %d, max %d\n", indicator, atoi(min_range), atoi(max_range));
+                break;
+            } 
+            if (byte == ','){
+                parse_max_range = 1;
+                min_range[i] = 0;
+                i = 0;
+                continue;
+            }
+            if (parse_max_range){
+                max_range[i++] = byte;
+            } else {
+                min_range[i++] = byte;
+            }
+        }
     }
-    
+}
+
+hfp_connection_t * handle_message(hfp_connection_t * context, uint8_t *packet, uint16_t size){
+    int offset = 0;
     if (context->wait_ok){
         if (strncmp((char *)packet, HFP_OK, strlen(HFP_OK)) == 0){
+            printf("Parsed %s\n", HFP_OK);
             switch (context->state){
-                case HFP_NOTIFY_ON_CODECS:
+                case HFP_W4_NOTIFY_ON_CODECS:
                     context->state = HFP_RETRIEVE_INDICATORS;
                     break;
-                case HFP_ENABLE_INDICATORS_STATUS_UPDATE:
+                case HFP_W4_ENABLE_INDICATORS_STATUS_UPDATE:
                     if (has_call_waiting_and_3way_calling_feature(context)){
                         context->state = HFP_RETRIEVE_CAN_HOLD_CALL;
                         break;
@@ -247,11 +309,11 @@ hfp_connection_t * hfp_handle_rfcomm_event(uint8_t packet_type, uint16_t channel
                     }
                     break;
 
-                case HFP_LIST_GENERIC_STATUS_INDICATORS:
+                case HFP_W4_LIST_GENERIC_STATUS_INDICATORS:
                     context->state = HFP_RETRIEVE_GENERIC_STATUS_INDICATORS;
                     break;
 
-                case HFP_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS:
+                case HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS:
                     context->state = HFP_ACTIVE;
                     printf(" state Active! \n");
                     break;
@@ -266,7 +328,7 @@ hfp_connection_t * hfp_handle_rfcomm_event(uint8_t packet_type, uint16_t channel
     if (strncmp((char *)packet, HFP_Supported_Features, strlen(HFP_Supported_Features)) == 0){
         offset = strlen(HFP_Supported_Features) + 1; // +1 for =
         context->remote_supported_features = atoi((char*)&packet[offset]);
-        
+        printf("Parsed %s: %d (expected 224)\n", HFP_Supported_Features, context->remote_supported_features);
         if (has_codec_negotiation_feature(context)){
             context->state = HFP_NOTIFY_ON_CODECS;
         } else {
@@ -276,28 +338,24 @@ hfp_connection_t * hfp_handle_rfcomm_event(uint8_t packet_type, uint16_t channel
         return context;
     }
 
-    if (strncmp((char *)packet, HFP_Codec_Indicator, strlen(HFP_Codec_Indicator)) == 0){
+    if (strncmp((char *)packet, HFP_Indicator, strlen(HFP_Indicator)) == 0){
         // https://www.bluetooth.org/en-us/specification/assigned-numbers/hands-free-profile
         /* 
          * 0x01 Enhanced Safety​, on/off
          * 0x02 Battery Level,   0-100
          */
-        offset = strlen(HFP_Codec_Indicator) + 1;
+       // printf("Parsed %s, expected  ("service",(0,1)),("call",(0,1)),("callsetup",(0,3)),("callheld",(0,2)),("signal",(0,5)),("roam",(0,1)),("battchg",(0,5))\n", HFP_Indicator);
+        
+        offset = strlen(HFP_Indicator) + 1;
         char * token = strtok((char*)&packet[offset], ",");
-       
         int pos = 0;
         switch (context->state){
-            case HFP_RETRIEVE_INDICATORS:
-                while (token){
-                    printf("%s\n", token);
-                    context->remote_indicators[pos++] = atoi(token);
-                    token = strtok(NULL, ",");
-                }
-                context->remote_indicators_nr = pos;
+            case HFP_W4_RETRIEVE_INDICATORS:
+                hfp_parse_indicators(context, &packet[offset], size-offset);
                 context->remote_indicators_status = 0;
                 context->state = HFP_RETRIEVE_INDICATORS_STATUS; 
                 break;
-            case HFP_RETRIEVE_INDICATORS_STATUS:
+            case HFP_W4_RETRIEVE_INDICATORS_STATUS:
                 while (token){
                     printf("%s\n", token);
                     store_bit(context->remote_indicators_status, pos, atoi(token));
@@ -327,7 +385,7 @@ hfp_connection_t * hfp_handle_rfcomm_event(uint8_t packet_type, uint16_t channel
        
         int pos = 0;
         switch (context->state){
-            case HFP_RETRIEVE_GENERIC_STATUS_INDICATORS:
+            case HFP_W4_RETRIEVE_GENERIC_STATUS_INDICATORS:
                 while (token){
                     printf("%s\n", token);
                     context->remote_hf_indicators[pos++] = atoi(token);
@@ -337,7 +395,7 @@ hfp_connection_t * hfp_handle_rfcomm_event(uint8_t packet_type, uint16_t channel
                 context->remote_hf_indicators_status = 0;
                 context->state = HFP_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS;
                 break;
-            case HFP_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS:{
+            case HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS:{
                 uint16_t indicator = atoi(token);
                 int status = atoi(strtok(NULL, ","));
                 if (!status) break;
@@ -360,6 +418,34 @@ hfp_connection_t * hfp_handle_rfcomm_event(uint8_t packet_type, uint16_t channel
     return context;
 }
 
+static void hfp_parse(hfp_connection_t * context, uint8_t byte){
+    if (byte != '\n' && byte != '\r'){
+        context->line_buffer[context->line_size] = byte;
+        context->line_size++;
+        return;
+    }
+
+    context->line_buffer[context->line_size] = '\0';
+    if (context->line_size > 0){
+        handle_message(context, context->line_buffer, context->line_size);
+    }
+    context->line_size = 0;
+}
+
+static void hfp_handle_rfcomm_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
+    hfp_connection_t * context = provide_hfp_connection_context_for_rfcomm_cid(channel);
+    if (!context) return;
+
+    packet[size] = 0;
+    printf("Received %s\n", packet);
+
+    int i;
+    for (i=0;i<size;i++){
+        hfp_parse(context, packet[i]);
+    }
+
+}
+
 static void hfp_run(){
     linked_item_t *it;
     for (it = hfp_get_connections(); it ; it = it->next){
diff --git a/test/pts/hfp_hf_test.c b/test/pts/hfp_hf_test.c
index b696f97bd..107ffd99f 100644
--- a/test/pts/hfp_hf_test.c
+++ b/test/pts/hfp_hf_test.c
@@ -133,6 +133,7 @@ int btstack_main(int argc, const char * argv[]){
     rfcomm_init();
     
     hfp_hf_init(rfcomm_channel_nr, HFP_Default_HF_Supported_Features, codecs, sizeof(codecs), indicators, sizeof(indicators)/sizeof(uint16_t), 1);
+    // hfp_hf_init(rfcomm_channel_nr, 0x0009, codecs, sizeof(codecs), indicators, sizeof(indicators)/sizeof(uint16_t), 1);
 
     hfp_register_packet_handler(packet_handler);