| /****************************************************************************** |
| * |
| * 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_api.h" |
| #include "bta_sys.h" |
| #include "bta_ag_co.h" |
| #include "bta_ag_int.h" |
| #include "bd.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 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 |
| *****************************************************************************/ |
| |
| /* AG control block */ |
| #if BTA_DYNAMIC_MEMORY == FALSE |
| tBTA_AG_CB bta_ag_cb; |
| #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 *) GKI_getbuf(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; |
| |
| /* set up timers */ |
| p_scb->act_timer.param = (UINT32) p_scb; |
| p_scb->act_timer.p_cback = bta_ag_timer_cback; |
| |
| APPL_TRACE_DEBUG1("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_WARNING0("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_DEBUG1("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 |
| |
| /* 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_WARNING1("ag scb idx %d not allocated", idx); |
| } |
| } |
| else |
| { |
| p_scb = NULL; |
| APPL_TRACE_DEBUG1("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_WARNING0("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]; |
| 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_DEBUG0("No other ag scb open"); |
| 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_DEBUG0("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_DEBUG0 ("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; |
| |
| /* 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_WARNING0 ("AG found collision (ACL) ..."); |
| } |
| else if (id == BTA_ID_AG) /* RFCOMM collision */ |
| { |
| APPL_TRACE_WARNING0 ("AG found collision (RFCOMM) ..."); |
| } |
| else |
| { |
| APPL_TRACE_WARNING0 ("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_DEBUG1 ("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_ERROR0 ("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; |
| |
| /* 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]; |
| BOOLEAN do_dereg = FALSE; |
| int i; |
| |
| if (!bta_sys_is_register (BTA_ID_AG)) |
| { |
| APPL_TRACE_ERROR0("BTA AG is already disabled, ignoring ..."); |
| return; |
| } |
| |
| /* De-register with BTA system manager */ |
| GKI_sched_lock(); |
| bta_sys_deregister(BTA_ID_AG); |
| GKI_sched_unlock(); |
| |
| 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) |
| { |
| 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) |
| { |
| 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) |
| { |
| 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; |
| int i; |
| |
| #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_EVENT5("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_EVENT3("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_ERROR0("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 (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_EVENT3("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; |
| |
| 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) |
| { |
| 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 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 |