diff --git a/include/zephyr/bluetooth/gatt.h b/include/zephyr/bluetooth/gatt.h index 17b125d602c5f..116d09a9ec550 100644 --- a/include/zephyr/bluetooth/gatt.h +++ b/include/zephyr/bluetooth/gatt.h @@ -156,6 +156,11 @@ struct bt_gatt_attr; * @note The GATT server propagates the return value from this * method back to the remote client. * + * @note If this function returns ``-EINPROGRESS``, the read operation + * is deferred and the application must later send the response + * using @ref bt_gatt_send_read_response(). Deferred responses + * are only supported when Enhanced ATT (EATT) is not active. + * * @param conn The connection that is requesting to read. * NULL if local. * @param attr The attribute that's being read @@ -199,6 +204,11 @@ typedef ssize_t (*bt_gatt_attr_read_func_t)(struct bt_conn *conn, * @note The GATT server propagates the return value from this * method back to the remote client. * + * @note If this function returns ``-EINPROGRESS``, the write operation + * is deferred and the application must later send the response + * using @ref bt_gatt_send_write_response(). Deferred responses + * are only supported when Enhanced ATT (EATT) is not active. + * * @param conn The connection that is requesting to write * @param attr The attribute that's being written * @param buf Buffer with the data to write @@ -2024,6 +2034,25 @@ struct bt_gatt_read_params { */ int bt_gatt_read(struct bt_conn *conn, struct bt_gatt_read_params *params); +/** @brief Send a deferred Read Response + * + * Used when an attribute read was deferred (attr->read returned + * -EINPROGRESS). The application later provides the + * response value via this API. + * + * @param conn Connection object. + * @param data Pointer to the attribute value buffer. + * @param length Length of the attribute value. + * + * @retval 0 Successfully queued response. + * + * @retval -ENOTCONN The connection is not established. + * @retval -EINVAL Invalid parameters. + * @retval -ENOMEM Out of memory. + * @retval -ENOTSUP EATT active and deferred responses not supported. + */ +int bt_gatt_send_read_response(struct bt_conn *conn, const void *data, uint16_t length); + struct bt_gatt_write_params; /** @typedef bt_gatt_write_func_t @@ -2073,6 +2102,24 @@ struct bt_gatt_write_params { */ int bt_gatt_write(struct bt_conn *conn, struct bt_gatt_write_params *params); +/** + * @brief Send a deferred Write Response + * + * Used when an attribute write was deferred (attr->write returned + * -EINPROGRESS). The application later + * responds using this API once the write has been processed. + * + * @param conn Connection object. + * + * @retval 0 Successfully queued response. + * + * @retval -ENOTCONN The connection is not established. + * @retval -EINVAL Invalid parameters. + * @retval -ENOMEM Out of memory. + * @retval -ENOTSUP EATT active and deferred responses not supported. + */ +int bt_gatt_send_write_response(struct bt_conn *conn); + /** @brief Write Attribute Value by handle without response with callback. * * This function works in the same way as @ref bt_gatt_write_without_response. diff --git a/subsys/bluetooth/host/att.c b/subsys/bluetooth/host/att.c index 66833d1cfcc53..5217c690fb936 100644 --- a/subsys/bluetooth/host/att.c +++ b/subsys/bluetooth/host/att.c @@ -1401,6 +1401,17 @@ static ssize_t att_chan_read(struct bt_att_chan *chan, read = attr->read(conn, attr, frag->data + frag->len, len, offset); + + if (read == -EINPROGRESS) { +#if defined(CONFIG_BT_EATT) + if (bt_att_is_enhanced(chan)) { + LOG_WRN("Async read not supported on EATT channel"); + return BT_ATT_ERR_NOT_SUPPORTED; + } +#endif + return -EINPROGRESS; + } + if (read < 0) { if (total) { return total; @@ -1586,6 +1597,7 @@ struct read_data { uint16_t offset; struct net_buf *buf; uint8_t err; + bool async; }; static uint8_t read_cb(const struct bt_gatt_attr *attr, uint16_t handle, @@ -1618,6 +1630,12 @@ static uint8_t read_cb(const struct bt_gatt_attr *attr, uint16_t handle, /* Read attribute value and store in the buffer */ ret = att_chan_read(chan, attr, data->buf, data->offset, NULL, NULL); + + if (ret == -EINPROGRESS) { + data->async = true; + return BT_GATT_ITER_STOP; + } + if (ret < 0) { data->err = err_to_att(ret); return BT_GATT_ITER_STOP; @@ -1666,6 +1684,12 @@ static uint8_t att_read_rsp(struct bt_att_chan *chan, uint8_t op, uint8_t rsp, return 0; } + if (data.async) { + net_buf_unref(data.buf); + /* Async read: release buffer, app send response later */ + return 0; + } + bt_att_chan_send_rsp(chan, data.buf); return 0; @@ -2029,6 +2053,7 @@ struct write_data { uint16_t len; uint16_t offset; uint8_t err; + bool async; }; static uint8_t write_cb(const struct bt_gatt_attr *attr, uint16_t handle, @@ -2063,6 +2088,12 @@ static uint8_t write_cb(const struct bt_gatt_attr *attr, uint16_t handle, /* Write attribute value */ write = attr->write(data->conn, attr, data->value, data->len, data->offset, flags); + + if (write == -EINPROGRESS) { + data->async = true; + return BT_GATT_ITER_STOP; + } + if (write < 0 || write != data->len) { data->err = err_to_att(write); return BT_GATT_ITER_STOP; @@ -2121,6 +2152,22 @@ static uint8_t att_write_rsp(struct bt_att_chan *chan, uint8_t req, uint8_t rsp, return req == BT_ATT_OP_EXEC_WRITE_REQ ? data.err : 0; } + if (data.async) { +#if defined(CONFIG_BT_EATT) + if (bt_att_is_enhanced(chan)) { + LOG_WRN("Async write not supported on EATT channel"); + if (rsp) { + net_buf_unref(data.buf); + send_err_rsp(chan, req, handle, BT_ATT_ERR_NOT_SUPPORTED); + } + return req == BT_ATT_OP_EXEC_WRITE_REQ ? BT_ATT_ERR_NOT_SUPPORTED : 0; + } +#endif + net_buf_unref(data.buf); + /* Async write: release buffer, app send response later */ + return 0; + } + if (data.buf) { bt_att_chan_send_rsp(chan, data.buf); } diff --git a/subsys/bluetooth/host/gatt.c b/subsys/bluetooth/host/gatt.c index ea1dc73e158a9..483177ef9defb 100644 --- a/subsys/bluetooth/host/gatt.c +++ b/subsys/bluetooth/host/gatt.c @@ -5155,6 +5155,64 @@ int bt_gatt_write_without_response_cb(struct bt_conn *conn, uint16_t handle, return bt_att_send(conn, buf); } +int bt_gatt_send_read_response(struct bt_conn *conn, const void *data, uint16_t length) +{ + struct net_buf *buf; + + __ASSERT(conn, "invalid connection\n"); + + if (conn->state != BT_CONN_CONNECTED) { + return -ENOTCONN; + } + +#if defined(CONFIG_BT_EATT) + if (bt_eatt_count(conn) > 0) { + LOG_WRN("EATT active: async response not supported"); + return -ENOTSUP; + } +#endif + + buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_RSP, length); + if (!buf) { + return -ENOMEM; + } + + if (data && length) { + net_buf_add_mem(buf, data, length); + } + + LOG_DBG("conn %p len %u", conn, length); + + return bt_att_send(conn, buf); +} + +int bt_gatt_send_write_response(struct bt_conn *conn) +{ + struct net_buf *buf; + + __ASSERT(conn, "invalid connection\n"); + + if (conn->state != BT_CONN_CONNECTED) { + return -ENOTCONN; + } + +#if defined(CONFIG_BT_EATT) + if (bt_eatt_count(conn) > 0) { + LOG_WRN("EATT active: async write response not supported"); + return -ENOTSUP; + } +#endif + + buf = bt_att_create_pdu(conn, BT_ATT_OP_WRITE_RSP, 0); + if (!buf) { + return -ENOMEM; + } + + LOG_DBG("conn %p", conn); + + return bt_att_send(conn, buf); +} + static int gatt_exec_encode(struct net_buf *buf, size_t len, void *user_data) { struct bt_att_exec_write_req *req;