/** @file * @brief Bluetooth Mesh shell * */ /* * Copyright (c) 2017 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include "syscfg/syscfg.h" #if MYNEWT_VAL(BLE_MESH_SHELL) #include #include #include #include "shell/shell.h" #include "console/console.h" #include "mesh/mesh.h" #include "mesh/main.h" #include "mesh/glue.h" #include "mesh/testing.h" /* Private includes for raw Network & Transport layer access */ #include "net.h" #include "rpl.h" #include "access.h" #include "mesh_priv.h" #include "lpn.h" #include "transport.h" #include "foundation.h" #include "testing.h" #include "settings.h" #if MYNEWT_VAL(BLE_MESH_SHELL_MODELS) #include "mesh/model_srv.h" #include "mesh/model_cli.h" #include "light_model.h" #endif /* This should be higher priority (lower value) than main task priority */ #define BLE_MESH_SHELL_TASK_PRIO 126 #define BLE_MESH_SHELL_STACK_SIZE 768 OS_TASK_STACK_DEFINE(g_blemesh_shell_stack, BLE_MESH_SHELL_STACK_SIZE); struct os_task mesh_shell_task; static struct os_eventq mesh_shell_queue; #define CID_NVAL 0xffff #define CID_VENDOR 0x05C3 /* Vendor Model data */ #define VND_MODEL_ID_1 0x1234 /* Default net, app & dev key values, unless otherwise specified */ static const uint8_t default_key[16] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, }; static struct { uint16_t local; uint16_t dst; uint16_t net_idx; uint16_t app_idx; } net = { .local = BT_MESH_ADDR_UNASSIGNED, .dst = BT_MESH_ADDR_UNASSIGNED, }; #define CUR_FAULTS_MAX 4 static uint8_t cur_faults[CUR_FAULTS_MAX]; static uint8_t reg_faults[CUR_FAULTS_MAX * 2]; static void get_faults(uint8_t *faults, uint8_t faults_size, uint8_t *dst, uint8_t *count) { uint8_t i, limit = *count; for (i = 0, *count = 0; i < faults_size && *count < limit; i++) { if (faults[i]) { *dst++ = faults[i]; (*count)++; } } } static int fault_get_cur(struct bt_mesh_model *model, uint8_t *test_id, uint16_t *company_id, uint8_t *faults, uint8_t *fault_count) { printk("Sending current faults\n"); *test_id = 0x00; *company_id = CID_VENDOR; get_faults(cur_faults, sizeof(cur_faults), faults, fault_count); return 0; } static int fault_get_reg(struct bt_mesh_model *model, uint16_t cid, uint8_t *test_id, uint8_t *faults, uint8_t *fault_count) { if (cid != CID_VENDOR) { printk("Faults requested for unknown Company ID 0x%04x\n", cid); return -EINVAL; } printk("Sending registered faults\n"); *test_id = 0x00; get_faults(reg_faults, sizeof(reg_faults), faults, fault_count); return 0; } static int fault_clear(struct bt_mesh_model *model, uint16_t cid) { if (cid != CID_VENDOR) { return -EINVAL; } memset(reg_faults, 0, sizeof(reg_faults)); return 0; } static int fault_test(struct bt_mesh_model *model, uint8_t test_id, uint16_t cid) { if (cid != CID_VENDOR) { return -EINVAL; } if (test_id != 0x00) { return -EINVAL; } return 0; } static const struct bt_mesh_health_srv_cb health_srv_cb = { .fault_get_cur = fault_get_cur, .fault_get_reg = fault_get_reg, .fault_clear = fault_clear, .fault_test = fault_test, }; static struct bt_mesh_health_srv health_srv = { .cb = &health_srv_cb, }; static struct bt_mesh_model_pub health_pub; static void health_pub_init(void) { health_pub.msg = BT_MESH_HEALTH_FAULT_MSG(CUR_FAULTS_MAX); } #if MYNEWT_VAL(BLE_MESH_CFG_CLI) static struct bt_mesh_cfg_cli cfg_cli = { }; #endif /* MYNEWT_VAL(BLE_MESH_CFG_CLI) */ #if MYNEWT_VAL(BLE_MESH_HEALTH_CLI) void show_faults(uint8_t test_id, uint16_t cid, uint8_t *faults, size_t fault_count) { size_t i; if (!fault_count) { printk("Health Test ID 0x%02x Company ID 0x%04x: no faults\n", test_id, cid); return; } printk("Health Test ID 0x%02x Company ID 0x%04x Fault Count %zu:\n", test_id, cid, fault_count); for (i = 0; i < fault_count; i++) { printk("\t0x%02x\n", faults[i]); } } static void health_current_status(struct bt_mesh_health_cli *cli, uint16_t addr, uint8_t test_id, uint16_t cid, uint8_t *faults, size_t fault_count) { printk("Health Current Status from 0x%04x\n", addr); show_faults(test_id, cid, faults, fault_count); } static struct bt_mesh_health_cli health_cli = { .current_status = health_current_status, }; #endif /* MYNEWT_VAL(BLE_MESH_HEALTH_CLI) */ #if MYNEWT_VAL(BLE_MESH_SHELL_MODELS) static struct bt_mesh_gen_model_cli gen_onoff_cli; static struct bt_mesh_model_pub gen_onoff_cli_pub; static struct bt_mesh_model_pub gen_onoff_srv_pub; static struct bt_mesh_gen_model_cli gen_level_cli; static struct bt_mesh_model_pub gen_level_cli_pub; static struct bt_mesh_model_pub gen_level_srv_pub; static struct bt_mesh_model_pub light_lightness_pub; static struct bt_mesh_gen_onoff_srv gen_onoff_srv = { .get = light_model_gen_onoff_get, .set = light_model_gen_onoff_set, }; static struct bt_mesh_gen_level_srv gen_level_srv = { .get = light_model_gen_level_get, .set = light_model_gen_level_set, }; static struct bt_mesh_light_lightness_srv light_lightness_srv = { .get = light_model_light_lightness_get, .set = light_model_light_lightness_set, }; void bt_mesh_set_gen_onoff_srv_cb(int (*get)(struct bt_mesh_model *model, uint8_t *state), int (*set)(struct bt_mesh_model *model, uint8_t state)) { gen_onoff_srv.get = get; gen_onoff_srv.set = set; } void bt_mesh_set_gen_level_srv_cb(int (*get)(struct bt_mesh_model *model, int16_t *level), int (*set)(struct bt_mesh_model *model, int16_t level)) { gen_level_srv.get = get; gen_level_srv.set = set; } void bt_mesh_set_light_lightness_srv_cb(int (*get)(struct bt_mesh_model *model, int16_t *level), int (*set)(struct bt_mesh_model *model, int16_t level)) { light_lightness_srv.get = get; light_lightness_srv.set = set; } #endif static struct bt_mesh_model root_models[] = { BT_MESH_MODEL_CFG_SRV, BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub), #if MYNEWT_VAL(BLE_MESH_CFG_CLI) BT_MESH_MODEL_CFG_CLI(&cfg_cli), #endif #if MYNEWT_VAL(BLE_MESH_HEALTH_CLI) BT_MESH_MODEL_HEALTH_CLI(&health_cli), #endif #if MYNEWT_VAL(BLE_MESH_SHELL_MODELS) BT_MESH_MODEL_GEN_ONOFF_SRV(&gen_onoff_srv, &gen_onoff_srv_pub), BT_MESH_MODEL_GEN_ONOFF_CLI(&gen_onoff_cli, &gen_onoff_cli_pub), BT_MESH_MODEL_GEN_LEVEL_SRV(&gen_level_srv, &gen_level_srv_pub), BT_MESH_MODEL_GEN_LEVEL_CLI(&gen_level_cli, &gen_level_cli_pub), BT_MESH_MODEL_LIGHT_LIGHTNESS_SRV(&light_lightness_srv, &light_lightness_pub), #endif }; static struct bt_mesh_model vnd_models[] = { BT_MESH_MODEL_VND(CID_VENDOR, VND_MODEL_ID_1, BT_MESH_MODEL_NO_OPS, NULL, NULL), }; static struct bt_mesh_elem elements[] = { BT_MESH_ELEM(0, root_models, vnd_models), }; static const struct bt_mesh_comp comp = { .cid = CID_VENDOR, .elem = elements, .elem_count = ARRAY_SIZE(elements), }; static uint8_t hex2val(char c) { if (c >= '0' && c <= '9') { return c - '0'; } else if (c >= 'a' && c <= 'f') { return c - 'a' + 10; } else if (c >= 'A' && c <= 'F') { return c - 'A' + 10; } else { return 0; } } int char2hex(char c, uint8_t *x) { if (c >= '0' && c <= '9') { *x = c - '0'; } else if (c >= 'a' && c <= 'f') { *x = c - 'a' + 10; } else if (c >= 'A' && c <= 'F') { *x = c - 'A' + 10; } else { return -EINVAL; } return 0; } int hex2char(uint8_t x, char *c) { if (x <= 9) { *c = x + '0'; } else if (x <= 15) { *c = x - 10 + 'a'; } else { return -EINVAL; } return 0; } size_t bin2hex(const uint8_t *buf, size_t buflen, char *hex, size_t hexlen) { if ((hexlen + 1) < buflen * 2) { return 0; } for (size_t i = 0; i < buflen; i++) { if (hex2char(buf[i] >> 4, &hex[2 * i]) < 0) { return 0; } if (hex2char(buf[i] & 0xf, &hex[2 * i + 1]) < 0) { return 0; } } hex[2 * buflen] = '\0'; return 2 * buflen; } static size_t hex2bin(const char *hex, uint8_t *bin, size_t bin_len) { size_t len = 0; while (*hex && len < bin_len) { bin[len] = hex2val(*hex++) << 4; if (!*hex) { len++; break; } bin[len++] |= hex2val(*hex++); } return len; } static void prov_complete(uint16_t net_idx, uint16_t addr) { printk("Local node provisioned, net_idx 0x%04x address 0x%04x\n", net_idx, addr); net.local = addr; net.net_idx = net_idx, net.dst = addr; } static void prov_node_added(uint16_t net_idx, uint8_t uuid[16], uint16_t addr, uint8_t num_elem) { printk("Node provisioned, net_idx 0x%04x address " "0x%04x elements %d", net_idx, addr, num_elem); net.net_idx = net_idx, net.dst = addr; } static void prov_input_complete(void) { printk("Input complete"); } static void prov_reset(void) { printk("The local node has been reset and needs reprovisioning\n"); } static int output_number(bt_mesh_output_action_t action, uint32_t number) { printk("OOB Number: %lu\n", number); return 0; } static int output_string(const char *str) { printk("OOB String: %s\n", str); return 0; } static bt_mesh_input_action_t input_act; static uint8_t input_size; static int cmd_input_num(int argc, char *argv[]) { int err; if (argc < 2) { return -EINVAL; } if (input_act != BT_MESH_ENTER_NUMBER) { printk("A number hasn't been requested!\n"); return 0; } if (strlen(argv[1]) < input_size) { printk("Too short input (%u digits required)\n", input_size); return 0; } err = bt_mesh_input_number(strtoul(argv[1], NULL, 10)); if (err) { printk("Numeric input failed (err %d)\n", err); return 0; } input_act = BT_MESH_NO_INPUT; return 0; } struct shell_cmd_help cmd_input_num_help = { NULL, "", NULL }; static int cmd_input_str(int argc, char *argv[]) { int err; if (argc < 2) { return -EINVAL; } if (input_act != BT_MESH_ENTER_STRING) { printk("A string hasn't been requested!\n"); return 0; } if (strlen(argv[1]) < input_size) { printk("Too short input (%u characters required)\n", input_size); return 0; } err = bt_mesh_input_string(argv[1]); if (err) { printk("String input failed (err %d)\n", err); return 0; } input_act = BT_MESH_NO_INPUT; return 0; } struct shell_cmd_help cmd_input_str_help = { NULL, "", NULL }; static int input(bt_mesh_input_action_t act, uint8_t size) { switch (act) { case BT_MESH_ENTER_NUMBER: printk("Enter a number (max %u digits) with: input-num \n", size); break; case BT_MESH_ENTER_STRING: printk("Enter a string (max %u chars) with: input-str \n", size); break; default: printk("Unknown input action %u (size %u) requested!\n", act, size); return -EINVAL; } input_act = act; input_size = size; return 0; } static const char *bearer2str(bt_mesh_prov_bearer_t bearer) { switch (bearer) { case BT_MESH_PROV_ADV: return "PB-ADV"; case BT_MESH_PROV_GATT: return "PB-GATT"; default: return "unknown"; } } static void link_open(bt_mesh_prov_bearer_t bearer) { printk("Provisioning link opened on %s\n", bearer2str(bearer)); } static void link_close(bt_mesh_prov_bearer_t bearer) { printk("Provisioning link closed on %s\n", bearer2str(bearer)); } static uint8_t dev_uuid[16] = MYNEWT_VAL(BLE_MESH_DEV_UUID); static uint8_t static_val[16]; static struct bt_mesh_prov prov = { .uuid = dev_uuid, .link_open = link_open, .link_close = link_close, .complete = prov_complete, .node_added = prov_node_added, .reset = prov_reset, .static_val = NULL, .static_val_len = 0, .output_size = MYNEWT_VAL(BLE_MESH_OOB_OUTPUT_SIZE), .output_actions = MYNEWT_VAL(BLE_MESH_OOB_OUTPUT_ACTIONS), .output_number = output_number, .output_string = output_string, .input_size = MYNEWT_VAL(BLE_MESH_OOB_INPUT_SIZE), .input_actions = MYNEWT_VAL(BLE_MESH_OOB_INPUT_ACTIONS), .input = input, .input_complete = prov_input_complete, }; static int cmd_static_oob(int argc, char *argv[]) { if (argc < 2) { prov.static_val = NULL; prov.static_val_len = 0; } else { prov.static_val_len = hex2bin(argv[1], static_val, 16); if (prov.static_val_len) { prov.static_val = static_val; } else { prov.static_val = NULL; } } if (prov.static_val) { printk("Static OOB value set (length %u)\n", prov.static_val_len); } else { printk("Static OOB value cleared\n"); } return 0; } struct shell_cmd_help cmd_static_oob_help = { NULL, "[val: 1-16 hex values]", NULL }; static int cmd_uuid(int argc, char *argv[]) { uint8_t uuid[16]; size_t len; if (argc < 2) { return -EINVAL; } len = hex2bin(argv[1], uuid, sizeof(uuid)); if (len < 1) { return -EINVAL; } memcpy(dev_uuid, uuid, len); memset(dev_uuid + len, 0, sizeof(dev_uuid) - len); printk("Device UUID set\n"); return 0; } struct shell_cmd_help cmd_uuid_help = { NULL, "", NULL }; static int cmd_reset(int argc, char *argv[]) { uint16_t addr; if (argc < 2) { return -EINVAL; } addr = strtoul(argv[1], NULL, 0); if (addr == net.local) { bt_mesh_reset(); printk("Local node reset complete"); } else { int err; bool reset = false; err = bt_mesh_cfg_node_reset(net.net_idx, net.dst, &reset); if (err) { printk("Unable to send " "Remote Node Reset (err %d)", err); return 0; } printk("Remote node reset complete"); } return 0; } struct shell_cmd_help cmd_reset_help = { NULL, "", NULL }; static uint8_t str2u8(const char *str) { if (isdigit(str[0])) { return strtoul(str, NULL, 0); } return (!strcmp(str, "on") || !strcmp(str, "enable")); } static bool str2bool(const char *str) { return str2u8(str); } #if MYNEWT_VAL(BLE_MESH_LOW_POWER) static int cmd_lpn(int argc, char *argv[]) { static bool enabled; int err; if (argc < 2) { printk("%s\n", enabled ? "enabled" : "disabled"); return 0; } if (str2bool(argv[1])) { if (enabled) { printk("LPN already enabled\n"); return 0; } err = bt_mesh_lpn_set(true); if (err) { printk("Enabling LPN failed (err %d)\n", err); } else { enabled = true; } } else { if (!enabled) { printk("LPN already disabled\n"); return 0; } err = bt_mesh_lpn_set(false); if (err) { printk("Enabling LPN failed (err %d)\n", err); } else { enabled = false; } } return 0; } static int cmd_poll(int argc, char *argv[]) { int err; err = bt_mesh_lpn_poll(); if (err) { printk("Friend Poll failed (err %d)\n", err); } return 0; } static void lpn_cb(uint16_t friend_addr, bool established) { if (established) { printk("Friendship (as LPN) established to Friend 0x%04x\n", friend_addr); } else { printk("Friendship (as LPN) lost with Friend 0x%04x\n", friend_addr); } } struct shell_cmd_help cmd_lpn_help = { NULL, "", NULL }; #endif /* MESH_LOW_POWER */ static int check_pub_addr_unassigned(void) { #ifdef ARCH_sim return 0; #else uint8_t zero_addr[BLE_DEV_ADDR_LEN] = { 0 }; return memcmp(MYNEWT_VAL(BLE_PUBLIC_DEV_ADDR), zero_addr, BLE_DEV_ADDR_LEN) == 0; #endif } int cmd_mesh_init(int argc, char *argv[]) { int err; ble_addr_t addr; if (check_pub_addr_unassigned()) { /* Use NRPA */ err = ble_hs_id_gen_rnd(1, &addr); assert(err == 0); err = ble_hs_id_set_rnd(addr.val); assert(err == 0); err = bt_mesh_init(addr.type, &prov, &comp); } else { err = bt_mesh_init(0, &prov, &comp); } if (err) { printk("Mesh initialization failed (err %d)\n", err); } printk("Mesh initialized\n"); if (IS_ENABLED(CONFIG_SETTINGS)) { settings_load(); } if (bt_mesh_is_provisioned()) { printk("Mesh network restored from flash\n"); } else { printk("Use \"pb-adv on\" or \"pb-gatt on\" to enable" " advertising\n"); } #if MYNEWT_VAL(BLE_MESH_LOW_POWER) bt_mesh_lpn_set_cb(lpn_cb); #endif return 0; } #if MYNEWT_VAL(BLE_MESH_GATT_PROXY) static int cmd_ident(int argc, char *argv[]) { int err; err = bt_mesh_proxy_identity_enable(); if (err) { printk("Failed advertise using Node Identity (err %d)\n", err); } return 0; } #endif /* MESH_GATT_PROXY */ static int cmd_dst(int argc, char *argv[]) { if (argc < 2) { printk("Destination address: 0x%04x%s\n", net.dst, net.dst == net.local ? " (local)" : ""); return 0; } if (!strcmp(argv[1], "local")) { net.dst = net.local; } else { net.dst = strtoul(argv[1], NULL, 0); } printk("Destination address set to 0x%04x%s\n", net.dst, net.dst == net.local ? " (local)" : ""); return 0; } struct shell_cmd_help cmd_dst_help = { NULL, "[destination address]", NULL }; static int cmd_netidx(int argc, char *argv[]) { if (argc < 2) { printk("NetIdx: 0x%04x\n", net.net_idx); return 0; } net.net_idx = strtoul(argv[1], NULL, 0); printk("NetIdx set to 0x%04x\n", net.net_idx); return 0; } struct shell_cmd_help cmd_netidx_help = { NULL, "[NetIdx]", NULL }; static int cmd_appidx(int argc, char *argv[]) { if (argc < 2) { printk("AppIdx: 0x%04x\n", net.app_idx); return 0; } net.app_idx = strtoul(argv[1], NULL, 0); printk("AppIdx set to 0x%04x\n", net.app_idx); return 0; } struct shell_cmd_help cmd_appidx_help = { NULL, "[AppIdx]", NULL }; static int cmd_net_send(int argc, char *argv[]) { struct os_mbuf *msg = NET_BUF_SIMPLE(32); struct bt_mesh_msg_ctx ctx = { .send_ttl = BT_MESH_TTL_DEFAULT, .net_idx = net.net_idx, .addr = net.dst, .app_idx = net.app_idx, }; struct bt_mesh_net_tx tx = { .ctx = &ctx, .src = net.local, }; size_t len; int err = 0; if (argc < 2) { err = -EINVAL; goto done; } net_buf_simple_init(msg, 0); len = hex2bin(argv[1], msg->om_data, net_buf_simple_tailroom(msg) - 4); net_buf_simple_add(msg, len); err = bt_mesh_trans_send(&tx, msg, NULL, NULL); if (err) { printk("Failed to send (err %d)\n", err); } done: os_mbuf_free_chain(msg); return err; } struct shell_cmd_help cmd_net_send_help = { NULL, "", NULL }; static int cmd_rpl_clear(int argc, char *argv[]) { bt_mesh_rpl_clear(); return 0; } #if MYNEWT_VAL(BLE_MESH_LOW_POWER) static int cmd_lpn_subscribe(int argc, char *argv[]) { uint16_t address; if (argc < 2) { return -EINVAL; } address = strtoul(argv[1], NULL, 0); printk("address 0x%04x", address); bt_mesh_lpn_group_add(address); return 0; } struct shell_cmd_help cmd_lpn_subscribe_help = { NULL, "", NULL }; static int cmd_lpn_unsubscribe(int argc, char *argv[]) { uint16_t address; if (argc < 2) { return -EINVAL; } address = strtoul(argv[1], NULL, 0); printk("address 0x%04x", address); bt_mesh_lpn_group_del(&address, 1); return 0; } struct shell_cmd_help cmd_lpn_unsubscribe_help = { NULL, "", NULL }; #endif #if MYNEWT_VAL(BLE_MESH_IV_UPDATE_TEST) static int cmd_iv_update(int argc, char *argv[]) { if (bt_mesh_iv_update()) { printk("Transitioned to IV Update In Progress state\n"); } else { printk("Transitioned to IV Update Normal state\n"); } printk("IV Index is 0x%08lx\n", bt_mesh.iv_index); return 0; } static int cmd_iv_update_test(int argc, char *argv[]) { bool enable; if (argc < 2) { return -EINVAL; } enable = str2bool(argv[1]); if (enable) { printk("Enabling IV Update test mode\n"); } else { printk("Disabling IV Update test mode\n"); } bt_mesh_iv_update_test(enable); return 0; } struct shell_cmd_help cmd_iv_update_test_help = { NULL, "", NULL }; #endif #if MYNEWT_VAL(BLE_MESH_CFG_CLI) int cmd_timeout(int argc, char *argv[]) { int32_t timeout; if (argc < 2) { timeout = bt_mesh_cfg_cli_timeout_get(); if (timeout == K_FOREVER) { printk("Message timeout: forever\n"); } else { printk("Message timeout: %lu seconds\n", timeout / 1000); } return 0; } timeout = strtol(argv[1], NULL, 0); if (timeout < 0 || timeout > (INT32_MAX / 1000)) { timeout = K_FOREVER; } else { timeout = timeout * 1000; } bt_mesh_cfg_cli_timeout_set(timeout); if (timeout == K_FOREVER) { printk("Message timeout: forever\n"); } else { printk("Message timeout: %lu seconds\n", timeout / 1000); } return 0; } struct shell_cmd_help cmd_timeout_help = { NULL, "[timeout in seconds]", NULL }; static int cmd_get_comp(int argc, char *argv[]) { struct os_mbuf *comp = NET_BUF_SIMPLE(32); uint8_t status, page = 0x00; int err = 0; if (argc > 1) { page = strtol(argv[1], NULL, 0); } net_buf_simple_init(comp, 0); err = bt_mesh_cfg_comp_data_get(net.net_idx, net.dst, page, &status, comp); if (err) { printk("Getting composition failed (err %d)\n", err); goto done; } if (status != 0x00) { printk("Got non-success status 0x%02x\n", status); goto done; } printk("Got Composition Data for 0x%04x:\n", net.dst); printk("\tCID 0x%04x\n", net_buf_simple_pull_le16(comp)); printk("\tPID 0x%04x\n", net_buf_simple_pull_le16(comp)); printk("\tVID 0x%04x\n", net_buf_simple_pull_le16(comp)); printk("\tCRPL 0x%04x\n", net_buf_simple_pull_le16(comp)); printk("\tFeatures 0x%04x\n", net_buf_simple_pull_le16(comp)); while (comp->om_len > 4) { uint8_t sig, vnd; uint16_t loc; int i; loc = net_buf_simple_pull_le16(comp); sig = net_buf_simple_pull_u8(comp); vnd = net_buf_simple_pull_u8(comp); printk("\n\tElement @ 0x%04x:\n", loc); if (comp->om_len < ((sig * 2) + (vnd * 4))) { printk("\t\t...truncated data!\n"); break; } if (sig) { printk("\t\tSIG Models:\n"); } else { printk("\t\tNo SIG Models\n"); } for (i = 0; i < sig; i++) { uint16_t mod_id = net_buf_simple_pull_le16(comp); printk("\t\t\t0x%04x\n", mod_id); } if (vnd) { printk("\t\tVendor Models:\n"); } else { printk("\t\tNo Vendor Models\n"); } for (i = 0; i < vnd; i++) { uint16_t cid = net_buf_simple_pull_le16(comp); uint16_t mod_id = net_buf_simple_pull_le16(comp); printk("\t\t\tCompany 0x%04x: 0x%04x\n", cid, mod_id); } } done: os_mbuf_free_chain(comp); return err; } struct shell_cmd_help cmd_get_comp_help = { NULL, "[page]", NULL }; static int cmd_beacon(int argc, char *argv[]) { uint8_t status; int err; if (argc < 2) { err = bt_mesh_cfg_beacon_get(net.net_idx, net.dst, &status); } else { uint8_t val = str2u8(argv[1]); err = bt_mesh_cfg_beacon_set(net.net_idx, net.dst, val, &status); } if (err) { printk("Unable to send Beacon Get/Set message (err %d)\n", err); return 0; } printk("Beacon state is 0x%02x\n", status); return 0; } static void print_unprovisioned_beacon(uint8_t uuid[16], bt_mesh_prov_oob_info_t oob_info, uint32_t *uri_hash) { char uuid_hex_str[32 + 1]; bin2hex(uuid, 16, uuid_hex_str, sizeof(uuid_hex_str)); printk("UUID %s, OOB Info 0x%04x, URI Hash 0x%lx", uuid_hex_str, oob_info, (uri_hash == NULL ? 0 : *uri_hash)); } static int cmd_beacon_listen(int argc, char *argv[]) { uint8_t val = str2u8(argv[1]); if (val) { prov.unprovisioned_beacon = print_unprovisioned_beacon; } else { prov.unprovisioned_beacon = NULL; } return 0; } struct shell_cmd_help cmd_beacon_help = { NULL, "[val: off, on]", NULL }; struct shell_cmd_help cmd_beacon_listen_help = { NULL, "[val: off, on]", NULL }; static int cmd_ttl(int argc, char *argv[]) { uint8_t ttl; int err; if (argc < 2) { err = bt_mesh_cfg_ttl_get(net.net_idx, net.dst, &ttl); } else { uint8_t val = strtoul(argv[1], NULL, 0); err = bt_mesh_cfg_ttl_set(net.net_idx, net.dst, val, &ttl); } if (err) { printk("Unable to send Default TTL Get/Set (err %d)\n", err); return 0; } printk("Default TTL is 0x%02x\n", ttl); return 0; } struct shell_cmd_help cmd_ttl_help = { NULL, "[ttl: 0x00, 0x02-0x7f]", NULL }; static int cmd_friend(int argc, char *argv[]) { uint8_t frnd; int err; if (argc < 2) { err = bt_mesh_cfg_friend_get(net.net_idx, net.dst, &frnd); } else { uint8_t val = str2u8(argv[1]); err = bt_mesh_cfg_friend_set(net.net_idx, net.dst, val, &frnd); } if (err) { printk("Unable to send Friend Get/Set (err %d)\n", err); return 0; } printk("Friend is set to 0x%02x\n", frnd); return 0; } struct shell_cmd_help cmd_friend_help = { NULL, "[val: off, on]", NULL }; static int cmd_gatt_proxy(int argc, char *argv[]) { uint8_t proxy; int err; if (argc < 2) { err = bt_mesh_cfg_gatt_proxy_get(net.net_idx, net.dst, &proxy); } else { uint8_t val = str2u8(argv[1]); err = bt_mesh_cfg_gatt_proxy_set(net.net_idx, net.dst, val, &proxy); } if (err) { printk("Unable to send GATT Proxy Get/Set (err %d)\n", err); return 0; } printk("GATT Proxy is set to 0x%02x\n", proxy); return 0; } struct shell_cmd_help cmd_gatt_proxy_help = { NULL, "[val: off, on]", NULL }; static int cmd_net_transmit(int argc, char *argv[]) { uint8_t transmit; int err; if (argc < 2) { err = bt_mesh_cfg_net_transmit_get(net.net_idx, net.dst, &transmit); } else { if (argc != 3) { printk("Wrong number of input arguments" "(2 arguments are required)"); return -EINVAL; } uint8_t count, interval, new_transmit; count = strtoul(argv[1], NULL, 0); interval = strtoul(argv[2], NULL, 0); new_transmit = BT_MESH_TRANSMIT(count, interval); err = bt_mesh_cfg_net_transmit_set(net.net_idx, net.dst, new_transmit, &transmit); } if (err) { printk("Unable to send network transmit" " Get/Set (err %d)", err); return 0; } printk("Transmit 0x%02x (count %u interval %ums)", transmit, BT_MESH_TRANSMIT_COUNT(transmit), BT_MESH_TRANSMIT_INT(transmit)); return 0; } struct shell_cmd_help cmd_net_transmit_help = { NULL, "[ ]", NULL }; static int cmd_relay(int argc, char *argv[]) { uint8_t relay, transmit; int err; if (argc < 2) { err = bt_mesh_cfg_relay_get(net.net_idx, net.dst, &relay, &transmit); } else { uint8_t val = str2u8(argv[1]); uint8_t count, interval, new_transmit; if (val) { if (argc > 2) { count = strtoul(argv[2], NULL, 0); } else { count = 2; } if (argc > 3) { interval = strtoul(argv[3], NULL, 0); } else { interval = 20; } new_transmit = BT_MESH_TRANSMIT(count, interval); } else { new_transmit = 0; } err = bt_mesh_cfg_relay_set(net.net_idx, net.dst, val, new_transmit, &relay, &transmit); } if (err) { printk("Unable to send Relay Get/Set (err %d)\n", err); return 0; } printk("Relay is 0x%02x, Transmit 0x%02x (count %u interval %ums)\n", relay, transmit, BT_MESH_TRANSMIT_COUNT(transmit), BT_MESH_TRANSMIT_INT(transmit)); return 0; } struct shell_cmd_help cmd_relay_help = { NULL, "[val: off, on] [count: 0-7] [interval: 0-32]", NULL }; static int cmd_net_key_add(int argc, char *argv[]) { bool has_key_val = (argc > 2); uint8_t key_val[16]; uint16_t key_net_idx; uint8_t status; int err; if (argc < 2) { return -EINVAL; } key_net_idx = strtoul(argv[1], NULL, 0); if (has_key_val) { size_t len; len = hex2bin(argv[3], key_val, sizeof(key_val)); memset(key_val, 0, sizeof(key_val) - len); } else { memcpy(key_val, default_key, sizeof(key_val)); } if (IS_ENABLED(CONFIG_BT_MESH_CDB)) { struct bt_mesh_cdb_subnet *subnet; subnet = bt_mesh_cdb_subnet_get(key_net_idx); if (subnet) { if (has_key_val) { printk("Subnet 0x%03x already has a value", key_net_idx); return 0; } memcpy(key_val, subnet->keys[0].net_key, 16); } else { subnet = bt_mesh_cdb_subnet_alloc(key_net_idx); if (!subnet) { printk("No space for subnet in cdb"); return 0; } memcpy(subnet->keys[0].net_key, key_val, 16); bt_mesh_cdb_subnet_store(subnet); } } err = bt_mesh_cfg_net_key_add(net.net_idx, net.dst, key_net_idx, key_val, &status); if (err) { printk("Unable to send NetKey Add (err %d)\n", err); return 0; } if (status) { printk("NetKeyAdd failed with status 0x%02x\n", status); } else { printk("NetKey added with NetKey Index 0x%03x\n", key_net_idx); } return 0; } struct shell_cmd_help cmd_net_key_add_help = { NULL, " [val]", NULL }; static int cmd_net_key_get(int argc, char *argv[]) { uint16_t keys[16]; size_t cnt; int err, i; cnt = ARRAY_SIZE(keys); err = bt_mesh_cfg_net_key_get(net.net_idx, net.dst, keys, &cnt); if (err) { printk("Unable to send NetKeyGet (err %d)", err); return 0; } printk("NetKeys known by 0x%04x:", net.dst); for (i = 0; i < cnt; i++) { printk("\t0x%03x", keys[i]); } return 0; } struct shell_cmd_help cmd_net_key_get_help = { NULL, NULL, NULL }; static int cmd_net_key_del(int argc, char *argv[]) { uint16_t key_net_idx; uint8_t status; int err; key_net_idx = strtoul(argv[1], NULL, 0); err = bt_mesh_cfg_net_key_del(net.net_idx, net.dst, key_net_idx, &status); if (err) { printk("Unable to send NetKeyDel (err %d)", err); return 0; } if (status) { printk("NetKeyDel failed with status 0x%02x", status); } else { printk("NetKey 0x%03x deleted", key_net_idx); } return 0; } struct shell_cmd_help cmd_net_key_del_help = { NULL, "", NULL }; static int cmd_app_key_add(int argc, char *argv[]) { uint8_t key_val[16]; uint16_t key_net_idx, key_app_idx; bool has_key_val = (argc > 3); uint8_t status; int err; if (argc < 3) { return -EINVAL; } key_net_idx = strtoul(argv[1], NULL, 0); key_app_idx = strtoul(argv[2], NULL, 0); if (has_key_val) { size_t len; len = hex2bin(argv[3], key_val, sizeof(key_val)); memset(key_val, 0, sizeof(key_val) - len); } else { memcpy(key_val, default_key, sizeof(key_val)); } if (IS_ENABLED(CONFIG_BT_MESH_CDB)) { struct bt_mesh_cdb_app_key *app_key; app_key = bt_mesh_cdb_app_key_get(key_app_idx); if (app_key) { if (has_key_val) { printk("App key 0x%03x already has a value", key_app_idx); return 0; } memcpy(key_val, app_key->keys[0].app_key, 16); } else { app_key = bt_mesh_cdb_app_key_alloc(key_net_idx, key_app_idx); if (!app_key) { printk("No space for app key in cdb"); return 0; } memcpy(app_key->keys[0].app_key, key_val, 16); bt_mesh_cdb_app_key_store(app_key); } } err = bt_mesh_cfg_app_key_add(net.net_idx, net.dst, key_net_idx, key_app_idx, key_val, &status); if (err) { printk("Unable to send App Key Add (err %d)\n", err); return 0; } if (status) { printk("AppKeyAdd failed with status 0x%02x\n", status); } else { printk("AppKey added, NetKeyIndex 0x%04x AppKeyIndex 0x%04x\n", key_net_idx, key_app_idx); } return 0; } struct shell_cmd_help cmd_app_key_add_help = { NULL, " [val]", NULL }; static int cmd_app_key_get(int argc, char *argv[]) { uint16_t net_idx; uint16_t keys[16]; size_t cnt; uint8_t status; int err, i; net_idx = strtoul(argv[1], NULL, 0); cnt = ARRAY_SIZE(keys); err = bt_mesh_cfg_app_key_get(net.net_idx, net.dst, net_idx, &status, keys, &cnt); if (err) { printk("Unable to send AppKeyGet (err %d)", err); return 0; } if (status) { printk("AppKeyGet failed with status 0x%02x", status); return 0; } printk( "AppKeys for NetKey 0x%03x known by 0x%04x:", net_idx, net.dst); for (i = 0; i < cnt; i++) { printk("\t0x%03x", keys[i]); } return 0; } struct shell_cmd_help cmd_app_key_get_help = { NULL, "", NULL }; static int cmd_app_key_del(int argc, char *argv[]) { uint16_t key_net_idx, key_app_idx; uint8_t status; int err; if (argc < 3) { return -EINVAL; } key_net_idx = strtoul(argv[1], NULL, 0); key_app_idx = strtoul(argv[2], NULL, 0); err = bt_mesh_cfg_app_key_del(net.net_idx, net.dst, key_net_idx, key_app_idx, &status); if (err) { printk("Unable to send App Key del(err %d)", err); return 0; } if (status) { printk("AppKeyDel failed with status 0x%02x", status); } else { printk("AppKey deleted, NetKeyIndex 0x%04x " "AppKeyIndex 0x%04x", key_net_idx, key_app_idx); } return 0; } struct shell_cmd_help cmd_app_key_del_help = { NULL, " ", NULL }; static int cmd_mod_app_bind(int argc, char *argv[]) { uint16_t elem_addr, mod_app_idx, mod_id, cid; uint8_t status; int err; if (argc < 4) { return -EINVAL; } elem_addr = strtoul(argv[1], NULL, 0); mod_app_idx = strtoul(argv[2], NULL, 0); mod_id = strtoul(argv[3], NULL, 0); if (argc > 4) { cid = strtoul(argv[4], NULL, 0); err = bt_mesh_cfg_mod_app_bind_vnd(net.net_idx, net.dst, elem_addr, mod_app_idx, mod_id, cid, &status); } else { err = bt_mesh_cfg_mod_app_bind(net.net_idx, net.dst, elem_addr, mod_app_idx, mod_id, &status); } if (err) { printk("Unable to send Model App Bind (err %d)\n", err); return 0; } if (status) { printk("Model App Bind failed with status 0x%02x\n", status); } else { printk("AppKey successfully bound\n"); } return 0; } struct shell_cmd_help cmd_mod_app_bind_help = { NULL, " [Company ID]", NULL }; static int cmd_mod_app_unbind(int argc, char *argv[]) { uint16_t elem_addr, mod_app_idx, mod_id, cid; uint8_t status; int err; if (argc < 4) { return -EINVAL; } elem_addr = strtoul(argv[1], NULL, 0); mod_app_idx = strtoul(argv[2], NULL, 0); mod_id = strtoul(argv[3], NULL, 0); if (argc > 4) { cid = strtoul(argv[4], NULL, 0); err = bt_mesh_cfg_mod_app_unbind_vnd(net.net_idx, net.dst, elem_addr, mod_app_idx, mod_id, cid, &status); } else { err = bt_mesh_cfg_mod_app_unbind(net.net_idx, net.dst, elem_addr, mod_app_idx, mod_id, &status); } if (err) { printk("Unable to send Model App Unbind (err %d)", err); return 0; } if (status) { printk("Model App Unbind failed with status 0x%02x", status); } else { printk("AppKey successfully unbound"); } return 0; } struct shell_cmd_help cmd_mod_app_unbind_help = { NULL, " [Company ID]", NULL }; static int cmd_mod_app_get(int argc, char *argv[]) { uint16_t elem_addr, mod_id, cid; uint16_t apps[16]; uint8_t status; size_t cnt; int err, i; elem_addr = strtoul(argv[1], NULL, 0); mod_id = strtoul(argv[2], NULL, 0); cnt = ARRAY_SIZE(apps); if (argc > 3) { cid = strtoul(argv[3], NULL, 0); err = bt_mesh_cfg_mod_app_get_vnd(net.net_idx, net.dst, elem_addr, mod_id, cid, &status, apps, &cnt); } else { err = bt_mesh_cfg_mod_app_get(net.net_idx, net.dst, elem_addr, mod_id, &status, apps, &cnt); } if (err) { printk("Unable to send Model App Get (err %d)", err); return 0; } if (status) { printk("Model App Get failed with status 0x%02x", status); } else { printk( "Apps bound to Element 0x%04x, Model 0x%04x %s:", elem_addr, mod_id, argc > 3 ? argv[3] : "(SIG)"); if (!cnt) { printk("\tNone."); } for (i = 0; i < cnt; i++) { printk("\t0x%04x", apps[i]); } } return 0; } struct shell_cmd_help cmd_mod_app_get_help = { NULL, " [Company ID]", NULL }; static int cmd_mod_sub_add(int argc, char *argv[]) { uint16_t elem_addr, sub_addr, mod_id, cid; uint8_t status; int err; if (argc < 4) { return -EINVAL; } elem_addr = strtoul(argv[1], NULL, 0); sub_addr = strtoul(argv[2], NULL, 0); mod_id = strtoul(argv[3], NULL, 0); if (argc > 4) { cid = strtoul(argv[4], NULL, 0); err = bt_mesh_cfg_mod_sub_add_vnd(net.net_idx, net.dst, elem_addr, sub_addr, mod_id, cid, &status); } else { err = bt_mesh_cfg_mod_sub_add(net.net_idx, net.dst, elem_addr, sub_addr, mod_id, &status); } if (err) { printk("Unable to send Model Subscription Add (err %d)\n", err); return 0; } if (status) { printk("Model Subscription Add failed with status 0x%02x\n", status); } else { printk("Model subscription was successful\n"); } return 0; } struct shell_cmd_help cmd_mod_sub_add_help = { NULL, " [Company ID]", NULL }; static int cmd_mod_sub_del(int argc, char *argv[]) { uint16_t elem_addr, sub_addr, mod_id, cid; uint8_t status; int err; if (argc < 4) { return -EINVAL; } elem_addr = strtoul(argv[1], NULL, 0); sub_addr = strtoul(argv[2], NULL, 0); mod_id = strtoul(argv[3], NULL, 0); if (argc > 4) { cid = strtoul(argv[4], NULL, 0); err = bt_mesh_cfg_mod_sub_del_vnd(net.net_idx, net.dst, elem_addr, sub_addr, mod_id, cid, &status); } else { err = bt_mesh_cfg_mod_sub_del(net.net_idx, net.dst, elem_addr, sub_addr, mod_id, &status); } if (err) { printk("Unable to send Model Subscription Delete (err %d)\n", err); return 0; } if (status) { printk("Model Subscription Delete failed with status 0x%02x\n", status); } else { printk("Model subscription deltion was successful\n"); } return 0; } struct shell_cmd_help cmd_mod_sub_del_help = { NULL, " [Company ID]", NULL }; static int cmd_mod_sub_add_va(int argc, char *argv[]) { uint16_t elem_addr, sub_addr, mod_id, cid; uint8_t label[16]; uint8_t status; size_t len; int err; if (argc < 4) { return -EINVAL; } elem_addr = strtoul(argv[1], NULL, 0); len = hex2bin(argv[2], label, sizeof(label)); memset(label + len, 0, sizeof(label) - len); mod_id = strtoul(argv[3], NULL, 0); if (argc > 4) { cid = strtoul(argv[4], NULL, 0); err = bt_mesh_cfg_mod_sub_va_add_vnd(net.net_idx, net.dst, elem_addr, label, mod_id, cid, &sub_addr, &status); } else { err = bt_mesh_cfg_mod_sub_va_add(net.net_idx, net.dst, elem_addr, label, mod_id, &sub_addr, &status); } if (err) { printk("Unable to send Mod Sub VA Add (err %d)\n", err); return 0; } if (status) { printk("Mod Sub VA Add failed with status 0x%02x\n", status); } else { printk("0x%04x subscribed to Label UUID %s (va 0x%04x)\n", elem_addr, argv[2], sub_addr); } return 0; } struct shell_cmd_help cmd_mod_sub_add_va_help = { NULL, "