Bluetooth: gatt: Fix automatic resubscription causing CCC removal

This fixes CCC subscriptions that were removed if the automatic
resubscription was aborted by ACL disconnection.
As the client renews subscriptions, there is no point of removing those
if the link is disconnected unexpectedly.
The API user won't be notified about the failure, as the automatic
resubscriptions are implicit, and after reconnection the subscriptions
will be still valid.

Signed-off-by: Mariusz Skamra <mariusz.skamra@codecoup.pl>
This commit is contained in:
Mariusz Skamra 2023-12-05 14:07:45 +01:00 committed by Carles Cufí
parent 3b4ce7f8e9
commit ddf172c187
3 changed files with 88 additions and 42 deletions

View file

@ -921,12 +921,12 @@ static void att_req_send_process(struct bt_att *att)
}
static uint8_t att_handle_rsp(struct bt_att_chan *chan, void *pdu, uint16_t len,
uint8_t err)
int err)
{
bt_att_func_t func = NULL;
void *params;
LOG_DBG("chan %p err 0x%02x len %u: %s", chan, err, len, bt_hex(pdu, len));
LOG_DBG("chan %p err %d len %u: %s", chan, err, len, bt_hex(pdu, len));
/* Cancel timeout if ongoing */
k_work_cancel_delayable(&chan->timeout_work);
@ -3012,7 +3012,7 @@ static void att_reset(struct bt_att *att)
node = sys_slist_get_not_empty(&att->reqs);
req = CONTAINER_OF(node, struct bt_att_req, node);
if (req->func) {
req->func(att->conn, BT_ATT_ERR_UNLIKELY, NULL, 0,
req->func(att->conn, -ECONNRESET, NULL, 0,
req->user_data);
}
@ -3042,7 +3042,7 @@ static void att_chan_detach(struct bt_att_chan *chan)
if (chan->req) {
/* Notify outstanding request */
att_handle_rsp(chan, NULL, 0, BT_ATT_ERR_UNLIKELY);
att_handle_rsp(chan, NULL, 0, -ECONNRESET);
}
chan->att = NULL;

View file

@ -270,7 +270,7 @@ struct bt_att_signed_write_cmd {
uint8_t value[0];
} __packed;
typedef void (*bt_att_func_t)(struct bt_conn *conn, uint8_t err,
typedef void (*bt_att_func_t)(struct bt_conn *conn, int err,
const void *pdu, uint16_t length,
void *user_data);

View file

@ -2469,13 +2469,26 @@ static int gatt_notify(struct bt_conn *conn, uint16_t handle,
return bt_att_send(conn, buf);
}
static void gatt_indicate_rsp(struct bt_conn *conn, uint8_t err,
/* Converts error (negative errno) to ATT Error code */
static uint8_t att_err_from_int(int err)
{
LOG_DBG("%d", err);
/* ATT error codes are 1 byte values, so any value outside the range is unknown */
if (!IN_RANGE(err, 0, UINT8_MAX)) {
return BT_ATT_ERR_UNLIKELY;
}
return err;
}
static void gatt_indicate_rsp(struct bt_conn *conn, int err,
const void *pdu, uint16_t length, void *user_data)
{
struct bt_gatt_indicate_params *params = user_data;
if (params->func) {
params->func(conn, params, err);
params->func(conn, params, att_err_from_int(err));
}
params->_ref--;
@ -3624,12 +3637,12 @@ static void remove_subscriptions(struct bt_conn *conn)
}
}
static void gatt_mtu_rsp(struct bt_conn *conn, uint8_t err, const void *pdu,
static void gatt_mtu_rsp(struct bt_conn *conn, int err, const void *pdu,
uint16_t length, void *user_data)
{
struct bt_gatt_exchange_params *params = user_data;
params->func(conn, err, params);
params->func(conn, att_err_from_int(err), params);
}
static int gatt_exchange_mtu_encode(struct net_buf *buf, size_t len,
@ -3706,7 +3719,7 @@ done:
params->func(conn, NULL, params);
}
static void gatt_find_type_rsp(struct bt_conn *conn, uint8_t err,
static void gatt_find_type_rsp(struct bt_conn *conn, int err,
const void *pdu, uint16_t length,
void *user_data)
{
@ -3715,7 +3728,7 @@ static void gatt_find_type_rsp(struct bt_conn *conn, uint8_t err,
uint8_t count;
uint16_t end_handle = 0U, start_handle;
LOG_DBG("err 0x%02x", err);
LOG_DBG("err %d", err);
if (err || (length % sizeof(struct bt_att_handle_group) != 0)) {
goto done;
@ -3820,7 +3833,7 @@ static int gatt_find_type(struct bt_conn *conn,
len, BT_ATT_CHAN_OPT(params));
}
static void read_included_uuid_cb(struct bt_conn *conn, uint8_t err,
static void read_included_uuid_cb(struct bt_conn *conn, int err,
const void *pdu, uint16_t length,
void *user_data)
{
@ -4144,14 +4157,14 @@ done:
return 0;
}
static void gatt_read_type_rsp(struct bt_conn *conn, uint8_t err,
static void gatt_read_type_rsp(struct bt_conn *conn, int err,
const void *pdu, uint16_t length,
void *user_data)
{
struct bt_gatt_discover_params *params = user_data;
uint16_t handle;
LOG_DBG("err 0x%02x", err);
LOG_DBG("err %d", err);
if (err) {
params->func(conn, NULL, params);
@ -4294,14 +4307,14 @@ done:
return 0;
}
static void gatt_read_group_rsp(struct bt_conn *conn, uint8_t err,
static void gatt_read_group_rsp(struct bt_conn *conn, int err,
const void *pdu, uint16_t length,
void *user_data)
{
struct bt_gatt_discover_params *params = user_data;
uint16_t handle;
LOG_DBG("err 0x%02x", err);
LOG_DBG("err %d", err);
if (err) {
params->func(conn, NULL, params);
@ -4347,7 +4360,7 @@ static int gatt_read_group(struct bt_conn *conn,
BT_ATT_CHAN_OPT(params));
}
static void gatt_find_info_rsp(struct bt_conn *conn, uint8_t err,
static void gatt_find_info_rsp(struct bt_conn *conn, int err,
const void *pdu, uint16_t length,
void *user_data)
{
@ -4367,7 +4380,7 @@ static void gatt_find_info_rsp(struct bt_conn *conn, uint8_t err,
int i;
bool skip = false;
LOG_DBG("err 0x%02x", err);
LOG_DBG("err %d", err);
if (err) {
goto done;
@ -4592,15 +4605,15 @@ static void parse_read_by_uuid(struct bt_conn *conn,
}
}
static void gatt_read_rsp(struct bt_conn *conn, uint8_t err, const void *pdu,
static void gatt_read_rsp(struct bt_conn *conn, int err, const void *pdu,
uint16_t length, void *user_data)
{
struct bt_gatt_read_params *params = user_data;
LOG_DBG("err 0x%02x", err);
LOG_DBG("err %d", err);
if (err || !length) {
params->func(conn, err, params, NULL, 0);
params->func(conn, att_err_from_int(err), params, NULL, 0);
return;
}
@ -4691,15 +4704,15 @@ static int gatt_read_uuid(struct bt_conn *conn,
}
#if defined(CONFIG_BT_GATT_READ_MULTIPLE)
static void gatt_read_mult_rsp(struct bt_conn *conn, uint8_t err, const void *pdu,
static void gatt_read_mult_rsp(struct bt_conn *conn, int err, const void *pdu,
uint16_t length, void *user_data)
{
struct bt_gatt_read_params *params = user_data;
LOG_DBG("err 0x%02x", err);
LOG_DBG("err %d", err);
if (err || !length) {
params->func(conn, err, params, NULL, 0);
params->func(conn, att_err_from_int(err), params, NULL, 0);
return;
}
@ -4742,7 +4755,7 @@ static int gatt_read_mult(struct bt_conn *conn,
#endif /* CONFIG_BT_GATT_READ_MULTIPLE */
#if defined(CONFIG_BT_GATT_READ_MULT_VAR_LEN)
static void gatt_read_mult_vl_rsp(struct bt_conn *conn, uint8_t err,
static void gatt_read_mult_vl_rsp(struct bt_conn *conn, int err,
const void *pdu, uint16_t length,
void *user_data)
{
@ -4750,10 +4763,10 @@ static void gatt_read_mult_vl_rsp(struct bt_conn *conn, uint8_t err,
const struct bt_att_read_mult_vl_rsp *rsp;
struct net_buf_simple buf;
LOG_DBG("err 0x%02x", err);
LOG_DBG("err %d", err);
if (err || !length) {
params->func(conn, err, params, NULL, 0);
params->func(conn, att_err_from_int(err), params, NULL, 0);
return;
}
@ -4858,14 +4871,14 @@ int bt_gatt_read(struct bt_conn *conn, struct bt_gatt_read_params *params)
BT_ATT_CHAN_OPT(params));
}
static void gatt_write_rsp(struct bt_conn *conn, uint8_t err, const void *pdu,
static void gatt_write_rsp(struct bt_conn *conn, int err, const void *pdu,
uint16_t length, void *user_data)
{
struct bt_gatt_write_params *params = user_data;
LOG_DBG("err 0x%02x", err);
LOG_DBG("err %d", err);
params->func(conn, err, params);
params->func(conn, att_err_from_int(err), params);
}
int bt_gatt_write_without_response_cb(struct bt_conn *conn, uint16_t handle,
@ -4961,7 +4974,7 @@ static int gatt_cancel_all_writes(struct bt_conn *conn,
BT_ATT_CHAN_OPT(params));
}
static void gatt_prepare_write_rsp(struct bt_conn *conn, uint8_t err,
static void gatt_prepare_write_rsp(struct bt_conn *conn, int err,
const void *pdu, uint16_t length,
void *user_data)
{
@ -4970,11 +4983,11 @@ static void gatt_prepare_write_rsp(struct bt_conn *conn, uint8_t err,
size_t len;
bool data_valid;
LOG_DBG("err 0x%02x", err);
LOG_DBG("err %d", err);
/* Don't continue in case of error */
if (err) {
params->func(conn, err, params);
params->func(conn, att_err_from_int(err), params);
return;
}
@ -5104,13 +5117,14 @@ int bt_gatt_write(struct bt_conn *conn, struct bt_gatt_write_params *params)
BT_ATT_OP_WRITE_REQ, len, BT_ATT_CHAN_OPT(params));
}
static void gatt_write_ccc_rsp(struct bt_conn *conn, uint8_t err,
static void gatt_write_ccc_rsp(struct bt_conn *conn, int err,
const void *pdu, uint16_t length,
void *user_data)
{
struct bt_gatt_subscribe_params *params = user_data;
uint8_t att_err;
LOG_DBG("err 0x%02x", err);
LOG_DBG("err %d", err);
atomic_clear_bit(params->flags, BT_GATT_SUBSCRIBE_FLAG_WRITE_PENDING);
@ -5138,12 +5152,14 @@ static void gatt_write_ccc_rsp(struct bt_conn *conn, uint8_t err,
params->notify(conn, params, NULL, 0);
}
att_err = att_err_from_int(err);
if (params->subscribe) {
params->subscribe(conn, err, params);
params->subscribe(conn, att_err, params);
} else if (params->write) {
/* TODO: Remove after deprecation */
LOG_WRN("write callback is deprecated, use subscribe cb instead");
params->write(conn, err, NULL);
params->write(conn, att_err, NULL);
}
}
@ -5163,7 +5179,8 @@ static int gatt_write_ccc_buf(struct net_buf *buf, size_t len, void *user_data)
}
static int gatt_write_ccc(struct bt_conn *conn,
struct bt_gatt_subscribe_params *params)
struct bt_gatt_subscribe_params *params,
bt_att_func_t rsp)
{
size_t len = sizeof(struct bt_att_write_req) + sizeof(uint16_t);
@ -5174,7 +5191,7 @@ static int gatt_write_ccc(struct bt_conn *conn,
*/
atomic_set_bit(params->flags, BT_GATT_SUBSCRIBE_FLAG_SENT);
return gatt_req_send(conn, gatt_write_ccc_rsp, params,
return gatt_req_send(conn, rsp, params,
gatt_write_ccc_buf, BT_ATT_OP_WRITE_REQ, len,
BT_ATT_CHAN_OPT(params));
}
@ -5289,7 +5306,7 @@ int bt_gatt_subscribe(struct bt_conn *conn,
return gatt_ccc_discover(conn, params);
}
#endif
err = gatt_write_ccc(conn, params);
err = gatt_write_ccc(conn, params, gatt_write_ccc_rsp);
if (err) {
gatt_sub_remove(conn, sub, NULL, NULL);
return err;
@ -5378,7 +5395,7 @@ int bt_gatt_unsubscribe(struct bt_conn *conn,
int err;
params->value = 0x0000;
err = gatt_write_ccc(conn, params);
err = gatt_write_ccc(conn, params, gatt_write_ccc_rsp);
if (err) {
return err;
}
@ -5419,6 +5436,29 @@ void bt_gatt_cancel(struct bt_conn *conn, void *params)
}
#if defined(CONFIG_BT_GATT_AUTO_RESUBSCRIBE)
static void gatt_resub_ccc_rsp(struct bt_conn *conn, int err,
const void *pdu, uint16_t length,
void *user_data)
{
LOG_DBG("err %d", err);
if (err == -ECONNRESET) {
/* The resubscriptions are implicit, thus in the case of ACL
* disconnection during the CCC value ATT Write, there is no
* need to notify the application.
*/
return;
}
gatt_write_ccc_rsp(conn, err, pdu, length, user_data);
}
static int gatt_resub_ccc(struct bt_conn *conn,
struct bt_gatt_subscribe_params *params)
{
return gatt_write_ccc(conn, params, gatt_resub_ccc_rsp);
}
static void add_subscriptions(struct bt_conn *conn)
{
struct gatt_sub *sub;
@ -5439,10 +5479,16 @@ static void add_subscriptions(struct bt_conn *conn)
BT_GATT_SUBSCRIBE_FLAG_SENT) &&
!atomic_test_bit(params->flags,
BT_GATT_SUBSCRIBE_FLAG_NO_RESUB)) {
int err;
/* Force write to CCC to workaround devices that don't
* track it properly.
*/
gatt_write_ccc(conn, params);
err = gatt_resub_ccc(conn, params);
if (err < 0) {
LOG_WRN("conn %p params %p resub failed (err %d)",
(void *)conn, params, err);
}
}
}
}