本文分析 L2CAP 底層的數據包發送函數。
l2c_link_check_send_pkts (tL2C_LCB p_lcb, tL2C_CCB p_ccb, BT_HDR *p_buf) 函數的主要作用是:
- 如果 p_buf 不為 null,p_ccb 為null(在signaling狀態中,通道是Fixed的) 說明這個 ACL 包是這類 L2CAP 的 command:command reject、configuration request、connection request、connection response、connection response neg、configuration response、configuration reject、disconnect response、echo request、echo response、info request、info response、disconnect request,或者說明這個 ACL 包是 S_frame(Supervisory Frame), 目前 Bluedroid 中只有這兩種情況;然後把這個包放到 p_lcb->link_xmit_data_q 中,注意是當前 Link 上的 link_xmit_data_q,跟 CCB 中的 xmit_hold_q 隊列是完全不同的兩個隊列。
- 檢查 l2cb.is_cong_cback_context 欄位,如果為 TRUE,說明當前 Link 擁堵,當前髮送過程結束,注意,這時候 ACL 包都已經進入隊列了,都在等待發送(即在此調用本函數)。
- 若 p_lcb 不為 null 或 p_lcb->link_xmit_quota 不為0:
- 首先檢查 p_lcb 的 partial_segment_being_sent(為TRUE,說明 Segment還沒發完,底層來保證,所以當前函數返回)
- p_lcb 的 link_state 不為 LST_CONNECTED,說明當前 Link 異常,當前函數返回
- L2C_LINK_CHECK_POWER_MODE 默認是關閉的,關於電源管理的,暫時不分析
- 步驟 1 分析知道,我們現在發送的是 當前 Link 上的 link_xmit_data_q 隊列裡的包,對於 BR/EDR來說,如果條件 l2cb.controller_xmit_window 不為 0(否則沒有窗口發送數據包了)並且 p_lcb->sent_not_acked < p_lcb->link_xmit_quota(沒回覆的包一定要小於當前Link最大發送值),不成立,那麼在這裡輪詢,直到有了發送窗口並且 sent_not_acked < link_xmit_quota 才將數據包交給 l2c_link_send_to_lower 函數做進一步處理。
- 這時,Link 上的數據包已經發送了,若 single_write(斷開前最後CCB中的數據處理標誌)為false,我們要繼續去看看當前 Link 上的每個 Channel 上的 Queue 中有沒有數據包,一旦找到一個 p_buf 數據包,就發送(不會循環找,找到了就返回)
OK,上面流程 1 -> 3 是一個單獨的流程, 處理一些 L2CAP command 或 Response 的發送情況。
下面偶在來分析另外一個 case,這三個參數全部為非空的情況,這種情況只在 L2CAP 發送 disconnect request 命令時出現(關閉當前 Link 要確保對應上層應用的 CCB 中的數據要發完),主要作用是:
- 在函數 l2cu_send_peer_disc_req 中我們看到 CCB 中的數據包直接從 CCB 的 xmit_hold_q 中出隊列,設置 ACL 包頭就直接交給 l2c_link_check_send_pkts(p_ccb->p_lcb, p_ccb, p_buf2)發送了。
- 看到 l2c_link_check_send_pkts 的三個參數均不為 null,說明我們把當前的 CCB 中 xmit_hold_q 的數據包 當成 Link 上的 link_xmit_data_q 發送出去了(相當於優先發送了),single_write 是一個標誌位,代表當前數據包由 CCB -> LCB 代替發送。由於判斷了 single_write 這個標誌位,程序不會再去 CCB 中找數據包了。
還有一種 case 是全部為空的情況,這是觸發了 RR 機制所致,正常情況還有發送窗口的情況下,會遍歷每個 Link ,然後先發當前 Link上的 Queue裡的數據發送出去,然後再發送 CCB 中 Queue 的數據。
OK,這個函數還剩最後一個 case,就是正常情況下,我們聽音樂的數據流發送的情況,這是最常見的一種情況,調用形式為 l2c_link_check_send_pkts (p_lcb, NULL, NULL)。
- 判斷當前 Link 是否擁堵。
- 首先看看 Link 上的 link_xmit_data_q 隊列有沒有數據,有的話發送
- 如果 link_xmit_data_q 沒有數據,在看看 Link 上的 CCB,通過 CCB 的鏈表,找到 CCB 上的隊列,一直找,直到找到一個 數據包,發送之。
- 注意:上層在發送數據包,僅僅是調用一下這個函數,至於 是不是這個數據包,那不一定;反正每次上層調用都會發送一個,上層發下來的包總會發出去的。
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 主要做了這些事情:
- 如果當前數據包 p_buf 的長度小於 ACL 包的最大值,sent_not_acked 加1,整個 L2CAP 的 controller_xmit_window 減1。然後通過 L2C_LINK_SEND_ACL_DATA 將此數據包發送出去。
- 如果當前數據包 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 包整個發送流程分析完了。
沒有留言:
張貼留言