diff --git a/src/hfp.c b/src/hfp.c index 52cace1ec..866b4b4e4 100644 --- a/src/hfp.c +++ b/src/hfp.c @@ -60,16 +60,63 @@ #include "debug.h" #include "hfp_ag.h" +#define HFP_HF_FEATURES_SIZE 10 +#define HFP_AG_FEATURES_SIZE 12 + +static const char * hfp_hf_features[] = { + "EC and/or NR function", + "Three-way calling", + "CLI presentation capability", + "Voice recognition activation", + "Remote volume control", + "Enhanced call status", + "Enhanced call control", + "Codec negotiation", + "HF Indicators", + "eSCO S4 (and T2) Settings Supported", + "Reserved for future definition" +}; + +static const char * hfp_ag_features[] = { + "Three-way calling", + "EC and/or NR function", + "Voice recognition function", + "In-band ring tone capability", + "Attach a number to a voice tag", + "Ability to reject a call", + "Enhanced call status", + "Enhanced call control", + "Extended Error Result Codes", + "Codec negotiation", + "HF Indicators", + "eSCO S4 (and T2) Settings Supported", + "Reserved for future definition" +}; + static hfp_callback_t hfp_callback; static linked_list_t hfp_connections = NULL; +const char * hfp_hf_feature(int index){ + if (index > HFP_HF_FEATURES_SIZE){ + return hfp_hf_features[HFP_HF_FEATURES_SIZE]; + } + return hfp_hf_features[index]; +} + +const char * hfp_ag_feature(int index){ + if (index > HFP_AG_FEATURES_SIZE){ + return hfp_ag_features[HFP_AG_FEATURES_SIZE]; + } + return hfp_ag_features[index]; +} + int send_str_over_rfcomm(uint16_t cid, char * command){ // if (!rfcomm_can_send_packet_now(cid)) return 1; int err = rfcomm_send_internal(cid, (uint8_t*) command, strlen(command)); if (err){ printf("rfcomm_send_internal -> error 0X%02x", err); } else { - printf("Sent %s", command); + printf("\nSent %s", command); } return err; } diff --git a/src/hfp.h b/src/hfp.h index 4121717a0..62e8b42ff 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -99,13 +99,13 @@ extern "C" { typedef enum { - HFP_IDLE, + HFP_IDLE = 0, //50 HFP_SDP_QUERY_RFCOMM_CHANNEL, HFP_W4_SDP_QUERY_COMPLETE, HFP_W4_RFCOMM_CONNECTED, HFP_EXCHANGE_SUPPORTED_FEATURES, - HFP_W4_EXCHANGE_SUPPORTED_FEATURES, + HFP_W4_EXCHANGE_SUPPORTED_FEATURES, // 5 HFP_NOTIFY_ON_CODECS, HFP_W4_NOTIFY_ON_CODECS, @@ -113,14 +113,14 @@ typedef enum { HFP_RETRIEVE_INDICATORS, HFP_W4_RETRIEVE_INDICATORS, - HFP_RETRIEVE_INDICATORS_STATUS, + HFP_RETRIEVE_INDICATORS_STATUS, // 10 HFP_W4_RETRIEVE_INDICATORS_STATUS, HFP_ENABLE_INDICATORS_STATUS_UPDATE, HFP_W4_ENABLE_INDICATORS_STATUS_UPDATE, HFP_RETRIEVE_CAN_HOLD_CALL, - HFP_W4_RETRIEVE_CAN_HOLD_CALL, + HFP_W4_RETRIEVE_CAN_HOLD_CALL, // 15 HFP_LIST_GENERIC_STATUS_INDICATORS, HFP_W4_LIST_GENERIC_STATUS_INDICATORS, @@ -128,7 +128,7 @@ typedef enum { HFP_RETRIEVE_GENERIC_STATUS_INDICATORS, HFP_W4_RETRIEVE_GENERIC_STATUS_INDICATORS, - HFP_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS, + HFP_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS, // 20 HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS, HFP_ACTIVE, @@ -195,6 +195,7 @@ typedef struct hfp_connection { hfp_callback_t callback; } hfp_connection_t; + void hfp_create_service(uint8_t * service, uint16_t service_uuid, int rfcomm_channel_nr, const char * name, uint16_t supported_features); void hfp_register_packet_handler(hfp_callback_t callback); hfp_connection_t * hfp_handle_hci_event(uint8_t packet_type, uint8_t *packet, uint16_t size); @@ -208,6 +209,9 @@ linked_list_t * hfp_get_connections(); int send_str_over_rfcomm(uint16_t cid, char * command); void join(char * buffer, int buffer_size, int buffer_offset, uint8_t * values, int values_nr, int value_size); +const char * hfp_hf_feature(int index); +const char * hfp_ag_feature(int index); + #if defined __cplusplus } #endif diff --git a/src/hfp_hf.c b/src/hfp_hf.c index 02d948e2e..12196b360 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -71,6 +71,21 @@ static uint8_t hfp_indicators_nr = 0; static uint8_t hfp_indicators[HFP_MAX_NUM_INDICATORS]; static uint8_t hfp_indicators_status; +static int get_bit(uint16_t bitmap, int position){ + return (bitmap >> position) & 1; +} + +int has_call_waiting_and_3way_calling_feature(hfp_connection_t * connection){ + return get_bit(hfp_supported_features,1) && get_bit(connection->remote_supported_features,0); +} + +int has_codec_negotiation_feature(hfp_connection_t * connection){ + return get_bit(hfp_supported_features,7) && get_bit(connection->remote_supported_features,9); +} + +int has_hf_indicators_feature(hfp_connection_t * connection){ + return get_bit(hfp_supported_features,8) && get_bit(connection->remote_supported_features,10); +} static void packet_handler(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); @@ -81,11 +96,6 @@ void hfp_hf_create_service(uint8_t * service, int rfcomm_channel_nr, const char hfp_create_service(service, SDP_Handsfree, rfcomm_channel_nr, name, supported_features); } - -static int get_bit(uint16_t bitmap, int position){ - return (bitmap >> position) & 1; -} - static int store_bit(uint32_t bitmap, int position, uint8_t value){ if (value){ bitmap |= 1 << position; @@ -125,13 +135,14 @@ 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){ +int hfp_hs_toggle_indicator_status_update_cmd(uint16_t cid, uint8_t activate){ char buffer[20]; - sprintf(buffer, "AT%s\r\n", HFP_Enable_Indicator_Status_Update); + sprintf(buffer, "AT%s=3,0,0,%d\r\n", HFP_Enable_Indicator_Status_Update, activate); // printf("toggle_indicator_status_update %s\n", buffer); return send_str_over_rfcomm(cid, buffer); } + int hfp_hs_retrieve_can_hold_call_cmd(uint16_t cid){ char buffer[20]; sprintf(buffer, "AT%s\r\n", HFP_Support_Call_Hold_And_Multiparty_Services); @@ -162,26 +173,11 @@ int hfp_hs_list_initital_supported_generic_status_indicators_cmd(uint16_t cid){ return send_str_over_rfcomm(cid, buffer); } -// TODO: +CIEV service, call, call setup change => new indicator value -// TODO: AT+CMER => disable ind. status update - -int has_call_waiting_and_3way_calling_feature(hfp_connection_t * connection){ - return get_bit(hfp_supported_features,1) && get_bit(connection->remote_supported_features,0); -} - -int has_codec_negotiation_feature(hfp_connection_t * connection){ - return get_bit(hfp_supported_features,7) && get_bit(connection->remote_supported_features,9); -} - -int has_hf_indicators_feature(hfp_connection_t * connection){ - return get_bit(hfp_supported_features,8) && get_bit(connection->remote_supported_features,10); -} - static void hfp_run_for_context(hfp_connection_t * connection){ if (!connection) return; - printf("hfp send cmd: context %p, RFCOMM cid %u\n", connection, connection->rfcomm_cid ); + // printf("hfp send cmd: context %p, RFCOMM cid %u \n", connection, connection->rfcomm_cid ); if (!rfcomm_can_send_packet_now(connection->rfcomm_cid)) return; - + int err = 0; switch (connection->state){ case HFP_EXCHANGE_SUPPORTED_FEATURES: @@ -202,18 +198,35 @@ static void hfp_run_for_context(hfp_connection_t * connection){ break; case HFP_ENABLE_INDICATORS_STATUS_UPDATE: if (connection->remote_indicators_update_enabled == 0){ - err = hfp_hs_toggle_indicator_status_update_cmd(connection->rfcomm_cid); - if (!err) connection->state = HFP_W4_ENABLE_INDICATORS_STATUS_UPDATE; + err = hfp_hs_toggle_indicator_status_update_cmd(connection->rfcomm_cid, 1); + if (!err) { + connection->state = HFP_W4_ENABLE_INDICATORS_STATUS_UPDATE; + connection->wait_ok = 1; + } + break; } - break; + printf("fall through to HFP_RETRIEVE_CAN_HOLD_CALL\n"); + connection->state = HFP_RETRIEVE_CAN_HOLD_CALL; case HFP_RETRIEVE_CAN_HOLD_CALL: - err = hfp_hs_retrieve_can_hold_call_cmd(connection->rfcomm_cid); - if (!err) connection->state = HFP_W4_RETRIEVE_CAN_HOLD_CALL; - break; + if (has_call_waiting_and_3way_calling_feature(connection)){ + err = hfp_hs_retrieve_can_hold_call_cmd(connection->rfcomm_cid); + if (!err) connection->state = HFP_W4_RETRIEVE_CAN_HOLD_CALL; + break; + } + printf("fall through to HFP_LIST_GENERIC_STATUS_INDICATORS (no CAN_HOLD_CALL feature)\n"); + connection->state = HFP_LIST_GENERIC_STATUS_INDICATORS; case HFP_LIST_GENERIC_STATUS_INDICATORS: - err = hfp_hs_list_supported_generic_status_indicators_cmd(connection->rfcomm_cid); - if (!err) connection->state = HFP_W4_LIST_GENERIC_STATUS_INDICATORS; + if (has_hf_indicators_feature(connection)){ + err = hfp_hs_list_supported_generic_status_indicators_cmd(connection->rfcomm_cid); + if (!err) connection->state = HFP_W4_LIST_GENERIC_STATUS_INDICATORS; + break; + } + printf("fall through to HFP_ACTIVE (no hf indicators feature)\n"); + connection->state = HFP_ACTIVE; + case HFP_ACTIVE: + printf("HFP_ACTIVE\n"); break; + case HFP_RETRIEVE_GENERIC_STATUS_INDICATORS: err = hfp_hs_retrieve_supported_generic_status_indicators_cmd(connection->rfcomm_cid); if (!err) connection->state = HFP_W4_RETRIEVE_GENERIC_STATUS_INDICATORS; @@ -232,7 +245,7 @@ void hfp_parse_indicators(hfp_connection_t * context, uint8_t *packet, uint16_t char min_range[3]; char max_range[3]; int i, pos; - int index = 0; + int index = 1; int state = 0; for (pos = 0; pos < size; pos++){ @@ -284,7 +297,7 @@ void hfp_parse_indicators(hfp_connection_t * context, uint8_t *packet, uint16_t } void hfp_parse_indicators_status(hfp_connection_t * context, uint8_t *packet, uint16_t size){ - int index = 0; + int index = 1; char * token = strtok((char*)&packet[0], ","); while (token){ printf("Indicator %d status: %d \n", index, atoi(token)); @@ -295,25 +308,18 @@ void hfp_parse_indicators_status(hfp_connection_t * context, uint8_t *packet, ui hfp_connection_t * handle_message(hfp_connection_t * context, uint8_t *packet, uint16_t size){ int offset = 0; + printf("Parsed %s, state %u\n", (char *)packet, context->state); + if (context->wait_ok){ if (strncmp((char *)packet, HFP_OK, strlen(HFP_OK)) == 0){ - printf("Parsed %s\n", HFP_OK); + context->wait_ok = 0; switch (context->state){ case HFP_W4_NOTIFY_ON_CODECS: context->state = HFP_RETRIEVE_INDICATORS; break; case HFP_W4_ENABLE_INDICATORS_STATUS_UPDATE: - 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_RETRIEVE_CAN_HOLD_CALL; break; - case HFP_W4_LIST_GENERIC_STATUS_INDICATORS: context->state = HFP_RETRIEVE_GENERIC_STATUS_INDICATORS; break; @@ -325,7 +331,6 @@ hfp_connection_t * handle_message(hfp_connection_t * context, uint8_t *packet, u default: break; } - context->wait_ok = 0; return context; } } @@ -333,7 +338,12 @@ hfp_connection_t * handle_message(hfp_connection_t * context, uint8_t *packet, u 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]); - printf("Parsed %s: %d (expected 224)\n", HFP_Supported_Features, context->remote_supported_features); + 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)); + } + } if (has_codec_negotiation_feature(context)){ context->state = HFP_NOTIFY_ON_CODECS; } else { @@ -344,13 +354,6 @@ hfp_connection_t * handle_message(hfp_connection_t * context, uint8_t *packet, u } if (strncmp((char *)packet, HFP_Indicator, strlen(HFP_Indicator)) == 0){ - // https://www.bluetooth.org/en-us/specification/assigned-numbers/hands-free-profile - /* - * 0x01 Enhanced Safety​, on/off - * 0x02 Battery Level, 0-100 - */ - // printf("Parsed %s, expected ("service",(0,1)),("call",(0,1)),("callsetup",(0,3)),("callheld",(0,2)),("signal",(0,5)),("roam",(0,1)),("battchg",(0,5))\n", HFP_Indicator); - offset = strlen(HFP_Indicator) + 1; switch (context->state){ case HFP_W4_RETRIEVE_INDICATORS: @@ -358,11 +361,10 @@ hfp_connection_t * handle_message(hfp_connection_t * context, uint8_t *packet, u context->remote_indicators_status = 0; context->state = HFP_RETRIEVE_INDICATORS_STATUS; break; - case HFP_W4_RETRIEVE_INDICATORS_STATUS:{ + 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; } @@ -379,6 +381,12 @@ hfp_connection_t * handle_message(hfp_connection_t * context, uint8_t *packet, u } 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 = char * token = strtok((char*)&packet[offset], ","); diff --git a/test/pts/hfp_hf_test.c b/test/pts/hfp_hf_test.c index 107ffd99f..a8c56dc1c 100644 --- a/test/pts/hfp_hf_test.c +++ b/test/pts/hfp_hf_test.c @@ -132,8 +132,8 @@ int btstack_main(int argc, const char * argv[]){ l2cap_init(); rfcomm_init(); - hfp_hf_init(rfcomm_channel_nr, HFP_Default_HF_Supported_Features, codecs, sizeof(codecs), indicators, sizeof(indicators)/sizeof(uint16_t), 1); - // hfp_hf_init(rfcomm_channel_nr, 0x0009, codecs, sizeof(codecs), indicators, sizeof(indicators)/sizeof(uint16_t), 1); + // hfp_hf_init(rfcomm_channel_nr, HFP_Default_HF_Supported_Features, codecs, sizeof(codecs), indicators, sizeof(indicators)/sizeof(uint16_t), 1); + hfp_hf_init(rfcomm_channel_nr, 0x0009, codecs, sizeof(codecs), indicators, sizeof(indicators)/sizeof(uint16_t), 1); hfp_register_packet_handler(packet_handler);