Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 44d9c458 authored by Łukasz Rymanowski's avatar Łukasz Rymanowski Committed by Łukasz Rymanowski (xWF)
Browse files

l2cap: Fix using FCS field in S/I frames

As per Version 6.0 | Vol 3, Part A, 5.5 Frame Check Sequence (FCS) option

If both sides configure channel for `No FCS`, then this field shall be
skipped.

Android by default uses FCS and this fix is needed only for PTS tests
and in case other products would like to configure stack in a different
way.

Bug: 371948052
Bug: 372605959
Test: mmm packages/modules/Bluetooth
Test: manual PTS tests L2CAP/FOC/BV-05-C
Flag: com.android.bluetooth.flags.l2cap_fcs_option_fix
Change-Id: I62eeeb0e497be1819d8f83bf25997641392c022a
parent 7ac30112
Loading
Loading
Loading
Loading
+44 −29
Original line number Diff line number Diff line
@@ -280,6 +280,8 @@ static void prepare_I_frame(tL2C_CCB* p_ccb, BT_HDR* p_buf, bool is_retransmissi
  uint16_t ctrl_word;
  bool set_f_bit = p_fcrb->send_f_rsp;

  uint8_t fcs_len = l2cu_get_fcs_len(p_ccb);

  p_fcrb->send_f_rsp = false;

  if (is_retransmission) {
@@ -317,8 +319,9 @@ static void prepare_I_frame(tL2C_CCB* p_ccb, BT_HDR* p_buf, bool is_retransmissi
  /* Compute the FCS and add to the end of the buffer if not bypassed */
  /* length field in l2cap header has to include FCS length */
  p = ((uint8_t*)(p_buf + 1)) + p_buf->offset;
  UINT16_TO_STREAM(p, p_buf->len + L2CAP_FCS_LEN - L2CAP_PKT_OVERHEAD);
  UINT16_TO_STREAM(p, p_buf->len + fcs_len - L2CAP_PKT_OVERHEAD);

  if (fcs_len != 0) {
    /* Calculate the FCS */
    fcs = l2c_fcr_tx_get_fcs(p_buf);

@@ -331,7 +334,8 @@ static void prepare_I_frame(tL2C_CCB* p_ccb, BT_HDR* p_buf, bool is_retransmissi

    UINT16_TO_STREAM(p, fcs);

  p_buf->len += L2CAP_FCS_LEN;
    p_buf->len += fcs_len;
  }

  if (is_retransmission) {
    log::verbose(
@@ -397,16 +401,20 @@ void l2c_fcr_send_S_frame(tL2C_CCB* p_ccb, uint16_t function_code, uint16_t pf_b
  /* Set the pointer to the beginning of the data */
  p = (uint8_t*)(p_buf + 1) + p_buf->offset;

  uint8_t fcs_len = l2cu_get_fcs_len(p_ccb);

  /* Put in the L2CAP header */
  UINT16_TO_STREAM(p, L2CAP_FCR_OVERHEAD + L2CAP_FCS_LEN);
  UINT16_TO_STREAM(p, L2CAP_FCR_OVERHEAD + fcs_len);
  UINT16_TO_STREAM(p, p_ccb->remote_cid);
  UINT16_TO_STREAM(p, ctrl_word);

  if (fcs_len != 0) {
    /* Compute the FCS and add to the end of the buffer if not bypassed */
    fcs = l2c_fcr_tx_get_fcs(p_buf);

    UINT16_TO_STREAM(p, fcs);
  p_buf->len += L2CAP_FCS_LEN;
    p_buf->len += fcs_len;
  }

  /* Now, the HCI transport header */
  p_buf->layer_specific = L2CAP_NON_FLUSHABLE_PKT;
@@ -462,7 +470,9 @@ void l2c_fcr_proc_pdu(tL2C_CCB* p_ccb, BT_HDR* p_buf) {
  uint16_t ctrl_word;

  /* Check the length */
  min_pdu_len = (uint16_t)(L2CAP_FCS_LEN + L2CAP_FCR_OVERHEAD);
  uint8_t fcs_len = l2cu_get_fcs_len(p_ccb);

  min_pdu_len = (uint16_t)(fcs_len + L2CAP_FCR_OVERHEAD);

  if (p_buf->len < min_pdu_len) {
    log::warn("Rx L2CAP PDU: CID: 0x{:04x}  Len too short: {}", p_ccb->local_cid, p_buf->len);
@@ -514,18 +524,20 @@ void l2c_fcr_proc_pdu(tL2C_CCB* p_ccb, BT_HDR* p_buf) {
          p_ccb->fcrb.last_ack_sent, fixed_queue_length(p_ccb->fcrb.waiting_for_ack_q),
          p_ccb->fcrb.num_tries);

  if (fcs_len != 0) {
    /* Verify FCS if using */
  p = ((uint8_t*)(p_buf + 1)) + p_buf->offset + p_buf->len - L2CAP_FCS_LEN;
    p = ((uint8_t*)(p_buf + 1)) + p_buf->offset + p_buf->len - fcs_len;

    /* Extract and drop the FCS from the packet */
    STREAM_TO_UINT16(fcs, p);
  p_buf->len -= L2CAP_FCS_LEN;
    p_buf->len -= fcs_len;

    if (l2c_fcr_rx_get_fcs(p_buf) != fcs) {
      log::warn("Rx L2CAP PDU: CID: 0x{:04x}  BAD FCS", p_ccb->local_cid);
      osi_free(p_buf);
      return;
    }
  }

  /* Get the control word */
  p = ((uint8_t*)(p_buf + 1)) + p_buf->offset;
@@ -1466,6 +1478,7 @@ BT_HDR* l2c_fcr_get_next_xmit_sdu_seg(tL2C_CCB* p_ccb, uint16_t max_packet_lengt
  }

  prepare_I_frame(p_ccb, p_xmit, false);
  uint8_t fcs_len = l2cu_get_fcs_len(p_ccb);

  if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) {
    BT_HDR* p_wack = l2c_fcr_clone_buf(p_xmit, HCI_DATA_PREAMBLE_SIZE, p_xmit->len);
@@ -1475,14 +1488,14 @@ BT_HDR* l2c_fcr_get_next_xmit_sdu_seg(tL2C_CCB* p_ccb, uint16_t max_packet_lengt
                 p_xmit->len);

      /* We will not save the FCS in case we reconfigure and change options */
      p_xmit->len -= L2CAP_FCS_LEN;
      p_xmit->len -= fcs_len;

      /* Pretend we sent it and it got lost */
      fixed_queue_enqueue(p_ccb->fcrb.waiting_for_ack_q, p_xmit);
      return NULL;
    } else {
      /* We will not save the FCS in case we reconfigure and change options */
      p_wack->len -= L2CAP_FCS_LEN;
      p_wack->len -= fcs_len;

      p_wack->layer_specific = p_xmit->layer_specific;
      fixed_queue_enqueue(p_ccb->fcrb.waiting_for_ack_q, p_wack);
@@ -1793,6 +1806,8 @@ uint8_t l2c_fcr_process_peer_cfg_req(tL2C_CCB* p_ccb, tL2CAP_CFG_INFO* p_cfg) {
    }
  }

  uint8_t fcs_len = l2cu_get_fcs_len(p_ccb);

  /* Configuration for FCR channels so make any adjustments and fwd to upper
   * layer */
  if (fcr_ok == L2CAP_PEER_CFG_OK) {
@@ -1807,7 +1822,7 @@ uint8_t l2c_fcr_process_peer_cfg_req(tL2C_CCB* p_ccb, tL2CAP_CFG_INFO* p_cfg) {
      }

      max_retrans_size = BT_DEFAULT_BUFFER_SIZE - sizeof(BT_HDR) - L2CAP_MIN_OFFSET -
                         L2CAP_SDU_LEN_OFFSET - L2CAP_FCS_LEN;
                         L2CAP_SDU_LEN_OFFSET - fcs_len;

      /* Ensure the MPS is not bigger than the MTU */
      if ((p_cfg->fcr.mps == 0) || (p_cfg->fcr.mps > p_ccb->peer_cfg.mtu)) {
+1 −0
Original line number Diff line number Diff line
@@ -762,6 +762,7 @@ void l2cu_release_ble_rcb(tL2C_RCB* p_rcb);
tL2C_RCB* l2cu_allocate_ble_rcb(uint16_t psm);
tL2C_RCB* l2cu_find_ble_rcb_by_psm(uint16_t psm);

uint8_t l2cu_get_fcs_len(tL2C_CCB* p_ccb);
uint8_t l2cu_process_peer_cfg_req(tL2C_CCB* p_ccb, tL2CAP_CFG_INFO* p_cfg);
void l2cu_process_peer_cfg_rsp(tL2C_CCB* p_ccb, tL2CAP_CFG_INFO* p_cfg);
void l2cu_process_our_cfg_req(tL2C_CCB* p_ccb, tL2CAP_CFG_INFO* p_cfg);
+24 −0
Original line number Diff line number Diff line
@@ -1849,6 +1849,30 @@ tL2C_RCB* l2cu_find_ble_rcb_by_psm(uint16_t psm) {
  return NULL;
}

/*************************************************************************************
 *
 * Function         l2cu_get_fcs_len
 *
 * Description      This function is called to determine FCS len in S/I Frames.
 *
 *
 * Returns          0 or L2CAP_FCS_LEN: 0 is returned when both sides configure `No FCS`
 *
 **************************************************************************************/
uint8_t l2cu_get_fcs_len(tL2C_CCB* p_ccb) {
  log::verbose("our.fcs_present: {} our.fcs: {},  peer.fcs_present: {} peer.fcs: {}",
               p_ccb->our_cfg.fcs_present, p_ccb->our_cfg.fcs, p_ccb->peer_cfg.fcs_present,
               p_ccb->peer_cfg.fcs);

  if (com::android::bluetooth::flags::l2cap_fcs_option_fix() &&
      (p_ccb->peer_cfg.fcs_present && p_ccb->peer_cfg.fcs == 0x00) &&
      (p_ccb->our_cfg.fcs_present && p_ccb->our_cfg.fcs == 0x00)) {
    return 0;
  }

  return L2CAP_FCS_LEN;
}

/*******************************************************************************
 *
 * Function         l2cu_process_peer_cfg_req