fix(ble/bluedroid): fix GATT server busy errors and sr_cmd handling

This commit is contained in:
zhiweijian
2026-07-07 17:18:35 +08:00
parent 0645ba469d
commit f86739b03d

View File

@@ -73,6 +73,48 @@ tGATT_STATUS gatt_send_packet (tGATT_TCB *p_tcb, UINT8 *p_data, UINT16 len)
return status;
}
/*******************************************************************************
**
** Function gatt_sr_next_trans_id
**
** Description Allocate the next transaction ID in [1, GATT_TRANS_ID_MAX - 1].
** Zero is reserved for enqueue failure (sr_cmd busy).
**
*******************************************************************************/
static UINT32 gatt_sr_next_trans_id(tGATT_TCB *p_tcb)
{
UINT32 trans_id = p_tcb->trans_id % GATT_TRANS_ID_MAX;
trans_id = (trans_id + 1) % GATT_TRANS_ID_MAX;
if (trans_id == 0) {
trans_id = 1;
}
p_tcb->trans_id = trans_id;
return trans_id;
}
/*******************************************************************************
**
** Function gatt_sr_busy_error_code
**
** Description Pick an ATT error code for a request received while another
** server procedure is pending. Common profile codes (0xFE) are
** not valid for all request types per ATT Table 3.44.
**
*******************************************************************************/
static UINT8 gatt_sr_busy_error_code(UINT8 op_code)
{
switch (op_code) {
case GATT_REQ_FIND_TYPE_VALUE:
return GATT_REQ_NOT_SUPPORTED;
case GATT_REQ_FIND_INFO:
/* ATT Table 3.44: only Invalid Handle (0x01) and Not Found (0x0A) are valid. */
return GATT_NOT_FOUND;
default:
return GATT_PRC_IN_PROGRESS;
}
}
/*******************************************************************************
**
** Function gatt_sr_enqueue_cmd
@@ -88,21 +130,20 @@ UINT32 gatt_sr_enqueue_cmd (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 handle)
tGATT_SR_CMD *p_cmd = &p_tcb->sr_cmd;
UINT32 trans_id = 0;
if ( (p_cmd->op_code == 0) ||
(op_code == GATT_HANDLE_VALUE_CONF)) { /* no pending request */
if (op_code == GATT_CMD_WRITE ||
op_code == GATT_SIGN_CMD_WRITE ||
op_code == GATT_REQ_MTU ||
op_code == GATT_HANDLE_VALUE_CONF) {
trans_id = ++p_tcb->trans_id;
} else {
p_cmd->trans_id = ++p_tcb->trans_id;
p_cmd->op_code = op_code;
p_cmd->handle = handle;
p_cmd->status = GATT_NOT_FOUND;
p_tcb->trans_id %= GATT_TRANS_ID_MAX;
trans_id = p_cmd->trans_id;
}
/* No-tracking ops do not occupy sr_cmd and may arrive while a request is pending. */
if (op_code == GATT_CMD_WRITE ||
op_code == GATT_SIGN_CMD_WRITE ||
op_code == GATT_REQ_MTU ||
op_code == GATT_HANDLE_VALUE_CONF) {
return gatt_sr_next_trans_id(p_tcb);
}
if (p_cmd->op_code == 0) {
p_cmd->trans_id = gatt_sr_next_trans_id(p_tcb);
p_cmd->op_code = op_code;
p_cmd->handle = handle;
p_cmd->status = GATT_NOT_FOUND;
trans_id = p_cmd->trans_id;
}
return trans_id;
@@ -564,6 +605,7 @@ void gatt_process_exec_write_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U
BOOLEAN sr_cmd_already_dequeued = FALSE;
tGATT_PREPARE_WRITE_RECORD *prepare_record = NULL;
tGATT_PREPARE_WRITE_QUEUE_DATA * queue_data = NULL;
tGATTS_DATA sr_data = {0};
/* Fix: Validate minimum length (flags: 1 byte) */
if (len < 1) {
@@ -587,6 +629,7 @@ void gatt_process_exec_write_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U
/* mask the flag */
flag &= GATT_PREP_WRITE_EXEC;
sr_data.exec_write = flag;
prepare_record = &(p_tcb->prepare_write_record);
queue_num = fixed_queue_length(prepare_record->queue);
@@ -663,7 +706,7 @@ void gatt_process_exec_write_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U
gatt_sr_send_req_callback(conn_id,
trans_id,
GATTS_REQ_TYPE_WRITE_EXEC,
(tGATTS_DATA *)&flag);
&sr_data);
p_tcb->prep_cnt[i] = 0;
}
}
@@ -745,7 +788,7 @@ void gatt_process_exec_write_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U
gatt_sr_send_req_callback(conn_id,
trans_id,
GATTS_REQ_TYPE_WRITE_EXEC,
(tGATTS_DATA *)&flag);
&sr_data);
p_tcb->prep_cnt[i] = 0;
}
}
@@ -773,6 +816,7 @@ void gatt_process_read_multi_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U
tGATT_STATUS err = GATT_SUCCESS;
UINT8 sec_flag, key_size;
tGATTS_RSP *p_msg;
BOOLEAN sr_cmd_enqueued = FALSE;
GATT_TRACE_DEBUG("gatt_process_read_multi_req" );
p_tcb->sr_cmd.multi_req.num_handles = 0;
@@ -831,6 +875,7 @@ void gatt_process_read_multi_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U
if (err == GATT_SUCCESS) {
if ((trans_id = gatt_sr_enqueue_cmd (p_tcb, op_code, p_tcb->sr_cmd.multi_req.handles[0])) != 0) {
sr_cmd_enqueued = TRUE;
gatt_sr_reset_cback_cnt(p_tcb); /* read multiple use multi_rsp_q's count*/
for (ll = 0; ll < p_tcb->sr_cmd.multi_req.num_handles; ll ++) {
@@ -854,12 +899,16 @@ void gatt_process_read_multi_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U
if (err == GATT_SUCCESS || err == GATT_STACK_RSP) {
gatt_sr_process_app_rsp(p_tcb, gatt_cb.sr_reg[i_rcb].gatt_if , trans_id, op_code, GATT_SUCCESS, p_msg);
} else if (err != GATT_PENDING && err != GATT_BUSY) {
osi_free(p_msg);
break;
}
/* either not using or done using the buffer, release it now */
osi_free(p_msg);
} else {
err = GATT_NO_RESOURCES;
gatt_dequeue_sr_cmd(p_tcb);
sr_cmd_enqueued = FALSE;
break;
}
}
@@ -869,7 +918,7 @@ void gatt_process_read_multi_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U
}
/* in theroy BUSY is not possible(should already been checked), protected check */
if (err != GATT_SUCCESS && err != GATT_STACK_RSP && err != GATT_PENDING && err != GATT_BUSY) {
gatt_send_error_rsp(p_tcb, err, op_code, handle, FALSE);
gatt_send_error_rsp(p_tcb, err, op_code, handle, sr_cmd_enqueued);
}
}
@@ -909,7 +958,7 @@ static tGATT_STATUS gatt_build_primary_service_rsp (BT_HDR *p_msg, tGATT_TCB *p_
p_rcb->type == GATT_UUID_PRI_SERVICE) {
if ((p_uuid = gatts_get_service_uuid (p_rcb->p_db)) != NULL) {
if (op_code == GATT_REQ_READ_BY_GRP_TYPE) {
handle_len = 4 + p_uuid->len;
handle_len = 4 + gatt_get_uuid_stream_len(*p_uuid);
}
/* get the length byte in the response */
@@ -1189,11 +1238,15 @@ static void gatts_process_find_info(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len,
if (p_rcb->in_use &&
!(p_rcb->s_hdl > e_hdl ||
p_rcb->e_hdl < s_hdl)) {
reason = gatt_build_find_info_rsp(p_rcb, p_msg, &buf_len, s_hdl, e_hdl);
if (reason == GATT_NO_RESOURCES) {
tGATT_STATUS build_status = gatt_build_find_info_rsp(p_rcb, p_msg, &buf_len, s_hdl, e_hdl);
if (build_status == GATT_SUCCESS) {
reason = GATT_SUCCESS;
} else if (build_status == GATT_NO_RESOURCES) {
reason = GATT_SUCCESS;
break;
}
/* GATT_NOT_FOUND for this service: keep reason (do not discard
* attributes already added from other services). */
}
p_srv = p_srv->p_next;
}
@@ -1230,6 +1283,7 @@ static void gatts_process_mtu_req (tGATT_TCB *p_tcb, UINT16 len, UINT8 *p_data)
UINT8 *p = p_data, i;
BT_HDR *p_buf;
UINT16 conn_id;
tGATTS_DATA sr_data = {0};
#if (BLE_EATT_INCLUDED == TRUE)
/* Exchange MTU applies to Legacy ATT bearer only (Core Spec Vol 3 Part G 5.3). */
@@ -1267,11 +1321,12 @@ static void gatts_process_mtu_req (tGATT_TCB *p_tcb, UINT16 len, UINT8 *p_data)
/* Notify all registered application with new MTU size. Us a transaction ID */
/* of 0, as no response is allowed from applications */
sr_data.mtu = p_tcb->payload_size;
for (i = 0; i < GATT_MAX_APPS; i ++) {
if (gatt_cb.cl_rcb[i].in_use ) {
conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, gatt_cb.cl_rcb[i].gatt_if);
gatt_sr_send_req_callback(conn_id, 0, GATTS_REQ_TYPE_MTU,
(tGATTS_DATA *)&p_tcb->payload_size);
&sr_data);
}
}
@@ -1379,7 +1434,13 @@ void gatts_process_read_by_type_req(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len,
}
p_srv = p_srv->p_next;
}
*p = (UINT8)p_msg->offset;
/* Defensive: Read By Type response record length is a 1-octet field (<= 255). */
if (p_msg->offset > UINT8_MAX) {
GATT_TRACE_ERROR("%s: invalid ReadByType pair_len=%u (>255)", __func__, p_msg->offset);
reason = GATT_INVALID_PDU;
} else {
*p = (UINT8)p_msg->offset;
}
p_msg->offset = L2CAP_MIN_OFFSET;
}
}
@@ -1831,7 +1892,7 @@ void gatts_process_attribute_req (tGATT_TCB *p_tcb, UINT8 op_code,
** Returns void
**
*******************************************************************************/
static void gatts_proc_srv_chg_ind_ack(tGATT_TCB *p_tcb )
void gatts_proc_srv_chg_ind_ack(tGATT_TCB *p_tcb )
{
tGATTS_SRV_CHG_REQ req;
tGATTS_SRV_CHG *p_buf = NULL;
@@ -1904,6 +1965,10 @@ void gatts_process_value_conf(tGATT_TCB *p_tcb, UINT8 op_code)
for (i = 0; i < GATT_MAX_SR_PROFILES; i ++, p_rcb ++) {
if (p_rcb->in_use && p_rcb->s_hdl <= handle && p_rcb->e_hdl >= handle) {
trans_id = gatt_sr_enqueue_cmd(p_tcb, op_code, handle);
if (trans_id == 0) {
GATT_TRACE_ERROR("%s: no trans_id for handle conf 0x%04x", __func__, handle);
continue;
}
conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_rcb->gatt_if);
tGATTS_DATA p_data = {0};
p_data.handle = handle;
@@ -1912,6 +1977,15 @@ void gatts_process_value_conf(tGATT_TCB *p_tcb, UINT8 op_code)
}
}
}
/* Retry Service Changed if a previous attempt was deferred (GATT_BUSY). */
{
tGATTS_SRV_CHG *p_srv_chg_clt;
if ((p_srv_chg_clt = gatt_is_bda_in_the_srv_chg_clt_list(p_tcb->peer_bda)) != NULL) {
gatt_chk_srv_chg(p_srv_chg_clt);
}
}
} else {
GATT_TRACE_ERROR("unexpected handle value confirmation");
}
@@ -2012,10 +2086,14 @@ static BOOLEAN gatts_handle_db_out_of_sync(tGATT_TCB *p_tcb, UINT8 op_code,
void gatt_server_handle_client_req (tGATT_TCB *p_tcb, UINT8 op_code,
UINT16 len, UINT8 *p_data)
{
/* there is pending command, discard this one */
if (!gatt_sr_cmd_empty(p_tcb) && op_code != GATT_HANDLE_VALUE_CONF) {
GATT_TRACE_WARNING("%s discard command opcode=%02x", __func__, op_code);
return;
if (!gatt_sr_cmd_empty(p_tcb)) {
if (op_code == GATT_CMD_WRITE || op_code == GATT_SIGN_CMD_WRITE) {
/* ATT commands have no flow control and may arrive while a request is pending. */
} else if (op_code != GATT_HANDLE_VALUE_CONF && op_code != GATT_REQ_MTU) {
GATT_TRACE_WARNING("%s reject opcode=%02x, procedure in progress", __func__, op_code);
gatt_send_error_rsp(p_tcb, gatt_sr_busy_error_code(op_code), op_code, 0, FALSE);
return;
}
}
/* the size of the message may not be bigger than the local max PDU size*/