Bluedroid: 音頻數據的傳輸流程

一. UIPC:api

  Audio Flinger獲取到a2dp的hw module,而後藍牙協議棧有專用於發送和接收media數據的線程,名稱:btif_media_task.併發

  藍牙與Audio的通訊則採用了socket的方式,管理socket的中間文件爲:UIPC。主要的做用就是,接收Audio的控制命令和音頻數據發送給BT,即 Audio_a2dp_hw -> UIPC -> btif_media_taskapp

  UIPC創建了兩條socket,分別爲:
    #define A2DP_CTRL_PATH "/data/misc/bluedroid/.a2dp_ctrl"
   
#define A2DP_DATA_PATH "/data/misc/bluedroid/.a2dp_data"框架

 

1.control socket在btif media task啓動的時建立:less

bool btif_a2dp_start_media_task(void)
{
    ...
    thread_post(worker_thread, btif_media_thread_init, NULL);

    APPL_TRACE_EVENT("## A2DP MEDIA THREAD STARTED ##");

    return true;
    ...
}

static void btif_media_thread_init(UNUSED_ATTR void *context) {
  memset(&btif_media_cb, 0, sizeof(btif_media_cb));
  UIPC_Init(NULL);

#if (BTA_AV_INCLUDED == TRUE)
  UIPC_Open(UIPC_CH_ID_AV_CTRL , btif_a2dp_ctrl_cb);
#endif

  raise_priority_a2dp(TASK_HIGH_MEDIA);
  media_task_running = MEDIA_TASK_STATE_ON;
}

2.data socket在收到audio cmd start時建立:socket

        case A2DP_CTRL_CMD_START:
            /* Don't sent START request to stack while we are in call.
               Some headsets like the Sony MW600, don't allow AVDTP START
               in call and respond BAD_STATE. */
            if (!btif_hf_is_call_idle())
            {
                a2dp_cmd_acknowledge(A2DP_CTRL_ACK_INCALL_FAILURE);
                break;
            }

            if (btif_av_stream_ready() == TRUE)
            {
                /* setup audio data channel listener */
                UIPC_Open(UIPC_CH_ID_AV_AUDIO, btif_a2dp_data_cb);

                /* post start event and wait for audio path to open */
                btif_dispatch_sm_event(BTIF_AV_START_STREAM_REQ_EVT, NULL, 0);

#if (BTA_AV_SINK_INCLUDED == TRUE)
                if (btif_media_cb.peer_sep == AVDT_TSEP_SRC)
                    a2dp_cmd_acknowledge(A2DP_CTRL_ACK_SUCCESS);//發送回執
#endif
            }
            else if (btif_av_stream_started_ready())
            {
                /* already started, setup audio data channel listener
                   and ack back immediately */
                UIPC_Open(UIPC_CH_ID_AV_AUDIO, btif_a2dp_data_cb);

                a2dp_cmd_acknowledge(A2DP_CTRL_ACK_SUCCESS);//發送回執
            }
            else
            {
                a2dp_cmd_acknowledge(A2DP_CTRL_ACK_FAILURE);
                break;
            }
            break;


3.UIPC接收command機制:
(1) 啓動時,建立一個thread,接收command:
ide

void UIPC_Init(void *p_data)
{
    UNUSED(p_data);

    BTIF_TRACE_DEBUG("UIPC_Init");

    memset(&uipc_main, 0, sizeof(tUIPC_MAIN));

    uipc_main_init();

    uipc_start_main_server_thread();
}

int uipc_start_main_server_thread(void)
{
    uipc_main.running = 1;

    if (pthread_create(&uipc_main.tid, (const pthread_attr_t *) NULL, (void*)uipc_read_task, NULL) < 0)
    {
        BTIF_TRACE_ERROR("uipc_thread_create pthread_create failed:%d", errno);
        return -1;
    }

    return 0;
}

(2)監聽每個socket,發現數據後,有限判斷是否是音頻,再判斷是否是命令:函數

