温馨提示:
L2CAP的连接流程分析之前的文章有分析过,请先阅读传统蓝牙L2CAP的连接流程(以被动连接ATT为例)-CSDN博客
本篇主要分析L2CAP连接部分的源码。
1. 源码分析
1.1. ACL数据包接收流程
在蓝牙通信中,ACL数据包是用户数据的主要载体,通过HCI层从BTC传输到蓝牙HOST。ACL数据包的接收流程大致如下:
1. 事件监听与数据包接收:
-
蓝牙主机中的守护线程会不断监听来自HCI层的事件。
-
当HCI层接收到ACL数据包时,它会通过某种机制(如中断、消息队列等)通知btu_task。
2. 事件处理与数据包识别:
-
btu_task收到事件后,会检查事件的类型。
-
如果事件是ACL数据包到达的事件,则会进一步处理这个数据包。
3. 数据包解析与分发:
-
对于ACL数据包,会调用acl_rcv_acl_data回调函数来处理。
-
对ACL数据包进行解析,提取出L2CAP层的数据。
-
根据L2CAP层的数据(如CID,即Channel ID),将数据包分发到相应的通道或上层协议进行处理。
4. 上层协议处理:
-
上层协议(如RFCOMM、SDP等)接收到来自L2CAP层的数据后,会根据自己的协议规范进行进一步的处理。
-
这些处理可能包括数据的解码、重组、验证等。
5. 数据应用:
-
最终,处理后的数据会被传递给应用程序,供其使用。
1.2.L2CAP Connection
1.2.1. L2CAP Connection Request with ATT PSM
a. acl_rcv_acl_data
/packages/modules/Bluetooth/system/stack/acl/btm_acl.cc
void acl_rcv_acl_data(BT_HDR* p_msg) {
acl_header_t acl_header{ //初始化acl头
.handle = HCI_INVALID_HANDLE,
.hci_len = 0,
};
const uint8_t* p = (uint8_t*)(p_msg + 1) + p_msg->offset; //跳过消息头中的某些部
STREAM_TO_UINT16(acl_header.handle, p); //从字节流中安全地读取数值
acl_header.handle = HCID_GET_HANDLE(acl_header.handle);
power_telemetry::GetInstance().LogRxAclPktData(p_msg->len);
STREAM_TO_UINT16(acl_header.hci_len, p); //读取HCI层数据长度
if (acl_header.hci_len < L2CAP_PKT_OVERHEAD ||
acl_header.hci_len != p_msg->len - sizeof(acl_header)) { //验证数据长度
log::warn("Received mismatched hci header length:{} data_len:{}",
acl_header.hci_len, p_msg->len - sizeof(acl_header));
osi_free(p_msg);
return;
}
l2c_rcv_acl_data(p_msg);//将处理好的消息传递给L2CAP层进行进一步的处理
}
处理蓝牙ACL数据包的接收逻辑,主要职责是接收ACL数据包,解析其头部信息,验证数据长度,并在验证通过后将其传递给L2CAP层进行后续处理。
b. l2c_rcv_acl_data
/packages/modules/Bluetooth/system/stack/l2cap/l2c_main.cc
void l2c_rcv_acl_data(BT_HDR* p_msg) {
uint8_t* p = (uint8_t*)(p_msg + 1) + p_msg->offset;
/* Extract the handle */
uint16_t handle;
STREAM_TO_UINT16(handle, p);
uint8_t pkt_type = HCID_GET_EVENT(handle);
handle = HCID_GET_HANDLE(handle);
/* Since the HCI Transport is putting segmented packets back together, we */
/* should never get a valid packet with the type set to "continuation" */
if (pkt_type == L2CAP_PKT_CONTINUE) { //数据包一定要是完整的,分包另作处理
log::warn("L2CAP - received packet continuation");
osi_free(p_msg);
return;
}
uint16_t hci_len;
STREAM_TO_UINT16(hci_len, p);
if (hci_len < L2CAP_PKT_OVERHEAD || hci_len != p_msg->len - 4) {
/* Remote-declared packet size must match HCI_ACL size - ACL header (4) */
log::warn("L2CAP - got incorrect hci header");
osi_free(p_msg);
return;
}
uint16_t l2cap_len, rcv_cid;
STREAM_TO_UINT16(l2cap_len, p);
STREAM_TO_UINT16(rcv_cid, p);
/* Find the LCB based on the handle */
tL2C_LCB* p_lcb = l2cu_find_lcb_by_handle(handle);
if (!p_lcb) {
log::error("L2CAP - rcvd ACL for unknown handle:{} ls:{} cid:{}", handle,
p_msg->layer_specific, rcv_cid);
osi_free(p_msg);
return;
}
/* Update the buffer header */
p_msg->offset += 4;
/* for BLE channel, always notify connection when ACL data received on the
* link */
if (p_lcb && p_lcb->transport == BT_TRANSPORT_LE &&
p_lcb->link_state != LST_DISCONNECTING) {
/* only process fixed channel data as channel open indication when link is
* not in disconnecting mode */
l2cble_notify_le_connection(p_lcb->remote_bd_addr);
}
/* Find the CCB for this CID */
tL2C_CCB* p_ccb = NULL;
if (rcv_cid >= L2CAP_BASE_APPL_CID) { // 说明此 rcv_cid 是上层应用普通数据流的 CID
p_ccb = l2cu_find_ccb_by_cid(p_lcb, rcv_cid);
if (!p_ccb) {
log::warn("L2CAP - unknown CID: 0x{:04x}", rcv_cid);
osi_free(p_msg);
return;
}
}
p_msg->len = hci_len - L2CAP_PKT_OVERHEAD;
p_msg->offset += L2CAP_PKT_OVERHEAD;
if (l2cap_len != p_msg->len) { //长度不相等,那数据包传送过程肯定出现了差错,丢包吧
log::warn("L2CAP - bad length in pkt. Exp: {} Act: {}", l2cap_len,
p_msg->len);
osi_free(p_msg);
return;
}
/* Send the data through the channel state machine */
if (rcv_cid == L2CAP_SIGNALLING_CID) { //控制创建和建立 Channel的 signalling
process_l2cap_cmd(p_lcb, p, l2cap_len); //此函数专门处理这个Channel的事件
osi_free(p_msg);
return;
}
if (rcv_cid == L2CAP_CONNECTIONLESS_CID) {
/* process_connectionless_data (p_lcb); */
osi_free(p_msg);
return;
}
if (rcv_cid == L2CAP_BLE_SIGNALLING_CID) {//LE 设备的专用 Channel
l2cble_process_sig_cmd(p_lcb, p, l2cap_len);
osi_free(p_msg);
return;
}
if ((rcv_cid >= L2CAP_FIRST_FIXED_CHNL) &&
(rcv_cid <= L2CAP_LAST_FIXED_CHNL) &&
(l2cb.fixed_reg[rcv_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb !=
NULL)) {
/* only process fixed channel data when link is open or wait for data
* indication */
if (!p_lcb || p_lcb->link_state == LST_DISCONNECTING ||
!l2cu_initialize_fixed_ccb(p_lcb, rcv_cid)) {
osi_free(p_msg);
return;
}
/* If no CCB for this channel, allocate one */
p_ccb = p_lcb->p_fixed_ccbs[rcv_cid - L2CAP_FIRST_FIXED_CHNL];
p_ccb->metrics.rx(p_msg->len);
if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE)
l2c_fcr_proc_pdu(p_ccb, p_msg);
else
(*l2cb.fixed_reg[rcv_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb)(
rcv_cid, p_lcb->remote_bd_addr, p_msg);
return;
}
if (!p_ccb) {
osi_free(p_msg);
return;
}
if (p_lcb->transport == BT_TRANSPORT_LE) {
l2c_lcc_proc_pdu(p_ccb, p_msg);
/* The remote device has one less credit left */
--p_ccb->remote_credit_count;
/* If the credits left on the remote device are getting low, send some */
if (p_ccb->remote_credit_count <= L2CA_LeCreditThreshold()) {
uint16_t credits = L2CA_LeCreditDefault() - p_ccb->remote_credit_count;
p_ccb->remote_credit_count = L2CA_LeCreditDefault();
/* Return back credits */
l2c_csm_execute(p_ccb, L2CEVT_L2CA_SEND_FLOW_CONTROL_CREDIT, &credits);
}
} else {
/* Basic mode packets go straight to the state machine */
if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_BASIC_MODE)
l2c_csm_execute(p_ccb, L2CEVT_L2CAP_DATA, p_msg); //普通的数据流都是经过这条通路的,下在发送数据包的时候也调用了他
else {
/* eRTM or streaming mode, so we need to validate states first */
if ((p_ccb->chnl_state == CST_OPEN) || (p_ccb->chnl_state == CST_CONFIG))
l2c_fcr_proc_pdu(p_ccb, p_msg);
else
osi_free(p_msg);
}
}
}
l2c_rcv_acl_data负责解析ACL数据包、验证数据包的合法性、查找相应的连接和通道控制块,并将数据传递给相应的处理函数。
1. 提取数据包信息;
2. 验证数据包:
-
检查包类型是否为继续包(L2CAP_PKT_CONTINUE),如果是,则释放数据包。
-
验证HCI层长度是否合法(即是否大于L2CAP包开销且等于HCI_ACL包大小减去ACL头部大小)。
-
验证L2CAP层长度是否与HCI层长度减去L2CAP包开销后的长度一致。
3. 查找LCB(L2CAP连接块):
-
使用连接句柄handle查找对应的LCB。如果找不到,释放数据包。
4. 处理BLE连接:
-
如果LCB存在且为BLE传输,且连接状态不是正在断开,则通知BLE连接。
5. 查找CCB(L2CAP通道控制块):
-
根据接收到的CID查找对应的CCB。如果CID是信号CID、无连接CID或BLE信号CID,则直接处理相应的命令。
-
如果CID是固定通道CID,并且存在处理该CID的回调函数,则调用该回调函数处理数据。
6. 处理数据:
-
如果找到了CCB,根据传输类型和流控制模式(基本模式、eRTM或流模式),将数据传递给相应的处理函数。
-
对于BLE传输,如果远程设备的信用计数较低(remote_credit_count),则发送信用以允许更多数据传输。
7. 释放或继续处理数据包:
-
如果数据包被成