/****************************************************************************** * * 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 extern fixed_queue_t* btu_bta_alarm_queue; #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(BD_ADDR 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 && !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. * ******************************************************************************/ 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, BD_ADDR 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_queue(p_scb->collision_timer, BTA_AG_COLLISION_TIMEOUT_MS, bta_ag_collision_timer_cback, p_scb, btu_bta_alarm_queue); } } /******************************************************************************* * * 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; /* 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