Skip to content

Bluetooth: Mesh: Composition data page 0 traversal #33614

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions include/bluetooth/mesh/access.h
Original file line number Diff line number Diff line change
Expand Up @@ -535,16 +535,21 @@ struct bt_mesh_model_cb {
void (*const reset)(struct bt_mesh_model *model);
};

/** Vendor model ID */
struct bt_mesh_mod_id_vnd {
/** Vendor's company ID */
uint16_t company;
/** Model ID */
uint16_t id;
};

/** Abstraction that describes a Mesh Model instance */
struct bt_mesh_model {
union {
/** SIG model ID */
const uint16_t id;
/** Vendor model ID */
struct {
uint16_t company; /**< Vendor's company ID */
uint16_t id; /**< Model ID */
} vnd;
const struct bt_mesh_mod_id_vnd vnd;
};

/* Internal information, mainly for persistent storage */
Expand Down
102 changes: 100 additions & 2 deletions include/bluetooth/mesh/cfg_cli.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,22 @@ struct bt_mesh_cfg_cli {
int bt_mesh_cfg_node_reset(uint16_t net_idx, uint16_t addr, bool *status);

/** @brief Get the target node's composition data.
*
* If the other device does not have the given composition data page, it will
* return the largest page number it supports that is less than the requested
* page index. The actual page the device responds with is returned in @c rsp.
*
* @param net_idx Network index to encrypt with.
* @param addr Target node address.
* @param page Composition data page, or 0xff to request the first available
* page.
* @param status Status response parameter.
* @param rsp Return parameter for the returned page number, or NULL.
* @param comp Composition data buffer to fill.
*
* @return 0 on success, or (negative) error code on failure.
*/
int bt_mesh_cfg_comp_data_get(uint16_t net_idx, uint16_t addr, uint8_t page,
uint8_t *status, struct net_buf_simple *comp);
uint8_t *rsp, struct net_buf_simple *comp);

/** @brief Get the target node's network beacon state.
*
Expand Down Expand Up @@ -936,6 +940,100 @@ int32_t bt_mesh_cfg_cli_timeout_get(void);
*/
void bt_mesh_cfg_cli_timeout_set(int32_t timeout);

/** Parsed Composition data page 0 representation.
*
* Should be pulled from the return buffer passed to
* @ref bt_mesh_cfg_comp_data_get using
* @ref bt_mesh_comp_p0_get.
*/
struct bt_mesh_comp_p0 {
/** Company ID */
uint16_t cid;
/** Product ID */
uint16_t pid;
/** Version ID */
uint16_t vid;
/** Replay protection list size */
uint16_t crpl;
/** Supported features, see @ref BT_MESH_FEAT_SUPPORTED. */
uint16_t feat;

struct net_buf_simple *_buf;
};

/** Composition data page 0 element representation */
struct bt_mesh_comp_p0_elem {
/** Element location */
uint16_t loc;
/** The number of SIG models in this element */
size_t nsig;
/** The number of vendor models in this element */
size_t nvnd;

uint8_t *_buf;
};

/** @brief Create a composition data page 0 representation from a buffer.
*
* The composition data page object will take ownership over the buffer, which
* should not be manipulated directly after this call.
*
* This function can be used in combination with @ref bt_mesh_cfg_comp_data_get
* to read out composition data page 0 from other devices:
*
* @code
* NET_BUF_SIMPLE_DEFINE(buf, BT_MESH_RX_SDU_MAX);
* struct bt_mesh_comp_p0 comp;
*
* err = bt_mesh_cfg_comp_data_get(net_idx, addr, 0, &page, &buf);
* if (!err) {
* bt_mesh_comp_p0_get(&comp, &buf);
* }
* @endcode
*
* @param buf Network buffer containing composition data.
* @param comp Composition data structure to fill.
*
* @return 0 on success, or (negative) error code on failure.
*/
int bt_mesh_comp_p0_get(struct bt_mesh_comp_p0 *comp,
struct net_buf_simple *buf);

/** @brief Pull a composition data page 0 element from a composition data page 0
* instance.
*
* Each call to this function will pull out a new element from the composition
* data page, until all elements have been pulled.
*
* @param comp Composition data page
* @param elem Element to fill.
*
* @return A pointer to @c elem on success, or NULL if no more elements could
* be pulled.
*/
struct bt_mesh_comp_p0_elem *bt_mesh_comp_p0_elem_pull(const struct bt_mesh_comp_p0 *comp,
struct bt_mesh_comp_p0_elem *elem);

/** @brief Get a SIG model from the given composition data page 0 element.
*
* @param elem Element to read the model from.
* @param idx Index of the SIG model to read.
*
* @return The Model ID of the SIG model at the given index, or 0xffff if the
* index is out of bounds.
*/
uint16_t bt_mesh_comp_p0_elem_mod(struct bt_mesh_comp_p0_elem *elem, int idx);

/** @brief Get a vendor model from the given composition data page 0 element.
*
* @param elem Element to read the model from.
* @param idx Index of the vendor model to read.
*
* @return The model ID of the vendor model at the given index, or
* {0xffff, 0xffff} if the index is out of bounds.
*/
struct bt_mesh_mod_id_vnd bt_mesh_comp_p0_elem_mod_vnd(struct bt_mesh_comp_p0_elem *elem, int idx);

/** @cond INTERNAL_HIDDEN */
extern const struct bt_mesh_model_op bt_mesh_cfg_cli_op[];
extern const struct bt_mesh_model_cb bt_mesh_cfg_cli_cb;
Expand Down
10 changes: 5 additions & 5 deletions samples/bluetooth/mesh_provisioner/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ Overview

This sample demonstrates how to use the Bluetooth Mesh APIs related to
provisioning and using the Configuration Database (CDB). It is intended to be
tested together with a device that support the Health Server model. For
example, one could use the sample in :zephyr_file:`tests/bluetooth/mesh_shell`.
tested together with a device capable of being provisioned. For
example, one could use the sample in :zephyr_file:`samples/bluetooth/mesh` or
:zephyr_file:`tests/bluetooth/mesh_shell`.

The application provisions itself and loads the CDB with an application key.
It then waits to receive an Unprovisioned Beacon from a device which will
Expand All @@ -18,9 +19,8 @@ be present in the CDB but not yet marked as configured. The application will
notice the unconfigured node and start configuring it. If no errors are
encountered, the node is marked as configured.

The configuration of a node involves adding an application key, binding the
Health Server model to that application key and then configuring the model to
publish Health Status every 10 seconds.
The configuration of a node involves adding an application key, getting the
composition data, and binding all its models to the application key.

Please note that this sample uses the CDB API which is currently marked as
EXPERIMENTAL and is likely to change.
Expand Down
7 changes: 5 additions & 2 deletions samples/bluetooth/mesh_provisioner/prj.conf
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#CONFIG_INIT_STACKS=y
CONFIG_MAIN_STACK_SIZE=1024
CONFIG_MAIN_STACK_SIZE=1408
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
# The Bluetooth API should not be used from a preemptive thread:
CONFIG_MAIN_THREAD_PRIORITY=-2

CONFIG_BT=y
CONFIG_BT_TINYCRYPT_ECC=y
Expand All @@ -13,6 +15,7 @@ CONFIG_BT_MESH_SUBNET_COUNT=1
CONFIG_BT_MESH_APP_KEY_COUNT=1
CONFIG_BT_MESH_ADV_BUF_COUNT=10
CONFIG_BT_MESH_TX_SEG_MSG_COUNT=3
CONFIG_BT_MESH_RX_SEG_MAX=32
CONFIG_BT_MESH_MODEL_GROUP_COUNT=2
CONFIG_BT_MESH_LABEL_COUNT=0
CONFIG_BT_MESH_CFG_CLI=y
Expand All @@ -24,7 +27,7 @@ CONFIG_BT_MESH_RELAY_RETRANSMIT_COUNT=3
CONFIG_BT_MESH_PROVISIONER=y
CONFIG_BT_MESH_PROV_DEVICE=n
CONFIG_BT_MESH_CDB=y
CONFIG_BT_MESH_CDB_NODE_COUNT=3
CONFIG_BT_MESH_CDB_NODE_COUNT=16
CONFIG_BT_MESH_CDB_SUBNET_COUNT=3
CONFIG_BT_MESH_CDB_APP_KEY_COUNT=3

Expand Down
95 changes: 67 additions & 28 deletions samples/bluetooth/mesh_provisioner/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ static void setup_cdb(void)
static void configure_self(struct bt_mesh_cdb_node *self)
{
struct bt_mesh_cdb_app_key *key;
uint8_t status = 0;
int err;

printk("Configuring self...\n");
Expand All @@ -97,17 +98,19 @@ static void configure_self(struct bt_mesh_cdb_node *self)

/* Add Application Key */
err = bt_mesh_cfg_app_key_add(self->net_idx, self->addr, self->net_idx,
app_idx, key->keys[0].app_key, NULL);
if (err < 0) {
printk("Failed to add app-key (err %d)\n", err);
app_idx, key->keys[0].app_key, &status);
if (err || status) {
printk("Failed to add app-key (err %d, status %d)\n", err,
status);
return;
}

err = bt_mesh_cfg_mod_app_bind(self->net_idx, self->addr, self->addr,
app_idx, BT_MESH_MODEL_ID_HEALTH_CLI,
NULL);
if (err < 0) {
printk("Failed to bind app-key (err %d)\n", err);
&status);
if (err || status) {
printk("Failed to bind app-key (err %d, status %d)\n", err,
status);
return;
}

Expand All @@ -122,10 +125,12 @@ static void configure_self(struct bt_mesh_cdb_node *self)

static void configure_node(struct bt_mesh_cdb_node *node)
{
NET_BUF_SIMPLE_DEFINE(buf, BT_MESH_RX_SDU_MAX);
struct bt_mesh_comp_p0_elem elem;
struct bt_mesh_cdb_app_key *key;
struct bt_mesh_cfg_mod_pub pub;
struct bt_mesh_comp_p0 comp;
uint8_t status;
int err;
int err, elem_addr;

printk("Configuring node 0x%04x...\n", node->addr);

Expand All @@ -137,35 +142,69 @@ static void configure_node(struct bt_mesh_cdb_node *node)

/* Add Application Key */
err = bt_mesh_cfg_app_key_add(net_idx, node->addr, net_idx, app_idx,
key->keys[0].app_key, NULL);
if (err < 0) {
printk("Failed to add app-key (err %d)\n", err);
key->keys[0].app_key, &status);
if (err || status) {
printk("Failed to add app-key (err %d status %d)\n", err, status);
return;
}

/* Bind to Health model */
err = bt_mesh_cfg_mod_app_bind(net_idx, node->addr, node->addr, app_idx,
BT_MESH_MODEL_ID_HEALTH_SRV, NULL);
if (err < 0) {
printk("Failed to bind app-key (err %d)\n", err);
/* Get the node's composition data and bind all models to the appkey */
err = bt_mesh_cfg_comp_data_get(net_idx, node->addr, 0, &status, &buf);
if (err || status) {
printk("Failed to get Composition data (err %d, status: %d)\n",
err, status);
return;
}

pub.addr = 1;
pub.app_idx = key->app_idx;
pub.cred_flag = false;
pub.ttl = 7;
pub.period = BT_MESH_PUB_PERIOD_10SEC(1);
pub.transmit = 0;

err = bt_mesh_cfg_mod_pub_set(net_idx, node->addr, node->addr,
BT_MESH_MODEL_ID_HEALTH_SRV, &pub,
&status);
if (err < 0) {
printk("mod_pub_set %d, %d\n", err, status);
err = bt_mesh_comp_p0_get(&comp, &buf);
if (err) {
printk("Unable to parse composition data (err: %d)\n", err);
return;
}

elem_addr = node->addr;
while (bt_mesh_comp_p0_elem_pull(&comp, &elem)) {
printk("Element @ 0x%04x: %u + %u models\n", elem_addr,
elem.nsig, elem.nvnd);
for (int i = 0; i < elem.nsig; i++) {
uint16_t id = bt_mesh_comp_p0_elem_mod(&elem, i);

if (id == BT_MESH_MODEL_ID_CFG_CLI ||
id == BT_MESH_MODEL_ID_CFG_SRV) {
continue;
}
printk("Binding AppKey to model 0x%03x:%04x\n",
elem_addr, id);

err = bt_mesh_cfg_mod_app_bind(net_idx, node->addr,
elem_addr, app_idx, id,
&status);
if (err || status) {
printk("Failed (err: %d, status: %d)\n", err,
status);
}
}

for (int i = 0; i < elem.nvnd; i++) {
struct bt_mesh_mod_id_vnd id =
bt_mesh_comp_p0_elem_mod_vnd(&elem, i);

printk("Binding AppKey to model 0x%03x:%04x:%04x\n",
elem_addr, id.company, id.id);

err = bt_mesh_cfg_mod_app_bind_vnd(net_idx, node->addr,
elem_addr, app_idx,
id.id, id.company,
&status);
if (err || status) {
printk("Failed (err: %d, status: %d)\n", err,
status);
}
}

elem_addr++;
}

atomic_set_bit(node->flags, BT_MESH_CDB_NODE_CONFIGURED);

if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
Expand Down
Loading