2015年1月21日 星期三

bluedroid源碼分析之ACL包發送和接收(一)(轉)

原網址 http://stackvoid.com/ACL-Send-Recv-Data_1/

ACL 鏈路在 Bluetooth 中非常重要,一些重要的應用如 A2DP, 基於 RFCOMM 的應用,BNEP等都要建立 ACL 鏈路,發送/接收ACL 包。Mike 跟大家一起來分析 ACL 包發送/接收流程,以及涉及到的重要 command/event。



ACL包發送

下面的圖(點擊大圖)是各種應用層使用 L2CAP 的 API:L2CA_DataWrite 發送數據流的過程,此API繼續往下走,我僅分析了正常數據流的走向(暫時沒有考慮別的情況)。
ACL_01


應用層數據到 L2CAP 的入口

我們假設一個聽音樂的場景,Mike跟大家一起分析音樂數據流 AVDTP 以下層的傳送。
在 AVDTP 中,所有的功能想發送 Data,必須調用 avdt_ad_write_req 這個函數,Mike 就從這個函數入手分析。

 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 這個函數。

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 層的處理

沒有留言:

張貼留言