From 2c868da208a5b6086c3917c6a0721d593f61f19a Mon Sep 17 00:00:00 2001 From: Milanka Ringwald Date: Thu, 30 Jul 2015 17:03:11 +0200 Subject: [PATCH] hfp: crude impl of hfp ag functions --- src/hfp.c | 138 +++++++++++++++++++- src/hfp.h | 30 +++-- src/hfp_ag.c | 288 +++++++++++++++++------------------------ src/hfp_ag.h | 9 +- src/hfp_hf.c | 150 ++------------------- test/pts/hfp_ag_test.c | 36 ++++-- 6 files changed, 315 insertions(+), 336 deletions(-) diff --git a/src/hfp.c b/src/hfp.c index c545df22a..4b0abae3e 100644 --- a/src/hfp.c +++ b/src/hfp.c @@ -186,8 +186,8 @@ static hfp_connection_t * create_hfp_connection_context(){ context->negotiated_codec = HFP_CODEC_CVSD; context->remote_supported_features = 0; context->remote_indicators_update_enabled = 0; - context->remote_indicators_nr = 0; - context->remote_indicators_status = 0; + + context->ag_indicators_nr = 0; linked_list_add(&hfp_connections, (linked_item_t*)context); return context; @@ -411,6 +411,140 @@ hfp_connection_t * hfp_handle_hci_event(uint8_t packet_type, uint8_t *packet, ui return context; } +void update_command(hfp_connection_t * context); + +void hfp_parse(hfp_connection_t * context, uint8_t byte){ + int i; + if (byte == ' ') return; + if ( (byte == '\n' || byte == '\r') && context->parser_state > HFP_PARSER_CMD_SEQUENCE) return; + + switch (context->parser_state){ + case HFP_PARSER_CMD_HEADER: // header + if (byte == ':'){ + context->parser_state = HFP_PARSER_CMD_SEQUENCE; + context->line_buffer[context->line_size] = 0; + context->line_size = 0; + update_command(context); + return; + } + if (byte == '\n' || byte == '\r'){ + // received OK + context->line_buffer[context->line_size] = 0; + context->line_size = 0; + update_command(context); + return; + } + context->line_buffer[context->line_size++] = byte; + break; + + case HFP_PARSER_CMD_SEQUENCE: // parse comma separated sequence, ignore breacktes + if (byte == '"'){ // indicators + context->parser_state = HFP_PARSER_CMD_INDICATOR_NAME; + context->line_size = 0; + break; + } + + if (byte == '(' ){ // tuple separated mit comma + break; + } + + if (byte == ',' || byte == '\n' || byte == '\r' || byte == ')'){ + context->line_buffer[context->line_size] = 0; + context->line_size = 0; + switch (context->state){ + case HFP_W4_EXCHANGE_SUPPORTED_FEATURES: + context->remote_supported_features = atoi((char *)&context->line_buffer[0]); + for (i=0; i<16; i++){ + if (get_bit(context->remote_supported_features,i)){ + printf("AG supported feature: %s\n", hfp_ag_feature(i)); + } + } + context->command = HFP_CMD_NONE; + context->parser_state = HFP_PARSER_CMD_HEADER; + break; + case HFP_W4_RETRIEVE_INDICATORS: + break; + case HFP_W4_RETRIEVE_INDICATORS_STATUS: + printf("Indicator with status: %s\n", context->line_buffer); + break; + case HFP_W4_RETRIEVE_CAN_HOLD_CALL: + printf("Support call hold: %s\n", context->line_buffer); + break; + case HFP_W4_RETRIEVE_GENERIC_STATUS_INDICATORS: // comma separated ints + printf("Generic status indicator: %s\n", context->line_buffer); + break; + case HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS: + printf("Generic status indicator: %s, ", context->line_buffer); + context->parser_state = HFP_PARSER_CMD_INITITAL_STATE_GENERIC_STATUS_INDICATORS; + break; + default: + break; + } + if (byte == '\n' || byte == '\r'){ + context->command = HFP_CMD_NONE; + context->parser_state = HFP_PARSER_CMD_HEADER; + break; + } + if (byte == ')' && context->state == HFP_W4_RETRIEVE_CAN_HOLD_CALL){ // tuple separated mit comma + context->command = HFP_CMD_NONE; + context->parser_state = HFP_PARSER_CMD_HEADER; + break; + } + break; + } + context->line_buffer[context->line_size++] = byte; + break; + case HFP_PARSER_CMD_INITITAL_STATE_GENERIC_STATUS_INDICATORS: + if (byte == '\n' || byte == '\r'){ + context->line_buffer[context->line_size] = 0; + context->line_size = 0; + context->command = HFP_CMD_NONE; + context->parser_state = HFP_PARSER_CMD_HEADER; + printf("status %s [0-dissabled, 1-enabled]\n", context->line_buffer); + break; + } + break; + case HFP_PARSER_CMD_INDICATOR_NAME: // parse indicator name + if (byte == '"'){ + context->line_buffer[context->line_size] = 0; + printf("Indicator %d: %s (", context->ag_indicators_nr, context->line_buffer); + context->line_size = 0; + break; + } + if (byte == '('){ // parse indicator range + context->parser_state = HFP_PARSER_CMD_INDICATOR_MIN_RANGE; + break; + } + context->line_buffer[context->line_size++] = byte; + break; + case HFP_PARSER_CMD_INDICATOR_MIN_RANGE: + if (byte == ',' || byte == '-'){ // end min_range + context->parser_state = HFP_PARSER_CMD_INDICATOR_MAX_RANGE; + context->line_buffer[context->line_size] = 0; + printf("%d, ", atoi((char *)&context->line_buffer[0])); + context->line_size = 0; + break; + } + // min. range + context->line_buffer[context->line_size++] = byte; + break; + case HFP_PARSER_CMD_INDICATOR_MAX_RANGE: + if (byte == ')'){ // end max_range + context->parser_state = HFP_PARSER_CMD_SEQUENCE; + + context->line_buffer[context->line_size] = 0; + printf("%d)\n", atoi((char *)&context->line_buffer[0])); + context->line_size = 0; + context->ag_indicators_nr+=1; + break; + } + // + context->line_buffer[context->line_size++] = byte; + break; + + } +} + void hfp_init(uint16_t rfcomm_channel_nr){ rfcomm_register_service_internal(NULL, rfcomm_channel_nr, 0xffff); sdp_query_rfcomm_register_callback(handle_query_rfcomm_event, NULL); diff --git a/src/hfp.h b/src/hfp.h index f80c9ee2a..c53c52b7b 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -97,8 +97,9 @@ extern "C" { #define HFP_DEFAULT_AG_SUPPORTED_FEATURES 0x0009 #define HFP_MAX_NUM_CODECS 20 -#define HFP_MAX_NUM_INDICATORS 20 -#define HFP_MAX_INDICATOR_DESC_SIZE 200 // TODO: change to 10 +#define HFP_MAX_NUM_AG_INDICATORS 20 +#define HFP_MAX_NUM_HF_INDICATORS 20 +#define HFP_MAX_INDICATOR_DESC_SIZE 10 #define HFP_SUPPORTED_FEATURES "+BRSF" #define HFP_AVAILABLE_CODECS "+BAC" @@ -175,6 +176,19 @@ typedef enum { typedef void (*hfp_callback_t)(uint8_t * event, uint16_t event_size); +typedef struct{ + uint16_t uuid; + uint8_t state; + uint8_t initial_state; +} hfp_hf_indicator_t; + +typedef struct{ + uint8_t index; + char name[HFP_MAX_INDICATOR_DESC_SIZE]; + uint8_t min_range; + uint8_t max_range; + uint8_t status; +} hfp_ag_indicator_t; typedef struct hfp_connection { linked_item_t item; @@ -191,20 +205,13 @@ typedef struct hfp_connection { uint16_t rfcomm_channel_nr; uint16_t rfcomm_cid; + int ag_indicators_nr; + // Retrieved during connection setup, not used yet uint8_t negotiated_codec; uint32_t remote_supported_features; uint8_t remote_indicators_update_enabled; - int remote_indicators_nr; - char remote_indicators[20][HFP_MAX_INDICATOR_DESC_SIZE]; - int remote_indicators_range[20][2]; - - uint32_t remote_indicators_status; - - uint8_t remote_hf_indicators_nr; - uint16_t remote_hf_indicators[20]; - uint32_t remote_hf_indicators_status; hfp_callback_t callback; } hfp_connection_t; @@ -218,6 +225,7 @@ void hfp_connect(bd_addr_t bd_addr, uint16_t service_uuid); hfp_connection_t * provide_hfp_connection_context_for_rfcomm_cid(uint16_t cid); linked_list_t * hfp_get_connections(); +void hfp_parse(hfp_connection_t * context, uint8_t byte); // TODO: move to utils int send_str_over_rfcomm(uint16_t cid, char * command); diff --git a/src/hfp_ag.c b/src/hfp_ag.c index 706b50d2c..d27563545 100644 --- a/src/hfp_ag.c +++ b/src/hfp_ag.c @@ -66,9 +66,14 @@ static uint16_t hfp_supported_features = HFP_DEFAULT_AG_SUPPORTED_FEATURES; static uint8_t hfp_codecs_nr = 0; static uint8_t hfp_codecs[HFP_MAX_NUM_CODECS]; -static uint8_t hfp_indicators_nr = 0; -static uint8_t hfp_indicators[HFP_MAX_NUM_INDICATORS]; -static uint8_t hfp_indicators_status; +static uint8_t hfp_hf_indicators_nr = 0; +static hfp_hf_indicator_t hfp_hf_indicators[HFP_MAX_NUM_HF_INDICATORS]; + +static int hfp_ag_indicators_nr = 0; +static hfp_ag_indicator_t hfp_ag_indicators[HFP_MAX_NUM_AG_INDICATORS]; + +static int hfp_ag_call_hold_services_nr = 0; +static char *hfp_ag_call_hold_services[6]; static void packet_handler(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); @@ -93,66 +98,130 @@ int hfp_ag_exchange_supported_features_cmd(uint16_t cid){ return send_str_over_rfcomm(cid, buffer); } +int hfp_ag_ok(uint16_t cid){ + char buffer[5]; + sprintf(buffer, "OK\r\n"); + return send_str_over_rfcomm(cid, buffer); +} + +int hfp_ag_retrieve_codec_cmd(uint16_t cid){ + return hfp_ag_ok(cid); +} + +int hfp_ag_indicators_join(char * buffer, int buffer_size){ + if (buffer_size < hfp_ag_indicators_nr * (1 + sizeof(hfp_ag_indicator_t))) return 0; + int i; + int offset = 0; + for (i = 0; i < hfp_ag_indicators_nr-1; i++) { + offset += snprintf(buffer+offset, buffer_size-offset, "\"%s\",(%d,%d),", hfp_ag_indicators[i].name, hfp_ag_indicators[i].min_range, hfp_ag_indicators[i].max_range);; + } + if (iparser_state > HFP_PARSER_CMD_SEQUENCE) return; - - switch (context->parser_state){ - case HFP_PARSER_CMD_HEADER: // header - if (byte == ':'){ - context->parser_state = HFP_PARSER_CMD_SEQUENCE; - context->line_buffer[context->line_size] = 0; - context->line_size = 0; - update_command(context); - return; - } - if (byte == '\n' || byte == '\r'){ - // received OK - context->line_buffer[context->line_size] = 0; - context->line_size = 0; - update_command(context); - return; - } - context->line_buffer[context->line_size++] = byte; - break; - - case HFP_PARSER_CMD_SEQUENCE: // parse comma separated sequence, ignore breacktes - if (byte == '"'){ // indicators - context->parser_state = HFP_PARSER_CMD_INDICATOR_NAME; - context->line_size = 0; - break; - } - - if (byte == '(' ){ // tuple separated mit comma - break; - } - - if (byte == ',' || byte == '\n' || byte == '\r' || byte == ')'){ - context->line_buffer[context->line_size] = 0; - context->line_size = 0; - switch (context->state){ - case HFP_W4_EXCHANGE_SUPPORTED_FEATURES: - context->remote_supported_features = atoi((char *)&context->line_buffer[0]); - for (i=0; i<16; i++){ - if (get_bit(context->remote_supported_features,i)){ - printf("AG supported feature: %s\n", hfp_ag_feature(i)); - } - } - context->command = HFP_CMD_NONE; - context->parser_state = HFP_PARSER_CMD_HEADER; - break; - case HFP_W4_RETRIEVE_INDICATORS: - break; - case HFP_W4_RETRIEVE_INDICATORS_STATUS: - printf("Indicator with status: %s\n", context->line_buffer); - break; - case HFP_W4_RETRIEVE_CAN_HOLD_CALL: - printf("Support call hold: %s\n", context->line_buffer); - break; - case HFP_W4_RETRIEVE_GENERIC_STATUS_INDICATORS: // comma separated ints - printf("Generic status indicator: %s\n", context->line_buffer); - break; - case HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS: - printf("Generic status indicator: %s, ", context->line_buffer); - context->parser_state = HFP_PARSER_CMD_INITITAL_STATE_GENERIC_STATUS_INDICATORS; - break; - default: - break; - } - if (byte == '\n' || byte == '\r'){ - context->command = HFP_CMD_NONE; - context->parser_state = HFP_PARSER_CMD_HEADER; - break; - } - if (byte == ')' && context->state == HFP_W4_RETRIEVE_CAN_HOLD_CALL){ // tuple separated mit comma - context->command = HFP_CMD_NONE; - context->parser_state = HFP_PARSER_CMD_HEADER; - break; - } - break; - } - context->line_buffer[context->line_size++] = byte; - break; - case HFP_PARSER_CMD_INITITAL_STATE_GENERIC_STATUS_INDICATORS: - if (byte == '\n' || byte == '\r'){ - context->line_buffer[context->line_size] = 0; - context->line_size = 0; - context->command = HFP_CMD_NONE; - context->parser_state = HFP_PARSER_CMD_HEADER; - printf("status %s [0-dissabled, 1-enabled]\n", context->line_buffer); - break; - } - break; - case HFP_PARSER_CMD_INDICATOR_NAME: // parse indicator name - if (byte == '"'){ - context->line_buffer[context->line_size] = 0; - printf("Indicator %d: %s (", context->remote_indicators_nr, context->line_buffer); - context->line_size = 0; - break; - } - if (byte == '('){ // parse indicator range - context->parser_state = HFP_PARSER_CMD_INDICATOR_MIN_RANGE; - break; - } - context->line_buffer[context->line_size++] = byte; - break; - case HFP_PARSER_CMD_INDICATOR_MIN_RANGE: - if (byte == ',' || byte == '-'){ // end min_range - context->parser_state = HFP_PARSER_CMD_INDICATOR_MAX_RANGE; - context->line_buffer[context->line_size] = 0; - printf("%d, ", atoi((char *)&context->line_buffer[0])); - context->line_size = 0; - break; - } - // min. range - context->line_buffer[context->line_size++] = byte; - break; - case HFP_PARSER_CMD_INDICATOR_MAX_RANGE: - if (byte == ')'){ // end max_range - context->parser_state = HFP_PARSER_CMD_SEQUENCE; - - context->line_buffer[context->line_size] = 0; - printf("%d)\n", atoi((char *)&context->line_buffer[0])); - context->line_size = 0; - context->remote_indicators_nr+=1; - break; - } - // - context->line_buffer[context->line_size++] = byte; - break; - }*/ -} - void handle_switch_on_ok(hfp_connection_t *context){ printf("handle switch on OK\n"); switch (context->state){ + default: break; } @@ -429,7 +368,11 @@ static void packet_handler(void * connection, uint8_t packet_type, uint16_t chan hfp_run(); } -void hfp_ag_init(uint16_t rfcomm_channel_nr, uint32_t supported_features, uint8_t * codecs, int codecs_nr, uint16_t * indicators, int indicators_nr, uint32_t indicators_status){ +void hfp_ag_init(uint16_t rfcomm_channel_nr, uint32_t supported_features, + uint8_t * codecs, int codecs_nr, + hfp_ag_indicator_t * ag_indicators, int ag_indicators_nr, + hfp_hf_indicator_t * hf_indicators, int hf_indicators_nr, + char *call_hold_services[], int call_hold_services_nr){ if (codecs_nr > HFP_MAX_NUM_CODECS){ log_error("hfp_init: codecs_nr (%d) > HFP_MAX_NUM_CODECS (%d)", codecs_nr, HFP_MAX_NUM_CODECS); return; @@ -445,11 +388,14 @@ void hfp_ag_init(uint16_t rfcomm_channel_nr, uint32_t supported_features, uint8_ hfp_codecs[i] = codecs[i]; } - hfp_indicators_nr = indicators_nr; - hfp_indicators_status = indicators_status; - for (i=0; iparser_state = HFP_PARSER_CMD_INDICATOR_NAME; - context->line_size = 0; - break; - } - - if (byte == '(' ){ // tuple separated mit comma - break; - } - - if (byte == ',' || byte == '\n' || byte == '\r' || byte == ')'){ - context->line_buffer[context->line_size] = 0; - context->line_size = 0; - switch (context->state){ - case HFP_W4_EXCHANGE_SUPPORTED_FEATURES: - context->remote_supported_features = atoi((char *)&context->line_buffer[0]); - for (i=0; i<16; i++){ - if (get_bit(context->remote_supported_features,i)){ - printf("AG supported feature: %s\n", hfp_ag_feature(i)); - } - } - context->command = HFP_CMD_NONE; - context->parser_state = HFP_PARSER_CMD_HEADER; - break; - case HFP_W4_RETRIEVE_INDICATORS: - break; - case HFP_W4_RETRIEVE_INDICATORS_STATUS: - printf("Indicator with status: %s\n", context->line_buffer); - break; - case HFP_W4_RETRIEVE_CAN_HOLD_CALL: - printf("Support call hold: %s\n", context->line_buffer); - break; - case HFP_W4_RETRIEVE_GENERIC_STATUS_INDICATORS: // comma separated ints - printf("Generic status indicator: %s\n", context->line_buffer); - break; - case HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS: - printf("Generic status indicator: %s, ", context->line_buffer); - context->parser_state = HFP_PARSER_CMD_INITITAL_STATE_GENERIC_STATUS_INDICATORS; - break; - default: - break; - } - if (byte == '\n' || byte == '\r'){ - context->command = HFP_CMD_NONE; - context->parser_state = HFP_PARSER_CMD_HEADER; - break; - } - if (byte == ')' && context->state == HFP_W4_RETRIEVE_CAN_HOLD_CALL){ // tuple separated mit comma - context->command = HFP_CMD_NONE; - context->parser_state = HFP_PARSER_CMD_HEADER; - break; - } - break; - } - context->line_buffer[context->line_size++] = byte; - break; - case HFP_PARSER_CMD_INITITAL_STATE_GENERIC_STATUS_INDICATORS: - if (byte == '\n' || byte == '\r'){ - context->line_buffer[context->line_size] = 0; - context->line_size = 0; - context->command = HFP_CMD_NONE; - context->parser_state = HFP_PARSER_CMD_HEADER; - printf("status %s [0-dissabled, 1-enabled]\n", context->line_buffer); - break; - } - break; - case HFP_PARSER_CMD_INDICATOR_NAME: // parse indicator name - if (byte == '"'){ - context->line_buffer[context->line_size] = 0; - printf("Indicator %d: %s (", context->remote_indicators_nr, context->line_buffer); - context->line_size = 0; - break; - } - if (byte == '('){ // parse indicator range - context->parser_state = HFP_PARSER_CMD_INDICATOR_MIN_RANGE; - break; - } - context->line_buffer[context->line_size++] = byte; - break; - case HFP_PARSER_CMD_INDICATOR_MIN_RANGE: - if (byte == ',' || byte == '-'){ // end min_range - context->parser_state = HFP_PARSER_CMD_INDICATOR_MAX_RANGE; - context->line_buffer[context->line_size] = 0; - printf("%d, ", atoi((char *)&context->line_buffer[0])); - context->line_size = 0; - break; - } - // min. range - context->line_buffer[context->line_size++] = byte; - break; - case HFP_PARSER_CMD_INDICATOR_MAX_RANGE: - if (byte == ')'){ // end max_range - context->parser_state = HFP_PARSER_CMD_SEQUENCE; - - context->line_buffer[context->line_size] = 0; - printf("%d)\n", atoi((char *)&context->line_buffer[0])); - context->line_size = 0; - context->remote_indicators_nr+=1; - break; - } - // - context->line_buffer[context->line_size++] = byte; - break; - - } -} - void handle_switch_on_ok(hfp_connection_t *context){ printf("handle switch on OK\n"); diff --git a/test/pts/hfp_ag_test.c b/test/pts/hfp_ag_test.c index ed0e69baf..7cc07b416 100644 --- a/test/pts/hfp_ag_test.c +++ b/test/pts/hfp_ag_test.c @@ -73,10 +73,28 @@ const char hfp_ag_service_name[] = "BTstack HFP AG Test"; static bd_addr_t pts_addr = {0x00,0x1b,0xDC,0x07,0x32,0xEF}; static bd_addr_t local_mac = {0x04, 0x0C, 0xCE, 0xE4, 0x85, 0xD3}; -static bd_addr_t phone = {0xD8,0xBb,0x2C,0xDf,0xF1,0x08}; +static bd_addr_t speaker = {0x00, 0x21, 0x3C, 0xAC, 0xF7, 0x38}; static uint8_t codecs[1] = {HFP_CODEC_CVSD}; -static uint16_t indicators[1] = {0x01}; +static int ag_indicators_nr = 7; +static hfp_ag_indicator_t ag_indicators[] = { + {1, "service", 0, 1, 1}, + {2, "call", 0, 1, 0}, + {3, "callsetup", 0, 3, 0}, + {4, "battchg", 0, 5, 3}, + {5, "signal", 0, 5, 5}, + {6, "roam", 0, 1, 0}, + {7, "callheld", 0, 2, 0}, +}; + +static int call_hold_services_nr = 5; +static char* call_hold_services[] = {"1", "1x", "2", "2x", "3"}; + +static int hf_indicators_nr = 2; +static hfp_hf_indicator_t hf_indicators[] = { + {1, 1, 1}, + {2, 1, 1}, +}; // prototypes static void show_usage(void); @@ -102,8 +120,8 @@ static int stdin_process(struct data_source *ds){ hfp_ag_connect(pts_addr); break; case 'e': - printf("Establishing HFP connection to %s...\n", bd_addr_to_str(phone)); - hfp_ag_connect(phone); + printf("Establishing HFP connection to %s...\n", bd_addr_to_str(speaker)); + hfp_ag_connect(speaker); break; case 'd': printf("Releasing HFP connection.\n"); @@ -131,8 +149,10 @@ int btstack_main(int argc, const char * argv[]){ l2cap_init(); rfcomm_init(); - // hfp_ag_init(rfcomm_channel_nr, HFP_DEFAULT_HF_SUPPORTED_FEATURES, codecs, sizeof(codecs), indicators, sizeof(indicators)/sizeof(uint16_t), 1); - hfp_ag_init(rfcomm_channel_nr, 438, codecs, sizeof(codecs), indicators, sizeof(indicators)/sizeof(uint16_t), 1); + hfp_ag_init(rfcomm_channel_nr, 438, codecs, sizeof(codecs), + ag_indicators, ag_indicators_nr, + hf_indicators, hf_indicators_nr, + call_hold_services, call_hold_services_nr); hfp_register_packet_handler(packet_handler); @@ -147,7 +167,7 @@ int btstack_main(int argc, const char * argv[]){ hci_power_control(HCI_POWER_ON); btstack_stdin_setup(stdin_process); - printf("Establishing HFP connection to %s...\n", bd_addr_to_str(phone)); - hfp_ag_connect(phone); + printf("Establishing HFP connection to %s...\n", bd_addr_to_str(speaker)); + hfp_ag_connect(speaker); return 0; }