static void uipc_read_task(void *arg)
{
    while (uipc_main.running)
    {
    result = select(uipc_main.max_fd+1, &uipc_main.read_set, NULL, NULL, NULL);   //發現有數據
    /* make sure we service audio channel first */
    uipc_check_fd_locked(UIPC_CH_ID_AV_AUDIO);   //先肯定是否是音頻

    /* check for other connections */
    for (ch_id = 0; ch_id < UIPC_CH_NUM; ch_id++) {
        if (ch_id != UIPC_CH_ID_AV_AUDIO)
            uipc_check_fd_locked(ch_id);         //再肯定是否是command
    }
}
static int uipc_check_fd_locked(tUIPC_CH_ID ch_id)
{
    if (uipc_main.ch[ch_id].cback)
        //通知btif,有command過來
        uipc_main.ch[ch_id].cback(ch_id, UIPC_RX_DATA_READY_EVT);
}

(3).通知btif去讀取command並處理:oop

static void btif_a2dp_ctrl_cb(tUIPC_CH_ID ch_id, tUIPC_EVENT event)
{
    switch(event)
    {
        case UIPC_RX_DATA_READY_EVT:
            btif_recv_ctrl_data();      //去獲取command
            break;
     }
}

static void btif_recv_ctrl_data(void)
{
    UINT8 cmd = 0;
    int n;
    n = UIPC_Read(UIPC_CH_ID_AV_CTRL, NULL, &cmd, 1);  //讀取該socket上的命令
}

 

2、btif_media_task:源碼分析

 1.btif_media_task是藍牙協議棧接收HAL層音頻數據的主處理線程,數據流程框圖以下:

 

 

  • BTIF reads PCM data from audio flinger via Audio HAL.(Step 6)
  • BTIF calls SBC encoder to encode PCM data to SBC frames which are put in a queue.(Step 7 and 8)
  • BTIF notifies BTA that the source data is ready in the queue.(Step 9~13)
  • BTA gets the SBC frames from the queue, then adds SBC Header. Media PL is constructed now.(Step 15~17)
  • BTA writes Media PL to AVDTP.(Step 18)
  • AVDTP adds Media Packet Header.(Step 19)

 

2.主要代碼框架:

  • btif/src/btif_av.c                     Bluedroid AV HAL implementation which implements the interface defined in AOSP/hardware/bt_av.h.
  • btif/src/btif_media_task.c    This is the multimedia module for the BTIF system. It contains task implementations AV, HS and HF profiles' audio&video processing.
  • btif/co/bta_av_co.c               This is the advanced audio/video call-out function implementation for BTIF.
  • bta/av/bta_av_ci.c                This is the implementation for advanced audio/video call-in functions which are called from BTIF.
  • bta/av/bta_av_api.c             This is the implementation of the API for the advanced audio/video(AV) subsystem of BTA. This interface is called from btif_av.c.
  • bta/av/bta_av_mian.c          This is the main implementation file for BTA advanced audio/video.
  • bta/av/bta_av_ssm.c            This is the stream state machine for the BTA advanced audio/video.
  • bta/av/bta_av_aact.c            This file contains action functions for advanced audio/video stream.
  • bta/av/bta_av_sbc.c             This module contains utility functions for dealing with SBC data frames and codec capabilities.
  • stack/a2dp/a2d_api.c           This is the implementation of the API for the Advanced Audio Distribution Profile(A2DP)
  • stack/a2dp/a2d_sbc.c          This file contains utility functions to help build and parse SBC codec information element and media payload.
  • embdrv/sbc/sbc/encoder     This folder contains the files which implement SBC decoder.

 

3、A2DP 數據處理流程:

1.ACL 包發送流程:

由前面所述,HAL層socket發送的音頻數據經過a2dp繼續下發到AVDTP:

而在 AVDTP 中,全部的功能想發送 Data,必須調用 avdt_ad_write_req 這個函數:

1 //當CCB或SCB給l2cap的 Channel 發送數據時,他們最終都會使用到L2CAP的 API:L2CA_Data_Write
 2 UINT8 avdt_ad_write_req(UINT8 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb, BT_HDR *p_buf)
 3 {
 4     UINT8   tcid;
 5 
 6     /* get tcid from type, scb */
 7     tcid = avdt_ad_type_to_tcid(type, p_scb);
 8 
 9 
10     return L2CA_DataWrite(avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid, p_buf);
11 }
12 //L2CA_DataWrite的返回形式有三種,分別是:
13 //1. L2CAP_DW_SUCCESS:此數據寫成功
14 //2.L2CAP_DW_CONGESTED:寫數據成功,可是當前信道擁堵
15 //3.L2CAP_DW_FAILED:寫數據失敗
16 UINT8 L2CA_DataWrite (UINT16 cid, BT_HDR *p_data)
17 {
18     L2CAP_TRACE_API2 ("L2CA_DataWrite()  CID: 0x%04x  Len: %d", cid, p_data->len);
19     return l2c_data_write (cid, p_data, L2CAP_FLUSHABLE_CH_BASED);
20 }

當音頻數據流到達 l2c_data_write 這個函數時,標誌數據流正式進入到L2CAP層。

l2c_data_write 這個函數作的事情主要有:

  1. 根據參數 cid(Channel ID) 找到 對應的 ccb(Channel Control Block), 找不到返回 L2CAP_DW_FAILED
  2. 若是測試者 打開 TESTER 這個宏,發送任意數據,當數據大小 大於 MTU 最大值,也會返回L2CAP_DW_FAILED
  3. 經過檢查 p_ccb->cong_sent 字段,TRUE,則說明當前 Channel 已經擁擠,此時L2CAP的這個Channel不在接收數據,返回 L2CAP_DW_FAILED
  4. 以上三個條件都經過,說明數據可發送,將數據經過 l2c_csm_execute 繼續處理。進入 l2c_csm_execute 函數,標誌着這筆數據已經成功交給 l2CAP 來處理,與上層已經沒有關係了。
  5. l2c_csm_execute 函數執行結束後,再次檢查 p_ccb->cong_sent 字段,看看當前的 Channel 是否擁擠,若是擁擠則告訴上層 L2CAP_DW_CONGESTED,不然返回 L2CAP_DW_SUCCESS,表示數據已經成功發送。
1 //返回的數據跟上面的 L2CA_DataWrite 做用相同
  2 UINT8 l2c_data_write (UINT16 cid, BT_HDR *p_data, UINT16 flags)
  3 {
  4     tL2C_CCB        *p_ccb;
  5 
  6     //遍歷l2cb.ccb_pool,經過Channel ID找到對應的Channel Control Block
  7     //l2cu_find_ccb_by_cid 見下面源碼註釋
  8     if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL)
  9     {
 10         L2CAP_TRACE_WARNING1 ("L2CAP - no CCB for L2CA_DataWrite, CID: %d", cid);
 11         GKI_freebuf (p_data);
 12         return (L2CAP_DW_FAILED);
 13     }
 14 
 15 #ifndef TESTER /* Tester may send any amount of data. otherwise sending message
 16                   bigger than mtu size of peer is a violation of protocol */
 17     if (p_data->len > p_ccb->peer_cfg.mtu)
 18     {
 19         L2CAP_TRACE_WARNING1 ("L2CAP - CID: 0x%04x  cannot send message bigger than peer's mtu size", cid);
 20         GKI_freebuf (p_data);
 21         return (L2CAP_DW_FAILED);
 22     }
 23 #endif
 24 
 25     /* channel based, packet based flushable or non-flushable */
 26     //Bluedroid中默認的是 L2CAP_FLUSHABLE_CH_BASED
 27     //這個 layer_specific 在 數據發送的 l2c_link_send_to_lower 中表示 ACL包分包 個數
 28     p_data->layer_specific = flags;
 29 
 30     //發現本 Channel 已經擁堵,直接返回L2CAP_DW_FAILED 告訴上層等會再發數據
 31     //當幾個應用 共用 此 Channel 可能會出現這種狀況
 32     if (p_ccb->cong_sent)
 33     {
 34         L2CAP_TRACE_ERROR3 ("L2CAP - CID: 0x%04x cannot send, already congested  xmit_hold_q.count: %u  buff_quota: %u",
 35                             p_ccb->local_cid, p_ccb->xmit_hold_q.count, p_ccb->buff_quota);
 36 
 37         GKI_freebuf (p_data);
 38         return (L2CAP_DW_FAILED);
 39     }
 40     //毫無疑問啦,這個函數就是咱們繼續須要分析的函數
 41     l2c_csm_execute (p_ccb, L2CEVT_L2CA_DATA_WRITE, p_data);
 42 
 43     //已經將上層的這筆數據發送完,若是此 Channel 擁擠了(以前發送這筆包還沒擁擠)
 44     //返回 L2CAP_DW_CONGESTED 告訴上層當前信道擁擠,你要給我L2CAP層發數據,是不發下來的
 45     if (p_ccb->cong_sent)
 46         return (L2CAP_DW_CONGESTED);
 47 
 48     //成功發送,而且此時 Channel 並不擁擠
 49     return (L2CAP_DW_SUCCESS);
 50 }
 51 
 52 //經過 Channel ID 找到 Channel Control Block
 53 tL2C_CCB *l2cu_find_ccb_by_cid (tL2C_LCB *p_lcb, UINT16 local_cid)
 54 {
 55     tL2C_CCB    *p_ccb = NULL;
 56 #if (L2CAP_UCD_INCLUDED == TRUE)
 57     UINT8 xx;
 58 #endif
 59 
 60     if (local_cid >= L2CAP_BASE_APPL_CID) //大於或等於 0x0040 說明不是 Fixed Channel
 61     {
 62         /* find the associated CCB by "index" */
 63         local_cid -= L2CAP_BASE_APPL_CID;
 64 
 65         if (local_cid >= MAX_L2CAP_CHANNELS)
 66             return NULL;
 67 
 68         p_ccb = l2cb.ccb_pool + local_cid; //直接經過地址偏移找到
 69 
 70         /* make sure the CCB is in use */
 71         if (!p_ccb->in_use)
 72         {
 73             p_ccb = NULL;
 74         }
 75         /* make sure it's for the same LCB */
 76         else if (p_lcb && p_lcb != p_ccb->p_lcb)
 77         {
 78             p_ccb = NULL;
 79         }
 80     }
 81 #if (L2CAP_UCD_INCLUDED == TRUE) //默認是關閉的,既然從上層來的都是 數據包了,我認爲不會用到 Fixed Channel
 82     else
 83     {
 84         /* searching fixed channel */
 85         p_ccb = l2cb.ccb_pool;
 86         for ( xx = 0; xx < MAX_L2CAP_CHANNELS; xx++ )
 87         {
 88             if ((p_ccb->local_cid == local_cid)
 89               &&(p_ccb->in_use)
 90               &&(p_lcb == p_ccb->p_lcb))
 91                 break;
 92             else
 93                 p_ccb++;
 94         }
 95         if ( xx >= MAX_L2CAP_CHANNELS )
 96             return NULL;
 97     }
 98 #endif
 99 
100     return (p_ccb);
101 }

