mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
fix(ble_mesh): align GATTS read/write response handling with ATT
bt_mesh_bta_gatts_cb did not always answer ATT Read/Write Requests:
- READ: on a callback error it only logged a warning and sent nothing; a
0-byte read (Read Blob at an offset equal to the value length) also sent
nothing, although it is a successful empty read.
- WRITE: on a callback error it sent nothing, and a partial/zero write was
treated as success.
- Both: when the handle was not found or the attribute had no read/write
callback, the request was silently dropped.
An ATT Request must always be answered:
- READ: len >= 0 is success -> Read Response (a 0-byte read yields an empty
value); len < 0 -> ATT Error Response carrying the callback's error code
(-len, since BLE_MESH_GATT_ERR(x) == -x). The copy length is clamped to
the source buffer size as a defensive bound. If the handle is unknown or
the attribute has no read callback, respond with INVALID_HANDLE /
READ_NOT_PERMITTED.
- WRITE: when need_rsp is set, always reply. len == write length -> Write
Response; otherwise (negative ATT error, partial write, or 0) -> ATT
Error Response (the negative code, or UNLIKELY for partial/0). If the
handle is unknown or the attribute has no write callback, respond with
INVALID_HANDLE / WRITE_NOT_PERMITTED. Write Without Response still sends
no response.
A non-success status passed to BTA_GATTS_SendRsp is turned into an ATT
Error Response by the GATT layer (gatt_sr_process_app_rsp ->
gatt_send_error_rsp).
(cherry picked from commit ed1f4de3a3)
Co-authored-by: luoxu <luoxu@espressif.com>
This commit is contained in:
@@ -1172,17 +1172,43 @@ static void bt_mesh_bta_gatts_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data)
|
||||
BT_DBG("gatts read, handle %d", p_data->req_data.p_data->read_req.handle);
|
||||
|
||||
if (attr != NULL && attr->read != NULL && index < ARRAY_SIZE(bt_mesh_gatts_conn)) {
|
||||
if ((len = attr->read(&bt_mesh_gatts_conn[index], attr, buf, 100,
|
||||
p_data->req_data.p_data->read_req.offset)) > 0) {
|
||||
len = attr->read(&bt_mesh_gatts_conn[index], attr, buf, 100,
|
||||
p_data->req_data.p_data->read_req.offset);
|
||||
if (len >= 0) {
|
||||
rsp.attr_value.handle = p_data->req_data.p_data->read_req.handle;
|
||||
if (len > sizeof(buf)) {
|
||||
/* A read callback must not return more than the buf_len it was
|
||||
* given; clamp to the source buffer size (also below the
|
||||
* rsp.attr_value.value capacity) to avoid an out-of-bounds copy. */
|
||||
BT_WARN("Mesh gatts read len %d exceeds buffer", (int)len);
|
||||
len = sizeof(buf);
|
||||
}
|
||||
rsp.attr_value.len = len;
|
||||
memcpy(&rsp.attr_value.value[0], buf, len);
|
||||
BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id,
|
||||
p_data->req_data.status, &rsp);
|
||||
BT_DBG("Send gatts read rsp, handle %d", attr->handle);
|
||||
} else {
|
||||
BT_WARN("Mesh gatts read failed");
|
||||
/* Attribute read callback returned an ATT error code (negative).
|
||||
* Reply with an ATT Error Response so the client does not time out. */
|
||||
BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id,
|
||||
(tBTA_GATT_STATUS)(-len), NULL);
|
||||
BT_WARN("Mesh gatts read failed, err %d", (int)len);
|
||||
}
|
||||
} else {
|
||||
/* No matching attribute, or read not supported: respond with an ATT
|
||||
* Error Response so the client does not time out. */
|
||||
tBTA_GATT_STATUS err;
|
||||
if (attr == NULL) {
|
||||
err = BLE_MESH_ATT_ERR_INVALID_HANDLE;
|
||||
} else if (attr->read == NULL) {
|
||||
err = BLE_MESH_ATT_ERR_READ_NOT_PERMITTED;
|
||||
} else {
|
||||
err = BLE_MESH_ATT_ERR_UNLIKELY; /* index out of range */
|
||||
}
|
||||
BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id, err, NULL);
|
||||
BT_WARN("Mesh gatts read rejected, handle %d, err 0x%02x",
|
||||
p_data->req_data.p_data->read_req.handle, (unsigned)err);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1196,16 +1222,40 @@ static void bt_mesh_bta_gatts_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data)
|
||||
bt_hex(p_data->req_data.p_data->write_req.value, p_data->req_data.p_data->write_req.len));
|
||||
|
||||
if (attr != NULL && attr->write != NULL && index < ARRAY_SIZE(bt_mesh_gatts_conn)) {
|
||||
if ((len = attr->write(&bt_mesh_gatts_conn[index], attr,
|
||||
p_data->req_data.p_data->write_req.value,
|
||||
p_data->req_data.p_data->write_req.len,
|
||||
p_data->req_data.p_data->write_req.offset, 0)) > 0) {
|
||||
if (p_data->req_data.p_data->write_req.need_rsp) {
|
||||
len = attr->write(&bt_mesh_gatts_conn[index], attr,
|
||||
p_data->req_data.p_data->write_req.value,
|
||||
p_data->req_data.p_data->write_req.len,
|
||||
p_data->req_data.p_data->write_req.offset, 0);
|
||||
if (p_data->req_data.p_data->write_req.need_rsp) {
|
||||
if (len == p_data->req_data.p_data->write_req.len) {
|
||||
BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id,
|
||||
p_data->req_data.status, NULL);
|
||||
BT_DBG("Send gatts write rsp, handle %d", attr->handle);
|
||||
} else {
|
||||
/* Write callback returned an ATT error code (negative), a partial
|
||||
* write, or 0. Match upstream Zephyr, which requires write == len
|
||||
* and otherwise replies with an ATT Error Response. */
|
||||
tBTA_GATT_STATUS err = (len < 0) ? (tBTA_GATT_STATUS)(-len)
|
||||
: BLE_MESH_ATT_ERR_UNLIKELY;
|
||||
BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id,
|
||||
err, NULL);
|
||||
BT_WARN("Mesh gatts write failed, len %d", (int)len);
|
||||
}
|
||||
}
|
||||
} else if (p_data->req_data.p_data->write_req.need_rsp) {
|
||||
/* No matching attribute, or write not supported: respond with an ATT
|
||||
* Error Response for Write Requests (Write Commands get no response). */
|
||||
tBTA_GATT_STATUS err;
|
||||
if (attr == NULL) {
|
||||
err = BLE_MESH_ATT_ERR_INVALID_HANDLE;
|
||||
} else if (attr->write == NULL) {
|
||||
err = BLE_MESH_ATT_ERR_WRITE_NOT_PERMITTED;
|
||||
} else {
|
||||
err = BLE_MESH_ATT_ERR_UNLIKELY; /* index out of range */
|
||||
}
|
||||
BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id, err, NULL);
|
||||
BT_WARN("Mesh gatts write rejected, handle %d, err 0x%02x",
|
||||
p_data->req_data.p_data->write_req.handle, (unsigned)err);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user