merge from master

This commit is contained in:
Matthias Ringwald 2015-11-25 14:48:19 +01:00
commit aa4dd81512
36 changed files with 2762 additions and 1060 deletions

View File

@ -61,7 +61,6 @@
#include "debug.h"
#include "btstack_memory.h"
#include "ble/gap.h"
#include "hci.h"
#include "hci_dump.h"
#include "l2cap.h"

View File

@ -59,7 +59,6 @@
#include "debug.h"
#include "btstack_memory.h"
#include "gap_le.h"
#include "hci.h"
#include "hci_dump.h"

View File

@ -112,9 +112,10 @@ static int get_link_key(bd_addr_t bd_addr, link_key_t link_key, link_key_type_t
int scan_result = sscan_link_key(link_key_str, link_key);
if (scan_result == 0 ) return 0;
scan_result = sscanf( (char *) link_key_type_str, "%d", link_key_type);
int link_key_type_buffer;
scan_result = sscanf( (char *) link_key_type_str, "%d", &link_key_type_buffer);
if (scan_result == 0 ) return 0;
*link_key_type = (link_key_type_t) link_key_type_buffer;
return 1;
}

27
platforms/posix-h4/.gitignore vendored Normal file
View File

@ -0,0 +1,27 @@
ancs_client
ancs_client.h
ble_central_test
ble_peripheral
ble_peripheral_sm_minimal
bnep_test
classic_test
gap_dedicated_bonding
gap_inquiry
gap_inquiry_and_bond
gatt_battery_query
gatt_browser
hsp_ag_test
hsp_hs_test
l2cap_test
profile.h
sdp_bnep_query
sdp_general_query
sdp_rfcomm_query
spp_and_le_counter
spp_and_le_counter.h
spp_counter
spp_streamer
led_counter
le_counter.h
ble_peripheral_test
le_counter

View File

@ -0,0 +1,27 @@
# Makefile for libusb based examples
BTSTACK_ROOT = ../..
POSIX_ROOT= ${BTSTACK_ROOT}/platforms/posix
CORE += main.c stdin_support.c
COMMON += hci_transport_h4.c run_loop_posix.c remote_device_db_fs.c
include ${BTSTACK_ROOT}/example/embedded/Makefile.inc
# CC = gcc-fsf-4.9
CFLAGS += -g -Wall
# CFLAGS += -Werror
VPATH += ${BTSTACK_ROOT}/platforms/posix/src
ifeq ($(OS),Windows_NT)
LDFLAGS += -lws2_32
endif
# Command Line examples require porting to win32, so only build on other unix-ish hosts
ifneq ($(OS),Windows_NT)
EXAMPLES += ${EXAMPLES_CLI}
CFLAGS += -I${POSIX_ROOT}/src
endif
all: ${BTSTACK_ROOT}/include/btstack/version.h ${EXAMPLES}

View File

@ -0,0 +1,24 @@
// config.h created by configure for BTstack Tue Jun 4 23:10:20 CEST 2013
#ifndef __BTSTACK_CONFIG
#define __BTSTACK_CONFIG
#define HAVE_TRANSPORT_USB
#define HAVE_BLE
#define USE_POSIX_RUN_LOOP
#define HAVE_SDP
#define HAVE_RFCOMM
#define REMOTE_DEVICE_DB remote_device_db_iphone
#define HAVE_SO_NOSIGPIPE
#define HAVE_TIME
#define HAVE_MALLOC
#define HAVE_BZERO
#define SDP_DES_DUMP
#define ENABLE_LOG_INFO
#define ENABLE_LOG_ERROR
#define HCI_INCOMING_PRE_BUFFER_SIZE 14 // sizeof benep heade, avoid memcpy
#define HCI_ACL_PAYLOAD_SIZE (1691 + 4)
#define HAVE_HCI_DUMP
#define SDP_DES_DUMP
#endif

115
platforms/posix-h4/main.c Normal file
View File

@ -0,0 +1,115 @@
/*
* 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
*
*/
// *****************************************************************************
//
// minimal setup for HCI code
//
// *****************************************************************************
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include "btstack-config.h"
#include <btstack/run_loop.h>
#include "debug.h"
#include "btstack_memory.h"
#include "hci.h"
#include "hci_dump.h"
#include "stdin_support.h"
int btstack_main(int argc, const char * argv[]);
static hci_uart_config_t hci_uart_config_generic = {
NULL,
115200,
};
static void sigint_handler(int param){
#ifndef _WIN32
// reset anyway
btstack_stdin_reset();
#endif
log_info(" <= SIGINT received, shutting down..\n");
hci_power_control(HCI_POWER_OFF);
hci_close();
log_info("Good bye, see you.\n");
exit(0);
}
static int led_state = 0;
void hal_led_toggle(void){
led_state = 1 - led_state;
printf("LED State %u\n", led_state);
}
int main(int argc, const char * argv[]){
/// GET STARTED with BTstack ///
btstack_memory_init();
run_loop_init(RUN_LOOP_POSIX);
// use logger: format HCI_DUMP_PACKETLOGGER, HCI_DUMP_BLUEZ or HCI_DUMP_STDOUT
hci_dump_open("/tmp/hci_dump.pklg", HCI_DUMP_PACKETLOGGER);
// pick serial port
hci_uart_config_generic.device_name = "/dev/tty.usbmodem1413";
// init HCI
hci_transport_t * transport = hci_transport_h4_instance();
remote_device_db_t * remote_db = (remote_device_db_t *) &remote_device_db_fs;
hci_init(transport, (void*) &hci_uart_config_generic, NULL, remote_db);
// handle CTRL-c
signal(SIGINT, sigint_handler);
// setup app
btstack_main(argc, argv);
// go
run_loop_execute();
return 0;
}

View File

@ -21,4 +21,6 @@
#define HAVE_HCI_DUMP
#define SDP_DES_DUMP
// #define HAVE_SCO
#endif

View File

@ -56,6 +56,7 @@
#include "hci.h"
#include "hci_dump.h"
#include "stdin_support.h"
#include "hal_led.h"
#include "bt_control_cc256x.h"
int btstack_main(int argc, const char * argv[]);

View File

@ -42,6 +42,7 @@
#include "ble/att.h"
#include "utils.h"
#include "debug.h"
#include "bluetooth.h"
// ATT DB Storage
#ifndef HAVE_MALLOC

View File

@ -683,7 +683,12 @@
#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
#define HFP_SUBEVENT_START_RINGINIG 0x0A
#define HFP_SUBEVENT_STOP_RINGINIG 0x0B
#define HFP_SUBEVENT_CALL_TERMINATED 0x0C
#define HFP_SUBEVENT_PLACE_CALL_WITH_NUMBER 0x0D
#define HFP_SUBEVENT_REDIAL_LAST_NUMBER 0x0E
#define HFP_SUBEVENT_ATTACH_NUMBER_TO_VOICE_TAG 0x0F
// ANCS Client
#define ANCS_CLIENT_CONNECTED 0xF0

View File

