You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
932 lines
32 KiB
932 lines
32 KiB
/****************************************************************************** |
|
* |
|
* 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 <string.h> |
|
|
|
#include "bta_ag_co.h" |
|
#include "bta_ag_int.h" |
|
#include "bta_api.h" |
|
#include "bta_sys.h" |
|
#include "osi/include/osi.h" |
|
#include "utl.h" |
|
|
|
/***************************************************************************** |
|
* Constants and types |
|
****************************************************************************/ |
|
#ifndef BTA_AG_DEBUG |
|
#define BTA_AG_DEBUG FALSE |
|
#endif |
|
|
|
#if (BTA_AG_DEBUG == TRUE) |
|
static char* bta_ag_evt_str(uint16_t event, tBTA_AG_RES result); |
|
static char* bta_ag_state_str(uint8_t 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_t 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_t 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_t 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_t 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_t (*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 |
|
****************************************************************************/ |
|
|
|
/* AG control block */ |
|
tBTA_AG_CB bta_ag_cb; |
|
|
|
/******************************************************************************* |
|
* |
|
* 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; |
|
p_scb->codec_updated = false; |
|
p_scb->codec_fallback = false; |
|
p_scb->peer_codecs = BTA_AG_CODEC_CVSD; |
|
p_scb->sco_codec = BTA_AG_CODEC_CVSD; |
|
/* set up timers */ |
|
p_scb->ring_timer = alarm_new("bta_ag.scb_ring_timer"); |
|
p_scb->collision_timer = alarm_new("bta_ag.scb_collision_timer"); |
|
p_scb->codec_negotiation_timer = |
|
alarm_new("bta_ag.scb_codec_negotiation_timer"); |
|
/* set eSCO mSBC setting to T2 as the preferred */ |
|
p_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T2; |
|
APPL_TRACE_DEBUG("bta_ag_scb_alloc %d", bta_ag_scb_to_idx(p_scb)); |
|
break; |
|
} |
|
} |
|
|
|
if (i == BTA_AG_NUM_SCB) { |
|
/* out of scbs */ |
|
p_scb = NULL; |
|
APPL_TRACE_WARNING("%s: Out of scbs", __func__); |
|
} |
|
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_t idx; |
|
bool allocated = false; |
|
|
|
APPL_TRACE_DEBUG("bta_ag_scb_dealloc %d", bta_ag_scb_to_idx(p_scb)); |
|
|
|
/* stop and free timers */ |
|
alarm_free(p_scb->ring_timer); |
|
alarm_free(p_scb->codec_negotiation_timer); |
|
alarm_free(p_scb->collision_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_t bta_ag_scb_to_idx(tBTA_AG_SCB* p_scb) { |
|
/* use array arithmetic to determine index */ |
|
return ((uint16_t)(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_t 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_t 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_t bta_ag_idx_by_bdaddr(const RawAddress* peer_addr) { |
|
tBTA_AG_SCB* p_scb = &bta_ag_cb.scb[0]; |
|
uint16_t i; |
|
|
|
if (peer_addr != NULL) { |
|
for (i = 0; i < BTA_AG_NUM_SCB; i++, p_scb++) { |
|
if (p_scb->in_use && *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. |
|
* |
|
******************************************************************************/ |
|
bool bta_ag_other_scb_open(tBTA_AG_SCB* p_curr_scb) { |
|
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 && 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. |
|
* |
|
******************************************************************************/ |
|
bool 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_t 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_collision_timer_cback |
|
* |
|
* Description AG connection collision timer callback |
|
* |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
static void bta_ag_collision_timer_cback(void* data) { |
|
tBTA_AG_SCB* p_scb = (tBTA_AG_SCB*)data; |
|
|
|
APPL_TRACE_DEBUG("%s", __func__); |
|
|
|
/* 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(UNUSED_ATTR tBTA_SYS_CONN_STATUS status, uint8_t id, |
|
UNUSED_ATTR uint8_t app_id, |
|
const RawAddress* peer_addr) { |
|
uint16_t handle; |
|
tBTA_AG_SCB* p_scb; |
|
|
|
/* 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 */ |
|
alarm_set_on_mloop(p_scb->collision_timer, BTA_AG_COLLISION_TIMEOUT_MS, |
|
bta_ag_collision_timer_cback, p_scb); |
|
} |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* 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 */ |
|
for (size_t i = 0; i < BTA_AG_NUM_SCB; i++) { |
|
alarm_free(bta_ag_cb.scb[i].ring_timer); |
|
alarm_free(bta_ag_cb.scb[i].codec_negotiation_timer); |
|
alarm_free(bta_ag_cb.scb[i].collision_timer); |
|
} |
|
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; |
|
|
|
/* call init call-out */ |
|
bta_ag_co_init(); |
|
|
|
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]; |
|
bool 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 */ |
|
p_scb = bta_ag_scb_alloc(); |
|
if (p_scb != NULL) { |
|
APPL_TRACE_DEBUG("bta_ag_api_register: p_scb 0x%08x ", 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) { |
|
p_scb = bta_ag_scb_by_idx(p_data->hdr.layer_specific); |
|
if (p_scb != NULL) { |
|
APPL_TRACE_DEBUG("bta_ag_api_result: p_scb 0x%08x ", 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 ", 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_t event, |
|
tBTA_AG_DATA* p_data) { |
|
tBTA_AG_ST_TBL state_table; |
|
uint8_t action; |
|
int i; |
|
#if (BTA_AG_DEBUG == TRUE) |
|
uint16_t previous_event = event; |
|
uint8_t previous_state = p_scb->state; |
|
|
|
/* Ignore displaying of AT results when not connected (Ignored in state |
|
* machine) */ |
|
if ((previous_event != BTA_AG_API_RESULT_EVT || |
|
p_scb->state == BTA_AG_OPEN_ST) && |
|
event != BTA_AG_CI_SCO_DATA_EVT) { |
|
APPL_TRACE_EVENT("%s: Handle 0x%04x, State %d (%s), Event 0x%04x (%s)", |
|
__func__, 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 |
|
if (event != BTA_AG_CI_SCO_DATA_EVT) { |
|
APPL_TRACE_EVENT("%s: Handle 0x%04x, State %d, Event 0x%04x", __func__, |
|
bta_ag_scb_to_idx(p_scb), p_scb->state, event); |
|
} |
|
#endif |
|
|
|
event &= 0x00FF; |
|
if (event >= (BTA_AG_MAX_EVT & 0x00FF)) { |
|
APPL_TRACE_ERROR("%s: event out of range, ignored", __func__); |
|
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 (i = 0; i < BTA_AG_ACTIONS; i++) { |
|
action = state_table[event][i]; |
|
if (action != BTA_AG_IGNORE) { |
|
(*bta_ag_action[action])(p_scb, p_data); |
|
} else { |
|
break; |
|
} |
|
} |
|
#if (BTA_AG_DEBUG == TRUE) |
|
if (p_scb->state != previous_state) { |
|
APPL_TRACE_EVENT("%s: State Change: [%s] -> [%s] after Event [%s]", |
|
__func__, bta_ag_state_str(previous_state), |
|
bta_ag_state_str(p_scb->state), |
|
bta_ag_evt_str(previous_event, p_data->api_result.result)); |
|
} |
|
#endif |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_hdl_event |
|
* |
|
* Description Data gateway main event handling function. |
|
* |
|
* |
|
* Returns bool |
|
* |
|
******************************************************************************/ |
|
bool 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) { |
|
case BTA_AG_API_ENABLE_EVT: |
|
bta_ag_api_enable((tBTA_AG_DATA*)p_msg); |
|
break; |
|
|
|
case BTA_AG_API_DISABLE_EVT: |
|
bta_ag_api_disable((tBTA_AG_DATA*)p_msg); |
|
break; |
|
|
|
case BTA_AG_API_REGISTER_EVT: |
|
bta_ag_api_register((tBTA_AG_DATA*)p_msg); |
|
break; |
|
|
|
case BTA_AG_API_RESULT_EVT: |
|
bta_ag_api_result((tBTA_AG_DATA*)p_msg); |
|
break; |
|
|
|
case BTA_AG_API_SET_SCO_ALLOWED_EVT: |
|
bta_ag_set_sco_allowed((tBTA_AG_DATA*)p_msg); |
|
break; |
|
|
|
/* all others reference scb by handle */ |
|
default: |
|
p_scb = bta_ag_scb_by_idx(p_msg->layer_specific); |
|
if (p_scb != NULL) { |
|
APPL_TRACE_DEBUG("bta_ag_hdl_event: p_scb 0x%08x ", p_scb); |
|
bta_ag_sm_execute(p_scb, p_msg->event, (tBTA_AG_DATA*)p_msg); |
|
} |
|
break; |
|
} |
|
return true; |
|
} |
|
|
|
#if (BTA_AG_DEBUG == TRUE) |
|
static char* bta_ag_evt_str(uint16_t 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_TIMEOUT_EVT: |
|
return "Ring Timeout"; |
|
case BTA_AG_SVC_TIMEOUT_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_t 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
|
|
|