establish/release SCO connection

This commit is contained in:
Milanka Ringwald 2015-11-12 12:13:43 +01:00
parent ce250f1a12
commit dd5554cc06
6 changed files with 153 additions and 77 deletions

View File

@ -469,7 +469,7 @@ void hfp_handle_hci_event(hfp_callback_t callback, uint8_t packet_type, uint8_t
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)
printf("RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE packet_handler type %u, packet[0] %x\n", packet_type, packet[0]);
printf("RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE packet_handler type %u, packet[0] %x, size %u\n", packet_type, packet[0], size);
bt_flip_addr(event_addr, &packet[3]);
context = get_hfp_connection_context_for_bd_addr(event_addr);
@ -501,13 +501,23 @@ void hfp_handle_hci_event(hfp_callback_t callback, uint8_t packet_type, uint8_t
break;
case HCI_EVENT_SYNCHRONOUS_CONNECTION_COMPLETE:{
bt_flip_addr(event_addr, &packet[5]);
printf("SCO Complete packet_handler type %u (HCI = %u), packet[0] %x, size %u\n", packet_type, HCI_EVENT_PACKET, packet[0], size);
int index = 2;
uint8_t status = packet[index++];
if (status != 0){
log_error("(e)SCO Connection is not established, status %u", status);
break;
}
uint16_t sco_handle = READ_BT_16(packet, index);
index+=2;
bd_addr_t address;
memcpy(address, &packet[index], 6);
bt_flip_addr(event_addr, &packet[index]);
index+=6;
uint8_t link_type = packet[index++];
uint8_t transmission_interval = packet[index++]; // measured in slots
uint8_t retransmission_interval = packet[index++];// measured in slots
@ -517,20 +527,16 @@ void hfp_handle_hci_event(hfp_callback_t callback, uint8_t packet_type, uint8_t
index+=2;
uint8_t air_mode = packet[index];
if (status != 0){
log_error("(e)SCO Connection is not established, status %u", status);
break;
}
switch (link_type){
case 0x00:
printf("SCO Connection established. \n");
log_info("SCO Connection established. \n");
if (transmission_interval != 0) log_error("SCO Connection: transmission_interval not zero: %d.", transmission_interval);
if (retransmission_interval != 0) log_error("SCO Connection: retransmission_interval not zero: %d.", retransmission_interval);
if (rx_packet_length != 0) log_error("SCO Connection: rx_packet_length not zero: %d.", rx_packet_length);
if (tx_packet_length != 0) log_error("SCO Connection: tx_packet_length not zero: %d.", tx_packet_length);
break;
case 0x02:
printf("eSCO Connection established. \n");
log_info("eSCO Connection established. \n");
break;
default:
log_error("(e)SCO reserved link_type 0x%2x", link_type);
@ -538,11 +544,17 @@ void hfp_handle_hci_event(hfp_callback_t callback, uint8_t packet_type, uint8_t
}
log_info("sco_handle 0x%2x, address %s, transmission_interval %u slots, retransmission_interval %u slots, "
" rx_packet_length %u bytes, tx_packet_length %u bytes, air_mode 0x%2x (0x02 == CVSD)", sco_handle,
bd_addr_to_str(address), transmission_interval, retransmission_interval, rx_packet_length, tx_packet_length, air_mode);
bd_addr_to_str(event_addr), transmission_interval, retransmission_interval, rx_packet_length, tx_packet_length, air_mode);
context = get_hfp_connection_context_for_bd_addr(address);
context = get_hfp_connection_context_for_bd_addr(event_addr);
if (!context) {
log_error("SCO link created, context not found.");
break;
}
if (context->state == HFP_W4_CONNECTION_ESTABLISHED_TO_SHUTDOWN){
log_info("sco about to disconnect: HFP_W4_CONNECTION_ESTABLISHED_TO_SHUTDOWN");
context->state = HFP_W2_DISCONNECT_SCO;
break;
}
@ -575,10 +587,12 @@ void hfp_handle_hci_event(hfp_callback_t callback, uint8_t packet_type, uint8_t
if (context->state != HFP_W4_SCO_DISCONNECTED){
log_info("Received gap disconnect in wrong hfp state");
}
log_info("Check SCO handle: incoming 0x%02x, context 0x%02x\n", handle,context->sco_handle);
if (handle == context->sco_handle){
printf("SCO disconnected, w2 disconnect RFCOMM\n");
log_info("SCO disconnected, w2 disconnect RFCOMM\n");
context->sco_handle = 0;
context->release_audio_connection = 0;
context->state = HFP_W2_DISCONNECT_RFCOMM;
hfp_emit_event(callback, HFP_SUBEVENT_AUDIO_CONNECTION_RELEASED, 0);
break;
@ -722,7 +736,8 @@ static void process_command(hfp_connection_t * context){
if (strncmp((char *)context->line_buffer+offset, "NOP", 3) == 0) return;
printf(" process unknown command 3 %s \n", context->line_buffer);
context->command = HFP_CMD_ERROR;
printf(" process unknown command %s \n", context->line_buffer);
}
#if 0
@ -1072,10 +1087,10 @@ void hfp_release_service_level_connection(hfp_connection_t * context){
return;
}
void hfp_release_audio_connection(hfp_connection_t * connection){
if (!connection) return;
if (connection->state >= HFP_W2_DISCONNECT_SCO) return;
connection->release_audio_connection = 1;
void hfp_release_audio_connection(hfp_connection_t * context){
if (!context) return;
if (context->state >= HFP_W2_DISCONNECT_SCO) return;
context->release_audio_connection = 1;
}

View File

@ -329,11 +329,12 @@ 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);
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_report_network_operator_name_cmd(uint16_t cid, hfp_network_opearator_t op){
char buffer[40];
@ -368,9 +369,10 @@ 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;
//printf(" AG run for context_service_level_connection \n");
int done = 0;
log_info(" AG run for context_service_level_connection 1\n");
switch(context->command){
case HFP_CMD_SUPPORTED_FEATURES:
@ -498,7 +500,7 @@ 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;
//printf(" SLC queries: ");
log_info(" SLC queries: ");
switch(context->command){
case HFP_CMD_AVAILABLE_CODECS:
@ -528,15 +530,19 @@ static int hfp_ag_run_for_context_service_level_connection_queries(hfp_connectio
}
break;
case HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE:{
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]);
hfp_ag_ok(context->rfcomm_cid);
done = 1;
context->ag_indicators[i].status_changed = 0;
return done;
}
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:
@ -550,8 +556,9 @@ static int hfp_ag_run_for_context_service_level_connection_queries(hfp_connectio
return done;
}
log_info("SLC queries: ag_trigger_codec_connection_setup");
if (context->ag_trigger_codec_connection_setup){ // received BCS
//printf(" send BCS \n");
log_info(" send BCS \n");
context->ag_trigger_codec_connection_setup = 0;
context->state = HFP_SLE_W4_EXCHANGE_COMMON_CODEC;
context->suggested_codec = hfp_ag_suggest_codec(context);
@ -578,15 +585,16 @@ 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: ");
if (context->state <= HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED ||
context->state > HFP_W2_DISCONNECT_SCO) return 0;
int done = 0;
//printf(" AG run for context_codecs_connection: ");
switch (context->state){
case HFP_SLE_W2_EXCHANGE_COMMON_CODEC:
if (context->ag_trigger_codec_connection_setup){ // received BCS
//printf(" send BCS \n");
log_info(" send BCS \n");
context->ag_trigger_codec_connection_setup = 0;
context->state = HFP_SLE_W4_EXCHANGE_COMMON_CODEC;
context->suggested_codec = hfp_ag_suggest_codec(context);
@ -596,10 +604,11 @@ static int hfp_ag_run_for_context_codecs_connection(hfp_connection_t * context){
}
break;
case HFP_SLE_W4_EXCHANGE_COMMON_CODEC:
log_info("entered HFP_SLE_W4_EXCHANGE_COMMON_CODEC state");
switch(context->command){
case HFP_CMD_AVAILABLE_CODECS:
if (context->notify_ag_on_new_codecs){ // received BAC
//printf(" received BAC\n");
log_info(" received BAC\n");
context->notify_ag_on_new_codecs = 0;
if (context->suggested_codec != hfp_ag_suggest_codec(context)){
context->suggested_codec = hfp_ag_suggest_codec(context);
@ -612,7 +621,7 @@ static int hfp_ag_run_for_context_codecs_connection(hfp_connection_t * context){
}
break;
case HFP_CMD_HF_CONFIRMED_CODEC:
//printf(" received AT+BCS\n");
log_info(" received AT+BCS\n");
if (context->codec_confirmed != context->suggested_codec){
context->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED;
hfp_ag_error(context->rfcomm_cid);
@ -626,6 +635,7 @@ static int hfp_ag_run_for_context_codecs_connection(hfp_connection_t * context){
done = 1;
break;
default:
log_info("command not handled");
break;
}
break;
@ -664,14 +674,19 @@ static int hfp_ag_run_for_context_codecs_connection(hfp_connection_t * context){
if (context->establish_audio_connection){
context->state = HFP_W4_SCO_CONNECTED;
hci_send_cmd(&hci_setup_synchronous_connection, context->con_handle, 8000, 8000, 0xFFFF, 0x0043, 0xFF, 0x003F);
hci_send_cmd(&hci_setup_synchronous_connection, context->con_handle, 8000, 8000, 0xFFFF, hci_get_sco_voice_setting(), 0xFF, 0x003F);
done = 1;
return done;
}
break;
case HFP_AUDIO_CONNECTION_ESTABLISHED:
case HFP_W2_DISCONNECT_SCO:
context->state = HFP_W4_SCO_DISCONNECTED;
gap_disconnect(context->sco_handle);
done = 1;
return done;
case HFP_AUDIO_CONNECTION_ESTABLISHED:
if (context->release_audio_connection){
context->state = HFP_W4_SCO_DISCONNECTED;
gap_disconnect(context->sco_handle);
@ -679,20 +694,42 @@ static int hfp_ag_run_for_context_codecs_connection(hfp_connection_t * context){
return done;
}
break;
default:
break;
}
if (done) return done;
if (context->release_audio_connection){
context->state = HFP_W4_SCO_DISCONNECTED;
gap_disconnect(context->sco_handle);
done = 1;
return done;
}
return done;
}
static void hfp_run_for_context(hfp_connection_t *context){
log_info("ag hfp_run_for_context entered");
if (!context) return;
log_info("ag hfp_run_for_context context found");
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");
hfp_ag_error(context->rfcomm_cid);
context->send_ok = 0;
context->send_error = 0;
context->command = HFP_CMD_NONE;
return;
}
// printf("AG hfp_run_for_context 1 state %d == %d (HFP_W2_DISCONNECT_SCO), command %d\n", context->state, HFP_W2_DISCONNECT_SCO, context->command);
if (context->send_ok){
log_info("ag hfp_run_for_context hfp_ag_ok");
hfp_ag_ok(context->rfcomm_cid);
context->send_ok = 0;
context->command = HFP_CMD_NONE;
@ -700,6 +737,7 @@ static void hfp_run_for_context(hfp_connection_t *context){
}
if (context->send_error){
log_info("ag hfp_run_for_context hfp_ag_error");
hfp_ag_error(context->rfcomm_cid);
context->send_error = 0;
context->command = HFP_CMD_NONE;
@ -707,16 +745,20 @@ static void hfp_run_for_context(hfp_connection_t *context){
}
int done = hfp_ag_run_for_context_service_level_connection(context);
log_info("hfp_ag_run_for_context_service_level_connection = %d", done);
if (rfcomm_can_send_packet_now(context->rfcomm_cid) && !done){
done = hfp_ag_run_for_context_service_level_connection_queries(context);
log_info("hfp_ag_run_for_context_service_level_connection_queries = %d", done);
if (rfcomm_can_send_packet_now(context->rfcomm_cid) && !done){
done = hfp_ag_run_for_context_codecs_connection(context);
log_info("hfp_ag_run_for_context_codecs_connection = %d", done);
}
}
if (context->command == HFP_CMD_NONE && !done){
log_info("context->command == HFP_CMD_NONE");
switch(context->state){
case HFP_W2_DISCONNECT_RFCOMM:
context->state = HFP_W4_RFCOMM_DISCONNECTED;
@ -783,7 +825,11 @@ void hfp_ag_init(uint16_t rfcomm_channel_nr, uint32_t supported_features,
log_error("hfp_init: codecs_nr (%d) > HFP_MAX_NUM_CODECS (%d)", codecs_nr, HFP_MAX_NUM_CODECS);
return;
}
l2cap_init();
l2cap_register_packet_handler(packet_handler);
rfcomm_register_packet_handler(packet_handler);
hfp_init(rfcomm_channel_nr);
hfp_supported_features = supported_features;
@ -906,13 +952,21 @@ static void hfp_ag_negotiate_codecs(bd_addr_t bd_addr){
void hfp_ag_establish_audio_connection(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 (!has_codec_negotiation_feature(connection)){
log_info("hfp_ag_establish_audio_connection 1 - no codec negotiation");
return;
}
connection->establish_audio_connection = 0;
if (connection->state == HFP_AUDIO_CONNECTION_ESTABLISHED) return;
if (connection->state >= HFP_W2_DISCONNECT_SCO) return;
log_info("hfp_ag_establish_audio_connection 2");
connection->establish_audio_connection = 1;
if (connection->state < HFP_SLE_W4_EXCHANGE_COMMON_CODEC){
log_info("hfp_ag_establish_audio_connection ag_trigger_codec_connection_setup");
connection->command = HFP_CMD_TRIGGER_CODEC_CONNECTION_SETUP;
connection->ag_trigger_codec_connection_setup = 1;
}
hfp_run_for_context(connection);

View File

@ -724,6 +724,8 @@ void hfp_hf_set_codecs(uint8_t * codecs, int codecs_nr){
}
void hfp_hf_init(uint16_t rfcomm_channel_nr, uint32_t supported_features, uint16_t * indicators, int indicators_nr, uint32_t indicators_status){
l2cap_init();
l2cap_register_packet_handler(packet_handler);
rfcomm_register_packet_handler(packet_handler);
hfp_init(rfcomm_channel_nr);

View File

@ -217,31 +217,31 @@ TEST(HFPClient, HFAudioConnectionEstablished){
CHECK_EQUAL(audio_connection_established, 0);
}
// TEST(HFPClient, HFCodecsConnectionEstablished){
// for (int i = 0; i < cc_tests_size(); i++){
// setup_hfp_service_level_connection(default_slc_setup(), default_slc_setup_size());
// CHECK_EQUAL(service_level_connection_established, 1);
TEST(HFPClient, HFCodecsConnectionEstablished){
for (int i = 0; i < cc_tests_size(); i++){
setup_hfp_service_level_connection(default_slc_setup(), default_slc_setup_size());
CHECK_EQUAL(service_level_connection_established, 1);
// setup_hfp_codecs_connection(hfp_cc_tests()[i].test, hfp_cc_tests()[i].len);
// CHECK_EQUAL(codecs_connection_established, 1);
// teardown();
// }
// }
setup_hfp_codecs_connection(hfp_cc_tests()[i].test, hfp_cc_tests()[i].len);
CHECK_EQUAL(codecs_connection_established, 1);
teardown();
}
}
// TEST(HFPClient, HFServiceLevelConnectionCommands){
// setup_hfp_service_level_connection(default_slc_setup(), default_slc_setup_size());
// CHECK_EQUAL(service_level_connection_established, 1);
// for (int i = 0; i < slc_cmds_tests_size(); i++){
// simulate_test_sequence(hfp_slc_cmds_tests()[i].test, hfp_slc_cmds_tests()[i].len);
// }
// }
TEST(HFPClient, HFServiceLevelConnectionCommands){
setup_hfp_service_level_connection(default_slc_setup(), default_slc_setup_size());
CHECK_EQUAL(service_level_connection_established, 1);
for (int i = 0; i < slc_cmds_tests_size(); i++){
simulate_test_sequence(hfp_slc_cmds_tests()[i].test, hfp_slc_cmds_tests()[i].len);
}
}
// TEST(HFPClient, HFServiceLevelConnectionEstablished){
// for (int i = 0; i < slc_tests_size(); i++){
// setup_hfp_service_level_connection(hfp_slc_tests()[i].test, hfp_slc_tests()[i].len);
// CHECK_EQUAL(service_level_connection_established, 1);
// }
// }
TEST(HFPClient, HFServiceLevelConnectionEstablished){
for (int i = 0; i < slc_tests_size(); i++){
setup_hfp_service_level_connection(hfp_slc_tests()[i].test, hfp_slc_tests()[i].len);
CHECK_EQUAL(service_level_connection_established, 1);
}
}
int main (int argc, const char * argv[]){

View File

@ -261,6 +261,9 @@ le_command_status_t gap_disconnect(hci_con_handle_t handle){
return BLE_PERIPHERAL_OK;
}
uint16_t hci_get_sco_voice_setting(){
return 0x40;
}
void inject_rfcomm_command_to_hf(uint8_t * data, int len){
if (memcmp((char*)data, "AT", 2) == 0) return;

View File

@ -102,9 +102,6 @@ char cmd;
// prototypes
static void show_usage();
static void reset_pst_flags(){
}
// Testig User Interface
static void show_usage(void){
printf("\n--- Bluetooth HFP Hands-Free (HF) unit Test Console ---\n");
@ -113,12 +110,12 @@ static void show_usage(void){
printf("a - establish HFP connection to PTS module\n");
printf("A - release HFP connection to PTS module\n");
printf("z - establish HFP connection to speaker\n");
printf("Z - release HFP connection to speaker\n");
printf("b - establish AUDIO connection\n");
printf("B - release AUDIO connection\n");
printf("z - establish HFP connection to local mac\n");
printf("Z - release HFP connection to local mac\n");
printf("d - report AG failure\n");
printf("---\n");
@ -181,7 +178,12 @@ static void packet_handler(uint8_t * event, uint16_t event_size){
break;
case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_RELEASED:
printf("Service level connection released.\n\n");
reset_pst_flags();
break;
case HFP_SUBEVENT_AUDIO_CONNECTION_ESTABLISHED:
printf("\n** Audio connection established **\n\n");
break;
case HFP_SUBEVENT_AUDIO_CONNECTION_RELEASED:
printf("\n** Audio connection released **\n\n");
break;
default:
printf("event not handled %u\n", event[2]);