From 3099ceee29cbe0e89f50254096d709be976642fb Mon Sep 17 00:00:00 2001 From: "mila@ringwald.ch" Date: Fri, 27 Feb 2015 16:17:11 +0000 Subject: [PATCH] hsp: ag emits events, added cmd line ag test --- example/embedded/hsp_ag.c | 68 +++++++++++----- example/embedded/hsp_ag.h | 2 +- example/embedded/hsp_ag_test.c | 139 +++++++++++++++++++++++++++++++-- example/embedded/hsp_hs.h | 2 +- example/embedded/hsp_hs_test.c | 4 +- include/btstack/hci_cmds.h | 11 ++- 6 files changed, 193 insertions(+), 33 deletions(-) diff --git a/example/embedded/hsp_ag.c b/example/embedded/hsp_ag.c index 367fa0f74..651fe61b2 100644 --- a/example/embedded/hsp_ag.c +++ b/example/embedded/hsp_ag.c @@ -75,7 +75,7 @@ static const char default_hsp_ag_service_name[] = "Audio Gateway"; -static bd_addr_t remote = {0x00, 0x21, 0x3C, 0xAC, 0xF7, 0x38}; +static bd_addr_t remote; static uint8_t channel_nr = 0; static uint16_t mtu; @@ -115,6 +115,11 @@ static hsp_state_t hsp_state = HSP_IDLE; static hsp_ag_callback_t hsp_ag_callback; + +static void hsp_run(); +static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); +static void handle_query_rfcomm_event(sdp_query_event_t * event, void * context); + static void dummy_notify(uint8_t * event, uint16_t size){} void hsp_ag_register_packet_handler(hsp_ag_callback_t callback){ @@ -124,15 +129,16 @@ void hsp_ag_register_packet_handler(hsp_ag_callback_t callback){ hsp_ag_callback = callback; } -static void emit_event(uint8_t * event, uint16_t size){ +static void emit_event(uint8_t event_subtype, uint8_t value){ if (!hsp_ag_callback) return; - (*hsp_ag_callback)(event, size); + uint8_t event[4]; + event[0] = HCI_EVENT_HSP_META; + event[1] = sizeof(event) - 2; + event[2] = event_subtype; + event[3] = value; // status 0 == OK + (*hsp_ag_callback)(event, sizeof(event)); } -static void hsp_run(); -static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); -static void handle_query_rfcomm_event(sdp_query_event_t * event, void * context); - void hsp_ag_create_service(uint8_t * service, int rfcomm_channel_nr, const char * name){ uint8_t* attribute; de_create_sequence(service); @@ -411,19 +417,30 @@ static void packet_handler (void * connection, uint8_t packet_type, uint16_t cha ag_send_ok = 1; hsp_timeout_stop(); - } else if (strncmp((char *)packet, HSP_HS_AT_CKPD, 7) == 0){ - printf("Received AT+CKPD %s\n", HSP_HS_AT_CKPD); - ag_at_ckpd_received = 1; - } else 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", packet[8]); + } else if (strncmp((char *)packet, HSP_HS_MICROPHONE_GAIN, 7) == 0){ + uint8_t gain = (uint8_t)atoi((char*)&packet[8]); ag_send_ok = 1; + emit_event(HSP_SUBEVENT_MICROPHONE_GAIN_CHANGED, gain); + + } else if (strncmp((char *)packet, HSP_HS_SPEAKER_GAIN, 7) == 0){ + uint8_t gain = (uint8_t)atoi((char*)&packet[8]); + ag_send_ok = 1; + emit_event(HSP_SUBEVENT_SPEAKER_GAIN_CHANGED, gain); + + } else if (strncmp((char *)packet, HSP_HS_AT_CKPD, 7) == 0){ + ag_at_ckpd_received = 1; } else if (strncmp((char *)packet, "AT+", 3) == 0){ ag_send_error = 1; - packet[size] = 0; - printf("Received not yet supported AT command %s...\n", (char*)packet); + } + + if (ag_at_ckpd_received || ag_send_error){ + if (!hsp_ag_callback) return; + // re-use incoming buffer to avoid reserving large buffers - ugly but efficient + uint8_t * event = packet - 3; + event[0] = HCI_EVENT_HSP_META; + event[1] = size + 1; + event[2] = HSP_SUBEVENT_HS_COMMAND; + (*hsp_ag_callback)(event, size+3); } hsp_run(); return; @@ -436,10 +453,9 @@ static void packet_handler (void * connection, uint8_t packet_type, uint16_t cha switch (event) { case BTSTACK_EVENT_STATE: - // bt stack activated, get started + // BTstack activated, get started if (packet[2] == HCI_STATE_WORKING){ - printf("Try to connect.\n"); - hsp_ag_connect(remote); + printf("BTstack activated, get started .\n"); } break; @@ -495,6 +511,8 @@ static void packet_handler (void * connection, uint8_t packet_type, uint16_t cha break; } hsp_state = HSP_ACTIVE; + + emit_event(HSP_SUBEVENT_AUDIO_CONNECTION_COMPLETE, 0); break; } @@ -517,6 +535,7 @@ static void packet_handler (void * connection, uint8_t packet_type, uint16_t cha if (packet[2]) { printf("RFCOMM channel open failed, status %u\n", packet[2]); hsp_ag_reset_state(); + emit_event(HSP_SUBEVENT_AUDIO_CONNECTION_COMPLETE, packet[2]); } else { // data: event(8) , len(8), status (8), address (48), handle (16), server channel(8), rfcomm_cid(16), max frame size(16) rfcomm_handle = READ_BT_16(packet, 9); @@ -560,6 +579,7 @@ static void packet_handler (void * connection, uint8_t packet_type, uint16_t cha } printf("RFCOMM channel closed\n"); hsp_ag_reset_state(); + emit_event(HSP_SUBEVENT_AUDIO_DISCONNECTION_COMPLETE,0); break; default: break; @@ -588,7 +608,13 @@ static void handle_query_rfcomm_event(sdp_query_event_t * event, void * context) } hsp_ag_reset_state(); printf("Service not found, status %u.\n", ce->status); - exit(0); + if (ce->status){ + emit_event(HSP_SUBEVENT_AUDIO_CONNECTION_COMPLETE, ce->status); + } else { + emit_event(HSP_SUBEVENT_AUDIO_CONNECTION_COMPLETE, SDP_SERVICE_NOT_FOUND); + } + break; + default: break; } } diff --git a/example/embedded/hsp_ag.h b/example/embedded/hsp_ag.h index 9cef0d4b6..a60f740bc 100644 --- a/example/embedded/hsp_ag.h +++ b/example/embedded/hsp_ag.h @@ -59,7 +59,7 @@ void hsp_ag_create_service(uint8_t * service, int rfcomm_channel_nr, const char void hsp_ag_init(uint8_t rfcomm_channel_nr); void hsp_ag_connect(bd_addr_t bd_addr); -void hsp_ag_disconnect(bd_addr_t bd_addr); +void hsp_ag_disconnect(); void hsp_ag_enable_in_band_ring_tone(int enabled); diff --git a/example/embedded/hsp_ag_test.c b/example/embedded/hsp_ag_test.c index 449cd3ff6..bb6f58541 100644 --- a/example/embedded/hsp_ag_test.c +++ b/example/embedded/hsp_ag_test.c @@ -41,14 +41,24 @@ // // ***************************************************************************** - #include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include #include +#include #include "hci.h" #include "l2cap.h" @@ -56,12 +66,129 @@ #include "debug.h" #include "hsp_ag.h" -const uint8_t hsp_service_buffer[150]; -const uint8_t rfcomm_channel_nr = 1; -const char hsp_ag_service_name[] = "Audio Gateway Test"; +static uint8_t hsp_service_buffer[150]; +static uint8_t rfcomm_channel_nr = 1; +static uint8_t ag_speaker_gain = 0; +static uint8_t ag_microphone_gain = 0; + +static char hsp_ag_service_name[] = "Audio Gateway Test"; +static bd_addr_t bt_speaker_addr = {0x00, 0x21, 0x3C, 0xAC, 0xF7, 0x38}; + +static char hs_cmd_buffer[100]; +// prototypes +static void show_usage(); + + +// Testig User Interface +static data_source_t stdin_source; + +static uint8_t next_ag_microphone_gain(){ + ag_microphone_gain++; + if (ag_microphone_gain > 15) ag_microphone_gain = 0; + return ag_microphone_gain; +} + +static uint8_t next_ag_speaker_gain(){ + ag_speaker_gain++; + if (ag_speaker_gain > 15) ag_speaker_gain = 0; + return ag_speaker_gain; +} + +static void show_usage(){ + + printf("\n--- Bluetooth HSP AudioGateway Test Console ---\n"); + printf("---\n"); + printf("e - establish audio connection to Bluetooth Speaker\n"); + printf("r - release audio connection from Bluetooth Speaker\n"); + printf("m - set microphone gain\n"); + printf("s - set speaker gain\n"); + printf("---\n"); + printf("Ctrl-c - exit\n"); + printf("---\n"); +} + +static int stdin_process(struct data_source *ds){ + char buffer; + read(ds->fd, &buffer, 1); + + switch (buffer){ + case 'e': + printf("Establishing audio connection to Bluetooth Speaker %s...\n", bd_addr_to_str(bt_speaker_addr)); + hsp_ag_connect(bt_speaker_addr); + break; + case 'r': + printf("Releasing audio connection from Bluetooth Speaker %s...\n", bd_addr_to_str(bt_speaker_addr)); + hsp_ag_disconnect(); + break; + case 'm': + printf("Setting microphone gain\n"); + hsp_ag_set_microphone_gain(next_ag_microphone_gain()); + break; + case 's': + printf("Setting speaker gain\n"); + hsp_ag_set_speaker_gain(next_ag_speaker_gain()); + break; + default: + show_usage(); + break; + + } + return 0; +} + +static void setup_cli(){ + + struct termios term = {0}; + if (tcgetattr(0, &term) < 0) + perror("tcsetattr()"); + term.c_lflag &= ~ICANON; + term.c_lflag &= ~ECHO; + term.c_cc[VMIN] = 1; + term.c_cc[VTIME] = 0; + if (tcsetattr(0, TCSANOW, &term) < 0) + perror("tcsetattr ICANON"); + + stdin_source.fd = 0; // stdin + stdin_source.process = &stdin_process; + run_loop_add_data_source(&stdin_source); + + show_usage(); +} + +// Audio Gateway routines void packet_handler(uint8_t * event, uint16_t event_size){ - + switch (event[2]) { + case HSP_SUBEVENT_AUDIO_CONNECTION_COMPLETE: + if (event[3] == 0){ + printf("Audio connection established.\n"); + } else { + printf("Audio connection establishment failed with status %u\n", event[3]); + } + break; + case HSP_SUBEVENT_AUDIO_DISCONNECTION_COMPLETE: + if (event[3] == 0){ + printf("Audio connection released.\n"); + } else { + printf("Audio connection releasing failed with status %u\n", event[3]); + } + break; + case HSP_SUBEVENT_MICROPHONE_GAIN_CHANGED: + printf("Received microphone gain change %d\n", event[3]); + break; + case HSP_SUBEVENT_SPEAKER_GAIN_CHANGED: + printf("Received speaker gain change %d\n", event[3]); + break; + case HSP_SUBEVENT_HS_COMMAND:{ + memset(hs_cmd_buffer, 0, sizeof(hs_cmd_buffer)); + int size = event_size <= sizeof(hs_cmd_buffer)? event_size : sizeof(hs_cmd_buffer); + memcpy(hs_cmd_buffer, &event[3], size - 1); + printf("Received command %s\n", hs_cmd_buffer); + break; + } + default: + break; + } } int btstack_main(int argc, const char * argv[]){ @@ -76,6 +203,8 @@ int btstack_main(int argc, const char * argv[]){ hsp_ag_register_packet_handler(packet_handler); // turn on! hci_power_control(HCI_POWER_ON); + + setup_cli(); // go! run_loop_execute(); return 0; diff --git a/example/embedded/hsp_hs.h b/example/embedded/hsp_hs.h index e42ad0c69..d1e7771d2 100644 --- a/example/embedded/hsp_hs.h +++ b/example/embedded/hsp_hs.h @@ -59,7 +59,7 @@ void hsp_hs_create_service(uint8_t * service, int rfcomm_channel_nr, const char void hsp_hs_init(uint8_t rfcomm_channel_nr); void hsp_hs_connect(bd_addr_t bd_addr); -void hsp_hs_disconnect(bd_addr_t bd_addr); +void hsp_hs_disconnect(); // AT+VGM=[0..15] void hsp_hs_set_microphone_gain(uint8_t gain); diff --git a/example/embedded/hsp_hs_test.c b/example/embedded/hsp_hs_test.c index ccff7872a..2889e6644 100644 --- a/example/embedded/hsp_hs_test.c +++ b/example/embedded/hsp_hs_test.c @@ -61,7 +61,9 @@ const uint8_t hsp_service_buffer[150]; const uint8_t rfcomm_channel_nr = 1; const char hsp_ag_service_name[] = "Headset Test"; -void packet_handler(uint8_t * event, uint16_t event_size){} +void packet_handler(uint8_t * event, uint16_t event_size){ + +} int btstack_main(int argc, const char * argv[]){ // init SDP, create record for SPP and register with SDP diff --git a/include/btstack/hci_cmds.h b/include/btstack/hci_cmds.h index 1a629e40d..cb95c58ef 100644 --- a/include/btstack/hci_cmds.h +++ b/include/btstack/hci_cmds.h @@ -591,12 +591,14 @@ extern "C" { #define HCI_EVENT_HSP_META 0xE8 -#define HSP_SUBEVENT_AUDIO_CONNECTION_ESTABLISHED 0x01 -#define HSP_SUBEVENT_AUDIO_CONNECTION_CLOSED 0x02 +#define HSP_SUBEVENT_AUDIO_CONNECTION_COMPLETE 0x01 +#define HSP_SUBEVENT_AUDIO_DISCONNECTION_COMPLETE 0x02 #define HSP_SUBEVENT_MICROPHONE_GAIN_CHANGED 0x03 #define HSP_SUBEVENT_SPEAKER_GAIN_CHANGED 0x04 -#define HSP_SUBEVENT_COMMAND 0x05 -#define HSP_SUBEVENT_INDICATION 0x06 +#define HSP_SUBEVENT_HS_COMMAND 0x05 +#define HSP_SUBEVENT_AG_INDICATION 0x06 +#define HSP_SUBEVENT_ERROR 0x07 +#define HSP_SUBEVENT_RING 0x08 // ANCS Client @@ -661,6 +663,7 @@ extern "C" { #define SDP_HANDLE_ALREADY_REGISTERED 0x80 #define SDP_QUERY_INCOMPLETE 0x81 +#define SDP_SERVICE_NOT_FOUND 0x82 #define ATT_HANDLE_VALUE_INDICATION_IN_PORGRESS 0x90 #define ATT_HANDLE_VALUE_INDICATION_TIMEOUT 0x91