/******************************************************************************* ** ** Function avdt_ad_open_req ** ** Description This function is called by a CCB or SCB to open a transport ** channel. This function allocates and initializes a ** transport channel table entry. The channel can be opened ** in two roles: as an initiator or acceptor. When opened ** as an initiator the function will start an L2CAP connection. ** When opened as an acceptor the function simply configures ** the table entry to listen for an incoming channel. ** ** ** Returns Nothing. ** *******************************************************************************/ void avdt_ad_open_req(UINT8 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb, UINT8 role) { tAVDT_TC_TBL *p_tbl; UINT16 lcid; if ((p_tbl = avdt_ad_tc_tbl_alloc(p_ccb)) == NULL) { AVDT_TRACE_ERROR("avdt_ad_open_req: Cannot allocate p_tbl"); return; } p_tbl->tcid = avdt_ad_type_to_tcid(type, p_scb); AVDT_TRACE_DEBUG("avdt_ad_open_req: type: %d, role: %d, tcid:%d\n", type, role, p_tbl->tcid); if (type == AVDT_CHAN_SIG) { /* if signaling, get mtu from registration control block */ p_tbl->my_mtu = avdt_cb.rcb.ctrl_mtu; p_tbl->my_flush_to = L2CAP_DEFAULT_FLUSH_TO; } else { /* otherwise get mtu from scb */ p_tbl->my_mtu = p_scb->cs.mtu; p_tbl->my_flush_to = p_scb->cs.flush_to; /* also set scb_hdl in rt_tbl */ avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][p_tbl->tcid].scb_hdl = avdt_scb_to_hdl(p_scb); AVDT_TRACE_DEBUG("avdt_cb.ad.rt_tbl[%d][%d].scb_hdl = %d\n", avdt_ccb_to_idx(p_ccb), p_tbl->tcid, avdt_scb_to_hdl(p_scb)); } /* if we're acceptor, we're done; just sit back and listen */ if (role == AVDT_ACP) { p_tbl->state = AVDT_AD_ST_ACP; } /* else we're inititator, start the L2CAP connection */ else { p_tbl->state = AVDT_AD_ST_CONN; /* call l2cap connect req */ if ((lcid = L2CA_ConnectReq(AVDT_PSM, p_ccb->peer_addr)) != 0) { /* if connect req ok, store tcid in lcid table */ avdt_cb.ad.lcid_tbl[lcid - L2CAP_BASE_APPL_CID] = avdt_ad_tc_tbl_to_idx(p_tbl); AVDT_TRACE_DEBUG("avdt_cb.ad.lcid_tbl[%d] = %d\n", (lcid - L2CAP_BASE_APPL_CID), avdt_ad_tc_tbl_to_idx(p_tbl)); avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][p_tbl->tcid].lcid = lcid; AVDT_TRACE_DEBUG("avdt_cb.ad.rt_tbl[%d][%d].lcid = 0x%x\n", avdt_ccb_to_idx(p_ccb), p_tbl->tcid, lcid); } else { /* if connect req failed, call avdt_ad_tc_close_ind() */ avdt_ad_tc_close_ind(p_tbl, 0); } } }
/******************************************************************************* ** ** Function avdt_ccb_cmd_fail ** ** Description This function is called when there is a response timeout. ** The currently pending command is freed and we fake a ** reject message back to ourselves. ** ** ** Returns void. ** *******************************************************************************/ void avdt_ccb_cmd_fail(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) { tAVDT_MSG msg; UINT8 evt; tAVDT_SCB *p_scb; if (p_ccb->p_curr_cmd != NULL) { /* set up data */ msg.hdr.err_code = p_data->err_code; msg.hdr.err_param = 0; msg.hdr.ccb_idx = avdt_ccb_to_idx(p_ccb); /* pretend that we received a rej message */ evt = avdt_msg_rej_2_evt[p_ccb->p_curr_cmd->event - 1]; if (evt & AVDT_CCB_MKR) { avdt_ccb_event(p_ccb, (UINT8) (evt & ~AVDT_CCB_MKR), (tAVDT_CCB_EVT *) &msg); } else { /* we get the scb out of the current cmd */ p_scb = avdt_scb_by_hdl(*((UINT8 *)(p_ccb->p_curr_cmd + 1))); if (p_scb != NULL) { avdt_scb_event(p_scb, evt, (tAVDT_SCB_EVT *) &msg); } } GKI_freebuf(p_ccb->p_curr_cmd); p_ccb->p_curr_cmd = NULL; } }
/******************************************************************************* ** ** Function AVDT_OpenReq ** ** Description This function initiates a connection to the AVDTP service ** on the peer device, if not already present, and connects ** to a stream endpoint on a peer device. When the connection ** is completed, an AVDT_OPEN_CFM_EVT is sent to the ** application via the control callback function for this handle. ** ** Returns AVDT_SUCCESS if successful, otherwise error. ** *******************************************************************************/ UINT16 AVDT_OpenReq(UINT8 handle, BD_ADDR bd_addr, UINT8 seid, tAVDT_CFG *p_cfg) { tAVDT_CCB *p_ccb = NULL; tAVDT_SCB *p_scb = NULL; UINT16 result = AVDT_SUCCESS; tAVDT_SCB_EVT evt; /* verify SEID */ if ((seid < AVDT_SEID_MIN) || (seid > AVDT_SEID_MAX)) { result = AVDT_BAD_PARAMS; } /* map handle to scb */ else if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) { result = AVDT_BAD_HANDLE; } /* find channel control block for this bd addr; if none, allocate one */ else if ((p_ccb = avdt_ccb_by_bd(bd_addr)) == NULL) { if ((p_ccb = avdt_ccb_alloc(bd_addr)) == NULL) { /* could not allocate channel control block */ result = AVDT_NO_RESOURCES; } } /* send event to scb */ if (result == AVDT_SUCCESS) { evt.msg.config_cmd.hdr.seid = seid; evt.msg.config_cmd.hdr.ccb_idx = avdt_ccb_to_idx(p_ccb); evt.msg.config_cmd.int_seid = handle; evt.msg.config_cmd.p_cfg = p_cfg; avdt_scb_event(p_scb, AVDT_SCB_API_SETCONFIG_REQ_EVT, &evt); } return result; }
/******************************************************************************* ** ** Function AVDT_GetSignalChannel ** ** Description Get the L2CAP CID used by the signal channel of the given handle. ** ** Returns CID if successful, otherwise 0. ** *******************************************************************************/ UINT16 AVDT_GetSignalChannel(UINT8 handle, BD_ADDR bd_addr) { tAVDT_SCB *p_scb; tAVDT_CCB *p_ccb; UINT8 tcid = 0; /* tcid is always 0 for signal channel */ UINT16 lcid = 0; /* map handle to scb */ if (((p_scb = avdt_scb_by_hdl(handle)) != NULL) && ((p_ccb = p_scb->p_ccb) != NULL)) { lcid = avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid; } else if ((p_ccb = avdt_ccb_by_bd(bd_addr)) != NULL) { lcid = avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid; } return (lcid); }
/******************************************************************************* ** ** Function avdt_ad_write_req ** ** Description This function is called by a CCB or SCB to send data to a ** transport channel. It looks up the LCID of the channel ** based on the type, CCB, and SCB (if present). Then it ** passes the data to L2CA_DataWrite(). ** ** ** Returns AVDT_AD_SUCCESS, if data accepted, else FALSE ** AVDT_AD_CONGESTED, if data accepted and the channel is congested ** AVDT_AD_FAILED, if error ** *******************************************************************************/ UINT8 avdt_ad_write_req(UINT8 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb, BT_HDR *p_buf) { UINT8 tcid; /* get tcid from type, scb */ tcid = avdt_ad_type_to_tcid(type, p_scb); return L2CA_DataWrite(avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid, p_buf); }
/******************************************************************************* ** ** Function AVDT_GetL2CapChannel ** ** Description Get the L2CAP CID used by the handle. ** ** Returns CID if successful, otherwise 0. ** *******************************************************************************/ UINT16 AVDT_GetL2CapChannel(UINT8 handle) { tAVDT_SCB *p_scb; tAVDT_CCB *p_ccb; UINT8 tcid; UINT16 lcid = 0; /* map handle to scb */ if (((p_scb = avdt_scb_by_hdl(handle)) != NULL) && ((p_ccb = p_scb->p_ccb) != NULL)) { /* get tcid from type, scb */ tcid = avdt_ad_type_to_tcid(AVDT_CHAN_MEDIA, p_scb); lcid = avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid; } return (lcid); }
/******************************************************************************* ** ** Function avdt_sec_check_complete_term ** ** Description The function called when Security Manager finishes ** verification of the service side connection ** ** Returns void ** *******************************************************************************/ static void avdt_sec_check_complete_term (BD_ADDR bd_addr, tBT_TRANSPORT transport, void *p_ref_data, UINT8 res) { tAVDT_CCB *p_ccb = NULL; tL2CAP_CFG_INFO cfg; tAVDT_TC_TBL *p_tbl; UNUSED(p_ref_data); AVDT_TRACE_DEBUG("avdt_sec_check_complete_term res: %d\n", res); if (!bd_addr) { AVDT_TRACE_WARNING("avdt_sec_check_complete_term: NULL BD_ADDR"); return; } p_ccb = avdt_ccb_by_bd(bd_addr); p_tbl = avdt_ad_tc_tbl_by_st(AVDT_CHAN_SIG, p_ccb, AVDT_AD_ST_SEC_ACP); if (p_tbl == NULL) { return; } if (res == BTM_SUCCESS) { /* Send response to the L2CAP layer. */ L2CA_ConnectRsp (bd_addr, p_tbl->id, p_tbl->lcid, L2CAP_CONN_OK, L2CAP_CONN_OK); /* store idx in LCID table, store LCID in routing table */ avdt_cb.ad.lcid_tbl[p_tbl->lcid - L2CAP_BASE_APPL_CID] = avdt_ad_tc_tbl_to_idx(p_tbl); avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][p_tbl->tcid].lcid = p_tbl->lcid; /* transition to configuration state */ p_tbl->state = AVDT_AD_ST_CFG; /* Send L2CAP config req */ memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); cfg.mtu_present = TRUE; cfg.mtu = p_tbl->my_mtu; cfg.flush_to_present = TRUE; cfg.flush_to = p_tbl->my_flush_to; L2CA_ConfigReq(p_tbl->lcid, &cfg); } else { L2CA_ConnectRsp (bd_addr, p_tbl->id, p_tbl->lcid, L2CAP_CONN_SECURITY_BLOCK, L2CAP_CONN_OK); avdt_ad_tc_close_ind(p_tbl, L2CAP_CONN_SECURITY_BLOCK); } }
/******************************************************************************* ** ** Function avdt_ad_tc_tbl_by_st ** ** Description Find adaption layer transport channel table entry matching ** the given state. ** ** ** Returns Pointer to matching entry. For control channel it returns ** the matching entry. For media or other it returns the ** first matching entry (there could be more than one). ** *******************************************************************************/ tAVDT_TC_TBL *avdt_ad_tc_tbl_by_st(UINT8 type, tAVDT_CCB *p_ccb, UINT8 state) { int i; tAVDT_TC_TBL *p_tbl = avdt_cb.ad.tc_tbl; UINT8 ccb_idx; if (p_ccb == NULL) { /* resending security req */ for (i = 0; i < AVDT_NUM_TC_TBL; i++, p_tbl++) { /* must be AVDT_CHAN_SIG - tcid always zero */ if ((p_tbl->tcid == 0) && (p_tbl->state == state)) { break; } } } else { ccb_idx = avdt_ccb_to_idx(p_ccb); for (i = 0; i < AVDT_NUM_TC_TBL; i++, p_tbl++) { if (type == AVDT_CHAN_SIG) { /* if control channel, tcid always zero */ if ((p_tbl->tcid == 0) && (p_tbl->ccb_idx == ccb_idx) && (p_tbl->state == state)) { break; } } else { /* if other channel, tcid is always > zero */ if ((p_tbl->tcid > 0) && (p_tbl->ccb_idx == ccb_idx) && (p_tbl->state == state)) { break; } } } } /* if nothing found return null */ if (i == AVDT_NUM_TC_TBL) { p_tbl = NULL; } return p_tbl; }
/******************************************************************************* ** ** Function avdt_ad_tc_tbl_by_type ** ** Description This function retrieves the transport channel table entry ** for a particular channel. ** ** ** Returns Pointer to transport channel table entry. ** *******************************************************************************/ tAVDT_TC_TBL *avdt_ad_tc_tbl_by_type(UINT8 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb) { UINT8 tcid; int i; tAVDT_TC_TBL *p_tbl = avdt_cb.ad.tc_tbl; UINT8 ccb_idx = avdt_ccb_to_idx(p_ccb); /* get tcid from type, scb */ tcid = avdt_ad_type_to_tcid(type, p_scb); for (i = 0; i < AVDT_NUM_TC_TBL; i++, p_tbl++) { if ((p_tbl->tcid == tcid) && (p_tbl->ccb_idx == ccb_idx)) { break; } } assert(i != AVDT_NUM_TC_TBL); return p_tbl; }
/******************************************************************************* ** ** Function avdt_ad_close_req ** ** Description This function is called by a CCB or SCB to close a ** transport channel. The function looks up the LCID for the ** channel and calls L2CA_DisconnectReq(). ** ** ** Returns Nothing. ** *******************************************************************************/ void avdt_ad_close_req(UINT8 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb) { UINT8 tcid; tAVDT_TC_TBL *p_tbl; p_tbl = avdt_ad_tc_tbl_by_type(type, p_ccb, p_scb); AVDT_TRACE_DEBUG("avdt_ad_close_req state: %d\n", p_tbl->state); switch (p_tbl->state) { case AVDT_AD_ST_UNUSED: /* probably for reporting */ break; case AVDT_AD_ST_ACP: /* if we're listening on this channel, send ourselves a close ind */ avdt_ad_tc_close_ind(p_tbl, 0); break; default: /* get tcid from type, scb */ tcid = avdt_ad_type_to_tcid(type, p_scb); /* call l2cap disconnect req */ L2CA_DisconnectReq(avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid); } }
/******************************************************************************* ** ** Function avdt_ad_tc_tbl_alloc ** ** Description Allocate an entry in the traffic channel table. ** ** ** Returns Pointer to entry. ** *******************************************************************************/ tAVDT_TC_TBL *avdt_ad_tc_tbl_alloc(tAVDT_CCB *p_ccb) { int i; tAVDT_TC_TBL *p_tbl = avdt_cb.ad.tc_tbl; /* find next free entry in tc table */ for (i = 0; i < AVDT_NUM_TC_TBL; i++, p_tbl++) { if (p_tbl->state == AVDT_AD_ST_UNUSED) { break; } } /* sanity check */ assert(i != AVDT_NUM_TC_TBL); /* initialize entry */ p_tbl->peer_mtu = L2CAP_DEFAULT_MTU; p_tbl->cfg_flags = 0; p_tbl->ccb_idx = avdt_ccb_to_idx(p_ccb); p_tbl->state = AVDT_AD_ST_IDLE; return p_tbl; }
/******************************************************************************* ** ** Function avdt_l2c_connect_ind_cback ** ** Description This is the L2CAP connect indication callback function. ** ** ** Returns void ** *******************************************************************************/ void avdt_l2c_connect_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id) { tAVDT_CCB *p_ccb; tAVDT_TC_TBL *p_tbl = NULL; UINT16 result; tL2CAP_CFG_INFO cfg; tBTM_STATUS rc; UNUSED(psm); /* do we already have a control channel for this peer? */ if ((p_ccb = avdt_ccb_by_bd(bd_addr)) == NULL) { /* no, allocate ccb */ if ((p_ccb = avdt_ccb_alloc(bd_addr)) == NULL) { /* no ccb available, reject L2CAP connection */ result = L2CAP_CONN_NO_RESOURCES; } else { /* allocate and set up entry; first channel is always signaling */ p_tbl = avdt_ad_tc_tbl_alloc(p_ccb); p_tbl->my_mtu = avdt_cb.rcb.ctrl_mtu; p_tbl->my_flush_to = L2CAP_DEFAULT_FLUSH_TO; p_tbl->tcid = AVDT_CHAN_SIG; p_tbl->lcid = lcid; p_tbl->id = id; p_tbl->state = AVDT_AD_ST_SEC_ACP; p_tbl->cfg_flags = AVDT_L2C_CFG_CONN_ACP; /* Check the security */ rc = btm_sec_mx_access_request (bd_addr, AVDT_PSM, FALSE, BTM_SEC_PROTO_AVDT, AVDT_CHAN_SIG, &avdt_sec_check_complete_term, NULL); if (rc == BTM_CMD_STARTED) { L2CA_ConnectRsp (p_ccb->peer_addr, p_tbl->id, lcid, L2CAP_CONN_PENDING, L2CAP_CONN_OK); } return; } } /* deal with simultaneous control channel connect case */ else if ((p_tbl = avdt_ad_tc_tbl_by_st(AVDT_CHAN_SIG, p_ccb, AVDT_AD_ST_CONN)) != NULL) { /* reject their connection */ result = L2CAP_CONN_NO_RESOURCES; } /* this must be a traffic channel; are we accepting a traffic channel ** for this ccb? */ else if ((p_tbl = avdt_ad_tc_tbl_by_st(AVDT_CHAN_MEDIA, p_ccb, AVDT_AD_ST_ACP)) != NULL) { /* yes; proceed with connection */ result = L2CAP_CONN_OK; } #if AVDT_REPORTING == TRUE /* this must be a reporting channel; are we accepting a reporting channel ** for this ccb? */ else if ((p_tbl = avdt_ad_tc_tbl_by_st(AVDT_CHAN_REPORT, p_ccb, AVDT_AD_ST_ACP)) != NULL) { /* yes; proceed with connection */ result = L2CAP_CONN_OK; } #endif /* else we're not listening for traffic channel; reject */ else { result = L2CAP_CONN_NO_PSM; } /* Send L2CAP connect rsp */ L2CA_ConnectRsp(bd_addr, id, lcid, result, 0); /* if result ok, proceed with connection */ if (result == L2CAP_CONN_OK) { /* store idx in LCID table, store LCID in routing table */ avdt_cb.ad.lcid_tbl[lcid - L2CAP_BASE_APPL_CID] = avdt_ad_tc_tbl_to_idx(p_tbl); avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][p_tbl->tcid].lcid = lcid; /* transition to configuration state */ p_tbl->state = AVDT_AD_ST_CFG; /* Send L2CAP config req */ memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); cfg.mtu_present = TRUE; cfg.mtu = p_tbl->my_mtu; cfg.flush_to_present = TRUE; cfg.flush_to = p_tbl->my_flush_to; L2CA_ConfigReq(lcid, &cfg); } }