Bluedroid协议栈L2CAP连接源码分析

温馨提示:

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. 释放或继续处理数据包:

  • 如果数据包被成

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值