hfp: start audio conneciton

This commit is contained in:
Milanka Ringwald 2015-08-21 17:29:10 +02:00
parent 289b079ef5
commit 912d34aa7f
9 changed files with 531 additions and 157 deletions

View File

@ -628,7 +628,8 @@ extern "C" {
#define HFP_SUBEVENT_AG_INDICATOR_STATUS_CHANGED 0x06
#define HFP_SUBEVENT_NETWORK_OPERATOR_CHANGED 0x07
#define HFP_SUBEVENT_EXTENDED_AUDIO_GATEWAY_ERROR 0x08
#define HFP_SUBEVENT_AUDIO_CONNECTION_COMPLETE 0x09
// ANCS Client
#define ANCS_CLIENT_CONNECTED 0xF0
#define ANCS_CLIENT_NOTIFICATION 0xF1

109
src/hfp.c
View File

@ -245,6 +245,41 @@ static hfp_connection_t * get_hfp_connection_context_for_handle(uint16_t handle)
return NULL;
}
void hfp_reset_context_flags(hfp_connection_t * context){
context->wait_ok = 0;
context->send_ok = 0;
context->send_error = 0;
context->keep_separator = 0;
context->retrieve_ag_indicators = 0; // HFP_CMD_INDICATOR, check if needed
context->retrieve_ag_indicators_status = 0;
context->list_generic_status_indicators = 0; // HFP_CMD_LIST_GENERIC_STATUS_INDICATOR
context->retrieve_generic_status_indicators = 0; // HFP_CMD_GENERIC_STATUS_INDICATOR
context->retrieve_generic_status_indicators_state = 0; // HFP_CMD_GENERIC_STATUS_INDICATOR_STATE
context->change_status_update_for_individual_ag_indicators = 0;
context->operator_name_format = 0;
context->operator_name = 0;
context->operator_name_changed = 0;
context->enable_extended_audio_gateway_error_report = 0;
context->extended_audio_gateway_error = 0;
// can come any time (here taken into account only after SLE),
// if codec negotiation feature is set
context->notify_ag_on_new_codecs = 0;
// establish codecs connection
context->trigger_codec_connection_setup = 0;
context->remote_codec_received = 0;
context->establish_audio_connection = 0;
context->release_audio_connection = 0;
}
static hfp_connection_t * create_hfp_connection_context(){
hfp_connection_t * context = btstack_memory_hfp_connection_get();
if (!context) return NULL;
@ -459,6 +494,59 @@ void hfp_handle_hci_event(hfp_callback_t callback, uint8_t packet_type, uint8_t
}
break;
case HCI_EVENT_SYNCHRONOUS_CONNECTION_COMPLETE:{
int index = 2;
uint8_t status = packet[index++];
uint16_t sco_handle = READ_BT_16(packet, index);
index+=2;
bd_addr_t address;
memcpy(address, &packet[index], 6);
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
uint16_t rx_packet_length = READ_BT_16(packet, index); // measured in bytes
index+=2;
uint16_t tx_packet_length = READ_BT_16(packet, index); // measured in bytes
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");
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");
break;
default:
log_error("(e)SCO reserved link_type 0x%2x", link_type);
break;
}
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);
context = get_hfp_connection_context_for_bd_addr(address);
if (context->state == HFP_W4_CONNECTION_ESTABLISHED_TO_SHUTDOWN){
context->state = HFP_W2_DISCONNECT_SCO;
break;
}
context->sco_handle = sco_handle;
context->state = HFP_AUDIO_CONNECTION_ESTABLISHED;
hfp_emit_event(callback, HFP_SUBEVENT_AUDIO_CONNECTION_COMPLETE, packet[2]);
break;
}
case RFCOMM_EVENT_CHANNEL_CLOSED:
rfcomm_cid = READ_BT_16(packet,2);
context = get_hfp_connection_context_for_rfcomm_cid(rfcomm_cid);
@ -605,6 +693,17 @@ void process_command(hfp_connection_t * context){
context->command = HFP_CMD_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR;
return;
}
if (strncmp((char *)context->line_buffer+offset, HFP_TRIGGER_CODEC_CONNECTION_SETUP, strlen(HFP_TRIGGER_CODEC_CONNECTION_SETUP)) == 0){
context->command = HFP_CMD_TRIGGER_CODEC_CONNECTION_SETUP;
return;
}
if (strncmp((char *)context->line_buffer+offset, HFP_CONFIRM_COMMON_CODEC, strlen(HFP_CONFIRM_COMMON_CODEC)) == 0){
context->command = HFP_CMD_CONFIRM_COMMON_CODEC;
return;
}
printf(" process unknown command 3 %s \n", context->line_buffer);
}
@ -921,10 +1020,7 @@ void hfp_establish_service_level_connection(bd_addr_t bd_addr, uint16_t service_
}
void hfp_release_service_level_connection(hfp_connection_t * context){
if (!context) {
log_error("hfp_release_service_level_connection failed");
return;
}
if (!context) return;
switch (context->state){
case HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED:
@ -939,4 +1035,9 @@ void hfp_release_service_level_connection(hfp_connection_t * context){
return;
}
void hfp_establish_audio_connection(hfp_connection_t * context, uint8_t codec_negotiation_feature_enabled){
}
void hfp_release_audio_connection(hfp_connection_t * context){
}

View File

@ -112,6 +112,8 @@ extern "C" {
#define HFP_QUERY_OPERATOR_SELECTION "+COPS" // +COPS: <mode>,0,<opearator>
#define HFP_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR "+CMEE"
#define HFP_EXTENDED_AUDIO_GATEWAY_ERROR "+CME ERROR"
#define HFP_TRIGGER_CODEC_CONNECTION_SETUP "+BCC"
#define HFP_CONFIRM_COMMON_CODEC "+BCS"
#define HFP_OK "OK"
#define HFP_ERROR "ERROR"
@ -137,7 +139,9 @@ typedef enum {
HFP_CMD_QUERY_OPERATOR_SELECTION,
HFP_CMD_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR,
HFP_CMD_EXTENDED_AUDIO_GATEWAY_ERROR
HFP_CMD_EXTENDED_AUDIO_GATEWAY_ERROR,
HFP_CMD_TRIGGER_CODEC_CONNECTION_SETUP,
HFP_CMD_CONFIRM_COMMON_CODEC
} hfp_command_t;
@ -238,6 +242,19 @@ typedef enum {
HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED, // 22
HFP_SLE_W2_EXCHANGE_COMMON_CODEC,
HFP_SLE_W4_EXCHANGE_COMMON_CODEC,
HFP_CODECS_CONNECTION_ESTABLISHED,
HFP_CCE_W2_ESTABLISH_SCO,
HFP_CCE_W4_SCO_CONNECTION_ESTABLISHED,
HFP_AUDIO_CONNECTION_ESTABLISHED,
HFP_W2_DISCONNECT_SCO,
HFP_W4_SCO_DISCONNECTED,
HFP_W2_DISCONNECT_RFCOMM,
HFP_W4_RFCOMM_DISCONNECTED,
HFP_W4_RFCOMM_DISCONNECTED_AND_RESTART,
@ -279,6 +296,7 @@ typedef struct hfp_connection {
bd_addr_t remote_addr;
uint16_t con_handle;
uint16_t sco_handle;
uint16_t rfcomm_channel_nr;
uint16_t rfcomm_cid;
@ -294,6 +312,8 @@ typedef struct hfp_connection {
int line_size;
uint32_t remote_supported_features;
// TODO: rename into hf_codecs_nr
int remote_codecs_nr;
uint16_t remote_codecs[HFP_MAX_INDICATOR_DESC_SIZE];
int ag_indicators_nr;
@ -335,6 +355,17 @@ typedef struct hfp_connection {
uint8_t enable_extended_audio_gateway_error_report;
uint8_t extended_audio_gateway_error;
// can come any time (here taken into account only after SLE),
// if codec negotiation feature is set
uint8_t notify_ag_on_new_codecs;
// establish codecs connection
uint8_t trigger_codec_connection_setup;
uint8_t ag_ready_for_codecs_connection_setup;
uint8_t remote_codec_received;
uint8_t establish_audio_connection;
uint8_t release_audio_connection;
} hfp_connection_t;
@ -358,7 +389,9 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte);
void hfp_init(uint16_t rfcomm_channel_nr);
void hfp_establish_service_level_connection(bd_addr_t bd_addr, uint16_t service_uuid);
void hfp_release_service_level_connection(hfp_connection_t * connection);
void hfp_establish_audio_connection(hfp_connection_t * context, uint8_t codec_negotiation_feature_enabled);
void hfp_release_audio_connection(hfp_connection_t * context);
void hfp_reset_context_flags(hfp_connection_t * context);
const char * hfp_hf_feature(int index);
const char * hfp_ag_feature(int index);

View File

@ -348,6 +348,27 @@ int hfp_ag_report_network_operator_name_cmd(uint16_t cid, hfp_network_opearator_
return send_str_over_rfcomm(cid, buffer);
}
int hfp_ag_cmd_confirm_codec(uint16_t cid, uint8_t codec){
char buffer[30];
sprintf(buffer, "\r\nOK\r\n%s=%d\r\n", HFP_CONFIRM_COMMON_CODEC, codec);
return send_str_over_rfcomm(cid, buffer);
}
static uint8_t hfp_ag_choose_codec(hfp_connection_t *context){
int i,j;
uint8_t codec = 0;
for (i = 0; i < hfp_codecs_nr; i++){
for (j = 0; j < context->remote_codecs_nr; j++){
if (context->remote_codecs[j] == hfp_codecs[i]){
codec = context->remote_codecs[j];
continue;
}
}
}
return codec;
}
void hfp_run_for_context(hfp_connection_t *context){
// printf(" hfp_run_for_context \n");
if (!context) return;
@ -387,7 +408,37 @@ void hfp_run_for_context(hfp_connection_t *context){
}
}
if (context->state == HFP_AUDIO_CONNECTION_ESTABLISHED){
// TODO
}
switch(context->command){
// START codec setup
case HFP_CMD_TRIGGER_CODEC_CONNECTION_SETUP:
if (!hfp_ag_choose_codec(context)){
hfp_ag_error(context->rfcomm_cid);
break;
}
switch (context->state){
case HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED:
// hfp_ag_ok(context->rfcomm_cid);
context->negotiated_codec = hfp_ag_choose_codec(context);
hfp_ag_cmd_confirm_codec(context->rfcomm_cid, context->negotiated_codec);
context->state = HFP_SLE_W4_EXCHANGE_COMMON_CODEC;
break;
default:
hfp_ag_error(context->rfcomm_cid);
break;
}
break;
case HFP_CMD_CONFIRM_COMMON_CODEC:
hfp_ag_ok(context->rfcomm_cid);
context->state = HFP_CODECS_CONNECTION_ESTABLISHED;
break;
// END codec setup
case HFP_CMD_SUPPORTED_FEATURES:
switch(context->state){
case HFP_W4_EXCHANGE_SUPPORTED_FEATURES:
@ -644,9 +695,9 @@ void hfp_ag_transfer_callsetup_status(bd_addr_t bd_addr, hfp_callsetup_status_t
}
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);
hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr);
if (!connection){
log_error("HFP HF: connection doesn't exist.");
log_error("HFP AG: connection doesn't exist.");
return;
}
if (!connection->enable_status_update_for_ag_indicators) return;
@ -654,24 +705,58 @@ void hfp_ag_transfer_callheld_status(bd_addr_t bd_addr, hfp_callheld_status_t st
hfp_run_for_context(connection);
}
void hfp_ag_audio_connection_setup(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);
void hfp_ag_codec_connection_setup(hfp_connection_t * connection){
if (!connection){
log_error("HFP HF: connection doesn't exist.");
log_error("HFP AG: connection doesn't exist.");
return;
}
// TODO:
hfp_run_for_context(connection);
}
void hfp_ag_audio_connection_release(bd_addr_t bd_addr){
/**
* @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);
*/
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 (!connection){
log_error("HFP HF: connection doesn't exist.");
log_error("HFP AG: connection doesn't exist.");
return;
}
if (connection->state == HFP_AUDIO_CONNECTION_ESTABLISHED) return;
// if (connection->remote_codecs_nr == 0) {
// log_error("HFP AG: codecs not exchanged, or no codecs specified in HF.");
// return;
// }
connection->trigger_codec_connection_setup = 1;
connection->establish_audio_connection = 1;
if (!has_codec_negotiation_feature(connection)){
connection->trigger_codec_connection_setup = 0;
connection->establish_audio_connection = 0;
return;
}
if (connection->state == HFP_CODECS_CONNECTION_ESTABLISHED){
connection->trigger_codec_connection_setup = 0;
return;
}
hfp_run_for_context(connection);
}
void hfp_ag_release_audio_connection(bd_addr_t bd_addr){
hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr);
// TODO:
hfp_run_for_context(connection);
}

View File

@ -152,12 +152,12 @@ void hfp_ag_transfer_callheld_status(bd_addr_t bd_addr, hfp_callheld_status_t st
/**
* @brief
*/
void hfp_ag_audio_connection_setup(bd_addr_t bd_addr);
void hfp_ag_establish_audio_connection(bd_addr_t bd_addr);
/**
* @brief
*/
void hfp_ag_audio_connection_release(bd_addr_t bd_addr);
void hfp_ag_release_audio_connection(bd_addr_t bd_addr);
/* API_END */

View File

@ -82,7 +82,13 @@ void hfp_hf_register_packet_handler(hfp_callback_t callback){
hfp_callback = callback;
}
static int hfp_hf_supports_codec(uint8_t codec){
int i;
for (i = 0; i < hfp_codecs_nr; i++){
if (hfp_codecs[i] == codec) return 1;
}
return 0;
}
static int has_codec_negotiation_feature(hfp_connection_t * connection){
int hf = get_bit(hfp_supported_features, HFP_HFSF_CODEC_NEGOTIATION);
int ag = get_bit(connection->remote_supported_features, HFP_AGSF_CODEC_NEGOTIATION);
@ -112,14 +118,14 @@ void hfp_hf_create_sdp_record(uint8_t * service, int rfcomm_channel_nr, const ch
}
int hfp_hs_exchange_supported_features_cmd(uint16_t cid){
int hfp_hf_cmd_exchange_supported_features(uint16_t cid){
char buffer[20];
sprintf(buffer, "AT%s=%d\r\n", HFP_SUPPORTED_FEATURES, hfp_supported_features);
// printf("exchange_supported_features %s\n", buffer);
return send_str_over_rfcomm(cid, buffer);
}
int hfp_hs_retrieve_codec_cmd(uint16_t cid){
int hfp_hf_cmd_notify_on_codecs(uint16_t cid){
char buffer[30];
int offset = snprintf(buffer, sizeof(buffer), "AT%s=", HFP_AVAILABLE_CODECS);
offset += join(buffer+offset, sizeof(buffer)-offset, hfp_codecs, hfp_codecs_nr);
@ -128,28 +134,28 @@ int hfp_hs_retrieve_codec_cmd(uint16_t cid){
return send_str_over_rfcomm(cid, buffer);
}
int hfp_hs_retrieve_indicators_cmd(uint16_t cid){
int hfp_hf_cmd_retrieve_indicators(uint16_t cid){
char buffer[20];
sprintf(buffer, "AT%s=?\r\n", HFP_INDICATOR);
// printf("retrieve_indicators %s\n", buffer);
return send_str_over_rfcomm(cid, buffer);
}
int hfp_hs_retrieve_indicators_status_cmd(uint16_t cid){
int hfp_hf_cmd_retrieve_indicators_status(uint16_t cid){
char buffer[20];
sprintf(buffer, "AT%s?\r\n", HFP_INDICATOR);
// printf("retrieve_indicators_status %s\n", buffer);
return send_str_over_rfcomm(cid, buffer);
}
int hfp_hs_activate_status_update_for_all_ag_indicators_cmd(uint16_t cid, uint8_t activate){
int hfp_hf_cmd_activate_status_update_for_all_ag_indicators(uint16_t cid, uint8_t activate){
char buffer[20];
sprintf(buffer, "AT%s=3,0,0,%d\r\n", HFP_ENABLE_STATUS_UPDATE_FOR_AG_INDICATORS, activate);
// printf("toggle_indicator_status_update %s\n", buffer);
return send_str_over_rfcomm(cid, buffer);
}
int hfp_hs_activate_status_update_for_ag_indicator_cmd(uint16_t cid, uint32_t indicators_status, int indicators_nr){
int hfp_hf_cmd_activate_status_update_for_ag_indicator(uint16_t cid, uint32_t indicators_status, int indicators_nr){
char buffer[50];
int offset = snprintf(buffer, sizeof(buffer), "AT%s=", HFP_UPDATE_ENABLE_STATUS_FOR_INDIVIDUAL_AG_INDICATORS);
offset += join_bitmap(buffer+offset, sizeof(buffer)-offset, indicators_status, indicators_nr);
@ -158,14 +164,14 @@ int hfp_hs_activate_status_update_for_ag_indicator_cmd(uint16_t cid, uint32_t in
return send_str_over_rfcomm(cid, buffer);
}
int hfp_hs_retrieve_can_hold_call_cmd(uint16_t cid){
int hfp_hf_cmd_retrieve_can_hold_call(uint16_t cid){
char buffer[20];
sprintf(buffer, "AT%s=?\r\n", HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES);
// printf("retrieve_can_hold_call %s\n", buffer);
return send_str_over_rfcomm(cid, buffer);
}
int hfp_hs_list_supported_generic_status_indicators_cmd(uint16_t cid){
int hfp_hf_cmd_list_supported_generic_status_indicators(uint16_t cid){
char buffer[30];
int offset = snprintf(buffer, sizeof(buffer), "AT%s=", HFP_GENERIC_STATUS_INDICATOR);
offset += join(buffer+offset, sizeof(buffer)-offset, hfp_indicators, hfp_indicators_nr);
@ -174,38 +180,50 @@ int hfp_hs_list_supported_generic_status_indicators_cmd(uint16_t cid){
return send_str_over_rfcomm(cid, buffer);
}
int hfp_hs_retrieve_supported_generic_status_indicators_cmd(uint16_t cid){
int hfp_hf_cmd_retrieve_supported_generic_status_indicators(uint16_t cid){
char buffer[20];
sprintf(buffer, "AT%s=?\r\n", HFP_GENERIC_STATUS_INDICATOR);
// printf("retrieve_supported_generic_status_indicators %s\n", buffer);
return send_str_over_rfcomm(cid, buffer);
}
int hfp_hs_list_initital_supported_generic_status_indicators_cmd(uint16_t cid){
int hfp_hf_cmd_list_initital_supported_generic_status_indicators(uint16_t cid){
char buffer[20];
sprintf(buffer, "AT%s?\r\n", HFP_GENERIC_STATUS_INDICATOR);
// printf("list_initital_supported_generic_status_indicators %s\n", buffer);
return send_str_over_rfcomm(cid, buffer);
}
int hfp_hs_query_operator_name_format_cmd(uint16_t cid){
int hfp_hf_cmd_query_operator_name_format(uint16_t cid){
char buffer[20];
sprintf(buffer, "AT%s=3,0\r\n", HFP_QUERY_OPERATOR_SELECTION);
return send_str_over_rfcomm(cid, buffer);
}
int hfp_hs_query_operator_name_cmd(uint16_t cid){
int hfp_hf_cmd_query_operator_name(uint16_t cid){
char buffer[20];
sprintf(buffer, "AT%s?\r\n", HFP_QUERY_OPERATOR_SELECTION);
return send_str_over_rfcomm(cid, buffer);
}
int hfp_hs_enable_extended_audio_gateway_error_report_cmd(uint16_t cid, uint8_t enable){
int hfp_hf_cmd_enable_extended_audio_gateway_error_report(uint16_t cid, uint8_t enable){
char buffer[20];
sprintf(buffer, "AT%s=%d\r\n", HFP_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR, enable);
return send_str_over_rfcomm(cid, buffer);
}
int hfp_hf_cmd_trigger_codec_connection_setup(uint16_t cid){
char buffer[20];
sprintf(buffer, "AT%s\r\n", HFP_TRIGGER_CODEC_CONNECTION_SETUP);
return send_str_over_rfcomm(cid, buffer);
}
int hfp_hf_cmd_confirm_codec(uint16_t cid, uint8_t codec){
char buffer[20];
sprintf(buffer, "AT%s=%d\r\n", HFP_CONFIRM_COMMON_CODEC, codec);
return send_str_over_rfcomm(cid, buffer);
}
static void hfp_emit_ag_indicator_event(hfp_callback_t callback, int status, hfp_ag_indicator_t indicator){
if (!callback) return;
uint8_t event[6];
@ -231,117 +249,67 @@ static void hfp_emit_network_operator_event(hfp_callback_t callback, int status,
(*callback)(event, sizeof(event));
}
static void hfp_run_for_context(hfp_connection_t * context){
if (!context) return;
// printf("hfp send cmd: context %p, RFCOMM cid %u \n", context, context->rfcomm_cid );
if (!rfcomm_can_send_packet_now(context->rfcomm_cid)) return;
static void hfp_hf_run_for_context_handle_service_level_connection_establishment(hfp_connection_t * context){
if (context->state >= HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED) return;
switch (context->state){
case HFP_EXCHANGE_SUPPORTED_FEATURES:
hfp_hs_exchange_supported_features_cmd(context->rfcomm_cid);
hfp_hf_cmd_exchange_supported_features(context->rfcomm_cid);
context->state = HFP_W4_EXCHANGE_SUPPORTED_FEATURES;
break;
case HFP_NOTIFY_ON_CODECS:
hfp_hs_retrieve_codec_cmd(context->rfcomm_cid);
hfp_hf_cmd_notify_on_codecs(context->rfcomm_cid);
context->state = HFP_W4_NOTIFY_ON_CODECS;
break;
case HFP_RETRIEVE_INDICATORS:
hfp_hs_retrieve_indicators_cmd(context->rfcomm_cid);
hfp_hf_cmd_retrieve_indicators(context->rfcomm_cid);
context->state = HFP_W4_RETRIEVE_INDICATORS;
context->retrieve_ag_indicators = 1;
context->retrieve_ag_indicators_status = 0;
break;
case HFP_RETRIEVE_INDICATORS_STATUS:
hfp_hs_retrieve_indicators_status_cmd(context->rfcomm_cid);
hfp_hf_cmd_retrieve_indicators_status(context->rfcomm_cid);
context->state = HFP_W4_RETRIEVE_INDICATORS_STATUS;
context->retrieve_ag_indicators_status = 1;
context->retrieve_ag_indicators = 0;
break;
case HFP_ENABLE_INDICATORS_STATUS_UPDATE:
hfp_hs_activate_status_update_for_all_ag_indicators_cmd(context->rfcomm_cid, 1);
hfp_hf_cmd_activate_status_update_for_all_ag_indicators(context->rfcomm_cid, 1);
context->state = HFP_W4_ENABLE_INDICATORS_STATUS_UPDATE;
break;
case HFP_RETRIEVE_CAN_HOLD_CALL:
hfp_hs_retrieve_can_hold_call_cmd(context->rfcomm_cid);
hfp_hf_cmd_retrieve_can_hold_call(context->rfcomm_cid);
context->state = HFP_W4_RETRIEVE_CAN_HOLD_CALL;
break;
case HFP_LIST_GENERIC_STATUS_INDICATORS:
hfp_hs_list_supported_generic_status_indicators_cmd(context->rfcomm_cid);
hfp_hf_cmd_list_supported_generic_status_indicators(context->rfcomm_cid);
context->state = HFP_W4_LIST_GENERIC_STATUS_INDICATORS;
context->list_generic_status_indicators = 1;
context->retrieve_generic_status_indicators = 0;
context->retrieve_generic_status_indicators_state = 0;
break;
case HFP_RETRIEVE_GENERIC_STATUS_INDICATORS:
hfp_hs_retrieve_supported_generic_status_indicators_cmd(context->rfcomm_cid);
hfp_hf_cmd_retrieve_supported_generic_status_indicators(context->rfcomm_cid);
context->state = HFP_W4_RETRIEVE_GENERIC_STATUS_INDICATORS;
context->list_generic_status_indicators = 0;
context->retrieve_generic_status_indicators = 1;
context->retrieve_generic_status_indicators_state = 0;
break;
case HFP_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS:
hfp_hs_list_initital_supported_generic_status_indicators_cmd(context->rfcomm_cid);
hfp_hf_cmd_list_initital_supported_generic_status_indicators(context->rfcomm_cid);
context->state = HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS;
context->list_generic_status_indicators = 0;
context->retrieve_generic_status_indicators = 0;
context->retrieve_generic_status_indicators_state = 1;
break;
case HFP_W2_DISCONNECT_RFCOMM:
context->state = HFP_W4_RFCOMM_DISCONNECTED;
rfcomm_disconnect_internal(context->rfcomm_cid);
break;
case HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED:{
int i;
for (i = 0; i < context->ag_indicators_nr; i++){
if (context->ag_indicators[i].status_changed) {
hfp_emit_ag_indicator_event(hfp_callback, 0, context->ag_indicators[i]);
context->ag_indicators[i].status_changed = 0;
break;
}
}
if (context->wait_ok) return;
if (context->enable_status_update_for_ag_indicators != 0xFF){
hfp_hs_activate_status_update_for_all_ag_indicators_cmd(context->rfcomm_cid, context->enable_status_update_for_ag_indicators);
context->wait_ok = 1;
break;
};
if (context->change_status_update_for_individual_ag_indicators){
hfp_hs_activate_status_update_for_ag_indicator_cmd(context->rfcomm_cid,
context->ag_indicators_status_update_bitmap,
context->ag_indicators_nr);
context->wait_ok = 1;
break;
}
if (context->operator_name_format){
hfp_hs_query_operator_name_format_cmd(context->rfcomm_cid);
context->wait_ok = 1;
break;
}
if (context->operator_name){
hfp_hs_query_operator_name_cmd(context->rfcomm_cid);
context->wait_ok = 1;
break;
}
if (context->enable_extended_audio_gateway_error_report){
hfp_hs_enable_extended_audio_gateway_error_report_cmd(context->rfcomm_cid, context->enable_extended_audio_gateway_error_report);
context->wait_ok = 1;
break;
}
break;
}
default:
break;
}
}
void handle_switch_on_ok(hfp_connection_t *context){
// printf("switch on ok\n");
void hfp_hf_switch_on_ok_handle_service_level_connection_establishment(hfp_connection_t *context){
if (context->state >= HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED) return;
switch (context->state){
case HFP_W4_EXCHANGE_SUPPORTED_FEATURES:
if (has_codec_negotiation_feature(context)){
@ -402,39 +370,185 @@ void handle_switch_on_ok(hfp_connection_t *context){
context->retrieve_generic_status_indicators_state = 0;
hfp_emit_event(hfp_callback, HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED, 0);
break;
case HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED:
context->wait_ok = 0;
if (context->enable_status_update_for_ag_indicators != 0xFF){
context->enable_status_update_for_ag_indicators = 0xFF;
hfp_emit_event(hfp_callback, HFP_SUBEVENT_COMPLETE, 0);
break;
};
if (context->change_status_update_for_individual_ag_indicators == 1){
context->change_status_update_for_individual_ag_indicators = 0;
hfp_emit_event(hfp_callback, HFP_SUBEVENT_COMPLETE, 0);
break;
}
if (context->operator_name_format){
context->operator_name_format = 0;
context->operator_name = 1;
break;
}
if (context->operator_name){
context->operator_name = 0;
hfp_emit_network_operator_event(hfp_callback, 0, context->network_operator);
break;
}
if (context->enable_extended_audio_gateway_error_report){
context->enable_extended_audio_gateway_error_report = 0;
break;
}
default:
break;
}
}
static void hfp_hf_run_for_context_handle_service_level_connection_queries(hfp_connection_t * context){
if (context->state != HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED) return;
if (context->wait_ok) return;
if (context->enable_status_update_for_ag_indicators != 0xFF){
hfp_hf_cmd_activate_status_update_for_all_ag_indicators(context->rfcomm_cid, context->enable_status_update_for_ag_indicators);
context->wait_ok = 1;
return;
};
if (context->change_status_update_for_individual_ag_indicators){
hfp_hf_cmd_activate_status_update_for_ag_indicator(context->rfcomm_cid,
context->ag_indicators_status_update_bitmap,
context->ag_indicators_nr);
context->wait_ok = 1;
return;
}
if (context->operator_name_format){
hfp_hf_cmd_query_operator_name_format(context->rfcomm_cid);
context->wait_ok = 1;
return;
}
if (context->operator_name){
hfp_hf_cmd_query_operator_name(context->rfcomm_cid);
context->wait_ok = 1;
return;
}
if (context->enable_extended_audio_gateway_error_report){
hfp_hf_cmd_enable_extended_audio_gateway_error_report(context->rfcomm_cid, context->enable_extended_audio_gateway_error_report);
context->wait_ok = 1;
return;
}
}
static void hfp_hf_switch_on_ok_handle_service_level_connection_queries(hfp_connection_t * context){
if (context->state != HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED) return;
if (context->enable_status_update_for_ag_indicators != 0xFF){
context->enable_status_update_for_ag_indicators = 0xFF;
hfp_emit_event(hfp_callback, HFP_SUBEVENT_COMPLETE, 0);
return;
};
if (context->change_status_update_for_individual_ag_indicators == 1){
context->change_status_update_for_individual_ag_indicators = 0;
hfp_emit_event(hfp_callback, HFP_SUBEVENT_COMPLETE, 0);
return;
}
if (context->operator_name_format){
context->operator_name_format = 0;
context->operator_name = 1;
return;
}
if (context->operator_name){
context->operator_name = 0;
hfp_emit_network_operator_event(hfp_callback, 0, context->network_operator);
return;
}
if (context->enable_extended_audio_gateway_error_report){
context->enable_extended_audio_gateway_error_report = 0;
return;
}
}
static void hfp_run_for_context(hfp_connection_t * context){
if (!context) return;
hfp_hf_run_for_context_handle_service_level_connection_establishment(context);
hfp_hf_run_for_context_handle_service_level_connection_queries(context);
// if (context->state >= HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED && context->state <= HFP_AUDIO_CONNECTION_ESTABLISHED){
// handle audio connection setup
switch (context->state){
case HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED:
if (context->wait_ok) return;
if (context->notify_ag_on_new_codecs){
hfp_hf_cmd_notify_on_codecs(context->rfcomm_cid);
context->wait_ok = 1;
break;
}
if (context->trigger_codec_connection_setup){
hfp_hf_cmd_trigger_codec_connection_setup(context->rfcomm_cid);
context->wait_ok = 1;
break;
}
break;
case HFP_SLE_W2_EXCHANGE_COMMON_CODEC:
if (!context->remote_codec_received) return;
context->wait_ok = 1;
if (hfp_hf_supports_codec(context->remote_codec_received)){
context->state = HFP_SLE_W4_EXCHANGE_COMMON_CODEC;
hfp_hf_cmd_confirm_codec(context->rfcomm_cid, context->negotiated_codec);
break;
}
context->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED;
context->notify_ag_on_new_codecs = 1;
context->remote_codec_received = 0;
hfp_hf_cmd_notify_on_codecs(context->rfcomm_cid);
break;
case HFP_CODECS_CONNECTION_ESTABLISHED:
if (context->notify_ag_on_new_codecs){
hfp_hf_cmd_notify_on_codecs(context->rfcomm_cid);
context->negotiated_codec = 0;
context->wait_ok = 1;
context->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED;
break;
}
if (context->establish_audio_connection){
// TODO AUDIO CONNECTION
}
break;
default:
break;
}
// deal with disconnect
switch (context->state){
case HFP_W2_DISCONNECT_RFCOMM:
context->state = HFP_W4_RFCOMM_DISCONNECTED;
rfcomm_disconnect_internal(context->rfcomm_cid);
break;
default:
break;
}
}
void hfp_hf_switch_on_ok(hfp_connection_t *context){
// printf("switch on ok\n");
context->wait_ok = 0;
hfp_hf_switch_on_ok_handle_service_level_connection_establishment(context);
hfp_hf_switch_on_ok_handle_service_level_connection_queries(context);
// handle audio connection setup
switch (context->state){
case HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED:
if (context->notify_ag_on_new_codecs){
context->notify_ag_on_new_codecs = 0;
if (context->trigger_codec_connection_setup){
hfp_run_for_context(context);
break;
}
break;
}
if (context->trigger_codec_connection_setup){
context->trigger_codec_connection_setup = 0;
context->state = HFP_SLE_W2_EXCHANGE_COMMON_CODEC;
break;
}
break;
case HFP_SLE_W4_EXCHANGE_COMMON_CODEC:
context->negotiated_codec = context->remote_codec_received;
context->remote_codec_received = 0;
context->state = HFP_CODECS_CONNECTION_ESTABLISHED;
break;
case HFP_AUDIO_CONNECTION_ESTABLISHED:
printf("HFP_AUDIO_CONNECTION_ESTABLISHED \n");
break;
default:
break;
@ -443,18 +557,29 @@ void handle_switch_on_ok(hfp_connection_t *context){
context->command = HFP_CMD_NONE;
}
static void 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;
packet[size] = 0;
int pos;
int pos, i;
printf("parse command: %s\n", packet+2);
for (pos = 0; pos < size ; pos++){
hfp_parse(context, packet[pos]);
// emit indicators status changed
for (i = 0; i < context->ag_indicators_nr; i++){
if (context->ag_indicators[i].status_changed) {
hfp_emit_ag_indicator_event(hfp_callback, 0, context->ag_indicators[i]);
context->ag_indicators[i].status_changed = 0;
break;
}
}
if (context->command == HFP_CMD_ERROR){
context->wait_ok = 0;
hfp_reset_context_flags(context);
hfp_emit_event(hfp_callback, HFP_SUBEVENT_COMPLETE, 1);
return;
}
@ -466,7 +591,7 @@ static void hfp_handle_rfcomm_event(uint8_t packet_type, uint16_t channel, uint8
}
if (context->command != HFP_CMD_OK) continue;
handle_switch_on_ok(context);
hfp_hf_switch_on_ok(context);
}
}
@ -492,24 +617,36 @@ static void packet_handler(void * connection, uint8_t packet_type, uint16_t chan
hfp_run();
}
void hfp_hf_init(uint16_t rfcomm_channel_nr, uint32_t supported_features, uint8_t * codecs, int codecs_nr, uint16_t * indicators, int indicators_nr, uint32_t indicators_status){
void hfp_hf_set_codecs(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);
log_error("hfp_hf_set_codecs: codecs_nr (%d) > HFP_MAX_NUM_CODECS (%d)", codecs_nr, HFP_MAX_NUM_CODECS);
return;
}
rfcomm_register_packet_handler(packet_handler);
hfp_init(rfcomm_channel_nr);
hfp_supported_features = supported_features;
hfp_codecs_nr = codecs_nr;
int i;
for (i=0; i<codecs_nr; i++){
hfp_codecs[i] = codecs[i];
}
linked_list_iterator_t it;
linked_list_iterator_init(&it, hfp_get_connections());
while (linked_list_iterator_has_next(&it)){
hfp_connection_t * connection = (hfp_connection_t *)linked_list_iterator_next(&it);
if (!connection) continue;
connection->notify_ag_on_new_codecs = 1;
}
}
void hfp_hf_init(uint16_t rfcomm_channel_nr, uint32_t supported_features, uint16_t * indicators, int indicators_nr, uint32_t indicators_status){
rfcomm_register_packet_handler(packet_handler);
hfp_init(rfcomm_channel_nr);
hfp_supported_features = supported_features;
hfp_indicators_nr = indicators_nr;
hfp_indicators_status = indicators_status;
int i;
for (i=0; i<indicators_nr; i++){
hfp_indicators[i] = indicators[i];
}
@ -571,25 +708,40 @@ void hfp_hf_enable_report_extended_audio_gateway_error_result_code(bd_addr_t bd_
hfp_run_for_context(connection);
}
void hfp_hf_audio_connection_setup(bd_addr_t bd_addr){
void hfp_hf_establish_audio_connection(bd_addr_t bd_addr){
hfp_hf_establish_service_level_connection(bd_addr);
hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr);
if (!connection){
log_error("HFP HF: connection doesn't exist.");
return;
}
// TODO:
hfp_run_for_context(connection);
}
void hfp_hf_audio_connection_release(bd_addr_t bd_addr){
hfp_hf_establish_service_level_connection(bd_addr);
hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr);
if (!connection){
log_error("HFP HF: connection doesn't exist.");
if (connection->state == HFP_AUDIO_CONNECTION_ESTABLISHED) return;
connection->trigger_codec_connection_setup = 1;
connection->establish_audio_connection = 1;
if (!has_codec_negotiation_feature(connection)){
connection->trigger_codec_connection_setup = 0;
connection->establish_audio_connection = 0;
return;
}
// TODO:
if (connection->state == HFP_CODECS_CONNECTION_ESTABLISHED){
connection->trigger_codec_connection_setup = 0;
return;
}
hfp_run_for_context(connection);
}
void hfp_hf_release_audio_connection(bd_addr_t bd_addr){
hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr);
if (!connection) return;
if (connection->state >= HFP_W2_DISCONNECT_SCO) return;
connection->release_audio_connection = 1;
hfp_run_for_context(connection);
}

View File

@ -65,7 +65,8 @@ void hfp_hf_create_sdp_record(uint8_t * service, int rfcomm_channel_nr, const ch
* @brief Intialize HFP Hands-Free (HF) device.
* TODO: move optional params into setters
*/
void hfp_hf_init(uint16_t rfcomm_channel_nr, uint32_t supported_features, uint8_t * codecs, int codecs_nr, uint16_t * indicators, int indicators_nr, uint32_t indicators_status);
void hfp_hf_init(uint16_t rfcomm_channel_nr, uint32_t supported_features, uint16_t * indicators, int indicators_nr, uint32_t indicators_status);
void hfp_hf_set_codecs(uint8_t * codecs, int codecs_nr);
/**
* @brief Register callback for the HFP Hands-Free (HF) client.
@ -140,12 +141,12 @@ void hfp_hf_enable_report_extended_audio_gateway_error_result_code(bd_addr_t bd_
/**
* @brief
*/
void hfp_hf_audio_connection_setup(bd_addr_t bd_addr);
void hfp_hf_establish_audio_connection(bd_addr_t bd_addr);
/**
* @brief
*/
void hfp_hf_audio_connection_release(bd_addr_t bd_addr);
void hfp_hf_release_audio_connection(bd_addr_t bd_addr);
/* API_END */

View File

@ -149,11 +149,11 @@ static int stdin_process(struct data_source *ds){
break;
case 'b':
printf("Establish Audio connection %s...\n", bd_addr_to_str(device_addr));
hfp_ag_audio_connection_setup(device_addr);
hfp_ag_establish_audio_connection(device_addr);
break;
case 'B':
printf("Release Audio connection.\n");
hfp_ag_audio_connection_release(device_addr);
hfp_ag_release_audio_connection(device_addr);
break;
case 'd':
printf("Report AG failure\n");

View File

@ -148,11 +148,11 @@ static int stdin_process(struct data_source *ds){
break;
case 'b':
printf("Establish Audio connection %s...\n", bd_addr_to_str(device_addr));
hfp_hf_audio_connection_setup(device_addr);
hfp_hf_establish_audio_connection(device_addr);
break;
case 'B':
printf("Release Audio connection.\n");
hfp_hf_audio_connection_release(device_addr);
hfp_hf_release_audio_connection(device_addr);
break;
case 'd':
@ -238,8 +238,9 @@ int btstack_main(int argc, const char * argv[]){
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, 438, codecs, sizeof(codecs), indicators, sizeof(indicators)/sizeof(uint16_t), 1);
hfp_hf_init(rfcomm_channel_nr, 438, indicators, sizeof(indicators)/sizeof(uint16_t), 1);
hfp_hf_set_codecs(codecs, sizeof(codecs));
hfp_hf_register_packet_handler(packet_handler);
sdp_init();