1
0
mirror of https://github.com/bluekitchen/btstack.git synced 2025-03-25 16:43:28 +00:00

hfp ag: start impl incoming call

This commit is contained in:
Milanka Ringwald 2015-11-12 17:25:31 +01:00
parent 8c72a38b3b
commit deaf6c13c4
8 changed files with 194 additions and 125 deletions

@ -637,6 +637,9 @@ extern "C" {
#define HFP_SUBEVENT_NETWORK_OPERATOR_CHANGED 0x07
#define HFP_SUBEVENT_EXTENDED_AUDIO_GATEWAY_ERROR 0x08
#define HFP_SUBEVENT_CODECS_CONNECTION_COMPLETE 0x09
#define HFP_SUBEVENT_START_RINGINIG 0x0A
#define HFP_SUBEVENT_STOP_RINGINIG 0x0B
// ANCS Client

@ -204,6 +204,16 @@ void hfp_emit_event(hfp_callback_t callback, uint8_t event_subtype, uint8_t valu
(*callback)(event, sizeof(event));
}
void hfp_emit_audio_connection_established_event(hfp_callback_t callback, uint8_t value, uint16_t sco_handle){
if (!callback) return;
uint8_t event[6];
event[0] = HCI_EVENT_HFP_META;
event[1] = sizeof(event) - 2;
event[2] = HFP_SUBEVENT_AUDIO_CONNECTION_ESTABLISHED;
event[3] = value; // status 0 == OK
bt_store_16(event, 4, sco_handle);
(*callback)(event, sizeof(event));
}
linked_list_t * hfp_get_connections(){
return (linked_list_t *) &hfp_connections;
@ -560,8 +570,9 @@ void hfp_handle_hci_event(hfp_callback_t callback, uint8_t packet_type, uint8_t
break;
}
context->sco_handle = sco_handle;
context->establish_audio_connection = 0;
context->state = HFP_AUDIO_CONNECTION_ESTABLISHED;
hfp_emit_event(callback, HFP_SUBEVENT_AUDIO_CONNECTION_ESTABLISHED, packet[2]);
hfp_emit_audio_connection_established_event(callback, packet[2], sco_handle);
break;
}
@ -735,10 +746,23 @@ static void process_command(hfp_connection_t * context){
return;
}
if (strncmp((char *)context->line_buffer+offset, "NOP", 3) == 0) return;
context->command = HFP_CMD_ERROR;
printf(" process unknown command %s \n", context->line_buffer);
if (strncmp((char *)context->line_buffer+offset, "AT+", 3) == 0){
context->command = HFP_CMD_UNKNOWN;
printf(" process unknown HF command %s \n", context->line_buffer);
return;
}
if (strncmp((char *)context->line_buffer+offset, "+", 1) == 0){
context->command = HFP_CMD_UNKNOWN;
printf(" process unknown AG command %s \n", context->line_buffer);
return;
}
if (strncmp((char *)context->line_buffer+offset, "NOP", 3) == 0){
context->command = HFP_CMD_NONE;
return;
}
}
#if 0
@ -941,7 +965,8 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte){
case HFP_CMD_TRANSFER_AG_INDICATOR_STATUS:
// indicators are indexed starting with 1
context->parser_item_index = atoi((char *)&context->line_buffer[0]) - 1;
log_info("Parsed status of the AG indicator %d, status ", context->parser_item_index);
printf("Parsed status of the AG indicator %d, status ", context->parser_item_index);
printf("\n");
break;
case HFP_CMD_QUERY_OPERATOR_SELECTION:
if (context->operator_name_format == 1){

@ -125,6 +125,7 @@ extern "C" {
typedef enum {
HFP_CMD_NONE = 0,
HFP_CMD_ERROR,
HFP_CMD_UNKNOWN,
HFP_CMD_OK,
HFP_CMD_SUPPORTED_FEATURES,
HFP_CMD_AVAILABLE_CODECS,
@ -370,6 +371,10 @@ typedef struct hfp_connection {
uint8_t establish_audio_connection;
uint8_t release_audio_connection;
uint8_t start_call;
uint8_t terminate_call;
uint8_t start_ringing;
uint8_t stop_ringing;
} hfp_connection_t;
// UTILS_START : TODO move to utils

@ -92,6 +92,16 @@ hfp_ag_indicator_t * get_hfp_ag_indicators(hfp_connection_t * context){
return (hfp_ag_indicator_t *)&(context->ag_indicators);
}
hfp_ag_indicator_t * get_ag_indicator_for_name(hfp_connection_t * context, const char * name){
int i;
for (i = 0; i < context->ag_indicators_nr; i++){
if (strcmp(context->ag_indicators[i].name, name) == 0){
return &context->ag_indicators[i];
}
}
return NULL;
}
void set_hfp_ag_indicators(hfp_ag_indicator_t * indicators, int indicator_nr){
memcpy(hfp_ag_indicators, indicators, indicator_nr * sizeof(hfp_ag_indicator_t));
hfp_ag_indicators_nr = indicator_nr;
@ -114,24 +124,6 @@ void hfp_ag_register_packet_handler(hfp_callback_t callback){
hfp_callback = callback;
}
static uint8_t hfp_get_indicator_index_by_name(hfp_connection_t * context, const char * name){
int i;
for (i=0; i<context->ag_indicators_nr; i++){
if (strcmp(context->ag_indicators[i].name, name) == 0){
return i;
}
}
return 0xFF;
}
static void hfp_ag_update_indicator_status(hfp_connection_t * context, const char * indicator_name, uint8_t status){
int index = hfp_get_indicator_index_by_name(context, indicator_name);
if (index == 0xFF) return;
if (context->ag_indicators[index].status == status) return;
context->ag_indicators[index].status = status;
context->ag_indicators[index].status_changed = 1;
}
static int has_codec_negotiation_feature(hfp_connection_t * connection){
int hf = get_bit(connection->remote_supported_features, HFP_HFSF_CODEC_NEGOTIATION);
int ag = get_bit(hfp_supported_features, HFP_AGSF_CODEC_NEGOTIATION);
@ -329,12 +321,11 @@ static int hfp_ag_retrieve_initital_supported_generic_status_indicators_cmd(uint
return send_str_over_rfcomm(cid, buffer);
}
// static int hfp_ag_transfer_ag_indicators_status_cmd(uint16_t cid, hfp_ag_indicator_t indicator){
// char buffer[20];
// sprintf(buffer, "\r\n%s:%d,%d\r\n\r\nOK\r\n", HFP_TRANSFER_AG_INDICATOR_STATUS, indicator.index, indicator.status);
// log_info("HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE send %s", buffer);
// return send_str_over_rfcomm(cid, buffer);
// }
static int hfp_ag_transfer_ag_indicators_status_cmd(uint16_t cid, hfp_ag_indicator_t * indicator){
char buffer[20];
sprintf(buffer, "\r\n%s:%d,%d\r\n", HFP_TRANSFER_AG_INDICATOR_STATUS, indicator->index, indicator->status);
return send_str_over_rfcomm(cid, buffer);
}
static int hfp_ag_report_network_operator_name_cmd(uint16_t cid, hfp_network_opearator_t op){
char buffer[40];
@ -369,10 +360,9 @@ static uint8_t hfp_ag_suggest_codec(hfp_connection_t *context){
static int hfp_ag_run_for_context_service_level_connection(hfp_connection_t * context){
log_info(" AG run for context_service_level_connection \n");
if (context->state >= HFP_CODECS_CONNECTION_ESTABLISHED) return 0;
int done = 0;
log_info(" AG run for context_service_level_connection 1\n");
// printf(" AG run for context_service_level_connection 1\n");
switch(context->command){
case HFP_CMD_SUPPORTED_FEATURES:
@ -500,8 +490,8 @@ static int hfp_ag_run_for_context_service_level_connection(hfp_connection_t * co
static int hfp_ag_run_for_context_service_level_connection_queries(hfp_connection_t * context){
if (context->state != HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED) return 0;
int done = 0;
log_info(" SLC queries: ");
printf(" SLC queries: \n");
switch(context->command){
case HFP_CMD_AVAILABLE_CODECS:
context->suggested_codec = hfp_ag_suggest_codec(context);
@ -530,20 +520,9 @@ static int hfp_ag_run_for_context_service_level_connection_queries(hfp_connectio
}
break;
case HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE:{
hfp_ag_ok(context->rfcomm_cid);
done = 1;
break;
// int i;
// for (i = 0; i < context->ag_indicators_nr; i++){
// if (context->ag_indicators[i].enabled == 0) continue;
// if (context->ag_indicators[i].status_changed == 0) continue;
// hfp_ag_transfer_ag_indicators_status_cmd(context->rfcomm_cid, context->ag_indicators[i]);
// done = 1;
// context->ag_indicators[i].status_changed = 0;
// return done;
// }
break;
}
case HFP_CMD_TRIGGER_CODEC_CONNECTION_SETUP:
if (context->hf_trigger_codec_connection_setup){ // received BCC
@ -577,6 +556,26 @@ static int hfp_ag_run_for_context_service_level_connection_queries(hfp_connectio
case HFP_CMD_ENABLE_INDICATOR_STATUS_UPDATE:
printf("TODO\n");
break;
case HFP_CMD_NONE:
if (context->start_call){
context->start_call = 0;
hfp_ag_indicator_t * indicator = get_ag_indicator_for_name(context, "callsetup");
if (!indicator) break;
// TODO: do we need this check?
if (indicator->status != HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS){
context->start_call = 1;
break;
}
indicator->status = HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS;
hfp_ag_transfer_ag_indicators_status_cmd(context->rfcomm_cid, indicator);
context->state = HFP_SLE_W2_EXCHANGE_COMMON_CODEC;
context->ag_trigger_codec_connection_setup = 1;
context->establish_audio_connection = 1;
context->start_ringing = 1;
done = 1;
return done;
}
default:
break;
}
@ -585,7 +584,7 @@ static int hfp_ag_run_for_context_service_level_connection_queries(hfp_connectio
static int hfp_ag_run_for_context_codecs_connection(hfp_connection_t * context){
log_info(" AG run for context_codecs_connection: ");
printf(" AG run for context_codecs_connection: \n");
if (context->state <= HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED ||
context->state > HFP_W2_DISCONNECT_SCO) return 0;
@ -691,10 +690,9 @@ static int hfp_ag_run_for_context_codecs_connection(hfp_connection_t * context){
}
if (done) return done;
if (context->establish_audio_connection){
if (context->state < HFP_SLE_W4_EXCHANGE_COMMON_CODEC){
printf("hfp_ag_establish_audio_connection ag_trigger_codec_connection_setup");
context->ag_trigger_codec_connection_setup = 0;
context->state = HFP_SLE_W4_EXCHANGE_COMMON_CODEC;
context->suggested_codec = hfp_ag_suggest_codec(context);
@ -702,7 +700,6 @@ static int hfp_ag_run_for_context_codecs_connection(hfp_connection_t * context){
done = 1;
return done;
} else {
printf("create sco");
context->state = HFP_W4_SCO_CONNECTED;
hci_send_cmd(&hci_setup_synchronous_connection, context->con_handle, 8000, 8000, 0xFFFF, hci_get_sco_voice_setting(), 0xFF, 0x003F);
done = 1;
@ -716,6 +713,17 @@ static int hfp_ag_run_for_context_codecs_connection(hfp_connection_t * context){
done = 1;
return done;
}
if (context->start_ringing){
context->start_ringing = 0;
hfp_emit_event(hfp_callback, HFP_SUBEVENT_START_RINGINIG, 0);
}
if (context->stop_ringing){
context->stop_ringing = 0;
hfp_emit_event(hfp_callback, HFP_SUBEVENT_STOP_RINGINIG, 0);
}
return done;
}
@ -728,8 +736,8 @@ static void hfp_run_for_context(hfp_connection_t *context){
if (!rfcomm_can_send_packet_now(context->rfcomm_cid)) return;
log_info("ag hfp_run_for_context rfcomm_can_send_packet_now");
if (context->command == HFP_CMD_ERROR){
log_info("ag hfp_run_for_context HFP_CMD_ERROR");
if (context->command == HFP_CMD_UNKNOWN){
log_info("ag hfp_run_for_context HFP_CMD_UNKNOWN");
hfp_ag_error(context->rfcomm_cid);
context->send_ok = 0;
context->send_error = 0;
@ -886,78 +894,6 @@ void hfp_ag_report_extended_audio_gateway_error_result_code(bd_addr_t bd_addr, h
hfp_run_for_context(connection);
}
void hfp_ag_transfer_call_status(bd_addr_t bd_addr, hfp_call_status_t status){
hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr);
if (!connection){
log_error("HFP HF: connection doesn't exist.");
return;
}
if (!connection->enable_status_update_for_ag_indicators) return;
hfp_ag_update_indicator_status(connection, (char *)"call", status);
}
void hfp_ag_transfer_callsetup_status(bd_addr_t bd_addr, hfp_callsetup_status_t status){
hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr);
if (!connection){
log_error("HFP HF: connection doesn't exist.");
return;
}
if (!connection->enable_status_update_for_ag_indicators) return;
hfp_ag_update_indicator_status(connection, (char *)"callsetup", status);
}
void hfp_ag_transfer_callheld_status(bd_addr_t bd_addr, hfp_callheld_status_t status){
hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr);
if (!connection){
log_error("HFP AG: connection doesn't exist.");
return;
}
if (!connection->enable_status_update_for_ag_indicators) return;
hfp_ag_update_indicator_status(connection, (char *)"callheld", status);
hfp_run_for_context(connection);
}
#if 0
static void hfp_ag_codec_connection_setup(hfp_connection_t * connection){
if (!connection){
log_error("HFP AG: connection doesn't exist.");
return;
}
// TODO:
hfp_run_for_context(connection);
}
#endif
/**
* @param handle
* @param transmit_bandwidth 8000(64kbps)
* @param receive_bandwidth 8000(64kbps)
* @param max_latency >= 7ms for eSCO, 0xFFFF do not care
* @param voice_settings e.g. CVSD, Input Coding: Linear, Input Data Format: 2s complement, data 16bit: 00011000000 == 0x60
* @param retransmission_effort e.g. 0xFF do not care
* @param packet_type at least EV3 for eSCO
hci_send_cmd(&hci_setup_synchronous_connection, rfcomm_handle, 8000, 8000, 0xFFFF, 0x0060, 0xFF, 0x003F);
*/
#if 0
static void hfp_ag_negotiate_codecs(bd_addr_t bd_addr){
hfp_ag_establish_service_level_connection(bd_addr);
hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr);
if (!has_codec_negotiation_feature(connection)) return;
if (connection->remote_codecs_nr == 0) return;
if (connection->state >= HFP_W2_DISCONNECT_SCO) return;
if (connection->state != HFP_SLE_W2_EXCHANGE_COMMON_CODEC &&
connection->state != HFP_SLE_W4_EXCHANGE_COMMON_CODEC){
connection->ag_trigger_codec_connection_setup = 1;
}
hfp_run_for_context(connection);
}
#endif
void hfp_ag_establish_audio_connection(bd_addr_t bd_addr){
hfp_ag_establish_service_level_connection(bd_addr);
@ -988,3 +924,26 @@ void hfp_ag_release_audio_connection(bd_addr_t bd_addr){
hfp_release_audio_connection(connection);
hfp_run_for_context(connection);
}
/**
* @brief
*/
void hfp_ag_call(bd_addr_t bd_addr){
hfp_ag_establish_service_level_connection(bd_addr);
hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr);
connection->start_call = 1;
printf("hfp_ag_call\n");
hfp_run_for_context(connection);
}
/**
* @brief
*/
void hfp_ag_terminate_call(bd_addr_t bd_addr){
hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr);
if (connection->state != HFP_AUDIO_CONNECTION_ESTABLISHED) return;
connection->terminate_call = 1;
hfp_run_for_context(connection);
}

@ -162,6 +162,16 @@ void hfp_ag_establish_audio_connection(bd_addr_t bd_addr);
void hfp_ag_release_audio_connection(bd_addr_t bd_addr);
/**
* @brief
*/
void hfp_ag_call(bd_addr_t bd_addr);
/**
* @brief
*/
void hfp_ag_terminate_call(bd_addr_t bd_addr);
/* API_END */
#if defined __cplusplus

@ -97,6 +97,7 @@ static hfp_generic_status_indicator_t hf_indicators[] = {
static uint8_t service_level_connection_established = 0;
static uint8_t codecs_connection_established = 0;
static uint8_t audio_connection_established = 0;
static uint8_t start_ringing = 0;
int expected_rfcomm_command(const char * expected_cmd){
@ -164,6 +165,14 @@ void packet_handler(uint8_t * event, uint16_t event_size){
printf("\n** AC released **\n\n");
audio_connection_established = 0;
break;
case HFP_SUBEVENT_START_RINGINIG:
printf("\n** Start ringing **\n\n");
start_ringing = 1;
break;
case HFP_SUBEVENT_STOP_RINGINIG:
printf("\n** Stop ringing **\n\n");
start_ringing = 0;
break;
default:
printf("event not handled %u\n", event[2]);
break;
@ -201,9 +210,21 @@ TEST_GROUP(HFPClient){
codecs_connection_established = 0;
simulate_test_sequence((char **) test_steps, nr_test_steps);
}
};
TEST(HFPClient, HFAnswerIncomingCallWithInBandRingTone){
setup_hfp_service_level_connection(default_slc_setup(), default_slc_setup_size());
CHECK_EQUAL(service_level_connection_established, 1);
hfp_ag_call(device_addr);
simulate_test_sequence(default_ic_setup(), default_ic_setup_size());
CHECK_EQUAL(audio_connection_established, 1);
simulate_test_sequence(alert_ic_setup(), alert_ic_setup_size());
CHECK_EQUAL(start_ringing, 1);
}
TEST(HFPClient, HFAudioConnectionEstablished){
setup_hfp_service_level_connection(default_slc_setup(), default_slc_setup_size());
CHECK_EQUAL(service_level_connection_established, 1);

@ -143,6 +143,41 @@ hfp_test_item_t cc_tests[] = {
TEST_SEQUENCE(cc_test4)
};
/* Incoming call sequence */
const char * ic_test1[] = {
"+CIEV:3,1",
"NOP",
"BCS:1",
"AT+BCS=1",
"OK",
"NOP"
};
const char * ic_alert_test1[] = {
"NOP",
// //"RING",
// "NOP",
// "+CLIP:\"1234\",128",
// "NOP",
// "InBandRingTone",
// "NOP",
// "RING",
// "NOP",
// "+CLIP:\"1234\",128", // 128-143, 144-159, 160-175
// "NOP",
// "InBandRingTone",
// "ATA",
// "OK",
// "NOP",
// "+CIEV:2,1", // call = 1
// "NOP",
// "+CIEV:3,0"
};
hfp_test_item_t ic_tests[] = {
TEST_SEQUENCE(ic_test1)
};
//////////////
@ -165,10 +200,15 @@ int default_slc_cmds_setup_size(){ return sizeof(slc_cmds_test1)/sizeof(char*);}
// CC
hfp_test_item_t * hfp_cc_tests(){ return cc_tests;}
int cc_tests_size(){ return sizeof(cc_tests) /test_item_size;
}
int cc_tests_size(){ return sizeof(cc_tests) /test_item_size;}
char ** default_cc_setup() { return (char **)cc_test1;}
int default_cc_setup_size(){ return sizeof(cc_test1)/sizeof(char*);}
// IC
char ** default_ic_setup() { return (char **)ic_test1;}
int default_ic_setup_size(){ return sizeof(ic_test1)/sizeof(char*);}
char ** alert_ic_setup() { return (char **)ic_alert_test1;}
int alert_ic_setup_size(){ return sizeof(ic_alert_test1)/sizeof(char*);}

@ -70,3 +70,9 @@ int cc_tests_size();
char ** default_cc_setup();
int default_cc_setup_size();
/* Incoming call (ic) test sequences */
char ** default_ic_setup();
int default_ic_setup_size();
char ** alert_ic_setup();
int alert_ic_setup_size();