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

Commit 629d585c authored by Łukasz Rymanowski's avatar Łukasz Rymanowski Committed by Gerrit Code Review
Browse files

Merge "gatt: Fix handling MTU Exchange request"

parents 361c3b18 e778518d
Loading
Loading
Loading
Loading
+58 −14
Original line number Diff line number Diff line
@@ -62,10 +62,6 @@ static void bta_gattc_conn_cback(tGATT_IF gattc_if, const RawAddress& bda,
static void bta_gattc_cmpl_cback(uint16_t conn_id, tGATTC_OPTYPE op,
                                 tGATT_STATUS status,
                                 tGATT_CL_COMPLETE* p_data);
static void bta_gattc_cmpl_sendmsg(uint16_t conn_id, tGATTC_OPTYPE op,
                                   tGATT_STATUS status,
                                   tGATT_CL_COMPLETE* p_data);

static void bta_gattc_deregister_cmpl(tBTA_GATTC_RCB* p_clreg);
static void bta_gattc_enc_cmpl_cback(tGATT_IF gattc_if, const RawAddress& bda);
static void bta_gattc_cong_cback(uint16_t conn_id, bool congested);
@@ -742,6 +738,42 @@ void bta_gattc_restart_discover(tBTA_GATTC_CLCB* p_clcb,

/** Configure MTU size on the GATT connection */
void bta_gattc_cfg_mtu(tBTA_GATTC_CLCB* p_clcb, const tBTA_GATTC_DATA* p_data) {
  uint16_t current_mtu = 0;
  auto result = GATTC_TryMtuRequest(p_clcb->bda, p_clcb->transport,
                                    p_clcb->bta_conn_id, &current_mtu);
  switch (result) {
    case MTU_EXCHANGE_DEVICE_DISCONNECTED:
      LOG_INFO("Device %s disconnected", ADDRESS_TO_LOGGABLE_CSTR(p_clcb->bda));
      bta_gattc_cmpl_sendmsg(p_clcb->bta_conn_id, GATTC_OPTYPE_CONFIG,
                             GATT_NO_RESOURCES, NULL);
      bta_gattc_continue(p_clcb);
      return;
    case MTU_EXCHANGE_NOT_ALLOWED:
      LOG_INFO("Not allowed for BR/EDR devices %s",
               ADDRESS_TO_LOGGABLE_CSTR(p_clcb->bda));
      bta_gattc_cmpl_sendmsg(p_clcb->bta_conn_id, GATTC_OPTYPE_CONFIG,
                             GATT_ERR_UNLIKELY, NULL);
      bta_gattc_continue(p_clcb);
      return;
    case MTU_EXCHANGE_ALREADY_DONE:
      /* Check if MTU is not already set, if so, just report it back to the user
       * and continue with other requests.
       */
      GATTC_UpdateUserAttMtuIfNeeded(p_clcb->bda, p_clcb->transport,
                                     p_data->api_mtu.mtu);
      bta_gattc_send_mtu_response(p_clcb, p_data, current_mtu);
      return;
    case MTU_EXCHANGE_IN_PROGRESS:
      LOG_INFO("Enqueue MTU Request  - waiting for response on p_clcb %p",
               p_clcb);
      bta_gattc_enqueue(p_clcb, p_data);
      return;

    case MTU_EXCHANGE_NOT_DONE_YET:
      /* OK to proceed */
      break;
  }

  if (bta_gattc_enqueue(p_clcb, p_data) == ENQUEUED_FOR_LATER) return;

  tGATT_STATUS status =
@@ -1171,18 +1203,31 @@ void bta_gattc_op_cmpl(tBTA_GATTC_CLCB* p_clcb, const tBTA_GATTC_DATA* p_data) {
  }

  /* service handle change void the response, discard it */
  if (op == GATTC_OPTYPE_READ)
  if (op == GATTC_OPTYPE_READ) {
    bta_gattc_read_cmpl(p_clcb, &p_data->op_cmpl);

  else if (op == GATTC_OPTYPE_WRITE)
  } else if (op == GATTC_OPTYPE_WRITE) {
    bta_gattc_write_cmpl(p_clcb, &p_data->op_cmpl);

  else if (op == GATTC_OPTYPE_EXE_WRITE)
  } else if (op == GATTC_OPTYPE_EXE_WRITE) {
    bta_gattc_exec_cmpl(p_clcb, &p_data->op_cmpl);

  else if (op == GATTC_OPTYPE_CONFIG)
  } else if (op == GATTC_OPTYPE_CONFIG) {
    bta_gattc_cfg_mtu_cmpl(p_clcb, &p_data->op_cmpl);

    /* If there are more clients waiting for the MTU results on the same device,
     * lets trigger them now.
     */

    auto outstanding_conn_ids =
        GATTC_GetAndRemoveListOfConnIdsWaitingForMtuRequest(p_clcb->bda);
    for (auto conn_id : outstanding_conn_ids) {
      tBTA_GATTC_CLCB* p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id);
      LOG_DEBUG("Continue MTU request clcb %p", p_clcb);
      if (p_clcb) {
        LOG_DEBUG("Continue MTU request for client conn_id=0x%04x", conn_id);
        bta_gattc_continue(p_clcb);
      }
    }
  }

  // If receive DATABASE_OUT_OF_SYNC error code, bta_gattc should start service
  // discovery immediately
  if (bta_gattc_is_robust_caching_enabled() &&
@@ -1542,9 +1587,8 @@ static void bta_gattc_cmpl_cback(uint16_t conn_id, tGATTC_OPTYPE op,
}

/** client operation complete send message */
static void bta_gattc_cmpl_sendmsg(uint16_t conn_id, tGATTC_OPTYPE op,
                                   tGATT_STATUS status,
                                   tGATT_CL_COMPLETE* p_data) {
void bta_gattc_cmpl_sendmsg(uint16_t conn_id, tGATTC_OPTYPE op,
                            tGATT_STATUS status, tGATT_CL_COMPLETE* p_data) {
  const size_t len = sizeof(tBTA_GATTC_OP_CMPL) + sizeof(tGATT_CL_COMPLETE);
  tBTA_GATTC_OP_CMPL* p_buf = (tBTA_GATTC_OP_CMPL*)osi_calloc(len);

+6 −0
Original line number Diff line number Diff line
@@ -445,6 +445,12 @@ extern BtaEnqueuedResult_t bta_gattc_enqueue(tBTA_GATTC_CLCB* p_clcb,
extern bool bta_gattc_is_data_queued(tBTA_GATTC_CLCB* p_clcb,
                                     const tBTA_GATTC_DATA* p_data);
extern void bta_gattc_continue(tBTA_GATTC_CLCB* p_clcb);
extern void bta_gattc_send_mtu_response(tBTA_GATTC_CLCB* p_clcb,
                                        const tBTA_GATTC_DATA* p_data,
                                        uint16_t current_mtu);
extern void bta_gattc_cmpl_sendmsg(uint16_t conn_id, tGATTC_OPTYPE op,
                                   tGATT_STATUS status,
                                   tGATT_CL_COMPLETE* p_data);

extern bool bta_gattc_check_notif_registry(tBTA_GATTC_RCB* p_clreg,
                                           tBTA_GATTC_SERV* p_srcb,
+63 −7
Original line number Diff line number Diff line
@@ -359,6 +359,27 @@ tBTA_GATTC_SERV* bta_gattc_srcb_alloc(const RawAddress& bda) {
  return p_tcb;
}

void bta_gattc_send_mtu_response(tBTA_GATTC_CLCB* p_clcb,
                                 const tBTA_GATTC_DATA* p_data,
                                 uint16_t current_mtu) {
  GATT_CONFIGURE_MTU_OP_CB cb = p_data->api_mtu.mtu_cb;
  if (cb) {
    void* my_cb_data = p_data->api_mtu.mtu_cb_data;
    cb(p_clcb->bta_conn_id, GATT_SUCCESS, my_cb_data);
  }

  tBTA_GATTC cb_data;
  p_clcb->status = GATT_SUCCESS;
  cb_data.cfg_mtu.conn_id = p_clcb->bta_conn_id;
  cb_data.cfg_mtu.status = GATT_SUCCESS;

  cb_data.cfg_mtu.mtu = current_mtu;

  if (p_clcb->p_rcb) {
    (*p_clcb->p_rcb->p_cback)(BTA_GATTC_CFG_MTU_EVT, &cb_data);
  }
}

void bta_gattc_continue(tBTA_GATTC_CLCB* p_clcb) {
  if (p_clcb->p_q_cmd != NULL) {
    LOG_INFO("Already scheduled another request for conn_id = 0x%04x",
@@ -366,14 +387,49 @@ void bta_gattc_continue(tBTA_GATTC_CLCB* p_clcb) {
    return;
  }

  if (p_clcb->p_q_cmd_queue.empty()) {
    LOG_INFO("Nothing to do for conn_id = 0x%04x", p_clcb->bta_conn_id);
  while (!p_clcb->p_q_cmd_queue.empty()) {
    const tBTA_GATTC_DATA* p_q_cmd = p_clcb->p_q_cmd_queue.front();
    if (p_q_cmd->hdr.event != BTA_GATTC_API_CFG_MTU_EVT) {
      p_clcb->p_q_cmd_queue.pop_front();
      bta_gattc_sm_execute(p_clcb, p_q_cmd->hdr.event, p_q_cmd);
      return;
    }

  const tBTA_GATTC_DATA* p_q_cmd = p_clcb->p_q_cmd_queue.front();
    /* The p_q_cmd is the MTU Request event. */
    uint16_t current_mtu = 0;
    auto result = GATTC_TryMtuRequest(p_clcb->bda, p_clcb->transport,
                                      p_clcb->bta_conn_id, &current_mtu);
    switch (result) {
      case MTU_EXCHANGE_DEVICE_DISCONNECTED:
        bta_gattc_cmpl_sendmsg(p_clcb->bta_conn_id, GATTC_OPTYPE_CONFIG,
                               GATT_NO_RESOURCES, NULL);
        /* Handled, free command below and continue with a p_q_cmd_queue */
        break;
      case MTU_EXCHANGE_NOT_ALLOWED:
        bta_gattc_cmpl_sendmsg(p_clcb->bta_conn_id, GATTC_OPTYPE_CONFIG,
                               GATT_ERR_UNLIKELY, NULL);
        /* Handled, free command below and continue with a p_q_cmd_queue */
        break;
      case MTU_EXCHANGE_ALREADY_DONE:
        bta_gattc_send_mtu_response(p_clcb, p_q_cmd, current_mtu);
        /* Handled, free command below and continue with a p_q_cmd_queue */
        break;
      case MTU_EXCHANGE_IN_PROGRESS:
        LOG_WARN("Waiting p_clcb %p", p_clcb);
        return;
      case MTU_EXCHANGE_NOT_DONE_YET:
        p_clcb->p_q_cmd_queue.pop_front();
        bta_gattc_sm_execute(p_clcb, p_q_cmd->hdr.event, p_q_cmd);
        return;
    }

    /* p_q_cmd was the MTU request and it was handled.
     * If MTU request was handled without actually ATT request,
     * it is ok to take another message from the queue and proceed.
     */
    p_clcb->p_q_cmd_queue.pop_front();
    osi_free_and_reset((void**)&p_q_cmd);
  }
}

bool bta_gattc_is_data_queued(tBTA_GATTC_CLCB* p_clcb,
+133 −4
Original line number Diff line number Diff line
@@ -713,15 +713,144 @@ tGATT_STATUS GATTC_ConfigureMTU(uint16_t conn_id, uint16_t mtu) {

  /* For this request only ATT CID is valid */
  p_clcb->cid = L2CAP_ATT_CID;
  p_clcb->p_tcb->payload_size = mtu;
  p_clcb->operation = GATTC_OPTYPE_CONFIG;
  tGATT_CL_MSG gatt_cl_msg;
  gatt_cl_msg.mtu = mtu;
  LOG_DEBUG("Configuring ATT mtu size conn_id:%hu mtu:%hu", conn_id, mtu);

  bluetooth::shim::arbiter::GetArbiter().OnOutgoingMtuReq(tcb_idx);

  return attp_send_cl_msg(*p_clcb->p_tcb, p_clcb, GATT_REQ_MTU, &gatt_cl_msg);
  /* Since GATT MTU Exchange can be done only once, and it is impossible to
   * predict what MTU will be requested by other applications, let's use
   * max possible MTU in the request. */
  gatt_cl_msg.mtu = GATT_MAX_MTU_SIZE;

  LOG_INFO("Configuring ATT mtu size conn_id:%hu mtu:%hu user mtu %hu", conn_id,
           gatt_cl_msg.mtu, mtu);

  auto result =
      attp_send_cl_msg(*p_clcb->p_tcb, p_clcb, GATT_REQ_MTU, &gatt_cl_msg);
  if (result == GATT_SUCCESS) {
    p_clcb->p_tcb->pending_user_mtu_exchange_value = mtu;
  }
  return result;
}

/******************************************************************************
 *
 * Function         GATTC_TryMtuRequest
 *
 * Description      This function shall be called before calling
 *                  GATTC_ConfgureMTU in order to check if operation is
 *                  available to do.
 *
 * Parameters        remote_bda : peer device address. (input)
 *                   transport  : physical transport of the GATT connection
 *                                 (BR/EDR or LE) (input)
 *                   conn_id    : connection id  (input)
 *                   current_mtu: current mtu on the link (output)
 *
 * Returns          tGATTC_TryMtuRequestResult:
 *                  - MTU_EXCHANGE_NOT_DONE_YET: There was no MTU Exchange
 *                      procedure on the link. User can call GATTC_ConfigureMTU
 *                      now.
 *                  - MTU_EXCHANGE_NOT_ALLOWED : Not allowed for BR/EDR or if
 *                      link does not exist
 *                  - MTU_EXCHANGE_ALREADY_DONE: MTU Exchange is done. MTU
 *                      should be taken from current_mtu
 *                  - MTU_EXCHANGE_IN_PROGRESS : Other use is doing MTU
 *                      Exchange. Conn_id is stored for result.
 *
 ******************************************************************************/
tGATTC_TryMtuRequestResult GATTC_TryMtuRequest(const RawAddress& remote_bda,
                                               tBT_TRANSPORT transport,
                                               uint16_t conn_id,
                                               uint16_t* current_mtu) {
  LOG_INFO("%s conn_id=0x%04x", remote_bda.ToString().c_str(), conn_id);
  *current_mtu = GATT_DEF_BLE_MTU_SIZE;

  if (transport == BT_TRANSPORT_BR_EDR) {
    LOG_ERROR("Device %s connected over BR/EDR",
              ADDRESS_TO_LOGGABLE_CSTR(remote_bda));
    return MTU_EXCHANGE_NOT_ALLOWED;
  }

  tGATT_TCB* p_tcb = gatt_find_tcb_by_addr(remote_bda, transport);
  if (!p_tcb) {
    LOG_ERROR("Device %s is not connected ",
              ADDRESS_TO_LOGGABLE_CSTR(remote_bda));
    return MTU_EXCHANGE_DEVICE_DISCONNECTED;
  }

  if (gatt_is_pending_mtu_exchange(p_tcb)) {
    LOG_DEBUG("Continue MTU pending for other client.");
    /* MTU Exchange is in progress, started by other GATT Client.
     * Wait until it is completed.
     */
    gatt_set_conn_id_waiting_for_mtu_exchange(p_tcb, conn_id);
    return MTU_EXCHANGE_IN_PROGRESS;
  }

  uint16_t mtu = gatt_get_mtu(remote_bda, transport);
  if (mtu == GATT_DEF_BLE_MTU_SIZE || mtu == 0) {
    LOG_DEBUG("MTU not yet updated for %s",
              ADDRESS_TO_LOGGABLE_CSTR(remote_bda));
    return MTU_EXCHANGE_NOT_DONE_YET;
  }

  *current_mtu = mtu;
  return MTU_EXCHANGE_ALREADY_DONE;
}

/*******************************************************************************
 * Function         GATTC_UpdateUserAttMtuIfNeeded
 *
 * Description      This function to be called when user requested MTU after
 *                  MTU Exchange has been already done. This will update data
 *                  length in the controller.
 *
 * Parameters        remote_bda : peer device address. (input)
 *                   transport  : physical transport of the GATT connection
 *                                 (BR/EDR or LE) (input)
 *                   user_mtu: user request mtu
 *
 ******************************************************************************/
void GATTC_UpdateUserAttMtuIfNeeded(const RawAddress& remote_bda,
                                    tBT_TRANSPORT transport,
                                    uint16_t user_mtu) {
  LOG_INFO("%s, mtu=%hu", ADDRESS_TO_LOGGABLE_CSTR(remote_bda), user_mtu);
  tGATT_TCB* p_tcb = gatt_find_tcb_by_addr(remote_bda, transport);
  if (!p_tcb) {
    LOG_WARN("Transport control block not found");
    return;
  }

  LOG_INFO("%s, current mtu: %d, max_user_mtu:%d, user_mtu: %d",
           ADDRESS_TO_LOGGABLE_CSTR(remote_bda), p_tcb->payload_size,
           p_tcb->max_user_mtu, user_mtu);

  if (p_tcb->payload_size < user_mtu) {
    LOG_INFO("User requested more than what GATT can handle. Trim it.");
    user_mtu = p_tcb->payload_size;
  }

  if (p_tcb->max_user_mtu >= user_mtu) {
    return;
  }

  p_tcb->max_user_mtu = user_mtu;
  BTM_SetBleDataLength(remote_bda, user_mtu);
}

std::list<uint16_t> GATTC_GetAndRemoveListOfConnIdsWaitingForMtuRequest(
    const RawAddress& remote_bda) {
  std::list result = std::list<uint16_t>();

  tGATT_TCB* p_tcb = gatt_find_tcb_by_addr(remote_bda, BT_TRANSPORT_LE);
  if (!p_tcb || p_tcb->conn_ids_waiting_for_mtu_exchange.empty()) {
    return result;
  }

  result.swap(p_tcb->conn_ids_waiting_for_mtu_exchange);
  return result;
}

/*******************************************************************************
+24 −6
Original line number Diff line number Diff line
@@ -1099,14 +1099,32 @@ void gatt_process_mtu_rsp(tGATT_TCB& tcb, tGATT_CLCB* p_clcb, uint16_t len,
  } else {
    STREAM_TO_UINT16(mtu, p_data);

    if (mtu < tcb.payload_size && mtu >= GATT_DEF_BLE_MTU_SIZE)
      tcb.payload_size = mtu;
    LOG_INFO("Local pending MTU %d, Remote (%s) MTU %d",
             tcb.pending_user_mtu_exchange_value,
             tcb.peer_bda.ToString().c_str(), mtu);

    /* Aim for MAX as we did in the request */
    if (mtu < GATT_DEF_BLE_MTU_SIZE) {
      tcb.payload_size = GATT_DEF_BLE_MTU_SIZE;
    } else {
      tcb.payload_size = std::min(mtu, (uint16_t)(GATT_MAX_MTU_SIZE));
    }

    bluetooth::shim::arbiter::GetArbiter().OnIncomingMtuResp(tcb.tcb_idx,
                                                             tcb.payload_size);

  BTM_SetBleDataLength(tcb.peer_bda, tcb.payload_size + L2CAP_PKT_OVERHEAD);
    /* This is just to track the biggest MTU requested by the user.
     * This value will be used in the BTM_SetBleDataLength */
    if (tcb.pending_user_mtu_exchange_value > tcb.max_user_mtu) {
      tcb.max_user_mtu =
          std::min(tcb.pending_user_mtu_exchange_value, tcb.payload_size);
    }
    tcb.pending_user_mtu_exchange_value = 0;

    LOG_INFO("MTU Exchange resulted in: %d", tcb.payload_size);

    BTM_SetBleDataLength(tcb.peer_bda, tcb.max_user_mtu + L2CAP_PKT_OVERHEAD);
  }

  gatt_end_operation(p_clcb, status, NULL);
}
Loading