diff --git a/include/btstack/hci_cmds.h b/include/btstack/hci_cmds.h index 4b90910e9..1fb2ea32d 100644 --- a/include/btstack/hci_cmds.h +++ b/include/btstack/hci_cmds.h @@ -624,6 +624,7 @@ extern "C" { #define HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_RELEASED 0x02 #define HFP_SUBEVENT_COMPLETE 0x03 #define HFP_SUBEVENT_AG_INDICATOR_STATUS_CHANGED 0x04 +#define HFP_SUBEVENT_NETWORK_OPERATOR_CHANGED 0x05 // ANCS Client #define ANCS_CLIENT_CONNECTED 0xF0 diff --git a/src/hfp.c b/src/hfp.c index ea50b0871..ec5ec4c6a 100644 --- a/src/hfp.c +++ b/src/hfp.c @@ -493,6 +493,8 @@ void hfp_handle_hci_event(hfp_callback_t callback, uint8_t packet_type, uint8_t // translates command string into hfp_command_t CMD and flags to distinguish between CMD=, CMD?, CMD=? void process_command(hfp_connection_t * context){ + if (context->line_size < 2) return; + // printf("process_command %s\n", context->line_buffer); context->command = HFP_CMD_NONE; int offset = 0; int isHandsFree = 1; @@ -508,6 +510,7 @@ void process_command(hfp_connection_t * context){ } if (strncmp((char *)context->line_buffer+offset, HFP_OK, strlen(HFP_OK)) == 0){ + //printf("parsed HFP_CMD_OK \n"); context->command = HFP_CMD_OK; return; } @@ -518,6 +521,7 @@ void process_command(hfp_connection_t * context){ } if (strncmp((char *)context->line_buffer+offset, HFP_INDICATOR, strlen(HFP_INDICATOR)) == 0){ + //printf("parsed HFP_INDICATOR \n"); context->command = HFP_CMD_INDICATOR; if (isHandsFree) return; @@ -615,9 +619,9 @@ static int hfp_parser_is_end_of_header(uint8_t byte){ static int hfp_parser_found_separator(hfp_connection_t * context, uint8_t byte){ if (context->keep_separator == 1) return 1; - int found_separator = byte == ',' || byte == '\n' || byte == '\r' || + int found_separator = byte == ',' || byte == '\n'|| byte == '\r'|| byte == ')' || byte == '(' || byte == ':' || - byte == '-' || byte == '"' || byte == '?' || byte == '='; + byte == '-' || byte == '"' || byte == '?'|| byte == '='; return found_separator; } @@ -625,7 +629,7 @@ static void hfp_parser_next_state(hfp_connection_t * context, uint8_t byte){ context->line_size = 0; if (hfp_parser_is_end_of_line(byte)){ context->parser_item_index = 0; - context->parser_state = HFP_PARSER_CMD_HEADER; + context->parser_state = HFP_PARSER_CMD_HEADER; return; } switch (context->parser_state){ @@ -681,10 +685,17 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte){ hfp_parser_store_byte(context, byte); return; } + if (hfp_parser_is_end_of_line(byte)) { + if (hfp_parser_is_buffer_empty(context)){ + context->parser_state = HFP_PARSER_CMD_HEADER; + } + } if (hfp_parser_is_buffer_empty(context)) return; + switch (context->parser_state){ case HFP_PARSER_CMD_HEADER: // header + // printf(" parse header 1 \n"); if (byte == '='){ context->keep_separator = 1; hfp_parser_store_byte(context, byte); @@ -696,8 +707,9 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte){ hfp_parser_store_byte(context, byte); return; } - + // printf(" parse header 2 %s, keep separator $ %d\n", context->line_buffer, context->keep_separator); if (hfp_parser_is_end_of_header(byte) || context->keep_separator == 1){ + // printf(" parse header 3 %s, keep separator $ %d\n", context->line_buffer, context->keep_separator); process_command(context); } break; diff --git a/src/hfp_hf.c b/src/hfp_hf.c index 471672350..651a64f2b 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -201,14 +201,28 @@ int hfp_hs_query_operator_name_cmd(uint16_t cid){ } -static void hfp_emit_ag_indicator_event(hfp_callback_t callback, hfp_ag_indicator_t indicator){ +static void hfp_emit_ag_indicator_event(hfp_callback_t callback, int status, hfp_ag_indicator_t indicator){ if (!callback) return; - uint8_t event[5]; + uint8_t event[6]; event[0] = HCI_EVENT_HFP_META; event[1] = sizeof(event) - 2; event[2] = HFP_SUBEVENT_AG_INDICATOR_STATUS_CHANGED; - event[3] = indicator.index; - event[4] = indicator.status; + event[3] = status; + event[4] = indicator.index; + event[5] = indicator.status; + (*callback)(event, sizeof(event)); +} + +static void hfp_emit_network_operator_event(hfp_callback_t callback, int status, hfp_network_opearator_t network_operator){ + if (!callback) return; + uint8_t event[24]; + event[0] = HCI_EVENT_HFP_META; + event[1] = sizeof(event) - 2; + event[2] = HFP_SUBEVENT_NETWORK_OPERATOR_CHANGED; + event[3] = status; + event[4] = network_operator.mode; + event[5] = network_operator.format; + strcpy((char*)&event[6], network_operator.name); (*callback)(event, sizeof(event)); } @@ -276,7 +290,7 @@ static void hfp_run_for_context(hfp_connection_t * context){ int i; for (i = 0; i < context->ag_indicators_nr; i++){ if (context->ag_indicators[i].status_changed == 1) { - hfp_emit_ag_indicator_event(hfp_callback, context->ag_indicators[i]); + hfp_emit_ag_indicator_event(hfp_callback, 0, context->ag_indicators[i]); context->ag_indicators[i].status_changed = 0; break; } @@ -287,7 +301,6 @@ static void hfp_run_for_context(hfp_connection_t * context){ if (context->enable_status_update_for_ag_indicators != 0xFF){ hfp_hs_activate_status_update_for_all_ag_indicators_cmd(context->rfcomm_cid, context->enable_status_update_for_ag_indicators); context->wait_ok = 1; - context->enable_status_update_for_ag_indicators = 0xFF; break; }; if (context->change_status_update_for_individual_ag_indicators == 1){ @@ -295,21 +308,17 @@ static void hfp_run_for_context(hfp_connection_t * context){ context->ag_indicators_status_update_bitmap, context->ag_indicators_nr); context->wait_ok = 1; - context->change_status_update_for_individual_ag_indicators = 0; break; } if (context->operator_name_format == 1){ hfp_hs_query_operator_name_format_cmd(context->rfcomm_cid); - context->operator_name_format = 0; - context->operator_name = 1; context->wait_ok = 1; break; } if (context->operator_name == 1){ hfp_hs_query_operator_name_cmd(context->rfcomm_cid); context->wait_ok = 1; - context->operator_name = 0; } break; } @@ -383,8 +392,29 @@ void handle_switch_on_ok(hfp_connection_t *context){ case HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED: context->wait_ok = 0; - hfp_emit_event(hfp_callback, HFP_SUBEVENT_COMPLETE, 0); - + + if (context->enable_status_update_for_ag_indicators != 0xFF){ + context->enable_status_update_for_ag_indicators = 0xFF; + hfp_emit_event(hfp_callback, HFP_SUBEVENT_COMPLETE, 0); + break; + }; + if (context->change_status_update_for_individual_ag_indicators == 1){ + context->change_status_update_for_individual_ag_indicators = 0; + hfp_emit_event(hfp_callback, HFP_SUBEVENT_COMPLETE, 0); + break; + } + + if (context->operator_name_format == 1){ + context->operator_name_format = 0; + context->operator_name = 1; + break; + } + if (context->operator_name == 1){ + context->operator_name = 0; + hfp_emit_network_operator_event(hfp_callback, 0, context->network_operator); + break; + } + break; default: break; @@ -404,7 +434,6 @@ static void hfp_handle_rfcomm_event(uint8_t packet_type, uint16_t channel, uint8 for (pos = 0; pos < size ; pos++){ hfp_parse(context, packet[pos]); - // trigger next action after CMD received if (context->command == HFP_CMD_ERROR){ if (context->state == HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED){ context->wait_ok = 0; diff --git a/test/hfp/hfp_hf_parser_test.c b/test/hfp/hfp_hf_parser_test.c index 338a19405..43695c17e 100644 --- a/test/hfp/hfp_hf_parser_test.c +++ b/test/hfp/hfp_hf_parser_test.c @@ -76,6 +76,7 @@ TEST_GROUP(HFPParser){ } }; + TEST(HFPParser, HFP_HF_OK){ sprintf(packet, "\r\n%s\r\n", HFP_OK); for (pos = 0; pos < strlen(packet); pos++){ @@ -85,21 +86,22 @@ TEST(HFPParser, HFP_HF_OK){ } TEST(HFPParser, HFP_HF_SUPPORTED_FEATURES){ - sprintf(packet, "\r\n%s:1007\r\n", HFP_SUPPORTED_FEATURES); + sprintf(packet, "\r\n%s:1007\r\n\r\nOK\r\n", HFP_SUPPORTED_FEATURES); for (pos = 0; pos < strlen(packet); pos++){ hfp_parse(&context, packet[pos]); } - CHECK_EQUAL(HFP_CMD_SUPPORTED_FEATURES, context.command); + CHECK_EQUAL(HFP_CMD_OK, context.command); CHECK_EQUAL(1007, context.remote_supported_features); } + TEST(HFPParser, HFP_HF_INDICATORS){ offset = 0; offset += snprintf(packet, sizeof(packet), "%s:", HFP_INDICATOR); for (pos = 0; pos < hfp_ag_indicators_nr - 1; pos++){ offset += snprintf(packet+offset, sizeof(packet)-offset, "\"%s\", (%d, %d),", hfp_ag_indicators[pos].name, hfp_ag_indicators[pos].min_range, hfp_ag_indicators[pos].max_range); } - offset += snprintf(packet+offset, sizeof(packet)-offset, "\"%s\", (%d, %d)\r\n", hfp_ag_indicators[pos].name, hfp_ag_indicators[pos].min_range, hfp_ag_indicators[pos].max_range); + offset += snprintf(packet+offset, sizeof(packet)-offset, "\"%s\", (%d, %d)\r\n\r\nOK\r\n", hfp_ag_indicators[pos].name, hfp_ag_indicators[pos].min_range, hfp_ag_indicators[pos].max_range); context.command = HFP_CMD_INDICATOR; context.retrieve_ag_indicators = 1; @@ -108,7 +110,7 @@ TEST(HFPParser, HFP_HF_INDICATORS){ for (pos = 0; pos < strlen(packet); pos++){ hfp_parse(&context, packet[pos]); } - CHECK_EQUAL(HFP_CMD_INDICATOR, context.command); + CHECK_EQUAL(HFP_CMD_OK, context.command); CHECK_EQUAL(hfp_ag_indicators_nr, context.ag_indicators_nr); for (pos = 0; pos < hfp_ag_indicators_nr; pos++){ CHECK_EQUAL(hfp_ag_indicators[pos].index, context.ag_indicators[pos].index); @@ -125,7 +127,7 @@ TEST(HFPParser, HFP_HF_INDICATOR_STATUS){ for (pos = 0; pos < hfp_ag_indicators_nr - 1; pos++){ offset += snprintf(packet+offset, sizeof(packet)-offset, "%d,", hfp_ag_indicators[pos].status); } - offset += snprintf(packet+offset, sizeof(packet)-offset, "%d\r\n", hfp_ag_indicators[pos].status); + offset += snprintf(packet+offset, sizeof(packet)-offset, "%d\r\n\r\nOK\r\n", hfp_ag_indicators[pos].status); context.command = HFP_CMD_INDICATOR; context.retrieve_ag_indicators_status = 1; @@ -135,18 +137,19 @@ TEST(HFPParser, HFP_HF_INDICATOR_STATUS){ hfp_parse(&context, packet[pos]); } - CHECK_EQUAL(HFP_CMD_INDICATOR, context.command); + CHECK_EQUAL(HFP_CMD_OK, context.command); for (pos = 0; pos < hfp_ag_indicators_nr; pos++){ CHECK_EQUAL(hfp_ag_indicators[pos].status, context.ag_indicators[pos].status); } } + TEST(HFPParser, HFP_HF_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES){ - sprintf(packet, "\r\n%s:(1,1x,2,2x,3)\r\n", HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES); + sprintf(packet, "\r\n%s:(1,1x,2,2x,3)\r\n\r\nOK\r\n", HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES); for (pos = 0; pos < strlen(packet); pos++){ hfp_parse(&context, packet[pos]); } - CHECK_EQUAL(HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES, context.command); + CHECK_EQUAL(HFP_CMD_OK, context.command); CHECK_EQUAL(5, context.remote_call_services_nr); CHECK_EQUAL(0, strcmp("1", (char*)context.remote_call_services[0].name)); @@ -157,7 +160,7 @@ TEST(HFPParser, HFP_HF_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES){ } TEST(HFPParser, HFP_HF_GENERIC_STATUS_INDICATOR){ - sprintf(packet, "\r\n%s:0,1,2,3,4\r\n", HFP_GENERIC_STATUS_INDICATOR); + sprintf(packet, "\r\n%s:0,1,2,3,4\r\n\r\nOK\r\n", HFP_GENERIC_STATUS_INDICATOR); context.command = HFP_CMD_GENERIC_STATUS_INDICATOR; context.list_generic_status_indicators = 0; @@ -168,7 +171,7 @@ TEST(HFPParser, HFP_HF_GENERIC_STATUS_INDICATOR){ hfp_parse(&context, packet[pos]); } - CHECK_EQUAL(HFP_CMD_GENERIC_STATUS_INDICATOR, context.command); + CHECK_EQUAL(HFP_CMD_OK, context.command); CHECK_EQUAL(5, context.generic_status_indicators_nr); for (pos = 0; pos < context.generic_status_indicators_nr; pos++){ @@ -177,7 +180,7 @@ TEST(HFPParser, HFP_HF_GENERIC_STATUS_INDICATOR){ } TEST(HFPParser, HFP_HF_GENERIC_STATUS_INDICATOR_STATE){ - sprintf(packet, "\r\n%s:0,1\r\n", HFP_GENERIC_STATUS_INDICATOR); + sprintf(packet, "\r\n%s:0,1\r\n\r\nOK\r\n", HFP_GENERIC_STATUS_INDICATOR); context.command = HFP_CMD_GENERIC_STATUS_INDICATOR; context.list_generic_status_indicators = 0; context.retrieve_generic_status_indicators = 0; @@ -187,7 +190,7 @@ TEST(HFPParser, HFP_HF_GENERIC_STATUS_INDICATOR_STATE){ hfp_parse(&context, packet[pos]); } - CHECK_EQUAL(HFP_CMD_GENERIC_STATUS_INDICATOR, context.command); + CHECK_EQUAL(HFP_CMD_OK, context.command); CHECK_EQUAL(1, context.generic_status_indicators[0].state); } @@ -198,23 +201,23 @@ TEST(HFPParser, HFP_HF_AG_INDICATOR_STATUS_UPDATE){ uint8_t index = 4; uint8_t status = 5; - sprintf(packet, "\r\n%s:%d,%d\r\n", HFP_TRANSFER_AG_INDICATOR_STATUS, index, status); + sprintf(packet, "\r\n%s:%d,%d\r\n\r\nOK\r\n", HFP_TRANSFER_AG_INDICATOR_STATUS, index, status); for (pos = 0; pos < strlen(packet); pos++){ hfp_parse(&context, packet[pos]); } - CHECK_EQUAL(HFP_CMD_TRANSFER_AG_INDICATOR_STATUS, context.command); + CHECK_EQUAL(HFP_CMD_OK, context.command); CHECK_EQUAL(context.ag_indicators[index - 1].status, status); } TEST(HFPParser, HFP_HF_AG_QUERY_OPERATOR_SELECTION){ - sprintf(packet, "\r\n%s:1,0,sunrise\r\n", HFP_QUERY_OPERATOR_SELECTION); + sprintf(packet, "\r\n%s:1,0,sunrise\r\n\r\nOK\r\n", HFP_QUERY_OPERATOR_SELECTION); for (pos = 0; pos < strlen(packet); pos++){ hfp_parse(&context, packet[pos]); } - CHECK_EQUAL(context.command, HFP_CMD_QUERY_OPERATOR_SELECTION); + CHECK_EQUAL(context.command, HFP_CMD_OK); CHECK_EQUAL(context.operator_name_format, 0); CHECK_EQUAL(context.operator_name, 1); CHECK_EQUAL(context.operator_name_changed, 0); diff --git a/test/pts/hfp_hf_test.c b/test/pts/hfp_hf_test.c index 407225a7a..a8326340f 100644 --- a/test/pts/hfp_hf_test.c +++ b/test/pts/hfp_hf_test.c @@ -93,6 +93,9 @@ static void show_usage(void){ printf("e - establish HFP connection to local mac\n"); printf("r - enable registration status update\n"); printf("d - release HFP connection\n"); + printf("i - enabling HFP AG registration status update for individual indicators\n"); + printf("o - query network operator\n"); + printf("---\n"); printf("Ctrl-c - exit\n"); printf("---\n"); @@ -123,6 +126,11 @@ static int stdin_process(struct data_source *ds){ printf("Enabling HFP AG registration status update for individual indicators.\n"); hfp_hf_enable_status_update_for_individual_ag_indicators(device_addr, 63); break; + case 'o': + printf("Query network operator.\n"); + hfp_hf_query_operator_selection(device_addr); + break; + default: show_usage(); break; @@ -150,12 +158,17 @@ void packet_handler(uint8_t * event, uint16_t event_size){ case 'r': printf("HFP AG registration status update enabled.\n"); break; + case 'i': + printf("HFP AG registration status update for individual indicators set.\n"); default: break; } break; case HFP_SUBEVENT_AG_INDICATOR_STATUS_CHANGED: - printf("AG_INDICATOR_STATUS_CHANGED, AG indicator index: %d, status %d\n", event[3], event[4]); + printf("AG_INDICATOR_STATUS_CHANGED, AG indicator index: %d, status: %d\n", event[4], event[5]); + break; + case HFP_SUBEVENT_NETWORK_OPERATOR_CHANGED: + printf("HFP_SUBEVENT_NETWORK_OPERATOR_CHANGED, operator mode: %d, format: %d, name: %s\n", event[4], event[5], (char *) &event[6]); break; default: printf("event not handled %u\n", event[2]);