diff --git a/src/hfp.c b/src/hfp.c index afe527533..3faf799de 100644 --- a/src/hfp.c +++ b/src/hfp.c @@ -182,7 +182,9 @@ static hfp_connection_t * create_hfp_connection_context(){ // init state context->state = HFP_IDLE; context->line_size = 0; - + context->cmd_value_state = HFP_CMD_HEADER; + context->cmd_state = HFP_CMD_NONE; + context->negotiated_codec = HFP_CODEC_CVSD; context->remote_supported_features = 0; context->remote_indicators_update_enabled = 0; diff --git a/src/hfp.h b/src/hfp.h index 3e74ee3bd..09ec909ac 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -112,6 +112,25 @@ extern "C" { #define HFP_CODEC_CVSD 0x01 #define HFP_CODEC_MSBC 0x02 +typedef enum { + HFP_CMD_NONE, + HFP_CMD_OK, + HFP_CMD_SUPPORTED_FEATURES, + HFP_CMD_AVAILABLE_CODECS, + HFP_CMD_INDICATOR, + HFP_CMD_ENABLE_INDICATOR_STATUS_UPDATE, + HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES, + HFP_CMD_GENERIC_STATUS_INDICATOR +} hfp_cmd_state_t; + +typedef enum { + HFP_CMD_HEADER, + HFP_CMD_SEQUENCE, + HFP_CMD_INDICATOR_NAME, + HFP_CMD_INDICATOR_MIN_RANGE, + HFP_CMD_INDICATOR_MAX_RANGE +} hfp_cmd_value_state_t; + typedef enum { HFP_IDLE = 0, //50 @@ -186,10 +205,12 @@ typedef struct hfp_connection { linked_item_t item; hfp_state_t state; - int line_size; + hfp_cmd_state_t cmd_state; + hfp_cmd_value_state_t cmd_value_state; + uint8_t line_buffer[HFP_MAX_INDICATOR_DESC_SIZE]; - uint8_t line_state; - + int line_size; + bd_addr_t remote_addr; uint16_t con_handle; uint16_t rfcomm_channel_nr; @@ -201,7 +222,9 @@ typedef struct hfp_connection { uint32_t remote_supported_features; uint8_t remote_indicators_update_enabled; uint8_t remote_indicators_nr; - uint16_t remote_indicators[20]; + 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; diff --git a/src/hfp_hf.c b/src/hfp_hf.c index 40226f7f2..2153f5172 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -255,106 +255,185 @@ static void hfp_run_for_context(hfp_connection_t * connection){ } } -void hfp_parse_indicators(hfp_connection_t * context, uint8_t *packet, uint16_t size){ - char indicator[10]; - char min_range[3]; - char max_range[3]; - int i, pos; - int index = 1; - int state = 0; +void update_cmd_state(hfp_connection_t * context){ + char * cmd = (char * )&context->line_buffer[0]; + context->cmd_state = HFP_CMD_NONE; + + if (strncmp(cmd, HFP_OK, strlen(HFP_OK)) == 0){ + context->cmd_state = HFP_CMD_OK; + return; + } + + if (strncmp(cmd, HFP_SUPPORTED_FEATURES, strlen(HFP_SUPPORTED_FEATURES)) == 0){ + context->cmd_state = HFP_CMD_SUPPORTED_FEATURES; + return; + } + + if (strncmp(cmd, HFP_INDICATOR, strlen(HFP_INDICATOR)) == 0){ + context->cmd_state = HFP_CMD_INDICATOR; + return; + } + + + if (strncmp(cmd, HFP_AVAILABLE_CODECS, strlen(HFP_AVAILABLE_CODECS)) == 0){ + context->cmd_state = HFP_CMD_AVAILABLE_CODECS; + return; + } + + if (strncmp(cmd, HFP_ENABLE_INDICATOR_STATUS_UPDATE, strlen(HFP_ENABLE_INDICATOR_STATUS_UPDATE)) == 0){ + context->cmd_state = HFP_CMD_ENABLE_INDICATOR_STATUS_UPDATE; + return; + } + + if (strncmp(cmd, HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES, strlen(HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES)) == 0){ + context->cmd_state = HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES; + return; + } + + if (strncmp(cmd, HFP_GENERIC_STATUS_INDICATOR, strlen(HFP_GENERIC_STATUS_INDICATOR)) == 0){ + context->cmd_state = HFP_CMD_GENERIC_STATUS_INDICATOR; + return; + } +} + +static void hfp_parse2(hfp_connection_t * context, uint8_t byte){ + int i; + if (byte == ' ') return; + if ( (byte == '\n' || byte == '\r') && context->cmd_value_state > HFP_CMD_SEQUENCE) return; - for (pos = 0; pos < size; pos++){ - uint8_t byte = packet[pos]; + switch (context->cmd_value_state){ + case HFP_CMD_HEADER: // header + if (byte == ':' || ( (byte == '\n' || byte == '\r' ) && context->line_size > 0) ) { + context->cmd_value_state = HFP_CMD_SEQUENCE; + context->line_size = 0; + update_cmd_state(context); + break; + } + context->line_buffer[context->line_size++] = byte; + break; - switch (state){ - case 0: // pre-indicator - if (byte != '"') break; - state++; - i = 0; + case HFP_CMD_SEQUENCE: // parse comma separated sequence, ignore breacktes + if (byte == '"'){ // indicators + context->cmd_value_state = HFP_CMD_INDICATOR_NAME; + context->remote_indicators_nr = 0; break; - case 1: // indicator - if (byte == '"'){ - state++; - indicator[i] = 0; + } + + if (byte == '('){ // tuple sepparated 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)); + } + } + break; + case HFP_W4_NOTIFY_ON_CODECS: + printf("Supported codec: %s\n", context->line_buffer); + 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: + printf("HFP_W4_RETRIEVE_GENERIC_STATUS_INDICATORS: %s\n", context->line_buffer); + break; + case HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS: + printf("HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS: %s\n", context->line_buffer); + break; + + default: + printf("sequence value : %s\n", context->line_buffer); + break; + } + + if (byte == '\n' || byte == '\r'){ + context->cmd_value_state = 0; break; } - indicator[i++] = byte; - break; - case 2: // pre-range - if (byte != '(') break; - state++; - i = 0; - break; - case 3: // min-range - if (byte == ',' || byte == '-'){ - state++; - min_range[i] = 0; - i = 0; + if (byte == ')') { + context->cmd_value_state = 1; break; } - min_range[i++] = byte; + + context->line_buffer[context->line_size++] = byte; + break; + + + case HFP_CMD_INDICATOR_NAME: // parse indicator name + if (byte == '('){ + // indicator name end + memcpy(context->remote_indicators[context->remote_indicators_nr], context->line_buffer, context->line_size); + context->cmd_value_state = HFP_CMD_INDICATOR_MIN_RANGE; + context->line_size = 0; break; - case 4: - if (byte == ')'){ - state = 0; - max_range[i] = 0; - printf("Indicator %d: %s, range [%d, %d] \n", index, indicator, atoi(min_range), atoi(max_range)); - index++; - i = 0; - break; - } - max_range[i++] = byte; + } + context->line_buffer[context->line_size++] = byte; + break; + case HFP_CMD_INDICATOR_MIN_RANGE: // minrange + if (byte == ',' || byte == '-'){ + context->line_buffer[context->line_size] = 0; + context->remote_indicators_range[context->remote_indicators_nr][0] = atoi((char *)&context->line_buffer[0]); + context->cmd_value_state = HFP_CMD_INDICATOR_MAX_RANGE; + context->line_size = 0; break; - default: + } + // min-range + context->line_buffer[context->line_size++] = byte; + break; + case HFP_CMD_INDICATOR_MAX_RANGE: + if (byte == ')'){ + context->line_buffer[context->line_size] = 0; + context->remote_indicators_range[context->remote_indicators_nr][1] = atoi((char *)&context->line_buffer[0]); + context->cmd_value_state = HFP_CMD_SEQUENCE; + context->line_size = 0; + + printf("Indicator %d: %s, range [%d, %d] \n", context->remote_indicators_nr, + context->remote_indicators[context->remote_indicators_nr], + context->remote_indicators_range[context->remote_indicators_nr][0], + context->remote_indicators_range[context->remote_indicators_nr][1]); + context->remote_indicators_nr++; break; + } + // max-range + context->line_buffer[context->line_size++] = byte; + break; } } } -void hfp_parse_indicators_status(hfp_connection_t * context, uint8_t *packet, uint16_t size){ - int index = 1; - char * token = strtok((char*)&packet[0], ","); - while (token){ - printf("Indicator %d status: %d \n", index, atoi(token)); - index++; - token = strtok(NULL, ","); - } +int wait_for_more_cmd_data(hfp_connection_t * context){ + return context->cmd_value_state == 0; } -void hfp_parse_comma_separated_tuple(hfp_connection_t * context, uint8_t *packet, uint16_t size){ - char feature[5]; - int i, pos; - int state = 0; - for (pos = 0; pos < size; pos++){ - uint8_t byte = packet[pos]; - - switch (state){ - case 0: // pre-feature - if (byte != '(') break; - state++; - i = 0; - break; - case 1: // feature - if (byte == ',' || byte == ')'){ - feature[i] = 0; - printf("call_hold_and_multiparty value: %s\n", feature); - i = 0; - if (byte == ')') state++; - break; - } - feature[i++] = byte; - break; - default: - break; - } - } +int wait_for_more_data(hfp_connection_t * context){ + return context->cmd_value_state != 0; } +static void hfp_handle_rfcomm_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + hfp_connection_t * context = provide_hfp_connection_context_for_rfcomm_cid(channel); + if (!context) return; -hfp_connection_t * handle_message(hfp_connection_t * context, uint8_t *packet, uint16_t size){ - int offset = 0; + packet[size] = 0; + printf("Received %s\n", packet); + + int pos = 0; + while (wait_for_more_cmd_data(context) && pos < size){ + hfp_parse2(context, packet[pos++]); + } + if (wait_for_more_cmd_data(context)) return; + if (context->wait_ok){ - if (strncmp((char *)packet, HFP_OK, strlen(HFP_OK)) == 0){ + if (context->cmd_state == HFP_CMD_OK){ context->wait_ok = 0; switch (context->state){ case HFP_W4_NOTIFY_ON_CODECS: @@ -366,151 +445,63 @@ hfp_connection_t * handle_message(hfp_connection_t * context, uint8_t *packet, u case HFP_W4_LIST_GENERIC_STATUS_INDICATORS: context->state = HFP_RETRIEVE_GENERIC_STATUS_INDICATORS; break; - case HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS: context->state = HFP_ACTIVE; break; default: break; } - return context; + return; } } - if (strncmp((char *)packet, HFP_SUPPORTED_FEATURES, strlen(HFP_SUPPORTED_FEATURES)) == 0){ - offset = strlen(HFP_SUPPORTED_FEATURES) + 1; // +1 for = - context->remote_supported_features = atoi((char*)&packet[offset]); - int i = 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->state = HFP_NOTIFY_ON_CODECS; - context->wait_ok = 1; - return context; + while (wait_for_more_data(context) && pos < size){ + hfp_parse2(context, packet[pos++]); } + if (wait_for_more_data(context)) return; - if (strncmp((char *)packet, HFP_INDICATOR, strlen(HFP_INDICATOR)) == 0){ - offset = strlen(HFP_INDICATOR) + 1; - switch (context->state){ - case HFP_W4_RETRIEVE_INDICATORS: - hfp_parse_indicators(context, &packet[offset], size-offset); - context->remote_indicators_status = 0; - context->state = HFP_RETRIEVE_INDICATORS_STATUS; - break; - case HFP_W4_RETRIEVE_INDICATORS_STATUS: - hfp_parse_indicators_status(context, &packet[offset], size-offset); - context->state = HFP_ENABLE_INDICATORS_STATUS_UPDATE; - break; - default: - break; - } - context->wait_ok = 1; - return context; - } - - if (strncmp((char *)packet, HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES, strlen(HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES)) == 0){ - offset = strlen(HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES) + 1; // +1 for = - hfp_parse_comma_separated_tuple(context, &packet[offset], size-offset); - context->state = HFP_LIST_GENERIC_STATUS_INDICATORS; - context->wait_ok = 1; - return context; - } - - if (strncmp((char *)packet, HFP_GENERIC_STATUS_INDICATOR, strlen(HFP_GENERIC_STATUS_INDICATOR)) == 0){ - // https://www.bluetooth.org/en-us/specification/assigned-numbers/hands-free-profile - /* HF Indicators - * 0x01 Enhanced Safety​, on/off - * 0x02 Battery Level, 0-100 - */ - offset = strlen(HFP_GENERIC_STATUS_INDICATOR) + 1; // +1 for = - - switch (context->state){ - case HFP_W4_RETRIEVE_GENERIC_STATUS_INDICATORS: - printf("Supported generic status indicators \n"); - hfp_parse_comma_separated_tuple(context, &packet[offset], size-offset); - context->remote_hf_indicators_status = 0; - context->state = HFP_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS; - break; - case HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS:{ - printf("Supported initial state generic status indicators \n"); - char * token = strtok((char*)&packet[offset], ","); - - uint16_t indicator = atoi(token); - int status = atoi(strtok(NULL, ",")); - if (!status) break; - - int i; - for (i=0; iremote_hf_indicators_nr; i++){ - if (context->remote_hf_indicators[i] == indicator){ - store_bit(context->remote_hf_indicators_status, i, atoi(token)); - } - } - break; - } - default: - break; - } - context->wait_ok = 1; - return context; - } - - return context; -} - -static void hfp_parse(hfp_connection_t * context, uint8_t byte){ - if (byte != '\n' && byte != '\r'){ - context->line_buffer[context->line_size] = byte; - context->line_size++; - return; - } - - context->line_buffer[context->line_size] = '\0'; - if (context->line_size > 0){ - handle_message(context, context->line_buffer, context->line_size); - } - context->line_size = 0; -} - - -static void hfp_parse2(hfp_connection_t * context, uint8_t byte){ - if (byte == '\n' || byte == '\r') return; - - switch (context->line_state){ - case 0: // header - if (byte == ':') { - context->line_state = 1; - return; - } - context->line_buffer[context->line_size] = byte; - context->line_size++; + switch (context->cmd_state){ + case HFP_CMD_SUPPORTED_FEATURES: + context->state = HFP_NOTIFY_ON_CODECS; break; - case 1: // values - if (byte == '"'){ // indicators - context->line_state = 2; - return; - } - if (byte == '('){ // tuple sepparated mit comma - context->line_state = 5; - } - case 2: + case HFP_CMD_AVAILABLE_CODECS: + context->state = HFP_RETRIEVE_INDICATORS; break; + case HFP_CMD_INDICATOR: + switch (context->state){ + case HFP_W4_RETRIEVE_INDICATORS: + context->remote_indicators_status = 0; + context->state = HFP_RETRIEVE_INDICATORS_STATUS; + break; + case HFP_W4_RETRIEVE_INDICATORS_STATUS: + context->state = HFP_ENABLE_INDICATORS_STATUS_UPDATE; + break; + default: + break; + } + break; + case HFP_CMD_ENABLE_INDICATOR_STATUS_UPDATE: + context->state = HFP_RETRIEVE_CAN_HOLD_CALL; + break; + case HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES: + context->state = HFP_LIST_GENERIC_STATUS_INDICATORS; + break; + case HFP_CMD_GENERIC_STATUS_INDICATOR: + switch (context->state){ + case HFP_W4_RETRIEVE_GENERIC_STATUS_INDICATORS: + context->remote_hf_indicators_status = 0; + context->state = HFP_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS; + break; + case HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS: + break; + default: + break; + } + break; + default: + return; } -} - -static void hfp_handle_rfcomm_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ - hfp_connection_t * context = provide_hfp_connection_context_for_rfcomm_cid(channel); - if (!context) return; - - packet[size] = 0; - printf("Received %s\n", packet); - - int i; - for (i=0;iwait_ok = 1; } static void hfp_run(){