/******************************************************************************* ** ** Function AVDT_WriteReqOpt ** ** Description Send a media packet to the peer device. The stream must ** be started before this function is called. Also, this ** function can only be called if the stream is a SRC. ** ** When AVDTP has sent the media packet and is ready for the ** next packet, an AVDT_WRITE_CFM_EVT is sent to the ** application via the control callback. The application must ** wait for the AVDT_WRITE_CFM_EVT before it makes the next ** call to AVDT_WriteReq(). If the applications calls ** AVDT_WriteReq() before it receives the event the packet ** will not be sent. The application may make its first call ** to AVDT_WriteReq() after it receives an AVDT_START_CFM_EVT ** or AVDT_START_IND_EVT. ** ** The application passes the packet using the BT_HDR structure. ** This structure is described in section 2.1. The offset ** field must be equal to or greater than AVDT_MEDIA_OFFSET ** (if NO_RTP is specified, L2CAP_MIN_OFFSET can be used). ** This allows enough space in the buffer for the L2CAP and ** AVDTP headers. ** ** The memory pointed to by p_pkt must be a GKI buffer ** allocated by the application. This buffer will be freed ** by the protocol stack; the application must not free ** this buffer. ** ** The opt parameter allows passing specific options like: ** - NO_RTP : do not add the RTP header to buffer ** ** Returns AVDT_SUCCESS if successful, otherwise error. ** *******************************************************************************/ UINT16 AVDT_WriteReqOpt(UINT8 handle, BT_HDR *p_pkt, UINT32 time_stamp, UINT8 m_pt, tAVDT_DATA_OPT_MASK opt) { tAVDT_SCB *p_scb; tAVDT_SCB_EVT evt; UINT16 result = AVDT_SUCCESS; BTTRC_AVDT_API0(AVDT_TRACE_API_WRITE_REQ); /* map handle to scb */ if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) { result = AVDT_BAD_HANDLE; } else { evt.apiwrite.p_buf = p_pkt; evt.apiwrite.time_stamp = time_stamp; evt.apiwrite.m_pt = m_pt; evt.apiwrite.opt = opt; #if AVDT_MULTIPLEXING == TRUE GKI_init_q (&evt.apiwrite.frag_q); #endif avdt_scb_event(p_scb, AVDT_SCB_API_WRITE_REQ_EVT, &evt); } return result; }
/******************************************************************************* ** ** 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_SetMediaBuf ** ** Description Assigns buffer for media packets or forbids using of assigned ** buffer if argument p_buf is NULL. This function can only ** be called if the stream is a SNK. ** ** AVDTP uses this buffer to reassemble fragmented media packets. ** When AVDTP receives a complete media packet, it calls the ** p_media_cback assigned by AVDT_CreateStream(). ** This function can be called during callback to assign a ** different buffer for next media packet or can leave the current ** buffer for next packet. ** ** Returns AVDT_SUCCESS if successful, otherwise error. ** *******************************************************************************/ AVDT_API extern UINT16 AVDT_SetMediaBuf(UINT8 handle, UINT8 *p_buf, UINT32 buf_len) { tAVDT_SCB *p_scb; UINT16 result = AVDT_SUCCESS; BTTRC_AVDT_API0(AVDT_TRACE_API_SET_MEDIABUF); /* map handle to scb */ if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) { result = AVDT_BAD_HANDLE; } else { if(p_buf && p_scb->cs.p_media_cback == NULL) result = AVDT_NO_RESOURCES; else { p_scb->p_media_buf = p_buf; p_scb->media_buf_len = buf_len; } } return result; }
/******************************************************************************* ** ** Function AVDT_SecurityRsp ** ** Description Respond to a security request from the peer device. ** This function must be called if the application receives ** an AVDT_SECURITY_IND_EVT through its control callback. ** (Please note that AVDTP security procedures are unrelated ** to Bluetooth link level security.) ** ** ** Returns AVDT_SUCCESS if successful, otherwise error. ** *******************************************************************************/ UINT16 AVDT_SecurityRsp(UINT8 handle, UINT8 label, UINT8 error_code, UINT8 *p_data, UINT16 len) { tAVDT_SCB *p_scb; UINT16 result = AVDT_SUCCESS; tAVDT_SCB_EVT evt; BTTRC_AVDT_API0(AVDT_TRACE_API_SECURITY_RSP); /* map handle to scb */ if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) { result = AVDT_BAD_HANDLE; } /* send event to scb */ else { evt.msg.security_rsp.hdr.err_code = error_code; evt.msg.security_rsp.hdr.label = label; evt.msg.security_rsp.p_data = p_data; evt.msg.security_rsp.len = len; avdt_scb_event(p_scb, AVDT_SCB_API_SECURITY_RSP_EVT, &evt); } return result; }
/******************************************************************************* ** ** Function avdt_ad_tc_data_ind ** ** Description This function is called by the L2CAP interface layer when ** incoming data is received from L2CAP. It looks up the CCB ** or SCB for the channel and routes the data accordingly. ** ** ** Returns Nothing. ** *******************************************************************************/ void avdt_ad_tc_data_ind(tAVDT_TC_TBL *p_tbl, BT_HDR *p_buf) { tAVDT_CCB *p_ccb; tAVDT_SCB *p_scb; /* store type (media, recovery, reporting) */ p_buf->layer_specific = avdt_ad_tcid_to_type(p_tbl->tcid); /* if signaling channel, handle control message */ if (p_tbl->tcid == 0) { p_ccb = avdt_ccb_by_idx(p_tbl->ccb_idx); avdt_msg_ind(p_ccb, p_buf); } /* if media or other channel, send event to scb */ else { 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_DATA_EVT, (tAVDT_SCB_EVT *) &p_buf); } else { GKI_freebuf(p_buf); AVDT_TRACE_ERROR(" avdt_ad_tc_data_ind buffer freed"); } } }
/******************************************************************************* ** ** 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_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_ConfigRsp ** ** Description Respond to a configure request from the peer device. This ** function must be called if the application receives an ** AVDT_CONFIG_IND_EVT through its control callback. ** ** ** Returns AVDT_SUCCESS if successful, otherwise error. ** *******************************************************************************/ UINT16 AVDT_ConfigRsp(UINT8 handle, UINT8 label, UINT8 error_code, UINT8 category) { tAVDT_SCB *p_scb; tAVDT_SCB_EVT evt; UINT16 result = AVDT_SUCCESS; UINT8 event_code; /* map handle to scb */ if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) { result = AVDT_BAD_HANDLE; } /* handle special case when this function is called but peer has not send ** a configuration cmd; ignore and return error result */ else if (!p_scb->in_use) { result = AVDT_BAD_HANDLE; } /* send event to scb */ else { evt.msg.hdr.err_code = error_code; evt.msg.hdr.err_param = category; evt.msg.hdr.label = label; if (error_code == 0) { event_code = AVDT_SCB_API_SETCONFIG_RSP_EVT; } else { event_code = AVDT_SCB_API_SETCONFIG_REJ_EVT; } avdt_scb_event(p_scb, event_code, &evt); } return result; }
/******************************************************************************* ** ** Function avdt_ccb_snd_suspend_cmd ** ** Description This function is called to send a suspend command to the ** peer. It verifies that all requested streams are in the ** proper state. If so, it sends a suspend command. ** Otherwise it calls the callback function for each requested ** stream and sends a suspend confirmation with failure. ** ** ** Returns void. ** *******************************************************************************/ void avdt_ccb_snd_suspend_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) { int i; tAVDT_SCB *p_scb; tAVDT_MSG avdt_msg; UINT8 seid_list[AVDT_NUM_SEPS]; /* make copy of our seid list */ memcpy(seid_list, p_data->msg.multi.seid_list, p_data->msg.multi.num_seps); /* verify all streams in the right state */ if ((avdt_msg.hdr.err_param = avdt_scb_verify(p_ccb, AVDT_VERIFY_STREAMING, p_data->msg.multi.seid_list, p_data->msg.multi.num_seps, &avdt_msg.hdr.err_code)) == 0) { /* set peer seid list in messsage */ avdt_scb_peer_seid_list(&p_data->msg.multi); /* send command */ avdt_msg_send_cmd(p_ccb, seid_list, AVDT_SIG_SUSPEND, &p_data->msg); } else { /* failed; send ourselves a reject for each stream */ for (i = 0; i < p_data->msg.multi.num_seps; i++) { if ((p_scb = avdt_scb_by_hdl(seid_list[i])) != NULL) { avdt_scb_event(p_scb, AVDT_SCB_MSG_SUSPEND_REJ_EVT, (tAVDT_SCB_EVT *) &avdt_msg.hdr); } } } }
/******************************************************************************* ** ** 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_WriteDataReq ** ** Description Send a media packet to the peer device. The stream must ** be started before this function is called. Also, this ** function can only be called if the stream is a SRC. ** ** When AVDTP has sent the media packet and is ready for the ** next packet, an AVDT_WRITE_CFM_EVT is sent to the ** application via the control callback. The application must ** wait for the AVDT_WRITE_CFM_EVT before it makes the next ** call to AVDT_WriteDataReq(). If the applications calls ** AVDT_WriteDataReq() before it receives the event the packet ** will not be sent. The application may make its first call ** to AVDT_WriteDataReq() after it receives an ** AVDT_START_CFM_EVT or AVDT_START_IND_EVT. ** ** Returns AVDT_SUCCESS if successful, otherwise error. ** *******************************************************************************/ extern UINT16 AVDT_WriteDataReq(UINT8 handle, UINT8 *p_data, UINT32 data_len, UINT32 time_stamp, UINT8 m_pt, UINT8 marker) { tAVDT_SCB *p_scb; tAVDT_SCB_EVT evt; UINT16 result = AVDT_SUCCESS; do { /* check length of media frame */ if (data_len > AVDT_MAX_MEDIA_SIZE) { result = AVDT_BAD_PARAMS; break; } /* map handle to scb */ if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) { result = AVDT_BAD_HANDLE; break; } AVDT_TRACE_WARNING("mux_tsid_media:%d\n", p_scb->curr_cfg.mux_tsid_media); if (p_scb->p_pkt != NULL || p_scb->p_ccb == NULL || !GKI_queue_is_empty(&p_scb->frag_q) || p_scb->frag_off != 0 || p_scb->curr_cfg.mux_tsid_media == 0) { result = AVDT_ERR_BAD_STATE; AVDT_TRACE_WARNING("p_scb->p_pkt=%p, p_scb->p_ccb=%p, IsQueueEmpty=%x, p_scb->frag_off=%x\n", p_scb->p_pkt, p_scb->p_ccb, GKI_queue_is_empty(&p_scb->frag_q), p_scb->frag_off); break; } evt.apiwrite.p_buf = 0; /* it will indicate using of fragments queue frag_q */ /* create queue of media fragments */ GKI_init_q (&evt.apiwrite.frag_q); /* compose fragments from media payload and put fragments into gueue */ avdt_scb_queue_frags(p_scb, &p_data, &data_len, &evt.apiwrite.frag_q); if (GKI_queue_is_empty(&evt.apiwrite.frag_q)) { AVDT_TRACE_WARNING("AVDT_WriteDataReq out of GKI buffers"); result = AVDT_ERR_RESOURCE; break; } evt.apiwrite.data_len = data_len; evt.apiwrite.p_data = p_data; /* process the fragments queue */ evt.apiwrite.time_stamp = time_stamp; evt.apiwrite.m_pt = m_pt | (marker << 7); avdt_scb_event(p_scb, AVDT_SCB_API_WRITE_REQ_EVT, &evt); } while (0); #if (BT_USE_TRACES == TRUE) if (result != AVDT_SUCCESS) { AVDT_TRACE_WARNING("*** AVDT_WriteDataReq failed result=%d\n", result); } #endif 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); }
void AVDT_AbortReq(UINT8 handle) { AVDT_TRACE_ERROR("%s\n", __func__); tAVDT_SCB *p_scb = avdt_scb_by_hdl(handle); if (p_scb != NULL) { avdt_scb_event(p_scb, AVDT_SCB_API_ABORT_REQ_EVT, NULL); } else { AVDT_TRACE_ERROR("%s Improper SCB, can not abort the stream\n", __func__); } }
/******************************************************************************* ** ** Function avdt_scb_peer_seid_list ** ** Description Given a list of SCB handles, return a list of peer SEIDs ** for the handles, copied in place into the struct passed in. ** ** ** Returns Nothing. ** *******************************************************************************/ void avdt_scb_peer_seid_list(tAVDT_MULTI *p_multi) { int i; tAVDT_SCB *p_scb; for (i = 0; i < p_multi->num_seps; i++) { if ((p_scb = avdt_scb_by_hdl(p_multi->seid_list[i])) != NULL) { p_multi->seid_list[i] = p_scb->peer_seid; } } }
/******************************************************************************* ** ** Function avdt_scb_verify ** ** Description Verify the condition of a list of scbs. ** ** ** Returns SEID that failed, or 0 if success. ** *******************************************************************************/ UINT8 avdt_scb_verify(tAVDT_CCB *p_ccb, UINT8 state, UINT8 *p_seid, UINT16 num_seid, UINT8 *p_err_code) { int i; tAVDT_SCB *p_scb; UINT8 nsc_mask; UINT8 chk_state; UINT8 ret = 0; AVDT_TRACE_DEBUG("avdt_scb_verify state %d", state); /* set nonsupported command mask */ /* translate public state into private state */ nsc_mask = 0; chk_state = AVDT_SCB_STREAM_ST; switch(state) { case AVDT_VERIFY_SUSPEND: nsc_mask = AVDT_NSC_SUSPEND; break; case AVDT_VERIFY_OPEN: case AVDT_VERIFY_START: chk_state = AVDT_SCB_OPEN_ST; break; } /* verify every scb */ for (i = 0; i < num_seid; i++) { if ((p_scb = avdt_scb_by_hdl(p_seid[i])) == NULL) { *p_err_code = AVDT_ERR_BAD_STATE; break; } else if ((p_scb->state != chk_state) || (p_scb->p_ccb != p_ccb)) { *p_err_code = AVDT_ERR_BAD_STATE; break; } else if (p_scb->cs.nsc_mask & nsc_mask) { *p_err_code = AVDT_ERR_NSC; break; } } if (i != num_seid) { ret = p_seid[i]; } AVDT_TRACE_DEBUG("avdt_scb_verify state %d, nsc_mask0x%x, ret: %d", chk_state, nsc_mask, ret); return ret; }
/******************************************************************************* ** ** Function AVDT_RemoveStream ** ** Description Remove a stream endpoint. This function is called when ** the application is no longer using a stream endpoint. ** If this function is called when the endpoint is connected ** the connection is closed and then the stream endpoint ** is removed. ** ** ** Returns AVDT_SUCCESS if successful, otherwise error. ** *******************************************************************************/ UINT16 AVDT_RemoveStream(UINT8 handle) { UINT16 result = AVDT_SUCCESS; tAVDT_SCB *p_scb; /* look up scb */ if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) { result = AVDT_BAD_HANDLE; } else { /* send remove event to scb */ avdt_scb_event(p_scb, AVDT_SCB_API_REMOVE_EVT, NULL); } return result; }
/******************************************************************************* ** ** Function avdt_ccb_snd_suspend_rsp ** ** Description This function is called to send a suspend response to the ** peer. It takes the stream information passed in the event ** and sends a suspend response. Then it sends a suspend event ** to the SCB for each stream. ** ** ** Returns void. ** *******************************************************************************/ void avdt_ccb_snd_suspend_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) { tAVDT_SCB *p_scb; int i; /* send response message */ avdt_msg_send_rsp(p_ccb, AVDT_SIG_SUSPEND, &p_data->msg); /* send start event to each scb */ for (i = 0; i < p_data->msg.multi.num_seps; i++) { if ((p_scb = avdt_scb_by_hdl(p_data->msg.multi.seid_list[i])) != NULL) { avdt_scb_event(p_scb, AVDT_SCB_MSG_SUSPEND_CMD_EVT, NULL); } } }
/******************************************************************************* ** ** Function avdt_scb_verify ** ** Description Verify the condition of a list of scbs. ** ** ** Returns SEID that failed, or 0 if success. ** *******************************************************************************/ UINT8 avdt_scb_verify(tAVDT_CCB *p_ccb, UINT8 state, UINT8 *p_seid, UINT16 num_seid, UINT8 *p_err_code) { int i; tAVDT_SCB *p_scb; UINT8 nsc_mask; UINT8 ret = 0; AVDT_TRACE_DEBUG("avdt_scb_verify state %d\n", state); /* set nonsupported command mask */ /* translate public state into private state */ nsc_mask = 0; if (state == AVDT_VERIFY_SUSPEND) { nsc_mask = AVDT_NSC_SUSPEND; } /* verify every scb */ for (i = 0, *p_err_code = 0; (i < num_seid) && (*p_err_code == 0) && (i < AVDT_NUM_SEPS); i++) { if ((p_scb = avdt_scb_by_hdl(p_seid[i])) == NULL) { *p_err_code = AVDT_ERR_BAD_STATE; } else if (p_scb->p_ccb != p_ccb) { *p_err_code = AVDT_ERR_BAD_STATE; } else if (p_scb->cs.nsc_mask & nsc_mask) { *p_err_code = AVDT_ERR_NSC; } switch (state) { case AVDT_VERIFY_OPEN: case AVDT_VERIFY_START: if (p_scb->state != AVDT_SCB_OPEN_ST && p_scb->state != AVDT_SCB_STREAM_ST) { *p_err_code = AVDT_ERR_BAD_STATE; } break; case AVDT_VERIFY_SUSPEND: case AVDT_VERIFY_STREAMING: if (p_scb->state != AVDT_SCB_STREAM_ST) { *p_err_code = AVDT_ERR_BAD_STATE; } break; } } if ((i != num_seid) && (i < AVDT_NUM_SEPS)) { ret = p_seid[i]; } return ret; }
/******************************************************************************* ** ** Function AVDT_CloseReq ** ** Description Close a stream endpoint. This stops the transfer of media ** packets and closes the transport channel associated with ** this stream endpoint. When the stream is closed, an ** AVDT_CLOSE_CFM_EVT is sent to the application via the ** control callback function for this handle. ** ** ** Returns AVDT_SUCCESS if successful, otherwise error. ** *******************************************************************************/ UINT16 AVDT_CloseReq(UINT8 handle) { tAVDT_SCB *p_scb; UINT16 result = AVDT_SUCCESS; /* map handle to scb */ if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) { result = AVDT_BAD_HANDLE; } else /* send event to scb */ { avdt_scb_event(p_scb, AVDT_SCB_API_CLOSE_REQ_EVT, NULL); } 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_SecurityReq ** ** Description Send a security request to the peer device. When the ** security procedure is completed, an AVDT_SECURITY_CFM_EVT ** is sent to the application via the control callback function ** for this handle. (Please note that AVDTP security procedures ** are unrelated to Bluetooth link level security.) ** ** ** Returns AVDT_SUCCESS if successful, otherwise error. ** *******************************************************************************/ UINT16 AVDT_SecurityReq(UINT8 handle, UINT8 *p_data, UINT16 len) { tAVDT_SCB *p_scb; UINT16 result = AVDT_SUCCESS; tAVDT_SCB_EVT evt; /* map handle to scb */ if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) { result = AVDT_BAD_HANDLE; } /* send event to scb */ else { evt.msg.security_rsp.p_data = p_data; evt.msg.security_rsp.len = len; avdt_scb_event(p_scb, AVDT_SCB_API_SECURITY_REQ_EVT, &evt); } return result; }
/******************************************************************************* ** ** 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_DelayReport ** ** Description This functions sends a Delay Report to the peer device ** that is associated with a particular SEID. ** This function is called by SNK device. ** ** Returns AVDT_SUCCESS if successful, otherwise error. ** *******************************************************************************/ UINT16 AVDT_DelayReport(UINT8 handle, UINT8 seid, UINT16 delay) { tAVDT_SCB *p_scb; UINT16 result = AVDT_SUCCESS; tAVDT_SCB_EVT evt; /* map handle to scb */ if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) { result = AVDT_BAD_HANDLE; } else /* send event to scb */ { evt.apidelay.hdr.seid = seid; evt.apidelay.delay = delay; avdt_scb_event(p_scb, AVDT_SCB_API_DELAY_RPT_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_ReconfigReq ** ** Description Reconfigure a stream endpoint. This allows the application ** to change the codec or content protection capabilities of ** a stream endpoint after it has been opened. This function ** can only be called if the stream is opened but not started ** or if the stream has been suspended. When the procedure ** is completed, an AVDT_RECONFIG_CFM_EVT is sent to the ** application via the control callback function for this handle. ** ** ** Returns AVDT_SUCCESS if successful, otherwise error. ** *******************************************************************************/ UINT16 AVDT_ReconfigReq(UINT8 handle, tAVDT_CFG *p_cfg) { tAVDT_SCB *p_scb; UINT16 result = AVDT_SUCCESS; tAVDT_SCB_EVT evt; /* map handle to scb */ if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) { result = AVDT_BAD_HANDLE; } /* send event to scb */ else { /* force psc_mask to zero */ p_cfg->psc_mask = 0; evt.msg.reconfig_cmd.p_cfg = p_cfg; avdt_scb_event(p_scb, AVDT_SCB_API_RECONFIG_REQ_EVT, &evt); } return result; }
/******************************************************************************* ** ** Function AVDT_ReconfigRsp ** ** Description Respond to a reconfigure request from the peer device. ** This function must be called if the application receives ** an AVDT_RECONFIG_IND_EVT through its control callback. ** ** ** Returns AVDT_SUCCESS if successful, otherwise error. ** *******************************************************************************/ UINT16 AVDT_ReconfigRsp(UINT8 handle, UINT8 label, UINT8 error_code, UINT8 category) { tAVDT_SCB *p_scb; tAVDT_SCB_EVT evt; UINT16 result = AVDT_SUCCESS; /* map handle to scb */ if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) { result = AVDT_BAD_HANDLE; } /* send event to scb */ else { evt.msg.hdr.err_code = error_code; evt.msg.hdr.err_param = category; evt.msg.hdr.label = label; avdt_scb_event(p_scb, AVDT_SCB_API_RECONFIG_RSP_EVT, &evt); } return result; }
/******************************************************************************* ** ** Function avdt_ccb_hdl_suspend_rsp ** ** Description This function is called when a suspend response or reject ** is received from the peer. Using the SEIDs stored in the ** current command message, it sends a suspend response or ** suspend reject event to each SCB associated with the command. ** ** ** ** Returns void. ** *******************************************************************************/ void avdt_ccb_hdl_suspend_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) { UINT8 event; int i; UINT8 *p; tAVDT_SCB *p_scb; /* determine rsp or rej event */ event = (p_data->msg.hdr.err_code == 0) ? AVDT_SCB_MSG_SUSPEND_RSP_EVT : AVDT_SCB_MSG_SUSPEND_REJ_EVT; /* get to where seid's are stashed in current cmd */ p = (UINT8 *)(p_ccb->p_curr_cmd + 1); /* little trick here; length of current command equals number of streams */ for (i = 0; i < p_ccb->p_curr_cmd->len; i++) { if ((p_scb = avdt_scb_by_hdl(p[i])) != NULL) { avdt_scb_event(p_scb, event, (tAVDT_SCB_EVT *) &p_data->msg); } } }
/******************************************************************************* ** ** 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); } } }
/******************************************************************************* ** ** Function AVDT_SendReport ** ** Description ** ** ** ** Returns ** *******************************************************************************/ UINT16 AVDT_SendReport(UINT8 handle, AVDT_REPORT_TYPE type, tAVDT_REPORT_DATA *p_data) { tAVDT_SCB *p_scb; UINT16 result = AVDT_BAD_PARAMS; BT_HDR *p_pkt; tAVDT_TC_TBL *p_tbl; UINT8 *p, *plen, *pm1, *p_end; #if AVDT_MULTIPLEXING == TRUE UINT8 *p_al = NULL, u; #endif UINT32 ssrc; UINT16 len; /* map handle to scb && verify parameters */ if (((p_scb = avdt_scb_by_hdl(handle)) != NULL) && (p_scb->p_ccb != NULL) && (((type == AVDT_RTCP_PT_SR) && (p_scb->cs.tsep == AVDT_TSEP_SRC)) || ((type == AVDT_RTCP_PT_RR) && (p_scb->cs.tsep == AVDT_TSEP_SNK)) || (type == AVDT_RTCP_PT_SDES)) ) { result = AVDT_NO_RESOURCES; /* build SR - assume fit in one packet */ p_tbl = avdt_ad_tc_tbl_by_type(AVDT_CHAN_REPORT, p_scb->p_ccb, p_scb); if ((p_tbl->state == AVDT_AD_ST_OPEN) && (p_pkt = (BT_HDR *)GKI_getbuf(p_tbl->peer_mtu)) != NULL) { p_pkt->offset = L2CAP_MIN_OFFSET; p = (UINT8 *)(p_pkt + 1) + p_pkt->offset; #if AVDT_MULTIPLEXING == TRUE if (p_scb->curr_cfg.psc_mask & AVDT_PSC_MUX) { /* Adaptation Layer header later */ p_al = p; p += 2; } #endif pm1 = p; *p++ = AVDT_MEDIA_OCTET1 | 1; *p++ = type; /* save the location for length */ plen = p; p += 2; ssrc = avdt_scb_gen_ssrc(p_scb); UINT32_TO_BE_STREAM(p, ssrc); switch (type) { case AVDT_RTCP_PT_SR: /* Sender Report */ *pm1 = AVDT_MEDIA_OCTET1; UINT32_TO_BE_STREAM(p, p_data->sr.ntp_sec); UINT32_TO_BE_STREAM(p, p_data->sr.ntp_frac); UINT32_TO_BE_STREAM(p, p_data->sr.rtp_time); UINT32_TO_BE_STREAM(p, p_data->sr.pkt_count); UINT32_TO_BE_STREAM(p, p_data->sr.octet_count); break; case AVDT_RTCP_PT_RR: /* Receiver Report */ *p++ = p_data->rr.frag_lost; AVDT_TRACE_API("packet_lost: %d\n", p_data->rr.packet_lost); p_data->rr.packet_lost &= 0xFFFFFF; AVDT_TRACE_API("packet_lost: %d\n", p_data->rr.packet_lost); UINT24_TO_BE_STREAM(p, p_data->rr.packet_lost); UINT32_TO_BE_STREAM(p, p_data->rr.seq_num_rcvd); UINT32_TO_BE_STREAM(p, p_data->rr.jitter); UINT32_TO_BE_STREAM(p, p_data->rr.lsr); UINT32_TO_BE_STREAM(p, p_data->rr.dlsr); break; case AVDT_RTCP_PT_SDES: /* Source Description */ *p++ = AVDT_RTCP_SDES_CNAME; len = strlen((char *)p_data->cname); if (len > AVDT_MAX_CNAME_SIZE) { len = AVDT_MAX_CNAME_SIZE; } *p++ = (UINT8)len; BCM_STRNCPY_S((char *)p, len + 1, (char *)p_data->cname, len + 1); p += len; break; } p_end = p; len = p - pm1 - 1; UINT16_TO_BE_STREAM(plen, len); #if AVDT_MULTIPLEXING == TRUE if (p_scb->curr_cfg.psc_mask & AVDT_PSC_MUX) { /* Adaptation Layer header */ p = p_al; len++; UINT16_TO_BE_STREAM(p_al, len ); /* TSID, no-fragment bit and coding of length(9-bit length field) */ u = *p; *p = (p_scb->curr_cfg.mux_tsid_report << 3) | AVDT_ALH_LCODE_9BITM0; if (u) { *p |= AVDT_ALH_LCODE_9BITM1; } } #endif /* set the actual payload length */ p_pkt->len = p_end - p; /* send the packet */ if (L2CAP_DW_FAILED != avdt_ad_write_req(AVDT_CHAN_REPORT, p_scb->p_ccb, p_scb, p_pkt)) { result = AVDT_SUCCESS; } } } return result; }