首先進入了 L2CAP 層的狀態機:

 1 void l2c_csm_execute (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
 2 {
 3     switch (p_ccb->chnl_state)
 4     {
 5     case CST_CLOSED:
 6         l2c_csm_closed (p_ccb, event, p_data);
 7         break;
 8 
 9     case CST_ORIG_W4_SEC_COMP:
10         l2c_csm_orig_w4_sec_comp (p_ccb, event, p_data);
11         break;
12 
13     case CST_TERM_W4_SEC_COMP:
14         l2c_csm_term_w4_sec_comp (p_ccb, event, p_data);
15         break;
16 
17     case CST_W4_L2CAP_CONNECT_RSP:
18         l2c_csm_w4_l2cap_connect_rsp (p_ccb, event, p_data);
19         break;
20 
21     case CST_W4_L2CA_CONNECT_RSP:
22         l2c_csm_w4_l2ca_connect_rsp (p_ccb, event, p_data);
23         break;
24 
25     case CST_CONFIG:
26         l2c_csm_config (p_ccb, event, p_data);
27         break;
28 
29     case CST_OPEN:
30         l2c_csm_open (p_ccb, event, p_data);
31         break;
32 
33     case CST_W4_L2CAP_DISCONNECT_RSP:
34         l2c_csm_w4_l2cap_disconnect_rsp (p_ccb, event, p_data);
35         break;
36 
37     case CST_W4_L2CA_DISCONNECT_RSP:
38         l2c_csm_w4_l2ca_disconnect_rsp (p_ccb, event, p_data);
39         break;
40 
41     default:
42         break;
43     }
44 }
具體的 Channel 狀態信息以下

 1 typedef enum
 2 {
 3     CST_CLOSED,                           /* Channel is in clodes state           */
 4     CST_ORIG_W4_SEC_COMP,                 /* Originator waits security clearence  */
 5     CST_TERM_W4_SEC_COMP,                 /* Acceptor waits security clearence    */
 6     CST_W4_L2CAP_CONNECT_RSP,             /* Waiting for peer conenct response    */
 7     CST_W4_L2CA_CONNECT_RSP,              /* Waiting for upper layer connect rsp  */
 8     CST_CONFIG,                           /* Negotiating configuration            */
 9     CST_OPEN,                             /* Data transfer state                  */
10     CST_W4_L2CAP_DISCONNECT_RSP,          /* Waiting for peer disconnect rsp      */
11     CST_W4_L2CA_DISCONNECT_RSP            /* Waiting for upper layer disc rsp     */
12 } tL2C_CHNL_STATE;

音頻數據包在函數 l2c_csm_open 中流轉,通過各類選擇和判斷,最後走的是 L2CEVT_L2CA_DATA_WRITE 這個 case。這個 case 調用了 l2c_enqueue_peer_data 讓數據進入到當前 ccb 的 xmit_hold_q 隊列中,暫存此數據包。l2c_link_check_send_pkts 這個函數發送數據包:

  1 //l2c_csm_open 處理 Channel 處於 OPEN 狀態下的各類 Event
  2 static void l2c_csm_open (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
  3 {
  4     UINT16                  local_cid = p_ccb->local_cid;
  5     tL2CAP_CFG_INFO         *p_cfg;
  6     tL2C_CHNL_STATE         tempstate;
  7     UINT8                   tempcfgdone;
  8     UINT8                   cfg_result;
  9 
 10 #if (BT_TRACE_VERBOSE == TRUE)
 11     L2CAP_TRACE_EVENT2 ("L2CAP - LCID: 0x%04x  st: OPEN  evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event));
 12 #else
 13     L2CAP_TRACE_EVENT1 ("L2CAP - st: OPEN evt: %d", event);
 14 #endif
 15 
 16 #if (L2CAP_UCD_INCLUDED == TRUE) //默認 UCD 是關閉的
 17     if ( local_cid == L2CAP_CONNECTIONLESS_CID )
 18     {
 19         /* check if this event can be processed by UCD */
 20         if ( l2c_ucd_process_event (p_ccb, event, p_data) )
 21         {
 22             /* The event is processed by UCD state machine */
 23             return;
 24         }
 25     }
 26 #endif
 27 
 28     switch (event)
 29     {
 30     case L2CEVT_LP_DISCONNECT_IND:  //Link 都斷開鏈接了,天然 Channel也沒有存在的必要了,各類清除 CCB 的工做
 31         L2CAP_TRACE_API1 ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x  No Conf Needed", p_ccb->local_cid);
 32         l2cu_release_ccb (p_ccb);//釋放 當前的 CCB 
 33         if (p_ccb->p_rcb)
 34             (*p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb)(local_cid, FALSE);
 35         break;
 36 
 37     case L2CEVT_LP_QOS_VIOLATION_IND:               /* QOS violation         */
 38         /* Tell upper layer. If service guaranteed, then clear the channel   */
 39         if (p_ccb->p_rcb->api.pL2CA_QoSViolationInd_Cb)
 40             (*p_ccb->p_rcb->api.pL2CA_QoSViolationInd_Cb)(p_ccb->p_lcb->remote_bd_addr);
 41         break;
 42 
 43     case L2CEVT_L2CAP_CONFIG_REQ:                  /* Peer config request   */
 44         p_cfg = (tL2CAP_CFG_INFO *)p_data;
 45 
 46         tempstate = p_ccb->chnl_state;
 47         tempcfgdone = p_ccb->config_done;
 48         p_ccb->chnl_state = CST_CONFIG; //若是數據流中的數據是 L2CEVT_L2CAP_CONFIG_REQ,固然要轉到 CST_CONFIG中繼續處理
 49         p_ccb->config_done &= ~CFG_DONE_MASK;
 50         //啓動一個 timer ,一段時間後,查看 cfg 的狀態
 51         //若是配置處於 L2CAP_PEER_CFG_UNACCEPTABLE,繼續嘗試配置
 52         //若是配置處於斷開狀態,那當前 Channel 直接斷開鏈接。
 53         btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT);
 54 
 55         if ((cfg_result = l2cu_process_peer_cfg_req (p_ccb, p_cfg)) == L2CAP_PEER_CFG_OK)
 56         {
 57             (*p_ccb->p_rcb->api.pL2CA_ConfigInd_Cb)(p_ccb->local_cid, p_cfg);
 58         }
 59 
 60         /* Error in config parameters: reset state and config flag */
 61         else if (cfg_result == L2CAP_PEER_CFG_UNACCEPTABLE)
 62         {
 63             btu_stop_timer(&p_ccb->timer_entry);
 64             p_ccb->chnl_state = tempstate;
 65             p_ccb->config_done = tempcfgdone;
 66             l2cu_send_peer_config_rsp (p_ccb, p_cfg);
 67         }
 68         else    /* L2CAP_PEER_CFG_DISCONNECT */
 69         {
 70             /* Disconnect if channels are incompatible
 71              * Note this should not occur if reconfigure
 72              * since this should have never passed original config.
 73              */
 74             l2cu_disconnect_chnl (p_ccb);
 75         }
 76         break;
 77 
 78     case L2CEVT_L2CAP_DISCONNECT_REQ:                  /* Peer disconnected request */
 79 // btla-specific ++
 80         /* Make sure we are not in sniff mode */
 81 #if BTM_PWR_MGR_INCLUDED == TRUE
 82         {
 83             tBTM_PM_PWR_MD settings;
 84             settings.mode = BTM_PM_MD_ACTIVE;
 85             BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p_ccb->p_lcb->remote_bd_addr, &settings);
 86         }
 87 #else
 88         BTM_CancelSniffMode (p_ccb->p_lcb->remote_bd_addr);
 89 #endif
 90 // btla-specific --
 91 
 92         p_ccb->chnl_state = CST_W4_L2CA_DISCONNECT_RSP; //Peer 發送 Disconnect,咱們要對此發 Response
 93         btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_DISCONNECT_TOUT);
 94         L2CAP_TRACE_API1 ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x  Conf Needed", p_ccb->local_cid);
 95         (*p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb)(p_ccb->local_cid, TRUE);
 96         break;
 97 
 98     case L2CEVT_L2CAP_DATA:                         /* Peer data packet rcvd    */
 99         //收到 Peer 傳來的數據,固然要把這個數據經過回調送到上層應用去
100         //pL2CA_DataInd_Cb 中定義了回調,交給上層處理收到的數據
101         (*p_ccb->p_rcb->api.pL2CA_DataInd_Cb)(p_ccb->local_cid, (BT_HDR *)p_data);
102         break;
103 
104     case L2CEVT_L2CA_DISCONNECT_REQ:                 /* Upper wants to disconnect */
105         /* Make sure we are not in sniff mode */
106 #if BTM_PWR_MGR_INCLUDED == TRUE
107         {
108             tBTM_PM_PWR_MD settings;
109             settings.mode = BTM_PM_MD_ACTIVE;
110             BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p_ccb->p_lcb->remote_bd_addr, &settings);
111         }
112 #else
113         BTM_CancelSniffMode (p_ccb->p_lcb->remote_bd_addr);
114 #endif
115 
116         l2cu_send_peer_disc_req (p_ccb);
117         p_ccb->chnl_state = CST_W4_L2CAP_DISCONNECT_RSP;
118         btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_DISCONNECT_TOUT);
119         break;
120 
121     case L2CEVT_L2CA_DATA_WRITE:                    /* Upper layer data to send */   //mike mark l2c
122         //上層將數據發送給下層
123         //咱們的音樂數據就是走這個 case(爲何?看整個函數的參數就明白了)
124         //首先將數據入隊,下面會展開分析這個函數
125         l2c_enqueue_peer_data (p_ccb, (BT_HDR *)p_data);
126         //最終調用 l2c_link_check_send_pkts 來發送咱們的音樂數據包
127         l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL);
128         break;
129 
130     case L2CEVT_L2CA_CONFIG_REQ:                   /* Upper layer config req   */
131         p_ccb->chnl_state = CST_CONFIG;
132         p_ccb->config_done &= ~CFG_DONE_MASK;
133         l2cu_process_our_cfg_req (p_ccb, (tL2CAP_CFG_INFO *)p_data);
134         l2cu_send_peer_config_req (p_ccb, (tL2CAP_CFG_INFO *)p_data);
135         btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT);
136         break;
137 
138     case L2CEVT_TIMEOUT:
139         /* Process the monitor/retransmission time-outs in flow control/retrans mode */
140         if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE)
141             l2c_fcr_proc_tout (p_ccb);
142         break;
143 
144     case L2CEVT_ACK_TIMEOUT:
145         l2c_fcr_proc_ack_tout (p_ccb);
146         break;
147     }
148 }

l2c_enqueue_peer_data 函數的主要做用是將咱們的音樂數據包入數據發送隊列以及處理 FCR segmentation 和當前 Channel 是否擁堵的檢測,其主要作了這麼幾件事:

1. 組裝好 p_buf 併入 當前 CCB 的 xmit_hold_q 隊列。

2. 檢查當前 Channel 擁堵狀況。

