ACL 鏈路在 Bluetooth 中非常重要,一些重要的應用如 A2DP, 基於 RFCOMM 的應用,BNEP等都要建立 ACL 鏈路,發送/接收ACL 包。Mike 跟大家一起來分析 ACL 包發送/接收流程,以及涉及到的重要 command/event。
ACL包發送
下面的圖(點擊大圖)是各種應用層使用 L2CAP 的 API:L2CA_DataWrite 發送數據流的過程,此API繼續往下走,我僅分析了正常數據流的走向(暫時沒有考慮別的情況)。
應用層數據到 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 這個函數做的事情主要有:
- 根據參數 cid(Channel ID) 找到 對應的 ccb(Channel Control Block), 找不到返回L2CAP_DW_FAILED
- 如果測試者 打開 TESTER 這個宏,發送任意數據,當數據大小 大於 MTU 最大值,也會返回 L2CAP_DW_FAILED
- 通過檢查 p_ccb->cong_sent 欄位,TRUE,則說明當前 Channel 已經擁擠,此時L2CAP的這個Channel不在接收數據,返回 L2CAP_DW_FAILED
- 以上三個條件都通過,說明數據可發送,將數據通過 l2c_csm_execute 繼續處理。進入 l2c_csm_execute 函數,標誌著這筆數據已經成功交給 l2CAP 來處理,與上層已經沒有關係了。
- 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 層的處理
沒有留言:
張貼留言