Merge branch 'master' into ble-api-cleanup

This commit is contained in:
Matthias Ringwald 2015-11-28 14:50:36 +01:00
commit ce263fc808
13 changed files with 2422 additions and 484 deletions

View File

@ -689,7 +689,14 @@
#define HFP_SUBEVENT_PLACE_CALL_WITH_NUMBER 0x0D #define HFP_SUBEVENT_PLACE_CALL_WITH_NUMBER 0x0D
#define HFP_SUBEVENT_REDIAL_LAST_NUMBER 0x0E #define HFP_SUBEVENT_REDIAL_LAST_NUMBER 0x0E
#define HFP_SUBEVENT_ATTACH_NUMBER_TO_VOICE_TAG 0x0F #define HFP_SUBEVENT_ATTACH_NUMBER_TO_VOICE_TAG 0x0F
#define HFP_SUBEVENT_TRANSMIT_DTMF_CODES 0x10 #define HFP_SUBEVENT_NUMBER_FOR_VOICE_TAG 0x10
#define HFP_SUBEVENT_TRANSMIT_DTMF_CODES 0x11
#define HFP_SUBEVENT_TRANSMIT_STATUS_OF_CURRENT_CALL 0x12
#define HFP_SUBEVENT_CALL_ANSWERED 0x13
#define HFP_SUBEVENT_CONFERENCE_CALL 0x14
#define HFP_SUBEVENT_RING 0x15
#define HFP_SUBEVENT_SPEAKER_VOLUME 0x16
#define HFP_SUBEVENT_MICROPHONE_VOLUME 0x17
// ANCS Client // ANCS Client
#define ANCS_CLIENT_CONNECTED 0xF0 #define ANCS_CLIENT_CONNECTED 0xF0

View File