3. 當前 Link 支持 RR,則檢查當前ACL數據包所在 Channel 的權限,若是當前 CCB 中的權限高於 RR,則把 RR 中的權限設置爲跟 CCB 相同。

4. 若 Link 上沒有發送窗口,則將 l2cb.check_round_robin 置爲TRUE,下一次須要 RR。

 1 void l2c_enqueue_peer_data (tL2C_CCB *p_ccb, BT_HDR *p_buf)
  2 {
  3     UINT8       *p;
  4 
  5     if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE)
  6     {
  7         p_buf->event = 0;
  8     }
  9     else
 10     {
 11         /* Save the channel ID for faster counting */
 12         p_buf->event = p_ccb->local_cid;
 13 
 14         /* Step back to add the L2CAP header */
 15         p_buf->offset -= L2CAP_PKT_OVERHEAD;
 16         p_buf->len    += L2CAP_PKT_OVERHEAD;
 17 
 18         /* Set the pointer to the beginning of the data */
 19         p = (UINT8 *)(p_buf + 1) + p_buf->offset;
 20 
 21         /* Now the L2CAP header */
 22         UINT16_TO_STREAM (p, p_buf->len - L2CAP_PKT_OVERHEAD);
 23         UINT16_TO_STREAM (p, p_ccb->remote_cid);
 24     }
 25 
 26     GKI_enqueue (&p_ccb->xmit_hold_q, p_buf);//真正將組裝好的 p_buf 入隊
 27 
 28     l2cu_check_channel_congestion (p_ccb);  //檢測當前 Channel 擁堵狀況,下面會繼續分析這個函數
 29 
 30 #if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE)
 31     /* if new packet is higher priority than serving ccb and it is not overrun */
 32     if (( p_ccb->p_lcb->rr_pri > p_ccb->ccb_priority ) //當前數據包所在Channel的權限
 33       &&( p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].quota > 0))
 34     {
 35         /* send out higher priority packet */
 36         p_ccb->p_lcb->rr_pri = p_ccb->ccb_priority;//當前要發送的數據的Channel,設置爲ccb_priority,比原來權限要高。
 37     }
 38 #endif
 39 
 40     //若是當前 link 上的 link_xmit_quota ==0(link上的發送窗口爲0),那麼有必要作一次 RR
 41     if (p_ccb->p_lcb->link_xmit_quota == 0)
 42         l2cb.check_round_robin = TRUE;
 43 }
 44 
 45 //check if any change in congestion status
 46 
 47 void l2cu_check_channel_congestion (tL2C_CCB *p_ccb)
 48 {
 49     UINT16 q_count = p_ccb->xmit_hold_q.count; //當前 CCB 中 發送數據隊列中數據包的總數
 50 
 51 #if (L2CAP_UCD_INCLUDED == TRUE)
 52     if ( p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID )
 53     {
 54         q_count += p_ccb->p_lcb->ucd_out_sec_pending_q.count;
 55     }
 56 #endif
 57 
 58     /* If the CCB queue limit is subject to a quota, check for congestion */
 59 
 60     /* if this channel has outgoing traffic */
 61     if ((p_ccb->p_rcb)&&(p_ccb->buff_quota != 0))
 62     {
 63         /* If this channel was congested */
 64         if ( p_ccb->cong_sent ) //當前 Channel 的這個字段爲TRUE,是否真正擁堵,咱們要繼續判斷
 65         {
 66             /* If the channel is not congested now, tell the app */
 67             //p_ccb->buff_quota = quota_per_weighted_chnls[HCI_ACL_POOL_ID] * p_ccb->tx_data_rate
 68             //在函數 l2c_link_adjust_chnl_allocation 中配置此值
 69             if (q_count <= (p_ccb->buff_quota / 2))//q_count爲 CCB 中的xmit_hold_q
 70             {
 71                 p_ccb->cong_sent = FALSE; //當前CCB中的 xmit_hold_q 小於 buffer_quota 值的一半,就認爲已經不擁堵了
 72                 if (p_ccb->p_rcb->api.pL2CA_CongestionStatus_Cb)
 73                 {
 74                     L2CAP_TRACE_DEBUG3 ("L2CAP - Calling CongestionStatus_Cb (FALSE), CID: 0x%04x  xmit_hold_q.count: %u  buff_quota: %u",
 75                                       p_ccb->local_cid, q_count, p_ccb->buff_quota);
 76 
 77                     /* Prevent recursive calling */
 78                     l2cb.is_cong_cback_context = TRUE;
 79                     (*p_ccb->p_rcb->api.pL2CA_CongestionStatus_Cb)(p_ccb->local_cid, FALSE);
 80                     l2cb.is_cong_cback_context = FALSE;
 81                 }
 82 #if (L2CAP_UCD_INCLUDED == TRUE)
 83                 else if ( p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID )//無鏈接的 CID
 84                 {
 85                     if ( p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb )
 86                     {
 87                         L2CAP_TRACE_DEBUG3 ("L2CAP - Calling UCD CongestionStatus_Cb (FALSE), SecPendingQ:%u,XmitQ:%u,Quota:%u",
 88                                              p_ccb->p_lcb->ucd_out_sec_pending_q.count,
 89                                              p_ccb->xmit_hold_q.count, p_ccb->buff_quota);
 90                         p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb( p_ccb->p_lcb->remote_bd_addr, FALSE );
 91                     }
 92                 }
 93 #endif
 94             }
 95         }
 96         else
 97         {
 98             /* If this channel was not congested but it is congested now, tell the app */
 99             if (q_count > p_ccb->buff_quota) //此時仍然處於擁堵狀態
100             {
101                 p_ccb->cong_sent = TRUE;
102                 if (p_ccb->p_rcb->api.pL2CA_CongestionStatus_Cb)
103                 {
104                     L2CAP_TRACE_DEBUG3 ("L2CAP - Calling CongestionStatus_Cb (TRUE),CID:0x%04x,XmitQ:%u,Quota:%u",
105                         p_ccb->local_cid, q_count, p_ccb->buff_quota);
106 
107                     (*p_ccb->p_rcb->api.pL2CA_CongestionStatus_Cb)(p_ccb->local_cid, TRUE);
108                 }
109 #if (L2CAP_UCD_INCLUDED == TRUE)
110                 else if ( p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID )
111                 {
112                     if ( p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb )
113                     {
114                         L2CAP_TRACE_DEBUG3 ("L2CAP - Calling UCD CongestionStatus_Cb (TRUE), SecPendingQ:%u,XmitQ:%u,Quota:%u",
115                                              p_ccb->p_lcb->ucd_out_sec_pending_q.count,
116                                              p_ccb->xmit_hold_q.count, p_ccb->buff_quota);
117                         p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb( p_ccb->p_lcb->remote_bd_addr, TRUE );
118                     }
119                 }
120 #endif
121             }
122         }
123     }
124 }

