From 40063d03c263d7d0529d61ffd764fff8608d21f3 Mon Sep 17 00:00:00 2001
From: "mila@ringwald.ch"
 <mila@ringwald.ch@1a0a8af8-31b5-11de-8e0c-53a27eea117e>
Date: Fri, 13 Feb 2015 16:23:52 +0000
Subject: [PATCH] hsp: support for microphone and speaker

---
 example/embedded/hsp_test.c | 156 ++++++++++++++++++++++++++++++------
 1 file changed, 131 insertions(+), 25 deletions(-)

diff --git a/example/embedded/hsp_test.c b/example/embedded/hsp_test.c
index 01c2bd3e4..301c8ae80 100644
--- a/example/embedded/hsp_test.c
+++ b/example/embedded/hsp_test.c
@@ -59,35 +59,127 @@
 #include "sdp_query_rfcomm.h"
 #include "debug.h"
 
+#define HSP_HS_BUTTON_PRESS "AT+CKPD=200\r"
+#define HSP_AG_OK "\r\nOK\r\n"
+#define HSP_AG_ERROR "\r\nERROR\r\n"
+#define HSP_AG_RING "\r\nRING\r\n"
+#define HSP_MICROPHONE_GAIN "+VGM"
+#define HSP_SPEAKER_GAIN "+VGS"
+
+#define HSP_HS_MICROPHONE_GAIN "AT+VGM="
+#define HSP_HS_SPEAKER_GAIN "AT+VGS="
+
 static bd_addr_t remote = {0x00, 0x21, 0x3C, 0xAC, 0xF7, 0x38};
 static uint8_t channel_nr = 0;
 
 static uint16_t mtu;
 static uint16_t rfcomm_cid = 0;
 
-static uint8_t connection_state = 0;
+// static uint8_t connection_state = 0;
+
+static int ag_microphone_gain = -1;
+static int ag_speaker_gain = -1;
+static uint8_t ag_ring = 0;
+static uint8_t ag_send_ok = 0;
+static uint8_t ag_send_error = 0;
+
+static void hsp_run();
+
+typedef enum {
+    HSP_AG_IDLE,
+    HSP_AG_START_SDP_RFCOOM_QUERY_FOR_UUID,
+    HSP_W4_SCO_OPENED,
+    HSP_AG_ACTIVE
+} hsp_state_t;
+
+static hsp_state_t hsp_state = HSP_AG_IDLE;
 
 // remote audio volume control
-static void hsp_set_microphone_gain(){}; // +VGM=13 [0..15]
-static void hsp_set_speaker_gain(){};    // +VGS=5  [0..15]
+// AG +VGM=13 [0..15] ; HS AT+VGM=6 | AG OK
 