@ -101,6 +101,7 @@ static int hfp_generic_status_indicators_nr = 0;
static hfp_generic_status_indicator_t hfp_generic_status_indicators[HFP_MAX_NUM_HF_INDICATORS]; static hfp_generic_status_indicator_t hfp_generic_status_indicators[HFP_MAX_NUM_HF_INDICATORS];
static linked_list_t hfp_connections = NULL; static linked_list_t hfp_connections = NULL;
static void parse_sequence(hfp_connection_t * context);
hfp_generic_status_indicator_t * get_hfp_generic_status_indicators(void){ hfp_generic_status_indicator_t * get_hfp_generic_status_indicators(void){
return (hfp_generic_status_indicator_t *) &hfp_generic_status_indicators; return (hfp_generic_status_indicator_t *) &hfp_generic_status_indicators;
@ -272,7 +273,7 @@ void hfp_reset_context_flags(hfp_connection_t * context){
context->ok_pending = 0; context->ok_pending = 0;
context->send_error = 0; context->send_error = 0;
context->keep_separator = 0; context->keep_byte = 0;
context->change_status_update_for_individual_ag_indicators = 0; context->change_status_update_for_individual_ag_indicators = 0;
context->operator_name_changed = 0; context->operator_name_changed = 0;
@ -515,12 +516,38 @@ void hfp_handle_hci_event(hfp_callback_t callback, uint8_t packet_type, uint8_t
break; break;
case HCI_EVENT_SYNCHRONOUS_CONNECTION_COMPLETE:{ case HCI_EVENT_SYNCHRONOUS_CONNECTION_COMPLETE:{
bt_flip_addr(event_addr, &packet[5]); bt_flip_addr(event_addr, &packet[5]);
int index = 2; int index = 2;
uint8_t status = packet[index++]; uint8_t status = packet[index++];
if (status != 0){ if (status != 0){
log_error("(e)SCO Connection is not established, status %u", status); log_error("(e)SCO Connection failed status %u", status);
// if outgoing && link_setting != d0 && appropriate error
if (status != 0x11 && status != 0x1f) break; // invalid params / unspecified error
context = get_hfp_connection_context_for_bd_addr(event_addr);
if (!context) break;
switch (context->link_setting){
case HFP_LINK_SETTINGS_D0:
return; // no other option left
case HFP_LINK_SETTINGS_D1:
// context->link_setting = HFP_LINK_SETTINGS_D0;
// break;
case HFP_LINK_SETTINGS_S1:
// context->link_setting = HFP_LINK_SETTINGS_D1;
// break;
case HFP_LINK_SETTINGS_S2:
case HFP_LINK_SETTINGS_S3:
case HFP_LINK_SETTINGS_S4:
// context->link_setting = HFP_LINK_SETTINGS_S1;
// break;
case HFP_LINK_SETTINGS_T1:
case HFP_LINK_SETTINGS_T2:
// context->link_setting = HFP_LINK_SETTINGS_S3;
context->link_setting = HFP_LINK_SETTINGS_D0;
break;
}
context->establish_audio_connection = 1;
break; break;
} }
@ -612,7 +639,13 @@ void hfp_handle_hci_event(hfp_callback_t callback, uint8_t packet_type, uint8_t
} }
break; break;
default: case HCI_EVENT_INQUIRY_RESULT:
case HCI_EVENT_INQUIRY_RESULT_WITH_RSSI:
case HCI_EVENT_INQUIRY_COMPLETE:
case BTSTACK_EVENT_REMOTE_NAME_CACHED:
case HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE:
// forward inquiry events to app - TODO: replace with new event handler architecture
(*callback)(packet, size);
break; break;
} }
} }
@ -621,8 +654,16 @@ void hfp_handle_hci_event(hfp_callback_t callback, uint8_t packet_type, uint8_t
static hfp_command_t parse_command(const char * line_buffer, int isHandsFree){ static hfp_command_t parse_command(const char * line_buffer, int isHandsFree){
int offset = isHandsFree ? 0 : 2; int offset = isHandsFree ? 0 : 2;
if (strncmp(line_buffer+offset, HFP_LIST_CURRENT_CALLS, strlen(HFP_LIST_CURRENT_CALLS)) == 0){
return HFP_CMD_LIST_CURRENT_CALLS;
}
if (strncmp(line_buffer+offset, HFP_SUBSCRIBER_NUMBER_INFORMATION, strlen(HFP_SUBSCRIBER_NUMBER_INFORMATION)) == 0){
return HFP_CMD_GET_SUBSCRIBER_NUMBER_INFORMATION;
}
if (strncmp(line_buffer+offset, HFP_PHONE_NUMBER_FOR_VOICE_TAG, strlen(HFP_PHONE_NUMBER_FOR_VOICE_TAG)) == 0){ if (strncmp(line_buffer+offset, HFP_PHONE_NUMBER_FOR_VOICE_TAG, strlen(HFP_PHONE_NUMBER_FOR_VOICE_TAG)) == 0){
if (isHandsFree) return HFP_CMD_AG_SEND_PHONE_NUMBER; if (isHandsFree) return HFP_CMD_AG_SENT_PHONE_NUMBER;
return HFP_CMD_HF_REQUEST_PHONE_NUMBER; return HFP_CMD_HF_REQUEST_PHONE_NUMBER;
} }
@ -655,7 +696,7 @@ static hfp_command_t parse_command(const char * line_buffer, int isHandsFree){
return HFP_CMD_CALL_PHONE_NUMBER; return HFP_CMD_CALL_PHONE_NUMBER;
} }
if (strncmp(line_buffer, HFP_REDIAL_LAST_NUMBER, strlen(HFP_REDIAL_LAST_NUMBER)) == 0){ if (strncmp(line_buffer+offset, HFP_REDIAL_LAST_NUMBER, strlen(HFP_REDIAL_LAST_NUMBER)) == 0){
return HFP_CMD_REDIAL_LAST_NUMBER; return HFP_CMD_REDIAL_LAST_NUMBER;
} }
@ -671,6 +712,10 @@ static hfp_command_t parse_command(const char * line_buffer, int isHandsFree){
return HFP_CMD_ERROR; return HFP_CMD_ERROR;
} }
if (strncmp(line_buffer+offset, HFP_RING, strlen(HFP_RING)) == 0){
return HFP_CMD_RING;
}
if (isHandsFree && strncmp(line_buffer+offset, HFP_OK, strlen(HFP_OK)) == 0){ if (isHandsFree && strncmp(line_buffer+offset, HFP_OK, strlen(HFP_OK)) == 0){
return HFP_CMD_OK; return HFP_CMD_OK;
} }
@ -679,6 +724,20 @@ static hfp_command_t parse_command(const char * line_buffer, int isHandsFree){
return HFP_CMD_SUPPORTED_FEATURES; return HFP_CMD_SUPPORTED_FEATURES;
} }
if (strncmp(line_buffer+offset, HFP_TRANSFER_HF_INDICATOR_STATUS, strlen(HFP_TRANSFER_HF_INDICATOR_STATUS)) == 0){
return HFP_CMD_HF_INDICATOR_STATUS;
}
if (strncmp(line_buffer+offset, HFP_RESPONSE_AND_HOLD, strlen(HFP_RESPONSE_AND_HOLD)) == 0){
if (strncmp(line_buffer+strlen(HFP_RESPONSE_AND_HOLD)+offset, "?", 1) == 0){
return HFP_CMD_RESPONSE_AND_HOLD_QUERY;
}
if (strncmp(line_buffer+strlen(HFP_RESPONSE_AND_HOLD)+offset, "=", 1) == 0){
return HFP_CMD_RESPONSE_AND_HOLD_COMMAND;
}
}
if (strncmp(line_buffer+offset, HFP_INDICATOR, strlen(HFP_INDICATOR)) == 0){ if (strncmp(line_buffer+offset, HFP_INDICATOR, strlen(HFP_INDICATOR)) == 0){
if (strncmp(line_buffer+strlen(HFP_INDICATOR)+offset, "?", 1) == 0){ if (strncmp(line_buffer+strlen(HFP_INDICATOR)+offset, "?", 1) == 0){
return HFP_CMD_RETRIEVE_AG_INDICATORS_STATUS; return HFP_CMD_RETRIEVE_AG_INDICATORS_STATUS;
@ -784,13 +843,8 @@ static hfp_command_t parse_command(const char * line_buffer, int isHandsFree){
return HFP_CMD_NONE; return HFP_CMD_NONE;
} }
#if 0
uint32_t fromBinary(char *s) {
return (uint32_t) strtol(s, NULL, 2);
}
#endif
static void hfp_parser_store_byte(hfp_connection_t * context, uint8_t byte){ static void hfp_parser_store_byte(hfp_connection_t * context, uint8_t byte){
// printf("hfp_parser_store_byte %c at pos %u\n", (char) byte, context->line_size);
// TODO: add limit // TODO: add limit
context->line_buffer[context->line_size++] = byte; context->line_buffer[context->line_size++] = byte;
context->line_buffer[context->line_size] = 0; context->line_buffer[context->line_size] = 0;
@ -808,7 +862,7 @@ static int hfp_parser_is_end_of_header(uint8_t byte){
} }
static int hfp_parser_found_separator(hfp_connection_t * context, uint8_t byte){ static int hfp_parser_found_separator(hfp_connection_t * context, uint8_t byte){
if (context->keep_separator == 1) return 1; if (context->keep_byte == 1) return 1;
int found_separator = byte == ',' || byte == '\n'|| byte == '\r'|| int found_separator = byte == ',' || byte == '\n'|| byte == '\r'||
byte == ')' || byte == '(' || byte == ':' || byte == ')' || byte == '(' || byte == ':' ||
@ -826,22 +880,20 @@ static void hfp_parser_next_state(hfp_connection_t * context, uint8_t byte){
switch (context->parser_state){ switch (context->parser_state){
case HFP_PARSER_CMD_HEADER: case HFP_PARSER_CMD_HEADER:
context->parser_state = HFP_PARSER_CMD_SEQUENCE; context->parser_state = HFP_PARSER_CMD_SEQUENCE;
if (context->keep_separator == 1){ if (context->keep_byte == 1){
hfp_parser_store_byte(context, byte); hfp_parser_store_byte(context, byte);
context->keep_separator = 0; context->keep_byte = 0;
} }
break; break;
case HFP_PARSER_CMD_SEQUENCE: case HFP_PARSER_CMD_SEQUENCE:
switch (context->command){ switch (context->command){
case HFP_CMD_AG_SENT_PHONE_NUMBER:
case HFP_CMD_TRANSFER_AG_INDICATOR_STATUS: case HFP_CMD_TRANSFER_AG_INDICATOR_STATUS:
case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME: case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME:
case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT: case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT:
context->parser_state = HFP_PARSER_SECOND_ITEM;
break;
case HFP_CMD_RETRIEVE_AG_INDICATORS: case HFP_CMD_RETRIEVE_AG_INDICATORS:
context->parser_state = HFP_PARSER_SECOND_ITEM;
break;
case HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS_STATE: case HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS_STATE:
case HFP_CMD_HF_INDICATOR_STATUS:
context->parser_state = HFP_PARSER_SECOND_ITEM; context->parser_state = HFP_PARSER_SECOND_ITEM;
break; break;
default: default:
@ -862,8 +914,6 @@ static void hfp_parser_next_state(hfp_connection_t * context, uint8_t byte){
} }
void hfp_parse(hfp_connection_t * context, uint8_t byte, int isHandsFree){ void hfp_parse(hfp_connection_t * context, uint8_t byte, int isHandsFree){
int value;
// handle ATD<dial_string>; // handle ATD<dial_string>;
if (strncmp((const char*)context->line_buffer, HFP_CALL_PHONE_NUMBER, strlen(HFP_CALL_PHONE_NUMBER)) == 0){ if (strncmp((const char*)context->line_buffer, HFP_CALL_PHONE_NUMBER, strlen(HFP_CALL_PHONE_NUMBER)) == 0){
// check for end-of-line or ';' // check for end-of-line or ';'
@ -879,11 +929,21 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte, int isHandsFree){
// TODO: handle space inside word // TODO: handle space inside word
if (byte == ' ' && context->parser_state > HFP_PARSER_CMD_HEADER) return; if (byte == ' ' && context->parser_state > HFP_PARSER_CMD_HEADER) return;
if (byte == ',' && context->command == HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE){
if (context->line_size == 0){
context->line_buffer[0] = 0;
context->ignore_value = 1;
parse_sequence(context);
return;
}
}
if (!hfp_parser_found_separator(context, byte)){ if (!hfp_parser_found_separator(context, byte)){
hfp_parser_store_byte(context, byte); hfp_parser_store_byte(context, byte);
return; return;
} }
if (hfp_parser_is_end_of_line(byte)) { if (hfp_parser_is_end_of_line(byte)) {
if (hfp_parser_is_buffer_empty(context)){ if (hfp_parser_is_buffer_empty(context)){
context->parser_state = HFP_PARSER_CMD_HEADER; context->parser_state = HFP_PARSER_CMD_HEADER;
@ -891,23 +951,27 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte, int isHandsFree){
} }
if (hfp_parser_is_buffer_empty(context)) return; if (hfp_parser_is_buffer_empty(context)) return;
switch (context->parser_state){ switch (context->parser_state){
case HFP_PARSER_CMD_HEADER: // header case HFP_PARSER_CMD_HEADER: // header
if (byte == '='){ if (byte == '='){
context->keep_separator = 1; context->keep_byte = 1;
hfp_parser_store_byte(context, byte); hfp_parser_store_byte(context, byte);
return; return;
} }
if (byte == '?'){ if (byte == '?'){
context->keep_separator = 0; context->keep_byte = 0;
hfp_parser_store_byte(context, byte); hfp_parser_store_byte(context, byte);
return; return;
} }
// printf(" parse header 2 %s, keep separator $ %d\n", context->line_buffer, context->keep_separator);
if (hfp_parser_is_end_of_header(byte) || context->keep_separator == 1){ if (byte == ','){
// printf(" parse header 3 %s, keep separator $ %d\n", context->line_buffer, context->keep_separator); context->resolve_byte = 1;
}
// printf(" parse header 2 %s, keep separator $ %d\n", context->line_buffer, context->keep_byte);
if (hfp_parser_is_end_of_header(byte) || context->keep_byte == 1){
// printf(" parse header 3 %s, keep separator $ %d\n", context->line_buffer, context->keep_byte);
char * line_buffer = (char *)context->line_buffer; char * line_buffer = (char *)context->line_buffer;
context->command = parse_command(line_buffer, isHandsFree); context->command = parse_command(line_buffer, isHandsFree);
@ -936,135 +1000,17 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte, int isHandsFree){
} }
break; break;
case HFP_PARSER_CMD_SEQUENCE: // parse comma separated sequence, ignore breacktes case HFP_PARSER_CMD_SEQUENCE:
switch (context->command){ parse_sequence(context);
case HFP_CMD_SET_MICROPHONE_GAIN:
value = atoi((char *)&context->line_buffer[0]);
context->microphone_gain = value;
log_info("hfp parse HFP_CMD_SET_MICROPHONE_GAIN %d\n", value);
break;
case HFP_CMD_SET_SPEAKER_GAIN:
value = atoi((char *)&context->line_buffer[0]);
context->speaker_gain = value;
log_info("hfp parse HFP_CMD_SET_SPEAKER_GAIN %d\n", value);
break;
case HFP_CMD_HF_ACTIVATE_VOICE_RECOGNITION:
value = atoi((char *)&context->line_buffer[0]);
context->ag_activate_voice_recognition = value;
log_info("hfp parse HFP_CMD_HF_ACTIVATE_VOICE_RECOGNITION %d\n", value);
break;
case HFP_CMD_TURN_OFF_EC_AND_NR:
value = atoi((char *)&context->line_buffer[0]);
context->ag_echo_and_noise_reduction = value;
log_info("hfp parse HFP_CMD_TURN_OFF_EC_AND_NR %d\n", value);
break;
case HFP_CMD_CHANGE_IN_BAND_RING_TONE_SETTING:
value = atoi((char *)&context->line_buffer[0]);
context->remote_supported_features = store_bit(context->remote_supported_features, HFP_AGSF_IN_BAND_RING_TONE, value);
log_info("hfp parse HFP_CHANGE_IN_BAND_RING_TONE_SETTING %d\n", value);
break;
case HFP_CMD_HF_CONFIRMED_CODEC:
context->codec_confirmed = atoi((char*)context->line_buffer);
log_info("hfp parse HFP_CMD_HF_CONFIRMED_CODEC %d\n", context->codec_confirmed);
break;
case HFP_CMD_AG_SUGGESTED_CODEC:
context->suggested_codec = atoi((char*)context->line_buffer);
log_info("hfp parse HFP_CMD_AG_SUGGESTED_CODEC %d\n", context->suggested_codec);
break;
case HFP_CMD_SUPPORTED_FEATURES:
context->remote_supported_features = atoi((char*)context->line_buffer);
log_info("Parsed supported feature %d\n", context->remote_supported_features);
break;
case HFP_CMD_AVAILABLE_CODECS:
log_info("Parsed codec %s\n", context->line_buffer);
context->remote_codecs[context->parser_item_index] = (uint16_t)atoi((char*)context->line_buffer);
context->parser_item_index++;
context->remote_codecs_nr = context->parser_item_index;
break;
case HFP_CMD_RETRIEVE_AG_INDICATORS:
strcpy((char *)context->ag_indicators[context->parser_item_index].name, (char *)context->line_buffer);
context->ag_indicators[context->parser_item_index].index = context->parser_item_index+1;
log_info("Indicator %d: %s (", context->ag_indicators_nr+1, context->line_buffer);
break;
case HFP_CMD_RETRIEVE_AG_INDICATORS_STATUS:
log_info("Parsed Indicator %d with status: %s\n", context->parser_item_index+1, context->line_buffer);
context->ag_indicators[context->parser_item_index].status = atoi((char *) context->line_buffer);
context->parser_item_index++;
break;
case HFP_CMD_ENABLE_INDICATOR_STATUS_UPDATE:
context->parser_item_index++;
if (context->parser_item_index != 4) break;
log_info("Parsed Enable indicators: %s\n", context->line_buffer);
value = atoi((char *)&context->line_buffer[0]);
context->enable_status_update_for_ag_indicators = (uint8_t) value;
break;
case HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES:
log_info("Parsed Support call hold: %s\n", context->line_buffer);
if (context->line_size > 2 ) break;
strcpy((char *)context->remote_call_services[context->remote_call_services_nr].name, (char *)context->line_buffer);
context->remote_call_services_nr++;
break;
case HFP_CMD_LIST_GENERIC_STATUS_INDICATORS:
case HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS:
log_info("Parsed Generic status indicator: %s\n", context->line_buffer);
context->generic_status_indicators[context->parser_item_index].uuid = (uint16_t)atoi((char*)context->line_buffer);
context->parser_item_index++;
context->generic_status_indicators_nr = context->parser_item_index;
break;
case HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS_STATE:
// HF parses inital AG gen. ind. state
log_info("Parsed List generic status indicator %s state: ", context->line_buffer);
context->parser_item_index = (uint8_t)atoi((char*)context->line_buffer);
break;
case HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE:
// AG parses new gen. ind. state
log_info("Parsed Enable ag indicator state: %s\n", context->line_buffer);
value = atoi((char *)&context->line_buffer[0]);
if (!context->ag_indicators[context->parser_item_index].mandatory){
context->ag_indicators[context->parser_item_index].enabled = value;
}
context->parser_item_index++;
break;
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);
break;
case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME:
context->network_operator.mode = atoi((char *)&context->line_buffer[0]);
log_info("Parsed network operator mode: %d, ", context->network_operator.mode);
break;
case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT:
if (context->line_buffer[0] == '3'){
log_info("Parsed Set network operator format : %s, ", context->line_buffer);
break;
}
// TODO emit ERROR, wrong format
log_info("ERROR Set network operator format: index %s not supported\n", context->line_buffer);
break;
case HFP_CMD_ERROR:
break;
case HFP_CMD_EXTENDED_AUDIO_GATEWAY_ERROR:
context->extended_audio_gateway_error = (uint8_t)atoi((char*)context->line_buffer);
break;
case HFP_CMD_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR:
context->enable_extended_audio_gateway_error_report = (uint8_t)atoi((char*)context->line_buffer);
context->ok_pending = 1;
context->extended_audio_gateway_error = 0;
break;
default:
break;
}
break; break;
case HFP_PARSER_SECOND_ITEM: case HFP_PARSER_SECOND_ITEM:
switch (context->command){ switch (context->command){
case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME: case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME:
printf("format %s, ", context->line_buffer); log_info("format %s, ", context->line_buffer);
context->network_operator.format = atoi((char *)&context->line_buffer[0]); context->network_operator.format = atoi((char *)&context->line_buffer[0]);
break; break;
case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT: case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT:
printf("format %s \n", context->line_buffer); log_info("format %s \n", context->line_buffer);
context->network_operator.format = atoi((char *)&context->line_buffer[0]); context->network_operator.format = atoi((char *)&context->line_buffer[0]);
break; break;
case HFP_CMD_LIST_GENERIC_STATUS_INDICATORS: case HFP_CMD_LIST_GENERIC_STATUS_INDICATORS:
@ -1074,13 +1020,16 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte, int isHandsFree){
break; break;
case HFP_CMD_TRANSFER_AG_INDICATOR_STATUS: case HFP_CMD_TRANSFER_AG_INDICATOR_STATUS:
context->ag_indicators[context->parser_item_index].status = (uint8_t)atoi((char*)context->line_buffer); context->ag_indicators[context->parser_item_index].status = (uint8_t)atoi((char*)context->line_buffer);
printf("%d \n", context->ag_indicators[context->parser_item_index].status); log_info("%d \n", context->ag_indicators[context->parser_item_index].status);
context->ag_indicators[context->parser_item_index].status_changed = 1; context->ag_indicators[context->parser_item_index].status_changed = 1;
break; break;
case HFP_CMD_RETRIEVE_AG_INDICATORS: case HFP_CMD_RETRIEVE_AG_INDICATORS:
context->ag_indicators[context->parser_item_index].min_range = atoi((char *)context->line_buffer); context->ag_indicators[context->parser_item_index].min_range = atoi((char *)context->line_buffer);
log_info("%s, ", context->line_buffer); log_info("%s, ", context->line_buffer);
break; break;
case HFP_CMD_AG_SENT_PHONE_NUMBER:
context->bnip_type = (uint8_t)atoi((char*)context->line_buffer);
break;
default: default:
break; break;
} }
@ -1104,6 +1053,153 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte, int isHandsFree){
break; break;
} }
hfp_parser_next_state(context, byte); hfp_parser_next_state(context, byte);
if (context->resolve_byte && context->command == HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE){
context->resolve_byte = 0;
context->ignore_value = 1;
parse_sequence(context);
context->line_buffer[0] = 0;
context->line_size = 0;
}
}
static void parse_sequence(hfp_connection_t * context){
int value;
switch (context->command){
case HFP_CMD_SET_MICROPHONE_GAIN:
value = atoi((char *)&context->line_buffer[0]);
context->microphone_gain = value;
log_info("hfp parse HFP_CMD_SET_MICROPHONE_GAIN %d\n", value);
break;
case HFP_CMD_SET_SPEAKER_GAIN:
value = atoi((char *)&context->line_buffer[0]);
context->speaker_gain = value;
log_info("hfp parse HFP_CMD_SET_SPEAKER_GAIN %d\n", value);
break;
case HFP_CMD_HF_ACTIVATE_VOICE_RECOGNITION:
value = atoi((char *)&context->line_buffer[0]);
context->ag_activate_voice_recognition = value;
log_info("hfp parse HFP_CMD_HF_ACTIVATE_VOICE_RECOGNITION %d\n", value);
break;
case HFP_CMD_TURN_OFF_EC_AND_NR:
value = atoi((char *)&context->line_buffer[0]);
context->ag_echo_and_noise_reduction = value;
log_info("hfp parse HFP_CMD_TURN_OFF_EC_AND_NR %d\n", value);
break;
case HFP_CMD_CHANGE_IN_BAND_RING_TONE_SETTING:
value = atoi((char *)&context->line_buffer[0]);
context->remote_supported_features = store_bit(context->remote_supported_features, HFP_AGSF_IN_BAND_RING_TONE, value);
log_info("hfp parse HFP_CHANGE_IN_BAND_RING_TONE_SETTING %d\n", value);
break;
case HFP_CMD_HF_CONFIRMED_CODEC:
context->codec_confirmed = atoi((char*)context->line_buffer);
log_info("hfp parse HFP_CMD_HF_CONFIRMED_CODEC %d\n", context->codec_confirmed);
break;
case HFP_CMD_AG_SUGGESTED_CODEC:
context->suggested_codec = atoi((char*)context->line_buffer);
log_info("hfp parse HFP_CMD_AG_SUGGESTED_CODEC %d\n", context->suggested_codec);
break;
case HFP_CMD_SUPPORTED_FEATURES:
context->remote_supported_features = atoi((char*)context->line_buffer);
log_info("Parsed supported feature %d\n", context->remote_supported_features);
break;
case HFP_CMD_AVAILABLE_CODECS:
log_info("Parsed codec %s\n", context->line_buffer);
context->remote_codecs[context->parser_item_index] = (uint16_t)atoi((char*)context->line_buffer);
context->parser_item_index++;
context->remote_codecs_nr = context->parser_item_index;
break;
case HFP_CMD_RETRIEVE_AG_INDICATORS:
strcpy((char *)context->ag_indicators[context->parser_item_index].name, (char *)context->line_buffer);
context->ag_indicators[context->parser_item_index].index = context->parser_item_index+1;
log_info("Indicator %d: %s (", context->ag_indicators_nr+1, context->line_buffer);
break;
case HFP_CMD_RETRIEVE_AG_INDICATORS_STATUS:
log_info("Parsed Indicator %d with status: %s\n", context->parser_item_index+1, context->line_buffer);
context->ag_indicators[context->parser_item_index].status = atoi((char *) context->line_buffer);
context->parser_item_index++;
break;
case HFP_CMD_ENABLE_INDICATOR_STATUS_UPDATE:
context->parser_item_index++;
if (context->parser_item_index != 4) break;
log_info("Parsed Enable indicators: %s\n", context->line_buffer);
value = atoi((char *)&context->line_buffer[0]);
context->enable_status_update_for_ag_indicators = (uint8_t) value;
break;
case HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES:
log_info("Parsed Support call hold: %s\n", context->line_buffer);
if (context->line_size > 2 ) break;
strcpy((char *)context->remote_call_services[context->remote_call_services_nr].name, (char *)context->line_buffer);
context->remote_call_services_nr++;
break;
case HFP_CMD_LIST_GENERIC_STATUS_INDICATORS:
case HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS:
log_info("Parsed Generic status indicator: %s\n", context->line_buffer);
context->generic_status_indicators[context->parser_item_index].uuid = (uint16_t)atoi((char*)context->line_buffer);
context->parser_item_index++;
context->generic_status_indicators_nr = context->parser_item_index;
break;
case HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS_STATE:
// HF parses inital AG gen. ind. state
log_info("Parsed List generic status indicator %s state: ", context->line_buffer);
context->parser_item_index = (uint8_t)atoi((char*)context->line_buffer);
break;
case HFP_CMD_HF_INDICATOR_STATUS:
context->parser_indicator_index = (uint8_t)atoi((char*)context->line_buffer);
log_info("Parsed HF indicator index %u", context->parser_indicator_index);
break;
case HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE:
// AG parses new gen. ind. state
if (context->ignore_value){
context->ignore_value = 0;
log_info("Parsed Enable AG indicator pos %u('%s') - unchanged (stays %u)\n", context->parser_item_index,
context->ag_indicators[context->parser_item_index].name, context->ag_indicators[context->parser_item_index].enabled);
}
else if (context->ag_indicators[context->parser_item_index].mandatory){
log_info("Parsed Enable AG indicator pos %u('%s') - ignore (mandatory)\n",
context->parser_item_index, context->ag_indicators[context->parser_item_index].name);
} else {
value = atoi((char *)&context->line_buffer[0]);
context->ag_indicators[context->parser_item_index].enabled = value;
log_info("Parsed Enable AG indicator pos %u('%s'): %u\n", context->parser_item_index,
context->ag_indicators[context->parser_item_index].name, value);
}
context->parser_item_index++;
break;
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);
break;
case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME:
context->network_operator.mode = atoi((char *)&context->line_buffer[0]);
log_info("Parsed network operator mode: %d, ", context->network_operator.mode);
break;
case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT:
if (context->line_buffer[0] == '3'){
log_info("Parsed Set network operator format : %s, ", context->line_buffer);
break;
}
// TODO emit ERROR, wrong format
log_info("ERROR Set network operator format: index %s not supported\n", context->line_buffer);
break;
case HFP_CMD_ERROR:
break;
case HFP_CMD_EXTENDED_AUDIO_GATEWAY_ERROR:
context->extended_audio_gateway_error = (uint8_t)atoi((char*)context->line_buffer);
break;
case HFP_CMD_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR:
context->enable_extended_audio_gateway_error_report = (uint8_t)atoi((char*)context->line_buffer);
context->ok_pending = 1;
context->extended_audio_gateway_error = 0;
break;
case HFP_CMD_AG_SENT_PHONE_NUMBER:
strncpy(context->bnip_number, (char *)context->line_buffer, sizeof(context->bnip_number));
context->bnip_number[sizeof(context->bnip_number)-1] = 0;
break;
default:
break;
}
} }
void hfp_init(uint16_t rfcomm_channel_nr){ void hfp_init(uint16_t rfcomm_channel_nr){
@ -1171,4 +1267,24 @@ void hfp_release_audio_connection(hfp_connection_t * context){
context->release_audio_connection = 1; context->release_audio_connection = 1;
} }
static const struct link_settings {
const uint16_t max_latency;
const uint8_t retransmission_effort;
const uint16_t packet_types;
} hfp_link_settings [] = {
{ 0xffff, 0xff, 0x03c1 }, // HFP_LINK_SETTINGS_D0, HV1
{ 0xffff, 0xff, 0x03c4 }, // HFP_LINK_SETTINGS_D1, HV3
{ 0x0007, 0x01, 0x03c8 }, // HFP_LINK_SETTINGS_S1, EV3
{ 0x0007, 0x01, 0x0380 }, // HFP_LINK_SETTINGS_S2, 2-EV3
{ 0x000a, 0x01, 0x0380 }, // HFP_LINK_SETTINGS_S3, 2-EV3
{ 0x000c, 0x02, 0x0380 }, // HFP_LINK_SETTINGS_S4, 2-EV3
{ 0x0008, 0x02, 0x03c8 }, // HFP_LINK_SETTINGS_T1, EV3
{ 0x000d, 0x02, 0x0380 } // HFP_LINK_SETTINGS_T2, 2-EV3
};
void hfp_setup_synchronous_connection(hci_con_handle_t handle, hfp_link_setttings_t setting){
// all packet types, fixed bandwidth
log_info("hfp_setup_synchronous_connection using setting nr %u", setting);
hci_send_cmd(&hci_setup_synchronous_connection, handle, 8000, 8000, hfp_link_settings[setting].max_latency,
hci_get_sco_voice_setting(), hfp_link_settings[setting].retransmission_effort, hfp_link_settings[setting].packet_types); // all types 0x003f, only 2-ev3 0x380
}

View File

@ -71,7 +71,7 @@ extern "C" {
#define HFP_HFSF_VOICE_RECOGNITION_FUNCTION 3 #define HFP_HFSF_VOICE_RECOGNITION_FUNCTION 3
#define HFP_HFSF_CODEC_NEGOTIATION 7 #define HFP_HFSF_CODEC_NEGOTIATION 7
#define HFP_HFSF_HF_INDICATORS 8 #define HFP_HFSF_HF_INDICATORS 8
#define HFP_HFSF_ESCO 9 #define HFP_HFSF_ESCO_S4 9
/* AG Supported Features: /* AG Supported Features:
0: Three-way calling 0: Three-way calling
@ -94,7 +94,7 @@ extern "C" {
#define HFP_AGSF_IN_BAND_RING_TONE 3 #define HFP_AGSF_IN_BAND_RING_TONE 3
#define HFP_AGSF_CODEC_NEGOTIATION 9 #define HFP_AGSF_CODEC_NEGOTIATION 9
#define HFP_AGSF_HF_INDICATORS 10 #define HFP_AGSF_HF_INDICATORS 10
#define HFP_AGSF_ESCO 11 #define HFP_AGSF_ESCO_S4 11
#define HFP_DEFAULT_HF_SUPPORTED_FEATURES 0x0000 #define HFP_DEFAULT_HF_SUPPORTED_FEATURES 0x0000
#define HFP_DEFAULT_AG_SUPPORTED_FEATURES 0x0009 #define HFP_DEFAULT_AG_SUPPORTED_FEATURES 0x0009
@ -114,6 +114,7 @@ extern "C" {
#define HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES "+CHLD" #define HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES "+CHLD"
#define HFP_GENERIC_STATUS_INDICATOR "+BIND" #define HFP_GENERIC_STATUS_INDICATOR "+BIND"
#define HFP_TRANSFER_AG_INDICATOR_STATUS "+CIEV" // +CIEV: <index>,<value> #define HFP_TRANSFER_AG_INDICATOR_STATUS "+CIEV" // +CIEV: <index>,<value>
#define HFP_TRANSFER_HF_INDICATOR_STATUS "+BIEV" // +BIEC: <index>,<value>
#define HFP_QUERY_OPERATOR_SELECTION "+COPS" // +COPS: <mode>,0,<opearator> #define HFP_QUERY_OPERATOR_SELECTION "+COPS" // +COPS: <mode>,0,<opearator>
#define HFP_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR "+CMEE" #define HFP_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR "+CMEE"
#define HFP_EXTENDED_AUDIO_GATEWAY_ERROR "+CME ERROR" #define HFP_EXTENDED_AUDIO_GATEWAY_ERROR "+CME ERROR"
@ -123,18 +124,20 @@ extern "C" {
#define HFP_HANG_UP_CALL "+CHUP" #define HFP_HANG_UP_CALL "+CHUP"
#define HFP_CHANGE_IN_BAND_RING_TONE_SETTING "+BSIR" #define HFP_CHANGE_IN_BAND_RING_TONE_SETTING "+BSIR"
#define HFP_CALL_PHONE_NUMBER "ATD" #define HFP_CALL_PHONE_NUMBER "ATD"
#define HFP_REDIAL_LAST_NUMBER "AT+BLDN" #define HFP_REDIAL_LAST_NUMBER "+BLDN"
#define HFP_TURN_OFF_EC_AND_NR "+NREC" // EC (Echo CAnceling), NR (Noise Reduction) #define HFP_TURN_OFF_EC_AND_NR "+NREC" // EC (Echo CAnceling), NR (Noise Reduction)
#define HFP_ACTIVATE_VOICE_RECOGNITION "+BVRA" // EC (Echo CAnceling), NR (Noise Reduction) #define HFP_ACTIVATE_VOICE_RECOGNITION "+BVRA" // EC (Echo CAnceling), NR (Noise Reduction)
#define HFP_SET_MICROPHONE_GAIN "+VGM" #define HFP_SET_MICROPHONE_GAIN "+VGM"
#define HFP_SET_SPEAKER_GAIN "+VGS" #define HFP_SET_SPEAKER_GAIN "+VGS"
#define HFP_PHONE_NUMBER_FOR_VOICE_TAG "+BINP" #define HFP_PHONE_NUMBER_FOR_VOICE_TAG "+BINP"
#define HFP_TRANSMIT_DTMF_CODES "+VTS" #define HFP_TRANSMIT_DTMF_CODES "+VTS"
#define HFP_SUBSCRIBER_NUMBER_INFORMATION "+CNUM"
#define HFP_LIST_CURRENT_CALLS "+CLCC"
#define HFP_RESPONSE_AND_HOLD "+BTRH"
#define HFP_OK "OK" #define HFP_OK "OK"
#define HFP_ERROR "ERROR" #define HFP_ERROR "ERROR"
#define HFP_RING "RING"
// Codecs // Codecs
#define HFP_CODEC_CVSD 0x01 #define HFP_CODEC_CVSD 0x01
@ -145,6 +148,7 @@ typedef enum {
HFP_CMD_ERROR, HFP_CMD_ERROR,
HFP_CMD_UNKNOWN, HFP_CMD_UNKNOWN,
HFP_CMD_OK, HFP_CMD_OK,
HFP_CMD_RING,
HFP_CMD_SUPPORTED_FEATURES, HFP_CMD_SUPPORTED_FEATURES,
HFP_CMD_AVAILABLE_CODECS, HFP_CMD_AVAILABLE_CODECS,
@ -183,10 +187,15 @@ typedef enum {
HFP_CMD_AG_ACTIVATE_VOICE_RECOGNITION, HFP_CMD_AG_ACTIVATE_VOICE_RECOGNITION,
HFP_CMD_HF_ACTIVATE_VOICE_RECOGNITION, HFP_CMD_HF_ACTIVATE_VOICE_RECOGNITION,
HFP_CMD_HF_REQUEST_PHONE_NUMBER, HFP_CMD_HF_REQUEST_PHONE_NUMBER,
HFP_CMD_AG_SEND_PHONE_NUMBER, HFP_CMD_AG_SENT_PHONE_NUMBER,
HFP_CMD_TRANSMIT_DTMF_CODES, HFP_CMD_TRANSMIT_DTMF_CODES,
HFP_CMD_SET_MICROPHONE_GAIN, HFP_CMD_SET_MICROPHONE_GAIN,
HFP_CMD_SET_SPEAKER_GAIN HFP_CMD_SET_SPEAKER_GAIN,
HFP_CMD_GET_SUBSCRIBER_NUMBER_INFORMATION,
HFP_CMD_LIST_CURRENT_CALLS,
HFP_CMD_RESPONSE_AND_HOLD_QUERY,
HFP_CMD_RESPONSE_AND_HOLD_COMMAND,
HFP_CMD_HF_INDICATOR_STATUS
} hfp_command_t; } hfp_command_t;
@ -255,9 +264,16 @@ typedef enum {
HFP_AG_OUTGOING_CALL_RINGING, HFP_AG_OUTGOING_CALL_RINGING,
HFP_AG_OUTGOING_CALL_ESTABLISHED, HFP_AG_OUTGOING_CALL_ESTABLISHED,
HFP_AG_OUTGOING_REDIAL_INITIATED, HFP_AG_OUTGOING_REDIAL_INITIATED,
HFP_AG_HELD_CALL_JOINED_BY_AG,
HFP_AG_TERMINATE_CALL_BY_AG, HFP_AG_TERMINATE_CALL_BY_AG,
HFP_AG_TERMINATE_CALL_BY_HF, HFP_AG_TERMINATE_CALL_BY_HF,
HFP_AG_CALL_DROPPED, HFP_AG_CALL_DROPPED,
HFP_AG_RESPONSE_AND_HOLD_ACCEPT_INCOMING_CALL_BY_AG,
HFP_AG_RESPONSE_AND_HOLD_ACCEPT_HELD_CALL_BY_AG,
HFP_AG_RESPONSE_AND_HOLD_REJECT_HELD_CALL_BY_AG,
HFP_AG_RESPONSE_AND_HOLD_ACCEPT_INCOMING_CALL_BY_HF,
HFP_AG_RESPONSE_AND_HOLD_ACCEPT_HELD_CALL_BY_HF,
HFP_AG_RESPONSE_AND_HOLD_REJECT_HELD_CALL_BY_HF,
} hfp_ag_call_event_t; } hfp_ag_call_event_t;
@ -325,6 +341,7 @@ typedef enum {
HFP_CODECS_W4_AG_COMMON_CODEC, HFP_CODECS_W4_AG_COMMON_CODEC,
HFP_CODECS_AG_SENT_COMMON_CODEC, HFP_CODECS_AG_SENT_COMMON_CODEC,
HFP_CODECS_AG_RESEND_COMMON_CODEC, HFP_CODECS_AG_RESEND_COMMON_CODEC,
HFP_CODECS_HF_CONFIRMED_CODEC,
HFP_CODECS_EXCHANGED, HFP_CODECS_EXCHANGED,
HFP_CODECS_ERROR HFP_CODECS_ERROR
} hfp_codecs_state_t; } hfp_codecs_state_t;
@ -343,6 +360,58 @@ typedef enum {
HFP_CALL_OUTGOING_RINGING HFP_CALL_OUTGOING_RINGING
} hfp_call_state_t; } hfp_call_state_t;
typedef enum{
HFP_ENHANCED_CALL_DIR_OUTGOING,
HFP_ENHANCED_CALL_DIR_INCOMING
} hfp_enhanced_call_dir_t;
typedef enum{
HFP_ENHANCED_CALL_STATUS_ACTIVE,
HFP_ENHANCED_CALL_STATUS_HELD,
HFP_ENHANCED_CALL_STATUS_OUTGOING_DIALING,
HFP_ENHANCED_CALL_STATUS_OUTGOING_ALERTING,
HFP_ENHANCED_CALL_STATUS_INCOMING,
HFP_ENHANCED_CALL_STATUS_INCOMING_WAITING,
HFP_ENHANCED_CALL_STATUS_CALL_HELD_BY_RESPONSE_AND_HOLD
} hfp_enhanced_call_status_t;
typedef enum{
HFP_ENHANCED_CALL_MODE_VOICE,
HFP_ENHANCED_CALL_MODE_DATA,
HFP_ENHANCED_CALL_MODE_FAX
} hfp_enhanced_call_mode_t;
typedef enum{
HFP_ENHANCED_CALL_MPTY_NOT_A_CONFERENCE_CALL,
HFP_ENHANCED_CALL_MPTY_CONFERENCE_CALL
} hfp_enhanced_call_mpty_t;
typedef enum {
HFP_RESPONSE_AND_HOLD_INCOMING_ON_HOLD = 0,
HFP_RESPONSE_AND_HOLD_HELD_INCOMING_ACCEPTED,
HFP_RESPONSE_AND_HOLD_HELD_INCOMING_REJECTED
} hfp_response_and_hold_state_t;
typedef enum {
HFP_HF_QUERY_OPERATOR_FORMAT_NOT_SET = 0,
HFP_HF_QUERY_OPERATOR_SET_FORMAT,
HFP_HF_QUERY_OPERATOR_W4_SET_FORMAT_OK,
HFP_HF_QUERY_OPERATOR_FORMAT_SET,
HFP_HF_QUERY_OPERATOR_SEND_QUERY,
HPF_HF_QUERY_OPERATOR_W4_RESULT
} hfp_hf_query_operator_state_t;
typedef enum {
HFP_LINK_SETTINGS_D0 = 0,
HFP_LINK_SETTINGS_D1,
HFP_LINK_SETTINGS_S1,
HFP_LINK_SETTINGS_S2,
HFP_LINK_SETTINGS_S3,
HFP_LINK_SETTINGS_S4,
HFP_LINK_SETTINGS_T1,
HFP_LINK_SETTINGS_T2
} hfp_link_setttings_t;
typedef enum{ typedef enum{
HFP_NONE_SM, HFP_NONE_SM,
HFP_SLC_SM, HFP_SLC_SM,
@ -403,6 +472,7 @@ typedef struct hfp_connection {
hfp_command_t command; hfp_command_t command;
hfp_parser_state_t parser_state; hfp_parser_state_t parser_state;
int parser_item_index; int parser_item_index;
int parser_indicator_index;
uint8_t line_buffer[HFP_MAX_INDICATOR_DESC_SIZE]; uint8_t line_buffer[HFP_MAX_INDICATOR_DESC_SIZE];
int line_size; int line_size;
@ -436,7 +506,9 @@ typedef struct hfp_connection {
// uint8_t send_ok; // uint8_t send_ok;
uint8_t send_error; uint8_t send_error;
uint8_t keep_separator; uint8_t keep_byte;
uint8_t ignore_value;
uint8_t resolve_byte;
uint8_t change_status_update_for_individual_ag_indicators; uint8_t change_status_update_for_individual_ag_indicators;
uint8_t operator_name_changed; uint8_t operator_name_changed;
@ -448,14 +520,12 @@ typedef struct hfp_connection {
uint8_t suggested_codec; uint8_t suggested_codec;
uint8_t codec_confirmed; uint8_t codec_confirmed;
hfp_link_setttings_t link_setting;
uint8_t establish_audio_connection; uint8_t establish_audio_connection;
uint8_t release_audio_connection; uint8_t release_audio_connection;
uint8_t change_in_band_ring_tone_setting; timer_source_t hfp_timeout;
uint8_t ag_ring;
uint8_t ag_send_clip;
uint8_t ag_echo_and_noise_reduction;
uint8_t ag_activate_voice_recognition;
uint8_t microphone_gain; uint8_t microphone_gain;
uint8_t send_microphone_gain; uint8_t send_microphone_gain;
@ -464,7 +534,50 @@ typedef struct hfp_connection {
uint8_t send_speaker_gain; uint8_t send_speaker_gain;
uint8_t send_phone_number_for_voice_tag; uint8_t send_phone_number_for_voice_tag;
timer_source_t hfp_timeout; uint8_t send_ag_status_indicators;
uint8_t send_response_and_hold_active;
uint8_t send_response_and_hold_status;
// AG only
uint8_t change_in_band_ring_tone_setting;
uint8_t ag_ring;
uint8_t ag_send_clip;
uint8_t ag_echo_and_noise_reduction;
uint8_t ag_activate_voice_recognition;
uint8_t send_subscriber_number;
uint8_t next_subscriber_number_to_send;
int send_status_of_current_calls;
// HF only
hfp_hf_query_operator_state_t hf_query_operator_state;
uint8_t hf_answer_incoming_call;
uint8_t hf_initiate_outgoing_call;
uint8_t hf_initiate_memory_dialing;
uint8_t hf_initiate_redial_last_number;
uint8_t hf_send_clip_enable;
uint8_t hf_send_chup;
uint8_t hf_send_chld_0;
uint8_t hf_send_chld_1;
uint8_t hf_send_chld_2;
uint8_t hf_send_chld_3;
uint8_t hf_send_chld_4;
char hf_send_dtmf_code;
uint8_t hf_send_binp;
uint8_t hf_activate_call_waiting_notification;
uint8_t hf_deactivate_call_waiting_notification;
uint8_t hf_activate_calling_line_notification;
uint8_t hf_deactivate_calling_line_notification;
uint8_t hf_activate_echo_canceling_and_noise_reduction;
uint8_t hf_deactivate_echo_canceling_and_noise_reduction;
uint8_t hf_activate_voice_recognition_notification;
uint8_t hf_deactivate_voice_recognition_notification;
uint8_t bnip_type; // 0 == not set
char bnip_number[25]; //
} hfp_connection_t; } hfp_connection_t;
// UTILS_START : TODO move to utils // UTILS_START : TODO move to utils
@ -498,6 +611,8 @@ void hfp_reset_context_flags(hfp_connection_t * context);
void hfp_release_audio_connection(hfp_connection_t * context); void hfp_release_audio_connection(hfp_connection_t * context);
void hfp_setup_synchronous_connection(hci_con_handle_t handle, hfp_link_setttings_t link_settings);
const char * hfp_hf_feature(int index); const char * hfp_hf_feature(int index);
const char * hfp_ag_feature(int index); const char * hfp_ag_feature(int index);

View File

@ -79,13 +79,21 @@ static hfp_callback_t hfp_callback;
static hfp_call_status_t hfp_ag_call_state; static hfp_call_status_t hfp_ag_call_state;
static hfp_callsetup_status_t hfp_ag_callsetup_state; static hfp_callsetup_status_t hfp_ag_callsetup_state;
static hfp_callheld_status_t hfp_ag_callheld_state; static hfp_callheld_status_t hfp_ag_callheld_state;
static hfp_response_and_hold_state_t hfp_ag_response_and_hold_state;
static int hfp_ag_response_and_hold_active = 0;
// CLIP feature // CLIP feature
static uint8_t clip_type; // 0 == not set static uint8_t clip_type; // 0 == not set
static char clip_number[25]; // static char clip_number[25]; //
// Subcriber information entries
static hfp_phone_number_t * subscriber_numbers = NULL;
static int subscriber_numbers_count = 0;
static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
static void hfp_run_for_context(hfp_connection_t *context); static void hfp_run_for_context(hfp_connection_t *context);
static void hfp_ag_setup_audio_connection(hfp_connection_t * connection); static void hfp_ag_setup_audio_connection(hfp_connection_t * connection);
static void hfp_ag_hf_start_ringing(hfp_connection_t * context);
hfp_generic_status_indicator_t * get_hfp_generic_status_indicators(); hfp_generic_status_indicator_t * get_hfp_generic_status_indicators();
int get_hfp_generic_status_indicators_nr(); int get_hfp_generic_status_indicators_nr();
@ -94,7 +102,6 @@ void set_hfp_ag_indicators(hfp_ag_indicator_t * indicators, int indicator_nr);
int get_hfp_ag_indicators_nr(hfp_connection_t * context); int get_hfp_ag_indicators_nr(hfp_connection_t * context);
hfp_ag_indicator_t * get_hfp_ag_indicators(hfp_connection_t * context); hfp_ag_indicator_t * get_hfp_ag_indicators(hfp_connection_t * context);
hfp_ag_indicator_t * get_hfp_ag_indicators(hfp_connection_t * context){ hfp_ag_indicator_t * get_hfp_ag_indicators(hfp_connection_t * context){
// TODO: save only value, and value changed in the context? // TODO: save only value, and value changed in the context?
if (context->ag_indicators_nr != hfp_ag_indicators_nr){ if (context->ag_indicators_nr != hfp_ag_indicators_nr){
@ -216,12 +223,17 @@ static int hfp_ag_send_clip(uint16_t cid){
return send_str_over_rfcomm(cid, buffer); return send_str_over_rfcomm(cid, buffer);
} }
static int hfp_ag_send_phone_number_for_voice_tag_cmd(uint16_t cid){ static int hfp_send_subscriber_number_cmd(uint16_t cid, uint8_t type, const char * number){
char buffer[50]; char buffer[50];
sprintf(buffer, "\r\n%s:%s\r\n", HFP_PHONE_NUMBER_FOR_VOICE_TAG, clip_number); sprintf(buffer, "\r\n%s: ,\"%s\",%u, , \r\n", HFP_SUBSCRIBER_NUMBER_INFORMATION, number, type);
return send_str_over_rfcomm(cid, buffer);
}
static int hfp_ag_send_phone_number_for_voice_tag_cmd(uint16_t cid){
char buffer[50];
sprintf(buffer, "\r\n%s: %s\r\n", HFP_PHONE_NUMBER_FOR_VOICE_TAG, clip_number);
return send_str_over_rfcomm(cid, buffer); return send_str_over_rfcomm(cid, buffer);
} }
static int hfp_ag_send_call_waiting_notification(uint16_t cid){ static int hfp_ag_send_call_waiting_notification(uint16_t cid){
if (!clip_type){ if (!clip_type){
@ -422,6 +434,12 @@ static int hfp_ag_set_microphone_gain_cmd(uint16_t cid, uint8_t gain){
return send_str_over_rfcomm(cid, buffer); return send_str_over_rfcomm(cid, buffer);
} }
static int hfp_ag_set_response_and_hold(uint16_t cid, int state){
char buffer[30];
sprintf(buffer, "\r\n%s: %d\r\n", HFP_RESPONSE_AND_HOLD, state);
return send_str_over_rfcomm(cid, buffer);
}
static uint8_t hfp_ag_suggest_codec(hfp_connection_t *context){ static uint8_t hfp_ag_suggest_codec(hfp_connection_t *context){
int i,j; int i,j;
@ -510,14 +528,33 @@ static int codecs_exchange_state_machine(hfp_connection_t * context){
return 0; return 0;
} }
static void hfp_init_link_settings(hfp_connection_t * context){
// determine highest possible link setting
context->link_setting = HFP_LINK_SETTINGS_D1;
if (hci_remote_eSCO_supported(context->con_handle)){
context->link_setting = HFP_LINK_SETTINGS_S3;
if ((context->remote_supported_features & (1<<HFP_HFSF_ESCO_S4))
&& (hfp_supported_features & (1<<HFP_AGSF_ESCO_S4))){
context->link_setting = HFP_LINK_SETTINGS_S4;
}
}
}
static void hfp_ag_slc_established(hfp_connection_t * context){ static void hfp_ag_slc_established(hfp_connection_t * context){
context->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED; context->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED;
hfp_emit_event(hfp_callback, HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED, 0); hfp_emit_event(hfp_callback, HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED, 0);
hfp_init_link_settings(context);
// if active call exist, set per-connection state active, too (when audio is on) // if active call exist, set per-connection state active, too (when audio is on)
if (hfp_ag_call_state == HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT){ if (hfp_ag_call_state == HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT){
context->call_state = HFP_CALL_W4_AUDIO_CONNECTION_FOR_ACTIVE; context->call_state = HFP_CALL_W4_AUDIO_CONNECTION_FOR_ACTIVE;
} }
// if AG is ringing, also start ringing on the HF
if (hfp_ag_call_state == HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS &&
hfp_ag_callsetup_state == HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS){
hfp_ag_hf_start_ringing(context);
}
} }
static int hfp_ag_run_for_context_service_level_connection(hfp_connection_t * context){ static int hfp_ag_run_for_context_service_level_connection(hfp_connection_t * context){
@ -576,10 +613,11 @@ static int hfp_ag_run_for_context_service_level_connection(hfp_connection_t * co
if (context->state != HFP_W4_RETRIEVE_CAN_HOLD_CALL) break; if (context->state != HFP_W4_RETRIEVE_CAN_HOLD_CALL) break;
if (has_hf_indicators_feature(context)){ if (has_hf_indicators_feature(context)){
context->state = HFP_W4_LIST_GENERIC_STATUS_INDICATORS; context->state = HFP_W4_LIST_GENERIC_STATUS_INDICATORS;
} else {
hfp_ag_slc_established(context);
} }
hfp_ag_retrieve_can_hold_call_cmd(context->rfcomm_cid); hfp_ag_retrieve_can_hold_call_cmd(context->rfcomm_cid);
if (!has_hf_indicators_feature(context)){
hfp_ag_slc_established(context);
}
return 1; return 1;
case HFP_CMD_LIST_GENERIC_STATUS_INDICATORS: case HFP_CMD_LIST_GENERIC_STATUS_INDICATORS:
@ -649,15 +687,14 @@ static int hfp_ag_run_for_context_service_level_connection_queries(hfp_connectio
return 1; return 1;
} }
case HFP_CMD_ENABLE_INDICATOR_STATUS_UPDATE: case HFP_CMD_ENABLE_INDICATOR_STATUS_UPDATE:
printf("TODO\n"); hfp_ag_ok(context->rfcomm_cid);
break; return 1;
default: default:
break; break;
} }
return 0; return 0;
} }
static int hfp_ag_run_for_audio_connection(hfp_connection_t * context){ static int hfp_ag_run_for_audio_connection(hfp_connection_t * context){
if (context->state < HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED || if (context->state < HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED ||
context->state > HFP_W2_DISCONNECT_SCO) return 0; context->state > HFP_W2_DISCONNECT_SCO) return 0;
@ -681,9 +718,8 @@ static int hfp_ag_run_for_audio_connection(hfp_connection_t * context){
if (context->establish_audio_connection){ if (context->establish_audio_connection){
context->state = HFP_W4_SCO_CONNECTED; context->state = HFP_W4_SCO_CONNECTED;
context->establish_audio_connection = 0; context->establish_audio_connection = 0;
hci_send_cmd(&hci_setup_synchronous_connection, context->con_handle, 8000, 8000, 0xFFFF, hci_get_sco_voice_setting(), 0xFF, 0x003F); hfp_setup_synchronous_connection(context->con_handle, context->link_setting);
return 1; return 1;
} }
return 0; return 0;
} }
@ -732,13 +768,13 @@ static void hfp_timeout_stop(hfp_connection_t * context){
// //
static void hfp_ag_hf_start_ringing(hfp_connection_t * context){ static void hfp_ag_hf_start_ringing(hfp_connection_t * context){
hfp_timeout_start(context);
context->ag_ring = 1;
context->ag_send_clip = clip_type && context->clip_enabled;
if (use_in_band_tone()){ if (use_in_band_tone()){
context->call_state = HFP_CALL_W4_AUDIO_CONNECTION_FOR_IN_BAND_RING; context->call_state = HFP_CALL_W4_AUDIO_CONNECTION_FOR_IN_BAND_RING;
hfp_ag_establish_audio_connection(context->remote_addr); hfp_ag_establish_audio_connection(context->remote_addr);
} else { } else {
hfp_timeout_start(context);
context->ag_ring = 1;
context->ag_send_clip = clip_type && context->clip_enabled;
context->call_state = HFP_CALL_RINGING; context->call_state = HFP_CALL_RINGING;
hfp_emit_event(hfp_callback, HFP_SUBEVENT_START_RINGINIG, 0); hfp_emit_event(hfp_callback, HFP_SUBEVENT_START_RINGINIG, 0);
} }
@ -894,6 +930,7 @@ static void hfp_ag_trigger_terminate_call(void){
if (connection->call_state == HFP_CALL_IDLE) continue; if (connection->call_state == HFP_CALL_IDLE) continue;
connection->call_state = HFP_CALL_IDLE; connection->call_state = HFP_CALL_IDLE;
connection->ag_indicators_status_update_bitmap = store_bit(connection->ag_indicators_status_update_bitmap, call_indicator_index, 1); connection->ag_indicators_status_update_bitmap = store_bit(connection->ag_indicators_status_update_bitmap, call_indicator_index, 1);
connection->release_audio_connection = 1;
hfp_run_for_context(connection); hfp_run_for_context(connection);
} }
hfp_emit_event(hfp_callback, HFP_SUBEVENT_CALL_TERMINATED, 0); hfp_emit_event(hfp_callback, HFP_SUBEVENT_CALL_TERMINATED, 0);
@ -947,12 +984,25 @@ static hfp_connection_t * hfp_ag_connection_for_call_state(hfp_call_state_t call
return NULL; return NULL;
} }
static void hfp_ag_send_response_and_hold_state(void){
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);
connection->send_response_and_hold_status = 1;
}
}
static int call_setup_state_machine(hfp_connection_t * connection){ static int call_setup_state_machine(hfp_connection_t * connection){
int indicator_index; int indicator_index;
switch (connection->call_state){ switch (connection->call_state){
case HFP_CALL_W4_AUDIO_CONNECTION_FOR_IN_BAND_RING: case HFP_CALL_W4_AUDIO_CONNECTION_FOR_IN_BAND_RING:
if (connection->state != HFP_AUDIO_CONNECTION_ESTABLISHED) return 0; if (connection->state != HFP_AUDIO_CONNECTION_ESTABLISHED) return 0;
// we got event: audio connection established // we got event: audio connection established
hfp_timeout_start(connection);
connection->ag_ring = 1;
connection->ag_send_clip = clip_type && connection->clip_enabled;
connection->call_state = HFP_CALL_RINGING;
connection->call_state = HFP_CALL_RINGING; connection->call_state = HFP_CALL_RINGING;
hfp_emit_event(hfp_callback, HFP_SUBEVENT_START_RINGINIG, 0); hfp_emit_event(hfp_callback, HFP_SUBEVENT_START_RINGINIG, 0);
break; break;
@ -1024,10 +1074,8 @@ static void hfp_ag_call_sm(hfp_ag_call_event_t event, hfp_connection_t * connect
printf("AG: current call is placed on hold, incoming call gets active\n"); printf("AG: current call is placed on hold, incoming call gets active\n");
hfp_ag_set_callsetup_state(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS); hfp_ag_set_callsetup_state(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS);
hfp_ag_set_callheld_state(HFP_CALLHELD_STATUS_CALL_ON_HOLD_OR_SWAPPED); hfp_ag_set_callheld_state(HFP_CALLHELD_STATUS_CALL_ON_HOLD_OR_SWAPPED);
// TODO: update AG indicators for all connections hfp_ag_transfer_callsetup_state();
// context->ag_indicators_status_update_bitmap = store_bit(context->ag_indicators_status_update_bitmap, callsetup_indicator_index, 1); hfp_ag_transfer_callheld_state();
// context->ag_indicators_status_update_bitmap = store_bit(context->ag_indicators_status_update_bitmap, callheld_indicator_index, 1);
// context->call_state = HFP_CALL_ACTIVE;
break; break;
default: default:
break; break;
@ -1035,6 +1083,25 @@ static void hfp_ag_call_sm(hfp_ag_call_event_t event, hfp_connection_t * connect
break; break;
} }
break; break;
case HFP_AG_HELD_CALL_JOINED_BY_AG:
switch (hfp_ag_call_state){
case HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT:
switch (hfp_ag_callheld_state){
case HFP_CALLHELD_STATUS_CALL_ON_HOLD_OR_SWAPPED:
printf("AG: joining held call with active call\n");
hfp_ag_set_callheld_state(HFP_CALLHELD_STATUS_NO_CALLS_HELD);
hfp_ag_transfer_callheld_state();
break;
default:
break;
}
break;
default:
break;
}
break;
case HFP_AG_INCOMING_CALL_ACCEPTED_BY_HF: case HFP_AG_INCOMING_CALL_ACCEPTED_BY_HF:
// clear CLIP // clear CLIP
clip_type = 0; clip_type = 0;
@ -1046,6 +1113,7 @@ static void hfp_ag_call_sm(hfp_ag_call_event_t event, hfp_connection_t * connect
hfp_ag_set_call_state(HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT); hfp_ag_set_call_state(HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT);
hfp_ag_hf_accept_call(connection); hfp_ag_hf_accept_call(connection);
printf("HF answers call, accept call by GSM\n"); printf("HF answers call, accept call by GSM\n");
hfp_emit_event(hfp_callback, HFP_CMD_CALL_ANSWERED, 0);
break; break;
default: default:
break; break;
@ -1056,6 +1124,76 @@ static void hfp_ag_call_sm(hfp_ag_call_event_t event, hfp_connection_t * connect
} }
break; break;
case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_INCOMING_CALL_BY_AG:
// clear CLIP
clip_type = 0;
switch (hfp_ag_call_state){
case HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS:
switch (hfp_ag_callsetup_state){
case HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS:
hfp_ag_response_and_hold_active = 1;
hfp_ag_response_and_hold_state = HFP_RESPONSE_AND_HOLD_INCOMING_ON_HOLD;
hfp_ag_send_response_and_hold_state();
// as with regualr call
hfp_ag_set_call_state(HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT);
hfp_ag_set_callsetup_state(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS);
hfp_ag_ag_accept_call();
printf("AG response and hold - hold by AG\n");
break;
default:
break;
}
break;
default:
break;
}
break;
case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_INCOMING_CALL_BY_HF:
// clear CLIP
clip_type = 0;
switch (hfp_ag_call_state){
case HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS:
switch (hfp_ag_callsetup_state){
case HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS:
hfp_ag_response_and_hold_active = 1;
hfp_ag_response_and_hold_state = HFP_RESPONSE_AND_HOLD_INCOMING_ON_HOLD;
hfp_ag_send_response_and_hold_state();
// as with regualr call
hfp_ag_set_call_state(HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT);
hfp_ag_set_callsetup_state(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS);
hfp_ag_hf_accept_call(connection);
printf("AG response and hold - hold by HF\n");
break;
default:
break;
}
break;
default:
break;
}
break;
case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_HELD_CALL_BY_AG:
case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_HELD_CALL_BY_HF:
if (!hfp_ag_response_and_hold_active) break;
if (hfp_ag_response_and_hold_state != HFP_RESPONSE_AND_HOLD_INCOMING_ON_HOLD) break;
hfp_ag_response_and_hold_state = HFP_RESPONSE_AND_HOLD_HELD_INCOMING_ACCEPTED;
hfp_ag_send_response_and_hold_state();
printf("Held Call accepted and active\n");
break;
case HFP_AG_RESPONSE_AND_HOLD_REJECT_HELD_CALL_BY_AG:
case HFP_AG_RESPONSE_AND_HOLD_REJECT_HELD_CALL_BY_HF:
if (!hfp_ag_response_and_hold_active) break;
if (hfp_ag_response_and_hold_state != HFP_RESPONSE_AND_HOLD_INCOMING_ON_HOLD) break;
hfp_ag_response_and_hold_state = HFP_RESPONSE_AND_HOLD_HELD_INCOMING_REJECTED;
hfp_ag_send_response_and_hold_state();
// from terminate by ag
hfp_ag_set_call_state(HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS);
hfp_ag_trigger_terminate_call();
break;
case HFP_AG_TERMINATE_CALL_BY_HF: case HFP_AG_TERMINATE_CALL_BY_HF:
// clear CLIP // clear CLIP
clip_type = 0; clip_type = 0;
@ -1131,6 +1269,10 @@ static void hfp_ag_call_sm(hfp_ag_call_event_t event, hfp_connection_t * connect
hfp_ag_transfer_callsetup_state(); hfp_ag_transfer_callsetup_state();
break; break;
case HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT: case HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT:
if (hfp_ag_response_and_hold_active) {
hfp_ag_response_and_hold_state = HFP_RESPONSE_AND_HOLD_HELD_INCOMING_REJECTED;
hfp_ag_send_response_and_hold_state();
}
hfp_ag_set_callsetup_state(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS); hfp_ag_set_callsetup_state(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS);
hfp_ag_set_call_state(HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS); hfp_ag_set_call_state(HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS);
hfp_ag_trigger_terminate_call(); hfp_ag_trigger_terminate_call();
@ -1220,6 +1362,7 @@ static void hfp_ag_call_sm(hfp_ag_call_event_t event, hfp_connection_t * connect
hfp_ag_transfer_callheld_state(); hfp_ag_transfer_callheld_state();
} }
break; break;
default: default:
break; break;
} }
@ -1229,6 +1372,11 @@ static void hfp_run_for_context(hfp_connection_t *context){
if (!context) return; if (!context) return;
if (!rfcomm_can_send_packet_now(context->rfcomm_cid)) return; if (!rfcomm_can_send_packet_now(context->rfcomm_cid)) return;
if (context->send_status_of_current_calls){
hfp_emit_event(hfp_callback, HFP_SUBEVENT_TRANSMIT_STATUS_OF_CURRENT_CALL, 0);
return;
}
if (context->command == HFP_CMD_UNKNOWN){ if (context->command == HFP_CMD_UNKNOWN){
context->ok_pending = 0; context->ok_pending = 0;
context->send_error = 0; context->send_error = 0;
@ -1237,13 +1385,6 @@ static void hfp_run_for_context(hfp_connection_t *context){
return; return;
} }
if (context->ok_pending){
context->ok_pending = 0;
context->command = HFP_CMD_NONE;
hfp_ag_ok(context->rfcomm_cid);
return;
}
if (context->send_error){ if (context->send_error){
context->send_error = 0; context->send_error = 0;
context->command = HFP_CMD_NONE; context->command = HFP_CMD_NONE;
@ -1251,12 +1392,36 @@ static void hfp_run_for_context(hfp_connection_t *context){
return; return;
} }
// note: before update AG indicators and ok_pending
if (context->send_response_and_hold_status){
context->send_response_and_hold_status = 0;
hfp_ag_set_response_and_hold(context->rfcomm_cid, hfp_ag_response_and_hold_state);
return;
}
if (context->send_response_and_hold_active){
context->send_response_and_hold_active = 0;
hfp_ag_set_response_and_hold(context->rfcomm_cid, 0);
return;
}
if (context->ok_pending){
context->ok_pending = 0;
context->command = HFP_CMD_NONE;
hfp_ag_ok(context->rfcomm_cid);
return;
}
// update AG indicators // update AG indicators
if (context->ag_indicators_status_update_bitmap){ if (context->ag_indicators_status_update_bitmap){
int i; int i;
for (i=0;i<context->ag_indicators_nr;i++){ for (i=0;i<context->ag_indicators_nr;i++){
if (get_bit(context->ag_indicators_status_update_bitmap, i)){ if (get_bit(context->ag_indicators_status_update_bitmap, i)){
context->ag_indicators_status_update_bitmap = store_bit(context->ag_indicators_status_update_bitmap, i, 0); context->ag_indicators_status_update_bitmap = store_bit(context->ag_indicators_status_update_bitmap, i, 0);
if (!context->enable_status_update_for_ag_indicators) {
log_info("+CMER:3,0,0,0 - not sending update for '%s'", hfp_ag_indicators[i].name);
break;
}
hfp_ag_transfer_ag_indicators_status_cmd(context->rfcomm_cid, &hfp_ag_indicators[i]); hfp_ag_transfer_ag_indicators_status_cmd(context->rfcomm_cid, &hfp_ag_indicators[i]);
return; return;
} }
@ -1285,6 +1450,18 @@ static void hfp_run_for_context(hfp_connection_t *context){
return; return;
} }
if (context->send_subscriber_number){
if (context->next_subscriber_number_to_send < subscriber_numbers_count){
hfp_phone_number_t phone = subscriber_numbers[context->next_subscriber_number_to_send++];
hfp_send_subscriber_number_cmd(context->rfcomm_cid, phone.type, phone.number);
} else {
context->send_subscriber_number = 0;
context->next_subscriber_number_to_send = 0;
hfp_ag_ok(context->rfcomm_cid);
}
context->command = HFP_CMD_NONE;
}
if (context->send_microphone_gain){ if (context->send_microphone_gain){
context->send_microphone_gain = 0; context->send_microphone_gain = 0;
context->command = HFP_CMD_NONE; context->command = HFP_CMD_NONE;
@ -1299,6 +1476,12 @@ static void hfp_run_for_context(hfp_connection_t *context){
return; return;
} }
if (context->send_ag_status_indicators){
context->send_ag_status_indicators = 0;
hfp_ag_retrieve_indicators_cmd(context->rfcomm_cid, context);
return;
}
int done = hfp_ag_run_for_context_service_level_connection(context); int done = hfp_ag_run_for_context_service_level_connection(context);
if (!done){ if (!done){
done = hfp_ag_run_for_context_service_level_connection_queries(context); done = hfp_ag_run_for_context_service_level_connection_queries(context);
@ -1327,6 +1510,16 @@ static void hfp_run_for_context(hfp_connection_t *context){
context->command = HFP_CMD_NONE; context->command = HFP_CMD_NONE;
} }
} }
static hfp_generic_status_indicator_t *get_hf_indicator_by_number(int number){
int i;
for (i=0;i< get_hfp_generic_status_indicators_nr();i++){
hfp_generic_status_indicator_t * indicator = &get_hfp_generic_status_indicators()[i];
if (indicator->uuid == number){
return indicator;
}
}
return NULL;
}
static void hfp_handle_rfcomm_data(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ static void hfp_handle_rfcomm_data(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); hfp_connection_t * context = get_hfp_connection_context_for_rfcomm_cid(channel);
@ -1335,7 +1528,81 @@ static void hfp_handle_rfcomm_data(uint8_t packet_type, uint16_t channel, uint8_
for (pos = 0; pos < size ; pos++){ for (pos = 0; pos < size ; pos++){
hfp_parse(context, packet[pos], 0); hfp_parse(context, packet[pos], 0);
} }
hfp_generic_status_indicator_t * indicator;
int value;
switch(context->command){ switch(context->command){
case HFP_CMD_RESPONSE_AND_HOLD_QUERY:
if (hfp_ag_response_and_hold_active){
context->send_response_and_hold_active = 1;
}
context->ok_pending = 1;
break;
case HFP_CMD_RESPONSE_AND_HOLD_COMMAND:
value = atoi((char *)&context->line_buffer[0]);
printf("HF Response and Hold: %u\n", value);
switch(value){
case HFP_RESPONSE_AND_HOLD_INCOMING_ON_HOLD:
hfp_ag_call_sm(HFP_AG_RESPONSE_AND_HOLD_ACCEPT_INCOMING_CALL_BY_HF, context);
break;
case HFP_RESPONSE_AND_HOLD_HELD_INCOMING_ACCEPTED:
hfp_ag_call_sm(HFP_AG_RESPONSE_AND_HOLD_ACCEPT_HELD_CALL_BY_HF, context);
break;
case HFP_RESPONSE_AND_HOLD_HELD_INCOMING_REJECTED:
hfp_ag_call_sm(HFP_AG_RESPONSE_AND_HOLD_REJECT_HELD_CALL_BY_HF, context);
break;
default:
break;
}
context->ok_pending = 1;
break;
case HFP_CMD_HF_INDICATOR_STATUS:
context->command = HFP_CMD_NONE;
// find indicator by assigned number
indicator = get_hf_indicator_by_number(context->parser_indicator_index);
if (!indicator){
context->send_error = 1;
break;
}
value = atoi((char *)&context->line_buffer[0]);
switch (indicator->uuid){
case 1: // enhanced security
if (value > 1) {
context->send_error = 1;
return;
}
printf("HF Indicator 'enhanced security' set to %u\n", value);
break;
case 2: // battery level
if (value > 100){
context->send_error = 1;
return;
}
printf("HF Indicator 'battery' set to %u\n", value);
break;
default:
printf("HF Indicator unknown set to %u\n", value);
break;
}
context->ok_pending = 1;
break;
case HFP_CMD_RETRIEVE_AG_INDICATORS_STATUS:
// expected by SLC state machine
if (context->state < HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED) break;
context->send_ag_status_indicators = 1;
break;
case HFP_CMD_LIST_CURRENT_CALLS:
context->command = HFP_CMD_NONE;
context->send_status_of_current_calls = 1;
hfp_emit_event(hfp_callback, HFP_SUBEVENT_TRANSMIT_STATUS_OF_CURRENT_CALL, 0);
break;
case HFP_CMD_GET_SUBSCRIBER_NUMBER_INFORMATION:
if (subscriber_numbers_count == 0){
hfp_ag_ok(context->rfcomm_cid);
break;
}
context->next_subscriber_number_to_send = 0;
context->send_subscriber_number = 1;
break;
case HFP_CMD_TRANSMIT_DTMF_CODES: case HFP_CMD_TRANSMIT_DTMF_CODES:
context->command = HFP_CMD_NONE; context->command = HFP_CMD_NONE;
hfp_emit_string_event(hfp_callback, HFP_SUBEVENT_TRANSMIT_DTMF_CODES, (const char *) &context->line_buffer[0]); hfp_emit_string_event(hfp_callback, HFP_SUBEVENT_TRANSMIT_DTMF_CODES, (const char *) &context->line_buffer[0]);
@ -1420,6 +1687,7 @@ static void hfp_handle_rfcomm_data(uint8_t packet_type, uint16_t channel, uint8_
printf("AG: Join 3-way-call\n"); printf("AG: Join 3-way-call\n");
hfp_ag_set_callheld_state(HFP_CALLHELD_STATUS_NO_CALLS_HELD); hfp_ag_set_callheld_state(HFP_CALLHELD_STATUS_NO_CALLS_HELD);
context->ag_indicators_status_update_bitmap = store_bit(context->ag_indicators_status_update_bitmap, callheld_indicator_index, 1); context->ag_indicators_status_update_bitmap = store_bit(context->ag_indicators_status_update_bitmap, callheld_indicator_index, 1);
hfp_emit_event(hfp_callback, HFP_SUBEVENT_CONFERENCE_CALL, 0);
} }
context->call_state = HFP_CALL_ACTIVE; context->call_state = HFP_CALL_ACTIVE;
break; break;
@ -1641,6 +1909,10 @@ void hfp_ag_answer_incoming_call(void){
hfp_ag_call_sm(HFP_AG_INCOMING_CALL_ACCEPTED_BY_AG, NULL); hfp_ag_call_sm(HFP_AG_INCOMING_CALL_ACCEPTED_BY_AG, NULL);
} }
void hfp_ag_join_held_call(void){
hfp_ag_call_sm(HFP_AG_HELD_CALL_JOINED_BY_AG, NULL);
}
void hfp_ag_terminate_call(void){ void hfp_ag_terminate_call(void){
hfp_ag_call_sm(HFP_AG_TERMINATE_CALL_BY_AG, NULL); hfp_ag_call_sm(HFP_AG_TERMINATE_CALL_BY_AG, NULL);
} }
@ -1660,15 +1932,34 @@ void hfp_ag_outgoing_call_rejected(void){
void hfp_ag_outgoing_call_accepted(void){ void hfp_ag_outgoing_call_accepted(void){
hfp_ag_call_sm(HFP_AG_OUTGOING_CALL_ACCEPTED, NULL); hfp_ag_call_sm(HFP_AG_OUTGOING_CALL_ACCEPTED, NULL);
} }
void hfp_ag_hold_incoming_call(void){
hfp_ag_call_sm(HFP_AG_RESPONSE_AND_HOLD_ACCEPT_INCOMING_CALL_BY_AG, NULL);
}
void hfp_ag_accept_held_incoming_call(void) {
hfp_ag_call_sm(HFP_AG_RESPONSE_AND_HOLD_ACCEPT_HELD_CALL_BY_AG, NULL);
}
void hfp_ag_reject_held_incoming_call(void){
hfp_ag_call_sm(HFP_AG_RESPONSE_AND_HOLD_REJECT_HELD_CALL_BY_AG, NULL);
}
static void hfp_ag_set_ag_indicator(const char * name, int value){ static void hfp_ag_set_ag_indicator(const char * name, int value){
int indicator_index = get_ag_indicator_index_for_name(name); int indicator_index = get_ag_indicator_index_for_name(name);
if (indicator_index < 0) return; if (indicator_index < 0) return;
hfp_ag_indicators[indicator_index].status = value; hfp_ag_indicators[indicator_index].status = value;
linked_list_iterator_t it; linked_list_iterator_t it;
linked_list_iterator_init(&it, hfp_get_connections()); linked_list_iterator_init(&it, hfp_get_connections());
while (linked_list_iterator_has_next(&it)){ while (linked_list_iterator_has_next(&it)){
hfp_connection_t * connection = (hfp_connection_t *)linked_list_iterator_next(&it); hfp_connection_t * connection = (hfp_connection_t *)linked_list_iterator_next(&it);
if (!connection->ag_indicators[indicator_index].enabled) {
log_info("AG indicator '%s' changed to %u but not enabled", hfp_ag_indicators[indicator_index].name, value);
continue;
}
log_info("AG indicator '%s' changed to %u, request transfer statur", hfp_ag_indicators[indicator_index].name, value);
connection->ag_indicators_status_update_bitmap = store_bit(connection->ag_indicators_status_update_bitmap, indicator_index, 1); connection->ag_indicators_status_update_bitmap = store_bit(connection->ag_indicators_status_update_bitmap, indicator_index, 1);
hfp_run_for_context(connection); hfp_run_for_context(connection);
} }
@ -1733,6 +2024,7 @@ void hfp_ag_set_microphone_gain(bd_addr_t bd_addr, int gain){
connection->microphone_gain = gain; connection->microphone_gain = gain;
connection->send_microphone_gain = 1; connection->send_microphone_gain = 1;
} }
hfp_run_for_context(connection);
} }
/* /*
@ -1744,6 +2036,7 @@ void hfp_ag_set_speaker_gain(bd_addr_t bd_addr, int gain){
connection->speaker_gain = gain; connection->speaker_gain = gain;
connection->send_speaker_gain = 1; connection->send_speaker_gain = 1;
} }
hfp_run_for_context(connection);
} }
/* /*
@ -1765,3 +2058,30 @@ void hfp_ag_send_dtmf_code_done(bd_addr_t bd_addr){
connection->ok_pending = 1; connection->ok_pending = 1;
} }
void hfp_ag_set_subcriber_number_information(hfp_phone_number_t * numbers, int numbers_count){
subscriber_numbers = numbers;
subscriber_numbers_count = numbers_count;
}
void hfp_ag_send_current_call_status(bd_addr_t bd_addr, int idx, hfp_enhanced_call_dir_t dir,
hfp_enhanced_call_status_t status, hfp_enhanced_call_mode_t mode,
hfp_enhanced_call_mpty_t mpty, uint8_t type, const char * number){
hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr);
char buffer[100];
int offset = snprintf(buffer, sizeof(buffer), "\r\n%s: %d,%d,%d,%d,%d", HFP_LIST_CURRENT_CALLS, idx, dir, status, mode, mpty);
if (number){
offset += snprintf(buffer+offset, sizeof(buffer)-offset, ", \"%s\",%u", number, type);
}
snprintf(buffer+offset, sizeof(buffer)-offset, "\r\n");
send_str_over_rfcomm(connection->rfcomm_cid, buffer);
}
void hfp_ag_send_current_call_status_done(bd_addr_t bd_addr){
hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr);
connection->ok_pending = 1;
connection->send_status_of_current_calls = 0;
}

View File

@ -54,6 +54,10 @@ extern "C" {
#endif #endif
/* API_START */ /* API_START */
typedef struct {
uint8_t type;
const char * number;
} hfp_phone_number_t;
/** /**
* @brief Create HFP Audio Gateway (AG) SDP service record. * @brief Create HFP Audio Gateway (AG) SDP service record.
@ -208,7 +212,12 @@ void hfp_ag_call_dropped(void);
/** /**
* @brief * @brief
*/ */
void hfp_ag_answer_incoming_call(void); void hfp_ag_answer_incoming_call(void);
/**
* @brief
*/
void hfp_ag_join_held_call(void);
/** /**
* @brief * @brief
@ -266,6 +275,38 @@ void hfp_ag_reject_phone_number_for_voice_tag(bd_addr_t bd_addr);
*/ */
void hfp_ag_send_dtmf_code_done(bd_addr_t bd_addr); void hfp_ag_send_dtmf_code_done(bd_addr_t bd_addr);
/*
* @brief
*/
void hfp_ag_set_subcriber_number_information(hfp_phone_number_t * numbers, int numbers_count);
/*
* @brief
*/
void hfp_ag_send_current_call_status(bd_addr_t bd_addr, int idx, hfp_enhanced_call_dir_t dir,
hfp_enhanced_call_status_t status, hfp_enhanced_call_mode_t mode,
hfp_enhanced_call_mpty_t mpty, uint8_t type, const char * number);
/*
* @brief
*/
void hfp_ag_send_current_call_status_done(bd_addr_t bd_addr);
/*
* @brief
*/
void hfp_ag_hold_incoming_call(void);
/*
* @brief
*/
void hfp_ag_accept_held_incoming_call(void);
/*
* @brief
*/
void hfp_ag_reject_held_incoming_call(void);
/* API_END */ /* API_END */
#if defined __cplusplus #if defined __cplusplus

File diff suppressed because it is too large Load Diff

View File

@ -84,7 +84,6 @@ void hfp_hf_register_packet_handler(hfp_callback_t callback);
*/ */
void hfp_hf_establish_service_level_connection(bd_addr_t bd_addr); void hfp_hf_establish_service_level_connection(bd_addr_t bd_addr);
/** /**
* @brief Release the RFCOMM channel and the audio connection between the HF and the AG. * @brief Release the RFCOMM channel and the audio connection between the HF and the AG.
* TODO: trigger release of the audio connection * TODO: trigger release of the audio connection
@ -94,12 +93,13 @@ void hfp_hf_release_service_level_connection(bd_addr_t bd_addr);
/** /**
* @brief Deactivate/reactivate status update for all indicators in the AG. * @brief Deactivate/reactivate status update for all indicators in the AG.
*/ */
void hfp_hf_enable_status_update_for_all_ag_indicators(bd_addr_t bd_addr, uint8_t enable); void hfp_hf_enable_status_update_for_all_ag_indicators(bd_addr_t bd_addr);
void hfp_hf_disable_status_update_for_all_ag_indicators(bd_addr_t bd_addr);
/** /**
* @brief Deactivate/reactivate status update for the individual indicators in the AG using bitmap. * @brief Deactivate/reactivate status update for the individual indicators in the AG using bitmap.
*/ */
void hfp_hf_enable_status_update_for_individual_ag_indicators(bd_addr_t bd_addr, uint32_t indicators_status_bitmap); void hfp_hf_set_status_update_for_individual_ag_indicators(bd_addr_t bd_addr, uint32_t indicators_status_bitmap);
/** /**
@ -136,7 +136,8 @@ void hfp_hf_query_operator_selection(bd_addr_t bd_addr);
* - +CME ERROR: 31 - network Timeout. * - +CME ERROR: 31 - network Timeout.
* - +CME ERROR: 32 - network not allowed Emergency calls only * - +CME ERROR: 32 - network not allowed Emergency calls only
*/ */
void hfp_hf_enable_report_extended_audio_gateway_error_result_code(bd_addr_t bd_addr, uint8_t enable); void hfp_hf_enable_report_extended_audio_gateway_error_result_code(bd_addr_t bd_addr);
void hfp_hf_disable_report_extended_audio_gateway_error_result_code(bd_addr_t bd_addr);
/** /**
* @brief * @brief
@ -148,6 +149,121 @@ void hfp_hf_establish_audio_connection(bd_addr_t bd_addr);
*/ */
void hfp_hf_release_audio_connection(bd_addr_t bd_addr); void hfp_hf_release_audio_connection(bd_addr_t bd_addr);
/**
* @brief
*/
void hfp_hf_answer_incoming_call(bd_addr_t bd_addr);
/**
* @brief
*/
void hfp_hf_reject_call(bd_addr_t bd_addr);
/**
* @brief
*/
void hfp_hf_user_busy(bd_addr_t addr);
/**
* @brief
*/
void hfp_hf_end_active_and_accept_other(bd_addr_t addr);
/**
* @brief
*/
void hfp_hf_swap_calls(bd_addr_t addr);
/**
* @brief
*/
void hfp_hf_join_held_call(bd_addr_t addr);
/**
* @brief
*/
void hfp_hf_connect_calls(bd_addr_t addr);
/**
* @brief
*/
void hfp_hf_terminate_call(bd_addr_t bd_addr);
/**
* @brief
*/
void hfp_hf_dial_number(bd_addr_t bd_addr, char * number);
/**
* @brief
*/
void hfp_hf_dial_memory(bd_addr_t bd_addr, char * number);
/**
* @brief
*/
void hfp_hf_redial_last_number(bd_addr_t bd_addr);
/*
* @brief
*/
void hfp_hf_activate_call_waiting_notification(bd_addr_t bd_addr);
/*
* @brief
*/
void hfp_hf_deactivate_call_waiting_notification(bd_addr_t bd_addr);
/*
* @brief
*/
void hfp_hf_activate_calling_line_notification(bd_addr_t bd_addr);
/*
* @brief
*/
void hfp_hf_deactivate_calling_line_notification(bd_addr_t bd_addr);
/*
* @brief
*/
void hfp_hf_activate_echo_canceling_and_noise_reduction(bd_addr_t bd_addr);
/*
* @brief
*/
void hfp_hf_deactivate_echo_canceling_and_noise_reduction(bd_addr_t bd_addr);
/*
* @brief
*/
void hfp_hf_activate_voice_recognition_notification(bd_addr_t bd_addr);
/*
* @brief
*/
void hfp_hf_deactivate_voice_recognition_notification(bd_addr_t bd_addr);
/*
* @brief
*/
void hfp_hf_set_microphone_gain(bd_addr_t bd_addr, int gain);
/*
* @brief
*/
void hfp_hf_set_speaker_gain(bd_addr_t bd_addr, int gain);
/*
* @brief
*/
void hfp_hf_send_dtmf_code(bd_addr_t bd_addr, char code);
/*
* @brief
*/
void hfp_hf_request_phone_number_for_voice_tag(bd_addr_t addr);
/* API_END */ /* API_END */

View File

@ -1330,6 +1330,10 @@ static void event_handler(uint8_t *packet, int size){
} }
conn->role = HCI_ROLE_SLAVE; conn->role = HCI_ROLE_SLAVE;
conn->state = RECEIVED_CONNECTION_REQUEST; conn->state = RECEIVED_CONNECTION_REQUEST;
// store info about eSCO
if (link_type == 0x02){
conn->remote_supported_feature_eSCO = 1;
}
hci_run(); hci_run();
break; break;
@ -1403,9 +1407,12 @@ static void event_handler(uint8_t *packet, int size){
if (features[6] & (1 << 3)){ if (features[6] & (1 << 3)){
conn->bonding_flags |= BONDING_REMOTE_SUPPORTS_SSP; conn->bonding_flags |= BONDING_REMOTE_SUPPORTS_SSP;
} }
if (features[3] & (1<<7)){
conn->remote_supported_feature_eSCO = 1;
}
} }
conn->bonding_flags |= BONDING_RECEIVED_REMOTE_FEATURES; conn->bonding_flags |= BONDING_RECEIVED_REMOTE_FEATURES;
log_info("HCI_EVENT_READ_REMOTE_SUPPORTED_FEATURES_COMPLETE, bonding flags %x", conn->bonding_flags); log_info("HCI_EVENT_READ_REMOTE_SUPPORTED_FEATURES_COMPLETE, bonding flags %x, eSCO %u", conn->bonding_flags, conn->remote_supported_feature_eSCO);
if (conn->bonding_flags & BONDING_DEDICATED){ if (conn->bonding_flags & BONDING_DEDICATED){
conn->bonding_flags |= BONDING_SEND_AUTHENTICATE_REQUEST; conn->bonding_flags |= BONDING_SEND_AUTHENTICATE_REQUEST;
} }
@ -2319,13 +2326,20 @@ void hci_run(void){
return; return;
case RECEIVED_CONNECTION_REQUEST: case RECEIVED_CONNECTION_REQUEST:
log_info("sending hci_accept_connection_request"); log_info("sending hci_accept_connection_request, remote eSCO %u", connection->remote_supported_feature_eSCO);
connection->state = ACCEPTED_CONNECTION_REQUEST; connection->state = ACCEPTED_CONNECTION_REQUEST;
connection->role = HCI_ROLE_SLAVE; connection->role = HCI_ROLE_SLAVE;
if (connection->address_type == BD_ADDR_TYPE_CLASSIC){ if (connection->address_type == BD_ADDR_TYPE_CLASSIC){
hci_send_cmd(&hci_accept_connection_request, connection->address, 1); hci_send_cmd(&hci_accept_connection_request, connection->address, 1);
} else { } else {
hci_send_cmd(&hci_accept_synchronous_connection, connection->address, 8000, 8000, 0xFFFF, hci_stack->sco_voice_setting, 0xFF, 0x003F); // remote supported feature eSCO is set if link type is eSCO
if (connection->remote_supported_feature_eSCO){
// eSCO: S4 - max latency == transmission interval = 0x000c == 12 ms,
hci_send_cmd(&hci_accept_synchronous_connection, connection->address, 8000, 8000, 0x000c, hci_stack->sco_voice_setting, 0x02, 0x388);
} else {
// SCO: max latency, retransmission interval: N/A. any packet type
hci_send_cmd(&hci_accept_synchronous_connection, connection->address, 8000, 8000, 0xffff, hci_stack->sco_voice_setting, 0xff, 0x003f);
}
} }
return; return;
@ -2889,6 +2903,13 @@ void hci_emit_dedicated_bonding_result(bd_addr_t address, uint8_t status){
hci_stack->packet_handler(HCI_EVENT_PACKET, event, sizeof(event)); hci_stack->packet_handler(HCI_EVENT_PACKET, event, sizeof(event));
} }
// query if remote side supports eSCO
int hci_remote_eSCO_supported(hci_con_handle_t con_handle){
hci_connection_t * connection = hci_connection_for_handle(con_handle);
if (!connection) return 0;
return connection->remote_supported_feature_eSCO;
}
// query if remote side supports SSP // query if remote side supports SSP
int hci_remote_ssp_supported(hci_con_handle_t con_handle){ int hci_remote_ssp_supported(hci_con_handle_t con_handle){
hci_connection_t * connection = hci_connection_for_handle(con_handle); hci_connection_t * connection = hci_connection_for_handle(con_handle);

View File

@ -373,6 +373,9 @@ typedef struct {
// //
link_key_type_t link_key_type; link_key_type_t link_key_type;
// remote supported features
uint8_t remote_supported_feature_eSCO;
// errands // errands
uint32_t authentication_flags; uint32_t authentication_flags;
@ -710,6 +713,8 @@ void hci_disconnect_security_block(hci_con_handle_t con_handle);
// send complete CMD packet // send complete CMD packet
int hci_send_cmd_packet(uint8_t *packet, int size); int hci_send_cmd_packet(uint8_t *packet, int size);
// query if remote side supports eSCO
int hci_remote_eSCO_supported(hci_con_handle_t con_handle);
/* API_START */ /* API_START */

View File

@ -91,7 +91,7 @@ TEST_GROUP(HFPParser){
TEST(HFPParser, HFP_AG_SUPPORTED_FEATURES){ TEST(HFPParser, HFP_AG_SUPPORTED_FEATURES){
sprintf(packet, "\r\nAT%s=159\r\n", HFP_SUPPORTED_FEATURES); sprintf(packet, "\r\nAT%s=159\r\n", HFP_SUPPORTED_FEATURES);
context.keep_separator = 0; //context.keep_separator = 0;
for (pos = 0; pos < strlen(packet); pos++){ for (pos = 0; pos < strlen(packet); pos++){
hfp_parse(&context, packet[pos], 0); hfp_parse(&context, packet[pos], 0);
} }
@ -167,19 +167,65 @@ TEST(HFPParser, HFP_AG_ENABLE_INDIVIDUAL_INDICATOR_STATUS_UPDATE){
CHECK_EQUAL(context.ag_indicators[pos].enabled, 0); CHECK_EQUAL(context.ag_indicators[pos].enabled, 0);
} }
} }
}
// sprintf(packet, "\r\nAT%s=1,,,1,1,1,\r\n", TEST(HFPParser, HFP_AG_ENABLE_INDIVIDUAL_INDICATOR_STATUS_UPDATE_OPT_VALUES3){
// HFP_UPDATE_ENABLE_STATUS_FOR_INDIVIDUAL_AG_INDICATORS); set_hfp_ag_indicators((hfp_ag_indicator_t *)&hfp_ag_indicators, hfp_ag_indicators_nr);
// for (pos = 0; pos < strlen(packet); pos++){ context.ag_indicators_nr = hfp_ag_indicators_nr;
// hfp_parse(&context, packet[pos], 0); memcpy(context.ag_indicators, hfp_ag_indicators, hfp_ag_indicators_nr * sizeof(hfp_ag_indicator_t));
// }
// CHECK_EQUAL(HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE, context.command); sprintf(packet, "\r\nAT%s=,1,,,,,1\r\n",
HFP_UPDATE_ENABLE_STATUS_FOR_INDIVIDUAL_AG_INDICATORS);
for (pos = 0; pos < strlen(packet); pos++){
hfp_parse(&context, packet[pos], 0);
}
// for (pos = 0; pos < hfp_ag_indicators_nr; pos++){ CHECK_EQUAL(HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE, context.command);
// CHECK_EQUAL(get_hfp_ag_indicators(&context)[pos].enabled, 1);
// CHECK_EQUAL(context.ag_indicators[pos].enabled, 1); for (pos = 0; pos < hfp_ag_indicators_nr; pos++){
// } CHECK_EQUAL(get_hfp_ag_indicators(&context)[pos].index, hfp_ag_indicators[pos].index);
CHECK_EQUAL(get_hfp_ag_indicators(&context)[pos].enabled, hfp_ag_indicators[pos].enabled);
CHECK_EQUAL(context.ag_indicators[pos].index, hfp_ag_indicators[pos].index);
CHECK_EQUAL(context.ag_indicators[pos].enabled, hfp_ag_indicators[pos].enabled);
}
}
TEST(HFPParser, HFP_AG_ENABLE_INDIVIDUAL_INDICATOR_STATUS_UPDATE_OPT_VALUES2){
set_hfp_ag_indicators((hfp_ag_indicator_t *)&hfp_ag_indicators, hfp_ag_indicators_nr);
context.ag_indicators_nr = hfp_ag_indicators_nr;
memcpy(context.ag_indicators, hfp_ag_indicators, hfp_ag_indicators_nr * sizeof(hfp_ag_indicator_t));
sprintf(packet, "\r\nAT%s=1,,,1,1,1,\r\n",
HFP_UPDATE_ENABLE_STATUS_FOR_INDIVIDUAL_AG_INDICATORS);
for (pos = 0; pos < strlen(packet); pos++){
hfp_parse(&context, packet[pos], 0);
}
CHECK_EQUAL(HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE, context.command);
for (pos = 0; pos < hfp_ag_indicators_nr; pos++){
CHECK_EQUAL(get_hfp_ag_indicators(&context)[pos].enabled, 1);
CHECK_EQUAL(context.ag_indicators[pos].enabled, 1);
}
}
TEST(HFPParser, HFP_AG_ENABLE_INDIVIDUAL_INDICATOR_STATUS_UPDATE_OPT_VALUES1){
set_hfp_ag_indicators((hfp_ag_indicator_t *)&hfp_ag_indicators, hfp_ag_indicators_nr);
context.ag_indicators_nr = hfp_ag_indicators_nr;
memcpy(context.ag_indicators, hfp_ag_indicators, hfp_ag_indicators_nr * sizeof(hfp_ag_indicator_t));
sprintf(packet, "\r\nAT%s=1,,,1,1,1,\r\n",
HFP_UPDATE_ENABLE_STATUS_FOR_INDIVIDUAL_AG_INDICATORS);
for (pos = 0; pos < strlen(packet); pos++){
hfp_parse(&context, packet[pos], 0);
}
CHECK_EQUAL(HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE, context.command);
for (pos = 0; pos < hfp_ag_indicators_nr; pos++){
CHECK_EQUAL(get_hfp_ag_indicators(&context)[pos].enabled, 1);
CHECK_EQUAL(context.ag_indicators[pos].enabled, 1);
}
} }
TEST(HFPParser, HFP_AG_HF_QUERY_OPERATOR_SELECTION){ TEST(HFPParser, HFP_AG_HF_QUERY_OPERATOR_SELECTION){

View File

@ -71,10 +71,18 @@ const uint8_t rfcomm_channel_nr = 1;
static bd_addr_t device_addr = {0xD8,0xBb,0x2C,0xDf,0xF1,0x08}; static bd_addr_t device_addr = {0xD8,0xBb,0x2C,0xDf,0xF1,0x08};
static uint8_t codecs[2] = {1,2}; static uint8_t codecs[2] = {1,2};
static uint8_t default_codecs[2] = {1};
static uint16_t indicators[1] = {0x01}; static uint16_t indicators[1] = {0x01};
static uint8_t service_level_connection_established = 0; static uint8_t service_level_connection_established = 0;
static uint8_t codecs_connection_established = 0; static uint8_t codecs_connection_established = 0;
static uint8_t audio_connection_established = 0;
static uint8_t start_ringing = 0;
static uint8_t stop_ringing = 0;
static uint8_t call_termiated = 0;
static int supported_features_with_codec_negotiation = 438; // 0001 1011 0110
static int supported_features_without_codec_negotiation = 310; // 0001 0011 0110
int expected_rfcomm_command(const char * cmd){ int expected_rfcomm_command(const char * cmd){
char * ag_cmd = (char *)get_rfcomm_payload(); char * ag_cmd = (char *)get_rfcomm_payload();
@ -143,28 +151,37 @@ void packet_handler(uint8_t * event, uint16_t event_size){
case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED: case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED:
printf("\n** SLC established **\n\n"); printf("\n** SLC established **\n\n");
service_level_connection_established = 1; service_level_connection_established = 1;
codecs_connection_established = 0;
audio_connection_established = 0;
break; break;
case HFP_SUBEVENT_CODECS_CONNECTION_COMPLETE: case HFP_SUBEVENT_CODECS_CONNECTION_COMPLETE:
printf("\n** CC established **\n\n"); printf("\n** CC established **\n\n");
codecs_connection_established = 1; codecs_connection_established = 1;
audio_connection_established = 0;
break; break;
case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_RELEASED: case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_RELEASED:
printf("\n** SLC released **\n\n");
service_level_connection_established = 0; service_level_connection_established = 0;
break; break;
case HFP_SUBEVENT_AUDIO_CONNECTION_ESTABLISHED:
case HFP_SUBEVENT_COMPLETE: printf("\n** AC established **\n\n");
printf("HFP_SUBEVENT_COMPLETE.\n\n"); audio_connection_established = 1;
break; break;
case HFP_SUBEVENT_AG_INDICATOR_STATUS_CHANGED: case HFP_SUBEVENT_AUDIO_CONNECTION_RELEASED:
printf("AG_INDICATOR_STATUS_CHANGED, AG indicator index: %d, status: %d\n", event[4], event[5]); printf("\n** AC released **\n\n");
audio_connection_established = 0;
break; break;
case HFP_SUBEVENT_NETWORK_OPERATOR_CHANGED: case HFP_SUBEVENT_START_RINGINIG:
printf("NETWORK_OPERATOR_CHANGED, operator mode: %d, format: %d, name: %s\n", event[4], event[5], (char *) &event[6]); printf("\n** Start ringing **\n\n");
start_ringing = 1;
break; break;
case HFP_SUBEVENT_EXTENDED_AUDIO_GATEWAY_ERROR: case HFP_SUBEVENT_STOP_RINGINIG:
if (event[4]) printf("\n** Stop ringing **\n\n");
printf("EXTENDED_AUDIO_GATEWAY_ERROR_REPORT, status : %d\n", event[3]); stop_ringing = 1;
start_ringing = 0;
break;
case HFP_SUBEVENT_CALL_TERMINATED:
call_termiated = 1;
break; break;
default: default:
printf("event not handled %u\n", event[2]); printf("event not handled %u\n", event[2]);
@ -179,14 +196,22 @@ TEST_GROUP(HFPClient){
void setup(void){ void setup(void){
service_level_connection_established = 0; service_level_connection_established = 0;
codecs_connection_established = 0; codecs_connection_established = 0;
audio_connection_established = 0;
start_ringing = 0;
stop_ringing = 0;
call_termiated = 0;
hfp_hf_init(rfcomm_channel_nr, supported_features_with_codec_negotiation, indicators, sizeof(indicators)/sizeof(uint16_t), 1);
hfp_hf_set_codecs(codecs, sizeof(codecs));
} }
void teardown(void){ void teardown(void){
if (service_level_connection_established){ hfp_hf_release_audio_connection(device_addr);
hfp_hf_release_service_level_connection(device_addr); hfp_hf_release_service_level_connection(device_addr);
CHECK_EQUAL(service_level_connection_established, 0);
} service_level_connection_established = 0;
codecs_connection_established = 0; codecs_connection_established = 0;
audio_connection_established = 0;
} }
void setup_hfp_service_level_connection(char ** test_steps, int nr_test_steps){ void setup_hfp_service_level_connection(char ** test_steps, int nr_test_steps){
@ -202,6 +227,20 @@ TEST_GROUP(HFPClient){
}; };
TEST(HFPClient, HFAudioConnectionEstablishedWithoutCodecNegotiation){
hfp_hf_init(rfcomm_channel_nr, supported_features_without_codec_negotiation, indicators, sizeof(indicators)/sizeof(uint16_t), 1);
hfp_hf_set_codecs(default_codecs, 1);
setup_hfp_service_level_connection(hfp_slc_tests()[1].test, hfp_slc_tests()[1].len);
CHECK_EQUAL(service_level_connection_established, 1);
setup_hfp_codecs_connection(default_cc_setup(), default_cc_setup_size());
CHECK_EQUAL(codecs_connection_established, 1);
hfp_hf_establish_audio_connection(device_addr);
hfp_hf_release_audio_connection(device_addr);
CHECK_EQUAL(audio_connection_established, 0);
}
TEST(HFPClient, HFCodecsConnectionEstablished){ TEST(HFPClient, HFCodecsConnectionEstablished){
for (int i = 0; i < cc_tests_size(); i++){ for (int i = 0; i < cc_tests_size(); i++){
@ -231,9 +270,6 @@ TEST(HFPClient, HFServiceLevelConnectionEstablished){
int main (int argc, const char * argv[]){ int main (int argc, const char * argv[]){
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); hfp_hf_register_packet_handler(packet_handler);
return CommandLineTestRunner::RunAllTests(argc, argv); return CommandLineTestRunner::RunAllTests(argc, argv);
} }

View File

@ -79,6 +79,15 @@ static uint16_t handle = -1;
static int memory_1_enabled = 1; static int memory_1_enabled = 1;
static int last_number_exists = 1; static int last_number_exists = 1;
static int current_call_index = 0;
static hfp_enhanced_call_dir_t current_call_dir;
static int current_call_exists_a = 0;
static int current_call_exists_b = 0;
static hfp_enhanced_call_status_t current_call_status_a;
static hfp_enhanced_call_status_t current_call_status_b;
static hfp_enhanced_call_mpty_t current_call_mpty = HFP_ENHANCED_CALL_MPTY_NOT_A_CONFERENCE_CALL;
static int ag_indicators_nr = 7; static int ag_indicators_nr = 7;
static hfp_ag_indicator_t ag_indicators[] = { static hfp_ag_indicator_t ag_indicators[] = {
// index, name, min range, max range, status, mandatory, enabled, status changed // index, name, min range, max range, status, mandatory, enabled, status changed
@ -86,8 +95,8 @@ static hfp_ag_indicator_t ag_indicators[] = {
{2, "call", 0, 1, 0, 1, 1, 0}, {2, "call", 0, 1, 0, 1, 1, 0},
{3, "callsetup", 0, 3, 0, 1, 1, 0}, {3, "callsetup", 0, 3, 0, 1, 1, 0},
{4, "battchg", 0, 5, 3, 0, 0, 0}, {4, "battchg", 0, 5, 3, 0, 0, 0},
{5, "signal", 0, 5, 5, 0, 0, 0}, {5, "signal", 0, 5, 5, 0, 1, 0},
{6, "roam", 0, 1, 0, 0, 0, 0}, {6, "roam", 0, 1, 0, 0, 1, 0},
{7, "callheld", 0, 2, 0, 1, 1, 0} {7, "callheld", 0, 2, 0, 1, 1, 0}
}; };
@ -104,6 +113,168 @@ char cmd;
// prototypes // prototypes
static void show_usage(); static void show_usage();
// GAP INQUIRY
#define MAX_DEVICES 10
enum DEVICE_STATE { REMOTE_NAME_REQUEST, REMOTE_NAME_INQUIRED, REMOTE_NAME_FETCHED };
struct device {
bd_addr_t address;
uint16_t clockOffset;
uint32_t classOfDevice;
uint8_t pageScanRepetitionMode;
uint8_t rssi;
enum DEVICE_STATE state;
};
#define INQUIRY_INTERVAL 5
struct device devices[MAX_DEVICES];
int deviceCount = 0;
enum STATE {INIT, W4_INQUIRY_MODE_COMPLETE, ACTIVE} ;
enum STATE state = INIT;
static int getDeviceIndexForAddress( bd_addr_t addr){
int j;
for (j=0; j< deviceCount; j++){
if (BD_ADDR_CMP(addr, devices[j].address) == 0){
return j;
}
}
return -1;
}
static void start_scan(void){
printf("Starting inquiry scan..\n");
hci_send_cmd(&hci_inquiry, HCI_INQUIRY_LAP, INQUIRY_INTERVAL, 0);
}
static int has_more_remote_name_requests(void){
int i;
for (i=0;i<deviceCount;i++) {
if (devices[i].state == REMOTE_NAME_REQUEST) return 1;
}
return 0;
}
static void do_next_remote_name_request(void){
int i;
for (i=0;i<deviceCount;i++) {
// remote name request
if (devices[i].state == REMOTE_NAME_REQUEST){
devices[i].state = REMOTE_NAME_INQUIRED;
printf("Get remote name of %s...\n", bd_addr_to_str(devices[i].address));
hci_send_cmd(&hci_remote_name_request, devices[i].address,
devices[i].pageScanRepetitionMode, 0, devices[i].clockOffset | 0x8000);
return;
}
}
}
static void continue_remote_names(void){
// don't get remote names for testing
if (has_more_remote_name_requests()){
do_next_remote_name_request();
return;
}
// try to find PTS
int i;
for (i=0;i<deviceCount;i++){
if (memcmp(devices[i].address, device_addr, 6) == 0){
printf("Inquiry scan over, successfully found PTS at index %u\nReady to connect to it.\n", i);
return;
}
}
printf("Inquiry scan over but PTS not found :(\n");
}
static void inquiry_packet_handler (uint8_t packet_type, uint8_t *packet, uint16_t size){
bd_addr_t addr;
int i;
int numResponses;
int index;
// printf("packet_handler: pt: 0x%02x, packet[0]: 0x%02x\n", packet_type, packet[0]);
if (packet_type != HCI_EVENT_PACKET) return;
uint8_t event = packet[0];
switch(event){
case HCI_EVENT_INQUIRY_RESULT:
case HCI_EVENT_INQUIRY_RESULT_WITH_RSSI:{
numResponses = packet[2];
int offset = 3;
for (i=0; i<numResponses && deviceCount < MAX_DEVICES;i++){
bt_flip_addr(addr, &packet[offset]);
offset += 6;
index = getDeviceIndexForAddress(addr);
if (index >= 0) continue; // already in our list
memcpy(devices[deviceCount].address, addr, 6);
devices[deviceCount].pageScanRepetitionMode = packet[offset];
offset += 1;
if (event == HCI_EVENT_INQUIRY_RESULT){
offset += 2; // Reserved + Reserved
devices[deviceCount].classOfDevice = READ_BT_24(packet, offset);
offset += 3;
devices[deviceCount].clockOffset = READ_BT_16(packet, offset) & 0x7fff;
offset += 2;
devices[deviceCount].rssi = 0;
} else {
offset += 1; // Reserved
devices[deviceCount].classOfDevice = READ_BT_24(packet, offset);
offset += 3;
devices[deviceCount].clockOffset = READ_BT_16(packet, offset) & 0x7fff;
offset += 2;
devices[deviceCount].rssi = packet[offset];
offset += 1;
}
devices[deviceCount].state = REMOTE_NAME_REQUEST;
printf("Device #%u found: %s with COD: 0x%06x, pageScan %d, clock offset 0x%04x, rssi 0x%02x\n",
deviceCount, bd_addr_to_str(addr),
devices[deviceCount].classOfDevice, devices[deviceCount].pageScanRepetitionMode,
devices[deviceCount].clockOffset, devices[deviceCount].rssi);
deviceCount++;
}
break;
}
case HCI_EVENT_INQUIRY_COMPLETE:
for (i=0;i<deviceCount;i++) {
// retry remote name request
if (devices[i].state == REMOTE_NAME_INQUIRED)
devices[i].state = REMOTE_NAME_REQUEST;
}
continue_remote_names();
break;
case BTSTACK_EVENT_REMOTE_NAME_CACHED:
bt_flip_addr(addr, &packet[3]);
printf("Cached remote name for %s: '%s'\n", bd_addr_to_str(addr), &packet[9]);
break;
case HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE:
bt_flip_addr(addr, &packet[3]);
index = getDeviceIndexForAddress(addr);
if (index >= 0) {
if (packet[2] == 0) {
printf("Name: '%s'\n", &packet[9]);
devices[index].state = REMOTE_NAME_FETCHED;
} else {
printf("Failed to get name: page timeout\n");
}
}
continue_remote_names();
break;
default:
break;
}
}
// GAP INQUIRY END
// Testig User Interface // Testig User Interface
static void show_usage(void){ static void show_usage(void){
printf("\n--- Bluetooth HFP Hands-Free (HF) unit Test Console ---\n"); printf("\n--- Bluetooth HFP Hands-Free (HF) unit Test Console ---\n");
@ -155,7 +326,22 @@ static void show_usage(void){
printf("n - Disable Voice Regocnition\n"); printf("n - Disable Voice Regocnition\n");
printf("N - Enable Voice Recognition\n"); printf("N - Enable Voice Recognition\n");
printf("o - Set speaker volume to 0 (minimum)\n");
printf("O - Set speaker volume to 9 (default)\n");
printf("p - Set speaker volume to 12 (higher)\n");
printf("P - Set speaker volume to 15 (maximum)\n");
printf("q - Set microphone gain to 0 (minimum)\n");
printf("Q - Set microphone gain to 9 (default)\n");
printf("s - Set microphone gain to 12 (higher)\n");
printf("S - Set microphone gain to 15 (maximum)\n");
printf("t - terminate connection\n"); printf("t - terminate connection\n");
printf("u - join held call\n");
printf("v - discover nearby HF units\n");
printf("w - put incoming call on hold (Response and Hold)\n");
printf("x - accept held incoming call (Response and Hold)\n");
printf("X - reject held incoming call (Response and Hold)\n");
printf("---\n"); printf("---\n");
printf("Ctrl-c - exit\n"); printf("Ctrl-c - exit\n");
@ -193,11 +379,17 @@ static int stdin_process(struct data_source *ds){
break; break;
case 'c': case 'c':
printf("Simulate incoming call from 1234567\n"); printf("Simulate incoming call from 1234567\n");
current_call_exists_a = 1;
current_call_status_a = HFP_ENHANCED_CALL_STATUS_INCOMING;
current_call_dir = HFP_ENHANCED_CALL_DIR_INCOMING;
hfp_ag_set_clip(129, "1234567"); hfp_ag_set_clip(129, "1234567");
hfp_ag_incoming_call(); hfp_ag_incoming_call();
break; break;
case 'm': case 'm':
printf("Simulate incoming call from 7654321\n"); printf("Simulate incoming call from 7654321\n");
current_call_exists_b = 1;
current_call_status_b = HFP_ENHANCED_CALL_STATUS_INCOMING;
current_call_dir = HFP_ENHANCED_CALL_DIR_INCOMING;
hfp_ag_set_clip(129, "7654321"); hfp_ag_set_clip(129, "7654321");
hfp_ag_incoming_call(); hfp_ag_incoming_call();
break; break;
@ -211,6 +403,12 @@ static int stdin_process(struct data_source *ds){
break; break;
case 'e': case 'e':
printf("Answer call on AG\n"); printf("Answer call on AG\n");
if (current_call_status_a == HFP_ENHANCED_CALL_STATUS_INCOMING){
current_call_status_a = HFP_ENHANCED_CALL_STATUS_ACTIVE;
}
if (current_call_status_b == HFP_ENHANCED_CALL_STATUS_INCOMING){
current_call_status_b = HFP_ENHANCED_CALL_STATUS_ACTIVE;
}
hfp_ag_answer_incoming_call(); hfp_ag_answer_incoming_call();
break; break;
case 'E': case 'E':
@ -281,6 +479,38 @@ static int stdin_process(struct data_source *ds){
printf("Enable Voice Recognition\n"); printf("Enable Voice Recognition\n");
hfp_ag_activate_voice_recognition(device_addr, 1); hfp_ag_activate_voice_recognition(device_addr, 1);
break; break;
case 'o':
printf("Set speaker gain to 0 (minimum)\n");
hfp_ag_set_speaker_gain(device_addr, 0);
break;
case 'O':
printf("Set speaker gain to 9 (default)\n");
hfp_ag_set_speaker_gain(device_addr, 9);
break;
case 'p':
printf("Set speaker gain to 12 (higher)\n");
hfp_ag_set_speaker_gain(device_addr, 12);
break;
case 'P':
printf("Set speaker gain to 15 (maximum)\n");
hfp_ag_set_speaker_gain(device_addr, 15);
break;
case 'q':
printf("Set microphone gain to 0\n");
hfp_ag_set_microphone_gain(device_addr, 0);
break;
case 'Q':
printf("Set microphone gain to 9\n");
hfp_ag_set_microphone_gain(device_addr, 9);
break;
case 's':
printf("Set microphone gain to 12\n");
hfp_ag_set_microphone_gain(device_addr, 12);
break;
case 'S':
printf("Set microphone gain to 15\n");
hfp_ag_set_microphone_gain(device_addr, 15);
break;
case 'R': case 'R':
printf("Enable in-band ring tone\n"); printf("Enable in-band ring tone\n");
hfp_ag_set_use_in_band_ring_tone(1); hfp_ag_set_use_in_band_ring_tone(1);
@ -288,6 +518,27 @@ static int stdin_process(struct data_source *ds){
case 't': case 't':
printf("Terminate HCI connection.\n"); printf("Terminate HCI connection.\n");
gap_disconnect(handle); gap_disconnect(handle);
break;
case 'u':
printf("Join held call\n");
current_call_mpty = HFP_ENHANCED_CALL_MPTY_CONFERENCE_CALL;
hfp_ag_join_held_call();
break;
case 'v':
start_scan();
break;
case 'w':
printf("AG: Put incoming call on hold (Response and Hold)\n");
hfp_ag_hold_incoming_call();
break;
case 'x':
printf("AG: Accept held incoming call (Response and Hold)\n");
hfp_ag_accept_held_incoming_call();
break;
case 'X':
printf("AG: Reject held incoming call (Response and Hold)\n");
hfp_ag_reject_held_incoming_call();
break;
default: default:
show_usage(); show_usage();
break; break;
@ -305,8 +556,31 @@ static void packet_handler(uint8_t * event, uint16_t event_size){
return; return;
} }
switch (event[0]){
case RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE:
handle = READ_BT_16(event, 9);
printf("RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE received for handle 0x%04x\n", handle);
return;
case HCI_EVENT_INQUIRY_RESULT:
case HCI_EVENT_INQUIRY_RESULT_WITH_RSSI:
case HCI_EVENT_INQUIRY_COMPLETE:
case HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE:
inquiry_packet_handler(HCI_EVENT_PACKET, event, event_size);
break;
default:
break;
}
if (event[0] != HCI_EVENT_HFP_META) return; if (event[0] != HCI_EVENT_HFP_META) return;
if (event[3] && event[2] != HFP_SUBEVENT_PLACE_CALL_WITH_NUMBER){
if (event[3]
&& event[2] != HFP_SUBEVENT_PLACE_CALL_WITH_NUMBER
&& event[2] != HFP_SUBEVENT_ATTACH_NUMBER_TO_VOICE_TAG
&& event[2] != HFP_SUBEVENT_TRANSMIT_DTMF_CODES
&& event[2] != HFP_SUBEVENT_TRANSMIT_STATUS_OF_CURRENT_CALL){
printf("ERROR, status: %u\n", event[3]); printf("ERROR, status: %u\n", event[3]);
return; return;
} }
@ -357,24 +631,64 @@ static void packet_handler(uint8_t * event, uint16_t event_size){
hfp_ag_outgoing_call_rejected(); hfp_ag_outgoing_call_rejected();
} }
break; break;
case HFP_SUBEVENT_ATTACH_NUMBER_TO_VOICE_TAG:
printf("\n** Attach number to voice tag. Sending '1234567\n");
hfp_ag_send_phone_number_for_voice_tag(device_addr, "1234567");
break;
case HFP_SUBEVENT_TRANSMIT_DTMF_CODES:
printf("\n** Send DTMF Codes: '%s'\n", &event[3]);
hfp_ag_send_dtmf_code_done(device_addr);
break;
case HFP_SUBEVENT_TRANSMIT_STATUS_OF_CURRENT_CALL:
if (current_call_index == 0 && current_call_exists_a){
hfp_ag_send_current_call_status(device_addr, 1, current_call_dir, current_call_status_a,
HFP_ENHANCED_CALL_MODE_VOICE, current_call_mpty, 129, "1234567");
current_call_index = 1;
break;
}
if (current_call_index == 1 && current_call_exists_b){
hfp_ag_send_current_call_status(device_addr, 2, current_call_dir, current_call_status_b,
HFP_ENHANCED_CALL_MODE_VOICE, current_call_mpty, 129, "7654321");
current_call_index = 2;
break;
}
hfp_ag_send_current_call_status_done(device_addr);
break;
case HFP_CMD_CALL_ANSWERED:
printf("Call answered by HF\n");
if (current_call_status_a == HFP_ENHANCED_CALL_STATUS_INCOMING){
current_call_status_a = HFP_ENHANCED_CALL_STATUS_ACTIVE;
}
if (current_call_status_b == HFP_ENHANCED_CALL_STATUS_INCOMING){
current_call_status_b = HFP_ENHANCED_CALL_STATUS_ACTIVE;
}
break;
case HFP_SUBEVENT_CONFERENCE_CALL:
current_call_mpty = HFP_ENHANCED_CALL_MPTY_CONFERENCE_CALL;
current_call_status_a = HFP_ENHANCED_CALL_STATUS_ACTIVE;
current_call_status_b = HFP_ENHANCED_CALL_STATUS_ACTIVE;
break;
default: default:
// printf("event not handled %u\n", event[2]); printf("Event not handled %u\n", event[2]);
break; break;
} }
} }
static hfp_phone_number_t subscriber_number = {
129, "225577"
};
int btstack_main(int argc, const char * argv[]); int btstack_main(int argc, const char * argv[]);
int btstack_main(int argc, const char * argv[]){ int btstack_main(int argc, const char * argv[]){
// init L2CAP // init L2CAP
l2cap_init(); l2cap_init();
rfcomm_init(); rfcomm_init();
hfp_ag_init(rfcomm_channel_nr, 0x3ef | (1<<HFP_AGSF_HF_INDICATORS), codecs, sizeof(codecs), hfp_ag_init(rfcomm_channel_nr, 0x3ef | (1<<HFP_AGSF_HF_INDICATORS) | (1<<HFP_AGSF_ESCO_S4), codecs, sizeof(codecs),
ag_indicators, ag_indicators_nr, ag_indicators, ag_indicators_nr,
hf_indicators, hf_indicators_nr, hf_indicators, hf_indicators_nr,
call_hold_services, call_hold_services_nr); call_hold_services, call_hold_services_nr);
hfp_ag_set_subcriber_number_information(&subscriber_number, 1);
hfp_ag_register_packet_handler(packet_handler); hfp_ag_register_packet_handler(packet_handler);
// init SDP, create record for SPP and register with SDP // init SDP, create record for SPP and register with SDP

View File

@ -1,3 +1,4 @@
/* /*
* Copyright (C) 2014 BlueKitchen GmbH * Copyright (C) 2014 BlueKitchen GmbH
* *
@ -76,6 +77,7 @@ static bd_addr_t pts_addr = {0x00,0x1b,0xDC,0x07,0x32,0xEF};
static bd_addr_t phone_addr = {0xD8,0xBb,0x2C,0xDf,0xF1,0x08}; static bd_addr_t phone_addr = {0xD8,0xBb,0x2C,0xDf,0xF1,0x08};
static bd_addr_t device_addr; static bd_addr_t device_addr;
static uint16_t handle = -1;
static uint8_t codecs[] = {HFP_CODEC_CVSD, HFP_CODEC_MSBC}; static uint8_t codecs[] = {HFP_CODEC_CVSD, HFP_CODEC_MSBC};
static uint16_t indicators[1] = {0x01}; static uint16_t indicators[1] = {0x01};
@ -88,28 +90,70 @@ static void show_usage();
static void show_usage(void){ static void show_usage(void){
printf("\n--- Bluetooth HFP Hands-Free (HF) unit Test Console ---\n"); printf("\n--- Bluetooth HFP Hands-Free (HF) unit Test Console ---\n");
printf("---\n"); printf("---\n");
printf("y - use PTS module as Audiogateway\n");
printf("z - use iPhone as Audiogateway\n"); printf("z - use iPhone as Audiogateway\n");
printf("h - establish HFP connection to device\n"); printf("a - establish SLC connection to device\n");
printf("H - release HFP connection to device\n"); printf("A - release SLC connection to device\n");
printf("a - establish Audio connection to device\n"); printf("b - establish Audio connection\n");
printf("A - release Audio connection to device\n"); printf("B - release Audio connection\n");
printf("b - establish AUDIO connection\n"); printf("c - disable registration status update for all AG indicators\n");
printf("B - release AUDIO connection\n"); printf("C - enable registration status update for all AG indicators\n");
printf("d - enable registration status update\n"); printf("d - query network operator.\n");
printf("D - disable registration status update\n"); printf("D - set HFP AG registration status update for individual indicators\n");
printf("e - disable reporting of the extended AG error result code\n");
printf("E - enable reporting of the extended AG error result code\n");
printf("e - enable HFP AG registration status update for individual indicators\n"); printf("f - answer incoming call\n");
printf("F - Hangup call\n");
printf("g - query network operator name\n");
printf("G - reject call\n");
printf("i - dial 1234567\n");
printf("I - dial 7654321\n");
printf("f - query network operator\n"); printf("j - dial #1\n");
printf("J - dial #99\n");
printf("g - enable reporting of the extended AG error result code\n"); printf("k - deactivate call waiting notification\n");
printf("G - disable reporting of the extended AG error result code\n"); printf("K - activate call waiting notification\n");
printf("l - deactivate calling line notification\n");
printf("L - activate calling line notification\n");
printf("m - deactivate echo canceling and noise reduction\n");
printf("M - activate echo canceling and noise reduction\n");
printf("n - deactivate voice recognition\n");
printf("N - activate voice recognition\n");
printf("o - Set speaker volume to 0 (minimum)\n");
printf("O - Set speaker volume to 9 (default)\n");
printf("p - Set speaker volume to 12 (higher)\n");
printf("P - Set speaker volume to 15 (maximum)\n");
printf("q - Set microphone gain to 0 (minimum)\n");
printf("Q - Set microphone gain to 9 (default)\n");
printf("s - Set microphone gain to 12 (higher)\n");
printf("S - Set microphone gain to 15 (maximum)\n");
printf("t - terminate connection\n");
printf("u - send 'user busy' (Three-Way Call 0)\n");
printf("U - end active call and accept other call' (Three-Way Call 1)\n");
printf("v - Swap active call and hold/waiting call (Three-Way Call 2)\n");
printf("V - Join held call (Three-Way Call 3)\n");
printf("w - Connect calls (Three-Way Call 4)\n");
printf("W - redial\n");
printf("0123456789#*-+ - send DTMF dial tones\n");
printf("x - request phone number for voice tag\n");
printf("---\n"); printf("---\n");
printf("Ctrl-c - exit\n"); printf("Ctrl-c - exit\n");
printf("---\n"); printf("---\n");
@ -117,64 +161,184 @@ static void show_usage(void){
static int stdin_process(struct data_source *ds){ static int stdin_process(struct data_source *ds){
read(ds->fd, &cmd, 1); read(ds->fd, &cmd, 1);
if (cmd >= '0' && cmd <= '9'){
printf("DTMF Code: %c\n", cmd);
hfp_hf_send_dtmf_code(device_addr, cmd);
return 0;
}
switch (cmd){ switch (cmd){
case '#':
case '-':
case '+':
case '*':
printf("DTMF Code: %c\n", cmd);
hfp_hf_send_dtmf_code(device_addr, cmd);
break;
case 'a': case 'a':
printf("Establish Audio connection to device with Bluetooth address %s...\n", bd_addr_to_str(device_addr)); printf("Establish Service level connection to device with Bluetooth address %s...\n", bd_addr_to_str(device_addr));
hfp_hf_establish_audio_connection(device_addr);
break;
case 'A':
printf("Release Audio service level connection.\n");
hfp_hf_release_audio_connection(device_addr);
break;
case 'h':
printf("Establish HFP service level connection to device with Bluetooth address %s...\n", bd_addr_to_str(device_addr));
hfp_hf_establish_service_level_connection(device_addr); hfp_hf_establish_service_level_connection(device_addr);
break; break;
case 'H': case 'A':
printf("Release HFP service level connection.\n"); printf("Release Service level connection.\n");
hfp_hf_release_service_level_connection(device_addr); hfp_hf_release_service_level_connection(device_addr);
break; break;
case 'b': case 'b':
printf("Establish Audio connection %s...\n", bd_addr_to_str(device_addr)); printf("Establish Audio connection to device with Bluetooth address %s...\n", bd_addr_to_str(device_addr));
hfp_hf_establish_audio_connection(device_addr); hfp_hf_establish_audio_connection(device_addr);
break; break;
case 'B': case 'B':
printf("Release Audio connection.\n"); printf("Release Audio service level connection.\n");
hfp_hf_release_audio_connection(device_addr); hfp_hf_release_audio_connection(device_addr);
break; break;
case 'd': case 'C':
printf("Enable HFP AG registration status update.\n"); printf("Enable registration status update for all AG indicators.\n");
hfp_hf_enable_status_update_for_all_ag_indicators(device_addr, 1); hfp_hf_enable_status_update_for_all_ag_indicators(device_addr);
case 'c':
printf("Disable registration status update for all AG indicators.\n");
hfp_hf_disable_status_update_for_all_ag_indicators(device_addr);
break;
case 'D': case 'D':
printf("Disable HFP AG registration status update.\n"); printf("Set HFP AG registration status update for individual indicators (0111111).\n");
hfp_hf_enable_status_update_for_all_ag_indicators(device_addr, 0); hfp_hf_set_status_update_for_individual_ag_indicators(device_addr, 63);
break; break;
case 'e': case 'd':
printf("Enable HFP AG registration status update for individual indicators.\n");
hfp_hf_enable_status_update_for_individual_ag_indicators(device_addr, 63);
break;
case 'f':
printf("Query network operator.\n"); printf("Query network operator.\n");
hfp_hf_query_operator_selection(device_addr); hfp_hf_query_operator_selection(device_addr);
break; break;
case 'g': case 'E':
printf("Enable reporting of the extended AG error result code.\n"); printf("Enable reporting of the extended AG error result code.\n");
hfp_hf_enable_report_extended_audio_gateway_error_result_code(device_addr, 1); hfp_hf_enable_report_extended_audio_gateway_error_result_code(device_addr);
break;
case 'e':
printf("Disable reporting of the extended AG error result code.\n");
hfp_hf_disable_report_extended_audio_gateway_error_result_code(device_addr);
break;
case 'f':
printf("Answer incoming call.\n");
hfp_hf_answer_incoming_call(device_addr);
break;
case 'F':
printf("Hangup call.\n");
hfp_hf_terminate_call(device_addr);
break; break;
case 'G': case 'G':
printf("Disable reporting of the extended AG error result code.\n"); printf("Reject call.\n");
hfp_hf_enable_report_extended_audio_gateway_error_result_code(device_addr, 0); hfp_hf_reject_call(device_addr);
break;
case 'g':
printf("Query operator.\n");
hfp_hf_query_operator_selection(device_addr);
break;
case 't':
printf("Terminate HCI connection.\n");
gap_disconnect(handle);
break; break;
case 'y': case 'y':
memcpy(device_addr, phone_addr, 6); memcpy(device_addr, phone_addr, 6);
printf("Use iPhone %s as Audiogateway.\n", bd_addr_to_str(device_addr)); printf("Use iPhone %s as Audiogateway.\n", bd_addr_to_str(device_addr));
break; break;
case 'z': case 'i':
memcpy(device_addr, pts_addr, 6); printf("Dial 1234567\n");
printf("Use PTS module %s as Audiogateway.\n", bd_addr_to_str(device_addr)); hfp_hf_dial_number(device_addr, "1234567");
break;
case 'I':
printf("Dial 7654321\n");
hfp_hf_dial_number(device_addr, "7654321");
break;
case 'j':
printf("Dial #1\n");
hfp_hf_dial_memory(device_addr,"1");
break;
case 'J':
printf("Dial #99\n");
hfp_hf_dial_memory(device_addr,"99");
break;
case 'k':
printf("Deactivate call waiting notification\n");
hfp_hf_deactivate_call_waiting_notification(device_addr);
break;
case 'K':
printf("Activate call waiting notification\n");
hfp_hf_activate_call_waiting_notification(device_addr);
break;
case 'l':
printf("Deactivate calling line notification\n");
hfp_hf_deactivate_calling_line_notification(device_addr);
break;
case 'L':
printf("Activate calling line notification\n");
hfp_hf_activate_calling_line_notification(device_addr);
break;
case 'm':
printf("Deactivate echo canceling and noise reduction\n");
hfp_hf_deactivate_echo_canceling_and_noise_reduction(device_addr);
break;
case 'M':
printf("Activate echo canceling and noise reduction\n");
hfp_hf_activate_echo_canceling_and_noise_reduction(device_addr);
break;
case 'n':
printf("Deactivate voice recognition\n");
hfp_hf_deactivate_voice_recognition_notification(device_addr);
break;
case 'N':
printf("Activate voice recognition\n");
hfp_hf_activate_voice_recognition_notification(device_addr);
break;
case 'o':
printf("Set speaker gain to 0 (minimum)\n");
hfp_hf_set_speaker_gain(device_addr, 0);
break;
case 'O':
printf("Set speaker gain to 9 (default)\n");
hfp_hf_set_speaker_gain(device_addr, 9);
break;
case 'p':
printf("Set speaker gain to 12 (higher)\n");
hfp_hf_set_speaker_gain(device_addr, 12);
break;
case 'P':
printf("Set speaker gain to 15 (maximum)\n");
hfp_hf_set_speaker_gain(device_addr, 15);
break;
case 'q':
printf("Set microphone gain to 0\n");
hfp_hf_set_microphone_gain(device_addr, 0);
break;
case 'Q':
printf("Set microphone gain to 9\n");
hfp_hf_set_microphone_gain(device_addr, 9);
break;
case 'u':
printf("Send 'user busy' (Three-Way Call 0)\n");
hfp_hf_user_busy(device_addr);
break;
case 'U':
printf("End active call and accept waiting/held call (Three-Way Call 1)\n");
hfp_hf_end_active_and_accept_other(device_addr);
break;
case 'v':
printf("Swap active call and hold/waiting call (Three-Way Call 2)\n");
hfp_hf_swap_calls(device_addr);
break;
case 'V':
printf("Join hold call (Three-Way Call 3)\n");
hfp_hf_join_held_call(device_addr);
break;
case 'w':
printf("Connect calls (Three-Way Call 4)\n");
hfp_hf_connect_calls(device_addr);
break;
case 'W':
printf("Redial\n");
hfp_hf_redial_last_number(device_addr);
break;
case 'x':
printf("Request phone number for voice tag\n");
hfp_hf_request_phone_number_for_voice_tag(device_addr);
break; break;
default: default:
show_usage(); show_usage();
break; break;
@ -185,8 +349,17 @@ static int stdin_process(struct data_source *ds){
static void packet_handler(uint8_t * event, uint16_t event_size){ static void packet_handler(uint8_t * event, uint16_t event_size){
if (event[0] == RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE){
handle = READ_BT_16(event, 9);
printf("RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE received for handle 0x%04x\n", handle);
return;
}
if (event[0] != HCI_EVENT_HFP_META) return; if (event[0] != HCI_EVENT_HFP_META) return;
if (event[3] && event[2] != HFP_SUBEVENT_EXTENDED_AUDIO_GATEWAY_ERROR){ if (event[3]
&& event[2] != HFP_SUBEVENT_EXTENDED_AUDIO_GATEWAY_ERROR
&& event[2] != HFP_SUBEVENT_NUMBER_FOR_VOICE_TAG
&& event[2] != HFP_SUBEVENT_SPEAKER_VOLUME
&& event[2] != HFP_SUBEVENT_MICROPHONE_VOLUME){
printf("ERROR, status: %u\n", event[3]); printf("ERROR, status: %u\n", event[3]);
return; return;
} }
@ -197,6 +370,12 @@ static void packet_handler(uint8_t * event, uint16_t event_size){
case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_RELEASED: case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_RELEASED:
printf("Service level connection released.\n\n"); printf("Service level connection released.\n\n");
break; break;
case HFP_SUBEVENT_AUDIO_CONNECTION_ESTABLISHED:
printf("\n** Audio connection established **\n");
break;
case HFP_SUBEVENT_AUDIO_CONNECTION_RELEASED:
printf("\n** Audio connection released **\n");
break;
case HFP_SUBEVENT_COMPLETE: case HFP_SUBEVENT_COMPLETE:
switch (cmd){ switch (cmd){
case 'd': case 'd':
@ -209,7 +388,7 @@ static void packet_handler(uint8_t * event, uint16_t event_size){
} }
break; break;
case HFP_SUBEVENT_AG_INDICATOR_STATUS_CHANGED: case HFP_SUBEVENT_AG_INDICATOR_STATUS_CHANGED:
printf("AG_INDICATOR_STATUS_CHANGED, AG indicator index: %d, status: %d\n", event[4], event[5]); printf("AG_INDICATOR_STATUS_CHANGED, AG indicator '%s' (index: %d) to: %d\n", (const char*) &event[6], event[4], event[5]);
break; break;
case HFP_SUBEVENT_NETWORK_OPERATOR_CHANGED: case HFP_SUBEVENT_NETWORK_OPERATOR_CHANGED:
printf("NETWORK_OPERATOR_CHANGED, operator mode: %d, format: %d, name: %s\n", event[4], event[5], (char *) &event[6]); printf("NETWORK_OPERATOR_CHANGED, operator mode: %d, format: %d, name: %s\n", event[4], event[5], (char *) &event[6]);
@ -218,6 +397,18 @@ static void packet_handler(uint8_t * event, uint16_t event_size){
if (event[4]) if (event[4])
printf("EXTENDED_AUDIO_GATEWAY_ERROR_REPORT, status : %d\n", event[3]); printf("EXTENDED_AUDIO_GATEWAY_ERROR_REPORT, status : %d\n", event[3]);
break; break;
case HFP_SUBEVENT_RING:
printf("** Ring **\n");
break;
case HFP_SUBEVENT_NUMBER_FOR_VOICE_TAG:
printf("Phone number for voice tag: %s\n", (const char *) &event[3]);
break;
case HFP_SUBEVENT_SPEAKER_VOLUME:
printf("Speaker volume: %u\n", event[3]);
break;
case HFP_SUBEVENT_MICROPHONE_VOLUME:
printf("Microphone volume: %u\n", event[3]);
break;
default: default:
printf("event not handled %u\n", event[2]); printf("event not handled %u\n", event[2]);
break; break;
@ -231,7 +422,7 @@ int btstack_main(int argc, const char * argv[]){
rfcomm_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, HFP_DEFAULT_HF_SUPPORTED_FEATURES, 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_init(rfcomm_channel_nr, 438 | (1<<HFP_HFSF_ESCO_S4) | (1<<HFP_HFSF_EC_NR_FUNCTION), indicators, sizeof(indicators)/sizeof(uint16_t), 1);
hfp_hf_set_codecs(codecs, sizeof(codecs)); hfp_hf_set_codecs(codecs, sizeof(codecs));
hfp_hf_register_packet_handler(packet_handler); hfp_hf_register_packet_handler(packet_handler);
@ -242,6 +433,9 @@ int btstack_main(int argc, const char * argv[]){
hfp_hf_create_sdp_record((uint8_t *)hfp_service_buffer, 0x10006, rfcomm_channel_nr, hfp_hf_service_name, 0); hfp_hf_create_sdp_record((uint8_t *)hfp_service_buffer, 0x10006, rfcomm_channel_nr, hfp_hf_service_name, 0);
sdp_register_service((uint8_t *)hfp_service_buffer); sdp_register_service((uint8_t *)hfp_service_buffer);
// pre-select pts
memcpy(device_addr, pts_addr, 6);
// turn on! // turn on!
hci_power_control(HCI_POWER_ON); hci_power_control(HCI_POWER_ON);