正常狀況下,咱們聽音樂的數據流發送的狀況,這是最多見的一種狀況,調用形式爲 l2c_link_check_send_pkts (p_lcb, NULL, NULL)。

  1. 判斷當前 Link 是否擁堵。
  2. 首先看看 Link 上的 link_xmit_data_q 隊列有沒有數據,有的話發送
  3. 若是 link_xmit_data_q 沒有數據,在看看 Link 上的 CCB,經過 CCB 的鏈表,找到 CCB 上的隊列,一直找,直到找到一個 數據包,發送之。
  4. 注意:上層在發送數據包,僅僅是調用一下這個函數,至於 是否是這個數據包,那不必定;反正每次上層調用都會發送一個,上層發下來的包總會發出去的。
 1 void l2c_link_check_send_pkts (tL2C_LCB *p_lcb, tL2C_CCB *p_ccb, BT_HDR *p_buf)
  2 {
  3     int         xx;
  4     BOOLEAN     single_write = FALSE; //最後 Link Disc 用來把 CCB 中的數據包放到 Link 上的隊列發,速度加快
  5     L2CAP_TRACE_DEBUG0("mike: in func-- l2c_link_check_send_pkts");
  6     /* Save the channel ID for faster counting */
  7     if (p_buf) //通常數據包都爲空,只有發送 L2CAP 發送 command/response 或 發送 S-Frame 纔用到
  8     {
  9         if (p_ccb != NULL) //這個 case 就是 當前 Link 即將斷開的狀況了
 10         {
 11             p_buf->event = p_ccb->local_cid;
 12             single_write = TRUE; //見上面註釋
 13             L2CAP_TRACE_DEBUG0("mike: l2c_link_check_send_pkts-- p_buf p_ccb not null");
 14         }
 15         else
 16             p_buf->event = 0;
 17 
 18         p_buf->layer_specific = 0;
 19         L2CAP_TRACE_DEBUG0("mike: l2c_link_check_send_pkts-- p_buf->layer_specific=0");
 20         GKI_enqueue (&p_lcb->link_xmit_data_q, p_buf); //把這個數據包放到 當前 link 上的 link_xmit_data_q隊列中
 21 
 22         if (p_lcb->link_xmit_quota == 0){
 23             l2cb.check_round_robin = TRUE; // 沒有發送窗口了,須要 RR 看看有沒有別的數據包能夠發送的
 24             L2CAP_TRACE_DEBUG0("mike: l2c_link_check_send_pkts-- p_lcb->link_xmit_quota");
 25         }
 26     }
 27 
 28     /* If this is called from uncongested callback context break recursive calling.
 29     ** This LCB will be served when receiving number of completed packet event.
 30     */
 31     if (l2cb.is_cong_cback_context){//當前 Link 擁堵了,不發送數據包直接返回,幾乎不會發生
 32         L2CAP_TRACE_DEBUG0("mike: l2c_link_check_send_pkts-- l2cd.is_cong_cback_context");
 33         return;
 34     }
 35     /* If we are in a scenario where there are not enough buffers for each link to
 36     ** have at least 1, then do a round-robin for all the LCBs
 37     */
 38     if ( (p_lcb == NULL) || (p_lcb->link_xmit_quota == 0) )
 39     {
 40         L2CAP_TRACE_DEBUG0("mike: l2c_link_check_send_pkts-- (p_lcb == NULL) ||(p_lcb->link_xmit_quota == 0)");
 41         if (p_lcb == NULL)
 42             p_lcb = l2cb.lcb_pool;
 43         else if (!single_write)
 44             p_lcb++;
 45 
 46         /* Loop through, starting at the next */
 47         //哎呀,沒有足夠發送窗口了,在全部的 Link 上作一次 RR
 48         for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_lcb++)
 49         {
 50             L2CAP_TRACE_DEBUG1("mike: l2c_link_check_send_pkts--Loop through: xx = %d",xx);
 51             /* If controller window is full, nothing to do */
 52             if ( (l2cb.controller_xmit_window == 0
 53 #if (BLE_INCLUDED == TRUE)
 54                   && !p_lcb->is_ble_link
 55 #endif
 56                 )
 57 #if (BLE_INCLUDED == TRUE)
 58                 || (p_lcb->is_ble_link && l2cb.controller_le_xmit_window == 0 )
 59 #endif
 60               || (l2cb.round_robin_unacked >= l2cb.round_robin_quota) )
 61                 break;
 62 
 63             /* Check for wraparound */
 64             if (p_lcb == &l2cb.lcb_pool[MAX_L2CAP_LINKS])
 65                 p_lcb = &l2cb.lcb_pool[0];
 66 
 67             if ( (!p_lcb->in_use)
 68                || (p_lcb->partial_segment_being_sent)
 69                || (p_lcb->link_state != LST_CONNECTED)
 70                || (p_lcb->link_xmit_quota != 0)
 71                || (L2C_LINK_CHECK_POWER_MODE (p_lcb)) )
 72                 continue;
 73 
 74             //首先從 當前 Link 上的 link_xmit_data_q 中取出數據包併發送  
 75             if ((p_buf = (BT_HDR *)GKI_dequeue (&p_lcb->link_xmit_data_q)) != NULL)
 76             {
 77                 L2CAP_TRACE_DEBUG0("mike: l2c_link_check_send_pkts--if ((p_buf = (BT_HDR*)GKI_dequeue (&p_lcb->link_xmit_data_q)) != NULL)");
 78                 l2c_link_send_to_lower (p_lcb, p_buf);
 79             }
 80             else if (single_write) //若是是 single_write 設置爲 TRUE,說明數據包 已經在 link_xmit_data_q 發送了,不必在執行下面的 code 了
 81             {
 82                 /* If only doing one write, break out */
 83                 L2CAP_TRACE_DEBUG0("mike: l2c_link_check_send_pkts--single write is true then break");
 84                 break;
 85             }
 86             //Link 上的 Queue 中沒有東西能夠發送,查找 CCB 中的 Queue,直到找到一個爲止。
 87             else if ((p_buf = l2cu_get_next_buffer_to_send (p_lcb)) != NULL)
 88             {
 89                 L2CAP_TRACE_DEBUG0("mike: l2c_link_check_send_pkts--(p_buf=l2cu_get_next_buffer_to_send (p_lcb)) != NULL");
 90                 l2c_link_send_to_lower (p_lcb, p_buf);
 91             }
 92         }
 93 
 94         /* If we finished without using up our quota, no need for a safety check */
 95 #if (BLE_INCLUDED == TRUE)
 96         if ( ((l2cb.controller_xmit_window > 0 && !p_lcb->is_ble_link) ||
 97              (l2cb.controller_le_xmit_window > 0 && p_lcb->is_ble_link))
 98           && (l2cb.round_robin_unacked < l2cb.round_robin_quota) )
 99 #else
100         if ( (l2cb.controller_xmit_window > 0)
101           && (l2cb.round_robin_unacked < l2cb.round_robin_quota) )
102 
103 #endif
104             l2cb.check_round_robin = FALSE;
105     }
106     else /* if this is not round-robin service */
107     {
108         /* If a partial segment is being sent, can't send anything else */
109         if ( (p_lcb->partial_segment_being_sent)
110           || (p_lcb->link_state != LST_CONNECTED)
111           || (L2C_LINK_CHECK_POWER_MODE (p_lcb)) )
112             return;
113 
114         /* See if we can send anything from the link queue */
115 #if (BLE_INCLUDED == TRUE)
116         while ( ((l2cb.controller_xmit_window != 0 && !p_lcb->is_ble_link) ||
117                  (l2cb.controller_le_xmit_window != 0 && p_lcb->is_ble_link))
118              && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota))
119 #else
120         while ( (l2cb.controller_xmit_window != 0)
121              && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota))
122 #endif
123         {
124             if ((p_buf = (BT_HDR *)GKI_dequeue (&p_lcb->link_xmit_data_q)) == NULL)//發送Link上的數據包
125                 break;
126 
127             if (!l2c_link_send_to_lower (p_lcb, p_buf))
128                 break;
129         }
130 
131         if (!single_write)//確保不是在 鏈路 disc 狀態下
132         {
133             /* See if we can send anything for any channel */
134 #if (BLE_INCLUDED == TRUE)
135             while ( ((l2cb.controller_xmit_window != 0 && !p_lcb->is_ble_link) ||
136                     (l2cb.controller_le_xmit_window != 0 && p_lcb->is_ble_link))
137                     && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota))
138 #else
139             while ((l2cb.controller_xmit_window != 0) && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota))
140 #endif
141             {
142                 if ((p_buf = l2cu_get_next_buffer_to_send (p_lcb)) == NULL)//找到一個數據包來發送
143                     break;
144 
145                 if (!l2c_link_send_to_lower (p_lcb, p_buf))
146                     break;
147             }
148         }
149 
150         /* There is a special case where we have readjusted the link quotas and  */
151         /* this link may have sent anything but some other link sent packets so  */
152         /* so we may need a timer to kick off this link's transmissions.         */
153         if ( (p_lcb->link_xmit_data_q.count) && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota) )
154             L2CAP_TRACE_DEBUG0("mike: l2c_link_check_send_pkts--a timer to kick off this link's transmissions");
155             btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_LINK_FLOW_CONTROL_TOUT);
156     }
157 
158 }

