diff --git a/components/bt/CMakeLists.txt b/components/bt/CMakeLists.txt index d9cae26f1..64d0197c7 100644 --- a/components/bt/CMakeLists.txt +++ b/components/bt/CMakeLists.txt @@ -33,6 +33,7 @@ if(CONFIG_BT_ENABLED) host/bluedroid/bta/av/include host/bluedroid/bta/dm/include host/bluedroid/bta/gatt/include + host/bluedroid/bta/hf_ag/include host/bluedroid/bta/hf_client/include host/bluedroid/bta/hh/include host/bluedroid/bta/jv/include @@ -74,6 +75,7 @@ if(CONFIG_BT_ENABLED) "host/bluedroid/api/esp_gatt_common_api.c" "host/bluedroid/api/esp_gattc_api.c" "host/bluedroid/api/esp_gatts_api.c" + "host/bluedroid/api/esp_hf_ag_api.c" "host/bluedroid/api/esp_hf_client_api.c" "host/bluedroid/api/esp_spp_api.c" "host/bluedroid/bta/ar/bta_ar.c" @@ -116,6 +118,15 @@ if(CONFIG_BT_ENABLED) "host/bluedroid/bta/jv/bta_jv_api.c" "host/bluedroid/bta/jv/bta_jv_cfg.c" "host/bluedroid/bta/jv/bta_jv_main.c" + "host/bluedroid/bta/hf_ag/bta_ag_act.c" + "host/bluedroid/bta/hf_ag/bta_ag_api.c" + "host/bluedroid/bta/hf_ag/bta_ag_at.c" + "host/bluedroid/bta/hf_ag/bta_ag_cfg.c" + "host/bluedroid/bta/hf_ag/bta_ag_cmd.c" + "host/bluedroid/bta/hf_ag/bta_ag_main.c" + "host/bluedroid/bta/hf_ag/bta_ag_rfc.c" + "host/bluedroid/bta/hf_ag/bta_ag_sco.c" + "host/bluedroid/bta/hf_ag/bta_ag_sdp.c" "host/bluedroid/bta/hf_client/bta_hf_client_act.c" "host/bluedroid/bta/hf_client/bta_hf_client_api.c" "host/bluedroid/bta/hf_client/bta_hf_client_at.c" @@ -151,6 +162,8 @@ if(CONFIG_BT_ENABLED) "host/bluedroid/btc/profile/std/a2dp/btc_av.c" "host/bluedroid/btc/profile/std/avrc/btc_avrc.c" "host/bluedroid/btc/profile/std/avrc/bta_avrc_co.c" + "host/bluedroid/btc/profile/std/hf_ag/bta_ag_co.c" + "host/bluedroid/btc/profile/std/hf_ag/btc_hf_ag.c" "host/bluedroid/btc/profile/std/hf_client/btc_hf_client.c" "host/bluedroid/btc/profile/std/hf_client/bta_hf_client_co.c" "host/bluedroid/btc/profile/std/gap/btc_gap_ble.c" diff --git a/components/bt/common/btc/core/btc_task.c b/components/bt/common/btc/core/btc_task.c index cdcfe7470..eeea36e96 100644 --- a/components/bt/common/btc/core/btc_task.c +++ b/components/bt/common/btc/core/btc_task.c @@ -47,6 +47,9 @@ #if (BTC_SPP_INCLUDED == TRUE) #include "btc_spp.h" #endif /* #if (BTC_SPP_INCLUDED == TRUE) */ +#if BTC_HF_INCLUDED +#include "btc_hf_ag.h" +#endif/* #if BTC_HF_INCLUDED */ #if BTC_HF_CLIENT_INCLUDED #include "btc_hf_client.h" #endif /* #if BTC_HF_CLIENT_INCLUDED */ @@ -108,6 +111,9 @@ static const btc_func_t profile_tab[BTC_PID_NUM] = { #if (BTC_SPP_INCLUDED == TRUE) [BTC_PID_SPP] = {btc_spp_call_handler, btc_spp_cb_handler }, #endif /* #if (BTC_SPP_INCLUDED == TRUE) */ +#if BTC_HF_INCLUDED + [BTC_PID_HF] = {btc_hf_call_handler, btc_hf_cb_handler}, +#endif /* #if BTC_HF_INCLUDED */ #if BTC_HF_CLIENT_INCLUDED [BTC_PID_HF_CLIENT] = {btc_hf_client_call_handler, btc_hf_client_cb_handler}, #endif /* #if BTC_HF_CLIENT_INCLUDED */ diff --git a/components/bt/common/btc/include/btc/btc_task.h b/components/bt/common/btc/include/btc/btc_task.h index 45512d729..eb37c1c36 100644 --- a/components/bt/common/btc/include/btc/btc_task.h +++ b/components/bt/common/btc/include/btc/btc_task.h @@ -63,6 +63,9 @@ typedef enum { BTC_PID_AVRC_CT, BTC_PID_AVRC_TG, BTC_PID_SPP, +#if (BTC_HF_INCLUDED == TRUE) + BTC_PID_HF, +#endif /* BTC_HF_INCLUDED */ #if (BTC_HF_CLIENT_INCLUDED == TRUE) BTC_PID_HF_CLIENT, #endif /* BTC_HF_CLIENT_INCLUDED */ diff --git a/components/bt/component.mk b/components/bt/component.mk index da70ea45a..41d9bb17f 100644 --- a/components/bt/component.mk +++ b/components/bt/component.mk @@ -27,6 +27,7 @@ ifdef CONFIG_BT_BLUEDROID_ENABLED COMPONENT_PRIV_INCLUDEDIRS += host/bluedroid/bta/include \ host/bluedroid/bta/ar/include \ host/bluedroid/bta/av/include \ + host/bluedroid/bta/hf_ag/include \ host/bluedroid/bta/hf_client/include \ host/bluedroid/bta/dm/include \ host/bluedroid/bta/gatt/include \ @@ -77,6 +78,7 @@ COMPONENT_SRCDIRS += host/bluedroid/bta/dm \ host/bluedroid/bta/ar \ host/bluedroid/bta/sys \ host/bluedroid/bta/jv \ + host/bluedroid/bta/hf_ag \ host/bluedroid/bta/hf_client \ host/bluedroid/bta \ host/bluedroid/btif \ @@ -94,6 +96,7 @@ COMPONENT_SRCDIRS += host/bluedroid/bta/dm \ host/bluedroid/btc/profile/std/a2dp \ host/bluedroid/btc/profile/std/avrc \ host/bluedroid/btc/profile/std/spp \ + host/bluedroid/btc/profile/std/hf_ag \ host/bluedroid/btc/profile/std/hf_client \ host/bluedroid/btc/profile \ host/bluedroid/stack/btm \ diff --git a/components/bt/host/bluedroid/Kconfig.in b/components/bt/host/bluedroid/Kconfig.in index fa39d4820..1d5551615 100644 --- a/components/bt/host/bluedroid/Kconfig.in +++ b/components/bt/host/bluedroid/Kconfig.in @@ -72,6 +72,9 @@ choice BT_HFP_ROLE config BT_HFP_CLIENT_ENABLE bool "Hands Free Unit" + + config BT_HFP_AG_ENABLE + bool "Audio Gateway" endchoice choice BT_HFP_AUDIO_DATA_PATH diff --git a/components/bt/host/bluedroid/api/esp_hf_ag_api.c b/components/bt/host/bluedroid/api/esp_hf_ag_api.c new file mode 100644 index 000000000..f10c5a871 --- /dev/null +++ b/components/bt/host/bluedroid/api/esp_hf_ag_api.c @@ -0,0 +1,529 @@ +// Copyright 2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include "bt_common.h" +#include "btc/btc_common.h" +#include "btc/btc_dm.h" +#include "btc_hf_ag.h" +#include "btc/btc_profile_queue.h" +#include "btc/btc_manage.h" +#include "btc/btc_util.h" +#include "bta/bta_ag_api.h" +#include "bta/bta_api.h" +#include "common/bt_target.h" +#include "common/bt_defs.h" +#include "device/bdaddr.h" +#include "esp_bt.h" +#include "esp_hf_ag_api.h" +#include "esp_err.h" +#include "esp_bt_main.h" +#include "osi/allocator.h" + +#if (BTC_HF_INCLUDED == TRUE) +esp_err_t esp_bt_hf_register_callback(esp_hf_cb_t callback) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + if (callback == NULL) { + return ESP_FAIL; + } + btc_profile_cb_set(BTC_PID_HF, callback); + return ESP_OK; +} + +esp_err_t esp_bt_hf_init(esp_bd_addr_t remote_addr) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_INIT_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.init), remote_addr, sizeof(esp_bd_addr_t)); + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL); + return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hf_deinit(esp_bd_addr_t remote_addr) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_DEINIT_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.deinit), remote_addr, sizeof(esp_bd_addr_t)); + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL); + return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hf_connect(esp_bd_addr_t remote_addr) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_CONNECT_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.connect), remote_addr, sizeof(esp_bd_addr_t)); + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL); + return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hf_disconnect(esp_bd_addr_t remote_addr) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_DISCONNECT_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.disconnect), remote_addr, sizeof(esp_bd_addr_t)); + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL); + return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hf_connect_audio(esp_bd_addr_t remote_addr) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_CONNECT_AUDIO_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.connect_audio), remote_addr, sizeof(esp_bd_addr_t)); + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL); + return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hf_disconnect_audio(esp_bd_addr_t remote_addr) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_DISCONNECT_AUDIO_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.disconnect_audio), remote_addr, sizeof(esp_bd_addr_t)); + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL); + return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hf_vra(esp_bd_addr_t remote_addr, esp_hf_vr_state_t value) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_VRA_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + arg.vra_rep.value = value; + memcpy(&(arg.volcon.remote_addr), remote_addr, sizeof(esp_bd_addr_t)); + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL); + return (status = BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hf_volume_control(esp_bd_addr_t remote_addr, esp_hf_volume_control_target_t type, int volume) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_VOLUME_CONTROL_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + arg.volcon.target_type = type; + arg.volcon.volume = volume; + memcpy(&(arg.volcon.remote_addr), remote_addr, sizeof(esp_bd_addr_t)); + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL); + return (status = BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_hf_unat_response(esp_bd_addr_t remote_addr, char *unat) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_UNAT_RESPONSE_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + arg.unat_rep.unat = unat; + memcpy(&(arg.unat_rep.remote_addr), remote_addr, sizeof(esp_bd_addr_t)); + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), btc_hf_arg_deep_copy); + return (status = BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hf_cmee_response(esp_bd_addr_t remote_addr, esp_hf_at_response_code_t response_code, esp_hf_cme_err_t error_code) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_CME_ERR_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + arg.ext_at.response_code = response_code; + arg.ext_at.error_code = error_code; + memcpy(&(arg.ext_at.remote_addr), remote_addr, sizeof(esp_bd_addr_t)); + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL); + return (status = BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hf_indchange_notification(esp_bd_addr_t remote_addr, + esp_hf_call_status_t call_state, + esp_hf_call_setup_status_t call_setup_state, + esp_hf_network_state_t ntk_state, int signal) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_IND_NOTIFICATION_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.ind_change.remote_addr), remote_addr, sizeof(esp_bd_addr_t)); + arg.ind_change.call_state = call_state; + arg.ind_change.call_setup_state = call_setup_state; + arg.ind_change.ntk_state = ntk_state; + arg.ind_change.signal = signal; + + /* Switch to BTC context */ + bt_status_t state = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL); + return (state == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hf_cind_response(esp_bd_addr_t remote_addr, + esp_hf_call_status_t call_state, + esp_hf_call_setup_status_t call_setup_state, + esp_hf_network_state_t ntk_state, int signal, esp_hf_roaming_status_t roam, int batt_lev, + esp_hf_call_held_status_t call_held_status) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_CIND_RESPONSE_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.cind_rep.remote_addr), remote_addr, sizeof(esp_bd_addr_t)); + arg.cind_rep.call_state = call_state; + arg.cind_rep.call_setup_state = call_setup_state; + arg.cind_rep.ntk_state = ntk_state; + arg.cind_rep.signal = signal; + arg.cind_rep.roam = roam; + arg.cind_rep.batt_lev = batt_lev; + arg.cind_rep.call_held_state = call_held_status; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hf_cops_response(esp_bd_addr_t remote_addr, char *name) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_COPS_RESPONSE_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.cops_rep.remote_addr), remote_addr, sizeof(esp_bd_addr_t)); + arg.cops_rep.name = name; //deep_copy + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), btc_hf_arg_deep_copy); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hf_clcc_response(esp_bd_addr_t remote_addr, int index, esp_hf_current_call_direction_t dir, + esp_hf_current_call_status_t current_call_state, esp_hf_current_call_mode_t mode, + esp_hf_current_call_mpty_type_t mpty, char *number, esp_hf_call_addr_type_t type) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_CLCC_RESPONSE_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.clcc_rep.remote_addr), remote_addr, sizeof(esp_bd_addr_t)); + //mandatory args + arg.clcc_rep.index = index; + arg.clcc_rep.dir = dir; + arg.clcc_rep.current_call_state = current_call_state; + arg.clcc_rep.mode = mode; + arg.clcc_rep.mpty = mpty; + // option args + arg.clcc_rep.number = number; //deep_copy + arg.clcc_rep.type = type; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), btc_hf_arg_deep_copy); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hf_cnum_response(esp_bd_addr_t remote_addr, char *number, esp_hf_subscriber_service_type_t type) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_CNUM_RESPONSE_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.cnum_rep), remote_addr, sizeof(esp_bd_addr_t)); + arg.cnum_rep.number = number; //deep_copy + arg.cnum_rep.type = type; + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), btc_hf_arg_deep_copy); + return (status = BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hf_bsir(esp_bd_addr_t remote_addr, esp_hf_in_band_ring_state_t state) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_INBAND_RING_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.bsir.remote_addr), remote_addr, sizeof(esp_bd_addr_t)); + arg.bsir.state = state; + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL); + return (status = BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hf_answer_call(esp_bd_addr_t remote_addr, int num_active, int num_held, + esp_hf_call_status_t call_state, esp_hf_call_setup_status_t call_setup_state, + char *number, esp_hf_call_addr_type_t call_addr_type) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_AC_INCALL_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.phone.remote_addr), remote_addr, sizeof(esp_bd_addr_t)); + arg.phone.num_active = num_active; + arg.phone.num_held = num_held; + arg.phone.call_state = call_state; + arg.phone.call_setup_state = call_setup_state; + arg.phone.number = number; //deep_copy + arg.phone.call_addr_type = call_addr_type; + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), btc_hf_arg_deep_copy); + return (status = BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hf_reject_call(esp_bd_addr_t remote_addr, int num_active, int num_held, + esp_hf_call_status_t call_state, esp_hf_call_setup_status_t call_setup_state, + char *number, esp_hf_call_addr_type_t call_addr_type) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_RJ_INCALL_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.phone.remote_addr), remote_addr, sizeof(esp_bd_addr_t)); + arg.phone.num_active = num_active; + arg.phone.num_held = num_held; + arg.phone.call_state = call_state; + arg.phone.call_setup_state = call_setup_state; + arg.phone.number = number; //deep_copy + arg.phone.call_addr_type = call_addr_type; + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), btc_hf_arg_deep_copy); + return (status = BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hf_end_call(esp_bd_addr_t remote_addr, int num_active, int num_held, + esp_hf_call_status_t call_state, esp_hf_call_setup_status_t call_setup_state, + char *number, esp_hf_call_addr_type_t call_addr_type) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_END_CALL_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.phone.remote_addr), remote_addr, sizeof(esp_bd_addr_t)); + arg.phone.num_active = num_active; + arg.phone.num_held = num_held; + arg.phone.call_state = call_state; + arg.phone.call_setup_state = call_setup_state; + arg.phone.number = number; //deep_copy + arg.phone.call_addr_type = call_addr_type; + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), btc_hf_arg_deep_copy); + return (status = BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hf_out_call(esp_bd_addr_t remote_addr, int num_active, int num_held, + esp_hf_call_status_t call_state, esp_hf_call_setup_status_t call_setup_state, + char *number, esp_hf_call_addr_type_t call_addr_type) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_OUT_CALL_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + memcpy(&(arg.phone.remote_addr), remote_addr, sizeof(esp_bd_addr_t)); + arg.phone.num_active = num_active; + arg.phone.num_held = num_held; + arg.phone.call_state = call_state; + arg.phone.call_setup_state = call_setup_state; + arg.phone.number = number; //deep_copy + arg.phone.call_addr_type = call_addr_type; + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), btc_hf_arg_deep_copy); + return (status = BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hf_register_data_callback(esp_hf_incoming_data_cb_t recv, esp_hf_outgoing_data_cb_t send) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HF; + msg.act = BTC_HF_REGISTER_DATA_CALLBACK_EVT; + + btc_hf_args_t arg; + memset(&arg, 0, sizeof(btc_hf_args_t)); + arg.reg_data_cb.recv = recv; + arg.reg_data_cb.send = send; + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL); + return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +#if (BTM_SCO_HCI_INCLUDED == TRUE) +void esp_hf_outgoing_data_ready(void) +{ + BTA_AgCiData(); +} +#endif /* #if (BTM_SCO_HCI_INCLUDED == TRUE ) */ + +#endif // BTC_HF_INCLUDED \ No newline at end of file diff --git a/components/bt/host/bluedroid/api/include/api/esp_hf_ag_api.h b/components/bt/host/bluedroid/api/include/api/esp_hf_ag_api.h new file mode 100644 index 000000000..65ac4cf3c --- /dev/null +++ b/components/bt/host/bluedroid/api/include/api/esp_hf_ag_api.h @@ -0,0 +1,535 @@ +// Copyright 2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef __ESP_HF_AG_API_H__ +#define __ESP_HF_AG_API_H__ + +#include "esp_err.h" +#include "esp_bt_defs.h" +#include "esp_hf_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* features masks of HF AG */ +#define ESP_HF_PEER_FEAT_3WAY 0x01 /* Three-way calling */ +#define ESP_HF_PEER_FEAT_ECNR 0x02 /* Echo cancellation and/or noise reduction */ +#define ESP_HF_PEER_FEAT_VREC 0x04 /* Voice recognition */ +#define ESP_HF_PEER_FEAT_INBAND 0x08 /* In-band ring tone */ +#define ESP_HF_PEER_FEAT_VTAG 0x10 /* Attach a phone number to a voice tag */ +#define ESP_HF_PEER_FEAT_REJECT 0x20 /* Ability to reject incoming call */ +#define ESP_HF_PEER_FEAT_ECS 0x40 /* Enhanced Call Status */ +#define ESP_HF_PEER_FEAT_ECC 0x80 /* Enhanced Call Control */ +#define ESP_HF_PEER_FEAT_EXTERR 0x100 /* Extended error codes */ +#define ESP_HF_PEER_FEAT_CODEC 0x200 /* Codec Negotiation */ + +/* CHLD feature masks of HF AG */ +#define ESP_HF_CHLD_FEAT_REL 0x01 /* 0 Release waiting call or held calls */ +#define ESP_HF_CHLD_FEAT_REL_ACC 0x02 /* 1 Release active calls and accept other waiting or held call */ +#define ESP_HF_CHLD_FEAT_REL_X 0x04 /* 1x Release specified active call only */ +#define ESP_HF_CHLD_FEAT_HOLD_ACC 0x08 /* 2 Active calls on hold and accept other waiting or held call */ +#define ESP_HF_CHLD_FEAT_PRIV_X 0x10 /* 2x Request private mode with specified call(put the rest on hold) */ +#define ESP_HF_CHLD_FEAT_MERGE 0x20 /* 3 Add held call to multiparty */ +#define ESP_HF_CHLD_FEAT_MERGE_DETACH 0x40 /* 4 Connect two calls and leave(disconnect from multiparty) */ + +/// HF callback events +typedef enum +{ + ESP_HF_CONNECTION_STATE_EVT = 0, /*!< Connection state changed event */ + ESP_HF_AUDIO_STATE_EVT, /*!< Audio connection state change event */ + ESP_HF_BVRA_EVT, /*!< Voice recognition state change event */ + ESP_HF_VOLUME_CONTROL_EVT, /*!< Audio volume control command from HF Client, provided by +VGM or +VGS message */ + + ESP_HF_UNAT_RESPONSE_EVT, /*!< Unknown AT cmd Response*/ + ESP_HF_CIND_RESPONSE_EVT, /*!< Call And Device Indicator Response*/ + ESP_HF_COPS_RESPONSE_EVT, /*!< Current operator information */ + ESP_HF_CLCC_RESPONSE_EVT, /*!< List of current calls notification */ + ESP_HF_CNUM_RESPONSE_EVT, /*!< Subscriber information response from HF Client */ + ESP_HF_VTS_RESPONSE_EVT, /*!< Enable or not DTMF */ + ESP_HF_NREC_RESPONSE_EVT, /*!< Enable or not NREC */ + + ESP_HF_ATA_RESPONSE_EVT, /*!< Answer an Incoming Call */ + ESP_HF_CHUP_RESPONSE_EVT, /*!< Reject an Incoming Call */ + ESP_HF_DIAL_EVT, /*!< Origin an outgoing call with specific number or the dial the last number */ + ESP_HF_BAC_RESPONSE_EVT, /*!< Codec Negotiation */ + ESP_HF_BCS_RESPONSE_EVT, /*!< Codec Negotiation */ +} esp_hf_cb_event_t; + +// HF callback parameters of corresponding esp event in esp_hf_cb_event_t +typedef union +{ + /** + * @brief ESP_HS_CONNECTION_STATE_EVT + */ + struct hf_conn_stat_param { + esp_bd_addr_t remote_bda; /*!< remote bluetooth device address */ + esp_hf_connection_state_t state; /*!< HF connection state */ + uint32_t peer_feat; /*!< AG supported features */ + uint32_t chld_feat; /*!< AG supported features on call hold and multiparty services */ + } conn_stat; /*!< AG callback param of ESP_AG_CONNECTION_STATE_EVT */ + + /** + * @brief ESP_HF_AUDIO_STATE_EVT + */ + struct hf_audio_stat_param { + esp_bd_addr_t remote_addr; /*!< remote bluetooth device address */ + esp_hf_audio_state_t state; /*!< audio connection state */ + } audio_stat; /*!< AG callback param of ESP_AG_AUDIO_STATE_EVT */ + + /** + * @brief ESP_HF_BVRA_EVT + */ + struct hf_bvra_param { + esp_bd_addr_t remote_addr; + esp_hf_vr_state_t value; /*!< voice recognition state */ + } vra_rep; /*!< AG callback param of ESP_AG_BVRA_EVT */ + + /** + * @brief ESP_HF_VOLUME_CONTROL_EVT + */ + struct hf_volume_control_param { + esp_hf_volume_type_t type; /*!< volume control target, speaker or microphone */ + int volume; /*!< gain, ranges from 0 to 15 */ + } volume_control; /*!< AG callback param of ESP_AG_VOLUME_CONTROL_EVT */ + + /** + * @brief ESP_HF_UNAT_RESPOSNE_EVT + */ + struct hf_unat_param { + char *unat; + }unat_rep; + + /** + * @brief ESP_HF_AT_RESPONSE_EVT + */ + struct hf_at_code_param { + esp_hf_at_response_code_t code; /*!< AT response code */ + esp_hf_cme_err_t cme; /*!< Extended Audio Gateway Error Result Code */ + } at; /*!< AG callback param of ESP_HF_EXT_AT_EVT */ + + /** + * @brief ESP_HF_CIND_CALL_EVT + */ + struct hf_cind_param { + esp_hf_call_status_t call_status; /*!< call status indicator */ + esp_hf_call_setup_status_t call_setup_status; /*!< call setup status indicator */ + esp_hf_network_state_t svc; /*!< bluetooth proprietary call hold status indicator */ + int signal_strength; /*!< bluetooth proprietary call hold status indicator */ + esp_hf_roaming_status_t roam; /*!< bluetooth proprietary call hold status indicator */ + int battery_level; /*!< battery charge value, ranges from 0 to 5 */ + esp_hf_call_held_status_t call_held_status; /*!< bluetooth proprietary call hold status indicator */ + } cind; + + /** + * @brief ESP_HF_VTS_RESPOSNE_EVT + */ + struct hf_vts_param { + char *code; + }vts_rep; + + /** + * @brief ESP_HF_NREC_RESPOSNE_EVT + */ + struct hf_nrec_param { + esp_hf_nrec_t state; + } nrec; + + struct hf_outcall_param { + esp_bd_addr_t remote_addr; + char *num_or_loc; + } out_call; + + /** + * @brief ESP_HF_BSIR_EVT + */ + struct hf_bsir_param { + esp_bd_addr_t remote_addr; + esp_hf_in_band_ring_state_t state; /*!< setting state of in-band ring tone */ + } bsir; + + /** + * @brief ESP_HF_BCS_RESPONSE_EVT + */ + struct hf_codec_param { + esp_hf_wbs_config_t mode; + } codec; + +} esp_hf_cb_param_t; + +/** + * @brief AG incoming data callback function, the callback is useful in case of + * Voice Over HCI. + * @param[in] buf : pointer to incoming data(payload of HCI synchronous data packet), the + * buffer is allocated inside bluetooth protocol stack and will be released after + * invoke of the callback is finished. + * @param[in] len : size(in bytes) in buf + */ +typedef void (* esp_hf_incoming_data_cb_t)(const uint8_t *buf, uint32_t len); + +/** + * @brief AG outgoing data callback function, the callback is useful in case of + * Voice Over HCI. Once audio connection is set up and the application layer has + * prepared data to send, the lower layer will call this function to read data + * and then send. This callback is supposed to be implemented as non-blocking, + * and if data is not enough, return value 0 is supposed. + * + * @param[in] buf : pointer to incoming data(payload of HCI synchronous data packet), the + * buffer is allocated inside bluetooth protocol stack and will be released after + * invoke of the callback is finished. + * @param[in] len : size(in bytes) in buf + * @param[out] length of data successfully read + */ +typedef uint32_t (* esp_hf_outgoing_data_cb_t) (uint8_t *buf, uint32_t len); + +/** + * @brief HF AG callback function type + * + * @param event : Event type + * + * @param param : Pointer to callback parameter + */ +typedef void (* esp_hf_cb_t) (esp_hf_cb_event_t event, esp_hf_cb_param_t *param); + +/************************************************************************************ +** ESP HF API +************************************************************************************/ +/** + * @brief Register application callback function to HFP client module. This function should be called + * only after esp_bluedroid_enable() completes successfully, used by HFP client + * + * @param[in] callback: HFP AG event callback function + * + * @return + * - ESP_OK: success + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: if callback is a NULL function pointer + * + */ +esp_err_t esp_bt_hf_register_callback(esp_hf_cb_t callback); + +/** + * + * @brief Initialize the bluetooth HF AG module. This function should be called + * after esp_bluedroid_enable() completes successfully + * + * @return + * - ESP_OK: if the initialization request is sent successfully + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_bt_hf_init(esp_bd_addr_t remote_addr); + +/** + * + * @brief De-initialize for HF AG module. This function + * should be called only after esp_bluedroid_enable() completes successfully + * + * @return + * - ESP_OK: success + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_bt_hf_deinit(esp_bd_addr_t remote_addr); + +/** + * + * @brief Connect to remote bluetooth HFP client device, must after esp_bt_hf_init() + * + * @param[in] remote_bda: remote bluetooth HFP client device address + * + * @return + * - ESP_OK: connect request is sent to lower layer + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_bt_hf_connect(esp_bd_addr_t remote_bda); + +/** + * + * @brief Disconnect from the remote HFP client + * + * @param[in] remote_bda: remote bluetooth device address + * @return + * - ESP_OK: disconnect request is sent to lower layer + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_bt_hf_disconnect(esp_bd_addr_t remote_bda); + +/** + * + * @brief Create audio connection with remote HFP client. As a precondition to use this API, + * Service Level Connection shall exist between HF client and AG. + * + * @param[in] remote_bda: remote bluetooth device address + * @return + * - ESP_OK: disconnect request is sent to lower layer + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_bt_hf_connect_audio(esp_bd_addr_t remote_bda); + +/** + * + * @brief Release the established audio connection with remote HFP client. + * + * @param[in] remote_bda: remote bluetooth device address + * @return + * - ESP_OK: disconnect request is sent to lower layer + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_bt_hf_disconnect_audio(esp_bd_addr_t remote_bda); + +/** + * + * @brief Response of Volume Recognition Command(AT+VRA) from HFP client. As a precondition to use this API, + * Service Level Connection shall exist with HFP client. + * + * @param[in] remote: volume control target, speaker or microphone + * @param[in] volume: gain of the speaker of microphone, ranges 0 to 15 + * + * @return + * - ESP_OK: disconnect request is sent to lower layer + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_bt_hf_vra(esp_bd_addr_t remote_bda, esp_hf_vr_state_t value); + +/** + * + * @brief Volume synchronization with HFP client. As a precondition to use this API, + * Service Level Connection shall exist with HFP client + * + * @param[in] type: volume control target, speaker or microphone + * @param[in] volume: gain of the speaker of microphone, ranges 0 to 15 + * + * @return + * - ESP_OK: disconnect request is sent to lower layer + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_bt_hf_volume_control(esp_bd_addr_t remote_bda, esp_hf_volume_control_target_t type, int volume); + + /** + * + * @brief Handle Unknown AT command from HFP Client. + * As a precondition to use this API, Service Level Connection shall exist between AG and HF Client + * + * @return + * - ESP_OK: disconnect request is sent to lower layer + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_hf_unat_response(esp_bd_addr_t remote_addr, char *unat); + + /** + * + * @brief Unsolicited send extend AT error code to HFP Client. + * As a precondition to use this API, Service Level Connection shall exist between AG and HF Client + * + * @return + * - ESP_OK: disconnect request is sent to lower layer + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_bt_hf_cmee_response(esp_bd_addr_t remote_bda, esp_hf_at_response_code_t response_code, esp_hf_cme_err_t error_code); + + /** + * + * @brief Usolicited send device status notificationto HFP Client. + * As a precondition to use this API, Service Level Connection shall exist between AG and HF Client + * + * @return + * - ESP_OK: disconnect request is sent to lower layer + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_bt_hf_indchange_notification(esp_bd_addr_t remote_addr, esp_hf_call_status_t call_state, + esp_hf_call_setup_status_t call_setup_state, + esp_hf_network_state_t ntk_state, int signal); + + /** + * + * @brief Response to device individual indicatiors to HFP Client. + * As a precondition to use this API, Service Level Connection shall exist between AG and HF Client. + * + * @return + * - ESP_OK: disconnect request is sent to lower layer + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_bt_hf_cind_response(esp_bd_addr_t remote_addr, + esp_hf_call_status_t call_state, + esp_hf_call_setup_status_t call_setup_state, + esp_hf_network_state_t ntk_state, int signal, esp_hf_roaming_status_t roam, int batt_lev, + esp_hf_call_held_status_t call_held_status); + +/** + * + * @brief Query the name of currently selected network operator in AG, + * As a precondition to use this API, Service Level Connection shall exist with HFP Client + * + * @return + * - ESP_OK: disconnect request is sent to lower layer + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_bt_hf_cops_response(esp_bd_addr_t remote_addr, char *name); + +/** + * + * @brief Response to Query list of current calls from HFP Client (use AT+CLCC command), + * As a precondition to use this API, Service Level Connection shall exist between AG and HF Client + * + * @return + * - ESP_OK: disconnect request is sent to lower layer + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_bt_hf_clcc_response(esp_bd_addr_t remote_addr, int index, esp_hf_current_call_direction_t dir, + esp_hf_current_call_status_t current_call_state, esp_hf_current_call_mode_t mode, + esp_hf_current_call_mpty_type_t mpty, char *number, esp_hf_call_addr_type_t type); + +/** + * + * @brief Get subscriber information number from HFP client(send AT+CNUM command), + * As a precondition to use this API, Service Level Connection shall exist with AG + * + * @return + * - ESP_OK: disconnect request is sent to lower layer + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_bt_hf_cnum_response(esp_bd_addr_t remote_addr, char *number, esp_hf_subscriber_service_type_t type); + +/** + * + * @brief Inform HF Client of Provided or not Inband Ring Tone. + * As a precondition to use this API, Service Level Connection shall exist with AG + * + * @return + * - ESP_OK: disconnect request is sent to lower layer + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_bt_hf_bsir(esp_bd_addr_t remote_addr, esp_hf_in_band_ring_state_t state); + +/** + * + * @brief Answer Incoming Call by AG or response to the AT+A command from Hands-Free Unit. + * As a precondition to use this API, Service Level Connection shall exist with AG. + * + * @return + * - ESP_OK: disconnect request is sent to lower layer + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_bt_hf_answer_call(esp_bd_addr_t remote_addr, int num_active, int num_held, + esp_hf_call_status_t call_state, esp_hf_call_setup_status_t call_setup_state, + char *number, esp_hf_call_addr_type_t call_addr_type); + +/** + * + * @brief Reject Incoming Call by AG or response to the AT+CHUP command from Hands-Free Unit. + * As a precondition to use this API, Service Level Connection shall exist with AG. + * + * @return + * - ESP_OK: disconnect request is sent to lower layer + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_bt_hf_reject_call(esp_bd_addr_t remote_addr, int num_active, int num_held, + esp_hf_call_status_t call_state, esp_hf_call_setup_status_t call_setup_state, + char *number, esp_hf_call_addr_type_t call_addr_type); + +/** + * + * @brief Reject Incoming Call by AG or response to the AT+CHUP command from Hands-Free Unit. + * As a precondition to use this API, Service Level Connection shall exist with AG. + * + * @return + * - ESP_OK: disconnect request is sent to lower layer + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_bt_hf_out_call(esp_bd_addr_t remote_addr, int num_active, int num_held, + esp_hf_call_status_t call_state, esp_hf_call_setup_status_t call_setup_state, + char *number, esp_hf_call_addr_type_t call_addr_type); + +/** + * + * @brief Reject Incoming Call by AG or response to the AT+CHUP command from Hands-Free Unit. + * As a precondition to use this API, Service Level Connection shall exist with AG. + * + * @return + * - ESP_OK: disconnect request is sent to lower layer + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + * + */ +esp_err_t esp_bt_hf_end_call(esp_bd_addr_t remote_addr, int num_active, int num_held, + esp_hf_call_status_t call_state, esp_hf_call_setup_status_t call_setup_state, + char *number, esp_hf_call_addr_type_t call_addr_type); + +/** + * @brief Register AG data output function; the callback is only used in + * the case that Voice Over HCI is enabled. + * + * @param[in] recv: HFP client incoming data callback function + * @param[in] send: HFP client outgoing data callback function + * + * @return + * - ESP_OK: success + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: if callback is a NULL function pointer + * + */ +esp_err_t esp_bt_hf_register_data_callback(esp_hf_incoming_data_cb_t recv, esp_hf_outgoing_data_cb_t send); + + +/** + * @brief Trigger the lower-layer to fetch and send audio data. This function is only + * only used in the case that Voice Over HCI is enabled. Precondition is that + * the HFP audio connection is connected. After this function is called, lower + * layer will invoke esp_hf_client_outgoing_data_cb_t to fetch data + * + */ +void esp_hf_outgoing_data_ready(void); + +#ifdef __cplusplus +} +#endif + +#endif //__ESP_HF_AG_API_H__ \ No newline at end of file diff --git a/components/bt/host/bluedroid/api/include/api/esp_hf_client_api.h b/components/bt/host/bluedroid/api/include/api/esp_hf_client_api.h index d82308b2c..d1ae05799 100644 --- a/components/bt/host/bluedroid/api/include/api/esp_hf_client_api.h +++ b/components/bt/host/bluedroid/api/include/api/esp_hf_client_api.h @@ -126,7 +126,7 @@ typedef union { * @brief ESP_HF_CLIENT_CIND_SERVICE_AVAILABILITY_EVT */ struct hf_client_service_availability_param { - esp_hf_service_availability_status_t status; /*!< service availability status */ + esp_hf_network_state_t status; /*!< service availability status */ } service_availability; /*!< HF callback param of ESP_HF_CLIENT_CIND_SERVICE_AVAILABILITY_EVT */ /** diff --git a/components/bt/host/bluedroid/api/include/api/esp_hf_defs.h b/components/bt/host/bluedroid/api/include/api/esp_hf_defs.h index 1ff9f14f6..5e3e3cfa4 100644 --- a/components/bt/host/bluedroid/api/include/api/esp_hf_defs.h +++ b/components/bt/host/bluedroid/api/include/api/esp_hf_defs.h @@ -21,6 +21,44 @@ extern "C" { #endif +#define ESP_BT_HF_NUMBER_LEN (32) +#define ESP_BT_HF_OPERATOR_NAME_LEN (16) + +#ifndef BTC_HSAG_SERVICE_NAME +#define BTC_HSAG_SERVICE_NAME ("Headset Gateway") +#endif + +#ifndef BTC_HFAG_SERVICE_NAME +#define BTC_HFAG_SERVICE_NAME ("Handsfree Gateway") +#endif + +#ifndef BTC_HF_SERVICES +#define BTC_HF_SERVICES (BTA_HSP_SERVICE_MASK | BTA_HFP_SERVICE_MASK ) +#endif + +#ifndef BTC_HF_SERVICE_NAMES +#define BTC_HF_SERVICE_NAMES {BTC_HSAG_SERVICE_NAME , BTC_HFAG_SERVICE_NAME} +#endif + +#ifndef BTC_HF_SECURITY +#define BTC_HF_SECURITY (BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT) +#endif + +#define BTC_HF_CALL_END_TIMEOUT 6 + +#define BTC_HF_INVALID_IDX -1 + +/// in-band ring tone state +typedef enum { + ESP_HF_IN_BAND_RINGTONE_NOT_PROVIDED = 0, + ESP_HF_IN_BAND_RINGTONE_PROVIDED, +} esp_hf_in_band_ring_state_t; + +/// voice recognition state +typedef enum { + ESP_HF_VR_STATE_DISABLED = 0, /*!< voice recognition disabled */ + ESP_HF_VR_STATE_ENABLED, /*!< voice recognition enabled */ +} esp_hf_vr_state_t; /// Bluetooth HFP audio volume control target typedef enum { @@ -28,11 +66,32 @@ typedef enum { ESP_HF_VOLUME_CONTROL_TARGET_MIC, /*!< microphone */ } esp_hf_volume_control_target_t; -/// +CIND roaming status indicator values +/// Bluetooth HFP audio connection status typedef enum { - ESP_HF_ROAMING_STATUS_INACTIVE = 0, /*!< roaming is not active */ - ESP_HF_ROAMING_STATUS_ACTIVE, /*!< a roaming is active */ -} esp_hf_roaming_status_t; + ESP_HF_AUDIO_STATE_DISCONNECTED = 0, /*!< audio connection released */ + ESP_HF_AUDIO_STATE_CONNECTING, /*!< audio connection has been initiated */ + ESP_HF_AUDIO_STATE_CONNECTED, /*!< audio connection is established */ + ESP_HF_AUDIO_STATE_CONNECTED_MSBC, /*!< mSBC audio connection is established */ +} esp_hf_audio_state_t; + +typedef enum { + ESP_HF_VOLUME_TYPE_SPK = 0, + ESP_HF_VOLUME_TYPE_MIC +} esp_hf_volume_type_t; + +/// +CIND network service availability status +typedef enum +{ + ESP_HF_NETWORK_STATE_NOT_AVAILABLE = 0, + ESP_HF_NETWORK_STATE_AVAILABLE +} esp_hf_network_state_t; + +/** +CIEV Service type */ +typedef enum +{ + ESP_HF_SERVICE_TYPE_HOME = 0, + ESP_HF_SERVICE_TYPE_ROAMING +} esp_hf_service_type_t; /// +CIND call status indicator values typedef enum { @@ -42,12 +101,18 @@ typedef enum { /// +CIND call setup status indicator values typedef enum { - ESP_HF_CALL_SETUP_STATUS_NONE = 0, /*!< no call setup in progress */ + ESP_HF_CALL_SETUP_STATUS_IDLE = 0, /*!< no call setup in progress */ ESP_HF_CALL_SETUP_STATUS_INCOMING = 1, /*!< incoming call setup in progress */ ESP_HF_CALL_SETUP_STATUS_OUTGOING_DIALING = 2, /*!< outgoing call setup in dialing state */ ESP_HF_CALL_SETUP_STATUS_OUTGOING_ALERTING = 3, /*!< outgoing call setup in alerting state */ } esp_hf_call_setup_status_t; +/// +CIND roaming status indicator values +typedef enum { + ESP_HF_ROAMING_STATUS_INACTIVE = 0, /*!< roaming is not active */ + ESP_HF_ROAMING_STATUS_ACTIVE, /*!< a roaming is active */ +} esp_hf_roaming_status_t; + /// +CIND call held indicator values typedef enum { ESP_HF_CALL_HELD_STATUS_NONE = 0, /*!< no calls held */ @@ -55,12 +120,6 @@ typedef enum { ESP_HF_CALL_HELD_STATUS_HELD = 2, /*!< call on hold, no active call*/ } esp_hf_call_held_status_t; -/// +CIND network service availability status -typedef enum { - ESP_HF_SERVICE_AVAILABILITY_STATUS_UNAVAILABLE = 0, /*!< service not available */ - ESP_HF_SERVICE_AVAILABILITY_STATUS_AVAILABLE, /*!< service available */ -} esp_hf_service_availability_status_t; - /// +CLCC status of the call typedef enum { ESP_HF_CURRENT_CALL_STATUS_ACTIVE = 0, /*!< active */ @@ -118,23 +177,35 @@ typedef enum { ESP_HF_BTRH_CMD_REJECT = 2, /*!< reject a held incoming call */ } esp_hf_btrh_cmd_t; -/// response indication codes for AT commands -typedef enum { - ESP_HF_AT_RESPONSE_CODE_OK = 0, /*!< acknowledges execution of a command line */ - ESP_HF_AT_RESPONSE_CODE_ERR, /*!< command not accepted */ - ESP_HF_AT_RESPONSE_CODE_NO_CARRIER, /*!< connection terminated */ - ESP_HF_AT_RESPONSE_CODE_BUSY, /*!< busy signal detected */ - ESP_HF_AT_RESPONSE_CODE_NO_ANSWER, /*!< connection completion timeout */ - ESP_HF_AT_RESPONSE_CODE_DELAYED, /*!< delayed */ - ESP_HF_AT_RESPONSE_CODE_BLACKLISTED, /*!< blacklisted */ - ESP_HF_AT_RESPONSE_CODE_CME, /*!< CME error */ -} esp_hf_at_response_code_t; +/* +NREC */ +typedef enum +{ + ESP_HF_NREC_STOP = 0, + ESP_HF_NREC_START +} esp_hf_nrec_t; -/// voice recognition state +///+CCWA resposne status typedef enum { - ESP_HF_VR_STATE_DISABLED = 0, /*!< voice recognition disabled */ - ESP_HF_VR_STATE_ENABLED, /*!< voice recognition enabled */ -} esp_hf_vr_state_t; + ESP_HF_CALL_WAITING_INACTIVE, + ESP_HF_CALL_WAITING_ACTIVE, +} esp_hf_call_waiting_status_t; + +/* WBS codec setting */ +typedef enum +{ + ESP_HF_WBS_NONE, + ESP_HF_WBS_NO, + ESP_HF_WBS_YES +}esp_hf_wbs_config_t; + +/// Bluetooth HFP RFCOMM connection and service level connection status +typedef enum { + ESP_HF_CONNECTION_STATE_DISCONNECTED = 0, /*!< RFCOMM data link channel released */ + ESP_HF_CONNECTION_STATE_CONNECTING, /*!< connecting remote device on the RFCOMM data link*/ + ESP_HF_CONNECTION_STATE_CONNECTED, /*!< RFCOMM connection established */ + ESP_HF_CONNECTION_STATE_SLC_CONNECTED, /*!< service level connection established */ + ESP_HF_CONNECTION_STATE_DISCONNECTING, /*!< disconnecting with remote device on the RFCOMM data link*/ +} esp_hf_connection_state_t; /// AT+CHLD command values typedef enum { @@ -147,6 +218,24 @@ typedef enum { ESP_HF_CHLD_TYPE_PRIV_X, /*!< <2x>, request private consultation mode with specified call */ } esp_hf_chld_type_t; +/* AT response code - OK/Error */ +typedef enum { + ESP_HF_AT_RESPONSE_CODE_OK = 0, /*!< acknowledges execution of a command line */ + ESP_HF_AT_RESPONSE_CODE_ERR, /*!< command not accepted */ + ESP_HF_AT_RESPONSE_CODE_NO_CARRIER, /*!< connection terminated */ + ESP_HF_AT_RESPONSE_CODE_BUSY, /*!< busy signal detected */ + ESP_HF_AT_RESPONSE_CODE_NO_ANSWER, /*!< connection completion timeout */ + ESP_HF_AT_RESPONSE_CODE_DELAYED, /*!< delayed */ + ESP_HF_AT_RESPONSE_CODE_BLACKLISTED, /*!< blacklisted */ + ESP_HF_AT_RESPONSE_CODE_CME, /*!< CME error */ +} esp_hf_at_response_code_t; + +/* AT response code - OK/Error */ +typedef enum { + ESP_HF_AT_RESPONSE_ERROR = 0, + ESP_HF_AT_RESPONSE_OK +} esp_hf_at_response_t; + /// Extended Audio Gateway Error Result Code Response typedef enum { ESP_HF_CME_AG_FAILURE = 0, /*!< ag failure */ @@ -174,6 +263,84 @@ typedef enum { ESP_HF_CME_NETWORK_NOT_ALLOWED = 32, /*!< network not allowed --emergency calls only */ } esp_hf_cme_err_t; +/** Callback for connection state change. + * state will have one of the values from BtHfConnectionState + */ +typedef void (* esp_hf_connection_state_callback)(esp_hf_connection_state_t state, esp_bd_addr_t *bd_addr); + +/** Callback for audio connection state change. + * state will have one of the values from BtHfAudioState + */ +typedef void (* esp_hf_audio_state_callback)(esp_hf_audio_state_t state, esp_bd_addr_t *bd_addr); + +/** Callback for VR connection state change. + * state will have one of the values from BtHfVRState + */ +typedef void (* esp_hf_vr_cmd_callback)(esp_hf_vr_state_t state, esp_bd_addr_t *bd_addr); + +/** Callback for answer incoming call (ATA) + */ +typedef void (* esp_hf_answer_call_cmd_callback)(esp_bd_addr_t *bd_addr); + +/** Callback for disconnect call (AT+CHUP) + */ +typedef void (* esp_hf_hangup_call_cmd_callback)(esp_bd_addr_t *bd_addr); + +/** Callback for disconnect call (AT+CHUP) + * type will denote Speaker/Mic gain (BtHfVolumeControl). + */ +typedef void (* esp_hf_volume_cmd_callback)(esp_hf_volume_control_target_t type, int volume, esp_bd_addr_t *bd_addr); + +/** Callback for dialing an outgoing call + * If number is NULL, redial + */ +typedef void (* esp_hf_dial_call_cmd_callback)(char *number, esp_bd_addr_t *bd_addr); + +/** Callback for sending DTMF tones + * tone contains the dtmf character to be sent + */ +typedef void (* esp_hf_dtmf_cmd_callback)(char tone, esp_bd_addr_t *bd_addr); + +/** Callback for enabling/disabling noise reduction/echo cancellation + * value will be 1 to enable, 0 to disable + */ +typedef void (* esp_hf_nrec_cmd_callback)(esp_hf_nrec_t nrec, esp_bd_addr_t *bd_addr); + +/** Callback for AT+BCS and event from BAC + * WBS enable, WBS disable + */ +typedef void (* esp_hf_wbs_callback)(esp_hf_wbs_config_t wbs, esp_bd_addr_t *bd_addr); + +/** Callback for call hold handling (AT+CHLD) + * value will contain the call hold command (0, 1, 2, 3) + */ +typedef void (* esp_hf_chld_cmd_callback)(esp_hf_chld_type_t chld, esp_bd_addr_t *bd_addr); + +/** Callback for CNUM (subscriber number) + */ +typedef void (* esp_hf_cnum_cmd_callback)(esp_bd_addr_t *bd_addr); + +/** Callback for indicators (CIND) + */ +typedef void (* esp_hf_cind_cmd_callback)(esp_bd_addr_t *bd_addr); + +/** Callback for operator selection (COPS) + */ +typedef void (* esp_hf_cops_cmd_callback)(esp_bd_addr_t *bd_addr); + +/** Callback for call list (AT+CLCC) + */ +typedef void (* esp_hf_clcc_cmd_callback) (esp_bd_addr_t *bd_addr); + +/** Callback for unknown AT command recd from AG + * at_string will contain the unparsed AT string + */ +typedef void (* esp_hf_unknown_at_cmd_callback)(char *at_string, esp_bd_addr_t *bd_addr); + +/** Callback for keypressed (HSP) event. + */ +typedef void (* esp_hf_key_pressed_cmd_callback)(esp_bd_addr_t *bd_addr); + #ifdef __cplusplus } #endif diff --git a/components/bt/host/bluedroid/bta/hf_ag/bta_ag_act.c b/components/bt/host/bluedroid/bta/hf_ag/bta_ag_act.c new file mode 100644 index 000000000..39387f594 --- /dev/null +++ b/components/bt/host/bluedroid/bta/hf_ag/bta_ag_act.c @@ -0,0 +1,818 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains action functions for the audio gateway. + * + ******************************************************************************/ +#include +#include "bta_ag_int.h" +#include "bta/bta_sys.h" +#include "bta/bta_ag_api.h" +#include "bta/bta_ag_co.h" +#include "stack/port_api.h" +#include "stack/l2c_api.h" +#include "bta_dm_int.h" +#include "bta/bta_sdp_api.h" +#include "bta/utl.h" +#include "common/bt_trace.h" +#include "osi/allocator.h" + +#if (BTA_AG_INCLUDED == TRUE) +/***************************************************************************** +** Constants +*****************************************************************************/ + +/* maximum length of data to read from RFCOMM */ +#define BTA_AG_RFC_READ_MAX 512 + +/* maximum AT command length */ +#define BTA_AG_CMD_MAX 512 + +const UINT16 bta_ag_uuid[BTA_AG_NUM_IDX] = +{ + UUID_SERVCLASS_HEADSET_AUDIO_GATEWAY, + UUID_SERVCLASS_AG_HANDSFREE +}; + +const UINT8 bta_ag_sec_id[BTA_AG_NUM_IDX] = +{ + BTM_SEC_SERVICE_HEADSET_AG, + BTM_SEC_SERVICE_AG_HANDSFREE +}; + +const tBTA_SERVICE_ID bta_ag_svc_id[BTA_AG_NUM_IDX] = +{ + BTA_HSP_SERVICE_ID, + BTA_HFP_SERVICE_ID +}; + +const tBTA_SERVICE_MASK bta_ag_svc_mask[BTA_AG_NUM_IDX] = +{ + BTA_HSP_SERVICE_MASK, + BTA_HFP_SERVICE_MASK +}; + +typedef void (*tBTA_AG_ATCMD_CBACK)(tBTA_AG_SCB *p_scb, UINT16 cmd, UINT8 arg_type, + char *p_arg, INT16 int_arg); + +const tBTA_AG_ATCMD_CBACK bta_ag_at_cback_tbl[BTA_AG_NUM_IDX] = +{ + bta_ag_at_hsp_cback, + bta_ag_at_hfp_cback +}; + +/******************************************************************************* +** +** Function bta_ag_cback_open +** +** Description Send open callback event to application. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_cback_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data, tBTA_AG_STATUS status) +{ + tBTA_AG_OPEN open; + /* call app callback with open event */ + open.hdr.handle = bta_ag_scb_to_idx(p_scb); + open.hdr.app_id = p_scb->app_id; + open.status = status; + open.service_id = bta_ag_svc_id[p_scb->conn_service]; + if (p_data) { + /* if p_data is provided then we need to pick the bd address from the open api structure */ + bdcpy(open.bd_addr, p_data->api_open.bd_addr); + } else { + bdcpy(open.bd_addr, p_scb->peer_addr); + } + (*bta_ag_cb.p_cback)(BTA_AG_OPEN_EVT, (tBTA_AG *) &open); +} + +/******************************************************************************* +** +** Function bta_ag_register +** +** Description This function initializes values of the AG cb and sets up +** the SDP record for the services. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_register(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + tBTA_AG_REGISTER reg; + /* initialize control block */ + p_scb->reg_services = p_data->api_register.services; + p_scb->serv_sec_mask = p_data->api_register.sec_mask; + p_scb->features = p_data->api_register.features; + p_scb->app_id = p_data->api_register.app_id; + /* create SDP records */ + bta_ag_create_records(p_scb, p_data); + /* start RFCOMM servers */ + bta_ag_start_servers(p_scb, p_scb->reg_services); + /* call app callback with register event */ + reg.hdr.handle = bta_ag_scb_to_idx(p_scb); + reg.hdr.app_id = p_scb->app_id; + reg.status = BTA_AG_SUCCESS; + (*bta_ag_cb.p_cback)(BTA_AG_REGISTER_EVT, (tBTA_AG *) ®); +} + +/******************************************************************************* +** +** Function bta_ag_deregister +** +** Description This function removes the sdp records, closes the RFCOMM +** servers, and deallocates the service control block. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_deregister(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + /* set dealloc */ + p_scb->dealloc = TRUE; + /* remove sdp records */ + bta_ag_del_records(p_scb, p_data); + /* remove rfcomm servers */ + bta_ag_close_servers(p_scb, p_scb->reg_services); + /* dealloc */ + bta_ag_scb_dealloc(p_scb); +} + +/******************************************************************************* +** +** Function bta_ag_start_dereg +** +** Description Start a deregister event. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_start_dereg(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + /* set dealloc */ + p_scb->dealloc = TRUE; + /* remove sdp records */ + bta_ag_del_records(p_scb, p_data); +} + +/******************************************************************************* +** +** Function bta_ag_start_open +** +** Description This starts an AG open. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_start_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + BD_ADDR pending_bd_addr; + /* store parameters */ + if (p_data) { + bdcpy(p_scb->peer_addr, p_data->api_open.bd_addr); + p_scb->open_services = p_data->api_open.services; + p_scb->cli_sec_mask = p_data->api_open.sec_mask; + } + /* Check if RFCOMM has any incoming connection to avoid collision. */ + if (PORT_IsOpening (pending_bd_addr)) { + /* Let the incoming connection goes through. */ + /* Issue collision for this scb for now. */ + /* We will decide what to do when we find incoming connetion later. */ + bta_ag_collision_cback (0, BTA_ID_AG, 0, p_scb->peer_addr); + return; + } + /* close servers */ + bta_ag_close_servers(p_scb, p_scb->reg_services); + /* set role */ + p_scb->role = BTA_AG_INT; + /* do service search */ + bta_ag_do_disc(p_scb, p_scb->open_services); +} + +/******************************************************************************* +** +** Function bta_ag_disc_int_res +** +** Description This function handles a discovery result when initiator. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_disc_int_res(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UINT16 event = BTA_AG_DISC_FAIL_EVT; + APPL_TRACE_DEBUG ("bta_ag_disc_int_res: Status: %d", p_data->disc_result.status); + + /* if found service */ + if (p_data->disc_result.status == SDP_SUCCESS || + p_data->disc_result.status == SDP_DB_FULL) { + /* get attributes */ + if (bta_ag_sdp_find_attr(p_scb, p_scb->open_services)) { + /* set connected service */ + p_scb->conn_service = bta_ag_service_to_idx(p_scb->open_services); + /* send ourselves sdp ok event */ + event = BTA_AG_DISC_OK_EVT; + } + } + /* free discovery db */ + bta_ag_free_db(p_scb, p_data); + /* if service not found check if we should search for other service */ + if ((event == BTA_AG_DISC_FAIL_EVT) && + (p_data->disc_result.status == SDP_SUCCESS || + p_data->disc_result.status == SDP_DB_FULL || + p_data->disc_result.status == SDP_NO_RECS_MATCH)) { + if ((p_scb->open_services & BTA_HFP_SERVICE_MASK) && + (p_scb->open_services & BTA_HSP_SERVICE_MASK)) { + /* search for HSP */ + p_scb->open_services &= ~BTA_HFP_SERVICE_MASK; + bta_ag_do_disc(p_scb, p_scb->open_services); + } else if ((p_scb->open_services & BTA_HSP_SERVICE_MASK) && + (p_scb->hsp_version == HSP_VERSION_1_2)) { + /* search for UUID_SERVCLASS_HEADSET for HSP 1.0 device */ + p_scb->hsp_version = HSP_VERSION_1_0; + bta_ag_do_disc(p_scb, p_scb->open_services); + } else { + /* send ourselves sdp ok/fail event */ + bta_ag_sm_execute(p_scb, event, p_data); + } + } else { + /* send ourselves sdp ok/fail event */ + bta_ag_sm_execute(p_scb, event, p_data); + } +} + +/******************************************************************************* +** +** Function bta_ag_disc_acp_res +** +** Description This function handles a discovery result when acceptor. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_disc_acp_res(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + /* if found service */ + if (p_data->disc_result.status == SDP_SUCCESS || + p_data->disc_result.status == SDP_DB_FULL) { + /* get attributes */ + bta_ag_sdp_find_attr(p_scb, bta_ag_svc_mask[p_scb->conn_service]); + } + /* free discovery db */ + bta_ag_free_db(p_scb, p_data); +} + +/******************************************************************************* +** +** Function bta_ag_disc_fail +** +** Description This function handles a discovery failure. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_disc_fail(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UNUSED(p_data); + /* reopen registered servers */ + bta_ag_start_servers(p_scb, p_scb->reg_services); + /* reinitialize stuff */ + /* call open cback w. failure */ + bta_ag_cback_open(p_scb, NULL, BTA_AG_FAIL_SDP); +} + +/******************************************************************************* +** +** Function bta_ag_open_fail +** +** Description open connection failed. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_open_fail(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + /* call open cback w. failure */ + bta_ag_cback_open(p_scb, p_data, BTA_AG_FAIL_RESOURCES); +} + +/******************************************************************************* +** +** Function bta_ag_rfc_fail +** +** Description RFCOMM connection failed. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_rfc_fail(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UNUSED(p_data); + /* reinitialize stuff */ + p_scb->conn_handle = 0; + p_scb->conn_service = 0; + p_scb->peer_features = 0; +#if (BTM_WBS_INCLUDED == TRUE ) + p_scb->peer_codecs = BTA_AG_CODEC_NONE; + p_scb->sco_codec = BTA_AG_CODEC_NONE; +#endif + p_scb->role = 0; + p_scb->svc_conn = FALSE; + p_scb->hsp_version = HSP_VERSION_1_2; + /*Clear the BD address*/ + bdcpy(p_scb->peer_addr, bd_addr_null); + /* reopen registered servers */ + bta_ag_start_servers(p_scb, p_scb->reg_services); + /* call open cback w. failure */ + bta_ag_cback_open(p_scb, NULL, BTA_AG_FAIL_RFCOMM); +} + +/******************************************************************************* +** +** Function bta_ag_rfc_close +** +** Description RFCOMM connection closed. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_rfc_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + tBTA_AG_CLOSE close; + tBTA_SERVICE_MASK services; + int i, num_active_conn = 0; + UNUSED(p_data); + /* reinitialize stuff */ + p_scb->conn_service = 0; + p_scb->peer_features = 0; +#if (BTM_WBS_INCLUDED == TRUE ) + p_scb->peer_codecs = BTA_AG_CODEC_NONE; + p_scb->sco_codec = BTA_AG_CODEC_NONE; + /* Clear these flags upon SLC teardown */ + p_scb->codec_updated = FALSE; + p_scb->codec_fallback = FALSE; + p_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T2; +#endif + p_scb->role = 0; + p_scb->post_sco = BTA_AG_POST_SCO_NONE; + p_scb->svc_conn = FALSE; + p_scb->hsp_version = HSP_VERSION_1_2; + bta_ag_at_reinit(&p_scb->at_cb); + /* stop timers */ + bta_sys_stop_timer(&p_scb->act_timer); +#if (BTM_WBS_INCLUDED == TRUE) + bta_sys_stop_timer(&p_scb->cn_timer); +#endif + close.hdr.handle = bta_ag_scb_to_idx(p_scb); + close.hdr.app_id = p_scb->app_id; + bdcpy(close.bd_addr, p_scb->peer_addr); + + bta_sys_conn_close(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + + /* call close call-out */ + // bta_ag_sco_co_close(close.hdr.handle); + bta_ag_sco_co_close(); + /* call close cback */ + (*bta_ag_cb.p_cback)(BTA_AG_CLOSE_EVT, (tBTA_AG *) &close); + + /* if not deregistering (deallocating) reopen registered servers */ + if (p_scb->dealloc == FALSE) { + /* Clear peer bd_addr so instance can be reused */ + bdcpy(p_scb->peer_addr, bd_addr_null); + + /* start only unopened server */ + services = p_scb->reg_services; + for (i = 0; i < BTA_AG_NUM_IDX && services != 0; i++) { + if (p_scb->serv_handle[i]) { + services &= ~((tBTA_SERVICE_MASK)1 << (BTA_HSP_SERVICE_ID + i)); + } + } + bta_ag_start_servers(p_scb, services); + p_scb->conn_handle = 0; + /* Make sure SCO state is BTA_AG_SCO_SHUTDOWN_ST */ + bta_ag_sco_shutdown(p_scb, NULL); + /* Check if all the SLCs are down */ + for (i = 0; i < BTA_AG_NUM_SCB; i++) { + if (bta_ag_cb.scb[i].in_use && bta_ag_cb.scb[i].svc_conn) { + num_active_conn++; + } + } + if (!num_active_conn) { + bta_sys_sco_unuse(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + } + } else { + /* else close port and deallocate scb */ + RFCOMM_RemoveServer(p_scb->conn_handle); + bta_ag_scb_dealloc(p_scb); + } +} + +/******************************************************************************* +** +** Function bta_ag_rfc_open +** +** Description Handle RFCOMM channel open. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_rfc_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + /* initialize AT feature variables */ + p_scb->clip_enabled = FALSE; + p_scb->ccwa_enabled = FALSE; + p_scb->cmer_enabled = FALSE; + p_scb->cmee_enabled = FALSE; + p_scb->inband_enabled = ((p_scb->features & BTA_AG_FEAT_INBAND) == BTA_AG_FEAT_INBAND); + + /* set up AT command interpreter */ + p_scb->at_cb.p_at_tbl = (tBTA_AG_AT_CMD *) bta_ag_at_tbl[p_scb->conn_service]; + p_scb->at_cb.p_cmd_cback = (tBTA_AG_AT_CMD_CBACK *) bta_ag_at_cback_tbl[p_scb->conn_service]; + p_scb->at_cb.p_err_cback = (tBTA_AG_AT_ERR_CBACK *) bta_ag_at_err_cback; + p_scb->at_cb.p_user = p_scb; + p_scb->at_cb.cmd_max_len = BTA_AG_CMD_MAX; + bta_ag_at_init(&p_scb->at_cb); + + /* call app open call-out */ + bta_ag_sco_co_open(bta_ag_scb_to_idx(p_scb), p_scb->air_mode, BTA_HFP_SCO_OUT_PKT_SIZE, bta_ag_svc_id[p_scb->conn_service]); + bta_sys_conn_open(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + bta_ag_cback_open(p_scb, NULL, BTA_AG_SUCCESS); + + if (p_scb->conn_service == BTA_AG_HFP) { + /* if hfp start timer for service level conn */ + bta_sys_start_timer(&p_scb->act_timer, BTA_AG_SVC_TOUT_EVT, p_bta_ag_cfg->conn_tout); + } else { + /* else service level conn is open */ + bta_ag_svc_conn_open(p_scb, p_data); + } +} + +/******************************************************************************* +** +** Function bta_ag_rfc_acp_open +** +** Description Handle RFCOMM channel open when accepting connection. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_rfc_acp_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UINT16 lcid; + int i; + tBTA_AG_SCB *ag_scb, *other_scb; + BD_ADDR dev_addr; + int status; + /* set role */ + p_scb->role = BTA_AG_ACP; + APPL_TRACE_DEBUG ("bta_ag_rfc_acp_open: serv_handle0 = %d serv_handle1 = %d", + p_scb->serv_handle[0], p_scb->serv_handle[1]); + /* get bd addr of peer */ + if (PORT_SUCCESS != (status=PORT_CheckConnection(p_data->rfc.port_handle, dev_addr, &lcid))) { + APPL_TRACE_DEBUG ("bta_ag_rfc_acp_open error PORT_CheckConnection returned status %d", status); + } + /* Collision Handling */ + for (i = 0, ag_scb = &bta_ag_cb.scb[0]; i < BTA_AG_NUM_SCB; i++, ag_scb++) { + if ((ag_scb->in_use) && (ag_scb->colli_tmr_on)) { + /* stop collision timer */ + ag_scb->colli_tmr_on = FALSE; + bta_sys_stop_timer (&ag_scb->colli_timer); + + if (bdcmp (dev_addr, ag_scb->peer_addr) == 0) { + /* If incoming and outgoing device are same, nothing more to do. */ + /* Outgoing conn will be aborted because we have successful incoming conn. */ + } else { + /* Resume outgoing connection. */ + other_scb = bta_ag_get_other_idle_scb (p_scb); + if (other_scb) { + bdcpy(other_scb->peer_addr, ag_scb->peer_addr); + other_scb->open_services = ag_scb->open_services; + other_scb->cli_sec_mask = ag_scb->cli_sec_mask; + bta_ag_resume_open (other_scb); + } + } + break; + } + } + bdcpy (p_scb->peer_addr, dev_addr); + /* determine connected service from port handle */ + for (i = 0; i < BTA_AG_NUM_IDX; i++) { + APPL_TRACE_DEBUG ("bta_ag_rfc_acp_open: i = %d serv_handle = %d port_handle = %d", + i, p_scb->serv_handle[i], p_data->rfc.port_handle); + if (p_scb->serv_handle[i] == p_data->rfc.port_handle) { + p_scb->conn_service = i; + p_scb->conn_handle = p_data->rfc.port_handle; + break; + } + } + APPL_TRACE_DEBUG ("bta_ag_rfc_acp_open: conn_service = %d conn_handle = %d", + p_scb->conn_service, p_scb->conn_handle); + /* close any unopened server */ + bta_ag_close_servers(p_scb, (p_scb->reg_services & ~bta_ag_svc_mask[p_scb->conn_service])); + /* do service discovery to get features */ + bta_ag_do_disc(p_scb, bta_ag_svc_mask[p_scb->conn_service]); + /* continue with common open processing */ + bta_ag_rfc_open(p_scb, p_data); +} + +/******************************************************************************* +** +** Function bta_ag_rfc_data +** +** Description Read and process data from RFCOMM. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_rfc_data(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UINT16 len; + char buf[BTA_AG_RFC_READ_MAX]; + UNUSED(p_data); + memset(buf, 0, BTA_AG_RFC_READ_MAX); + + APPL_TRACE_DEBUG("bta_ag_rfc_data"); + /* do the following */ + for (;;) { + /* read data from rfcomm; if bad status, we're done */ + if (PORT_ReadData(p_scb->conn_handle, buf, BTA_AG_RFC_READ_MAX, &len) != PORT_SUCCESS) { + break; + } + /* if no data, we're done */ + if (len == 0) { + break; + } + /* run AT command interpreter on data */ + bta_sys_busy(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + bta_ag_at_parse(&p_scb->at_cb, buf, len); + bta_sys_idle(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + /* no more data to read, we're done */ + if (len < BTA_AG_RFC_READ_MAX) { + break; + } + } +} + +/******************************************************************************* +** +** Function bta_ag_start_close +** +** Description Start the process of closing SCO and RFCOMM connection. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_start_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + /* Take the link out of sniff and set L2C idle time to 0 */ + bta_dm_pm_active(p_scb->peer_addr); + L2CA_SetIdleTimeoutByBdAddr(p_scb->peer_addr, 0, BT_TRANSPORT_BR_EDR); + /* if SCO is open close SCO and wait on RFCOMM close */ + if (bta_ag_sco_is_open(p_scb)) { + p_scb->post_sco = BTA_AG_POST_SCO_CLOSE_RFC; + } else { + p_scb->post_sco = BTA_AG_POST_SCO_NONE; + bta_ag_rfc_do_close(p_scb, p_data); + } + /* Always do SCO shutdown to handle all SCO corner cases */ + bta_ag_sco_shutdown(p_scb, p_data); +} + +/******************************************************************************* +** +** Function bta_ag_post_sco_open +** +** Description Perform post-SCO open action, if any +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_post_sco_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + switch (p_scb->post_sco) { + case BTA_AG_POST_SCO_RING: + bta_ag_send_ring(p_scb, p_data); + p_scb->post_sco = BTA_AG_POST_SCO_NONE; + break; + + case BTA_AG_POST_SCO_CALL_CONN: + bta_ag_send_call_inds(p_scb, BTA_AG_IN_CALL_CONN_RES); + p_scb->post_sco = BTA_AG_POST_SCO_NONE; + break; + + default: + break; + } +} + +/******************************************************************************* +** +** Function bta_ag_post_sco_close +** +** Description Perform post-SCO close action, if any +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_post_sco_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + switch (p_scb->post_sco) { + case BTA_AG_POST_SCO_CLOSE_RFC: + bta_ag_rfc_do_close(p_scb, p_data); + p_scb->post_sco = BTA_AG_POST_SCO_NONE; + break; + + case BTA_AG_POST_SCO_CALL_CONN: + bta_ag_send_call_inds(p_scb, BTA_AG_IN_CALL_CONN_RES); + p_scb->post_sco = BTA_AG_POST_SCO_NONE; + break; + + case BTA_AG_POST_SCO_CALL_ORIG: + bta_ag_send_call_inds(p_scb, BTA_AG_OUT_CALL_ORIG_RES); + p_scb->post_sco = BTA_AG_POST_SCO_NONE; + break; + + case BTA_AG_POST_SCO_CALL_END: + bta_ag_send_call_inds(p_scb, BTA_AG_END_CALL_RES); + p_scb->post_sco = BTA_AG_POST_SCO_NONE; + break; + + case BTA_AG_POST_SCO_CALL_END_INCALL: + { + bta_ag_send_call_inds(p_scb, BTA_AG_END_CALL_RES); + /* Sending callsetup IND and Ring were defered to after SCO close. */ + bta_ag_send_call_inds(p_scb, BTA_AG_IN_CALL_RES); + + if (bta_ag_inband_enabled(p_scb) && !(p_scb->features & BTA_AG_FEAT_NOSCO)) { + p_scb->post_sco = BTA_AG_POST_SCO_RING; + bta_ag_sco_open(p_scb, p_data); + } else { + p_scb->post_sco = BTA_AG_POST_SCO_NONE; + bta_ag_send_ring(p_scb, p_data); + } + break; + } + + default: + break; + } +} + +/******************************************************************************* +** +** Function bta_ag_svc_conn_open +** +** Description Service level connection opened +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_svc_conn_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + tBTA_AG_CONN evt; + UNUSED(p_data); + + if (!p_scb->svc_conn) { + /* set state variable */ + p_scb->svc_conn = TRUE; + /* Clear AT+BIA mask from previous SLC if any. */ + p_scb->bia_masked_out = 0; + /* stop timer */ + bta_sys_stop_timer(&p_scb->act_timer); + /* call callback */ + evt.hdr.handle = bta_ag_scb_to_idx(p_scb); + evt.hdr.app_id = p_scb->app_id; + evt.peer_feat = p_scb->peer_features; + bdcpy(evt.bd_addr, p_scb->peer_addr); +#if (BTM_WBS_INCLUDED == TRUE ) + evt.peer_codec = p_scb->peer_codecs; +#endif + if ((p_scb->call_ind != BTA_AG_CALL_INACTIVE) || + (p_scb->callsetup_ind != BTA_AG_CALLSETUP_NONE)) { + bta_sys_sco_use(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + } + (*bta_ag_cb.p_cback)(BTA_AG_CONN_EVT, (tBTA_AG *) &evt); + } +} + +/******************************************************************************* +** +** Function bta_ag_ci_rx_data +** +** Description Send result code to RFCOMM +** +** Returns void +** +*******************************************************************************/ +void bta_ag_ci_rx_data(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UINT16 len; + tBTA_AG_CI_RX_WRITE *p_rx_write_msg = (tBTA_AG_CI_RX_WRITE *)p_data; + char *p_data_area = (char *)(p_rx_write_msg+1); /* Point to data area after header */ + + APPL_TRACE_DEBUG("bta_ag_ci_rx_data:"); + /* send to RFCOMM */ + bta_sys_busy(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + PORT_WriteData(p_scb->conn_handle, p_data_area, strlen(p_data_area), &len); + bta_sys_idle(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); +} + +/******************************************************************************* +** +** Function bta_ag_rcvd_slc_ready +** +** Description Handles SLC ready call-in in case of pass-through mode. +** +** Returns void +** +*******************************************************************************/ +void bta_ag_rcvd_slc_ready(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UNUSED(p_data); + APPL_TRACE_DEBUG("bta_ag_rcvd_slc_ready: handle = %d", bta_ag_scb_to_idx(p_scb)); + + if (bta_ag_cb.parse_mode == BTA_AG_PASS_THROUGH) { + /* In pass-through mode, BTA knows that SLC is ready only through call-in. */ + bta_ag_svc_conn_open(p_scb, NULL); + } +} + +/******************************************************************************* +** +** Function bta_ag_setcodec +** +** Description Handle API SetCodec +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_setcodec(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ +#if (BTM_WBS_INCLUDED == TRUE ) + tBTA_AG_PEER_CODEC codec_type = p_data->api_setcodec.codec; + tBTA_AG_VAL val; + + /* Check if the requested codec type is valid */ + if((codec_type != BTA_AG_CODEC_NONE) && + (codec_type != BTA_AG_CODEC_CVSD) && + (codec_type != BTA_AG_CODEC_MSBC)) { + val.num = codec_type; + val.hdr.status = BTA_AG_FAIL_RESOURCES; + APPL_TRACE_ERROR("bta_ag_setcodec error: unsupported codec type %d", codec_type); + (*bta_ag_cb.p_cback)(BTA_AG_WBS_EVT, (tBTA_AG *) &val); + return; + } + + if((p_scb->peer_codecs & codec_type) || (codec_type == BTA_AG_CODEC_NONE) || + (codec_type == BTA_AG_CODEC_CVSD)) { + p_scb->sco_codec = codec_type; + p_scb->codec_updated = TRUE; + val.num = codec_type; + val.hdr.status = BTA_AG_SUCCESS; + APPL_TRACE_DEBUG("bta_ag_setcodec: Updated codec type %d", codec_type); + } else { + val.num = codec_type; + val.hdr.status = BTA_AG_FAIL_RESOURCES; + APPL_TRACE_ERROR("bta_ag_setcodec error: unsupported codec type %d", codec_type); + } + (*bta_ag_cb.p_cback)(BTA_AG_WBS_EVT, (tBTA_AG *) &val); +#endif +} + +#endif /* #if (BTA_AG_INCLUDED == TRUE) */ diff --git a/components/bt/host/bluedroid/bta/hf_ag/bta_ag_api.c b/components/bt/host/bluedroid/bta/hf_ag/bta_ag_api.c new file mode 100644 index 000000000..d3fdf8ce4 --- /dev/null +++ b/components/bt/host/bluedroid/bta/hf_ag/bta_ag_api.c @@ -0,0 +1,322 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the implementation of the API for the audio gateway (AG) + * subsystem of BTA, Broadcom's Bluetooth application layer for mobile + * phones. + * + ******************************************************************************/ + +#include +#include "bta/bta_api.h" +#include "bta/bta_sys.h" +#include "bta/bta_ag_api.h" +#include "bta_ag_int.h" +#include "osi/allocator.h" + +#if (BTA_AG_INCLUDED == TRUE) +/***************************************************************************** +** Constants +*****************************************************************************/ + +static const tBTA_SYS_REG bta_ag_reg = +{ + bta_ag_hdl_event, + BTA_AgDisable +}; + +/******************************************************************************* +** +** Function BTA_AgEnable +** +** Description Enable the audio gateway service. When the enable +** operation is complete the callback function will be +** called with a BTA_AG_ENABLE_EVT. This function must +** be called before other function in the AG API are +** called. +** +** Returns BTA_SUCCESS if OK, BTA_FAILURE otherwise. +** +*******************************************************************************/ +tBTA_STATUS BTA_AgEnable(tBTA_AG_PARSE_MODE parse_mode, tBTA_AG_CBACK *p_cback) +{ + tBTA_AG_API_ENABLE *p_buf; + UINT8 idx; + + /* Error if AG is already enabled, or AG is in the middle of disabling. */ + for (idx = 0; idx < BTA_AG_NUM_SCB; idx++) { + if (bta_ag_cb.scb[idx].in_use) { + APPL_TRACE_ERROR ("BTA_AgEnable: FAILED, AG already enabled."); + return BTA_FAILURE; + } + } + /* register with BTA system manager */ + bta_sys_register(BTA_ID_AG, &bta_ag_reg); + + if ((p_buf = (tBTA_AG_API_ENABLE *) osi_malloc(sizeof(tBTA_AG_API_ENABLE))) != NULL) { + p_buf->hdr.event = BTA_AG_API_ENABLE_EVT; + p_buf->parse_mode = parse_mode; + p_buf->p_cback = p_cback; + bta_sys_sendmsg(p_buf); + } + return BTA_SUCCESS; +} + +/******************************************************************************* +** +** Function BTA_AgDisable +** +** Description Disable the audio gateway service +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgDisable(void) +{ + BT_HDR *p_buf; + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_AG_API_DISABLE_EVT; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AgRegister +** +** Description Register an Audio Gateway service. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgRegister(tBTA_SERVICE_MASK services, tBTA_SEC sec_mask,tBTA_AG_FEAT features, + char * p_service_names[], UINT8 app_id) +{ + tBTA_AG_API_REGISTER *p_buf; + int i; + + if ((p_buf = (tBTA_AG_API_REGISTER *) osi_malloc(sizeof(tBTA_AG_API_REGISTER))) != NULL) { + p_buf->hdr.event = BTA_AG_API_REGISTER_EVT; + p_buf->features = features; + p_buf->sec_mask = sec_mask; + p_buf->services = services; + p_buf->app_id = app_id; + for (i = 0; i < BTA_AG_NUM_IDX; i++) { + if(p_service_names[i]) { + BCM_STRNCPY_S(p_buf->p_name[i], BTA_SERVICE_NAME_LEN+1, p_service_names[i], BTA_SERVICE_NAME_LEN); + p_buf->p_name[i][BTA_SERVICE_NAME_LEN] = 0; + } else { + p_buf->p_name[i][0] = 0; + } + } + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AgDeregister +** +** Description Deregister an audio gateway service. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgDeregister(UINT16 handle) +{ + BT_HDR *p_buf; + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_AG_API_DEREGISTER_EVT; + p_buf->layer_specific = handle; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AgOpen +** +** Description Opens a connection to a headset or hands-free device. +** When connection is open callback function is called +** with a BTA_AG_OPEN_EVT. Only the data connection is +** opened. The audio connection is not opened. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgOpen(UINT16 handle, BD_ADDR bd_addr, tBTA_SEC sec_mask, tBTA_SERVICE_MASK services) +{ + tBTA_AG_API_OPEN *p_buf; + + if ((p_buf = (tBTA_AG_API_OPEN *) osi_malloc(sizeof(tBTA_AG_API_OPEN))) != NULL) { + p_buf->hdr.event = BTA_AG_API_OPEN_EVT; + p_buf->hdr.layer_specific = handle; + bdcpy(p_buf->bd_addr, bd_addr); + p_buf->services = services; + p_buf->sec_mask = sec_mask; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AgClose +** +** Description Close the current connection to a headset or a handsfree +** Any current audio connection will also be closed. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgClose(UINT16 handle) +{ + BT_HDR *p_buf; + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_AG_API_CLOSE_EVT; + p_buf->layer_specific = handle; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AgAudioOpen +** +** Description Opens an audio connection to the currently connected +** headset or handsfree. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgAudioOpen(UINT16 handle) +{ + BT_HDR *p_buf; + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_AG_API_AUDIO_OPEN_EVT; + p_buf->layer_specific = handle; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AgAudioClose +** +** Description Close the currently active audio connection to a headset +** or handsfree. The data connection remains open +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgAudioClose(UINT16 handle) +{ + BT_HDR *p_buf; + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_AG_API_AUDIO_CLOSE_EVT; + p_buf->layer_specific = handle; + bta_sys_sendmsg(p_buf); + } +} + + +/******************************************************************************* +** +** Function BTA_AgResult +** +** Description Send an AT result code to a headset or hands-free device. +** This function is only used when the AG parse mode is set +** to BTA_AG_PARSE. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgResult(UINT16 handle, tBTA_AG_RES result, tBTA_AG_RES_DATA *p_data) +{ + tBTA_AG_API_RESULT *p_buf; + + // printf("BTA_AgReslut: %d\n",result); + + if ((p_buf = (tBTA_AG_API_RESULT *) osi_malloc(sizeof(tBTA_AG_API_RESULT))) != NULL) { + p_buf->hdr.event = BTA_AG_API_RESULT_EVT; + p_buf->hdr.layer_specific = handle; + p_buf->result = result; + if(p_data) { + memcpy(&p_buf->data, p_data, sizeof(p_buf->data)); + } + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function BTA_AgSetCodec +** +** Description Specify the codec type to be used for the subsequent +** audio connection. +** +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgSetCodec(UINT16 handle, tBTA_AG_PEER_CODEC codec) +{ + tBTA_AG_API_SETCODEC *p_buf; + + if ((p_buf = (tBTA_AG_API_SETCODEC *) osi_malloc(sizeof(tBTA_AG_API_SETCODEC))) != NULL) { + p_buf->hdr.event = BTA_AG_API_SETCODEC_EVT; + p_buf->hdr.layer_specific = handle; + p_buf->codec = codec; + bta_sys_sendmsg(p_buf); + } +} + +#if (BTM_SCO_HCI_INCLUDED == TRUE ) +/************************************************************************************************ + * Function BTA_AgCiData + * + * Description Trigger the lower-layer to fetch and send audio data. This function is only + * only used in the case that Voice Over HCI is enabled. Precondition is that + * the HFP audio connection is connected. After this function is called, lower + * layer will invoke esp_hf_client_outgoing_data_cb_t to fetch data + * + ***********************************************************************************************/ +void BTA_AgCiData(void) +{ + BT_HDR *p_buf; + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_AG_CI_SCO_DATA_EVT; + bta_sys_sendmsg(p_buf); + } +} +#endif /* #if (BTM_SCO_HCI_INCLUDED == TRUE ) */ + +#endif /* #if (BTA_AG_INCLUDED == TRUE)*/ \ No newline at end of file diff --git a/components/bt/host/bluedroid/bta/hf_ag/bta_ag_at.c b/components/bt/host/bluedroid/bta/hf_ag/bta_ag_at.c new file mode 100644 index 000000000..01a22dfa2 --- /dev/null +++ b/components/bt/host/bluedroid/bta/hf_ag/bta_ag_at.c @@ -0,0 +1,201 @@ +/****************************************************************************** + * + * Copyright (C) 2004-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * BTA AG AT command interpreter. + * + ******************************************************************************/ + +#include +#include "osi/allocator.h" +#include "bta_ag_at.h" +#include "bta/utl.h" + +#if (BTA_AG_INCLUDED == TRUE) +/****************************************************************************** +** +** Function bta_ag_at_init +** +** Description Initialize the AT command parser control block. +** +** +** Returns void +** +******************************************************************************/ +void bta_ag_at_init(tBTA_AG_AT_CB *p_cb) +{ + p_cb->p_cmd_buf = NULL; + p_cb->cmd_pos = 0; +} + +/****************************************************************************** +** +** Function bta_ag_at_reinit +** +** Description Re-initialize the AT command parser control block. This +** function resets the AT command parser state and frees +** any GKI buffer. +** +** +** Returns void +** +******************************************************************************/ +void bta_ag_at_reinit(tBTA_AG_AT_CB *p_cb) +{ + if (p_cb->p_cmd_buf != NULL) { + osi_free(p_cb->p_cmd_buf); + p_cb->p_cmd_buf = NULL; + } + p_cb->cmd_pos = 0; +} +/****************************************************************************** +** +** Function bta_ag_process_at +** +** Description Parse AT commands. This function will take the input +** character string and parse it for AT commands according to +** the AT command table passed in the control block. +** +** +** Returns void +** +******************************************************************************/ +void bta_ag_process_at(tBTA_AG_AT_CB *p_cb) +{ + UINT16 idx; + UINT8 arg_type; + char *p_arg; + INT16 int_arg = 0; + /* loop through at command table looking for match */ + for (idx = 0; p_cb->p_at_tbl[idx].p_cmd[0] != 0; idx++) { + if (!utl_strucmp(p_cb->p_at_tbl[idx].p_cmd, p_cb->p_cmd_buf)) { + break; + } + } + + /* if there is a match; verify argument type */ + if (p_cb->p_at_tbl[idx].p_cmd[0] != 0) { + /* start of argument is p + strlen matching command */ + p_arg = p_cb->p_cmd_buf + strlen(p_cb->p_at_tbl[idx].p_cmd); + /* if no argument */ + if (p_arg[0] == 0) { + arg_type = BTA_AG_AT_NONE; + } + /* else if arg is '?' and it is last character */ + else if (p_arg[0] == '?' && p_arg[1] == 0) { + arg_type = BTA_AG_AT_READ; /* we have a read */ + } + /* else if arg is '=' */ + else if (p_arg[0] == '=' && p_arg[1] != 0) { + if (p_arg[1] == '?' && p_arg[2] == 0) { + arg_type = BTA_AG_AT_TEST; /* we have a test */ + } else { + arg_type = BTA_AG_AT_SET; /* we have a set */ + p_arg++; /* skip past '=' */ + } + } + /* else it is freeform argument */ + else { + arg_type = BTA_AG_AT_FREE; + } + /* if arguments match command capabilities */ + if ((arg_type & p_cb->p_at_tbl[idx].arg_type) != 0) { + /* if it's a set integer check max, min range */ + if (arg_type == BTA_AG_AT_SET && p_cb->p_at_tbl[idx].fmt == BTA_AG_AT_INT) { + int_arg = utl_str2int(p_arg); + if (int_arg < (INT16) p_cb->p_at_tbl[idx].min || + int_arg > (INT16) p_cb->p_at_tbl[idx].max) { + /* arg out of range; error */ + (*p_cb->p_err_cback)(p_cb->p_user, FALSE, NULL); + } else { + (*p_cb->p_cmd_cback)(p_cb->p_user, idx, arg_type, p_arg, int_arg); + } + } else { + (*p_cb->p_cmd_cback)(p_cb->p_user, idx, arg_type, p_arg, int_arg); + } + } else { + /* else error */ + (*p_cb->p_err_cback)(p_cb->p_user, FALSE, NULL); + } + } else { + /* else no match call error callback */ + (*p_cb->p_err_cback)(p_cb->p_user, TRUE, p_cb->p_cmd_buf); + } +} + +/****************************************************************************** +** +** Function bta_ag_at_parse +** +** Description Parse AT commands. This function will take the input +** character string and parse it for AT commands according to +** the AT command table passed in the control block. +** +** +** Returns void +** +******************************************************************************/ +void bta_ag_at_parse(tBTA_AG_AT_CB *p_cb, char *p_buf, UINT16 len) +{ + int i = 0; + char* p_save; + + if (p_cb->p_cmd_buf == NULL) { + if ((p_cb->p_cmd_buf = (char *) osi_malloc(p_cb->cmd_max_len)) == NULL) { + APPL_TRACE_ERROR("%s: osi_malloc() failed allocation", __func__); + return; + } + p_cb->cmd_pos = 0; + } + + for (i = 0; i < len;) { + while (p_cb->cmd_pos < p_cb->cmd_max_len-1 && i < len) { + /* Skip null characters between AT commands. */ + if ((p_cb->cmd_pos == 0) && (p_buf[i] == 0)) { + i++; + continue; + } + p_cb->p_cmd_buf[p_cb->cmd_pos] = p_buf[i++]; + if ( p_cb->p_cmd_buf[p_cb->cmd_pos] == '\r' || p_cb->p_cmd_buf[p_cb->cmd_pos] == '\n') { + p_cb->p_cmd_buf[p_cb->cmd_pos] = 0; + if ((p_cb->cmd_pos > 2) && + (p_cb->p_cmd_buf[0] == 'A' || p_cb->p_cmd_buf[0] == 'a') && + (p_cb->p_cmd_buf[1] == 'T' || p_cb->p_cmd_buf[1] == 't')) { + p_save = p_cb->p_cmd_buf; + p_cb->p_cmd_buf += 2; + bta_ag_process_at(p_cb); + p_cb->p_cmd_buf = p_save; + } + p_cb->cmd_pos = 0; + } else if( p_cb->p_cmd_buf[p_cb->cmd_pos] == 0x1A || p_cb->p_cmd_buf[p_cb->cmd_pos] == 0x1B) { + p_cb->p_cmd_buf[++p_cb->cmd_pos] = 0; + (*p_cb->p_err_cback)(p_cb->p_user, TRUE, p_cb->p_cmd_buf); + p_cb->cmd_pos = 0; + } else { + ++p_cb->cmd_pos; + } + } + + if (i < len) { + p_cb->cmd_pos = 0; + } + } +} + +#endif /* #if (BTA_AG_INCLUDED == TRUE) */ \ No newline at end of file diff --git a/components/bt/host/bluedroid/bta/hf_ag/bta_ag_cfg.c b/components/bt/host/bluedroid/bta/hf_ag/bta_ag_cfg.c new file mode 100644 index 000000000..2ede36411 --- /dev/null +++ b/components/bt/host/bluedroid/bta/hf_ag/bta_ag_cfg.c @@ -0,0 +1,60 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains compile-time configurable constants for the audio + * gateway. + * + ******************************************************************************/ + +#include "osi/allocator.h" +#include "bta/bta_api.h" +#include "bta/bta_ag_api.h" +#include "common/bt_target.h" + +#if (BTA_AG_INCLUDED == TRUE) + +#ifndef BTA_AG_CIND_INFO +#define BTA_AG_CIND_INFO "(\"call\",(0,1)),(\"callsetup\",(0-3)),(\"service\",(0-3)),(\"signal\",(0-6)),(\"roam\",(0,1)),(\"battchg\",(0-5)),(\"callheld\",(0-2))" +#endif + +#ifndef BTA_AG_CONN_TIMEOUT +#define BTA_AG_CONN_TIMEOUT 5000 +#endif + +#ifndef BTA_AG_SCO_PKT_TYPES +/* S1 packet type setting from HFP 1.5 spec */ +#define BTA_AG_SCO_PKT_TYPES /* BTM_SCO_LINK_ALL_PKT_MASK */ (BTM_SCO_LINK_ONLY_MASK | \ + BTM_SCO_PKT_TYPES_MASK_EV3 | \ + BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 | \ + BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 | \ + BTM_SCO_PKT_TYPES_MASK_NO_3_EV5) +#endif /* BTA_AG_SCO_PKT_TYPES */ + +const tBTA_AG_CFG bta_ag_cfg = +{ + BTA_AG_CIND_INFO, + BTA_AG_CONN_TIMEOUT, + BTA_AG_SCO_PKT_TYPES, + BTA_AG_CHLD_VAL_ECC, + BTA_AG_CHLD_VAL +}; +tBTA_AG_CFG *p_bta_ag_cfg = (tBTA_AG_CFG *) &bta_ag_cfg; + +#endif /* #if (BTA_AG_INCLUDED == TRUE) */ \ No newline at end of file diff --git a/components/bt/host/bluedroid/bta/hf_ag/bta_ag_cmd.c b/components/bt/host/bluedroid/bta/hf_ag/bta_ag_cmd.c new file mode 100644 index 000000000..52bbd3ee6 --- /dev/null +++ b/components/bt/host/bluedroid/bta/hf_ag/bta_ag_cmd.c @@ -0,0 +1,1661 @@ +/****************************************************************************** + * + * Copyright (C) 2004-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions for processing AT commands and results. + * + ******************************************************************************/ +#include +#include +#include +#include "common/bt_target.h" +#include "common/bt_trace.h" +#include "stack/bt_types.h" +#include "bta/bta_ag_api.h" +#include "bta_ag_at.h" +#include "bta_ag_int.h" +#include "bta/bta_api.h" +#include "bta/bta_sys.h" +#include "osi/allocator.h" +#include "stack/port_api.h" +#include "bta/utl.h" + + +#if (BTA_AG_INCLUDED == TRUE) +/***************************************************************************** +** Constants +*****************************************************************************/ + +/* ring timeout */ +#define BTA_AG_RING_TOUT 5000 +#define BTA_AG_CMD_MAX_VAL 32767 /* Maximum value is signed 16-bit value */ + +/* Invalid Chld command */ +#define BTA_AG_INVALID_CHLD 255 + +/* clip type constants */ +#define BTA_AG_CLIP_TYPE_MIN 128 +#define BTA_AG_CLIP_TYPE_MAX 175 +#define BTA_AG_CLIP_TYPE_DEFAULT 129 +#define BTA_AG_CLIP_TYPE_VOIP 255 + +/******************************************* +* HSP callback +********************************************/ +/* callback event lookup table for HSP */ +const tBTA_AG_EVT bta_ag_hsp_cb_evt[] = +{ + BTA_AG_AT_CKPD_EVT, /* BTA_AG_HS_CMD_CKPD */ + BTA_AG_SPK_EVT, /* BTA_AG_HS_CMD_VGS */ + BTA_AG_MIC_EVT /* BTA_AG_HS_CMD_VGM */ +}; +/* HSP AT commands matches bta_ag_hsp_cmd[] */ +enum +{ + BTA_AG_HS_CMD_CKPD, + BTA_AG_HS_CMD_VGS, + BTA_AG_HS_CMD_VGM +}; +/* HSP AT command interpreter table */ +const tBTA_AG_AT_CMD bta_ag_hsp_cmd[] = +{ + {"+CKPD", BTA_AG_AT_SET, BTA_AG_AT_INT, 200, 200}, + {"+VGS", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 15}, + {"+VGM", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 15}, + {"", BTA_AG_AT_NONE, BTA_AG_AT_STR, 0, 0} +}; + +/******************************************* +* HFP callback +********************************************/ +/* callback event lookup table for HFP (Indexed by command) */ +const tBTA_AG_EVT bta_ag_hfp_cb_evt[] = +{ + BTA_AG_AT_A_EVT, /* BTA_AG_HF_CMD_A */ + BTA_AG_AT_D_EVT, /* BTA_AG_HF_CMD_D */ + BTA_AG_SPK_EVT, /* BTA_AG_HF_CMD_VGS */ + BTA_AG_MIC_EVT, /* BTA_AG_HF_CMD_VGM */ + 0, /* BTA_AG_HF_CMD_CCWA */ + BTA_AG_AT_CHLD_EVT, /* BTA_AG_HF_CMD_CHLD */ + BTA_AG_AT_CHUP_EVT, /* BTA_AG_HF_CMD_CHUP */ + BTA_AG_AT_CIND_EVT, /* BTA_AG_HF_CMD_CIND */ + 0, /* BTA_AG_HF_CMD_CLIP */ + 0, /* BTA_AG_HF_CMD_CMER */ + BTA_AG_AT_VTS_EVT, /* BTA_AG_HF_CMD_VTS */ + BTA_AG_AT_BINP_EVT, /* BTA_AG_HF_CMD_BINP */ + BTA_AG_AT_BLDN_EVT, /* BTA_AG_HF_CMD_BLDN */ + BTA_AG_AT_BVRA_EVT, /* BTA_AG_HF_CMD_BVRA */ + 0, /* BTA_AG_HF_CMD_BRSF */ + BTA_AG_AT_NREC_EVT, /* BTA_AG_HF_CMD_NREC */ + BTA_AG_AT_CNUM_EVT, /* BTA_AG_HF_CMD_CNUM */ + BTA_AG_AT_BTRH_EVT, /* BTA_AG_HF_CMD_BTRH */ + BTA_AG_AT_CLCC_EVT, /* BTA_AG_HF_CMD_CLCC */ + BTA_AG_AT_COPS_EVT, /* BTA_AG_HF_CMD_COPS */ + 0, /* BTA_AG_HF_CMD_CMEE */ + 0, /* BTA_AG_HF_CMD_BIA */ + BTA_AG_AT_CBC_EVT, /* BTA_AG_HF_CMD_CBC */ + 0, /* BTA_AG_HF_CMD_BCC */ + BTA_AG_AT_BCS_EVT, /* BTA_AG_HF_CMD_BCS */ + BTA_AG_AT_BAC_EVT /* BTA_AG_HF_CMD_BAC */ +}; + +/* HFP AT commands matches bta_ag_hfp_cmd[] */ +enum +{ + BTA_AG_HF_CMD_A, + BTA_AG_HF_CMD_D, + BTA_AG_HF_CMD_VGS, + BTA_AG_HF_CMD_VGM, + BTA_AG_HF_CMD_CCWA, + BTA_AG_HF_CMD_CHLD, + BTA_AG_HF_CMD_CHUP, + BTA_AG_HF_CMD_CIND, + BTA_AG_HF_CMD_CLIP, + BTA_AG_HF_CMD_CMER, + BTA_AG_HF_CMD_VTS, + BTA_AG_HF_CMD_BINP, + BTA_AG_HF_CMD_BLDN, + BTA_AG_HF_CMD_BVRA, + BTA_AG_HF_CMD_BRSF, + BTA_AG_HF_CMD_NREC, + BTA_AG_HF_CMD_CNUM, + BTA_AG_HF_CMD_BTRH, + BTA_AG_HF_CMD_CLCC, + BTA_AG_HF_CMD_COPS, + BTA_AG_HF_CMD_CMEE, + BTA_AG_HF_CMD_BIA, + BTA_AG_HF_CMD_CBC, + BTA_AG_HF_CMD_BCC, + BTA_AG_HF_CMD_BCS, + BTA_AG_HF_CMD_BAC +}; + +/* HFP AT command interpreter table */ +const tBTA_AG_AT_CMD bta_ag_hfp_cmd[] = +{ + {"A", BTA_AG_AT_NONE, BTA_AG_AT_STR, 0, 0}, + {"D", (BTA_AG_AT_NONE | BTA_AG_AT_FREE), BTA_AG_AT_STR, 0, 0}, + {"+VGS", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 15}, + {"+VGM", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 15}, + {"+CCWA", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 1}, + /* Consider CHLD as str to take care of indexes for ECC */ + {"+CHLD", (BTA_AG_AT_SET | BTA_AG_AT_TEST), BTA_AG_AT_STR, 0, 4}, + {"+CHUP", BTA_AG_AT_NONE, BTA_AG_AT_STR, 0, 0}, + {"+CIND", (BTA_AG_AT_READ | BTA_AG_AT_TEST), BTA_AG_AT_STR, 0, 0}, + {"+CLIP", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 1}, + {"+CMER", BTA_AG_AT_SET, BTA_AG_AT_STR, 0, 0}, + {"+VTS", BTA_AG_AT_SET, BTA_AG_AT_STR, 0, 0}, + {"+BINP", BTA_AG_AT_SET, BTA_AG_AT_INT, 1, 1}, + {"+BLDN", BTA_AG_AT_NONE, BTA_AG_AT_STR, 0, 0}, + {"+BVRA", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 1}, + {"+BRSF", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, BTA_AG_CMD_MAX_VAL}, + {"+NREC", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 0}, + {"+CNUM", BTA_AG_AT_NONE, BTA_AG_AT_STR, 0, 0}, + {"+BTRH", (BTA_AG_AT_READ | BTA_AG_AT_SET), BTA_AG_AT_INT, 0, 2}, + {"+CLCC", BTA_AG_AT_NONE, BTA_AG_AT_STR, 0, 0}, + {"+COPS", (BTA_AG_AT_READ | BTA_AG_AT_SET), BTA_AG_AT_STR, 0, 0}, + {"+CMEE", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 1}, + {"+BIA", BTA_AG_AT_SET, BTA_AG_AT_STR, 0, 20}, + {"+CBC", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 100}, + {"+BCC", BTA_AG_AT_NONE, BTA_AG_AT_STR, 0, 0}, + {"+BCS", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, BTA_AG_CMD_MAX_VAL}, + {"+BAC", BTA_AG_AT_SET, BTA_AG_AT_STR, 0, 0}, + {"", BTA_AG_AT_NONE, BTA_AG_AT_STR, 0, 0} +}; + +/******************************************* +* AT Result +********************************************/ +const tBTA_AG_AT_CMD *bta_ag_at_tbl[BTA_AG_NUM_IDX] = +{ + bta_ag_hsp_cmd, + bta_ag_hfp_cmd +}; + +/* AT result code argument types */ +enum +{ + BTA_AG_RES_FMT_NONE, /* no argument */ + BTA_AG_RES_FMT_INT, /* integer argument */ + BTA_AG_RES_FMT_STR /* string argument */ +}; + +#if defined(BTA_AG_MULTI_RESULT_INCLUDED) && (BTA_AG_MULTI_RESULT_INCLUDED == TRUE) +#define BTA_AG_AT_MULTI_LEN 2 +#define AT_SET_RES_CB(res_cb, c, p, i) {res_cb.code = c; res_cb.p_arg = p; res_cb.int_arg = i;} + +/* type for AT result code block */ +typedef struct +{ + UINT8 code; + char *p_arg; + INT16 int_arg; +} tBTA_AG_RESULT_CB; + +/* type for multiple AT result codes block */ +typedef struct +{ + UINT8 num_result; + tBTA_AG_RESULT_CB res_cb[BTA_AG_AT_MULTI_LEN]; +} tBTA_AG_MULTI_RESULT_CB; +#endif + +/* AT result code table element */ +typedef struct +{ + const char *p_res; /* AT result string */ + UINT8 fmt; /* whether argument is int or string */ +} tBTA_AG_RESULT; + +/* AT result code constant table (Indexed by result code) */ +const tBTA_AG_RESULT bta_ag_result_tbl[] = +{ + {"OK", BTA_AG_RES_FMT_NONE}, + {"ERROR", BTA_AG_RES_FMT_NONE}, + {"RING", BTA_AG_RES_FMT_NONE}, + {"+VGS: ", BTA_AG_RES_FMT_INT}, + {"+VGM: ", BTA_AG_RES_FMT_INT}, + {"+CCWA: ", BTA_AG_RES_FMT_STR}, + {"+CHLD: ", BTA_AG_RES_FMT_STR}, + {"+CIND: ", BTA_AG_RES_FMT_STR}, + {"+CLIP: ", BTA_AG_RES_FMT_STR}, + {"+CIEV: ", BTA_AG_RES_FMT_STR}, + {"+BINP: ", BTA_AG_RES_FMT_STR}, + {"+BVRA: ", BTA_AG_RES_FMT_INT}, + {"+BRSF: ", BTA_AG_RES_FMT_INT}, + {"+BSIR: ", BTA_AG_RES_FMT_INT}, + {"+CNUM: ", BTA_AG_RES_FMT_STR}, + {"+BTRH: ", BTA_AG_RES_FMT_INT}, + {"+CLCC: ", BTA_AG_RES_FMT_STR}, + {"+COPS: ", BTA_AG_RES_FMT_STR}, + {"+CME ERROR: ", BTA_AG_RES_FMT_INT}, + {"+BCS: ", BTA_AG_RES_FMT_INT}, + {"", BTA_AG_RES_FMT_STR} +}; + +/* AT result codes, matches bta_ag_result_tbl[] */ +enum +{ + BTA_AG_RES_OK, + BTA_AG_RES_ERROR, + BTA_AG_RES_RING, + BTA_AG_RES_VGS, + BTA_AG_RES_VGM, + BTA_AG_RES_CCWA, + BTA_AG_RES_CHLD, + BTA_AG_RES_CIND, + BTA_AG_RES_CLIP, + BTA_AG_RES_CIEV, + BTA_AG_RES_BINP, + BTA_AG_RES_BVRA, + BTA_AG_RES_BRSF, + BTA_AG_RES_BSIR, + BTA_AG_RES_CNUM, + BTA_AG_RES_BTRH, + BTA_AG_RES_CLCC, + BTA_AG_RES_COPS, + BTA_AG_RES_CMEE, + BTA_AG_RES_BCS, + BTA_AG_RES_UNAT +}; + +/* translation of API result code values to internal values */ +const UINT8 bta_ag_trans_result[] = +{ + BTA_AG_RES_VGS, /* BTA_AG_SPK_RES */ + BTA_AG_RES_VGM, /* BTA_AG_MIC_RES */ + BTA_AG_RES_BSIR, /* BTA_AG_INBAND_RING_RES */ + BTA_AG_RES_CIND, /* BTA_AG_CIND_RES */ + BTA_AG_RES_BINP, /* BTA_AG_BINP_RES */ + BTA_AG_RES_CIEV, /* BTA_AG_IND_RES */ + BTA_AG_RES_BVRA, /* BTA_AG_BVRA_RES */ + BTA_AG_RES_CNUM, /* BTA_AG_CNUM_RES */ + BTA_AG_RES_BTRH, /* BTA_AG_BTRH_RES */ + BTA_AG_RES_CLCC, /* BTA_AG_CLCC_RES */ + BTA_AG_RES_COPS, /* BTA_AG_COPS_RES */ + 0, /* BTA_AG_IN_CALL_RES */ + 0, /* BTA_AG_IN_CALL_CONN_RES */ + BTA_AG_RES_CCWA, /* BTA_AG_CALL_WAIT_RES */ + 0, /* BTA_AG_OUT_CALL_ORIG_RES */ + 0, /* BTA_AG_OUT_CALL_ALERT_RES */ + 0, /* BTA_AG_OUT_CALL_CONN_RES */ + 0, /* BTA_AG_CALL_CANCEL_RES */ + 0, /* BTA_AG_END_CALL_RES */ + 0, /* BTA_AG_IN_CALL_HELD_RES */ + BTA_AG_RES_UNAT /* BTA_AG_UNAT_RES */ +}; + +/* callsetup indicator value lookup table */ +const UINT8 bta_ag_callsetup_ind_tbl[] = +{ + 0, /* BTA_AG_SPK_RES */ + 0, /* BTA_AG_MIC_RES */ + 0, /* BTA_AG_INBAND_RING_RES */ + 0, /* BTA_AG_CIND_RES */ + 0, /* BTA_AG_BINP_RES */ + 0, /* BTA_AG_IND_RES */ + 0, /* BTA_AG_BVRA_RES */ + 0, /* BTA_AG_CNUM_RES */ + 0, /* BTA_AG_BTRH_RES */ + 0, /* BTA_AG_CLCC_RES */ + 0, /* BTA_AG_COPS_RES */ + BTA_AG_CALLSETUP_INCOMING, /* BTA_AG_IN_CALL_RES */ + BTA_AG_CALLSETUP_NONE, /* BTA_AG_IN_CALL_CONN_RES */ + BTA_AG_CALLSETUP_INCOMING, /* BTA_AG_CALL_WAIT_RES */ + BTA_AG_CALLSETUP_OUTGOING, /* BTA_AG_OUT_CALL_ORIG_RES */ + BTA_AG_CALLSETUP_ALERTING, /* BTA_AG_OUT_CALL_ALERT_RES */ + BTA_AG_CALLSETUP_NONE, /* BTA_AG_OUT_CALL_CONN_RES */ + BTA_AG_CALLSETUP_NONE, /* BTA_AG_CALL_CANCEL_RES */ + BTA_AG_CALLSETUP_NONE, /* BTA_AG_END_CALL_RES */ + BTA_AG_CALLSETUP_NONE /* BTA_AG_IN_CALL_HELD_RES */ +}; + +#if defined(BTA_HSP_RESULT_REPLACE_COLON) && (BTA_HSP_RESULT_REPLACE_COLON == TRUE) +#define COLON_IDX_4_VGSVGM 4 +#endif + +/******************************************* +* Funcitons Result +********************************************/ +/******************************************************************************* +** +** Function bta_ag_send_result +** +** Description Send an AT result code. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_send_result(tBTA_AG_SCB *p_scb, UINT8 code, char *p_arg, INT16 int_arg) +{ + char buf[BTA_AG_AT_MAX_LEN + 16]; + char *p = buf; + UINT16 len; +#if (BTIF_TRACE_DEBUG == TRUE) + memset(buf, NULL, sizeof(buf)); +#endif + /* init with \r\n */ + *p++ = '\r'; + *p++ = '\n'; + + /* copy result code string */ + BCM_STRCPY_S(p, sizeof(buf), bta_ag_result_tbl[code].p_res); +#if defined(BTA_HSP_RESULT_REPLACE_COLON) && (BTA_HSP_RESULT_REPLACE_COLON == TRUE) + if(p_scb->conn_service == BTA_AG_HSP) { + /* If HSP then ":"symbol should be changed as "=" for HSP compatibility */ + switch(code) { + case BTA_AG_RES_VGS: + case BTA_AG_RES_VGM: + { + if(*(p+COLON_IDX_4_VGSVGM) == ':') + { + #if defined(BTA_AG_RESULT_DEBUG) && (BTA_AG_RESULT_DEBUG == TRUE) + APPL_TRACE_DEBUG("[HSP] ':'symbol is changed as '=' for HSP compatibility"); + #endif + *(p+COLON_IDX_4_VGSVGM) = '='; + } + break; + } + } + } +#endif + p += strlen(bta_ag_result_tbl[code].p_res); + + /* copy argument if any */ + if (bta_ag_result_tbl[code].fmt == BTA_AG_RES_FMT_INT) { + p += utl_itoa((UINT16) int_arg, p); + } else if (bta_ag_result_tbl[code].fmt == BTA_AG_RES_FMT_STR) { + BCM_STRCPY_S(p, sizeof(buf), p_arg); + p += strlen(p_arg); + } + /* finish with \r\n */ + *p++ = '\r'; + *p++ = '\n'; + APPL_TRACE_DEBUG("bta_ag_send_result: %s", buf); + /* send to RFCOMM */ + PORT_WriteData(p_scb->conn_handle, buf, (UINT16) (p - buf), &len); +} + +#if defined(BTA_AG_MULTI_RESULT_INCLUDED) && (BTA_AG_MULTI_RESULT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function bta_ag_send_multi_result +** +** Description Send multiple AT result codes. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_send_multi_result(tBTA_AG_SCB *p_scb, tBTA_AG_MULTI_RESULT_CB *m_res_cb) +{ + char buf[BTA_AG_AT_MAX_LEN * BTA_AG_AT_MULTI_LEN + 16]; + char *p = buf; + UINT16 len; + UINT8 res_idx = 0; + + if((!m_res_cb) || (m_res_cb->num_result == 0) || (m_res_cb->num_result > BTA_AG_AT_MULTI_LEN)) { + APPL_TRACE_DEBUG("m_res_cb is NULL or num_result is out of range."); + return; + } + +#if defined(BTA_AG_RESULT_DEBUG) && (BTA_AG_RESULT_DEBUG == TRUE) + memset(buf, NULL, sizeof(buf)); +#endif + + while(res_idx < m_res_cb->num_result) { + /* init with \r\n */ + *p++ = '\r'; + *p++ = '\n'; + + /* copy result code string */ + BCM_STRCPY_S(p, sizeof(buf), bta_ag_result_tbl[m_res_cb->res_cb[res_idx].code].p_res); + p += strlen(bta_ag_result_tbl[m_res_cb->res_cb[res_idx].code].p_res); + + /* copy argument if any */ + if (bta_ag_result_tbl[m_res_cb->res_cb[res_idx].code].fmt == BTA_AG_RES_FMT_INT) { + p += utl_itoa((UINT16) m_res_cb->res_cb[res_idx].int_arg, p); + } else if (bta_ag_result_tbl[m_res_cb->res_cb[res_idx].code].fmt == BTA_AG_RES_FMT_STR) { + BCM_STRCPY_S(p, sizeof(buf), m_res_cb->res_cb[res_idx].p_arg); + p += strlen(m_res_cb->res_cb[res_idx].p_arg); + } + /* finish with \r\n */ + *p++ = '\r'; + *p++ = '\n'; + res_idx++; + } +#if defined(BTA_AG_RESULT_DEBUG) && (BTA_AG_RESULT_DEBUG == TRUE) + APPL_TRACE_DEBUG("send_result: %s", buf); +#endif + /* send to RFCOMM */ + PORT_WriteData(p_scb->conn_handle, buf, (UINT16) (p - buf), &len); +} +#endif /* #if defined(BTA_AG_MULTI_RESULT_INCLUDED) && (BTA_AG_MULTI_RESULT_INCLUDED == TRUE) */ + +/******************************************************************************* +** +** Function bta_ag_send_ok +** +** Description Send an OK result code. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_send_ok(tBTA_AG_SCB *p_scb) +{ + bta_ag_send_result(p_scb, BTA_AG_RES_OK, NULL, 0); +} + +/******************************************************************************* +** +** Function bta_ag_send_error +** +** Description Send an ERROR result code. +** errcode - used to send verbose errocode +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_send_error(tBTA_AG_SCB *p_scb, INT16 errcode) +{ + /* If HFP and extended audio gateway error codes are enabled */ + if (p_scb->conn_service == BTA_AG_HFP && p_scb->cmee_enabled) { + bta_ag_send_result(p_scb, BTA_AG_RES_CMEE, NULL, errcode); + } else { + bta_ag_send_result(p_scb, BTA_AG_RES_ERROR, NULL, 0); + } +} + +/******************************************************************************* +** +** Function bta_ag_send_ind +** +** Description Send an indicator CIEV result code. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_send_ind(tBTA_AG_SCB *p_scb, UINT16 id, UINT16 value, BOOLEAN on_demand) +{ + char str[12]; + char *p = str; + /* If the indicator is masked out, just return */ + /* Mandatory indicators can not be masked out. */ + if ((p_scb->bia_masked_out & ((UINT32)1 << id)) && + ((id != BTA_AG_IND_CALL) && (id != BTA_AG_IND_CALLSETUP) && (id != BTA_AG_IND_CALLHELD))) + return; + + /* Ensure we do not send duplicate indicators if not requested by app */ + /* If it was requested by app, transmit CIEV even if it is duplicate. */ + if (id == BTA_AG_IND_CALL) { + if ((value == p_scb->call_ind) && (on_demand == FALSE)) { + return; + } + p_scb->call_ind = (UINT8)value; + } + if ((id == BTA_AG_IND_CALLSETUP) && (on_demand == FALSE)) { + if (value == p_scb->callsetup_ind) { + return; + } + p_scb->callsetup_ind = (UINT8)value; + } + if ((id == BTA_AG_IND_SERVICE) && (on_demand == FALSE)) { + if (value == p_scb->service_ind) { + return; + } + p_scb->service_ind = (UINT8)value; + } + if ((id == BTA_AG_IND_SIGNAL) && (on_demand == FALSE)) { + if (value == p_scb->signal_ind) { + return; + } + p_scb->signal_ind = (UINT8)value; + } + if ((id == BTA_AG_IND_ROAM) && (on_demand == FALSE)) { + if (value == p_scb->roam_ind) { + return; + } + p_scb->roam_ind = (UINT8)value; + } + if ((id == BTA_AG_IND_BATTCHG) && (on_demand == FALSE)) { + if (value == p_scb->battchg_ind) { + return; + } + p_scb->battchg_ind = (UINT8)value; + } + if ((id == BTA_AG_IND_CALLHELD) && (on_demand == FALSE)) { + /* call swap could result in sending callheld=1 multiple times */ + if ((value != 1) && (value == p_scb->callheld_ind)) { + return; + } + p_scb->callheld_ind = (UINT8)value; + } + if (p_scb->cmer_enabled) { + p += utl_itoa(id, p); + *p++ = ','; + utl_itoa(value, p); + bta_ag_send_result(p_scb, BTA_AG_RES_CIEV, str, 0); + } +} + +/******************************************************************************* +** +** Function bta_ag_parse_cmer +** +** Description Parse AT+CMER parameter string. +** +** +** Returns TRUE if parsed ok, FALSE otherwise. +** +*******************************************************************************/ +static BOOLEAN bta_ag_parse_cmer(char *p_s, BOOLEAN *p_enabled) +{ + INT16 n[4] = {-1, -1, -1, -1}; + int i; + char *p; + + for (i = 0; i < 4; i++) { + /* skip to comma delimiter */ + for (p = p_s; *p != ',' && *p != 0; p++); + /* get integer value */ + *p = 0; + n[i] = utl_str2int(p_s); + p_s = p + 1; + if (p_s == 0) { + break; + } + } + /* process values */ + if (n[0] < 0 || n[3] < 0) { + return FALSE; + } + if ((n[0] == 3) && ((n[3] == 1) || (n[3] == 0))) { + *p_enabled = (BOOLEAN) n[3]; + } + return TRUE; +} + +/******************************************************************************* +** +** Function bta_ag_parse_chld +** +** Description Parse AT+CHLD parameter string. +** +** +** Returns Returns idx (1-7), 0 if ECC not enabled or BTA_AG_INVALID_CHLD + if idx doesn't exist/1st character of argument is not a digit +** +*******************************************************************************/ +static UINT8 bta_ag_parse_chld(tBTA_AG_SCB *p_scb, char *p_s) +{ + UINT8 retval = 0; + INT16 idx = -1; + UNUSED(p_scb); + + if (!isdigit(p_s[0])) { + return BTA_AG_INVALID_CHLD; + } + + if (p_s[1] != 0) { + /* p_idxstr++; point to beginning of call number */ + idx = utl_str2int(&p_s[1]); + if (idx != -1 && idx < 255) { + retval = (UINT8)idx; + } else { + retval = BTA_AG_INVALID_CHLD; + } + } + return(retval); +} + +#if (BTM_WBS_INCLUDED == TRUE ) +/******************************************************************************* +** +** Function bta_ag_parse_bac +** +** Description Parse AT+BAC parameter string. +** +** Returns Returns bitmap of supported codecs. +** +*******************************************************************************/ +static tBTA_AG_PEER_CODEC bta_ag_parse_bac(tBTA_AG_SCB *p_scb, char *p_s) +{ + tBTA_AG_PEER_CODEC retval = BTA_AG_CODEC_NONE; + UINT16 uuid_codec; + BOOLEAN cont = FALSE; /* Continue processing */ + char *p; + + while (p_s) { + /* skip to comma delimiter */ + for (p = p_s; *p != ',' && *p != 0; p++); + + /* get integre value */ + if (*p != 0) { + *p = 0; + cont = TRUE; + } else { + cont = FALSE; + } + uuid_codec = utl_str2int(p_s); + switch(uuid_codec) { + case UUID_CODEC_CVSD: + retval |= BTA_AG_CODEC_CVSD; + break; + + case UUID_CODEC_MSBC: + retval |= BTA_AG_CODEC_MSBC; + break; + + default: + APPL_TRACE_ERROR("Unknown Codec UUID(%d) received", uuid_codec); + return BTA_AG_CODEC_NONE; + } + if (cont) { + p_s = p + 1; + } + else { + break; + } + } + return (retval); +} +#endif /* #if (BTM_WBS_INCLUDED == TRUE ) */ + +/******************************************************************************* +** +** Function bta_ag_process_unat_res +** +** Description Process the unat response data and remove extra carriage return +** and line feed +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_process_unat_res(char *unat_result) +{ + UINT8 str_leng; + UINT8 i = 0; + UINT8 j = 0; + UINT8 pairs_of_nl_cr; + char trim_data[BTA_AG_AT_MAX_LEN]; + + str_leng = strlen(unat_result); + /* If no extra CR and LF, just return */ + if(str_leng < 4) { + return; + } + + /* Remove the carriage return and left feed */ + while(unat_result[0] =='\r' && unat_result[1] =='\n' + && unat_result[str_leng-2] =='\r' && unat_result[str_leng-1] =='\n') { + pairs_of_nl_cr = 1; + for (i=0;i<(str_leng-4*pairs_of_nl_cr);i++) { + trim_data[j++] = unat_result[i+pairs_of_nl_cr*2]; + } + /* Add EOF */ + trim_data[j] = '\0'; + str_leng = str_leng - 4; + BCM_STRNCPY_S(unat_result, BTA_AG_AT_MAX_LEN+1, trim_data,str_leng+1); + i=0; + j=0; + if(str_leng <4) { + return; + } + } + return; +} + +/******************************************************************************* +** +** Function bta_ag_inband_enabled +** +** Description Determine whether in-band ring can be used. +** +** +** Returns void +** +*******************************************************************************/ +BOOLEAN bta_ag_inband_enabled(tBTA_AG_SCB *p_scb) +{ + /* if feature is enabled and no other scbs connected */ + if (p_scb->inband_enabled && !bta_ag_other_scb_open(p_scb)) { + return TRUE; + } else { + return FALSE; + } +} + +/******************************************************************************* +** +** Function bta_ag_send_call_inds +** +** Description Send call and callsetup indicators. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_send_call_inds(tBTA_AG_SCB *p_scb, tBTA_AG_RES result) +{ + UINT8 call = p_scb->call_ind; + UINT8 callsetup; + /* set new call and callsetup values based on BTA_AgResult */ + callsetup = bta_ag_callsetup_ind_tbl[result]; + + if (result == BTA_AG_END_CALL_RES) { + call = BTA_AG_CALL_INACTIVE; + } else if (result == BTA_AG_IN_CALL_CONN_RES || result == BTA_AG_OUT_CALL_CONN_RES + || result == BTA_AG_IN_CALL_HELD_RES) { + call = BTA_AG_CALL_ACTIVE; + } else { + call = p_scb->call_ind; + } + /* Send indicator function tracks if the values have actually changed */ + bta_ag_send_ind(p_scb, BTA_AG_IND_CALL, call, FALSE); + bta_ag_send_ind(p_scb, BTA_AG_IND_CALLSETUP, callsetup, FALSE); +} + +/******************************************************************************* +** +** Function bta_ag_at_hsp_cback +** +** Description AT command processing callback for HSP. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_at_hsp_cback(tBTA_AG_SCB *p_scb, UINT16 cmd, UINT8 arg_type, + char *p_arg, INT16 int_arg) +{ + tBTA_AG_VAL val; + APPL_TRACE_DEBUG("AT cmd:%d arg_type:%d arg:%d arg:%s", cmd, arg_type, int_arg, p_arg); + + /* send OK */ + bta_ag_send_ok(p_scb); + val.hdr.handle = bta_ag_scb_to_idx(p_scb); + val.hdr.app_id = p_scb->app_id; + val.num = (UINT16) int_arg; + BCM_STRNCPY_S(val.str, sizeof(val.str), p_arg, BTA_AG_AT_MAX_LEN); + val.str[BTA_AG_AT_MAX_LEN] = 0; + /* call callback with event */ + (*bta_ag_cb.p_cback)(bta_ag_hsp_cb_evt[cmd], (tBTA_AG *) &val); +} + +/******************************************************************************* +** +** Function bta_ag_at_hfp_cback +** +** Description AT command processing callback for HFP. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_at_hfp_cback(tBTA_AG_SCB *p_scb, UINT16 cmd, UINT8 arg_type, + char *p_arg, INT16 int_arg) +{ + tBTA_AG_VAL val; + tBTA_AG_EVT event; + tBTA_AG_SCB *ag_scb; + UINT32 i, ind_id; + UINT32 bia_masked_out; +#if (BTM_WBS_INCLUDED == TRUE) + tBTA_AG_PEER_CODEC codec_type, codec_sent; +#endif + if (p_arg == NULL) { + APPL_TRACE_ERROR("%s: p_arg is null, send error and return", __func__); + bta_ag_send_error(p_scb, BTA_AG_ERR_INV_CHAR_IN_TSTR); + return; + } + APPL_TRACE_DEBUG("HFP AT cmd:%d arg_type:%d arg:%d arg:%s", cmd, arg_type, int_arg, p_arg); + + val.hdr.handle = bta_ag_scb_to_idx(p_scb); + val.hdr.app_id = p_scb->app_id; + val.num = int_arg; + bdcpy(val.bd_addr, p_scb->peer_addr); + BCM_STRNCPY_S(val.str, sizeof(val.str), p_arg, BTA_AG_AT_MAX_LEN); + val.str[BTA_AG_AT_MAX_LEN] = 0; + event = bta_ag_hfp_cb_evt[cmd]; + + switch (cmd) + { + case BTA_AG_HF_CMD_A: + case BTA_AG_HF_CMD_VGS: + case BTA_AG_HF_CMD_VGM: + case BTA_AG_HF_CMD_CHUP: + case BTA_AG_HF_CMD_CBC: + /* send OK */ + bta_ag_send_ok(p_scb); + break; + + case BTA_AG_HF_CMD_BLDN: + /* Do not send OK, App will send error or OK depending on last-dial-umber enabled or not */ + break; + + case BTA_AG_HF_CMD_D: + { + /* Do not send OK for Dial cmds Let application decide whether to send OK or ERROR*/ + /* if mem dial cmd, make sure string contains only digits */ + if(p_arg[0] == '>') { + if(!utl_isintstr(p_arg+1)) { + event = 0; + bta_ag_send_error(p_scb, BTA_AG_ERR_INV_CHAR_IN_DSTR); + } + } else if (p_arg[0] == 'V') { + /* ATDV : Dial VoIP Call */ + /* We do not check string. Code will be added later if needed. */ + if(!((p_scb->peer_features & BTA_AG_PEER_FEAT_VOIP) && (p_scb->features & BTA_AG_FEAT_VOIP))) { + event = 0; + bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); + } + } else { + /* If dial cmd, make sure string contains only dial digits + ** Dial digits are 0-9, A-C, *, #, + */ + if(!utl_isdialstr(p_arg)) { + event = 0; + bta_ag_send_error(p_scb, BTA_AG_ERR_INV_CHAR_IN_DSTR); + } + } + break; + } + + case BTA_AG_HF_CMD_CCWA: + p_scb->ccwa_enabled = (BOOLEAN) int_arg; /* store setting */ + bta_ag_send_ok(p_scb); /* send OK */ + break; + + case BTA_AG_HF_CMD_CHLD: + { + if (arg_type == BTA_AG_AT_TEST) { + /* don't call callback */ + event = 0; + /* send CHLD string */ + /* Form string based on supported 1.5 feature */ + if ((p_scb->peer_version >= HFP_VERSION_1_5) && + (p_scb->features & BTA_AG_FEAT_ECC) && + (p_scb->peer_features & BTA_AG_PEER_FEAT_ECC)) { + bta_ag_send_result(p_scb, BTA_AG_RES_CHLD, p_bta_ag_cfg->chld_val_ecc, 0); + } else { + bta_ag_send_result(p_scb, BTA_AG_RES_CHLD, p_bta_ag_cfg->chld_val, 0); + } + /* send OK */ + bta_ag_send_ok(p_scb); + /* if service level conn. not already open, now it's open */ + bta_ag_svc_conn_open(p_scb, NULL); + } else { + val.idx = bta_ag_parse_chld(p_scb, val.str); + if (val.idx == BTA_AG_INVALID_CHLD) { + event = 0; + bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); + break; + } + if(val.idx && !((p_scb->features & BTA_AG_FEAT_ECC) && (p_scb->peer_features & BTA_AG_PEER_FEAT_ECC))) { + /* we do not support ECC, but HF is sending us a CHLD with call index*/ + event = 0; + bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); + + } else { + /* If it is swap between calls, set call held indicator to 3(out of valid 0-2) + ** Application will set it back to 1 + ** callheld indicator will be sent across to the peer. */ + if(val.str[0] == '2') { + for (i = 0, ag_scb = &bta_ag_cb.scb[0]; i < BTA_AG_NUM_SCB; i++, ag_scb++) { + if (ag_scb->in_use) { + if((ag_scb->call_ind == BTA_AG_CALL_ACTIVE) + && (ag_scb->callsetup_ind == BTA_AG_CALLSETUP_NONE)) { + ag_scb->callheld_ind = BTA_AG_CALLHELD_NOACTIVE + 1; + } + } + } + } + } + /* Do not send OK. Let app decide after parsing the val str */ + /* bta_ag_send_ok(p_scb); */ + } + break; + } + + case BTA_AG_HF_CMD_CIND: + if (arg_type == BTA_AG_AT_TEST) { + /* don't call callback */ + event = 0; + /* send CIND string, send OK */ + bta_ag_send_result(p_scb, BTA_AG_RES_CIND, p_bta_ag_cfg->cind_info, 0); + bta_ag_send_ok(p_scb); + } + break; + + case BTA_AG_HF_CMD_CLIP: + /* store setting, send OK */ + p_scb->clip_enabled = (BOOLEAN) int_arg; + bta_ag_send_ok(p_scb); + break; + + case BTA_AG_HF_CMD_CMER: + /* if parsed ok store setting, send OK */ + if (bta_ag_parse_cmer(p_arg, &p_scb->cmer_enabled)) { + bta_ag_send_ok(p_scb); + /* if service level conn. not already open and our features and + ** peer features do not have 3-way, service level conn. now open + */ + if (!p_scb->svc_conn && !((p_scb->features & BTA_AG_FEAT_3WAY) && (p_scb->peer_features & BTA_AG_PEER_FEAT_3WAY))) { + bta_ag_svc_conn_open(p_scb, NULL); + } + } else { + bta_ag_send_error(p_scb, BTA_AG_ERR_INV_CHAR_IN_TSTR); + } + break; + + case BTA_AG_HF_CMD_VTS: + /* check argument */ + if (strlen(p_arg) == 1) { + bta_ag_send_ok(p_scb); + } else { + event = 0; + bta_ag_send_error(p_scb, BTA_AG_ERR_INV_CHAR_IN_TSTR); + } + break; + + case BTA_AG_HF_CMD_BINP: + /* if feature not set don't call callback, send ERROR */ + if (!(p_scb->features & BTA_AG_FEAT_VTAG)) { + event = 0; + bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); + } + break; + + case BTA_AG_HF_CMD_BVRA: + /* if feature not supported don't call callback, send ERROR. App will send OK */ + if (!(p_scb->features & BTA_AG_FEAT_VREC)) { + event = 0; + bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); + } + break; + + case BTA_AG_HF_CMD_BRSF: + { + /* store peer features */ + p_scb->peer_features = (UINT16) int_arg; + /* send BRSF, send OK */ + bta_ag_send_result(p_scb, BTA_AG_RES_BRSF, NULL, (INT16) (p_scb->features & BTA_AG_BSRF_FEAT_SPEC)); + bta_ag_send_ok(p_scb); + break; + } + + case BTA_AG_HF_CMD_NREC: + /* if feature send OK, else don't call callback, send ERROR */ + if (p_scb->features & BTA_AG_FEAT_ECNR) { + bta_ag_send_ok(p_scb); + } else { + event = 0; + bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); + } + break; + + case BTA_AG_HF_CMD_BTRH: + /* if feature send BTRH, send OK:, else don't call callback, send ERROR */ + if (p_scb->features & BTA_AG_FEAT_BTRH) { + /* If set command; send response and notify app */ + if (arg_type == BTA_AG_AT_SET) { + for (i = 0, ag_scb = &bta_ag_cb.scb[0]; i < BTA_AG_NUM_SCB; i++, ag_scb++) { + if (ag_scb->in_use) { + bta_ag_send_result(ag_scb, BTA_AG_RES_BTRH, NULL, int_arg); + } + } + bta_ag_send_ok(p_scb); + } else { + /* Read Command */ + val.num = BTA_AG_BTRH_READ; + } + } else { + event = 0; + bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); + } + break; + + case BTA_AG_HF_CMD_COPS: + if (arg_type == BTA_AG_AT_SET) { + /* don't call callback */ + event = 0; + /* send OK */ + bta_ag_send_ok(p_scb); + } + break; + + case BTA_AG_HF_CMD_CMEE: + if (p_scb->features & BTA_AG_FEAT_EXTERR) { + /* store setting */ + p_scb->cmee_enabled = (BOOLEAN) int_arg; + /* send OK */ + bta_ag_send_ok(p_scb); + } else { + bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); + } + /* don't call callback */ + event = 0; + break; + + case BTA_AG_HF_CMD_BIA: + { + /* don't call callback */ + event = 0; + bia_masked_out = p_scb->bia_masked_out; + /* Parse the indicator mask */ + for (i = 0, ind_id = 1; (val.str[i] != 0) && (ind_id <= 20); i++, ind_id++) { + if (val.str[i] == ',') + continue; + if (val.str[i] == '0') + bia_masked_out |= ((UINT32)1 << ind_id); + else if (val.str[i] == '1') + bia_masked_out &= ~((UINT32)1 << ind_id); + else + break; + i++; + if ( (val.str[i] != 0) && (val.str[i] != ',') ) + break; + } + if (val.str[i] == 0) { + p_scb->bia_masked_out = bia_masked_out; + bta_ag_send_ok (p_scb); + } else { + bta_ag_send_error (p_scb, BTA_AG_ERR_INVALID_INDEX); + } + break; + } + + case BTA_AG_HF_CMD_CNUM: + if(!(p_scb->features & BTA_AG_FEAT_ECS)) { + event = 0; + bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); + } + break; + + case BTA_AG_HF_CMD_CLCC: + if(!(p_scb->features & BTA_AG_FEAT_ECS)) { + event = 0; + bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); + } + break; + +#if (BTM_WBS_INCLUDED == TRUE ) + case BTA_AG_HF_CMD_BAC: + { + bta_ag_send_ok(p_scb); + /* store available codecs from the peer */ + if((p_scb->peer_features & BTA_AG_PEER_FEAT_CODEC) && (p_scb->features & BTA_AG_FEAT_CODEC)) { + p_scb->peer_codecs = bta_ag_parse_bac(p_scb, p_arg); + p_scb->codec_updated = TRUE; + + if (p_scb->peer_codecs & BTA_AG_CODEC_MSBC) { + p_scb->sco_codec = UUID_CODEC_MSBC; + APPL_TRACE_DEBUG("Received AT+BAC, updating sco codec to MSBC"); + } else { + p_scb->sco_codec = UUID_CODEC_CVSD; + APPL_TRACE_DEBUG("Received AT+BAC, updating sco codec to CVSD"); + } + /* The above logic sets the stack preferred codec based on local and peer codec + capabilities. This can be overridden by the application depending on its preference + using the bta_ag_setcodec API. We send the peer_codecs to the application. */ + val.num = p_scb->peer_codecs; + /* Received BAC while in codec negotiation. */ + if ((bta_ag_cb.sco.state == BTA_AG_SCO_CODEC_ST) && (bta_ag_cb.sco.p_curr_scb == p_scb)) { + bta_ag_codec_negotiate (p_scb); + } + } else { + p_scb->peer_codecs = BTA_AG_CODEC_NONE; + APPL_TRACE_ERROR("Unexpected CMD:AT+BAC, Codec Negotiation is not supported"); + } + break; + } + + case BTA_AG_HF_CMD_BCS: + { + bta_ag_send_ok(p_scb); + /* stop cn timer */ + bta_sys_stop_timer(&p_scb->cn_timer); + + switch(int_arg) { + case UUID_CODEC_CVSD: + codec_type = BTA_AG_CODEC_CVSD; + break; + + case UUID_CODEC_MSBC: + codec_type = BTA_AG_CODEC_MSBC; + break; + + default: + APPL_TRACE_ERROR("Unknown codec_uuid %d", int_arg); + codec_type = 0xFFFF; + break; + } + + if (p_scb->codec_fallback) { + codec_sent = BTA_AG_CODEC_CVSD; + } else { + codec_sent = p_scb->sco_codec; + } + + if(codec_type == codec_sent) { + bta_ag_sco_codec_nego(p_scb, TRUE); + } else { + bta_ag_sco_codec_nego(p_scb, FALSE); + } + /* send final codec info to callback */ + val.num = codec_sent; + break; + } + + case BTA_AG_HF_CMD_BCC: + { + bta_ag_send_ok(p_scb); + bta_ag_sco_open(p_scb, NULL); + break; + } +#endif + default: + bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); + break; + } + /* call callback */ + if (event != 0) { + (*bta_ag_cb.p_cback)(event, (tBTA_AG *) &val); + } +} + +/******************************************************************************* +** +** Function bta_ag_at_err_cback +** +** Description AT command parser error callback. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_at_err_cback(tBTA_AG_SCB *p_scb, BOOLEAN unknown, char *p_arg) +{ + tBTA_AG_VAL val; + + if(unknown && (!strlen(p_arg))) { + APPL_TRACE_DEBUG("Empty AT cmd string received"); + bta_ag_send_ok(p_scb); + return; + } + + /* if unknown AT command and configured to pass these to app */ + if (unknown && (p_scb->features & BTA_AG_FEAT_UNAT)) { + val.hdr.handle = bta_ag_scb_to_idx(p_scb); + val.hdr.app_id = p_scb->app_id; + val.num = 0; + BCM_STRNCPY_S(val.str, sizeof(val.str), p_arg, BTA_AG_AT_MAX_LEN); + val.str[BTA_AG_AT_MAX_LEN] = 0; + (*bta_ag_cb.p_cback)(BTA_AG_AT_UNAT_EVT, (tBTA_AG *) &val); + } else { + bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); + } +} + +/******************************************************************************* +** +** Function bta_ag_hsp_result +** +** Description Handle API result for HSP connections. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_hsp_result(tBTA_AG_SCB *p_scb, tBTA_AG_API_RESULT *p_result) +{ + UINT8 code = bta_ag_trans_result[p_result->result]; + + APPL_TRACE_DEBUG("bta_ag_hsp_result : res = %d", p_result->result); + + switch(p_result->result) { + case BTA_AG_SPK_RES: + case BTA_AG_MIC_RES: + bta_ag_send_result(p_scb, code, NULL, p_result->data.num); + break; + + case BTA_AG_IN_CALL_RES: + { + /* tell sys to stop av if any */ + bta_sys_sco_use(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + /* if sco already opened or no inband ring send ring now */ + if (bta_ag_sco_is_open(p_scb) || !bta_ag_inband_enabled(p_scb) || + (p_scb->features & BTA_AG_FEAT_NOSCO)) { + bta_ag_send_ring(p_scb, (tBTA_AG_DATA *) p_result); + } else { + /* else open sco, send ring after sco opened */ + /* HSPv1.2: AG shall not send RING if using in-band ring tone. */ + if (p_scb->hsp_version >= HSP_VERSION_1_2) { + p_scb->post_sco = BTA_AG_POST_SCO_NONE; + } else { + p_scb->post_sco = BTA_AG_POST_SCO_RING; + } + bta_ag_sco_open(p_scb, (tBTA_AG_DATA *) p_result); + } + break; + } + + case BTA_AG_IN_CALL_CONN_RES: + case BTA_AG_OUT_CALL_ORIG_RES: + { + /* if incoming call connected stop ring timer */ + if (p_result->result == BTA_AG_IN_CALL_CONN_RES) { + bta_sys_stop_timer(&p_scb->act_timer); + } + + if (!(p_scb->features & BTA_AG_FEAT_NOSCO)) { + /* if audio connected to this scb open sco */ + if (p_result->data.audio_handle == bta_ag_scb_to_idx(p_scb)) { + bta_ag_sco_open(p_scb, (tBTA_AG_DATA *) p_result); + } else if (p_result->data.audio_handle == BTA_AG_HANDLE_NONE) { + /* else if no audio at call close sco */ + bta_ag_sco_close(p_scb, (tBTA_AG_DATA *) p_result); + } + } + break; + } + + case BTA_AG_END_CALL_RES: + { + /* stop ring timer */ + bta_sys_stop_timer(&p_scb->act_timer); + /* close sco */ + if ((bta_ag_sco_is_open(p_scb) || bta_ag_sco_is_opening(p_scb)) && !(p_scb->features & BTA_AG_FEAT_NOSCO)) { + bta_ag_sco_close(p_scb, (tBTA_AG_DATA *) p_result); + } else { + /* if av got suspended by this call, let it resume. */ + bta_sys_sco_unuse(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + } + break; + } + + case BTA_AG_INBAND_RING_RES: + p_scb->inband_enabled = p_result->data.state; + APPL_TRACE_DEBUG("inband_enabled set to %d", p_scb->inband_enabled); + break; + + case BTA_AG_UNAT_RES: + { + if (p_result->data.ok_flag != BTA_AG_OK_ERROR) { + if (p_result->data.str[0] != 0) { + bta_ag_send_result(p_scb, code, p_result->data.str, 0); + } + if (p_result->data.ok_flag == BTA_AG_OK_DONE) { + bta_ag_send_ok(p_scb); + } + } else { + bta_ag_send_error(p_scb, BTA_AG_ERR_INV_CHAR_IN_TSTR); + } + break; + } + default: + break; /* ignore all others */ + } +} + +/******************************************************************************* +** +** Function bta_ag_hfp_result +** +** Description Handle API result for HFP connections. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_hfp_result(tBTA_AG_SCB *p_scb, tBTA_AG_API_RESULT *p_result) +{ + UINT8 code = bta_ag_trans_result[p_result->result]; + + APPL_TRACE_DEBUG("bta_ag_hfp_result : res = %d", p_result->result); + + switch(p_result->result) + { + case BTA_AG_SPK_RES: + case BTA_AG_MIC_RES: + bta_ag_send_result(p_scb, code, NULL, p_result->data.num); + break; + + case BTA_AG_IN_CALL_RES: + { + /* tell sys to stop av if any */ + bta_sys_sco_use(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + /* store caller id string. + * append type info at the end. + * make sure a valid type info is passed. + * otherwise add 129 as default type */ + if ((p_result->data.num < BTA_AG_CLIP_TYPE_MIN) || (p_result->data.num > BTA_AG_CLIP_TYPE_MAX)) { + if (p_result->data.num != BTA_AG_CLIP_TYPE_VOIP) { + p_result->data.num = BTA_AG_CLIP_TYPE_DEFAULT; + } + } + APPL_TRACE_DEBUG("CLIP type :%d", p_result->data.num); + p_scb->clip[0] = 0; + if (p_result->data.str[0] != 0) { + snprintf(p_scb->clip, sizeof(p_scb->clip), "%s,%d", p_result->data.str, p_result->data.num); + } + /* send callsetup indicator */ + if (p_scb->post_sco == BTA_AG_POST_SCO_CALL_END) { + /* Need to sent 2 callsetup IND's(Call End and Incoming call) after SCO close. */ + p_scb->post_sco = BTA_AG_POST_SCO_CALL_END_INCALL; + } else { + bta_ag_send_call_inds(p_scb, p_result->result); + /* if sco already opened or no inband ring send ring now */ + if (bta_ag_sco_is_open(p_scb) || !bta_ag_inband_enabled(p_scb) || + (p_scb->features & BTA_AG_FEAT_NOSCO)) { + bta_ag_send_ring(p_scb, (tBTA_AG_DATA *) p_result); + } else { + /* else open sco, send ring after sco opened */ + p_scb->post_sco = BTA_AG_POST_SCO_RING; + bta_ag_sco_open(p_scb, (tBTA_AG_DATA *) p_result); + } + } + break; + } + + case BTA_AG_IN_CALL_CONN_RES: + { + /* stop ring timer */ + bta_sys_stop_timer(&p_scb->act_timer); + /* if sco not opened and we need to open it, send indicators first + ** then open sco. */ + bta_ag_send_call_inds(p_scb, p_result->result); + + if (!(p_scb->features & BTA_AG_FEAT_NOSCO)) { + if (p_result->data.audio_handle == bta_ag_scb_to_idx(p_scb)) { + bta_ag_sco_open(p_scb, (tBTA_AG_DATA *) p_result); + } else if ((p_result->data.audio_handle == BTA_AG_HANDLE_NONE) && bta_ag_sco_is_open(p_scb)) { + bta_ag_sco_close(p_scb, (tBTA_AG_DATA *) p_result); + } + } + break; + } + + case BTA_AG_IN_CALL_HELD_RES: + /* stop ring timer */ + bta_sys_stop_timer(&p_scb->act_timer); + bta_ag_send_call_inds(p_scb, p_result->result); + break; + + case BTA_AG_OUT_CALL_ORIG_RES: + bta_ag_send_call_inds(p_scb, p_result->result); + if (p_result->data.audio_handle == bta_ag_scb_to_idx(p_scb) && !(p_scb->features & BTA_AG_FEAT_NOSCO)) { + bta_ag_sco_open(p_scb, (tBTA_AG_DATA *) p_result); + } + break; + + case BTA_AG_OUT_CALL_ALERT_RES: + /* send indicators */ + bta_ag_send_call_inds(p_scb, p_result->result); + if (p_result->data.audio_handle == bta_ag_scb_to_idx(p_scb) && !(p_scb->features & BTA_AG_FEAT_NOSCO)) { + bta_ag_sco_open(p_scb, (tBTA_AG_DATA *) p_result); + } + break; + + case BTA_AG_MULTI_CALL_RES: + /* open SCO at SLC for this three way call */ + APPL_TRACE_DEBUG("Headset Connected in three way call"); + if (!(p_scb->features & BTA_AG_FEAT_NOSCO)) { + if (p_result->data.audio_handle == bta_ag_scb_to_idx(p_scb)) { + bta_ag_sco_open(p_scb, (tBTA_AG_DATA *) p_result); + } else if (p_result->data.audio_handle == BTA_AG_HANDLE_NONE) { + bta_ag_sco_close(p_scb, (tBTA_AG_DATA *) p_result); + } + } + break; + + case BTA_AG_OUT_CALL_CONN_RES: + /* send indicators */ + bta_ag_send_call_inds(p_scb, p_result->result); + /* open or close sco */ + if (!(p_scb->features & BTA_AG_FEAT_NOSCO)) { + if (p_result->data.audio_handle == bta_ag_scb_to_idx(p_scb)) { + bta_ag_sco_open(p_scb, (tBTA_AG_DATA *) p_result); + } else if (p_result->data.audio_handle == BTA_AG_HANDLE_NONE) { + bta_ag_sco_close(p_scb, (tBTA_AG_DATA *) p_result); + } + } + break; + + case BTA_AG_CALL_CANCEL_RES: + /* send indicators */ + bta_ag_send_call_inds(p_scb, p_result->result); + break; + + case BTA_AG_END_CALL_RES: + /* stop ring timer */ + bta_sys_stop_timer(&p_scb->act_timer); + /* if sco open, close sco then send indicator values */ + if ((bta_ag_sco_is_open(p_scb) || bta_ag_sco_is_opening(p_scb)) && !(p_scb->features & BTA_AG_FEAT_NOSCO)) { + p_scb->post_sco = BTA_AG_POST_SCO_CALL_END; + bta_ag_sco_close(p_scb, (tBTA_AG_DATA *) p_result); + } else if (p_scb->post_sco == BTA_AG_POST_SCO_CALL_END_INCALL) { + /* sco closing for outgoing call because of incoming call */ + /* Send only callsetup end indicator after sco close */ + p_scb->post_sco = BTA_AG_POST_SCO_CALL_END; + } else { + bta_ag_send_call_inds(p_scb, p_result->result); + /* if av got suspended by this call, let it resume. */ + bta_sys_sco_unuse(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + } + break; + + case BTA_AG_INBAND_RING_RES: + p_scb->inband_enabled = p_result->data.state; + APPL_TRACE_DEBUG("inband_enabled set to %d", p_scb->inband_enabled); + bta_ag_send_result(p_scb, code, NULL, p_result->data.state); + break; + + case BTA_AG_CIND_RES: + { + /* store local values */ + p_scb->call_ind = p_result->data.str[0] - '0'; + p_scb->callsetup_ind = p_result->data.str[2] - '0'; + p_scb->service_ind = p_result->data.str[4] - '0'; + p_scb->signal_ind = p_result->data.str[6] - '0'; + p_scb->roam_ind = p_result->data.str[8] - '0'; + p_scb->battchg_ind = p_result->data.str[10] - '0'; + p_scb->callheld_ind = p_result->data.str[12] - '0'; + + APPL_TRACE_DEBUG("cind call:%d callsetup:%d", p_scb->call_ind, p_scb->callsetup_ind); + bta_ag_send_result(p_scb, code, p_result->data.str, 0); + bta_ag_send_ok(p_scb); + break; + } + + case BTA_AG_BINP_RES: // Not supported yet + case BTA_AG_CNUM_RES: + case BTA_AG_CLCC_RES: + case BTA_AG_COPS_RES: + { + if (p_result->data.ok_flag != BTA_AG_OK_ERROR) { + if (p_result->data.str[0] != 0) { + bta_ag_send_result(p_scb, code, p_result->data.str, 0); + bta_ag_send_ok(p_scb); + } + if (p_result->data.ok_flag == BTA_AG_OK_DONE) { + bta_ag_send_ok(p_scb); + } + } else { + bta_ag_send_error(p_scb, p_result->data.errcode); + } + break; + } + + case BTA_AG_UNAT_RES: + if (p_result->data.ok_flag != BTA_AG_OK_ERROR) { + if (p_result->data.str[0] != 0) { + bta_ag_process_unat_res(p_result->data.str); + APPL_TRACE_DEBUG("BTA_AG_RES :%s",p_result->data.str); + bta_ag_send_result(p_scb, code, p_result->data.str, 0); + } + + if (p_result->data.ok_flag == BTA_AG_OK_DONE) { + bta_ag_send_ok(p_scb); + } + } else { + bta_ag_send_error(p_scb, p_result->data.errcode); + } + break; + + case BTA_AG_CALL_WAIT_RES: + if (p_scb->ccwa_enabled) { + bta_ag_send_result(p_scb, code, p_result->data.str, 0); + } + bta_ag_send_call_inds(p_scb, p_result->result); + break; + + case BTA_AG_IND_RES: + bta_ag_send_ind(p_scb, p_result->data.ind.type, p_result->data.ind.value, FALSE); + break; + + case BTA_AG_BVRA_RES: + bta_ag_send_result(p_scb, code, NULL, p_result->data.state); + break; + + case BTA_AG_BTRH_RES: // Not supported yet + if (p_result->data.ok_flag != BTA_AG_OK_ERROR) { + /* Don't respond to read if not in response & hold state */ + if (p_result->data.num != BTA_AG_BTRH_NO_RESP) { + bta_ag_send_result(p_scb, code, NULL, p_result->data.num); + } + /* In case of a response to a read request we need to send OK */ + if (p_result->data.ok_flag == BTA_AG_OK_DONE) { + bta_ag_send_ok(p_scb); + } + } else { + bta_ag_send_error(p_scb, p_result->data.errcode); + } + break; + + default: + break; + } +} + + +/******************************************************************************* +** +** Function bta_ag_result +** +** Description Handle API result. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_result(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + if (p_scb->conn_service == BTA_AG_HSP) { + bta_ag_hsp_result(p_scb, &p_data->api_result); + } else { + bta_ag_hfp_result(p_scb, &p_data->api_result); + } +} + +/******************************************************************************* +** +** Function bta_ag_send_bcs +** +** Description Send +BCS AT command to peer. +** +** Returns void +** +*******************************************************************************/ +#if (BTM_WBS_INCLUDED == TRUE ) +void bta_ag_send_bcs(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UINT16 codec_uuid; + + if (p_scb->codec_fallback) { + codec_uuid = UUID_CODEC_CVSD; + } else { + switch(p_scb->sco_codec) { + case BTA_AG_CODEC_NONE: + codec_uuid = UUID_CODEC_CVSD; + break; + + case BTA_AG_CODEC_CVSD: + codec_uuid = UUID_CODEC_CVSD; + break; + + case BTA_AG_CODEC_MSBC: + codec_uuid = UUID_CODEC_MSBC; + break; + + default: + APPL_TRACE_ERROR("bta_ag_send_bcs: unknown codec %d, use CVSD", p_scb->sco_codec); + codec_uuid = UUID_CODEC_CVSD; + break; + } + } + /* send +BCS */ + APPL_TRACE_DEBUG("send +BCS codec is %d", codec_uuid); + bta_ag_send_result(p_scb, BTA_AG_RES_BCS, NULL, codec_uuid); +} +#endif + +/******************************************************************************* +** +** Function bta_ag_send_ring +** +** Description Send RING result code to peer. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_send_ring(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UNUSED(p_data); + +#if defined(BTA_AG_MULTI_RESULT_INCLUDED) && (BTA_AG_MULTI_RESULT_INCLUDED == TRUE) + tBTA_AG_MULTI_RESULT_CB m_res_cb; + if (p_scb->conn_service == BTA_AG_HFP && p_scb->clip_enabled && p_scb->clip[0] != 0) { + memset(&m_res_cb, NULL, sizeof(tBTA_AG_MULTI_RESULT_CB)); + m_res_cb.num_result = 2; + AT_SET_RES_CB(m_res_cb.res_cb[0], BTA_AG_RES_RING, NULL, 0) + AT_SET_RES_CB(m_res_cb.res_cb[1], BTA_AG_RES_CLIP, p_scb->clip, 0) + bta_ag_send_multi_result(p_scb, &m_res_cb); + } else { + /* send RING ONLY */ + bta_ag_send_result(p_scb, BTA_AG_RES_RING, NULL, 0); + } +#else + /* send RING */ + bta_ag_send_result(p_scb, BTA_AG_RES_RING, NULL, 0); + /* if HFP and clip enabled and clip data send CLIP */ + if (p_scb->conn_service == BTA_AG_HFP && p_scb->clip_enabled && p_scb->clip[0] != 0) { + bta_ag_send_result(p_scb, BTA_AG_RES_CLIP, p_scb->clip, 0); + } +#endif + /* restart ring timer */ + bta_sys_start_timer(&p_scb->act_timer, BTA_AG_RING_TOUT_EVT, BTA_AG_RING_TOUT); +} + +#endif /* #if (BTA_AG_INCLUDED == TRUE) */ \ No newline at end of file diff --git a/components/bt/host/bluedroid/bta/hf_ag/bta_ag_main.c b/components/bt/host/bluedroid/bta/hf_ag/bta_ag_main.c new file mode 100644 index 000000000..529047ac8 --- /dev/null +++ b/components/bt/host/bluedroid/bta/hf_ag/bta_ag_main.c @@ -0,0 +1,972 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the main implementation file for the BTA audio gateway. + * + ******************************************************************************/ + +#include +#include +#include "bta/bta_api.h" +#include "bta/bta_sys.h" +#include "bta/bta_ag_api.h" +#include "bta/bta_ag_co.h" +#include "bta/utl.h" +#include "common/bt_defs.h" +#include "common/bt_trace.h" +#include "osi/allocator.h" +#include "bta_ag_int.h" + +#if (BTA_AG_INCLUDED == TRUE) +/***************************************************************************** +** Constants and types +*****************************************************************************/ +#ifndef BTA_AG_DEBUG +#define BTA_AG_DEBUG TRUE +#endif + +#if BTA_AG_DEBUG == TRUE +static char *bta_ag_evt_str(UINT16 event, tBTA_AG_RES result); +static char *bta_ag_state_str(UINT8 state); +#endif + +/* state machine states */ +enum +{ + BTA_AG_INIT_ST, + BTA_AG_OPENING_ST, + BTA_AG_OPEN_ST, + BTA_AG_CLOSING_ST +}; + +/* state machine action enumeration list */ +enum +{ + BTA_AG_REGISTER, + BTA_AG_DEREGISTER, + BTA_AG_START_OPEN, + BTA_AG_RFC_DO_OPEN, + BTA_AG_RFC_DO_CLOSE, + BTA_AG_START_DEREG, + BTA_AG_START_CLOSE, + BTA_AG_RFC_OPEN, + BTA_AG_OPEN_FAIL, + BTA_AG_RFC_ACP_OPEN, + BTA_AG_RFC_CLOSE, + BTA_AG_RFC_FAIL, + BTA_AG_RFC_DATA, + BTA_AG_DISC_INT_RES, + BTA_AG_DISC_FAIL, + BTA_AG_DISC_ACP_RES, + BTA_AG_FREE_DB, + BTA_AG_SCO_CONN_OPEN, + BTA_AG_SCO_CONN_CLOSE, + BTA_AG_SCO_LISTEN, + BTA_AG_SCO_OPEN, + BTA_AG_SCO_CLOSE, + BTA_AG_SCO_SHUTDOWN, + BTA_AG_POST_SCO_OPEN, + BTA_AG_POST_SCO_CLOSE, + BTA_AG_SVC_CONN_OPEN, + BTA_AG_RESULT, + BTA_AG_SETCODEC, + BTA_AG_SEND_RING, + BTA_AG_CI_SCO_DATA, + BTA_AG_CI_RX_DATA, + BTA_AG_RCVD_SLC_READY, + BTA_AG_NUM_ACTIONS +}; + +#define BTA_AG_IGNORE BTA_AG_NUM_ACTIONS + +/* type for action functions */ +typedef void (*tBTA_AG_ACTION) (tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); + +/* action functions */ +const tBTA_AG_ACTION bta_ag_action[] = +{ + bta_ag_register, + bta_ag_deregister, + bta_ag_start_open, + bta_ag_rfc_do_open, + bta_ag_rfc_do_close, + bta_ag_start_dereg, + bta_ag_start_close, + bta_ag_rfc_open, + bta_ag_open_fail, + bta_ag_rfc_acp_open, + bta_ag_rfc_close, + bta_ag_rfc_fail, + bta_ag_rfc_data, + bta_ag_disc_int_res, + bta_ag_disc_fail, + bta_ag_disc_acp_res, + bta_ag_free_db, + bta_ag_sco_conn_open, + bta_ag_sco_conn_close, + bta_ag_sco_listen, + bta_ag_sco_open, + bta_ag_sco_close, + bta_ag_sco_shutdown, + bta_ag_post_sco_open, + bta_ag_post_sco_close, + bta_ag_svc_conn_open, + bta_ag_result, + bta_ag_setcodec, + bta_ag_send_ring, + bta_ag_ci_sco_data, + bta_ag_ci_rx_data, + bta_ag_rcvd_slc_ready +}; + +/* state table information */ +#define BTA_AG_ACTIONS 2 /* number of actions */ +#define BTA_AG_NEXT_STATE 2 /* position of next state */ +#define BTA_AG_NUM_COLS 3 /* number of columns in state tables */ + +/* state table for init state */ +const UINT8 bta_ag_st_init[][BTA_AG_NUM_COLS] = +{ +/* Event Action 1 Action 2 Next state */ +/* API_REGISTER_EVT */ {BTA_AG_REGISTER, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* API_DEREGISTER_EVT */ {BTA_AG_DEREGISTER, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* API_OPEN_EVT */ {BTA_AG_START_OPEN, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* API_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* API_AUDIO_OPEN_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* API_AUDIO_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* API_RESULT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* API_SETCODEC_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* RFC_OPEN_EVT */ {BTA_AG_RFC_ACP_OPEN, BTA_AG_SCO_LISTEN, BTA_AG_OPEN_ST}, +/* RFC_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* RFC_SRV_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* RFC_DATA_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* SCO_OPEN_EVT */ {BTA_AG_SCO_CONN_OPEN, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* SCO_CLOSE_EVT */ {BTA_AG_SCO_CONN_CLOSE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* DISC_ACP_RES_EVT */ {BTA_AG_FREE_DB, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* DISC_INT_RES_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* DISC_OK_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* DISC_FAIL_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* CI_RX_WRITE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* RING_TOUT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* SVC_TOUT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* CI_SCO_DATA_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* CI_SLC_READY_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST} +}; + +/* state table for opening state */ +const UINT8 bta_ag_st_opening[][BTA_AG_NUM_COLS] = +{ +/* Event Action 1 Action 2 Next state */ +/* API_REGISTER_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* API_DEREGISTER_EVT */ {BTA_AG_RFC_DO_CLOSE, BTA_AG_START_DEREG, BTA_AG_CLOSING_ST}, +/* API_OPEN_EVT */ {BTA_AG_OPEN_FAIL, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* API_CLOSE_EVT */ {BTA_AG_RFC_DO_CLOSE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* API_AUDIO_OPEN_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* API_AUDIO_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* API_RESULT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* API_SETCODEC_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* RFC_OPEN_EVT */ {BTA_AG_RFC_OPEN, BTA_AG_SCO_LISTEN, BTA_AG_OPEN_ST}, +/* RFC_CLOSE_EVT */ {BTA_AG_RFC_FAIL, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* RFC_SRV_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* RFC_DATA_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* SCO_OPEN_EVT */ {BTA_AG_SCO_CONN_OPEN, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* SCO_CLOSE_EVT */ {BTA_AG_SCO_CONN_CLOSE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* DISC_ACP_RES_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* DISC_INT_RES_EVT */ {BTA_AG_DISC_INT_RES, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* DISC_OK_EVT */ {BTA_AG_RFC_DO_OPEN, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* DISC_FAIL_EVT */ {BTA_AG_DISC_FAIL, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* CI_RX_WRITE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* RING_TOUT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* SVC_TOUT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* CI_SCO_DATA_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, +/* CI_SLC_READY_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST} +}; + +/* state table for open state */ +const UINT8 bta_ag_st_open[][BTA_AG_NUM_COLS] = +{ +/* Event Action 1 Action 2 Next state */ +/* API_REGISTER_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* API_DEREGISTER_EVT */ {BTA_AG_START_CLOSE, BTA_AG_START_DEREG, BTA_AG_CLOSING_ST}, +/* API_OPEN_EVT */ {BTA_AG_OPEN_FAIL, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* API_CLOSE_EVT */ {BTA_AG_START_CLOSE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* API_AUDIO_OPEN_EVT */ {BTA_AG_SCO_OPEN, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* API_AUDIO_CLOSE_EVT */ {BTA_AG_SCO_CLOSE, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* API_RESULT_EVT */ {BTA_AG_RESULT, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* API_SETCODEC_EVT */ {BTA_AG_SETCODEC, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* RFC_OPEN_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* RFC_CLOSE_EVT */ {BTA_AG_RFC_CLOSE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* RFC_SRV_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* RFC_DATA_EVT */ {BTA_AG_RFC_DATA, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* SCO_OPEN_EVT */ {BTA_AG_SCO_CONN_OPEN, BTA_AG_POST_SCO_OPEN, BTA_AG_OPEN_ST}, +/* SCO_CLOSE_EVT */ {BTA_AG_SCO_CONN_CLOSE, BTA_AG_POST_SCO_CLOSE, BTA_AG_OPEN_ST}, +/* DISC_ACP_RES_EVT */ {BTA_AG_DISC_ACP_RES, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* DISC_INT_RES_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* DISC_OK_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* DISC_FAIL_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* CI_RX_WRITE_EVT */ {BTA_AG_CI_RX_DATA, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* RING_TOUT_EVT */ {BTA_AG_SEND_RING, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* SVC_TOUT_EVT */ {BTA_AG_START_CLOSE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* CI_SCO_DATA_EVT */ {BTA_AG_CI_SCO_DATA, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, +/* CI_SLC_READY_EVT */ {BTA_AG_RCVD_SLC_READY, BTA_AG_IGNORE, BTA_AG_OPEN_ST} +}; + +/* state table for closing state */ +const UINT8 bta_ag_st_closing[][BTA_AG_NUM_COLS] = +{ +/* Event Action 1 Action 2 Next state */ +/* API_REGISTER_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* API_DEREGISTER_EVT */ {BTA_AG_START_DEREG, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* API_OPEN_EVT */ {BTA_AG_OPEN_FAIL, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* API_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* API_AUDIO_OPEN_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* API_AUDIO_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* API_RESULT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* API_SETCODEC_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* RFC_OPEN_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* RFC_CLOSE_EVT */ {BTA_AG_RFC_CLOSE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* RFC_SRV_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* RFC_DATA_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* SCO_OPEN_EVT */ {BTA_AG_SCO_CONN_OPEN, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* SCO_CLOSE_EVT */ {BTA_AG_SCO_CONN_CLOSE, BTA_AG_POST_SCO_CLOSE, BTA_AG_CLOSING_ST}, +/* DISC_ACP_RES_EVT */ {BTA_AG_FREE_DB, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* DISC_INT_RES_EVT */ {BTA_AG_FREE_DB, BTA_AG_IGNORE, BTA_AG_INIT_ST}, +/* DISC_OK_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* DISC_FAIL_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* CI_RX_WRITE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* RING_TOUT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* SVC_TOUT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* CI_SCO_DATA_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, +/* CI_SLC_READY_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST} +}; + +/* type for state table */ +typedef const UINT8 (*tBTA_AG_ST_TBL)[BTA_AG_NUM_COLS]; + +/* state table */ +const tBTA_AG_ST_TBL bta_ag_st_tbl[] = +{ + bta_ag_st_init, + bta_ag_st_opening, + bta_ag_st_open, + bta_ag_st_closing +}; + +/***************************************************************************** +** Global data +*****************************************************************************/ +const char *bta_ag_version = "1.5"; //"1.6" +/* AG control block */ +#if BTA_DYNAMIC_MEMORY == FALSE +tBTA_AG_CB bta_ag_cb; +#else +tBTA_AG_CB *bta_ag_cb_ptr; +#endif + +#if BTA_AG_DEBUG == TRUE +static char *bta_ag_evt_str(UINT16 event, tBTA_AG_RES result) +{ + switch (event) + { + case BTA_AG_API_REGISTER_EVT: + return "Register Request"; + case BTA_AG_API_DEREGISTER_EVT: + return "Deregister Request"; + case BTA_AG_API_OPEN_EVT: + return "Open SLC Request"; + case BTA_AG_API_CLOSE_EVT: + return "Close SLC Request"; + case BTA_AG_API_AUDIO_OPEN_EVT: + return "Open Audio Request"; + case BTA_AG_API_AUDIO_CLOSE_EVT: + return "Close Audio Request"; + case BTA_AG_API_RESULT_EVT: + switch (result) { + case BTA_AG_SPK_RES: return ("AT Result BTA_AG_SPK_RES"); + case BTA_AG_MIC_RES: return ("AT Result BTA_AG_MIC_RES"); + case BTA_AG_INBAND_RING_RES: return ("AT Result BTA_AG_INBAND_RING_RES"); + case BTA_AG_CIND_RES: return ("AT Result BTA_AG_CIND_RES"); + case BTA_AG_BINP_RES: return ("AT Result BTA_AG_BINP_RES"); + case BTA_AG_IND_RES: return ("AT Result BTA_AG_IND_RES"); + case BTA_AG_BVRA_RES: return ("AT Result BTA_AG_BVRA_RES"); + case BTA_AG_CNUM_RES: return ("AT Result BTA_AG_CNUM_RES"); + case BTA_AG_BTRH_RES: return ("AT Result BTA_AG_BTRH_RES"); + case BTA_AG_CLCC_RES: return ("AT Result BTA_AG_CLCC_RES"); + case BTA_AG_COPS_RES: return ("AT Result BTA_AG_COPS_RES"); + case BTA_AG_IN_CALL_RES: return ("AT Result BTA_AG_IN_CALL_RES"); + case BTA_AG_IN_CALL_CONN_RES: return ("AT Result BTA_AG_IN_CALL_CONN_RES"); + case BTA_AG_CALL_WAIT_RES: return ("AT Result BTA_AG_CALL_WAIT_RES"); + case BTA_AG_OUT_CALL_ORIG_RES: return ("AT Result BTA_AG_OUT_CALL_ORIG_RES"); + case BTA_AG_OUT_CALL_ALERT_RES: return ("AT Result BTA_AG_OUT_CALL_ALERT_RES"); + case BTA_AG_OUT_CALL_CONN_RES: return ("AT Result BTA_AG_OUT_CALL_CONN_RES"); + case BTA_AG_CALL_CANCEL_RES: return ("AT Result BTA_AG_CALL_CANCEL_RES"); + case BTA_AG_END_CALL_RES: return ("AT Result BTA_AG_END_CALL_RES"); + case BTA_AG_UNAT_RES: return ("AT Result BTA_AG_UNAT_RES"); + default: return ("Unknown AG Result"); + } + case BTA_AG_API_SETCODEC_EVT: + return "Set Codec Request"; + case BTA_AG_RFC_OPEN_EVT: + return "RFC Opened"; + case BTA_AG_RFC_CLOSE_EVT: + return "RFC Closed"; + case BTA_AG_RFC_SRV_CLOSE_EVT: + return "RFC SRV Closed"; + case BTA_AG_RFC_DATA_EVT: + return "RFC Data"; + case BTA_AG_SCO_OPEN_EVT: + return "Audio Opened"; + case BTA_AG_SCO_CLOSE_EVT: + return "Audio Closed"; + case BTA_AG_DISC_ACP_RES_EVT: + return "Discovery ACP Result"; + case BTA_AG_DISC_INT_RES_EVT: + return "Discovery INT Result"; + case BTA_AG_DISC_OK_EVT: + return "Discovery OK"; + case BTA_AG_DISC_FAIL_EVT: + return "Discovery Failed"; + case BTA_AG_CI_RX_WRITE_EVT: + return "CI RX Write"; + case BTA_AG_RING_TOUT_EVT: + return "Ring Timeout"; + case BTA_AG_SVC_TOUT_EVT: + return "Service Timeout"; + case BTA_AG_API_ENABLE_EVT: + return "Enable AG"; + case BTA_AG_API_DISABLE_EVT: + return "Disable AG"; + case BTA_AG_CI_SCO_DATA_EVT: + return "SCO data Callin"; + case BTA_AG_CI_SLC_READY_EVT: + return "SLC Ready Callin"; + default: + return "Unknown AG Event"; + } +} + +static char *bta_ag_state_str(UINT8 state) +{ + switch (state) { + case BTA_AG_INIT_ST: + return "Initial"; + case BTA_AG_OPENING_ST: + return "Opening"; + case BTA_AG_OPEN_ST: + return "Open"; + case BTA_AG_CLOSING_ST: + return "Closing"; + default: + return "Unknown AG State"; + } +} + +#endif + +/******************************************************************************* +** +** Function bta_ag_timer_cback +** +** Description AG timer callback. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_timer_cback(void *p) +{ + BT_HDR *p_buf; + TIMER_LIST_ENT *p_tle = (TIMER_LIST_ENT *) p; + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = p_tle->event; + p_buf->layer_specific = bta_ag_scb_to_idx((tBTA_AG_SCB *) p_tle->param); + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function bta_ag_scb_alloc +** +** Description Allocate an AG service control block. +** +** +** Returns pointer to the scb, or NULL if none could be allocated. +** +*******************************************************************************/ +static tBTA_AG_SCB *bta_ag_scb_alloc(void) +{ + tBTA_AG_SCB *p_scb = &bta_ag_cb.scb[0]; + int i; + + for (i = 0; i < BTA_AG_NUM_SCB; i++, p_scb++) { + if (!p_scb->in_use) { + /* initialize variables */ + p_scb->in_use = TRUE; + p_scb->sco_idx = BTM_INVALID_SCO_INDEX; +#if (BTM_WBS_INCLUDED == TRUE ) + p_scb->codec_updated = FALSE; +#endif + /* set up timers */ + p_scb->act_timer.param = (UINT32) p_scb; + p_scb->act_timer.p_cback = bta_ag_timer_cback; +#if (BTM_WBS_INCLUDED == TRUE) + /* set eSCO mSBC setting to T2 as the preferred */ + p_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T2; +#endif + APPL_TRACE_DEBUG("bta_ag_scb_alloc %d", bta_ag_scb_to_idx(p_scb)); + break; + } + } + + if (i == BTA_AG_NUM_SCB) { + p_scb = NULL; /* out of scbs */ + APPL_TRACE_WARNING("Out of ag scbs"); + } + return p_scb; +} + +/******************************************************************************* +** +** Function bta_ag_scb_dealloc +** +** Description Deallocate a service control block. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_scb_dealloc(tBTA_AG_SCB *p_scb) +{ + UINT8 idx; + BOOLEAN allocated = FALSE; + + APPL_TRACE_DEBUG("bta_ag_scb_dealloc %d", bta_ag_scb_to_idx(p_scb)); + /* stop timers */ + bta_sys_stop_timer(&p_scb->act_timer); +#if (BTM_WBS_INCLUDED == TRUE) + bta_sys_stop_timer(&p_scb->cn_timer); +#endif + bta_sys_stop_timer(&p_scb->colli_timer); + + /* initialize control block */ + memset(p_scb, 0, sizeof(tBTA_AG_SCB)); + p_scb->sco_idx = BTM_INVALID_SCO_INDEX; + /* If all scbs are deallocated, callback with disable event */ + if (!bta_sys_is_register (BTA_ID_AG)) { + for (idx = 0; idx < BTA_AG_NUM_SCB; idx++) { + if (bta_ag_cb.scb[idx].in_use) { + allocated = TRUE; + break; + } + } + if (!allocated) { + (*bta_ag_cb.p_cback)(BTA_AG_DISABLE_EVT, NULL); + } + } +} + +/******************************************************************************* +** +** Function bta_ag_scb_to_idx +** +** Description Given a pointer to an scb, return its index. +** +** +** Returns Index of scb. +** +*******************************************************************************/ +UINT16 bta_ag_scb_to_idx(tBTA_AG_SCB *p_scb) +{ + /* use array arithmetic to determine index */ + return ((UINT16) (p_scb - bta_ag_cb.scb)) + 1; +} + +/******************************************************************************* +** +** Function bta_ag_scb_by_idx +** +** Description Given an scb index return pointer to scb. +** +** +** Returns Pointer to scb or NULL if not allocated. +** +*******************************************************************************/ +tBTA_AG_SCB *bta_ag_scb_by_idx(UINT16 idx) +{ + tBTA_AG_SCB *p_scb; + /* verify index */ + if (idx > 0 && idx <= BTA_AG_NUM_SCB) { + p_scb = &bta_ag_cb.scb[idx - 1]; + if (!p_scb->in_use) { + p_scb = NULL; + APPL_TRACE_WARNING("ag scb idx %d not allocated", idx); + } + } else { + p_scb = NULL; + APPL_TRACE_DEBUG("ag scb idx %d out of range", idx); + } + return p_scb; +} + +/******************************************************************************* +** +** Function bta_ag_service_to_idx +** +** Description Given a BTA service mask convert to profile index. +** +** +** Returns Profile ndex of scb. +** +*******************************************************************************/ +UINT8 bta_ag_service_to_idx(tBTA_SERVICE_MASK services) +{ + if (services & BTA_HFP_SERVICE_MASK) { + return BTA_AG_HFP; + } else { + return BTA_AG_HSP; + } +} + +/******************************************************************************* +** +** Function bta_ag_idx_by_bdaddr +** +** Description Find SCB associated with peer BD address. +** +** +** Returns Index of SCB or zero if none found. +** +*******************************************************************************/ +UINT16 bta_ag_idx_by_bdaddr(BD_ADDR peer_addr) +{ + tBTA_AG_SCB *p_scb = &bta_ag_cb.scb[0]; + UINT16 i; + + if (peer_addr != NULL) { + for (i = 0; i < BTA_AG_NUM_SCB; i++, p_scb++) { + if (p_scb->in_use && !bdcmp(peer_addr, p_scb->peer_addr)) { + return (i + 1); + } + } + } + /* no scb found */ + APPL_TRACE_WARNING("No ag scb for peer addr"); + return 0; +} + +/******************************************************************************* +** +** Function bta_ag_other_scb_open +** +** Description Check whether any other scb is in open state. +** +** +** Returns TRUE if another scb is in open state, FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN bta_ag_other_scb_open(tBTA_AG_SCB *p_curr_scb) +{ + tBTA_AG_SCB *p_scb = &bta_ag_cb.scb[0]; + + for (int i = 0; i < BTA_AG_NUM_SCB; i++, p_scb++) { + if (p_scb->in_use && p_scb != p_curr_scb && p_scb->state == BTA_AG_OPEN_ST) { + return TRUE; + } + } + /* no other scb found */ + APPL_TRACE_DEBUG("No other ag scb open"); + return FALSE; +} + +/******************************************************************************* +** +** Function bta_ag_scb_open +** +** Description Check whether given scb is in open state. +** +** +** Returns TRUE if scb is in open state, FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN bta_ag_scb_open(tBTA_AG_SCB *p_curr_scb) +{ + if (p_curr_scb && p_curr_scb->in_use && p_curr_scb->state == BTA_AG_OPEN_ST) { + return TRUE; + } + return FALSE; +} + +/******************************************************************************* +** +** Function bta_ag_get_other_idle_scb +** +** Description Return other scb if it is in INIT st. +** +** +** Returns Pointer to other scb if INIT st, NULL otherwise. +** +*******************************************************************************/ +tBTA_AG_SCB *bta_ag_get_other_idle_scb (tBTA_AG_SCB *p_curr_scb) +{ + tBTA_AG_SCB *p_scb = &bta_ag_cb.scb[0]; + UINT8 xx; + + for (xx = 0; xx < BTA_AG_NUM_SCB; xx++, p_scb++) { + if (p_scb->in_use && (p_scb != p_curr_scb) && (p_scb->state == BTA_AG_INIT_ST)) { + return p_scb; + } + } + /* no other scb found */ + APPL_TRACE_DEBUG("bta_ag_get_other_idle_scb: No idle AG scb"); + return NULL; +} + +/******************************************************************************* +** +** Function bta_ag_colli_timer_cback +** +** Description AG connection collision timer callback +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_colli_timer_cback (TIMER_LIST_ENT *p_tle) +{ + tBTA_AG_SCB *p_scb; + + APPL_TRACE_DEBUG ("bta_ag_colli_timer_cback"); + if (p_tle) { + p_scb = (tBTA_AG_SCB *)p_tle->param; + if (p_scb) { + p_scb->colli_tmr_on = FALSE; + /* If the peer haven't opened AG connection */ + /* we will restart opening process. */ + bta_ag_resume_open (p_scb); + } + } +} + +/******************************************************************************* +** +** Function bta_ag_collision_cback +** +** Description Get notified about collision. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_collision_cback (tBTA_SYS_CONN_STATUS status, UINT8 id, UINT8 app_id, BD_ADDR peer_addr) +{ + UINT16 handle; + tBTA_AG_SCB *p_scb; + UNUSED(status); + UNUSED(app_id); + + /* Check if we have opening scb for the peer device. */ + handle = bta_ag_idx_by_bdaddr (peer_addr); + p_scb = bta_ag_scb_by_idx (handle); + + if (p_scb && (p_scb->state == BTA_AG_OPENING_ST)) { + if (id == BTA_ID_SYS) { + /* ACL collision */ + APPL_TRACE_WARNING ("AG found collision (ACL) ..."); + } else if (id == BTA_ID_AG) { + /* RFCOMM collision */ + APPL_TRACE_WARNING ("AG found collision (RFCOMM) ..."); + } else { + APPL_TRACE_WARNING ("AG found collision (\?\?\?) ..."); + } + p_scb->state = BTA_AG_INIT_ST; + /* Cancel SDP if it had been started. */ + if(p_scb->p_disc_db) { + (void)SDP_CancelServiceSearch (p_scb->p_disc_db); + bta_ag_free_db(p_scb, NULL); + } + /* reopen registered servers */ + /* Collision may be detected before or after we close servers. */ + if (bta_ag_is_server_closed (p_scb)) { + bta_ag_start_servers(p_scb, p_scb->reg_services); + } + /* Start timer to han */ + p_scb->colli_timer.p_cback = (TIMER_CBACK*)&bta_ag_colli_timer_cback; + p_scb->colli_timer.param = (INT32)p_scb; + bta_sys_start_timer(&p_scb->colli_timer, 0, BTA_AG_COLLISION_TIMER); + p_scb->colli_tmr_on = TRUE; + } +} + +/******************************************************************************* +** +** Function bta_ag_resume_open +** +** Description Resume opening process. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_resume_open (tBTA_AG_SCB *p_scb) +{ + if (p_scb) { + APPL_TRACE_DEBUG ("bta_ag_resume_open, Handle(%d)", bta_ag_scb_to_idx(p_scb)); + /* resume opening process. */ + if (p_scb->state == BTA_AG_INIT_ST) { + p_scb->state = BTA_AG_OPENING_ST; + bta_ag_start_open (p_scb, NULL); + } + } else { + APPL_TRACE_ERROR ("bta_ag_resume_open, Null p_scb"); + } +} + +/******************************************************************************* +** +** Function bta_ag_api_enable +** +** Description Handle an API enable event. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_api_enable(tBTA_AG_DATA *p_data) +{ + /* initialize control block */ + memset(&bta_ag_cb, 0, sizeof(tBTA_AG_CB)); + /* store callback function */ + bta_ag_cb.p_cback = p_data->api_enable.p_cback; + bta_ag_cb.parse_mode = p_data->api_enable.parse_mode; + /* check if mSBC support enabled */ + if (strcmp(bta_ag_version, "1.6") == 0) { + bta_ag_cb.msbc_enabled = TRUE; + bta_ag_cb.scb->negotiated_codec = BTM_SCO_CODEC_MSBC; + } else{ + bta_ag_cb.msbc_enabled = FALSE; + bta_ag_cb.scb->negotiated_codec = BTM_SCO_CODEC_CVSD; + } + + /* set deault setting for eSCO/SCO */ + BTM_WriteVoiceSettings(AG_VOICE_SETTINGS); + bta_sys_collision_register (BTA_ID_AG, bta_ag_collision_cback); + /* call callback with enable event */ + (*bta_ag_cb.p_cback)(BTA_AG_ENABLE_EVT, NULL); +} + +/******************************************************************************* +** +** Function bta_ag_api_disable +** +** Description Handle an API disable event. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_api_disable(tBTA_AG_DATA *p_data) +{ + /* deregister all scbs in use */ + tBTA_AG_SCB *p_scb = &bta_ag_cb.scb[0]; + BOOLEAN do_dereg = FALSE; + int i; + + if (!bta_sys_is_register (BTA_ID_AG)) { + APPL_TRACE_ERROR("BTA AG is already disabled, ignoring ..."); + return; + } + /* De-register with BTA system manager */ + bta_sys_deregister(BTA_ID_AG); + + for (i = 0; i < BTA_AG_NUM_SCB; i++, p_scb++) { + if (p_scb->in_use) { + bta_ag_sm_execute(p_scb, BTA_AG_API_DEREGISTER_EVT, p_data); + do_dereg = TRUE; + } + } + + if (!do_dereg) { + /* Done, send callback evt to app */ + (*bta_ag_cb.p_cback)(BTA_AG_DISABLE_EVT, NULL); + } + bta_sys_collision_register (BTA_ID_AG, NULL); +} + +/******************************************************************************* +** +** Function bta_ag_api_register +** +** Description Handle an API event registers a new service. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_api_register(tBTA_AG_DATA *p_data) +{ + tBTA_AG_SCB *p_scb; + tBTA_AG_REGISTER reg; + + /* allocate an scb */ + if ((p_scb = bta_ag_scb_alloc()) != NULL) { + APPL_TRACE_DEBUG("bta_ag_api_register: p_scb 0x%08x ", (unsigned int)p_scb); + bta_ag_sm_execute(p_scb, p_data->hdr.event, p_data); + } else { + reg.status = BTA_AG_FAIL_RESOURCES; + (*bta_ag_cb.p_cback)(BTA_AG_REGISTER_EVT, (tBTA_AG *) ®); + } +} + +/******************************************************************************* +** +** Function bta_ag_api_result +** +** Description Handle an API result event. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_api_result(tBTA_AG_DATA *p_data) +{ + tBTA_AG_SCB *p_scb; + int i; + + if (p_data->hdr.layer_specific != BTA_AG_HANDLE_ALL) { + if ((p_scb = bta_ag_scb_by_idx(p_data->hdr.layer_specific)) != NULL) { + APPL_TRACE_DEBUG("bta_ag_api_result: p_scb 0x%08x ", (unsigned int)p_scb); + bta_ag_sm_execute(p_scb, BTA_AG_API_RESULT_EVT, p_data); + } + } else { + for (i = 0, p_scb = &bta_ag_cb.scb[0]; i < BTA_AG_NUM_SCB; i++, p_scb++) { + if (p_scb->in_use && p_scb->svc_conn) { + APPL_TRACE_DEBUG("bta_ag_api_result p_scb 0x%08x ", (unsigned int)p_scb); + bta_ag_sm_execute(p_scb, BTA_AG_API_RESULT_EVT, p_data); + } + } + } +} + +/******************************************************************************* +** +** Function bta_ag_sm_execute +** +** Description State machine event handling function for AG +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sm_execute(tBTA_AG_SCB *p_scb, UINT16 event, tBTA_AG_DATA *p_data) +{ + tBTA_AG_ST_TBL state_table; + UINT8 action; + +#if BTA_AG_DEBUG == TRUE + UINT16 in_event = event; + UINT8 in_state = p_scb->state; + /* Ignore displaying of AT results when not connected (Ignored in state machine) */ + if (in_event != BTA_AG_API_RESULT_EVT || p_scb->state == BTA_AG_OPEN_ST) { + APPL_TRACE_EVENT("AG evt (hdl 0x%04x): State %d (%s), Event 0x%04x (%s)", + bta_ag_scb_to_idx(p_scb), + p_scb->state, bta_ag_state_str(p_scb->state), + event, bta_ag_evt_str(event, p_data->api_result.result)); + } +#else + APPL_TRACE_EVENT("AG evt (hdl 0x%04x): State %d, Event 0x%04x", + bta_ag_scb_to_idx(p_scb), p_scb->state, event); +#endif + event &= 0x00FF; + if (event >= (BTA_AG_MAX_EVT & 0x00FF)) { + APPL_TRACE_ERROR("AG evt out of range, ignoring..."); + return; + } + /* look up the state table for the current state */ + state_table = bta_ag_st_tbl[p_scb->state]; + /* set next state */ + p_scb->state = state_table[event][BTA_AG_NEXT_STATE]; + /* execute action functions */ + for (int i = 0; i < BTA_AG_ACTIONS; i++) { + if ((action = state_table[event][i]) != BTA_AG_IGNORE) { + (*bta_ag_action[action])(p_scb, p_data); + } else { + break; + } + } +#if BTA_AG_DEBUG == TRUE + if (p_scb->state != in_state) { + APPL_TRACE_EVENT("BTA AG State Change: [%s] -> [%s] after Event [%s]", + bta_ag_state_str(in_state), + bta_ag_state_str(p_scb->state), + bta_ag_evt_str(in_event, p_data->api_result.result)); + } +#endif +} + +/******************************************************************************* +** +** Function bta_ag_hdl_event +** +** Description Data gateway main event handling function. +** +** +** Returns BOOLEAN +** +*******************************************************************************/ +BOOLEAN bta_ag_hdl_event(BT_HDR *p_msg) +{ + tBTA_AG_SCB *p_scb; + APPL_TRACE_DEBUG("bta_ag_hdl_event: Event 0x%04x ", p_msg->event); + + switch (p_msg->event) { + /* handle enable event */ + case BTA_AG_API_ENABLE_EVT: + bta_ag_api_enable((tBTA_AG_DATA *) p_msg); + break; + + /* handle disable event */ + case BTA_AG_API_DISABLE_EVT: + bta_ag_api_disable((tBTA_AG_DATA *) p_msg); + break; + + /* handle register event */ + case BTA_AG_API_REGISTER_EVT: + bta_ag_api_register((tBTA_AG_DATA *) p_msg); + break; + + /* handle result event */ + case BTA_AG_API_RESULT_EVT: + bta_ag_api_result((tBTA_AG_DATA *) p_msg); + break; + + /* all others reference scb by handle */ + default: + if ((p_scb = bta_ag_scb_by_idx(p_msg->layer_specific)) != NULL) { + APPL_TRACE_DEBUG("bta_ag_hdl_event: p_scb 0x%08x ", (unsigned int)p_scb); + bta_ag_sm_execute(p_scb, p_msg->event, (tBTA_AG_DATA *) p_msg); + } + break; + } + return TRUE; +} + +#endif /* #if (BTA_AG_INCLUDED == TRUE) */ \ No newline at end of file diff --git a/components/bt/host/bluedroid/bta/hf_ag/bta_ag_rfc.c b/components/bt/host/bluedroid/bta/hf_ag/bta_ag_rfc.c new file mode 100644 index 000000000..b6288da9f --- /dev/null +++ b/components/bt/host/bluedroid/bta/hf_ag/bta_ag_rfc.c @@ -0,0 +1,403 @@ +/****************************************************************************** + * + * Copyright (C) 2004-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the audio gateway functions controlling the RFCOMM + * connections. + * + ******************************************************************************/ + + +#include +#include "bta_ag_int.h" +#include "bta/bta_api.h" +#include "bta/bta_sys.h" +#include "bta/bta_ag_api.h" +#include "bta/bta_ag_co.h" +#include "bta/utl.h" +#include "stack/btm_api.h" +#include "stack/port_api.h" +#include "stack/rfcdefs.h" +#include "common/bt_trace.h" +#include "osi/allocator.h" + + +#if (BTA_AG_INCLUDED == TRUE) +/* Event mask for RfCOMM port callback */ +#define BTA_AG_PORT_EV_MASK PORT_EV_RXCHAR + +/* each scb has its own rfcomm callbacks */ +void bta_ag_port_cback_1(UINT32 code, UINT16 port_handle); +void bta_ag_port_cback_2(UINT32 code, UINT16 port_handle); +void bta_ag_port_cback_3(UINT32 code, UINT16 port_handle); + +void bta_ag_mgmt_cback_1(UINT32 code, UINT16 port_handle); +void bta_ag_mgmt_cback_2(UINT32 code, UINT16 port_handle); +void bta_ag_mgmt_cback_3(UINT32 code, UINT16 port_handle); + +int bta_ag_data_cback_1(UINT16 port_handle, void *p_data, UINT16 len); +int bta_ag_data_cback_2(UINT16 port_handle, void *p_data, UINT16 len); +int bta_ag_data_cback_3(UINT16 port_handle, void *p_data, UINT16 len); + +/* rfcomm callback function tables */ +typedef tPORT_CALLBACK *tBTA_AG_PORT_CBACK; +const tBTA_AG_PORT_CBACK bta_ag_port_cback_tbl[] = +{ + bta_ag_port_cback_1, + bta_ag_port_cback_2, + bta_ag_port_cback_3 +}; + +const tBTA_AG_PORT_CBACK bta_ag_mgmt_cback_tbl[] = +{ + bta_ag_mgmt_cback_1, + bta_ag_mgmt_cback_2, + bta_ag_mgmt_cback_3 +}; + +typedef tPORT_DATA_CALLBACK *tBTA_AG_DATA_CBACK; +const tBTA_AG_DATA_CBACK bta_ag_data_cback_tbl[] = +{ + bta_ag_data_cback_1, + bta_ag_data_cback_2, + bta_ag_data_cback_3 +}; + +/******************************************************************************* +** +** Function bta_ag_port_cback +** +** Description RFCOMM Port callback +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_port_cback(UINT32 code, UINT16 port_handle, UINT16 handle) +{ + BT_HDR *p_buf; + tBTA_AG_SCB *p_scb; + UNUSED(code); + + if ((p_scb = bta_ag_scb_by_idx(handle)) != NULL) { + /* ignore port events for port handles other than connected handle */ + if (port_handle != p_scb->conn_handle) { + APPL_TRACE_DEBUG("ag_port_cback ignoring handle:%d conn_handle = %d other handle = %d", + port_handle, p_scb->conn_handle, handle); + return; + } + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_AG_RFC_DATA_EVT; + p_buf->layer_specific = handle; + bta_sys_sendmsg(p_buf); + } + } +} + +/******************************************************************************* +** +** Function bta_ag_mgmt_cback +** +** Description RFCOMM management callback +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_mgmt_cback(UINT32 code, UINT16 port_handle, UINT16 handle) +{ + tBTA_AG_RFC *p_buf; + tBTA_AG_SCB *p_scb; + UINT16 event; + UINT8 i; + BOOLEAN found_handle = FALSE; + + APPL_TRACE_DEBUG("ag_mgmt_cback : code = %d, port_handle = %d, handle = %d", + code, port_handle, handle); + + if ((p_scb = bta_ag_scb_by_idx(handle)) != NULL) { + /* ignore close event for port handles other than connected handle */ + if ((code != PORT_SUCCESS) && (port_handle != p_scb->conn_handle)) { + APPL_TRACE_DEBUG("ag_mgmt_cback ignoring handle:%d", port_handle); + return; + } + + if (code == PORT_SUCCESS) { + if (p_scb->conn_handle) { + /* Outgoing connection */ + if (port_handle == p_scb->conn_handle) + found_handle = TRUE; + } else { + /* Incoming connection */ + for (i = 0; i < BTA_AG_NUM_IDX; i++) { + if (port_handle == p_scb->serv_handle[i]) + found_handle = TRUE; + } + } + + if (!found_handle) { + APPL_TRACE_ERROR ("bta_ag_mgmt_cback: PORT_SUCCESS, ignoring handle = %d", port_handle); + return; + } + event = BTA_AG_RFC_OPEN_EVT; + } else if (port_handle == p_scb->conn_handle) { + /* distinguish server close events */ + event = BTA_AG_RFC_CLOSE_EVT; + } else { + event = BTA_AG_RFC_SRV_CLOSE_EVT; + } + + if ((p_buf = (tBTA_AG_RFC *) osi_malloc(sizeof(tBTA_AG_RFC))) != NULL) { + p_buf->hdr.event = event; + p_buf->hdr.layer_specific = handle; + p_buf->port_handle = port_handle; + bta_sys_sendmsg(p_buf); + } + } +} + +/******************************************************************************* +** +** Function bta_ag_data_cback +** +** Description RFCOMM data callback +** +** +** Returns void +** +*******************************************************************************/ +static int bta_ag_data_cback(UINT16 port_handle, void *p_data, UINT16 len, UINT16 handle) +{ + UNUSED(port_handle); + + /* call data call-out directly */ + bta_ag_co_tx_write(handle, (UINT8 *) p_data, len); + return 0; +} + +/******************************************************************************* +** +** Function bta_ag_port_cback_1 to 3 +** bta_ag_mgmt_cback_1 to 3 +** +** Description RFCOMM callback functions. This is an easy way to +** distinguish scb from the callback. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_mgmt_cback_1(UINT32 code, UINT16 handle) {bta_ag_mgmt_cback(code, handle, 1);} +void bta_ag_mgmt_cback_2(UINT32 code, UINT16 handle) {bta_ag_mgmt_cback(code, handle, 2);} +void bta_ag_mgmt_cback_3(UINT32 code, UINT16 handle) {bta_ag_mgmt_cback(code, handle, 3);} +void bta_ag_port_cback_1(UINT32 code, UINT16 handle) {bta_ag_port_cback(code, handle, 1);} +void bta_ag_port_cback_2(UINT32 code, UINT16 handle) {bta_ag_port_cback(code, handle, 2);} +void bta_ag_port_cback_3(UINT32 code, UINT16 handle) {bta_ag_port_cback(code, handle, 3);} + +/******************************************************************************* +** +** Function bta_ag_data_cback_1 to 3 +** +** Description RFCOMM data callback functions. This is an easy way to +** distinguish scb from the callback. +** +** +** Returns void +** +*******************************************************************************/ +int bta_ag_data_cback_1(UINT16 port_handle, void *p_data, UINT16 len) +{ + return bta_ag_data_cback(port_handle, p_data, len, 1); +} +int bta_ag_data_cback_2(UINT16 port_handle, void *p_data, UINT16 len) +{ + return bta_ag_data_cback(port_handle, p_data, len, 2); +} +int bta_ag_data_cback_3(UINT16 port_handle, void *p_data, UINT16 len) +{ + return bta_ag_data_cback(port_handle, p_data, len, 3); +} + +/******************************************************************************* +** +** Function bta_ag_setup_port +** +** Description Setup RFCOMM port for use by AG. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_setup_port(tBTA_AG_SCB *p_scb, UINT16 handle) +{ + UINT16 i = bta_ag_scb_to_idx(p_scb) - 1; + + /* set up data callback if using pass through mode */ + if (bta_ag_cb.parse_mode == BTA_AG_PASS_THROUGH) { + PORT_SetDataCallback(handle, bta_ag_data_cback_tbl[i]); + } + PORT_SetEventMask(handle, BTA_AG_PORT_EV_MASK); + PORT_SetEventCallback(handle, bta_ag_port_cback_tbl[i]); +} + +/******************************************************************************* +** +** Function bta_ag_start_servers +** +** Description Setup RFCOMM servers for use by AG. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_start_servers(tBTA_AG_SCB *p_scb, tBTA_SERVICE_MASK services) +{ + int bta_ag_port_status; + + services >>= BTA_HSP_SERVICE_ID; + for (int i = 0; i < BTA_AG_NUM_IDX && services != 0; i++, services >>= 1) { + /* if service is set in mask */ + if (services & 1) { + BTM_SetSecurityLevel(FALSE, "", bta_ag_sec_id[i], p_scb->serv_sec_mask, + BT_PSM_RFCOMM, BTM_SEC_PROTO_RFCOMM, bta_ag_cb.profile[i].scn); + + bta_ag_port_status = RFCOMM_CreateConnection(bta_ag_uuid[i], bta_ag_cb.profile[i].scn, + TRUE, BTA_AG_MTU, (UINT8 *) bd_addr_any, &(p_scb->serv_handle[i]), + bta_ag_mgmt_cback_tbl[bta_ag_scb_to_idx(p_scb) - 1]); + + if( bta_ag_port_status == PORT_SUCCESS ) { + bta_ag_setup_port(p_scb, p_scb->serv_handle[i]); + } else { + /* TODO: CR#137125 to handle to error properly */ + APPL_TRACE_DEBUG("bta_ag_start_servers: RFCOMM_CreateConnection returned error:%d", bta_ag_port_status); + } + } + } +} + +/******************************************************************************* +** +** Function bta_ag_close_servers +** +** Description Close RFCOMM servers port for use by AG. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_close_servers(tBTA_AG_SCB *p_scb, tBTA_SERVICE_MASK services) +{ + int i; + + services >>= BTA_HSP_SERVICE_ID; + for (i = 0; i < BTA_AG_NUM_IDX && services != 0; i++, services >>= 1) { + /* if service is set in mask */ + if (services & 1) { + RFCOMM_RemoveServer(p_scb->serv_handle[i]); + p_scb->serv_handle[i] = 0; + } + } +} + +/******************************************************************************* +** +** Function bta_ag_is_server_closed +** +** Description Returns TRUE if all servers are closed. +** +** +** Returns TRUE if all servers are closed, FALSE otherwise +** +*******************************************************************************/ +BOOLEAN bta_ag_is_server_closed (tBTA_AG_SCB *p_scb) +{ + UINT8 i; + BOOLEAN is_closed = TRUE; + + for (i = 0; i < BTA_AG_NUM_IDX; i++) { + if (p_scb->serv_handle[i] != 0) + is_closed = FALSE; + } + return is_closed; +} + +/******************************************************************************* +** +** Function bta_ag_rfc_do_open +** +** Description Open an RFCOMM connection to the peer device. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_rfc_do_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + BTM_SetSecurityLevel(TRUE, "", bta_ag_sec_id[p_scb->conn_service], + p_scb->cli_sec_mask, BT_PSM_RFCOMM, BTM_SEC_PROTO_RFCOMM, p_scb->peer_scn); + + if (RFCOMM_CreateConnection(bta_ag_uuid[p_scb->conn_service], p_scb->peer_scn, + FALSE, BTA_AG_MTU, p_scb->peer_addr, &(p_scb->conn_handle), + bta_ag_mgmt_cback_tbl[bta_ag_scb_to_idx(p_scb) - 1]) == PORT_SUCCESS) { + bta_ag_setup_port(p_scb, p_scb->conn_handle); + APPL_TRACE_DEBUG("bta_ag_rfc_do_open : conn_handle = %d", p_scb->conn_handle); + } else { + /* RFCOMM create connection failed; send ourselves RFCOMM close event */ + bta_ag_sm_execute(p_scb, BTA_AG_RFC_CLOSE_EVT, p_data); + } +} + +/******************************************************************************* +** +** Function bta_ag_rfc_do_close +** +** Description Close RFCOMM connection. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_rfc_do_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + tBTA_AG_RFC *p_buf; + UNUSED(p_data); + + if (p_scb->conn_handle) { + RFCOMM_RemoveConnection(p_scb->conn_handle); + } else { + /* Close API was called while AG is in Opening state. */ + /* Need to trigger the state machine to send callback to the app */ + /* and move back to INIT state. */ + if ((p_buf = (tBTA_AG_RFC *) osi_malloc(sizeof(tBTA_AG_RFC))) != NULL) { + p_buf->hdr.event = BTA_AG_RFC_CLOSE_EVT; + p_buf->hdr.layer_specific = bta_ag_scb_to_idx(p_scb); + bta_sys_sendmsg(p_buf); + } + /* Cancel SDP if it had been started. */ + /* + if(p_scb->p_disc_db) + { + (void)SDP_CancelServiceSearch (p_scb->p_disc_db); + } + */ + } +} + +#endif /* #if (BTA_AG_INCLUDED == TRUE) */ \ No newline at end of file diff --git a/components/bt/host/bluedroid/bta/hf_ag/bta_ag_sco.c b/components/bt/host/bluedroid/bta/hf_ag/bta_ag_sco.c new file mode 100644 index 000000000..88e089f80 --- /dev/null +++ b/components/bt/host/bluedroid/bta/hf_ag/bta_ag_sco.c @@ -0,0 +1,1767 @@ +/****************************************************************************** + * + * Copyright (C) 2004-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions for managing the SCO connection used in AG. + * + ******************************************************************************/ +#include +#include "bta_ag_int.h" +#include "bta/bta_api.h" +#include "bta/bta_ag_api.h" +#include "bta/bta_ag_co.h" +#include "bta/bta_hfp_defs.h" + +#if (BTM_SCO_HCI_INCLUDED == TRUE ) +#include "bta/bta_dm_co.h" +#include "hci/hci_audio.h" +#endif + +#include "bta/utl.h" +#include "stack/btm_api.h" +#include "common/bt_trace.h" +#include "osi/allocator.h" + +#if (BTA_AG_INCLUDED == TRUE) + +#ifndef BTA_AG_CODEC_NEGO_TIMEOUT +#define BTA_AG_CODEC_NEGO_TIMEOUT 3000 +#endif + +static char *bta_ag_sco_evt_str(UINT8 event); +static char *bta_ag_sco_state_str(UINT8 state); + +#define BTA_AG_NO_EDR_ESCO (BTM_SCO_PKT_TYPES_MASK_NO_2_EV3 | \ + BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 | \ + BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 | \ + BTM_SCO_PKT_TYPES_MASK_NO_3_EV5) + +/* sco events */ +enum +{ + BTA_AG_SCO_LISTEN_E, /* listen request */ + BTA_AG_SCO_OPEN_E, /* open request */ + BTA_AG_SCO_XFER_E, /* transfer request */ +#if (BTM_WBS_INCLUDED == TRUE ) + BTA_AG_SCO_CN_DONE_E, /* codec negotiation done */ + BTA_AG_SCO_REOPEN_E, /* Retry with other codec when failed */ +#endif + BTA_AG_SCO_CLOSE_E, /* close request */ + BTA_AG_SCO_SHUTDOWN_E, /* shutdown request */ + BTA_AG_SCO_CONN_OPEN_E, /* sco open */ + BTA_AG_SCO_CONN_CLOSE_E, /* sco closed */ +#if (BTM_SCO_HCI_INCLUDED == TRUE) + BTA_AG_SCO_CI_DATA_E /* SCO data ready */ +#endif +}; + +#if (BTM_WBS_INCLUDED == TRUE ) +#define BTA_AG_NUM_CODECS 3 +#define BTA_AG_ESCO_SETTING_IDX_CVSD 0 /* eSCO setting for CVSD */ +#define BTA_AG_ESCO_SETTING_IDX_T1 1 /* eSCO setting for mSBC T1 */ +#define BTA_AG_ESCO_SETTING_IDX_T2 2 /* eSCO setting for mSBC T2 */ + +static const tBTM_ESCO_PARAMS bta_ag_esco_params[BTA_AG_NUM_CODECS] = +{ + /* CVSD */ + { + BTM_64KBITS_RATE, /* TX Bandwidth (64 kbits/sec) */ + BTM_64KBITS_RATE, /* RX Bandwidth (64 kbits/sec) */ + 0x000a, /* 10 ms (HS/HF can use EV3, 2-EV3, 3-EV3) */ + BTM_VOICE_SETTING_CVSD, /* Inp Linear, Air CVSD, 2s Comp, 16bit */ + (BTM_SCO_PKT_TYPES_MASK_HV1 + /* Packet Types */ + BTM_SCO_PKT_TYPES_MASK_HV2 + + BTM_SCO_PKT_TYPES_MASK_HV3 + + BTM_SCO_PKT_TYPES_MASK_EV3 + + BTM_SCO_PKT_TYPES_MASK_EV4 + + BTM_SCO_PKT_TYPES_MASK_EV5 + + BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 + + BTM_SCO_PKT_TYPES_MASK_NO_3_EV5), + BTM_ESCO_RETRANS_POWER /* Retransmission effort */ + }, + /* mSBC T1 */ + { + BTM_64KBITS_RATE, /* TX Bandwidth (64 kbits/sec), 8000 */ + BTM_64KBITS_RATE, /* RX Bandwidth (64 kbits/sec), 8000 */ + 8, /* 8 ms */ + BTM_VOICE_SETTING_TRANS, /* Inp Linear, Transparent, 2s Comp, 16bit */ + (BTM_SCO_PKT_TYPES_MASK_EV3 | /* Packet Types : EV3 + NO_2_EV3 */ + BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 | + BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 | + BTM_SCO_PKT_TYPES_MASK_NO_3_EV5 | + BTM_SCO_PKT_TYPES_MASK_NO_2_EV3 ), + BTM_ESCO_RETRANS_QUALITY /* Retransmission effort */ + }, + /* mSBC T2*/ + { + BTM_64KBITS_RATE, /* TX Bandwidth (64 kbits/sec), 8000 */ + BTM_64KBITS_RATE, /* RX Bandwidth (64 kbits/sec), 8000 */ + 13, /* 13 ms */ + BTM_VOICE_SETTING_TRANS, /* Inp Linear, Transparent, 2s Comp, 16bit */ + (BTM_SCO_PKT_TYPES_MASK_EV3 | /* Packet Types : EV3 + 2-EV3 */ + BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 | + BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 | + BTM_SCO_PKT_TYPES_MASK_NO_3_EV5), + BTM_ESCO_RETRANS_QUALITY /* Retransmission effort */ + } +}; +#else +/* WBS not included, CVSD by default */ +static const tBTM_ESCO_PARAMS bta_ag_esco_params = +{ + BTM_64KBITS_RATE, /* TX Bandwidth (64 kbits/sec) */ + BTM_64KBITS_RATE, /* RX Bandwidth (64 kbits/sec) */ + 0x000a, /* 10 ms (HS/HF can use EV3, 2-EV3, 3-EV3) */ + 0x0060, /* Inp Linear, Air CVSD, 2s Comp, 16bit */ + (BTM_SCO_PKT_TYPES_MASK_HV1 + /* Packet Types */ + BTM_SCO_PKT_TYPES_MASK_HV2 + + BTM_SCO_PKT_TYPES_MASK_HV3 + + BTM_SCO_PKT_TYPES_MASK_EV3 + + BTM_SCO_PKT_TYPES_MASK_EV4 + + BTM_SCO_PKT_TYPES_MASK_EV5 + + BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 + + BTM_SCO_PKT_TYPES_MASK_NO_3_EV5), + BTM_ESCO_RETRANS_POWER /* Retransmission effort */ +}; +#endif + +/******************************************************************************* +** +** Function bta_ag_sco_conn_cback +** +** Description BTM SCO connection callback. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_sco_conn_cback(UINT16 sco_idx) +{ + UINT16 handle; + BT_HDR *p_buf; + tBTA_AG_SCB *p_scb = &bta_ag_cb.scb[0]; + tBTM_ESCO_DATA sco_data; + + APPL_TRACE_DEBUG("%s %d", __FUNCTION__, sco_idx); + + /* match callback to scb; first check current sco scb */ + if (bta_ag_cb.sco.p_curr_scb != NULL && bta_ag_cb.sco.p_curr_scb->in_use) + { + handle = bta_ag_scb_to_idx(bta_ag_cb.sco.p_curr_scb); + } + /* then check for scb connected to this peer */ + else + { + /* Check if SLC is up */ + handle = bta_ag_idx_by_bdaddr(BTM_ReadScoBdAddr(sco_idx)); + p_scb = bta_ag_scb_by_idx(handle); + if(p_scb && !p_scb->svc_conn) + handle = 0; + } + + if (handle != 0) + { + BTM_ReadEScoLinkParms(sco_idx, &sco_data); + + p_scb->link_type = sco_data.link_type; + p_scb->tx_interval = sco_data.tx_interval; + p_scb->retrans_window = sco_data.retrans_window; + p_scb->air_mode = sco_data.air_mode; + + if (sco_data.air_mode == BTM_SCO_AIR_MODE_CVSD) + { + p_scb->out_pkt_len = sco_data.tx_pkt_len * 2; + p_scb->in_pkt_len = sco_data.rx_pkt_len * 2; + } + else { + p_scb->out_pkt_len = sco_data.tx_pkt_len; + p_scb->in_pkt_len = sco_data.rx_pkt_len; + } + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) + { + p_buf->event = BTA_AG_SCO_OPEN_EVT; + p_buf->layer_specific = handle; + bta_sys_sendmsg(p_buf); + } + } + /* no match found; disconnect sco, init sco variables */ + else + { + bta_ag_cb.sco.p_curr_scb = NULL; + bta_ag_cb.sco.state = BTA_AG_SCO_SHUTDOWN_ST; + BTM_RemoveSco(sco_idx); + } +} + +/******************************************************************************* +** +** Function bta_ag_sco_disc_cback +** +** Description BTM SCO disconnection callback. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_sco_disc_cback(UINT16 sco_idx) +{ + BT_HDR *p_buf; + UINT16 handle = 0; + + APPL_TRACE_DEBUG ("bta_ag_sco_disc_cback(): sco_idx: 0x%x p_cur_scb: 0x%08x sco.state: %d", (unsigned int)sco_idx, (unsigned int)bta_ag_cb.sco.p_curr_scb, (unsigned int)bta_ag_cb.sco.state); + + APPL_TRACE_DEBUG ("bta_ag_sco_disc_cback(): scb[0] addr: 0x%08x in_use: %u sco_idx: 0x%x sco state: %u", + (unsigned int) &bta_ag_cb.scb[0], (unsigned int)bta_ag_cb.scb[0].in_use, (unsigned int)bta_ag_cb.scb[0].sco_idx, (unsigned int)bta_ag_cb.scb[0].state); + APPL_TRACE_DEBUG ("bta_ag_sco_disc_cback(): scb[1] addr: 0x%08x in_use: %u sco_idx: 0x%x sco state: %u", + (unsigned int) &bta_ag_cb.scb[1], (unsigned int) bta_ag_cb.scb[1].in_use, (unsigned int) bta_ag_cb.scb[1].sco_idx, (unsigned int) bta_ag_cb.scb[1].state); + + /* match callback to scb */ + if (bta_ag_cb.sco.p_curr_scb != NULL && bta_ag_cb.sco.p_curr_scb->in_use) + { + /* We only care about callbacks for the active SCO */ + if (bta_ag_cb.sco.p_curr_scb->sco_idx != sco_idx) + { + if (bta_ag_cb.sco.p_curr_scb->sco_idx != 0xFFFF) + return; + } + handle = bta_ag_scb_to_idx(bta_ag_cb.sco.p_curr_scb); + } + + if (handle != 0) + { +#if (BTM_SCO_HCI_INCLUDED == TRUE ) + tBTM_STATUS status = BTM_ConfigScoPath(BTM_SCO_ROUTE_PCM, NULL, NULL, TRUE); + APPL_TRACE_DEBUG("bta_ag_sco_disc_cback sco close config status = %d", status); + /* SCO clean up here */ + bta_ag_sco_co_close(); +#endif + +#if (BTM_WBS_INCLUDED == TRUE ) + /* Restore settings */ + if(bta_ag_cb.sco.p_curr_scb->inuse_codec == BTA_AG_CODEC_MSBC) + { + /* set_sco_codec(BTM_SCO_CODEC_NONE); we should get a close */ + BTM_WriteVoiceSettings (BTM_VOICE_SETTING_CVSD); + + /* If SCO open was initiated by AG and failed for mSBC, then attempt + mSBC with T1 settings i.e. 'Safe Settings'. If this fails, then switch to CVSD */ + if (bta_ag_sco_is_opening (bta_ag_cb.sco.p_curr_scb)) + { + if (bta_ag_cb.sco.p_curr_scb->codec_msbc_settings == BTA_AG_SCO_MSBC_SETTINGS_T2) + { + APPL_TRACE_DEBUG("Fallback to mSBC T1 settings"); + bta_ag_cb.sco.p_curr_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T1; + } + else + { + APPL_TRACE_DEBUG("Fallback to CVSD settings"); + bta_ag_cb.sco.p_curr_scb->codec_fallback = TRUE; + } + } + } + + bta_ag_cb.sco.p_curr_scb->inuse_codec = BTA_AG_CODEC_NONE; +#endif + + if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) + { + p_buf->event = BTA_AG_SCO_CLOSE_EVT; + p_buf->layer_specific = handle; + bta_sys_sendmsg(p_buf); + } + } + /* no match found */ + else + { + APPL_TRACE_DEBUG("no scb for ag_sco_disc_cback"); + + /* sco could be closed after scb dealloc'ed */ + if (bta_ag_cb.sco.p_curr_scb != NULL) + { + bta_ag_cb.sco.p_curr_scb->sco_idx = BTM_INVALID_SCO_INDEX; + bta_ag_cb.sco.p_curr_scb = NULL; + bta_ag_cb.sco.state = BTA_AG_SCO_SHUTDOWN_ST; + } + } +} + + +#if (BTM_SCO_HCI_INCLUDED == TRUE ) +/******************************************************************************* +** +** Function bta_ag_sco_read_cback +** +** Description Callback function is the callback function for incoming +** SCO data over HCI. +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_sco_read_cback(UINT16 sco_inx, BT_HDR *p_data, tBTM_SCO_DATA_FLAG status) +{ + if (status != BTM_SCO_DATA_CORRECT) + { + APPL_TRACE_DEBUG("bta_ag_sco_read_cback: status(%d)", status); + } + + /* Callout function must free the data. */ + bta_ag_sco_co_in_data(p_data, status); +} +#endif +/******************************************************************************* +** +** Function bta_ag_remove_sco +** +** Description Removes the specified SCO from the system. +** If only_active is TRUE, then SCO is only removed if connected +** +** Returns BOOLEAN - TRUE if Sco removal was started +** +*******************************************************************************/ +static BOOLEAN bta_ag_remove_sco(tBTA_AG_SCB *p_scb, BOOLEAN only_active) +{ + BOOLEAN removed_started = FALSE; + tBTM_STATUS status; + + if (p_scb->sco_idx != BTM_INVALID_SCO_INDEX) + { + if (!only_active || p_scb->sco_idx == bta_ag_cb.sco.cur_idx) + { + status = BTM_RemoveSco(p_scb->sco_idx); + + APPL_TRACE_DEBUG("ag remove sco: inx 0x%04x, status:0x%x", p_scb->sco_idx, status); + + if (status == BTM_CMD_STARTED) + { + /* Sco is connected; set current control block */ + bta_ag_cb.sco.p_curr_scb = p_scb; + + removed_started = TRUE; + } + /* If no connection reset the sco handle */ + else if ( (status == BTM_SUCCESS) || (status == BTM_UNKNOWN_ADDR) ) + { + p_scb->sco_idx = BTM_INVALID_SCO_INDEX; + } + } + } + return removed_started; +} + + +/******************************************************************************* +** +** Function bta_ag_esco_connreq_cback +** +** Description BTM eSCO connection requests and eSCO change requests +** Only the connection requests are processed by BTA. +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_esco_connreq_cback(tBTM_ESCO_EVT event, tBTM_ESCO_EVT_DATA *p_data) +{ + tBTA_AG_SCB *p_scb; + UINT16 handle; + UINT16 sco_inx = p_data->conn_evt.sco_inx; + + /* Only process connection requests */ + if (event == BTM_ESCO_CONN_REQ_EVT) + { + if ((handle = bta_ag_idx_by_bdaddr(BTM_ReadScoBdAddr(sco_inx))) != 0 && + ((p_scb = bta_ag_scb_by_idx(handle)) != NULL) && p_scb->svc_conn) + { + p_scb->sco_idx = sco_inx; + + /* If no other SCO active, allow this one */ + if (!bta_ag_cb.sco.p_curr_scb) + { + APPL_TRACE_EVENT("bta_ag_esco_connreq_cback: Accept Conn Request (sco_inx 0x%04x)", sco_inx); + bta_ag_sco_conn_rsp(p_scb, &p_data->conn_evt); + + bta_ag_cb.sco.state = BTA_AG_SCO_OPENING_ST; + bta_ag_cb.sco.p_curr_scb = p_scb; + bta_ag_cb.sco.cur_idx = p_scb->sco_idx; + } + else /* Begin a transfer: Close current SCO before responding */ + { + APPL_TRACE_DEBUG("bta_ag_esco_connreq_cback: Begin XFER"); + bta_ag_cb.sco.p_xfer_scb = p_scb; + bta_ag_cb.sco.conn_data = p_data->conn_evt; + bta_ag_cb.sco.state = BTA_AG_SCO_OPEN_XFER_ST; + + if (!bta_ag_remove_sco(bta_ag_cb.sco.p_curr_scb, TRUE)) + { + APPL_TRACE_ERROR("bta_ag_esco_connreq_cback: Nothing to remove so accept Conn Request (sco_inx 0x%04x)", sco_inx); + bta_ag_cb.sco.p_xfer_scb = NULL; + bta_ag_cb.sco.state = BTA_AG_SCO_LISTEN_ST; + + bta_ag_sco_conn_rsp(p_scb, &p_data->conn_evt); + } + } + } + /* If error occurred send reject response immediately */ + else + { + APPL_TRACE_WARNING("no scb for bta_ag_esco_connreq_cback or no resources"); + BTM_EScoConnRsp(p_data->conn_evt.sco_inx, HCI_ERR_HOST_REJECT_RESOURCES, NULL); + } + } + /* Received a change in the esco link */ + else if (event == BTM_ESCO_CHG_EVT) + { + APPL_TRACE_EVENT("eSCO change event (inx %d): rtrans %d, rxlen %d, txlen %d, txint %d", + p_data->chg_evt.sco_inx, + p_data->chg_evt.retrans_window, p_data->chg_evt.rx_pkt_len, + p_data->chg_evt.tx_pkt_len, p_data->chg_evt.tx_interval); + } +} + +/******************************************************************************* +** +** Function bta_ag_cback_sco +** +** Description Call application callback function with SCO event. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_cback_sco(tBTA_AG_SCB *p_scb, UINT8 event) +{ + tBTA_AG_HDR sco; + + sco.handle = bta_ag_scb_to_idx(p_scb); + sco.app_id = p_scb->app_id; + + /* call close cback */ + (*bta_ag_cb.p_cback)(event, (tBTA_AG *) &sco); +} + +/******************************************************************************* +** +** Function bta_ag_create_sco +** +** Description Create a sco connection and is is_orig is TRUE means AG originate +** this connection, if FALSE it's peer device originate the connection. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_create_sco(tBTA_AG_SCB *p_scb, BOOLEAN is_orig) +{ + tBTM_STATUS status; + UINT8 *p_bd_addr = NULL; + tBTM_ESCO_PARAMS params; +#if (BTM_WBS_INCLUDED == TRUE) + tBTA_AG_PEER_CODEC esco_codec = BTM_SCO_CODEC_CVSD; + int codec_index = 0; +#endif +#if (BTM_SCO_HCI_INCLUDED == TRUE) + tBTM_SCO_ROUTE_TYPE sco_route; + tBTA_HFP_CODEC_INFO codec_info = {BTA_HFP_SCO_CODEC_PCM}; + UINT32 pcm_sample_rate; +#endif + + /* Make sure this sco handle is not already in use */ + if (p_scb->sco_idx != BTM_INVALID_SCO_INDEX) + { + APPL_TRACE_WARNING("bta_ag_create_sco: Index 0x%04x Already In Use!", + p_scb->sco_idx); + return; + } + +#if (BTM_WBS_INCLUDED == TRUE) + + if ((p_scb->sco_codec == BTM_SCO_CODEC_MSBC) && !p_scb->codec_fallback && !p_scb->retry_with_sco_only) + { + esco_codec = BTM_SCO_CODEC_MSBC; + } + if (p_scb->codec_fallback) + { + p_scb->codec_fallback = FALSE; + /* Force AG to send +BCS for the next audio connection. */ + p_scb->codec_updated = TRUE; + } + /* If WBS included, use CVSD by default, index is 0 for CVSD by initialization */ + /* If eSCO codec is mSBC, index is T2 or T1 */ + if (esco_codec == BTM_SCO_CODEC_MSBC) + { + if (p_scb->codec_msbc_settings == BTA_AG_SCO_MSBC_SETTINGS_T2) + { + codec_index = BTA_AG_ESCO_SETTING_IDX_T2; + } + else + { + codec_index = BTA_AG_ESCO_SETTING_IDX_T1; + } + } + params = bta_ag_esco_params[codec_index]; +#else + /* When WBS is not included, use CVSD by default */ + params = bta_ag_esco_params; +#endif + + if(bta_ag_cb.sco.param_updated) /* If we do not use the default parameters */ + params = bta_ag_cb.sco.params; + + if(!bta_ag_cb.sco.param_updated) + { +#if (BTM_WBS_INCLUDED == TRUE) + if (esco_codec == BTM_SCO_CODEC_CVSD) /* For CVSD */ +#endif + { + /* Use the application packet types (5 slot EV packets not allowed) */ + params.packet_types = p_bta_ag_cfg->sco_pkt_types | + BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 | + BTM_SCO_PKT_TYPES_MASK_NO_3_EV5; + } + } + + /* if initiating, set current scb and peer bd addr */ + if (is_orig) + { + /* Attempt to use eSCO if remote host supports HFP >= 1.5 */ + /* Need to find out from SIG if HSP can use eSCO; for now use SCO */ + if (p_scb->conn_service == BTA_AG_HFP && p_scb->peer_version >= HFP_VERSION_1_5 && !p_scb->retry_with_sco_only) + { + BTM_SetEScoMode(BTM_LINK_TYPE_ESCO, ¶ms); + /* If ESCO or EDR ESCO, retry with SCO only in case of failure */ + if((params.packet_types & BTM_ESCO_LINK_ONLY_MASK) + ||!((params.packet_types & ~(BTM_ESCO_LINK_ONLY_MASK | BTM_SCO_LINK_ONLY_MASK)) ^ BTA_AG_NO_EDR_ESCO)) + { +#if (BTM_WBS_INCLUDED == TRUE) + if (esco_codec != BTA_AG_CODEC_MSBC) + { + p_scb->retry_with_sco_only = TRUE; + APPL_TRACE_API("Setting retry_with_sco_only to TRUE"); + } + else /* Do not use SCO when using mSBC */ + { + p_scb->retry_with_sco_only = FALSE; + APPL_TRACE_API("Setting retry_with_sco_only to FALSE"); + } +#else + p_scb->retry_with_sco_only = TRUE; + APPL_TRACE_API("Setting retry_with_sco_only to TRUE"); +#endif + } + } + else + { + if(p_scb->retry_with_sco_only){ + APPL_TRACE_API("retrying with SCO only"); + } + p_scb->retry_with_sco_only = FALSE; + BTM_SetEScoMode(BTM_LINK_TYPE_SCO, ¶ms); + } + + bta_ag_cb.sco.p_curr_scb = p_scb; + /* tell sys to stop av if any */ + bta_sys_sco_use(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + +#if (BTM_WBS_INCLUDED == TRUE) + /* Allow any platform specific pre-SCO set up to take place */ + bta_ag_sco_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, SCO_STATE_SETUP, esco_codec); + + /* This setting may not be necessary */ + /* To be verified with stable 2049 boards */ + if (esco_codec == BTA_AG_CODEC_MSBC) + BTM_WriteVoiceSettings(BTM_VOICE_SETTING_TRANS); + else + BTM_WriteVoiceSettings(BTM_VOICE_SETTING_CVSD); + /* save the current codec because sco_codec can be updated while SCO is open. */ + p_scb->inuse_codec = esco_codec; +#else + /* Allow any platform specific pre-SCO set up to take place */ + bta_ag_sco_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, SCO_STATE_SETUP); +#endif + +#if (BTM_SCO_HCI_INCLUDED == TRUE) +#if (BTM_WBS_INCLUDED == TRUE) + if (esco_codec == BTA_AG_CODEC_MSBC) + pcm_sample_rate = BTA_HFP_SCO_SAMP_RATE_16K; +#endif + pcm_sample_rate = BTA_HFP_SCO_SAMP_RATE_8K; + sco_route = bta_ag_sco_co_init(pcm_sample_rate, pcm_sample_rate, &codec_info, p_scb->app_id); +#endif + + +#if (BTM_SCO_HCI_INCLUDED == TRUE) + /* initialize SCO setup, no voice setting for AG, data rate <==> sample rate */ + BTM_ConfigScoPath(sco_route, bta_ag_sco_read_cback, NULL, TRUE); +#endif + bta_ag_cb.sco.cur_idx = p_scb->sco_idx; + } + else{ + p_scb->retry_with_sco_only = FALSE; + } + + p_bd_addr = p_scb->peer_addr; + + status = BTM_CreateSco(p_bd_addr, is_orig, params.packet_types, &p_scb->sco_idx, bta_ag_sco_conn_cback, bta_ag_sco_disc_cback); + + if (status == BTM_CMD_STARTED) + { + if (!is_orig) + { + BTM_RegForEScoEvts(p_scb->sco_idx, bta_ag_esco_connreq_cback); + } + else /* Initiating the connection, set the current sco handle */ + { + bta_ag_cb.sco.cur_idx = p_scb->sco_idx; + } + } + + APPL_TRACE_API("ag create sco: orig %d, inx 0x%04x, status 0x%x, pkt types 0x%04x", + is_orig, p_scb->sco_idx, status, params.packet_types); +} + +#if (BTM_WBS_INCLUDED == TRUE ) +/******************************************************************************* +** +** Function bta_ag_attempt_msbc_safe_settings +** +** Description Checks if ESCO connection needs to be attempted using mSBC T1(safe) settings +** +** +** Returns TRUE if T1 settings has to be used, FALSE otherwise +** +*******************************************************************************/ +BOOLEAN bta_ag_attempt_msbc_safe_settings(tBTA_AG_SCB *p_scb) +{ + if (p_scb->svc_conn && p_scb->sco_codec == BTM_SCO_CODEC_MSBC && + p_scb->codec_msbc_settings == BTA_AG_SCO_MSBC_SETTINGS_T1) + return TRUE; + else + return FALSE; +} + +/******************************************************************************* +** +** Function bta_ag_cn_timer_cback +** +** Description +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_cn_timer_cback (TIMER_LIST_ENT *p_tle) +{ + tBTA_AG_SCB *p_scb; + + if (p_tle) + { + p_scb = (tBTA_AG_SCB *)p_tle->param; + + if (p_scb) + { + /* Announce that codec negotiation failed. */ + bta_ag_sco_codec_nego(p_scb, FALSE); + + /* call app callback */ + bta_ag_cback_sco(p_scb, BTA_AG_AUDIO_CLOSE_EVT); + } + } +} + +/******************************************************************************* +** +** Function bta_ag_codec_negotiate +** +** Description Initiate codec negotiation by sending AT command. +** If not necessary, skip negotiation. +** +** Returns void +** +*******************************************************************************/ +void bta_ag_codec_negotiate(tBTA_AG_SCB *p_scb) +{ + bta_ag_cb.sco.p_curr_scb = p_scb; + + if ((p_scb->codec_updated || p_scb->codec_fallback || + bta_ag_attempt_msbc_safe_settings(p_scb)) && + (p_scb->peer_features & BTA_AG_PEER_FEAT_CODEC)) + { + /* Change the power mode to Active until sco open is completed. */ + bta_sys_busy(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + + /* Send +BCS to the peer */ + bta_ag_send_bcs(p_scb, NULL); + + /* Start timer to handle timeout */ + p_scb->cn_timer.p_cback = (TIMER_CBACK*)&bta_ag_cn_timer_cback; + p_scb->cn_timer.param = (INT32)p_scb; + bta_sys_start_timer(&p_scb->cn_timer, 0, BTA_AG_CODEC_NEGO_TIMEOUT); + } + else + { + /* use same codec type as previous SCO connection, skip codec negotiation */ + APPL_TRACE_DEBUG("use same codec type as previous SCO connection,skip codec negotiation"); + bta_ag_sco_codec_nego(p_scb, TRUE); + } +} +#endif /* (BTM_WBS_INCLUDED == TRUE ) */ + +/******************************************************************************* +** +** Function bta_ag_sco_event +** +** Description AG Sco State Machine +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_sco_event(tBTA_AG_SCB *p_scb, UINT8 event) +{ +#if (BTM_SCO_HCI_INCLUDED == TRUE) + tBTA_AG_SCO_CB *p_sco = &bta_ag_cb.sco; + BT_HDR *p_buf; +#endif +#if (BTM_WBS_INCLUDED == TRUE) + tBTA_AG_SCB *p_cn_scb = NULL; /* For codec negotiation */ +#endif + UINT8 in_state = p_sco->state; + APPL_TRACE_EVENT("BTA ag sco evt (hdl 0x%04x): State %d (%s), Event %d (%s)", + p_scb->sco_idx, p_sco->state, + bta_ag_sco_state_str(p_sco->state), event, bta_ag_sco_evt_str(event)); + +#if (BTM_SCO_HCI_INCLUDED == TRUE) + if (event == BTA_AG_SCO_CI_DATA_E) + { + UINT16 pkt_offset = 1 + HCI_SCO_PREAMBLE_SIZE; + UINT16 len_to_send = 0; + while (TRUE) + { + p_buf = osi_calloc(sizeof(BT_HDR) + pkt_offset + p_scb->out_pkt_len); + if (!p_buf) { + APPL_TRACE_WARNING("%s, no mem", __FUNCTION__); + break; + } + p_buf->offset = pkt_offset; + len_to_send = bta_ag_sco_co_out_data(p_buf->data + pkt_offset); + p_buf->len = len_to_send; + + if (len_to_send == p_scb->out_pkt_len) + { + if (p_sco->state == BTA_AG_SCO_OPEN_ST) { + tBTM_STATUS write_stat = BTM_WriteScoData(p_sco->p_curr_scb->sco_idx, p_buf); + if (write_stat != BTM_SUCCESS) { + break; + } + else { + osi_free(p_buf); + } + } + else { + osi_free(p_buf); + break; + } + } + return; + } + } +#endif + + /* State Machine Start */ + switch (p_sco->state) + { + case BTA_AG_SCO_SHUTDOWN_ST: + switch (event) + { + case BTA_AG_SCO_LISTEN_E: + /* create sco listen connection */ + bta_ag_create_sco(p_scb, FALSE); + p_sco->state = BTA_AG_SCO_LISTEN_ST; + break; + + default: + APPL_TRACE_WARNING("BTA_AG_SCO_SHUTDOWN_ST: Ignoring event %d", event); + break; + } + break; + + case BTA_AG_SCO_LISTEN_ST: + switch (event) + { + case BTA_AG_SCO_LISTEN_E: + /* create sco listen connection (Additional channel) */ + bta_ag_create_sco(p_scb, FALSE); + break; + + case BTA_AG_SCO_OPEN_E: + /* remove listening connection */ + bta_ag_remove_sco(p_scb, FALSE); +#if (BTM_WBS_INCLUDED == TRUE ) + /* start codec negotiation */ + p_sco->state = BTA_AG_SCO_CODEC_ST; + p_cn_scb = p_scb; +#else + /* create sco connection to peer */ + bta_ag_create_sco(p_scb, TRUE); + p_sco->state = BTA_AG_SCO_OPENING_ST; +#endif + break; + + case BTA_AG_SCO_SHUTDOWN_E: + /* remove listening connection */ + bta_ag_remove_sco(p_scb, FALSE); + + if (p_scb == p_sco->p_curr_scb) + p_sco->p_curr_scb = NULL; + + /* If last SCO instance then finish shutting down */ + if (!bta_ag_other_scb_open(p_scb)) + { + p_sco->state = BTA_AG_SCO_SHUTDOWN_ST; + } + break; + + case BTA_AG_SCO_CLOSE_E: + /* remove listening connection */ + /* Ignore the event. We need to keep listening SCO for the active SLC */ + APPL_TRACE_WARNING("BTA_AG_SCO_LISTEN_ST: Ignoring event %d", event); + break; + + case BTA_AG_SCO_CONN_CLOSE_E: + /* sco failed; create sco listen connection */ + bta_ag_create_sco(p_scb, FALSE); + p_sco->state = BTA_AG_SCO_LISTEN_ST; + break; + + default: + APPL_TRACE_WARNING("BTA_AG_SCO_LISTEN_ST: Ignoring event %d", event); + break; + } + break; + +#if (BTM_WBS_INCLUDED == TRUE ) + case BTA_AG_SCO_CODEC_ST: + switch (event) + { + case BTA_AG_SCO_LISTEN_E: + /* create sco listen connection (Additional channel) */ + bta_ag_create_sco(p_scb, FALSE); + break; + + case BTA_AG_SCO_CN_DONE_E: + /* create sco connection to peer */ + bta_ag_create_sco(p_scb, TRUE); + p_sco->state = BTA_AG_SCO_OPENING_ST; + break; + + case BTA_AG_SCO_XFER_E: + /* save xfer scb */ + p_sco->p_xfer_scb = p_scb; + p_sco->state = BTA_AG_SCO_CLOSE_XFER_ST; + break; + + case BTA_AG_SCO_SHUTDOWN_E: + /* remove listening connection */ + bta_ag_remove_sco(p_scb, FALSE); + + if (p_scb == p_sco->p_curr_scb) + p_sco->p_curr_scb = NULL; + + /* If last SCO instance then finish shutting down */ + if (!bta_ag_other_scb_open(p_scb)) + { + p_sco->state = BTA_AG_SCO_SHUTDOWN_ST; + } + break; + + case BTA_AG_SCO_CLOSE_E: + /* sco open is not started yet. just go back to listening */ + p_sco->state = BTA_AG_SCO_LISTEN_ST; + break; + + case BTA_AG_SCO_CONN_CLOSE_E: + /* sco failed; create sco listen connection */ + bta_ag_create_sco(p_scb, FALSE); + p_sco->state = BTA_AG_SCO_LISTEN_ST; + break; + + default: + APPL_TRACE_WARNING("BTA_AG_SCO_CODEC_ST: Ignoring event %d", event); + break; + } + break; +#endif + + case BTA_AG_SCO_OPENING_ST: + switch (event) + { + case BTA_AG_SCO_LISTEN_E: + /* second headset has now joined */ + /* create sco listen connection (Additional channel) */ + if (p_scb != p_sco->p_curr_scb) + { + bta_ag_create_sco(p_scb, FALSE); + } + break; + +#if (BTM_WBS_INCLUDED == TRUE) + case BTA_AG_SCO_REOPEN_E: + /* start codec negotiation */ + p_sco->state = BTA_AG_SCO_CODEC_ST; + p_cn_scb = p_scb; + break; +#endif + + case BTA_AG_SCO_XFER_E: + /* save xfer scb */ + p_sco->p_xfer_scb = p_scb; + p_sco->state = BTA_AG_SCO_CLOSE_XFER_ST; + break; + + case BTA_AG_SCO_CLOSE_E: + p_sco->state = BTA_AG_SCO_OPEN_CL_ST; + break; + + case BTA_AG_SCO_SHUTDOWN_E: + /* If not opening scb, just close it */ + if (p_scb != p_sco->p_curr_scb) + { + /* remove listening connection */ + bta_ag_remove_sco(p_scb, FALSE); + } + else + p_sco->state = BTA_AG_SCO_SHUTTING_ST; + + break; + + case BTA_AG_SCO_CONN_OPEN_E: + p_sco->state = BTA_AG_SCO_OPEN_ST; + break; + + case BTA_AG_SCO_CONN_CLOSE_E: + /* sco failed; create sco listen connection */ + bta_ag_create_sco(p_scb, FALSE); + p_sco->state = BTA_AG_SCO_LISTEN_ST; + break; + + default: + APPL_TRACE_WARNING("BTA_AG_SCO_OPENING_ST: Ignoring event %d", event); + break; + } + break; + + case BTA_AG_SCO_OPEN_CL_ST: + switch (event) + { + case BTA_AG_SCO_XFER_E: + /* save xfer scb */ + p_sco->p_xfer_scb = p_scb; + + p_sco->state = BTA_AG_SCO_CLOSE_XFER_ST; + break; + + case BTA_AG_SCO_OPEN_E: + p_sco->state = BTA_AG_SCO_OPENING_ST; + break; + + case BTA_AG_SCO_SHUTDOWN_E: + /* If not opening scb, just close it */ + if (p_scb != p_sco->p_curr_scb) + { + /* remove listening connection */ + bta_ag_remove_sco(p_scb, FALSE); + } + else + p_sco->state = BTA_AG_SCO_SHUTTING_ST; + + break; + + case BTA_AG_SCO_CONN_OPEN_E: + /* close sco connection */ + bta_ag_remove_sco(p_scb, TRUE); + + p_sco->state = BTA_AG_SCO_CLOSING_ST; + break; + + case BTA_AG_SCO_CONN_CLOSE_E: + /* sco failed; create sco listen connection */ + + p_sco->state = BTA_AG_SCO_LISTEN_ST; + break; + + default: + APPL_TRACE_WARNING("BTA_AG_SCO_OPEN_CL_ST: Ignoring event %d", event); + break; + } + break; + + case BTA_AG_SCO_OPEN_XFER_ST: + switch (event) + { + case BTA_AG_SCO_CLOSE_E: + /* close sco connection */ + bta_ag_remove_sco(p_scb, TRUE); + p_sco->state = BTA_AG_SCO_CLOSING_ST; + break; + + case BTA_AG_SCO_SHUTDOWN_E: + /* remove all connection */ + bta_ag_remove_sco(p_scb, FALSE); + p_sco->state = BTA_AG_SCO_SHUTTING_ST; + break; + + case BTA_AG_SCO_CONN_CLOSE_E: + /* closed sco; place in listen mode and + accept the transferred connection */ + bta_ag_create_sco(p_scb, FALSE); /* Back into listen mode */ + /* Accept sco connection with xfer scb */ + bta_ag_sco_conn_rsp(p_sco->p_xfer_scb, &p_sco->conn_data); + p_sco->state = BTA_AG_SCO_OPENING_ST; + p_sco->p_curr_scb = p_sco->p_xfer_scb; + p_sco->cur_idx = p_sco->p_xfer_scb->sco_idx; + p_sco->p_xfer_scb = NULL; + break; + + default: + APPL_TRACE_WARNING("BTA_AG_SCO_OPEN_XFER_ST: Ignoring event %d", event); + break; + } + break; + + case BTA_AG_SCO_OPEN_ST: + switch (event) + { + case BTA_AG_SCO_LISTEN_E: + /* second headset has now joined */ + /* create sco listen connection (Additional channel) */ + if (p_scb != p_sco->p_curr_scb) + { + bta_ag_create_sco(p_scb, FALSE); + } + break; + + case BTA_AG_SCO_XFER_E: + /* close current sco connection */ + bta_ag_remove_sco(p_sco->p_curr_scb, TRUE); + /* save xfer scb */ + p_sco->p_xfer_scb = p_scb; + p_sco->state = BTA_AG_SCO_CLOSE_XFER_ST; + break; + + case BTA_AG_SCO_CLOSE_E: + /* close sco connection if active */ + if (bta_ag_remove_sco(p_scb, TRUE)) + { + p_sco->state = BTA_AG_SCO_CLOSING_ST; + } + break; + + case BTA_AG_SCO_SHUTDOWN_E: + /* remove all listening connections */ + bta_ag_remove_sco(p_scb, FALSE); + /* If SCO was active on this scb, close it */ + if (p_scb == p_sco->p_curr_scb) + { + p_sco->state = BTA_AG_SCO_SHUTTING_ST; + } + break; + + case BTA_AG_SCO_CONN_CLOSE_E: + /* peer closed sco; create sco listen connection */ + bta_ag_create_sco(p_scb, FALSE); + p_sco->state = BTA_AG_SCO_LISTEN_ST; + break; + + default: + APPL_TRACE_WARNING("BTA_AG_SCO_OPEN_ST: Ignoring event %d", event); + break; + } + break; + + case BTA_AG_SCO_CLOSING_ST: + switch (event) + { + case BTA_AG_SCO_LISTEN_E: + /* create sco listen connection (Additional channel) */ + if (p_scb != p_sco->p_curr_scb) + { + bta_ag_create_sco(p_scb, FALSE); + } + break; + + case BTA_AG_SCO_OPEN_E: + p_sco->state = BTA_AG_SCO_CLOSE_OP_ST; + break; + + case BTA_AG_SCO_XFER_E: + /* save xfer scb */ + p_sco->p_xfer_scb = p_scb; + p_sco->state = BTA_AG_SCO_CLOSE_XFER_ST; + break; + + case BTA_AG_SCO_SHUTDOWN_E: + /* If not closing scb, just close it */ + if (p_scb != p_sco->p_curr_scb) + { + /* remove listening connection */ + bta_ag_remove_sco(p_scb, FALSE); + } + else + p_sco->state = BTA_AG_SCO_SHUTTING_ST; + break; + + case BTA_AG_SCO_CONN_CLOSE_E: + /* peer closed sco; create sco listen connection */ + bta_ag_create_sco(p_scb, FALSE); + p_sco->state = BTA_AG_SCO_LISTEN_ST; + break; + + default: + APPL_TRACE_WARNING("BTA_AG_SCO_CLOSING_ST: Ignoring event %d", event); + break; + } + break; + + case BTA_AG_SCO_CLOSE_OP_ST: + switch (event) + { + case BTA_AG_SCO_CLOSE_E: + p_sco->state = BTA_AG_SCO_CLOSING_ST; + break; + + case BTA_AG_SCO_SHUTDOWN_E: + p_sco->state = BTA_AG_SCO_SHUTTING_ST; + break; + + case BTA_AG_SCO_CONN_CLOSE_E: +#if (BTM_WBS_INCLUDED == TRUE ) + /* start codec negotiation */ + p_sco->state = BTA_AG_SCO_CODEC_ST; + p_cn_scb = p_scb; +#else + /* open sco connection */ + bta_ag_create_sco(p_scb, TRUE); + p_sco->state = BTA_AG_SCO_OPENING_ST; +#endif + break; + + case BTA_AG_SCO_LISTEN_E: + /* create sco listen connection (Additional channel) */ + if (p_scb != p_sco->p_curr_scb) + { + bta_ag_create_sco(p_scb, FALSE); + } + break; + + default: + APPL_TRACE_WARNING("BTA_AG_SCO_CLOSE_OP_ST: Ignoring event %d", event); + break; + } + break; + + case BTA_AG_SCO_CLOSE_XFER_ST: + switch (event) + { + case BTA_AG_SCO_CONN_OPEN_E: + /* close sco connection so headset can be transferred + Probably entered this state from "opening state" */ + bta_ag_remove_sco(p_scb, TRUE); + break; + + case BTA_AG_SCO_CLOSE_E: + /* clear xfer scb */ + p_sco->p_xfer_scb = NULL; + p_sco->state = BTA_AG_SCO_CLOSING_ST; + break; + + case BTA_AG_SCO_SHUTDOWN_E: + /* clear xfer scb */ + p_sco->p_xfer_scb = NULL; + p_sco->state = BTA_AG_SCO_SHUTTING_ST; + break; + + case BTA_AG_SCO_CONN_CLOSE_E: + /* closed sco; place old sco in listen mode, + take current sco out of listen, and + create originating sco for current */ + bta_ag_create_sco(p_scb, FALSE); + bta_ag_remove_sco(p_sco->p_xfer_scb, FALSE); + +#if (BTM_WBS_INCLUDED == TRUE) + /* start codec negotiation */ + p_sco->state = BTA_AG_SCO_CODEC_ST; + p_cn_scb = p_sco->p_xfer_scb; + p_sco->p_xfer_scb = NULL; +#else + /* create sco connection to peer */ + bta_ag_create_sco(p_sco->p_xfer_scb, TRUE); + p_sco->p_xfer_scb = NULL; + p_sco->state = BTA_AG_SCO_OPENING_ST; +#endif + break; + + default: + APPL_TRACE_WARNING("BTA_AG_SCO_CLOSE_XFER_ST: Ignoring event %d", event); + break; + } + break; + + case BTA_AG_SCO_SHUTTING_ST: + switch (event) + { + case BTA_AG_SCO_CONN_OPEN_E: + /* close sco connection; wait for conn close event */ + bta_ag_remove_sco(p_scb, TRUE); + break; + + case BTA_AG_SCO_CONN_CLOSE_E: + /* If last SCO instance then finish shutting down */ + if (!bta_ag_other_scb_open(p_scb)) + { + p_sco->state = BTA_AG_SCO_SHUTDOWN_ST; + } + else /* Other instance is still listening */ + { + p_sco->state = BTA_AG_SCO_LISTEN_ST; + } + + /* If SCO closed for other HS which is not being disconnected, + then create listen sco connection for it as scb still open */ + if (bta_ag_scb_open(p_scb)) + { + bta_ag_create_sco(p_scb, FALSE); + p_sco->state = BTA_AG_SCO_LISTEN_ST; + } + + if (p_scb == p_sco->p_curr_scb) + { + p_sco->p_curr_scb->sco_idx = BTM_INVALID_SCO_INDEX; + p_sco->p_curr_scb = NULL; + } + break; + + case BTA_AG_SCO_LISTEN_E: + /* create sco listen connection (Additional channel) */ + if (p_scb != p_sco->p_curr_scb) + { + bta_ag_create_sco(p_scb, FALSE); + } + break; + + case BTA_AG_SCO_SHUTDOWN_E: + if (!bta_ag_other_scb_open(p_scb)) + { + p_sco->state = BTA_AG_SCO_SHUTDOWN_ST; + } + else /* Other instance is still listening */ + { + p_sco->state = BTA_AG_SCO_LISTEN_ST; + } + + if (p_scb == p_sco->p_curr_scb) + { + p_sco->p_curr_scb->sco_idx = BTM_INVALID_SCO_INDEX; + p_sco->p_curr_scb = NULL; + } + break; + + default: + APPL_TRACE_WARNING("BTA_AG_SCO_SHUTTING_ST: Ignoring event %d", event); + break; + } + break; + + default: + break; + } + + if (p_sco->state != in_state) + { + APPL_TRACE_EVENT("BTA AG SCO State Change: [%s] -> [%s] after Event [%s]", + bta_ag_sco_state_str(in_state), + bta_ag_sco_state_str(p_sco->state), + bta_ag_sco_evt_str(event)); + } + +#if (BTM_WBS_INCLUDED == TRUE ) + if (p_cn_scb) + { + bta_ag_codec_negotiate(p_cn_scb); + } +#endif +} + +/******************************************************************************* +** +** Function bta_ag_sco_is_open +** +** Description Check if sco is open for this scb. +** +** +** Returns TRUE if sco open for this scb, FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN bta_ag_sco_is_open(tBTA_AG_SCB *p_scb) +{ + return ((bta_ag_cb.sco.state == BTA_AG_SCO_OPEN_ST) && + (bta_ag_cb.sco.p_curr_scb == p_scb)); +} + +/******************************************************************************* +** +** Function bta_ag_sco_is_opening +** +** Description Check if sco is in Opening state. +** +** +** Returns TRUE if sco is in Opening state for this scb, FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN bta_ag_sco_is_opening(tBTA_AG_SCB *p_scb) +{ +#if (BTM_WBS_INCLUDED == TRUE ) + return (((bta_ag_cb.sco.state == BTA_AG_SCO_OPENING_ST) || + (bta_ag_cb.sco.state == BTA_AG_SCO_OPENING_ST)) && + (bta_ag_cb.sco.p_curr_scb == p_scb)); +#else + return ((bta_ag_cb.sco.state == BTA_AG_SCO_OPENING_ST) && + (bta_ag_cb.sco.p_curr_scb == p_scb)); +#endif +} + +/******************************************************************************* +** +** Function bta_ag_sco_listen +** +** Description +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sco_listen(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UNUSED(p_data); + bta_ag_sco_event(p_scb, BTA_AG_SCO_LISTEN_E); +} + +/******************************************************************************* +** +** Function bta_ag_sco_open +** +** Description +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sco_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UINT8 event; + UNUSED(p_data); + + /* if another scb using sco, this is a transfer */ + if (bta_ag_cb.sco.p_curr_scb != NULL && bta_ag_cb.sco.p_curr_scb != p_scb) + { + event = BTA_AG_SCO_XFER_E; + } + /* else it is an open */ + else + { + event = BTA_AG_SCO_OPEN_E; + } + + bta_ag_sco_event(p_scb, event); +} + +/******************************************************************************* +** +** Function bta_ag_sco_close +** +** Description +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sco_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UNUSED(p_data); + + /* if scb is in use */ +#if (BTM_WBS_INCLUDED == TRUE ) + /* sco_idx is not allocated in SCO_CODEC_ST, we still need to move to listening state. */ + if ((p_scb->sco_idx != BTM_INVALID_SCO_INDEX) || (bta_ag_cb.sco.state == BTA_AG_SCO_CODEC_ST)) +#else + if (p_scb->sco_idx != BTM_INVALID_SCO_INDEX) +#endif + { + APPL_TRACE_DEBUG("bta_ag_sco_close: sco_inx = %d", p_scb->sco_idx); + bta_ag_sco_event(p_scb, BTA_AG_SCO_CLOSE_E); + } +} + +#if (BTM_WBS_INCLUDED == TRUE ) + +/******************************************************************************* +** +** Function bta_ag_sco_codec_nego +** +** Description +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sco_codec_nego(tBTA_AG_SCB *p_scb, BOOLEAN result) +{ + if(result == TRUE) + { + /* Subsequent sco connection will skip codec negotiation */ + p_scb->codec_updated = FALSE; + + bta_ag_sco_event(p_scb, BTA_AG_SCO_CN_DONE_E); + } + else /* codec negotiation failed */ + bta_ag_sco_event(p_scb, BTA_AG_SCO_CLOSE_E); +} +#endif + +/******************************************************************************* +** +** Function bta_ag_sco_shutdown +** +** Description +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sco_shutdown(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UNUSED(p_data); + + bta_ag_sco_event(p_scb, BTA_AG_SCO_SHUTDOWN_E); +} + +/******************************************************************************* +** +** Function bta_ag_sco_conn_open +** +** Description +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sco_conn_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UNUSED(p_data); + + bta_ag_sco_event(p_scb, BTA_AG_SCO_CONN_OPEN_E); + + bta_sys_sco_open(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + +#if (BTM_SCO_HCI_INCLUDED == TRUE) + +#if (BTM_WBS_INCLUDED == TRUE) + bta_ag_sco_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, SCO_STATE_ON, p_scb->inuse_codec); +#else + bta_ag_sco_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, SCO_STATE_ON); +#endif + /* open SCO codec if SCO is routed through transport */ + bta_ag_sco_co_open(bta_ag_scb_to_idx(p_scb), p_scb->air_mode, BTA_HFP_SCO_OUT_PKT_SIZE, BTA_AG_CI_SCO_DATA_EVT); +#endif + + /* call app callback */ + bta_ag_cback_sco(p_scb, BTA_AG_AUDIO_OPEN_EVT); + + p_scb->retry_with_sco_only = FALSE; +#if (BTM_WBS_INCLUDED == TRUE) + /* reset to mSBC T2 settings as the preferred */ + p_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T2; +#endif +} + +/******************************************************************************* +** +** Function bta_ag_sco_conn_close +** +** Description This function is called when a SCO connection is closed +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sco_conn_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UINT16 handle = bta_ag_scb_to_idx(p_scb); + UNUSED(p_data); + + /* clear current scb */ + bta_ag_cb.sco.p_curr_scb = NULL; + p_scb->sco_idx = BTM_INVALID_SCO_INDEX; + +#if (BTM_WBS_INCLUDED == TRUE) + /* codec_fallback is set when AG is initiator and connection failed for mSBC. */ + /* OR if codec is msbc and T2 settings failed, then retry Safe T1 settings */ + if ((p_scb->codec_fallback && p_scb->svc_conn) || + bta_ag_attempt_msbc_safe_settings(p_scb)) + { + bta_ag_sco_event(p_scb, BTA_AG_SCO_REOPEN_E); + } + else if (p_scb->retry_with_sco_only && p_scb->svc_conn) + { + /* retry_with_sco_only is set when AG is initiator and connection failed for eSCO */ + bta_ag_create_sco(p_scb, TRUE); + } +#else + /* retry_with_sco_only, will be set only when AG is initiator + ** and AG is first trying to establish an eSCO connection */ + if (p_scb->retry_with_sco_only && p_scb->svc_conn) + { + bta_ag_create_sco(p_scb, TRUE); + } +#endif + else + { + sco_state_t sco_state = bta_ag_cb.sco.p_xfer_scb ? SCO_STATE_OFF_TRANSFER : SCO_STATE_OFF; +#if (BTM_WBS_INCLUDED == TRUE) + /* Indicate if the closing of audio is because of transfer */ + bta_ag_sco_audio_state(handle, p_scb->app_id, sco_state, p_scb->inuse_codec); +#else + /* Indicate if the closing of audio is because of transfer */ + bta_ag_sco_audio_state(handle, p_scb->app_id, sco_state); +#endif + bta_ag_sco_event(p_scb, BTA_AG_SCO_CONN_CLOSE_E); + + bta_sys_sco_close(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + + /* if av got suspended by this call, let it resume. */ + /* In case call stays alive regardless of sco, av should not be affected. */ + if(((p_scb->call_ind == BTA_AG_CALL_INACTIVE) && (p_scb->callsetup_ind == BTA_AG_CALLSETUP_NONE)) + || (p_scb->post_sco == BTA_AG_POST_SCO_CALL_END)) + { + bta_sys_sco_unuse(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + } + + /* call app callback */ + bta_ag_cback_sco(p_scb, BTA_AG_AUDIO_CLOSE_EVT); +#if (BTM_WBS_INCLUDED == TRUE) + p_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T2; +#endif + } + p_scb->retry_with_sco_only = FALSE; +} + +/******************************************************************************* +** +** Function bta_ag_sco_conn_rsp +** +** Description Process the SCO connection request +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sco_conn_rsp(tBTA_AG_SCB *p_scb, tBTM_ESCO_CONN_REQ_EVT_DATA *p_data) +{ + tBTM_ESCO_PARAMS resp; + UINT8 hci_status = HCI_SUCCESS; +#if (BTM_SCO_HCI_INCLUDED == TRUE) + tBTA_HFP_CODEC_INFO codec_info = {BTA_HFP_SCO_CODEC_PCM}; + UINT32 pcm_sample_rate; +#endif + + if (bta_ag_cb.sco.state == BTA_AG_SCO_LISTEN_ST || + bta_ag_cb.sco.state == BTA_AG_SCO_CLOSE_XFER_ST || + bta_ag_cb.sco.state == BTA_AG_SCO_OPEN_XFER_ST) + { + /* If script overrided sco parameter by BTA_CMD_SET_ESCO_PARAM */ + if (bta_ag_cb.sco.param_updated) + { + resp = bta_ag_cb.sco.params; + } + else + { + resp.rx_bw = BTM_64KBITS_RATE; + resp.tx_bw = BTM_64KBITS_RATE; + resp.max_latency = 10; + resp.voice_contfmt = 0x60; + resp.retrans_effort = BTM_ESCO_RETRANS_POWER; + + if (p_data->link_type == BTM_LINK_TYPE_SCO) + { + resp.packet_types = (BTM_SCO_LINK_ONLY_MASK | + BTM_SCO_PKT_TYPES_MASK_NO_2_EV3 | + BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 | + BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 | + BTM_SCO_PKT_TYPES_MASK_NO_3_EV5); + } + else /* Allow controller to use all types available except 5-slot EDR */ + { + resp.packet_types = (BTM_SCO_LINK_ALL_PKT_MASK | + BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 | + BTM_SCO_PKT_TYPES_MASK_NO_3_EV5); + } + } + + /* tell sys to stop av if any */ + bta_sys_sco_use(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); + +#if (BTM_WBS_INCLUDED == FALSE) + /* Allow any platform specific pre-SCO set up to take place */ + bta_ag_sco_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, SCO_STATE_SETUP); +#else + /* When HS initiated SCO, it cannot be WBS. */ + /* Allow any platform specific pre-SCO set up to take place */ + bta_ag_sco_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, SCO_STATE_SETUP, + BTA_AG_CODEC_CVSD); +#endif + +#if (BTM_SCO_HCI_INCLUDED == TRUE) + pcm_sample_rate = BTA_HFP_SCO_SAMP_RATE_8K; + /* initialize SCO setup, no voice setting for AG, data rate <==> sample rate */ + BTM_ConfigScoPath(bta_ag_sco_co_init(pcm_sample_rate, pcm_sample_rate, &codec_info, p_scb->app_id), + bta_ag_sco_read_cback, NULL, TRUE); +#endif + } + else + hci_status = HCI_ERR_HOST_REJECT_DEVICE; + +#if (BTM_WBS_INCLUDED == TRUE) + /* If SCO open was initiated from HS, it must be CVSD */ + p_scb->inuse_codec = BTA_AG_CODEC_NONE; +#endif + BTM_EScoConnRsp(p_data->sco_inx, hci_status, &resp); +} + +/******************************************************************************* +** +** Function bta_ag_ci_sco_data +** +** Description Process the SCO data ready callin event +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_ci_sco_data(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UNUSED(p_scb); + UNUSED(p_data); + +#if (BTM_SCO_HCI_INCLUDED == TRUE ) + bta_ag_sco_event(p_scb, BTA_AG_SCO_CI_DATA_E); +#endif +} + +/******************************************************************************* +** +** Function bta_ag_set_esco_param +** +** Description Update esco parameters from script wrapper. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_set_esco_param(BOOLEAN set_reset, tBTM_ESCO_PARAMS *param) +{ + if(set_reset == FALSE) /* reset the parameters to default */ + { + bta_ag_cb.sco.param_updated = FALSE; + APPL_TRACE_DEBUG("bta_ag_set_esco_param : Resetting ESCO parameters to default"); + } + else + { + bta_ag_cb.sco.param_updated = TRUE; + bta_ag_cb.sco.params = *param; + APPL_TRACE_DEBUG("bta_ag_set_esco_param : Setting ESCO parameters"); + } +} + +/******************************************************************************* +** Debugging functions +*******************************************************************************/ +static char *bta_ag_sco_evt_str(UINT8 event) +{ + switch (event) + { + case BTA_AG_SCO_LISTEN_E: + return "Listen Request"; + case BTA_AG_SCO_OPEN_E: + return "Open Request"; + case BTA_AG_SCO_XFER_E: + return "Transfer Request"; +#if (BTM_WBS_INCLUDED == TRUE ) + case BTA_AG_SCO_CN_DONE_E: + return "Codec Negotiation Done"; + case BTA_AG_SCO_REOPEN_E: + return "Reopen Request"; +#endif + case BTA_AG_SCO_CLOSE_E: + return "Close Request"; + case BTA_AG_SCO_SHUTDOWN_E: + return "Shutdown Request"; + case BTA_AG_SCO_CONN_OPEN_E: + return "Opened"; + case BTA_AG_SCO_CONN_CLOSE_E: + return "Closed"; + case BTA_AG_SCO_CI_DATA_E : + return "Sco Data"; + default: + return "Unknown SCO Event"; + } +} + +static char *bta_ag_sco_state_str(UINT8 state) +{ + switch (state) + { + case BTA_AG_SCO_SHUTDOWN_ST: + return "Shutdown"; + case BTA_AG_SCO_LISTEN_ST: + return "Listening"; +#if (BTM_WBS_INCLUDED == TRUE ) + case BTA_AG_SCO_CODEC_ST: + return "Codec Negotiation"; +#endif + case BTA_AG_SCO_OPENING_ST: + return "Opening"; + case BTA_AG_SCO_OPEN_CL_ST: + return "Open while closing"; + case BTA_AG_SCO_OPEN_XFER_ST: + return "Opening while Transferring"; + case BTA_AG_SCO_OPEN_ST: + return "Open"; + case BTA_AG_SCO_CLOSING_ST: + return "Closing"; + case BTA_AG_SCO_CLOSE_OP_ST: + return "Close while Opening"; + case BTA_AG_SCO_CLOSE_XFER_ST: + return "Close while Transferring"; + case BTA_AG_SCO_SHUTTING_ST: + return "Shutting Down"; + default: + return "Unknown SCO State"; + } +} + +#endif //#if (BTA_AG_INCLUDED == TRUE) \ No newline at end of file diff --git a/components/bt/host/bluedroid/bta/hf_ag/bta_ag_sdp.c b/components/bt/host/bluedroid/bta/hf_ag/bta_ag_sdp.c new file mode 100644 index 000000000..376399732 --- /dev/null +++ b/components/bt/host/bluedroid/bta/hf_ag/bta_ag_sdp.c @@ -0,0 +1,446 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the audio gateway functions performing SDP + * operations. + * + ******************************************************************************/ + +#include +#include "bta_ag_int.h" +#include "bta/bta_ag_api.h" +#include "bta/bta_sys.h" +#include "bta/bta_ag_api.h" +#include "bta/utl.h" +#include "stack/sdp_api.h" +#include "stack/btm_api.h" +#include "common/bt_trace.h" +#include "osi/allocator.h" + +#if (BTA_AG_INCLUDED == TRUE) + +/* Number of protocol elements in protocol element list. */ +#define BTA_AG_NUM_PROTO_ELEMS 2 + +/* Number of elements in service class id list. */ +#define BTA_AG_NUM_SVC_ELEMS 2 + +/* size of database for service discovery */ +#ifndef BTA_AG_DISC_BUF_SIZE +#define BTA_AG_DISC_BUF_SIZE (4096+16) +#endif + +/* declare sdp callback functions */ +void bta_ag_sdp_cback_1(UINT16 status); +void bta_ag_sdp_cback_2(UINT16 status); +void bta_ag_sdp_cback_3(UINT16 status); + +/* SDP callback function table */ +typedef tSDP_DISC_CMPL_CB *tBTA_AG_SDP_CBACK; +const tBTA_AG_SDP_CBACK bta_ag_sdp_cback_tbl[] = +{ + bta_ag_sdp_cback_1, + bta_ag_sdp_cback_2, + bta_ag_sdp_cback_3 +}; + +/******************************************************************************* +** +** Function bta_ag_sdp_cback +** +** Description SDP callback function. +** +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_sdp_cback(UINT16 status, UINT8 idx) +{ + tBTA_AG_DISC_RESULT *p_buf; + UINT16 event; + tBTA_AG_SCB *p_scb; + + APPL_TRACE_DEBUG("bta_ag_sdp_cback status:0x%x", status); + + if ((p_scb = bta_ag_scb_by_idx(idx)) != NULL) { + /* set event according to int/acp */ + if (p_scb->role == BTA_AG_ACP) { + event = BTA_AG_DISC_ACP_RES_EVT; + } else { + event = BTA_AG_DISC_INT_RES_EVT; + } + + if ((p_buf = (tBTA_AG_DISC_RESULT *) osi_malloc(sizeof(tBTA_AG_DISC_RESULT))) != NULL) { + p_buf->hdr.event = event; + p_buf->hdr.layer_specific = idx; + p_buf->status = status; + bta_sys_sendmsg(p_buf); + } + } +} + +/******************************************************************************* +** +** Function bta_ag_sdp_cback_1 to 3 +** +** Description SDP callback functions. Since there is no way to +** distinguish scb from the callback we need separate +** callbacks for each scb. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sdp_cback_1(UINT16 status) {bta_ag_sdp_cback(status, 1);} +void bta_ag_sdp_cback_2(UINT16 status) {bta_ag_sdp_cback(status, 2);} +void bta_ag_sdp_cback_3(UINT16 status) {bta_ag_sdp_cback(status, 3);} + +/****************************************************************************** +** +** Function bta_ag_add_record +** +** Description This function is called by a server application to add +** HSP or HFP information to an SDP record. Prior to +** calling this function the application must call +** SDP_CreateRecord() to create an SDP record. +** +** Returns TRUE if function execution succeeded, +** FALSE if function execution failed. +** +******************************************************************************/ +BOOLEAN bta_ag_add_record(UINT16 service_uuid, char *p_service_name, UINT8 scn, + tBTA_AG_FEAT features, UINT32 sdp_handle) +{ + tSDP_PROTOCOL_ELEM proto_elem_list[BTA_AG_NUM_PROTO_ELEMS]; + UINT16 svc_class_id_list[BTA_AG_NUM_SVC_ELEMS]; + UINT16 browse_list[] = {UUID_SERVCLASS_PUBLIC_BROWSE_GROUP}; + UINT16 version; + UINT16 profile_uuid; + UINT8 network; + BOOLEAN result = TRUE; + BOOLEAN codec_supported = FALSE; + UINT8 buf[2]; + + APPL_TRACE_DEBUG("bta_ag_add_record uuid: %x", service_uuid); + + memset( proto_elem_list, 0 , BTA_AG_NUM_PROTO_ELEMS*sizeof(tSDP_PROTOCOL_ELEM)); + /* add the protocol element sequence */ + proto_elem_list[0].protocol_uuid = UUID_PROTOCOL_L2CAP; + proto_elem_list[0].num_params = 0; + proto_elem_list[1].protocol_uuid = UUID_PROTOCOL_RFCOMM; + proto_elem_list[1].num_params = 1; + proto_elem_list[1].params[0] = scn; + result &= SDP_AddProtocolList(sdp_handle, BTA_AG_NUM_PROTO_ELEMS, proto_elem_list); + + /* add service class id list */ + svc_class_id_list[0] = service_uuid; + svc_class_id_list[1] = UUID_SERVCLASS_GENERIC_AUDIO; + result &= SDP_AddServiceClassIdList(sdp_handle, BTA_AG_NUM_SVC_ELEMS, svc_class_id_list); + + /* add profile descriptor list */ + if (service_uuid == UUID_SERVCLASS_AG_HANDSFREE) { + profile_uuid = UUID_SERVCLASS_HF_HANDSFREE; + version = HFP_VERSION_1_6; + } else { + profile_uuid = UUID_SERVCLASS_HEADSET; + version = HSP_VERSION_1_2; + } + result &= SDP_AddProfileDescriptorList(sdp_handle, profile_uuid, version); + + /* add service name */ + if (p_service_name != NULL && p_service_name[0] != 0) { + result &= SDP_AddAttribute(sdp_handle, ATTR_ID_SERVICE_NAME, TEXT_STR_DESC_TYPE, + (UINT32)(strlen(p_service_name)+1), (UINT8 *) p_service_name); + } + + /* add features and network */ + if (service_uuid == UUID_SERVCLASS_AG_HANDSFREE) { + network = (features & BTA_AG_FEAT_REJECT) ? 1 : 0; + result &= SDP_AddAttribute(sdp_handle, ATTR_ID_DATA_STORES_OR_NETWORK, + UINT_DESC_TYPE, 1, &network); + + if (features & BTA_AG_FEAT_CODEC) { + codec_supported = TRUE; + } + features &= BTA_AG_SDP_FEAT_SPEC; + /* Codec bit position is different in SDP and in BRSF */ + if (codec_supported) { + features |= 0x0020; + } + UINT16_TO_BE_FIELD(buf, features); + result &= SDP_AddAttribute(sdp_handle, ATTR_ID_SUPPORTED_FEATURES, UINT_DESC_TYPE, 2, buf); + } + /* add browse group list */ + result &= SDP_AddUuidSequence(sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, browse_list); + return result; +} + +/******************************************************************************* +** +** Function bta_ag_create_records +** +** Description Create SDP records for registered services. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_create_records(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + tBTA_SERVICE_MASK services; + services = p_scb->reg_services >> BTA_HSP_SERVICE_ID; + + for (int i = 0; i < BTA_AG_NUM_IDX && services != 0; i++, services >>= 1) { + /* if service is set in mask */ + if (services & 1) { + /* add sdp record if not already registered */ + if (bta_ag_cb.profile[i].sdp_handle == 0) { + bta_ag_cb.profile[i].sdp_handle = SDP_CreateRecord(); + bta_ag_cb.profile[i].scn = BTM_AllocateSCN(); + bta_ag_add_record(bta_ag_uuid[i], p_data->api_register.p_name[i], + bta_ag_cb.profile[i].scn, p_data->api_register.features, + bta_ag_cb.profile[i].sdp_handle); + bta_sys_add_uuid(bta_ag_uuid[i]); + } + } + } + p_scb->hsp_version = HSP_VERSION_1_2; +} + +/******************************************************************************* +** +** Function bta_ag_del_records +** +** Description Delete SDP records for any registered services. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_del_records(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + tBTA_AG_SCB *p = &bta_ag_cb.scb[0]; + tBTA_SERVICE_MASK services; + tBTA_SERVICE_MASK others = 0; + int i; + UNUSED(p_data); + + /* get services of all other registered servers */ + for (i = 0; i < BTA_AG_NUM_IDX; i++, p++) { + if (p_scb == p) { + continue; + } + if (p->in_use && p->dealloc == FALSE) { + others |= p->reg_services; + } + } + others >>= BTA_HSP_SERVICE_ID; + services = p_scb->reg_services >> BTA_HSP_SERVICE_ID; + for (i = 0; i < BTA_AG_NUM_IDX && services != 0; i++, services >>= 1, others >>= 1) + { + /* if service registered for this scb and not registered for any other scb */ + if (((services & 1) == 1) && ((others & 1) == 0)) { + APPL_TRACE_DEBUG("bta_ag_del_records %d", i); + if (bta_ag_cb.profile[i].sdp_handle != 0) { + SDP_DeleteRecord(bta_ag_cb.profile[i].sdp_handle); + bta_ag_cb.profile[i].sdp_handle = 0; + } + BTM_FreeSCN(bta_ag_cb.profile[i].scn); + BTM_SecClrService(bta_ag_sec_id[i]); + bta_sys_remove_uuid(bta_ag_uuid[i]); + } + } +} + +/******************************************************************************* +** +** Function bta_ag_sdp_find_attr +** +** Description Process SDP discovery results to find requested attributes +** for requested service. +** +** +** Returns TRUE if results found, FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN bta_ag_sdp_find_attr(tBTA_AG_SCB *p_scb, tBTA_SERVICE_MASK service) +{ + tSDP_DISC_REC *p_rec = NULL; + tSDP_DISC_ATTR *p_attr; + tSDP_PROTOCOL_ELEM pe; + UINT16 uuid; + BOOLEAN result = FALSE; + + if (service & BTA_HFP_SERVICE_MASK) { + uuid = UUID_SERVCLASS_HF_HANDSFREE; + p_scb->peer_version = HFP_VERSION_1_1; /* Default version */ + } else if (service & BTA_HSP_SERVICE_MASK && p_scb->role == BTA_AG_INT) { + uuid = UUID_SERVCLASS_HEADSET_HS; + p_scb->peer_version = 0x0100; /* Default version */ + } else { + return result; + } + + /* loop through all records we found */ + while (TRUE) + { + /* get next record; if none found, we're done */ + if ((p_rec = SDP_FindServiceInDb(p_scb->p_disc_db, uuid, p_rec)) == NULL) { + if (uuid == UUID_SERVCLASS_HEADSET_HS) { + /* Search again in case the peer device is HSP v1.0 */ + uuid = UUID_SERVCLASS_HEADSET; + if ((p_rec = SDP_FindServiceInDb(p_scb->p_disc_db, uuid, p_rec)) == NULL) { + break; + } + } else { + break; + } + } + + /* get scn from proto desc list if initiator */ + if (p_scb->role == BTA_AG_INT) { + if (SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_RFCOMM, &pe)) { + p_scb->peer_scn = (UINT8) pe.params[0]; + } else { + continue; + } + } + /* get profile version (if failure, version parameter is not updated) */ + SDP_FindProfileVersionInRec(p_rec, uuid, &p_scb->peer_version); + /* get features if HFP */ + if (service & BTA_HFP_SERVICE_MASK) { + if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SUPPORTED_FEATURES)) != NULL) { + /* Found attribute. Get value. */ + /* There might be race condition between SDP and BRSF. */ + /* Do not update if we already received BRSF. */ + if (p_scb->peer_features == 0) + p_scb->peer_features = p_attr->attr_value.v.u16; + } + } else { + /* HSP */ + if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_REMOTE_AUDIO_VOLUME_CONTROL)) != NULL) { + /* Remote volume control of HSP */ + if (p_attr->attr_value.v.u8) { + p_scb->peer_features |= BTA_AG_PEER_FEAT_VOL; + } else { + p_scb->peer_features &= ~BTA_AG_PEER_FEAT_VOL; + } + } + } + /* found what we needed */ + result = TRUE; + break; + } + return result; +} + +/******************************************************************************* +** +** Function bta_ag_do_disc +** +** Description Do service discovery. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_do_disc(tBTA_AG_SCB *p_scb, tBTA_SERVICE_MASK service) +{ + tSDP_UUID uuid_list[2]; + UINT16 num_uuid = 1; + UINT16 attr_list[4]; + UINT8 num_attr; + BOOLEAN db_inited = FALSE; + + /* HFP initiator; get proto list and features */ + if (service & BTA_HFP_SERVICE_MASK && p_scb->role == BTA_AG_INT) { + attr_list[0] = ATTR_ID_SERVICE_CLASS_ID_LIST; + attr_list[1] = ATTR_ID_PROTOCOL_DESC_LIST; + attr_list[2] = ATTR_ID_BT_PROFILE_DESC_LIST; + attr_list[3] = ATTR_ID_SUPPORTED_FEATURES; + num_attr = 4; + uuid_list[0].uu.uuid16 = UUID_SERVCLASS_HF_HANDSFREE; + } else if (service & BTA_HFP_SERVICE_MASK && p_scb->role == BTA_AG_ACP) { + /* HFP acceptor; get features */ + attr_list[0] = ATTR_ID_SERVICE_CLASS_ID_LIST; + attr_list[1] = ATTR_ID_BT_PROFILE_DESC_LIST; + attr_list[2] = ATTR_ID_SUPPORTED_FEATURES; + num_attr = 3; + uuid_list[0].uu.uuid16 = UUID_SERVCLASS_HF_HANDSFREE; + } else if (service & BTA_HSP_SERVICE_MASK && p_scb->role == BTA_AG_INT) { + /* HSP initiator; get proto list */ + attr_list[0] = ATTR_ID_SERVICE_CLASS_ID_LIST; + attr_list[1] = ATTR_ID_PROTOCOL_DESC_LIST; + attr_list[2] = ATTR_ID_BT_PROFILE_DESC_LIST; + attr_list[3] = ATTR_ID_REMOTE_AUDIO_VOLUME_CONTROL; + num_attr = 4; + uuid_list[0].uu.uuid16 = UUID_SERVCLASS_HEADSET; /* Legacy from HSP v1.0 */ + if (p_scb->hsp_version >= HSP_VERSION_1_2) { + uuid_list[1].uu.uuid16 = UUID_SERVCLASS_HEADSET_HS; + num_uuid = 2; + } + } else { + /* HSP acceptor; no discovery */ + return; + } + + /* allocate buffer for sdp database */ + p_scb->p_disc_db = (tSDP_DISCOVERY_DB *) osi_malloc(BTA_AG_DISC_BUF_SIZE); + if(p_scb->p_disc_db) { + /* set up service discovery database; attr happens to be attr_list len */ + uuid_list[0].len = LEN_UUID_16; + uuid_list[1].len = LEN_UUID_16; + db_inited = SDP_InitDiscoveryDb(p_scb->p_disc_db, BTA_AG_DISC_BUF_SIZE, num_uuid, + uuid_list, num_attr, attr_list); + } + + if(db_inited) { + /*Service discovery not initiated */ + db_inited = SDP_ServiceSearchAttributeRequest(p_scb->peer_addr, p_scb->p_disc_db, + bta_ag_sdp_cback_tbl[bta_ag_scb_to_idx(p_scb) - 1]); + } + if(!db_inited) { + /*free discover db */ + bta_ag_free_db(p_scb, NULL); + /* sent failed event */ + bta_ag_sm_execute(p_scb, BTA_AG_DISC_FAIL_EVT, NULL); + } +} + +/******************************************************************************* +** +** Function bta_ag_free_db +** +** Description Free discovery database. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_free_db(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) +{ + UNUSED(p_data); + if (p_scb->p_disc_db != NULL) { + osi_free(p_scb->p_disc_db); + p_scb->p_disc_db = NULL; + } +} + +#endif /* #if (BTA_AG_INCLUDED == TRUE) */ \ No newline at end of file diff --git a/components/bt/host/bluedroid/bta/hf_ag/include/bta_ag_at.h b/components/bt/host/bluedroid/bta/hf_ag/include/bta_ag_at.h new file mode 100644 index 000000000..d9d3cdebc --- /dev/null +++ b/components/bt/host/bluedroid/bta/hf_ag/include/bta_ag_at.h @@ -0,0 +1,126 @@ +/****************************************************************************** + * + * Copyright (C) 2004-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * Interface file for BTA AG AT command interpreter. + * + ******************************************************************************/ +#ifndef BTA_AG_AT_H +#define BTA_AG_AT_H + +#include "stack/bt_types.h" +#include "common/bt_target.h" + +/***************************************************************************** +** Constants +*****************************************************************************/ +#if (BTA_AG_INCLUDED == TRUE) + +/* AT command argument capabilities */ +#define BTA_AG_AT_NONE 0x01 /* no argument */ +#define BTA_AG_AT_SET 0x02 /* set value */ +#define BTA_AG_AT_READ 0x04 /* read value */ +#define BTA_AG_AT_TEST 0x08 /* test value range */ +#define BTA_AG_AT_FREE 0x10 /* freeform argument */ + +/* AT command argument format */ +#define BTA_AG_AT_STR 0 /* string */ +#define BTA_AG_AT_INT 1 /* integer */ + +/***************************************************************************** +** Data types +*****************************************************************************/ + +/* AT command table element */ +typedef struct +{ + const char *p_cmd; /* AT command string */ + UINT8 arg_type; /* allowable argument type syntax */ + UINT8 fmt; /* whether arg is int or string */ + UINT8 min; /* minimum value for int arg */ + INT16 max; /* maximum value for int arg */ +} tBTA_AG_AT_CMD; + +/* callback function executed when command is parsed */ +typedef void (tBTA_AG_AT_CMD_CBACK)(void *p_user, UINT16 cmd, UINT8 arg_type, + char *p_arg, INT16 int_arg); + +/* callback function executed to send "ERROR" result code */ +typedef void (tBTA_AG_AT_ERR_CBACK)(void *p_user, BOOLEAN unknown, char *p_arg); + +/* AT command parsing control block */ +typedef struct +{ + tBTA_AG_AT_CMD *p_at_tbl; /* AT command table */ + tBTA_AG_AT_CMD_CBACK *p_cmd_cback; /* command callback */ + tBTA_AG_AT_ERR_CBACK *p_err_cback; /* error callback */ + void *p_user; /* user-defined data */ + char *p_cmd_buf; /* temp parsing buffer */ + UINT16 cmd_pos; /* position in temp buffer */ + UINT16 cmd_max_len; /* length of temp buffer to allocate */ + UINT8 state; /* parsing state */ +} tBTA_AG_AT_CB; + +/***************************************************************************** +** Function prototypes +*****************************************************************************/ + +/***************************************************************************** +** +** Function bta_ag_at_init +** +** Description Initialize the AT command parser control block. +** +** +** Returns void +** +*****************************************************************************/ +extern void bta_ag_at_init(tBTA_AG_AT_CB *p_cb); + +/***************************************************************************** +** +** Function bta_ag_at_reinit +** +** Description Re-initialize the AT command parser control block. This +** function resets the AT command parser state and frees +** any GKI buffer. +** +** +** Returns void +** +*****************************************************************************/ +extern void bta_ag_at_reinit(tBTA_AG_AT_CB *p_cb); + +/***************************************************************************** +** +** Function bta_ag_at_parse +** +** Description Parse AT commands. This function will take the input +** character string and parse it for AT commands according to +** the AT command table passed in the control block. +** +** +** Returns void +** +*****************************************************************************/ +extern void bta_ag_at_parse(tBTA_AG_AT_CB *p_cb, char *p_buf, UINT16 len); + +#endif /* #if (BTA_AG_INCLUDED == TRUE) */ + +#endif /* BTA_AG_AT_H */ \ No newline at end of file diff --git a/components/bt/host/bluedroid/bta/hf_ag/include/bta_ag_int.h b/components/bt/host/bluedroid/bta/hf_ag/include/bta_ag_int.h new file mode 100644 index 000000000..440fe1931 --- /dev/null +++ b/components/bt/host/bluedroid/bta/hf_ag/include/bta_ag_int.h @@ -0,0 +1,446 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the private interface file for the BTA audio gateway. + * + ******************************************************************************/ +#ifndef BTA_AG_INT_H +#define BTA_AG_INT_H + +#include "bta_ag_at.h" +#include "bta/bta_sys.h" +#include "bta/bta_api.h" +#include "bta/bta_ag_api.h" +#include "stack/sdp_api.h" + +#if (BTA_AG_INCLUDED == TRUE) + +/* Send RING & CLIP in one AT cmd */ +#ifndef BTA_AG_MULTI_RESULT_INCLUDED +#define BTA_AG_MULTI_RESULT_INCLUDED FALSE +#endif + +/* Replace : in VGS and VGM for HSP */ +#ifndef BTA_HSP_RESULT_REPLACE_COLON +#define BTA_HSP_RESULT_REPLACE_COLON TRUE +#endif + +/***************************************************************************** +** Constants +*****************************************************************************/ +#define HFP_VERSION_1_1 0x0101 +#define HFP_VERSION_1_5 0x0105 +#define HFP_VERSION_1_6 0x0106 + +#define HSP_VERSION_1_0 0x0100 +#define HSP_VERSION_1_2 0x0102 + +/* Number of SCBs (AG service instances that can be registered) */ +#ifndef BTA_AG_NUM_SCB +#define BTA_AG_NUM_SCB 1 +#endif + +/* Timer to wait for retry in case of collision */ +#ifndef BTA_AG_COLLISION_TIMER +#define BTA_AG_COLLISION_TIMER 2000 +#endif + +/* RFCOMM MTU SIZE */ +#define BTA_AG_MTU 256 + +/* Internal profile indexes */ +#define BTA_AG_HSP 0 /* index for HSP */ +#define BTA_AG_HFP 1 /* index for HFP */ +#define BTA_AG_NUM_IDX 2 /* number of profile indexes */ + +/* profile role for connection */ +#define BTA_AG_ACP 0 /* accepted connection */ +#define BTA_AG_INT 1 /* initiating connection */ + +/* feature mask that matches spec */ +#define BTA_AG_BSRF_FEAT_SPEC (BTA_AG_FEAT_3WAY | BTA_AG_FEAT_ECNR | \ + BTA_AG_FEAT_VREC | BTA_AG_FEAT_INBAND | \ + BTA_AG_FEAT_VTAG | BTA_AG_FEAT_REJECT | \ + BTA_AG_FEAT_ECS | BTA_AG_FEAT_ECC | \ + BTA_AG_FEAT_EXTERR | BTA_AG_FEAT_CODEC | \ + BTA_AG_FEAT_VOIP) + +#define BTA_AG_SDP_FEAT_SPEC (BTA_AG_FEAT_3WAY | BTA_AG_FEAT_ECNR | \ + BTA_AG_FEAT_VREC | BTA_AG_FEAT_INBAND | \ + BTA_AG_FEAT_VTAG) + +enum +{ + /* these events are handled by the state machine */ + BTA_AG_API_REGISTER_EVT = BTA_SYS_EVT_START(BTA_ID_AG), + BTA_AG_API_DEREGISTER_EVT, + BTA_AG_API_OPEN_EVT, + BTA_AG_API_CLOSE_EVT, + BTA_AG_API_AUDIO_OPEN_EVT, + BTA_AG_API_AUDIO_CLOSE_EVT, + BTA_AG_API_RESULT_EVT, + BTA_AG_API_SETCODEC_EVT, + BTA_AG_RFC_OPEN_EVT, + BTA_AG_RFC_CLOSE_EVT, + BTA_AG_RFC_SRV_CLOSE_EVT, + BTA_AG_RFC_DATA_EVT, + BTA_AG_SCO_OPEN_EVT, + BTA_AG_SCO_CLOSE_EVT, + BTA_AG_DISC_ACP_RES_EVT, + BTA_AG_DISC_INT_RES_EVT, + BTA_AG_DISC_OK_EVT, + BTA_AG_DISC_FAIL_EVT, + BTA_AG_CI_RX_WRITE_EVT, + BTA_AG_RING_TOUT_EVT, + BTA_AG_SVC_TOUT_EVT, + #if (BTM_SCO_HCI_INCLUDED == TRUE ) + BTA_AG_CI_SCO_DATA_EVT, + #endif /* (BTM_SCO_HCI_INCLUDED == TRUE ) */ + BTA_AG_CI_SLC_READY_EVT, + BTA_AG_MAX_EVT, + + /* these events are handled outside of the state machine */ + BTA_AG_API_ENABLE_EVT, + BTA_AG_API_DISABLE_EVT +}; + +/* Actions to perform after a SCO event */ +enum +{ + BTA_AG_POST_SCO_NONE, /* no action */ + BTA_AG_POST_SCO_CLOSE_RFC, /* close RFCOMM channel after SCO closes */ + BTA_AG_POST_SCO_RING, /* send RING result code after SCO opens */ + BTA_AG_POST_SCO_CALL_CONN, /* send call indicators after SCO opens/closes */ + BTA_AG_POST_SCO_CALL_ORIG, /* send call indicators after SCO closes */ + BTA_AG_POST_SCO_CALL_END, /* send call indicators after SCO closes */ + BTA_AG_POST_SCO_CALL_END_INCALL /* send call indicators for end call & incoming call after SCO closes */ +}; + +/* sco states */ +enum +{ + BTA_AG_SCO_SHUTDOWN_ST, /* no sco listening, all sco connections closed */ + BTA_AG_SCO_LISTEN_ST, /* sco listening */ +#if (BTM_WBS_INCLUDED == TRUE ) + BTA_AG_SCO_CODEC_ST, /* sco codec negotiation */ +#endif + BTA_AG_SCO_OPENING_ST, /* sco connection opening */ + BTA_AG_SCO_OPEN_CL_ST, /* opening sco connection being closed */ + BTA_AG_SCO_OPEN_XFER_ST, /* opening sco connection being transferred */ + BTA_AG_SCO_OPEN_ST, /* sco open */ + BTA_AG_SCO_CLOSING_ST, /* sco closing */ + BTA_AG_SCO_CLOSE_OP_ST, /* closing sco being opened */ + BTA_AG_SCO_CLOSE_XFER_ST, /* closing sco being transferred */ + BTA_AG_SCO_SHUTTING_ST /* sco shutting down */ +}; + +/***************************************************************************** +** Data types +*****************************************************************************/ + +/* data type for BTA_AG_API_ENABLE_EVT */ +typedef struct +{ + BT_HDR hdr; + tBTA_AG_PARSE_MODE parse_mode; + tBTA_AG_CBACK *p_cback; +} tBTA_AG_API_ENABLE; + +/* data type for BTA_AG_API_REGISTER_EVT */ +typedef struct +{ + BT_HDR hdr; + char p_name[2][BTA_SERVICE_NAME_LEN+1]; + tBTA_SERVICE_MASK services; + tBTA_SEC sec_mask; + tBTA_AG_FEAT features; + UINT8 app_id; +} tBTA_AG_API_REGISTER; + +/* data type for BTA_AG_API_OPEN_EVT */ +typedef struct +{ + BT_HDR hdr; + BD_ADDR bd_addr; + tBTA_SERVICE_MASK services; + tBTA_SEC sec_mask; +} tBTA_AG_API_OPEN; + +/* data type for BTA_AG_API_RESULT_EVT */ +typedef struct +{ + BT_HDR hdr; + tBTA_AG_RES result; + tBTA_AG_RES_DATA data; +} tBTA_AG_API_RESULT; + +/* data type for BTA_AG_API_SETCODEC_EVT */ +typedef struct +{ + BT_HDR hdr; + tBTA_AG_PEER_CODEC codec; +} tBTA_AG_API_SETCODEC; + +/* data type for BTA_AG_DISC_RESULT_EVT */ +typedef struct +{ + BT_HDR hdr; + UINT16 status; +} tBTA_AG_DISC_RESULT; + +/* data type for RFCOMM events */ +typedef struct +{ + BT_HDR hdr; + UINT16 port_handle; +} tBTA_AG_RFC; + +/* data type for BTA_AG_CI_RX_WRITE_EVT */ +typedef struct +{ + BT_HDR hdr; + char p_data[BTA_AG_MTU+1]; +} tBTA_AG_CI_RX_WRITE; + +/* union of all event datatypes */ +typedef union +{ + BT_HDR hdr; + tBTA_AG_API_ENABLE api_enable; + tBTA_AG_API_REGISTER api_register; + tBTA_AG_API_OPEN api_open; + tBTA_AG_API_RESULT api_result; +#if (BTM_WBS_INCLUDED == TRUE ) + tBTA_AG_API_SETCODEC api_setcodec; +#endif + tBTA_AG_DISC_RESULT disc_result; + tBTA_AG_RFC rfc; + tBTA_AG_CI_RX_WRITE ci_rx_write; +} tBTA_AG_DATA; + +/* type for each profile */ +typedef struct +{ + UINT32 sdp_handle; + UINT8 scn; +} tBTA_AG_PROFILE; + +#if (BTM_WBS_INCLUDED == TRUE) +typedef enum +{ + BTA_AG_SCO_MSBC_SETTINGS_T2 = 0, /* preferred/default when codec is mSBC */ + BTA_AG_SCO_MSBC_SETTINGS_T1, +} tBTA_AG_SCO_MSBC_SETTINGS; +#endif + +/* type for each service control block */ +typedef struct +{ + char clip[BTA_AG_AT_MAX_LEN+10]; /* number string used for CLIP */ + UINT16 serv_handle[BTA_AG_NUM_IDX]; /* RFCOMM server handles */ + tBTA_AG_AT_CB at_cb; /* AT command interpreter */ + TIMER_LIST_ENT act_timer; /* ring timer */ + BD_ADDR peer_addr; /* peer bd address */ + tSDP_DISCOVERY_DB *p_disc_db; /* pointer to discovery database */ + tBTA_SERVICE_MASK reg_services; /* services specified in register API */ + tBTA_SERVICE_MASK open_services; /* services specified in open API */ + UINT16 conn_handle; /* RFCOMM handle of connected service */ + tBTA_SEC serv_sec_mask; /* server security mask */ + tBTA_SEC cli_sec_mask; /* client security mask */ + tBTA_AG_FEAT features; /* features registered by application */ + tBTA_AG_PEER_FEAT peer_features; /* peer device features */ + UINT16 peer_version; /* profile version of peer device */ + UINT16 hsp_version; /* HSP profile version */ +#if (BTM_WBS_INCLUDED == TRUE) + tBTA_AG_PEER_CODEC peer_codecs; /* codecs for eSCO supported by the peer */ + tBTA_AG_PEER_CODEC sco_codec; /* codec to be used for eSCO connection */ + tBTA_AG_PEER_CODEC inuse_codec; /* codec being used for the current SCO connection */ + BOOLEAN codec_updated; /* set to TRUE whenever the app updates codec type */ + BOOLEAN codec_fallback; /* If sco nego fails for mSBC, fallback to CVSD */ + tBTA_AG_SCO_MSBC_SETTINGS codec_msbc_settings; /* settings to be used for the impending eSCO */ + TIMER_LIST_ENT cn_timer; /* codec negotiation timer */ +#endif + UINT16 sco_idx; /* SCO handle */ + BOOLEAN in_use; /* scb in use */ + BOOLEAN dealloc; /* TRUE if service shutting down */ + BOOLEAN clip_enabled; /* set to TRUE if HF enables CLIP reporting */ + BOOLEAN ccwa_enabled; /* set to TRUE if HF enables CCWA reporting */ + BOOLEAN cmer_enabled; /* set to TRUE if HF enables CMER reporting */ + BOOLEAN cmee_enabled; /* set to TRUE if HF enables CME ERROR reporting */ + BOOLEAN inband_enabled; /* set to TRUE if inband ring enabled */ + BOOLEAN svc_conn; /* set to TRUE when service level connection up */ + TIMER_LIST_ENT colli_timer; /* Collision timer */ + BOOLEAN colli_tmr_on; /* TRUE if collision timer is active */ + UINT8 state; /* state machine state */ + UINT8 conn_service; /* connected service */ + UINT8 peer_scn; /* peer scn */ + UINT8 app_id; /* application id */ + UINT8 role; /* initiator/acceptor role */ + tBTM_SCO_CODEC_TYPE negotiated_codec; /* negotiated codec */ + UINT8 post_sco; /* action to perform after sco event */ + UINT8 call_ind; /* CIEV call indicator value */ + UINT8 callsetup_ind; /* CIEV callsetup indicator value */ + UINT8 service_ind; /* CIEV service indicator value */ + UINT8 signal_ind; /* CIEV signal indicator value */ + UINT8 roam_ind; /* CIEV roam indicator value */ + UINT8 battchg_ind; /* CIEV battery charge indicator value */ + UINT8 callheld_ind; /* CIEV call held indicator value */ + BOOLEAN retry_with_sco_only; /* indicator to try with SCO only when eSCO fails */ + UINT32 bia_masked_out; /* indicators HF does not want us to send */ + /* add */ + UINT16 in_pkt_len; + UINT16 out_pkt_len; + UINT8 link_type; /* BTM_LINK_TYPE_SCO or BTM_LINK_TYPE_ESCO */ + UINT8 tx_interval; + UINT8 retrans_window; + UINT8 air_mode; +} tBTA_AG_SCB; + +/* type for sco data */ +typedef struct +{ + tBTM_ESCO_CONN_REQ_EVT_DATA conn_data; /* CO data for pending conn requestS */ + tBTA_AG_SCB *p_curr_scb; /* SCB associated with SCO connection */ + tBTA_AG_SCB *p_xfer_scb; /* SCB associated with SCO transfer */ + UINT16 cur_idx; /* SCO handle */ + UINT8 state; /* SCO state variable */ + BOOLEAN param_updated; /* if params were updated to non-default */ + tBTM_ESCO_PARAMS params; /* ESCO parameters */ + tBTA_AG_DATA *p_data; +} tBTA_AG_SCO_CB; + +/* type for AG control block */ +typedef struct +{ + tBTA_AG_SCB scb[BTA_AG_NUM_SCB]; /* service control blocks */ + tBTA_AG_PROFILE profile[BTA_AG_NUM_IDX]; /* profile-specific data */ + tBTA_AG_SCO_CB sco; /* SCO data */ + tBTA_AG_CBACK *p_cback; /* application callback */ + tBTA_AG_PARSE_MODE parse_mode; /* parse/pass-through mode */ + BOOLEAN msbc_enabled; +} tBTA_AG_CB; + +/***************************************************************************** +** Global data +*****************************************************************************/ + +/* constant lookup tables */ +extern const UINT16 bta_ag_uuid[BTA_AG_NUM_IDX]; +extern const UINT8 bta_ag_sec_id[BTA_AG_NUM_IDX]; +extern const tBTA_AG_AT_CMD *bta_ag_at_tbl[BTA_AG_NUM_IDX]; + +/* control block declaration */ +#if BTA_DYNAMIC_MEMORY == FALSE +extern tBTA_AG_CB bta_ag_cb; +#else +extern tBTA_AG_CB *bta_ag_cb_ptr; +#define bta_ag_cb (*bta_ag_cb_ptr) +#endif + +/* config struct */ +extern tBTA_AG_CFG *p_bta_ag_cfg; + +/***************************************************************************** +** Function prototypes +*****************************************************************************/ + +/* main functions */ +extern void bta_ag_scb_dealloc(tBTA_AG_SCB *p_scb); +extern UINT16 bta_ag_scb_to_idx(tBTA_AG_SCB *p_scb); +extern tBTA_AG_SCB *bta_ag_scb_by_idx(UINT16 idx); +extern UINT8 bta_ag_service_to_idx(tBTA_SERVICE_MASK services); +extern UINT16 bta_ag_idx_by_bdaddr(BD_ADDR peer_addr); +extern BOOLEAN bta_ag_other_scb_open(tBTA_AG_SCB *p_curr_scb); +extern BOOLEAN bta_ag_scb_open(tBTA_AG_SCB *p_curr_scb); +extern tBTA_AG_SCB *bta_ag_get_other_idle_scb (tBTA_AG_SCB *p_curr_scb); +extern void bta_ag_sm_execute(tBTA_AG_SCB *p_scb, UINT16 event, tBTA_AG_DATA *p_data); +extern BOOLEAN bta_ag_hdl_event(BT_HDR *p_msg); +extern void bta_ag_collision_cback(tBTA_SYS_CONN_STATUS status, UINT8 id, UINT8 app_id, BD_ADDR peer_addr); +extern void bta_ag_resume_open(tBTA_AG_SCB *p_scb); + +/* SDP functions */ +extern BOOLEAN bta_ag_add_record(UINT16 service_uuid, char *p_service_name, UINT8 scn, tBTA_AG_FEAT features, UINT32 sdp_handle); +extern void bta_ag_create_records(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_del_records(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern BOOLEAN bta_ag_sdp_find_attr(tBTA_AG_SCB *p_scb, tBTA_SERVICE_MASK service); +extern void bta_ag_do_disc(tBTA_AG_SCB *p_scb, tBTA_SERVICE_MASK service); +extern void bta_ag_free_db(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); + +/* RFCOMM functions */ +extern void bta_ag_start_servers(tBTA_AG_SCB *p_scb, tBTA_SERVICE_MASK services); +extern void bta_ag_close_servers(tBTA_AG_SCB *p_scb, tBTA_SERVICE_MASK services); +extern BOOLEAN bta_ag_is_server_closed (tBTA_AG_SCB *p_scb); +extern void bta_ag_rfc_do_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_rfc_do_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); + +/* SCO functions */ +extern BOOLEAN bta_ag_sco_is_open(tBTA_AG_SCB *p_scb); +extern BOOLEAN bta_ag_sco_is_opening(tBTA_AG_SCB *p_scb); +extern void bta_ag_sco_conn_rsp(tBTA_AG_SCB *p_scb, tBTM_ESCO_CONN_REQ_EVT_DATA *p_data); + +/* AT command functions */ +extern void bta_ag_at_hsp_cback(tBTA_AG_SCB *p_scb, UINT16 cmd, UINT8 arg_type, char *p_arg, INT16 int_arg); +extern void bta_ag_at_hfp_cback(tBTA_AG_SCB *p_scb, UINT16 cmd, UINT8 arg_type, char *p_arg, INT16 int_arg); +extern void bta_ag_at_err_cback(tBTA_AG_SCB *p_scb, BOOLEAN unknown, char *p_arg); +extern BOOLEAN bta_ag_inband_enabled(tBTA_AG_SCB *p_scb); +extern void bta_ag_send_call_inds(tBTA_AG_SCB *p_scb, tBTA_AG_RES result); + +/* Action functions */ +extern void bta_ag_register(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_deregister(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_start_dereg(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_start_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_start_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_disc_int_res(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_disc_acp_res(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_disc_fail(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_open_fail(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_rfc_fail(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_rfc_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_rfc_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_rfc_acp_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_rfc_data(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_sco_listen(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_sco_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_sco_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +#if (BTM_WBS_INCLUDED == TRUE) +extern void bta_ag_sco_codec_nego(tBTA_AG_SCB *p_scb, BOOLEAN result); +extern void bta_ag_codec_negotiate (tBTA_AG_SCB *p_scb); +#endif +extern void bta_ag_sco_shutdown(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_sco_conn_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_sco_conn_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_post_sco_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_post_sco_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_svc_conn_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_result(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_setcodec(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +#if (BTM_WBS_INCLUDED == TRUE) +extern void bta_ag_send_bcs(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +#endif +extern void bta_ag_send_ring(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_ci_sco_data(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_set_esco_param(BOOLEAN set_reset, tBTM_ESCO_PARAMS *param); +extern void bta_ag_ci_rx_data(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); +extern void bta_ag_rcvd_slc_ready(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data); + +#endif /* #if (BTA_AG_INCLUDED == TRUE) */ + +#endif /* BTA_AG_INT_H */ \ No newline at end of file diff --git a/components/bt/host/bluedroid/bta/include/bta/bta_ag_api.h b/components/bt/host/bluedroid/bta/include/bta/bta_ag_api.h new file mode 100644 index 000000000..993f2fd4b --- /dev/null +++ b/components/bt/host/bluedroid/bta/include/bta/bta_ag_api.h @@ -0,0 +1,594 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the public interface file for the audio gateway (AG) subsystem + * of BTA, Broadcom's Bluetooth application layer for mobile phones. + * + ******************************************************************************/ +#ifndef BTA_AG_API_H +#define BTA_AG_API_H + +#include "bta_api.h" +#include "bta_hfp_defs.h" +#include "esp_hf_defs.h" + +#if (BTA_AG_INCLUDED == TRUE) +/***************************************************************************** +** Constants and data types +*****************************************************************************/ + +/* AG feature masks */ +#define BTA_AG_FEAT_3WAY 0x00000001 /* Three-way calling */ +#define BTA_AG_FEAT_ECNR 0x00000002 /* Echo cancellation and/or noise reduction */ +#define BTA_AG_FEAT_VREC 0x00000004 /* Voice recognition */ +#define BTA_AG_FEAT_INBAND 0x00000008 /* In-band ring tone */ +#define BTA_AG_FEAT_VTAG 0x00000010 /* Attach a phone number to a voice tag */ +#define BTA_AG_FEAT_REJECT 0x00000020 /* Ability to reject incoming call */ +#define BTA_AG_FEAT_ECS 0x00000040 /* Enhanced Call Status */ +#define BTA_AG_FEAT_ECC 0x00000080 /* Enhanced Call Control */ +#define BTA_AG_FEAT_EXTERR 0x00000100 /* Extended error codes */ +#define BTA_AG_FEAT_CODEC 0x00000200 /* Codec Negotiation */ +#define BTA_AG_FEAT_VOIP 0x00000400 /* VoIP call */ +/* Proprietary features: using 31 ~ 16 bits */ +#define BTA_AG_FEAT_BTRH 0x00010000 /* CCAP incoming call hold */ +#define BTA_AG_FEAT_UNAT 0x00020000 /* Pass unknown AT commands to application */ +#define BTA_AG_FEAT_NOSCO 0x00040000 /* No SCO control performed by BTA AG */ +#define BTA_AG_FEAT_NO_ESCO 0x00080000 /* Do not allow or use eSCO */ +typedef UINT32 tBTA_AG_FEAT; + +/* HFP peer features */ +#define BTA_AG_PEER_FEAT_ECNR 0x0001 /* Echo cancellation and/or noise reduction */ +#define BTA_AG_PEER_FEAT_3WAY 0x0002 /* Call waiting and three-way calling */ +#define BTA_AG_PEER_FEAT_CLI 0x0004 /* Caller ID presentation capability */ +#define BTA_AG_PEER_FEAT_VREC 0x0008 /* Voice recognition activation */ +#define BTA_AG_PEER_FEAT_VOL 0x0010 /* Remote volume control */ +#define BTA_AG_PEER_FEAT_ECS 0x0020 /* Enhanced Call Status */ +#define BTA_AG_PEER_FEAT_ECC 0x0040 /* Enhanced Call Control */ +#define BTA_AG_PEER_FEAT_CODEC 0x0080 /* Codec Negotiation */ +#define BTA_AG_PEER_FEAT_VOIP 0x0100 /* VoIP call */ +typedef UINT16 tBTA_AG_PEER_FEAT; + +/* AG extended call handling - masks not related to any spec */ +#define BTA_AG_CLIENT_CHLD_REL 0x00000001 /* 0 Release waiting call or held calls */ +#define BTA_AG_CLIENT_CHLD_REL_ACC 0x00000002 /* 1 Release active calls and accept other (waiting or held) cal */ +#define BTA_AG_CLIENT_CHLD_REL_X 0x00000004 /* 1x Release x call*/ +#define BTA_AG_CLIENT_CHLD_HOLD_ACC 0x00000008 /* 2 Active calls on hold and accept other call */ +#define BTA_AG_CLIENT_CHLD_PRIV_X 0x00000010 /* 2x Active multiparty call on hold except call x */ +#define BTA_AG_CLIENT_CHLD_MERGE 0x00000020 /* 3 Add held call to multiparty */ +#define BTA_AG_CLIENT_CHLD_MERGE_DETACH 0x00000040 /* 4 Add held call to multiparty */ +typedef UINT16 tBTA_AG_CHLD_FEAT; + +/* HFP peer supported codec masks */ +// TODO(google) This should use common definitions +// in hci/include/hci_audio.h +#define BTA_AG_CODEC_NONE BTM_SCO_CODEC_NONE +#define BTA_AG_CODEC_CVSD BTM_SCO_CODEC_CVSD /* CVSD */ +#define BTA_AG_CODEC_MSBC BTM_SCO_CODEC_MSBC /* mSBC */ +typedef UINT16 tBTA_AG_PEER_CODEC; + +/* AG parse mode */ +#define BTA_AG_PARSE 0 /* Perform AT command parsing in AG */ +#define BTA_AG_PASS_THROUGH 1 /* Pass data directly to phones AT command interpreter */ +typedef UINT8 tBTA_AG_PARSE_MODE; + +/* AG open status */ +#define BTA_AG_SUCCESS 0 /* Connection successfully opened */ +#define BTA_AG_FAIL_SDP 1 /* Open failed due to SDP */ +#define BTA_AG_FAIL_RFCOMM 2 /* Open failed due to RFCOMM */ +#define BTA_AG_FAIL_RESOURCES 3 /* out of resources failure */ +typedef UINT8 tBTA_AG_STATUS; + +/* handle values used with BTA_AgResult */ +#define BTA_AG_HANDLE_NONE 0 +#define BTA_AG_HANDLE_ALL 0xFFFF +/* It is safe to use the same value as BTA_AG_HANDLE_ALL + * HANDLE_ALL is used for delivering indication + * SCO_NO_CHANGE is used for changing sco behavior + * They donot interfere with each other + */ +#define BTA_AG_HANDLE_SCO_NO_CHANGE 0xFFFF + +/* AG result codes used with BTA_AgResult */ +#define BTA_AG_SPK_RES 0 /* Update speaker volume */ +#define BTA_AG_MIC_RES 1 /* Update microphone volume */ +#define BTA_AG_INBAND_RING_RES 2 /* Update inband ring state AT+BSIR */ +#define BTA_AG_CIND_RES 3 /* Send indicator response for AT+CIND */ +#define BTA_AG_BINP_RES 4 /* Send phone number for voice tag for AT+BINP */ +#define BTA_AG_IND_RES 5 /* Update an indicator value +CIEV<...> */ +#define BTA_AG_BVRA_RES 6 /* Update voice recognition state for AT+BVRA */ +#define BTA_AG_CNUM_RES 7 /* Send subscriber number response for AT+CNUM */ +#define BTA_AG_BTRH_RES 8 /* Send CCAP incoming call hold */ +#define BTA_AG_CLCC_RES 9 /* Query list of calls AT+CLCC */ +#define BTA_AG_COPS_RES 10 /* Read network operator for AT+COPS */ +#define BTA_AG_IN_CALL_RES 11 /* Indicate incoming phone call */ +#define BTA_AG_IN_CALL_CONN_RES 12 /* Incoming phone call connected*/ +#define BTA_AG_CALL_WAIT_RES 13 /* Call waiting notification for AT+CCWA */ +#define BTA_AG_OUT_CALL_ORIG_RES 14 /* Outgoing phone call origination AT+ATD*/ +#define BTA_AG_OUT_CALL_ALERT_RES 15 /* Outgoing phone call alerting remote party */ +#define BTA_AG_OUT_CALL_CONN_RES 16 /* Outgoing phone call connected */ +#define BTA_AG_CALL_CANCEL_RES 17 /* Incoming/outgoing 3-way canceled before connected */ +#define BTA_AG_END_CALL_RES 18 /* End call AT+CHUP */ +#define BTA_AG_IN_CALL_HELD_RES 19 /* Incoming call held AT+CHLD */ +#define BTA_AG_UNAT_RES 20 /* Response to unknown AT command event AT+UNAT */ +#define BTA_AG_MULTI_CALL_RES 21 /* SLC at three way call */ +typedef UINT8 tBTA_AG_RES; + +/* AG callback events */ +#define BTA_AG_ENABLE_EVT 0 /* AG enabled */ +#define BTA_AG_REGISTER_EVT 1 /* AG registered */ +#define BTA_AG_OPEN_EVT 2 /* AG connection open */ +#define BTA_AG_CLOSE_EVT 3 /* AG connection closed */ +#define BTA_AG_CONN_EVT 4 /* Service level connection opened */ +#define BTA_AG_AUDIO_OPEN_EVT 5 /* Audio connection open */ +#define BTA_AG_AUDIO_CLOSE_EVT 6 /* Audio connection closed */ +#define BTA_AG_SPK_EVT 7 /* Speaker volume changed */ +#define BTA_AG_MIC_EVT 8 /* Microphone volume changed */ +#define BTA_AG_AT_CKPD_EVT 9 /* CKPD from the HS */ +#define BTA_AG_DISABLE_EVT 30 /* AG disabled */ +#if (BTM_WBS_INCLUDED == TRUE ) +#define BTA_AG_WBS_EVT 31 /* SCO codec nego */ +#endif + +/* Values below are for HFP only */ +#define BTA_AG_AT_A_EVT 10 /* Answer a incoming call */ +#define BTA_AG_AT_D_EVT 11 /* Place a call using number or memory dial */ +#define BTA_AG_AT_CHLD_EVT 12 /* Call hold */ +#define BTA_AG_AT_CHUP_EVT 13 /* Hang up a call */ +#define BTA_AG_AT_CIND_EVT 14 /* Read indicator settings */ +#define BTA_AG_AT_VTS_EVT 15 /* Transmit DTMF tone */ +#define BTA_AG_AT_BINP_EVT 16 /* Retrieve number from voice tag */ +#define BTA_AG_AT_BLDN_EVT 17 /* Place call to last dialed number */ +#define BTA_AG_AT_BVRA_EVT 18 /* Enable/disable voice recognition */ +#define BTA_AG_AT_NREC_EVT 19 /* Disable echo canceling */ +#define BTA_AG_AT_CNUM_EVT 20 /* Retrieve subscriber number */ +#define BTA_AG_AT_BTRH_EVT 21 /* CCAP-style incoming call hold */ +#define BTA_AG_AT_CLCC_EVT 22 /* Query list of current calls */ +#define BTA_AG_AT_COPS_EVT 23 /* Query Current Operator Name on AG */ +#define BTA_AG_AT_UNAT_EVT 24 /* Unknown AT command */ +#define BTA_AG_AT_CBC_EVT 25 /* Indicator Update */ +#define BTA_AG_AT_BAC_EVT 26 /* avablable codec */ +#define BTA_AG_AT_BCS_EVT 27 /* Codec select */ +typedef UINT8 tBTA_AG_EVT; + +/* HFP errcode - Set when BTA_AG_OK_ERROR is returned in 'ok_flag' */ +#define BTA_AG_ERR_PHONE_FAILURE 0 /* Phone Failure */ +#define BTA_AG_ERR_NO_CONN_PHONE 1 /* No connection to phone */ +#define BTA_AG_ERR_OP_NOT_ALLOWED 3 /* Operation not allowed */ +#define BTA_AG_ERR_OP_NOT_SUPPORTED 4 /* Operation not supported */ +#define BTA_AG_ERR_PHSIM_PIN_REQ 5 /* PH-SIM PIN required */ +#define BTA_AG_ERR_SIM_NOT_INSERTED 10 /* SIM not inserted */ +#define BTA_AG_ERR_SIM_PIN_REQ 11 /* SIM PIN required */ +#define BTA_AG_ERR_SIM_PUK_REQ 12 /* SIM PUK required */ +#define BTA_AG_ERR_SIM_FAILURE 13 /* SIM failure */ +#define BTA_AG_ERR_SIM_BUSY 14 /* SIM busy */ +#define BTA_AG_ERR_INCORRECT_PWD 16 /* Incorrect password */ +#define BTA_AG_ERR_SIM_PIN2_REQ 17 /* SIM PIN2 required */ +#define BTA_AG_ERR_SIM_PUK2_REQ 18 /* SIM PUK2 required */ +#define BTA_AG_ERR_MEMORY_FULL 20 /* Memory full */ +#define BTA_AG_ERR_INVALID_INDEX 21 /* Invalid index */ +#define BTA_AG_ERR_MEMORY_FAILURE 23 /* Memory failure */ +#define BTA_AG_ERR_TEXT_TOO_LONG 24 /* Text string too long */ +#define BTA_AG_ERR_INV_CHAR_IN_TSTR 25 /* Invalid characters in text string */ +#define BTA_AG_ERR_DSTR_TOO_LONG 26 /* Dial string too long */ +#define BTA_AG_ERR_INV_CHAR_IN_DSTR 27 /* Invalid characters in dial string */ +#define BTA_AG_ERR_NO_NETWORK_SERV 30 /* No network service */ +#define BTA_AG_ERR_NETWORK_TIME_OUT 31 /* Network timeout */ +#define BTA_AG_ERR_NO_NET_EMG_ONLY 32 /* Network not allowed - emergency service only */ +#define BTA_AG_ERR_VOIP_CS_CALLS 33 /* AG cannot create simultaneous VoIP and CS calls */ +#define BTA_AG_ERR_NOT_FOR_VOIP 34 /* Not supported on this call type(VoIP) */ +#define BTA_AG_ERR_SIP_RESP_CODE 35 /* SIP 3 digit response code */ +typedef UINT8 tBTA_AG_ERR_TYPE; + +#if 0 /* Not Used in Bluetooth HFP 1.5 Specification */ +#define BTA_AG_ERR_PHADAP_LNK_RES 2 /* Phone-adapter link reserved */ +#define BTA_AG_ERR_PHFSIM_PIN_REQ 6 /* PH-FSIM PIN required */ +#define BTA_AG_ERR_PHFSIM_PUK_REQ 7 /* PH-FSIM PUK required */ +#define BTA_AG_ERR_SIM_WRONG 15 /* SIM wrong */ +#define BTA_AG_ERR_NOT_FOUND 22 /* Not found */ +#define BTA_AG_ERR_NETWORK_TIMEOUT 31 /* Network timeout */ +#define BTA_AG_ERR_NET_PIN_REQ 40 /* Network personalization PIN required */ +#define BTA_AG_ERR_NET_PUK_REQ 41 /* Network personalization PUK required */ +#define BTA_AG_ERR_SUBSET_PIN_REQ 42 /* Network subset personalization PIN required */ +#define BTA_AG_ERR_SUBSET_PUK_REQ 43 /* Network subset personalization PUK required */ +#define BTA_AG_ERR_SERVPRO_PIN_REQ 44 /* Service provider personalization PIN required */ +#define BTA_AG_ERR_SERVPRO_PUK_REQ 45 /* Service provider personalization PUK required */ +#define BTA_AG_ERR_CORP_PIN_REQ 46 /* Corporate personalization PIN required */ +#define BTA_AG_ERR_CORP_PUK_REQ 47 /* Corporate personalization PUK required */ +#define BTA_AG_ERR_UNKNOWN 100 /* Unknown error */ +/* GPRS-related errors */ +#define BTA_AG_ERR_ILL_MS 103 /* Illegal MS (#3) */ +#define BTA_AG_ERR_ILL_ME 106 /* Illegal ME (#6) */ +#define BTA_AG_ERR_GPRS_NOT_ALLOWED 107 /* GPRS services not allowed (#7) */ +#define BTA_AG_ERR_PLMN_NOT_ALLOWED 111 /* PLMN services not allowed (#11) */ +#define BTA_AG_ERR_LOC_NOT_ALLOWED 112 /* Location area not allowed (#12) */ +#define BTA_AG_ERR_ROAM_NOT_ALLOWED 113 /* Roaming not allowed in this location area (#13) */ +/* Errors related to a failure to Activate a Context */ +#define BTA_AG_ERR_OPT_NOT_SUPP 132 /* Service option not supported (#32) */ +#define BTA_AG_ERR_OPT_NOT_SUBSCR 133 /* Requested service option not subscribed (#33) */ +#define BTA_AG_ERR_OPT_OUT_OF_ORDER 134 /* Service option temporarily out of order (#34) */ +#define BTA_AG_ERR_PDP_AUTH_FAILURE 149 /* PDP authentication failure */ +/* Other GPRS errors */ +#define BTA_AG_ERR_INV_MOBILE_CLASS 150 /* Invalid mobile class */ +#define BTA_AG_ERR_UNSPEC_GPRS_ERR 148 /* Unspecified GPRS error */ +#endif /* Unused error codes */ + +/* HFP result data 'ok_flag' */ +#define BTA_AG_OK_CONTINUE 0 /* Send out response (more responses coming) */ +#define BTA_AG_OK_DONE 1 /* Send out response followed by OK (finished) */ +#define BTA_AG_OK_ERROR 2 /* Error response */ +typedef UINT8 tBTA_AG_AT_RESULT_TYPE; + +/* BTRH values */ +#define BTA_AG_BTRH_SET_HOLD 0 /* Put incoming call on hold */ +#define BTA_AG_BTRH_SET_ACC 1 /* Accept incoming call on hold */ +#define BTA_AG_BTRH_SET_REJ 2 /* Reject incoming call on hold */ +#define BTA_AG_BTRH_READ 3 /* Read the current value */ +#define BTA_AG_BTRH_NO_RESP 4 /* Not in RH States (reply to read) */ +typedef UINT8 tBTA_AG_BTRH_TYPE; + +/* ASCII character string of arguments to the AT command or result */ +#ifndef BTA_AG_AT_MAX_LEN +#define BTA_AG_AT_MAX_LEN 256 +#endif + +/* indicator constants HFP 1.1 and later */ +#define BTA_AG_IND_CALL 0 /* position of call indicator */ +#define BTA_AG_IND_CALLSETUP 1 /* position of callsetup indicator */ +#define BTA_AG_IND_SERVICE 2 /* position of service indicator */ +/* indicator constants HFP 1.5 and later */ +#define BTA_AG_IND_SIGNAL 3 /* position of signal strength indicator */ +#define BTA_AG_IND_ROAM 4 /* position of roaming indicator */ +#define BTA_AG_IND_BATTCHG 5 /* position of battery charge indicator */ +#define BTA_AG_IND_CALLHELD 6 /* position of callheld indicator */ +#define BTA_AG_IND_BEARER 7 /* position of bearer indicator */ +typedef UINT16 tBTA_AG_IND_TYPE; + +/* call indicator values */ +#define BTA_AG_CALL_INACTIVE 0 /* Phone call inactive */ +#define BTA_AG_CALL_ACTIVE 1 /* Phone call active */ +/* callsetup indicator values */ +#define BTA_AG_CALLSETUP_NONE 0 /* Not currently in call set up */ +#define BTA_AG_CALLSETUP_INCOMING 1 /* Incoming call process ongoing */ +#define BTA_AG_CALLSETUP_OUTGOING 2 /* Outgoing call set up is ongoing */ +#define BTA_AG_CALLSETUP_ALERTING 3 /* Remote party being alerted in an outgoing call */ +/* service indicator values */ +#define BTA_AG_SERVICE_NONE 0 /* Neither CS nor VoIP service is available */ +#define BTA_AG_SERVICE_CS 1 /* Only CS service is available */ +#define BTA_AG_SERVICE_VOIP 2 /* Only VoIP service is available */ +#define BTA_AG_SERVICE_CS_VOIP 3 /* Both CS and VoIP services available */ +/* callheld indicator values */ +#define BTA_AG_CALLHELD_INACTIVE 0 /* No held calls */ +#define BTA_AG_CALLHELD_ACTIVE 1 /* Call held and call active */ +#define BTA_AG_CALLHELD_NOACTIVE 2 /* Call held and no call active */ +/* signal strength indicator values */ +#define BTA_AG_ROAMING_INACTIVE 0 /* Phone call inactive */ +#define BTA_AG_ROAMING_ACTIVE 1 /* Phone call active */ +/* bearer indicator values */ +#define BTA_AG_BEARER_WLAN 0 /* WLAN */ +#define BTA_AG_BEARER_BLUETOOTH 1 /* Bluetooth */ +#define BTA_AG_BEARER_WIRED 2 /* Wired */ +#define BTA_AG_BEARER_2G3G 3 /* 2G 3G */ +#define BTA_AG_BEARER_WIMAX 4 /* WIMAX */ +#define BTA_AG_BEARER_RES1 5 /* Reserved */ +#define BTA_AG_BEARER_RES2 6 /* Reserved */ +#define BTA_AG_BEARER_RES3 7 /* Reserved */ + +/* data associated with BTA_AG_IND_RES */ +typedef struct +{ + tBTA_AG_IND_TYPE type; + UINT16 value; +} tBTA_AG_IND; + +/* data type for BTA_AgResult() */ +typedef struct +{ + char str[BTA_AG_AT_MAX_LEN+1]; /* used for cops,clcc,cnum... */ + tBTA_AG_IND ind; /* used for indicator type */ + UINT16 num; /* used for codec state */ + UINT16 audio_handle; /* used for audio path */ + UINT16 errcode; /* Valid only if 'ok_flag' is set to BTA_AG_OK_ERROR */ + UINT8 ok_flag; /* Indicates if response is finished, and if error occurred */ + BOOLEAN state; +} tBTA_AG_RES_DATA; + +/* data associated with most non-AT events */ +typedef struct +{ + UINT16 handle; + UINT8 app_id; + tBTA_AG_STATUS status; +} tBTA_AG_HDR; + +/* data associated with BTA_AG_REGISTER_EVT */ +typedef struct +{ + tBTA_AG_HDR hdr; + UINT16 handle; + tBTA_AG_STATUS status; +} tBTA_AG_REGISTER; + +/* data associated with BTA_AG_OPEN_EVT */ +typedef struct +{ + tBTA_AG_HDR hdr; + BD_ADDR bd_addr; + tBTA_SERVICE_ID service_id; + tBTA_AG_STATUS status; +} tBTA_AG_OPEN; + +/* data associated with BTA_AG_CLOSE_EVT */ +typedef struct +{ + tBTA_AG_HDR hdr; + BD_ADDR bd_addr; +} tBTA_AG_CLOSE; + +/* data associated with BTA_AG_CONN_EVT */ +typedef struct +{ + tBTA_AG_HDR hdr; + tBTA_AG_PEER_FEAT peer_feat; + BD_ADDR bd_addr; + tBTA_AG_PEER_CODEC peer_codec; + tBTA_AG_CHLD_FEAT chld_feat; +} tBTA_AG_CONN; + +/* data associated with AT command event */ +typedef struct +{ + tBTA_AG_HDR hdr; + BD_ADDR bd_addr; + char str[BTA_AG_AT_MAX_LEN+1]; + UINT16 num; /* voice recognition state*/ + UINT8 idx; /* call number used by CLCC and CHLD */ + UINT16 value; +} tBTA_AG_VAL; + +/* data associated with BTA_AG_CLIP_EVT and BTA_AG_CCWA_EVT*/ +#define BTA_AG_NUMBER_LEN 32 +typedef struct { + char number[BTA_AG_NUMBER_LEN + 1]; +} tBTA_AG_NUMBER; + +/* data associated with BTA_HF_CLIENT_OPERATOR_NAME_EVT */ +#define BTA_AG_COPS_LEN 16 +typedef struct { + char name[BTA_AG_COPS_LEN + 1]; +} tBTA_AG_COPS; + +/* data associated with BTA_AG_AT_RESULT_EVT event */ +typedef struct { + tBTA_AG_AT_RESULT_TYPE type; + UINT16 cme; +} tBTA_AG_AT_RESULT; + +/* data associated with BTA_AG_CLCC_EVT event */ +typedef struct { + UINT32 idx; + BOOLEAN inc; + UINT8 status; + BOOLEAN mpty; + BOOLEAN number_present; + char number[BTA_AG_NUMBER_LEN + 1]; +} tBTA_AG_CLCC; + +/* data associated with BTA_AG_CNUM_EVT event */ +typedef struct { + UINT16 service; + char number[BTA_AG_NUMBER_LEN + 1]; +} tBTA_AG_CNUM; + +/* union of data associated with AG callback */ +typedef union +{ + tBTA_AG_HDR hdr; + tBTA_AG_REGISTER reg; + tBTA_AG_OPEN open; + tBTA_AG_CLOSE close; + tBTA_AG_CONN conn; + tBTA_AG_IND ind; + tBTA_AG_VAL val; + //add + tBTA_AG_COPS operator; + tBTA_AG_NUMBER number; + tBTA_AG_AT_RESULT result; + tBTA_AG_CLCC clcc; + tBTA_AG_CNUM cnum; +} tBTA_AG; + +/* AG callback */ +typedef void (tBTA_AG_CBACK)(tBTA_AG_EVT event, tBTA_AG *p_data); + +/* AG configuration structure */ +typedef struct +{ + char *cind_info; + INT32 conn_tout; + UINT16 sco_pkt_types; + char *chld_val_ecc; + char *chld_val; +} tBTA_AG_CFG; + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ + +/******************************************************************************* +** +** Function BTA_AgEnable +** +** Description Enable the audio gateway service. When the enable +** operation is complete the callback function will be +** called with a BTA_AG_ENABLE_EVT. This function must +** be called before other function in the AG API are +** called. +** +** Returns BTA_SUCCESS if OK, BTA_FAILURE otherwise. +** +*******************************************************************************/ +tBTA_STATUS BTA_AgEnable(tBTA_AG_PARSE_MODE parse_mode, tBTA_AG_CBACK *p_cback); + +/******************************************************************************* +** +** Function BTA_AgDisable +** +** Description Disable the audio gateway service +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgDisable(void); + +/******************************************************************************* +** +** Function BTA_AgRegister +** +** Description Register an Audio Gateway service. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgRegister(tBTA_SERVICE_MASK services, tBTA_SEC sec_mask, + tBTA_AG_FEAT features, char *p_service_names[], UINT8 app_id); + +/******************************************************************************* +** +** Function BTA_AgDeregister +** +** Description Deregister an audio gateway service. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgDeregister(UINT16 handle); + +/******************************************************************************* +** +** Function BTA_AgOpen +** +** Description Opens a connection to a headset or hands-free device. +** When connection is open callback function is called +** with a BTA_AG_OPEN_EVT. Only the data connection is +** opened. The audio connection is not opened. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgOpen(UINT16 handle, BD_ADDR bd_addr, tBTA_SEC sec_mask, tBTA_SERVICE_MASK services); + +/******************************************************************************* +** +** Function BTA_AgClose +** +** Description Close the current connection to a headset or a handsfree +** Any current audio connection will also be closed +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgClose(UINT16 handle); + +/******************************************************************************* +** +** Function BTA_AgAudioOpen +** +** Description Opens an audio connection to the currently connected +** headset or hnadsfree +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgAudioOpen(UINT16 handle); + +/******************************************************************************* +** +** Function BTA_AgAudioClose +** +** Description Close the currently active audio connection to a headset +** or hnadsfree. The data connection remains open +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgAudioClose(UINT16 handle); + +/******************************************************************************* +** +** Function BTA_AgResult +** +** Description Send an AT result code to a headset or hands-free device. +** This function is only used when the AG parse mode is set +** to BTA_AG_PARSE. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgResult(UINT16 handle, tBTA_AG_RES result, tBTA_AG_RES_DATA *p_data); + +/******************************************************************************* +** +** Function BTA_AgSetCodec +** +** Description Specify the codec type to be used for the subsequent +** audio connection. +** +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgSetCodec(UINT16 handle, tBTA_AG_PEER_CODEC codec); + + +#if (BTM_SCO_HCI_INCLUDED == TRUE ) +/******************************************************************************* +** +** Function BTA_AgCiData +** +** Description Give an EVT to BTA that tell outgoing data is ready. +** +** +** Returns void +** +*******************************************************************************/ +void BTA_AgCiData(void); +#endif /*#if (BTM_SCO_HCI_INCLUDED == TRUE ) */ + +#ifdef __cplusplus +} +#endif + +#endif /* #if (BTA_AG_INCLUDED == TRUE) */ + +#endif /* BTA_HF_API_H */ \ No newline at end of file diff --git a/components/bt/host/bluedroid/bta/include/bta/bta_ag_co.h b/components/bt/host/bluedroid/bta/include/bta/bta_ag_co.h new file mode 100644 index 000000000..818611d8f --- /dev/null +++ b/components/bt/host/bluedroid/bta/include/bta/bta_ag_co.h @@ -0,0 +1,160 @@ +/****************************************************************************** + * + * Copyright (C) 2003-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This is the interface file for audio gateway call-out and call-in functions. + * + ******************************************************************************/ +#ifndef BTA_AG_CIO_H +#define BTA_AG_CIO_H + +#include "bta/bta_ag_api.h" +#include "hci/hci_audio.h" + +#if (BTA_AG_INCLUDED == TRUE) +/******************************************************************************* +** +** Function bta_ag_sco_audio_state +** +** Description This function is called by the AG before the audio connection +** is brought up, after it comes up, and after it goes down. +** +** Parameters handle - handle of the AG instance +** state - Audio state +** codec - if WBS support is compiled in, codec to going to be used is provided +** and when in SCO_STATE_SETUP, BTM_I2SPCMConfig() must be called with +** the correct platform parameters. +** in the other states codec type should not be ignored +** +** Returns void +** +*******************************************************************************/ +#if (BTM_WBS_INCLUDED == TRUE ) +void bta_ag_sco_audio_state(UINT16 handle, UINT8 app_id, UINT8 state, tBTA_AG_PEER_CODEC codec); +#else +void bta_ag_sco_audio_state(UINT16 handle, UINT8 app_id, UINT8 state); +#endif + +/******************************************************************************* +** +** Function bta_ag_sco_co_init +** +** Description Set default data path for SCO/eSCO. +** This callout function is executed by AG when it is +** started by calling BTA_AgEnable(). This function can be +** used by the phone to initialize audio paths or for other +** initialization purposes. +** +** +** Returns Void. +** +*******************************************************************************/ +tBTA_HFP_SCO_ROUTE_TYPE bta_ag_sco_co_init(UINT32 rx_bw, UINT32 tx_bw, tBTA_HFP_CODEC_INFO *p_codec_info, UINT8 app_id); + +/******************************************************************************* +** +** Function bta_ag_sco_co_open +** +** Description This function is executed by AG when a service level connection +** is opened. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sco_co_open(UINT16 handle, tBTM_SCO_AIR_MODE_TYPE air_mode, UINT8 inout_pkt_size, UINT16 event); + +/******************************************************************************* +** +** Function bta_ag_sco_co_close +** +** Description This function is called by AG when a service level +** connection is closed. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sco_co_close(void); + +/******************************************************************************* +** +** Function bta_ag_sco_co_out_data +** +** Description This function is called to send SCO data over HCI. +** +** Returns number of bytes got from application +** +*******************************************************************************/ +uint32_t bta_ag_sco_co_out_data(UINT8 *p_buf); + +/******************************************************************************* +** +** Function bta_hf_client_sco_co_in_data +** +** Description This function is called to send incoming SCO data to application. +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sco_co_in_data(BT_HDR *p_buf, tBTM_SCO_DATA_FLAG status); + +/******************************************************************************* +** +** Function bta_ag_co_tx_write +** +** Description This function is called by the AG to send data to the +** phone when the AG is configured for AT command pass-through. +** The implementation of this function must copy the data to +** the phones memory. +** +** Returns void +** +*******************************************************************************/ +void bta_ag_co_tx_write(UINT16 handle, UINT8 *p_data, UINT16 len); + +/******************************************************************************* +** +** Function bta_ag_ci_rx_write +** +** Description This function is called to send data to the AG when the AG +** is configured for AT command pass-through. The function +** copies data to an event buffer and sends it. +** +** Returns void +** +*******************************************************************************/ +extern void bta_ag_ci_rx_write(UINT16 handle, char *p_data, UINT16 len); + +/****************************************************************************** +** +** Function bta_ag_ci_slc_ready +** +** Description This function is called to notify AG that SLC is up at +** the application. This funcion is only used when the app +** is running in pass-through mode. +** +** Returns void +** +******************************************************************************/ +extern void bta_ag_ci_slc_ready(UINT16 handle); + +#endif /* #if (BTA_AG_INCLUDED == TRUE) */ + +#endif /* BTA_AG_CIO_H */ \ No newline at end of file diff --git a/components/bt/host/bluedroid/btc/core/btc_dm.c b/components/bt/host/bluedroid/btc/core/btc_dm.c index 5b712dbf1..0f252df90 100644 --- a/components/bt/host/bluedroid/btc/core/btc_dm.c +++ b/components/bt/host/bluedroid/btc/core/btc_dm.c @@ -57,6 +57,9 @@ btc_dm_cb_t *btc_dm_cb_ptr; extern bt_status_t btc_av_source_execute_service(BOOLEAN b_enable); extern bt_status_t btc_av_sink_execute_service(BOOLEAN b_enable); #endif +#if BTC_HF_INCLUDED +extern bt_status_t btc_hf_execute_service(BOOLEAN b_enable); +#endif #if BTC_HF_CLIENT_INCLUDED extern bt_status_t btc_hf_client_execute_service(BOOLEAN b_enable); #endif @@ -510,6 +513,11 @@ static bt_status_t btc_in_execute_service_request(tBTA_SERVICE_ID service_id, btc_av_sink_execute_service(b_enable); break; #endif +#if BTC_HF_INCLUDED + case BTA_HFP_SERVICE_ID: + btc_hf_execute_service(b_enable); + break; +#endif /* #if BTC_HF_INCLUDED */ #if BTC_HF_CLIENT_INCLUDED case BTA_HFP_HS_SERVICE_ID: btc_hf_client_execute_service(b_enable); diff --git a/components/bt/host/bluedroid/btc/core/btc_util.c b/components/bt/host/bluedroid/btc/core/btc_util.c index 4174a1152..c08074a31 100644 --- a/components/bt/host/bluedroid/btc/core/btc_util.c +++ b/components/bt/host/bluedroid/btc/core/btc_util.c @@ -30,6 +30,11 @@ #if (BTA_AV_INCLUDED == TRUE) #include "bta/bta_av_api.h" #endif ///BTA_AV_INCLUDED == TRUE + +#if (BTA_AG_INCLUDED == TRUE) +#include "bta/bta_ag_api.h" +#endif ///BTA_AG_INCLUDED == TRUE + #include "common/bt_defs.h" #include "stack/btm_api.h" #include "bta/bta_api.h" @@ -121,6 +126,89 @@ const char *dump_rc_pdu(UINT8 pdu) } #endif ///BTA_AV_INCLUDED == TRUE +#if (BTA_AG_INCLUDED == TRUE) +const char* dump_hf_conn_state(UINT16 event) +{ + switch(event) + { + CASE_RETURN_STR(ESP_HF_CONNECTION_STATE_DISCONNECTED) + CASE_RETURN_STR(ESP_HF_CONNECTION_STATE_CONNECTING) + CASE_RETURN_STR(ESP_HF_CONNECTION_STATE_CONNECTED) + CASE_RETURN_STR(ESP_HF_CONNECTION_STATE_SLC_CONNECTED) + CASE_RETURN_STR(ESP_HF_CONNECTION_STATE_DISCONNECTING) + default: + return "UNKNOWN MSG ID"; + } +} + +const char* dump_hf_event(UINT16 event) +{ + switch(event) + { + CASE_RETURN_STR(BTA_AG_ENABLE_EVT) + CASE_RETURN_STR(BTA_AG_REGISTER_EVT) + CASE_RETURN_STR(BTA_AG_OPEN_EVT) + CASE_RETURN_STR(BTA_AG_CLOSE_EVT) + CASE_RETURN_STR(BTA_AG_CONN_EVT) + CASE_RETURN_STR(BTA_AG_AUDIO_OPEN_EVT) + CASE_RETURN_STR(BTA_AG_AUDIO_CLOSE_EVT) + CASE_RETURN_STR(BTA_AG_SPK_EVT) + CASE_RETURN_STR(BTA_AG_MIC_EVT) + CASE_RETURN_STR(BTA_AG_AT_CKPD_EVT) + CASE_RETURN_STR(BTA_AG_DISABLE_EVT) +#if (BTM_WBS_INCLUDED == TRUE ) + CASE_RETURN_STR(BTA_AG_WBS_EVT) +#endif + CASE_RETURN_STR(BTA_AG_AT_A_EVT) + CASE_RETURN_STR(BTA_AG_AT_D_EVT) + CASE_RETURN_STR(BTA_AG_AT_CHLD_EVT) + CASE_RETURN_STR(BTA_AG_AT_CHUP_EVT) + CASE_RETURN_STR(BTA_AG_AT_CIND_EVT) + CASE_RETURN_STR(BTA_AG_AT_VTS_EVT) + CASE_RETURN_STR(BTA_AG_AT_BINP_EVT) + CASE_RETURN_STR(BTA_AG_AT_BLDN_EVT) + CASE_RETURN_STR(BTA_AG_AT_BVRA_EVT) + CASE_RETURN_STR(BTA_AG_AT_NREC_EVT) + CASE_RETURN_STR(BTA_AG_AT_CNUM_EVT) + CASE_RETURN_STR(BTA_AG_AT_BTRH_EVT) + CASE_RETURN_STR(BTA_AG_AT_CLCC_EVT) + CASE_RETURN_STR(BTA_AG_AT_COPS_EVT) + CASE_RETURN_STR(BTA_AG_AT_UNAT_EVT) + CASE_RETURN_STR(BTA_AG_AT_CBC_EVT) + CASE_RETURN_STR(BTA_AG_AT_BAC_EVT) + CASE_RETURN_STR(BTA_AG_AT_BCS_EVT) + + default: + return "UNKNOWN MSG ID"; + } +} + +const char* dump_hf_call_state(esp_hf_call_status_t call_state) +{ + switch(call_state) + { + CASE_RETURN_STR(ESP_HF_CALL_STATUS_NO_CALLS) + CASE_RETURN_STR(ESP_HF_CALL_STATUS_CALL_IN_PROGRESS) + default: + return "UNKNOWN CALL STATE"; + } +} + +const char* dump_hf_call_setup_state(esp_hf_call_setup_status_t call_setup_state) +{ + switch(call_setup_state) + { + CASE_RETURN_STR(ESP_HF_CALL_SETUP_STATUS_IDLE) + CASE_RETURN_STR(ESP_HF_CALL_SETUP_STATUS_INCOMING) + CASE_RETURN_STR(ESP_HF_CALL_SETUP_STATUS_OUTGOING_DIALING) + CASE_RETURN_STR(ESP_HF_CALL_SETUP_STATUS_OUTGOING_ALERTING) + default: + return "UNKNOWN CALL SETUP STATE"; + } +} + +#endif // #if (BTA_AG_INCLUDED == TRUE) + UINT32 devclass2uint(DEV_CLASS dev_class) { UINT32 cod = 0; diff --git a/components/bt/host/bluedroid/btc/include/btc/btc_util.h b/components/bt/host/bluedroid/btc/include/btc/btc_util.h index bc0570dd3..aa96e4601 100644 --- a/components/bt/host/bluedroid/btc/include/btc/btc_util.h +++ b/components/bt/host/bluedroid/btc/include/btc/btc_util.h @@ -19,6 +19,7 @@ #include "stack/bt_types.h" #include "common/bt_defs.h" #include "esp_bt_defs.h" +#include "esp_hf_defs.h" /******************************************************************************* ** Constants & Macros @@ -34,9 +35,18 @@ typedef char bdstr_t[18]; /******************************************************************************* ** Functions ********************************************************************************/ +#if(BTA_AV_INCLUDED == TRUE) const char *dump_rc_event(UINT8 event); const char *dump_rc_notification_event_id(UINT8 event_id); const char *dump_rc_pdu(UINT8 pdu); +#endif + +#if(BTA_AG_INCLUDED == TRUE) +const char *dump_hf_conn_state(UINT16 event); +const char *dump_hf_event(UINT16 event); +const char *dump_hf_call_state(esp_hf_call_status_t call_state); +const char* dump_hf_call_setup_state(esp_hf_call_setup_status_t call_setup_state); +#endif UINT32 devclass2uint(DEV_CLASS dev_class); void uint2devclass(UINT32 dev, DEV_CLASS dev_class); diff --git a/components/bt/host/bluedroid/btc/profile/std/hf_ag/bta_ag_co.c b/components/bt/host/bluedroid/btc/profile/std/hf_ag/bta_ag_co.c new file mode 100644 index 000000000..22bec07f1 --- /dev/null +++ b/components/bt/host/bluedroid/btc/profile/std/hf_ag/bta_ag_co.c @@ -0,0 +1,590 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#define LOG_TAG "bt_btc_bta_ag" + +#include "btc_hf_ag.h" +#include "bta_ag_int.h" +#include "bta/bta_api.h" +#include "bta/bta_ag_api.h" +#include "bta/bta_ag_co.h" +#include "bta/bta_dm_co.h" +#include "common/bt_target.h" +#include "hci/hci_audio.h" +#include "osi/allocator.h" +#include + +#if (BTA_AG_INCLUDED == TRUE) + +/******************************************************************************* + * CONST +********************************************************************************/ +#if (BTM_SCO_HCI_INCLUDED == TRUE) +#include "oi_codec_sbc.h" +#include "oi_status.h" +#include "sbc_encoder.h" + +#if (PLC_INCLUDED == TRUE) +#include "sbc_plc.h" +typedef struct { + bool first_good_frame_found; + sbc_plc_state_t plc_state; + int16_t sbc_plc_out[SBC_FS]; +} bta_hf_ct_plc_t; +#if HFP_DYNAMIC_MEMORY == FALSE +static bta_hf_ct_plc_t bta_hf_ct_plc; +#else +static bta_hf_ct_plc_t *bta_hf_ct_plc_ptr; +#define bta_hf_ct_plc (*bta_hf_ct_plc_ptr) +#endif ///HFP_DYNAMIC_MEMORY == FALSE +#endif ///(PLC_INCLUDED == TRUE) + +#define HF_SBC_DEC_CONTEXT_DATA_LEN (CODEC_DATA_WORDS(1, SBC_CODEC_FAST_FILTER_BUFFERS)) +#define HF_SBC_DEC_RAW_DATA_SIZE 240 +#define HF_SBC_ENC_RAW_DATA_SIZE 240 + +/* BTA-AG-CO control block to map bdaddr to BTA handle */ +typedef struct +{ + OI_CODEC_SBC_DECODER_CONTEXT decoder_context; + OI_UINT32 decoder_context_data[HF_SBC_DEC_CONTEXT_DATA_LEN]; + OI_INT16 decode_raw_data[HF_SBC_DEC_RAW_DATA_SIZE]; + + SBC_ENC_PARAMS encoder; + + UINT8 sequence_number; + bool is_bad_frame; + bool decode_first_pkt; + OI_BYTE decode_msbc_data[BTM_MSBC_FRAME_SIZE]; + bool encode_first_pkt; + OI_BYTE encode_msbc_data[BTM_MSBC_FRAME_SIZE]; +} bta_ag_co_cb_t; + +#if HFP_DYNAMIC_MEMORY == FALSE +static bta_ag_co_cb_t bta_ag_co_cb; +#else +static bta_ag_co_cb_t *bta_ag_co_cb_ptr; +#define bta_ag_co_cb (*bta_ag_co_cb_ptr) +#endif /* HFP_DYNAMIC_MEMORY == FALSE */ + +static UINT8 hf_air_mode = BTM_SCO_AIR_MODE_TRANSPNT; +static UINT8 hf_inout_pkt_size = 0; + +/* ========================================================================= +* AG pass-through mode handle +*===========================================================================*/ +/******************************************************************************* + ** + ** Function bta_ag_co_tx_write + ** + ** Description This function is called by the AG to send data to the + ** phone when the AG is configured for AT command pass-through. + ** The implementation of this function must copy the data to + ** the phones memory. + ** + ** Returns void + ** + *******************************************************************************/ +void bta_ag_co_tx_write(UINT16 handle, UNUSED_ATTR UINT8 * p_data, UINT16 len) +{ + BTIF_TRACE_DEBUG( "bta_ag_co_tx_write: handle: %d, len: %d", handle, len ); +} + +/****************************************************************************** +** +** Function bta_ag_ci_rx_write +** +** Description This function is called to send data to the AG when the AG +** is configured for AT command pass-through. The function +** copies data to an event buffer and sends it. +** +** Returns void +** +******************************************************************************/ +void bta_ag_ci_rx_write(UINT16 handle, char *p_data, UINT16 len) +{ + tBTA_AG_CI_RX_WRITE *p_buf; + UINT16 len_remaining = len; + char *p_data_area; + + if (len > (BT_DEFAULT_BUFFER_SIZE - sizeof(tBTA_AG_CI_RX_WRITE) - 1)) { + len = BT_DEFAULT_BUFFER_SIZE - sizeof(tBTA_AG_CI_RX_WRITE) - 1; + } + + while (len_remaining) { + if (len_remaining < len) { + len = len_remaining; + } + if ((p_buf = (tBTA_AG_CI_RX_WRITE *) osi_malloc((UINT16)(sizeof(tBTA_AG_CI_RX_WRITE) + len + 1))) != NULL) { + p_buf->hdr.event = BTA_AG_CI_RX_WRITE_EVT; + p_buf->hdr.layer_specific = handle; + p_data_area = (char *)(p_buf+1); /* Point to data area after header */ + strncpy(p_data_area, p_data, len); + p_data_area[len] = 0; + bta_sys_sendmsg(p_buf); + } else { + APPL_TRACE_ERROR("ERROR: Unable to allocate buffer to hold AT response code. len=%i", len); + break; + } + len_remaining-=len; + p_data+=len; + } +} + +/****************************************************************************** +** +** Function bta_ag_ci_slc_ready +** +** Description This function is called to notify AG that SLC is up at +** the application. This funcion is only used when the app +** is running in pass-through mode. +** +** Returns void +** +******************************************************************************/ +void bta_ag_ci_slc_ready(UINT16 handle) +{ + tBTA_AG_DATA *p_buf; + if ((p_buf = (tBTA_AG_DATA *)osi_malloc(sizeof(tBTA_AG_DATA))) != NULL) { + p_buf->hdr.event = BTA_AG_CI_SLC_READY_EVT; + p_buf->hdr.layer_specific = handle; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* + * H2 & DEC +********************************************************************************/ +/******************************************************************************* +** +** Function bta_ag_h2_header +** +** Description This function is called to fill in H2 header +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_h2_header(UINT16 *p_buf) +{ + // H2: Header with synchronization word and sequence number +#define BTA_HF_H2_HEADER 0x0801 +#define BTA_HF_H2_HEADER_BIT0_MASK (1 << 0) +#define BTA_HF_H2_HEADER_BIT1_MASK (1 << 1) +#define BTA_HF_H2_HEADER_SN0_BIT_OFFSET1 12 +#define BTA_HF_H2_HEADER_SN0_BIT_OFFSET2 13 +#define BTA_HF_H2_HEADER_SN1_BIT_OFFSET1 14 +#define BTA_HF_H2_HEADER_SN1_BIT_OFFSET2 15 + + UINT16 h2_header = BTA_HF_H2_HEADER; + UINT8 h2_header_sn0 = bta_ag_co_cb.sequence_number & BTA_HF_H2_HEADER_BIT0_MASK; + UINT8 h2_header_sn1 = bta_ag_co_cb.sequence_number & BTA_HF_H2_HEADER_BIT1_MASK; + h2_header |= (h2_header_sn0 << BTA_HF_H2_HEADER_SN0_BIT_OFFSET1 + | h2_header_sn0 << BTA_HF_H2_HEADER_SN0_BIT_OFFSET2 + | h2_header_sn1 << (BTA_HF_H2_HEADER_SN1_BIT_OFFSET1 - 1) + | h2_header_sn1 << (BTA_HF_H2_HEADER_SN1_BIT_OFFSET2 - 1) + ); + bta_ag_co_cb.sequence_number++; + *p_buf = h2_header; +} + +/******************************************************************************* + ** + ** Function bta_hf_dec_init + ** + ** Description Initialize decoding task + ** + ** Returns void + ** + *******************************************************************************/ +static void bta_hf_dec_init(void) +{ +#if (PLC_INCLUDED == TRUE) + sbc_plc_init(&(bta_hf_ct_plc.plc_state)); +#endif ///(PLC_INCLUDED == TRUE) + + OI_STATUS status = OI_CODEC_SBC_DecoderReset(&bta_ag_co_cb.decoder_context, bta_ag_co_cb.decoder_context_data, + HF_SBC_DEC_CONTEXT_DATA_LEN * sizeof(OI_UINT32), 1, 1, FALSE, TRUE); + if (!OI_SUCCESS(status)) { + APPL_TRACE_ERROR("OI_CODEC_SBC_DecoderReset failed with error code %d\n", status); + } +} + +/******************************************************************************* + ** + ** Function bta_hf_enc_init + ** + ** Description Initialize encoding task for mSBC + ** + ** Returns void + ** + *******************************************************************************/ +static void bta_hf_enc_init(void) +{ + bta_ag_co_cb.sequence_number = 0; + bta_ag_co_cb.decode_first_pkt = true; + bta_ag_co_cb.encode_first_pkt = true; + bta_ag_co_cb.is_bad_frame = false; + + bta_ag_co_cb.encoder.sbc_mode = SBC_MODE_MSBC; + bta_ag_co_cb.encoder.s16NumOfBlocks = 15; + bta_ag_co_cb.encoder.s16NumOfSubBands = 8; + bta_ag_co_cb.encoder.s16AllocationMethod = SBC_LOUDNESS; + bta_ag_co_cb.encoder.s16BitPool = 26; + bta_ag_co_cb.encoder.s16ChannelMode = SBC_MONO; + bta_ag_co_cb.encoder.s16NumOfChannels = 1; + bta_ag_co_cb.encoder.s16SamplingFreq = SBC_sf16000; + + SBC_Encoder_Init(&(bta_ag_co_cb.encoder)); +} + +/******************************************************************************* +** +** Function bta_ag_decode_msbc_frame +** +** Description This function is called decode a mSBC frame +** +** Returns void +** +*******************************************************************************/ +static void bta_ag_decode_msbc_frame(UINT8 **data, UINT8 *length, BOOLEAN is_bad_frame) +{ + OI_STATUS status; + const OI_BYTE *zero_signal_frame_data; + UINT8 zero_signal_frame_len = BTM_MSBC_FRAME_DATA_SIZE; + UINT32 sbc_raw_data_size = HF_SBC_DEC_RAW_DATA_SIZE; + + if (is_bad_frame) { + status = OI_CODEC_SBC_CHECKSUM_MISMATCH; + } else { + status = OI_CODEC_SBC_DecodeFrame(&bta_ag_co_cb.decoder_context, (const OI_BYTE **)data, + (OI_UINT32 *)length, + (OI_INT16 *)bta_ag_co_cb.decode_raw_data, + (OI_UINT32 *)&sbc_raw_data_size); + } + +// PLC_INCLUDED will be set to TRUE when enabling Wide Band Speech +#if (PLC_INCLUDED == TRUE) + switch(status) { + case OI_OK: + { + bta_hf_ct_plc.first_good_frame_found = TRUE; + sbc_plc_good_frame(&(bta_hf_ct_plc.plc_state), (int16_t *)bta_ag_co_cb.decode_raw_data, bta_hf_ct_plc.sbc_plc_out); + } + + case OI_CODEC_SBC_NOT_ENOUGH_HEADER_DATA: + case OI_CODEC_SBC_NOT_ENOUGH_BODY_DATA: + case OI_CODEC_SBC_NOT_ENOUGH_AUDIO_DATA: + break; + + case OI_CODEC_SBC_NO_SYNCWORD: + case OI_CODEC_SBC_CHECKSUM_MISMATCH: + { + if (!bta_hf_ct_plc.first_good_frame_found) { + break; + } + zero_signal_frame_data = sbc_plc_zero_signal_frame(); + sbc_raw_data_size = HF_SBC_DEC_RAW_DATA_SIZE; + status = OI_CODEC_SBC_DecodeFrame(&bta_ag_co_cb.decoder_context, &zero_signal_frame_data, + (OI_UINT32 *)&zero_signal_frame_len, + (OI_INT16 *)bta_ag_co_cb.decode_raw_data, + (OI_UINT32 *)&sbc_raw_data_size); + sbc_plc_bad_frame(&(bta_hf_ct_plc.plc_state), bta_ag_co_cb.decode_raw_data, bta_hf_ct_plc.sbc_plc_out); + APPL_TRACE_DEBUG("bad frame, using PLC to fix it."); + break; + } + + case OI_STATUS_INVALID_PARAMETERS: + { + // This caused by corrupt frames. + // The codec apparently does not recover from this. + // Re-initialize the codec. + APPL_TRACE_ERROR("Frame decode error: OI_STATUS_INVALID_PARAMETERS"); + + if (!OI_SUCCESS(OI_CODEC_SBC_DecoderReset(&bta_ag_co_cb.decoder_context, bta_ag_co_cb.decoder_context_data, + HF_SBC_DEC_CONTEXT_DATA_LEN * sizeof(OI_UINT32), 1, 1, FALSE, TRUE))) { + APPL_TRACE_ERROR("OI_CODEC_SBC_DecoderReset failed with error code %d\n", status); + } + break; + } + + default: + APPL_TRACE_ERROR("Frame decode error: %d", status); + break; + } +#endif ///(PLC_INCLUDED == TRUE) + + if (OI_SUCCESS(status)) { + btc_hf_incoming_data_cb_to_app((const uint8_t *)(bta_hf_ct_plc.sbc_plc_out), sbc_raw_data_size); + } +} + +/******************************************************************************* + * BTA AG SCO CO FUNCITONS +********************************************************************************/ +/******************************************************************************* +** +** Function bta_ag_sco_audio_state +** +** Description This function is called by the AG before the audio connection +** is brought up, after it comes up, and after it goes down. +** +** Parameters handle - handle of the AG instance +** state - Audio state +** codec - if WBS support is compiled in, codec to going to be used is provided +** and when in SCO_STATE_SETUP, BTM_I2SPCMConfig() must be called with +** the correct platform parameters. +** in the other states codec type should not be ignored +** +** Returns void +** +*******************************************************************************/ +#if (BTM_WBS_INCLUDED == TRUE ) +void bta_ag_sco_audio_state(UINT16 handle, UINT8 app_id, UINT8 state, tBTA_AG_PEER_CODEC codec) +#else +void bta_ag_sco_audio_state(UINT16 handle, UINT8 app_id, UINT8 state) +#endif +{ + BTIF_TRACE_DEBUG("bta_ag_sco_audio_state: handle %d, state %d", handle, state); + switch (state) { + case SCO_STATE_ON: + case SCO_STATE_OFF: + case SCO_STATE_OFF_TRANSFER: + case SCO_STATE_SETUP: + default: + break; + } +} + +/******************************************************************************* +** +** Function bta_ag_sco_co_init +** +** Description Set default data path for SCO/eSCO. +** +** +** Returns Void. +** +*******************************************************************************/ +tBTA_HFP_SCO_ROUTE_TYPE bta_ag_sco_co_init(UINT32 rx_bw, UINT32 tx_bw, tBTA_HFP_CODEC_INFO *p_codec_info, UINT8 app_id) +{ + APPL_TRACE_EVENT("%s rx_bw %d, tx_bw %d, codec %d", __FUNCTION__, rx_bw, tx_bw, p_codec_info->codec_type); + return BTA_HFP_SCO_ROUTE_HCI; +} + +/******************************************************************************* +** +** Function bta_ag_sco_co_open +** +** Description This function is executed by AG when a service level connection +** is opened. +** +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sco_co_open(UINT16 handle, tBTM_SCO_AIR_MODE_TYPE air_mode, UINT8 inout_pkt_size, UINT16 event) +{ + APPL_TRACE_EVENT("%s hdl %x, pkt_sz %u, event %u", __FUNCTION__, handle, inout_pkt_size, event); + hf_air_mode = air_mode; + hf_inout_pkt_size = inout_pkt_size; + + if (air_mode == BTM_SCO_AIR_MODE_TRANSPNT) { +#if (HFP_DYNAMIC_MEMORY == TRUE) + bta_ag_co_cb_ptr = osi_calloc(sizeof(bta_ag_co_cb_t)); + if (!bta_ag_co_cb_ptr) { + APPL_TRACE_ERROR("%s allocate failed", __FUNCTION__); + goto error_exit; + } +#if (PLC_INCLUDED == TRUE) + bta_hf_ct_plc_ptr = (bta_hf_ct_plc_t *)osi_calloc(sizeof(bta_hf_ct_plc_t)); + if (!bta_hf_ct_plc_ptr) { + APPL_TRACE_ERROR("%s malloc fail.", __FUNCTION__); + goto error_exit; + } +#endif ///(PLC_INCLUDED == TRUE) +#endif /// (HFP_DYNAMIC_MEMORY == TRUE) + bta_hf_dec_init(); + bta_hf_enc_init(); + return; + } else { + return; // Nothing to do + } + +#if (HFP_DYNAMIC_MEMORY == TRUE) +error_exit:; + hf_air_mode = BTM_SCO_AIR_MODE_UNKNOWN; + hf_inout_pkt_size = 0; + if (bta_ag_co_cb_ptr) { + osi_free(bta_ag_co_cb_ptr); + bta_ag_co_cb_ptr = NULL; + } +#if (PLC_INCLUDED == TRUE) + if (bta_hf_ct_plc_ptr) { + osi_free(bta_hf_ct_plc_ptr); + bta_hf_ct_plc_ptr = NULL; + } +#endif ///(PLC_INCLUDED == TRUE) +#endif /// (HFP_DYNAMIC_MEMORY == TRUE) + return; +} + +/******************************************************************************* +** +** Function bta_ag_sco_co_close +** +** Description Nothing but print some log. +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sco_co_close(void) +{ + APPL_TRACE_EVENT("%s", __FUNCTION__); + if (hf_air_mode == BTM_SCO_AIR_MODE_TRANSPNT) { +#if (PLC_INCLUDED == TRUE) + sbc_plc_deinit(&(bta_hf_ct_plc.plc_state)); + bta_hf_ct_plc.first_good_frame_found = FALSE; +#if (HFP_DYNAMIC_MEMORY == TRUE) + osi_free(bta_hf_ct_plc_ptr); + bta_hf_ct_plc_ptr = NULL; +#endif /// (HFP_DYNAMIC_MEMORY == TRUE) +#endif ///(PLC_INCLUDED == TRUE) + +#if (HFP_DYNAMIC_MEMORY == TRUE) + osi_free(bta_ag_co_cb_ptr); + bta_ag_co_cb_ptr = NULL; +#endif /* HFP_DYNAMIC_MEMORY == TRUE */ + } else { + // Nothing to do + } + hf_air_mode = BTM_SCO_AIR_MODE_UNKNOWN; + hf_inout_pkt_size = 0; +} + +/******************************************************************************* +** +** Function bta_ag_sco_co_out_data +** +** Description This function is called to send SCO data over HCI. +** +** Returns number of bytes got from application +** +*******************************************************************************/ +uint32_t bta_ag_sco_co_out_data(UINT8 *p_buf) +{ + if (hf_air_mode == BTM_SCO_AIR_MODE_CVSD) { + // CVSD + uint32_t hf_raw_pkt_size = hf_inout_pkt_size; + return btc_hf_outgoing_data_cb_to_app(p_buf, hf_raw_pkt_size); + } else if (hf_air_mode == BTM_SCO_AIR_MODE_TRANSPNT) { + // mSBC + if (hf_inout_pkt_size == BTM_MSBC_FRAME_SIZE / 2) { + if (bta_ag_co_cb.encode_first_pkt) { + UINT32 size = btc_hf_outgoing_data_cb_to_app((UINT8 *)bta_ag_co_cb.encoder.as16PcmBuffer, HF_SBC_ENC_RAW_DATA_SIZE); + if (size != HF_SBC_ENC_RAW_DATA_SIZE) { + return 0; + } + bta_ag_h2_header((UINT16 *)bta_ag_co_cb.encode_msbc_data); + bta_ag_co_cb.encoder.pu8Packet = bta_ag_co_cb.encode_msbc_data + 2; + + SBC_Encoder(&bta_ag_co_cb.encoder); + memcpy(p_buf, bta_ag_co_cb.encode_msbc_data, hf_inout_pkt_size); + bta_ag_co_cb.encode_first_pkt = !bta_ag_co_cb.encode_first_pkt; + return hf_inout_pkt_size; + } else { + memcpy(p_buf, bta_ag_co_cb.encode_msbc_data + hf_inout_pkt_size, hf_inout_pkt_size); + bta_ag_co_cb.encode_first_pkt = !bta_ag_co_cb.encode_first_pkt; + return hf_inout_pkt_size; + } + } else if (hf_inout_pkt_size == BTM_MSBC_FRAME_SIZE) { + UINT32 size = btc_hf_outgoing_data_cb_to_app((UINT8 *)bta_ag_co_cb.encoder.as16PcmBuffer, HF_SBC_ENC_RAW_DATA_SIZE); + if (size != HF_SBC_ENC_RAW_DATA_SIZE) { + return 0; + } + bta_ag_h2_header((UINT16 *)p_buf); + bta_ag_co_cb.encoder.pu8Packet = p_buf + 2; + + SBC_Encoder(&bta_ag_co_cb.encoder); + return hf_inout_pkt_size; + } else { + //Never run to here. + } + } else { + APPL_TRACE_ERROR("%s invaild air mode: %d", __FUNCTION__, hf_air_mode); + } + return 0; +} + +/******************************************************************************* +** +** Function bta_ag_sco_co_in_data +** +** Description This function is called to send incoming SCO data to application. +** +** Returns void +** +*******************************************************************************/ +void bta_ag_sco_co_in_data(BT_HDR *p_buf, tBTM_SCO_DATA_FLAG status) +{ + UINT8 *p = (UINT8 *)(p_buf + 1) + p_buf->offset; + UINT8 pkt_size = 0; + STREAM_SKIP_UINT16(p); + STREAM_TO_UINT8(pkt_size, p); + + if (hf_air_mode == BTM_SCO_AIR_MODE_CVSD) { + // CVSD + if(status != BTM_SCO_DATA_CORRECT) { + APPL_TRACE_DEBUG("%s: not a correct frame(%d).", __func__, status); + } + btc_hf_incoming_data_cb_to_app(p, pkt_size); + } else if (hf_air_mode == BTM_SCO_AIR_MODE_TRANSPNT) { + // mSBC + UINT8 *data = NULL; + if (pkt_size != hf_inout_pkt_size) { + bta_ag_co_cb.is_bad_frame = true; + } + if (status != BTM_SCO_DATA_CORRECT) { + bta_ag_co_cb.is_bad_frame = true; + } + if (hf_inout_pkt_size == BTM_MSBC_FRAME_SIZE / 2) { + if (bta_ag_co_cb.decode_first_pkt) { + if (!bta_ag_co_cb.is_bad_frame) { + memcpy(bta_ag_co_cb.decode_msbc_data, p, pkt_size); + } + } else { + if (!bta_ag_co_cb.is_bad_frame) { + memcpy(bta_ag_co_cb.decode_msbc_data + BTM_MSBC_FRAME_SIZE / 2, p, pkt_size); + } + data = bta_ag_co_cb.decode_msbc_data; + bta_ag_decode_msbc_frame(&data, &pkt_size, bta_ag_co_cb.is_bad_frame); + bta_ag_co_cb.is_bad_frame = false; + } + bta_ag_co_cb.decode_first_pkt = !bta_ag_co_cb.decode_first_pkt; + } else if (hf_inout_pkt_size == BTM_MSBC_FRAME_SIZE) { + data = p; + bta_ag_decode_msbc_frame(&data, &pkt_size, bta_ag_co_cb.is_bad_frame); + bta_ag_co_cb.is_bad_frame = false; + } else { + //Never run to here. + } + } else { + APPL_TRACE_ERROR("%s invaild air mode: %d", __FUNCTION__, hf_air_mode); + } +} +#endif /* #if (BTM_SCO_HCI_INCLUDED == TRUE) */ +#endif /* #if (BTA_AG_INCLUDED == TRUE) */ \ No newline at end of file diff --git a/components/bt/host/bluedroid/btc/profile/std/hf_ag/btc_hf_ag.c b/components/bt/host/bluedroid/btc/profile/std/hf_ag/btc_hf_ag.c new file mode 100644 index 000000000..32a55d122 --- /dev/null +++ b/components/bt/host/bluedroid/btc/profile/std/hf_ag/btc_hf_ag.c @@ -0,0 +1,1466 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/************************************************************************************ + * + * Filename: btc_hf.c + * + * Description: Handsfree Profile Bluetooth Interface + * * + ***********************************************************************************/ +#include +#include +#include +#include +#include +#include "time.h" +#include "btc/btc_dm.h" +#include "btc_hf_ag.h" +#include "btc/btc_profile_queue.h" +#include "btc/btc_manage.h" +#include "btc/btc_util.h" +#include "btc/btc_common.h" +#include "bta/bta_ag_api.h" +#include "bt_common.h" +#include "common/bt_target.h" +#include "common/bt_trace.h" +#include "common/bt_defs.h" +#include "device/bdaddr.h" +#include "esp_bt.h" +#include "esp_hf_ag_api.h" +#include "osi/allocator.h" + + +#if (BTC_HF_INCLUDED == TRUE) +/************************************************************************************ +** Constants & Macros +************************************************************************************/ +/* Max HF Clients Supported From App */ +static UINT16 btc_max_hf_clients = 1; +/* HF Param Definition */ +static hf_local_param_t hf_local_param[BTC_HF_NUM_CB]; + +#if (BTM_WBS_INCLUDED == TRUE) +#ifndef BTC_HF_FEATURES +#define BTC_HF_FEATURES ( BTA_AG_FEAT_ECNR | \ + BTA_AG_FEAT_REJECT | \ + BTA_AG_FEAT_ECS | \ + BTA_AG_FEAT_EXTERR | \ + BTA_AG_FEAT_VREC | \ + BTA_AG_FEAT_INBAND | \ + BTA_AG_FEAT_CODEC | \ + BTA_AG_FEAT_UNAT) +#endif +#else +#ifndef BTC_HF_FEATURES +#define BTC_HF_FEATURES ( BTA_AG_FEAT_ECNR | \ + BTA_AG_FEAT_REJECT | \ + BTA_AG_FEAT_ECS | \ + BTA_AG_FEAT_EXTERR | \ + BTA_AG_FEAT_VREC | \ + BTA_AG_FEAT_INBAND | \ + BTA_AG_FEAT_UNAT) +#endif +#endif + +/* wide band synchronous */ +#ifndef BTC_HF_WBS_PREFERRED +#define BTC_HF_WBS_PREFERRED FALSE +#endif +BOOLEAN btc_conf_hf_force_wbs = BTC_HF_WBS_PREFERRED; + +#define CHECK_HF_INIT() do { \ +if (! hf_local_param[idx].btc_hf_cb.initialized) { \ +BTIF_TRACE_WARNING("CHECK_HF_INIT: %s: HF AG not initialized", __FUNCTION__);\ +return BT_STATUS_NOT_READY; \ +} \ +else\ +{\ +BTIF_TRACE_EVENT("CHECK_HF_INIT: %s", __FUNCTION__);\ +}\ +} while (0) + +#define CHECK_HF_SLC_CONNECTED() do { \ +if (! hf_local_param[idx].btc_hf_cb.initialized || \ + hf_local_param[idx].btc_hf_cb.connection_state != ESP_HF_CONNECTION_STATE_SLC_CONNECTED) { \ +BTIF_TRACE_WARNING("CHECK_HF_INIT: %s: HF AG not initialized", __FUNCTION__);\ +return BT_STATUS_NOT_READY; \ +} \ +else\ +{\ +BTIF_TRACE_EVENT("CHECK_HF_INIT: %s", __FUNCTION__);\ +}\ +} while (0) + + +#define clear_phone_state() \ + hf_local_param[idx].btc_hf_cb.call_state = ESP_HF_CALL_STATUS_NO_CALLS; \ + hf_local_param[idx].btc_hf_cb.call_setup_state = ESP_HF_CALL_SETUP_STATUS_IDLE;\ + hf_local_param[idx].btc_hf_cb.num_active = 0; \ + hf_local_param[idx].btc_hf_cb.num_held = 0; + +/************************************************************************************ +** Static Function +************************************************************************************/ +static int btc_hf_idx_by_bdaddr(bt_bdaddr_t *bd_addr) +{ + for (int i = 0; i < btc_max_hf_clients; ++i) { + if ((bdcmp(bd_addr->address, hf_local_param[i].btc_hf_cb.connected_bda.address) == 0) + || bd_addr->address) { + return i; + } + } + return BTC_HF_INVALID_IDX; +} + +static BOOLEAN is_connected(bt_bdaddr_t *bd_addr) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + if (((hf_local_param[idx].btc_hf_cb.connection_state == ESP_HF_CONNECTION_STATE_CONNECTED) || + (hf_local_param[idx].btc_hf_cb.connection_state == ESP_HF_CONNECTION_STATE_SLC_CONNECTED)) && + ((bd_addr == NULL) || (bdcmp(bd_addr->address,hf_local_param[idx].btc_hf_cb.connected_bda.address) == 0))) { + return TRUE; + } + return FALSE; +} + +static int btc_hf_latest_connected_idx(void) +{ + struct timespec now, conn_time_delta; + int latest_conn_idx = BTC_HF_INVALID_IDX; + clock_gettime(CLOCK_MONOTONIC, &now); + conn_time_delta.tv_sec = now.tv_sec; + + for (int i = 0; i < btc_max_hf_clients; i++) { + if (hf_local_param[i].btc_hf_cb.connection_state == ESP_HF_CONNECTION_STATE_SLC_CONNECTED) { + if ((now.tv_sec - hf_local_param[i].btc_hf_cb.connected_timestamp.tv_sec) < conn_time_delta.tv_sec) { + conn_time_delta.tv_sec = now.tv_sec - hf_local_param[i].btc_hf_cb.connected_timestamp.tv_sec; + latest_conn_idx = i; + } + } + } + return latest_conn_idx; +} + +/************************************************************************************ +** Cb and Evt +************************************************************************************/ +static inline void btc_hf_cb_to_app(esp_hf_cb_event_t event, esp_hf_cb_param_t *param) +{ + esp_hf_cb_t btc_hf_callbacks = (esp_hf_cb_t)btc_profile_cb_get(BTC_PID_HF); + if (btc_hf_callbacks) { + btc_hf_callbacks(event, param); + } +} + +static void send_indicator_update(UINT16 indicator, UINT16 value) +{ + tBTA_AG_RES_DATA ag_res; + memset(&ag_res, 0, sizeof(tBTA_AG_RES_DATA)); + ag_res.ind.type = indicator; + ag_res.ind.value = value; + BTA_AgResult(BTA_AG_HANDLE_ALL, BTA_AG_IND_RES, &ag_res); +} + +static void btc_hf_cind_evt(tBTA_AG_IND *ind) +{ + esp_hf_cb_param_t param; + memset(¶m, 0, sizeof(esp_hf_cb_param_t)); + + switch (ind->type) { + case BTA_AG_IND_CALL: + param.cind.call_status = ind->value; + break; + case BTA_AG_IND_CALLSETUP: + param.cind.call_setup_status = ind->value; + break; + case BTA_AG_IND_SERVICE: + param.cind.svc = ind->value; + break; + case BTA_AG_IND_SIGNAL: + param.cind.signal_strength = ind->value; + break; + case BTA_AG_IND_ROAM: + param.cind.roam = ind->value; + break; + case BTA_AG_IND_BATTCHG: + param.cind.battery_level = ind->value; + break; + case BTA_AG_IND_CALLHELD: + param.cind.call_held_status = ind->value; + break; + } + btc_hf_cb_to_app(ESP_HF_CIND_RESPONSE_EVT, ¶m); +} + +static void bte_hf_evt(tBTA_AG_EVT event, tBTA_AG *param) +{ + int param_len = 0; + /* TODO: BTA sends the union members and not tBTA_AG. If using param_len=sizeof(tBTA_AG), we get a crash on memcpy */ + if (BTA_AG_REGISTER_EVT == event) { + param_len = sizeof(tBTA_AG_REGISTER); + } + else if (BTA_AG_OPEN_EVT == event) { + param_len = sizeof(tBTA_AG_OPEN); + } + else if ((BTA_AG_CLOSE_EVT == event) || (BTA_AG_AUDIO_OPEN_EVT == event) || (BTA_AG_AUDIO_CLOSE_EVT == event)) { + param_len = sizeof(tBTA_AG_HDR); + } + else if (BTA_AG_CONN_EVT == event) { + param_len = sizeof(tBTA_AG_CONN); + } + else if (param) { + param_len = sizeof(tBTA_AG_VAL); + } + btc_msg_t msg; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_HF; + msg.act = event; + + /* Switch to BTC context */ + bt_status_t status = btc_transfer_context(&msg, param, param_len, NULL); + /* catch any failed context transfers */ + BTC_ASSERTC(status == BT_STATUS_SUCCESS, "context transfer failed", status); +} + +/************************************************************************************ +** Data flow control & Service management. +************************************************************************************/ +void btc_hf_reg_data_cb(esp_hf_incoming_data_cb_t recv, esp_hf_outgoing_data_cb_t send) +{ + hf_local_param[0].btc_hf_incoming_data_cb = recv; + hf_local_param[0].btc_hf_outgoing_data_cb = send; +} + +void btc_hf_incoming_data_cb_to_app(const uint8_t *data, uint32_t len) +{ + int idx = 0; + // todo: critical section protection + if (hf_local_param[idx].btc_hf_incoming_data_cb) { + hf_local_param[idx].btc_hf_incoming_data_cb(data, len); + } +} + +uint32_t btc_hf_outgoing_data_cb_to_app(uint8_t *data, uint32_t len) +{ + int idx = 0; + // todo: critical section protection + if (hf_local_param[idx].btc_hf_outgoing_data_cb) { + return hf_local_param[idx].btc_hf_outgoing_data_cb(data, len); + } else { + return 0; + } +} + +bt_status_t btc_hf_execute_service(BOOLEAN b_enable) +{ + char * p_service_names[] = BTC_HF_SERVICE_NAMES; + int idx; + if (b_enable) { + /* Enable and register with BTA-AG */ + BTA_AgEnable(BTA_AG_PARSE, bte_hf_evt); + for (idx = 0; idx < btc_max_hf_clients; idx++) { + BTA_AgRegister(BTC_HF_SERVICES, BTC_HF_SECURITY, BTC_HF_FEATURES, p_service_names, BTC_HF_ID_1); + } + } else { + /* De-register AG */ + for (idx = 0; idx < btc_max_hf_clients; idx++) { + BTA_AgDeregister(hf_local_param[idx].btc_hf_cb.handle); + } + /* Disable AG */ + BTA_AgDisable(); + } + return BT_STATUS_SUCCESS; +} + +/************************************************************************************ +** BTC HFP AG API FUNCTION +************************************************************************************/ +/************************************************************************************ +** Initialization and Connection Handle +************************************************************************************/ +bt_status_t btc_hf_init(bt_bdaddr_t *bd_addr) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + BTC_TRACE_DEBUG("%s - max_hf_clients=%d", __func__, btc_max_hf_clients); + /* Invoke the enable service API to the core to set the appropriate service_id + * Internally, the HSP_SERVICE_ID shall also be enabled if HFP is enabled (phone) + * othwerwise only HSP is enabled (tablet)*/ +#if (defined(BTC_HF_SERVICES) && (BTC_HF_SERVICES & BTA_HFP_SERVICE_MASK)) + btc_dm_enable_service(BTA_HFP_SERVICE_ID); +#else + btc_dm_enable_service(BTA_HSP_SERVICE_ID); +#endif + clear_phone_state(); + memset(&hf_local_param[idx].btc_hf_cb, 0, sizeof(btc_hf_cb_t)); + // custom initialization here + hf_local_param[idx].btc_hf_cb.initialized = true; +// set audio path +#if BTM_SCO_HCI_INCLUDED + uint8_t data_path = ESP_SCO_DATA_PATH_HCI; +#else + uint8_t data_path = ESP_SCO_DATA_PATH_PCM; +#endif + esp_bredr_sco_datapath_set(data_path); + return BT_STATUS_SUCCESS; +} + +void btc_hf_deinit(bt_bdaddr_t *bd_addr) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + BTC_TRACE_EVENT("%s", __FUNCTION__); + btc_dm_disable_service(BTA_HFP_SERVICE_ID); + hf_local_param[idx].btc_hf_cb.initialized = false; +} + +static bt_status_t connect_init(bt_bdaddr_t *bd_addr, uint16_t uuid) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + CHECK_HF_INIT(); + if (idx == btc_max_hf_clients) { + return BT_STATUS_BUSY; + } + + if (!is_connected(bd_addr)) { + hf_local_param[idx].btc_hf_cb.connection_state = ESP_HF_CONNECTION_STATE_CONNECTING; + bdcpy(hf_local_param[idx].btc_hf_cb.connected_bda.address, bd_addr->address); + BTA_AgOpen(hf_local_param[idx].btc_hf_cb.handle, hf_local_param[idx].btc_hf_cb.connected_bda.address, BTC_HF_SECURITY, BTC_HF_SERVICES); + return BT_STATUS_SUCCESS; + } + return BT_STATUS_BUSY; +} +bt_status_t btc_hf_connect(bt_bdaddr_t *bd_addr) +{ + return btc_queue_connect(UUID_SERVCLASS_AG_HANDSFREE, bd_addr, connect_init); +} + +bt_status_t btc_hf_disconnect(bt_bdaddr_t *bd_addr) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + CHECK_HF_INIT(); + if ((idx < 0) || (idx >= BTC_HF_NUM_CB)) { + BTC_TRACE_ERROR("%s: Invalid index %d", __FUNCTION__, idx); + return BT_STATUS_FAIL; + } + + if (is_connected(bd_addr) && (idx != BTC_HF_INVALID_IDX)) { + BTA_AgClose(hf_local_param[idx].btc_hf_cb.handle); + return BT_STATUS_SUCCESS; + } + return BT_STATUS_FAIL; +} + +bt_status_t btc_hf_connect_audio(bt_bdaddr_t *bd_addr) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + CHECK_HF_SLC_CONNECTED(); + if ((idx < 0) || (idx >= BTC_HF_NUM_CB)) { + BTC_TRACE_ERROR("%s: Invalid index %d", __FUNCTION__, idx); + return BT_STATUS_FAIL; + } + + if (is_connected(bd_addr) && (idx != BTC_HF_INVALID_IDX)) { + BTA_AgAudioOpen(hf_local_param[idx].btc_hf_cb.handle); + /* Inform the application that the audio connection has been initiated successfully */ + do { + esp_hf_cb_param_t param; + memset(¶m, 0, sizeof(esp_hf_cb_param_t)); + param.audio_stat.state = ESP_HF_AUDIO_STATE_CONNECTING; + memcpy(param.audio_stat.remote_addr, &hf_local_param[idx].btc_hf_cb.connected_bda, sizeof(esp_bd_addr_t)); + btc_hf_cb_to_app(ESP_HF_AUDIO_STATE_EVT, ¶m); + } while (0); + return BT_STATUS_SUCCESS; + } + return BT_STATUS_FAIL; +} + +bt_status_t btc_hf_disconnect_audio(bt_bdaddr_t *bd_addr) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + CHECK_HF_SLC_CONNECTED(); + if ((idx < 0) || (idx >= BTC_HF_NUM_CB)) { + BTC_TRACE_ERROR("%s: Invalid index %d", __FUNCTION__, idx); + return BT_STATUS_FAIL; + } + + if (is_connected(bd_addr) && (idx != BTC_HF_INVALID_IDX)) { + BTA_AgAudioClose(hf_local_param[idx].btc_hf_cb.handle); + return BT_STATUS_SUCCESS; + } + return BT_STATUS_FAIL; +} + +/************************************************************************************ +** AT cmd Handle +************************************************************************************/ +//AT+VRA +static bt_status_t btc_hf_vra(bt_bdaddr_t *bd_addr, esp_hf_vr_state_t value) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + CHECK_HF_SLC_CONNECTED(); + if ((idx < 0) || (idx >= BTC_HF_NUM_CB)) { + BTC_TRACE_ERROR("%s: Invalid index %d", __FUNCTION__, idx); + return BT_STATUS_FAIL; + } + + if (is_connected(bd_addr) && (idx != BTC_HF_INVALID_IDX)) { + if (hf_local_param[idx].btc_hf_cb.peer_feat & BTA_AG_PEER_FEAT_VREC) { + tBTA_AG_RES_DATA ag_res; + memset(&ag_res, 0, sizeof(ag_res)); + ag_res.state = value; + BTA_AgResult(hf_local_param[idx].btc_hf_cb.handle, BTA_AG_BVRA_RES, &ag_res); + return BT_STATUS_SUCCESS; + } else { + return BT_STATUS_UNSUPPORTED; + } + } + return BT_STATUS_NOT_READY; +} + +static bt_status_t btc_hf_volume_control(bt_bdaddr_t *bd_addr, esp_hf_volume_type_t type, int volume) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + CHECK_HF_SLC_CONNECTED(); + if ((idx < 0) || (idx >= BTC_HF_NUM_CB)) { + BTC_TRACE_ERROR("%s: Invalid index %d", __FUNCTION__, idx); + return BT_STATUS_FAIL; + } + tBTA_AG_RES_DATA ag_res; + memset(&ag_res, 0, sizeof(tBTA_AG_RES_DATA)); + + if (is_connected(bd_addr) && (idx != BTC_HF_INVALID_IDX)) { + ag_res.num = volume; + BTA_AgResult(hf_local_param[idx].btc_hf_cb.handle, (type == ESP_HF_VOLUME_TYPE_SPK) ? BTA_AG_SPK_RES : BTA_AG_MIC_RES, &ag_res); + return BT_STATUS_SUCCESS; + } + return BT_STATUS_FAIL; +} + +// Pre-formatted AT response, typically in response to unknown AT cmd +static bt_status_t btc_hf_unat_response(bt_bdaddr_t *bd_addr, const char *unat) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + CHECK_HF_INIT(); + if ((idx < 0) || (idx >= BTC_HF_NUM_CB)) { + BTC_TRACE_ERROR("%s: Invalid index %d", __FUNCTION__, idx); + return BT_STATUS_FAIL; + } + + if (is_connected(NULL) && (idx != BTC_HF_INVALID_IDX)) + { + tBTA_AG_RES_DATA ag_res; + /* Format the response and send */ + memset(&ag_res, 0, sizeof(ag_res)); + strncpy(ag_res.str, unat, BTA_AG_AT_MAX_LEN); + BTA_AgResult(hf_local_param[idx].btc_hf_cb.handle, BTA_AG_UNAT_RES, &ag_res); + return BT_STATUS_SUCCESS; + } + return BT_STATUS_FAIL; +} + +// +CMEE: +static bt_status_t btc_hf_cmee_response(bt_bdaddr_t *bd_addr, esp_hf_at_response_code_t response_code, esp_hf_cme_err_t error_code) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + CHECK_HF_INIT(); + if ((idx < 0) || (idx >= BTC_HF_NUM_CB)) { + BTC_TRACE_ERROR("%s: Invalid index %d", __FUNCTION__, idx); + return BT_STATUS_FAIL; + } + + if (is_connected(bd_addr) && (idx != BTC_HF_INVALID_IDX)) { + tBTA_AG_RES_DATA ag_res; + memset(&ag_res, 0, sizeof(ag_res)); + if (response_code == ESP_HF_AT_RESPONSE_CODE_OK) { + ag_res.ok_flag = BTA_AG_OK_DONE; + } else { + ag_res.ok_flag = BTA_AG_OK_ERROR; + ag_res.errcode = error_code; + } + BTA_AgResult(hf_local_param[idx].btc_hf_cb.handle, BTA_AG_UNAT_RES, &ag_res); + return BT_STATUS_SUCCESS; + } + return BT_STATUS_FAIL; +} + +// +CIEV<...> for device status update +static bt_status_t btc_hf_indchange_notification(bt_bdaddr_t *bd_addr, + esp_hf_call_status_t call_state, + esp_hf_call_setup_status_t call_setup_state, + esp_hf_network_state_t ntk_state, int signal) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + CHECK_HF_INIT(); + if (is_connected(bd_addr)) { + /* Send all indicators to BTA. + * BTA will make sure no duplicates are sent out*/ + send_indicator_update(BTA_AG_IND_CALL, call_state); + send_indicator_update(BTA_AG_IND_CALLSETUP, call_setup_state); + send_indicator_update(BTA_AG_IND_SERVICE, ntk_state); + send_indicator_update(BTA_AG_IND_SIGNAL, signal); + return BT_STATUS_SUCCESS; + } + return BT_STATUS_SUCCESS; +} + +//AT+CIND response +static bt_status_t btc_hf_cind_response(bt_bdaddr_t *bd_addr, + esp_hf_call_setup_status_t call_status, + esp_hf_call_setup_status_t call_setup_status, + esp_hf_network_state_t ntk_state, int signal, esp_hf_service_type_t roam, int batt_lev, + esp_hf_call_held_status_t call_held_status) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + CHECK_HF_INIT(); + if ((idx < 0) || (idx >= BTC_HF_NUM_CB)) { + BTC_TRACE_ERROR("%s: Invalid index %d", __FUNCTION__, idx); + return BT_STATUS_FAIL; + } + + if (is_connected(bd_addr) && (idx != BTC_HF_INVALID_IDX)) { + tBTA_AG_RES_DATA ag_res; + memset(&ag_res, 0, sizeof (ag_res)); + sprintf(ag_res.str, "%d,%d,%d,%d,%d,%d,%d", + call_status, /* Call state*/ + call_setup_status, /* Callsetup state */ + ntk_state, /* network service */ + signal, /* Signal strength */ + roam, /* Roaming indicator */ + batt_lev, /* Battery level */ + call_held_status /* Callheld state */ + ); + BTA_AgResult(hf_local_param[idx].btc_hf_cb.handle, BTA_AG_CIND_RES, &ag_res); + return BT_STATUS_SUCCESS; + } + return BT_STATUS_FAIL; +} + +//AT+COPS response +static bt_status_t btc_hf_cops_response(bt_bdaddr_t *bd_addr, const char *name) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + CHECK_HF_SLC_CONNECTED(); + if ((idx < 0) || (idx >= BTC_HF_NUM_CB)) { + BTC_TRACE_ERROR("%s: Invalid index %d", __FUNCTION__, idx); + return BT_STATUS_FAIL; + } + + if (is_connected(bd_addr) && (idx != BTC_HF_INVALID_IDX)) { + tBTA_AG_RES_DATA ag_res; + memset (&ag_res, 0, sizeof (ag_res)); + /* Format the response */ + sprintf(ag_res.str, "0,0,\"%s\"", name); + ag_res.ok_flag = BTA_AG_OK_DONE; + BTA_AgResult(hf_local_param[idx].btc_hf_cb.handle, BTA_AG_COPS_RES, &ag_res); + return BT_STATUS_SUCCESS; + } + return BT_STATUS_FAIL; +} + +//AT+CLCC response +static bt_status_t btc_hf_clcc_response(bt_bdaddr_t *bd_addr, int index, esp_hf_current_call_direction_t dir, + esp_hf_current_call_status_t current_call_state, + esp_hf_current_call_mode_t mode, esp_hf_current_call_mpty_type_t mpty, + const char *number, esp_hf_call_addr_type_t type) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + CHECK_HF_SLC_CONNECTED(); + if ((idx < 0) || (idx >= BTC_HF_NUM_CB)) { + BTC_TRACE_ERROR("%s: Invalid index %d", __FUNCTION__, idx); + return BT_STATUS_FAIL; + } + + if (is_connected(bd_addr) && (idx != BTC_HF_INVALID_IDX)) { + tBTA_AG_RES_DATA ag_res; + memset (&ag_res, 0, sizeof (ag_res)); + if (index == 0) { + ag_res.ok_flag = BTA_AG_OK_DONE; + } else { + BTC_TRACE_EVENT("clcc_response: [%d] dir: %d current_call_state: %d mode: %d number: %s type: %d", + index, dir, current_call_state, mode, number, type); + int loc = sprintf (ag_res.str, "%d,%d,%d,%d,%d", index, dir, current_call_state, mode, mpty); + if (number) { + if ((type == ESP_HF_CALL_ADDR_TYPE_INTERNATIONAL) && (*number != '+')) { + sprintf(&ag_res.str[loc], ",\"+%s\",%d", number, type); + } else { + sprintf(&ag_res.str[loc], ",\"%s\",%d", number, type); + } + } + } + BTA_AgResult(hf_local_param[idx].btc_hf_cb.handle, BTA_AG_CLCC_RES, &ag_res); + return BT_STATUS_SUCCESS; + } + return BT_STATUS_FAIL; +} + +//AT+CNUM +static bt_status_t btc_hf_cnum_response(bt_bdaddr_t *bd_addr, const char *number, esp_hf_subscriber_service_type_t type) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + CHECK_HF_SLC_CONNECTED(); + + if (is_connected(bd_addr) && (idx != BTC_HF_INVALID_IDX)) { + tBTA_AG_RES_DATA ag_res; + memset(&ag_res, 0, sizeof (ag_res)); + BTC_TRACE_EVENT("cnum_response: number = %s, type = %d", number, type); + if (number) { + sprintf(ag_res.str, ",\"%s\",%d",number, type); + } else { + sprintf(ag_res.str, ",\"\",%d",type); + } + ag_res.ok_flag = BTA_AG_OK_DONE; + BTA_AgResult(hf_local_param[idx].btc_hf_cb.handle, BTA_AG_CNUM_RES, &ag_res); + return BT_STATUS_SUCCESS; + } + return BT_STATUS_FAIL; +} + +//+BSIR +static bt_status_t btc_hf_inband_ring(bt_bdaddr_t *bd_addr, esp_hf_in_band_ring_state_t state) +{ + int idx = btc_hf_idx_by_bdaddr(bd_addr); + CHECK_HF_SLC_CONNECTED(); + + if (is_connected(bd_addr) && (idx != BTC_HF_INVALID_IDX)) { + tBTA_AG_RES_DATA ag_res; + memset (&ag_res, 0, sizeof (ag_res)); + ag_res.state = state; + BTA_AgResult(hf_local_param[idx].btc_hf_cb.handle, BTA_AG_INBAND_RING_RES, &ag_res); + return BT_STATUS_SUCCESS; + } + return BT_STATUS_FAIL; +} + +//Update phone status whether AG or HF act +static bt_status_t btc_hf_phone_state_update(bt_bdaddr_t *bd_addr,int num_active, int num_held, + esp_hf_call_status_t call_state, esp_hf_call_setup_status_t call_setup_state, + char *number, esp_hf_call_addr_type_t type) +{ + tBTA_AG_RES res = 0xff; + tBTA_AG_RES_DATA ag_res; + bt_status_t status = BT_STATUS_SUCCESS; + BOOLEAN activeCallUpdated = FALSE; + int idx = btc_hf_idx_by_bdaddr(bd_addr), i; + + /* hf_idx is index of connected HS that sent ATA/BLDN, otherwise index of latest connected HS */ + if (hf_local_param->hf_idx != BTC_HF_INVALID_IDX) { + idx = hf_local_param->hf_idx; + } else { + idx = btc_hf_latest_connected_idx(); + } + + BTC_TRACE_DEBUG("phone_state_change: idx = %d", idx); + CHECK_HF_SLC_CONNECTED(); + BTC_TRACE_DEBUG("phone_state_change: num_active=%d [prev: %d] num_held=%d[prev: %d] call =%s [prev: %s] call_setup=%s [prev: %s]", + num_active, hf_local_param[idx].btc_hf_cb.num_active, + num_held, hf_local_param[idx].btc_hf_cb.num_held, + dump_hf_call_state(call_state), dump_hf_call_state(hf_local_param[idx].btc_hf_cb.call_state), + dump_hf_call_setup_state(call_setup_state), dump_hf_call_setup_state(hf_local_param[idx].btc_hf_cb.call_setup_state)); + + /* If all indicators are 0, send end call and return */ + if (num_active == 0 && num_held == 0 && call_state == ESP_HF_CALL_STATUS_NO_CALLS && call_setup_state == ESP_HF_CALL_SETUP_STATUS_IDLE) { + BTC_TRACE_DEBUG("%s: Phone on hook", __FUNCTION__); + + /* Record call termination timestamp if there was an active/held call or call_setup_state > ESP_HF_CALL_SETUP_STATUS_IDLE */ + if ((hf_local_param[idx].btc_hf_cb.call_state != ESP_HF_CALL_STATUS_NO_CALLS) || + (hf_local_param[idx].btc_hf_cb.call_setup_state != ESP_HF_CALL_SETUP_STATUS_IDLE) || + (hf_local_param[idx].btc_hf_cb.num_active) || + (hf_local_param[idx].btc_hf_cb.num_held)) { + BTC_TRACE_DEBUG("%s: Record call termination timestamp", __FUNCTION__); + clock_gettime(CLOCK_MONOTONIC, &hf_local_param[0].btc_hf_cb.call_end_timestamp); + } + BTA_AgResult(BTA_AG_HANDLE_ALL, BTA_AG_END_CALL_RES, NULL); + hf_local_param->hf_idx = BTC_HF_INVALID_IDX; + + /* If held call was present, reset that as well. */ + if (hf_local_param[idx].btc_hf_cb.num_held) { + send_indicator_update(BTA_AG_IND_CALLHELD, 0); + } + goto update_call_states; + } + + /* Phone state can change when: + * 1. An outgoing/incoming call was answered. + * 2. A held was resumed. + * 3. Without callsetup notifications, call became active. (HF Unit links in during an Active call.) + */ + + /* Handle case(3) here prior to call setup handling.*/ + if (((num_active + num_held) > 0) && + (hf_local_param[idx].btc_hf_cb.num_active == 0) && + (hf_local_param[idx].btc_hf_cb.num_held == 0) && + (hf_local_param[idx].btc_hf_cb.call_setup_state == ESP_HF_CALL_SETUP_STATUS_IDLE)) { + BTC_TRACE_DEBUG("%s: Active/Held call notification received without call setup update", __FUNCTION__); + + memset(&ag_res, 0, sizeof(tBTA_AG_RES_DATA)); + ag_res.audio_handle = hf_local_param[idx].btc_hf_cb.handle; + + /* Addition callsetup with the Active call. */ + if (call_setup_state != ESP_HF_CALL_SETUP_STATUS_IDLE) { + res = BTA_AG_MULTI_CALL_RES; + } else { + res = BTA_AG_OUT_CALL_CONN_RES; + } + /* CIND response should have been updated. */ + BTA_AgResult(BTA_AG_HANDLE_ALL, res, &ag_res); + /* Just open SCO conenction. */ + BTA_AgAudioOpen(ag_res.audio_handle); + activeCallUpdated = TRUE; + } + + /* Handle call_setup indicator change. */ + if (call_setup_state != hf_local_param[idx].btc_hf_cb.call_setup_state) { + BTC_TRACE_DEBUG("%s: Call setup states changed. old: %s new: %s", __FUNCTION__, + dump_hf_call_setup_state(hf_local_param[idx].btc_hf_cb.call_setup_state), + dump_hf_call_setup_state(call_setup_state)); + memset(&ag_res, 0, sizeof(tBTA_AG_RES_DATA)); + + switch(call_setup_state) + { + case ESP_HF_CALL_SETUP_STATUS_IDLE: + { + switch(hf_local_param[idx].btc_hf_cb.call_setup_state) + { + case ESP_HF_CALL_SETUP_STATUS_INCOMING: + { + if (num_active > hf_local_param[idx].btc_hf_cb.num_active) { + res = BTA_AG_IN_CALL_CONN_RES; + ag_res.audio_handle = hf_local_param[idx].btc_hf_cb.handle; + } else if (num_held > hf_local_param[idx].btc_hf_cb.num_held) { + res = BTA_AG_IN_CALL_HELD_RES; + } else { + res = BTA_AG_CALL_CANCEL_RES; + } + break; + } + + case ESP_HF_CALL_SETUP_STATUS_OUTGOING_DIALING: + case ESP_HF_CALL_SETUP_STATUS_OUTGOING_ALERTING: + { + if (num_active > hf_local_param[idx].btc_hf_cb.num_active) { + res = BTA_AG_OUT_CALL_CONN_RES; + ag_res.audio_handle = BTA_AG_HANDLE_SCO_NO_CHANGE; + } else { + res = BTA_AG_CALL_CANCEL_RES; + } + break; + } + + default: + BTC_TRACE_ERROR("%s: Call setup state no change.", __FUNCTION__); + status = BT_STATUS_PARM_INVALID; + break; + } + break; + } + + case ESP_HF_CALL_SETUP_STATUS_INCOMING: + { + if (num_active || num_held) { + res = BTA_AG_CALL_WAIT_RES; + } else { + res = BTA_AG_IN_CALL_RES; + } + + if (number) { + int loc = 0; + if ((type == ESP_HF_CALL_ADDR_TYPE_INTERNATIONAL) && (*number != '+')) { + loc = sprintf (ag_res.str, "\"+%s\"", number); + } else { + loc = sprintf (ag_res.str, "\"%s\"", number); + } + ag_res.num = type; + if (res == BTA_AG_CALL_WAIT_RES) { + sprintf(&ag_res.str[loc], ",%d", type); + } + } + break; + } + + case ESP_HF_CALL_SETUP_STATUS_OUTGOING_DIALING: + { + if (!(num_active + num_held)) { + ag_res.audio_handle = hf_local_param[idx].btc_hf_cb.handle; + } + res = BTA_AG_OUT_CALL_ORIG_RES; + break; + } + + case ESP_HF_CALL_SETUP_STATUS_OUTGOING_ALERTING: + { + if ((hf_local_param[idx].btc_hf_cb.call_setup_state == ESP_HF_CALL_SETUP_STATUS_IDLE) && + !(num_active + num_held)) { + ag_res.audio_handle = hf_local_param[idx].btc_hf_cb.handle; + /* Force SCO setup here.*/ + BTA_AgAudioOpen(ag_res.audio_handle); + } + res = BTA_AG_OUT_CALL_ALERT_RES; + break; + } + + default: + BTC_TRACE_ERROR("%s: Incorrect new ringing call state", __FUNCTION__); + status = BT_STATUS_PARM_INVALID; + break; + } + BTC_TRACE_DEBUG("%s: Call setup state changed. res=%d, audio_handle=%d", __FUNCTION__, res, ag_res.audio_handle); + if (res) { + BTA_AgResult(BTA_AG_HANDLE_ALL, res, &ag_res); + } + /* If call setup is idle, we have already updated call indicator, jump out */ + if (call_setup_state == ESP_HF_CALL_SETUP_STATUS_IDLE) { + /* Check & Update call_held_state */ + if ((num_held > 0) && (num_active > 0)) + send_indicator_update(BTA_AG_IND_CALLHELD, ESP_HF_CALL_HELD_STATUS_HELD_AND_ACTIVE); + goto update_call_states; + } + } + + memset(&ag_res, 0, sizeof(tBTA_AG_RES_DATA)); + + /* Handle call_state indicator change. */ + if (!activeCallUpdated && + ((num_active + num_held) != (hf_local_param[idx].btc_hf_cb.num_active + hf_local_param[idx].btc_hf_cb.num_held))) { + BTC_TRACE_DEBUG("%s: Active call states changed. old: %d new: %d", __FUNCTION__, hf_local_param[idx].btc_hf_cb.num_active, num_active); + send_indicator_update(BTA_AG_IND_CALL, ((num_active + num_held) > 0) ? 1 : 0); + } + + /* Handle call_held_state indicator change. */ + if (num_held != hf_local_param[idx].btc_hf_cb.num_held || + ((num_active == 0) && ((num_held + hf_local_param[idx].btc_hf_cb.num_held) > 1))) { + BTC_TRACE_DEBUG("%s: Held call states changed. old: %d new: %d", __FUNCTION__, hf_local_param[idx].btc_hf_cb.num_held, num_held); + send_indicator_update(BTA_AG_IND_CALLHELD, ((num_held == 0) ? 0 : ((num_active == 0) ? 2 : 1))); + } + + /* Handle Call Active/Held Swap indicator update.*/ + if ((call_setup_state == hf_local_param[idx].btc_hf_cb.call_setup_state) && + (num_active) && + (num_held) && + (num_active == hf_local_param[idx].btc_hf_cb.num_active) && + (num_held == hf_local_param[idx].btc_hf_cb.num_held)) { + BTC_TRACE_DEBUG("%s: Calls swapped", __FUNCTION__); + send_indicator_update(BTA_AG_IND_CALLHELD, 1); + } + +update_call_states: + for (i = 0; i < btc_max_hf_clients; i++) { + hf_local_param[i].btc_hf_cb.num_active = num_active; + hf_local_param[i].btc_hf_cb.num_held = num_held; + hf_local_param[i].btc_hf_cb.call_state = call_state; + hf_local_param[i].btc_hf_cb.call_setup_state = call_setup_state; + } + return status; +} + +/************************************************************************************ +** Memory malloc and release +************************************************************************************/ +void btc_hf_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + btc_hf_args_t *dst = (btc_hf_args_t *) p_dest; + btc_hf_args_t *src = (btc_hf_args_t *) p_src; + + switch (msg->act) { + case BTC_HF_UNAT_RESPONSE_EVT: + { + dst->unat_rep.unat = (char *)osi_malloc(strlen(src->unat_rep.unat)+1); + if(dst->unat_rep.unat) { + memcpy(dst->unat_rep.unat, src->unat_rep.unat, strlen(src->unat_rep.unat)+1); + } else if (strlen(src->unat_rep.unat) == 0) { + BTC_TRACE_DEBUG("%s %d no mem\n", __func__, msg->act); + } else { + BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act); + } + break; + } + + case BTC_HF_COPS_RESPONSE_EVT: + { + dst->cops_rep.name = (char *)osi_malloc(strlen(src->cops_rep.name)+1); + if(dst->cops_rep.name) { + memcpy(dst->cops_rep.name, src->cops_rep.name, strlen(src->cops_rep.name)+1); + } else if (strlen(src->cops_rep.name) == 0) { + BTC_TRACE_DEBUG("%s %d no mem\n", __func__, msg->act); + } else { + BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act); + } + break; + } + + case BTC_HF_CLCC_RESPONSE_EVT: + { + dst->clcc_rep.number = (char *)osi_malloc(strlen(src->clcc_rep.number)+1); + if(dst->clcc_rep.number) { + memcpy(dst->clcc_rep.number, src->clcc_rep.number, strlen(src->clcc_rep.number)+1); + } else if (strlen(src->clcc_rep.number) == 0) { + BTC_TRACE_DEBUG("%s %d no mem\n", __func__, msg->act); + } else { + BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act); + } + break; + } + + case BTC_HF_CNUM_RESPONSE_EVT: + { + dst->cnum_rep.number = (char *)osi_malloc(strlen(src->cnum_rep.number)+1); + if(dst->cnum_rep.number) { + memcpy(dst->cnum_rep.number, src->cnum_rep.number, strlen(src->cnum_rep.number)+1); + } else if (strlen(src->cnum_rep.number) == 0) { + BTC_TRACE_DEBUG("%s %d no mem\n", __func__, msg->act); + } else { + BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act); + } + break; + } + + case BTC_HF_AC_INCALL_EVT: + case BTC_HF_RJ_INCALL_EVT: + case BTC_HF_OUT_CALL_EVT: + case BTC_HF_END_CALL_EVT: + { + dst->phone.number = (char *)osi_malloc(strlen(src->phone.number)+1); + if(dst->phone.number) { + memcpy(dst->phone.number, src->phone.number, strlen(src->phone.number)+1); + } else if (strlen(src->phone.number) == 0) { + BTC_TRACE_DEBUG("%s %d no mem\n", __func__, msg->act); + } else { + BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act); + } + break; + } + + default: + break; + } +} + +void btc_hf_arg_deep_free(btc_msg_t *msg) +{ + btc_hf_args_t *arg = (btc_hf_args_t *)msg->arg; + + switch (msg->act) { + case BTC_HF_UNAT_RESPONSE_EVT: + { + if (arg->unat_rep.unat) { + osi_free(arg->unat_rep.unat); + } + break; + } + + case BTC_HF_COPS_RESPONSE_EVT: + { + if (arg->cops_rep.name) { + osi_free(arg->cops_rep.name); + } + break; + } + + case BTC_HF_CLCC_RESPONSE_EVT: + { + if (arg->clcc_rep.number) { + osi_free(arg->clcc_rep.number); + } + break; + } + + case BTC_HF_CNUM_RESPONSE_EVT: + { + if (arg->cnum_rep.number) { + osi_free(arg->cnum_rep.number); + } + break; + } + + case BTC_HF_AC_INCALL_EVT: + case BTC_HF_RJ_INCALL_EVT: + case BTC_HF_OUT_CALL_EVT: + case BTC_HF_END_CALL_EVT: + { + if (arg->phone.number) { + osi_free(arg->phone.number); + } + break; + } + + default: + break; + } +} + +/************************************************************************************ +** Handler Functions (handle the cmd from app) +************************************************************************************/ +void btc_hf_call_handler(btc_msg_t *msg) +{ + btc_hf_args_t *arg = (btc_hf_args_t *)(msg->arg); + + switch (msg->act) { + case BTC_HF_INIT_EVT: + { + btc_hf_init(&arg->init); + break; + } + + case BTC_HF_DEINIT_EVT: + { + btc_hf_deinit(&arg->deinit); + break; + } + + case BTC_HF_CONNECT_EVT: + { + btc_hf_connect(&arg->connect); + break; + } + + case BTC_HF_DISCONNECT_EVT: + { + btc_hf_disconnect(&arg->disconnect); + break; + } + + case BTC_HF_CONNECT_AUDIO_EVT: + { + btc_hf_connect_audio(&arg->connect_audio); + break; + } + + case BTC_HF_DISCONNECT_AUDIO_EVT: + { + btc_hf_disconnect_audio(&arg->disconnect_audio); + break; + } + + case BTC_HF_VRA_EVT: + { + btc_hf_vra(&arg->vra_rep.remote_addr, arg->vra_rep.value); + if (arg->vra_rep.value) { + btc_hf_connect_audio(&arg->vra_rep.remote_addr); + } else { + btc_hf_disconnect_audio(&arg->vra_rep.remote_addr); + } + break; + } + + case BTC_HF_VOLUME_CONTROL_EVT: + { + btc_hf_volume_control(&arg->volcon.remote_addr, arg->volcon.target_type, arg->volcon.volume); + break; + } + + case BTC_HF_UNAT_RESPONSE_EVT: + { + btc_hf_unat_response(&arg->unat_rep.remote_addr, arg->unat_rep.unat); + } + + case BTC_HF_CME_ERR_EVT: + { + btc_hf_cmee_response(&arg->ext_at.remote_addr, arg->ext_at.response_code, arg->ext_at.error_code); + break; + } + + case BTC_HF_IND_NOTIFICATION_EVT: + { + btc_hf_indchange_notification(&arg->ind_change.remote_addr, + arg->ind_change.call_state, arg->ind_change.call_setup_state, + arg->ind_change.ntk_state, arg->ind_change.signal); + break; + } + + case BTC_HF_CIND_RESPONSE_EVT: + { + btc_hf_cind_response(&arg->cind_rep.remote_addr, + arg->cind_rep.call_state, arg->cind_rep.call_setup_state, + arg->cind_rep.ntk_state, arg->cind_rep.signal, arg->cind_rep.roam, arg->cind_rep.batt_lev, + arg->cind_rep.call_held_state); + break; + } + + case BTC_HF_COPS_RESPONSE_EVT: + { + btc_hf_cops_response(&arg->cops_rep.remote_addr, arg->cops_rep.name); + break; + } + + case BTC_HF_CLCC_RESPONSE_EVT: + { + btc_hf_clcc_response(&arg->clcc_rep.remote_addr, arg->clcc_rep.index, + arg->clcc_rep.dir, arg->clcc_rep.current_call_state, + arg->clcc_rep.mode, arg->clcc_rep.mpty, arg->clcc_rep.number, arg->clcc_rep.type); + break; + } + + case BTC_HF_CNUM_RESPONSE_EVT: + { + btc_hf_cnum_response(&arg->cnum_rep.remote_addr, arg->cnum_rep.number, arg->cnum_rep.type); + break; + } + + case BTC_HF_INBAND_RING_EVT: + { + btc_hf_inband_ring(&arg->bsir.remote_addr, arg->bsir.state); + break; + } + + case BTC_HF_AC_INCALL_EVT: + { + btc_hf_phone_state_update(&arg->phone.remote_addr, arg->phone.num_active, arg->phone.num_held, + arg->phone.call_state, arg->phone.call_setup_state, arg->phone.number, + arg->phone.call_addr_type); + break; + } + + case BTC_HF_RJ_INCALL_EVT: + { + btc_hf_phone_state_update(&arg->phone.remote_addr, arg->phone.num_active, arg->phone.num_held, + arg->phone.call_state, arg->phone.call_setup_state, arg->phone.number, + arg->phone.call_addr_type); + btc_hf_disconnect_audio(&arg->phone.remote_addr); + break; + } + + case BTC_HF_OUT_CALL_EVT: + { + btc_hf_connect_audio(&arg->phone.remote_addr); + btc_hf_phone_state_update(&arg->phone.remote_addr, arg->phone.num_active, arg->phone.num_held, + arg->phone.call_state, arg->phone.call_setup_state, arg->phone.number, + arg->phone.call_addr_type); + break; + } + + case BTC_HF_END_CALL_EVT: + { + btc_hf_phone_state_update(&arg->phone.remote_addr, arg->phone.num_active, arg->phone.num_held, + arg->phone.call_state, arg->phone.call_setup_state, arg->phone.number, + arg->phone.call_addr_type); + btc_hf_disconnect_audio(&arg->phone.remote_addr); + break; + } + + case BTC_HF_REGISTER_DATA_CALLBACK_EVT: + { + btc_hf_reg_data_cb(arg->reg_data_cb.recv, arg->reg_data_cb.send); + } + break; + + default: + BTC_TRACE_WARNING("%s : unhandled event: %d\n", __FUNCTION__, msg->act); + } + btc_hf_arg_deep_free(msg); +} + +void btc_hf_cb_handler(btc_msg_t *msg) +{ + uint16_t event = msg->act; + tBTA_AG *p_data = (tBTA_AG *)msg->arg; + esp_hf_cb_param_t param; + bdstr_t bdstr; + int idx; + + if (p_data == NULL) { + idx = BTC_HF_INVALID_IDX; + } else { + idx = p_data->hdr.handle - 1; + } + + BTC_TRACE_DEBUG("%s: event = %s", __FUNCTION__, dump_hf_event(event)); + if ((idx < 0) || (idx >= BTC_HF_NUM_CB)) { + BTC_TRACE_ERROR("%s: Invalid index %d", __FUNCTION__, idx); + return; + } + + switch (event) { + case BTA_AG_ENABLE_EVT: + case BTA_AG_DISABLE_EVT: + break; + + case BTA_AG_REGISTER_EVT: + { + hf_local_param[idx].btc_hf_cb.handle = p_data->reg.hdr.handle; + BTC_TRACE_DEBUG("%s: BTA_AG_REGISTER_EVT," "hf_local_param[%d].btc_hf_cb.handle = %d", + __FUNCTION__, idx, hf_local_param[idx].btc_hf_cb.handle); + break; + } + + case BTA_AG_OPEN_EVT: + { + if (p_data->open.status == BTA_AG_SUCCESS) + { + bdcpy(hf_local_param[idx].btc_hf_cb.connected_bda.address, p_data->open.bd_addr); + hf_local_param[idx].btc_hf_cb.connection_state = ESP_HF_CONNECTION_STATE_CONNECTED; + hf_local_param[idx].btc_hf_cb.peer_feat = 0; + hf_local_param[idx].btc_hf_cb.chld_feat = 0; + //clear_phone_state(); + } else if (hf_local_param[idx].btc_hf_cb.connection_state == ESP_HF_CONNECTION_STATE_CONNECTING) { + hf_local_param[idx].btc_hf_cb.connection_state = ESP_HF_CONNECTION_STATE_DISCONNECTED; + } else { + BTC_TRACE_WARNING("%s: AG open failed, but another device connected. status=%d state=%d connected device=%s", __FUNCTION__, + p_data->open.status, hf_local_param[idx].btc_hf_cb.connection_state, + bdaddr_to_string(&hf_local_param[idx].btc_hf_cb.connected_bda, bdstr, sizeof(bdstr))); + break; + } + + do { + memset(¶m, 0, sizeof(esp_hf_cb_param_t)); + memcpy(param.conn_stat.remote_bda, &hf_local_param[idx].btc_hf_cb.connected_bda, sizeof(esp_bd_addr_t)); + param.conn_stat.state = hf_local_param[idx].btc_hf_cb.connection_state; + param.conn_stat.peer_feat = 0; + param.conn_stat.chld_feat = 0; + btc_hf_cb_to_app(ESP_HF_CONNECTION_STATE_EVT, ¶m); + } while (0); + + if (hf_local_param[idx].btc_hf_cb.connection_state == ESP_HF_CONNECTION_STATE_DISCONNECTED) + bdsetany(hf_local_param[idx].btc_hf_cb.connected_bda.address); + + if (p_data->open.status != BTA_AG_SUCCESS) + btc_queue_advance(); + break; + } + + case BTA_AG_CONN_EVT: + { + clock_gettime(CLOCK_MONOTONIC, &(hf_local_param[idx].btc_hf_cb.connected_timestamp)); + BTC_TRACE_DEBUG("%s: BTA_AG_CONN_EVT, idx = %d ", __FUNCTION__, idx); + hf_local_param[idx].btc_hf_cb.peer_feat = p_data->conn.peer_feat; + hf_local_param[idx].btc_hf_cb.chld_feat = p_data->conn.chld_feat; + hf_local_param[idx].btc_hf_cb.connection_state = ESP_HF_CONNECTION_STATE_SLC_CONNECTED; + + do { + memset(¶m, 0, sizeof(esp_hf_cb_param_t)); + param.conn_stat.state = hf_local_param[idx].btc_hf_cb.connection_state; + param.conn_stat.peer_feat = hf_local_param[idx].btc_hf_cb.peer_feat; + param.conn_stat.chld_feat = hf_local_param[idx].btc_hf_cb.chld_feat; + memcpy(param.conn_stat.remote_bda, &hf_local_param[idx].btc_hf_cb.connected_bda, sizeof(esp_bd_addr_t)); + btc_hf_cb_to_app(ESP_HF_CONNECTION_STATE_EVT, ¶m); + } while(0); + hf_local_param[idx].hf_idx = btc_hf_latest_connected_idx(); + btc_queue_advance(); + break; + } + + case BTA_AG_CLOSE_EVT: + { + hf_local_param[idx].btc_hf_cb.connected_timestamp.tv_sec = 0; + hf_local_param[idx].btc_hf_cb.connection_state = ESP_HF_CONNECTION_STATE_DISCONNECTED; + BTC_TRACE_DEBUG("%s: BTA_AG_CLOSE_EVT," "hf_local_param[%d].btc_hf_cb.handle = %d", __FUNCTION__, + idx, hf_local_param[idx].btc_hf_cb.handle); + do { + memset(¶m, 0, sizeof(esp_hf_cb_param_t)); + param.conn_stat.state = ESP_HF_CONNECTION_STATE_DISCONNECTED; + param.conn_stat.peer_feat = 0; + param.conn_stat.chld_feat = 0; + memcpy(param.conn_stat.remote_bda, &hf_local_param[idx].btc_hf_cb.connected_bda, sizeof(esp_bd_addr_t)); + btc_hf_cb_to_app(ESP_HF_CONNECTION_STATE_EVT, ¶m); + } while(0); + bdsetany(hf_local_param[idx].btc_hf_cb.connected_bda.address); + clear_phone_state(); + hf_local_param[idx].hf_idx = btc_hf_latest_connected_idx(); + btc_queue_advance(); + break; + } + + case BTA_AG_AUDIO_OPEN_EVT: + { + do { + memset(¶m, 0, sizeof(esp_hf_cb_param_t)); + param.audio_stat.state = ESP_HF_AUDIO_STATE_CONNECTED; + memcpy(param.audio_stat.remote_addr, &hf_local_param[idx].btc_hf_cb.connected_bda,sizeof(esp_bd_addr_t)); + btc_hf_cb_to_app(ESP_HF_AUDIO_STATE_EVT, ¶m); + } while(0); + break; + } + + case BTA_AG_AUDIO_CLOSE_EVT: + { + do { + memset(¶m, 0, sizeof(esp_hf_cb_param_t)); + param.audio_stat.state = ESP_HF_AUDIO_STATE_DISCONNECTED; + memcpy(param.audio_stat.remote_addr, &hf_local_param[idx].btc_hf_cb.connected_bda, sizeof(esp_bd_addr_t)); + btc_hf_cb_to_app(ESP_HF_AUDIO_STATE_EVT, ¶m); + } while(0); + break; + } + + case BTA_AG_AT_BVRA_EVT: + { + do { + memset(¶m, 0, sizeof(esp_hf_cb_param_t)); + param.vra_rep.value = p_data->val.num; + btc_hf_cb_to_app(ESP_HF_BVRA_EVT, ¶m); + } while (0); + break; + } + + case BTA_AG_SPK_EVT: + case BTA_AG_MIC_EVT: + { + do { + memset(¶m, 0, sizeof(esp_hf_cb_param_t)); + param.volume_control.type = (event == BTA_AG_SPK_EVT) ? ESP_HF_VOLUME_CONTROL_TARGET_SPK : ESP_HF_VOLUME_CONTROL_TARGET_MIC; + param.volume_control.volume = p_data->val.num; + btc_hf_cb_to_app(ESP_HF_VOLUME_CONTROL_EVT, ¶m); + } while (0); + break; + } + + case BTA_AG_AT_UNAT_EVT: + { + memset(¶m, 0, sizeof(esp_hf_cb_param_t)); + param.unat_rep.unat = p_data->val.str; + btc_hf_cb_to_app(ESP_HF_UNAT_RESPONSE_EVT, ¶m); + break; + } + + case BTA_AG_AT_CBC_EVT: + case BTA_AG_AT_CIND_EVT: + { + btc_hf_cind_evt(&p_data->ind); + break; + } + + case BTA_AG_AT_COPS_EVT: + { + btc_hf_cb_to_app(ESP_HF_COPS_RESPONSE_EVT, NULL); + break; + } + + case BTA_AG_AT_CLCC_EVT: + { + btc_hf_cb_to_app(ESP_HF_CLCC_RESPONSE_EVT, NULL); + break; + } + + case BTA_AG_AT_CNUM_EVT: + { + btc_hf_cb_to_app(ESP_HF_CNUM_RESPONSE_EVT, NULL); + break; + } + + case BTA_AG_AT_VTS_EVT: + { + do { + memset(¶m, 0, sizeof(esp_hf_cb_param_t)); + param.vts_rep.code = p_data->val.str; + btc_hf_cb_to_app(ESP_HF_VTS_RESPONSE_EVT, ¶m); + } while(0); + break; + } + + case BTA_AG_AT_NREC_EVT: + { + do { + memset(¶m, 0, sizeof(esp_hf_cb_param_t)); + param.nrec.state = p_data->val.num; + btc_hf_cb_to_app(ESP_HF_NREC_RESPONSE_EVT, ¶m); + } while(0); + break; + } + + case BTA_AG_AT_A_EVT: + { + btc_hf_cb_to_app(ESP_HF_ATA_RESPONSE_EVT, NULL); + break; + } + + case BTA_AG_AT_CHUP_EVT: + { + btc_hf_cb_to_app(ESP_HF_CHUP_RESPONSE_EVT, NULL); + break; + } + + case BTA_AG_AT_BLDN_EVT: + case BTA_AG_AT_D_EVT: + { + do { + if (event == BTA_AG_AT_D_EVT && p_data->val.str) { // dial_number_or_memory + memset(¶m, 0, sizeof(esp_hf_cb_param_t)); + param.out_call.num_or_loc = osi_malloc((strlen(p_data->val.str) + 1) * sizeof(char)); + sprintf(param.out_call.num_or_loc, p_data->val.str); + btc_hf_cb_to_app(ESP_HF_DIAL_EVT, ¶m); + send_indicator_update(BTA_AG_IND_CALLSETUP,BTA_AG_CALLSETUP_OUTGOING); + osi_free(param.out_call.num_or_loc); + } else if (event == BTA_AG_AT_BLDN_EVT) { //dial_last + memset(¶m, 0, sizeof(esp_hf_cb_param_t)); + btc_hf_cb_to_app(ESP_HF_DIAL_EVT, ¶m); + } + } while(0); + break; + } + + case BTA_AG_AT_BINP_EVT: + case BTA_AG_AT_BTRH_EVT: + { + tBTA_AG_RES_DATA ag_res; + memset(&ag_res, 0, sizeof(ag_res)); + ag_res.ok_flag = BTA_AG_OK_ERROR; + ag_res.errcode = BTA_AG_ERR_OP_NOT_SUPPORTED; + BTA_AgResult(hf_local_param[idx].btc_hf_cb.handle, BTA_AG_UNAT_RES, &ag_res); + break; + } + + case BTA_AG_AT_BAC_EVT: + { + BTC_TRACE_DEBUG("AG Bitmap of peer-codecs %d", p_data->val.num); + memset(¶m, 0, sizeof(esp_hf_cb_param_t)); + param.codec.mode = p_data->val.num; + btc_hf_cb_to_app(ESP_HF_BAC_RESPONSE_EVT, ¶m); + break; + } + +#if (BTM_WBS_INCLUDED == TRUE ) + case BTA_AG_WBS_EVT: + { + BTC_TRACE_DEBUG("BTA_AG_WBS_EVT Set codec status %d codec %d 1=CVSD 2=MSBC", p_data->val.hdr.status, p_data->val.num); + memset(¶m, 0, sizeof(esp_hf_cb_param_t)); + param.codec.mode = p_data->val.num; + if(p_data->val.num == BTA_AG_CODEC_CVSD) { + btc_hf_cb_to_app(ESP_HF_BCS_RESPONSE_EVT, ¶m); + } else if(p_data->val.num == BTA_AG_CODEC_MSBC) { + btc_hf_cb_to_app(ESP_HF_BCS_RESPONSE_EVT, ¶m); + } else { + btc_hf_cb_to_app(ESP_HF_BCS_RESPONSE_EVT, ¶m); + } + break; + } +#endif + +#if (BTM_WBS_INCLUDED == TRUE) + /* If the peer supports mSBC and the BTC prefferred codec is also mSBC, then + ** we should set the BTA AG Codec to mSBC. This would trigger a +BCS to mSBC at the time + ** of SCO connection establishment */ + if ((btc_conf_hf_force_wbs == TRUE) && (p_data->val.num & BTA_AG_CODEC_MSBC)) { + BTC_TRACE_EVENT("%s btc_hf override-Preferred Codec to MSBC", __FUNCTION__); + BTA_AgSetCodec(hf_local_param[idx].btc_hf_cb.handle,BTA_AG_CODEC_MSBC); + } + else { + BTC_TRACE_EVENT("%s btif_hf override-Preferred Codec to CVSD", __FUNCTION__); + BTA_AgSetCodec(hf_local_param[idx].btc_hf_cb.handle,BTA_AG_CODEC_CVSD); + } +#endif + break; + case BTA_AG_AT_BCS_EVT: + BTC_TRACE_DEBUG("AG final seleded codec is %d 1=CVSD 2=MSBC", p_data->val.num); + /* no ESP_HF_WBS_NONE case, becuase HF 1.6 supported device can send BCS */ + btc_hf_cb_to_app(ESP_HF_BCS_RESPONSE_EVT, ¶m); + break; + default: + BTC_TRACE_WARNING("%s: Unhandled event: %d", __FUNCTION__, event); + break; + } +} +#endif // #if (BTC_HF_INCLUDED == TRUE) \ No newline at end of file diff --git a/components/bt/host/bluedroid/btc/profile/std/include/btc_hf_ag.h b/components/bt/host/bluedroid/btc/profile/std/include/btc_hf_ag.h new file mode 100644 index 000000000..e0f7484ed --- /dev/null +++ b/components/bt/host/bluedroid/btc/profile/std/include/btc_hf_ag.h @@ -0,0 +1,259 @@ +// Copyright 2018 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/******************************************************************************* + * + * Filename: btc_hf_ag.h + * + * Description: Main API header file for all BTC HF AG functions accessed + * from internal stack. + * + *******************************************************************************/ + +#ifndef __BTC_HF_AG_H__ +#define __BTC_HF_AG_H__ + +#include "common/bt_target.h" +#include "btc/btc_task.h" +#include "btc/btc_common.h" +#include "bta/bta_ag_api.h" +#include "esp_hf_ag_api.h" + + +#if (BTC_HF_INCLUDED == TRUE) + +/******************************************************************************* +** Type Defs +********************************************************************************/ +/* btc_hf_act_t */ +typedef enum +{ + //INIT + BTC_HF_INIT_EVT, + BTC_HF_DEINIT_EVT, + BTC_HF_CONNECT_EVT, + BTC_HF_DISCONNECT_EVT, + BTC_HF_CONNECT_AUDIO_EVT, + BTC_HF_DISCONNECT_AUDIO_EVT, + BTC_HF_VRA_EVT, + BTC_HF_VOLUME_CONTROL_EVT, + //AT_RESPONSE + BTC_HF_UNAT_RESPONSE_EVT, + BTC_HF_CME_ERR_EVT, + BTC_HF_IND_NOTIFICATION_EVT, + BTC_HF_CIND_RESPONSE_EVT, + BTC_HF_COPS_RESPONSE_EVT, + BTC_HF_CLCC_RESPONSE_EVT, + BTC_HF_CNUM_RESPONSE_EVT, + BTC_HF_INBAND_RING_EVT, + //CALL_HANDLE + BTC_HF_AC_INCALL_EVT, + BTC_HF_RJ_INCALL_EVT, + BTC_HF_OUT_CALL_EVT, + BTC_HF_END_CALL_EVT, + //REG + BTC_HF_REGISTER_DATA_CALLBACK_EVT +} btc_hf_act_t; + +/* btc_hf_args_t */ +typedef union +{ + // BTC_HF_INIT_EVT + bt_bdaddr_t init; + + //BTC_HF_DEINIT_EVT + bt_bdaddr_t deinit; + + // BTC_HF_CONNECT_EVT + bt_bdaddr_t connect; + + // BTC_HF_DISCONNECT_EVT + bt_bdaddr_t disconnect; + + // BTC_HF_CONNECT_AUDIO_EVT + bt_bdaddr_t connect_audio; + + // BTC_HF_DISCONNECT_AUDIO_EVT + bt_bdaddr_t disconnect_audio; + + //BTC_HF_VRA_EVT + struct vra_param { + bt_bdaddr_t remote_addr; + esp_hf_vr_state_t value; + } vra_rep; + + // BTC_HF_VOLUME_CONTROL_EVT + struct volcon_args { + bt_bdaddr_t remote_addr; + esp_hf_volume_control_target_t target_type; + int volume; + } volcon; + + //BTC_HF_UNAT_RESPONSE_EVT + struct unat_param { + bt_bdaddr_t remote_addr; + char *unat; + } unat_rep; + + //BTC_HF_CME_ERR_EVT + struct at_ok_err_args { + bt_bdaddr_t remote_addr; + esp_hf_at_response_code_t response_code; + esp_hf_cme_err_t error_code; + } ext_at; + + // BTC_HF_IND_NOTIFICATION_EVT + struct indchange_status { + bt_bdaddr_t remote_addr; + esp_hf_call_status_t call_state; + esp_hf_call_setup_status_t call_setup_state; + esp_hf_network_state_t ntk_state; + int signal; + } ind_change; + + //BTC_HF_CIND_RESPONSE_EVT + struct cind_args { + bt_bdaddr_t remote_addr; + esp_hf_call_status_t call_state; + esp_hf_call_setup_status_t call_setup_state; + esp_hf_network_state_t ntk_state; + int signal; + esp_hf_roaming_status_t roam; + int batt_lev; + esp_hf_call_held_status_t call_held_state; + } cind_rep; + + //BTC_HF_COPS_RESPONSE_EVT + struct cops_args { + bt_bdaddr_t remote_addr; + char *name; + } cops_rep; + + // BTC_HF_CLCC_RESPONSE_EVT + struct clcc_args { + bt_bdaddr_t remote_addr; + int index; + esp_hf_current_call_direction_t dir; + esp_hf_current_call_status_t current_call_state; + esp_hf_current_call_mode_t mode; + esp_hf_current_call_mpty_type_t mpty; + char *number; + esp_hf_call_addr_type_t type; + } clcc_rep; + + // BTC_HF_CNUM_RESPONSE_EVT + struct cnum_args { + bt_bdaddr_t remote_addr; + char *number; + esp_hf_subscriber_service_type_t type; + } cnum_rep; + + //BTC_HF_NREC_RESPONSE_EVT + bt_bdaddr_t nrec_rep; + + //BTC_HF_VTC_RESPONSE_EVT + struct bts_args { + bt_bdaddr_t remote_addr; + char *code; + } vts_rep; + + //BTC_HF_INBAND_RING_EVT + struct bsir_args { + bt_bdaddr_t remote_addr; + esp_hf_in_band_ring_state_t state; + } bsir; + + // BTC_HF_AC_INCALL_EVT + // BTC_HF_RJ_INCALL_EVT + // BTC_HF_OUT_CALL_EVT + // BTC_HF_END_CALL_EVT + struct phone_args { + bt_bdaddr_t remote_addr; + int num_active; + int num_held; + esp_hf_call_status_t call_state; + esp_hf_call_setup_status_t call_setup_state; + char *number; + esp_hf_call_addr_type_t call_addr_type; + } phone; + + // BTC_HF_REGISTER_DATA_CALLBACK_EVT + struct reg_data_callback { + esp_hf_incoming_data_cb_t recv; + esp_hf_outgoing_data_cb_t send; + } reg_data_cb; + +} btc_hf_args_t; + +/************************************************************************************ +** Local definitions +************************************************************************************/ +/* Number of BTC-HF-AG control blocks */ +#define BTC_HF_NUM_CB 1 + +/* Handsfree AG app ids for service registration */ +/* APP ID definition*/ +#define BTC_HF_ID_1 0 + +#if HFP_DYNAMIC_MEMORY == TRUE +extern hf_local_param_t *hf_local_param_ptr; +#define hf_local_param (*hf_local_param_ptr) +#endif + +/* BTC-AG control block to map bdaddr to BTA handle */ +typedef struct +{ + bool initialized; + UINT16 handle; + bt_bdaddr_t connected_bda; + tBTA_AG_PEER_FEAT peer_feat; + tBTA_AG_CHLD_FEAT chld_feat; + struct timespec call_end_timestamp; + struct timespec connected_timestamp; + esp_hf_connection_state_t connection_state; + esp_hf_vr_state_t vr_state; + int num_active; + int num_held; + esp_hf_call_status_t call_state; + esp_hf_call_setup_status_t call_setup_state; +} btc_hf_cb_t; + +typedef struct +{ + int hf_idx; + UINT32 btc_hf_features; + btc_hf_cb_t btc_hf_cb; + esp_hf_incoming_data_cb_t btc_hf_incoming_data_cb; + esp_hf_outgoing_data_cb_t btc_hf_outgoing_data_cb; +} hf_local_param_t; + +/******************************************************************************* +** BTC HF AG Handle Hub +********************************************************************************/ +void btc_hf_call_handler(btc_msg_t *msg); // act the cmd from esp-application + +void btc_hf_cb_handler(btc_msg_t *msg); //handle the event from bta + +void btc_hf_incoming_data_cb_to_app(const uint8_t *data, uint32_t len); + +uint32_t btc_hf_outgoing_data_cb_to_app(uint8_t *data, uint32_t len); + +void btc_hf_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); + +void btc_hf_arg_deep_free(btc_msg_t *msg); + +#endif // BTC_HF_INCLUDED == TRUE + +#endif /* __BTC_HF_AG_H__ */ + diff --git a/components/bt/host/bluedroid/common/include/common/bluedroid_user_config.h b/components/bt/host/bluedroid/common/include/common/bluedroid_user_config.h index 305ca11dc..cd05ce5fa 100644 --- a/components/bt/host/bluedroid/common/include/common/bluedroid_user_config.h +++ b/components/bt/host/bluedroid/common/include/common/bluedroid_user_config.h @@ -53,7 +53,14 @@ #define UC_BT_SPP_ENABLED FALSE #endif -//HFP +//HFP(AG) +#ifdef CONFIG_BT_HFP_AG_ENABLE +#define UC_BT_HFP_AG_ENABLED CONFIG_BT_HFP_AG_ENABLE +#else +#define UC_BT_HFP_AG_ENABLED FALSE +#endif + +//HFP(Client) #ifdef CONFIG_BT_HFP_CLIENT_ENABLE #define UC_BT_HFP_CLIENT_ENABLED CONFIG_BT_HFP_CLIENT_ENABLE #else diff --git a/components/bt/host/bluedroid/common/include/common/bt_target.h b/components/bt/host/bluedroid/common/include/common/bt_target.h index 973b6a334..e0be1af4a 100644 --- a/components/bt/host/bluedroid/common/include/common/bt_target.h +++ b/components/bt/host/bluedroid/common/include/common/bt_target.h @@ -80,6 +80,27 @@ #define BTC_SPP_INCLUDED TRUE #endif /* UC_BT_SPP_ENABLED */ +#if (UC_BT_HFP_AG_ENABLED == TRUE) +#define BTC_HF_INCLUDED TRUE +#define BTA_AG_INCLUDED TRUE +#define PLC_INCLUDED TRUE +#ifndef RFCOMM_INCLUDED +#define RFCOMM_INCLUDED TRUE +#endif +#ifndef BTM_SCO_INCLUDED +#define BTM_SCO_INCLUDED TRUE +#endif +#ifndef BTM_MAX_SCO_LINKS +#define BTM_MAX_SCO_LINKS (1) +#endif +#ifndef SBC_DEC_INCLUDED +#define SBC_DEC_INCLUDED TRUE +#endif +#ifndef SBC_ENC_INCLUDED +#define SBC_ENC_INCLUDED TRUE +#endif +#endif /* UC_BT_HFP_AG_ENABLED */ + #if (UC_BT_HFP_CLIENT_ENABLED == TRUE) #define BTC_HF_CLIENT_INCLUDED TRUE #define BTA_HF_INCLUDED TRUE diff --git a/components/bt/host/bluedroid/stack/btm/btm_sco.c b/components/bt/host/bluedroid/stack/btm/btm_sco.c index 25d8c4183..a291a63ec 100644 --- a/components/bt/host/bluedroid/stack/btm/btm_sco.c +++ b/components/bt/host/bluedroid/stack/btm/btm_sco.c @@ -215,7 +215,7 @@ static void btm_esco_conn_rsp (UINT16 sco_inx, UINT8 hci_status, BD_ADDR bda, #if BTM_SCO_HCI_INCLUDED == TRUE void btm_sco_process_num_bufs (UINT16 num_lm_sco_bufs) { - BTM_TRACE_ERROR("%s, %d", __FUNCTION__, num_lm_sco_bufs); + BTM_TRACE_DEBUG("%s, %d", __FUNCTION__, num_lm_sco_bufs); btm_cb.sco_cb.num_lm_sco_bufs = btm_cb.sco_cb.xmit_window_size = num_lm_sco_bufs; } diff --git a/components/bt/host/bluedroid/stack/btu/btu_task.c b/components/bt/host/bluedroid/stack/btu/btu_task.c index 3e73cbb85..56fe6c168 100644 --- a/components/bt/host/bluedroid/stack/btu/btu_task.c +++ b/components/bt/host/bluedroid/stack/btu/btu_task.c @@ -152,6 +152,7 @@ static void btu_hci_msg_process(void *param) case BT_EVT_TO_BTU_HCI_SCO: #if BTM_SCO_INCLUDED == TRUE btm_route_sco_data (p_msg); + osi_free(p_msg); break; #endif