hfp: report extended AG error result code

This commit is contained in:
Milanka Ringwald 2015-08-20 12:54:47 +02:00
parent d89553ebba
commit 8ca6f17bb0
11 changed files with 388 additions and 81 deletions

View File

@ -625,6 +625,7 @@ extern "C" {
#define HFP_SUBEVENT_COMPLETE 0x03 #define HFP_SUBEVENT_COMPLETE 0x03
#define HFP_SUBEVENT_AG_INDICATOR_STATUS_CHANGED 0x04 #define HFP_SUBEVENT_AG_INDICATOR_STATUS_CHANGED 0x04
#define HFP_SUBEVENT_NETWORK_OPERATOR_CHANGED 0x05 #define HFP_SUBEVENT_NETWORK_OPERATOR_CHANGED 0x05
#define HFP_SUBEVENT_EXTENDED_AUDIO_GATEWAY_ERROR 0x06
// ANCS Client // ANCS Client
#define ANCS_CLIENT_CONNECTED 0xF0 #define ANCS_CLIENT_CONNECTED 0xF0

View File

@ -509,7 +509,7 @@ void process_command(hfp_connection_t * context){
return; return;
} }
if (strncmp((char *)context->line_buffer+offset, HFP_OK, strlen(HFP_OK)) == 0){ if (isHandsFree && strncmp((char *)context->line_buffer+offset, HFP_OK, strlen(HFP_OK)) == 0){
//printf("parsed HFP_CMD_OK \n"); //printf("parsed HFP_CMD_OK \n");
context->command = HFP_CMD_OK; context->command = HFP_CMD_OK;
return; return;
@ -593,6 +593,19 @@ void process_command(hfp_connection_t * context){
context->command = HFP_CMD_TRANSFER_AG_INDICATOR_STATUS; context->command = HFP_CMD_TRANSFER_AG_INDICATOR_STATUS;
return; return;
} }
if (isHandsFree && strncmp((char *)context->line_buffer+offset, HFP_EXTENDED_AUDIO_GATEWAY_ERROR, strlen(HFP_EXTENDED_AUDIO_GATEWAY_ERROR)) == 0){
printf(" process command 1 %s \n", context->line_buffer);
context->command = HFP_CMD_EXTENDED_AUDIO_GATEWAY_ERROR;
return;
}
if (!isHandsFree && strncmp((char *)context->line_buffer+offset, HFP_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR, strlen(HFP_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR)) == 0){
printf(" process command 2 %s \n", context->line_buffer);
context->command = HFP_CMD_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR;
return;
}
printf(" process unknown command 3 %s \n", context->line_buffer);
} }
uint32_t fromBinary(char *s) { uint32_t fromBinary(char *s) {
@ -679,7 +692,7 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte){
int value; int value;
// TODO: handle space inside word // TODO: handle space inside word
if (byte == ' ') return; if (byte == ' ' && context->parser_state > HFP_PARSER_CMD_HEADER) return;
if (!hfp_parser_found_separator(context, byte)){ if (!hfp_parser_found_separator(context, byte)){
hfp_parser_store_byte(context, byte); hfp_parser_store_byte(context, byte);
@ -805,7 +818,16 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte){
break; break;
} }
break; break;
case HFP_CMD_ERROR:
break;
case HFP_CMD_EXTENDED_AUDIO_GATEWAY_ERROR:
context->extended_audio_gateway_error = (uint8_t)atoi((char*)context->line_buffer);
break;
case HFP_CMD_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR:
context->enable_extended_audio_gateway_error_report = (uint8_t)atoi((char*)context->line_buffer);
context->send_ok = 1;
context->extended_audio_gateway_error = 0;
break;
default: default:
break; break;
} }

View File

