Merge branch 'master' into ble-api-cleanup

This commit is contained in:
Matthias Ringwald 2016-03-03 14:26:11 +01:00
commit a0ffb263e0
19 changed files with 2099 additions and 1649 deletions

View File

@ -142,10 +142,16 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packe
/* @text In INIT, an inquiry scan is started, and the application transits to
* ACTIVE state.
*/
case INIT:
if (packet[2] == HCI_STATE_WORKING) {
start_scan();
state = ACTIVE;
case INIT:
switch(event){
case BTSTACK_EVENT_STATE:
if (packet[2] == HCI_STATE_WORKING){
start_scan();
state = ACTIVE;
}
break;
default:
break;
}
break;

View File

@ -128,9 +128,15 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
switch(state){
case INIT:
if (packet[2] == HCI_STATE_WORKING) {
hci_send_cmd(&hci_write_inquiry_mode, 0x01); // with RSSI
state = W4_INQUIRY_MODE_COMPLETE;
switch(event){
case BTSTACK_EVENT_STATE:
if (packet[2] == HCI_STATE_WORKING){
hci_send_cmd(&hci_write_inquiry_mode, 0x01); // with RSSI
state = W4_INQUIRY_MODE_COMPLETE;
}
break;
default:
break;
}
break;

View File

@ -110,18 +110,6 @@ void hfp_set_callback(hfp_callback_t callback){
hfp_callback = callback;
}
hfp_generic_status_indicator_t * get_hfp_generic_status_indicators(void){
return (hfp_generic_status_indicator_t *) &hfp_generic_status_indicators;
}
int get_hfp_generic_status_indicators_nr(void){
return hfp_generic_status_indicators_nr;
}
void set_hfp_generic_status_indicators(hfp_generic_status_indicator_t * indicators, int indicator_nr){
if (indicator_nr > HFP_MAX_NUM_HF_INDICATORS) return;
hfp_generic_status_indicators_nr = indicator_nr;
memcpy(hfp_generic_status_indicators, indicators, indicator_nr * sizeof(hfp_generic_status_indicator_t));
}
const char * hfp_hf_feature(int index){
if (index > HFP_HF_FEATURES_SIZE){
return hfp_hf_features[HFP_HF_FEATURES_SIZE];
@ -147,17 +135,17 @@ int send_str_over_rfcomm(uint16_t cid, char * command){
}
#if 0
void hfp_set_codec(hfp_connection_t * context, uint8_t *packet, uint16_t size){
void hfp_set_codec(hfp_connection_t * hfp_connection, uint8_t *packet, uint16_t size){
// parse available codecs
int pos = 0;
int i;
for (i=0; i<size; i++){
pos+=8;
if (packet[pos] > context->negotiated_codec){
context->negotiated_codec = packet[pos];
if (packet[pos] > hfp_connection->negotiated_codec){
hfp_connection->negotiated_codec = packet[pos];
}
}
printf("Negotiated Codec 0x%02x\n", context->negotiated_codec);
printf("Negotiated Codec 0x%02x\n", hfp_connection->negotiated_codec);
}
#endif
@ -203,6 +191,15 @@ int join_bitmap(char * buffer, int buffer_size, uint32_t values, int values_nr){
return offset;
}
void hfp_emit_simple_event(hfp_callback_t callback, uint8_t event_subtype){
if (!callback) return;
uint8_t event[3];
event[0] = HCI_EVENT_HFP_META;
event[1] = sizeof(event) - 2;
event[2] = event_subtype;
(*callback)(event, sizeof(event));
}
void hfp_emit_event(hfp_callback_t callback, uint8_t event_subtype, uint8_t value){
if (!callback) return;
uint8_t event[4];
@ -244,9 +241,9 @@ hfp_connection_t * get_hfp_connection_context_for_rfcomm_cid(uint16_t cid){
btstack_linked_list_iterator_t it;
btstack_linked_list_iterator_init(&it, hfp_get_connections());
while (btstack_linked_list_iterator_has_next(&it)){
hfp_connection_t * connection = (hfp_connection_t *)btstack_linked_list_iterator_next(&it);
if (connection->rfcomm_cid == cid){
return connection;
hfp_connection_t * hfp_connection = (hfp_connection_t *)btstack_linked_list_iterator_next(&it);
if (hfp_connection->rfcomm_cid == cid){
return hfp_connection;
}
}
return NULL;
@ -256,9 +253,9 @@ hfp_connection_t * get_hfp_connection_context_for_bd_addr(bd_addr_t bd_addr){
btstack_linked_list_iterator_t it;
btstack_linked_list_iterator_init(&it, hfp_get_connections());
while (btstack_linked_list_iterator_has_next(&it)){
hfp_connection_t * connection = (hfp_connection_t *)btstack_linked_list_iterator_next(&it);
if (memcmp(connection->remote_addr, bd_addr, 6) == 0) {
return connection;
hfp_connection_t * hfp_connection = (hfp_connection_t *)btstack_linked_list_iterator_next(&it);
if (memcmp(hfp_connection->remote_addr, bd_addr, 6) == 0) {
return hfp_connection;
}
}
return NULL;
@ -268,69 +265,68 @@ hfp_connection_t * get_hfp_connection_context_for_sco_handle(uint16_t handle){
btstack_linked_list_iterator_t it;
btstack_linked_list_iterator_init(&it, hfp_get_connections());
while (btstack_linked_list_iterator_has_next(&it)){
hfp_connection_t * connection = (hfp_connection_t *)btstack_linked_list_iterator_next(&it);
if (connection->sco_handle == handle){
return connection;
hfp_connection_t * hfp_connection = (hfp_connection_t *)btstack_linked_list_iterator_next(&it);
if (hfp_connection->sco_handle == handle){
return hfp_connection;
}
}
return NULL;
}
void hfp_reset_context_flags(hfp_connection_t * context){
if (!context) return;
context->ok_pending = 0;
context->send_error = 0;
void hfp_reset_context_flags(hfp_connection_t * hfp_connection){
if (!hfp_connection) return;
hfp_connection->ok_pending = 0;
hfp_connection->send_error = 0;
context->keep_byte = 0;
hfp_connection->keep_byte = 0;
context->change_status_update_for_individual_ag_indicators = 0;
context->operator_name_changed = 0;
hfp_connection->change_status_update_for_individual_ag_indicators = 0;
hfp_connection->operator_name_changed = 0;
context->enable_extended_audio_gateway_error_report = 0;
context->extended_audio_gateway_error = 0;
hfp_connection->enable_extended_audio_gateway_error_report = 0;
hfp_connection->extended_audio_gateway_error = 0;
// establish codecs connection
context->suggested_codec = 0;
context->negotiated_codec = 0;
context->codec_confirmed = 0;
// establish codecs hfp_connection
hfp_connection->suggested_codec = 0;
hfp_connection->negotiated_codec = 0;
hfp_connection->codec_confirmed = 0;
context->establish_audio_connection = 0;
hfp_connection->establish_audio_connection = 0;
hfp_connection->call_waiting_notification_enabled = 0;
hfp_connection->command = HFP_CMD_NONE;
hfp_connection->enable_status_update_for_ag_indicators = 0xFF;
}
static hfp_connection_t * create_hfp_connection_context(){
hfp_connection_t * context = btstack_memory_hfp_connection_get();
if (!context) return NULL;
hfp_connection_t * hfp_connection = btstack_memory_hfp_connection_get();
if (!hfp_connection) return NULL;
// init state
memset(context,0, sizeof(hfp_connection_t));
memset(hfp_connection,0, sizeof(hfp_connection_t));
context->state = HFP_IDLE;
context->call_state = HFP_CALL_IDLE;
context->codecs_state = HFP_CODECS_IDLE;
hfp_connection->state = HFP_IDLE;
hfp_connection->call_state = HFP_CALL_IDLE;
hfp_connection->codecs_state = HFP_CODECS_IDLE;
context->parser_state = HFP_PARSER_CMD_HEADER;
context->command = HFP_CMD_NONE;
context->negotiated_codec = 0;
hfp_connection->parser_state = HFP_PARSER_CMD_HEADER;
hfp_connection->command = HFP_CMD_NONE;
context->enable_status_update_for_ag_indicators = 0xFF;
hfp_reset_context_flags(hfp_connection);
context->generic_status_indicators_nr = hfp_generic_status_indicators_nr;
memcpy(context->generic_status_indicators, hfp_generic_status_indicators, hfp_generic_status_indicators_nr * sizeof(hfp_generic_status_indicator_t));
btstack_linked_list_add(&hfp_connections, (btstack_linked_item_t*)context);
return context;
btstack_linked_list_add(&hfp_connections, (linked_item_t*)hfp_connection);
return hfp_connection;
}
static void remove_hfp_connection_context(hfp_connection_t * context){
btstack_linked_list_remove(&hfp_connections, (btstack_linked_item_t*)context);
static void remove_hfp_connection_context(hfp_connection_t * hfp_connection){
btstack_linked_list_remove(&hfp_connections, (btstack_linked_item_t*) hfp_connection);
}
static hfp_connection_t * provide_hfp_connection_context_for_bd_addr(bd_addr_t bd_addr){
hfp_connection_t * context = get_hfp_connection_context_for_bd_addr(bd_addr);
if (context) return context;
context = create_hfp_connection_context();
printf("created context for address %s\n", bd_addr_to_str(bd_addr));
memcpy(context->remote_addr, bd_addr, 6);
return context;
hfp_connection_t * hfp_connection = get_hfp_connection_context_for_bd_addr(bd_addr);
if (hfp_connection) return hfp_connection;
hfp_connection = create_hfp_connection_context();
printf("created hfp_connection for address %s\n", bd_addr_to_str(bd_addr));
memcpy(hfp_connection->remote_addr, bd_addr, 6);
return hfp_connection;
}
/* @param network.
@ -426,24 +422,24 @@ static void handle_hci_event(uint8_t packet_type, uint16_t channel, uint8_t *pac
}
static void handle_query_rfcomm_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
hfp_connection_t * connection = connection_doing_sdp_query;
hfp_connection_t * hfp_connection = connection_doing_sdp_query;
if ( connection->state != HFP_W4_SDP_EVENT_QUERY_COMPLETE) return;
if ( hfp_connection->state != HFP_W4_SDP_EVENT_QUERY_COMPLETE) return;
switch (hci_event_packet_get_type(packet)){
case SDP_EVENT_QUERY_RFCOMM_SERVICE:
if (!connection) {
if (!hfp_connection) {
log_error("handle_query_rfcomm_event alloc connection for RFCOMM port %u failed", sdp_event_query_rfcomm_service_get_rfcomm_channel(packet));
return;
}
connection->rfcomm_channel_nr = sdp_event_query_rfcomm_service_get_rfcomm_channel(packet);
hfp_connection->rfcomm_channel_nr = sdp_event_query_rfcomm_service_get_rfcomm_channel(packet);
break;
case SDP_EVENT_QUERY_COMPLETE:
connection_doing_sdp_query = NULL;
if (connection->rfcomm_channel_nr > 0){
connection->state = HFP_W4_RFCOMM_CONNECTED;
log_info("HFP: SDP_EVENT_QUERY_COMPLETE context %p, addr %s, state %d", connection, bd_addr_to_str( connection->remote_addr), connection->state);
rfcomm_create_channel(handle_hci_event, connection->remote_addr, connection->rfcomm_channel_nr, NULL);
if (hfp_connection->rfcomm_channel_nr > 0){
hfp_connection->state = HFP_W4_RFCOMM_CONNECTED;
log_info("HFP: SDP_EVENT_QUERY_COMPLETE context %p, addr %s, state %d", hfp_connection, bd_addr_to_str( hfp_connection->remote_addr), hfp_connection->state);
rfcomm_create_channel(handle_hci_event, hfp_connection->remote_addr, hfp_connection->rfcomm_channel_nr, NULL);
break;
}
log_info("rfcomm service not found, status %u.", sdp_event_query_complete_get_status(packet));
@ -456,7 +452,7 @@ static void handle_query_rfcomm_event(uint8_t packet_type, uint16_t channel, uin
void hfp_handle_hci_event(uint8_t packet_type, uint8_t *packet, uint16_t size){
bd_addr_t event_addr;
uint16_t rfcomm_cid, handle;
hfp_connection_t * context = NULL;
hfp_connection_t * hfp_connection = NULL;
// printf("AG packet_handler type %u, event type %x, size %u\n", packet_type, hci_event_packet_get_type(packet), size);
@ -744,10 +740,12 @@ static hfp_command_t parse_command(const char * line_buffer, int isHandsFree){
}
if (strncmp(line_buffer+offset, HFP_ENABLE_CLIP, strlen(HFP_ENABLE_CLIP)) == 0){
if (isHandsFree) return HFP_CMD_AG_SENT_CLIP_INFORMATION;
return HFP_CMD_ENABLE_CLIP;
}
if (strncmp(line_buffer+offset, HFP_ENABLE_CALL_WAITING_NOTIFICATION, strlen(HFP_ENABLE_CALL_WAITING_NOTIFICATION)) == 0){
if (isHandsFree) return HFP_CMD_AG_SENT_CALL_WAITING_NOTIFICATION_UPDATE;
return HFP_CMD_ENABLE_CALL_WAITING_NOTIFICATION;
}
@ -831,14 +829,14 @@ static hfp_command_t parse_command(const char * line_buffer, int isHandsFree){
return HFP_CMD_NONE;
}
static void hfp_parser_store_byte(hfp_connection_t * context, uint8_t byte){
static void hfp_parser_store_byte(hfp_connection_t * hfp_connection, uint8_t byte){
// printf("hfp_parser_store_byte %c at pos %u\n", (char) byte, context->line_size);
// TODO: add limit
context->line_buffer[context->line_size++] = byte;
context->line_buffer[context->line_size] = 0;
hfp_connection->line_buffer[hfp_connection->line_size++] = byte;
hfp_connection->line_buffer[hfp_connection->line_size] = 0;
}
static int hfp_parser_is_buffer_empty(hfp_connection_t * context){
return context->line_size == 0;
static int hfp_parser_is_buffer_empty(hfp_connection_t * hfp_connection){
return hfp_connection->line_size == 0;
}
static int hfp_parser_is_end_of_line(uint8_t byte){
@ -849,8 +847,8 @@ static int hfp_parser_is_end_of_header(uint8_t byte){
return hfp_parser_is_end_of_line(byte) || byte == ':' || byte == '?';
}
static int hfp_parser_found_separator(hfp_connection_t * context, uint8_t byte){
if (context->keep_byte == 1) return 1;
static int hfp_parser_found_separator(hfp_connection_t * hfp_connection, uint8_t byte){
if (hfp_connection->keep_byte == 1) return 1;
int found_separator = byte == ',' || byte == '\n'|| byte == '\r'||
byte == ')' || byte == '(' || byte == ':' ||
@ -858,129 +856,131 @@ static int hfp_parser_found_separator(hfp_connection_t * context, uint8_t byte){
return found_separator;
}
static void hfp_parser_next_state(hfp_connection_t * context, uint8_t byte){
context->line_size = 0;
static void hfp_parser_next_state(hfp_connection_t * hfp_connection, uint8_t byte){
hfp_connection->line_size = 0;
if (hfp_parser_is_end_of_line(byte)){
context->parser_item_index = 0;
context->parser_state = HFP_PARSER_CMD_HEADER;
hfp_connection->parser_item_index = 0;
hfp_connection->parser_state = HFP_PARSER_CMD_HEADER;
return;
}
switch (context->parser_state){
switch (hfp_connection->parser_state){
case HFP_PARSER_CMD_HEADER:
context->parser_state = HFP_PARSER_CMD_SEQUENCE;
if (context->keep_byte == 1){
hfp_parser_store_byte(context, byte);
context->keep_byte = 0;
hfp_connection->parser_state = HFP_PARSER_CMD_SEQUENCE;
if (hfp_connection->keep_byte == 1){
hfp_parser_store_byte(hfp_connection, byte);
hfp_connection->keep_byte = 0;
}
break;
case HFP_PARSER_CMD_SEQUENCE:
switch (context->command){
switch (hfp_connection->command){
case HFP_CMD_AG_SENT_PHONE_NUMBER:
case HFP_CMD_AG_SENT_CALL_WAITING_NOTIFICATION_UPDATE:
case HFP_CMD_AG_SENT_CLIP_INFORMATION:
case HFP_CMD_TRANSFER_AG_INDICATOR_STATUS:
case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME:
case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT:
case HFP_CMD_RETRIEVE_AG_INDICATORS:
case HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS_STATE:
case HFP_CMD_HF_INDICATOR_STATUS:
context->parser_state = HFP_PARSER_SECOND_ITEM;
hfp_connection->parser_state = HFP_PARSER_SECOND_ITEM;
break;
default:
break;
}
break;
case HFP_PARSER_SECOND_ITEM:
context->parser_state = HFP_PARSER_THIRD_ITEM;
hfp_connection->parser_state = HFP_PARSER_THIRD_ITEM;
break;
case HFP_PARSER_THIRD_ITEM:
if (context->command == HFP_CMD_RETRIEVE_AG_INDICATORS){
context->parser_state = HFP_PARSER_CMD_SEQUENCE;
if (hfp_connection->command == HFP_CMD_RETRIEVE_AG_INDICATORS){
hfp_connection->parser_state = HFP_PARSER_CMD_SEQUENCE;
break;
}
context->parser_state = HFP_PARSER_CMD_HEADER;
hfp_connection->parser_state = HFP_PARSER_CMD_HEADER;
break;
}
}
void hfp_parse(hfp_connection_t * context, uint8_t byte, int isHandsFree){
void hfp_parse(hfp_connection_t * hfp_connection, uint8_t byte, int isHandsFree){
// 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*)hfp_connection->line_buffer, HFP_CALL_PHONE_NUMBER, strlen(HFP_CALL_PHONE_NUMBER)) == 0){
// check for end-of-line or ';'
if (byte == ';' || hfp_parser_is_end_of_line(byte)){
context->line_buffer[context->line_size] = 0;
context->line_size = 0;
context->command = HFP_CMD_CALL_PHONE_NUMBER;
hfp_connection->line_buffer[hfp_connection->line_size] = 0;
hfp_connection->line_size = 0;
hfp_connection->command = HFP_CMD_CALL_PHONE_NUMBER;
} else {
context->line_buffer[context->line_size++] = byte;
hfp_connection->line_buffer[hfp_connection->line_size++] = byte;
}
return;
}
// TODO: handle space inside word
if (byte == ' ' && context->parser_state > HFP_PARSER_CMD_HEADER) return;
if (byte == ' ' && hfp_connection->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);
if (byte == ',' && hfp_connection->command == HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE){
if (hfp_connection->line_size == 0){
hfp_connection->line_buffer[0] = 0;
hfp_connection->ignore_value = 1;
parse_sequence(hfp_connection);
return;
}
}
if (!hfp_parser_found_separator(context, byte)){
hfp_parser_store_byte(context, byte);
if (!hfp_parser_found_separator(hfp_connection, byte)){
hfp_parser_store_byte(hfp_connection, byte);
return;
}
if (hfp_parser_is_end_of_line(byte)) {
if (hfp_parser_is_buffer_empty(context)){
context->parser_state = HFP_PARSER_CMD_HEADER;
if (hfp_parser_is_buffer_empty(hfp_connection)){
hfp_connection->parser_state = HFP_PARSER_CMD_HEADER;
}
}
if (hfp_parser_is_buffer_empty(context)) return;
if (hfp_parser_is_buffer_empty(hfp_connection)) return;
switch (context->parser_state){
switch (hfp_connection->parser_state){
case HFP_PARSER_CMD_HEADER: // header
if (byte == '='){
context->keep_byte = 1;
hfp_parser_store_byte(context, byte);
hfp_connection->keep_byte = 1;
hfp_parser_store_byte(hfp_connection, byte);
return;
}
if (byte == '?'){
context->keep_byte = 0;
hfp_parser_store_byte(context, byte);
hfp_connection->keep_byte = 0;
hfp_parser_store_byte(hfp_connection, byte);
return;
}
if (byte == ','){
context->resolve_byte = 1;
hfp_connection->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;
context->command = parse_command(line_buffer, isHandsFree);
// printf(" parse header 2 %s, keep separator $ %d\n", hfp_connection->line_buffer, hfp_connection->keep_byte);
if (hfp_parser_is_end_of_header(byte) || hfp_connection->keep_byte == 1){
// printf(" parse header 3 %s, keep separator $ %d\n", hfp_connection->line_buffer, hfp_connection->keep_byte);
char * line_buffer = (char *)hfp_connection->line_buffer;
hfp_connection->command = parse_command(line_buffer, isHandsFree);
/* resolve command name according to context */
if (context->command == HFP_CMD_UNKNOWN){
switch(context->state){
/* resolve command name according to hfp_connection */
if (hfp_connection->command == HFP_CMD_UNKNOWN){
switch(hfp_connection->state){
case HFP_W4_LIST_GENERIC_STATUS_INDICATORS:
context->command = HFP_CMD_LIST_GENERIC_STATUS_INDICATORS;
hfp_connection->command = HFP_CMD_LIST_GENERIC_STATUS_INDICATORS;
break;
case HFP_W4_RETRIEVE_GENERIC_STATUS_INDICATORS:
context->command = HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS;
hfp_connection->command = HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS;
break;
case HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS:
context->command = HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS_STATE;
hfp_connection->command = HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS_STATE;
break;
case HFP_W4_RETRIEVE_INDICATORS_STATUS:
context->command = HFP_CMD_RETRIEVE_AG_INDICATORS_STATUS;
hfp_connection->command = HFP_CMD_RETRIEVE_AG_INDICATORS_STATUS;
break;
case HFP_W4_RETRIEVE_INDICATORS:
context->send_ag_indicators_segment = 0;
context->command = HFP_CMD_RETRIEVE_AG_INDICATORS;
hfp_connection->send_ag_indicators_segment = 0;
hfp_connection->command = HFP_CMD_RETRIEVE_AG_INDICATORS;
break;
default:
break;
@ -990,34 +990,36 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte, int isHandsFree){
break;
case HFP_PARSER_CMD_SEQUENCE:
parse_sequence(context);
parse_sequence(hfp_connection);
break;
case HFP_PARSER_SECOND_ITEM:
switch (context->command){
switch (hfp_connection->command){
case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME:
log_info("format %s, ", context->line_buffer);
context->network_operator.format = atoi((char *)&context->line_buffer[0]);
log_info("format %s, ", hfp_connection->line_buffer);
hfp_connection->network_operator.format = atoi((char *)&hfp_connection->line_buffer[0]);
break;
case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT:
log_info("format %s \n", context->line_buffer);
context->network_operator.format = atoi((char *)&context->line_buffer[0]);
log_info("format %s \n", hfp_connection->line_buffer);
hfp_connection->network_operator.format = atoi((char *)&hfp_connection->line_buffer[0]);
break;
case HFP_CMD_LIST_GENERIC_STATUS_INDICATORS:
case HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS:
case HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS_STATE:
context->generic_status_indicators[context->parser_item_index].state = (uint8_t)atoi((char*)context->line_buffer);
hfp_connection->generic_status_indicators[hfp_connection->parser_item_index].state = (uint8_t)atoi((char*)hfp_connection->line_buffer);
break;
case HFP_CMD_TRANSFER_AG_INDICATOR_STATUS:
context->ag_indicators[context->parser_item_index].status = (uint8_t)atoi((char*)context->line_buffer);
log_info("%d \n", context->ag_indicators[context->parser_item_index].status);
context->ag_indicators[context->parser_item_index].status_changed = 1;
hfp_connection->ag_indicators[hfp_connection->parser_item_index].status = (uint8_t)atoi((char*)hfp_connection->line_buffer);
log_info("%d \n", hfp_connection->ag_indicators[hfp_connection->parser_item_index].status);
hfp_connection->ag_indicators[hfp_connection->parser_item_index].status_changed = 1;
break;
case HFP_CMD_RETRIEVE_AG_INDICATORS:
context->ag_indicators[context->parser_item_index].min_range = atoi((char *)context->line_buffer);
log_info("%s, ", context->line_buffer);
hfp_connection->ag_indicators[hfp_connection->parser_item_index].min_range = atoi((char *)hfp_connection->line_buffer);
log_info("%s, ", hfp_connection->line_buffer);
break;
case HFP_CMD_AG_SENT_PHONE_NUMBER:
context->bnip_type = (uint8_t)atoi((char*)context->line_buffer);
case HFP_CMD_AG_SENT_CALL_WAITING_NOTIFICATION_UPDATE:
case HFP_CMD_AG_SENT_CLIP_INFORMATION:
hfp_connection->bnip_type = (uint8_t)atoi((char*)hfp_connection->line_buffer);
break;
default:
break;
@ -1025,236 +1027,239 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte, int isHandsFree){
break;
case HFP_PARSER_THIRD_ITEM:
switch (context->command){
switch (hfp_connection->command){
case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME:
strcpy(context->network_operator.name, (char *)context->line_buffer);
log_info("name %s\n", context->line_buffer);
strcpy(hfp_connection->network_operator.name, (char *)hfp_connection->line_buffer);
log_info("name %s\n", hfp_connection->line_buffer);
break;
case HFP_CMD_RETRIEVE_AG_INDICATORS:
context->ag_indicators[context->parser_item_index].max_range = atoi((char *)context->line_buffer);
context->parser_item_index++;
context->ag_indicators_nr = context->parser_item_index;
log_info("%s)\n", context->line_buffer);
hfp_connection->ag_indicators[hfp_connection->parser_item_index].max_range = atoi((char *)hfp_connection->line_buffer);
hfp_connection->parser_item_index++;
hfp_connection->ag_indicators_nr = hfp_connection->parser_item_index;
log_info("%s)\n", hfp_connection->line_buffer);
break;
default:
break;
}
break;
}
hfp_parser_next_state(context, byte);
hfp_parser_next_state(hfp_connection, 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;
if (hfp_connection->resolve_byte && hfp_connection->command == HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE){
hfp_connection->resolve_byte = 0;
hfp_connection->ignore_value = 1;
parse_sequence(hfp_connection);
hfp_connection->line_buffer[0] = 0;
hfp_connection->line_size = 0;
}
}
static void parse_sequence(hfp_connection_t * context){
static void parse_sequence(hfp_connection_t * hfp_connection){
int value;
switch (context->command){
switch (hfp_connection->command){
case HFP_CMD_SET_GENERIC_STATUS_INDICATOR_STATUS:
value = atoi((char *)&context->line_buffer[0]);
value = atoi((char *)&hfp_connection->line_buffer[0]);
int i;
switch (context->parser_item_index){
switch (hfp_connection->parser_item_index){
case 0:
for (i=0;i<context->generic_status_indicators_nr;i++){
if (context->generic_status_indicators[i].uuid == value){
context->parser_indicator_index = i;
for (i=0;i<hfp_connection->generic_status_indicators_nr;i++){
if (hfp_connection->generic_status_indicators[i].uuid == value){
hfp_connection->parser_indicator_index = i;
break;
}
}
break;
case 1:
if (context->parser_indicator_index <0) break;
context->generic_status_indicators[context->parser_indicator_index].state = value;
if (hfp_connection->parser_indicator_index <0) break;
hfp_connection->generic_status_indicators[hfp_connection->parser_indicator_index].state = value;
log_info("HFP_CMD_SET_GENERIC_STATUS_INDICATOR_STATUS set indicator at index %u, to %u\n",
context->parser_item_index, value);
hfp_connection->parser_item_index, value);
break;
default:
break;
}
context->parser_item_index++;
hfp_connection->parser_item_index++;
break;
case HFP_CMD_GET_SUBSCRIBER_NUMBER_INFORMATION:
switch(context->parser_item_index){
switch(hfp_connection->parser_item_index){
case 0:
strncpy(context->bnip_number, (char *)context->line_buffer, sizeof(context->bnip_number));
context->bnip_number[sizeof(context->bnip_number)-1] = 0;
strncpy(hfp_connection->bnip_number, (char *)hfp_connection->line_buffer, sizeof(hfp_connection->bnip_number));
hfp_connection->bnip_number[sizeof(hfp_connection->bnip_number)-1] = 0;
break;
case 1:
value = atoi((char *)&context->line_buffer[0]);
context->bnip_type = value;
value = atoi((char *)&hfp_connection->line_buffer[0]);
hfp_connection->bnip_type = value;
break;
default:
break;
}
context->parser_item_index++;
hfp_connection->parser_item_index++;
break;
case HFP_CMD_LIST_CURRENT_CALLS:
switch(context->parser_item_index){
switch(hfp_connection->parser_item_index){
case 0:
value = atoi((char *)&context->line_buffer[0]);
context->clcc_idx = value;
value = atoi((char *)&hfp_connection->line_buffer[0]);
hfp_connection->clcc_idx = value;
break;
case 1:
value = atoi((char *)&context->line_buffer[0]);
context->clcc_dir = value;
value = atoi((char *)&hfp_connection->line_buffer[0]);
hfp_connection->clcc_dir = value;
break;
case 2:
value = atoi((char *)&context->line_buffer[0]);
context->clcc_status = value;
value = atoi((char *)&hfp_connection->line_buffer[0]);
hfp_connection->clcc_status = value;
break;
case 3:
value = atoi((char *)&context->line_buffer[0]);
context->clcc_mpty = value;
value = atoi((char *)&hfp_connection->line_buffer[0]);
hfp_connection->clcc_mpty = value;
break;
case 4:
strncpy(context->bnip_number, (char *)context->line_buffer, sizeof(context->bnip_number));
context->bnip_number[sizeof(context->bnip_number)-1] = 0;
strncpy(hfp_connection->bnip_number, (char *)hfp_connection->line_buffer, sizeof(hfp_connection->bnip_number));
hfp_connection->bnip_number[sizeof(hfp_connection->bnip_number)-1] = 0;
break;
case 5:
value = atoi((char *)&context->line_buffer[0]);
context->bnip_type = value;
value = atoi((char *)&hfp_connection->line_buffer[0]);
hfp_connection->bnip_type = value;
break;
default:
break;
}
context->parser_item_index++;
hfp_connection->parser_item_index++;
break;
case HFP_CMD_SET_MICROPHONE_GAIN:
value = atoi((char *)&context->line_buffer[0]);
context->microphone_gain = value;
value = atoi((char *)&hfp_connection->line_buffer[0]);
hfp_connection->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;
value = atoi((char *)&hfp_connection->line_buffer[0]);
hfp_connection->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;
value = atoi((char *)&hfp_connection->line_buffer[0]);
hfp_connection->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;
value = atoi((char *)&hfp_connection->line_buffer[0]);
hfp_connection->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);
value = atoi((char *)&hfp_connection->line_buffer[0]);
hfp_connection->remote_supported_features = store_bit(hfp_connection->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);
hfp_connection->codec_confirmed = atoi((char*)hfp_connection->line_buffer);
log_info("hfp parse HFP_CMD_HF_CONFIRMED_CODEC %d\n", hfp_connection->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);
hfp_connection->suggested_codec = atoi((char*)hfp_connection->line_buffer);
log_info("hfp parse HFP_CMD_AG_SUGGESTED_CODEC %d\n", hfp_connection->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);
hfp_connection->remote_supported_features = atoi((char*)hfp_connection->line_buffer);
log_info("Parsed supported feature %d\n", hfp_connection->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;
log_info("Parsed codec %s\n", hfp_connection->line_buffer);
hfp_connection->remote_codecs[hfp_connection->parser_item_index] = (uint16_t)atoi((char*)hfp_connection->line_buffer);
hfp_connection->parser_item_index++;
hfp_connection->remote_codecs_nr = hfp_connection->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);
strcpy((char *)hfp_connection->ag_indicators[hfp_connection->parser_item_index].name, (char *)hfp_connection->line_buffer);
hfp_connection->ag_indicators[hfp_connection->parser_item_index].index = hfp_connection->parser_item_index+1;
log_info("Indicator %d: %s (", hfp_connection->ag_indicators_nr+1, hfp_connection->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++;
log_info("Parsed Indicator %d with status: %s\n", hfp_connection->parser_item_index+1, hfp_connection->line_buffer);
hfp_connection->ag_indicators[hfp_connection->parser_item_index].status = atoi((char *) hfp_connection->line_buffer);
hfp_connection->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;
hfp_connection->parser_item_index++;
if (hfp_connection->parser_item_index != 4) break;
log_info("Parsed Enable indicators: %s\n", hfp_connection->line_buffer);
value = atoi((char *)&hfp_connection->line_buffer[0]);
hfp_connection->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++;
log_info("Parsed Support call hold: %s\n", hfp_connection->line_buffer);
if (hfp_connection->line_size > 2 ) break;
strcpy((char *)hfp_connection->remote_call_services[hfp_connection->remote_call_services_nr].name, (char *)hfp_connection->line_buffer);
hfp_connection->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;
log_info("Parsed Generic status indicator: %s\n", hfp_connection->line_buffer);
hfp_connection->generic_status_indicators[hfp_connection->parser_item_index].uuid = (uint16_t)atoi((char*)hfp_connection->line_buffer);
hfp_connection->parser_item_index++;
hfp_connection->generic_status_indicators_nr = hfp_connection->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);
log_info("Parsed List generic status indicator %s state: ", hfp_connection->line_buffer);
hfp_connection->parser_item_index = (uint8_t)atoi((char*)hfp_connection->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);
hfp_connection->parser_indicator_index = (uint8_t)atoi((char*)hfp_connection->line_buffer);
log_info("Parsed HF indicator index %u", hfp_connection->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);
if (hfp_connection->ignore_value){
hfp_connection->ignore_value = 0;
log_info("Parsed Enable AG indicator pos %u('%s') - unchanged (stays %u)\n", hfp_connection->parser_item_index,
hfp_connection->ag_indicators[hfp_connection->parser_item_index].name, hfp_connection->ag_indicators[hfp_connection->parser_item_index].enabled);
}
else if (context->ag_indicators[context->parser_item_index].mandatory){
else if (hfp_connection->ag_indicators[hfp_connection->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);
hfp_connection->parser_item_index, hfp_connection->ag_indicators[hfp_connection->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);
value = atoi((char *)&hfp_connection->line_buffer[0]);
hfp_connection->ag_indicators[hfp_connection->parser_item_index].enabled = value;
log_info("Parsed Enable AG indicator pos %u('%s'): %u\n", hfp_connection->parser_item_index,
hfp_connection->ag_indicators[hfp_connection->parser_item_index].name, value);
}
context->parser_item_index++;
hfp_connection->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);
hfp_connection->parser_item_index = atoi((char *)&hfp_connection->line_buffer[0]) - 1;
log_info("Parsed status of the AG indicator %d, status ", hfp_connection->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);
hfp_connection->network_operator.mode = atoi((char *)&hfp_connection->line_buffer[0]);
log_info("Parsed network operator mode: %d, ", hfp_connection->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);
if (hfp_connection->line_buffer[0] == '3'){
log_info("Parsed Set network operator format : %s, ", hfp_connection->line_buffer);
break;
}
// TODO emit ERROR, wrong format
log_info("ERROR Set network operator format: index %s not supported\n", context->line_buffer);
log_info("ERROR Set network operator format: index %s not supported\n", hfp_connection->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);
hfp_connection->extended_audio_gateway_error = 1;
hfp_connection->extended_audio_gateway_error_value = (uint8_t)atoi((char*)hfp_connection->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;
hfp_connection->enable_extended_audio_gateway_error_report = (uint8_t)atoi((char*)hfp_connection->line_buffer);
hfp_connection->ok_pending = 1;
hfp_connection->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;
case HFP_CMD_AG_SENT_CALL_WAITING_NOTIFICATION_UPDATE:
case HFP_CMD_AG_SENT_CLIP_INFORMATION:
strncpy(hfp_connection->bnip_number, (char *)hfp_connection->line_buffer, sizeof(hfp_connection->bnip_number));
hfp_connection->bnip_number[sizeof(hfp_connection->bnip_number)-1] = 0;
break;
default:
break;
@ -1262,63 +1267,64 @@ static void parse_sequence(hfp_connection_t * context){
}
void hfp_establish_service_level_connection(bd_addr_t bd_addr, uint16_t service_uuid){
hfp_connection_t * context = provide_hfp_connection_context_for_bd_addr(bd_addr);
log_info("hfp_connect %s, context %p", bd_addr_to_str(bd_addr), context);
hfp_connection_t * hfp_connection = provide_hfp_connection_context_for_bd_addr(bd_addr);
log_info("hfp_connect %s, hfp_connection %p", bd_addr_to_str(bd_addr), hfp_connection);
if (!context) {
if (!hfp_connection) {
log_error("hfp_establish_service_level_connection for addr %s failed", bd_addr_to_str(bd_addr));
return;
}
switch (context->state){
switch (hfp_connection->state){
case HFP_W2_DISCONNECT_RFCOMM:
context->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED;
hfp_connection->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED;
return;
case HFP_W4_RFCOMM_DISCONNECTED:
context->state = HFP_W4_RFCOMM_DISCONNECTED_AND_RESTART;
hfp_connection->state = HFP_W4_RFCOMM_DISCONNECTED_AND_RESTART;
return;
case HFP_IDLE:
memcpy(context->remote_addr, bd_addr, 6);
context->state = HFP_W4_SDP_EVENT_QUERY_COMPLETE;
connection_doing_sdp_query = context;
context->service_uuid = service_uuid;
sdp_query_rfcomm_channel_and_name_for_uuid(&handle_query_rfcomm_event, context->remote_addr, service_uuid);
memcpy(hfp_connection->remote_addr, bd_addr, 6);
hfp_connection->state = HFP_W4_SDP_EVENT_QUERY_COMPLETE;
connection_doing_sdp_query = hfp_connection;
hfp_connection->service_uuid = service_uuid;
sdp_query_rfcomm_channel_and_name_for_uuid(&handle_query_rfcomm_event, hfp_connection->remote_addr, service_uuid);
break;
default:
break;
}
}
void hfp_release_service_level_connection(hfp_connection_t * context){
if (!context) return;
if (context->state < HFP_W4_RFCOMM_CONNECTED){
context->state = HFP_IDLE;
void hfp_release_service_level_connection(hfp_connection_t * hfp_connection){
if (!hfp_connection) return;
hfp_release_audio_connection(hfp_connection);
if (hfp_connection->state < HFP_W4_RFCOMM_CONNECTED){
hfp_connection->state = HFP_IDLE;
return;
}
if (context->state == HFP_W4_RFCOMM_CONNECTED){
context->state = HFP_W4_CONNECTION_ESTABLISHED_TO_SHUTDOWN;
if (hfp_connection->state == HFP_W4_RFCOMM_CONNECTED){
hfp_connection->state = HFP_W4_CONNECTION_ESTABLISHED_TO_SHUTDOWN;
return;
}
if (context->state < HFP_W4_SCO_CONNECTED){
context->state = HFP_W2_DISCONNECT_RFCOMM;
if (hfp_connection->state < HFP_W4_SCO_CONNECTED){
hfp_connection->state = HFP_W2_DISCONNECT_RFCOMM;
return;
}
if (context->state < HFP_W4_SCO_DISCONNECTED){
context->state = HFP_W2_DISCONNECT_SCO;
if (hfp_connection->state < HFP_W4_SCO_DISCONNECTED){
hfp_connection->state = HFP_W2_DISCONNECT_SCO;
return;
}
return;
}
void hfp_release_audio_connection(hfp_connection_t * context){
if (!context) return;
if (context->state >= HFP_W2_DISCONNECT_SCO) return;
context->release_audio_connection = 1;
void hfp_release_audio_connection(hfp_connection_t * hfp_connection){
if (!hfp_connection) return;
if (hfp_connection->state >= HFP_W2_DISCONNECT_SCO) return;
hfp_connection->release_audio_connection = 1;
}
static const struct link_settings {

View File

@ -37,7 +37,7 @@
// *****************************************************************************
//
// HFP Hands-Free (HF) unit and Audio-Gateway Commons
// HFP Hands-Free (HF) unit and Audio Gateway Commons
//
// *****************************************************************************
@ -159,7 +159,9 @@ typedef enum {
HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE,
HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES,
HFP_CMD_ENABLE_CLIP,
HFP_CMD_AG_SENT_CLIP_INFORMATION,
HFP_CMD_ENABLE_CALL_WAITING_NOTIFICATION,
HFP_CMD_AG_SENT_CALL_WAITING_NOTIFICATION_UPDATE,
HFP_CMD_LIST_GENERIC_STATUS_INDICATORS,
HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS,
@ -297,7 +299,7 @@ typedef enum {
typedef enum {
HFP_IDLE = 0, //0
HFP_SDP_QUERY_RFCOMM_CHANNEL,
HFP_W4_SDP_EVENT_QUERY_COMPLETE,
HFP_W4_SDP_QUERY_COMPLETE,
HFP_W4_RFCOMM_CONNECTED,
HFP_EXCHANGE_SUPPORTED_FEATURES,
@ -526,6 +528,7 @@ typedef struct hfp_connection {
uint8_t operator_name_changed;
uint8_t enable_extended_audio_gateway_error_report;
uint8_t extended_audio_gateway_error_value;
uint8_t extended_audio_gateway_error;
// establish codecs connection
@ -556,6 +559,7 @@ typedef struct hfp_connection {
uint8_t ag_send_clip;
uint8_t ag_echo_and_noise_reduction;
uint8_t ag_activate_voice_recognition;
uint8_t ag_notify_incoming_call_waiting;
uint8_t send_subscriber_number;
uint8_t next_subscriber_number_to_send;
@ -568,6 +572,7 @@ typedef struct hfp_connection {
uint8_t hf_initiate_outgoing_call;
uint8_t hf_initiate_memory_dialing;
uint8_t hf_initiate_redial_last_number;
int memory_id;
uint8_t hf_send_clip_enable;
uint8_t hf_send_chup;
@ -602,7 +607,7 @@ typedef struct hfp_connection {
uint8_t clcc_mpty;
uint8_t call_index;
// also used for CLCC if set
// also used for CLCC, CCWA, CLIP if set
uint8_t bnip_type; // 0 == not set
char bnip_number[25]; //
@ -621,24 +626,21 @@ void hfp_set_callback(hfp_callback_t callback);
void hfp_create_sdp_record(uint8_t * service, uint32_t service_record_handle, uint16_t service_uuid, int rfcomm_channel_nr, const char * name);
void hfp_handle_hci_event(uint8_t packet_type, uint8_t *packet, uint16_t size);
void hfp_emit_event(hfp_callback_t callback, uint8_t event_subtype, uint8_t value);
void hfp_emit_simple_event(hfp_callback_t callback, uint8_t event_subtype);
void hfp_emit_string_event(hfp_callback_t callback, uint8_t event_subtype, const char * value);
hfp_connection_t * get_hfp_connection_context_for_rfcomm_cid(uint16_t cid);
hfp_connection_t * get_hfp_connection_context_for_bd_addr(bd_addr_t bd_addr);
hfp_connection_t * get_hfp_connection_context_for_sco_handle(uint16_t handle);
int get_hfp_generic_status_indicators_nr(void);
hfp_generic_status_indicator_t * get_hfp_generic_status_indicators(void);
void set_hfp_generic_status_indicators(hfp_generic_status_indicator_t * indicators, int indicator_nr);
btstack_linked_list_t * hfp_get_connections(void);
void hfp_parse(hfp_connection_t * context, uint8_t byte, int isHandsFree);
void hfp_parse(hfp_connection_t * connection, uint8_t byte, int isHandsFree);
void hfp_establish_service_level_connection(bd_addr_t bd_addr, uint16_t service_uuid);
void hfp_release_service_level_connection(hfp_connection_t * connection);
void hfp_reset_context_flags(hfp_connection_t * context);
void hfp_reset_context_flags(hfp_connection_t * connection);
void hfp_release_audio_connection(hfp_connection_t * context);
void hfp_release_audio_connection(hfp_connection_t * connection);
void hfp_setup_synchronous_connection(hci_con_handle_t handle, hfp_link_setttings_t link_settings);

File diff suppressed because it is too large Load Diff

View File

@ -62,26 +62,64 @@ typedef struct {
/**
* @brief Create HFP Audio Gateway (AG) SDP service record.
* @param service
* @param rfcomm_channel_nr
* @param name
* @param ability_to_reject_call
* @param suported_features 32-bit bitmap, see HFP_AGSF_* values in hfp.h
*/
void hfp_ag_create_sdp_record(uint8_t * service, uint32_t service_record_handle, int rfcomm_channel_nr, const char * name, uint8_t ability_to_reject_call, uint16_t supported_features);;
/**
* @brief Intialize HFP Audio Gateway (AG) device.
* TODO: move optional params into setters
* @brief Set up HFP Audio Gateway (AG) device without additional supported features.
* @param rfcomm_channel_nr
*/
void hfp_ag_init(uint16_t rfcomm_channel_nr, uint32_t supported_features,
uint8_t * codecs, int codecs_nr,
hfp_ag_indicator_t * ag_indicators, int ag_indicators_nr,
hfp_generic_status_indicator_t * hf_indicators, int hf_indicators_nr,
const char *call_hold_services[], int call_hold_services_nr);
void hfp_ag_init(uint16_t rfcomm_channel_nr);
/**
* @brief Set codecs.
* @param codecs_nr
* @param codecs
*/
void hfp_ag_init_codecs(int codecs_nr, uint8_t * codecs);
/**
* @brief Set supported features.
* @param supported_features 32-bit bitmap, see HFP_AGSF_* values in hfp.h
*/
void hfp_ag_init_supported_features(uint32_t supported_features);
/**
* @brief Set AG indicators.
* @param indicators_nr
* @param indicators
*/
void hfp_ag_init_ag_indicators(int ag_indicators_nr, hfp_ag_indicator_t * ag_indicators);
/**
* @brief Set HF indicators.
* @param indicators_nr
* @param indicators
*/
void hfp_ag_init_hf_indicators(int hf_indicators_nr, hfp_generic_status_indicator_t * hf_indicators);
/**
* @brief Set Call Hold services.
* @param indicators_nr
* @param indicators
*/
void hfp_ag_init_call_hold_services(int call_hold_services_nr, const char * call_hold_services[]);
/**
* @brief Register callback for the HFP Audio Gateway (AG) client.
* @param callback
*/
void hfp_ag_register_packet_handler(hfp_callback_t callback);
/**
* @brief Enable in-band ring tone
* @brief Enable in-band ring tone.
* @param use_in_band_ring_tone
*/
void hfp_ag_set_use_in_band_ring_tone(int use_in_band_ring_tone);
@ -96,152 +134,192 @@ void hfp_ag_set_use_in_band_ring_tone(int use_in_band_ring_tone);
* - accept the information about available codecs in the Hands-Free (HF), if sent
* - report own information describing the call hold and multiparty services, if possible
* - report which HF indicators are enabled on the AG, if possible
* The status of SLC connection establishment is reported via
* HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED.
*
* @param bd_addr Bluetooth address of the HF
*/
void hfp_ag_establish_service_level_connection(bd_addr_t bd_addr);
/**
* @brief Release the RFCOMM channel and the audio connection between the HF and the AG.
* TODO: trigger release of the audio connection ??
* If the audio connection exists, it will be released.
* The status of releasing the SLC connection is reported via
* HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_RELEASED.
*
* @param bd_addr Bluetooth address of the HF
*/
void hfp_ag_release_service_level_connection(bd_addr_t bd_addr);
/**
* @brief
* @brief Establish audio connection.
* The status of Audio connection establishment is reported via is reported via
* HSP_SUBEVENT_AUDIO_CONNECTION_COMPLETE.
* @param bd_addr Bluetooth address of the HF
*/
void hfp_ag_establish_audio_connection(bd_addr_t bd_addr);
/**
* @brief
* @brief Release audio connection.
* The status of releasing the Audio connection is reported via is reported via
* HSP_SUBEVENT_AUDIO_DISCONNECTION_COMPLETE.
* @param bd_addr Bluetooth address of the HF
*/
void hfp_ag_release_audio_connection(bd_addr_t bd_addr);
/**
* @brief
* @brief Put the current call on hold, if it exists, and accept incoming call.
*/
void hfp_ag_answer_incoming_call(void);
/**
* @brief
* @brief Join held call with active call.
*/
void hfp_ag_join_held_call(void);
/**
* @brief
* @brief Reject incoming call, if exists, or terminate active call.
*/
void hfp_ag_terminate_call(void);
/*
* @brief
* @brief Put incoming call on hold.
*/
void hfp_ag_hold_incoming_call(void);
/*
* @brief
* @brief Accept the held incoming call.
*/
void hfp_ag_accept_held_incoming_call(void);
/*
* @brief
* @brief Reject the held incoming call.
*/
void hfp_ag_reject_held_incoming_call(void);
/*
* @brief
* @brief Set microphone gain.
* @param bd_addr Bluetooth address of the HF
* @param gain Valid range: [0,15]
*/
void hfp_ag_set_microphone_gain(bd_addr_t bd_addr, int gain);
/*
* @brief
* @brief Set speaker gain.
* @param bd_addr Bluetooth address of the HF
* @param gain Valid range: [0,15]
*/
void hfp_ag_set_speaker_gain(bd_addr_t bd_addr, int gain);
/*
* @brief
* @brief Set battery level.
* @param level Valid range: [0,5]
*/
void hfp_ag_set_battery_level(int level);
/*
* @brief
* @brief Clear last dialed number.
*/
void hfp_ag_clear_last_dialed_number(void);
/*
* @brief Notify the HF that an incoming call is waiting
* during an ongoing call. The notification will be sent only if the HF has
* has previously enabled the "Call Waiting notification" in the AG.
* @param bd_addr Bluetooth address of the HF
*/
void hfp_ag_notify_incoming_call_waiting(bd_addr_t bd_addr);
// Voice Recognition
/*
* @brief
* @brief Activate voice recognition.
* @param bd_addr Bluetooth address of the HF
* @param activate
*/
void hfp_ag_activate_voice_recognition(bd_addr_t bd_addr, int activate);
/*
* @brief
* @brief Send a phone number back to the HF.
* @param bd_addr Bluetooth address of the HF
* @param phone_number
*/
void hfp_ag_send_phone_number_for_voice_tag(bd_addr_t bd_addr, const char * number);
void hfp_ag_send_phone_number_for_voice_tag(bd_addr_t bd_addr, const char * phone_number);
/*
* @brief
* @brief Reject sending a phone number to the HF.
* @param bd_addr Bluetooth address of the HF
*/
void hfp_ag_reject_phone_number_for_voice_tag(bd_addr_t bd_addr);
/**
* @brief Store phone number with initiated call.
* @param type
* @param number
*/
void hfp_ag_set_clip(uint8_t type, const char * number);
// Cellular Actions
/**
* @brief
* @brief Pass the accept incoming call event to the AG.
*/
void hfp_ag_incoming_call(void);
/**
* @brief number is stored.
*/
void hfp_ag_set_clip(uint8_t type, const char * number);
/**
* @brief
* @brief Pass the reject outgoing call event to the AG.
*/
void hfp_ag_outgoing_call_rejected(void);
/**
* @brief
* @brief Pass the accept outgoing call event to the AG.
*/
void hfp_ag_outgoing_call_accepted(void);
/**
* @brief
* @brief Pass the outgoing call ringing event to the AG.
*/
void hfp_ag_outgoing_call_ringing(void);
/**
* @brief
* @brief Pass the outgoing call established event to the AG.
*/
void hfp_ag_outgoing_call_established(void);
/**
* @brief
* @brief Pass the call droped event to the AG.
*/
void hfp_ag_call_dropped(void);
/*
* @brief
* @brief Set network registration status.
* @param status 0 - not registered, 1 - registered
*/
void hfp_ag_set_registration_status(int status);
/*
* @brief
* @brief Set network signal strength.
* @param strength [0-5]
*/
void hfp_ag_set_signal_strength(int strength);
/*
* @brief
* @brief Set roaming status.
* @param status 0 - no roaming, 1 - roaming active
*/
void hfp_ag_set_roaming_status(int status);
/*
* @brief
* @brief Set subcriber number information, e.g. the phone number
* @param numbers
* @param numbers_count
*/
void hfp_ag_set_subcriber_number_information(hfp_phone_number_t * numbers, int numbers_count);
/*
* @brief Called by cellular unit after a DTMF code was transmitted, so that the next one can be emitted
* @brief Called by cellular unit after a DTMF code was transmitted, so that the next one can be emitted.
* @param bd_addr Bluetooth address of the HF
*/
void hfp_ag_send_dtmf_code_done(bd_addr_t bd_addr);
@ -272,6 +350,9 @@ void hfp_ag_send_dtmf_code_done(bd_addr_t bd_addr);
* - +CME ERROR: 30 - no network service
* - +CME ERROR: 31 - network Timeout.
* - +CME ERROR: 32 - network not allowed Emergency calls only
*
* @param bd_addr Bluetooth address of the HF
* @param error
*/
void hfp_ag_report_extended_audio_gateway_error_result_code(bd_addr_t bd_addr, hfp_cme_error_t error);

File diff suppressed because it is too large Load Diff

View File

@ -57,262 +57,361 @@ extern "C" {
/**
* @brief Create HFP Hands-Free (HF) SDP service record.
* @param service
* @param rfcomm_channel_nr
* @param name
* @param suported_features 32-bit bitmap, see HFP_HFSF_* values in hfp.h
*/
void hfp_hf_create_sdp_record(uint8_t * service, uint32_t service_record_handle, int rfcomm_channel_nr, const char * name, uint16_t supported_features);
/**
* @brief Intialize HFP Hands-Free (HF) device.
* TODO: move optional params into setters
* @brief Set up HFP Hands-Free (HF) device without additional supported features.
* @param rfcomm_channel_nr
*/
void hfp_hf_init(uint16_t rfcomm_channel_nr, uint32_t supported_features, uint16_t * indicators, int indicators_nr, uint32_t indicators_status);
void hfp_hf_init(uint16_t rfcomm_channel_nr);
void hfp_hf_set_codecs(uint8_t * codecs, int codecs_nr);
/**
* @brief Set codecs.
* @param codecs_nr
* @param codecs
*/
void hfp_hf_init_codecs(int codecs_nr, uint8_t * codecs);
/**
* @brief Set supported features.
* @param supported_features 32-bit bitmap, see HFP_HFSF_* values in hfp.h
*/
void hfp_hf_init_supported_features(uint32_t supported_features);
/**
* @brief Set HF indicators.
* @param indicators_nr
* @param indicators
*/
void hfp_hf_init_hf_indicators(int indicators_nr, uint16_t * indicators);
void hfp_hf_set_supported_features(uint32_t supported_features);
/**
* @brief Register callback for the HFP Hands-Free (HF) client.
* @param callback
*/
void hfp_hf_register_packet_handler(hfp_callback_t callback);
/**
* @brief Establish RFCOMM connection, and perform service level connection agreement:
* - exchange of supported features
* @brief Establish RFCOMM connection with the AG with given Bluetooth address,
* and perform service level connection (SLC) agreement:
* - exchange supported features
* - retrieve Audio Gateway (AG) indicators and their status
* - enable indicator status update in the AG
* - notify the AG about its own available codecs, if possible
* - retrieve the AG information describing the call hold and multiparty services, if possible
* - retrieve which HF indicators are enabled on the AG, if possible
* The status of SLC connection establishment is reported via
* HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED.
*
* @param bd_addr Bluetooth address of the AG
*/
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.
* TODO: trigger release of the audio connection
* The status of releasing the SLC connection is reported via
* HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_RELEASED.
*
* @param bd_addr Bluetooth address of the AG
*/
void hfp_hf_release_service_level_connection(bd_addr_t bd_addr);
/**
* @brief Deactivate/reactivate status update for all indicators in the AG.
* @brief Enable status update for all indicators in the AG.
* The status field of the HFP_SUBEVENT_COMPLETE reports if the command was accepted.
* The status of an AG indicator is reported via HFP_SUBEVENT_AG_INDICATOR_STATUS_CHANGED.
*
* @param bd_addr Bluetooth address of the AG
*/
void hfp_hf_enable_status_update_for_all_ag_indicators(bd_addr_t bd_addr);
/**
* @brief Disable status update for all indicators in the AG.
* The status field of the HFP_SUBEVENT_COMPLETE reports if the command was accepted.
* @param bd_addr Bluetooth address of the AG
*/
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 Enable or disable status update for the individual indicators in the AG using bitmap.
* The status field of the HFP_SUBEVENT_COMPLETE reports if the command was accepted.
* The status of an AG indicator is reported via HFP_SUBEVENT_AG_INDICATOR_STATUS_CHANGED.
*
* @param bd_addr Bluetooth address of the AG
* @param indicators_status_bitmap 32-bit bitmap, 0 - indicator is disabled, 1 - indicator is enabled
*/
void hfp_hf_set_status_update_for_individual_ag_indicators(bd_addr_t bd_addr, uint32_t indicators_status_bitmap);
/**
* @brief Find out the name of the currently selected Network operator by AG.
* The name is restricted to max 16 characters.
*
* TODO: what is the result of this?
* @brief Query the name of the currently selected Network operator by AG.
*
* The name is restricted to max 16 characters. The result is reported via
* HFP_SUBEVENT_NETWORK_OPERATOR_CHANGED subtype
* containing network operator mode, format and name.
* If no operator is selected, format and operator are omitted.
*
* @param bd_addr Bluetooth address of the AG
*/
void hfp_hf_query_operator_selection(bd_addr_t bd_addr);
/**
* @brief Enable/disable Extended Audio Gateway Error result codes in the AG.
* @brief Enable Extended Audio Gateway Error result codes in the AG.
* Whenever there is an error relating to the functionality of the AG as a
* result of AT command, the AG shall send +CME ERROR:
* - +CME ERROR: 0 - AG failure
* - +CME ERROR: 1 - no connection to phone
* - +CME ERROR: 3 - operation not allowed
* - +CME ERROR: 4 - operation not supported
* - +CME ERROR: 5 - PH-SIM PIN required
* - +CME ERROR: 10 - SIM not inserted
* - +CME ERROR: 11 - SIM PIN required
* - +CME ERROR: 12 - SIM PUK required
* - +CME ERROR: 13 - SIM failure
* - +CME ERROR: 14 - SIM busy
* - +CME ERROR: 16 - incorrect password
* - +CME ERROR: 17 - SIM PIN2 required
* - +CME ERROR: 18 - SIM PUK2 required
* - +CME ERROR: 20 - memory full
* - +CME ERROR: 21 - invalid index
* - +CME ERROR: 23 - memory failure
* - +CME ERROR: 24 - text string too long
* - +CME ERROR: 25 - invalid characters in text string
* - +CME ERROR: 26 - dial string too long
* - +CME ERROR: 27 - invalid characters in dial string
* - +CME ERROR: 30 - no network service
* - +CME ERROR: 31 - network Timeout.
* - +CME ERROR: 32 - network not allowed Emergency calls only
* result of AT command, the AG shall send +CME ERROR. This error is reported via
* HFP_SUBEVENT_EXTENDED_AUDIO_GATEWAY_ERROR, see hfp_cme_error_t in hfp.h
*
* @param bd_addr Bluetooth address of the AG
*/
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 Disable Extended Audio Gateway Error result codes in the AG.
*
* @param bd_addr Bluetooth address of the AG
*/
void hfp_hf_disable_report_extended_audio_gateway_error_result_code(bd_addr_t bd_addr);
/**
* @brief Establish audio connection.
* The status of audio connection establishment is reported via
* HFP_SUBEVENT_AUDIO_CONNECTION_ESTABLISHED.
* @param bd_addr Bluetooth address of the AG
*/
void hfp_hf_establish_audio_connection(bd_addr_t bd_addr);
/**
* @brief
* @brief Release audio connection.
* The status of releasing of the audio connection is reported via
* HFP_SUBEVENT_AUDIO_CONNECTION_RELEASED.
*
* @param bd_addr Bluetooth address of the AG
*/
void hfp_hf_release_audio_connection(bd_addr_t bd_addr);
/**
* @brief
* @brief Answer incoming call.
* @param bd_addr Bluetooth address of the AG
*/
void hfp_hf_answer_incoming_call(bd_addr_t bd_addr);
/**
* @brief
* @brief Reject incoming call.
* @param bd_addr Bluetooth address of the AG
*/
void hfp_hf_reject_call(bd_addr_t bd_addr);
void hfp_hf_reject_incoming_call(bd_addr_t bd_addr);
/**
* @brief
* @brief Release all held calls or sets User Determined User Busy (UDUB) for a waiting call.
* @param bd_addr Bluetooth address of the AG
*/
void hfp_hf_user_busy(bd_addr_t addr);
/**
* @brief
* @brief Release all active calls (if any exist) and accepts the other (held or waiting) call.
* @param bd_addr Bluetooth address of the AG
*/
void hfp_hf_end_active_and_accept_other(bd_addr_t addr);
/**
* @brief
* @brief Place all active calls (if any exist) on hold and accepts the other (held or waiting) call.
* @param bd_addr Bluetooth address of the AG
*/
void hfp_hf_swap_calls(bd_addr_t addr);
/**
* @brief
* @brief Add a held call to the conversation.
* @param bd_addr Bluetooth address of the AG
*/
void hfp_hf_join_held_call(bd_addr_t addr);
/**
* @brief
* @brief Connect the two calls and disconnects the subscriber from both calls (Explicit Call
Transfer).
* @param bd_addr Bluetooth address of the AG
*/
void hfp_hf_connect_calls(bd_addr_t addr);
/**
* @brief
* @brief Terminate an incoming or an outgoing call.
* HFP_SUBEVENT_CALL_TERMINATED is sent upon call termination.
* @param bd_addr Bluetooth address of the AG
*/
void hfp_hf_terminate_call(bd_addr_t bd_addr);
/**
* @brief
* @brief Initiate outgoing voice call by providing the destination phone number to the AG.
* @param bd_addr Bluetooth address of the AG
* @param number
*/
void hfp_hf_dial_number(bd_addr_t bd_addr, char * number);
/**
* @brief
* TODO: use int for number instead of string?
* @brief Initiate outgoing voice call using the memory dialing feature of the AG.
* @param bd_addr Bluetooth address of the AG
* @param memory_id
*/
void hfp_hf_dial_memory(bd_addr_t bd_addr, char * number);
void hfp_hf_dial_memory(bd_addr_t bd_addr, int memory_id);
/**
* @brief
* @brief Initiate outgoing voice call by recalling the last number dialed by the AG.
* @param bd_addr Bluetooth address of the AG
*/
void hfp_hf_redial_last_number(bd_addr_t bd_addr);
/*
* @brief
* @brief Enable the Call Waiting notification function in the AG.
* The AG shall send the corresponding result code to the HF whenever
* an incoming call is waiting during an ongoing call. In that event,
* the HFP_SUBEVENT_CALL_WAITING_NOTIFICATION is emitted.
*
* @param bd_addr Bluetooth address of the AG
*/
void hfp_hf_activate_call_waiting_notification(bd_addr_t bd_addr);
/*
* @brief
* @brief Disable the Call Waiting notification function in the AG.
* @param bd_addr Bluetooth address of the AG
*/
void hfp_hf_deactivate_call_waiting_notification(bd_addr_t bd_addr);
/*
* @brief
* @brief Enable the Calling Line Identification notification function in the AG.
* The AG shall issue the corresponding result code just after every RING indication,
* when the HF is alerted in an incoming call. In that event,
* the HFP_SUBEVENT_CALLING_LINE_INDETIFICATION_NOTIFICATION is emitted.
* @param bd_addr Bluetooth address of the AG
*/
void hfp_hf_activate_calling_line_notification(bd_addr_t bd_addr);
/*
* @brief
* @brief Disable the Calling Line Identification notification function in the AG.
* @param bd_addr Bluetooth address of the AG
*/
void hfp_hf_deactivate_calling_line_notification(bd_addr_t bd_addr);
/*
* @brief
* @brief Activate echo canceling and noise reduction in the AG. By default,
* if the AG supports its own embedded echo canceling and/or noise reduction
* functions, it shall have them activated until this function is called.
* If the AG does not support any echo canceling and noise reduction functions,
* it shall respond with the ERROR indicator (TODO)
* @param bd_addr Bluetooth address of the AG
*/
void hfp_hf_activate_echo_canceling_and_noise_reduction(bd_addr_t bd_addr);
/*
* @brief
* @brief Deactivate echo canceling and noise reduction in the AG.
*/
void hfp_hf_deactivate_echo_canceling_and_noise_reduction(bd_addr_t bd_addr);
/*
* @brief
* @brief Activate voice recognition function.
* @param bd_addr Bluetooth address of the AG
*/
void hfp_hf_activate_voice_recognition_notification(bd_addr_t bd_addr);
/*
* @brief
* @brief Dectivate voice recognition function.
* @param bd_addr Bluetooth address of the AG
*/
void hfp_hf_deactivate_voice_recognition_notification(bd_addr_t bd_addr);
/*
* @brief
* @brief Set microphone gain.
* @param bd_addr Bluetooth address of the AG
* @param gain Valid range: [0,15]
*/
void hfp_hf_set_microphone_gain(bd_addr_t bd_addr, int gain);
/*
* @brief
* @brief Set speaker gain.
* @param bd_addr Bluetooth address of the AG
* @param gain Valid range: [0,15]
*/
void hfp_hf_set_speaker_gain(bd_addr_t bd_addr, int gain);
/*
* @brief
* @brief Instruct the AG to transmit a DTMF code.
* @param bd_addr Bluetooth address of the AG
* @param dtmf_code
*/
void hfp_hf_send_dtmf_code(bd_addr_t bd_addr, char code);
/*
* @brief
* @brief Read numbers from the AG for the purpose of creating
* a unique voice tag and storing the number and its linked voice
* tag in the HFs memory.
* The number is reported via HFP_SUBEVENT_NUMBER_FOR_VOICE_TAG.
* @param bd_addr Bluetooth address of the AG
*/
void hfp_hf_request_phone_number_for_voice_tag(bd_addr_t addr);
/*
* @brief
* @brief Query the list of current calls in AG.
* The result is received via HFP_SUBEVENT_ENHANCED_CALL_STATUS.
* @param bd_addr Bluetooth address of the AG
*/
void hfp_hf_query_current_call_status(bd_addr_t addr);
/*
* @brief
* @brief Release a call with index in the AG.
* @param bd_addr Bluetooth address of the AG
* @param index
*/
void hfp_hf_release_call_with_index(bd_addr_t addr, int index);
/*
* @brief
* @brief Place all parties of a multiparty call on hold with the
* exception of the specified call.
* @param bd_addr Bluetooth address of the AG
* @param index
*/
void hfp_hf_private_consultation_with_call(bd_addr_t addr, int index);
/*
* @brief
* @brief Query the status of the Response and Hold state of the AG.
* The result is reported via HFP_SUBEVENT_RESPONSE_AND_HOLD_STATUS.
* @param bd_addr Bluetooth address of the AG
*/
void hfp_hf_rrh_query_status(bd_addr_t addr);
/*
* @brief
* @brief Put an incoming call on hold in the AG.
* @param bd_addr Bluetooth address of the AG
*/
void hfp_hf_rrh_hold_call(bd_addr_t addr);
/*
* @brief
* @brief Accept held incoming call in the AG.
* @param bd_addr Bluetooth address of the AG
*/
void hfp_hf_rrh_accept_held_call(bd_addr_t addr);
/*
* @brief
* @brief Reject held incoming call in the AG.
* @param bd_addr Bluetooth address of the AG
*/
void hfp_hf_rrh_reject_held_call(bd_addr_t addr);
/*
* @brief
* @brief Query the AG subscriber number.
* The result is reported via HFP_SUBEVENT_SUBSCRIBER_NUMBER_INFORMATION.
* @param bd_addr Bluetooth address of the AG
*/
void hfp_hf_query_subscriber_number(bd_addr_t addr);
/*
* @brief
* @brief Set HF indicator.
* @param bd_addr Bluetooth address of the AG
* @param assigned_number
* @param value
*/
void hfp_hf_set_hf_indicator(bd_addr_t addr, int assigned_number, int value);

View File

@ -161,7 +161,7 @@ static int hsp_hs_send_str_over_rfcomm(uint16_t cid, const char * command){
if (!rfcomm_can_send_packet_now(rfcomm_cid)) return 1;
int err = rfcomm_send(cid, (uint8_t*) command, strlen(command));
if (err){
printf("rfcomm_send -> error 0X%02x", err);
log_info("rfcomm_send_internal -> error 0X%02x", err);
}
return err;
}
@ -288,12 +288,10 @@ void hsp_hs_connect(bd_addr_t bd_addr){
void hsp_hs_disconnect(bd_addr_t bd_addr){
switch (hsp_state){
case HSP_ACTIVE:
printf("HSP_W4_USER_ACTION\n");
hsp_state = HSP_W4_USER_ACTION;
hs_send_button_press = 1;
break;
case HSP_W4_RFCOMM_CONNECTED:
printf("HSP_W4_CONNECTION_ESTABLISHED_TO_SHUTDOWN \n");
hsp_state = HSP_W4_CONNECTION_ESTABLISHED_TO_SHUTDOWN;
break;
default:
@ -305,7 +303,7 @@ void hsp_hs_disconnect(bd_addr_t bd_addr){
void hsp_hs_set_microphone_gain(uint8_t gain){
if (gain < 0 || gain >15) {
printf("Gain must be in interval [0..15], it is given %d\n", gain);
log_info("Gain must be in interval [0..15], it is given %d", gain);
return;
}
hs_microphone_gain = gain;
@ -315,7 +313,7 @@ void hsp_hs_set_microphone_gain(uint8_t gain){
// AG +VGS=5 [0..15] ; HS AT+VGM=6 | AG OK
void hsp_hs_set_speaker_gain(uint8_t gain){
if (gain < 0 || gain >15) {
printf("Gain must be in interval [0..15], it is given %d\n", gain);
log_info("Gain must be in interval [0..15], it is given %d", gain);
return;
}
hs_speaker_gain = gain;
@ -393,7 +391,6 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
if (strncmp((char *)packet, HSP_AG_RING, strlen(HSP_AG_RING)) == 0){
emit_ring_event();
} else if (strncmp((char *)packet, HSP_AG_OK, strlen(HSP_AG_OK)) == 0){
printf("OK RECEIVED\n");
switch (hsp_state){
case HSP_W4_RFCOMM_CONNECTED:
hsp_state = HSP_W2_CONNECT_SCO;
@ -437,20 +434,6 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
uint16_t handle;
switch (event) {
case BTSTACK_EVENT_STATE:
// bt stack activated, get started
if (packet[2] == HCI_STATE_WORKING){
printf("BTstack activated, get started .\n");
}
hsp_hs_callback(packet, size);
break;
case HCI_EVENT_PIN_CODE_REQUEST:
// inform about pin code request
printf("Pin code request - using '0000'\n\r");
reverse_bd_addr(&packet[2], event_addr);
hci_send_cmd(&hci_pin_code_request_reply, &event_addr, 4, "0000");
break;
case HCI_EVENT_SYNCHRONOUS_CONNECTION_COMPLETE:{
int index = 2;
uint8_t status = packet[index++];
@ -475,14 +458,14 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
}
switch (link_type){
case 0x00:
printf("SCO Connection established. \n");
log_info("SCO Connection established.");
if (transmission_interval != 0) log_error("SCO Connection: transmission_interval not zero: %d.", transmission_interval);
if (retransmission_interval != 0) log_error("SCO Connection: retransmission_interval not zero: %d.", retransmission_interval);
if (rx_packet_length != 0) log_error("SCO Connection: rx_packet_length not zero: %d.", rx_packet_length);
if (tx_packet_length != 0) log_error("SCO Connection: tx_packet_length not zero: %d.", tx_packet_length);
break;
case 0x02:
printf("eSCO Connection established. \n");
log_info("eSCO Connection established.");
break;
default:
log_error("(e)SCO reserved link_type 0x%2x", link_type);
@ -511,17 +494,17 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
reverse_bd_addr(&packet[2], event_addr);
rfcomm_cid = little_endian_read_16(packet, 9);
printf("RFCOMM channel %u requested for %s\n", packet[8], bd_addr_to_str(event_addr));
log_info("RFCOMM channel %u requested for %s", packet[8], bd_addr_to_str(event_addr));
rfcomm_accept_connection(rfcomm_cid);
hsp_state = HSP_W4_RFCOMM_CONNECTED;
break;
case RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE:
printf("RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE packet_handler type %u\n", packet_type);
// printf("RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE packet_handler type %u, packet[0] %x\n", packet_type, packet[0]);
// data: event(8), len(8), status (8), address (48), handle(16), server channel(8), rfcomm_cid(16), max frame size(16)
if (packet[2]) {
printf("RFCOMM channel open failed, status %u\n", packet[2]);
log_info("RFCOMM channel open failed, status %u", packet[2]);
hsp_hs_reset_state();
emit_event(HSP_SUBEVENT_AUDIO_CONNECTION_COMPLETE, packet[2]);
hs_outgoing_connection = 0;
@ -530,7 +513,7 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
rfcomm_handle = little_endian_read_16(packet, 9);
rfcomm_cid = little_endian_read_16(packet, 12);
mtu = little_endian_read_16(packet, 14);
printf("RFCOMM channel open succeeded. New RFCOMM Channel ID %u, max frame size %u\n", rfcomm_cid, mtu);
log_info("RFCOMM channel open succeeded. New RFCOMM Channel ID %u, max frame size %u", rfcomm_cid, mtu);
if (hs_outgoing_connection){
hs_outgoing_connection = 0;
@ -559,12 +542,10 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
if (handle == sco_handle){
sco_handle = 0;
hsp_state = HSP_W2_DISCONNECT_RFCOMM;
printf(" HSP_W2_DISCONNECT_RFCOMM\n");
break;
}
break;
case RFCOMM_EVENT_CHANNEL_CLOSED:
printf("RFCOMM channel closed\n");
hsp_hs_reset_state();
emit_event(HSP_SUBEVENT_AUDIO_DISCONNECTION_COMPLETE,0);
break;
@ -578,18 +559,17 @@ static void handle_query_rfcomm_event(uint8_t packet_type, uint16_t channel, uin
switch (hci_event_packet_get_type(packet)){
case SDP_EVENT_QUERY_RFCOMM_SERVICE:
channel_nr = sdp_event_query_rfcomm_service_get_rfcomm_channel(packet);
printf("** Service name: '%s', RFCOMM port %u\n", sdp_event_query_rfcomm_service_get_name(packet), channel_nr);
log_info("** Service name: '%s', RFCOMM port %u", ve->service_name, channel_nr);
break;
case SDP_EVENT_QUERY_COMPLETE:
if (channel_nr > 0){
hsp_state = HSP_W4_RFCOMM_CONNECTED;
printf("RFCOMM create channel.\n");
log_info("RFCOMM create channel");
rfcomm_create_channel(packet_handler, remote, channel_nr, NULL);
break;
}
hsp_hs_reset_state();
printf("Service not found, status %u.\n", sdp_event_query_complete_get_status(packet));
exit(0);
log_info("Service not found, status %u.", sdp_event_query_complete_get_status(packet));
break;
}
}

280
src/sdp_parser.c Normal file
View File

@ -0,0 +1,280 @@
/*
* Copyright (C) 2014 BlueKitchen GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at
* contact@bluekitchen-gmbh.com
*
*/
/*
* sdp_parser.c
*/
#include <btstack/hci_cmds.h>
#include "sdp_parser.h"
#include "debug.h"
typedef enum {
GET_LIST_LENGTH = 1,
GET_RECORD_LENGTH,
GET_ATTRIBUTE_ID_HEADER_LENGTH,
GET_ATTRIBUTE_ID,
GET_ATTRIBUTE_VALUE_LENGTH,
GET_ATTRIBUTE_VALUE
} state_t;
static state_t state = GET_LIST_LENGTH;
static uint16_t attribute_id = 0;
static uint16_t attribute_bytes_received = 0;
static uint16_t attribute_bytes_delivered = 0;
static uint16_t list_offset = 0;
static uint16_t list_size;
static uint16_t record_offset = 0;
static uint16_t record_size;
static uint16_t attribute_value_size;
static int record_counter = 0;
#ifdef HAVE_SDP_EXTRA_QUERIES
static uint32_t record_handle;
#endif
static void (*sdp_query_callback)(sdp_query_event_t * event);
// Low level parser
static de_state_t de_header_state;
void de_state_init(de_state_t * de_state){
de_state->in_state_GET_DE_HEADER_LENGTH = 1;
de_state->addon_header_bytes = 0;
de_state->de_size = 0;
de_state->de_offset = 0;
}
int de_state_size(uint8_t eventByte, de_state_t *de_state){
if (de_state->in_state_GET_DE_HEADER_LENGTH){
de_state->addon_header_bytes = de_get_header_size(&eventByte) - 1;
de_state->de_size = 0;
de_state->de_offset = 0;
if (de_state->addon_header_bytes == 0){
de_state->de_size = de_get_data_size(&eventByte);
if (de_state->de_size == 0) {
log_error(" ERROR: ID size is zero");
}
// log_info("Data element payload is %d bytes.", de_state->de_size);
return 1;
}
de_state->in_state_GET_DE_HEADER_LENGTH = 0;
return 0;
}
if (de_state->addon_header_bytes > 0){
de_state->de_size = (de_state->de_size << 8) | eventByte;
de_state->addon_header_bytes--;
}
if (de_state->addon_header_bytes > 0) return 0;
// log_info("Data element payload is %d bytes.", de_state->de_size);
de_state->in_state_GET_DE_HEADER_LENGTH = 1;
return 1;
}
static void dummy_notify(sdp_query_event_t* event){}
void sdp_parser_register_callback(void (*sdp_callback)(sdp_query_event_t* event)){
sdp_query_callback = dummy_notify;
if (sdp_callback != NULL){
sdp_query_callback = sdp_callback;
}
}
static void parse(uint8_t eventByte){
// count all bytes
list_offset++;
record_offset++;
// log_info(" parse BYTE_RECEIVED %02x", eventByte);
switch(state){
case GET_LIST_LENGTH:
if (!de_state_size(eventByte, &de_header_state)) break;
list_offset = de_header_state.de_offset;
list_size = de_header_state.de_size;
// log_info("parser: List offset %u, list size %u", list_offset, list_size);
record_counter = 0;
state = GET_RECORD_LENGTH;
break;
case GET_RECORD_LENGTH:
// check size
if (!de_state_size(eventByte, &de_header_state)) break;
// log_info("parser: Record payload is %d bytes.", de_header_state.de_size);
record_offset = de_header_state.de_offset;
record_size = de_header_state.de_size;
state = GET_ATTRIBUTE_ID_HEADER_LENGTH;
break;
case GET_ATTRIBUTE_ID_HEADER_LENGTH:
if (!de_state_size(eventByte, &de_header_state)) break;
attribute_id = 0;
log_info("ID data is stored in %d bytes.", (int) de_header_state.de_size);
state = GET_ATTRIBUTE_ID;
break;
case GET_ATTRIBUTE_ID:
attribute_id = (attribute_id << 8) | eventByte;
de_header_state.de_size--;
if (de_header_state.de_size > 0) break;
log_info("parser: Attribute ID: %04x.", attribute_id);
state = GET_ATTRIBUTE_VALUE_LENGTH;
attribute_bytes_received = 0;
attribute_bytes_delivered = 0;
attribute_value_size = 0;
de_state_init(&de_header_state);
break;
case GET_ATTRIBUTE_VALUE_LENGTH:
attribute_bytes_received++;
{
sdp_query_attribute_value_event_t attribute_value_event = {
SDP_QUERY_ATTRIBUTE_VALUE,
record_counter,
attribute_id,
attribute_value_size,
attribute_bytes_delivered++,
eventByte
};
(*sdp_query_callback)((sdp_query_event_t*)&attribute_value_event);
}
if (!de_state_size(eventByte, &de_header_state)) break;
attribute_value_size = de_header_state.de_size + attribute_bytes_received;
state = GET_ATTRIBUTE_VALUE;
break;
case GET_ATTRIBUTE_VALUE:
attribute_bytes_received++;
{
sdp_query_attribute_value_event_t attribute_value_event = {
SDP_QUERY_ATTRIBUTE_VALUE,
record_counter,
attribute_id,
attribute_value_size,
attribute_bytes_delivered++,
eventByte
};
(*sdp_query_callback)((sdp_query_event_t*)&attribute_value_event);
}
// log_info("paser: attribute_bytes_received %u, attribute_value_size %u", attribute_bytes_received, attribute_value_size);
if (attribute_bytes_received < attribute_value_size) break;
// log_info("parser: Record offset %u, record size %u", record_offset, record_size);
if (record_offset != record_size){
state = GET_ATTRIBUTE_ID_HEADER_LENGTH;
// log_info("Get next attribute");
break;
}
record_offset = 0;
// log_info("parser: List offset %u, list size %u", list_offset, list_size);
if (list_size > 0 && list_offset != list_size){
record_counter++;
state = GET_RECORD_LENGTH;
log_info("parser: END_OF_RECORD");
break;
}
list_offset = 0;
de_state_init(&de_header_state);
state = GET_LIST_LENGTH;
record_counter = 0;
log_info("parser: END_OF_RECORD & DONE");
break;
default:
break;
}
}
void sdp_parser_init(void){
// init
de_state_init(&de_header_state);
state = GET_LIST_LENGTH;
list_offset = 0;
record_offset = 0;
record_counter = 0;
}
void sdp_parser_handle_chunk(uint8_t * data, uint16_t size){
int i;
for (i=0;i<size;i++){
parse(data[i]);
}
}
#ifdef HAVE_SDP_EXTRA_QUERIES
void sdp_parser_init_service_attribute_search(void){
// init
de_state_init(&de_header_state);
state = GET_RECORD_LENGTH;
list_offset = 0;
record_offset = 0;
record_counter = 0;
}
void sdp_parser_init_service_search(void){
record_offset = 0;
}
void sdp_parser_handle_service_search(uint8_t * data, uint16_t total_count, uint16_t record_handle_count){
int i;
for (i=0;i<record_handle_count;i++){
record_handle = READ_NET_32(data, i*4);
record_counter++;
sdp_query_service_record_handle_event_t service_record_handle_event = {
SDP_QUERY_SERVICE_RECORD_HANDLE,
total_count,
(uint16_t) record_counter,
record_handle
};
(*sdp_query_callback)((sdp_query_event_t*)&service_record_handle_event);
}
}
#endif
void sdp_parser_handle_done(uint8_t status){
sdp_query_complete_event_t complete_event = {
SDP_QUERY_COMPLETE,
status
};
(*sdp_query_callback)((sdp_query_event_t*)&complete_event);
}

View File

@ -4,7 +4,7 @@ CC = g++
BTSTACK_ROOT = ../..
CFLAGS = -DUNIT_TEST -x c++ -g -Wall -I. -I../ -I${BTSTACK_ROOT}/src -I${BTSTACK_ROOT}/ble -I${BTSTACK_ROOT}/include
CFLAGS = -DUNIT_TEST -x c++ -g -Wall -Wnarrowing -Wconversion-null -I. -I../ -I${BTSTACK_ROOT}/src -I${BTSTACK_ROOT}/ble -I${BTSTACK_ROOT}/include
LDFLAGS += -lCppUTest -lCppUTestExt
VPATH += ${BTSTACK_ROOT}/src

View File

@ -24,8 +24,8 @@ uint16_t get_gatt_client_handle(void){
}
void mock_simulate_command_complete(const hci_cmd_t *cmd){
uint8_t packet[] = {HCI_EVENT_COMMAND_COMPLETE, 4, 1, cmd->opcode & 0xff, cmd->opcode >> 8, 0};
registered_hci_event_handler(HCI_EVENT_PACKET, 0, (uint8_t *)&packet, sizeof(packet));
uint8_t packet[] = {HCI_EVENT_COMMAND_COMPLETE, 4, 1, (uint8_t) (cmd->opcode & 0xff), (uint8_t) (cmd->opcode >> 8), 0};
registered_hci_event_handler(HCI_EVENT_PACKET, NULL, (uint8_t *)&packet, sizeof(packet));
}
void mock_simulate_hci_state_working(void){

View File

@ -43,7 +43,7 @@ COMMON_OBJ = $(COMMON:.c=.o)
MOCK_OBJ = $(MOCK:.c=.o)
# CC = gcc-fsf-4.9
CFLAGS = -g -Wall \
CFLAGS = -g -Wall -Wmissing-prototype -Wnarrowing \
-I. \
-I.. \
-I${BTSTACK_ROOT}/src

View File

@ -361,8 +361,7 @@ void packet_handler(uint8_t * event, uint16_t event_size){
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){
&& event[2] != HFP_SUBEVENT_TRANSMIT_DTMF_CODES){
printf("ERROR, status: %u\n", event[3]);
return;
}
@ -417,11 +416,12 @@ void packet_handler(uint8_t * event, uint16_t event_size){
TEST_GROUP(HFPClient){
void setup(void){
hfp_ag_init(rfcomm_channel_nr, supported_features_with_codec_negotiation,
codecs, sizeof(codecs),
ag_indicators, ag_indicators_nr,
hf_indicators, hf_indicators_nr,
call_hold_services, call_hold_services_nr);
hfp_ag_init(rfcomm_channel_nr);
hfp_ag_init_supported_features(supported_features_with_codec_negotiation);
hfp_ag_init_codecs(sizeof(codecs), codecs);
hfp_ag_init_ag_indicators(ag_indicators_nr, ag_indicators);
hfp_ag_init_hf_indicators(hf_indicators_nr, hf_indicators);
hfp_ag_init_call_hold_services(call_hold_services_nr, call_hold_services);
}
void teardown(void){

View File

@ -47,12 +47,8 @@
#include "classic/hfp.h"
void hfp_parse(hfp_connection_t * context, uint8_t byte, int isHandsFree);
hfp_generic_status_indicator_t * get_hfp_generic_status_indicators();
void set_hfp_generic_status_indicators(hfp_generic_status_indicator_t * indicators, int indicator_nr);
hfp_ag_indicator_t * get_hfp_ag_indicators(hfp_connection_t * context);
int get_hfp_ag_indicators_nr(hfp_connection_t * context);
void set_hfp_ag_indicators(hfp_ag_indicator_t * indicators, int indicator_nr);
hfp_ag_indicator_t * hfp_ag_get_ag_indicators(hfp_connection_t * hfp_connection);
// static int hf_indicators_nr = 3;
// static hfp_generic_status_indicator_t hf_indicators[] = {
@ -78,8 +74,7 @@ static hfp_connection_t context;
TEST_GROUP(HFPParser){
char packet[200];
int pos;
int offset;
uint16_t pos;
void setup(void){
context.parser_state = HFP_PARSER_CMD_HEADER;
@ -140,13 +135,13 @@ TEST(HFPParser, HFP_AG_ENABLE_INDICATOR_STATUS_UPDATE){
TEST(HFPParser, HFP_AG_ENABLE_INDIVIDUAL_INDICATOR_STATUS_UPDATE){
set_hfp_ag_indicators((hfp_ag_indicator_t *)&hfp_ag_indicators, hfp_ag_indicators_nr);
hfp_ag_init_ag_indicators(hfp_ag_indicators_nr, (hfp_ag_indicator_t *)&hfp_ag_indicators);
context.ag_indicators_nr = hfp_ag_indicators_nr;
memcpy(context.ag_indicators, hfp_ag_indicators, hfp_ag_indicators_nr * sizeof(hfp_ag_indicator_t));
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(hfp_ag_get_ag_indicators(&context)[pos].index, hfp_ag_indicators[pos].index);
CHECK_EQUAL(hfp_ag_get_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);
}
@ -159,18 +154,18 @@ TEST(HFPParser, HFP_AG_ENABLE_INDIVIDUAL_INDICATOR_STATUS_UPDATE){
CHECK_EQUAL(HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE, context.command);
for (pos = 0; pos < hfp_ag_indicators_nr; pos++){
if (get_hfp_ag_indicators(&context)[pos].mandatory){
CHECK_EQUAL(get_hfp_ag_indicators(&context)[pos].enabled, 1);
if (hfp_ag_get_ag_indicators(&context)[pos].mandatory){
CHECK_EQUAL(hfp_ag_get_ag_indicators(&context)[pos].enabled, 1);
CHECK_EQUAL(context.ag_indicators[pos].enabled, 1);
} else {
CHECK_EQUAL(get_hfp_ag_indicators(&context)[pos].enabled, 0);
CHECK_EQUAL(hfp_ag_get_ag_indicators(&context)[pos].enabled, 0);
CHECK_EQUAL(context.ag_indicators[pos].enabled, 0);
}
}
}
TEST(HFPParser, HFP_AG_ENABLE_INDIVIDUAL_INDICATOR_STATUS_UPDATE_OPT_VALUES3){
set_hfp_ag_indicators((hfp_ag_indicator_t *)&hfp_ag_indicators, hfp_ag_indicators_nr);
hfp_ag_init_ag_indicators(hfp_ag_indicators_nr, (hfp_ag_indicator_t *)&hfp_ag_indicators);
context.ag_indicators_nr = hfp_ag_indicators_nr;
memcpy(context.ag_indicators, hfp_ag_indicators, hfp_ag_indicators_nr * sizeof(hfp_ag_indicator_t));
@ -183,15 +178,15 @@ TEST(HFPParser, HFP_AG_ENABLE_INDIVIDUAL_INDICATOR_STATUS_UPDATE_OPT_VALUES3){
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].index, hfp_ag_indicators[pos].index);
CHECK_EQUAL(get_hfp_ag_indicators(&context)[pos].enabled, hfp_ag_indicators[pos].enabled);
CHECK_EQUAL(hfp_ag_get_ag_indicators(&context)[pos].index, hfp_ag_indicators[pos].index);
CHECK_EQUAL(hfp_ag_get_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);
hfp_ag_init_ag_indicators(hfp_ag_indicators_nr, (hfp_ag_indicator_t *)&hfp_ag_indicators);
context.ag_indicators_nr = hfp_ag_indicators_nr;
memcpy(context.ag_indicators, hfp_ag_indicators, hfp_ag_indicators_nr * sizeof(hfp_ag_indicator_t));
@ -204,13 +199,13 @@ TEST(HFPParser, HFP_AG_ENABLE_INDIVIDUAL_INDICATOR_STATUS_UPDATE_OPT_VALUES2){
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(hfp_ag_get_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);
hfp_ag_init_ag_indicators(hfp_ag_indicators_nr, (hfp_ag_indicator_t *)&hfp_ag_indicators);
context.ag_indicators_nr = hfp_ag_indicators_nr;
memcpy(context.ag_indicators, hfp_ag_indicators, hfp_ag_indicators_nr * sizeof(hfp_ag_indicator_t));
@ -223,7 +218,7 @@ TEST(HFPParser, HFP_AG_ENABLE_INDIVIDUAL_INDICATOR_STATUS_UPDATE_OPT_VALUES1){
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(hfp_ag_get_ag_indicators(&context)[pos].enabled, 1);
CHECK_EQUAL(context.ag_indicators[pos].enabled, 1);
}
}

View File

@ -148,8 +148,8 @@ static void user_command(char cmd){
hfp_hf_terminate_call(device_addr);
break;
case 'G':
printf("Reject call.\n");
hfp_hf_reject_call(device_addr);
printf("Reject incoming call.\n");
hfp_hf_reject_incoming_call(device_addr);
break;
case 'g':
printf("Query operator.\n");
@ -169,11 +169,11 @@ static void user_command(char cmd){
break;
case 'j':
printf("Dial #1\n");
hfp_hf_dial_memory(device_addr, (char *)"1");
hfp_hf_dial_memory(device_addr, 1);
break;
case 'J':
printf("Dial #99\n");
hfp_hf_dial_memory(device_addr, (char *)"99");
hfp_hf_dial_memory(device_addr, 99);
break;
case 'k':
printf("Deactivate call waiting notification\n");
@ -337,7 +337,7 @@ void simulate_test_sequence(hfp_test_item_t * test_item){
sscanf(&expected_cmd[7],"%d,%d", &parsed_codecs[0], &parsed_codecs[1]);
new_codecs[0] = parsed_codecs[0];
new_codecs[1] = parsed_codecs[1];
hfp_hf_set_codecs((uint8_t*)new_codecs, 2);
hfp_hf_init_codecs(2, (uint8_t*)new_codecs);
while (has_more_hfp_hf_commands()){
// empty rfcomm payload buffer
get_next_hfp_hf_command();
@ -348,7 +348,9 @@ void simulate_test_sequence(hfp_test_item_t * test_item){
sscanf(&expected_cmd[8],"%d", &supported_features);
printf("Call hfp_hf_init with SF %d\n", supported_features);
hfp_hf_release_service_level_connection(device_addr);
hfp_hf_init(rfcomm_channel_nr, supported_features, indicators, sizeof(indicators)/sizeof(uint16_t), 1);
hfp_hf_init_supported_features(supported_features);
user_command('a');
while (has_more_hfp_hf_commands()){
// empty rfcomm payload buffer
@ -391,14 +393,6 @@ void simulate_test_sequence(hfp_test_item_t * test_item){
void packet_handler(uint8_t * event, uint16_t event_size){
if (event[0] != HCI_EVENT_HFP_META) return;
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]);
return;
}
switch (event[2]) {
case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED:
@ -440,10 +434,10 @@ void packet_handler(uint8_t * event, uint16_t event_size){
printf("HFP AG HFP_SUBEVENT_COMPLETE.\n");
break;
case HFP_SUBEVENT_AG_INDICATOR_STATUS_CHANGED:
printf("AG_INDICATOR_STATUS_CHANGED, AG indicator '%s' (index: %d) to: %d\n", (const char*) &event[6], event[4], event[5]);
printf("AG_INDICATOR_STATUS_CHANGED, AG indicator '%s' (index: %d) to: %d\n", (const char*) &event[5], event[3], event[4]);
break;
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[3], event[4], (char *) &event[5]);
break;
case HFP_SUBEVENT_EXTENDED_AUDIO_GATEWAY_ERROR:
if (event[4])
@ -478,8 +472,11 @@ TEST_GROUP(HFPClient){
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));
hfp_hf_init(rfcomm_channel_nr);
hfp_hf_init_supported_features(supported_features_with_codec_negotiation);
hfp_hf_init_hf_indicators(sizeof(indicators)/sizeof(uint16_t), indicators);
hfp_hf_init_codecs(sizeof(codecs), codecs);
}
void teardown(void){

View File

@ -239,7 +239,7 @@ TEST(HFPParser, HFP_HF_EXTENDED_AUDIO_GATEWAY_ERROR){
}
CHECK_EQUAL(context.command, HFP_CMD_EXTENDED_AUDIO_GATEWAY_ERROR);
CHECK_EQUAL(context.extended_audio_gateway_error, HFP_CME_ERROR_NO_NETWORK_SERVICE);
CHECK_EQUAL(context.extended_audio_gateway_error_value, HFP_CME_ERROR_NO_NETWORK_SERVICE);
}

View File

@ -599,8 +599,7 @@ static void packet_handler(uint8_t * event, uint16_t event_size){
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){
&& event[2] != HFP_SUBEVENT_TRANSMIT_DTMF_CODES){
printf("ERROR, status: %u\n", event[3]);
return;
}
@ -665,10 +664,13 @@ int btstack_main(int argc, const char * argv[]){
l2cap_init();
rfcomm_init();
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,
hf_indicators, hf_indicators_nr,
call_hold_services, call_hold_services_nr);
hfp_ag_init(rfcomm_channel_nr);
hfp_ag_init_supported_features(0x3ef | (1<<HFP_AGSF_HF_INDICATORS) | (1<<HFP_AGSF_ESCO_S4));
hfp_ag_init_codecs(sizeof(codecs), codecs);
hfp_ag_init_ag_indicators(ag_indicators_nr, ag_indicators);
hfp_ag_init_hf_indicators(hf_indicators_nr, hf_indicators);
hfp_ag_init_call_hold_services(call_hold_services_nr, call_hold_services);
hfp_ag_set_subcriber_number_information(&subscriber_number, 1);
hfp_ag_register_packet_handler(packet_handler);

View File

@ -113,7 +113,7 @@ static void show_usage(void){
printf("F - Hangup call\n");
printf("g - query network operator name\n");
printf("G - reject call\n");
printf("G - reject incoming call\n");
printf("i - dial 1234567\n");
printf("I - dial 7654321\n");
@ -252,8 +252,8 @@ static int stdin_process(struct btstack_data_source *ds){
break;
case 'G':
log_info("USER:\'%c\'", cmd);
printf("Reject call.\n");
hfp_hf_reject_call(device_addr);
printf("Reject incoming call.\n");
hfp_hf_reject_incoming_call(device_addr);
break;
case 'g':
log_info("USER:\'%c\'", cmd);
@ -278,12 +278,12 @@ static int stdin_process(struct btstack_data_source *ds){
case 'j':
log_info("USER:\'%c\'", cmd);
printf("Dial #1\n");
hfp_hf_dial_memory(device_addr,"1");
hfp_hf_dial_memory(device_addr,1);
break;
case 'J':
log_info("USER:\'%c\'", cmd);
printf("Dial #99\n");
hfp_hf_dial_memory(device_addr,"99");
hfp_hf_dial_memory(device_addr,99);
break;
case 'k':
log_info("USER:\'%c\'", cmd);
@ -467,14 +467,7 @@ static void packet_handler(uint8_t * event, uint16_t event_size){
return;
}
if (event[0] != HCI_EVENT_HFP_META) return;
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]);
return;
}
switch (event[2]) {
case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED:
printf("Service level connection established.\n\n");
@ -500,10 +493,10 @@ static void packet_handler(uint8_t * event, uint16_t event_size){
}
break;
case HFP_SUBEVENT_AG_INDICATOR_STATUS_CHANGED:
printf("AG_INDICATOR_STATUS_CHANGED, AG indicator '%s' (index: %d) to: %d\n", (const char*) &event[6], event[4], event[5]);
printf("AG_INDICATOR_STATUS_CHANGED, AG indicator '%s' (index: %d) to: %d\n", (const char*) &event[5], event[3], event[4]);
break;
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[3], event[4], (char *) &event[5]);
break;
case HFP_SUBEVENT_EXTENDED_AUDIO_GATEWAY_ERROR:
if (event[4])
@ -534,8 +527,10 @@ int btstack_main(int argc, const char * argv[]){
rfcomm_init();
// hfp_hf_init(rfcomm_channel_nr, HFP_DEFAULT_HF_SUPPORTED_FEATURES, codecs, sizeof(codecs), indicators, sizeof(indicators)/sizeof(uint16_t), 1);
hfp_hf_init(rfcomm_channel_nr, 438 | (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_init(rfcomm_channel_nr);
hfp_hf_init_supported_features(438 | (1<<HFP_HFSF_ESCO_S4) | (1<<HFP_HFSF_EC_NR_FUNCTION));
hfp_hf_init_hf_indicators(sizeof(indicators)/sizeof(uint16_t), indicators);
hfp_hf_init_codecs(sizeof(codecs), codecs);
hfp_hf_register_packet_handler(packet_handler);