最終 l2c_link_check_send_pkts 把數據包交給了 l2c_link_send_to_lower 來作處理,咱們的音樂數據包最終也被從某個 CCB 中的隊列出隊列給了 l2c_link_send_to_lower。l2c_link_send_to_lower 主要作了這些事情:

  1. 若是當前數據包 p_buf 的長度小於 ACL 包的最大值,sent_not_acked 加1,整個 L2CAP 的 controller_xmit_window 減1。而後經過 L2C_LINK_SEND_ACL_DATA 將此數據包發送出去。
  2. 若是當前數據包 p_buf 的長度大於 ACL 包的最大值,先看看能分紅幾個分包(爲了求的幾個窗口能容下),而後窗口值減掉這些分包個數,而後將整個數據包交給 L2C_LINK_SEND_ACL_DATA (大於ACL包長度),具體分包發送由 H5(串口) 部分來負責。
 1 /*******************************************************************************
  2 **
  3 ** Function         l2c_link_send_to_lower
  4 **
  5 ** Description      This function queues the buffer for HCI transmission
  6 **
  7 ** Returns          TRUE for success, FALSE for fail
  8 **
  9 *******************************************************************************/
 10 static BOOLEAN l2c_link_send_to_lower (tL2C_LCB *p_lcb, BT_HDR *p_buf)
 11 {
 12     UINT16      num_segs;
 13     UINT16      xmit_window, acl_data_size;
 14     L2CAP_TRACE_DEBUG0("mike: l2c_link_send_to_lower");
 15 #if (BLE_INCLUDED == TRUE)
 16     if ((!p_lcb->is_ble_link && (p_buf->len <= btu_cb.hcit_acl_pkt_size)) ||
 17         (p_lcb->is_ble_link && (p_buf->len <= btu_cb.hcit_ble_acl_pkt_size)))
 18 #else
 19     if (p_buf->len <= btu_cb.hcit_acl_pkt_size) //通常都是走這條路徑,p_buf通常不會超過 ACL 長度最大值
 20 #endif
 21     {
 22         if (p_lcb->link_xmit_quota == 0){ // Link 上沒有窗口了,controller_xmit_window 窗口仍是有的,此時就是 round_roubin_unack了(由於上面的數據已經下來了,必須得發送)
 23             L2CAP_TRACE_DEBUG0("mike: l2c_link_send_to_lower--if (p_lcb->link_xmit_quota == 0)");
 24             l2cb.round_robin_unacked++;
 25             L2CAP_TRACE_DEBUG1("mike: l2c_link_send_to_lower--l2cb.round_robin_unacked=%d",l2cb.round_robin_unacked);
 26         }
 27         p_lcb->sent_not_acked++; //整個 Link 已經發送可是沒有回覆的數據包個數
 28         L2CAP_TRACE_DEBUG1("mike:l2c_link_send_to_lower--p_lcb->sent_not_acked:",p_lcb->sent_not_acked);
 29         p_buf->layer_specific = 0;
 30 
 31 #if (BLE_INCLUDED == TRUE)
 32         if (p_lcb->is_ble_link)
 33         {
 34             l2cb.controller_le_xmit_window--;
 35             L2C_LINK_SEND_BLE_ACL_DATA (p_buf);
 36         }
 37         else
 38 #endif
 39         {
 40             l2cb.controller_xmit_window--; //當前 controller 發送窗口減1
 41             L2CAP_TRACE_DEBUG1("mike:l2c_link_send_to_lower--,l2cb.controller_xmit_window=%d",l2cb.controller_xmit_window);
 42             L2CAP_TRACE_DEBUG0("mike: l2c_link_send_to_lower--L2C_LINK_SEND_ACL_DATA");
 43             L2C_LINK_SEND_ACL_DATA (p_buf); //發送當前這個數據包
 44         }
 45     }
 46     else
 47     {
 48 #if BLE_INCLUDED == TRUE
 49         if (p_lcb->is_ble_link)
 50         {
 51             acl_data_size = btu_cb.hcit_ble_acl_data_size;
 52             xmit_window = l2cb.controller_le_xmit_window;
 53 
 54         }
 55         else
 56 #endif
 57         {
 58             acl_data_size = btu_cb.hcit_acl_data_size;//ACL 包額度最大值
 59             xmit_window = l2cb.controller_xmit_window; //controller目前爲止的可用窗口
 60         }
 61         num_segs = (p_buf->len - HCI_DATA_PREAMBLE_SIZE + acl_data_size - 1) / acl_data_size;
 62         L2CAP_TRACE_DEBUG3("mike: l2c_link_send_to_lower-- num_segs:%d, acl_data_size:%d,xmit_window=%d", num_segs,acl_data_size, xmit_window);
 63 
 64         /* If doing round-robin, then only 1 segment each time */
 65         if (p_lcb->link_xmit_quota == 0)
 66         {
 67             num_segs = 1;
 68             p_lcb->partial_segment_being_sent = TRUE;
 69         }
 70         else
 71         {
 72             /* Multi-segment packet. Make sure it can fit */
 73             if (num_segs > xmit_window)
 74             {
 75                 num_segs = xmit_window;//分包個數比 controller 窗口的個數還多,只能發 controller 個數的包
 76                 p_lcb->partial_segment_being_sent = TRUE; //標誌位,還有分包,須要繼續發送,Btu_task 中有個 Event 就是處理分包的
 77             }
 78 
 79             if (num_segs > (p_lcb->link_xmit_quota - p_lcb->sent_not_acked))
 80             {
 81                 num_segs = (p_lcb->link_xmit_quota - p_lcb->sent_not_acked);
 82                 p_lcb->partial_segment_being_sent = TRUE;
 83             }
 84         }
 85 
 86         p_buf->layer_specific        = num_segs;
 87 #if BLE_INCLUDED == TRUE
 88         if (p_lcb->is_ble_link)
 89         {
 90             l2cb.controller_le_xmit_window -= num_segs;
 91 
 92         }
 93         else
 94 #endif
 95         l2cb.controller_xmit_window -= num_segs;//分包占用的窗口數
 96 
 97         if (p_lcb->link_xmit_quota == 0)
 98             l2cb.round_robin_unacked += num_segs;
 99 
100         p_lcb->sent_not_acked += num_segs;
101 #if BLE_INCLUDED == TRUE
102         if (p_lcb->is_ble_link)
103         {
104             L2C_LINK_SEND_BLE_ACL_DATA(p_buf);
105         }
106         else
107 #endif
108         {
109             L2C_LINK_SEND_ACL_DATA (p_buf);//發送數據包
110         }
111     }
112 
113 #if (L2CAP_HCI_FLOW_CONTROL_DEBUG == TRUE)
114 #if (BLE_INCLUDED == TRUE)
115     if (p_lcb->is_ble_link)
116     {
117         L2CAP_TRACE_DEBUG6 ("TotalWin=%d,Hndl=0x%x,Quota=%d,Unack=%d,RRQuota=%d,RRUnack=%d",
118                 l2cb.controller_le_xmit_window,
119                 p_lcb->handle,
120                 p_lcb->link_xmit_quota, p_lcb->sent_not_acked,
121                 l2cb.round_robin_quota, l2cb.round_robin_unacked);
122     }
123     else
124 #endif
125     {
126         L2CAP_TRACE_DEBUG6 ("TotalWin=%d,Hndl=0x%x,Quota=%d,Unack=%d,RRQuota=%d,RRUnack=%d",
127                 l2cb.controller_xmit_window,
128                 p_lcb->handle,
129                 p_lcb->link_xmit_quota, p_lcb->sent_not_acked,
130                 l2cb.round_robin_quota, l2cb.round_robin_unacked);
131     }
132 #endif
133 
134     return TRUE;
135 }

l2c_link_send_to_lower 把數據交給了 L2C_LINK_SEND_ACL_DATA,L2C_LINK_SEND_ACL_DATA 實際上是 bte_main_hci_send 函數,bte_main_hci_send 函數經過調用 hci 的接口 transmit_buf 來轉送數據包。transmit_buf 函數做用比較簡單,將此數據包入 tx_q 隊列,串口(H5)的守護線程 bt_hc_worker_thread 會從 tx_q 隊列中獲取數據包,並將其發送。

1 static int transmit_buf(TRANSAC transac, char *p_buf, int len)
2 {
3     utils_enqueue(&tx_q, (void *) transac);
4 
5     bthc_signal_event(HC_EVENT_TX);
6 
7     return BT_HC_STATUS_SUCCESS;
8 }

到這裏爲止, ACL 包整個發送流程分析完了。

 

2.ACL 包接收流程:

有關 ACL 包接收的過程都是在 btu_task 這個守護線程中處理的。

咱們看到 btu_task 處理數據包的過程:

  1. 等待事件,事件到來後,若是是 TASK_MBOX_0_EVT_MASK(是否是 MBOX裏的Task),那麼從 mbox 中取出這個數據包,並判斷是什麼類型的 Event。
  2. 若是是 BT_EVT_TO_BTU_HCI_ACL,說明是 ACL 數據,交給 l2cap 來處理。
  3. 若是是 BT_EVT_TO_BTU_L2C_SEG_XMIT,說明是 L2CAP 的分包數據沒有發送完,那繼續發送分包數據。