@ -110,7 +110,8 @@ extern "C" {
#define HFP_GENERIC_STATUS_INDICATOR "+BIND" #define HFP_GENERIC_STATUS_INDICATOR "+BIND"
#define HFP_TRANSFER_AG_INDICATOR_STATUS "+CIEV" // +CIEV: <index>,<value> #define HFP_TRANSFER_AG_INDICATOR_STATUS "+CIEV" // +CIEV: <index>,<value>
#define HFP_QUERY_OPERATOR_SELECTION "+COPS" // +COPS: <mode>,0,<opearator> #define HFP_QUERY_OPERATOR_SELECTION "+COPS" // +COPS: <mode>,0,<opearator>
#define HFP_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR "+CMEE"
#define HFP_EXTENDED_AUDIO_GATEWAY_ERROR "+CME ERROR"
#define HFP_OK "OK" #define HFP_OK "OK"
#define HFP_ERROR "ERROR" #define HFP_ERROR "ERROR"
@ -133,9 +134,49 @@ typedef enum {
HFP_CMD_GENERIC_STATUS_INDICATOR, HFP_CMD_GENERIC_STATUS_INDICATOR,
HFP_CMD_TRANSFER_AG_INDICATOR_STATUS, HFP_CMD_TRANSFER_AG_INDICATOR_STATUS,
HFP_CMD_QUERY_OPERATOR_SELECTION HFP_CMD_QUERY_OPERATOR_SELECTION,
HFP_CMD_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR,
HFP_CMD_EXTENDED_AUDIO_GATEWAY_ERROR
} hfp_command_t; } hfp_command_t;
typedef enum {
HFP_CME_ERROR_AG_FAILURE = 0,
HFP_CME_ERROR_NO_CONNECTION_TO_PHONE,
HFP_CME_ERROR_2,
HFP_CME_ERROR_OPERATION_NOT_ALLOWED,
HFP_CME_ERROR_OPERATION_NOT_SUPPORTED,
HFP_CME_ERROR_PH_SIM_PIN_REQUIRED,
HFP_CME_ERROR_6,
HFP_CME_ERROR_7,
HFP_CME_ERROR_8,
HFP_CME_ERROR_9,
HFP_CME_ERROR_SIM_NOT_INSERTED,
HFP_CME_ERROR_SIM_PIN_REQUIRED,
HFP_CME_ERROR_SIM_PUK_REQUIRED,
HFP_CME_ERROR_SIM_FAILURE,
HFP_CME_ERROR_SIM_BUSY,
HFP_CME_ERROR_15,
HFP_CME_ERROR_INCORRECT_PASSWORD,
HFP_CME_ERROR_SIM_PIN2_REQUIRED,
HFP_CME_ERROR_SIM_PUK2_REQUIRED,
HFP_CME_ERROR_19,
HFP_CME_ERROR_MEMORY_FULL,
HFP_CME_ERROR_INVALID_INDEX,
HFP_CME_ERROR_22,
HFP_CME_ERROR_MEMORY_FAILURE,
HFP_CME_ERROR_TEXT_STRING_TOO_LONG,
HFP_CME_ERROR_INVALID_CHARACTERS_IN_TEXT_STRING,
HFP_CME_ERROR_DIAL_STRING_TOO_LONG,
HFP_CME_ERROR_INVALID_CHARACTERS_IN_DIAL_STRING,
HFP_CME_ERROR_28,
HFP_CME_ERROR_29,
HFP_CME_ERROR_NO_NETWORK_SERVICE,
HFP_CME_ERROR_NETWORK_TIMEOUT,
HFP_CME_ERROR_NETWORK_NOT_ALLOWED_EMERGENCY_CALLS_ONLY
} hfp_cme_error_t;
typedef enum { typedef enum {
HFP_PARSER_CMD_HEADER = 0, HFP_PARSER_CMD_HEADER = 0,
HFP_PARSER_CMD_SEQUENCE, HFP_PARSER_CMD_SEQUENCE,
@ -254,6 +295,8 @@ typedef struct hfp_connection {
// TODO: put these bit flags in a bitmap // TODO: put these bit flags in a bitmap
uint8_t wait_ok; uint8_t wait_ok;
uint8_t send_ok;
uint8_t send_error;
uint8_t keep_separator; uint8_t keep_separator;
@ -264,11 +307,14 @@ typedef struct hfp_connection {
uint8_t retrieve_generic_status_indicators; // HFP_CMD_GENERIC_STATUS_INDICATOR uint8_t retrieve_generic_status_indicators; // HFP_CMD_GENERIC_STATUS_INDICATOR
uint8_t retrieve_generic_status_indicators_state; // HFP_CMD_GENERIC_STATUS_INDICATOR_STATE uint8_t retrieve_generic_status_indicators_state; // HFP_CMD_GENERIC_STATUS_INDICATOR_STATE
uint8_t change_status_update_for_individual_ag_indicators;
uint8_t operator_name_format; uint8_t operator_name_format;
uint8_t operator_name; uint8_t operator_name;
uint8_t operator_name_changed; uint8_t operator_name_changed;
uint8_t change_status_update_for_individual_ag_indicators; uint8_t enable_extended_audio_gateway_error_report;
uint8_t extended_audio_gateway_error;
} hfp_connection_t; } hfp_connection_t;

View File

@ -163,6 +163,12 @@ int hfp_ag_error(uint16_t cid){
return send_str_over_rfcomm(cid, buffer); return send_str_over_rfcomm(cid, buffer);
} }
int hfp_ag_report_extended_audio_gateway_error(uint16_t cid, uint8_t error){
char buffer[20];
sprintf(buffer, "\r\n%s=%d\r\n", HFP_EXTENDED_AUDIO_GATEWAY_ERROR, error);
return send_str_over_rfcomm(cid, buffer);
}
int hfp_ag_retrieve_codec_cmd(uint16_t cid){ int hfp_ag_retrieve_codec_cmd(uint16_t cid){
return hfp_ag_ok(cid); return hfp_ag_ok(cid);
} }
@ -333,17 +339,36 @@ void hfp_run_for_context(hfp_connection_t *context){
//printf(" hfp_run_for_context 1 state %d, command %d\n", context->state, context->command); //printf(" hfp_run_for_context 1 state %d, command %d\n", context->state, context->command);
if (context->state == HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED){ if (context->state == HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED){
if (context->enable_status_update_for_ag_indicators == 1){ if (context->send_ok){
hfp_ag_ok(context->rfcomm_cid);
context->send_ok = 0;
return;
}
if (context->send_error){
hfp_ag_error(context->rfcomm_cid);
context->send_error = 0;
return;
}
if (context->enable_status_update_for_ag_indicators){
int i; int i;
for (i = 0; i < context->ag_indicators_nr; i++){ for (i = 0; i < context->ag_indicators_nr; i++){
if (context->ag_indicators[i].enabled == 0) continue; if (context->ag_indicators[i].enabled == 0) continue;
if (context->ag_indicators[i].status_changed == 0) continue; if (context->ag_indicators[i].status_changed == 0) continue;
hfp_ag_transfer_ag_indicators_status_cmd(context->rfcomm_cid, context->ag_indicators[i]); hfp_ag_transfer_ag_indicators_status_cmd(context->rfcomm_cid, context->ag_indicators[i]);
context->ag_indicators[i].status_changed = 0; context->ag_indicators[i].status_changed = 0;
return; return;
} }
} }
if (context->enable_extended_audio_gateway_error_report){
if (context->extended_audio_gateway_error){
hfp_ag_report_extended_audio_gateway_error(context->rfcomm_cid, context->extended_audio_gateway_error);
context->extended_audio_gateway_error = 0;
return;
}
}
} }
switch(context->command){ switch(context->command){
@ -568,3 +593,16 @@ void hfp_ag_release_service_level_connection(bd_addr_t bd_addr){
hfp_run_for_context(connection); hfp_run_for_context(connection);
} }
void hfp_ag_report_extended_audio_gateway_error_result_code(bd_addr_t bd_addr, hfp_cme_error_t error){
hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr);
if (!connection){
log_error("HFP HF: connection doesn't exist.");
return;
}
connection->extended_audio_gateway_error = 0;
if (!connection->enable_extended_audio_gateway_error_report){
return;
}
connection->extended_audio_gateway_error = error;
hfp_run_for_context(connection);
}

View File

@ -92,6 +92,37 @@ void hfp_ag_establish_service_level_connection(bd_addr_t bd_addr);
*/ */
void hfp_ag_release_service_level_connection(bd_addr_t bd_addr); void hfp_ag_release_service_level_connection(bd_addr_t bd_addr);
/**
* @brief Report 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
*/
void hfp_ag_report_extended_audio_gateway_error_result_code(bd_addr_t bd_addr, hfp_cme_error_t error);
/* API_END */ /* API_END */
#if defined __cplusplus #if defined __cplusplus

View File

@ -200,6 +200,11 @@ int hfp_hs_query_operator_name_cmd(uint16_t cid){
return send_str_over_rfcomm(cid, buffer); return send_str_over_rfcomm(cid, buffer);
} }
int hfp_hs_enable_extended_audio_gateway_error_report_cmd(uint16_t cid, uint8_t enable){
char buffer[20];
sprintf(buffer, "AT%s=%d\r\n", HFP_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR, enable);
return send_str_over_rfcomm(cid, buffer);
}
static void hfp_emit_ag_indicator_event(hfp_callback_t callback, int status, hfp_ag_indicator_t indicator){ static void hfp_emit_ag_indicator_event(hfp_callback_t callback, int status, hfp_ag_indicator_t indicator){
if (!callback) return; if (!callback) return;
@ -289,21 +294,21 @@ static void hfp_run_for_context(hfp_connection_t * context){
case HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED:{ case HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED:{
int i; int i;
for (i = 0; i < context->ag_indicators_nr; i++){ for (i = 0; i < context->ag_indicators_nr; i++){
if (context->ag_indicators[i].status_changed == 1) { if (context->ag_indicators[i].status_changed) {
hfp_emit_ag_indicator_event(hfp_callback, 0, context->ag_indicators[i]); hfp_emit_ag_indicator_event(hfp_callback, 0, context->ag_indicators[i]);
context->ag_indicators[i].status_changed = 0; context->ag_indicators[i].status_changed = 0;
break; break;
} }
} }
if (context->wait_ok == 1) return; if (context->wait_ok) return;
if (context->enable_status_update_for_ag_indicators != 0xFF){ if (context->enable_status_update_for_ag_indicators != 0xFF){
hfp_hs_activate_status_update_for_all_ag_indicators_cmd(context->rfcomm_cid, context->enable_status_update_for_ag_indicators); hfp_hs_activate_status_update_for_all_ag_indicators_cmd(context->rfcomm_cid, context->enable_status_update_for_ag_indicators);
context->wait_ok = 1; context->wait_ok = 1;
break; break;
}; };
if (context->change_status_update_for_individual_ag_indicators == 1){ if (context->change_status_update_for_individual_ag_indicators){
hfp_hs_activate_status_update_for_ag_indicator_cmd(context->rfcomm_cid, hfp_hs_activate_status_update_for_ag_indicator_cmd(context->rfcomm_cid,
context->ag_indicators_status_update_bitmap, context->ag_indicators_status_update_bitmap,
context->ag_indicators_nr); context->ag_indicators_nr);
@ -311,15 +316,23 @@ static void hfp_run_for_context(hfp_connection_t * context){
break; break;
} }
if (context->operator_name_format == 1){ if (context->operator_name_format){
hfp_hs_query_operator_name_format_cmd(context->rfcomm_cid); hfp_hs_query_operator_name_format_cmd(context->rfcomm_cid);
context->wait_ok = 1; context->wait_ok = 1;
break; break;
} }
if (context->operator_name == 1){ if (context->operator_name){
hfp_hs_query_operator_name_cmd(context->rfcomm_cid); hfp_hs_query_operator_name_cmd(context->rfcomm_cid);
context->wait_ok = 1; context->wait_ok = 1;
break;
} }
if (context->enable_extended_audio_gateway_error_report){
hfp_hs_enable_extended_audio_gateway_error_report_cmd(context->rfcomm_cid, context->enable_extended_audio_gateway_error_report);
context->wait_ok = 1;
break;
}
break; break;
} }
default: default:
@ -398,22 +411,28 @@ void handle_switch_on_ok(hfp_connection_t *context){
hfp_emit_event(hfp_callback, HFP_SUBEVENT_COMPLETE, 0); hfp_emit_event(hfp_callback, HFP_SUBEVENT_COMPLETE, 0);
break; break;
}; };
if (context->change_status_update_for_individual_ag_indicators == 1){ if (context->change_status_update_for_individual_ag_indicators == 1){
context->change_status_update_for_individual_ag_indicators = 0; context->change_status_update_for_individual_ag_indicators = 0;
hfp_emit_event(hfp_callback, HFP_SUBEVENT_COMPLETE, 0); hfp_emit_event(hfp_callback, HFP_SUBEVENT_COMPLETE, 0);
break; break;
} }
if (context->operator_name_format == 1){ if (context->operator_name_format){
context->operator_name_format = 0; context->operator_name_format = 0;
context->operator_name = 1; context->operator_name = 1;
break; break;
} }
if (context->operator_name == 1){
if (context->operator_name){
context->operator_name = 0; context->operator_name = 0;
hfp_emit_network_operator_event(hfp_callback, 0, context->network_operator); hfp_emit_network_operator_event(hfp_callback, 0, context->network_operator);
break; break;
} }
if (context->enable_extended_audio_gateway_error_report){
context->enable_extended_audio_gateway_error_report = 0;
break;
}
break; break;
default: default:
@ -435,14 +454,17 @@ static void hfp_handle_rfcomm_event(uint8_t packet_type, uint16_t channel, uint8
hfp_parse(context, packet[pos]); hfp_parse(context, packet[pos]);
if (context->command == HFP_CMD_ERROR){ if (context->command == HFP_CMD_ERROR){
if (context->state == HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED){ context->wait_ok = 0;
context->wait_ok = 0; hfp_emit_event(hfp_callback, HFP_SUBEVENT_COMPLETE, 1);
// TODO: reset state? repeat commands? restore bitmaps? get ERROR codes. return;
hfp_emit_event(hfp_callback, HFP_SUBEVENT_COMPLETE, 1);
} else {
}
} }
if (context->command == HFP_CMD_EXTENDED_AUDIO_GATEWAY_ERROR){
context->wait_ok = 0;
hfp_emit_event(hfp_callback, HFP_SUBEVENT_EXTENDED_AUDIO_GATEWAY_ERROR, context->extended_audio_gateway_error);
context->extended_audio_gateway_error = 0;
return;
}
if (context->command != HFP_CMD_OK) continue; if (context->command != HFP_CMD_OK) continue;
handle_switch_on_ok(context); handle_switch_on_ok(context);
} }
@ -510,7 +532,7 @@ void hfp_hf_enable_status_update_for_all_ag_indicators(bd_addr_t bd_addr, uint8_
log_error("HFP HF: connection doesn't exist."); log_error("HFP HF: connection doesn't exist.");
return; return;
} }
connection->enable_status_update_for_ag_indicators = 1; connection->enable_status_update_for_ag_indicators = enable;
hfp_run_for_context(connection); hfp_run_for_context(connection);
} }
@ -537,3 +559,15 @@ void hfp_hf_query_operator_selection(bd_addr_t bd_addr){
connection->operator_name_format = 1; connection->operator_name_format = 1;
hfp_run_for_context(connection); hfp_run_for_context(connection);
} }
void hfp_hf_enable_report_extended_audio_gateway_error_result_code(bd_addr_t bd_addr, uint8_t enable){
hfp_hf_establish_service_level_connection(bd_addr);
hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr);
if (!connection){
log_error("HFP HF: connection doesn't exist.");
return;
}
connection->enable_extended_audio_gateway_error_report = enable;
hfp_run_for_context(connection);
}

