diff --git a/src/hfp.c b/src/hfp.c index 675f28907..f4df5713e 100644 --- a/src/hfp.c +++ b/src/hfp.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -58,7 +59,6 @@ #include "sdp_query_rfcomm.h" #include "sdp.h" #include "debug.h" -#include "hfp_ag.h" #define HFP_HF_FEATURES_SIZE 10 #define HFP_AG_FEATURES_SIZE 12 @@ -115,9 +115,7 @@ 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("\nSent %s", command); - } + } return err; } @@ -161,6 +159,18 @@ int join(char * buffer, int buffer_size, uint8_t * values, int values_nr){ return offset; } +int join_bitmap(char * buffer, int buffer_size, uint32_t values, int values_nr){ + if (buffer_size < values_nr * 3) return 0; + int i; + int offset = 0; + for (i = 0; i < values_nr-1; i++) { + offset += snprintf(buffer+offset, buffer_size-offset, "%d,", get_bit(values,i)); // puts string into buffer + } + if (istate = HFP_IDLE; - context->line_size = 0; context->parser_state = HFP_PARSER_CMD_HEADER; context->command = HFP_CMD_NONE; - context->negotiated_codec = HFP_CODEC_CVSD; - context->remote_supported_features = 0; - context->remote_indicators_update_enabled = 0; - context->ag_indicators_nr = 0; + context->enable_status_update_for_ag_indicators = 0xFF; + context->change_enable_status_update_for_individual_ag_indicators = 0xFF; linked_list_add(&hfp_connections, (linked_item_t*)context); return context; @@ -451,11 +458,18 @@ void hfp_handle_hci_event(hfp_callback_t callback, uint8_t packet_type, uint8_t void update_command(hfp_connection_t * context); +uint32_t fromBinary(char *s) { + return (uint32_t) strtol(s, NULL, 2); +} + void hfp_parse(hfp_connection_t * context, uint8_t byte){ int i; + int value; + if (byte == ' ') return; - if ( (byte == '\n' || byte == '\r') && context->parser_state > HFP_PARSER_CMD_SEQUENCE) return; - + if (context->line_size == 0 && (byte == '\n' || byte == '\r')) return; + if ((byte == '\n' || byte == '\r') && (context->parser_state > HFP_PARSER_CMD_SEQUENCE && context->parser_state != HFP_PARSER_CMD_INITITAL_STATE_GENERIC_STATUS_INDICATORS) )return; + //printf("%c", byte); switch (context->parser_state){ case HFP_PARSER_CMD_HEADER: // header if (byte == ':' || byte == '='){ @@ -466,10 +480,11 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte){ return; } if (byte == '\n' || byte == '\r'){ - // received OK context->line_buffer[context->line_size] = 0; + if (context->line_size == 2){ + update_command(context); + } context->line_size = 0; - update_command(context); return; } context->line_buffer[context->line_size++] = byte; @@ -488,41 +503,68 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte){ 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]); + + switch (context->command){ + case HFP_CMD_SUPPORTED_FEATURES: + context->remote_supported_features = 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)); - } + if (context->line_buffer[i] == '1'){ + context->remote_supported_features = store_bit(context->remote_supported_features,15-i,1); + } } + // printf("Received supported feature %d\n", context->remote_supported_features); context->parser_state = HFP_PARSER_CMD_HEADER; break; - case HFP_W4_RETRIEVE_INDICATORS: + case HFP_CMD_AVAILABLE_CODECS: + // printf("Received codec %s\n", context->line_buffer); + context->remote_codecs[context->remote_codecs_nr] = (uint16_t)atoi((char*)context->line_buffer); + context->remote_codecs_nr++; break; - case HFP_W4_RETRIEVE_INDICATORS_STATUS: - printf("Indicator with status: %s\n", context->line_buffer); + case HFP_CMD_INDICATOR_STATUS: + // printf("Indicator %d with status: %s\n", context->parser_item_index+1, context->line_buffer); + context->ag_indicators[context->parser_item_index].status = atoi((char *) context->line_buffer); + context->parser_item_index++; break; - case HFP_W4_RETRIEVE_CAN_HOLD_CALL: - printf("Support call hold: %s\n", context->line_buffer); + case HFP_CMD_ENABLE_INDICATOR_STATUS_UPDATE: + if (context->parser_item_index == 3){ + printf("Enable indicators: %s\n", context->line_buffer); + value = atoi((char *)&context->line_buffer[0]); + context->enable_status_update_for_ag_indicators = (uint8_t) value; + } break; - case HFP_W4_RETRIEVE_GENERIC_STATUS_INDICATORS: // comma separated ints - printf("Generic status indicator: %s\n", context->line_buffer); + case HFP_CMD_ENABLE_INDIVIDUAL_INDICATOR_STATUS_UPDATE: + value = atoi((char *)&context->line_buffer[0]); + printf("Indicator %d status: %d\n", context->ag_indicators_nr, value); + store_bit(context->ag_indicators_status_update_bitmap, context->ag_indicators_nr, (uint8_t)value); break; - case HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS: - printf("Generic status indicator: %s, ", context->line_buffer); + case HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES: + // printf("Support call hold: %s\n", context->line_buffer); + if (context->line_size > 2 ) break; + strcpy((char *)context->remote_call_services[context->remote_call_services_nr].name, (char *)context->line_buffer); + context->remote_call_services_nr++; + break; + case HFP_CMD_GENERIC_STATUS_INDICATOR: + context->generic_status_indicators[context->generic_status_indicators_nr].uuid = (uint16_t)atoi((char*)context->line_buffer); + context->generic_status_indicators_nr++; + break; + case HFP_CMD_GENERIC_STATUS_INDICATOR_STATE: + printf("HFP_CMD_GENERIC_STATUS_INDICATOR_STATE %s, %d\n", context->line_buffer, context->command); context->parser_state = HFP_PARSER_CMD_INITITAL_STATE_GENERIC_STATUS_INDICATORS; + context->generic_status_indicator_state_index = (uint8_t)atoi((char*)context->line_buffer); break; default: break; } + context->line_size = 0; + if (byte == '\n' || byte == '\r'){ context->parser_state = HFP_PARSER_CMD_HEADER; + context->parser_item_index = 0; break; } - if (byte == ')' && context->state == HFP_W4_RETRIEVE_CAN_HOLD_CALL){ // tuple separated mit comma + if (byte == ')' && context->command == HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES){ // tuple separated mit comma context->parser_state = HFP_PARSER_CMD_HEADER; + context->parser_item_index = 0; break; } break; @@ -530,20 +572,31 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte){ context->line_buffer[context->line_size++] = byte; break; case HFP_PARSER_CMD_INITITAL_STATE_GENERIC_STATUS_INDICATORS: + context->line_buffer[context->line_size] = 0; + printf("HFP_PARSER_CMD_INITITAL_STATE_GENERIC_STATUS_INDICATORS 1 %s\n", context->line_buffer); + if (byte == ',') break; + printf("HFP_PARSER_CMD_INITITAL_STATE_GENERIC_STATUS_INDICATORS 2 %s\n", context->line_buffer); if (byte == '\n' || byte == '\r'){ + printf("HFP_PARSER_CMD_INITITAL_STATE_GENERIC_STATUS_INDICATORS 4%s\n", context->line_buffer); + 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); + context->generic_status_indicators[context->generic_status_indicator_state_index].state = (uint8_t)atoi((char*)context->line_buffer); break; } + context->line_buffer[context->line_size++] = byte; + printf("HFP_PARSER_CMD_INITITAL_STATE_GENERIC_STATUS_INDICATORS 3 %s\n", context->line_buffer); 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; + //printf("Indicator %d: %s (", context->ag_indicators_nr+1, context->line_buffer); + strcpy((char *)context->ag_indicators[context->ag_indicators_nr].name, (char *)context->line_buffer); + context->ag_indicators[context->ag_indicators_nr].index = context->ag_indicators_nr+1; break; } if (byte == '('){ // parse indicator range @@ -556,7 +609,8 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte){ 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])); + //printf("%d, ", atoi((char *)&context->line_buffer[0])); + context->ag_indicators[context->ag_indicators_nr].min_range = atoi((char *)context->line_buffer); context->line_size = 0; break; } @@ -566,14 +620,14 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte){ 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])); + //printf("%d)\n", atoi((char *)&context->line_buffer[0])); + context->ag_indicators[context->ag_indicators_nr].max_range = atoi((char *)context->line_buffer); context->line_size = 0; - context->ag_indicators_nr+=1; + context->ag_indicators_nr++; break; } - // + context->line_buffer[context->line_size++] = byte; break; @@ -590,7 +644,7 @@ void hfp_establish_service_level_connection(bd_addr_t bd_addr, uint16_t service_ log_info("hfp_connect %s, context %p", bd_addr_to_str(bd_addr), context); if (!context) { - log_error("hfp_connect for addr %s failed", bd_addr_to_str(bd_addr)); + log_error("hfp_establish_service_level_connection for addr %s failed", bd_addr_to_str(bd_addr)); return; } if (context->state != HFP_IDLE) return; @@ -602,11 +656,10 @@ void hfp_establish_service_level_connection(bd_addr_t bd_addr, uint16_t service_ sdp_query_rfcomm_channel_and_name_for_uuid(context->remote_addr, service_uuid); } -hfp_connection_t * hfp_release_service_level_connection(bd_addr_t bd_addr){ - hfp_connection_t * context = get_hfp_connection_context_for_bd_addr(bd_addr); +void hfp_release_service_level_connection(hfp_connection_t * context){ if (!context) { - log_error("hfp_disconnect for addr %s failed", bd_addr_to_str(bd_addr)); - return NULL; + log_error("hfp_release_service_level_connection failed"); + return; } switch (context->state){ @@ -619,7 +672,7 @@ hfp_connection_t * hfp_release_service_level_connection(bd_addr_t bd_addr){ default: break; } - return context; + return; } diff --git a/src/hfp.h b/src/hfp.h index b045102d4..d90d6d803 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -99,14 +99,16 @@ extern "C" { #define HFP_MAX_NUM_CODECS 20 #define HFP_MAX_NUM_AG_INDICATORS 20 #define HFP_MAX_NUM_HF_INDICATORS 20 -#define HFP_MAX_INDICATOR_DESC_SIZE 10 +#define HFP_MAX_INDICATOR_DESC_SIZE 20 #define HFP_SUPPORTED_FEATURES "+BRSF" #define HFP_AVAILABLE_CODECS "+BAC" #define HFP_INDICATOR "+CIND" -#define HFP_ENABLE_INDICATOR_STATUS_UPDATE "+CMER" +#define HFP_ENABLE_STATUS_UPDATE_FOR_AG_INDICATORS "+CMER" +#define HFP_UPDATE_ENABLE_STATUS_FOR_INDIVIDUAL_AG_INDICATORS "+BIA" #define HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES "+CHLD" #define HFP_GENERIC_STATUS_INDICATOR "+BIND" + #define HFP_OK "OK" // Codecs @@ -119,9 +121,13 @@ typedef enum { HFP_CMD_SUPPORTED_FEATURES, HFP_CMD_AVAILABLE_CODECS, HFP_CMD_INDICATOR, + HFP_CMD_INDICATOR_STATUS, // 5 HFP_CMD_ENABLE_INDICATOR_STATUS_UPDATE, + HFP_CMD_ENABLE_INDIVIDUAL_INDICATOR_STATUS_UPDATE, HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES, - HFP_CMD_GENERIC_STATUS_INDICATOR + HFP_CMD_LIST_GENERIC_STATUS_INDICATOR, + HFP_CMD_GENERIC_STATUS_INDICATOR, // 10 + HFP_CMD_GENERIC_STATUS_INDICATOR_STATE, } hfp_command_t; typedef enum { @@ -180,7 +186,7 @@ typedef struct{ uint16_t uuid; uint8_t state; uint8_t initial_state; -} hfp_hf_indicator_t; +} hfp_generic_status_indicators_t; typedef struct{ uint8_t index; @@ -190,36 +196,56 @@ typedef struct{ uint8_t status; } hfp_ag_indicator_t; +typedef struct{ + char name[3]; +} hfp_call_service_t; + typedef struct hfp_connection { linked_item_t item; - hfp_state_t state; - - hfp_command_t command; - hfp_parser_state_t parser_state; - - uint8_t line_buffer[HFP_MAX_INDICATOR_DESC_SIZE]; - int line_size; bd_addr_t remote_addr; uint16_t con_handle; 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; - uint32_t remote_indicators_status; + hfp_state_t state; + + // used during service level connection establishment + hfp_command_t command; + hfp_parser_state_t parser_state; + int parser_item_index; + uint8_t line_buffer[HFP_MAX_INDICATOR_DESC_SIZE]; + int line_size; + + uint32_t remote_supported_features; + int remote_codecs_nr; + uint16_t remote_codecs[HFP_MAX_INDICATOR_DESC_SIZE]; + int ag_indicators_nr; + hfp_ag_indicator_t ag_indicators[HFP_MAX_INDICATOR_DESC_SIZE]; + int remote_call_services_nr; + hfp_call_service_t remote_call_services[HFP_MAX_INDICATOR_DESC_SIZE]; + int generic_status_indicators_nr; + hfp_generic_status_indicators_t generic_status_indicators[HFP_MAX_INDICATOR_DESC_SIZE]; + uint8_t generic_status_indicator_state_index; + // TODO: put in a bitmap + // 0 = deactivate, 1 = activate, 0xff = do nothing + uint8_t wait_ok; + hfp_command_t sent_command; + uint8_t enable_status_update_for_ag_indicators; + uint8_t change_enable_status_update_for_individual_ag_indicators; + + uint32_t ag_indicators_status_update_bitmap; + + + // Retrieved during service level connection establishment, not used yet + uint8_t negotiated_codec; } hfp_connection_t; // UTILS_START : TODO move to utils int send_str_over_rfcomm(uint16_t cid, char * command); int join(char * buffer, int buffer_size, uint8_t * values, int values_nr); +int join_bitmap(char * buffer, int buffer_size, uint32_t values, int values_nr); int get_bit(uint16_t bitmap, int position); int store_bit(uint32_t bitmap, int position, uint8_t value); // UTILS_END @@ -228,12 +254,14 @@ void hfp_create_sdp_record(uint8_t * service, uint16_t service_uuid, int rfcomm_ void hfp_handle_hci_event(hfp_callback_t callback, uint8_t packet_type, uint8_t *packet, uint16_t size); void hfp_emit_event(hfp_callback_t callback, uint8_t event_subtype, uint8_t value); hfp_connection_t * get_hfp_connection_context_for_rfcomm_cid(uint16_t cid); +hfp_connection_t * get_hfp_connection_context_for_bd_addr(bd_addr_t bd_addr); + linked_list_t * hfp_get_connections(); void hfp_parse(hfp_connection_t * context, uint8_t byte); void hfp_init(uint16_t rfcomm_channel_nr); void hfp_establish_service_level_connection(bd_addr_t bd_addr, uint16_t service_uuid); -hfp_connection_t * hfp_release_service_level_connection(bd_addr_t bd_addr); +void hfp_release_service_level_connection(hfp_connection_t * connection); const char * hfp_hf_feature(int index); diff --git a/src/hfp_ag.c b/src/hfp_ag.c index 8c1a396cd..0586fc991 100644 --- a/src/hfp_ag.c +++ b/src/hfp_ag.c @@ -257,43 +257,47 @@ int hfp_ag_retrieve_initital_supported_generic_status_indicators_cmd(uint16_t ci void update_command(hfp_connection_t * context){ context->command = HFP_CMD_NONE; - + printf("Received %s\n", context->line_buffer); + if (strncmp((char *)context->line_buffer+2, HFP_SUPPORTED_FEATURES, strlen(HFP_SUPPORTED_FEATURES)) == 0){ - printf("Received AT+BRSF\n"); context->command = HFP_CMD_SUPPORTED_FEATURES; return; } if (strncmp((char *)context->line_buffer+2, HFP_INDICATOR, strlen(HFP_INDICATOR)) == 0){ - printf("Received AT+CIND\n"); - context->command = HFP_CMD_INDICATOR; + if (strncmp((char *)context->line_buffer+strlen(HFP_INDICATOR)+2, "?", 1) == 0){ + context->command = HFP_CMD_INDICATOR_STATUS; + } else { + context->command = HFP_CMD_INDICATOR; + } return; } if (strncmp((char *)context->line_buffer+2, HFP_AVAILABLE_CODECS, strlen(HFP_AVAILABLE_CODECS)) == 0){ - printf("Received AT+BAC\n"); context->command = HFP_CMD_AVAILABLE_CODECS; return; } - if (strncmp((char *)context->line_buffer+2, HFP_ENABLE_INDICATOR_STATUS_UPDATE, strlen(HFP_ENABLE_INDICATOR_STATUS_UPDATE)) == 0){ - printf("Received AT+CMER\n"); + if (strncmp((char *)context->line_buffer+2, HFP_ENABLE_STATUS_UPDATE_FOR_AG_INDICATORS, strlen(HFP_ENABLE_STATUS_UPDATE_FOR_AG_INDICATORS)) == 0){ context->command = HFP_CMD_ENABLE_INDICATOR_STATUS_UPDATE; return; } if (strncmp((char *)context->line_buffer+2, HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES, strlen(HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES)) == 0){ - printf("Received AT+CHLD\n"); context->command = HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES; return; } if (strncmp((char *)context->line_buffer+2, HFP_GENERIC_STATUS_INDICATOR, strlen(HFP_GENERIC_STATUS_INDICATOR)) == 0){ - printf("Received AT+BIND\n"); context->command = HFP_CMD_GENERIC_STATUS_INDICATOR; return; } + + if (strncmp((char *)context->line_buffer+2, HFP_UPDATE_ENABLE_STATUS_FOR_INDIVIDUAL_AG_INDICATORS, strlen(HFP_UPDATE_ENABLE_STATUS_FOR_INDIVIDUAL_AG_INDICATORS)) == 0){ + context->command = HFP_CMD_ENABLE_INDIVIDUAL_INDICATOR_STATUS_UPDATE; + return; + } } @@ -332,22 +336,15 @@ void hfp_run_for_context(hfp_connection_t *context){ hfp_ag_retrieve_indicators_cmd(context->rfcomm_cid); context->state = HFP_W4_RETRIEVE_INDICATORS_STATUS; break; + default: + break; + } + break; + case HFP_CMD_INDICATOR_STATUS: + switch(context->state){ case HFP_W4_RETRIEVE_INDICATORS_STATUS: hfp_ag_retrieve_indicators_status_cmd(context->rfcomm_cid); - if (context->remote_indicators_update_enabled == 0){ - context->state = HFP_W4_ENABLE_INDICATORS_STATUS_UPDATE; - break; - } - if (has_call_waiting_and_3way_calling_feature(context)){ - context->state = HFP_W4_RETRIEVE_CAN_HOLD_CALL; - break; - } - if (has_hf_indicators_feature(context)){ - context->state = HFP_W4_LIST_GENERIC_STATUS_INDICATORS; - break; - } - context->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED; - hfp_emit_event(hfp_callback, HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED, 0); + context->state = HFP_W4_ENABLE_INDICATORS_STATUS_UPDATE; break; default: break; @@ -368,6 +365,9 @@ void hfp_run_for_context(hfp_connection_t *context){ context->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED; hfp_emit_event(hfp_callback, HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED, 0); break; + case HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED: + // TODO + break; default: break; } @@ -407,6 +407,10 @@ void hfp_run_for_context(hfp_connection_t *context){ break; } break; + case HFP_CMD_ENABLE_INDIVIDUAL_INDICATOR_STATUS_UPDATE: + + break; + case HFP_CMD_NONE: switch(context->state){ case HFP_W2_DISCONNECT_RFCOMM: @@ -432,6 +436,7 @@ static void hfp_handle_rfcomm_event(uint8_t packet_type, uint16_t channel, uint8 context->state = HFP_W4_EXCHANGE_SUPPORTED_FEATURES; } + printf("hfp_handle_rfcomm_event %s\n", &packet[0]); packet[size] = 0; int pos; for (pos = 0; pos < size ; pos++){ @@ -500,9 +505,9 @@ void hfp_ag_establish_service_level_connection(bd_addr_t bd_addr){ hfp_establish_service_level_connection(bd_addr, SDP_Handsfree); } -// TODO trigger release audio connection void hfp_ag_release_service_level_connection(bd_addr_t bd_addr){ - hfp_connection_t * connection = hfp_release_service_level_connection(bd_addr); + hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr); + hfp_release_service_level_connection(connection); hfp_run_for_context(connection); } diff --git a/src/hfp_hf.c b/src/hfp_hf.c index fb0b2869f..061d20f43 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -142,13 +142,21 @@ int hfp_hs_retrieve_indicators_status_cmd(uint16_t cid){ return send_str_over_rfcomm(cid, buffer); } -int hfp_hs_toggle_indicator_status_update_cmd(uint16_t cid, uint8_t activate){ +int hfp_hs_activate_status_update_for_all_ag_indicators_cmd(uint16_t cid, uint8_t activate){ char buffer[20]; - sprintf(buffer, "AT%s=3,0,0,%d\r\n", HFP_ENABLE_INDICATOR_STATUS_UPDATE, activate); + sprintf(buffer, "AT%s=3,0,0,%d\r\n", HFP_ENABLE_STATUS_UPDATE_FOR_AG_INDICATORS, activate); // printf("toggle_indicator_status_update %s\n", buffer); return send_str_over_rfcomm(cid, buffer); } +int hfp_hs_activate_status_update_for_ag_indicator_cmd(uint16_t cid, uint32_t indicators_status, int indicators_nr, uint8_t activate){ + char buffer[20]; + int offset = snprintf(buffer, sizeof(buffer), "AT%s=", HFP_UPDATE_ENABLE_STATUS_FOR_INDIVIDUAL_AG_INDICATORS); + offset += join_bitmap(buffer+offset, sizeof(buffer)-offset, indicators_status, indicators_nr); + offset += snprintf(buffer+offset, sizeof(buffer)-offset, "\r\n"); + buffer[offset] = 0; + return send_str_over_rfcomm(cid, buffer); +} int hfp_hs_retrieve_can_hold_call_cmd(uint16_t cid){ char buffer[20]; @@ -197,13 +205,15 @@ static void hfp_run_for_context(hfp_connection_t * context){ case HFP_RETRIEVE_INDICATORS: hfp_hs_retrieve_indicators_cmd(context->rfcomm_cid); context->state = HFP_W4_RETRIEVE_INDICATORS; + context->sent_command = HFP_CMD_INDICATOR; break; case HFP_RETRIEVE_INDICATORS_STATUS: hfp_hs_retrieve_indicators_status_cmd(context->rfcomm_cid); context->state = HFP_W4_RETRIEVE_INDICATORS_STATUS; + context->sent_command = HFP_CMD_INDICATOR_STATUS; break; case HFP_ENABLE_INDICATORS_STATUS_UPDATE: - hfp_hs_toggle_indicator_status_update_cmd(context->rfcomm_cid, 1); + hfp_hs_activate_status_update_for_all_ag_indicators_cmd(context->rfcomm_cid, 1); context->state = HFP_W4_ENABLE_INDICATORS_STATUS_UPDATE; break; case HFP_RETRIEVE_CAN_HOLD_CALL: @@ -213,22 +223,39 @@ static void hfp_run_for_context(hfp_connection_t * context){ case HFP_LIST_GENERIC_STATUS_INDICATORS: hfp_hs_list_supported_generic_status_indicators_cmd(context->rfcomm_cid); context->state = HFP_W4_LIST_GENERIC_STATUS_INDICATORS; + context->sent_command = HFP_CMD_LIST_GENERIC_STATUS_INDICATOR; break; case HFP_RETRIEVE_GENERIC_STATUS_INDICATORS: hfp_hs_retrieve_supported_generic_status_indicators_cmd(context->rfcomm_cid); context->state = HFP_W4_RETRIEVE_GENERIC_STATUS_INDICATORS; + context->sent_command = HFP_CMD_GENERIC_STATUS_INDICATOR; break; case HFP_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS: hfp_hs_list_initital_supported_generic_status_indicators_cmd(context->rfcomm_cid); context->state = HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS; - break; - case HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED: - hfp_emit_event(hfp_callback, HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED, 0); + context->sent_command = HFP_CMD_GENERIC_STATUS_INDICATOR_STATE; break; case HFP_W2_DISCONNECT_RFCOMM: context->state = HFP_W4_RFCOMM_DISCONNECTED; rfcomm_disconnect_internal(context->rfcomm_cid); break; + case HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED: + if (context->wait_ok == 1) return; + + 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; + break; + }; + if (context->change_enable_status_update_for_individual_ag_indicators != 0xFF){ + hfp_hs_activate_status_update_for_ag_indicator_cmd(context->rfcomm_cid, + context->ag_indicators_status_update_bitmap, + context->ag_indicators_nr, + context->change_enable_status_update_for_individual_ag_indicators); + context->wait_ok = 1; + break; + } + break; default: break; } @@ -249,19 +276,19 @@ void update_command(hfp_connection_t * context){ } if (strncmp((char *)context->line_buffer, HFP_INDICATOR, strlen(HFP_INDICATOR)) == 0){ - printf("Received +CIND\n"); - context->command = HFP_CMD_INDICATOR; + printf("Received +CIND, %d\n", context->sent_command); + context->command = context->sent_command; + context->sent_command = HFP_CMD_NONE; return; } - if (strncmp((char *)context->line_buffer, HFP_AVAILABLE_CODECS, strlen(HFP_AVAILABLE_CODECS)) == 0){ printf("Received +BAC\n"); context->command = HFP_CMD_AVAILABLE_CODECS; return; } - if (strncmp((char *)context->line_buffer, HFP_ENABLE_INDICATOR_STATUS_UPDATE, strlen(HFP_ENABLE_INDICATOR_STATUS_UPDATE)) == 0){ + if (strncmp((char *)context->line_buffer, HFP_ENABLE_STATUS_UPDATE_FOR_AG_INDICATORS, strlen(HFP_ENABLE_STATUS_UPDATE_FOR_AG_INDICATORS)) == 0){ printf("Received +CMER\n"); context->command = HFP_CMD_ENABLE_INDICATOR_STATUS_UPDATE; return; @@ -275,9 +302,17 @@ void update_command(hfp_connection_t * context){ if (strncmp((char *)context->line_buffer, HFP_GENERIC_STATUS_INDICATOR, strlen(HFP_GENERIC_STATUS_INDICATOR)) == 0){ printf("Received +BIND\n"); - context->command = HFP_CMD_GENERIC_STATUS_INDICATOR; + context->command = context->sent_command; + printf("Received +BIND %d\n", context->sent_command); return; } + + if (strncmp((char *)context->line_buffer, HFP_UPDATE_ENABLE_STATUS_FOR_INDIVIDUAL_AG_INDICATORS, strlen(HFP_UPDATE_ENABLE_STATUS_FOR_INDIVIDUAL_AG_INDICATORS)) == 0){ + printf("Received +BIA\n"); + context->command = HFP_CMD_ENABLE_INDIVIDUAL_INDICATOR_STATUS_UPDATE; + return; + } + } @@ -300,21 +335,9 @@ void handle_switch_on_ok(hfp_connection_t *context){ break; case HFP_W4_RETRIEVE_INDICATORS_STATUS: - if (context->remote_indicators_update_enabled == 0){ - context->state = HFP_ENABLE_INDICATORS_STATUS_UPDATE; - break; - } - if (has_call_waiting_and_3way_calling_feature(context)){ - context->state = HFP_RETRIEVE_CAN_HOLD_CALL; - break; - } - if (has_hf_indicators_feature(context)){ - context->state = HFP_LIST_GENERIC_STATUS_INDICATORS; - break; - } - context->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED; + context->state = HFP_ENABLE_INDICATORS_STATUS_UPDATE; break; - + case HFP_W4_ENABLE_INDICATORS_STATUS_UPDATE: if (has_call_waiting_and_3way_calling_feature(context)){ context->state = HFP_RETRIEVE_CAN_HOLD_CALL; @@ -325,6 +348,7 @@ void handle_switch_on_ok(hfp_connection_t *context){ break; } context->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED; + hfp_emit_event(hfp_callback, HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED, 0); break; case HFP_W4_RETRIEVE_CAN_HOLD_CALL: @@ -333,7 +357,8 @@ void handle_switch_on_ok(hfp_connection_t *context){ break; } context->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED; - break; + hfp_emit_event(hfp_callback, HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED, 0); + break; case HFP_W4_LIST_GENERIC_STATUS_INDICATORS: context->state = HFP_RETRIEVE_GENERIC_STATUS_INDICATORS; @@ -346,8 +371,12 @@ void handle_switch_on_ok(hfp_connection_t *context){ case HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS: printf("Supported initial state generic status indicators \n"); context->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED; + hfp_emit_event(hfp_callback, HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED, 0); break; + case HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED: + context->wait_ok = 0; + break; default: break; @@ -421,16 +450,30 @@ void hfp_hf_establish_service_level_connection(bd_addr_t bd_addr){ } void hfp_hf_release_service_level_connection(bd_addr_t bd_addr){ - hfp_connection_t * connection = hfp_release_service_level_connection(bd_addr); + hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr); + hfp_release_service_level_connection(connection); hfp_run_for_context(connection); } -void hfp_hf_transfer_registration_status(bd_addr_t bd_addr){ - +void hfp_hf_enable_status_update_for_all_ag_indicators(bd_addr_t bd_addr, uint8_t enable){ + hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr); + if (!connection){ + log_error("HFP HF: connection doesn't exist."); + return; + } + connection->enable_status_update_for_ag_indicators = 1; + hfp_run_for_context(connection); } -void hfp_hf_activate_ag_indicator(bd_addr_t bd_addr){ - +void hfp_hf_enable_status_update_for_ag_indicator(bd_addr_t bd_addr, uint32_t indicators_status_bitmap, uint8_t enable){ + hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr); + if (!connection){ + log_error("HFP HF: connection doesn't exist."); + return; + } + connection->change_enable_status_update_for_individual_ag_indicators = 1; + connection->ag_indicators_status_update_bitmap = indicators_status_bitmap; + hfp_run_for_context(connection); } void hfp_hf_transfer_signal_strength_indication(bd_addr_t bd_addr){ diff --git a/src/hfp_hf.h b/src/hfp_hf.h index b0115ed7c..10c1dc039 100644 --- a/src/hfp_hf.h +++ b/src/hfp_hf.h @@ -91,14 +91,14 @@ void hfp_hf_establish_service_level_connection(bd_addr_t bd_addr); void hfp_hf_release_service_level_connection(bd_addr_t bd_addr); /** - * @brief Enable registration status update in the AG. + * @brief Deactivate/reactivate status update for all indicators in the AG. */ -void hfp_hf_transfer_registration_status(bd_addr_t bd_addr); +void hfp_hf_enable_status_update_for_all_ag_indicators(bd_addr_t bd_addr, uint8_t enable); /** - * @brief Deactivate/reactivate individual indicators in the AG. + * @brief Deactivate/reactivate status update for the individual indicators in the AG using bitmap. */ -void hfp_hf_activate_ag_indicator(bd_addr_t bd_addr); +void hfp_hf_enable_status_update_for_ag_indicator(bd_addr_t bd_addr, uint32_t indicators_status_bitmap, uint8_t enable); /** diff --git a/src/l2cap_signaling.c b/src/l2cap_signaling.c index b8befb206..0c1569505 100644 --- a/src/l2cap_signaling.c +++ b/src/l2cap_signaling.c @@ -99,7 +99,7 @@ uint16_t l2cap_create_signaling_internal(uint8_t * acl_buffer, hci_con_handle_t uint16_t pos = 12; // skip AMP commands if (cmd >= CONNECTION_PARAMETER_UPDATE_REQUEST){ - cmd -= 6; + cmd = (L2CAP_SIGNALING_COMMANDS) (((int) cmd) - 6); } const char *format = l2cap_signaling_commands_format[cmd-1]; uint16_t word; diff --git a/test/hfp/Makefile b/test/hfp/Makefile new file mode 100644 index 000000000..ed0edf450 --- /dev/null +++ b/test/hfp/Makefile @@ -0,0 +1,54 @@ +CC=g++ + +# Makefile for libusb based PTS tests +BTSTACK_ROOT = ../.. +POSIX_ROOT= ${BTSTACK_ROOT}/platforms/posix +CPPUTEST_HOME = ${BTSTACK_ROOT}/test/cpputest + +COMMON = \ + btstack_memory.c \ + linked_list.c \ + memory_pool.c \ + run_loop.c \ + run_loop_posix.c \ + hci.c \ + hci_cmds.c \ + hci_dump.c \ + l2cap.c \ + l2cap_signaling.c \ + rfcomm.c \ + remote_device_db_memory.c \ + sdp_util.c \ + utils.c \ + hfp.c \ + hfp_hf.c \ + sdp_client.c \ + sdp_parser.c \ + sdp_query_util.c \ + sdp_query_rfcomm.c \ + sdp.c \ + + +COMMON_OBJ = $(COMMON:.c=.o) + +# CC = gcc-fsf-4.9 +CFLAGS = -g -Wall -I. -I../ -I${BTSTACK_ROOT}/src -I${BTSTACK_ROOT}/include +# CFLAGS += -Werror + +VPATH += ${BTSTACK_ROOT}/src +VPATH += ${BTSTACK_ROOT}/ble +VPATH += ${BTSTACK_ROOT}/platforms/posix/src + +CFLAGS = -g -Wall -I. -I../ -I${BTSTACK_ROOT}/src -I${BTSTACK_ROOT}/include -I${BTSTACK_ROOT}/ble +LDFLAGS += -lCppUTest -lCppUTestExt + + +EXAMPLES = hfp_hf_parser_test + +all: ${BTSTACK_ROOT}/include/btstack/version.h ${EXAMPLES} + +clean: + rm -rf *.o $(EXAMPLES) *.dSYM + +hfp_hf_parser_test: ${COMMON_OBJ} hfp.o hfp_hf_parser_test.c + ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ diff --git a/test/hfp/hfp_hf_parser_test.c b/test/hfp/hfp_hf_parser_test.c new file mode 100644 index 000000000..e5e407c96 --- /dev/null +++ b/test/hfp/hfp_hf_parser_test.c @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2014 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + + +#include +#include +#include +#include + +#include "CppUTest/TestHarness.h" +#include "CppUTest/CommandLineTestRunner.h" + +#include "hfp.h" + +void hfp_parse(hfp_connection_t * context, uint8_t byte); + +static hfp_connection_t context; +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}, +}; + +TEST_GROUP(HFPParser){ + char packet[200]; + int pos; + int offset; + + void parser_init(hfp_connection_t * context){ + context->parser_state = HFP_PARSER_CMD_HEADER; + context->parser_item_index = 0; + context->line_size = 0; + context->ag_indicators_nr = 0; + context->remote_codecs_nr = 0; + memset(packet,0, sizeof(packet)); + } + + void setup(void){ + parser_init(&context); + } +}; + +TEST(HFPParser, HFP_HF_OK){ + sprintf(packet, "\r\n%s\r\n", HFP_OK); + for (pos = 0; pos < strlen(packet); pos++){ + hfp_parse(&context, packet[pos]); + } + CHECK_EQUAL(HFP_CMD_OK, context.command); +} + +TEST(HFPParser, HFP_HF_SUPPORTED_FEATURES){ + sprintf(packet, "\r\n%s:0000001111101111\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(1007, context.remote_supported_features); +} + +TEST(HFPParser, HFP_HF_AVAILABLE_CODECS){ + sprintf(packet, "\r\n%s:0,1,2\r\n", HFP_AVAILABLE_CODECS); + for (pos = 0; pos < strlen(packet); pos++){ + hfp_parse(&context, packet[pos]); + } + CHECK_EQUAL(HFP_CMD_AVAILABLE_CODECS, context.command); + CHECK_EQUAL(3, context.remote_codecs_nr); + for (pos = 0; pos < 3; pos++){ + CHECK_EQUAL(pos, context.remote_codecs[pos]); + } +} + +TEST(HFPParser, HFP_HF_INDICATORS){ + offset = 0; + offset += snprintf(packet, sizeof(packet), "%s:", HFP_INDICATOR); + for (pos = 0; pos < ag_indicators_nr - 1; pos++){ + offset += snprintf(packet+offset, sizeof(packet)-offset, "\"%s\", (%d, %d),", ag_indicators[pos].name, ag_indicators[pos].min_range, ag_indicators[pos].max_range); + } + offset += snprintf(packet+offset, sizeof(packet)-offset, "\"%s\", (%d, %d)\r\n", ag_indicators[pos].name, ag_indicators[pos].min_range, ag_indicators[pos].max_range); + + context.sent_command = HFP_CMD_INDICATOR; + context.ag_indicators_nr = 0; + + for (pos = 0; pos < strlen(packet); pos++){ + hfp_parse(&context, packet[pos]); + } + CHECK_EQUAL(HFP_CMD_INDICATOR, context.command); + CHECK_EQUAL(ag_indicators_nr, context.ag_indicators_nr); + for (pos = 0; pos < ag_indicators_nr; pos++){ + CHECK_EQUAL(ag_indicators[pos].index, context.ag_indicators[pos].index); + CHECK_EQUAL(0, strcmp(ag_indicators[pos].name, context.ag_indicators[pos].name)); + CHECK_EQUAL(ag_indicators[pos].min_range, context.ag_indicators[pos].min_range); + CHECK_EQUAL(ag_indicators[pos].max_range, context.ag_indicators[pos].max_range); + } +} + +TEST(HFPParser, HFP_HF_INDICATOR_STATUS){ + // send status + offset = 0; + offset += snprintf(packet, sizeof(packet), "%s:", HFP_INDICATOR); + for (pos = 0; pos < ag_indicators_nr - 1; pos++){ + offset += snprintf(packet+offset, sizeof(packet)-offset, "%d,", ag_indicators[pos].status); + } + offset += snprintf(packet+offset, sizeof(packet)-offset, "%d\r\n", ag_indicators[pos].status); + + context.sent_command = HFP_CMD_INDICATOR_STATUS; + + for (pos = 0; pos < strlen(packet); pos++){ + hfp_parse(&context, packet[pos]); + } + + CHECK_EQUAL(HFP_CMD_INDICATOR_STATUS, context.command); + for (pos = 0; pos < ag_indicators_nr; pos++){ + CHECK_EQUAL(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); + 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(5, context.remote_call_services_nr); + + CHECK_EQUAL(0, strcmp("1", (char*)context.remote_call_services[0].name)); + CHECK_EQUAL(0, strcmp("1x", (char*)context.remote_call_services[1].name)); + CHECK_EQUAL(0, strcmp("2", (char*)context.remote_call_services[2].name)); + CHECK_EQUAL(0, strcmp("2x", (char*)context.remote_call_services[3].name)); + CHECK_EQUAL(0, strcmp("3", (char*)context.remote_call_services[4].name)); +} + +TEST(HFPParser, HFP_HF_GENERIC_STATUS_INDICATOR){ + sprintf(packet, "\r\n%s:0,1\r\n", HFP_GENERIC_STATUS_INDICATOR); + context.sent_command = HFP_CMD_GENERIC_STATUS_INDICATOR; + + for (pos = 0; pos < strlen(packet); pos++){ + hfp_parse(&context, packet[pos]); + } + + CHECK_EQUAL(HFP_CMD_GENERIC_STATUS_INDICATOR, context.command); + CHECK_EQUAL(2, context.generic_status_indicators_nr); + + for (pos = 0; pos < context.generic_status_indicators_nr; pos++){ + CHECK_EQUAL(pos, context.generic_status_indicators[pos].uuid); + } +} + +TEST(HFPParser, HFP_HF_GENERIC_STATUS_INDICATOR_STATE){ + sprintf(packet, "\r\n%s:0,1\r\n", HFP_GENERIC_STATUS_INDICATOR); + context.sent_command = HFP_CMD_GENERIC_STATUS_INDICATOR_STATE; + + for (pos = 0; pos < strlen(packet); pos++){ + hfp_parse(&context, packet[pos]); + } + + CHECK_EQUAL(HFP_CMD_GENERIC_STATUS_INDICATOR_STATE, context.command); + CHECK_EQUAL(1, context.generic_status_indicators[0].state); +} + +// TEST(HFPParser, HFP_AG_ENABLE_INDICATOR_STATUS_UPDATE){ +// } + +// TEST(HFPParser, HFP_AG_ENABLE_INDIVIDUAL_INDICATOR_STATUS_UPDATE){ +// } + +int main (int argc, const char * argv[]){ + return CommandLineTestRunner::RunAllTests(argc, argv); +}