-static void send_str_over_rfcomm(uint16_t cid, char * command){
-    printf("Send %s.\n", command);
+void hsp_connect(bd_addr_t bd_addr){
+    if (hsp_state != HSP_AG_IDLE) return;
+    hsp_state = HSP_AG_START_SDP_RFCOOM_QUERY_FOR_UUID;
+    memcpy(remote, bd_addr, 6);
+    hsp_run();
+}
+
+void hsp_ring(){
+    if (hsp_state != HSP_AG_ACTIVE) return;
+
+}
+
+void hsp_ag_set_microphone_gain(uint8_t gain){
+    if (gain < 0 || gain >15) {
+        printf("Gain must be in interval [0..15], it is given %d\n", gain);
+        return; 
+    }
+    ag_microphone_gain = gain;
+    hsp_run();
+}; 
+
+// AG +VGS=5  [0..15] ; HS AT+VGM=6 | AG OK
+void hsp_ag_set_speaker_gain(uint8_t gain){
+    if (gain < 0 || gain >15) {
+        printf("Gain must be in interval [0..15], it is given %d\n", gain);
+        return; 
+    }
+    ag_speaker_gain = gain;
+    hsp_run();
+};    
+
+static int send_str_over_rfcomm(uint16_t cid, char * command){
+    if (!rfcomm_can_send_packet_now(rfcomm_cid)) return 1;
     int err = rfcomm_send_internal(cid, (uint8_t*) command, strlen(command));
     if (err){
         printf("rfcomm_send_internal -> error 0X%02x", err);
     }
+    return err;
 }
 
-static void send_packet(){
-    switch (connection_state){
-        case 1:
-            send_str_over_rfcomm(rfcomm_cid, "\r\nRING\r\n");
-            connection_state++;
+
+static void hsp_run(){
+    int err;
+
+    switch (hsp_state){
+        case HSP_AG_START_SDP_RFCOOM_QUERY_FOR_UUID:
+            printf("Start SDP RFCOMM Query for UUID 0x%02x\n", SDP_HSP);
+            sdp_query_rfcomm_channel_and_name_for_uuid(remote, SDP_HSP);
+            hsp_state = HSP_W4_SCO_OPENED;
             break;
-        case 3:
-            send_str_over_rfcomm(rfcomm_cid, "\r\nOK\r\n");
-            connection_state++;
+        
+        case HSP_AG_ACTIVE:
+            if (ag_send_ok){
+                err = send_str_over_rfcomm(rfcomm_cid, HSP_AG_OK);
+                if (!err) ag_send_ok = 0;
+                break;
+            }
+            if (ag_send_error){
+                err = send_str_over_rfcomm(rfcomm_cid, HSP_AG_ERROR);
+                if (!err) ag_send_error = 0;
+                break;
+            }
+            if (ag_ring){
+                err = send_str_over_rfcomm(rfcomm_cid, HSP_AG_RING);
+                if (!err) ag_ring = 0;
+                break;
+            }
+            
+            if (ag_microphone_gain >= 0){
+                char buffer[10];
+                sprintf(buffer, "%s=%d\r", HSP_MICROPHONE_GAIN, ag_microphone_gain);
+                err = send_str_over_rfcomm(rfcomm_cid, buffer);
+                if (!err) ag_microphone_gain = -1;
+                break;
+            }
+
+            if (ag_speaker_gain >= 0){
+                char buffer[10];
+                sprintf(buffer, "%s=%d\r", HSP_SPEAKER_GAIN, ag_speaker_gain);
+                err = send_str_over_rfcomm(rfcomm_cid, buffer);
+                if (!err) ag_speaker_gain = -1;
+                break;
+            }
             break;
         default:
             break;
@@ -98,12 +190,27 @@ static void send_packet(){
 static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
     // printf("packet_handler type %u, packet[0] %x\n", packet_type, packet[0]);
     if (packet_type == RFCOMM_DATA_PACKET){
-        if (strncmp((char *)packet, "AT+CKPD", 7) == 0){
-            printf("Received AT+CKPD\n");
-            connection_state++;
-            if (rfcomm_can_send_packet_now(rfcomm_cid)) send_packet();
+        if (strncmp((char *)packet, HSP_HS_BUTTON_PRESS, strlen(HSP_HS_BUTTON_PRESS)) == 0){
+            printf("Received button press %s\n", HSP_HS_BUTTON_PRESS);
+            ag_send_ok = 1;
+            hsp_run();
+            return;
+        } 
+        if (strncmp((char *)packet, HSP_HS_MICROPHONE_GAIN, 7) == 0 ||
+            strncmp((char *)packet, HSP_HS_SPEAKER_GAIN, 7) == 0){
+            // uint8_t gain = packet[8];
+            // TODO: parse gain
+            // printf("Received changed gain info %c\n", gain);
+            ag_send_ok = 1;
+            hsp_run();
+            return;
         }
-        return;   
+        //ag_send_error = 1;
+        if (strncmp((char *)packet, "AT+", 3) == 0){
+            log_info("Received not yet supported AT command\n");
+        }
+        //hsp_run();
+        return;
     }
 
     if (packet_type != HCI_EVENT_PACKET) return;
@@ -115,8 +222,7 @@ static void packet_handler (void * connection, uint8_t packet_type, uint16_t cha
         case BTSTACK_EVENT_STATE:
             // bt stack activated, get started 
             if (packet[2] == HCI_STATE_WORKING){
-                printf("Start SDP RFCOMM Query for UUID 0x%02x\n", SDP_HSP);
-                sdp_query_rfcomm_channel_and_name_for_uuid(remote, SDP_HSP);
+                hsp_connect(remote);
             }
             break;
 
@@ -168,9 +274,8 @@ static void packet_handler (void * connection, uint8_t packet_type, uint16_t cha
                  " rx_packet_length %u bytes, tx_packet_length %u bytes, air_mode 0x%2x (0x02 == CVSD)", connection_handle,
                  bd_addr_to_str(address), transmission_interval, retransmission_interval, rx_packet_length, tx_packet_length, air_mode);
 
-            // Send "RING"
-            connection_state++;
-            if (rfcomm_can_send_packet_now(rfcomm_cid)) send_packet();
+            hsp_state = HSP_AG_ACTIVE;
+            hsp_run();
             break;                
         }
 
@@ -201,7 +306,7 @@ static void packet_handler (void * connection, uint8_t packet_type, uint16_t cha
         case DAEMON_EVENT_HCI_PACKET_SENT:
         case RFCOMM_EVENT_CREDITS:
             if (!rfcomm_cid) break;
-            if (rfcomm_can_send_packet_now(rfcomm_cid)) send_packet();
+            hsp_run();
             break;
         default:
             break;
@@ -226,7 +331,8 @@ void handle_query_rfcomm_event(sdp_query_event_t * event, void * context){
                 rfcomm_create_channel_internal(NULL, &remote, channel_nr); 
                 break;
             }
-            printf("Service not found.\n");
+
+            printf("Service not found, status %u.\n", ce->status);
             exit(0);
             break;
     }