View File

@ -107,6 +107,35 @@ void hfp_hf_enable_status_update_for_individual_ag_indicators(bd_addr_t bd_addr,
*/ */
void hfp_hf_query_operator_selection(bd_addr_t bd_addr); void hfp_hf_query_operator_selection(bd_addr_t bd_addr);
/**
* @brief Enable/disable 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
*/
void hfp_hf_enable_report_extended_audio_gateway_error_result_code(bd_addr_t bd_addr, uint8_t enable);
/* API_END */ /* API_END */

View File

@ -211,6 +211,18 @@ TEST(HFPParser, HFP_AG_HF_QUERY_OPERATOR_SELECTION){
CHECK_EQUAL(context.operator_name_changed, 0); CHECK_EQUAL(context.operator_name_changed, 0);
} }
TEST(HFPParser, HFP_AG_EXTENDED_AUDIO_GATEWAY_ERROR){
sprintf(packet, "\r\nAT%s=1\r\n", HFP_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR);
for (pos = 0; pos < strlen(packet); pos++){
hfp_parse(&context, packet[pos]);
}
CHECK_EQUAL(context.command, HFP_CMD_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR);
CHECK_EQUAL(context.enable_extended_audio_gateway_error_report, 1);
}
int main (int argc, const char * argv[]){ int main (int argc, const char * argv[]){
return CommandLineTestRunner::RunAllTests(argc, argv); return CommandLineTestRunner::RunAllTests(argc, argv);
} }

