/******************************************************************************* ** ** 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_ccb_hdl_discover_cmd ** ** Description This function is called when a discover command is ** received from the peer. It gathers up the stream ** information for all allocated streams and initiates ** sending of a discover response. ** ** ** Returns void. ** *******************************************************************************/ void avdt_ccb_hdl_discover_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) { tAVDT_SEP_INFO sep_info[AVDT_NUM_SEPS]; tAVDT_SCB *p_scb = &avdt_cb.scb[0]; int i; p_data->msg.discover_rsp.p_sep_info = sep_info; p_data->msg.discover_rsp.num_seps = 0; /* for all allocated scbs */ for (i = 0; i < AVDT_NUM_SEPS; i++, p_scb++) { if (p_scb->allocated) { /* copy sep info */ sep_info[p_data->msg.discover_rsp.num_seps].in_use = p_scb->in_use; sep_info[p_data->msg.discover_rsp.num_seps].seid = i + 1; sep_info[p_data->msg.discover_rsp.num_seps].media_type = p_scb->cs.media_type; sep_info[p_data->msg.discover_rsp.num_seps].tsep = p_scb->cs.tsep; p_data->msg.discover_rsp.num_seps++; } } /* send response */ avdt_ccb_event(p_ccb, AVDT_CCB_API_DISCOVER_RSP_EVT, p_data); }
/******************************************************************************* ** ** Function AVDT_ConnectReq ** ** Description This function initiates an AVDTP signaling connection ** to the peer device. When the connection is completed, an ** AVDT_CONNECT_IND_EVT is sent to the application via its ** control callback function. If the connection attempt fails ** an AVDT_DISCONNECT_IND_EVT is sent. The security mask ** parameter overrides the outgoing security mask set in ** AVDT_Register(). ** ** Returns AVDT_SUCCESS if successful, otherwise error. ** *******************************************************************************/ UINT16 AVDT_ConnectReq(BD_ADDR bd_addr, UINT8 sec_mask, tAVDT_CTRL_CBACK *p_cback) { tAVDT_CCB *p_ccb = NULL; UINT16 result = AVDT_SUCCESS; tAVDT_CCB_EVT evt; /* find channel control block for this bd addr; if none, allocate one */ 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; } } else if (p_ccb->ll_opened == FALSE) { AVDT_TRACE_WARNING("AVDT_ConnectReq: CCB LL is in the middle of opening"); /* ccb was already allocated for the incoming signalling. */ result = AVDT_BUSY; } if (result == AVDT_SUCCESS) { /* send event to ccb */ evt.connect.p_cback = p_cback; evt.connect.sec_mask = sec_mask; avdt_ccb_event(p_ccb, AVDT_CCB_API_CONNECT_REQ_EVT, &evt); } return result; }
/******************************************************************************* ** ** Function avdt_ad_tc_close_ind ** ** Description This function is called by the L2CAP interface when the ** L2CAP channel is closed. It looks up the CCB or SCB for ** the channel and sends it a close event. The reason ** parameter is the same value passed by the L2CAP ** callback function. ** ** ** Returns Nothing. ** *******************************************************************************/ void avdt_ad_tc_close_ind(tAVDT_TC_TBL *p_tbl, UINT16 reason) { tAVDT_CCB *p_ccb; tAVDT_SCB *p_scb; tAVDT_SCB_TC_CLOSE close; // UNUSED(reason); close.old_tc_state = p_tbl->state; /* clear avdt_ad_tc_tbl entry */ p_tbl->state = AVDT_AD_ST_UNUSED; p_tbl->cfg_flags = 0; p_tbl->peer_mtu = L2CAP_DEFAULT_MTU; AVDT_TRACE_DEBUG("avdt_ad_tc_close_ind tcid: %d, old: %d\n", p_tbl->tcid, close.old_tc_state); /* if signaling channel, notify ccb that channel open */ if (p_tbl->tcid == 0) { p_ccb = avdt_ccb_by_idx(p_tbl->ccb_idx); p_ccb->disc_rsn = (reason == AVDT_DISC_RSN_ABNORMAL) ? AVDT_DISC_RSN_ABNORMAL : AVDT_DISC_RSN_NORMAL; avdt_ccb_event(p_ccb, AVDT_CCB_LL_CLOSE_EVT, NULL); } /* if media or other channel, notify scb that channel close */ else { /* look up scb in stream routing table by ccb, tcid */ p_scb = avdt_scb_by_hdl(avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].scb_hdl); if (p_scb != NULL) { close.tcid = p_tbl->tcid; close.type = avdt_ad_tcid_to_type(p_tbl->tcid); close.disc_rsn = (reason == AVDT_DISC_RSN_ABNORMAL) ? AVDT_DISC_RSN_ABNORMAL : AVDT_DISC_RSN_NORMAL; avdt_scb_event(p_scb, AVDT_SCB_TC_CLOSE_EVT, (tAVDT_SCB_EVT *)&close); } } }
/******************************************************************************* ** ** Function AVDT_SuspendReq ** ** Description Suspend one or more stream endpoints. This suspends the ** transfer of media packets for the streams. All stream ** endpoints must previously be open and started. When the ** streams are suspended, an AVDT_SUSPEND_CFM_EVT is sent to ** the application via the control callback function for ** each stream. ** ** ** Returns AVDT_SUCCESS if successful, otherwise error. ** *******************************************************************************/ UINT16 AVDT_SuspendReq(UINT8 *p_handles, UINT8 num_handles) { tAVDT_SCB *p_scb = NULL; tAVDT_CCB_EVT evt; UINT16 result = AVDT_SUCCESS; int i; if ((num_handles == 0) || (num_handles > AVDT_NUM_SEPS)) { result = AVDT_BAD_PARAMS; } else { /* verify handles */ for (i = 0; i < num_handles; i++) { if ((p_scb = avdt_scb_by_hdl(p_handles[i])) == NULL) { result = AVDT_BAD_HANDLE; break; } } } if (result == AVDT_SUCCESS) { if (p_scb->p_ccb == NULL) { result = AVDT_BAD_HANDLE; } else { /* send event to ccb */ memcpy(evt.msg.multi.seid_list, p_handles, num_handles); evt.msg.multi.num_seps = num_handles; avdt_ccb_event(p_scb->p_ccb, AVDT_CCB_API_SUSPEND_REQ_EVT, &evt); } } return result; }
/******************************************************************************* ** ** Function avdt_process_timeout ** ** Description This function is called by BTU when an AVDTP timer ** expires. The function sends a timer event to the ** appropriate CCB or SCB state machine. ** ** This function is for use internal to the stack only. ** ** ** Returns void ** *******************************************************************************/ void avdt_process_timeout(TIMER_LIST_ENT *p_tle) { UINT8 event = 0; UINT8 err_code = AVDT_ERR_TIMEOUT; switch (p_tle->event) { case BTU_TTYPE_AVDT_CCB_RET: event = AVDT_CCB_RET_TOUT_EVT + AVDT_CCB_MKR; break; case BTU_TTYPE_AVDT_CCB_RSP: event = AVDT_CCB_RSP_TOUT_EVT + AVDT_CCB_MKR; break; case BTU_TTYPE_AVDT_CCB_IDLE: event = AVDT_CCB_IDLE_TOUT_EVT + AVDT_CCB_MKR; break; case BTU_TTYPE_AVDT_SCB_TC: event = AVDT_SCB_TC_TOUT_EVT; break; default: break; } if (event & AVDT_CCB_MKR) { avdt_ccb_event((tAVDT_CCB *) p_tle->param, (UINT8) (event & ~AVDT_CCB_MKR), (tAVDT_CCB_EVT *) &err_code); } else { avdt_scb_event((tAVDT_SCB *) p_tle->param, event, NULL); } }
/******************************************************************************* ** ** Function avdt_get_cap_req ** ** Description internal function to serve both AVDT_GetCapReq and ** AVDT_GetAllCapReq ** ** Returns AVDT_SUCCESS if successful, otherwise error. ** *******************************************************************************/ static UINT16 avdt_get_cap_req(BD_ADDR bd_addr, tAVDT_CCB_API_GETCAP *p_evt) { tAVDT_CCB *p_ccb = NULL; UINT16 result = AVDT_SUCCESS; /* verify SEID */ if ((p_evt->single.seid < AVDT_SEID_MIN) || (p_evt->single.seid > AVDT_SEID_MAX)) { AVDT_TRACE_ERROR("seid: %d\n", p_evt->single.seid); result = AVDT_BAD_PARAMS; } /* 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; } } if (result == AVDT_SUCCESS) { /* make sure no discovery or get capabilities req already in progress */ if (p_ccb->proc_busy) { result = AVDT_BUSY; } /* send event to ccb */ else { avdt_ccb_event(p_ccb, AVDT_CCB_API_GETCAP_REQ_EVT, (tAVDT_CCB_EVT *)p_evt); } } return result; }
/******************************************************************************* ** ** Function AVDT_DiscoverReq ** ** Description This function initiates a connection to the AVDTP service ** on the peer device, if not already present, and discovers ** the stream endpoints on the peer device. (Please note ** that AVDTP discovery is unrelated to SDP discovery). ** This function can be called at any time regardless of whether ** there is an AVDTP connection to the peer device. ** ** When discovery is complete, an AVDT_DISCOVER_CFM_EVT ** is sent to the application via its callback function. ** The application must not call AVDT_GetCapReq() or ** AVDT_DiscoverReq() again to the same device until ** discovery is complete. ** ** The memory addressed by sep_info is allocated by the ** application. This memory is written to by AVDTP as part ** of the discovery procedure. This memory must remain ** accessible until the application receives the ** AVDT_DISCOVER_CFM_EVT. ** ** Returns AVDT_SUCCESS if successful, otherwise error. ** *******************************************************************************/ UINT16 AVDT_DiscoverReq(BD_ADDR bd_addr, tAVDT_SEP_INFO *p_sep_info, UINT8 max_seps, tAVDT_CTRL_CBACK *p_cback) { tAVDT_CCB *p_ccb; UINT16 result = AVDT_SUCCESS; tAVDT_CCB_EVT evt; /* find channel control block for this bd addr; if none, allocate one */ 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; } } if (result == AVDT_SUCCESS) { /* make sure no discovery or get capabilities req already in progress */ if (p_ccb->proc_busy) { result = AVDT_BUSY; } /* send event to ccb */ else { evt.discover.p_sep_info = p_sep_info; evt.discover.num_seps = max_seps; evt.discover.p_cback = p_cback; avdt_ccb_event(p_ccb, AVDT_CCB_API_DISCOVER_REQ_EVT, &evt); } } return result; }
/******************************************************************************* ** ** Function avdt_ccb_hdl_getcap_cmd ** ** Description This function is called when a get capabilities command ** is received from the peer. It retrieves the stream ** configuration for the requested stream and initiates ** sending of a get capabilities response. ** ** ** Returns void. ** *******************************************************************************/ void avdt_ccb_hdl_getcap_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) { tAVDT_SCB *p_scb; /* look up scb for seid sent to us */ p_scb = avdt_scb_by_hdl(p_data->msg.single.seid); p_data->msg.svccap.p_cfg = &p_scb->cs.cfg; avdt_ccb_event(p_ccb, AVDT_CCB_API_GETCAP_RSP_EVT, p_data); }
/******************************************************************************* ** ** Function avdt_ccb_hdl_start_cmd ** ** Description This function is called when a start command is received ** from the peer. It verifies that all requested streams ** are in the proper state. If so, it initiates sending of ** a start response. Otherwise it sends a start reject. ** ** ** Returns void. ** *******************************************************************************/ void avdt_ccb_hdl_start_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) { UINT8 err_code = 0; /* verify all streams in the right state */ UINT8 seid = avdt_scb_verify(p_ccb, AVDT_VERIFY_START, p_data->msg.multi.seid_list, p_data->msg.multi.num_seps, &err_code); if (seid == 0 && err_code == 0) { /* we're ok, send response */ avdt_ccb_event(p_ccb, AVDT_CCB_API_START_RSP_EVT, p_data); } else { /* not ok, send reject */ p_data->msg.hdr.err_code = err_code; p_data->msg.hdr.err_param = seid; avdt_msg_send_rej(p_ccb, AVDT_SIG_START, &p_data->msg); } }
/******************************************************************************* ** ** Function AVDT_DisconnectReq ** ** Description This function disconnect an AVDTP signaling connection ** to the peer device. When disconnected an ** AVDT_DISCONNECT_IND_EVT is sent to the application via its ** control callback function. ** ** Returns AVDT_SUCCESS if successful, otherwise error. ** *******************************************************************************/ UINT16 AVDT_DisconnectReq(BD_ADDR bd_addr, tAVDT_CTRL_CBACK *p_cback) { tAVDT_CCB *p_ccb = NULL; UINT16 result = AVDT_SUCCESS; tAVDT_CCB_EVT evt; /* find channel control block for this bd addr; if none, error */ if ((p_ccb = avdt_ccb_by_bd(bd_addr)) == NULL) { result = AVDT_BAD_PARAMS; } if (result == AVDT_SUCCESS) { /* send event to ccb */ evt.disconnect.p_cback = p_cback; avdt_ccb_event(p_ccb, AVDT_CCB_API_DISCONNECT_REQ_EVT, &evt); } return result; }
/******************************************************************************* ** ** Function avdt_ad_tc_cong_ind ** ** Description This function is called by the L2CAP interface layer when ** L2CAP calls the congestion callback. It looks up the CCB ** or SCB for the channel and sends it a congestion event. ** The is_congested parameter is the same value passed by ** the L2CAP callback function. ** ** ** Returns Nothing. ** *******************************************************************************/ void avdt_ad_tc_cong_ind(tAVDT_TC_TBL *p_tbl, BOOLEAN is_congested) { tAVDT_CCB *p_ccb; tAVDT_SCB *p_scb; /* if signaling channel, notify ccb of congestion */ if (p_tbl->tcid == 0) { p_ccb = avdt_ccb_by_idx(p_tbl->ccb_idx); avdt_ccb_event(p_ccb, AVDT_CCB_LL_CONG_EVT, (tAVDT_CCB_EVT *) &is_congested); } /* if media or other channel, notify scb that channel open */ else { /* look up scb in stream routing table by ccb, tcid */ p_scb = avdt_scb_by_hdl(avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].scb_hdl); if (p_scb != NULL) { avdt_scb_event(p_scb, AVDT_SCB_TC_CONG_EVT, (tAVDT_SCB_EVT *) &is_congested); } } }
/******************************************************************************* ** ** Function avdt_ccb_chk_reconn ** ** Description This function is called to check if a reconnect attempt ** is enabled. If enabled, it sends an AVDT_CCB_UL_OPEN_EVT ** to the CCB. If disabled, the CCB is deallocated. ** ** ** Returns void. ** *******************************************************************************/ void avdt_ccb_chk_reconn(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) { UINT8 err_code = AVDT_ERR_CONNECT; UNUSED(p_data); if (p_ccb->reconn) { p_ccb->reconn = FALSE; /* clear out ccb */ avdt_ccb_clear_ccb(p_ccb); /* clear out current command, if any */ avdt_ccb_cmd_fail(p_ccb, (tAVDT_CCB_EVT *) &err_code); /* reopen the signaling channel */ avdt_ccb_event(p_ccb, AVDT_CCB_UL_OPEN_EVT, NULL); } else { avdt_ccb_ll_closed(p_ccb, NULL); } }
/******************************************************************************* ** ** Function avdt_ad_tc_open_ind ** ** Description This function is called by the L2CAP interface when ** the L2CAP channel is opened. It looks up the CCB or SCB ** for the channel and sends it an open event. ** ** ** Returns Nothing. ** *******************************************************************************/ void avdt_ad_tc_open_ind(tAVDT_TC_TBL *p_tbl) { tAVDT_CCB *p_ccb; tAVDT_SCB *p_scb; tAVDT_OPEN open; tAVDT_EVT_HDR evt; p_tbl->state = AVDT_AD_ST_OPEN; /* if signaling channel, notify ccb that channel open */ if (p_tbl->tcid == 0) { /* set the signal channel to use high priority within the ACL link */ L2CA_SetTxPriority(avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][AVDT_CHAN_SIG].lcid, L2CAP_CHNL_PRIORITY_HIGH); p_ccb = avdt_ccb_by_idx(p_tbl->ccb_idx); /* use err_param to indicate the role of connection. * AVDT_ACP, if ACP */ evt.err_param = AVDT_INT; if (p_tbl->cfg_flags & AVDT_L2C_CFG_CONN_ACP) { evt.err_param = AVDT_ACP; } avdt_ccb_event(p_ccb, AVDT_CCB_LL_OPEN_EVT, (tAVDT_CCB_EVT *)&evt); } /* if media or other channel, notify scb that channel open */ else { /* look up scb in stream routing table by ccb, tcid */ p_scb = avdt_scb_by_hdl(avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].scb_hdl); /* put lcid in event data */ if (p_scb != NULL) { open.peer_mtu = p_tbl->peer_mtu; open.lcid = avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].lcid; open.hdr.err_code = avdt_ad_tcid_to_type(p_tbl->tcid); avdt_scb_event(p_scb, AVDT_SCB_TC_OPEN_EVT, (tAVDT_SCB_EVT *) &open); } } }