blob: 4001c7c9236cb882bb713242c339d00d022a1ab2 [file] [log] [blame]
/*
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "mesh_test.h"
#define LOG_MODULE_NAME mesh_test
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
/* Max number of messages that can be pending on RX at the same time */
#define RECV_QUEUE_SIZE 32
const struct bt_mesh_test_cfg *cfg;
K_MEM_SLAB_DEFINE_STATIC(msg_pool, sizeof(struct bt_mesh_test_msg),
RECV_QUEUE_SIZE, 4);
static K_QUEUE_DEFINE(recv);
struct bt_mesh_test_stats test_stats;
struct bt_mesh_msg_ctx test_send_ctx;
static void (*ra_cb)(uint8_t *, size_t);
static int msg_rx(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
size_t len = buf->len + BT_MESH_MODEL_OP_LEN(TEST_MSG_OP_1);
static uint8_t prev_seq;
struct bt_mesh_test_msg *msg;
uint8_t seq = 0;
if (buf->len) {
seq = net_buf_simple_pull_u8(buf);
if (prev_seq == seq) {
FAIL("Received same message twice");
return -EINVAL;
}
prev_seq = seq;
}
LOG_INF("Received packet 0x%02x:", seq);
LOG_INF("\tlen: %d bytes", len);
LOG_INF("\tsrc: 0x%04x", ctx->addr);
LOG_INF("\tdst: 0x%04x", ctx->recv_dst);
LOG_INF("\tttl: %u", ctx->recv_ttl);
LOG_INF("\trssi: %d", ctx->recv_rssi);
for (int i = 1; buf->len; i++) {
if (net_buf_simple_pull_u8(buf) != (i & 0xff)) {
FAIL("Invalid message content (byte %u)", i);
return -EINVAL;
}
}
test_stats.received++;
if (k_mem_slab_alloc(&msg_pool, (void **)&msg, K_NO_WAIT)) {
test_stats.recv_overflow++;
return -EOVERFLOW;
}
msg->len = len;
msg->seq = seq;
msg->ctx = *ctx;
k_queue_append(&recv, msg);
return 0;
}
static int ra_rx(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
LOG_INF("\tlen: %d bytes", buf->len);
LOG_INF("\tsrc: 0x%04x", ctx->addr);
LOG_INF("\tdst: 0x%04x", ctx->recv_dst);
LOG_INF("\tttl: %u", ctx->recv_ttl);
LOG_INF("\trssi: %d", ctx->recv_rssi);
if (ra_cb) {
ra_cb(net_buf_simple_pull_mem(buf, buf->len), buf->len);
}
return 0;
}
static const struct bt_mesh_model_op model_op[] = {
{ TEST_MSG_OP_1, 0, msg_rx },
{ TEST_MSG_OP_2, 0, ra_rx },
BT_MESH_MODEL_OP_END
};
int __weak test_model_pub_update(struct bt_mesh_model *mod)
{
return -1;
}
int __weak test_model_settings_set(struct bt_mesh_model *model,
const char *name, size_t len_rd,
settings_read_cb read_cb, void *cb_arg)
{
return -1;
}
void __weak test_model_reset(struct bt_mesh_model *model)
{
/* No-op. */
}
static const struct bt_mesh_model_cb test_model_cb = {
.settings_set = test_model_settings_set,
.reset = test_model_reset,
};
static struct bt_mesh_model_pub pub = {
.msg = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX),
.update = test_model_pub_update,
};
static const struct bt_mesh_model_op vnd_model_op[] = {
BT_MESH_MODEL_OP_END,
};
int __weak test_vnd_model_pub_update(struct bt_mesh_model *mod)
{
return -1;
}
int __weak test_vnd_model_settings_set(struct bt_mesh_model *model,
const char *name, size_t len_rd,
settings_read_cb read_cb, void *cb_arg)
{
return -1;
}
void __weak test_vnd_model_reset(struct bt_mesh_model *model)
{
/* No-op. */
}
static const struct bt_mesh_model_cb test_vnd_model_cb = {
.settings_set = test_vnd_model_settings_set,
.reset = test_vnd_model_reset,
};
static struct bt_mesh_model_pub vnd_pub = {
.msg = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX),
.update = test_vnd_model_pub_update,
};
static struct bt_mesh_cfg_cli cfg_cli;
static struct bt_mesh_health_srv health_srv;
static struct bt_mesh_model_pub health_pub = {
.msg = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX),
};
static struct bt_mesh_model models[] = {
BT_MESH_MODEL_CFG_SRV,
BT_MESH_MODEL_CFG_CLI(&cfg_cli),
BT_MESH_MODEL_CB(TEST_MOD_ID, model_op, &pub, NULL, &test_model_cb),
BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub),
};
struct bt_mesh_model *test_model = &models[2];
static struct bt_mesh_model vnd_models[] = {
BT_MESH_MODEL_VND_CB(TEST_VND_COMPANY_ID, TEST_VND_MOD_ID, vnd_model_op, &vnd_pub,
NULL, &test_vnd_model_cb),
};
struct bt_mesh_model *test_vnd_model = &vnd_models[0];
static struct bt_mesh_elem elems[] = {
BT_MESH_ELEM(0, models, vnd_models),
};
const struct bt_mesh_comp comp = {
.elem = elems,
.elem_count = ARRAY_SIZE(elems),
};
const uint8_t test_net_key[16] = { 1, 2, 3 };
const uint8_t test_app_key[16] = { 4, 5, 6 };
const uint8_t test_va_uuid[16] = "Mesh Label UUID";
static void bt_mesh_device_provision_and_configure(void)
{
uint8_t status;
int err;
err = bt_mesh_provision(test_net_key, 0, 0, 0, cfg->addr, cfg->dev_key);
if (err == -EALREADY) {
LOG_INF("Using stored settings");
return;
} else if (err) {
FAIL("Provisioning failed (err %d)", err);
return;
}
/* Self configure */
err = bt_mesh_cfg_cli_app_key_add(0, cfg->addr, 0, 0, test_app_key, &status);
if (err || status) {
FAIL("AppKey add failed (err %d, status %u)", err, status);
return;
}
err = bt_mesh_cfg_cli_mod_app_bind(0, cfg->addr, cfg->addr, 0, TEST_MOD_ID, &status);
if (err || status) {
FAIL("Mod app bind failed (err %d, status %u)", err, status);
return;
}
err = bt_mesh_cfg_cli_net_transmit_set(0, cfg->addr, BT_MESH_TRANSMIT(2, 20), &status);
if (err || status != BT_MESH_TRANSMIT(2, 20)) {
FAIL("Net transmit set failed (err %d, status %u)", err,
status);
return;
}
}
void bt_mesh_device_setup(const struct bt_mesh_prov *prov, const struct bt_mesh_comp *comp)
{
int err;
err = bt_enable(NULL);
if (err) {
FAIL("Bluetooth init failed (err %d)", err);
return;
}
LOG_INF("Bluetooth initialized");
err = bt_mesh_init(prov, comp);
if (err) {
FAIL("Initializing mesh failed (err %d)", err);
return;
}
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
LOG_INF("Loading stored settings");
settings_load();
}
LOG_INF("Mesh initialized");
}
void bt_mesh_test_setup(void)
{
static struct bt_mesh_prov prov;
net_buf_simple_init(pub.msg, 0);
net_buf_simple_init(vnd_pub.msg, 0);
bt_mesh_device_setup(&prov, &comp);
bt_mesh_device_provision_and_configure();
}
void bt_mesh_test_timeout(bs_time_t HW_device_time)
{
if (bst_result != Passed) {
FAIL("Test timeout (not passed after %i seconds)",
HW_device_time / USEC_PER_SEC);
}
bs_trace_silent_exit(0);
}
void bt_mesh_test_cfg_set(const struct bt_mesh_test_cfg *my_cfg, int wait_time)
{
bst_ticker_set_next_tick_absolute(wait_time * USEC_PER_SEC);
bst_result = In_progress;
cfg = my_cfg;
}
static struct bt_mesh_test_msg *blocking_recv(k_timeout_t timeout)
{
if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) {
return 0;
}
return k_queue_get(&recv, timeout);
}
int bt_mesh_test_recv(uint16_t len, uint16_t dst, k_timeout_t timeout)
{
struct bt_mesh_test_msg *msg = blocking_recv(timeout);
if (!msg) {
return -ETIMEDOUT;
}
if (len != msg->len) {
LOG_ERR("Recv: Invalid message length (%u, expected %u)", msg->len, len);
return -EINVAL;
}
if (dst != BT_MESH_ADDR_UNASSIGNED && dst != msg->ctx.recv_dst) {
LOG_ERR("Recv: Invalid dst 0x%04x, expected 0x%04x", msg->ctx.recv_dst, dst);
return -EINVAL;
}
k_mem_slab_free(&msg_pool, (void **)&msg);
return 0;
}
int bt_mesh_test_recv_msg(struct bt_mesh_test_msg *msg, k_timeout_t timeout)
{
struct bt_mesh_test_msg *queued = blocking_recv(timeout);
if (!queued) {
return -ETIMEDOUT;
}
*msg = *queued;
k_mem_slab_free(&msg_pool, (void **)&queued);
return 0;
}
int bt_mesh_test_recv_clear(void)
{
struct bt_mesh_test_msg *queued;
int count = 0;
while ((queued = k_queue_get(&recv, K_NO_WAIT))) {
k_mem_slab_free(&msg_pool, (void **)&queued);
count++;
}
return count;
}
struct sync_send_ctx {
struct k_sem sem;
int err;
};
static void tx_started(uint16_t dur, int err, void *data)
{
struct sync_send_ctx *send_ctx = data;
if (err) {
LOG_ERR("Couldn't start sending (err: %d)", err);
send_ctx->err = err;
k_sem_give(&send_ctx->sem);
return;
}
LOG_INF("Sending started");
}
static void tx_ended(int err, void *data)
{
struct sync_send_ctx *send_ctx = data;
send_ctx->err = err;
if (err) {
LOG_ERR("Send failed (%d)", err);
} else {
LOG_INF("Sending ended");
}
k_sem_give(&send_ctx->sem);
}
int bt_mesh_test_send_async(uint16_t addr, size_t len,
enum bt_mesh_test_send_flags flags,
const struct bt_mesh_send_cb *send_cb,
void *cb_data)
{
const size_t mic_len =
(flags & LONG_MIC) ? BT_MESH_MIC_LONG : BT_MESH_MIC_SHORT;
static uint8_t count = 1;
int err;
test_send_ctx.addr = addr;
test_send_ctx.send_rel = (flags & FORCE_SEGMENTATION);
test_send_ctx.send_ttl = BT_MESH_TTL_DEFAULT;
BT_MESH_MODEL_BUF_DEFINE(buf, TEST_MSG_OP_1, BT_MESH_TX_SDU_MAX);
bt_mesh_model_msg_init(&buf, TEST_MSG_OP_1);
if (len > BT_MESH_MODEL_OP_LEN(TEST_MSG_OP_1)) {
net_buf_simple_add_u8(&buf, count);
}
/* Subtract the length of the opcode and the sequence ID */
for (int i = 1; i < len - BT_MESH_MODEL_OP_LEN(TEST_MSG_OP_1); i++) {
net_buf_simple_add_u8(&buf, i);
}
if (net_buf_simple_tailroom(&buf) < mic_len) {
LOG_ERR("No room for MIC of len %u in %u byte buffer", mic_len,
buf.len);
return -EINVAL;
}
/* Seal the buffer to prevent accidentally long MICs: */
buf.size = buf.len + mic_len;
LOG_INF("Sending packet 0x%02x: %u %s to 0x%04x force seg: %u...",
count, buf.len, (buf.len == 1 ? "byte" : "bytes"), addr,
(flags & FORCE_SEGMENTATION));
err = bt_mesh_model_send(test_model, &test_send_ctx, &buf, send_cb,
cb_data);
if (err) {
LOG_ERR("bt_mesh_model_send failed (err: %d)", err);
return err;
}
count++;
test_stats.sent++;
return 0;
}
int bt_mesh_test_send(uint16_t addr, size_t len,
enum bt_mesh_test_send_flags flags, k_timeout_t timeout)
{
if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) {
return bt_mesh_test_send_async(addr, len, flags, NULL, NULL);
}
static const struct bt_mesh_send_cb send_cb = {
.start = tx_started,
.end = tx_ended,
};
int64_t uptime = k_uptime_get();
struct sync_send_ctx send_ctx;
int err;
k_sem_init(&send_ctx.sem, 0, 1);
err = bt_mesh_test_send_async(addr, len, flags, &send_cb, &send_ctx);
if (err) {
return err;
}
err = k_sem_take(&send_ctx.sem, timeout);
if (err) {
LOG_ERR("Send timed out");
return err;
}
if (send_ctx.err) {
return send_ctx.err;
}
LOG_INF("Sending completed (%lld ms)", k_uptime_delta(&uptime));
return 0;
}
int bt_mesh_test_send_ra(uint16_t addr, uint8_t *data, size_t len,
const struct bt_mesh_send_cb *send_cb,
void *cb_data)
{
int err;
test_send_ctx.addr = addr;
test_send_ctx.send_rel = 0;
test_send_ctx.send_ttl = BT_MESH_TTL_DEFAULT;
BT_MESH_MODEL_BUF_DEFINE(buf, TEST_MSG_OP_2, BT_MESH_TX_SDU_MAX);
bt_mesh_model_msg_init(&buf, TEST_MSG_OP_2);
net_buf_simple_add_mem(&buf, data, len);
err = bt_mesh_model_send(test_model, &test_send_ctx, &buf, send_cb, cb_data);
if (err) {
LOG_ERR("bt_mesh_model_send failed (err: %d)", err);
return err;
}
return 0;
}
void bt_mesh_test_ra_cb_setup(void (*cb)(uint8_t *, size_t))
{
ra_cb = cb;
}