1 //部分 btu_task 源碼
 2 ...........
 3    /* Wait for, and process, events */
 4     for (;;)
 5     {
 6         event = GKI_wait (0xFFFF, 0);
 7 
 8         if (event & TASK_MBOX_0_EVT_MASK)
 9         {
10             /* Process all messages in the queue */
11             while ((p_msg = (BT_HDR *) GKI_read_mbox (BTU_HCI_RCV_MBOX)) != NULL)
12             {
13                 /* Determine the input message type. */
14                 switch (p_msg->event & BT_EVT_MASK)
15                 {
16                     case BT_EVT_TO_BTU_HCI_ACL:
17                         /* All Acl Data goes to L2CAP */
18                         l2c_rcv_acl_data (p_msg);//咱們的 ACL 數據來了,關鍵分析這個函數
19                         break;
20 
21                     case BT_EVT_TO_BTU_L2C_SEG_XMIT:
22                         /* L2CAP segment transmit complete */
23                         l2c_link_segments_xmitted (p_msg);
24                         break;
25 
26                     case BT_EVT_TO_BTU_HCI_SCO:
27 #if BTM_SCO_INCLUDED == TRUE
28                         btm_route_sco_data (p_msg);
29                         break;
30 #endif
31 
32                     case BT_EVT_TO_BTU_HCI_EVT:
33                         btu_hcif_process_event ((UINT8)(p_msg->event & BT_SUB_EVT_MASK), p_msg);
34                         GKI_freebuf(p_msg);
35 
36 ....

l2c_rcv_acl_data 這個函數,處理收到的 ACL 包,下面咱們來分析一下 l2c_rcv_acl_data 這個函數:

  1. 在收到的 ACL 包中找出 pkt_type(分包的話要另做處理) 和 handle。

  2. 若此 ACL 包是一個完整的數據包:

    • 首先經過 handle 找到 LCB
    • rcv_cid 大於 L2CAP_BASE_APPL_CID(0x0040),說明是上層應用普通數據包,經過 CID 找到當前包的 CCB。
    • hci_len 長度確定要大於 L2CAP 頭長度,不然確定頭部出錯了。
    • 若是 rcv_cid 是 L2CAP_SIGNALLING_CID,說明數據包是 建立和創建 Channel 用的(上層應用傳輸數據),使用函數 process_l2cap_cmd 來處理。
    • 若是 rcv_cid 是 L2CAP_CONNECTIONLESS_CID 說明是 廣播或單播,使用函數 tcs_proc_bcst_msg 處理。
    • 若是 rcv_cid 是 L2CAP_BLE_SIGNALLING_CID 說明是 BLE 的signalling包,交給函數 l2cble_process_sig_cmd 處理。
    • 普通數據包,直接交給 L2CAP 的數據流狀態機 l2c_csm_execute (p_ccb, L2CEVT_L2CAP_DATA, p_msg) 來處理,找到 L2CEVT_L2CAP_DATA 的這個 case,原來經過回調,將此數據包交給上層了(如 RFCOMM,最終是交給 RFCOMM_BufDataInd函數做進一步處理)

上述數據包,咱們僅僅考慮了最普通的數據流。

 1 void l2c_rcv_acl_data (BT_HDR *p_msg)
  2 {
  3     UINT8       *p = (UINT8 *)(p_msg + 1) + p_msg->offset;
  4     UINT16      handle, hci_len;
  5     UINT8       pkt_type;
  6     tL2C_LCB    *p_lcb;
  7     tL2C_CCB    *p_ccb = NULL;
  8     UINT16      l2cap_len, rcv_cid, psm;
  9 
 10     /* Extract the handle */
 11     STREAM_TO_UINT16 (handle, p);
 12     pkt_type = HCID_GET_EVENT (handle);
 13     handle   = HCID_GET_HANDLE (handle);
 14 
 15     /* Since the HCI Transport is putting segmented packets back together, we */
 16     /* should never get a valid packet with the type set to "continuation"    */
 17     if (pkt_type != L2CAP_PKT_CONTINUE)//數據包必定要是完整的,分包另做處理
 18     {
 19         /* Find the LCB based on the handle */
 20         if ((p_lcb = l2cu_find_lcb_by_handle (handle)) == NULL)
 21         {
 22             UINT8       cmd_code;
 23 
 24             /* There is a slight possibility (specifically with USB) that we get an */
 25             /* L2CAP connection request before we get the HCI connection complete.  */
 26             /* So for these types of messages, hold them for up to 2 seconds.       */
 27             STREAM_TO_UINT16 (hci_len, p);
 28             STREAM_TO_UINT16 (l2cap_len, p);
 29             STREAM_TO_UINT16 (rcv_cid, p);
 30             STREAM_TO_UINT8  (cmd_code, p);
 31 
 32             if ((p_msg->layer_specific == 0) && (rcv_cid == L2CAP_SIGNALLING_CID)
 33                 && (cmd_code == L2CAP_CMD_INFO_REQ || cmd_code == L2CAP_CMD_CONN_REQ))
 34             {
 35                 L2CAP_TRACE_WARNING5 ("L2CAP - holding ACL for unknown handle:%d ls:%d cid:%d opcode:%d cur count:%d",
 36                                     handle, p_msg->layer_specific, rcv_cid, cmd_code,
 37                                     l2cb.rcv_hold_q.count);
 38                 p_msg->layer_specific = 2;
 39                 GKI_enqueue (&l2cb.rcv_hold_q, p_msg);//添加到隊列中,等待 connect 數據包到達,有種可能發生的 case
 40 
 41                 if (l2cb.rcv_hold_q.count == 1)
 42                     btu_start_timer (&l2cb.rcv_hold_tle, BTU_TTYPE_L2CAP_HOLD, BT_1SEC_TIMEOUT);
 43 
 44                 return;
 45             }
 46             else
 47             {
 48                 L2CAP_TRACE_ERROR5 ("L2CAP - rcvd ACL for unknown handle:%d ls:%d cid:%d opcode:%d cur count:%d",
 49                                     handle, p_msg->layer_specific, rcv_cid, cmd_code, l2cb.rcv_hold_q.count);
 50             }
 51             GKI_freebuf (p_msg);
 52             return;
 53         }
 54     }
 55     else
 56     {
 57         L2CAP_TRACE_WARNING1 ("L2CAP - expected pkt start or complete, got: %d", pkt_type);
 58         GKI_freebuf (p_msg);
 59         return;
 60     }
 61     //下面是咱們把 ACL 數據包給部分拆包了,即除掉 L2CAP 的控制部分,還原上層的數據包。
 62     /* Extract the length and update the buffer header */
 63     STREAM_TO_UINT16 (hci_len, p);
 64     p_msg->offset += 4;
 65 
 66 #if (L2CAP_HOST_FLOW_CTRL == TRUE)
 67     /* Send ack if we hit the threshold */
 68     if (++p_lcb->link_pkts_unacked >= p_lcb->link_ack_thresh)
 69         btu_hcif_send_host_rdy_for_data();
 70 #endif
 71 
 72     /* Extract the length and CID */
 73     STREAM_TO_UINT16 (l2cap_len, p);
 74     STREAM_TO_UINT16 (rcv_cid, p);
 75 
 76     /* Find the CCB for this CID */
 77     if (rcv_cid >= L2CAP_BASE_APPL_CID)// 說明此 rcv_cid 是上層應用普通數據流的 CID 
 78     {
 79         if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, rcv_cid)) == NULL)
 80         {
 81             L2CAP_TRACE_WARNING1 ("L2CAP - unknown CID: 0x%04x", rcv_cid);
 82             GKI_freebuf (p_msg);
 83             return;
 84         }
 85     }
 86 
 87     if (hci_len >= L2CAP_PKT_OVERHEAD)  //數據包長度確定要大於 Head的值,不然必然是個錯包
 88     {
 89         p_msg->len    = hci_len - L2CAP_PKT_OVERHEAD;
 90         p_msg->offset += L2CAP_PKT_OVERHEAD;
 91     }
 92     else
 93     {
 94         L2CAP_TRACE_WARNING0 ("L2CAP - got incorrect hci header" );
 95         GKI_freebuf (p_msg);
 96         return;
 97     }
 98 
 99     if (l2cap_len != p_msg->len) //長度不相等,那數據包傳送過程確定出現了差錯,丟包吧
100     {
101         L2CAP_TRACE_WARNING2 ("L2CAP - bad length in pkt. Exp: %d  Act: %d",
102                               l2cap_len, p_msg->len);
103 
104         GKI_freebuf (p_msg);
105         return;
106     }
107 
108     /* Send the data through the channel state machine */
109     if (rcv_cid == L2CAP_SIGNALLING_CID)//控制建立和創建 Channel的 signalling
110     {
111         process_l2cap_cmd (p_lcb, p, l2cap_len); //此函數專門處理這個Channel的事件
112         GKI_freebuf (p_msg);
113     }
114     else if (rcv_cid == L2CAP_CONNECTIONLESS_CID)
115     {
116         /* process_connectionless_data (p_lcb); */
117         STREAM_TO_UINT16 (psm, p);
118         L2CAP_TRACE_DEBUG1( "GOT CONNECTIONLESS DATA PSM:%d", psm ) ;
119 #if (TCS_BCST_SETUP_INCLUDED == TRUE && TCS_INCLUDED == TRUE)
120         if (psm == TCS_PSM_INTERCOM || psm == TCS_PSM_CORDLESS)
121         {
122             p_msg->offset += L2CAP_BCST_OVERHEAD;
123             p_msg->len -= L2CAP_BCST_OVERHEAD;
124             tcs_proc_bcst_msg( p_lcb->remote_bd_addr, p_msg ) ;
125             GKI_freebuf (p_msg);
126         }
127         else
128 #endif
129 
130 #if (L2CAP_UCD_INCLUDED == TRUE)
131         /* if it is not broadcast, check UCD registration */
132         if ( l2c_ucd_check_rx_pkts( p_lcb, p_msg ) )
133         {
134             /* nothing to do */
135         }
136         else
137 #endif
138             GKI_freebuf (p_msg);
139     }
140 #if (BLE_INCLUDED == TRUE)
141     else if (rcv_cid == L2CAP_BLE_SIGNALLING_CID) //LE 設備的專用 Channel
142     {
143         l2cble_process_sig_cmd (p_lcb, p, l2cap_len);
144         GKI_freebuf (p_msg);
145     }
146 #endif
147 #if (L2CAP_NUM_FIXED_CHNLS > 0)
148     else if ((rcv_cid >= L2CAP_FIRST_FIXED_CHNL) && (rcv_cid <= L2CAP_LAST_FIXED_CHNL) &&
149              (l2cb.fixed_reg[rcv_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb != NULL) )
150     {
151         /* If no CCB for this channel, allocate one */
152         if (l2cu_initialize_fixed_ccb (p_lcb, rcv_cid, &l2cb.fixed_reg[rcv_cid - L2CAP_FIRST_FIXED_CHNL].fixed_chnl_opts))
153         {
154             p_ccb = p_lcb->p_fixed_ccbs[rcv_cid - L2CAP_FIRST_FIXED_CHNL];
155 
156             if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE)
157                 l2c_fcr_proc_pdu (p_ccb, p_msg);
158             else
159                 (*l2cb.fixed_reg[rcv_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb)(p_lcb->remote_bd_addr, p_msg);
160         }
161         else
162             GKI_freebuf (p_msg);
163     }
164 #endif
165 
166     else
167     {
168         if (p_ccb == NULL)
169             GKI_freebuf (p_msg);
170         else
171         {
172             /* Basic mode packets go straight to the state machine */
173             if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_BASIC_MODE)
174             //普通的數據流都是通過這條通路的,下面這個函數以爲很熟悉吧,由於在發送數據包的時候也調用了他。
175                 l2c_csm_execute (p_ccb, L2CEVT_L2CAP_DATA, p_msg);
176             else
177             {
178                 /* eRTM or streaming mode, so we need to validate states first */
179                 if ((p_ccb->chnl_state == CST_OPEN) || (p_ccb->chnl_state == CST_CONFIG))
180                     l2c_fcr_proc_pdu (p_ccb, p_msg);
181                 else
182                     GKI_freebuf (p_msg);
183             }
184         }
185     }
186 }

l2c_link_process_num_completed_pkts這個函數都作了些什麼呢?咱們來深刻代碼瞭解一下整個過程。

  1. 經過 num_sent 來更新數據 controller_xmit_window,sent_not_acked。
  2. 既然又多了 credits(Snoop中的),那麼天然調用 l2c_link_check_send_pkts 去找更多的包發送下去。
  3. l2c_link_process_num_completed_pkts 和 l2c_link_check_send_pkts 造成了一個遞歸式的調用,l2c_link_check_send_pkts 會產生 process num complete 這個event,l2c_link_process_num_completed_pkts找到 Link後在讓 l2c_link_check_send_pkts 發包。
 1 void l2c_link_process_num_completed_pkts (UINT8 *p)
  2 {
  3     UINT8       num_handles, xx;
  4     UINT16      handle;
  5     UINT16      num_sent; //已經發下去的數據,這個數據要更新controller_xmit_window
  6     tL2C_LCB    *p_lcb;
  7 
  8     L2CAP_TRACE_DEBUG0("mike: l2c_link_process_num_completed_pkts");
  9     STREAM_TO_UINT8 (num_handles, p);
 10     L2CAP_TRACE_DEBUG1("mike: l2c_link_process_num_completed_pkts--number_handles:%d", num_handles);
 11     for (xx = 0; xx < num_handles; xx++)//handle 對應着每條邏輯鏈路(Link)
 12     {
 13         STREAM_TO_UINT16 (handle, p);
 14         STREAM_TO_UINT16 (num_sent, p);
 15 
 16         p_lcb = l2cu_find_lcb_by_handle (handle);
 17 
 18         /* Callback for number of completed packet event    */
 19         /* Originally designed for [3DSG]                   */
 20         if((p_lcb != NULL) && (p_lcb->p_nocp_cb))
 21         {
 22             L2CAP_TRACE_DEBUG0 ("L2CAP - calling NoCP callback");
 23             (*p_lcb->p_nocp_cb)(p_lcb->remote_bd_addr);
 24         }
 25 
 26         if (p_lcb)
 27         {
 28 #if (BLE_INCLUDED == TRUE)
 29             if (p_lcb->is_ble_link)
 30             {
 31                 l2cb.controller_le_xmit_window += num_sent;
 32             }
 33             else
 34 #endif
 35             {
 36                 /* Maintain the total window to the controller */
 37                 l2cb.controller_xmit_window += num_sent;
 38             }
 39             /* If doing round-robin, adjust communal counts */
 40             if (p_lcb->link_xmit_quota == 0)
 41             {
 42                 /* Don't go negative */
 43                 if (l2cb.round_robin_unacked > num_sent)
 44                     l2cb.round_robin_unacked -= num_sent;
 45                 else
 46                     l2cb.round_robin_unacked = 0;
 47             }
 48 
 49             /* Don't go negative */
 50             if (p_lcb->sent_not_acked > num_sent)
 51                 p_lcb->sent_not_acked -= num_sent; //更新
 52             else
 53                 p_lcb->sent_not_acked = 0;
 54 
 55             l2c_link_check_send_pkts (p_lcb, NULL, NULL);
 56 
 57             L2CAP_TRACE_DEBUG1("mike:l2c_link_process_num_completed_pkts--l2cb.controller_xmit_window=%d",l2cb.controller_xmit_window);
 58 
 59             /* If we were doing round-robin for low priority links, check 'em */
 60             if ( (p_lcb->acl_priority == L2CAP_PRIORITY_HIGH)
 61               && (l2cb.check_round_robin)
 62               && (l2cb.round_robin_unacked < l2cb.round_robin_quota) )
 63             {
 64               l2c_link_check_send_pkts (NULL, NULL, NULL);
 65             }
 66         }
 67 
 68 #if (L2CAP_HCI_FLOW_CONTROL_DEBUG == TRUE)
 69         if (p_lcb)
 70         {
 71 #if (BLE_INCLUDED == TRUE)
 72             if (p_lcb->is_ble_link)
 73             {
 74                 L2CAP_TRACE_DEBUG5 ("TotalWin=%d,LinkUnack(0x%x)=%d,RRCheck=%d,RRUnack=%d",
 75                     l2cb.controller_le_xmit_window,
 76                     p_lcb->handle, p_lcb->sent_not_acked,
 77                     l2cb.check_round_robin, l2cb.round_robin_unacked);
 78             }
 79             else
 80 #endif
 81             {
 82                 L2CAP_TRACE_DEBUG5 ("TotalWin=%d,LinkUnack(0x%x)=%d,RRCheck=%d,RRUnack=%d",
 83                     l2cb.controller_xmit_window,
 84                     p_lcb->handle, p_lcb->sent_not_acked,
 85                     l2cb.check_round_robin, l2cb.round_robin_unacked);
 86 
 87             }
 88         }
 89         else
 90         {
 91 #if (BLE_INCLUDED == TRUE)
 92             L2CAP_TRACE_DEBUG5 ("TotalWin=%d  LE_Win: %d, Handle=0x%x, RRCheck=%d, RRUnack=%d",
 93                 l2cb.controller_xmit_window,
 94                 l2cb.controller_le_xmit_window,
 95                 handle,
 96                 l2cb.check_round_robin, l2cb.round_robin_unacked);
 97 #else
 98             L2CAP_TRACE_DEBUG4 ("TotalWin=%d  Handle=0x%x  RRCheck=%d  RRUnack=%d",
 99                 l2cb.controller_xmit_window,
100                 handle,
101                 l2cb.check_round_robin, l2cb.round_robin_unacked);
102 #endif
103         }
104 #endif
105     }
106 
107 #if (defined(HCILP_INCLUDED) && HCILP_INCLUDED == TRUE)
108     /* only full stack can enable sleep mode */
109     btu_check_bt_sleep ();
110 #endif
111 }

ACL 包發送和接收部分源碼分析完了!

-end-

相關文章
相關標籤/搜索