View File

@ -103,7 +103,6 @@ TEST(HFPParser, HFP_HF_INDICATORS){
} }
offset += snprintf(packet+offset, sizeof(packet)-offset, "\"%s\", (%d, %d)\r\n\r\nOK\r\n", hfp_ag_indicators[pos].name, hfp_ag_indicators[pos].min_range, hfp_ag_indicators[pos].max_range); offset += snprintf(packet+offset, sizeof(packet)-offset, "\"%s\", (%d, %d)\r\n\r\nOK\r\n", hfp_ag_indicators[pos].name, hfp_ag_indicators[pos].min_range, hfp_ag_indicators[pos].max_range);
context.command = HFP_CMD_INDICATOR;
context.retrieve_ag_indicators = 1; context.retrieve_ag_indicators = 1;
context.retrieve_ag_indicators_status = 0; context.retrieve_ag_indicators_status = 0;
@ -161,8 +160,6 @@ TEST(HFPParser, HFP_HF_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES){
TEST(HFPParser, HFP_HF_GENERIC_STATUS_INDICATOR){ TEST(HFPParser, HFP_HF_GENERIC_STATUS_INDICATOR){
sprintf(packet, "\r\n%s:0,1,2,3,4\r\n\r\nOK\r\n", HFP_GENERIC_STATUS_INDICATOR); sprintf(packet, "\r\n%s:0,1,2,3,4\r\n\r\nOK\r\n", HFP_GENERIC_STATUS_INDICATOR);
context.command = HFP_CMD_GENERIC_STATUS_INDICATOR;
context.list_generic_status_indicators = 0; context.list_generic_status_indicators = 0;
context.retrieve_generic_status_indicators = 1; context.retrieve_generic_status_indicators = 1;
context.retrieve_generic_status_indicators_state = 0; context.retrieve_generic_status_indicators_state = 0;
@ -181,7 +178,6 @@ TEST(HFPParser, HFP_HF_GENERIC_STATUS_INDICATOR){
TEST(HFPParser, HFP_HF_GENERIC_STATUS_INDICATOR_STATE){ TEST(HFPParser, HFP_HF_GENERIC_STATUS_INDICATOR_STATE){
sprintf(packet, "\r\n%s:0,1\r\n\r\nOK\r\n", HFP_GENERIC_STATUS_INDICATOR); sprintf(packet, "\r\n%s:0,1\r\n\r\nOK\r\n", HFP_GENERIC_STATUS_INDICATOR);
context.command = HFP_CMD_GENERIC_STATUS_INDICATOR;
context.list_generic_status_indicators = 0; context.list_generic_status_indicators = 0;
context.retrieve_generic_status_indicators = 0; context.retrieve_generic_status_indicators = 0;
context.retrieve_generic_status_indicators_state = 1; context.retrieve_generic_status_indicators_state = 1;
@ -211,7 +207,7 @@ TEST(HFPParser, HFP_HF_AG_INDICATOR_STATUS_UPDATE){
} }
TEST(HFPParser, HFP_HF_AG_QUERY_OPERATOR_SELECTION){ TEST(HFPParser, HFP_HF_AG_QUERY_OPERATOR_SELECTION){
sprintf(packet, "\r\n%s:1,0,sunrise\r\n\r\nOK\r\n", HFP_QUERY_OPERATOR_SELECTION); sprintf(packet, "\r\n%s:1,0,\"sunrise\"\r\n\r\nOK\r\n", HFP_QUERY_OPERATOR_SELECTION);
for (pos = 0; pos < strlen(packet); pos++){ for (pos = 0; pos < strlen(packet); pos++){
hfp_parse(&context, packet[pos]); hfp_parse(&context, packet[pos]);
@ -224,6 +220,27 @@ TEST(HFPParser, HFP_HF_AG_QUERY_OPERATOR_SELECTION){
CHECK_EQUAL( strcmp("sunrise", context.network_operator.name), 0); CHECK_EQUAL( strcmp("sunrise", context.network_operator.name), 0);
} }
TEST(HFPParser, HFP_HF_ERROR){
sprintf(packet, "\r\n%s\r\n", HFP_ERROR);
for (pos = 0; pos < strlen(packet); pos++){
hfp_parse(&context, packet[pos]);
}
CHECK_EQUAL(context.command, HFP_CMD_ERROR);
}
TEST(HFPParser, HFP_HF_EXTENDED_AUDIO_GATEWAY_ERROR){
sprintf(packet, "\r\n%s:%d\r\n", HFP_EXTENDED_AUDIO_GATEWAY_ERROR, HFP_CME_ERROR_NO_NETWORK_SERVICE);
for (pos = 0; pos < strlen(packet); pos++){
hfp_parse(&context, packet[pos]);
}
CHECK_EQUAL(context.command, HFP_CMD_EXTENDED_AUDIO_GATEWAY_ERROR);
CHECK_EQUAL(context.extended_audio_gateway_error, HFP_CME_ERROR_NO_NETWORK_SERVICE);
}
int main (int argc, const char * argv[]){ int main (int argc, const char * argv[]){
return CommandLineTestRunner::RunAllTests(argc, argv); return CommandLineTestRunner::RunAllTests(argc, argv);
} }

View File

@ -71,6 +71,7 @@ const uint32_t hfp_service_buffer[150/4]; // implicit alignment to 4-byte memo
const uint8_t rfcomm_channel_nr = 1; const uint8_t rfcomm_channel_nr = 1;
const char hfp_ag_service_name[] = "BTstack HFP AG Test"; const char hfp_ag_service_name[] = "BTstack HFP AG Test";
static bd_addr_t device_addr;
static bd_addr_t pts_addr = {0x00,0x1b,0xDC,0x07,0x32,0xEF}; static bd_addr_t pts_addr = {0x00,0x1b,0xDC,0x07,0x32,0xEF};
static bd_addr_t speaker = {0x00, 0x21, 0x3C, 0xAC, 0xF7, 0x38}; static bd_addr_t speaker = {0x00, 0x21, 0x3C, 0xAC, 0xF7, 0x38};
static uint8_t codecs[1] = {HFP_CODEC_CVSD}; static uint8_t codecs[1] = {HFP_CODEC_CVSD};
@ -96,37 +97,65 @@ static hfp_generic_status_indicator_t hf_indicators[] = {
{2, 1}, {2, 1},
}; };
uint8_t hfp_connect = 1;
char cmd;
// prototypes // prototypes
static void show_usage(void); static void show_usage();
static void reset_pst_flags(){
hfp_connect = 1;
}
// Testig User Interface // Testig User Interface
static void show_usage(void){ static void show_usage(void){
printf("\n--- Bluetooth HFP Hands-Free (HF) unit Test Console ---\n"); printf("\n--- Bluetooth HFP Hands-Free (HF) unit Test Console ---\n");
printf("---\n"); printf("---\n");
printf("p - establish HFP connection to PTS module\n"); if (hfp_connect){
printf("e - establish HFP connection to local mac\n"); printf("p - establish HFP connection to PTS module\n");
printf("d - release HFP connection\n"); printf("c - establish HFP connection to local mac\n");
} else {
printf("p - release HFP connection to PTS module\n");
printf("c - release HFP connection to local mac\n");
}
printf("d - report AG failure\n");
printf("---\n"); printf("---\n");
printf("Ctrl-c - exit\n"); printf("Ctrl-c - exit\n");
printf("---\n"); printf("---\n");
} }
static int stdin_process(struct data_source *ds){ static int stdin_process(struct data_source *ds){
char buffer; read(ds->fd, &cmd, 1);
read(ds->fd, &buffer, 1); switch (cmd){
switch (buffer){
case 'p': case 'p':
printf("Establishing HFP service level connection to PTS module %s...\n", bd_addr_to_str(pts_addr)); memcpy(device_addr, pts_addr, 6);
hfp_ag_establish_service_level_connection(pts_addr); if (hfp_connect){
printf("Establish HFP service level connection to PTS module %s...\n", bd_addr_to_str(device_addr));
hfp_ag_establish_service_level_connection(device_addr);
} else {
printf("Release HFP service level connection.\n");
hfp_ag_release_service_level_connection(device_addr);
}
hfp_connect = !hfp_connect;
break; break;
case 'e': case 'c':
printf("Establishing HFP service level connection to %s...\n", bd_addr_to_str(speaker)); memcpy(device_addr, speaker, 6);
hfp_ag_establish_service_level_connection(speaker); if (hfp_connect){
printf("Establish HFP service level connection to %s...\n", bd_addr_to_str(device_addr));
hfp_ag_establish_service_level_connection(device_addr);
} else {
printf("Release HFP service level connection.\n");
hfp_ag_release_service_level_connection(device_addr);
}
hfp_connect = !hfp_connect;
break; break;
case 'd': case 'd':
printf("Releasing HFP service level connection.\n"); printf("Report AG failure\n");
hfp_ag_release_service_level_connection(speaker); hfp_ag_report_extended_audio_gateway_error_result_code(device_addr, HFP_CME_ERROR_AG_FAILURE);
break;
default: default:
show_usage(); show_usage();
break; break;
@ -135,23 +164,21 @@ static int stdin_process(struct data_source *ds){
return 0; return 0;
} }
void packet_handler(uint8_t * event, uint16_t event_size){ void packet_handler(uint8_t * event, uint16_t event_size){
if (event[0] != HCI_EVENT_HFP_META) return; if (event[0] != HCI_EVENT_HFP_META) return;
if (event[3]){
printf("ERROR, status: %u\n", event[3]);
return;
}
switch (event[2]) { switch (event[2]) {
case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED: case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED:
if (event[3] == 0){ printf("Service level connection established.\n\n");
printf("Service level connection established.\n\n");
} else {
printf("Service level connection establishment failed with status %u\n", event[3]);
}
break; break;
case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_RELEASED: case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_RELEASED:
if (event[3] == 0){ printf("Service level connection released.\n\n");
printf("Service level connection released.\n\n"); reset_pst_flags();
} else {
printf("Service level connection releasing failed with status %u\n", event[3]);
}
break; break;
default: default:
printf("event not handled %u\n", event[2]); printf("event not handled %u\n", event[2]);

View File

@ -81,21 +81,45 @@ static uint16_t indicators[1] = {0x01};
char cmd; char cmd;
uint8_t hfp_enable_extended_audio_gateway_error_report = 1;
uint8_t hfp_connect = 1;
uint8_t hfp_enable_status_update_for_all_ag_indicators = 1;
// prototypes // prototypes
static void show_usage(); static void show_usage();
static void reset_pst_flags(){
hfp_enable_extended_audio_gateway_error_report = 1;
hfp_connect = 1;
hfp_enable_status_update_for_all_ag_indicators = 1;
}
// Testig User Interface // Testig User Interface
static void show_usage(void){ static void show_usage(void){
printf("\n--- Bluetooth HFP Hands-Free (HF) unit Test Console ---\n"); printf("\n--- Bluetooth HFP Hands-Free (HF) unit Test Console ---\n");
printf("---\n"); printf("---\n");
printf("p - establish HFP connection to PTS module\n"); if (hfp_connect){
printf("e - establish HFP connection to local mac\n"); printf("p - establish HFP connection to PTS module\n");
printf("r - enable registration status update\n"); printf("c - establish HFP connection to local mac\n");
printf("d - release HFP connection\n"); } else {
printf("i - enabling HFP AG registration status update for individual indicators\n"); printf("p - release HFP connection to PTS module\n");
printf("o - query network operator\n"); printf("c - release HFP connection to local mac\n");
}
if (hfp_enable_status_update_for_all_ag_indicators){
printf("d - enable registration status update\n");
} else {
printf("d - disable registration status update\n");
}
printf("e - enable HFP AG registration status update for individual indicators\n");
printf("f - query network operator\n");
if (hfp_enable_extended_audio_gateway_error_report){
printf("g - enable reporting of the extended AG error result code\n");
} else {
printf("g - disable reporting of the extended AG error result code\n");
}
printf("---\n"); printf("---\n");
printf("Ctrl-c - exit\n"); printf("Ctrl-c - exit\n");
printf("---\n"); printf("---\n");
@ -106,31 +130,52 @@ static int stdin_process(struct data_source *ds){
switch (cmd){ switch (cmd){
case 'p': case 'p':
memcpy(device_addr, pts_addr, 6); memcpy(device_addr, pts_addr, 6);
printf("Establishing HFP service level connection to PTS module %s...\n", bd_addr_to_str(device_addr)); if (hfp_connect){
hfp_hf_establish_service_level_connection(device_addr); printf("Establish HFP service level connection to PTS module %s...\n", bd_addr_to_str(device_addr));
hfp_hf_establish_service_level_connection(device_addr);
} else {
printf("Release HFP service level connection.\n");
hfp_hf_release_service_level_connection(device_addr);
}
hfp_connect = !hfp_connect;
break; break;
case 'e': case 'c':
memcpy(device_addr, phone_addr, 6); memcpy(device_addr, phone_addr, 6);
printf("Establishing HFP service level connection to %s...\n", bd_addr_to_str(device_addr)); if (hfp_connect){
hfp_hf_establish_service_level_connection(device_addr); printf("Establish HFP service level connection to %s...\n", bd_addr_to_str(device_addr));
hfp_hf_establish_service_level_connection(device_addr);
} else {
printf("Release HFP service level connection.\n");
hfp_hf_release_service_level_connection(device_addr);
}
hfp_connect = !hfp_connect;
break; break;
case 'd': case 'd':
printf("Releasing HFP service level connection.\n"); if (hfp_enable_status_update_for_all_ag_indicators){
hfp_hf_release_service_level_connection(device_addr); printf("Enable HFP AG registration status update.\n");
} else {
printf("Disable HFP AG registration status update.\n");
}
hfp_hf_enable_status_update_for_all_ag_indicators(device_addr, hfp_enable_status_update_for_all_ag_indicators);
hfp_enable_status_update_for_all_ag_indicators = !hfp_enable_status_update_for_all_ag_indicators;
break; break;
case 'r': case 'e':
printf("Enabling HFP AG registration status update.\n"); printf("Enable HFP AG registration status update for individual indicators.\n");
hfp_hf_enable_status_update_for_all_ag_indicators(device_addr, 1);
break;
case 'i':
printf("Enabling HFP AG registration status update for individual indicators.\n");
hfp_hf_enable_status_update_for_individual_ag_indicators(device_addr, 63); hfp_hf_enable_status_update_for_individual_ag_indicators(device_addr, 63);
break; break;
case 'o': case 'f':
printf("Query network operator.\n"); printf("Query network operator.\n");
hfp_hf_query_operator_selection(device_addr); hfp_hf_query_operator_selection(device_addr);
break; break;
case 'g':
if (hfp_enable_extended_audio_gateway_error_report){
printf("Enable reporting of the extended AG error result code.\n");
} else {
printf("Disable reporting of the extended AG error result code.\n");
}
hfp_hf_enable_report_extended_audio_gateway_error_result_code(device_addr, hfp_enable_extended_audio_gateway_error_report);
hfp_enable_extended_audio_gateway_error_report = !hfp_enable_extended_audio_gateway_error_report;
break;
default: default:
show_usage(); show_usage();
break; break;
@ -142,8 +187,8 @@ static int stdin_process(struct data_source *ds){
void packet_handler(uint8_t * event, uint16_t event_size){ void packet_handler(uint8_t * event, uint16_t event_size){
if (event[0] != HCI_EVENT_HFP_META) return; if (event[0] != HCI_EVENT_HFP_META) return;
if (event[3]){ if (event[3] && event[2] != HFP_SUBEVENT_EXTENDED_AUDIO_GATEWAY_ERROR){
printf("Command \'%c\' failed with status %u\n", cmd, event[3]); printf("ERROR, status: %u\n", event[3]);
return; return;
} }
switch (event[2]) { switch (event[2]) {
@ -152,13 +197,14 @@ void packet_handler(uint8_t * event, uint16_t event_size){
break; break;
case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_RELEASED: case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_RELEASED:
printf("Service level connection released.\n\n"); printf("Service level connection released.\n\n");
reset_pst_flags();
break; break;
case HFP_SUBEVENT_COMPLETE: case HFP_SUBEVENT_COMPLETE:
switch (cmd){ switch (cmd){
case 'r': case 'd':
printf("HFP AG registration status update enabled.\n"); printf("HFP AG registration status update enabled.\n");
break; break;
case 'i': case 'e':
printf("HFP AG registration status update for individual indicators set.\n"); printf("HFP AG registration status update for individual indicators set.\n");
default: default:
break; break;
@ -168,7 +214,11 @@ void packet_handler(uint8_t * event, uint16_t event_size){
printf("AG_INDICATOR_STATUS_CHANGED, AG indicator index: %d, status: %d\n", event[4], event[5]); printf("AG_INDICATOR_STATUS_CHANGED, AG indicator index: %d, status: %d\n", event[4], event[5]);
break; break;
case HFP_SUBEVENT_NETWORK_OPERATOR_CHANGED: case HFP_SUBEVENT_NETWORK_OPERATOR_CHANGED:
printf("HFP_SUBEVENT_NETWORK_OPERATOR_CHANGED, operator mode: %d, format: %d, name: %s\n", event[4], event[5], (char *) &event[6]); printf("NETWORK_OPERATOR_CHANGED, operator mode: %d, format: %d, name: %s\n", event[4], event[5], (char *) &event[6]);
break;
case HFP_SUBEVENT_EXTENDED_AUDIO_GATEWAY_ERROR:
if (event[4])
printf("EXTENDED_AUDIO_GATEWAY_ERROR_REPORT, status : %d\n", event[3]);
break; break;
default: default:
printf("event not handled %u\n", event[2]); printf("event not handled %u\n", event[2]);