/******************************************************************************* ** ** Function btc_a2dp_sink_enque_buf ** ** Description This function is called by the av_co to fill A2DP Sink Queue ** ** ** Returns size of the queue *******************************************************************************/ UINT8 btc_a2dp_sink_enque_buf(BT_HDR *p_pkt) { tBT_SBC_HDR *p_msg; if (btc_aa_snk_cb.rx_flush == TRUE) { /* Flush enabled, do not enque*/ return fixed_queue_length(btc_aa_snk_cb.RxSbcQ); } if (fixed_queue_length(btc_aa_snk_cb.RxSbcQ) >= MAX_OUTPUT_A2DP_SNK_FRAME_QUEUE_SZ) { APPL_TRACE_WARNING("Pkt dropped\n"); return fixed_queue_length(btc_aa_snk_cb.RxSbcQ); } APPL_TRACE_DEBUG("btc_a2dp_sink_enque_buf + "); /* allocate and Queue this buffer */ if ((p_msg = (tBT_SBC_HDR *) osi_malloc(sizeof(tBT_SBC_HDR) + p_pkt->offset + p_pkt->len)) != NULL) { memcpy(p_msg, p_pkt, (sizeof(BT_HDR) + p_pkt->offset + p_pkt->len)); p_msg->num_frames_to_be_processed = (*((UINT8 *)(p_msg + 1) + p_msg->offset)) & 0x0f; APPL_TRACE_VERBOSE("btc_a2dp_sink_enque_buf %d + \n", p_msg->num_frames_to_be_processed); fixed_queue_enqueue(btc_aa_snk_cb.RxSbcQ, p_msg); btc_a2dp_sink_data_post(BTC_A2DP_SINK_DATA_EVT); } else { /* let caller deal with a failed allocation */ APPL_TRACE_WARNING("btc_a2dp_sink_enque_buf No Buffer left - "); } return fixed_queue_length(btc_aa_snk_cb.RxSbcQ); }
/******************************************************************************* ** ** Function port_flow_control_user ** ** Description Check the current user flow control and if necessary return ** events to be send to the user based on the user's specified ** flow control type. ** ** Returns event mask to be returned to the application ** *******************************************************************************/ UINT32 port_flow_control_user (tPORT *p_port) { UINT32 event = 0; /* Flow control to the user can be caused by flow controlling by the peer */ /* (FlowInd, or flow control by the peer RFCOMM (Fcon) or internally if */ /* tx_queue is full */ BOOLEAN fc = p_port->tx.peer_fc || !p_port->rfc.p_mcb || !p_port->rfc.p_mcb->peer_ready || (p_port->tx.queue_size > PORT_TX_HIGH_WM) || (fixed_queue_length(p_port->tx.queue) > PORT_TX_BUF_HIGH_WM); if (p_port->tx.user_fc == fc) { return (0); } p_port->tx.user_fc = fc; if (fc) { event = PORT_EV_FC; } else { event = PORT_EV_FC | PORT_EV_FCS; } return (event); }
/******************************************************************************* ** ** Function L2CA_UcdDataWrite ** ** Description Send UCD to remote device ** ** Parameters: PSM ** BD Address of remote ** Pointer to buffer of type BT_HDR ** flags : L2CAP_FLUSHABLE_CH_BASED ** L2CAP_FLUSHABLE_PKT ** L2CAP_NON_FLUSHABLE_PKT ** ** Return value L2CAP_DW_SUCCESS, if data accepted ** L2CAP_DW_FAILED, if error ** *******************************************************************************/ UINT16 L2CA_UcdDataWrite (UINT16 psm, BD_ADDR rem_bda, BT_HDR *p_buf, UINT16 flags) { tL2C_LCB *p_lcb; tL2C_CCB *p_ccb; tL2C_RCB *p_rcb; UINT8 *p; L2CAP_TRACE_API ("L2CA_UcdDataWrite() PSM: 0x%04x BDA: %08x%04x", psm, (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3], (rem_bda[4] << 8) + rem_bda[5]); /* Fail if the PSM is not registered */ if (((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL) || ( p_rcb->ucd.state == L2C_UCD_STATE_UNUSED )) { L2CAP_TRACE_WARNING ("L2CAP - no RCB for L2CA_UcdDataWrite, PSM: 0x%04x", psm); osi_free (p_buf); return (L2CAP_DW_FAILED); } /* First, see if we already have a link to the remote */ /* then find the channel control block for UCD */ if (((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda, BT_TRANSPORT_BR_EDR)) == NULL) || ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) == NULL)) { if ( l2c_ucd_connect (rem_bda) == FALSE ) { osi_free (p_buf); return (L2CAP_DW_FAILED); } /* If we still don't have lcb and ccb after connect attempt, then can't proceed */ if (((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda, BT_TRANSPORT_BR_EDR)) == NULL) || ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) == NULL)) { osi_free (p_buf); return (L2CAP_DW_FAILED); } } /* write PSM */ p_buf->offset -= L2CAP_UCD_OVERHEAD; p_buf->len += L2CAP_UCD_OVERHEAD; p = (UINT8 *)(p_buf + 1) + p_buf->offset; UINT16_TO_STREAM (p, psm); /* UCD MTU check */ if ((p_lcb->ucd_mtu) && (p_buf->len > p_lcb->ucd_mtu)) { L2CAP_TRACE_WARNING ("L2CAP - Handle: 0x%04x UCD bigger than peer's UCD mtu size cannot be sent", p_lcb->handle); osi_free (p_buf); return (L2CAP_DW_FAILED); } /* If already congested, do not accept any more packets */ if (p_ccb->cong_sent) { L2CAP_TRACE_ERROR ("L2CAP - Handle: 0x%04x UCD cannot be sent, already congested count: %u buff_quota: %u", p_lcb->handle, (fixed_queue_length(p_ccb->xmit_hold_q) + fixed_queue_length(p_lcb->ucd_out_sec_pending_q)), p_ccb->buff_quota); osi_free (p_buf); return (L2CAP_DW_FAILED); } /* channel based, packet based flushable or non-flushable */ p_buf->layer_specific = flags; l2c_csm_execute (p_ccb, L2CEVT_L2CA_DATA_WRITE, p_buf); if (p_ccb->cong_sent) { return (L2CAP_DW_CONGESTED); } else { return (L2CAP_DW_SUCCESS); } }
/******************************************************************************* ** ** Function PORT_DataInd ** ** Description This function is called from the RFCOMM layer when data ** buffer is received from the peer. ** *******************************************************************************/ void PORT_DataInd (tRFC_MCB *p_mcb, UINT8 dlci, BT_HDR *p_buf) { tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); UINT8 rx_char1; UINT32 events = 0; UINT8 *p; int i; RFCOMM_TRACE_EVENT("PORT_DataInd with data length %d, p_mcb:%p,p_port:%p,dlci:%d", p_buf->len, p_mcb, p_port, dlci); if (!p_port) { osi_free (p_buf); return; } /* If client registered callout callback with flow control we can just deliver receive data */ if (p_port->p_data_co_callback) { /* Another packet is delivered to user. Send credits to peer if required */ if (p_port->p_data_co_callback(p_port->inx, (UINT8 *)p_buf, -1, DATA_CO_CALLBACK_TYPE_INCOMING)) { port_flow_control_peer(p_port, TRUE, 1); } else { port_flow_control_peer(p_port, FALSE, 0); } //osi_free (p_buf); return; } else { RFCOMM_TRACE_DEBUG("PORT_DataInd, p_port:%p, p_data_co_callback is null", p_port); } /* If client registered callback we can just deliver receive data */ if (p_port->p_data_callback) { /* Another packet is delivered to user. Send credits to peer if required */ port_flow_control_peer(p_port, TRUE, 1); p_port->p_data_callback (p_port->inx, (UINT8 *)(p_buf + 1) + p_buf->offset, p_buf->len); osi_free (p_buf); return; } /* Check if rx queue exceeds the limit */ if ((p_port->rx.queue_size + p_buf->len > PORT_RX_CRITICAL_WM) || (fixed_queue_length(p_port->rx.queue) + 1 > p_port->rx_buf_critical)) { RFCOMM_TRACE_EVENT ("PORT_DataInd. Buffer over run. Dropping the buffer"); osi_free (p_buf); RFCOMM_LineStatusReq (p_mcb, dlci, LINE_STATUS_OVERRUN); return; } /* If user registered to receive notification when a particular byte is */ /* received we mast check all received bytes */ if (((rx_char1 = p_port->user_port_pars.rx_char1) != 0) && (p_port->ev_mask & PORT_EV_RXFLAG)) { for (i = 0, p = (UINT8 *)(p_buf + 1) + p_buf->offset; i < p_buf->len; i++) { if (*p++ == rx_char1) { events |= PORT_EV_RXFLAG; break; } } } osi_mutex_global_lock(); fixed_queue_enqueue(p_port->rx.queue, p_buf); p_port->rx.queue_size += p_buf->len; osi_mutex_global_unlock(); /* perform flow control procedures if necessary */ port_flow_control_peer(p_port, FALSE, 0); /* If user indicated flow control can not deliver any notifications to him */ if (p_port->rx.user_fc) { if (events & PORT_EV_RXFLAG) { p_port->rx_flag_ev_pending = TRUE; } return; } events |= PORT_EV_RXCHAR; /* Mask out all events that are not of interest to user */ events &= p_port->ev_mask; if (p_port->p_callback && events) { p_port->p_callback (events, p_port->inx); } }
/******************************************************************************* ** ** Function port_flow_control_peer ** ** Description Send flow control messages to the peer for both enabling ** and disabling flow control, for both credit-based and ** TS 07.10 flow control mechanisms. ** ** Returns nothing ** *******************************************************************************/ void port_flow_control_peer(tPORT *p_port, BOOLEAN enable, UINT16 count) { if (!p_port->rfc.p_mcb) { return; } /* If using credit based flow control */ if (p_port->rfc.p_mcb->flow == PORT_FC_CREDIT) { /* if want to enable flow from peer */ if (enable) { /* update rx credits */ if (count > p_port->credit_rx) { p_port->credit_rx = 0; } else { p_port->credit_rx -= count; } /* If credit count is less than low credit watermark, and user */ /* did not force flow control, send a credit update */ /* There might be a special case when we just adjusted rx_max */ if ((p_port->credit_rx <= p_port->credit_rx_low) && !p_port->rx.user_fc && (p_port->credit_rx_max > p_port->credit_rx)) { rfc_send_credit(p_port->rfc.p_mcb, p_port->dlci, (UINT8) (p_port->credit_rx_max - p_port->credit_rx)); p_port->credit_rx = p_port->credit_rx_max; p_port->rx.peer_fc = FALSE; } } /* else want to disable flow from peer */ else { /* if client registered data callback, just do what they want */ if (p_port->p_data_callback || p_port->p_data_co_callback) { p_port->rx.peer_fc = TRUE; } /* if queue count reached credit rx max, set peer fc */ else if (fixed_queue_length(p_port->rx.queue) >= p_port->credit_rx_max) { p_port->rx.peer_fc = TRUE; } } } /* else using TS 07.10 flow control */ else { /* if want to enable flow from peer */ if (enable) { /* If rfcomm suspended traffic from the peer based on the rx_queue_size */ /* check if it can be resumed now */ if (p_port->rx.peer_fc && (p_port->rx.queue_size < PORT_RX_LOW_WM) && (fixed_queue_length(p_port->rx.queue) < PORT_RX_BUF_LOW_WM)) { p_port->rx.peer_fc = FALSE; /* If user did not force flow control allow traffic now */ if (!p_port->rx.user_fc) { RFCOMM_FlowReq (p_port->rfc.p_mcb, p_port->dlci, TRUE); } } } /* else want to disable flow from peer */ else { /* if client registered data callback, just do what they want */ if (p_port->p_data_callback || p_port->p_data_co_callback) { p_port->rx.peer_fc = TRUE; RFCOMM_FlowReq (p_port->rfc.p_mcb, p_port->dlci, FALSE); } /* Check the size of the rx queue. If it exceeds certain */ /* level and flow control has not been sent to the peer do it now */ else if ( ((p_port->rx.queue_size > PORT_RX_HIGH_WM) || (fixed_queue_length(p_port->rx.queue) > PORT_RX_BUF_HIGH_WM)) && !p_port->rx.peer_fc) { RFCOMM_TRACE_EVENT ("PORT_DataInd Data reached HW. Sending FC set."); p_port->rx.peer_fc = TRUE; RFCOMM_FlowReq (p_port->rfc.p_mcb, p_port->dlci, FALSE); } } } }