diff --git a/include/zephyr/bluetooth/gatt.h b/include/zephyr/bluetooth/gatt.h index a2d553ad3ed..64df43e3c8f 100644 --- a/include/zephyr/bluetooth/gatt.h +++ b/include/zephyr/bluetooth/gatt.h @@ -1167,6 +1167,20 @@ ssize_t bt_gatt_attr_write_ccc(struct bt_conn *conn, BT_GATT_CCC_MANAGED(((struct _bt_gatt_ccc[]) \ {BT_GATT_CCC_INITIALIZER(_changed, NULL, NULL)}), _perm) +/** + * @brief Client Characteristic Configuration Declaration Macro with write callback. + * + * Helper macro to declare a CCC attribute with a write callback. + * + * @param _changed Configuration changed callback. + * @param _write Configuration write callback. + * @param _perm CCC access permissions, + * a bitmap of @ref bt_gatt_perm values. + */ +#define BT_GATT_CCC_WITH_WRITE_CB(_changed, _write, _perm) \ + BT_GATT_CCC_MANAGED(((struct _bt_gatt_ccc[]) \ + {BT_GATT_CCC_INITIALIZER(_changed, _write, NULL) }), _perm) + /** @brief Read Characteristic Extended Properties Attribute helper * * Read CEP attribute value from local database storing the result into buffer diff --git a/subsys/bluetooth/host/cs.c b/subsys/bluetooth/host/cs.c index 3a2e588bdd0..e9caaba5b96 100644 --- a/subsys/bluetooth/host/cs.c +++ b/subsys/bluetooth/host/cs.c @@ -606,6 +606,10 @@ int bt_le_cs_start_test(const struct bt_le_cs_test_param *params) cp->override_parameters_length = override_parameters_length; + struct bt_hci_cmd_hdr *hdr = (struct bt_hci_cmd_hdr *)buf->data; + + hdr->param_len += override_parameters_length; + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CS_TEST, buf, NULL); } #endif /* CONFIG_BT_CHANNEL_SOUNDING_TEST */ diff --git a/tests/bluetooth/gatt/src/main.c b/tests/bluetooth/gatt/src/main.c index d17e8e8b793..7acf9eb7a56 100644 --- a/tests/bluetooth/gatt/src/main.c +++ b/tests/bluetooth/gatt/src/main.c @@ -39,6 +39,11 @@ static void test1_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t valu nfy_enabled = (value == BT_GATT_CCC_NOTIFY) ? 1 : 0; } +static ssize_t test1_ccc_cfg_write_cb(struct bt_conn *conn, const struct bt_gatt_attr *attr, uint16_t value) +{ + return sizeof(value); +} + static ssize_t read_test(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, uint16_t len, uint16_t offset) { @@ -507,3 +512,25 @@ ZTEST(test_gatt, test_bt_gatt_err_to_str) zassert_not_null(bt_gatt_err_to_str(-i), ": %d", i); } } + +ZTEST(test_gatt, test_gatt_ccc_write_cb) +{ + struct bt_gatt_attr test_write_cb_attrs[] = { + /* Vendor Primary Service Declaration */ + BT_GATT_PRIMARY_SERVICE(&test1_uuid), + + BT_GATT_CHARACTERISTIC(&test1_nfy_uuid.uuid, + BT_GATT_CHRC_NOTIFY, BT_GATT_PERM_NONE, + NULL, NULL, &nfy_enabled), + BT_GATT_CCC_WITH_WRITE_CB(test1_ccc_cfg_changed, + test1_ccc_cfg_write_cb, + BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT), + }; + + struct bt_gatt_service test_write_cb_svc = BT_GATT_SERVICE(test_write_cb_attrs); + + zassert_false(bt_gatt_service_register(&test_write_cb_svc), + "Test service registration failed"); + zassert_false(bt_gatt_service_unregister(&test_write_cb_svc), + "Test service1 unregister failed"); +} diff --git a/tests/bsim/bluetooth/host/gatt/ccc_store/src/central.c b/tests/bsim/bluetooth/host/gatt/ccc_store/src/central.c index 664a65ca30d..471e93cbdac 100644 --- a/tests/bsim/bluetooth/host/gatt/ccc_store/src/central.c +++ b/tests/bsim/bluetooth/host/gatt/ccc_store/src/central.c @@ -41,6 +41,7 @@ static struct bt_conn *default_conn; static struct bt_conn_cb central_cb; +DEFINE_FLAG_STATIC(gatt_subscribed_rejected_flag); DEFINE_FLAG_STATIC(gatt_subscribed_flag); static uint8_t notify_cb(struct bt_conn *conn, struct bt_gatt_subscribe_params *params, @@ -69,6 +70,9 @@ static uint8_t notify_cb(struct bt_conn *conn, struct bt_gatt_subscribe_params * static void subscribe_cb(struct bt_conn *conn, uint8_t err, struct bt_gatt_subscribe_params *params) { if (err) { + if (err == BT_ATT_ERR_WRITE_NOT_PERMITTED) { + SET_FLAG(gatt_subscribed_rejected_flag); + } return; } @@ -81,6 +85,7 @@ static void ccc_subscribe(void) { int err; + UNSET_FLAG(gatt_subscribed_rejected_flag); UNSET_FLAG(gatt_subscribed_flag); subscribe_params.notify = notify_cb; @@ -94,6 +99,13 @@ static void ccc_subscribe(void) TEST_FAIL("Failed to subscribe (att err %d)", err); } + WAIT_FOR_FLAG(gatt_subscribed_rejected_flag); + + err = bt_gatt_subscribe(default_conn, &subscribe_params); + if (err) { + TEST_FAIL("Failed to subscribe (att err %d)", err); + } + WAIT_FOR_FLAG(gatt_subscribed_flag); } diff --git a/tests/bsim/bluetooth/host/gatt/ccc_store/src/peripheral.c b/tests/bsim/bluetooth/host/gatt/ccc_store/src/peripheral.c index fe12cb3c0e2..f2f6da4e328 100644 --- a/tests/bsim/bluetooth/host/gatt/ccc_store/src/peripheral.c +++ b/tests/bsim/bluetooth/host/gatt/ccc_store/src/peripheral.c @@ -41,6 +41,8 @@ static struct bt_conn *default_conn; static struct bt_conn_cb peripheral_cb; +static bool notif_write_allowed; + static void ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value) { ARG_UNUSED(attr); @@ -52,10 +54,24 @@ static void ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value) SET_FLAG(ccc_cfg_changed_flag); } +static ssize_t ccc_cfg_write_cb(struct bt_conn *conn, const struct bt_gatt_attr *attr, uint16_t value) +{ + if (notif_write_allowed) { + LOG_INF("CCC Write Request accepted."); + return sizeof(value); + } else { + LOG_INF("CCC Write Request rejected."); + notif_write_allowed = true; + return BT_GATT_ERR(BT_ATT_ERR_WRITE_NOT_PERMITTED); + } +} + BT_GATT_SERVICE_DEFINE(dummy_svc, BT_GATT_PRIMARY_SERVICE(&dummy_service), BT_GATT_CHARACTERISTIC(¬ify_characteristic_uuid.uuid, BT_GATT_CHRC_NOTIFY, BT_GATT_PERM_NONE, NULL, NULL, NULL), - BT_GATT_CCC(ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)); + BT_GATT_CCC_WITH_WRITE_CB(ccc_cfg_changed, ccc_cfg_write_cb, + BT_GATT_PERM_READ | BT_GATT_PERM_WRITE) +); static void create_adv(struct bt_le_ext_adv **adv) {