This commit is contained in:
Matthias Ringwald 2015-07-30 23:28:59 +02:00
commit d11d38848c
7 changed files with 522 additions and 224 deletions

160
src/hfp.c
View File

@ -121,8 +121,8 @@ int send_str_over_rfcomm(uint16_t cid, char * command){
return err;
}
void join(char * buffer, int buffer_size, uint8_t * values, int values_nr){
if (buffer_size < values_nr * 3) return;
int join(char * buffer, int buffer_size, uint8_t * values, int values_nr){
if (buffer_size < values_nr * 3) return 0;
int i;
int offset = 0;
for (i = 0; i < values_nr-1; i++) {
@ -131,9 +131,7 @@ void join(char * buffer, int buffer_size, uint8_t * values, int values_nr){
if (i<values_nr){
offset += snprintf(buffer+offset, buffer_size-offset, "%d", values[i]);
}
offset += snprintf(buffer+offset, buffer_size-offset, "\r\n");
buffer[offset] = 0;
return offset;
}
@ -188,8 +186,8 @@ static hfp_connection_t * create_hfp_connection_context(){
context->negotiated_codec = HFP_CODEC_CVSD;
context->remote_supported_features = 0;
context->remote_indicators_update_enabled = 0;
context->remote_indicators_nr = 0;
context->remote_indicators_status = 0;
context->ag_indicators_nr = 0;
linked_list_add(&hfp_connections, (linked_item_t*)context);
return context;
@ -413,6 +411,140 @@ hfp_connection_t * hfp_handle_hci_event(uint8_t packet_type, uint8_t *packet, ui
return context;
}
void update_command(hfp_connection_t * context);
void hfp_parse(hfp_connection_t * context, uint8_t byte){
int i;
if (byte == ' ') return;
if ( (byte == '\n' || byte == '\r') && context->parser_state > HFP_PARSER_CMD_SEQUENCE) return;
switch (context->parser_state){
case HFP_PARSER_CMD_HEADER: // header
if (byte == ':'){
context->parser_state = HFP_PARSER_CMD_SEQUENCE;
context->line_buffer[context->line_size] = 0;
context->line_size = 0;
update_command(context);
return;
}
if (byte == '\n' || byte == '\r'){
// received OK
context->line_buffer[context->line_size] = 0;
context->line_size = 0;
update_command(context);
return;
}
context->line_buffer[context->line_size++] = byte;
break;
case HFP_PARSER_CMD_SEQUENCE: // parse comma separated sequence, ignore breacktes
if (byte == '"'){ // indicators
context->parser_state = HFP_PARSER_CMD_INDICATOR_NAME;
context->line_size = 0;
break;
}
if (byte == '(' ){ // tuple separated mit comma
break;
}
if (byte == ',' || byte == '\n' || byte == '\r' || byte == ')'){
context->line_buffer[context->line_size] = 0;
context->line_size = 0;
switch (context->state){
case HFP_W4_EXCHANGE_SUPPORTED_FEATURES:
context->remote_supported_features = atoi((char *)&context->line_buffer[0]);
for (i=0; i<16; i++){
if (get_bit(context->remote_supported_features,i)){
printf("AG supported feature: %s\n", hfp_ag_feature(i));
}
}
context->command = HFP_CMD_NONE;
context->parser_state = HFP_PARSER_CMD_HEADER;
break;
case HFP_W4_RETRIEVE_INDICATORS:
break;
case HFP_W4_RETRIEVE_INDICATORS_STATUS:
printf("Indicator with status: %s\n", context->line_buffer);
break;
case HFP_W4_RETRIEVE_CAN_HOLD_CALL:
printf("Support call hold: %s\n", context->line_buffer);
break;
case HFP_W4_RETRIEVE_GENERIC_STATUS_INDICATORS: // comma separated ints
printf("Generic status indicator: %s\n", context->line_buffer);
break;
case HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS:
printf("Generic status indicator: %s, ", context->line_buffer);
context->parser_state = HFP_PARSER_CMD_INITITAL_STATE_GENERIC_STATUS_INDICATORS;
break;
default:
break;
}
if (byte == '\n' || byte == '\r'){
context->command = HFP_CMD_NONE;
context->parser_state = HFP_PARSER_CMD_HEADER;
break;
}
if (byte == ')' && context->state == HFP_W4_RETRIEVE_CAN_HOLD_CALL){ // tuple separated mit comma
context->command = HFP_CMD_NONE;
context->parser_state = HFP_PARSER_CMD_HEADER;
break;
}
break;
}
context->line_buffer[context->line_size++] = byte;
break;
case HFP_PARSER_CMD_INITITAL_STATE_GENERIC_STATUS_INDICATORS:
if (byte == '\n' || byte == '\r'){
context->line_buffer[context->line_size] = 0;
context->line_size = 0;
context->command = HFP_CMD_NONE;
context->parser_state = HFP_PARSER_CMD_HEADER;
printf("status %s [0-dissabled, 1-enabled]\n", context->line_buffer);
break;
}
break;
case HFP_PARSER_CMD_INDICATOR_NAME: // parse indicator name
if (byte == '"'){
context->line_buffer[context->line_size] = 0;
printf("Indicator %d: %s (", context->ag_indicators_nr, context->line_buffer);
context->line_size = 0;
break;
}
if (byte == '('){ // parse indicator range
context->parser_state = HFP_PARSER_CMD_INDICATOR_MIN_RANGE;
break;
}
context->line_buffer[context->line_size++] = byte;
break;
case HFP_PARSER_CMD_INDICATOR_MIN_RANGE:
if (byte == ',' || byte == '-'){ // end min_range
context->parser_state = HFP_PARSER_CMD_INDICATOR_MAX_RANGE;
context->line_buffer[context->line_size] = 0;
printf("%d, ", atoi((char *)&context->line_buffer[0]));
context->line_size = 0;
break;
}
// min. range
context->line_buffer[context->line_size++] = byte;
break;
case HFP_PARSER_CMD_INDICATOR_MAX_RANGE:
if (byte == ')'){ // end max_range
context->parser_state = HFP_PARSER_CMD_SEQUENCE;
context->line_buffer[context->line_size] = 0;
printf("%d)\n", atoi((char *)&context->line_buffer[0]));
context->line_size = 0;
context->ag_indicators_nr+=1;
break;
}
//
context->line_buffer[context->line_size++] = byte;
break;
}
}
void hfp_init(uint16_t rfcomm_channel_nr){
rfcomm_register_service_internal(NULL, rfcomm_channel_nr, 0xffff);
sdp_query_rfcomm_register_callback(handle_query_rfcomm_event, NULL);
@ -447,3 +579,17 @@ void hfp_set_codec(hfp_connection_t * context, uint8_t *packet, uint16_t size){
}
printf("Negotiated Codec 0x%02x\n", context->negotiated_codec);
}
// UTILS
int get_bit(uint16_t bitmap, int position){
return (bitmap >> position) & 1;
}
int store_bit(uint32_t bitmap, int position, uint8_t value){
if (value){
bitmap |= 1 << position;
} else {
bitmap &= ~ (1 << position);
}
return bitmap;
}

View File

@ -97,8 +97,9 @@ extern "C" {
#define HFP_DEFAULT_AG_SUPPORTED_FEATURES 0x0009
#define HFP_MAX_NUM_CODECS 20
#define HFP_MAX_NUM_INDICATORS 20
#define HFP_MAX_INDICATOR_DESC_SIZE 200 // TODO: change to 10
#define HFP_MAX_NUM_AG_INDICATORS 20
#define HFP_MAX_NUM_HF_INDICATORS 20
#define HFP_MAX_INDICATOR_DESC_SIZE 10
#define HFP_SUPPORTED_FEATURES "+BRSF"
#define HFP_AVAILABLE_CODECS "+BAC"
@ -175,6 +176,19 @@ typedef enum {
typedef void (*hfp_callback_t)(uint8_t * event, uint16_t event_size);
typedef struct{
uint16_t uuid;
uint8_t state;
uint8_t initial_state;
} hfp_hf_indicator_t;
typedef struct{
uint8_t index;
char name[HFP_MAX_INDICATOR_DESC_SIZE];
uint8_t min_range;
uint8_t max_range;
uint8_t status;
} hfp_ag_indicator_t;
typedef struct hfp_connection {
linked_item_t item;
@ -191,19 +205,13 @@ typedef struct hfp_connection {
uint16_t rfcomm_channel_nr;
uint16_t rfcomm_cid;
int ag_indicators_nr;
// Retrieved during connection setup, not used yet
uint8_t negotiated_codec;
uint32_t remote_supported_features;
uint8_t remote_indicators_update_enabled;
int remote_indicators_nr;
char remote_indicators[20][HFP_MAX_INDICATOR_DESC_SIZE];
int remote_indicators_range[20][2];
uint32_t remote_indicators_status;
uint8_t remote_hf_indicators_nr;
uint16_t remote_hf_indicators[20];
uint32_t remote_hf_indicators_status;
hfp_callback_t callback;
} hfp_connection_t;
@ -217,10 +225,13 @@ void hfp_connect(bd_addr_t bd_addr, uint16_t service_uuid);
hfp_connection_t * provide_hfp_connection_context_for_rfcomm_cid(uint16_t cid);
linked_list_t * hfp_get_connections();
void hfp_parse(hfp_connection_t * context, uint8_t byte);
// TODO: move to utils
int send_str_over_rfcomm(uint16_t cid, char * command);
void join(char * buffer, int buffer_size, uint8_t * values, int values_nr);
int join(char * buffer, int buffer_size, uint8_t * values, int values_nr);
int get_bit(uint16_t bitmap, int position);
int store_bit(uint32_t bitmap, int position, uint8_t value);
const char * hfp_hf_feature(int index);
const char * hfp_ag_feature(int index);

View File

@ -62,10 +62,19 @@
#include "hfp_ag.h"
static const char default_hfp_ag_service_name[] = "Voice gateway";
static uint16_t hfp_supported_features = HFP_DEFAULT_HF_SUPPORTED_FEATURES;
static uint16_t hfp_supported_features = HFP_DEFAULT_AG_SUPPORTED_FEATURES;
static uint8_t hfp_codecs_nr = 0;
static uint8_t hfp_codecs[HFP_MAX_NUM_CODECS];
static uint8_t hfp_hf_indicators_nr = 0;
static hfp_hf_indicator_t hfp_hf_indicators[HFP_MAX_NUM_HF_INDICATORS];
static int hfp_ag_indicators_nr = 0;
static hfp_ag_indicator_t hfp_ag_indicators[HFP_MAX_NUM_AG_INDICATORS];
static int hfp_ag_call_hold_services_nr = 0;
static char *hfp_ag_call_hold_services[6];
static void packet_handler(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
void hfp_ag_create_service(uint8_t * service, int rfcomm_channel_nr, const char * name, uint8_t ability_to_reject_call, uint16_t supported_features){
@ -82,54 +91,295 @@ void hfp_ag_create_service(uint8_t * service, int rfcomm_channel_nr, const char
*/
}
static void hfp_run(hfp_connection_t * connection){
if (!connection) return;
int hfp_ag_exchange_supported_features_cmd(uint16_t cid){
char buffer[20];
sprintf(buffer, "%s=%d\r\nOK\r\n", HFP_SUPPORTED_FEATURES, hfp_supported_features);
// printf("exchange_supported_features %s\n", buffer);
return send_str_over_rfcomm(cid, buffer);
}
int hfp_ag_ok(uint16_t cid){
char buffer[5];
sprintf(buffer, "OK\r\n");
return send_str_over_rfcomm(cid, buffer);
}
int hfp_ag_retrieve_codec_cmd(uint16_t cid){
return hfp_ag_ok(cid);
}
int hfp_ag_indicators_join(char * buffer, int buffer_size){
if (buffer_size < hfp_ag_indicators_nr * (1 + sizeof(hfp_ag_indicator_t))) return 0;
int i;
int offset = 0;
for (i = 0; i < hfp_ag_indicators_nr-1; i++) {
offset += snprintf(buffer+offset, buffer_size-offset, "\"%s\",(%d,%d),", hfp_ag_indicators[i].name, hfp_ag_indicators[i].min_range, hfp_ag_indicators[i].max_range);;
}
if (i<hfp_ag_indicators_nr){
offset += snprintf(buffer+offset, buffer_size-offset, "\"%s\",(%d,%d)", hfp_ag_indicators[i].name, hfp_ag_indicators[i].min_range, hfp_ag_indicators[i].max_range);
}
return offset;
}
int hfp_hf_indicators_join(char * buffer, int buffer_size){
if (buffer_size < hfp_ag_indicators_nr * 3) return 0;
int i;
int offset = 0;
for (i = 0; i < hfp_ag_indicators_nr-1; i++) {
offset += snprintf(buffer+offset, buffer_size-offset, "%d,", hfp_hf_indicators[i].uuid);
}
if (i<hfp_ag_indicators_nr){
offset += snprintf(buffer+offset, buffer_size-offset, "%d,", hfp_hf_indicators[i].uuid);
}
return offset;
}
int hfp_hf_indicators_initial_status_join(char * buffer, int buffer_size){
if (buffer_size < hfp_hf_indicators_nr * 3) return 0;
int i;
int offset = 0;
for (i = 0; i < hfp_hf_indicators_nr; i++) {
offset += snprintf(buffer+offset, buffer_size-offset, "%s=%d,%d\r\n", HFP_GENERIC_STATUS_INDICATOR, hfp_hf_indicators[i].uuid, hfp_hf_indicators[i].initial_state);
}
return offset;
}
int hfp_ag_indicators_status_join(char * buffer, int buffer_size){
if (buffer_size < hfp_ag_indicators_nr * 3) return 0;
int i;
int offset = 0;
for (i = 0; i < hfp_ag_indicators_nr-1; i++) {
offset += snprintf(buffer+offset, buffer_size-offset, "%d,", hfp_ag_indicators[i].status);
}
if (i<hfp_ag_indicators_nr){
offset += snprintf(buffer+offset, buffer_size-offset, "%d", hfp_ag_indicators[i].status);
}
return offset;
}
int hfp_ag_call_services_join(char * buffer, int buffer_size){
if (buffer_size < hfp_ag_call_hold_services_nr * 3) return 0;
int i;
int offset = 0;
for (i = 0; i < hfp_ag_call_hold_services_nr-1; i++) {
offset += snprintf(buffer+offset, buffer_size-offset, "%s,", hfp_ag_call_hold_services[i]);
}
if (i<hfp_ag_call_hold_services_nr){
offset += snprintf(buffer+offset, buffer_size-offset, "%s", hfp_ag_call_hold_services[i]);
}
return offset;
}
int hfp_ag_retrieve_indicators_cmd(uint16_t cid){
char buffer[150];
int offset = snprintf(buffer, sizeof(buffer), "%s=", HFP_INDICATOR);
offset += hfp_ag_indicators_join(buffer+offset, sizeof(buffer)-offset);
offset += snprintf(buffer+offset, sizeof(buffer)-offset, "\r\nOK\r\n");
buffer[offset] = 0;
return send_str_over_rfcomm(cid, buffer);
}
int hfp_ag_retrieve_indicators_status_cmd(uint16_t cid){
char buffer[20];
int offset = snprintf(buffer, sizeof(buffer), "%s=", HFP_INDICATOR);
offset += hfp_ag_indicators_status_join(buffer+offset, sizeof(buffer)-offset);
offset += snprintf(buffer+offset, sizeof(buffer)-offset, "\r\nOK\r\n");
buffer[offset] = 0;
return send_str_over_rfcomm(cid, buffer);
}
int hfp_ag_toggle_indicator_status_update_cmd(uint16_t cid, uint8_t activate){
// AT%s=3,0,0,%d\r\n
return hfp_ag_ok(cid);
}
int hfp_ag_retrieve_can_hold_call_cmd(uint16_t cid){
char buffer[20];
int offset = snprintf(buffer, sizeof(buffer), "%s=", HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES);
offset += hfp_ag_call_services_join(buffer+offset, sizeof(buffer)-offset);
offset += snprintf(buffer+offset, sizeof(buffer)-offset, "\r\nOK\r\n");
buffer[offset] = 0;
return send_str_over_rfcomm(cid, buffer);
}
int hfp_ag_list_supported_generic_status_indicators_cmd(uint16_t cid){
return hfp_ag_ok(cid);
}
int hfp_ag_retrieve_supported_generic_status_indicators_cmd(uint16_t cid){
char buffer[30];
int offset = snprintf(buffer, sizeof(buffer), "%s=", HFP_GENERIC_STATUS_INDICATOR);
offset += hfp_hf_indicators_join(buffer+offset, sizeof(buffer)-offset);
offset += snprintf(buffer+offset, sizeof(buffer)-offset, "\r\nOK\r\n");
buffer[offset] = 0;
return send_str_over_rfcomm(cid, buffer);
}
int hfp_ag_list_initital_supported_generic_status_indicators_cmd(uint16_t cid){
char buffer[30];
int offset = hfp_hf_indicators_initial_status_join(buffer, sizeof(buffer));
offset += snprintf(buffer+offset, sizeof(buffer)-offset, "OK\r\n");
buffer[offset] = 0;
return send_str_over_rfcomm(cid, buffer);
}
static void hfp_run_for_context(hfp_connection_t * connection){
if (!connection) return;
// printf("hfp send cmd: context %p, RFCOMM cid %u \n", connection, connection->rfcomm_cid );
if (!rfcomm_can_send_packet_now(connection->rfcomm_cid)) return;
switch (connection->state){
case HFP_EXCHANGE_SUPPORTED_FEATURES:
hfp_ag_exchange_supported_features_cmd(connection->rfcomm_cid);
connection->state = HFP_W4_EXCHANGE_SUPPORTED_FEATURES;
break;
case HFP_NOTIFY_ON_CODECS:
hfp_ag_retrieve_codec_cmd(connection->rfcomm_cid);
connection->state = HFP_W4_NOTIFY_ON_CODECS;
break;
case HFP_RETRIEVE_INDICATORS:
hfp_ag_retrieve_indicators_cmd(connection->rfcomm_cid);
connection->state = HFP_W4_RETRIEVE_INDICATORS;
break;
case HFP_RETRIEVE_INDICATORS_STATUS:
hfp_ag_retrieve_indicators_status_cmd(connection->rfcomm_cid);
connection->state = HFP_W4_RETRIEVE_INDICATORS_STATUS;
break;
case HFP_ENABLE_INDICATORS_STATUS_UPDATE:
hfp_ag_toggle_indicator_status_update_cmd(connection->rfcomm_cid, 1);
connection->state = HFP_W4_ENABLE_INDICATORS_STATUS_UPDATE;
break;
case HFP_RETRIEVE_CAN_HOLD_CALL:
hfp_ag_retrieve_can_hold_call_cmd(connection->rfcomm_cid);
connection->state = HFP_W4_RETRIEVE_CAN_HOLD_CALL;
break;
case HFP_LIST_GENERIC_STATUS_INDICATORS:
hfp_ag_list_supported_generic_status_indicators_cmd(connection->rfcomm_cid);
connection->state = HFP_W4_LIST_GENERIC_STATUS_INDICATORS;
break;
case HFP_RETRIEVE_GENERIC_STATUS_INDICATORS:
hfp_ag_retrieve_supported_generic_status_indicators_cmd(connection->rfcomm_cid);
connection->state = HFP_W4_RETRIEVE_GENERIC_STATUS_INDICATORS;
break;
case HFP_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS:
hfp_ag_list_initital_supported_generic_status_indicators_cmd(connection->rfcomm_cid);
connection->state = HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS;
break;
case HFP_ACTIVE:
printf("HFP_ACTIVE\n");
break;
default:
break;
}
}
hfp_connection_t * hfp_handle_rfcomm_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
hfp_connection_t * context = provide_hfp_connection_context_for_rfcomm_cid(channel);
if (!context) return NULL;
while (size > 0 && (packet[0] == '\n' || packet[0] == '\r')){
size--;
packet++;
void update_command(hfp_connection_t * context){
context->command = HFP_CMD_NONE;
if (strncmp((char *)context->line_buffer+2, HFP_SUPPORTED_FEATURES, strlen(HFP_SUPPORTED_FEATURES)) == 0){
printf("Received AT+BRSF\n");
context->command = HFP_CMD_SUPPORTED_FEATURES;
return;
}
return context;
if (strncmp((char *)context->line_buffer+2, HFP_INDICATOR, strlen(HFP_INDICATOR)) == 0){
printf("Received AT+CIND\n");
context->command = HFP_CMD_INDICATOR;
return;
}
if (strncmp((char *)context->line_buffer+2, HFP_AVAILABLE_CODECS, strlen(HFP_AVAILABLE_CODECS)) == 0){
printf("Received AT+BAC\n");
context->command = HFP_CMD_AVAILABLE_CODECS;
return;
}
if (strncmp((char *)context->line_buffer+2, HFP_ENABLE_INDICATOR_STATUS_UPDATE, strlen(HFP_ENABLE_INDICATOR_STATUS_UPDATE)) == 0){
printf("Received AT+CMER\n");
context->command = HFP_CMD_ENABLE_INDICATOR_STATUS_UPDATE;
return;
}
if (strncmp((char *)context->line_buffer+2, HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES, strlen(HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES)) == 0){
printf("Received AT+CHLD\n");
context->command = HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES;
return;
}
if (strncmp((char *)context->line_buffer+2, HFP_GENERIC_STATUS_INDICATOR, strlen(HFP_GENERIC_STATUS_INDICATOR)) == 0){
printf("Received AT+BIND\n");
context->command = HFP_CMD_GENERIC_STATUS_INDICATOR;
return;
}
}
void handle_switch_on_ok(hfp_connection_t *context){
printf("handle switch on OK\n");
switch (context->state){
default:
break;
}
// done
context->command = HFP_CMD_NONE;
}
static void hfp_handle_rfcomm_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
hfp_connection_t * context = provide_hfp_connection_context_for_rfcomm_cid(channel);
if (!context) return;
packet[size] = 0;
int pos;
for (pos = 0; pos < size ; pos++){
hfp_parse(context, packet[pos]);
// trigger next action after CMD received
if (context->command != HFP_CMD_OK) continue;
handle_switch_on_ok(context);
}
}
static void hfp_run(){
linked_list_iterator_t it;
linked_list_iterator_init(&it, hfp_get_connections());
while (linked_list_iterator_has_next(&it)){
hfp_connection_t * connection = (hfp_connection_t *)linked_list_iterator_next(&it);
hfp_run_for_context(connection);
}
}
static void packet_handler(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
// printf("packet_handler type %u, packet[0] %x\n", packet_type, packet[0]);
hfp_connection_t * context = NULL;
switch (packet_type){
case RFCOMM_DATA_PACKET:
context = hfp_handle_rfcomm_event(packet_type, channel, packet, size);
hfp_handle_rfcomm_event(packet_type, channel, packet, size);
break;
case HCI_EVENT_PACKET:
context = hfp_handle_hci_event(packet_type, packet, size);
hfp_handle_hci_event(packet_type, packet, size);
break;
default:
break;
}
hfp_run(context);
hfp_run();
}
void hfp_ag_init(uint16_t rfcomm_channel_nr, uint32_t supported_features, uint8_t * codecs, int codecs_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_hf_indicator_t * hf_indicators, int hf_indicators_nr,
char *call_hold_services[], int call_hold_services_nr){
if (codecs_nr > HFP_MAX_NUM_CODECS){
log_error("hfp_init: codecs_nr (%d) > HFP_MAX_NUM_CODECS (%d)", codecs_nr, HFP_MAX_NUM_CODECS);
return;
}
hfp_init(rfcomm_channel_nr);
rfcomm_register_packet_handler(packet_handler);
// connection->codecs = codecs;
hfp_init(rfcomm_channel_nr);
hfp_supported_features = supported_features;
hfp_codecs_nr = codecs_nr;
@ -137,6 +387,15 @@ void hfp_ag_init(uint16_t rfcomm_channel_nr, uint32_t supported_features, uint8_
for (i=0; i<codecs_nr; i++){
hfp_codecs[i] = codecs[i];
}
hfp_ag_indicators_nr = ag_indicators_nr;
memcpy(hfp_ag_indicators, ag_indicators, ag_indicators_nr * sizeof(hfp_ag_indicator_t));
hfp_hf_indicators_nr = hf_indicators_nr;
memcpy(hfp_hf_indicators, hf_indicators, hf_indicators_nr * sizeof(hfp_hf_indicator_t));
hfp_ag_call_hold_services_nr = call_hold_services_nr;
memcpy(hfp_ag_call_hold_services, call_hold_services, sizeof(char *));
}
void hfp_ag_connect(bd_addr_t bd_addr){

View File

@ -53,8 +53,13 @@
extern "C" {
#endif
void hfp_ag_create_service(uint8_t * service, int rfcomm_channel_nr, const char * name, uint8_t ability_to_reject_call, uint16_t supported_features);
void hfp_ag_init(uint16_t rfcomm_channel_nr, uint32_t supported_features, uint8_t * codecs, int num_codecs);
void hfp_ag_create_service(uint8_t * service, int rfcomm_channel_nr, const char * name, uint8_t ability_to_reject_call, uint16_t supported_features);;
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_hf_indicator_t * hf_indicators, int hf_indicators_nr,
char *call_hold_services[], int call_hold_services_nr);
void hfp_ag_connect(bd_addr_t bd_addr);
void hfp_ag_disconnect(bd_addr_t bd_addr);

View File

@ -71,10 +71,6 @@ static uint8_t hfp_indicators_nr = 0;
static uint8_t hfp_indicators[HFP_MAX_NUM_INDICATORS];
static uint8_t hfp_indicators_status;
static int get_bit(uint16_t bitmap, int position){
return (bitmap >> position) & 1;
}
int has_codec_negotiation_feature(hfp_connection_t * connection){
int hf = get_bit(hfp_supported_features, HFP_HFSF_CODEC_NEGOTIATION);
@ -104,14 +100,6 @@ void hfp_hf_create_service(uint8_t * service, int rfcomm_channel_nr, const char
hfp_create_service(service, SDP_Handsfree, rfcomm_channel_nr, name, supported_features);
}
static int store_bit(uint32_t bitmap, int position, uint8_t value){
if (value){
bitmap |= 1 << position;
} else {
bitmap &= ~ (1 << position);
}
return bitmap;
}
int hfp_hs_exchange_supported_features_cmd(uint16_t cid){
char buffer[20];
@ -122,13 +110,13 @@ int hfp_hs_exchange_supported_features_cmd(uint16_t cid){
int hfp_hs_retrieve_codec_cmd(uint16_t cid){
char buffer[30];
int buffer_offset = snprintf(buffer, sizeof(buffer), "AT%s=", HFP_AVAILABLE_CODECS);
join(buffer+buffer_offset, sizeof(buffer)-buffer_offset, hfp_codecs, hfp_codecs_nr);
// printf("retrieve_codec %s\n", buffer);
int offset = snprintf(buffer, sizeof(buffer), "AT%s=", HFP_AVAILABLE_CODECS);
offset += join(buffer+offset, sizeof(buffer)-offset, hfp_codecs, hfp_codecs_nr);
offset += snprintf(buffer+offset, sizeof(buffer)-offset, "\r\n");
buffer[offset] = 0;
return send_str_over_rfcomm(cid, buffer);
}
int hfp_hs_retrieve_indicators_cmd(uint16_t cid){
char buffer[20];
sprintf(buffer, "AT%s=?\r\n", HFP_INDICATOR);
@ -158,18 +146,18 @@ int hfp_hs_retrieve_can_hold_call_cmd(uint16_t cid){
return send_str_over_rfcomm(cid, buffer);
}
int hfp_hs_list_supported_generic_status_indicators_cmd(uint16_t cid){
char buffer[30];
int buffer_offset = snprintf(buffer, sizeof(buffer), "AT%s=", HFP_GENERIC_STATUS_INDICATOR);
join(buffer+buffer_offset, sizeof(buffer)-buffer_offset, hfp_indicators, hfp_indicators_nr);
// printf("list_supported_generic_status_indicators %s\n", buffer);
int offset = snprintf(buffer, sizeof(buffer), "AT%s=", HFP_GENERIC_STATUS_INDICATOR);
offset += join(buffer+offset, sizeof(buffer)-offset, hfp_indicators, hfp_indicators_nr);
offset += snprintf(buffer+offset, sizeof(buffer)-offset, "\r\n");
buffer[offset] = 0;
return send_str_over_rfcomm(cid, buffer);
}
int hfp_hs_retrieve_supported_generic_status_indicators_cmd(uint16_t cid){
char buffer[20];
sprintf(buffer, "AT%s=?\r\rn", HFP_GENERIC_STATUS_INDICATOR);
sprintf(buffer, "AT%s=?\r\n", HFP_GENERIC_STATUS_INDICATOR);
// printf("retrieve_supported_generic_status_indicators %s\n", buffer);
return send_str_over_rfcomm(cid, buffer);
}
@ -277,138 +265,6 @@ void update_command(hfp_connection_t * context){
}
}
static void hfp_parse(hfp_connection_t * context, uint8_t byte){
int i;
if (byte == ' ') return;
if ( (byte == '\n' || byte == '\r') && context->parser_state > HFP_PARSER_CMD_SEQUENCE) return;
switch (context->parser_state){
case HFP_PARSER_CMD_HEADER: // header
if (byte == ':'){
context->parser_state = HFP_PARSER_CMD_SEQUENCE;
context->line_buffer[context->line_size] = 0;
context->line_size = 0;
update_command(context);
return;
}
if (byte == '\n' || byte == '\r'){
// received OK
context->line_buffer[context->line_size] = 0;
context->line_size = 0;
update_command(context);
return;
}
context->line_buffer[context->line_size++] = byte;
break;
case HFP_PARSER_CMD_SEQUENCE: // parse comma separated sequence, ignore breacktes
if (byte == '"'){ // indicators
context->parser_state = HFP_PARSER_CMD_INDICATOR_NAME;
context->line_size = 0;
break;
}
if (byte == '(' ){ // tuple separated mit comma
break;
}
if (byte == ',' || byte == '\n' || byte == '\r' || byte == ')'){
context->line_buffer[context->line_size] = 0;
context->line_size = 0;
switch (context->state){
case HFP_W4_EXCHANGE_SUPPORTED_FEATURES:
context->remote_supported_features = atoi((char *)&context->line_buffer[0]);
for (i=0; i<16; i++){
if (get_bit(context->remote_supported_features,i)){
printf("AG supported feature: %s\n", hfp_ag_feature(i));
}
}
context->command = HFP_CMD_NONE;
context->parser_state = HFP_PARSER_CMD_HEADER;
break;
case HFP_W4_RETRIEVE_INDICATORS:
break;
case HFP_W4_RETRIEVE_INDICATORS_STATUS:
printf("Indicator with status: %s\n", context->line_buffer);
break;
case HFP_W4_RETRIEVE_CAN_HOLD_CALL:
printf("Support call hold: %s\n", context->line_buffer);
break;
case HFP_W4_RETRIEVE_GENERIC_STATUS_INDICATORS: // comma separated ints
printf("Generic status indicator: %s\n", context->line_buffer);
break;
case HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS:
printf("Generic status indicator: %s, ", context->line_buffer);
context->parser_state = HFP_PARSER_CMD_INITITAL_STATE_GENERIC_STATUS_INDICATORS;
break;
default:
break;
}
if (byte == '\n' || byte == '\r'){
context->command = HFP_CMD_NONE;
context->parser_state = HFP_PARSER_CMD_HEADER;
break;
}
if (byte == ')' && context->state == HFP_W4_RETRIEVE_CAN_HOLD_CALL){ // tuple separated mit comma
context->command = HFP_CMD_NONE;
context->parser_state = HFP_PARSER_CMD_HEADER;
break;
}
break;
}
context->line_buffer[context->line_size++] = byte;
break;
case HFP_PARSER_CMD_INITITAL_STATE_GENERIC_STATUS_INDICATORS:
if (byte == '\n' || byte == '\r'){
context->line_buffer[context->line_size] = 0;
context->line_size = 0;
context->command = HFP_CMD_NONE;
context->parser_state = HFP_PARSER_CMD_HEADER;
printf("status %s [0-dissabled, 1-enabled]\n", context->line_buffer);
break;
}
break;
case HFP_PARSER_CMD_INDICATOR_NAME: // parse indicator name
if (byte == '"'){
context->line_buffer[context->line_size] = 0;
printf("Indicator %d: %s (", context->remote_indicators_nr, context->line_buffer);
context->line_size = 0;
break;
}
if (byte == '('){ // parse indicator range
context->parser_state = HFP_PARSER_CMD_INDICATOR_MIN_RANGE;
break;
}
context->line_buffer[context->line_size++] = byte;
break;
case HFP_PARSER_CMD_INDICATOR_MIN_RANGE:
if (byte == ',' || byte == '-'){ // end min_range
context->parser_state = HFP_PARSER_CMD_INDICATOR_MAX_RANGE;
context->line_buffer[context->line_size] = 0;
printf("%d, ", atoi((char *)&context->line_buffer[0]));
context->line_size = 0;
break;
}
// min. range
context->line_buffer[context->line_size++] = byte;
break;
case HFP_PARSER_CMD_INDICATOR_MAX_RANGE:
if (byte == ')'){ // end max_range
context->parser_state = HFP_PARSER_CMD_SEQUENCE;
context->line_buffer[context->line_size] = 0;
printf("%d)\n", atoi((char *)&context->line_buffer[0]));
context->line_size = 0;
context->remote_indicators_nr+=1;
break;
}
//
context->line_buffer[context->line_size++] = byte;
break;
}
}
void handle_switch_on_ok(hfp_connection_t *context){
printf("handle switch on OK\n");
@ -533,12 +389,10 @@ void hfp_hf_init(uint16_t rfcomm_channel_nr, uint32_t supported_features, uint8_
rfcomm_register_packet_handler(packet_handler);
hfp_init(rfcomm_channel_nr);
int i;
// connection->codecs = codecs;
hfp_supported_features = supported_features;
hfp_codecs_nr = codecs_nr;
int i;
for (i=0; i<codecs_nr; i++){
hfp_codecs[i] = codecs[i];
}

View File

@ -37,12 +37,10 @@
// *****************************************************************************
//
// Minimal test for HSP Headset (!! UNDER DEVELOPMENT !!)
// Minimal test for HSP Audio Gateway (!! UNDER DEVELOPMENT !!)
//
// *****************************************************************************
#include "btstack-config.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@ -67,26 +65,47 @@
#include "debug.h"
#include "hfp_ag.h"
#include "stdin_support.h"
const uint32_t hfp_service_buffer[150/4]; // implicit alignment to 4-byte memory address
const uint8_t rfcomm_channel_nr = 1;
const char hfp_ag_service_name[] = "Headset Test";
const char hfp_ag_service_name[] = "BTstack HFP AG Test";
static bd_addr_t pts_addr = {0x00,0x1b,0xDC,0x07,0x32,0xEF};
static bd_addr_t local_mac = {0x04, 0x0C, 0xCE, 0xE4, 0x85, 0xD3};
static bd_addr_t speaker = {0x00, 0x21, 0x3C, 0xAC, 0xF7, 0x38};
static uint8_t codecs[1] = {HFP_CODEC_CVSD};
static int ag_indicators_nr = 7;
static hfp_ag_indicator_t ag_indicators[] = {
{1, "service", 0, 1, 1},
{2, "call", 0, 1, 0},
{3, "callsetup", 0, 3, 0},
{4, "battchg", 0, 5, 3},
{5, "signal", 0, 5, 5},
{6, "roam", 0, 1, 0},
{7, "callheld", 0, 2, 0},
};
static int call_hold_services_nr = 5;
static char* call_hold_services[] = {"1", "1x", "2", "2x", "3"};
static int hf_indicators_nr = 2;
static hfp_hf_indicator_t hf_indicators[] = {
{1, 1, 1},
{2, 1, 1},
};
// prototypes
static void show_usage();
static void show_usage(void);
// Testig User Interface
static void show_usage(void){
printf("\n--- Bluetooth HFP Audio Gateway (AG) unit Test Console ---\n");
printf("\n--- Bluetooth HFP Hands-Free (HF) unit Test Console ---\n");
printf("---\n");
printf("p - establish audio connection to PTS module\n");
printf("e - establish audio connection to local mac\n");
printf("d - release audio connection from Bluetooth Speaker\n");
printf("p - establish HFP connection to PTS module\n");
printf("e - establish HFP connection to local mac\n");
printf("d - release HFP connection\n");
printf("---\n");
printf("Ctrl-c - exit\n");
printf("---\n");
@ -95,18 +114,17 @@ static void show_usage(void){
static int stdin_process(struct data_source *ds){
char buffer;
read(ds->fd, &buffer, 1);
switch (buffer){
case 'p':
printf("Establishing audio connection to PTS module %s...\n", bd_addr_to_str(pts_addr));
printf("Establishing HFP connection to PTS module %s...\n", bd_addr_to_str(pts_addr));
hfp_ag_connect(pts_addr);
break;
case 'e':
printf("Establishing audio connection to local mac %s...\n", bd_addr_to_str(local_mac));
hfp_ag_connect(local_mac);
printf("Establishing HFP connection to %s...\n", bd_addr_to_str(speaker));
hfp_ag_connect(speaker);
break;
case 'd':
printf("Releasing audio connection.\n");
printf("Releasing HFP connection.\n");
hfp_ag_disconnect(pts_addr);
break;
default:
@ -125,26 +143,31 @@ void packet_handler(uint8_t * event, uint16_t event_size){
}
}
int btstack_main(int argc, const char * argv[]){
// init L2CAP
l2cap_init();
rfcomm_init();
// TODO: hfp_ag_init(rfcomm_channel_nr);
hfp_ag_init(rfcomm_channel_nr, 438, codecs, sizeof(codecs),
ag_indicators, ag_indicators_nr,
hf_indicators, hf_indicators_nr,
call_hold_services, call_hold_services_nr);
hfp_register_packet_handler(packet_handler);
sdp_init();
// init SDP, create record for SPP and register with SDP
memset((uint8_t *)hfp_service_buffer, 0, sizeof(hfp_service_buffer));
hfp_ag_create_service((uint8_t *)hfp_service_buffer, rfcomm_channel_nr, hfp_ag_service_name, 0, 0);
sdp_register_service_internal(NULL, (uint8_t *)hfp_service_buffer);
// turn on!
hci_power_control(HCI_POWER_ON);
btstack_stdin_setup(stdin_process);
printf("Establishing HFP connection to %s...\n", bd_addr_to_str(speaker));
hfp_ag_connect(speaker);
return 0;
}

View File

@ -70,11 +70,14 @@
const uint32_t hfp_service_buffer[150/4]; // implicit alignment to 4-byte memory address
const uint8_t rfcomm_channel_nr = 1;
const char hfp_hf_service_name[] = "BTstack HF Test";
const char hfp_hf_service_name[] = "BTstack HFP HF Test";
static bd_addr_t pts_addr = {0x00,0x1b,0xDC,0x07,0x32,0xEF};
static bd_addr_t local_mac = {0x04, 0x0C, 0xCE, 0xE4, 0x85, 0xD3};
static bd_addr_t phone = {0xD8,0xBb,0x2C,0xDf,0xF1,0x08};
static uint8_t codecs[1] = {HFP_CODEC_CVSD};
static uint16_t indicators[1] = {0x01};
// prototypes
static void show_usage();
@ -94,7 +97,6 @@ static void show_usage(void){
static int stdin_process(struct data_source *ds){
char buffer;
read(ds->fd, &buffer, 1);
switch (buffer){
case 'p':
printf("Establishing HFP connection to PTS module %s...\n", bd_addr_to_str(pts_addr));
@ -126,9 +128,6 @@ void packet_handler(uint8_t * event, uint16_t event_size){
int btstack_main(int argc, const char * argv[]){
// init L2CAP
uint8_t codecs[1] = {HFP_CODEC_CVSD};
uint16_t indicators[1] = {0x01};
l2cap_init();
rfcomm_init();
@ -147,6 +146,7 @@ int btstack_main(int argc, const char * argv[]){
hci_power_control(HCI_POWER_ON);
btstack_stdin_setup(stdin_process);
// printf("Establishing HFP connection to %s...\n", bd_addr_to_str(phone));
// hfp_hf_connect(phone);
return 0;
}