diff --git a/include/btstack/hci_cmds.h b/include/btstack/hci_cmds.h index 9e965971b..ad7a1f1b5 100644 --- a/include/btstack/hci_cmds.h +++ b/include/btstack/hci_cmds.h @@ -614,6 +614,11 @@ extern "C" { #define HSP_SUBEVENT_ERROR 0x07 #define HSP_SUBEVENT_RING 0x08 +#define HCI_EVENT_HFP_META 0xE9 + +#define HFP_SUBEVENT_AUDIO_CONNECTION_COMPLETE 0x01 +#define HFP_SUBEVENT_SUPPORTED_FEATURES_EXCHANGE 0x02 + // ANCS Client #define ANCS_CLIENT_CONNECTED 0xF0 diff --git a/src/hfp.c b/src/hfp.c index 39c758b27..f252c867e 100644 --- a/src/hfp.c +++ b/src/hfp.c @@ -63,10 +63,57 @@ static hfp_callback_t hfp_callback; static linked_list_t hfp_connections = NULL; +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); + } + return err; +} + +void join(char * buffer, int buffer_size, int buffer_offset, uint8_t * values, int values_nr){ + int req_size = values_nr * 2; + if (buffer_size - buffer_offset < req_size ) { + log_error("join: buffer too small (size: %u. req: %u)", buffer_size, req_size); + return; + } + int pos = buffer_offset; + int i; + for (i = 0; i < values_nr-1; i++){ + buffer[pos++] = values[i]; + buffer[pos++] = ','; + } + buffer[pos++] = values[i]; + buffer[pos] = '\0'; +} + +static void hfp_emit_event(hfp_callback_t callback, uint8_t event_subtype, uint8_t value){ + if (!callback) return; + uint8_t event[4]; + event[0] = HCI_EVENT_HFP_META; + event[1] = sizeof(event) - 2; + event[2] = event_subtype; + event[3] = value; // status 0 == OK + (*callback)(event, sizeof(event)); +} + + static linked_item_t * get_hfp_connections(){ return (linked_item_t *) &hfp_connections; } +hfp_connection_t * get_hfp_connection_context_for_rfcomm_cid(uint16_t cid){ + linked_item_t *it; + for (it = get_hfp_connections(); it ; it = it->next){ + hfp_connection_t * connection = (hfp_connection_t *) it; + if (connection->rfcomm_cid == cid){ + return connection; + } + } + return NULL; +} + static hfp_connection_t * get_hfp_connection_context_for_handle(uint16_t handle){ linked_item_t *it; for (it = get_hfp_connections(); it ; it = it->next){ @@ -123,6 +170,24 @@ void hfp_register_packet_handler(hfp_callback_t callback){ hfp_callback = callback; } +/* @param suported_features + * HF bit 0: EC and/or NR function (yes/no, 1 = yes, 0 = no) + * HF bit 1: Call waiting or three-way calling(yes/no, 1 = yes, 0 = no) + * HF bit 2: CLI presentation capability (yes/no, 1 = yes, 0 = no) + * HF bit 3: Voice recognition activation (yes/no, 1= yes, 0 = no) + * HF bit 4: Remote volume control (yes/no, 1 = yes, 0 = no) + * HF bit 5: Wide band speech (yes/no, 1 = yes, 0 = no) + */ + /* Bit position: + * AG bit 0: Three-way calling (yes/no, 1 = yes, 0 = no) + * AG bit 1: EC and/or NR function (yes/no, 1 = yes, 0 = no) + * AG bit 2: Voice recognition function (yes/no, 1 = yes, 0 = no) + * AG bit 3: In-band ring tone capability (yes/no, 1 = yes, 0 = no) + * AG bit 4: Attach a phone number to a voice tag (yes/no, 1 = yes, 0 = no) + * AG bit 5: Wide band speech (yes/no, 1 = yes, 0 = no) + */ + + void hfp_create_service(uint8_t * service, uint16_t service_uuid, int rfcomm_channel_nr, const char * name, uint16_t supported_features){ uint8_t* attribute; de_create_sequence(service); @@ -187,14 +252,6 @@ void hfp_create_service(uint8_t * service, uint16_t service_uuid, int rfcomm_cha de_add_data(service, DE_STRING, strlen(name), (uint8_t *) name); de_add_number(service, DE_UINT, DE_SIZE_16, supported_features); - /* Bit position: - * 0: EC and/or NR function (yes/no, 1 = yes, 0 = no) - * 1: Call waiting or three-way calling(yes/no, 1 = yes, 0 = no) - * 2: CLI presentation capability (yes/no, 1 = yes, 0 = no) - * 3: Voice recognition activation (yes/no, 1= yes, 0 = no) - * 4: Remote volume control (yes/no, 1 = yes, 0 = no) - * 5: Wide band speech (yes/no, 1 = yes, 0 = no) - */ } static hfp_connection_t * connection_doing_sdp_query = NULL; @@ -230,9 +287,13 @@ static void handle_query_rfcomm_event(sdp_query_event_t * event, void * context) } } -hfp_connection_t * handle_hci_event(uint8_t packet_type, uint8_t *packet, uint16_t size){ - if (packet_type != HCI_EVENT_PACKET) return NULL; - +static void hfp_reset_state(hfp_connection_t * connection){ + if (!connection) return; + connection->state = HFP_IDLE; +} + + +hfp_connection_t * hfp_handle_hci_event(uint8_t packet_type, uint8_t *packet, uint16_t size){ bd_addr_t event_addr; hfp_connection_t * context = NULL; @@ -266,18 +327,19 @@ hfp_connection_t * handle_hci_event(uint8_t packet_type, uint8_t *packet, uint16 case RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE: // data: event(8), len(8), status (8), address (48), handle(16), server channel(8), rfcomm_cid(16), max frame size(16) - if (packet[2]) { - // hfp_hf_reset_state(); - // emit_event(HFP_SUBEVENT_AUDIO_CONNECTION_COMPLETE, packet[2]); - } else { - bt_flip_addr(event_addr, &packet[2]); - context = provide_hfp_connection_context_for_bd_addr(event_addr); - - if (!context || context->state != HFP_W4_RFCOMM_CONNECTED) return context; + bt_flip_addr(event_addr, &packet[2]); + context = provide_hfp_connection_context_for_bd_addr(event_addr); + + if (!context || context->state != HFP_W4_RFCOMM_CONNECTED) return context; + if (packet[2]) { + hfp_reset_state(context); + hfp_emit_event(context->callback, HFP_SUBEVENT_AUDIO_CONNECTION_COMPLETE, packet[2]); + } else { context->con_handle = READ_BT_16(packet, 9); context->rfcomm_cid = READ_BT_16(packet, 12); uint16_t mtu = READ_BT_16(packet, 14); + context->state = HFP_W4_SUPPORTED_FEATURES_EXCHANGE; printf("RFCOMM channel open succeeded. New RFCOMM Channel ID %u, max frame size %u\n", context->rfcomm_cid, mtu); } break; @@ -306,8 +368,9 @@ void hfp_connect(bd_addr_t bd_addr, uint16_t service_uuid){ } if (connection->state != HFP_IDLE) return; - connection->state = HFP_W4_SDP_QUERY_COMPLETE; memcpy(connection->remote_addr, bd_addr, 6); + connection->state = HFP_W4_SDP_QUERY_COMPLETE; + connection_doing_sdp_query = connection; sdp_query_rfcomm_channel_and_name_for_uuid(connection->remote_addr, service_uuid); } diff --git a/src/hfp.h b/src/hfp.h index 278c89db2..b04f571aa 100644 --- a/src/hfp.h +++ b/src/hfp.h @@ -52,11 +52,65 @@ extern "C" { #endif +#define HFP_Default_HF_Supported_Features 0x0000 +#define HFP_Default_AG_Supported_Features 0x0009 +#define HFP_MAX_NUM_CODECS 20 + +/* AT+BRSF Result: +0: EC and/or NR function +1: Three-way calling +2: CLI presentation capability +3: Voice recognition activation +4: Remote volume control +5: Enhanced call status +6: Enhanced call control +7: Codec negotiation +8: HF Indicators +9: eSCO S4 (and T2) Settings Supported +10-31: Reserved for future definition +*/ +/* +BRSF Result: +0: Three-way calling +1: EC and/or NR function +2: Voice recognition function +3: In-band ring tone capability +4: Attach a number to a voice tag +5: Ability to reject a call +6: Enhanced call status +7: Enhanced call control +8: Extended Error Result Codes +9: Codec negotiation +10: HF Indicators +11: eSCO S4 (and T2) Settings Supported +12-31: Reserved for future definition +*/ +#define HFP_Supported_Features "+BRSF" +#define HFP_Available_Codecs "+BAC" +#define HFP_Codec_Indicator "+CIND" +#define HFP_Enable_Indicator_Status_Update "+CMER" +#define HFP_Support_Call_Hold_And_Multiparty_Services "+CHLD" +#define HFP_Generic_Status_Indicator "+BIND" +#define HFP_OK "OK" + + typedef enum { HFP_IDLE, HFP_SDP_QUERY_RFCOMM_CHANNEL, HFP_W4_SDP_QUERY_COMPLETE, HFP_W4_RFCOMM_CONNECTED, + HFP_W4_SUPPORTED_FEATURES_EXCHANGE, + HFP_W4_CODEC_NEGOTIATION, + HFP_W4_INDICATORS, + HFP_W4_INDICATORS_STATUS, + HFP_W4_INDICATORS_STATUS_UPDATE, + HFP_W4_CAN_HOLD_CALL, + HFP_W4_GENERIC_STATUS_INDICATORS, + HFP_W4_HF_GENERIC_STATUS_INDICATORS, + HFP_W4_AG_GENERIC_STATUS_INDICATORS, + HFP_W4_INITITAL_STATE_GENERIC_STATUS_INDICATORS, + + HFP_CMD_SENT, + HFP_ACTIVE, HFP_W2_DISCONNECT_RFCOMM, HFP_W4_RFCOMM_DISCONNECTED, @@ -74,16 +128,24 @@ typedef struct hfp_connection { uint16_t rfcomm_channel_nr; uint16_t rfcomm_cid; - uint16_t query_service_uuid; + uint8_t wait_ok; + uint8_t * codecs; + 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 * handle_hci_event(uint8_t packet_type, uint8_t *packet, uint16_t size); +hfp_connection_t * hfp_handle_hci_event(uint8_t packet_type, uint8_t *packet, uint16_t size); void hfp_init(uint16_t rfcomm_channel_nr); void hfp_connect(bd_addr_t bd_addr, uint16_t service_uuid); +hfp_connection_t * get_hfp_connection_context_for_rfcomm_cid(uint16_t cid); + +// TODO: move to utils +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); + #if defined __cplusplus } #endif diff --git a/src/hfp_ag.c b/src/hfp_ag.c index 161f8904b..513c0cd09 100644 --- a/src/hfp_ag.c +++ b/src/hfp_ag.c @@ -62,6 +62,9 @@ #include "hfp_ag.h" static const char default_hfp_ag_service_name[] = "Voice gateway"; +static uint16_t hfp_supported_features = HFP_Default_HF_Supported_Features; +static uint8_t hfp_codecs_nr = 0; +static uint8_t hfp_codecs[HFP_MAX_NUM_CODECS]; static void packet_handler(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); @@ -79,31 +82,61 @@ void hfp_ag_create_service(uint8_t * service, int rfcomm_channel_nr, const char */ } - static void hfp_run(hfp_connection_t * connection){ if (!connection) return; - + switch (connection->state){ + case HFP_W4_SUPPORTED_FEATURES_EXCHANGE: + + break; default: break; } } +hfp_connection_t * hfp_handle_rfcomm_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + hfp_connection_t * context = get_hfp_connection_context_for_rfcomm_cid(channel); + if (!context) return NULL; + while (size > 0 && (packet[0] == '\n' || packet[0] == '\r')){ + size--; + packet++; + } + + return context; +} + static void packet_handler(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ // printf("packet_handler type %u, packet[0] %x\n", packet_type, packet[0]); hfp_connection_t * context = NULL; - if (packet_type == RFCOMM_DATA_PACKET){ - hfp_run(context); - return; - } - context = handle_hci_event(packet_type, packet, size); + switch (packet_type){ + case RFCOMM_DATA_PACKET: + context = hfp_handle_rfcomm_event(packet_type, channel, packet, size); + break; + case HCI_EVENT_PACKET: + context = hfp_handle_hci_event(packet_type, packet, size); + break; + default: + break; + } hfp_run(context); } -void hfp_ag_init(uint16_t rfcomm_channel_nr){ - rfcomm_register_packet_handler(packet_handler); +void hfp_ag_init(uint16_t rfcomm_channel_nr, uint16_t supported_features, uint8_t * codecs, int codecs_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; + } hfp_init(rfcomm_channel_nr); + rfcomm_register_packet_handler(packet_handler); + // connection->codecs = codecs; + hfp_supported_features = supported_features; + hfp_codecs_nr = codecs_nr; + + int i; + for (i=0; istate = HFP_IDLE; -// } -void hfp_hf_init(uint16_t rfcomm_channel_nr){ - hfp_init(rfcomm_channel_nr); - rfcomm_register_packet_handler(packet_handler); +static int bit(uint16_t bitmap, int position){ + return (bitmap >> position) & 1; } +int hfp_hs_supported_features_exchange_cmd(uint16_t cid){ + char buffer[20]; + sprintf(buffer, "AT%s=%d\r\n", HFP_Supported_Features, hfp_supported_features); + return send_str_over_rfcomm(cid, buffer); +} + +int hfp_hs_codec_negotiation_cmd(uint16_t cid){ + char buffer[30]; + int buffer_offset = sprintf(buffer, "AT%s=", HFP_Available_Codecs); + join(buffer, sizeof(buffer), buffer_offset, hfp_codecs, hfp_codecs_nr); + return send_str_over_rfcomm(cid, buffer); +} + +void hfp_hs_retrieve_indicators_information(); +void hfp_hs_request_indicators_status(); +void hfp_hs_request_indicator_status_update(); +void hfp_hs_list_generic_status_indicators(); static void hfp_run(hfp_connection_t * connection){ if (!connection) return; - + + int err = 0; switch (connection->state){ + case HFP_W4_SUPPORTED_FEATURES_EXCHANGE: + err = hfp_hs_supported_features_exchange_cmd(connection->rfcomm_cid); + break; + case HFP_W4_CODEC_NEGOTIATION: + err = hfp_hs_codec_negotiation_cmd(connection->rfcomm_cid); + break; + case HFP_W4_INDICATORS: + break; + case HFP_W4_INDICATORS_STATUS: + break; + case HFP_W4_INDICATORS_STATUS_UPDATE: + break; + case HFP_W4_CAN_HOLD_CALL: + break; + case HFP_W4_GENERIC_STATUS_INDICATORS: + break; + case HFP_W4_HF_GENERIC_STATUS_INDICATORS: + break; + case HFP_W4_AG_GENERIC_STATUS_INDICATORS: + break; + case HFP_W4_INITITAL_STATE_GENERIC_STATUS_INDICATORS: + break; + default: break; } + if (!err) connection->state = HFP_CMD_SENT; +} + +hfp_connection_t * hfp_handle_rfcomm_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + hfp_connection_t * context = get_hfp_connection_context_for_rfcomm_cid(channel); + if (!context) return NULL; + while (size > 0 && (packet[0] == '\n' || packet[0] == '\r')){ + size--; + packet++; + } + + if (context->wait_ok){ + if (strncmp((char *)packet, HFP_OK, strlen(HFP_OK)) == 0){ + context->wait_ok = 0; + return context; + } + } + + if (strncmp((char *)packet, HFP_Supported_Features, strlen(HFP_Supported_Features)) == 0){ + uint16_t supported_features = (uint16_t)atoi((char*)&packet[strlen(HFP_Supported_Features+1)]); + if (bit(supported_features, 7) && bit(hfp_supported_features,9)){ + context->state = HFP_W4_CODEC_NEGOTIATION; + } else { + context->state = HFP_W4_INDICATORS; + } + context->wait_ok = 1; + } + if (strncmp((char *)packet, HFP_Available_Codecs, strlen(HFP_Available_Codecs)) == 0){ + // parse available codecs + } + return context; } static void packet_handler(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ // printf("packet_handler type %u, packet[0] %x\n", packet_type, packet[0]); hfp_connection_t * context = NULL; - if (packet_type == RFCOMM_DATA_PACKET){ - hfp_run(context); - return; - } - context = handle_hci_event(packet_type, packet, size); + switch (packet_type){ + case RFCOMM_DATA_PACKET: + context = hfp_handle_rfcomm_event(packet_type, channel, packet, size); + break; + case HCI_EVENT_PACKET: + context = hfp_handle_hci_event(packet_type, packet, size); + break; + default: + break; + } hfp_run(context); } +void hfp_hf_init(uint16_t rfcomm_channel_nr, uint16_t supported_features, uint8_t * codecs, int codecs_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; + } + hfp_init(rfcomm_channel_nr); + rfcomm_register_packet_handler(packet_handler); + + // connection->codecs = codecs; + hfp_supported_features = supported_features; + hfp_codecs_nr = codecs_nr; + + int i; + for (i=0; i