hfp ag create/release sco connection

This commit is contained in:
Milanka Ringwald 2015-11-11 15:45:35 +01:00
parent c26caa2172
commit 6f1de21cfe
8 changed files with 149 additions and 69 deletions

View File

@ -637,8 +637,8 @@ extern "C" {
#define HFP_SUBEVENT_NETWORK_OPERATOR_CHANGED 0x07
#define HFP_SUBEVENT_EXTENDED_AUDIO_GATEWAY_ERROR 0x08
#define HFP_SUBEVENT_CODECS_CONNECTION_COMPLETE 0x09
#define HFP_SUBEVENT_AUDIO_CONNECTION_COMPLETE 0x0A
// ANCS Client
#define ANCS_CLIENT_CONNECTED 0xF0
#define ANCS_CLIENT_NOTIFICATION 0xF1

View File

@ -233,12 +233,12 @@ hfp_connection_t * get_hfp_connection_context_for_bd_addr(bd_addr_t bd_addr){
return NULL;
}
static hfp_connection_t * get_hfp_connection_context_for_handle(uint16_t handle){
hfp_connection_t * get_hfp_connection_context_for_sco_handle(uint16_t handle){
linked_list_iterator_t it;
linked_list_iterator_init(&it, hfp_get_connections());
while (linked_list_iterator_has_next(&it)){
hfp_connection_t * connection = (hfp_connection_t *)linked_list_iterator_next(&it);
if (connection->con_handle == handle){
if (connection->sco_handle == handle){
return connection;
}
}
@ -470,6 +470,7 @@ void hfp_handle_hci_event(hfp_callback_t callback, uint8_t packet_type, uint8_t
case RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE:
// data: event(8), len(8), status (8), address (48), handle(16), server channel(8), rfcomm_cid(16), max frame size(16)
printf("RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE packet_handler type %u, packet[0] %x\n", packet_type, packet[0]);
bt_flip_addr(event_addr, &packet[3]);
context = get_hfp_connection_context_for_bd_addr(event_addr);
if (!context || context->state != HFP_W4_RFCOMM_CONNECTED) return;
@ -479,6 +480,8 @@ void hfp_handle_hci_event(hfp_callback_t callback, uint8_t packet_type, uint8_t
remove_hfp_connection_context(context);
} else {
context->con_handle = READ_BT_16(packet, 9);
printf("RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE con_handle 0x%02x\n", context->con_handle);
context->rfcomm_cid = READ_BT_16(packet, 12);
uint16_t mtu = READ_BT_16(packet, 14);
printf("RFCOMM channel open succeeded. Context %p, RFCOMM Channel ID 0x%02x, max frame size %u\n", context, context->rfcomm_cid, mtu);
@ -543,10 +546,9 @@ void hfp_handle_hci_event(hfp_callback_t callback, uint8_t packet_type, uint8_t
context->state = HFP_W2_DISCONNECT_SCO;
break;
}
context->sco_handle = sco_handle;
context->state = HFP_AUDIO_CONNECTION_ESTABLISHED;
hfp_emit_event(callback, HFP_SUBEVENT_AUDIO_CONNECTION_COMPLETE, packet[2]);
hfp_emit_event(callback, HFP_SUBEVENT_AUDIO_CONNECTION_ESTABLISHED, packet[2]);
break;
}
@ -566,15 +568,21 @@ void hfp_handle_hci_event(hfp_callback_t callback, uint8_t packet_type, uint8_t
case HCI_EVENT_DISCONNECTION_COMPLETE:
handle = READ_BT_16(packet,3);
context = get_hfp_connection_context_for_handle(handle);
context = get_hfp_connection_context_for_sco_handle(handle);
if (!context) break;
if (context->state == HFP_W4_RFCOMM_DISCONNECTED_AND_RESTART){
context->state = HFP_IDLE;
hfp_establish_service_level_connection(context->remote_addr, context->service_uuid);
if (context->state != HFP_W4_SCO_DISCONNECTED){
log_info("Received gap disconnect in wrong hfp state");
}
if (handle == context->sco_handle){
printf("SCO disconnected, w2 disconnect RFCOMM\n");
context->sco_handle = 0;
context->state = HFP_W2_DISCONNECT_RFCOMM;
hfp_emit_event(callback, HFP_SUBEVENT_AUDIO_CONNECTION_RELEASED, 0);
break;
}
hfp_emit_event(callback, HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_RELEASED, packet[2]);
remove_hfp_connection_context(context);
break;
default:
@ -1051,7 +1059,7 @@ void hfp_release_service_level_connection(hfp_connection_t * context){
return;
}
if (context->state < HFP_CCE_W4_SCO_CONNECTION_ESTABLISHED){
if (context->state < HFP_W4_SCO_CONNECTED){
context->state = HFP_W2_DISCONNECT_RFCOMM;
return;
}

View File

@ -248,8 +248,8 @@ typedef enum {
HFP_CODECS_CONNECTION_ESTABLISHED, // 25
HFP_CCE_W2_ESTABLISH_SCO,
HFP_CCE_W4_SCO_CONNECTION_ESTABLISHED,
HFP_W2_CONNECT_SCO,
HFP_W4_SCO_CONNECTED,
HFP_AUDIO_CONNECTION_ESTABLISHED,
@ -383,8 +383,11 @@ int store_bit(uint32_t bitmap, int position, uint8_t value);
void hfp_create_sdp_record(uint8_t * service, uint16_t service_uuid, int rfcomm_channel_nr, const char * name, uint16_t supported_features);
void hfp_handle_hci_event(hfp_callback_t callback, 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);
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(bd_addr_t bd_addr);
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);

View File

@ -579,7 +579,7 @@ static int hfp_ag_run_for_context_service_level_connection_queries(hfp_connectio
static int hfp_ag_run_for_context_codecs_connection(hfp_connection_t * context){
if (context->state <= HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED ||
context->state > HFP_CODECS_CONNECTION_ESTABLISHED) return 0;
context->state > HFP_W2_DISCONNECT_SCO) return 0;
int done = 0;
//printf(" AG run for context_codecs_connection: ");
@ -660,6 +660,25 @@ static int hfp_ag_run_for_context_codecs_connection(hfp_connection_t * context){
default:
break;
}
if (done) return done;
if (context->establish_audio_connection){
context->state = HFP_W4_SCO_CONNECTED;
hci_send_cmd(&hci_setup_synchronous_connection, context->con_handle, 8000, 8000, 0xFFFF, 0x0043, 0xFF, 0x003F);
done = 1;
return done;
}
break;
case HFP_AUDIO_CONNECTION_ESTABLISHED:
case HFP_W2_DISCONNECT_SCO:
if (context->release_audio_connection){
context->state = HFP_W4_SCO_DISCONNECTED;
gap_disconnect(context->sco_handle);
done = 1;
return done;
}
break;
default:
break;
@ -672,7 +691,7 @@ static void hfp_run_for_context(hfp_connection_t *context){
if (!rfcomm_can_send_packet_now(context->rfcomm_cid)) return;
// printf("AG hfp_run_for_context 1 state %d, command %d\n", context->state, context->command);
// printf("AG hfp_run_for_context 1 state %d == %d (HFP_W2_DISCONNECT_SCO), command %d\n", context->state, HFP_W2_DISCONNECT_SCO, context->command);
if (context->send_ok){
hfp_ag_ok(context->rfcomm_cid);
context->send_ok = 0;

View File

@ -28,7 +28,7 @@ COMMON = \
MOCK = \
btstack_memory.c \
btstack_memory.c \
linked_list.c \
memory_pool.c \
remote_device_db_memory.c \
@ -58,7 +58,7 @@ EXAMPLES = hfp_ag_parser_test hfp_ag_client_test hfp_hf_parser_test hfp_hf_clien
all: ${BTSTACK_ROOT}/include/btstack/version.h ${EXAMPLES}
clean:
rm -rf *.o $(PARSER_EXAMPLES) $(CLIENT_EXAMPLES) *.dSYM
rm -rf *.o $(EXAMPLES) $(CLIENT_EXAMPLES) *.dSYM
hfp_ag_parser_test: ${COMMON_OBJ} hfp_ag.o hfp.o hfp_ag_parser_test.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@

View File

@ -97,7 +97,6 @@ static hfp_generic_status_indicator_t hf_indicators[] = {
static uint8_t service_level_connection_established = 0;
static uint8_t codecs_connection_established = 0;
static uint8_t audio_connection_established = 0;
static uint8_t service_level_connection_released = 0;
int expected_rfcomm_command(const char * expected_cmd){
@ -153,11 +152,17 @@ void packet_handler(uint8_t * event, uint16_t event_size){
codecs_connection_established = 1;
audio_connection_established = 0;
break;
case HFP_SUBEVENT_AUDIO_CONNECTION_COMPLETE:
case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_RELEASED:
printf("\n** SLC released **\n\n");
service_level_connection_established = 0;
break;
case HFP_SUBEVENT_AUDIO_CONNECTION_ESTABLISHED:
printf("\n** AC established **\n\n");
audio_connection_established = 1;
break;
case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_RELEASED:
service_level_connection_released = 1;
case HFP_SUBEVENT_AUDIO_CONNECTION_RELEASED:
printf("\n** AC released **\n\n");
audio_connection_established = 0;
break;
default:
printf("event not handled %u\n", event[2]);
@ -171,16 +176,19 @@ TEST_GROUP(HFPClient){
service_level_connection_established = 0;
codecs_connection_established = 0;
audio_connection_established = 0;
service_level_connection_released = 0;
}
void teardown(void){
if (audio_connection_established){
hfp_ag_release_audio_connection(device_addr);
}
if (service_level_connection_established){
hfp_ag_release_service_level_connection(device_addr);
CHECK_EQUAL(service_level_connection_released, 1);
service_level_connection_established = 0;
service_level_connection_released = 0;
}
service_level_connection_established = 0;
codecs_connection_established = 0;
audio_connection_established = 0;
}
void setup_hfp_service_level_connection(char ** test_steps, int nr_test_steps){
@ -196,31 +204,44 @@ TEST_GROUP(HFPClient){
};
TEST(HFPClient, HFCodecsConnectionEstablished){
for (int i = 0; i < cc_tests_size(); i++){
setup_hfp_service_level_connection(default_slc_setup(), default_slc_setup_size());
CHECK_EQUAL(service_level_connection_established, 1);
setup_hfp_codecs_connection(hfp_cc_tests()[i].test, hfp_cc_tests()[i].len);
CHECK_EQUAL(codecs_connection_established, 1);
teardown();
}
}
TEST(HFPClient, HFServiceLevelConnectionCommands){
TEST(HFPClient, HFAudioConnectionEstablished){
setup_hfp_service_level_connection(default_slc_setup(), default_slc_setup_size());
CHECK_EQUAL(service_level_connection_established, 1);
for (int i = 0; i < slc_cmds_tests_size(); i++){
simulate_test_sequence(hfp_slc_cmds_tests()[i].test, hfp_slc_cmds_tests()[i].len);
}
setup_hfp_codecs_connection(default_cc_setup(), default_cc_setup_size());
CHECK_EQUAL(codecs_connection_established, 1);
hfp_ag_establish_audio_connection(device_addr);
CHECK_EQUAL(audio_connection_established, 1);
hfp_ag_release_audio_connection(device_addr);
CHECK_EQUAL(audio_connection_established, 0);
}
TEST(HFPClient, HFServiceLevelConnectionEstablished){
for (int i = 0; i < slc_tests_size(); i++){
setup_hfp_service_level_connection(hfp_slc_tests()[i].test, hfp_slc_tests()[i].len);
CHECK_EQUAL(service_level_connection_established, 1);
}
}
// TEST(HFPClient, HFCodecsConnectionEstablished){
// for (int i = 0; i < cc_tests_size(); i++){
// setup_hfp_service_level_connection(default_slc_setup(), default_slc_setup_size());
// CHECK_EQUAL(service_level_connection_established, 1);
// setup_hfp_codecs_connection(hfp_cc_tests()[i].test, hfp_cc_tests()[i].len);
// CHECK_EQUAL(codecs_connection_established, 1);
// teardown();
// }
// }
// TEST(HFPClient, HFServiceLevelConnectionCommands){
// setup_hfp_service_level_connection(default_slc_setup(), default_slc_setup_size());
// CHECK_EQUAL(service_level_connection_established, 1);
// for (int i = 0; i < slc_cmds_tests_size(); i++){
// simulate_test_sequence(hfp_slc_cmds_tests()[i].test, hfp_slc_cmds_tests()[i].len);
// }
// }
// TEST(HFPClient, HFServiceLevelConnectionEstablished){
// for (int i = 0; i < slc_tests_size(); i++){
// setup_hfp_service_level_connection(hfp_slc_tests()[i].test, hfp_slc_tests()[i].len);
// CHECK_EQUAL(service_level_connection_established, 1);
// }
// }
int main (int argc, const char * argv[]){

View File

@ -75,8 +75,6 @@ static uint16_t indicators[1] = {0x01};
static uint8_t service_level_connection_established = 0;
static uint8_t codecs_connection_established = 0;
static uint8_t audio_connection_established = 0;
static uint8_t service_level_connection_released = 0;
int expected_rfcomm_command(const char * cmd){
char * ag_cmd = (char *)get_rfcomm_payload();
@ -145,19 +143,14 @@ void packet_handler(uint8_t * event, uint16_t event_size){
case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED:
printf("\n** SLC established **\n\n");
service_level_connection_established = 1;
codecs_connection_established = 0;
audio_connection_established = 0;
break;
case HFP_SUBEVENT_CODECS_CONNECTION_COMPLETE:
printf("\n** CC established **\n\n");
codecs_connection_established = 1;
audio_connection_established = 0;
break;
case HFP_SUBEVENT_AUDIO_CONNECTION_COMPLETE:
audio_connection_established = 1;
break;
case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_RELEASED:
service_level_connection_released = 1;
service_level_connection_established = 0;
break;
case HFP_SUBEVENT_COMPLETE:
@ -186,33 +179,25 @@ TEST_GROUP(HFPClient){
void setup(void){
service_level_connection_established = 0;
codecs_connection_established = 0;
audio_connection_established = 0;
service_level_connection_released = 0;
}
void teardown(void){
if (service_level_connection_established){
hfp_hf_release_service_level_connection(device_addr);
CHECK_EQUAL(service_level_connection_released, 1);
service_level_connection_established = 0;
service_level_connection_released = 0;
CHECK_EQUAL(service_level_connection_established, 0);
}
codecs_connection_established = 0;
}
void setup_hfp_service_level_connection(char ** test_steps, int nr_test_steps){
service_level_connection_established = 0;
hfp_hf_establish_service_level_connection(device_addr);
simulate_test_sequence((char **) test_steps, nr_test_steps);
// CHECK_EQUAL(service_level_connection_established, 1);
// hfp_hf_set_codecs(codecs, 1);
// inject_rfcomm_command((uint8_t*)HFP_OK, strlen(HFP_OK));
}
void setup_hfp_codecs_connection(char ** test_steps, int nr_test_steps){
codecs_connection_established = 0;
// hfp_hf_negotiate_codecs(device_addr);
simulate_test_sequence((char **) test_steps, nr_test_steps);
// CHECK_EQUAL(codecs_connection_established, 1);
}
};
@ -224,7 +209,6 @@ TEST(HFPClient, HFCodecsConnectionEstablished){
CHECK_EQUAL(service_level_connection_established, 1);
setup_hfp_codecs_connection(hfp_cc_tests()[i].test, hfp_cc_tests()[i].len);
//CHECK_EQUAL(codecs_connection_established, 1);
teardown();
}
}

View File

@ -59,6 +59,8 @@ static void *registered_sdp_app_context;
static uint8_t sdp_rfcomm_channel_nr = 1;
const char sdp_rfcomm_service_name[] = "BTstackMock";
static uint16_t rfcomm_cid = 1;
static bd_addr_t dev_addr;
static uint16_t sco_handle = 10;
static uint8_t rfcomm_payload[200];
static uint16_t rfcomm_payload_len;
void * active_connection;
@ -133,8 +135,33 @@ int rfcomm_send_internal(uint16_t rfcomm_cid, uint8_t *data, uint16_t len){
return 0;
}
static void hci_event_sco_complete(){
uint8_t event[20];
uint8_t pos = 0;
event[pos++] = HCI_EVENT_SYNCHRONOUS_CONNECTION_COMPLETE;
event[pos++] = sizeof(event) - 2;
event[pos++] = 0; //status
bt_store_16(event, pos, sco_handle); pos += 2; // sco handle
bt_flip_addr(&event[pos], dev_addr); pos += 6;
printf("hci_event_sco_complete sco_handle 0x%02x, address %s\n", sco_handle, bd_addr_to_str(&event[pos-6]));
event[pos++] = 0; // link_type
event[pos++] = 0; // transmission_interval
event[pos++] = 0; // retransmission_interval
bt_store_16(event, pos, 0); pos += 2; // rx_packet_length
bt_store_16(event, pos, 0); pos += 2; // tx_packet_length
event[pos++] = 0; // air_mode
(*registered_rfcomm_packet_handler)(0, HCI_EVENT_PACKET, 0, event, sizeof(event));
}
int hci_send_cmd(const hci_cmd_t *cmd, ...){
printf("hci_send_cmd opcode 0x%02x\n", cmd->opcode);
// printf("hci_send_cmd opcode 0x%02x\n", cmd->opcode);
if (cmd->opcode == 0x428){
hci_event_sco_complete();
}
return 0;
}
@ -167,6 +194,7 @@ void sdp_query_rfcomm_channel_and_name_for_uuid(bd_addr_t remote, uint16_t uuid)
sdp_query_complete_response(0);
}
void rfcomm_create_channel_internal(void * connection, bd_addr_t addr, uint8_t channel){
// RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE
// printf("rfcomm_create_channel_internal\n");
@ -178,6 +206,8 @@ void rfcomm_create_channel_internal(void * connection, bd_addr_t addr, uint8_t c
event[pos++] = 0;
bt_flip_addr(&event[pos], addr); pos += 6;
bt_flip_addr(dev_addr, addr);
bt_store_16(event, pos, 1); pos += 2;
event[pos++] = 0;
@ -216,6 +246,21 @@ void rfcomm_accept_connection_internal(uint16_t rfcomm_cid){
printf("rfcomm_accept_connection_internal \n");
}
void hci_emit_disconnection_complete(uint16_t handle, uint8_t reason){
uint8_t event[6];
event[0] = HCI_EVENT_DISCONNECTION_COMPLETE;
event[1] = sizeof(event) - 2;
event[2] = 0; // status = OK
bt_store_16(event, 3, handle);
event[5] = reason;
(*registered_rfcomm_packet_handler)(0, HCI_EVENT_PACKET, 0, event, sizeof(event));
}
le_command_status_t gap_disconnect(hci_con_handle_t handle){
hci_emit_disconnection_complete(handle, 0);
return BLE_PERIPHERAL_OK;
}
void inject_rfcomm_command_to_hf(uint8_t * data, int len){
if (memcmp((char*)data, "AT", 2) == 0) return;