@ -204,6 +204,28 @@ void hfp_emit_event(hfp_callback_t callback, uint8_t event_subtype, uint8_t valu
(*callback)(event, sizeof(event));
}
void hfp_emit_string_event(hfp_callback_t callback, uint8_t event_subtype, const char * value){
if (!callback) return;
uint8_t event[24];
event[0] = HCI_EVENT_HFP_META;
event[1] = sizeof(event) - 2;
event[2] = event_subtype;
int size = (strlen(value) < sizeof(event) - 4) ? strlen(value) : sizeof(event) - 4;
strncpy((char*)&event[3], value, size);
event[3 + size] = 0;
(*callback)(event, sizeof(event));
}
static void hfp_emit_audio_connection_established_event(hfp_callback_t callback, uint8_t value, uint16_t sco_handle){
if (!callback) return;
uint8_t event[6];
event[0] = HCI_EVENT_HFP_META;
event[1] = sizeof(event) - 2;
event[2] = HFP_SUBEVENT_AUDIO_CONNECTION_ESTABLISHED;
event[3] = value; // status 0 == OK
bt_store_16(event, 4, sco_handle);
(*callback)(event, sizeof(event));
}
linked_list_t * hfp_get_connections(){
return (linked_list_t *) &hfp_connections;
@ -233,12 +255,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;
}
}
@ -247,35 +269,18 @@ static hfp_connection_t * get_hfp_connection_context_for_handle(uint16_t handle)
void hfp_reset_context_flags(hfp_connection_t * context){
if (!context) return;
context->wait_ok = 0;
context->send_ok = 0;
context->ok_pending = 0;
context->send_error = 0;
context->keep_separator = 0;
context->retrieve_ag_indicators = 0; // HFP_CMD_INDICATOR, check if needed
context->retrieve_ag_indicators_status = 0;
context->list_generic_status_indicators = 0; // HFP_CMD_LIST_GENERIC_STATUS_INDICATOR
context->retrieve_generic_status_indicators = 0; // HFP_CMD_GENERIC_STATUS_INDICATOR
context->retrieve_generic_status_indicators_state = 0; // HFP_CMD_GENERIC_STATUS_INDICATOR_STATE
context->change_status_update_for_individual_ag_indicators = 0;
context->operator_name_format = 0;
context->operator_name = 0;
context->operator_name_changed = 0;
context->enable_extended_audio_gateway_error_report = 0;
context->extended_audio_gateway_error = 0;
// can come any time (here taken into account only after SLE),
// if codec negotiation feature is set
context->notify_ag_on_new_codecs = 0;
// establish codecs connection
context->ag_trigger_codec_connection_setup = 0;
context->hf_trigger_codec_connection_setup = 0;
context->suggested_codec = 0;
context->negotiated_codec = 0;
context->codec_confirmed = 0;
@ -290,6 +295,9 @@ static hfp_connection_t * create_hfp_connection_context(){
memset(context,0, sizeof(hfp_connection_t));
context->state = HFP_IDLE;
context->call_state = HFP_CALL_IDLE;
context->codecs_state = HFP_CODECS_IDLE;
context->parser_state = HFP_PARSER_CMD_HEADER;
context->command = HFP_CMD_NONE;
context->negotiated_codec = 0;
@ -311,10 +319,15 @@ static hfp_connection_t * provide_hfp_connection_context_for_bd_addr(bd_addr_t b
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;
}
/* @param network.
* 0 == no ability to reject a call.
* 1 == ability to reject a call.
*/
/* @param suported_features
* HF bit 0: EC and/or NR function (yes/no, 1 = yes, 0 = no)
@ -333,8 +346,7 @@ static hfp_connection_t * provide_hfp_connection_context_for_bd_addr(bd_addr_t b
* AG bit 5: Wide band speech (yes/no, 1 = yes, 0 = no)
*/
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_create_sdp_record(uint8_t * service, uint16_t service_uuid, int rfcomm_channel_nr, const char * name){
uint8_t* attribute;
de_create_sequence(service);
@ -396,8 +408,6 @@ void hfp_create_sdp_record(uint8_t * service, uint16_t service_uuid, int rfcomm_
// 0x0100 "Service Name"
de_add_number(service, DE_UINT, DE_SIZE_16, 0x0100);
de_add_data(service, DE_STRING, strlen(name), (uint8_t *) name);
de_add_number(service, DE_UINT, DE_SIZE_16, supported_features);
}
static hfp_connection_t * connection_doing_sdp_query = NULL;
@ -439,6 +449,8 @@ void hfp_handle_hci_event(hfp_callback_t callback, uint8_t packet_type, uint8_t
uint16_t rfcomm_cid, handle;
hfp_connection_t * context = NULL;
// printf("AG packet_handler type %u, packet[0] %x, size %u\n", packet_type, packet[0], size);
switch (packet[0]) {
case BTSTACK_EVENT_STATE:
// bt stack activated, get started
@ -457,7 +469,7 @@ void hfp_handle_hci_event(hfp_callback_t callback, uint8_t packet_type, uint8_t
case RFCOMM_EVENT_INCOMING_CONNECTION:
// data: event (8), len(8), address(48), channel (8), rfcomm_cid (16)
bt_flip_addr(event_addr, &packet[2]);
context = get_hfp_connection_context_for_bd_addr(event_addr);
context = provide_hfp_connection_context_for_bd_addr(event_addr);
if (!context || context->state != HFP_IDLE) return;
@ -469,7 +481,8 @@ 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]);
printf("RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE packet_handler type %u, packet[0] %x, size %u\n", packet_type, packet[0], size);
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 +492,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);
@ -494,17 +509,27 @@ void hfp_handle_hci_event(hfp_callback_t callback, uint8_t packet_type, uint8_t
default:
break;
}
// forward event to app, to learn about con_handle
(*callback)(packet, size);
}
break;
case HCI_EVENT_SYNCHRONOUS_CONNECTION_COMPLETE:{
bt_flip_addr(event_addr, &packet[5]);
int index = 2;
uint8_t status = packet[index++];
if (status != 0){
log_error("(e)SCO Connection is not established, status %u", status);
break;
}
uint16_t sco_handle = READ_BT_16(packet, index);
index+=2;
bd_addr_t address;
memcpy(address, &packet[index], 6);
bt_flip_addr(event_addr, &packet[index]);
index+=6;
uint8_t link_type = packet[index++];
uint8_t transmission_interval = packet[index++]; // measured in slots
uint8_t retransmission_interval = packet[index++];// measured in slots
@ -514,39 +539,41 @@ void hfp_handle_hci_event(hfp_callback_t callback, uint8_t packet_type, uint8_t
index+=2;
uint8_t air_mode = packet[index];
if (status != 0){
log_error("(e)SCO Connection is not established, status %u", status);
break;
}
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. \n");
break;
default:
log_error("(e)SCO reserved link_type 0x%2x", link_type);
break;
}
log_info("sco_handle 0x%2x, address %s, transmission_interval %u slots, retransmission_interval %u slots, "
" rx_packet_length %u bytes, tx_packet_length %u bytes, air_mode 0x%2x (0x02 == CVSD)", sco_handle,
bd_addr_to_str(address), transmission_interval, retransmission_interval, rx_packet_length, tx_packet_length, air_mode);
" rx_packet_length %u bytes, tx_packet_length %u bytes, air_mode 0x%2x (0x02 == CVSD)\n", sco_handle,
bd_addr_to_str(event_addr), transmission_interval, retransmission_interval, rx_packet_length, tx_packet_length, air_mode);
context = get_hfp_connection_context_for_bd_addr(address);
context = get_hfp_connection_context_for_bd_addr(event_addr);
if (!context) {
log_error("SCO link created, context for address %s not found.", bd_addr_to_str(event_addr));
break;
}
if (context->state == HFP_W4_CONNECTION_ESTABLISHED_TO_SHUTDOWN){
log_info("SCO about to disconnect: HFP_W4_CONNECTION_ESTABLISHED_TO_SHUTDOWN");
context->state = HFP_W2_DISCONNECT_SCO;
break;
}
context->sco_handle = sco_handle;
context->establish_audio_connection = 0;
context->state = HFP_AUDIO_CONNECTION_ESTABLISHED;
hfp_emit_event(callback, HFP_SUBEVENT_AUDIO_CONNECTION_COMPLETE, packet[2]);
hfp_emit_audio_connection_established_event(callback, packet[2], sco_handle);
break;
}
@ -566,15 +593,23 @@ 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");
}
log_info("Check SCO handle: incoming 0x%02x, context 0x%02x\n", handle,context->sco_handle);
if (handle == context->sco_handle){
log_info("SCO disconnected, w2 disconnect RFCOMM\n");
context->sco_handle = 0;
context->release_audio_connection = 0;
context->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED;
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:
@ -582,145 +617,171 @@ void hfp_handle_hci_event(hfp_callback_t callback, uint8_t packet_type, uint8_t
}
}
// translates command string into hfp_command_t CMD and flags to distinguish between CMD=, CMD?, CMD=?
static void process_command(hfp_connection_t * context){
if (context->line_size < 2) return;
// printf("process_command %s\n", context->line_buffer);
context->command = HFP_CMD_NONE;
int offset = 0;
int isHandsFree = 1;
// translates command string into hfp_command_t CMD
static hfp_command_t parse_command(const char * line_buffer, int isHandsFree){
int offset = isHandsFree ? 0 : 2;
if (strncmp((char *)context->line_buffer, "AT", 2) == 0){
offset = 2;
isHandsFree = 0;
if (strncmp(line_buffer+offset, HFP_PHONE_NUMBER_FOR_VOICE_TAG, strlen(HFP_PHONE_NUMBER_FOR_VOICE_TAG)) == 0){
if (isHandsFree) return HFP_CMD_AG_SEND_PHONE_NUMBER;
return HFP_CMD_HF_REQUEST_PHONE_NUMBER;
}
if (strncmp(line_buffer+offset, HFP_TRANSMIT_DTMF_CODES, strlen(HFP_TRANSMIT_DTMF_CODES)) == 0){
return HFP_CMD_TRANSMIT_DTMF_CODES;
}
if (strncmp(line_buffer+offset, HFP_SET_MICROPHONE_GAIN, strlen(HFP_SET_MICROPHONE_GAIN)) == 0){
return HFP_CMD_SET_MICROPHONE_GAIN;
}
if (strncmp(line_buffer+offset, HFP_SET_SPEAKER_GAIN, strlen(HFP_SET_SPEAKER_GAIN)) == 0){
return HFP_CMD_SET_SPEAKER_GAIN;
}
if (strncmp((char *)context->line_buffer+offset, HFP_ERROR, strlen(HFP_ERROR)) == 0){
context->command = HFP_CMD_ERROR;
return;
if (strncmp(line_buffer+offset, HFP_ACTIVATE_VOICE_RECOGNITION, strlen(HFP_ACTIVATE_VOICE_RECOGNITION)) == 0){
if (isHandsFree) return HFP_CMD_AG_ACTIVATE_VOICE_RECOGNITION;
return HFP_CMD_HF_ACTIVATE_VOICE_RECOGNITION;
}
if (isHandsFree && strncmp((char *)context->line_buffer+offset, HFP_OK, strlen(HFP_OK)) == 0){
//printf("parsed HFP_CMD_OK \n");
context->command = HFP_CMD_OK;
return;
if (strncmp(line_buffer+offset, HFP_TURN_OFF_EC_AND_NR, strlen(HFP_TURN_OFF_EC_AND_NR)) == 0){
return HFP_CMD_TURN_OFF_EC_AND_NR;
}
if (strncmp((char *)context->line_buffer+offset, HFP_SUPPORTED_FEATURES, strlen(HFP_SUPPORTED_FEATURES)) == 0){
context->command = HFP_CMD_SUPPORTED_FEATURES;
return;
if (strncmp(line_buffer, HFP_CALL_ANSWERED, strlen(HFP_CALL_ANSWERED)) == 0){
return HFP_CMD_CALL_ANSWERED;
}
if (strncmp((char *)context->line_buffer+offset, HFP_INDICATOR, strlen(HFP_INDICATOR)) == 0){
//printf("parsed HFP_INDICATOR \n");
context->command = HFP_CMD_INDICATOR;
if (isHandsFree) return;
if (strncmp(line_buffer, HFP_CALL_PHONE_NUMBER, strlen(HFP_CALL_PHONE_NUMBER)) == 0){
return HFP_CMD_CALL_PHONE_NUMBER;
}
if (strncmp(line_buffer, HFP_REDIAL_LAST_NUMBER, strlen(HFP_REDIAL_LAST_NUMBER)) == 0){
return HFP_CMD_REDIAL_LAST_NUMBER;
}
if (strncmp(line_buffer+offset, HFP_CHANGE_IN_BAND_RING_TONE_SETTING, strlen(HFP_CHANGE_IN_BAND_RING_TONE_SETTING)) == 0){
return HFP_CMD_CHANGE_IN_BAND_RING_TONE_SETTING;
}
if (strncmp(line_buffer+offset, HFP_HANG_UP_CALL, strlen(HFP_HANG_UP_CALL)) == 0){
return HFP_CMD_HANG_UP_CALL;
}
if (strncmp(line_buffer+offset, HFP_ERROR, strlen(HFP_ERROR)) == 0){
return HFP_CMD_ERROR;
}
if (isHandsFree && strncmp(line_buffer+offset, HFP_OK, strlen(HFP_OK)) == 0){
return HFP_CMD_OK;
}
if (strncmp(line_buffer+offset, HFP_SUPPORTED_FEATURES, strlen(HFP_SUPPORTED_FEATURES)) == 0){
return HFP_CMD_SUPPORTED_FEATURES;
}
if (strncmp(line_buffer+offset, HFP_INDICATOR, strlen(HFP_INDICATOR)) == 0){
if (strncmp(line_buffer+strlen(HFP_INDICATOR)+offset, "?", 1) == 0){
return HFP_CMD_RETRIEVE_AG_INDICATORS_STATUS;
}
if (strncmp(line_buffer+strlen(HFP_INDICATOR)+offset, "=?", 2) == 0){
return HFP_CMD_RETRIEVE_AG_INDICATORS;
}
}
if (strncmp(line_buffer+offset, HFP_AVAILABLE_CODECS, strlen(HFP_AVAILABLE_CODECS)) == 0){
return HFP_CMD_AVAILABLE_CODECS;
}
if (strncmp(line_buffer+offset, HFP_ENABLE_STATUS_UPDATE_FOR_AG_INDICATORS, strlen(HFP_ENABLE_STATUS_UPDATE_FOR_AG_INDICATORS)) == 0){
return HFP_CMD_ENABLE_INDICATOR_STATUS_UPDATE;
}
if (strncmp(line_buffer+offset, HFP_ENABLE_CLIP, strlen(HFP_ENABLE_CLIP)) == 0){
return HFP_CMD_ENABLE_CLIP;
}
if (strncmp(line_buffer+offset, HFP_ENABLE_CALL_WAITING_NOTIFICATION, strlen(HFP_ENABLE_CALL_WAITING_NOTIFICATION)) == 0){
return HFP_CMD_ENABLE_CALL_WAITING_NOTIFICATION;
}
if (strncmp(line_buffer+offset, HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES, strlen(HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES)) == 0){
if (strncmp((char *)context->line_buffer+strlen(HFP_INDICATOR)+offset, "?", 1) == 0){
context->retrieve_ag_indicators_status = 1;
context->retrieve_ag_indicators = 0;
} else {
context->retrieve_ag_indicators = 1;
context->retrieve_ag_indicators_status = 0;
if (isHandsFree) return HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES;
if (strncmp(line_buffer+strlen(HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES)+offset, "=?", 2) == 0){
return HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES;
}
return;
}
if (strncmp((char *)context->line_buffer+offset, HFP_AVAILABLE_CODECS, strlen(HFP_AVAILABLE_CODECS)) == 0){
context->command = HFP_CMD_AVAILABLE_CODECS;
context->notify_ag_on_new_codecs = 1;
return;
}
if (strncmp((char *)context->line_buffer+offset, HFP_ENABLE_STATUS_UPDATE_FOR_AG_INDICATORS, strlen(HFP_ENABLE_STATUS_UPDATE_FOR_AG_INDICATORS)) == 0){
context->command = HFP_CMD_ENABLE_INDICATOR_STATUS_UPDATE;
return;
}
if (strncmp((char *)context->line_buffer+offset, HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES, strlen(HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES)) == 0){
context->command = HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES;
return;
}
if (strncmp((char *)context->line_buffer+offset, HFP_GENERIC_STATUS_INDICATOR, strlen(HFP_GENERIC_STATUS_INDICATOR)) == 0){
context->command = HFP_CMD_GENERIC_STATUS_INDICATOR;
if (isHandsFree) return;
if (strncmp((char *)context->line_buffer+strlen(HFP_GENERIC_STATUS_INDICATOR)+offset, "=?", 2) == 0){
context->list_generic_status_indicators = 0;
context->retrieve_generic_status_indicators = 1;
context->retrieve_generic_status_indicators_state = 0;
} else if (strncmp((char *)context->line_buffer+strlen(HFP_GENERIC_STATUS_INDICATOR)+offset, "=", 1) == 0){
context->list_generic_status_indicators = 1;
context->retrieve_generic_status_indicators = 0;
context->retrieve_generic_status_indicators_state = 0;
} else {
context->list_generic_status_indicators = 0;
context->retrieve_generic_status_indicators = 0;
context->retrieve_generic_status_indicators_state = 1;
if (strncmp(line_buffer+strlen(HFP_GENERIC_STATUS_INDICATOR)+offset, "=", 1) == 0){
return HFP_CMD_CALL_HOLD;
}
return;
return HFP_CMD_UNKNOWN;
}
if (strncmp((char *)context->line_buffer+offset, HFP_UPDATE_ENABLE_STATUS_FOR_INDIVIDUAL_AG_INDICATORS, strlen(HFP_UPDATE_ENABLE_STATUS_FOR_INDIVIDUAL_AG_INDICATORS)) == 0){
context->command = HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE;
return;
}
if (strncmp(line_buffer+offset, HFP_GENERIC_STATUS_INDICATOR, strlen(HFP_GENERIC_STATUS_INDICATOR)) == 0){
if (isHandsFree) return HFP_CMD_UNKNOWN;
if (strncmp((char *)context->line_buffer+offset, HFP_QUERY_OPERATOR_SELECTION, strlen(HFP_QUERY_OPERATOR_SELECTION)) == 0){
context->command = HFP_CMD_QUERY_OPERATOR_SELECTION;
context->operator_name = 1;
context->operator_name_format = 0;
if (isHandsFree) return;
context->operator_name = 0;
if (strncmp((char *)context->line_buffer+strlen(HFP_QUERY_OPERATOR_SELECTION)+offset, "=", 1) == 0){
context->operator_name_format = 1;
if (strncmp(line_buffer+strlen(HFP_GENERIC_STATUS_INDICATOR)+offset, "=?", 2) == 0){
return HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS;
}
return;
}
if (strncmp((char *)context->line_buffer+offset, HFP_TRANSFER_AG_INDICATOR_STATUS, strlen(HFP_TRANSFER_AG_INDICATOR_STATUS)) == 0){
context->command = HFP_CMD_TRANSFER_AG_INDICATOR_STATUS;
return;
}
if (isHandsFree && strncmp((char *)context->line_buffer+offset, HFP_EXTENDED_AUDIO_GATEWAY_ERROR, strlen(HFP_EXTENDED_AUDIO_GATEWAY_ERROR)) == 0){
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){
context->command = HFP_CMD_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR;
return;
}
if (strncmp((char *)context->line_buffer+offset, HFP_TRIGGER_CODEC_CONNECTION_SETUP, strlen(HFP_TRIGGER_CODEC_CONNECTION_SETUP)) == 0){
context->command = HFP_CMD_TRIGGER_CODEC_CONNECTION_SETUP;
// printf("HFP_CMD_TRIGGER_CODEC_CONNECTION_SETUP update command\n");
if (isHandsFree){
context->hf_trigger_codec_connection_setup = 1;
printf("update command: hf_trigger_codec_connection_setup = 1\n");
} else {
context->hf_trigger_codec_connection_setup = 1;
printf("update command: hf_trigger_codec_connection_setup = 1\n");
if (strncmp(line_buffer+strlen(HFP_GENERIC_STATUS_INDICATOR)+offset, "=", 1) == 0){
return HFP_CMD_LIST_GENERIC_STATUS_INDICATORS;
}
return;
return HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS_STATE;
}
if (strncmp((char *)context->line_buffer+offset, HFP_CONFIRM_COMMON_CODEC, strlen(HFP_CONFIRM_COMMON_CODEC)) == 0){
if (!isHandsFree){
context->command = HFP_CMD_HF_CONFIRMED_CODEC;
} else {
context->command = HFP_CMD_AG_SUGGESTED_CODEC;
}
return;
if (strncmp(line_buffer+offset, HFP_UPDATE_ENABLE_STATUS_FOR_INDIVIDUAL_AG_INDICATORS, strlen(HFP_UPDATE_ENABLE_STATUS_FOR_INDIVIDUAL_AG_INDICATORS)) == 0){
return HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE;
}
if (strncmp((char *)context->line_buffer+offset, "NOP", 3) == 0) return;
printf(" process unknown command 3 %s \n", context->line_buffer);
if (strncmp(line_buffer+offset, HFP_QUERY_OPERATOR_SELECTION, strlen(HFP_QUERY_OPERATOR_SELECTION)) == 0){
if (strncmp(line_buffer+strlen(HFP_QUERY_OPERATOR_SELECTION)+offset, "=", 1) == 0){
return HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT;
}
return HFP_CMD_QUERY_OPERATOR_SELECTION_NAME;
}
if (strncmp(line_buffer+offset, HFP_TRANSFER_AG_INDICATOR_STATUS, strlen(HFP_TRANSFER_AG_INDICATOR_STATUS)) == 0){
return HFP_CMD_TRANSFER_AG_INDICATOR_STATUS;
}
if (isHandsFree && strncmp(line_buffer+offset, HFP_EXTENDED_AUDIO_GATEWAY_ERROR, strlen(HFP_EXTENDED_AUDIO_GATEWAY_ERROR)) == 0){
return HFP_CMD_EXTENDED_AUDIO_GATEWAY_ERROR;
}
if (!isHandsFree && strncmp(line_buffer+offset, HFP_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR, strlen(HFP_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR)) == 0){
return HFP_CMD_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR;
}
if (strncmp(line_buffer+offset, HFP_TRIGGER_CODEC_CONNECTION_SETUP, strlen(HFP_TRIGGER_CODEC_CONNECTION_SETUP)) == 0){
return HFP_CMD_TRIGGER_CODEC_CONNECTION_SETUP;
}
if (strncmp(line_buffer+offset, HFP_CONFIRM_COMMON_CODEC, strlen(HFP_CONFIRM_COMMON_CODEC)) == 0){
if (isHandsFree){
return HFP_CMD_AG_SUGGESTED_CODEC;
} else {
return HFP_CMD_HF_CONFIRMED_CODEC;
}
}
if (strncmp(line_buffer+offset, "AT+", 3) == 0){
printf(" process unknown HF command %s \n", line_buffer);
return HFP_CMD_UNKNOWN;
}
if (strncmp(line_buffer+offset, "+", 1) == 0){
printf(" process unknown AG command %s \n", line_buffer);
return HFP_CMD_UNKNOWN;
}
if (strncmp(line_buffer+offset, "NOP", 3) == 0){
return HFP_CMD_NONE;
}
return HFP_CMD_NONE;
}
#if 0
@ -773,20 +834,15 @@ static void hfp_parser_next_state(hfp_connection_t * context, uint8_t byte){
case HFP_PARSER_CMD_SEQUENCE:
switch (context->command){
case HFP_CMD_TRANSFER_AG_INDICATOR_STATUS:
case HFP_CMD_QUERY_OPERATOR_SELECTION:
case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME:
case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT:
context->parser_state = HFP_PARSER_SECOND_ITEM;
break;
case HFP_CMD_INDICATOR:
if (context->retrieve_ag_indicators == 1){
context->parser_state = HFP_PARSER_SECOND_ITEM;
break;
}
case HFP_CMD_RETRIEVE_AG_INDICATORS:
context->parser_state = HFP_PARSER_SECOND_ITEM;
break;
case HFP_CMD_GENERIC_STATUS_INDICATOR:
if (context->retrieve_generic_status_indicators_state == 1){
context->parser_state = HFP_PARSER_SECOND_ITEM;
break;
}
case HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS_STATE:
context->parser_state = HFP_PARSER_SECOND_ITEM;
break;
default:
break;
@ -796,7 +852,7 @@ static void hfp_parser_next_state(hfp_connection_t * context, uint8_t byte){
context->parser_state = HFP_PARSER_THIRD_ITEM;
break;
case HFP_PARSER_THIRD_ITEM:
if (context->command == HFP_CMD_INDICATOR && context->retrieve_ag_indicators){
if (context->command == HFP_CMD_RETRIEVE_AG_INDICATORS){
context->parser_state = HFP_PARSER_CMD_SEQUENCE;
break;
}
@ -805,9 +861,22 @@ static void hfp_parser_next_state(hfp_connection_t * context, uint8_t byte){
}
}
void hfp_parse(hfp_connection_t * context, uint8_t byte){
void hfp_parse(hfp_connection_t * context, uint8_t byte, int isHandsFree){
int value;
// handle ATD<dial_string>;
if (strncmp((const char*)context->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;
} else {
context->line_buffer[context->line_size++] = byte;
}
return;
}
// TODO: handle space inside word
if (byte == ' ' && context->parser_state > HFP_PARSER_CMD_HEADER) return;
@ -839,12 +908,61 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte){
// printf(" parse header 2 %s, keep separator $ %d\n", context->line_buffer, context->keep_separator);
if (hfp_parser_is_end_of_header(byte) || context->keep_separator == 1){
// printf(" parse header 3 %s, keep separator $ %d\n", context->line_buffer, context->keep_separator);
process_command(context);
char * line_buffer = (char *)context->line_buffer;
context->command = parse_command(line_buffer, isHandsFree);
/* resolve command name according to context */
if (context->command == HFP_CMD_UNKNOWN){
switch(context->state){
case HFP_W4_LIST_GENERIC_STATUS_INDICATORS:
context->command = HFP_CMD_LIST_GENERIC_STATUS_INDICATORS;
break;
case HFP_W4_RETRIEVE_GENERIC_STATUS_INDICATORS:
context->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;
break;
case HFP_W4_RETRIEVE_INDICATORS_STATUS:
context->command = HFP_CMD_RETRIEVE_AG_INDICATORS_STATUS;
break;
case HFP_W4_RETRIEVE_INDICATORS:
context->command = HFP_CMD_RETRIEVE_AG_INDICATORS;
break;
default:
break;
}
}
}
break;
case HFP_PARSER_CMD_SEQUENCE: // parse comma separated sequence, ignore breacktes
switch (context->command){
case HFP_CMD_SET_MICROPHONE_GAIN:
value = atoi((char *)&context->line_buffer[0]);
context->microphone_gain = value;
log_info("hfp parse HFP_CMD_SET_MICROPHONE_GAIN %d\n", value);
break;
case HFP_CMD_SET_SPEAKER_GAIN:
value = atoi((char *)&context->line_buffer[0]);
context->speaker_gain = value;
log_info("hfp parse HFP_CMD_SET_SPEAKER_GAIN %d\n", value);
break;
case HFP_CMD_HF_ACTIVATE_VOICE_RECOGNITION:
value = atoi((char *)&context->line_buffer[0]);
context->ag_activate_voice_recognition = value;
log_info("hfp parse HFP_CMD_HF_ACTIVATE_VOICE_RECOGNITION %d\n", value);
break;
case HFP_CMD_TURN_OFF_EC_AND_NR:
value = atoi((char *)&context->line_buffer[0]);
context->ag_echo_and_noise_reduction = value;
log_info("hfp parse HFP_CMD_TURN_OFF_EC_AND_NR %d\n", value);
break;
case HFP_CMD_CHANGE_IN_BAND_RING_TONE_SETTING:
value = atoi((char *)&context->line_buffer[0]);
context->remote_supported_features = store_bit(context->remote_supported_features, HFP_AGSF_IN_BAND_RING_TONE, value);
log_info("hfp parse HFP_CHANGE_IN_BAND_RING_TONE_SETTING %d\n", value);
break;
case HFP_CMD_HF_CONFIRMED_CODEC:
context->codec_confirmed = atoi((char*)context->line_buffer);
log_info("hfp parse HFP_CMD_HF_CONFIRMED_CODEC %d\n", context->codec_confirmed);
@ -863,19 +981,15 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte){
context->parser_item_index++;
context->remote_codecs_nr = context->parser_item_index;
break;
case HFP_CMD_INDICATOR:
if (context->retrieve_ag_indicators == 1){
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);
}
if (context->retrieve_ag_indicators_status == 1){
log_info("Parsed Indicator %d with status: %s\n", context->parser_item_index+1, context->line_buffer);
context->ag_indicators[context->parser_item_index].status = atoi((char *) context->line_buffer);
context->parser_item_index++;
break;
}
case HFP_CMD_RETRIEVE_AG_INDICATORS:
strcpy((char *)context->ag_indicators[context->parser_item_index].name, (char *)context->line_buffer);
context->ag_indicators[context->parser_item_index].index = context->parser_item_index+1;
log_info("Indicator %d: %s (", context->ag_indicators_nr+1, context->line_buffer);
break;
case HFP_CMD_RETRIEVE_AG_INDICATORS_STATUS:
log_info("Parsed Indicator %d with status: %s\n", context->parser_item_index+1, context->line_buffer);
context->ag_indicators[context->parser_item_index].status = atoi((char *) context->line_buffer);
context->parser_item_index++;
break;
case HFP_CMD_ENABLE_INDICATOR_STATUS_UPDATE:
context->parser_item_index++;
@ -890,27 +1004,18 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte){
strcpy((char *)context->remote_call_services[context->remote_call_services_nr].name, (char *)context->line_buffer);
context->remote_call_services_nr++;
break;
case HFP_CMD_GENERIC_STATUS_INDICATOR:
log_info("parser HFP_CMD_GENERIC_STATUS_INDICATOR 1 (%d, %d, %d)\n",
context->list_generic_status_indicators,
context->retrieve_generic_status_indicators,
context->retrieve_generic_status_indicators_state);
if (context->retrieve_generic_status_indicators == 1 || context->list_generic_status_indicators == 1){
log_info("Parsed Generic status indicator: %s\n", context->line_buffer);
context->generic_status_indicators[context->parser_item_index].uuid = (uint16_t)atoi((char*)context->line_buffer);
context->parser_item_index++;
context->generic_status_indicators_nr = context->parser_item_index;
break;
}
log_info("parser HFP_CMD_GENERIC_STATUS_INDICATOR 2\n");
if (context->retrieve_generic_status_indicators_state == 1){
// HF parses inital AG gen. ind. state
log_info("Parsed List generic status indicator %s state: ", context->line_buffer);
context->parser_item_index = (uint8_t)atoi((char*)context->line_buffer);
break;
}
case HFP_CMD_LIST_GENERIC_STATUS_INDICATORS:
case HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS:
log_info("Parsed Generic status indicator: %s\n", context->line_buffer);
context->generic_status_indicators[context->parser_item_index].uuid = (uint16_t)atoi((char*)context->line_buffer);
context->parser_item_index++;
context->generic_status_indicators_nr = context->parser_item_index;
break;
case HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS_STATE:
// HF parses inital AG gen. ind. state
log_info("Parsed List generic status indicator %s state: ", context->line_buffer);
context->parser_item_index = (uint8_t)atoi((char*)context->line_buffer);
break;
case HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE:
// AG parses new gen. ind. state
log_info("Parsed Enable ag indicator state: %s\n", context->line_buffer);
@ -925,22 +1030,17 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte){
context->parser_item_index = atoi((char *)&context->line_buffer[0]) - 1;
log_info("Parsed status of the AG indicator %d, status ", context->parser_item_index);
break;
case HFP_CMD_QUERY_OPERATOR_SELECTION:
if (context->operator_name_format == 1){
if (context->line_buffer[0] == '3'){
log_info("Parsed Set network operator format : %s, ", context->line_buffer);
break;
}
// TODO emit ERROR, wrong format
log_info("ERROR Set network operator format: index %s not supported\n", context->line_buffer);
break;
}
if (context->operator_name == 1) {
context->network_operator.mode = atoi((char *)&context->line_buffer[0]);
log_info("Parsed network operator mode: %d, ", context->network_operator.mode);
case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME:
context->network_operator.mode = atoi((char *)&context->line_buffer[0]);
log_info("Parsed network operator mode: %d, ", context->network_operator.mode);
break;
case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT:
if (context->line_buffer[0] == '3'){
log_info("Parsed Set network operator format : %s, ", context->line_buffer);
break;
}
// TODO emit ERROR, wrong format
log_info("ERROR Set network operator format: index %s not supported\n", context->line_buffer);
break;
case HFP_CMD_ERROR:
break;
@ -949,7 +1049,7 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte){
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->ok_pending = 1;
context->extended_audio_gateway_error = 0;
break;
default:
@ -959,29 +1059,27 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte){
case HFP_PARSER_SECOND_ITEM:
switch (context->command){
case HFP_CMD_QUERY_OPERATOR_SELECTION:
if (context->operator_name_format == 1) {
log_info("format %s \n", context->line_buffer);
context->network_operator.format = atoi((char *)&context->line_buffer[0]);
break;
}
if (context->operator_name == 1){
log_info("format %s, ", context->line_buffer);
context->network_operator.format = atoi((char *)&context->line_buffer[0]);
}
case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME:
printf("format %s, ", context->line_buffer);
context->network_operator.format = atoi((char *)&context->line_buffer[0]);
break;
case HFP_CMD_GENERIC_STATUS_INDICATOR:
case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT:
printf("format %s \n", context->line_buffer);
context->network_operator.format = atoi((char *)&context->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);
break;
case HFP_CMD_TRANSFER_AG_INDICATOR_STATUS:
context->ag_indicators[context->parser_item_index].status = (uint8_t)atoi((char*)context->line_buffer);
printf("%d \n", context->ag_indicators[context->parser_item_index].status);
context->ag_indicators[context->parser_item_index].status_changed = 1;
break;
case HFP_CMD_INDICATOR:
if (context->retrieve_ag_indicators == 1){
context->ag_indicators[context->parser_item_index].min_range = atoi((char *)context->line_buffer);
log_info("%s, ", context->line_buffer);
}
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);
break;
default:
break;
@ -990,19 +1088,15 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte){
case HFP_PARSER_THIRD_ITEM:
switch (context->command){
case HFP_CMD_QUERY_OPERATOR_SELECTION:
if (context->operator_name == 1){
strcpy(context->network_operator.name, (char *)context->line_buffer);
log_info("name %s\n", context->line_buffer);
}
case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME:
strcpy(context->network_operator.name, (char *)context->line_buffer);
log_info("name %s\n", context->line_buffer);
break;
case HFP_CMD_INDICATOR:
if (context->retrieve_ag_indicators == 1){
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);
}
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);
break;
default:
break;
@ -1025,6 +1119,7 @@ void hfp_establish_service_level_connection(bd_addr_t bd_addr, uint16_t service_
log_error("hfp_establish_service_level_connection for addr %s failed", bd_addr_to_str(bd_addr));
return;
}
switch (context->state){
case HFP_W2_DISCONNECT_RFCOMM:
context->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED;
@ -1057,7 +1152,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;
}
@ -1070,10 +1165,10 @@ void hfp_release_service_level_connection(hfp_connection_t * context){
return;
}
void hfp_release_audio_connection(hfp_connection_t * connection){
if (!connection) return;
if (connection->state >= HFP_W2_DISCONNECT_SCO) return;
connection->release_audio_connection = 1;
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;
}

View File

@ -66,8 +66,9 @@ extern "C" {
9: eSCO S4 (and T2) Settings Supported
10-31: Reserved for future definition
*/
#define HFP_HFSF_THREE_WAY_CALLING 1
#define HFP_HFSF_EC_NR_FUNCTION 0
#define HFP_HFSF_THREE_WAY_CALLING 1
#define HFP_HFSF_VOICE_RECOGNITION_FUNCTION 3
#define HFP_HFSF_CODEC_NEGOTIATION 7
#define HFP_HFSF_HF_INDICATORS 8
#define HFP_HFSF_ESCO 9
@ -89,6 +90,8 @@ extern "C" {
*/
#define HFP_AGSF_THREE_WAY_CALLING 0
#define HFP_AGSF_EC_NR_FUNCTION 1
#define HFP_AGSF_VOICE_RECOGNITION_FUNCTION 2
#define HFP_AGSF_IN_BAND_RING_TONE 3
#define HFP_AGSF_CODEC_NEGOTIATION 9
#define HFP_AGSF_HF_INDICATORS 10
#define HFP_AGSF_ESCO 11
@ -105,6 +108,8 @@ extern "C" {
#define HFP_AVAILABLE_CODECS "+BAC"
#define HFP_INDICATOR "+CIND"
#define HFP_ENABLE_STATUS_UPDATE_FOR_AG_INDICATORS "+CMER"
#define HFP_ENABLE_CLIP "+CLIP"
#define HFP_ENABLE_CALL_WAITING_NOTIFICATION "+CCWA"
#define HFP_UPDATE_ENABLE_STATUS_FOR_INDIVIDUAL_AG_INDICATORS "+BIA" // +BIA:<enabled>,,<enabled>,,,<enabled>
#define HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES "+CHLD"
#define HFP_GENERIC_STATUS_INDICATOR "+BIND"
@ -114,6 +119,19 @@ extern "C" {
#define HFP_EXTENDED_AUDIO_GATEWAY_ERROR "+CME ERROR"
#define HFP_TRIGGER_CODEC_CONNECTION_SETUP "+BCC"
#define HFP_CONFIRM_COMMON_CODEC "+BCS"
#define HFP_CALL_ANSWERED "ATA"
#define HFP_HANG_UP_CALL "+CHUP"
#define HFP_CHANGE_IN_BAND_RING_TONE_SETTING "+BSIR"
#define HFP_CALL_PHONE_NUMBER "ATD"
#define HFP_REDIAL_LAST_NUMBER "AT+BLDN"
#define HFP_TURN_OFF_EC_AND_NR "+NREC" // EC (Echo CAnceling), NR (Noise Reduction)
#define HFP_ACTIVATE_VOICE_RECOGNITION "+BVRA" // EC (Echo CAnceling), NR (Noise Reduction)
#define HFP_SET_MICROPHONE_GAIN "+VGM"
#define HFP_SET_SPEAKER_GAIN "+VGS"
#define HFP_PHONE_NUMBER_FOR_VOICE_TAG "+BINP"
#define HFP_TRANSMIT_DTMF_CODES "+VTS"
#define HFP_OK "OK"
#define HFP_ERROR "ERROR"
@ -125,26 +143,52 @@ extern "C" {
typedef enum {
HFP_CMD_NONE = 0,
HFP_CMD_ERROR,
HFP_CMD_UNKNOWN,
HFP_CMD_OK,
HFP_CMD_SUPPORTED_FEATURES,
HFP_CMD_AVAILABLE_CODECS,
HFP_CMD_INDICATOR, // 5
HFP_CMD_RETRIEVE_AG_INDICATORS,
HFP_CMD_RETRIEVE_AG_INDICATORS_STATUS,
HFP_CMD_ENABLE_INDICATOR_STATUS_UPDATE,
HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE,
HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES,
HFP_CMD_ENABLE_CLIP,
HFP_CMD_ENABLE_CALL_WAITING_NOTIFICATION,
HFP_CMD_GENERIC_STATUS_INDICATOR,
HFP_CMD_LIST_GENERIC_STATUS_INDICATORS,
HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS,
HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS_STATE,
HFP_CMD_TRANSFER_AG_INDICATOR_STATUS,
HFP_CMD_QUERY_OPERATOR_SELECTION,
HFP_CMD_QUERY_OPERATOR_SELECTION_NAME,
HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT,
HFP_CMD_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR,
HFP_CMD_EXTENDED_AUDIO_GATEWAY_ERROR,
HFP_CMD_TRIGGER_CODEC_CONNECTION_SETUP,
HFP_CMD_AG_SEND_COMMON_CODEC,
HFP_CMD_AG_SUGGESTED_CODEC,
HFP_CMD_HF_CONFIRMED_CODEC
HFP_CMD_HF_CONFIRMED_CODEC,
HFP_CMD_CALL_ANSWERED,
HFP_CMD_CALL_HOLD,
HFP_CMD_AG_ANSWER_CALL,
HFP_CMD_HANG_UP_CALL,
HFP_CMD_CHANGE_IN_BAND_RING_TONE_SETTING,
HFP_CMD_CALL_PHONE_NUMBER,
HFP_CMD_REDIAL_LAST_NUMBER,
HFP_CMD_TURN_OFF_EC_AND_NR,
HFP_CMD_AG_ACTIVATE_VOICE_RECOGNITION,
HFP_CMD_HF_ACTIVATE_VOICE_RECOGNITION,
HFP_CMD_HF_REQUEST_PHONE_NUMBER,
HFP_CMD_AG_SEND_PHONE_NUMBER,
HFP_CMD_TRANSMIT_DTMF_CODES,
HFP_CMD_SET_MICROPHONE_GAIN,
HFP_CMD_SET_SPEAKER_GAIN
} hfp_command_t;
typedef enum {
HFP_CME_ERROR_AG_FAILURE = 0,
@ -195,11 +239,28 @@ typedef enum {
} hfp_callsetup_status_t;
typedef enum {
HFP_HELDCALL_STATUS_NO_CALLS_HELD = 0,
HFP_HELDCALL_STATUS_CALL_ON_HOLD_OR_SWAPPED,
HFP_HELDCALL_STATUS_CALL_ON_HOLD_AND_NO_ACTIVE_CALLS
HFP_CALLHELD_STATUS_NO_CALLS_HELD = 0,
HFP_CALLHELD_STATUS_CALL_ON_HOLD_OR_SWAPPED,
HFP_CALLHELD_STATUS_CALL_ON_HOLD_AND_NO_ACTIVE_CALLS
} hfp_callheld_status_t;
typedef enum {
HFP_AG_INCOMING_CALL,
HFP_AG_INCOMING_CALL_ACCEPTED_BY_AG,
HFP_AG_INCOMING_CALL_ACCEPTED_BY_HF,
HFP_AG_AUDIO_CONNECTION_ESTABLISHED,
HFP_AG_OUTGOING_CALL_INITIATED,
HFP_AG_OUTGOING_CALL_REJECTED,
HFP_AG_OUTGOING_CALL_ACCEPTED,
HFP_AG_OUTGOING_CALL_RINGING,
HFP_AG_OUTGOING_CALL_ESTABLISHED,
HFP_AG_OUTGOING_REDIAL_INITIATED,
HFP_AG_TERMINATE_CALL_BY_AG,
HFP_AG_TERMINATE_CALL_BY_HF,
HFP_AG_CALL_DROPPED,
} hfp_ag_call_event_t;
typedef enum {
HFP_PARSER_CMD_HEADER = 0,
HFP_PARSER_CMD_SEQUENCE,
@ -238,30 +299,59 @@ typedef enum {
HFP_RETRIEVE_GENERIC_STATUS_INDICATORS,
HFP_W4_RETRIEVE_GENERIC_STATUS_INDICATORS,
HFP_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS, // 20
HFP_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS,
HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS,
HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED, // 22
HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED,
HFP_SLE_W2_EXCHANGE_COMMON_CODEC,
HFP_SLE_W4_EXCHANGE_COMMON_CODEC,
HFP_W2_CONNECT_SCO,
HFP_W4_SCO_CONNECTED,
HFP_CODECS_CONNECTION_ESTABLISHED, // 25
HFP_CCE_W2_ESTABLISH_SCO,
HFP_CCE_W4_SCO_CONNECTION_ESTABLISHED,
HFP_AUDIO_CONNECTION_ESTABLISHED,
HFP_AUDIO_CONNECTION_ESTABLISHED,
HFP_W2_DISCONNECT_SCO,
HFP_W4_SCO_DISCONNECTED, // 30
HFP_W4_SCO_DISCONNECTED,
HFP_W2_DISCONNECT_RFCOMM,
HFP_W4_RFCOMM_DISCONNECTED,
HFP_W4_RFCOMM_DISCONNECTED_AND_RESTART,
HFP_W4_RFCOMM_DISCONNECTED_AND_RESTART,
HFP_W4_CONNECTION_ESTABLISHED_TO_SHUTDOWN
} hfp_state_t;
typedef enum {
HFP_CODECS_IDLE,
HFP_CODECS_RECEIVED_LIST,
HFP_CODECS_RECEIVED_TRIGGER_CODEC_EXCHANGE,
HFP_CODECS_W4_AG_COMMON_CODEC,
HFP_CODECS_AG_SENT_COMMON_CODEC,
HFP_CODECS_AG_RESEND_COMMON_CODEC,
HFP_CODECS_EXCHANGED,
HFP_CODECS_ERROR
} hfp_codecs_state_t;
typedef enum {
HFP_CALL_IDLE,
HFP_CALL_TRIGGER_AUDIO_CONNECTION,
HFP_CALL_W4_AUDIO_CONNECTION_FOR_IN_BAND_RING,
HFP_CALL_RINGING,
HFP_CALL_W4_AUDIO_CONNECTION_FOR_ACTIVE,
HFP_CALL_ACTIVE,
HFP_CALL_W2_SEND_CALL_WAITING,
HFP_CALL_W4_CHLD,
HFP_CALL_OUTGOING_INITIATED,
HFP_CALL_OUTGOING_DIALING,
HFP_CALL_OUTGOING_RINGING
} hfp_call_state_t;
typedef enum{
HFP_NONE_SM,
HFP_SLC_SM,
HFP_SLC_QUERIES_SM,
HFP_CODECS_CONNECTION_SM,
HFP_AUDIO_CONNECTION_SM,
HFP_CALL_SM
} hfp_state_machine_t;
typedef void (*hfp_callback_t)(uint8_t * event, uint16_t event_size);
typedef struct{
@ -291,7 +381,7 @@ typedef struct{
char name[17]; // enabled
} hfp_network_opearator_t;
typedef struct hfp_connection {
linked_item_t item;
@ -301,7 +391,11 @@ typedef struct hfp_connection {
uint16_t rfcomm_channel_nr;
uint16_t rfcomm_cid;
hfp_state_machine_t state_machine;
hfp_call_state_t call_state;
hfp_state_t state;
hfp_codecs_state_t codecs_state;
// needed for reestablishing connection
uint16_t service_uuid;
@ -333,43 +427,44 @@ typedef struct hfp_connection {
// Retrieved during service level connection establishment, not used yet
uint8_t negotiated_codec;
// HF -> AG configuration
uint8_t clip_enabled;
uint8_t call_waiting_notification_enabled;
// TODO: put these bit flags in a bitmap
uint8_t wait_ok;
uint8_t send_ok;
uint8_t ok_pending;
// uint8_t send_ok;
uint8_t send_error;
uint8_t keep_separator;
uint8_t retrieve_ag_indicators; // HFP_CMD_INDICATOR, check if needed
uint8_t retrieve_ag_indicators_status;
uint8_t list_generic_status_indicators; // HFP_CMD_LIST_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 change_status_update_for_individual_ag_indicators;
uint8_t operator_name_format;
uint8_t operator_name;
uint8_t operator_name_changed;
uint8_t enable_extended_audio_gateway_error_report;
uint8_t extended_audio_gateway_error;
// can come any time (here taken into account only after SLE),
// if codec negotiation feature is set
uint8_t notify_ag_on_new_codecs;
// establish codecs connection
uint8_t hf_trigger_codec_connection_setup;
uint8_t ag_trigger_codec_connection_setup;
uint8_t ag_ready_for_codecs_connection_setup;
uint8_t suggested_codec;
uint8_t codec_confirmed;
uint8_t establish_audio_connection;
uint8_t release_audio_connection;
uint8_t change_in_band_ring_tone_setting;
uint8_t ag_ring;
uint8_t ag_send_clip;
uint8_t ag_echo_and_noise_reduction;
uint8_t ag_activate_voice_recognition;
uint8_t microphone_gain;
uint8_t send_microphone_gain;
uint8_t speaker_gain;
uint8_t send_speaker_gain;
uint8_t send_phone_number_for_voice_tag;
timer_source_t hfp_timeout;
} hfp_connection_t;
// UTILS_START : TODO move to utils
@ -380,17 +475,21 @@ int get_bit(uint16_t bitmap, int position);
int store_bit(uint32_t bitmap, int position, uint8_t value);
// UTILS_END
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_create_sdp_record(uint8_t * service, uint16_t service_uuid, int rfcomm_channel_nr, const char * name);
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);
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);
linked_list_t * hfp_get_connections(void);
void hfp_parse(hfp_connection_t * context, uint8_t byte);
void hfp_parse(hfp_connection_t * context, uint8_t byte, int isHandsFree);
void hfp_init(uint16_t rfcomm_channel_nr);
void hfp_establish_service_level_connection(bd_addr_t bd_addr, uint16_t service_uuid);

File diff suppressed because it is too large Load Diff

View File

@ -154,6 +154,9 @@ void hfp_ag_transfer_callheld_status(bd_addr_t bd_addr, hfp_callheld_status_t st
*/
void hfp_ag_negotiate_codecs(bd_addr_t bd_addr);
/**
* @brief
*/
void hfp_ag_establish_audio_connection(bd_addr_t bd_addr);
/**
@ -162,6 +165,102 @@ void hfp_ag_establish_audio_connection(bd_addr_t bd_addr);
void hfp_ag_release_audio_connection(bd_addr_t bd_addr);
/**
* @brief Enable in-band ring tone
*/
void hfp_ag_set_use_in_band_ring_tone(int use_in_band_ring_tone);
/**
* @brief
*/
void hfp_ag_incoming_call(void);
/**
* @brief number is stored.
*/
void hfp_ag_set_clip(uint8_t type, const char * number);
/**
* @brief
*/
void hfp_ag_outgoing_call_rejected(void);
/**
* @brief
*/
void hfp_ag_outgoing_call_accepted(void);
/**
* @brief
*/
void hfp_ag_outgoing_call_ringing(void);
/**
* @brief
*/
void hfp_ag_outgoing_call_established(void);
/**
* @brief
*/
void hfp_ag_call_dropped(void);
/**
* @brief
*/
void hfp_ag_answer_incoming_call(void);
/**
* @brief
*/
void hfp_ag_terminate_call(void);
/*
* @brief
*/
void hfp_ag_set_registration_status(int status);
/*
* @brief
*/
void hfp_ag_set_signal_strength(int strength);
/*
* @brief
*/
void hfp_ag_set_roaming_status(int status);
/*
* @brief
*/
void hfp_ag_set_battery_level(int level);
/*
* @brief
*/
void hfp_ag_activate_voice_recognition(bd_addr_t bd_addr, int activate);
/*
* @brief
*/
void hfp_ag_set_microphone_gain(bd_addr_t bd_addr, int gain);
/*
* @brief
*/
void hfp_ag_set_speaker_gain(bd_addr_t bd_addr, int gain);
/*
* @brief
*/
void hfp_ag_send_phone_number_for_voice_tag(bd_addr_t bd_addr, const char * number);
/*
* @brief
*/
void hfp_ag_reject_phone_number_for_voice_tag(bd_addr_t bd_addr);
/* API_END */
#if defined __cplusplus

View File

@ -114,9 +114,11 @@ void hfp_hf_create_sdp_record(uint8_t * service, int rfcomm_channel_nr, const ch
if (!name){
name = default_hfp_hf_service_name;
}
hfp_create_sdp_record(service, SDP_Handsfree, rfcomm_channel_nr, name, supported_features);
}
hfp_create_sdp_record(service, SDP_Handsfree, rfcomm_channel_nr, name);
de_add_number(service, DE_UINT, DE_SIZE_16, 0x0311); // Hands-Free Profile - SupportedFeatures
de_add_number(service, DE_UINT, DE_SIZE_16, supported_features);
}
static int hfp_hf_cmd_exchange_supported_features(uint16_t cid){
char buffer[20];
@ -251,60 +253,48 @@ static void hfp_emit_network_operator_event(hfp_callback_t callback, int status,
static int hfp_hf_run_for_context_service_level_connection(hfp_connection_t * context){
if (context->state >= HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED) return 0;
int done = 0;
if (context->wait_ok) return done;
if (context->ok_pending) return 0;
int done = 1;
switch (context->state){
case HFP_EXCHANGE_SUPPORTED_FEATURES:
hfp_hf_cmd_exchange_supported_features(context->rfcomm_cid);
context->state = HFP_W4_EXCHANGE_SUPPORTED_FEATURES;
hfp_hf_cmd_exchange_supported_features(context->rfcomm_cid);
break;
case HFP_NOTIFY_ON_CODECS:
hfp_hf_cmd_notify_on_codecs(context->rfcomm_cid);
context->state = HFP_W4_NOTIFY_ON_CODECS;
hfp_hf_cmd_notify_on_codecs(context->rfcomm_cid);
break;
case HFP_RETRIEVE_INDICATORS:
hfp_hf_cmd_retrieve_indicators(context->rfcomm_cid);
context->state = HFP_W4_RETRIEVE_INDICATORS;
context->retrieve_ag_indicators = 1;
context->retrieve_ag_indicators_status = 0;
hfp_hf_cmd_retrieve_indicators(context->rfcomm_cid);
break;
case HFP_RETRIEVE_INDICATORS_STATUS:
hfp_hf_cmd_retrieve_indicators_status(context->rfcomm_cid);
context->state = HFP_W4_RETRIEVE_INDICATORS_STATUS;
context->retrieve_ag_indicators_status = 1;
context->retrieve_ag_indicators = 0;
hfp_hf_cmd_retrieve_indicators_status(context->rfcomm_cid);
break;
case HFP_ENABLE_INDICATORS_STATUS_UPDATE:
hfp_hf_cmd_activate_status_update_for_all_ag_indicators(context->rfcomm_cid, 1);
context->state = HFP_W4_ENABLE_INDICATORS_STATUS_UPDATE;
hfp_hf_cmd_activate_status_update_for_all_ag_indicators(context->rfcomm_cid, 1);
break;
case HFP_RETRIEVE_CAN_HOLD_CALL:
hfp_hf_cmd_retrieve_can_hold_call(context->rfcomm_cid);
context->state = HFP_W4_RETRIEVE_CAN_HOLD_CALL;
hfp_hf_cmd_retrieve_can_hold_call(context->rfcomm_cid);
break;
case HFP_LIST_GENERIC_STATUS_INDICATORS:
hfp_hf_cmd_list_supported_generic_status_indicators(context->rfcomm_cid);
context->state = HFP_W4_LIST_GENERIC_STATUS_INDICATORS;
context->list_generic_status_indicators = 1;
context->retrieve_generic_status_indicators = 0;
context->retrieve_generic_status_indicators_state = 0;
hfp_hf_cmd_list_supported_generic_status_indicators(context->rfcomm_cid);
break;
case HFP_RETRIEVE_GENERIC_STATUS_INDICATORS:
hfp_hf_cmd_retrieve_supported_generic_status_indicators(context->rfcomm_cid);
context->state = HFP_W4_RETRIEVE_GENERIC_STATUS_INDICATORS;
context->list_generic_status_indicators = 0;
context->retrieve_generic_status_indicators = 1;
context->retrieve_generic_status_indicators_state = 0;
hfp_hf_cmd_retrieve_supported_generic_status_indicators(context->rfcomm_cid);
break;
case HFP_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS:
hfp_hf_cmd_list_initital_supported_generic_status_indicators(context->rfcomm_cid);
context->state = HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS;
context->list_generic_status_indicators = 0;
context->retrieve_generic_status_indicators = 0;
context->retrieve_generic_status_indicators_state = 1;
hfp_hf_cmd_list_initital_supported_generic_status_indicators(context->rfcomm_cid);
break;
default:
done = 0;
break;
}
return done;
@ -327,12 +317,10 @@ static void hfp_hf_handle_ok_service_level_connection_establishment(hfp_connecti
case HFP_W4_RETRIEVE_INDICATORS:
context->state = HFP_RETRIEVE_INDICATORS_STATUS;
context->retrieve_ag_indicators = 0;
break;
case HFP_W4_RETRIEVE_INDICATORS_STATUS:
context->state = HFP_ENABLE_INDICATORS_STATUS_UPDATE;
context->retrieve_ag_indicators_status = 0;
break;
case HFP_W4_ENABLE_INDICATORS_STATUS_UPDATE:
@ -359,17 +347,14 @@ static void hfp_hf_handle_ok_service_level_connection_establishment(hfp_connecti
case HFP_W4_LIST_GENERIC_STATUS_INDICATORS:
context->state = HFP_RETRIEVE_GENERIC_STATUS_INDICATORS;
context->retrieve_generic_status_indicators = 0;
break;
case HFP_W4_RETRIEVE_GENERIC_STATUS_INDICATORS:
context->state = HFP_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS;
context->retrieve_generic_status_indicators = 0;
break;
case HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS:
context->state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED;
context->retrieve_generic_status_indicators_state = 0;
hfp_emit_event(hfp_callback, HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED, 0);
break;
default:
@ -377,39 +362,47 @@ static void hfp_hf_handle_ok_service_level_connection_establishment(hfp_connecti
}
}
static void hfp_hf_run_for_context_service_level_connection_queries(hfp_connection_t * context){
if (context->state != HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED) return;
if (context->wait_ok) return;
static int hfp_hf_run_for_context_service_level_connection_queries(hfp_connection_t * context){
if (context->state != HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED) return 0;
if (context->ok_pending) return 0;
int done = 0;
if (context->enable_status_update_for_ag_indicators != 0xFF){
context->ok_pending = 1;
done = 1;
hfp_hf_cmd_activate_status_update_for_all_ag_indicators(context->rfcomm_cid, context->enable_status_update_for_ag_indicators);
context->wait_ok = 1;
return;
return done;
};
if (context->change_status_update_for_individual_ag_indicators){
context->ok_pending = 1;
done = 1;
hfp_hf_cmd_activate_status_update_for_ag_indicator(context->rfcomm_cid,
context->ag_indicators_status_update_bitmap,
context->ag_indicators_nr);
context->wait_ok = 1;
return;
return done;
}
if (context->operator_name_format){
if (context->command == HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT){
context->ok_pending = 1;
done = 1;
hfp_hf_cmd_query_operator_name_format(context->rfcomm_cid);
context->wait_ok = 1;
return;
return done;
}
if (context->operator_name){
if (context->command == HFP_CMD_QUERY_OPERATOR_SELECTION_NAME){
context->ok_pending = 1;
done = 1;
hfp_hf_cmd_query_operator_name(context->rfcomm_cid);
context->wait_ok = 1;
return;
return done;
}
if (context->enable_extended_audio_gateway_error_report){
context->ok_pending = 1;
done = 1;
hfp_hf_cmd_enable_extended_audio_gateway_error_report(context->rfcomm_cid, context->enable_extended_audio_gateway_error_report);
context->wait_ok = 1;
return;
return done;
}
return done;
}
static void hfp_hf_handle_ok_service_level_connection_queries(hfp_connection_t * context){
@ -427,14 +420,12 @@ static void hfp_hf_handle_ok_service_level_connection_queries(hfp_connection_t *
return;
}
if (context->operator_name_format){
context->operator_name_format = 0;
context->operator_name = 1;
if (context->command == HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT){
context->command = HFP_CMD_QUERY_OPERATOR_SELECTION_NAME;
return;
}
if (context->operator_name){
context->operator_name = 0;
if (context->command == HFP_CMD_QUERY_OPERATOR_SELECTION_NAME){
hfp_emit_network_operator_event(hfp_callback, 0, context->network_operator);
return;
}
@ -444,141 +435,77 @@ static void hfp_hf_handle_ok_service_level_connection_queries(hfp_connection_t *
}
}
static void hfp_hf_run_for_context_codecs_connection(hfp_connection_t * context){
// if (context->state >= HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED && context->state <= HFP_AUDIO_CONNECTION_ESTABLISHED){
// handle audio connection setup
// printf("hfp_run_for_context state %d \n", context->state);
if (context->wait_ok) return;
switch (context->state){
case HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED:
if (context->notify_ag_on_new_codecs){
context->wait_ok = 1;
hfp_hf_cmd_notify_on_codecs(context->rfcomm_cid);
break;
}
if (context->hf_trigger_codec_connection_setup){
context->state = HFP_SLE_W2_EXCHANGE_COMMON_CODEC;
context->wait_ok = 1;
hfp_hf_cmd_trigger_codec_connection_setup(context->rfcomm_cid);
break;
}
if (context->suggested_codec){
context->state = HFP_SLE_W4_EXCHANGE_COMMON_CODEC;
context->codec_confirmed = 1;
context->wait_ok = 1;
hfp_hf_cmd_confirm_codec(context->rfcomm_cid, context->suggested_codec);
break;
}
break;
case HFP_SLE_W4_EXCHANGE_COMMON_CODEC:
if (context->notify_ag_on_new_codecs){
context->wait_ok = 1;
context->codec_confirmed = 0;
context->suggested_codec = 0;
context->negotiated_codec = 0;
hfp_hf_cmd_notify_on_codecs(context->rfcomm_cid);
break;
}
if (context->suggested_codec){
if (hfp_hf_supports_codec(context->suggested_codec)){
context->codec_confirmed = context->suggested_codec;
context->wait_ok = 1;
hfp_hf_cmd_confirm_codec(context->rfcomm_cid, context->suggested_codec);
} else {
context->notify_ag_on_new_codecs = 1;
context->wait_ok = 1;
static int codecs_exchange_state_machine(hfp_connection_t * context){
if (context->ok_pending) return 0;
int done = 1;
switch(context->command){
case HFP_CMD_AVAILABLE_CODECS:
switch (context->codecs_state){
case HFP_CODECS_W4_AG_COMMON_CODEC:
context->codec_confirmed = 0;
context->suggested_codec = 0;
context->negotiated_codec = 0;
hfp_hf_cmd_notify_on_codecs(context->rfcomm_cid);
}
break;
break;
case HFP_CODECS_EXCHANGED:
context->negotiated_codec = 0;
context->codecs_state = HFP_CODECS_W4_AG_COMMON_CODEC;
break;
default:
break;
}
hfp_hf_cmd_notify_on_codecs(context->rfcomm_cid);
break;
case HFP_CODECS_CONNECTION_ESTABLISHED:
if (context->notify_ag_on_new_codecs){
case HFP_CMD_TRIGGER_CODEC_CONNECTION_SETUP:
context->codecs_state = HFP_CODECS_RECEIVED_TRIGGER_CODEC_EXCHANGE;
hfp_hf_cmd_trigger_codec_connection_setup(context->rfcomm_cid);
break;
case HFP_CMD_AG_SUGGESTED_CODEC:
if (hfp_hf_supports_codec(context->suggested_codec)){
context->codec_confirmed = context->suggested_codec;
hfp_hf_cmd_confirm_codec(context->rfcomm_cid, context->suggested_codec);
} else {
context->codec_confirmed = 0;
context->suggested_codec = 0;
context->negotiated_codec = 0;
context->wait_ok = 1;
context->state = HFP_SLE_W4_EXCHANGE_COMMON_CODEC;
hfp_hf_cmd_notify_on_codecs(context->rfcomm_cid);
break;
}
if (context->hf_trigger_codec_connection_setup){
context->state = HFP_SLE_W2_EXCHANGE_COMMON_CODEC;
context->wait_ok = 1;
hfp_hf_cmd_trigger_codec_connection_setup(context->rfcomm_cid);
break;
}
if (context->establish_audio_connection){
// TODO AUDIO CONNECTION
}
break;
default:
break;
default:
return 0;
}
if (done){
context->ok_pending = 1;
}
return done;
}
static void hfp_hf_handle_ok_codecs_connection(hfp_connection_t * context){
// handle audio connection setup
switch (context->state){
case HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED:
if (context->notify_ag_on_new_codecs){
context->notify_ag_on_new_codecs = 0;
break;
}
case HFP_SLE_W2_EXCHANGE_COMMON_CODEC:
if (context->hf_trigger_codec_connection_setup){
context->hf_trigger_codec_connection_setup = 0;
context->state = HFP_SLE_W4_EXCHANGE_COMMON_CODEC;
break;
}
// handle audio connection setup
switch (context->codecs_state){
case HFP_CODECS_RECEIVED_TRIGGER_CODEC_EXCHANGE:
context->codecs_state = HFP_CODECS_W4_AG_COMMON_CODEC;
break;
case HFP_SLE_W4_EXCHANGE_COMMON_CODEC:
if (context->notify_ag_on_new_codecs){
context->codec_confirmed = 0;
context->suggested_codec = 0;
context->notify_ag_on_new_codecs = 0;
break;
}
if (context->codec_confirmed && context->suggested_codec){
context->negotiated_codec = context->suggested_codec;
context->codec_confirmed = 0;
context->suggested_codec = 0;
context->state = HFP_CODECS_CONNECTION_ESTABLISHED;
hfp_emit_event(hfp_callback, HFP_SUBEVENT_CODECS_CONNECTION_COMPLETE, 0);
break;
}
break;
case HFP_AUDIO_CONNECTION_ESTABLISHED:
printf("HFP_AUDIO_CONNECTION_ESTABLISHED \n");
break;
default:
break;
}
}
static void hfp_run_for_context(hfp_connection_t * context){
if (!context) return;
if (!rfcomm_can_send_packet_now(context->rfcomm_cid)) return;
hfp_hf_run_for_context_service_level_connection(context);
hfp_hf_run_for_context_service_level_connection_queries(context);
hfp_hf_run_for_context_codecs_connection(context);
int done = hfp_hf_run_for_context_service_level_connection(context);
if (!done){
done = hfp_hf_run_for_context_service_level_connection_queries(context);
}
if (!done){
done = codecs_exchange_state_machine(context);
}
if (done) return;
// deal with disconnect
switch (context->state){
case HFP_W2_DISCONNECT_RFCOMM:
@ -593,12 +520,11 @@ static void hfp_run_for_context(hfp_connection_t * context){
static void hfp_hf_switch_on_ok(hfp_connection_t *context){
// printf("switch on ok\n");
context->wait_ok = 0;
context->ok_pending = 0;
hfp_hf_handle_ok_service_level_connection_establishment(context);
hfp_hf_handle_ok_service_level_connection_queries(context);
hfp_hf_handle_ok_codecs_connection(context);
// done
context->command = HFP_CMD_NONE;
}
@ -612,27 +538,27 @@ static void hfp_handle_rfcomm_event(uint8_t packet_type, uint16_t channel, uint8
int pos, i;
//printf("\nHF received: %s", packet+2);
for (pos = 0; pos < size ; pos++){
hfp_parse(context, packet[pos]);
hfp_parse(context, packet[pos], 1);
// emit indicators status changed
for (i = 0; i < context->ag_indicators_nr; i++){
if (context->ag_indicators[i].status_changed) {
hfp_emit_ag_indicator_event(hfp_callback, 0, context->ag_indicators[i]);
context->ag_indicators[i].status_changed = 0;
hfp_emit_ag_indicator_event(hfp_callback, 0, context->ag_indicators[i]);
break;
}
}
if (context->command == HFP_CMD_ERROR){
context->wait_ok = 0;
context->ok_pending = 0;
hfp_reset_context_flags(context);
hfp_emit_event(hfp_callback, HFP_SUBEVENT_COMPLETE, 1);
return;
}
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->ok_pending = 0;
context->extended_audio_gateway_error = 0;
hfp_emit_event(hfp_callback, HFP_SUBEVENT_EXTENDED_AUDIO_GATEWAY_ERROR, context->extended_audio_gateway_error);
return;
}
@ -685,12 +611,14 @@ void hfp_hf_set_codecs(uint8_t * codecs, int codecs_nr){
while (linked_list_iterator_has_next(&it)){
hfp_connection_t * connection = (hfp_connection_t *)linked_list_iterator_next(&it);
if (!connection) continue;
connection->notify_ag_on_new_codecs = 1;
connection->command = HFP_CMD_AVAILABLE_CODECS;
hfp_run_for_context(connection);
}
}
void hfp_hf_init(uint16_t rfcomm_channel_nr, uint32_t supported_features, uint16_t * indicators, int indicators_nr, uint32_t indicators_status){
l2cap_init();
l2cap_register_packet_handler(packet_handler);
rfcomm_register_packet_handler(packet_handler);
hfp_init(rfcomm_channel_nr);
@ -745,7 +673,7 @@ void hfp_hf_query_operator_selection(bd_addr_t bd_addr){
log_error("HFP HF: connection doesn't exist.");
return;
}
connection->operator_name_format = 1;
connection->command = HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT;
hfp_run_for_context(connection);
}
@ -760,22 +688,6 @@ void hfp_hf_enable_report_extended_audio_gateway_error_result_code(bd_addr_t bd_
hfp_run_for_context(connection);
}
void hfp_hf_negotiate_codecs(bd_addr_t bd_addr){
hfp_hf_establish_service_level_connection(bd_addr);
hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr);
if (!has_codec_negotiation_feature(connection)) return;
if (connection->remote_codecs_nr == 0) return;
if (connection->state >= HFP_W2_DISCONNECT_SCO) return;
if (connection->state != HFP_SLE_W2_EXCHANGE_COMMON_CODEC &&
connection->state != HFP_SLE_W4_EXCHANGE_COMMON_CODEC){
connection->hf_trigger_codec_connection_setup = 1;
}
hfp_run_for_context(connection);
}
void hfp_hf_establish_audio_connection(bd_addr_t bd_addr){
hfp_hf_establish_service_level_connection(bd_addr);
hfp_connection_t * connection = get_hfp_connection_context_for_bd_addr(bd_addr);
@ -785,9 +697,13 @@ void hfp_hf_establish_audio_connection(bd_addr_t bd_addr){
if (connection->state >= HFP_W2_DISCONNECT_SCO) return;
connection->establish_audio_connection = 1;
if (connection->state < HFP_SLE_W4_EXCHANGE_COMMON_CODEC){
connection->hf_trigger_codec_connection_setup = 1;
}
switch (connection->codecs_state){
case HFP_CODECS_W4_AG_COMMON_CODEC:
break;
default:
connection->command = HFP_CMD_TRIGGER_CODEC_CONNECTION_SETUP;
break;
}
hfp_run_for_context(connection);
}

View File

@ -141,8 +141,6 @@ void hfp_hf_enable_report_extended_audio_gateway_error_result_code(bd_addr_t bd_
/**
* @brief
*/
void hfp_hf_negotiate_codecs(bd_addr_t bd_addr);
void hfp_hf_establish_audio_connection(bd_addr_t bd_addr);
/**

View File

@ -207,7 +207,7 @@ void hsp_ag_create_service(uint8_t * service, int rfcomm_channel_nr, const char
}
static int hsp_ag_send_str_over_rfcomm(uint16_t cid, char * command){
if (!rfcomm_can_send_packet_now(rfcomm_cid)) return 1;
if (!rfcomm_can_send_packet_now(cid)) return 1;
int err = rfcomm_send_internal(cid, (uint8_t*) command, strlen(command));
if (err){
printf("rfcomm_send_internal -> error 0X%02x", err);
@ -337,16 +337,20 @@ static void hsp_run(void){
int err;
if (ag_send_ok){
ag_send_ok = 0;
err = hsp_ag_send_str_over_rfcomm(rfcomm_cid, HSP_AG_OK);
if (!err){
ag_send_ok = 0;
if (err){
ag_send_ok = 1;
}
return;
}
if (ag_send_error){
ag_send_error = 0;
err = hsp_ag_send_str_over_rfcomm(rfcomm_cid, HSP_AG_ERROR);
if (!err) ag_send_error = 0;
if (err) {
ag_send_error = 1;
}
return;
}
@ -359,18 +363,24 @@ static void hsp_run(void){
case HSP_W4_RING_ANSWER:
if (ag_ring){
ag_ring = 0;
err = hsp_ag_send_str_over_rfcomm(rfcomm_cid, HSP_AG_RING);
if (!err) ag_ring = 0;
if (err) {
ag_ring = 1;
}
break;
}
if (!ag_num_button_press_received) break;
ag_send_ok = 0;
ag_num_button_press_received = 0;
hsp_state = HSP_W2_CONNECT_SCO;
err = hsp_ag_send_str_over_rfcomm(rfcomm_cid, HSP_AG_OK);
if (!err) {
hsp_state = HSP_W2_CONNECT_SCO;
ag_send_ok = 0;
ag_num_button_press_received = 0;
if (err) {
hsp_state = HSP_W4_RING_ANSWER;
ag_num_button_press_received = 1;
}
break;
case HSP_W2_CONNECT_SCO:
@ -393,18 +403,26 @@ static void hsp_run(void){
case HSP_ACTIVE:
if (ag_microphone_gain >= 0){
int gain = ag_microphone_gain;
ag_microphone_gain = -1;
char buffer[10];
sprintf(buffer, "%s=%d\r\n", HSP_MICROPHONE_GAIN, ag_microphone_gain);
err = hsp_ag_send_str_over_rfcomm(rfcomm_cid, buffer);
if (!err) ag_microphone_gain = -1;
if (err) {
ag_microphone_gain = gain;
}
break;
}
if (ag_speaker_gain >= 0){
int gain = ag_speaker_gain;
ag_speaker_gain = -1;
char buffer[10];
sprintf(buffer, "%s=%d\r\n", HSP_SPEAKER_GAIN, ag_speaker_gain);
err = hsp_ag_send_str_over_rfcomm(rfcomm_cid, buffer);
if (!err) ag_speaker_gain = -1;
if (err) {
ag_speaker_gain = gain;
}
break;
}
break;

View File

@ -349,18 +349,26 @@ static void hsp_run(void){
if (hs_ok_received) break;
if (hs_microphone_gain >= 0){
int gain = hs_microphone_gain;
hs_microphone_gain = -1;
char buffer[20];
sprintf(buffer, "%s=%d\r\n", HSP_HS_MICROPHONE_GAIN, hs_microphone_gain);
err = hsp_hs_send_str_over_rfcomm(rfcomm_cid, buffer);
if (!err) hs_microphone_gain = -1;
if (err) {
hs_microphone_gain = gain;
}
break;
}
if (hs_speaker_gain >= 0){
int gain = hs_speaker_gain;
hs_speaker_gain = -1;
char buffer[20];
sprintf(buffer, "%s=%d\r\n", HSP_HS_SPEAKER_GAIN, hs_speaker_gain);
err = hsp_hs_send_str_over_rfcomm(rfcomm_cid, buffer);
if (!err) hs_speaker_gain = -1;
if (err) {
hs_speaker_gain = gain;
}
break;
}

View File

@ -2124,6 +2124,7 @@ int rfcomm_query_port_configuration(uint16_t rfcomm_cid){
return rfcomm_send_uih_rpn_req(channel->multiplexer, channel->dlci);
}
static uint8_t rfcomm_create_channel_internal(bd_addr_t addr, uint8_t server_channel, uint8_t incoming_flow_control, uint8_t initial_credits, uint16_t * out_rfcomm_cid){
log_info("RFCOMM_CREATE_CHANNEL addr %s channel #%u init credits %u", bd_addr_to_str(addr), server_channel, initial_credits);
@ -2199,8 +2200,7 @@ void rfcomm_disconnect_internal(uint16_t rfcomm_cid){
rfcomm_run();
}
static uint8_t rfcomm_register_service_internal(uint8_t channel, uint16_t max_frame_size, uint8_t incoming_flow_control, uint8_t initial_credits){
log_info("RFCOMM_REGISTER_SERVICE channel #%u mtu %u flow_control %u credits %u",
static uint8_t rfcomm_register_service_internal(uint8_t channel, uint16_t max_frame_size, uint8_t incoming_flow_control, uint8_t initial_credits){ log_info("RFCOMM_REGISTER_SERVICE channel #%u mtu %u flow_control %u credits %u",
channel, max_frame_size, incoming_flow_control, initial_credits);
// check if already registered

View File

@ -53,6 +53,9 @@
// called by test/sdp_client
void sdp_query_rfcomm_init(void);
// called by test/sdp_client
void sdp_query_rfcomm_init(void);
static void dummy_notify_app(sdp_query_event_t* event, void * context);
typedef enum {

View File

@ -43,7 +43,8 @@ extern "C" {
#endif
#include "btstack_defines.h"
#include "utils.h"
typedef enum {
// MITM protection not required

View File

@ -6,14 +6,12 @@ SUBDIRS = \
ble_client \
des_iterator \
gatt_client \
hfp \
linked_list \
remote_device_db \
sdp_client \
security_manager \
# hfp \
subdirs:
echo Building all tests
@set -e; \

View File

@ -53,7 +53,8 @@
#include "ble/att.h"
#include "ble/att_db_util.h"
#include "utils.h"
#include "bluetooth.h"
#include "le_counter.h"
#if 0

View File

@ -11,6 +11,8 @@
#include <stdlib.h>
#include <string.h>
#include "bluetooth.h"
#include "classic/sdp_util.h"
#include "CppUTest/TestHarness.h"
#include "CppUTest/CommandLineTestRunner.h"

View File

@ -61,7 +61,7 @@ EXAMPLES = hfp_ag_parser_test hfp_ag_client_test hfp_hf_parser_test hfp_hf_clien
all: ${BTSTACK_ROOT}/src/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

@ -84,6 +84,9 @@ static hfp_ag_indicator_t ag_indicators[] = {
{7, "callheld", 0, 2, 0, 1, 1, 0}
};
static int supported_features_with_codec_negotiation = 1007; // 0011 1110 1111
static int supported_features_without_codec_negotiation = 495; // 0001 1110 1111
static int call_hold_services_nr = 5;
static const char* call_hold_services[] = {"1", "1x", "2", "2x", "3"};
@ -97,8 +100,9 @@ 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;
static uint8_t start_ringing = 0;
static uint8_t stop_ringing = 0;
static uint8_t call_termiated = 0;
int expected_rfcomm_command(const char * expected_cmd){
char * ag_cmd = (char *)get_rfcomm_payload();
@ -108,7 +112,7 @@ int expected_rfcomm_command(const char * expected_cmd){
if ( (ag_cmd+i)[0] == '\r' || (ag_cmd+i)[0] == '\n' ) {
continue;
}
if (memcmp(ag_cmd + i, expected_cmd, expected_len) == 0) return 1;
if (strncmp(ag_cmd + i, expected_cmd, expected_len) == 0) return 1;
}
return 0;
}
@ -119,9 +123,9 @@ void simulate_test_sequence(char ** test_steps, int nr_test_steps){
for (i=0; i < nr_test_steps; i++){
char * cmd = test_steps[i];
printf("\n---> NEXT STEP %s\n", cmd);
if (memcmp(cmd, "AT", 2) == 0){
if (strncmp(cmd, "AT", 2) == 0){
inject_rfcomm_command_to_ag((uint8_t*)cmd, strlen(cmd));
} else if (memcmp(cmd, "NOP", 3) == 0){
} else if (strncmp(cmd, "NOP", 3) == 0){
inject_rfcomm_command_to_ag((uint8_t*)"NOP",3);
} else {
int expected_cmd = expected_rfcomm_command(cmd);
@ -153,11 +157,29 @@ 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;
case HFP_SUBEVENT_START_RINGINIG:
printf("\n** Start ringing **\n\n");
start_ringing = 1;
break;
case HFP_SUBEVENT_STOP_RINGINIG:
printf("\n** Stop ringing **\n\n");
stop_ringing = 1;
start_ringing = 0;
break;
case HFP_SUBEVENT_CALL_TERMINATED:
call_termiated = 1;
break;
default:
printf("event not handled %u\n", event[2]);
@ -171,16 +193,24 @@ TEST_GROUP(HFPClient){
service_level_connection_established = 0;
codecs_connection_established = 0;
audio_connection_established = 0;
service_level_connection_released = 0;
start_ringing = 0;
stop_ringing = 0;
call_termiated = 0;
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);
}
void teardown(void){
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;
}
hfp_ag_release_audio_connection(device_addr);
hfp_ag_release_service_level_connection(device_addr);
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){
@ -193,9 +223,78 @@ TEST_GROUP(HFPClient){
codecs_connection_established = 0;
simulate_test_sequence((char **) test_steps, nr_test_steps);
}
};
// TEST(HFPClient, HFAnswerIncomingCallWithInBandRingToneHFTermiantesCall){
// setup_hfp_service_level_connection(default_slc_setup(), default_slc_setup_size());
// CHECK_EQUAL(service_level_connection_established, 1);
// hfp_ag_set_use_in_band_ring_tone(1);
// hfp_ag_incoming_call();
// simulate_test_sequence(default_ic_setup(), default_ic_setup_size());
// CHECK_EQUAL(audio_connection_established, 1);
// simulate_test_sequence(alert_ic_setup(), alert_ic_setup_size());
// CHECK_EQUAL(stop_ringing, 1);
// simulate_test_sequence(terminate_ic_hf_setup(), terminate_ic_hf_setup_size());
// CHECK_EQUAL(call_termiated,1);
// }
// TEST(HFPClient, HFAnswerIncomingCallWithInBandRingToneAGTerminatesCall){
// setup_hfp_service_level_connection(default_slc_setup(), default_slc_setup_size());
// CHECK_EQUAL(service_level_connection_established, 1);
// hfp_ag_set_use_in_band_ring_tone(1);
// hfp_ag_incoming_call();
// simulate_test_sequence(default_ic_setup(), default_ic_setup_size());
// CHECK_EQUAL(audio_connection_established, 1);
// simulate_test_sequence(alert_ic_setup(), alert_ic_setup_size());
// CHECK_EQUAL(stop_ringing, 1);
// // AG terminates call
// hfp_ag_terminate_call();
// simulate_test_sequence(terminate_ic_ag_setup(), terminate_ic_ag_setup_size());
// CHECK_EQUAL(call_termiated,1);
// }
TEST(HFPClient, HFAudioConnectionEstablishedWithCodecNegotiation){
setup_hfp_service_level_connection(default_slc_setup(), default_slc_setup_size());
CHECK_EQUAL(service_level_connection_established, 1);
setup_hfp_codecs_connection(default_cc_setup(), default_cc_setup_size());
CHECK_EQUAL(codecs_connection_established, 1);
hfp_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, HFAudioConnectionEstablishedWithoutCodecNegotiation){
hfp_ag_init(rfcomm_channel_nr, supported_features_without_codec_negotiation,
codecs, sizeof(codecs),
ag_indicators, ag_indicators_nr,
hf_indicators, hf_indicators_nr,
call_hold_services, call_hold_services_nr);
setup_hfp_service_level_connection(hfp_slc_tests()[1].test, hfp_slc_tests()[1].len);
CHECK_EQUAL(service_level_connection_established, 1);
setup_hfp_codecs_connection(default_cc_setup(), default_cc_setup_size());
CHECK_EQUAL(codecs_connection_established, 1);
hfp_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, HFCodecsConnectionEstablished){
for (int i = 0; i < cc_tests_size(); i++){
setup_hfp_service_level_connection(default_slc_setup(), default_slc_setup_size());
@ -215,20 +314,22 @@ TEST(HFPClient, HFServiceLevelConnectionCommands){
}
}
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[]){
hfp_ag_init(rfcomm_channel_nr, 1007, codecs, sizeof(codecs),
TEST(HFPClient, HFServiceLevelConnectionEstablishedWithoutCodecNegotiation){
hfp_ag_init(rfcomm_channel_nr, supported_features_without_codec_negotiation,
codecs, sizeof(codecs),
ag_indicators, ag_indicators_nr,
hf_indicators, hf_indicators_nr,
call_hold_services, call_hold_services_nr);
setup_hfp_service_level_connection(hfp_slc_tests()[1].test, hfp_slc_tests()[1].len);
CHECK_EQUAL(service_level_connection_established, 1);
}
TEST(HFPClient, HFServiceLevelConnectionEstablishedWithCodecNegotiation){
setup_hfp_service_level_connection(hfp_slc_tests()[0].test, hfp_slc_tests()[0].len);
CHECK_EQUAL(service_level_connection_established, 1);
}
int main (int argc, const char * argv[]){
hfp_ag_register_packet_handler(packet_handler);
return CommandLineTestRunner::RunAllTests(argc, argv);

View File

@ -46,7 +46,7 @@
#include "classic/hfp.h"
void hfp_parse(hfp_connection_t * context, uint8_t byte);
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);
@ -93,7 +93,7 @@ TEST(HFPParser, HFP_AG_SUPPORTED_FEATURES){
sprintf(packet, "\r\nAT%s=159\r\n", HFP_SUPPORTED_FEATURES);
context.keep_separator = 0;
for (pos = 0; pos < strlen(packet); pos++){
hfp_parse(&context, packet[pos]);
hfp_parse(&context, packet[pos], 0);
}
CHECK_EQUAL(HFP_CMD_SUPPORTED_FEATURES, context.command);
CHECK_EQUAL(159, context.remote_supported_features);
@ -102,7 +102,7 @@ TEST(HFPParser, HFP_AG_SUPPORTED_FEATURES){
TEST(HFPParser, HFP_AG_AVAILABLE_CODECS){
sprintf(packet, "\r\nAT%s=0,1,2\r\n", HFP_AVAILABLE_CODECS);
for (pos = 0; pos < strlen(packet); pos++){
hfp_parse(&context, packet[pos]);
hfp_parse(&context, packet[pos], 0);
}
CHECK_EQUAL(HFP_CMD_AVAILABLE_CODECS, context.command);
CHECK_EQUAL(3, context.remote_codecs_nr);
@ -116,14 +116,10 @@ TEST(HFPParser, HFP_AG_GENERIC_STATUS_INDICATOR){
sprintf(packet, "\r\nAT%s=0,1,2,3,4\r\n", HFP_GENERIC_STATUS_INDICATOR);
for (pos = 0; pos < strlen(packet); pos++){
hfp_parse(&context, packet[pos]);
hfp_parse(&context, packet[pos], 0);
}
CHECK_EQUAL(context.command, HFP_CMD_GENERIC_STATUS_INDICATOR);
CHECK_EQUAL(context.list_generic_status_indicators, 1);
CHECK_EQUAL(context.retrieve_generic_status_indicators, 0);
CHECK_EQUAL(context.retrieve_generic_status_indicators_state, 0);
CHECK_EQUAL(context.command, HFP_CMD_LIST_GENERIC_STATUS_INDICATORS);
CHECK_EQUAL(5, context.generic_status_indicators_nr);
for (pos = 0; pos < context.generic_status_indicators_nr; pos++){
@ -135,7 +131,7 @@ TEST(HFPParser, HFP_AG_GENERIC_STATUS_INDICATOR){
TEST(HFPParser, HFP_AG_ENABLE_INDICATOR_STATUS_UPDATE){
sprintf(packet, "\r\nAT%s=3,0,0,1\r\n", HFP_ENABLE_STATUS_UPDATE_FOR_AG_INDICATORS);
for (pos = 0; pos < strlen(packet); pos++){
hfp_parse(&context, packet[pos]);
hfp_parse(&context, packet[pos], 0);
}
CHECK_EQUAL(HFP_CMD_ENABLE_INDICATOR_STATUS_UPDATE, context.command);
CHECK_EQUAL(1, context.enable_status_update_for_ag_indicators);
@ -157,7 +153,7 @@ TEST(HFPParser, HFP_AG_ENABLE_INDIVIDUAL_INDICATOR_STATUS_UPDATE){
sprintf(packet, "\r\nAT%s=0,0,0,0,0,0,0\r\n",
HFP_UPDATE_ENABLE_STATUS_FOR_INDIVIDUAL_AG_INDICATORS);
for (pos = 0; pos < strlen(packet); pos++){
hfp_parse(&context, packet[pos]);
hfp_parse(&context, packet[pos], 0);
}
CHECK_EQUAL(HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE, context.command);
@ -175,7 +171,7 @@ TEST(HFPParser, HFP_AG_ENABLE_INDIVIDUAL_INDICATOR_STATUS_UPDATE){
// sprintf(packet, "\r\nAT%s=1,,,1,1,1,\r\n",
// HFP_UPDATE_ENABLE_STATUS_FOR_INDIVIDUAL_AG_INDICATORS);
// for (pos = 0; pos < strlen(packet); pos++){
// hfp_parse(&context, packet[pos]);
// hfp_parse(&context, packet[pos], 0);
// }
// CHECK_EQUAL(HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE, context.command);
@ -191,23 +187,17 @@ TEST(HFPParser, HFP_AG_HF_QUERY_OPERATOR_SELECTION){
sprintf(packet, "\r\nAT%s=3,0\r\n", HFP_QUERY_OPERATOR_SELECTION);
for (pos = 0; pos < strlen(packet); pos++){
hfp_parse(&context, packet[pos]);
hfp_parse(&context, packet[pos], 0);
}
CHECK_EQUAL(context.operator_name_format, 1);
CHECK_EQUAL(context.operator_name, 0);
CHECK_EQUAL(context.operator_name_changed, 0);
CHECK_EQUAL(HFP_CMD_QUERY_OPERATOR_SELECTION, context.command);
CHECK_EQUAL(context.network_operator.format, 0);
CHECK_EQUAL(context.network_operator.mode, 0);
CHECK_EQUAL(HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT, context.command);
sprintf(packet, "\r\nAT%s?\r\n", HFP_QUERY_OPERATOR_SELECTION);
for (pos = 0; pos < strlen(packet); pos++){
hfp_parse(&context, packet[pos]);
hfp_parse(&context, packet[pos], 0);
}
CHECK_EQUAL(context.operator_name_format, 0);
CHECK_EQUAL(context.operator_name, 0);
CHECK_EQUAL(context.operator_name_changed, 0);
}
@ -215,7 +205,7 @@ 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]);
hfp_parse(&context, packet[pos], 0);
}
CHECK_EQUAL(context.command, HFP_CMD_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR);
@ -224,13 +214,10 @@ TEST(HFPParser, HFP_AG_EXTENDED_AUDIO_GATEWAY_ERROR){
TEST(HFPParser, HFP_AG_TRIGGER_CODEC_CONNECTION_SETUP){
sprintf(packet, "\r\nAT%s\r\n", HFP_TRIGGER_CODEC_CONNECTION_SETUP);
for (pos = 0; pos < strlen(packet); pos++){
hfp_parse(&context, packet[pos]);
hfp_parse(&context, packet[pos], 0);
}
CHECK_EQUAL(context.command, HFP_CMD_TRIGGER_CODEC_CONNECTION_SETUP);
CHECK_EQUAL(context.ag_trigger_codec_connection_setup, 1);
}
TEST(HFPParser, HFP_AG_CONFIRM_COMMON_CODEC){
@ -238,7 +225,7 @@ TEST(HFPParser, HFP_AG_CONFIRM_COMMON_CODEC){
sprintf(packet, "\r\nAT%s=%d\r\n", HFP_CONFIRM_COMMON_CODEC, codec);
for (pos = 0; pos < strlen(packet); pos++){
hfp_parse(&context, packet[pos]);
hfp_parse(&context, packet[pos], 0);
}
CHECK_EQUAL(context.command, HFP_CMD_HF_CONFIRMED_CODEC);

View File

@ -75,15 +75,13 @@ 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();
int offset = 2;
int cmd_size = strlen(cmd);
int cmd_found = memcmp(ag_cmd+offset, cmd, cmd_size) == 0;
int cmd_found = strncmp(ag_cmd+offset, cmd, cmd_size) == 0;
while (!cmd_found && get_rfcomm_payload_len() - cmd_size >= offset){
offset++;
cmd_found = strncmp(ag_cmd+offset, cmd, cmd_size) == 0;
@ -91,31 +89,31 @@ int expected_rfcomm_command(const char * cmd){
if (!cmd_found) return 0;
// AG cmds that are not followed by OK
if (memcmp(ag_cmd+offset, "+BCS", 4) == 0){
if (strncmp(ag_cmd+offset, "+BCS", 4) == 0){
return cmd_found;
}
offset += strlen(cmd)+4;
// printf("cmd found, offset %d, cmd %s\n", offset, ag_cmd+offset);
int ok_found = memcmp(ag_cmd+offset, "OK", 2) == 0;
int ok_found = strncmp(ag_cmd+offset, "OK", 2) == 0;
while (!ok_found && get_rfcomm_payload_len() - 2 >= offset){
offset++;
// printf("cmd found, offset %d, cmd %s\n", offset, ag_cmd+offset);
ok_found = memcmp(ag_cmd+offset, "OK", 2) == 0;
ok_found = strncmp(ag_cmd+offset, "OK", 2) == 0;
}
// printf("cmd found, ok found %d\n", ok_found);
return cmd_found && ok_found;
}
void hfp_hf_run_test_sequence(char ** test_steps, int nr_test_steps){
void simulate_test_sequence(char ** test_steps, int nr_test_steps){
int i = 0;
for (i=0; i < nr_test_steps; i++){
char * cmd = test_steps[i];
printf("\n---> NEXT STEP %s\n", cmd);
if (memcmp(cmd, "AT", 2) == 0){
if (strncmp(cmd, "AT", 2) == 0){
int parsed_codecs[2];
uint8_t new_codecs[2];
if (memcmp(cmd, "AT+BAC=", 7) == 0){
if (strncmp(cmd, "AT+BAC=", 7) == 0){
printf("Send BAC\n");
sscanf(&cmd[7],"%d,%d", &parsed_codecs[0], &parsed_codecs[1]);
new_codecs[0] = parsed_codecs[0];
@ -143,19 +141,16 @@ void packet_handler(uint8_t * event, uint16_t event_size){
}
switch (event[2]) {
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:
@ -184,31 +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);
CHECK_EQUAL(service_level_connection_established, 0);
}
codecs_connection_established = 0;
}
void verify_hfp_service_level_connection_established(char ** test_steps, int nr_test_steps){
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);
hfp_hf_run_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));
simulate_test_sequence((char **) test_steps, nr_test_steps);
}
void verify_hfp_codecs_connection_established(char ** test_steps, int nr_test_steps){
void setup_hfp_codecs_connection(char ** test_steps, int nr_test_steps){
codecs_connection_established = 0;
hfp_hf_negotiate_codecs(device_addr);
hfp_hf_run_test_sequence((char **) test_steps, nr_test_steps);
CHECK_EQUAL(codecs_connection_established, 1);
simulate_test_sequence((char **) test_steps, nr_test_steps);
}
};
@ -220,17 +209,16 @@ 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();
}
}
TEST(HFPClient, HFServiceLevelConnectionCommands){
verify_hfp_service_level_connection_established(default_slc_setup(), default_slc_setup_size());
for (int i = 0; i < slc_cmds_tests_size(); i++){
hfp_hf_run_test_sequence(hfp_slc_cmds_tests()[i].test, hfp_slc_cmds_tests()[i].len);
}
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){

View File

@ -46,7 +46,7 @@
#include "classic/hfp.h"
void hfp_parse(hfp_connection_t * context, uint8_t byte);
void hfp_parse(hfp_connection_t * context, uint8_t byte, int isHandsFree);
static hfp_connection_t context;
static int hfp_ag_indicators_nr = 7;
@ -84,7 +84,7 @@ TEST_GROUP(HFPParser){
TEST(HFPParser, HFP_HF_OK){
sprintf(packet, "\r\n%s\r\n", HFP_OK);
for (pos = 0; pos < strlen(packet); pos++){
hfp_parse(&context, packet[pos]);
hfp_parse(&context, packet[pos], 1);
}
CHECK_EQUAL(HFP_CMD_OK, context.command);
}
@ -92,7 +92,7 @@ TEST(HFPParser, HFP_HF_OK){
TEST(HFPParser, HFP_HF_SUPPORTED_FEATURES){
sprintf(packet, "\r\n%s:1007\r\n\r\nOK\r\n", HFP_SUPPORTED_FEATURES);
for (pos = 0; pos < strlen(packet); pos++){
hfp_parse(&context, packet[pos]);
hfp_parse(&context, packet[pos], 1);
}
CHECK_EQUAL(HFP_CMD_OK, context.command);
CHECK_EQUAL(1007, context.remote_supported_features);
@ -107,11 +107,11 @@ 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);
context.retrieve_ag_indicators = 1;
context.retrieve_ag_indicators_status = 0;
//context.command = HFP_CMD_RETRIEVE_AG_INDICATORS;
context.state = HFP_W4_RETRIEVE_INDICATORS;
for (pos = 0; pos < strlen(packet); pos++){
hfp_parse(&context, packet[pos]);
hfp_parse(&context, packet[pos], 1);
}
CHECK_EQUAL(HFP_CMD_OK, context.command);
CHECK_EQUAL(hfp_ag_indicators_nr, context.ag_indicators_nr);
@ -132,12 +132,11 @@ TEST(HFPParser, HFP_HF_INDICATOR_STATUS){
}
offset += snprintf(packet+offset, sizeof(packet)-offset, "%d\r\n\r\nOK\r\n", hfp_ag_indicators[pos].status);
context.command = HFP_CMD_INDICATOR;
context.retrieve_ag_indicators_status = 1;
context.retrieve_ag_indicators = 0;
//context.command = HFP_CMD_RETRIEVE_AG_INDICATORS_STATUS;
context.state = HFP_W4_RETRIEVE_INDICATORS_STATUS;
for (pos = 0; pos < strlen(packet); pos++){
hfp_parse(&context, packet[pos]);
hfp_parse(&context, packet[pos], 1);
}
CHECK_EQUAL(HFP_CMD_OK, context.command);
@ -150,7 +149,7 @@ TEST(HFPParser, HFP_HF_INDICATOR_STATUS){
TEST(HFPParser, HFP_HF_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES){
sprintf(packet, "\r\n%s:(1,1x,2,2x,3)\r\n\r\nOK\r\n", HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES);
for (pos = 0; pos < strlen(packet); pos++){
hfp_parse(&context, packet[pos]);
hfp_parse(&context, packet[pos], 1);
}
CHECK_EQUAL(HFP_CMD_OK, context.command);
CHECK_EQUAL(5, context.remote_call_services_nr);
@ -164,12 +163,11 @@ TEST(HFPParser, HFP_HF_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES){
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);
context.list_generic_status_indicators = 0;
context.retrieve_generic_status_indicators = 1;
context.retrieve_generic_status_indicators_state = 0;
//context.command = HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS;
context.state = HFP_W4_RETRIEVE_GENERIC_STATUS_INDICATORS;
for (pos = 0; pos < strlen(packet); pos++){
hfp_parse(&context, packet[pos]);
hfp_parse(&context, packet[pos], 1);
}
CHECK_EQUAL(HFP_CMD_OK, context.command);
@ -182,12 +180,11 @@ TEST(HFPParser, HFP_HF_GENERIC_STATUS_INDICATOR){
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);
context.list_generic_status_indicators = 0;
context.retrieve_generic_status_indicators = 0;
context.retrieve_generic_status_indicators_state = 1;
// context.command = HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS_STATE;
context.state = HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS;
for (pos = 0; pos < strlen(packet); pos++){
hfp_parse(&context, packet[pos]);
hfp_parse(&context, packet[pos], 1);
}
CHECK_EQUAL(HFP_CMD_OK, context.command);
@ -203,7 +200,7 @@ TEST(HFPParser, HFP_HF_AG_INDICATOR_STATUS_UPDATE){
sprintf(packet, "\r\n%s:%d,%d\r\n\r\nOK\r\n", HFP_TRANSFER_AG_INDICATOR_STATUS, index, status);
for (pos = 0; pos < strlen(packet); pos++){
hfp_parse(&context, packet[pos]);
hfp_parse(&context, packet[pos], 1);
}
CHECK_EQUAL(HFP_CMD_OK, context.command);
@ -213,13 +210,13 @@ TEST(HFPParser, HFP_HF_AG_INDICATOR_STATUS_UPDATE){
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);
context.command = HFP_CMD_QUERY_OPERATOR_SELECTION_NAME;
for (pos = 0; pos < strlen(packet); pos++){
hfp_parse(&context, packet[pos]);
hfp_parse(&context, packet[pos], 1);
}
CHECK_EQUAL(context.command, HFP_CMD_OK);
CHECK_EQUAL(context.operator_name_format, 0);
CHECK_EQUAL(context.operator_name, 1);
CHECK_EQUAL(context.operator_name_changed, 0);
CHECK_EQUAL( strcmp("sunrise", context.network_operator.name), 0);
}
@ -228,7 +225,7 @@ 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]);
hfp_parse(&context, packet[pos], 1);
}
CHECK_EQUAL(context.command, HFP_CMD_ERROR);
@ -238,7 +235,7 @@ 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]);
hfp_parse(&context, packet[pos], 1);
}
CHECK_EQUAL(context.command, HFP_CMD_EXTENDED_AUDIO_GATEWAY_ERROR);
@ -255,7 +252,7 @@ TEST(HFPParser, HFP_HF_AG_INDICATOR_CALLS_STATUS_UPDATE){
uint8_t index = call_status_index;
sprintf(packet, "\r\n%s:%d,%d\r\n\r\nOK\r\n", HFP_TRANSFER_AG_INDICATOR_STATUS, index, status);
for (pos = 0; pos < strlen(packet); pos++){
hfp_parse(&context, packet[pos]);
hfp_parse(&context, packet[pos], 1);
}
CHECK_EQUAL(HFP_CMD_OK, context.command);
CHECK_EQUAL(context.ag_indicators[index - 1].status, status);
@ -264,7 +261,7 @@ TEST(HFPParser, HFP_HF_AG_INDICATOR_CALLS_STATUS_UPDATE){
index = callsetup_status_index;
sprintf(packet, "\r\n%s:%d,%d\r\n\r\nOK\r\n", HFP_TRANSFER_AG_INDICATOR_STATUS, index, status);
for (pos = 0; pos < strlen(packet); pos++){
hfp_parse(&context, packet[pos]);
hfp_parse(&context, packet[pos], 1);
}
CHECK_EQUAL(HFP_CMD_OK, context.command);
CHECK_EQUAL(context.ag_indicators[index - 1].status, status);
@ -273,7 +270,7 @@ TEST(HFPParser, HFP_HF_AG_INDICATOR_CALLS_STATUS_UPDATE){
index = callheld_status_index;
sprintf(packet, "\r\n%s:%d,%d\r\n\r\nOK\r\n", HFP_TRANSFER_AG_INDICATOR_STATUS, index, status);
for (pos = 0; pos < strlen(packet); pos++){
hfp_parse(&context, packet[pos]);
hfp_parse(&context, packet[pos], 1);
}
CHECK_EQUAL(HFP_CMD_OK, context.command);
CHECK_EQUAL(context.ag_indicators[index - 1].status, status);

View File

@ -58,11 +58,14 @@ 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;
hfp_connection_t * hfp_context;
void (*registered_rfcomm_packet_handler)(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
void (*registered_rfcomm_packet_handler)(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
void (*registered_sdp_app_callback)(sdp_query_event_t * event, void * context);
uint8_t * get_rfcomm_payload(){
@ -119,6 +122,12 @@ static void print_without_newlines(uint8_t *data, uint16_t len){
printf("\n");
}
extern "C" void l2cap_init(void){}
extern "C" void l2cap_register_packet_handler(void (*handler)(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size)){
}
int rfcomm_send_internal(uint16_t rfcomm_cid, uint8_t *data, uint16_t len){
if (strncmp((char*)data, "AT", 2) == 0){
printf("Verify HF state machine response: ");
@ -132,8 +141,32 @@ 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[19];
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;
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)(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);
if (cmd->opcode == 0x428){
hci_event_sco_complete();
}
return 0;
}
@ -166,23 +199,30 @@ 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){
uint8_t rfcomm_create_channel(bd_addr_t addr, uint8_t channel, uint16_t * out_cid){
// RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE
// printf("rfcomm_create_channel_internal\n");
active_connection = connection;
uint8_t event[16];
uint8_t pos = 0;
event[pos++] = RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE;
event[pos++] = sizeof(event) - 2;
event[pos++] = 0;
bt_flip_addr(&event[pos], addr); pos += 6;
bt_flip_addr(&event[pos], addr);
memcpy(dev_addr, addr, 6);
pos += 6;
bt_store_16(event, pos, 1); pos += 2;
event[pos++] = 0;
bt_store_16(event, pos, rfcomm_cid); pos += 2; // channel ID
bt_store_16(event, pos, 200); pos += 2; // max frame size
(*registered_rfcomm_packet_handler)(connection, HCI_EVENT_PACKET, 0, (uint8_t *) event, pos);
(*registered_rfcomm_packet_handler)(HCI_EVENT_PACKET, 0, (uint8_t *) event, pos);
if (out_cid){
*out_cid = rfcomm_cid;
}
return 0;
}
int rfcomm_can_send_packet_now(uint16_t rfcomm_cid){
@ -194,15 +234,16 @@ void rfcomm_disconnect_internal(uint16_t rfcomm_cid){
event[0] = RFCOMM_EVENT_CHANNEL_CLOSED;
event[1] = sizeof(event) - 2;
bt_store_16(event, 2, rfcomm_cid);
(*registered_rfcomm_packet_handler)(active_connection, HCI_EVENT_PACKET, 0, (uint8_t *) event, sizeof(event));
(*registered_rfcomm_packet_handler)(HCI_EVENT_PACKET, 0, (uint8_t *) event, sizeof(event));
}
void rfcomm_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size)){
void rfcomm_register_packet_handler(void (*handler)(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size)){
registered_rfcomm_packet_handler = handler;
}
void rfcomm_register_service_internal(void * connection, uint8_t channel, uint16_t max_frame_size){
printf("rfcomm_register_service_internal\n");
uint8_t rfcomm_register_service(uint8_t channel, uint16_t max_frame_size){
printf("rfcomm_register_service\n");
return 0;
}
@ -215,6 +256,37 @@ void rfcomm_accept_connection_internal(uint16_t rfcomm_cid){
printf("rfcomm_accept_connection_internal \n");
}
void run_loop_add_timer(timer_source_t *timer){
}
int run_loop_remove_timer(timer_source_t *timer){
return 0;
}
void run_loop_set_timer_handler(timer_source_t *ts, void (*process)(timer_source_t *_ts)){
}
void run_loop_set_timer(timer_source_t *a, uint32_t timeout_in_ms){
}
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)(HCI_EVENT_PACKET, 0, event, sizeof(event));
}
uint8_t gap_disconnect(hci_con_handle_t handle){
hci_emit_disconnection_complete(handle, 0);
return 0;
}
uint16_t hci_get_sco_voice_setting(){
return 0x40;
}
void inject_rfcomm_command_to_hf(uint8_t * data, int len){
if (memcmp((char*)data, "AT", 2) == 0) return;
@ -225,7 +297,7 @@ void inject_rfcomm_command_to_hf(uint8_t * data, int len){
} else {
printf("Trigger HF state machine - %s", data);
}
(*registered_rfcomm_packet_handler)(active_connection, RFCOMM_DATA_PACKET, rfcomm_cid, (uint8_t *) &rfcomm_payload[0], rfcomm_payload_len);
(*registered_rfcomm_packet_handler)(RFCOMM_DATA_PACKET, rfcomm_cid, (uint8_t *) &rfcomm_payload[0], rfcomm_payload_len);
}
void inject_rfcomm_command_to_ag(uint8_t * data, int len){
@ -237,9 +309,8 @@ void inject_rfcomm_command_to_ag(uint8_t * data, int len){
} else {
printf("Trigger AG state machine - %s", data);
}
(*registered_rfcomm_packet_handler)(active_connection, RFCOMM_DATA_PACKET, rfcomm_cid, (uint8_t *) &rfcomm_payload[0], rfcomm_payload_len);
(*registered_rfcomm_packet_handler)( RFCOMM_DATA_PACKET, rfcomm_cid, (uint8_t *) &rfcomm_payload[0], rfcomm_payload_len);
}

View File

@ -53,6 +53,7 @@
/* Service Level Connection (slc) test sequences */
// with codec negotiation feature
const char * slc_test1[] = {
"AT+BRSF=438",
"+BRSF:1007",
@ -72,8 +73,27 @@ const char * slc_test1[] = {
"OK"
};
// without codec negotiation feature
const char * slc_test2[] = {
"AT+BRSF=438",
"+BRSF:495",
"OK",
"AT+CIND=?",
"+CIND:(\"service\",(0,1)),(\"call\",(0,1)),(\"callsetup\",(0,3)),(\"battchg\",(0,5)),(\"signal\",(0,5)),(\"roam\",(0,1)),(\"callheld\",(0,2))",
"OK",
"AT+CIND?",
"+CIND:1,0,0,3,5,0,0",
"OK",
"AT+CMER=3,0,0,1",
"OK",
"AT+CHLD=?",
"+CHLD:(1,1x,2,2x,3)",
"OK"
};
hfp_test_item_t slc_tests[] = {
TEST_SEQUENCE(slc_test1)
TEST_SEQUENCE(slc_test1),
TEST_SEQUENCE(slc_test2)
};
/* Service Level Connection (slc) common commands */
@ -136,6 +156,7 @@ const char * cc_test4[] = {
"OK"
};
hfp_test_item_t cc_tests[] = {
TEST_SEQUENCE(cc_test1),
TEST_SEQUENCE(cc_test2),
@ -143,6 +164,44 @@ hfp_test_item_t cc_tests[] = {
TEST_SEQUENCE(cc_test4)
};
/* Incoming call sequence */
const char * ic_test1[] = {
"+CIEV:3,1",
"NOP",
"BCS:1",
"AT+BCS=1",
"OK",
"NOP"
};
const char * ic_alert_test1[] = {
"NOP",
"ATA",
"OK",
"NOP",
"+CIEV:2,1", // call = 1
"NOP",
"+CIEV:3,0",
};
const char * ic_ag_terminates_call[] = {
// AG terminates call
"+CIEV:2,0"
};
const char * ic_hf_terminates_call[] = {
// HF terminates call
"NOP",
"AT+CHUP",
"OK",
"NOP",
"+CIEV:2,0"
};
hfp_test_item_t ic_tests[] = {
TEST_SEQUENCE(ic_test1)
};
//////////////
@ -165,10 +224,22 @@ int default_slc_cmds_setup_size(){ return sizeof(slc_cmds_test1)/sizeof(char*);}
// CC
hfp_test_item_t * hfp_cc_tests(){ return cc_tests;}
int cc_tests_size(){ return sizeof(cc_tests) /test_item_size;
}
int cc_tests_size(){ return sizeof(cc_tests) /test_item_size;}
char ** default_cc_setup() { return (char **)cc_test1;}
int default_cc_setup_size(){ return sizeof(cc_test1)/sizeof(char*);}
// IC
char ** default_ic_setup() { return (char **)ic_test1;}
int default_ic_setup_size(){ return sizeof(ic_test1)/sizeof(char*);}
char ** alert_ic_setup() { return (char **)ic_alert_test1;}
int alert_ic_setup_size(){ return sizeof(ic_alert_test1)/sizeof(char*);}
char ** terminate_ic_ag_setup() { return (char **)ic_ag_terminates_call;}
int terminate_ic_ag_setup_size(){ return sizeof(ic_ag_terminates_call)/sizeof(char*);}
char ** terminate_ic_hf_setup() { return (char **)ic_hf_terminates_call;}
int terminate_ic_hf_setup_size(){ return sizeof(ic_hf_terminates_call)/sizeof(char*);}

View File

@ -70,3 +70,16 @@ int cc_tests_size();
char ** default_cc_setup();
int default_cc_setup_size();
/* Incoming call (ic) test sequences */
char ** default_ic_setup();
int default_ic_setup_size();
char ** alert_ic_setup();
int alert_ic_setup_size();
char ** terminate_ic_ag_setup();
int terminate_ic_ag_setup_size();
char ** terminate_ic_hf_setup();
int terminate_ic_hf_setup_size();

7
test/pts/.gitignore vendored
View File

@ -1,6 +1,8 @@
ancs_client
ble_central_test
ble_central_test.h
ble_peripheral_test
ble_peripheral_test.h
bnep_test
classic_test
hfp_ag_test
@ -9,7 +11,4 @@ hsp_ag_test
hsp_hs_test
l2cap_test
profile.h
ble_peripheral_test.h
ble_central_test.h
sco_loopback
sco_loopback

View File

@ -75,6 +75,9 @@ static bd_addr_t device_addr;
static bd_addr_t pts_addr = {0x00,0x1b,0xDC,0x07,0x32,0xEF};
static bd_addr_t speaker_addr = {0x00, 0x21, 0x3C, 0xAC, 0xF7, 0x38};
static uint8_t codecs[1] = {HFP_CODEC_CVSD};
static uint16_t handle = -1;
static int memory_1_enabled = 1;
static int last_number_exists = 1;
static int ag_indicators_nr = 7;
static hfp_ag_indicator_t ag_indicators[] = {
@ -97,30 +100,63 @@ static hfp_generic_status_indicator_t hf_indicators[] = {
{2, 1},
};
char cmd;
// prototypes
static void show_usage();
static void reset_pst_flags(){
}
// Testig User Interface
static void show_usage(void){
printf("\n--- Bluetooth HFP Hands-Free (HF) unit Test Console ---\n");
printf("---\n");
printf("a - establish HFP connection to PTS module\n");
printf("A - release HFP connection to PTS module\n");
// printf("A - release HFP connection to PTS module\n");
printf("z - establish HFP connection to speaker\n");
// printf("Z - release HFP connection to speaker\n");
printf("b - establish AUDIO connection\n");
printf("B - release AUDIO connection\n");
printf("z - establish HFP connection to local mac\n");
printf("Z - release HFP connection to local mac\n");
printf("c - simulate incoming call from 1234567\n");
printf("C - simulate call from 1234567 dropped\n");
printf("d - report AG failure\n");
printf("e - answer call on AG\n");
printf("E - reject call on AG\n");
printf("r - disable in-band ring tone\n");
printf("R - enable in-band ring tone\n");
printf("f - Disable cellular network\n");
printf("F - Enable cellular network\n");
printf("g - Set signal strength to 0\n");
printf("G - Set signal strength to 5\n");
printf("h - Disable roaming\n");
printf("H - Enable roaming\n");
printf("i - Set battery level to 3\n");
printf("I - Set battery level to 5\n");
printf("j - Answering call on remote side\n");
printf("k - Clear memory #1\n");
printf("K - Set memory #1\n");
printf("l - Clear last number\n");
printf("L - Set last number\n");
printf("m - simulate incoming call from 7654321\n");
// printf("M - simulate call from 7654321 dropped\n");
printf("n - Disable Voice Regocnition\n");
printf("N - Enable Voice Recognition\n");
printf("t - terminate connection\n");
printf("---\n");
printf("Ctrl-c - exit\n");
printf("---\n");
@ -155,10 +191,103 @@ static int stdin_process(struct data_source *ds){
printf("Release Audio connection.\n");
hfp_ag_release_audio_connection(device_addr);
break;
case 'c':
printf("Simulate incoming call from 1234567\n");
hfp_ag_set_clip(129, "1234567");
hfp_ag_incoming_call();
break;
case 'm':
printf("Simulate incoming call from 7654321\n");
hfp_ag_set_clip(129, "7654321");
hfp_ag_incoming_call();
break;
case 'C':
printf("Simulate terminate call\n");
hfp_ag_call_dropped();
break;
case 'd':
printf("Report AG failure\n");
hfp_ag_report_extended_audio_gateway_error_result_code(device_addr, HFP_CME_ERROR_AG_FAILURE);
break;
case 'e':
printf("Answer call on AG\n");
hfp_ag_answer_incoming_call();
break;
case 'E':
printf("Reject call on AG\n");
hfp_ag_terminate_call();
break;
case 'f':
printf("Disable cellular network\n");
hfp_ag_set_registration_status(0);
break;
case 'F':
printf("Enable cellular network\n");
hfp_ag_set_registration_status(1);
break;
case 'g':
printf("Set signal strength to 0\n");
hfp_ag_set_signal_strength(0);
break;
case 'G':
printf("Set signal strength to 5\n");
hfp_ag_set_signal_strength(5);
break;
case 'h':
printf("Disable roaming\n");
hfp_ag_set_roaming_status(0);
break;
case 'H':
printf("Enable roaming\n");
hfp_ag_set_roaming_status(1);
break;
case 'i':
printf("Set battery level to 3\n");
hfp_ag_set_battery_level(3);
break;
case 'I':
printf("Set battery level to 5\n");
hfp_ag_set_battery_level(5);
break;
case 'j':
printf("Answering call on remote side\n");
hfp_ag_outgoing_call_established();
break;
case 'r':
printf("Disable in-band ring tone\n");
hfp_ag_set_use_in_band_ring_tone(0);
break;
case 'k':
printf("Memory 1 cleared\n");
memory_1_enabled = 0;
break;
case 'K':
printf("Memory 1 set\n");
memory_1_enabled = 1;
break;
case 'l':
printf("Last dialed number cleared\n");
last_number_exists = 0;
break;
case 'L':
printf("Last dialed number set\n");
last_number_exists = 1;
break;
case 'n':
printf("Disable Voice Recognition\n");
hfp_ag_activate_voice_recognition(device_addr, 0);
break;
case 'N':
printf("Enable Voice Recognition\n");
hfp_ag_activate_voice_recognition(device_addr, 1);
break;
case 'R':
printf("Enable in-band ring tone\n");
hfp_ag_set_use_in_band_ring_tone(1);
break;
case 't':
printf("Terminate HCI connection.\n");
gap_disconnect(handle);
default:
show_usage();
break;
@ -169,22 +298,68 @@ static int stdin_process(struct data_source *ds){
static void packet_handler(uint8_t * event, uint16_t event_size){
if (event[0] == RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE){
handle = READ_BT_16(event, 9);
printf("RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE received for handle 0x%04x\n", handle);
return;
}
if (event[0] != HCI_EVENT_HFP_META) return;
if (event[3]){
if (event[3] && event[2] != HFP_SUBEVENT_PLACE_CALL_WITH_NUMBER){
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");
printf("Service level connection established.\n");
break;
case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_RELEASED:
printf("Service level connection released.\n\n");
reset_pst_flags();
printf("Service level connection released.\n");
break;
case HFP_SUBEVENT_AUDIO_CONNECTION_ESTABLISHED:
printf("\n** Audio connection established **\n");
break;
case HFP_SUBEVENT_AUDIO_CONNECTION_RELEASED:
printf("\n** Audio connection released **\n");
break;
case HFP_SUBEVENT_START_RINGINIG:
printf("\n** Start Ringing **\n");
break;
case HFP_SUBEVENT_STOP_RINGINIG:
printf("\n** Stop Ringing **\n");
break;
case HFP_SUBEVENT_PLACE_CALL_WITH_NUMBER:
printf("\n** Outgoing call '%s' **\n", &event[3]);
// validate number
if ( strcmp("1234567", (char*) &event[3]) == 0
|| strcmp("7654321", (char*) &event[3]) == 0
|| (memory_1_enabled && strcmp(">1", (char*) &event[3]) == 0)){
printf("Dialstring valid: accept call\n");
hfp_ag_outgoing_call_accepted();
// TODO: calling ringing right away leads to callstatus=2 being skipped. don't call for now
// hfp_ag_outgoing_call_ringing();
} else {
printf("Dialstring invalid: reject call\n");
hfp_ag_outgoing_call_rejected();
}
break;
case HFP_SUBEVENT_REDIAL_LAST_NUMBER:
printf("\n** Redial last number\n");
if (last_number_exists){
hfp_ag_outgoing_call_accepted();
printf("Last number exists: accept call\n");
// TODO: calling ringing right away leads to callstatus=2 being skipped. don't call for now
// hfp_ag_outgoing_call_ringing();
} else {
printf("Last number missing: reject call\n");
hfp_ag_outgoing_call_rejected();
}
break;
default:
printf("event not handled %u\n", event[2]);
// printf("event not handled %u\n", event[2]);
break;
}
}
@ -195,7 +370,7 @@ int btstack_main(int argc, const char * argv[]){
l2cap_init();
rfcomm_init();
hfp_ag_init(rfcomm_channel_nr, 1007, codecs, sizeof(codecs),
hfp_ag_init(rfcomm_channel_nr, 0x3ef | (1<<HFP_AGSF_HF_INDICATORS), codecs, sizeof(codecs),
ag_indicators, ag_indicators_nr,
hf_indicators, hf_indicators_nr,
call_hold_services, call_hold_services_nr);
@ -206,7 +381,12 @@ int btstack_main(int argc, const char * argv[]){
sdp_init();
memset((uint8_t *)hfp_service_buffer, 0, sizeof(hfp_service_buffer));
hfp_ag_create_sdp_record((uint8_t *)hfp_service_buffer, rfcomm_channel_nr, hfp_ag_service_name, 0, 0);
sdp_register_service((uint8_t *)hfp_service_buffer);
sdp_register_service_internal(NULL, (uint8_t *)hfp_service_buffer);
// pre-select pts
memcpy(device_addr, pts_addr, 6);
// turn on!
hci_power_control(HCI_POWER_ON);

View File

@ -305,6 +305,9 @@ int btstack_main(int argc, const char * argv[]){
setup_audio();
hci_register_sco_packet_handler(&sco_packet_handler);
memset((uint8_t *)hsp_service_buffer, 0, sizeof(hsp_service_buffer));
hsp_hs_create_service((uint8_t *)hsp_service_buffer, rfcomm_channel_nr, hsp_hs_service_name, 0);
hsp_hs_init(rfcomm_channel_nr);
hsp_hs_register_packet_handler(packet_handler);