feat(ble_mesh): Device Firmware Update (Zephyr v4.0.0)
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define BT_MESH_DFD_OP_RECEIVERS_ADD BT_MESH_MODEL_OP_2(0x83, 0x11)
|
||||
#define BT_MESH_DFD_OP_RECEIVERS_DELETE_ALL BT_MESH_MODEL_OP_2(0x83, 0x12)
|
||||
#define BT_MESH_DFD_OP_RECEIVERS_STATUS BT_MESH_MODEL_OP_2(0x83, 0x13)
|
||||
#define BT_MESH_DFD_OP_RECEIVERS_GET BT_MESH_MODEL_OP_2(0x83, 0x14)
|
||||
#define BT_MESH_DFD_OP_RECEIVERS_LIST BT_MESH_MODEL_OP_2(0x83, 0x15)
|
||||
#define BT_MESH_DFD_OP_CAPABILITIES_GET BT_MESH_MODEL_OP_2(0x83, 0x16)
|
||||
#define BT_MESH_DFD_OP_CAPABILITIES_STATUS BT_MESH_MODEL_OP_2(0x83, 0x17)
|
||||
#define BT_MESH_DFD_OP_GET BT_MESH_MODEL_OP_2(0x83, 0x18)
|
||||
#define BT_MESH_DFD_OP_START BT_MESH_MODEL_OP_2(0x83, 0x19)
|
||||
#define BT_MESH_DFD_OP_SUSPEND BT_MESH_MODEL_OP_2(0x83, 0x1a)
|
||||
#define BT_MESH_DFD_OP_CANCEL BT_MESH_MODEL_OP_2(0x83, 0x1b)
|
||||
#define BT_MESH_DFD_OP_APPLY BT_MESH_MODEL_OP_2(0x83, 0x1c)
|
||||
#define BT_MESH_DFD_OP_STATUS BT_MESH_MODEL_OP_2(0x83, 0x1d)
|
||||
#define BT_MESH_DFD_OP_UPLOAD_GET BT_MESH_MODEL_OP_2(0x83, 0x1e)
|
||||
#define BT_MESH_DFD_OP_UPLOAD_START BT_MESH_MODEL_OP_2(0x83, 0x1f)
|
||||
#define BT_MESH_DFD_OP_UPLOAD_START_OOB BT_MESH_MODEL_OP_2(0x83, 0x20)
|
||||
#define BT_MESH_DFD_OP_UPLOAD_CANCEL BT_MESH_MODEL_OP_2(0x83, 0x21)
|
||||
#define BT_MESH_DFD_OP_UPLOAD_STATUS BT_MESH_MODEL_OP_2(0x83, 0x22)
|
||||
#define BT_MESH_DFD_OP_FW_GET BT_MESH_MODEL_OP_2(0x83, 0x23)
|
||||
#define BT_MESH_DFD_OP_FW_GET_BY_INDEX BT_MESH_MODEL_OP_2(0x83, 0x24)
|
||||
#define BT_MESH_DFD_OP_FW_DELETE BT_MESH_MODEL_OP_2(0x83, 0x25)
|
||||
#define BT_MESH_DFD_OP_FW_DELETE_ALL BT_MESH_MODEL_OP_2(0x83, 0x26)
|
||||
#define BT_MESH_DFD_OP_FW_STATUS BT_MESH_MODEL_OP_2(0x83, 0x27)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFD_SRV_INTERNAL_H__
|
||||
#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFD_SRV_INTERNAL_H__
|
||||
|
||||
#include <zephyr/bluetooth/mesh.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct bt_mesh_dfd_start_params {
|
||||
uint16_t app_idx, timeout_base, slot_idx, group;
|
||||
enum bt_mesh_blob_xfer_mode xfer_mode;
|
||||
uint8_t ttl;
|
||||
bool apply;
|
||||
};
|
||||
|
||||
enum bt_mesh_dfd_status bt_mesh_dfd_srv_receiver_add(struct bt_mesh_dfd_srv *srv, uint16_t addr,
|
||||
uint8_t img_idx);
|
||||
|
||||
enum bt_mesh_dfd_status bt_mesh_dfd_srv_receivers_delete_all(struct bt_mesh_dfd_srv *srv);
|
||||
|
||||
enum bt_mesh_dfd_status bt_mesh_dfd_srv_start(struct bt_mesh_dfd_srv *srv,
|
||||
struct bt_mesh_dfd_start_params *params);
|
||||
|
||||
enum bt_mesh_dfd_status bt_mesh_dfd_srv_suspend(struct bt_mesh_dfd_srv *srv);
|
||||
|
||||
enum bt_mesh_dfd_status bt_mesh_dfd_srv_cancel(struct bt_mesh_dfd_srv *srv,
|
||||
struct bt_mesh_msg_ctx *ctx);
|
||||
|
||||
enum bt_mesh_dfd_status bt_mesh_dfd_srv_apply(struct bt_mesh_dfd_srv *srv);
|
||||
|
||||
enum bt_mesh_dfd_status bt_mesh_dfd_srv_fw_delete(struct bt_mesh_dfd_srv *srv, size_t *fwid_len,
|
||||
const uint8_t **fwid);
|
||||
|
||||
enum bt_mesh_dfd_status bt_mesh_dfd_srv_fw_delete_all(struct bt_mesh_dfd_srv *srv);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFD_SRV_INTERNAL_H__ */
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define BT_MESH_DFU_OP_UPDATE_INFO_GET BT_MESH_MODEL_OP_2(0x83, 0x08)
|
||||
#define BT_MESH_DFU_OP_UPDATE_INFO_STATUS BT_MESH_MODEL_OP_2(0x83, 0x09)
|
||||
|
||||
#define BT_MESH_DFU_OP_UPDATE_METADATA_CHECK BT_MESH_MODEL_OP_2(0x83, 0x0a)
|
||||
#define BT_MESH_DFU_OP_UPDATE_METADATA_STATUS BT_MESH_MODEL_OP_2(0x83, 0x0b)
|
||||
|
||||
#define BT_MESH_DFU_OP_UPDATE_GET BT_MESH_MODEL_OP_2(0x83, 0x0c)
|
||||
#define BT_MESH_DFU_OP_UPDATE_START BT_MESH_MODEL_OP_2(0x83, 0x0d)
|
||||
#define BT_MESH_DFU_OP_UPDATE_CANCEL BT_MESH_MODEL_OP_2(0x83, 0x0e)
|
||||
#define BT_MESH_DFU_OP_UPDATE_APPLY BT_MESH_MODEL_OP_2(0x83, 0x0f)
|
||||
#define BT_MESH_DFU_OP_UPDATE_STATUS BT_MESH_MODEL_OP_2(0x83, 0x10)
|
||||
|
||||
#define DFU_UPDATE_INFO_STATUS_MSG_MINLEN (4 + CONFIG_BT_MESH_DFU_FWID_MAXLEN + \
|
||||
CONFIG_BT_MESH_DFU_URI_MAXLEN)
|
||||
#define DFU_UPDATE_START_MSG_MAXLEN (12 + CONFIG_BT_MESH_DFU_METADATA_MAXLEN)
|
||||
|
||||
static inline uint16_t dfu_metadata_checksum(struct net_buf_simple *buf)
|
||||
{
|
||||
/* Simple Fletcher-16 checksum to ensure duplicate start messages don't
|
||||
* have different metadata.
|
||||
*/
|
||||
struct net_buf_simple_state state;
|
||||
uint8_t sum[2] = {0, 0};
|
||||
|
||||
net_buf_simple_save(buf, &state);
|
||||
|
||||
|
||||
while (buf->len) {
|
||||
uint8_t byte = net_buf_simple_pull_u8(buf);
|
||||
|
||||
sum[0] += byte;
|
||||
sum[1] += sum[0];
|
||||
}
|
||||
|
||||
net_buf_simple_restore(buf, &state);
|
||||
|
||||
return (sum[0] << 8U) | sum[1];
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/net_buf.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <zephyr/bluetooth/mesh.h>
|
||||
#include "crypto.h"
|
||||
#include "access.h"
|
||||
|
||||
int bt_mesh_dfu_metadata_decode(struct net_buf_simple *buf,
|
||||
struct bt_mesh_dfu_metadata *metadata)
|
||||
{
|
||||
if (buf->len < 12) {
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
metadata->fw_ver.major = net_buf_simple_pull_u8(buf);
|
||||
metadata->fw_ver.minor = net_buf_simple_pull_u8(buf);
|
||||
metadata->fw_ver.revision = net_buf_simple_pull_le16(buf);
|
||||
metadata->fw_ver.build_num = net_buf_simple_pull_le32(buf);
|
||||
metadata->fw_size = net_buf_simple_pull_le24(buf);
|
||||
metadata->fw_core_type = net_buf_simple_pull_u8(buf);
|
||||
|
||||
if (metadata->fw_core_type & BT_MESH_DFU_FW_CORE_TYPE_APP) {
|
||||
if (buf->len < 6) {
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
metadata->comp_hash = net_buf_simple_pull_le32(buf);
|
||||
metadata->elems = net_buf_simple_pull_le16(buf);
|
||||
}
|
||||
|
||||
metadata->user_data = buf->len > 0 ? buf->data : NULL;
|
||||
metadata->user_data_len = buf->len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int bt_mesh_dfu_metadata_encode(const struct bt_mesh_dfu_metadata *metadata,
|
||||
struct net_buf_simple *buf)
|
||||
{
|
||||
size_t md_len_min = 12 + metadata->user_data_len;
|
||||
|
||||
if (metadata->fw_core_type & BT_MESH_DFU_FW_CORE_TYPE_APP) {
|
||||
md_len_min += 6;
|
||||
}
|
||||
|
||||
if (net_buf_simple_tailroom(buf) < md_len_min) {
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
net_buf_simple_add_u8(buf, metadata->fw_ver.major);
|
||||
net_buf_simple_add_u8(buf, metadata->fw_ver.minor);
|
||||
net_buf_simple_add_le16(buf, metadata->fw_ver.revision);
|
||||
net_buf_simple_add_le32(buf, metadata->fw_ver.build_num);
|
||||
net_buf_simple_add_le24(buf, metadata->fw_size);
|
||||
net_buf_simple_add_u8(buf, metadata->fw_core_type);
|
||||
net_buf_simple_add_le32(buf, metadata->comp_hash);
|
||||
net_buf_simple_add_le16(buf, metadata->elems);
|
||||
|
||||
if (metadata->user_data_len > 0) {
|
||||
net_buf_simple_add_mem(buf, metadata->user_data, metadata->user_data_len);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bt_mesh_dfu_metadata_comp_hash_get(struct net_buf_simple *buf, uint8_t *key, uint32_t *hash)
|
||||
{
|
||||
uint8_t mac[16];
|
||||
int err;
|
||||
struct bt_mesh_sg sg = {.data = buf->data, .len = buf->len};
|
||||
|
||||
err = bt_mesh_aes_cmac_raw_key(key, &sg, 1, mac);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
*hash = sys_get_le32(mac);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bt_mesh_dfu_metadata_comp_hash_local_get(uint8_t *key, uint32_t *hash)
|
||||
{
|
||||
NET_BUF_SIMPLE_DEFINE(buf, BT_MESH_TX_SDU_MAX);
|
||||
int err;
|
||||
|
||||
err = bt_mesh_comp_data_get_page_0(&buf, 0);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = bt_mesh_dfu_metadata_comp_hash_get(&buf, key, hash);
|
||||
return err;
|
||||
}
|
||||
@@ -0,0 +1,402 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/settings/settings.h>
|
||||
#include "dfu_slot.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
#include <common/bt_str.h>
|
||||
|
||||
#define LOG_LEVEL CONFIG_BT_MESH_DFU_LOG_LEVEL
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(bt_mesh_dfu_slot);
|
||||
|
||||
#define SLOT_ENTRY_BUFLEN 25
|
||||
|
||||
#define DFU_SLOT_SETTINGS_PATH "bt/mesh-dfu/slot"
|
||||
|
||||
#define HEADER_SIZE offsetof(struct slot, slot.fwid)
|
||||
|
||||
#define PROP_HEADER "h"
|
||||
#define PROP_FWID "id"
|
||||
#define PROP_METADATA "m"
|
||||
|
||||
static sys_slist_t list;
|
||||
|
||||
static struct slot {
|
||||
uint32_t idx;
|
||||
struct bt_mesh_dfu_slot slot;
|
||||
sys_snode_t n;
|
||||
} slots[CONFIG_BT_MESH_DFU_SLOT_CNT];
|
||||
|
||||
static uint32_t slot_index;
|
||||
|
||||
static char *slot_entry_encode(uint16_t idx, char buf[SLOT_ENTRY_BUFLEN],
|
||||
const char *property)
|
||||
{
|
||||
snprintf(buf, SLOT_ENTRY_BUFLEN, DFU_SLOT_SETTINGS_PATH "/%x/%s", idx,
|
||||
property);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static bool slot_eq(const struct bt_mesh_dfu_slot *slot,
|
||||
const uint8_t *fwid, size_t fwid_len)
|
||||
{
|
||||
return (slot->fwid_len == fwid_len) &&
|
||||
!memcmp(fwid, slot->fwid, fwid_len);
|
||||
}
|
||||
|
||||
static bool is_slot_committed(struct slot *slot_to_check)
|
||||
{
|
||||
struct slot *s;
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) {
|
||||
if (s == slot_to_check) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int slot_store(const struct slot *slot_to_store)
|
||||
{
|
||||
uint16_t idx = ARRAY_INDEX(slots, slot_to_store);
|
||||
char buf[SLOT_ENTRY_BUFLEN];
|
||||
int err;
|
||||
|
||||
err = settings_save_one(slot_entry_encode(idx, buf, PROP_HEADER),
|
||||
slot_to_store, HEADER_SIZE);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = settings_save_one(slot_entry_encode(idx, buf, PROP_FWID),
|
||||
slot_to_store->slot.fwid, slot_to_store->slot.fwid_len);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = settings_save_one(slot_entry_encode(idx, buf,
|
||||
PROP_METADATA),
|
||||
slot_to_store->slot.metadata, slot_to_store->slot.metadata_len);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void slot_erase(struct slot *slot_to_erase)
|
||||
{
|
||||
uint16_t idx = ARRAY_INDEX(slots, slot_to_erase);
|
||||
char buf[SLOT_ENTRY_BUFLEN];
|
||||
|
||||
(void)settings_delete(slot_entry_encode(idx, buf, PROP_HEADER));
|
||||
(void)settings_delete(slot_entry_encode(idx, buf, PROP_FWID));
|
||||
(void)settings_delete(slot_entry_encode(idx, buf, PROP_METADATA));
|
||||
}
|
||||
|
||||
static void slot_index_defrag(void)
|
||||
{
|
||||
slot_index = 0;
|
||||
struct slot *s;
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) {
|
||||
s->idx = ++slot_index;
|
||||
slot_store(s);
|
||||
}
|
||||
}
|
||||
|
||||
int bt_mesh_dfu_slot_count(void)
|
||||
{
|
||||
int cnt = 0;
|
||||
sys_snode_t *n;
|
||||
|
||||
SYS_SLIST_FOR_EACH_NODE(&list, n) {
|
||||
cnt++;
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_reserve(void)
|
||||
{
|
||||
struct slot *slot = NULL;
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(slots); ++i) {
|
||||
if (slots[i].idx == 0) {
|
||||
slot = &slots[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!slot) {
|
||||
LOG_WRN("No space");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (slot_index == UINT32_MAX) {
|
||||
slot_index_defrag();
|
||||
}
|
||||
|
||||
slot->slot.fwid_len = 0;
|
||||
slot->slot.metadata_len = 0;
|
||||
slot->slot.size = 0;
|
||||
slot->idx = ++slot_index;
|
||||
|
||||
LOG_DBG("Reserved slot #%u", slot - &slots[0]);
|
||||
|
||||
return &slot->slot;
|
||||
}
|
||||
|
||||
int bt_mesh_dfu_slot_info_set(struct bt_mesh_dfu_slot *dfu_slot, size_t size,
|
||||
const uint8_t *metadata, size_t metadata_len)
|
||||
{
|
||||
struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot);
|
||||
|
||||
if (metadata_len > CONFIG_BT_MESH_DFU_METADATA_MAXLEN) {
|
||||
return -EFBIG;
|
||||
}
|
||||
|
||||
if (slot->idx == 0 || is_slot_committed(slot)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
slot->slot.size = size;
|
||||
slot->slot.metadata_len = metadata_len;
|
||||
memcpy(slot->slot.metadata, metadata, metadata_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bt_mesh_dfu_slot_fwid_set(struct bt_mesh_dfu_slot *dfu_slot,
|
||||
const uint8_t *fwid, size_t fwid_len)
|
||||
{
|
||||
struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot);
|
||||
|
||||
if (fwid_len > CONFIG_BT_MESH_DFU_FWID_MAXLEN) {
|
||||
return -EFBIG;
|
||||
}
|
||||
|
||||
if (slot->idx == 0 || is_slot_committed(slot)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(slots); i++) {
|
||||
if (slots[i].idx != 0 &&
|
||||
slot_eq(&slots[i].slot, fwid, fwid_len)) {
|
||||
return is_slot_committed(&slots[i]) ?
|
||||
-EEXIST : -EALREADY;
|
||||
}
|
||||
}
|
||||
|
||||
slot->slot.fwid_len = fwid_len;
|
||||
memcpy(slot->slot.fwid, fwid, fwid_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bt_mesh_dfu_slot_commit(struct bt_mesh_dfu_slot *dfu_slot)
|
||||
{
|
||||
int err;
|
||||
struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot);
|
||||
|
||||
if (slot->idx == 0 ||
|
||||
slot->slot.fwid_len == 0 ||
|
||||
slot->slot.size == 0 ||
|
||||
is_slot_committed(slot)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = slot_store(slot);
|
||||
if (err) {
|
||||
LOG_WRN("Store failed (err: %d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
sys_slist_append(&list, &slot->n);
|
||||
|
||||
LOG_DBG("Stored slot #%u: %s", ARRAY_INDEX(slots, slot),
|
||||
bt_hex(slot->slot.fwid, slot->slot.fwid_len));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bt_mesh_dfu_slot_release(const struct bt_mesh_dfu_slot *dfu_slot)
|
||||
{
|
||||
struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot);
|
||||
|
||||
if (is_slot_committed(slot)) {
|
||||
return;
|
||||
}
|
||||
|
||||
slot->idx = 0;
|
||||
}
|
||||
|
||||
int bt_mesh_dfu_slot_del(const struct bt_mesh_dfu_slot *dfu_slot)
|
||||
{
|
||||
struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot);
|
||||
|
||||
if (!sys_slist_find_and_remove(&list, &slot->n)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int idx = ARRAY_INDEX(slots, slot);
|
||||
|
||||
LOG_DBG("%u", idx);
|
||||
|
||||
slot_erase(slot);
|
||||
slot->idx = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bt_mesh_dfu_slot_del_all(void)
|
||||
{
|
||||
struct slot *s;
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) {
|
||||
slot_erase(s);
|
||||
s->idx = 0;
|
||||
}
|
||||
|
||||
sys_slist_init(&list);
|
||||
}
|
||||
|
||||
const struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_at(uint16_t img_idx)
|
||||
{
|
||||
struct slot *s;
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) {
|
||||
if (!img_idx--) {
|
||||
return &s->slot;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int bt_mesh_dfu_slot_get(const uint8_t *fwid, size_t fwid_len, struct bt_mesh_dfu_slot **slot)
|
||||
{
|
||||
struct slot *s;
|
||||
int idx = 0;
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) {
|
||||
if (slot_eq(&s->slot, fwid, fwid_len)) {
|
||||
if (slot) {
|
||||
*slot = &s->slot;
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int bt_mesh_dfu_slot_img_idx_get(const struct bt_mesh_dfu_slot *dfu_slot)
|
||||
{
|
||||
struct slot *s;
|
||||
int idx = 0;
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) {
|
||||
if (&s->slot == dfu_slot) {
|
||||
return idx;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
size_t bt_mesh_dfu_slot_foreach(bt_mesh_dfu_slot_cb_t cb, void *user_data)
|
||||
{
|
||||
enum bt_mesh_dfu_iter iter;
|
||||
size_t cnt = 0;
|
||||
struct slot *s;
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) {
|
||||
cnt++;
|
||||
|
||||
if (!cb) {
|
||||
continue;
|
||||
}
|
||||
|
||||
iter = cb(&s->slot, user_data);
|
||||
if (iter != BT_MESH_DFU_ITER_CONTINUE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
static int slot_data_load(const char *key, size_t len_rd,
|
||||
settings_read_cb read_cb, void *cb_arg)
|
||||
{
|
||||
const char *prop;
|
||||
size_t len;
|
||||
uint16_t idx;
|
||||
|
||||
idx = strtol(key, NULL, 16);
|
||||
|
||||
if (idx >= ARRAY_SIZE(slots)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = settings_name_next(key, &prop);
|
||||
|
||||
if (!strncmp(prop, PROP_HEADER, len)) {
|
||||
if (read_cb(cb_arg, &slots[idx], HEADER_SIZE) > 0) {
|
||||
struct slot *s, *prev = NULL;
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) {
|
||||
if (s->idx > slots[idx].idx) {
|
||||
break;
|
||||
}
|
||||
|
||||
prev = s;
|
||||
}
|
||||
|
||||
if (prev == NULL) {
|
||||
sys_slist_prepend(&list, &slots[idx].n);
|
||||
} else {
|
||||
sys_slist_insert(&list, &prev->n, &slots[idx].n);
|
||||
}
|
||||
|
||||
if (slots[idx].idx >= slot_index) {
|
||||
slot_index = slots[idx].idx + 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strncmp(prop, PROP_FWID, len)) {
|
||||
if (read_cb(cb_arg, &slots[idx].slot.fwid,
|
||||
sizeof(slots[idx].slot.fwid)) < 0) {
|
||||
slots[idx].idx = 0;
|
||||
sys_slist_find_and_remove(&list, &slots[idx].n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
slots[idx].slot.fwid_len = len_rd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strncmp(prop, PROP_METADATA, len)) {
|
||||
if (read_cb(cb_arg, &slots[idx].slot.metadata,
|
||||
sizeof(slots[idx].slot.metadata)) < 0) {
|
||||
slots[idx].idx = 0;
|
||||
sys_slist_find_and_remove(&list, &slots[idx].n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
slots[idx].slot.metadata_len = len_rd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SETTINGS_STATIC_HANDLER_DEFINE(bt_mesh_dfu_slots, DFU_SLOT_SETTINGS_PATH, NULL,
|
||||
slot_data_load, NULL, NULL);
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/bluetooth/mesh.h>
|
||||
|
||||
/** @brief Slot iteration callback.
|
||||
*
|
||||
* @param slot A valid DFU image slot.
|
||||
* @param user_data User data passed to @ref bt_mesh_dfu_slot_foreach.
|
||||
*
|
||||
* @return Iteration action determining next step.
|
||||
*/
|
||||
typedef enum bt_mesh_dfu_iter (*bt_mesh_dfu_slot_cb_t)(
|
||||
const struct bt_mesh_dfu_slot *slot, void *user_data);
|
||||
|
||||
/** @brief Get the number of slots committed to the firmware list.
|
||||
*
|
||||
* @return Number of committed slots.
|
||||
*/
|
||||
int bt_mesh_dfu_slot_count(void);
|
||||
|
||||
/** @brief Reserve a new DFU image slot for a distributable image.
|
||||
*
|
||||
* A DFU image slot represents a single distributable DFU image with all its
|
||||
* metadata. The slot data must be set using @ref bt_mesh_dfu_slot_info_set and
|
||||
* @ref bt_mesh_dfu_slot_fwid_set, and the slot committed using
|
||||
* @ref bt_mesh_dfu_slot_commit for the slot to be considered part of the slot
|
||||
* list.
|
||||
*
|
||||
* @return A pointer to the reserved slot, or NULL if allocation failed.
|
||||
*/
|
||||
struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_reserve(void);
|
||||
|
||||
/** @brief Set the size and metadata for a reserved slot.
|
||||
*
|
||||
* @param dfu_slot Pointer to the reserved slot for which to set the
|
||||
* metadata.
|
||||
* @param size The size of the image.
|
||||
* @param metadata Metadata or NULL.
|
||||
* @param metadata_len Length of the metadata, at most @c
|
||||
* CONFIG_BT_MESH_DFU_METADATA_MAXLEN.
|
||||
*
|
||||
* @return 0 on success, (negative) error code otherwise.
|
||||
*/
|
||||
int bt_mesh_dfu_slot_info_set(struct bt_mesh_dfu_slot *dfu_slot, size_t size,
|
||||
const uint8_t *metadata, size_t metadata_len);
|
||||
|
||||
/** @brief Set the new fwid for the incoming image for a reserved slot.
|
||||
*
|
||||
* @param dfu_slot Pointer to the reserved slot for which to set the fwid.
|
||||
* @param fwid Fwid to set.
|
||||
* @param fwid_len Length of the fwid, at most @c
|
||||
* CONFIG_BT_MESH_DFU_FWID_MAXLEN.
|
||||
*
|
||||
* @return 0 on success, (negative) error code otherwise.
|
||||
*/
|
||||
int bt_mesh_dfu_slot_fwid_set(struct bt_mesh_dfu_slot *dfu_slot,
|
||||
const uint8_t *fwid, size_t fwid_len);
|
||||
|
||||
/** @brief Commit the reserved slot to the list of slots, and store it
|
||||
* persistently.
|
||||
*
|
||||
* If the commit fails for any reason, the slot will still be in the reserved
|
||||
* state after this call.
|
||||
*
|
||||
* @param dfu_slot Pointer to the reserved slot.
|
||||
*
|
||||
* @return 0 on success, (negative) error code otherwise.
|
||||
*/
|
||||
int bt_mesh_dfu_slot_commit(struct bt_mesh_dfu_slot *dfu_slot);
|
||||
|
||||
/** @brief Release a reserved slot so that it can be reserved again.
|
||||
*
|
||||
* @param dfu_slot Pointer to the reserved slot.
|
||||
*/
|
||||
void bt_mesh_dfu_slot_release(const struct bt_mesh_dfu_slot *dfu_slot);
|
||||
|
||||
/** @brief Delete a committed DFU image slot.
|
||||
*
|
||||
* @param slot Slot to delete. Must be a valid pointer acquired from this
|
||||
* module.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code on failure.
|
||||
*/
|
||||
int bt_mesh_dfu_slot_del(const struct bt_mesh_dfu_slot *slot);
|
||||
|
||||
/** @brief Delete all DFU image slots.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code on failure.
|
||||
*/
|
||||
void bt_mesh_dfu_slot_del_all(void);
|
||||
|
||||
/** @brief Get the DFU image slot at the given firmware image list index.
|
||||
*
|
||||
* @param idx DFU image slot index.
|
||||
*
|
||||
* @return The DFU image slot at the given index, or NULL if no slot exists with the
|
||||
* given index.
|
||||
*/
|
||||
const struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_at(uint16_t img_idx);
|
||||
|
||||
/** @brief Get the committed DFU image slot for the image with the given
|
||||
* firmware ID.
|
||||
*
|
||||
* @param fwid Firmware ID.
|
||||
* @param fwid_len Firmware ID length.
|
||||
* @param slot Slot pointer to fill.
|
||||
*
|
||||
* @return Slot index on success, or negative error code on failure.
|
||||
*/
|
||||
int bt_mesh_dfu_slot_get(const uint8_t *fwid, size_t fwid_len, struct bt_mesh_dfu_slot **slot);
|
||||
|
||||
/** @brief Get the index in the firmware image list for the given slot.
|
||||
*
|
||||
* @param slot Slot to find.
|
||||
*
|
||||
* @return Slot index on success, or negative error code on failure.
|
||||
*/
|
||||
int bt_mesh_dfu_slot_img_idx_get(const struct bt_mesh_dfu_slot *slot);
|
||||
|
||||
/** @brief Iterate through all DFU image slots.
|
||||
*
|
||||
* Calls the callback for every DFU image slot or until the callback returns
|
||||
* something other than @ref BT_MESH_DFU_ITER_CONTINUE.
|
||||
*
|
||||
* @param cb Callback to call for each slot, or NULL to just count the
|
||||
* number of slots.
|
||||
* @param user_data User data to pass to the callback.
|
||||
*
|
||||
* @return The number of slots iterated over.
|
||||
*/
|
||||
size_t bt_mesh_dfu_slot_foreach(bt_mesh_dfu_slot_cb_t cb, void *user_data);
|
||||
@@ -0,0 +1,617 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/bluetooth/mesh.h>
|
||||
#include "dfu.h"
|
||||
#include "blob.h"
|
||||
#include "access.h"
|
||||
|
||||
#define LOG_LEVEL CONFIG_BT_MESH_DFU_LOG_LEVEL
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(bt_mesh_dfu_srv);
|
||||
|
||||
#define UPDATE_IDX_NONE 0xff
|
||||
|
||||
BUILD_ASSERT((DFU_UPDATE_START_MSG_MAXLEN + BT_MESH_MODEL_OP_LEN(BT_MESH_DFU_OP_UPDATE_START) +
|
||||
BT_MESH_MIC_SHORT) <= BT_MESH_RX_SDU_MAX,
|
||||
"The Firmware Update Start message does not fit into the maximum incoming SDU size.");
|
||||
|
||||
BUILD_ASSERT((DFU_UPDATE_INFO_STATUS_MSG_MINLEN +
|
||||
BT_MESH_MODEL_OP_LEN(BT_MESH_DFU_OP_UPDATE_INFO_STATUS) + BT_MESH_MIC_SHORT)
|
||||
<= BT_MESH_TX_SDU_MAX,
|
||||
"The Firmware Update Info Status message does not fit into the maximum outgoing SDU "
|
||||
"size.");
|
||||
|
||||
static void store_state(struct bt_mesh_dfu_srv *srv)
|
||||
{
|
||||
bt_mesh_model_data_store(srv->mod, false, NULL, &srv->update,
|
||||
sizeof(srv->update));
|
||||
}
|
||||
|
||||
static void erase_state(struct bt_mesh_dfu_srv *srv)
|
||||
{
|
||||
bt_mesh_model_data_store(srv->mod, false, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
static void xfer_failed(struct bt_mesh_dfu_srv *srv)
|
||||
{
|
||||
if (!bt_mesh_dfu_srv_is_busy(srv) ||
|
||||
srv->update.idx >= srv->img_count) {
|
||||
return;
|
||||
}
|
||||
|
||||
erase_state(srv);
|
||||
|
||||
if (srv->cb->end) {
|
||||
srv->cb->end(srv, &srv->imgs[srv->update.idx], false);
|
||||
}
|
||||
}
|
||||
|
||||
static enum bt_mesh_dfu_status metadata_check(struct bt_mesh_dfu_srv *srv,
|
||||
uint8_t idx,
|
||||
struct net_buf_simple *buf,
|
||||
enum bt_mesh_dfu_effect *effect)
|
||||
{
|
||||
*effect = BT_MESH_DFU_EFFECT_NONE;
|
||||
|
||||
if (idx >= srv->img_count) {
|
||||
return BT_MESH_DFU_ERR_FW_IDX;
|
||||
}
|
||||
|
||||
if (!srv->cb->check) {
|
||||
return BT_MESH_DFU_SUCCESS;
|
||||
}
|
||||
|
||||
if (srv->cb->check(srv, &srv->imgs[idx], buf, effect)) {
|
||||
*effect = BT_MESH_DFU_EFFECT_NONE;
|
||||
return BT_MESH_DFU_ERR_METADATA;
|
||||
}
|
||||
|
||||
return BT_MESH_DFU_SUCCESS;
|
||||
}
|
||||
|
||||
static void apply_rsp_sent(int err, void *cb_params)
|
||||
{
|
||||
struct bt_mesh_dfu_srv *srv = cb_params;
|
||||
|
||||
if (err) {
|
||||
/* return phase back to give client one more chance. */
|
||||
srv->update.phase = BT_MESH_DFU_PHASE_VERIFY_OK;
|
||||
LOG_WRN("Apply response failed, wait for retry (err %d)", err);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DBG("");
|
||||
|
||||
if (!srv->cb->apply || srv->update.idx == UPDATE_IDX_NONE) {
|
||||
srv->update.phase = BT_MESH_DFU_PHASE_IDLE;
|
||||
store_state(srv);
|
||||
LOG_DBG("Prerequisites for apply callback are wrong");
|
||||
return;
|
||||
}
|
||||
|
||||
store_state(srv);
|
||||
|
||||
err = srv->cb->apply(srv, &srv->imgs[srv->update.idx]);
|
||||
if (err) {
|
||||
srv->update.phase = BT_MESH_DFU_PHASE_IDLE;
|
||||
store_state(srv);
|
||||
LOG_DBG("Application apply callback failed (err %d)", err);
|
||||
}
|
||||
}
|
||||
|
||||
static void apply_rsp_sending(uint16_t duration, int err, void *cb_params)
|
||||
{
|
||||
if (err) {
|
||||
apply_rsp_sent(err, cb_params);
|
||||
}
|
||||
}
|
||||
|
||||
static void verify(struct bt_mesh_dfu_srv *srv)
|
||||
{
|
||||
srv->update.phase = BT_MESH_DFU_PHASE_VERIFY;
|
||||
|
||||
if (srv->update.idx >= srv->img_count) {
|
||||
bt_mesh_dfu_srv_rejected(srv);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!srv->cb->end) {
|
||||
bt_mesh_dfu_srv_verified(srv);
|
||||
return;
|
||||
}
|
||||
|
||||
srv->cb->end(srv, &srv->imgs[srv->update.idx], true);
|
||||
if (srv->update.phase == BT_MESH_DFU_PHASE_VERIFY) {
|
||||
store_state(srv);
|
||||
}
|
||||
}
|
||||
|
||||
static int handle_info_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
|
||||
struct net_buf_simple *buf)
|
||||
{
|
||||
struct bt_mesh_dfu_srv *srv = mod->rt->user_data;
|
||||
uint8_t idx, limit;
|
||||
|
||||
if (srv->update.phase == BT_MESH_DFU_PHASE_APPLYING) {
|
||||
LOG_INF("Still applying, not responding");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
idx = net_buf_simple_pull_u8(buf);
|
||||
limit = net_buf_simple_pull_u8(buf);
|
||||
|
||||
LOG_DBG("from %u (limit: %u)", idx, limit);
|
||||
|
||||
NET_BUF_SIMPLE_DEFINE(rsp, BT_MESH_TX_SDU_MAX);
|
||||
bt_mesh_model_msg_init(&rsp, BT_MESH_DFU_OP_UPDATE_INFO_STATUS);
|
||||
net_buf_simple_add_u8(&rsp, srv->img_count);
|
||||
net_buf_simple_add_u8(&rsp, idx);
|
||||
|
||||
for (; idx < srv->img_count && limit > 0; ++idx) {
|
||||
uint32_t entry_len;
|
||||
|
||||
if (!srv->imgs[idx].fwid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
entry_len = 2 + srv->imgs[idx].fwid_len;
|
||||
if (srv->imgs[idx].uri) {
|
||||
entry_len += strlen(srv->imgs[idx].uri);
|
||||
}
|
||||
|
||||
if (net_buf_simple_tailroom(&rsp) + BT_MESH_MIC_SHORT < entry_len) {
|
||||
break;
|
||||
}
|
||||
|
||||
net_buf_simple_add_u8(&rsp, srv->imgs[idx].fwid_len);
|
||||
net_buf_simple_add_mem(&rsp, srv->imgs[idx].fwid,
|
||||
srv->imgs[idx].fwid_len);
|
||||
|
||||
if (srv->imgs[idx].uri) {
|
||||
size_t len = strlen(srv->imgs[idx].uri);
|
||||
|
||||
net_buf_simple_add_u8(&rsp, len);
|
||||
net_buf_simple_add_mem(&rsp, srv->imgs[idx].uri, len);
|
||||
} else {
|
||||
net_buf_simple_add_u8(&rsp, 0);
|
||||
}
|
||||
|
||||
limit--;
|
||||
}
|
||||
|
||||
if (srv->update.phase != BT_MESH_DFU_PHASE_IDLE) {
|
||||
ctx->send_ttl = srv->update.ttl;
|
||||
}
|
||||
|
||||
bt_mesh_model_send(mod, ctx, &rsp, NULL, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_metadata_check(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
|
||||
struct net_buf_simple *buf)
|
||||
{
|
||||
struct bt_mesh_dfu_srv *srv = mod->rt->user_data;
|
||||
enum bt_mesh_dfu_status status;
|
||||
enum bt_mesh_dfu_effect effect;
|
||||
uint8_t idx;
|
||||
|
||||
BT_MESH_MODEL_BUF_DEFINE(rsp, BT_MESH_DFU_OP_UPDATE_METADATA_STATUS, 2);
|
||||
bt_mesh_model_msg_init(&rsp, BT_MESH_DFU_OP_UPDATE_METADATA_STATUS);
|
||||
|
||||
idx = net_buf_simple_pull_u8(buf);
|
||||
status = metadata_check(srv, idx, buf, &effect);
|
||||
|
||||
LOG_DBG("%u", idx);
|
||||
|
||||
net_buf_simple_add_u8(&rsp, (status & BIT_MASK(3)) | (effect << 3));
|
||||
net_buf_simple_add_u8(&rsp, idx);
|
||||
|
||||
if (srv->update.phase != BT_MESH_DFU_PHASE_IDLE) {
|
||||
ctx->send_ttl = srv->update.ttl;
|
||||
}
|
||||
|
||||
bt_mesh_model_send(mod, ctx, &rsp, NULL, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void update_status_rsp(struct bt_mesh_dfu_srv *srv,
|
||||
struct bt_mesh_msg_ctx *ctx,
|
||||
enum bt_mesh_dfu_status status,
|
||||
const struct bt_mesh_send_cb *send_cb)
|
||||
{
|
||||
BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_STATUS, 14);
|
||||
bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_STATUS);
|
||||
|
||||
net_buf_simple_add_u8(&buf, ((status & BIT_MASK(3)) |
|
||||
(srv->update.phase << 5)));
|
||||
|
||||
if (srv->update.phase != BT_MESH_DFU_PHASE_IDLE) {
|
||||
net_buf_simple_add_u8(&buf, srv->update.ttl);
|
||||
net_buf_simple_add_u8(&buf, srv->update.effect);
|
||||
net_buf_simple_add_le16(&buf, srv->update.timeout_base);
|
||||
net_buf_simple_add_le64(&buf, srv->blob.state.xfer.id);
|
||||
net_buf_simple_add_u8(&buf, srv->update.idx);
|
||||
|
||||
ctx->send_ttl = srv->update.ttl;
|
||||
}
|
||||
|
||||
bt_mesh_model_send(srv->mod, ctx, &buf, send_cb, srv);
|
||||
}
|
||||
|
||||
static int handle_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
|
||||
struct net_buf_simple *buf)
|
||||
{
|
||||
struct bt_mesh_dfu_srv *srv = mod->rt->user_data;
|
||||
|
||||
LOG_DBG("");
|
||||
|
||||
update_status_rsp(srv, ctx, BT_MESH_DFU_SUCCESS, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool is_active_update(struct bt_mesh_dfu_srv *srv, uint8_t idx,
|
||||
uint16_t timeout_base,
|
||||
const uint64_t *blob_id, uint8_t ttl,
|
||||
uint16_t meta_checksum)
|
||||
{
|
||||
return (srv->update.idx != idx || srv->blob.state.xfer.id != *blob_id ||
|
||||
srv->update.ttl != ttl ||
|
||||
srv->update.timeout_base != timeout_base ||
|
||||
srv->update.meta != meta_checksum);
|
||||
}
|
||||
|
||||
static int handle_start(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
|
||||
struct net_buf_simple *buf)
|
||||
{
|
||||
struct bt_mesh_dfu_srv *srv = mod->rt->user_data;
|
||||
const struct bt_mesh_blob_io *io;
|
||||
uint16_t timeout_base, meta_checksum;
|
||||
enum bt_mesh_dfu_status status;
|
||||
uint8_t ttl, idx;
|
||||
uint64_t blob_id;
|
||||
int err;
|
||||
struct net_buf_simple_state buf_state;
|
||||
|
||||
ttl = net_buf_simple_pull_u8(buf);
|
||||
timeout_base = net_buf_simple_pull_le16(buf);
|
||||
blob_id = net_buf_simple_pull_le64(buf);
|
||||
idx = net_buf_simple_pull_u8(buf);
|
||||
meta_checksum = dfu_metadata_checksum(buf);
|
||||
|
||||
LOG_DBG("%u ttl: %u extra time: %u", idx, ttl, timeout_base);
|
||||
|
||||
if ((!buf->len || meta_checksum == srv->update.meta) &&
|
||||
srv->update.phase == BT_MESH_DFU_PHASE_TRANSFER_ERR &&
|
||||
srv->update.ttl == ttl &&
|
||||
srv->update.timeout_base == timeout_base &&
|
||||
srv->update.idx == idx &&
|
||||
srv->blob.state.xfer.id == blob_id) {
|
||||
srv->update.phase = BT_MESH_DFU_PHASE_TRANSFER_ACTIVE;
|
||||
status = BT_MESH_DFU_SUCCESS;
|
||||
store_state(srv);
|
||||
/* blob srv will resume the transfer. */
|
||||
LOG_DBG("Resuming transfer");
|
||||
goto rsp;
|
||||
}
|
||||
|
||||
if (bt_mesh_dfu_srv_is_busy(srv)) {
|
||||
if (is_active_update(srv, idx, timeout_base, &blob_id, ttl,
|
||||
meta_checksum)) {
|
||||
status = BT_MESH_DFU_ERR_WRONG_PHASE;
|
||||
} else {
|
||||
status = BT_MESH_DFU_SUCCESS;
|
||||
srv->update.ttl = ttl;
|
||||
srv->blob.state.xfer.id = blob_id;
|
||||
}
|
||||
|
||||
LOG_WRN("Busy. Phase: %u", srv->update.phase);
|
||||
goto rsp;
|
||||
}
|
||||
|
||||
net_buf_simple_save(buf, &buf_state);
|
||||
status = metadata_check(srv, idx, buf,
|
||||
(enum bt_mesh_dfu_effect *)&srv->update.effect);
|
||||
net_buf_simple_restore(buf, &buf_state);
|
||||
if (status != BT_MESH_DFU_SUCCESS) {
|
||||
goto rsp;
|
||||
}
|
||||
|
||||
srv->update.ttl = ttl;
|
||||
srv->update.timeout_base = timeout_base;
|
||||
srv->update.meta = meta_checksum;
|
||||
|
||||
io = NULL;
|
||||
err = srv->cb->start(srv, &srv->imgs[idx], buf, &io);
|
||||
if (err == -EALREADY || (!err && bt_mesh_has_addr(ctx->addr))) {
|
||||
/* This image has already been received or this is a
|
||||
* self-update. Skip the transfer phase and proceed to
|
||||
* verifying update.
|
||||
*/
|
||||
status = BT_MESH_DFU_SUCCESS;
|
||||
srv->update.idx = idx;
|
||||
srv->blob.state.xfer.id = blob_id;
|
||||
srv->update.phase = BT_MESH_DFU_PHASE_VERIFY;
|
||||
update_status_rsp(srv, ctx, status, NULL);
|
||||
verify(srv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (err == -ENOMEM) {
|
||||
status = BT_MESH_DFU_ERR_RESOURCES;
|
||||
goto rsp;
|
||||
}
|
||||
|
||||
if (err == -EBUSY) {
|
||||
status = BT_MESH_DFU_ERR_TEMPORARILY_UNAVAILABLE;
|
||||
goto rsp;
|
||||
}
|
||||
|
||||
if (err || !io || !io->wr) {
|
||||
status = BT_MESH_DFU_ERR_INTERNAL;
|
||||
goto rsp;
|
||||
}
|
||||
|
||||
err = bt_mesh_blob_srv_recv(&srv->blob, blob_id, io,
|
||||
ttl, timeout_base);
|
||||
if (err) {
|
||||
status = BT_MESH_DFU_ERR_BLOB_XFER_BUSY;
|
||||
goto rsp;
|
||||
}
|
||||
|
||||
srv->update.idx = idx;
|
||||
srv->update.phase = BT_MESH_DFU_PHASE_TRANSFER_ACTIVE;
|
||||
status = BT_MESH_DFU_SUCCESS;
|
||||
store_state(srv);
|
||||
|
||||
rsp:
|
||||
update_status_rsp(srv, ctx, status, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_cancel(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
|
||||
struct net_buf_simple *buf)
|
||||
{
|
||||
struct bt_mesh_dfu_srv *srv = mod->rt->user_data;
|
||||
|
||||
if (srv->update.idx == UPDATE_IDX_NONE) {
|
||||
goto rsp;
|
||||
}
|
||||
|
||||
LOG_DBG("");
|
||||
|
||||
bt_mesh_blob_srv_cancel(&srv->blob);
|
||||
srv->update.phase = BT_MESH_DFU_PHASE_IDLE;
|
||||
xfer_failed(srv);
|
||||
|
||||
rsp:
|
||||
update_status_rsp(srv, ctx, BT_MESH_DFU_SUCCESS, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_apply(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
|
||||
struct net_buf_simple *buf)
|
||||
{
|
||||
struct bt_mesh_dfu_srv *srv = mod->rt->user_data;
|
||||
static const struct bt_mesh_send_cb send_cb = {
|
||||
.start = apply_rsp_sending,
|
||||
.end = apply_rsp_sent,
|
||||
};
|
||||
|
||||
LOG_DBG("");
|
||||
|
||||
if (srv->update.phase == BT_MESH_DFU_PHASE_APPLYING) {
|
||||
update_status_rsp(srv, ctx, BT_MESH_DFU_SUCCESS, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (srv->update.phase != BT_MESH_DFU_PHASE_VERIFY_OK) {
|
||||
LOG_WRN("Apply: Invalid phase %u", srv->update.phase);
|
||||
update_status_rsp(srv, ctx, BT_MESH_DFU_ERR_WRONG_PHASE, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Postponing the apply callback until the response has been sent, in
|
||||
* case it triggers a reboot:
|
||||
*/
|
||||
srv->update.phase = BT_MESH_DFU_PHASE_APPLYING;
|
||||
update_status_rsp(srv, ctx, BT_MESH_DFU_SUCCESS, &send_cb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct bt_mesh_model_op _bt_mesh_dfu_srv_op[] = {
|
||||
{ BT_MESH_DFU_OP_UPDATE_INFO_GET, BT_MESH_LEN_EXACT(2), handle_info_get },
|
||||
{ BT_MESH_DFU_OP_UPDATE_METADATA_CHECK, BT_MESH_LEN_MIN(1), handle_metadata_check },
|
||||
{ BT_MESH_DFU_OP_UPDATE_GET, BT_MESH_LEN_EXACT(0), handle_get },
|
||||
{ BT_MESH_DFU_OP_UPDATE_START, BT_MESH_LEN_MIN(12), handle_start },
|
||||
{ BT_MESH_DFU_OP_UPDATE_CANCEL, BT_MESH_LEN_EXACT(0), handle_cancel },
|
||||
{ BT_MESH_DFU_OP_UPDATE_APPLY, BT_MESH_LEN_EXACT(0), handle_apply },
|
||||
BT_MESH_MODEL_OP_END,
|
||||
};
|
||||
|
||||
static int dfu_srv_init(const struct bt_mesh_model *mod)
|
||||
{
|
||||
struct bt_mesh_dfu_srv *srv = mod->rt->user_data;
|
||||
|
||||
srv->mod = mod;
|
||||
srv->update.idx = UPDATE_IDX_NONE;
|
||||
|
||||
if (!srv->cb || !srv->cb->start || !srv->imgs || srv->img_count == 0 ||
|
||||
srv->img_count == UPDATE_IDX_NONE) {
|
||||
LOG_ERR("Invalid DFU Server initialization");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_BT_MESH_MODEL_EXTENSIONS)) {
|
||||
bt_mesh_model_extend(mod, srv->blob.mod);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dfu_srv_settings_set(const struct bt_mesh_model *mod, const char *name,
|
||||
size_t len_rd, settings_read_cb read_cb,
|
||||
void *cb_arg)
|
||||
{
|
||||
struct bt_mesh_dfu_srv *srv = mod->rt->user_data;
|
||||
ssize_t len;
|
||||
|
||||
if (len_rd < sizeof(srv->update)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
len = read_cb(cb_arg, &srv->update, sizeof(srv->update));
|
||||
if (len < 0) {
|
||||
return len;
|
||||
}
|
||||
|
||||
LOG_DBG("Recovered transfer (phase: %u, idx: %u)", srv->update.phase,
|
||||
srv->update.idx);
|
||||
if (srv->update.phase == BT_MESH_DFU_PHASE_TRANSFER_ACTIVE) {
|
||||
LOG_DBG("Settings recovered mid-transfer, setting transfer error");
|
||||
srv->update.phase = BT_MESH_DFU_PHASE_TRANSFER_ERR;
|
||||
} else if (srv->update.phase == BT_MESH_DFU_PHASE_VERIFY_OK) {
|
||||
LOG_DBG("Settings recovered before application, setting verification fail");
|
||||
srv->update.phase = BT_MESH_DFU_PHASE_VERIFY_FAIL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dfu_srv_reset(const struct bt_mesh_model *mod)
|
||||
{
|
||||
struct bt_mesh_dfu_srv *srv = mod->rt->user_data;
|
||||
|
||||
srv->update.phase = BT_MESH_DFU_PHASE_IDLE;
|
||||
erase_state(srv);
|
||||
}
|
||||
|
||||
const struct bt_mesh_model_cb _bt_mesh_dfu_srv_cb = {
|
||||
.init = dfu_srv_init,
|
||||
.settings_set = dfu_srv_settings_set,
|
||||
.reset = dfu_srv_reset,
|
||||
};
|
||||
|
||||
static void blob_suspended(struct bt_mesh_blob_srv *b)
|
||||
{
|
||||
struct bt_mesh_dfu_srv *srv = CONTAINER_OF(b, struct bt_mesh_dfu_srv, blob);
|
||||
|
||||
srv->update.phase = BT_MESH_DFU_PHASE_TRANSFER_ERR;
|
||||
store_state(srv);
|
||||
}
|
||||
|
||||
static void blob_end(struct bt_mesh_blob_srv *b, uint64_t id, bool success)
|
||||
{
|
||||
struct bt_mesh_dfu_srv *srv =
|
||||
CONTAINER_OF(b, struct bt_mesh_dfu_srv, blob);
|
||||
|
||||
LOG_DBG("success: %u", success);
|
||||
|
||||
if (!success) {
|
||||
srv->update.phase = BT_MESH_DFU_PHASE_TRANSFER_ERR;
|
||||
xfer_failed(srv);
|
||||
return;
|
||||
}
|
||||
|
||||
verify(srv);
|
||||
}
|
||||
|
||||
static int blob_recover(struct bt_mesh_blob_srv *b,
|
||||
struct bt_mesh_blob_xfer *xfer,
|
||||
const struct bt_mesh_blob_io **io)
|
||||
{
|
||||
struct bt_mesh_dfu_srv *srv =
|
||||
CONTAINER_OF(b, struct bt_mesh_dfu_srv, blob);
|
||||
|
||||
if (!srv->cb->recover ||
|
||||
srv->update.phase != BT_MESH_DFU_PHASE_TRANSFER_ERR ||
|
||||
srv->update.idx >= srv->img_count) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return srv->cb->recover(srv, &srv->imgs[srv->update.idx], io);
|
||||
}
|
||||
|
||||
const struct bt_mesh_blob_srv_cb _bt_mesh_dfu_srv_blob_cb = {
|
||||
.suspended = blob_suspended,
|
||||
.end = blob_end,
|
||||
.recover = blob_recover,
|
||||
};
|
||||
|
||||
void bt_mesh_dfu_srv_verified(struct bt_mesh_dfu_srv *srv)
|
||||
{
|
||||
if (srv->update.phase != BT_MESH_DFU_PHASE_VERIFY) {
|
||||
LOG_WRN("Wrong state");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DBG("");
|
||||
|
||||
srv->update.phase = BT_MESH_DFU_PHASE_VERIFY_OK;
|
||||
store_state(srv);
|
||||
}
|
||||
|
||||
void bt_mesh_dfu_srv_rejected(struct bt_mesh_dfu_srv *srv)
|
||||
{
|
||||
if (srv->update.phase != BT_MESH_DFU_PHASE_VERIFY) {
|
||||
LOG_WRN("Wrong state");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DBG("");
|
||||
|
||||
srv->update.phase = BT_MESH_DFU_PHASE_VERIFY_FAIL;
|
||||
store_state(srv);
|
||||
}
|
||||
|
||||
void bt_mesh_dfu_srv_cancel(struct bt_mesh_dfu_srv *srv)
|
||||
{
|
||||
if (srv->update.phase == BT_MESH_DFU_PHASE_IDLE) {
|
||||
LOG_WRN("Wrong state");
|
||||
return;
|
||||
}
|
||||
|
||||
(void)bt_mesh_blob_srv_cancel(&srv->blob);
|
||||
}
|
||||
|
||||
void bt_mesh_dfu_srv_applied(struct bt_mesh_dfu_srv *srv)
|
||||
{
|
||||
if (srv->update.phase != BT_MESH_DFU_PHASE_APPLYING) {
|
||||
LOG_WRN("Wrong state");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DBG("");
|
||||
|
||||
srv->update.phase = BT_MESH_DFU_PHASE_IDLE;
|
||||
store_state(srv);
|
||||
}
|
||||
|
||||
bool bt_mesh_dfu_srv_is_busy(const struct bt_mesh_dfu_srv *srv)
|
||||
{
|
||||
return srv->update.phase != BT_MESH_DFU_PHASE_IDLE &&
|
||||
srv->update.phase != BT_MESH_DFU_PHASE_TRANSFER_ERR &&
|
||||
srv->update.phase != BT_MESH_DFU_PHASE_VERIFY_FAIL;
|
||||
}
|
||||
|
||||
uint8_t bt_mesh_dfu_srv_progress(const struct bt_mesh_dfu_srv *srv)
|
||||
{
|
||||
if (!bt_mesh_dfu_srv_is_busy(srv)) {
|
||||
return 0U;
|
||||
}
|
||||
|
||||
if (srv->update.phase == BT_MESH_DFU_PHASE_TRANSFER_ACTIVE) {
|
||||
return bt_mesh_blob_srv_progress(&srv->blob);
|
||||
}
|
||||
|
||||
return 100U;
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFD_H__
|
||||
#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFD_H__
|
||||
|
||||
#include <zephyr/bluetooth/mesh.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @defgroup bt_mesh_dfd Firmware Distribution models
|
||||
* @ingroup bt_mesh
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** Firmware distribution status. */
|
||||
enum bt_mesh_dfd_status {
|
||||
/** The message was processed successfully. */
|
||||
BT_MESH_DFD_SUCCESS,
|
||||
|
||||
/** Insufficient resources on the node. */
|
||||
BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES,
|
||||
|
||||
/** The operation cannot be performed while the Server is in the current
|
||||
* phase.
|
||||
*/
|
||||
BT_MESH_DFD_ERR_WRONG_PHASE,
|
||||
|
||||
/** An internal error occurred on the node. */
|
||||
BT_MESH_DFD_ERR_INTERNAL,
|
||||
|
||||
/** The requested firmware image is not stored on the Distributor. */
|
||||
BT_MESH_DFD_ERR_FW_NOT_FOUND,
|
||||
|
||||
/** The AppKey identified by the AppKey Index is not known to the node.
|
||||
*/
|
||||
BT_MESH_DFD_ERR_INVALID_APPKEY_INDEX,
|
||||
|
||||
/** There are no Target nodes in the Distribution Receivers List
|
||||
* state.
|
||||
*/
|
||||
BT_MESH_DFD_ERR_RECEIVERS_LIST_EMPTY,
|
||||
|
||||
/** Another firmware image distribution is in progress. */
|
||||
BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION,
|
||||
|
||||
/** Another upload is in progress. */
|
||||
BT_MESH_DFD_ERR_BUSY_WITH_UPLOAD,
|
||||
|
||||
/** The URI scheme name indicated by the Update URI is not supported. */
|
||||
BT_MESH_DFD_ERR_URI_NOT_SUPPORTED,
|
||||
|
||||
/** The format of the Update URI is invalid. */
|
||||
BT_MESH_DFD_ERR_URI_MALFORMED,
|
||||
|
||||
/** The URI is currently unreachable. */
|
||||
BT_MESH_DFD_ERR_URI_UNREACHABLE,
|
||||
|
||||
/** The Check Firmware OOB procedure did not find any new firmware. */
|
||||
BT_MESH_DFD_ERR_NEW_FW_NOT_AVAILABLE,
|
||||
|
||||
/** The suspension of the Distribute Firmware procedure failed. */
|
||||
BT_MESH_DFD_ERR_SUSPEND_FAILED,
|
||||
};
|
||||
|
||||
/** Firmware distribution phases. */
|
||||
enum bt_mesh_dfd_phase {
|
||||
/** No firmware distribution is in progress. */
|
||||
BT_MESH_DFD_PHASE_IDLE,
|
||||
|
||||
/** Firmware distribution is in progress. */
|
||||
BT_MESH_DFD_PHASE_TRANSFER_ACTIVE,
|
||||
|
||||
/** The Transfer BLOB procedure has completed successfully. */
|
||||
BT_MESH_DFD_PHASE_TRANSFER_SUCCESS,
|
||||
|
||||
/** The Apply Firmware on Target Nodes procedure is being executed. */
|
||||
BT_MESH_DFD_PHASE_APPLYING_UPDATE,
|
||||
|
||||
/** The Distribute Firmware procedure has completed successfully. */
|
||||
BT_MESH_DFD_PHASE_COMPLETED,
|
||||
|
||||
/** The Distribute Firmware procedure has failed. */
|
||||
BT_MESH_DFD_PHASE_FAILED,
|
||||
|
||||
/** The Cancel Firmware Update procedure is being executed. */
|
||||
BT_MESH_DFD_PHASE_CANCELING_UPDATE,
|
||||
|
||||
/** The Transfer BLOB procedure is suspended. */
|
||||
BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED,
|
||||
};
|
||||
|
||||
/** Firmware upload phases. */
|
||||
enum bt_mesh_dfd_upload_phase {
|
||||
/** No firmware upload is in progress. */
|
||||
BT_MESH_DFD_UPLOAD_PHASE_IDLE,
|
||||
|
||||
/** The Store Firmware procedure is being executed. */
|
||||
BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE,
|
||||
|
||||
/** The Store Firmware procedure or Store Firmware OOB procedure failed.
|
||||
*/
|
||||
BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ERROR,
|
||||
|
||||
/** The Store Firmware procedure or the Store Firmware OOB procedure
|
||||
* completed successfully.
|
||||
*/
|
||||
BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS,
|
||||
};
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFD_H__ */
|
||||
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @defgroup bt_mesh_dfd_srv Firmware Distribution Server model
|
||||
* @ingroup bt_mesh_dfd
|
||||
* @{
|
||||
* @brief API for the Firmware Distribution Server model
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFD_SRV_H__
|
||||
#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFD_SRV_H__
|
||||
|
||||
#include <zephyr/bluetooth/mesh/access.h>
|
||||
#include <zephyr/bluetooth/mesh/dfd.h>
|
||||
#include <zephyr/bluetooth/mesh/blob_srv.h>
|
||||
#include <zephyr/bluetooth/mesh/blob_cli.h>
|
||||
#include <zephyr/bluetooth/mesh/dfu_cli.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_BT_MESH_DFD_SRV_TARGETS_MAX
|
||||
#define CONFIG_BT_MESH_DFD_SRV_TARGETS_MAX 0
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_BT_MESH_DFD_SRV_SLOT_MAX_SIZE
|
||||
#define CONFIG_BT_MESH_DFD_SRV_SLOT_MAX_SIZE 0
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE
|
||||
#define CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE 0
|
||||
#endif
|
||||
|
||||
struct bt_mesh_dfd_srv;
|
||||
|
||||
#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD
|
||||
/**
|
||||
*
|
||||
* @brief Initialization parameters for the @ref bt_mesh_dfd_srv with OOB
|
||||
* upload support.
|
||||
*
|
||||
* @param[in] _cb Pointer to a @ref bt_mesh_dfd_srv_cb instance.
|
||||
* @param[in] _oob_schemes Array of OOB schemes supported by the server,
|
||||
* each scheme being a code point from the
|
||||
* Bluetooth SIG Assigned Numbers document.
|
||||
* @param[in] _oob_schemes_count Number of schemes in @c _oob_schemes.
|
||||
*/
|
||||
#define BT_MESH_DFD_SRV_OOB_INIT(_cb, _oob_schemes, _oob_schemes_count) \
|
||||
{ \
|
||||
.cb = _cb, \
|
||||
.dfu = BT_MESH_DFU_CLI_INIT(&_bt_mesh_dfd_srv_dfu_cb), \
|
||||
.upload = { \
|
||||
.blob = { .cb = &_bt_mesh_dfd_srv_blob_cb }, \
|
||||
}, \
|
||||
.oob_schemes = { \
|
||||
.schemes = _oob_schemes, \
|
||||
.count = _oob_schemes_count, \
|
||||
}, \
|
||||
}
|
||||
#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Initialization parameters for the @ref bt_mesh_dfd_srv.
|
||||
*
|
||||
* @param[in] _cb Pointer to a @ref bt_mesh_dfd_srv_cb instance.
|
||||
*/
|
||||
#define BT_MESH_DFD_SRV_INIT(_cb) \
|
||||
{ \
|
||||
.cb = _cb, \
|
||||
.dfu = BT_MESH_DFU_CLI_INIT(&_bt_mesh_dfd_srv_dfu_cb), \
|
||||
.upload = { \
|
||||
.blob = { .cb = &_bt_mesh_dfd_srv_blob_cb }, \
|
||||
}, \
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Firmware Distribution Server model Composition Data entry.
|
||||
*
|
||||
* @param _srv Pointer to a @ref bt_mesh_dfd_srv instance.
|
||||
*/
|
||||
#define BT_MESH_MODEL_DFD_SRV(_srv) \
|
||||
BT_MESH_MODEL_DFU_CLI(&(_srv)->dfu), \
|
||||
BT_MESH_MODEL_BLOB_SRV(&(_srv)->upload.blob), \
|
||||
BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_DFD_SRV, _bt_mesh_dfd_srv_op, NULL, \
|
||||
_srv, &_bt_mesh_dfd_srv_cb)
|
||||
|
||||
/** Firmware Distribution Server callbacks: */
|
||||
struct bt_mesh_dfd_srv_cb {
|
||||
|
||||
/** @brief Slot receive callback.
|
||||
*
|
||||
* Called at the start of an upload procedure. The callback must fill
|
||||
* @c io with a pointer to a writable BLOB stream for the Firmware Distribution
|
||||
* Server to write the firmware image to.
|
||||
*
|
||||
* @param srv Firmware Distribution Server model instance.
|
||||
* @param slot DFU image slot being received.
|
||||
* @param io BLOB stream response pointer.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code otherwise.
|
||||
*/
|
||||
int (*recv)(struct bt_mesh_dfd_srv *srv,
|
||||
const struct bt_mesh_dfu_slot *slot,
|
||||
const struct bt_mesh_blob_io **io);
|
||||
|
||||
#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD
|
||||
/** @brief Firmware upload OOB start callback.
|
||||
*
|
||||
* Called at the start of an OOB firmware upload. The application must
|
||||
* start a firmware check using an OOB mechanism, and then call
|
||||
* @ref bt_mesh_dfd_srv_oob_check_complete. Depending on the return
|
||||
* value of this function, the application must then start storing the
|
||||
* firmware image using an OOB mechanism, and call
|
||||
* @ref bt_mesh_dfd_srv_oob_store_complete. This callback is mandatory
|
||||
* to support OOB uploads.
|
||||
*
|
||||
* @param srv Firmware Distribution Server model instance.
|
||||
* @param slot Slot to be used for the upload.
|
||||
* @param uri Pointer to buffer containing the URI used to
|
||||
* check for new firmware.
|
||||
* @param uri_len Length of the URI buffer.
|
||||
* @param fwid Pointer to buffer containing the current
|
||||
* firmware ID to be used when checking for
|
||||
* availability of new firmware.
|
||||
* @param fwid_len Length of the current firmware ID. Must be set
|
||||
* to the length of the new firmware ID if it is
|
||||
* available, or to 0 if new firmware is not
|
||||
* available.
|
||||
*
|
||||
* @return BT_MESH_DFD_SUCCESS on success, or error code otherwise.
|
||||
*/
|
||||
int (*start_oob_upload)(struct bt_mesh_dfd_srv *srv,
|
||||
const struct bt_mesh_dfu_slot *slot,
|
||||
const char *uri, uint8_t uri_len,
|
||||
const uint8_t *fwid, uint16_t fwid_len);
|
||||
|
||||
/** @brief Cancel store OOB callback
|
||||
*
|
||||
* Called when an OOB store is cancelled. The application must stop
|
||||
* any ongoing OOB image transfer. This callback is mandatory to
|
||||
* support OOB uploads.
|
||||
*
|
||||
* @param srv Firmware Distribution Server model instance.
|
||||
* @param slot DFU image slot to cancel
|
||||
*/
|
||||
void (*cancel_oob_upload)(struct bt_mesh_dfd_srv *srv,
|
||||
const struct bt_mesh_dfu_slot *slot);
|
||||
|
||||
/** @brief Get the progress of an ongoing OOB store
|
||||
*
|
||||
* Called by the Firmware Distribution Server model when it needs to
|
||||
* get the current progress of an ongoing OOB store from the
|
||||
* application. This callback is mandatory to support OOB uploads.
|
||||
*
|
||||
* @param srv Firmware Distribution Server model instance.
|
||||
* @param slot DFU image slot to get progress for.
|
||||
*
|
||||
* @return The current progress of the ongoing OOB store, in percent.
|
||||
*/
|
||||
uint8_t (*oob_progress_get)(struct bt_mesh_dfd_srv *srv,
|
||||
const struct bt_mesh_dfu_slot *slot);
|
||||
#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */
|
||||
|
||||
/** @brief Slot delete callback.
|
||||
*
|
||||
* Called when the Firmware Distribution Server is about to delete a DFU image slot.
|
||||
* All allocated data associated with the firmware slot should be
|
||||
* deleted.
|
||||
*
|
||||
* @param srv Firmware Update Server instance.
|
||||
* @param slot DFU image slot being deleted.
|
||||
*/
|
||||
void (*del)(struct bt_mesh_dfd_srv *srv,
|
||||
const struct bt_mesh_dfu_slot *slot);
|
||||
|
||||
/** @brief Slot send callback.
|
||||
*
|
||||
* Called at the start of a distribution procedure. The callback must
|
||||
* fill @c io with a pointer to a readable BLOB stream for the Firmware
|
||||
* Distribution Server to read the firmware image from.
|
||||
*
|
||||
* @param srv Firmware Distribution Server model instance.
|
||||
* @param slot DFU image slot being sent.
|
||||
* @param io BLOB stream response pointer.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code otherwise.
|
||||
*/
|
||||
int (*send)(struct bt_mesh_dfd_srv *srv,
|
||||
const struct bt_mesh_dfu_slot *slot,
|
||||
const struct bt_mesh_blob_io **io);
|
||||
|
||||
/** @brief Phase change callback (Optional).
|
||||
*
|
||||
* Called whenever the phase of the Firmware Distribution Server changes.
|
||||
*
|
||||
* @param srv Firmware Distribution Server model instance.
|
||||
* @param phase New Firmware Distribution phase.
|
||||
*/
|
||||
void (*phase)(struct bt_mesh_dfd_srv *srv, enum bt_mesh_dfd_phase phase);
|
||||
};
|
||||
|
||||
/** Firmware Distribution Server instance. */
|
||||
struct bt_mesh_dfd_srv {
|
||||
const struct bt_mesh_dfd_srv_cb *cb;
|
||||
const struct bt_mesh_model *mod;
|
||||
struct bt_mesh_dfu_cli dfu;
|
||||
struct bt_mesh_dfu_target targets[CONFIG_BT_MESH_DFD_SRV_TARGETS_MAX];
|
||||
struct bt_mesh_blob_target_pull pull_ctxs[CONFIG_BT_MESH_DFD_SRV_TARGETS_MAX];
|
||||
const struct bt_mesh_blob_io *io;
|
||||
uint16_t target_cnt;
|
||||
uint16_t slot_idx;
|
||||
bool apply;
|
||||
enum bt_mesh_dfd_phase phase;
|
||||
struct bt_mesh_blob_cli_inputs inputs;
|
||||
|
||||
struct {
|
||||
enum bt_mesh_dfd_upload_phase phase;
|
||||
struct bt_mesh_dfu_slot *slot;
|
||||
const struct flash_area *area;
|
||||
struct bt_mesh_blob_srv blob;
|
||||
#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD
|
||||
bool is_oob;
|
||||
bool is_pending_oob_check;
|
||||
struct {
|
||||
uint8_t uri_len;
|
||||
uint8_t uri[CONFIG_BT_MESH_DFU_URI_MAXLEN];
|
||||
uint16_t current_fwid_len;
|
||||
uint8_t current_fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN];
|
||||
struct bt_mesh_msg_ctx ctx;
|
||||
} oob;
|
||||
#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */
|
||||
} upload;
|
||||
|
||||
#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD
|
||||
struct {
|
||||
const uint8_t *schemes;
|
||||
const uint8_t count;
|
||||
} oob_schemes;
|
||||
#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */
|
||||
};
|
||||
|
||||
#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD
|
||||
/** @brief Call when an OOB check has completed or failed
|
||||
*
|
||||
* This should be called by the application after an OOB check started by the @c start_oob_upload
|
||||
* callback has completed or failed. The @p status param should be set to one of the following
|
||||
* values:
|
||||
*
|
||||
* * @c BT_MESH_DFD_SUCCESS if the check was successful and a new firmware ID was found.
|
||||
* * @c BT_MESH_DFD_ERR_URI_MALFORMED if the URI is not formatted correctly.
|
||||
* * @c BT_MESH_DFD_ERR_URI_NOT_SUPPORTED if the URI scheme is not supported by the node.
|
||||
* * @c BT_MESH_DFD_ERR_URI_UNREACHABLE if the URI can't be reached.
|
||||
* * @c BT_MESH_DFD_ERR_NEW_FW_NOT_AVAILABLE if the check completes successfully but no new
|
||||
* firmware is available.
|
||||
*
|
||||
* If this function returns 0, the application should then download the firmware to the
|
||||
* slot. If an error code is returned, the application should abort the OOB upload.
|
||||
*
|
||||
* @param srv Firmware Distribution Server model instance.
|
||||
* @param slot The slot used in the OOB upload.
|
||||
* @param status The result of the firmware check.
|
||||
* @param fwid If the check was successful and new firmware found, this should point to a
|
||||
* buffer containing the new firmware ID to store.
|
||||
* @param fwid_len The length of the firmware ID pointed to by @p fwid.
|
||||
*
|
||||
* @return 0 on success, (negative) error code otherwise.
|
||||
*/
|
||||
int bt_mesh_dfd_srv_oob_check_complete(struct bt_mesh_dfd_srv *srv,
|
||||
const struct bt_mesh_dfu_slot *slot, int status,
|
||||
uint8_t *fwid, size_t fwid_len);
|
||||
|
||||
/** @brief Call when an OOB store has completed or failed
|
||||
*
|
||||
* This should be called by the application after an OOB store started after a successful call to
|
||||
* @c bt_mesh_dfd_srv_oob_check_complete has completed successfully or failed.
|
||||
*
|
||||
* @param srv Firmware Distribution Server model instance.
|
||||
* @param slot The slot used when storing the firmware image.
|
||||
* @param success @c true if the OOB store completed successfully, @c false otherwise.
|
||||
* @param size The size of the stored firmware image, in bytes.
|
||||
* @param metadata Pointer to the metadata received OOB, or @c NULL if no metadata was
|
||||
* received.
|
||||
* @param metadata_len Size of the metadata pointed to by @p metadata.
|
||||
*
|
||||
* @return 0 on success, (negative) error code otherwise.
|
||||
*/
|
||||
int bt_mesh_dfd_srv_oob_store_complete(struct bt_mesh_dfd_srv *srv,
|
||||
const struct bt_mesh_dfu_slot *slot, bool success,
|
||||
size_t size, const uint8_t *metadata, size_t metadata_len);
|
||||
#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */
|
||||
|
||||
/** @cond INTERNAL_HIDDEN */
|
||||
extern const struct bt_mesh_model_op _bt_mesh_dfd_srv_op[];
|
||||
extern const struct bt_mesh_model_cb _bt_mesh_dfd_srv_cb;
|
||||
extern const struct bt_mesh_dfu_cli_cb _bt_mesh_dfd_srv_dfu_cb;
|
||||
extern const struct bt_mesh_blob_srv_cb _bt_mesh_dfd_srv_blob_cb;
|
||||
/** @endcond */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFD_SRV_H__ */
|
||||
|
||||
/** @} */
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_H__
|
||||
#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_H__
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/bluetooth/mesh/blob.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @defgroup bt_mesh_dfu Bluetooth Mesh Device Firmware Update
|
||||
* @ingroup bt_mesh
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_BT_MESH_DFU_FWID_MAXLEN
|
||||
#define CONFIG_BT_MESH_DFU_FWID_MAXLEN 0
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_BT_MESH_DFU_METADATA_MAXLEN
|
||||
#define CONFIG_BT_MESH_DFU_METADATA_MAXLEN 0
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_BT_MESH_DFU_URI_MAXLEN
|
||||
#define CONFIG_BT_MESH_DFU_URI_MAXLEN 0
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_BT_MESH_DFU_SLOT_CNT
|
||||
#define CONFIG_BT_MESH_DFU_SLOT_CNT 0
|
||||
#endif
|
||||
|
||||
/** DFU transfer phase. */
|
||||
enum bt_mesh_dfu_phase {
|
||||
/** Ready to start a Receive Firmware procedure. */
|
||||
BT_MESH_DFU_PHASE_IDLE,
|
||||
|
||||
/** The Transfer BLOB procedure failed. */
|
||||
BT_MESH_DFU_PHASE_TRANSFER_ERR,
|
||||
|
||||
/** The Receive Firmware procedure is being executed. */
|
||||
BT_MESH_DFU_PHASE_TRANSFER_ACTIVE,
|
||||
|
||||
/** The Verify Firmware procedure is being executed. */
|
||||
BT_MESH_DFU_PHASE_VERIFY,
|
||||
|
||||
/** The Verify Firmware procedure completed successfully. */
|
||||
BT_MESH_DFU_PHASE_VERIFY_OK,
|
||||
|
||||
/** The Verify Firmware procedure failed. */
|
||||
BT_MESH_DFU_PHASE_VERIFY_FAIL,
|
||||
|
||||
/** The Apply New Firmware procedure is being executed. */
|
||||
BT_MESH_DFU_PHASE_APPLYING,
|
||||
|
||||
/** Firmware transfer has been canceled. */
|
||||
BT_MESH_DFU_PHASE_TRANSFER_CANCELED,
|
||||
|
||||
/** Firmware applying succeeded. */
|
||||
BT_MESH_DFU_PHASE_APPLY_SUCCESS,
|
||||
|
||||
/** Firmware applying failed. */
|
||||
BT_MESH_DFU_PHASE_APPLY_FAIL,
|
||||
|
||||
/** Phase of a node was not yet retrieved. */
|
||||
BT_MESH_DFU_PHASE_UNKNOWN,
|
||||
};
|
||||
|
||||
|
||||
/** DFU status. */
|
||||
enum bt_mesh_dfu_status {
|
||||
/** The message was processed successfully. */
|
||||
BT_MESH_DFU_SUCCESS,
|
||||
|
||||
/** Insufficient resources on the node */
|
||||
BT_MESH_DFU_ERR_RESOURCES,
|
||||
|
||||
/** The operation cannot be performed while the Server is in the current
|
||||
* phase.
|
||||
*/
|
||||
BT_MESH_DFU_ERR_WRONG_PHASE,
|
||||
|
||||
/** An internal error occurred on the node. */
|
||||
BT_MESH_DFU_ERR_INTERNAL,
|
||||
|
||||
/** The message contains a firmware index value that is not expected. */
|
||||
BT_MESH_DFU_ERR_FW_IDX,
|
||||
|
||||
/** The metadata check failed. */
|
||||
BT_MESH_DFU_ERR_METADATA,
|
||||
|
||||
/** The Server cannot start a firmware update. */
|
||||
BT_MESH_DFU_ERR_TEMPORARILY_UNAVAILABLE,
|
||||
|
||||
/** Another BLOB transfer is in progress. */
|
||||
BT_MESH_DFU_ERR_BLOB_XFER_BUSY,
|
||||
};
|
||||
|
||||
/** Expected effect of a DFU transfer. */
|
||||
enum bt_mesh_dfu_effect {
|
||||
/** No changes to node Composition Data. */
|
||||
BT_MESH_DFU_EFFECT_NONE,
|
||||
|
||||
/** Node Composition Data changed and the node does not support remote
|
||||
* provisioning.
|
||||
*/
|
||||
BT_MESH_DFU_EFFECT_COMP_CHANGE_NO_RPR,
|
||||
|
||||
/** Node Composition Data changed, and remote provisioning is supported.
|
||||
* The node supports remote provisioning and Composition Data Page
|
||||
* 0x80. Page 0x80 contains different Composition Data than Page 0x0.
|
||||
*/
|
||||
BT_MESH_DFU_EFFECT_COMP_CHANGE,
|
||||
|
||||
/** Node will be unprovisioned after the update. */
|
||||
BT_MESH_DFU_EFFECT_UNPROV,
|
||||
};
|
||||
|
||||
/** Action for DFU iteration callbacks. */
|
||||
enum bt_mesh_dfu_iter {
|
||||
/** Stop iterating. */
|
||||
BT_MESH_DFU_ITER_STOP,
|
||||
|
||||
/** Continue iterating. */
|
||||
BT_MESH_DFU_ITER_CONTINUE,
|
||||
};
|
||||
|
||||
/** DFU image instance.
|
||||
*
|
||||
* Each DFU image represents a single updatable firmware image.
|
||||
*/
|
||||
struct bt_mesh_dfu_img {
|
||||
/** Firmware ID. */
|
||||
const void *fwid;
|
||||
|
||||
/** Length of the firmware ID. */
|
||||
size_t fwid_len;
|
||||
|
||||
/** Update URI, or NULL. */
|
||||
const char *uri;
|
||||
};
|
||||
|
||||
/** DFU image slot for DFU distribution. */
|
||||
struct bt_mesh_dfu_slot {
|
||||
/** Size of the firmware in bytes. */
|
||||
size_t size;
|
||||
/** Length of the firmware ID. */
|
||||
size_t fwid_len;
|
||||
/** Length of the metadata. */
|
||||
size_t metadata_len;
|
||||
/** Firmware ID. */
|
||||
uint8_t fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN];
|
||||
/** Metadata. */
|
||||
uint8_t metadata[CONFIG_BT_MESH_DFU_METADATA_MAXLEN];
|
||||
};
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_H__ */
|
||||
@@ -0,0 +1,409 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @defgroup bt_mesh_dfu_cli Firmware Uppdate Client model
|
||||
* @ingroup bt_mesh_dfu
|
||||
* @{
|
||||
* @brief API for the Bluetooth Mesh Firmware Update Client model
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_CLI_H__
|
||||
#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_CLI_H__
|
||||
|
||||
#include <zephyr/bluetooth/mesh/access.h>
|
||||
#include <zephyr/bluetooth/mesh/blob_cli.h>
|
||||
#include <zephyr/bluetooth/mesh/dfu.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct bt_mesh_dfu_cli;
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Initialization parameters for the @ref bt_mesh_dfu_cli.
|
||||
*
|
||||
* @sa bt_mesh_dfu_cli_cb.
|
||||
*
|
||||
* @param _handlers Handler callback structure.
|
||||
*/
|
||||
#define BT_MESH_DFU_CLI_INIT(_handlers) \
|
||||
{ \
|
||||
.cb = _handlers, \
|
||||
.blob = { .cb = &_bt_mesh_dfu_cli_blob_handlers }, \
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Firmware Update Client model Composition Data entry.
|
||||
*
|
||||
* @param _cli Pointer to a @ref bt_mesh_dfu_cli instance.
|
||||
*/
|
||||
#define BT_MESH_MODEL_DFU_CLI(_cli) \
|
||||
BT_MESH_MODEL_BLOB_CLI(&(_cli)->blob), \
|
||||
BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_DFU_CLI, _bt_mesh_dfu_cli_op, NULL, \
|
||||
_cli, &_bt_mesh_dfu_cli_cb)
|
||||
|
||||
/** DFU Target node. */
|
||||
struct bt_mesh_dfu_target {
|
||||
/** BLOB Target node */
|
||||
struct bt_mesh_blob_target blob;
|
||||
/** Image index on the Target node */
|
||||
uint8_t img_idx;
|
||||
/** Expected DFU effect, see @ref bt_mesh_dfu_effect. */
|
||||
uint8_t effect;
|
||||
/** Current DFU status, see @ref bt_mesh_dfu_status. */
|
||||
uint8_t status;
|
||||
/** Current DFU phase, see @ref bt_mesh_dfu_phase. */
|
||||
uint8_t phase;
|
||||
};
|
||||
|
||||
/** Metadata status response. */
|
||||
struct bt_mesh_dfu_metadata_status {
|
||||
/** Image index. */
|
||||
uint8_t idx;
|
||||
/** Status code. */
|
||||
enum bt_mesh_dfu_status status;
|
||||
/** Effect of transfer. */
|
||||
enum bt_mesh_dfu_effect effect;
|
||||
};
|
||||
|
||||
/** DFU Target node status parameters. */
|
||||
struct bt_mesh_dfu_target_status {
|
||||
/** Status of the previous operation. */
|
||||
enum bt_mesh_dfu_status status;
|
||||
/** Phase of the current DFU transfer. */
|
||||
enum bt_mesh_dfu_phase phase;
|
||||
/** The effect the update will have on the Target device's state. */
|
||||
enum bt_mesh_dfu_effect effect;
|
||||
/** BLOB ID used in the transfer. */
|
||||
uint64_t blob_id;
|
||||
/** Image index to transfer. */
|
||||
uint8_t img_idx;
|
||||
/** TTL used in the transfer. */
|
||||
uint8_t ttl;
|
||||
|
||||
/** Additional response time for the Target nodes, in 10-second increments.
|
||||
*
|
||||
* The extra time can be used to give the Target nodes more time to respond
|
||||
* to messages from the Client. The actual timeout will be calculated
|
||||
* according to the following formula:
|
||||
*
|
||||
* @verbatim
|
||||
* timeout = 20 seconds + (10 seconds * timeout_base) + (100 ms * TTL)
|
||||
* @endverbatim
|
||||
*
|
||||
* If a Target node fails to respond to a message from the Client within the
|
||||
* configured transfer timeout, the Target node is dropped.
|
||||
*/
|
||||
uint16_t timeout_base;
|
||||
};
|
||||
|
||||
/** @brief DFU image callback.
|
||||
*
|
||||
* The image callback is called for every DFU image on the Target node when
|
||||
* calling @ref bt_mesh_dfu_cli_imgs_get.
|
||||
*
|
||||
* @param cli Firmware Update Client model instance.
|
||||
* @param ctx Message context of the received message.
|
||||
* @param idx Image index.
|
||||
* @param total Total number of images on the Target node.
|
||||
* @param img Image information for the given image index.
|
||||
* @param cb_data Callback data.
|
||||
*
|
||||
* @retval BT_MESH_DFU_ITER_STOP Stop iterating through the image list and
|
||||
* return from @ref bt_mesh_dfu_cli_imgs_get.
|
||||
* @retval BT_MESH_DFU_ITER_CONTINUE Continue iterating through the image list
|
||||
* if any images remain.
|
||||
*/
|
||||
typedef enum bt_mesh_dfu_iter (*bt_mesh_dfu_img_cb_t)(
|
||||
struct bt_mesh_dfu_cli *cli, struct bt_mesh_msg_ctx *ctx, uint8_t idx,
|
||||
uint8_t total, const struct bt_mesh_dfu_img *img, void *cb_data);
|
||||
|
||||
/** Firmware Update Client event callbacks. */
|
||||
struct bt_mesh_dfu_cli_cb {
|
||||
/** @brief BLOB transfer is suspended.
|
||||
*
|
||||
* Called when the BLOB transfer is suspended due to response timeout from all Target nodes.
|
||||
*
|
||||
* @param cli Firmware Update Client model instance.
|
||||
*/
|
||||
void (*suspended)(struct bt_mesh_dfu_cli *cli);
|
||||
|
||||
/** @brief DFU ended.
|
||||
*
|
||||
* Called when the DFU transfer ends, either because all Target nodes were
|
||||
* lost or because the transfer was completed successfully.
|
||||
*
|
||||
* @param cli Firmware Update Client model instance.
|
||||
* @param reason Reason for ending.
|
||||
*/
|
||||
void (*ended)(struct bt_mesh_dfu_cli *cli,
|
||||
enum bt_mesh_dfu_status reason);
|
||||
|
||||
/** @brief DFU transfer applied on all active Target nodes.
|
||||
*
|
||||
* Called at the end of the apply procedure started by @ref
|
||||
* bt_mesh_dfu_cli_apply.
|
||||
*
|
||||
* @param cli Firmware Update Client model instance.
|
||||
*/
|
||||
void (*applied)(struct bt_mesh_dfu_cli *cli);
|
||||
|
||||
/** @brief DFU transfer confirmed on all active Target nodes.
|
||||
*
|
||||
* Called at the end of the apply procedure started by @ref
|
||||
* bt_mesh_dfu_cli_confirm.
|
||||
*
|
||||
* @param cli Firmware Update Client model instance.
|
||||
*/
|
||||
void (*confirmed)(struct bt_mesh_dfu_cli *cli);
|
||||
|
||||
/** @brief DFU Target node was lost.
|
||||
*
|
||||
* A DFU Target node was dropped from the receivers list. The Target node's
|
||||
* @c status is set to reflect the reason for the failure.
|
||||
*
|
||||
* @param cli Firmware Update Client model instance.
|
||||
* @param target DFU Target node that was lost.
|
||||
*/
|
||||
void (*lost_target)(struct bt_mesh_dfu_cli *cli,
|
||||
struct bt_mesh_dfu_target *target);
|
||||
};
|
||||
|
||||
/** Firmware Update Client model instance.
|
||||
*
|
||||
* Should be initialized with @ref BT_MESH_DFU_CLI_INIT.
|
||||
*/
|
||||
struct bt_mesh_dfu_cli {
|
||||
/** Callback structure. */
|
||||
const struct bt_mesh_dfu_cli_cb *cb;
|
||||
/** Underlying BLOB Transfer Client. */
|
||||
struct bt_mesh_blob_cli blob;
|
||||
|
||||
/* runtime state */
|
||||
|
||||
uint32_t op;
|
||||
const struct bt_mesh_model *mod;
|
||||
|
||||
struct {
|
||||
const struct bt_mesh_dfu_slot *slot;
|
||||
const struct bt_mesh_blob_io *io;
|
||||
struct bt_mesh_blob_xfer blob;
|
||||
uint8_t state;
|
||||
uint8_t flags;
|
||||
} xfer;
|
||||
|
||||
struct {
|
||||
uint8_t ttl;
|
||||
uint8_t type;
|
||||
uint8_t img_cnt;
|
||||
uint16_t addr;
|
||||
struct k_sem sem;
|
||||
void *params;
|
||||
bt_mesh_dfu_img_cb_t img_cb;
|
||||
} req;
|
||||
};
|
||||
|
||||
/** BLOB parameters for Firmware Update Client transfer: */
|
||||
struct bt_mesh_dfu_cli_xfer_blob_params {
|
||||
/* Logarithmic representation of the block size. */
|
||||
uint8_t block_size_log;
|
||||
/** Base chunk size. May be smaller for the last chunk. */
|
||||
uint16_t chunk_size;
|
||||
};
|
||||
|
||||
/** Firmware Update Client transfer parameters: */
|
||||
struct bt_mesh_dfu_cli_xfer {
|
||||
/** BLOB ID to use for this transfer, or 0 to set it randomly. */
|
||||
uint64_t blob_id;
|
||||
/** DFU image slot to transfer. */
|
||||
const struct bt_mesh_dfu_slot *slot;
|
||||
/** Transfer mode (Push (Push BLOB Transfer Mode) or Pull (Pull BLOB Transfer Mode)) */
|
||||
enum bt_mesh_blob_xfer_mode mode;
|
||||
/** BLOB parameters to be used for the transfer, or NULL to retrieve Target nodes'
|
||||
* capabilities before sending a firmware.
|
||||
*/
|
||||
const struct bt_mesh_dfu_cli_xfer_blob_params *blob_params;
|
||||
};
|
||||
|
||||
/** @brief Start distributing a DFU.
|
||||
*
|
||||
* Starts distribution of the firmware in the given slot to the list of DFU
|
||||
* Target nodes in @c ctx. The transfer runs in the background, and its end is
|
||||
* signalled through the @ref bt_mesh_dfu_cli_cb::ended callback.
|
||||
*
|
||||
* @note The BLOB Transfer Client transfer inputs @c targets list must point to a list of @ref
|
||||
* bt_mesh_dfu_target nodes.
|
||||
*
|
||||
* @param cli Firmware Update Client model instance.
|
||||
* @param inputs BLOB Transfer Client transfer inputs.
|
||||
* @param io BLOB stream to read BLOB from.
|
||||
* @param xfer Firmware Update Client transfer parameters.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code otherwise.
|
||||
*/
|
||||
int bt_mesh_dfu_cli_send(struct bt_mesh_dfu_cli *cli,
|
||||
const struct bt_mesh_blob_cli_inputs *inputs,
|
||||
const struct bt_mesh_blob_io *io,
|
||||
const struct bt_mesh_dfu_cli_xfer *xfer);
|
||||
|
||||
/** @brief Suspend a DFU transfer.
|
||||
*
|
||||
* @param cli Firmware Update Client instance.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code otherwise.
|
||||
*/
|
||||
int bt_mesh_dfu_cli_suspend(struct bt_mesh_dfu_cli *cli);
|
||||
|
||||
/** @brief Resume the suspended transfer.
|
||||
*
|
||||
* @param cli Firmware Update Client instance.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code otherwise.
|
||||
*/
|
||||
int bt_mesh_dfu_cli_resume(struct bt_mesh_dfu_cli *cli);
|
||||
|
||||
/** @brief Cancel a DFU transfer.
|
||||
*
|
||||
* Will cancel the ongoing DFU transfer, or the transfer on a specific Target
|
||||
* node if @c ctx is valid.
|
||||
*
|
||||
* @param cli Firmware Update Client model instance.
|
||||
* @param ctx Message context, or NULL to cancel the ongoing DFU transfer.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code otherwise.
|
||||
*/
|
||||
int bt_mesh_dfu_cli_cancel(struct bt_mesh_dfu_cli *cli,
|
||||
struct bt_mesh_msg_ctx *ctx);
|
||||
|
||||
/** @brief Apply the completed DFU transfer.
|
||||
*
|
||||
* A transfer can only be applied after it has ended successfully. The Firmware
|
||||
* Update Client's @c applied callback is called at the end of the apply procedure.
|
||||
*
|
||||
* @param cli Firmware Update Client model instance.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code otherwise.
|
||||
*/
|
||||
int bt_mesh_dfu_cli_apply(struct bt_mesh_dfu_cli *cli);
|
||||
|
||||
/** @brief Confirm that the active transfer has been applied on the Target nodes.
|
||||
*
|
||||
* A transfer can only be confirmed after it has been applied. The Firmware Update
|
||||
* Client's @c confirmed callback is called at the end of the confirm
|
||||
* procedure.
|
||||
*
|
||||
* Target nodes that have reported the effect as @ref BT_MESH_DFU_EFFECT_UNPROV
|
||||
* are expected to not respond to the query, and will fail if they do.
|
||||
*
|
||||
* @param cli Firmware Update Client model instance.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code otherwise.
|
||||
*/
|
||||
int bt_mesh_dfu_cli_confirm(struct bt_mesh_dfu_cli *cli);
|
||||
|
||||
/** @brief Get progress as a percentage of completion.
|
||||
*
|
||||
* @param cli Firmware Update Client model instance.
|
||||
*
|
||||
* @return The progress of the current transfer in percent, or 0 if no
|
||||
* transfer is active.
|
||||
*/
|
||||
uint8_t bt_mesh_dfu_cli_progress(struct bt_mesh_dfu_cli *cli);
|
||||
|
||||
/** @brief Check whether a DFU transfer is in progress.
|
||||
*
|
||||
* @param cli Firmware Update Client model instance.
|
||||
*
|
||||
* @return true if the BLOB Transfer Client is currently participating in a transfer,
|
||||
* false otherwise.
|
||||
*/
|
||||
bool bt_mesh_dfu_cli_is_busy(struct bt_mesh_dfu_cli *cli);
|
||||
|
||||
/** @brief Perform a DFU image list request.
|
||||
*
|
||||
* Requests the full list of DFU images on a Target node, and iterates through
|
||||
* them, calling the @c cb for every image.
|
||||
*
|
||||
* The DFU image list request can be used to determine which image index the
|
||||
* Target node holds its different firmwares in.
|
||||
*
|
||||
* Waits for a response until the procedure timeout expires.
|
||||
*
|
||||
* @param cli Firmware Update Client model instance.
|
||||
* @param ctx Message context.
|
||||
* @param cb Callback to call for each image index.
|
||||
* @param cb_data Callback data to pass to @c cb.
|
||||
* @param max_count Max number of images to return.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code otherwise.
|
||||
*/
|
||||
int bt_mesh_dfu_cli_imgs_get(struct bt_mesh_dfu_cli *cli,
|
||||
struct bt_mesh_msg_ctx *ctx,
|
||||
bt_mesh_dfu_img_cb_t cb, void *cb_data,
|
||||
uint8_t max_count);
|
||||
|
||||
/** @brief Perform a metadata check for the given DFU image slot.
|
||||
*
|
||||
* The metadata check procedure allows the Firmware Update Client to check if a Target
|
||||
* node will accept a transfer of this DFU image slot, and what the effect would be.
|
||||
*
|
||||
* Waits for a response until the procedure timeout expires.
|
||||
*
|
||||
* @param cli Firmware Update Client model instance.
|
||||
* @param ctx Message context.
|
||||
* @param img_idx Target node's image index to check.
|
||||
* @param slot DFU image slot to check for.
|
||||
* @param rsp Metadata status response buffer.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code otherwise.
|
||||
*/
|
||||
int bt_mesh_dfu_cli_metadata_check(struct bt_mesh_dfu_cli *cli,
|
||||
struct bt_mesh_msg_ctx *ctx, uint8_t img_idx,
|
||||
const struct bt_mesh_dfu_slot *slot,
|
||||
struct bt_mesh_dfu_metadata_status *rsp);
|
||||
|
||||
/** @brief Get the status of a Target node.
|
||||
*
|
||||
* @param cli Firmware Update Client model instance.
|
||||
* @param ctx Message context.
|
||||
* @param rsp Response data buffer.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code otherwise.
|
||||
*/
|
||||
int bt_mesh_dfu_cli_status_get(struct bt_mesh_dfu_cli *cli,
|
||||
struct bt_mesh_msg_ctx *ctx,
|
||||
struct bt_mesh_dfu_target_status *rsp);
|
||||
|
||||
/** @brief Get the current procedure timeout value.
|
||||
*
|
||||
* @return The configured procedure timeout.
|
||||
*/
|
||||
int32_t bt_mesh_dfu_cli_timeout_get(void);
|
||||
|
||||
/** @brief Set the procedure timeout value.
|
||||
*
|
||||
* @param timeout The new procedure timeout.
|
||||
*/
|
||||
void bt_mesh_dfu_cli_timeout_set(int32_t timeout);
|
||||
|
||||
/** @cond INTERNAL_HIDDEN */
|
||||
extern const struct bt_mesh_blob_cli_cb _bt_mesh_dfu_cli_blob_handlers;
|
||||
extern const struct bt_mesh_model_cb _bt_mesh_dfu_cli_cb;
|
||||
extern const struct bt_mesh_model_op _bt_mesh_dfu_cli_op[];
|
||||
/** @endcond */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_CLI_H__ */
|
||||
|
||||
/** @} */
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @defgroup bt_mesh_dfu_metadata Bluetooth Mesh Device Firmware Update (DFU) metadata
|
||||
* @ingroup bt_mesh_dfu
|
||||
* @{
|
||||
* @brief Common types and functions for the Bluetooth Mesh DFU metadata.
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_METADATA_H__
|
||||
#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_METADATA_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Firmware version. */
|
||||
struct bt_mesh_dfu_metadata_fw_ver {
|
||||
/** Firmware major version. */
|
||||
uint8_t major;
|
||||
/** Firmware minor version. */
|
||||
uint8_t minor;
|
||||
/** Firmware revision. */
|
||||
uint16_t revision;
|
||||
/** Firmware build number. */
|
||||
uint32_t build_num;
|
||||
};
|
||||
|
||||
/** Firmware core type. */
|
||||
enum bt_mesh_dfu_metadata_fw_core_type {
|
||||
/** Application core. */
|
||||
BT_MESH_DFU_FW_CORE_TYPE_APP = BIT(0),
|
||||
/** Network core. */
|
||||
BT_MESH_DFU_FW_CORE_TYPE_NETWORK = BIT(1),
|
||||
/** Application-specific BLOB. */
|
||||
BT_MESH_DFU_FW_CORE_TYPE_APP_SPECIFIC_BLOB = BIT(2),
|
||||
};
|
||||
|
||||
/** Firmware metadata. */
|
||||
struct bt_mesh_dfu_metadata {
|
||||
/** New firmware version. */
|
||||
struct bt_mesh_dfu_metadata_fw_ver fw_ver;
|
||||
/** New firmware size. */
|
||||
uint32_t fw_size;
|
||||
/** New firmware core type. */
|
||||
enum bt_mesh_dfu_metadata_fw_core_type fw_core_type;
|
||||
/** Hash of incoming Composition Data. */
|
||||
uint32_t comp_hash;
|
||||
/** New number of node elements. */
|
||||
uint16_t elems;
|
||||
/** Application-specific data for new firmware. This field is optional. */
|
||||
uint8_t *user_data;
|
||||
/** Length of the application-specific field. */
|
||||
uint32_t user_data_len;
|
||||
};
|
||||
|
||||
/** @brief Decode a firmware metadata from a network buffer.
|
||||
*
|
||||
* @param buf Buffer containing a raw metadata to be decoded.
|
||||
* @param metadata Pointer to a metadata structure to be filled.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code otherwise.
|
||||
*/
|
||||
int bt_mesh_dfu_metadata_decode(struct net_buf_simple *buf,
|
||||
struct bt_mesh_dfu_metadata *metadata);
|
||||
|
||||
/** @brief Encode a firmware metadata into a network buffer.
|
||||
*
|
||||
* @param metadata Firmware metadata to be encoded.
|
||||
* @param buf Buffer to store the encoded metadata.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code otherwise.
|
||||
*/
|
||||
int bt_mesh_dfu_metadata_encode(const struct bt_mesh_dfu_metadata *metadata,
|
||||
struct net_buf_simple *buf);
|
||||
|
||||
/** @brief Compute hash of the Composition Data state.
|
||||
*
|
||||
* The format of the Composition Data is defined in MshPRTv1.1: 4.2.2.1.
|
||||
*
|
||||
* @param buf Pointer to buffer holding Composition Data.
|
||||
* @param key 128-bit key to be used in the hash computation.
|
||||
* @param hash Pointer to a memory location to which the hash will be stored.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code otherwise.
|
||||
*/
|
||||
int bt_mesh_dfu_metadata_comp_hash_get(struct net_buf_simple *buf, uint8_t *key, uint32_t *hash);
|
||||
|
||||
/** @brief Compute hash of the Composition Data Page 0 of this device.
|
||||
*
|
||||
* @param key 128-bit key to be used in the hash computation.
|
||||
* @param hash Pointer to a memory location to which the hash will be stored.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code otherwise.
|
||||
*/
|
||||
int bt_mesh_dfu_metadata_comp_hash_local_get(uint8_t *key, uint32_t *hash);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_METADATA_H__ */
|
||||
|
||||
/** @} */
|
||||
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @defgroup bt_mesh_dfu_srv Firmware Update Server model
|
||||
* @ingroup bt_mesh_dfu
|
||||
* @{
|
||||
* @brief API for the Bluetooth Mesh Firmware Update Server model
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_SRV_H__
|
||||
#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_SRV_H__
|
||||
|
||||
#include <zephyr/bluetooth/mesh/dfu.h>
|
||||
#include <zephyr/bluetooth/mesh/blob_srv.h>
|
||||
#include <zephyr/bluetooth/mesh/access.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct bt_mesh_dfu_srv;
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Initialization parameters for @ref bt_mesh_dfu_srv.
|
||||
*
|
||||
* @param _handlers DFU handler function structure.
|
||||
* @param _imgs List of @ref bt_mesh_dfu_img managed by this Server.
|
||||
* @param _img_count Number of DFU images managed by this Server.
|
||||
*/
|
||||
#define BT_MESH_DFU_SRV_INIT(_handlers, _imgs, _img_count) \
|
||||
{ \
|
||||
.blob = { .cb = &_bt_mesh_dfu_srv_blob_cb }, .cb = _handlers, \
|
||||
.imgs = _imgs, .img_count = _img_count, \
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Firmware Update Server model entry.
|
||||
*
|
||||
* @param _srv Pointer to a @ref bt_mesh_dfu_srv instance.
|
||||
*/
|
||||
#define BT_MESH_MODEL_DFU_SRV(_srv) \
|
||||
BT_MESH_MODEL_BLOB_SRV(&(_srv)->blob), \
|
||||
BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_DFU_SRV, _bt_mesh_dfu_srv_op, NULL, \
|
||||
_srv, &_bt_mesh_dfu_srv_cb)
|
||||
|
||||
/** @brief Firmware Update Server event callbacks. */
|
||||
struct bt_mesh_dfu_srv_cb {
|
||||
/** @brief Transfer check callback.
|
||||
*
|
||||
* The transfer check can be used to validate the incoming transfer
|
||||
* before it starts. The contents of the metadata is implementation
|
||||
* specific, and should contain all the information the application
|
||||
* needs to determine whether this image should be accepted, and what
|
||||
* the effect of the transfer would be.
|
||||
*
|
||||
* If applying the image will have an effect on the provisioning state
|
||||
* of the mesh stack, this can be communicated through the @c effect
|
||||
* return parameter.
|
||||
*
|
||||
* The metadata check can be performed both as part of starting a new
|
||||
* transfer and as a separate procedure.
|
||||
*
|
||||
* This handler is optional.
|
||||
*
|
||||
* @param srv Firmware Update Server instance.
|
||||
* @param img DFU image the metadata check is performed on.
|
||||
* @param metadata Image metadata.
|
||||
* @param effect Return parameter for the image effect on the
|
||||
* provisioning state of the mesh stack.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code otherwise.
|
||||
*/
|
||||
int (*check)(struct bt_mesh_dfu_srv *srv,
|
||||
const struct bt_mesh_dfu_img *img,
|
||||
struct net_buf_simple *metadata,
|
||||
enum bt_mesh_dfu_effect *effect);
|
||||
|
||||
/** @brief Transfer start callback.
|
||||
*
|
||||
* Called when the Firmware Update Server is ready to start a new DFU transfer.
|
||||
* The application must provide an initialized BLOB stream to be used
|
||||
* during the DFU transfer.
|
||||
*
|
||||
* The following error codes are treated specially, and should be used
|
||||
* to communicate these issues:
|
||||
* - @c -ENOMEM: The device cannot fit this image.
|
||||
* - @c -EBUSY: The application is temporarily unable to accept the
|
||||
* transfer.
|
||||
* - @c -EALREADY: The device has already received and verified this
|
||||
* image, and there's no need to transfer it again. The Firmware Update model
|
||||
* will skip the transfer phase, and mark the image as verified.
|
||||
*
|
||||
* This handler is mandatory.
|
||||
*
|
||||
* @param srv Firmware Update Server instance.
|
||||
* @param img DFU image being updated.
|
||||
* @param metadata Image metadata.
|
||||
* @param io BLOB stream return parameter. Must be set to a
|
||||
* valid BLOB stream by the callback.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code otherwise. Return
|
||||
* codes @c -ENOMEM, @c -EBUSY @c -EALREADY will be passed to
|
||||
* the updater, other error codes are reported as internal
|
||||
* errors.
|
||||
*/
|
||||
int (*start)(struct bt_mesh_dfu_srv *srv,
|
||||
const struct bt_mesh_dfu_img *img,
|
||||
struct net_buf_simple *metadata,
|
||||
const struct bt_mesh_blob_io **io);
|
||||
|
||||
/** @brief Transfer end callback.
|
||||
*
|
||||
* This handler is optional.
|
||||
*
|
||||
* If the transfer is successful, the application should verify the
|
||||
* firmware image, and call either @ref bt_mesh_dfu_srv_verified or
|
||||
* @ref bt_mesh_dfu_srv_rejected depending on the outcome.
|
||||
*
|
||||
* If the transfer fails, the Firmware Update Server will be available for new
|
||||
* transfers immediately after this function returns.
|
||||
*
|
||||
* @param srv Firmware Update Server instance.
|
||||
* @param img DFU image that failed the update.
|
||||
* @param success Whether the DFU transfer was successful.
|
||||
*/
|
||||
void (*end)(struct bt_mesh_dfu_srv *srv,
|
||||
const struct bt_mesh_dfu_img *img, bool success);
|
||||
|
||||
/** @brief Transfer recovery callback.
|
||||
*
|
||||
* If the device reboots in the middle of a transfer, the Firmware Update Server
|
||||
* calls this function when the Bluetooth Mesh subsystem is started.
|
||||
*
|
||||
* This callback is optional, but transfers will not be recovered after
|
||||
* a reboot without it.
|
||||
*
|
||||
* @param srv Firmware Update Server instance.
|
||||
* @param img DFU image being updated.
|
||||
* @param io BLOB stream return parameter. Must be set to a valid BLOB
|
||||
* stream by the callback.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code to abandon the
|
||||
* transfer.
|
||||
*/
|
||||
int (*recover)(struct bt_mesh_dfu_srv *srv,
|
||||
const struct bt_mesh_dfu_img *img,
|
||||
const struct bt_mesh_blob_io **io);
|
||||
|
||||
/** @brief Transfer apply callback.
|
||||
*
|
||||
* Called after a transfer has been validated, and the updater sends an
|
||||
* apply message to the Target nodes.
|
||||
*
|
||||
* This handler is optional.
|
||||
*
|
||||
* @param srv Firmware Update Server instance.
|
||||
* @param img DFU image that should be applied.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code otherwise.
|
||||
*/
|
||||
int (*apply)(struct bt_mesh_dfu_srv *srv,
|
||||
const struct bt_mesh_dfu_img *img);
|
||||
};
|
||||
|
||||
/** @brief Firmware Update Server instance.
|
||||
*
|
||||
* Should be initialized with @ref BT_MESH_DFU_SRV_INIT.
|
||||
*/
|
||||
struct bt_mesh_dfu_srv {
|
||||
/** Underlying BLOB Transfer Server. */
|
||||
struct bt_mesh_blob_srv blob;
|
||||
/** Callback structure. */
|
||||
const struct bt_mesh_dfu_srv_cb *cb;
|
||||
/** List of updatable images. */
|
||||
const struct bt_mesh_dfu_img *imgs;
|
||||
/** Number of updatable images. */
|
||||
size_t img_count;
|
||||
|
||||
/* Runtime state */
|
||||
const struct bt_mesh_model *mod;
|
||||
struct {
|
||||
/* Effect of transfer, @see bt_mesh_dfu_effect. */
|
||||
uint8_t effect;
|
||||
/* Current phase, @see bt_mesh_dfu_phase. */
|
||||
uint8_t phase;
|
||||
uint8_t ttl;
|
||||
uint8_t idx;
|
||||
uint16_t timeout_base;
|
||||
uint16_t meta;
|
||||
} update;
|
||||
};
|
||||
|
||||
/** @brief Accept the received DFU transfer.
|
||||
*
|
||||
* Should be called at the end of a successful DFU transfer.
|
||||
*
|
||||
* If the DFU transfer completes successfully, the application should verify
|
||||
* the image validity (including any image authentication or integrity checks),
|
||||
* and call this function if the image is ready to be applied.
|
||||
*
|
||||
* @param srv Firmware Update Server instance.
|
||||
*/
|
||||
void bt_mesh_dfu_srv_verified(struct bt_mesh_dfu_srv *srv);
|
||||
|
||||
/** @brief Reject the received DFU transfer.
|
||||
*
|
||||
* Should be called at the end of a successful DFU transfer.
|
||||
*
|
||||
* If the DFU transfer completes successfully, the application should verify
|
||||
* the image validity (including any image authentication or integrity checks),
|
||||
* and call this function if one of the checks fail.
|
||||
*
|
||||
* @param srv Firmware Update Server instance.
|
||||
*/
|
||||
void bt_mesh_dfu_srv_rejected(struct bt_mesh_dfu_srv *srv);
|
||||
|
||||
/** @brief Cancel the ongoing DFU transfer.
|
||||
*
|
||||
* @param srv Firmware Update Server instance.
|
||||
*/
|
||||
void bt_mesh_dfu_srv_cancel(struct bt_mesh_dfu_srv *srv);
|
||||
|
||||
/** @brief Confirm that the received DFU transfer was applied.
|
||||
*
|
||||
* Should be called as a result of the @ref bt_mesh_dfu_srv_cb.apply callback.
|
||||
*
|
||||
* @param srv Firmware Update Server instance.
|
||||
*/
|
||||
void bt_mesh_dfu_srv_applied(struct bt_mesh_dfu_srv *srv);
|
||||
|
||||
/** @brief Check if the Firmware Update Server is busy processing a transfer.
|
||||
*
|
||||
* @param srv Firmware Update Server instance.
|
||||
*
|
||||
* @return true if a DFU procedure is in progress, false otherwise.
|
||||
*/
|
||||
bool bt_mesh_dfu_srv_is_busy(const struct bt_mesh_dfu_srv *srv);
|
||||
|
||||
/** @brief Get the progress of the current DFU procedure, in percent.
|
||||
*
|
||||
* @param srv Firmware Update Server instance.
|
||||
*
|
||||
* @return The current transfer progress in percent.
|
||||
*/
|
||||
uint8_t bt_mesh_dfu_srv_progress(const struct bt_mesh_dfu_srv *srv);
|
||||
|
||||
/** @cond INTERNAL_HIDDEN */
|
||||
extern const struct bt_mesh_model_op _bt_mesh_dfu_srv_op[];
|
||||
extern const struct bt_mesh_model_cb _bt_mesh_dfu_srv_cb;
|
||||
extern const struct bt_mesh_blob_srv_cb _bt_mesh_dfu_srv_blob_cb;
|
||||
/** @endcond */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_SRV_H__ */
|
||||
|
||||
/** @} */
|
||||
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_BLOB_H__
|
||||
#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_BLOB_H__
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @defgroup bt_mesh_blob Bluetooth Mesh BLOB model API
|
||||
* @ingroup bt_mesh
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_BT_MESH_BLOB_CHUNK_COUNT_MAX
|
||||
#define CONFIG_BT_MESH_BLOB_CHUNK_COUNT_MAX 0
|
||||
#endif
|
||||
|
||||
/** BLOB transfer mode. */
|
||||
enum bt_mesh_blob_xfer_mode {
|
||||
/** No valid transfer mode. */
|
||||
BT_MESH_BLOB_XFER_MODE_NONE,
|
||||
/** Push mode (Push BLOB Transfer Mode). */
|
||||
BT_MESH_BLOB_XFER_MODE_PUSH,
|
||||
/** Pull mode (Pull BLOB Transfer Mode). */
|
||||
BT_MESH_BLOB_XFER_MODE_PULL,
|
||||
/** Both modes are valid. */
|
||||
BT_MESH_BLOB_XFER_MODE_ALL,
|
||||
};
|
||||
|
||||
/** Transfer phase. */
|
||||
enum bt_mesh_blob_xfer_phase {
|
||||
/** The BLOB Transfer Server is awaiting configuration. */
|
||||
BT_MESH_BLOB_XFER_PHASE_INACTIVE,
|
||||
/** The BLOB Transfer Server is ready to receive a BLOB transfer. */
|
||||
BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START,
|
||||
/** The BLOB Transfer Server is waiting for the next block of data. */
|
||||
BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_BLOCK,
|
||||
/** The BLOB Transfer Server is waiting for the next chunk of data. */
|
||||
BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_CHUNK,
|
||||
/** The BLOB was transferred successfully. */
|
||||
BT_MESH_BLOB_XFER_PHASE_COMPLETE,
|
||||
/** The BLOB transfer is paused. */
|
||||
BT_MESH_BLOB_XFER_PHASE_SUSPENDED,
|
||||
};
|
||||
|
||||
/** BLOB model status codes. */
|
||||
enum bt_mesh_blob_status {
|
||||
/** The message was processed successfully. */
|
||||
BT_MESH_BLOB_SUCCESS,
|
||||
/** The Block Number field value is not within the range of blocks being
|
||||
* transferred.
|
||||
*/
|
||||
BT_MESH_BLOB_ERR_INVALID_BLOCK_NUM,
|
||||
/** The block size is smaller than the size indicated by the Min Block
|
||||
* Size Log state or is larger than the size indicated by the Max Block
|
||||
* Size Log state.
|
||||
*/
|
||||
BT_MESH_BLOB_ERR_INVALID_BLOCK_SIZE,
|
||||
/** The chunk size exceeds the size indicated by the Max Chunk Size
|
||||
* state, or the number of chunks exceeds the number specified by the
|
||||
* Max Total Chunks state.
|
||||
*/
|
||||
BT_MESH_BLOB_ERR_INVALID_CHUNK_SIZE,
|
||||
/** The operation cannot be performed while the server is in the current
|
||||
* phase.
|
||||
*/
|
||||
BT_MESH_BLOB_ERR_WRONG_PHASE,
|
||||
/** A parameter value in the message cannot be accepted. */
|
||||
BT_MESH_BLOB_ERR_INVALID_PARAM,
|
||||
/** The message contains a BLOB ID value that is not expected. */
|
||||
BT_MESH_BLOB_ERR_WRONG_BLOB_ID,
|
||||
/** There is not enough space available in memory to receive the BLOB.
|
||||
*/
|
||||
BT_MESH_BLOB_ERR_BLOB_TOO_LARGE,
|
||||
/** The transfer mode is not supported by the BLOB Transfer Server
|
||||
* model.
|
||||
*/
|
||||
BT_MESH_BLOB_ERR_UNSUPPORTED_MODE,
|
||||
/** An internal error occurred on the node. */
|
||||
BT_MESH_BLOB_ERR_INTERNAL,
|
||||
/** The requested information cannot be provided while the server is in
|
||||
* the current phase.
|
||||
*/
|
||||
BT_MESH_BLOB_ERR_INFO_UNAVAILABLE,
|
||||
};
|
||||
|
||||
/** BLOB transfer data block. */
|
||||
struct bt_mesh_blob_block {
|
||||
/** Block size in bytes */
|
||||
size_t size;
|
||||
/** Offset in bytes from the start of the BLOB. */
|
||||
off_t offset;
|
||||
/** Block number */
|
||||
uint16_t number;
|
||||
/** Number of chunks in block. */
|
||||
uint16_t chunk_count;
|
||||
/** Bitmap of missing chunks. */
|
||||
uint8_t missing[DIV_ROUND_UP(CONFIG_BT_MESH_BLOB_CHUNK_COUNT_MAX,
|
||||
8)];
|
||||
};
|
||||
|
||||
/** BLOB data chunk. */
|
||||
struct bt_mesh_blob_chunk {
|
||||
/** Offset of the chunk data from the start of the block. */
|
||||
off_t offset;
|
||||
/** Chunk data size. */
|
||||
size_t size;
|
||||
/** Chunk data. */
|
||||
uint8_t *data;
|
||||
};
|
||||
|
||||
/** BLOB transfer. */
|
||||
struct bt_mesh_blob_xfer {
|
||||
/** BLOB ID. */
|
||||
uint64_t id;
|
||||
/** Total BLOB size in bytes. */
|
||||
size_t size;
|
||||
/** BLOB transfer mode. */
|
||||
enum bt_mesh_blob_xfer_mode mode;
|
||||
/* Logarithmic representation of the block size. */
|
||||
uint8_t block_size_log;
|
||||
/** Base chunk size. May be smaller for the last chunk. */
|
||||
uint16_t chunk_size;
|
||||
};
|
||||
|
||||
/** BLOB stream interaction mode. */
|
||||
enum bt_mesh_blob_io_mode {
|
||||
/** Read data from the stream. */
|
||||
BT_MESH_BLOB_READ,
|
||||
/** Write data to the stream. */
|
||||
BT_MESH_BLOB_WRITE,
|
||||
};
|
||||
|
||||
/** BLOB stream. */
|
||||
struct bt_mesh_blob_io {
|
||||
/** @brief Open callback.
|
||||
*
|
||||
* Called when the reader is opened for reading.
|
||||
*
|
||||
* @param io BLOB stream.
|
||||
* @param xfer BLOB transfer.
|
||||
* @param mode Direction of the stream (read/write).
|
||||
*
|
||||
* @return 0 on success, or (negative) error code otherwise.
|
||||
*/
|
||||
int (*open)(const struct bt_mesh_blob_io *io,
|
||||
const struct bt_mesh_blob_xfer *xfer,
|
||||
enum bt_mesh_blob_io_mode mode);
|
||||
|
||||
/** @brief Close callback.
|
||||
*
|
||||
* Called when the reader is closed.
|
||||
*
|
||||
* @param io BLOB stream.
|
||||
* @param xfer BLOB transfer.
|
||||
*/
|
||||
void (*close)(const struct bt_mesh_blob_io *io,
|
||||
const struct bt_mesh_blob_xfer *xfer);
|
||||
|
||||
/** @brief Block start callback.
|
||||
*
|
||||
* Called when a new block is opened for sending. Each block is only
|
||||
* sent once, and are always sent in increasing order. The data chunks
|
||||
* inside a single block may be requested out of order and multiple
|
||||
* times.
|
||||
*
|
||||
* @param io BLOB stream.
|
||||
* @param xfer BLOB transfer.
|
||||
* @param block Block that was started.
|
||||
*/
|
||||
int (*block_start)(const struct bt_mesh_blob_io *io,
|
||||
const struct bt_mesh_blob_xfer *xfer,
|
||||
const struct bt_mesh_blob_block *block);
|
||||
|
||||
/** @brief Block end callback.
|
||||
*
|
||||
* Called when the current block has been transmitted in full.
|
||||
* No data from this block will be requested again, and the application
|
||||
* data associated with this block may be discarded.
|
||||
*
|
||||
* @param io BLOB stream.
|
||||
* @param xfer BLOB transfer.
|
||||
* @param block Block that finished sending.
|
||||
*/
|
||||
void (*block_end)(const struct bt_mesh_blob_io *io,
|
||||
const struct bt_mesh_blob_xfer *xfer,
|
||||
const struct bt_mesh_blob_block *block);
|
||||
|
||||
/** @brief Chunk data write callback.
|
||||
*
|
||||
* Used by the BLOB Transfer Server on incoming data.
|
||||
*
|
||||
* Each block is divided into chunks of data. This callback is called
|
||||
* when a new chunk of data is received. Chunks may be received in
|
||||
* any order within their block.
|
||||
*
|
||||
* If the callback returns successfully, this chunk will be marked as
|
||||
* received, and will not be received again unless the block is
|
||||
* restarted due to a transfer suspension. If the callback returns a
|
||||
* non-zero value, the chunk remains unreceived, and the BLOB Transfer
|
||||
* Client will attempt to resend it later.
|
||||
*
|
||||
* Note that the Client will only perform a limited number of attempts
|
||||
* at delivering a chunk before dropping a Target node from the transfer.
|
||||
* The number of retries performed by the Client is implementation
|
||||
* specific.
|
||||
*
|
||||
* @param io BLOB stream.
|
||||
* @param xfer BLOB transfer.
|
||||
* @param block Block the chunk is part of.
|
||||
* @param chunk Received chunk.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code otherwise.
|
||||
*/
|
||||
int (*wr)(const struct bt_mesh_blob_io *io,
|
||||
const struct bt_mesh_blob_xfer *xfer,
|
||||
const struct bt_mesh_blob_block *block,
|
||||
const struct bt_mesh_blob_chunk *chunk);
|
||||
|
||||
/** @brief Chunk data read callback.
|
||||
*
|
||||
* Used by the BLOB Transfer Client to fetch outgoing data.
|
||||
*
|
||||
* The Client calls the chunk data request callback to populate a chunk
|
||||
* message going out to the Target nodes. The data request callback
|
||||
* may be called out of order and multiple times for each offset, and
|
||||
* cannot be used as an indication of progress.
|
||||
*
|
||||
* Returning a non-zero status code on the chunk data request callback
|
||||
* results in termination of the transfer.
|
||||
*
|
||||
* @param io BLOB stream.
|
||||
* @param xfer BLOB transfer.
|
||||
* @param block Block the chunk is part of.
|
||||
* @param chunk Chunk to get the data of. The buffer pointer to by the
|
||||
* @c data member should be filled by the callback.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code otherwise.
|
||||
*/
|
||||
int (*rd)(const struct bt_mesh_blob_io *io,
|
||||
const struct bt_mesh_blob_xfer *xfer,
|
||||
const struct bt_mesh_blob_block *block,
|
||||
const struct bt_mesh_blob_chunk *chunk);
|
||||
};
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_BLOB_H__ */
|
||||
@@ -0,0 +1,454 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_BLOB_CLI_H_
|
||||
#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_BLOB_CLI_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <zephyr/bluetooth/mesh/access.h>
|
||||
#include <zephyr/bluetooth/mesh/blob.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @defgroup bt_mesh_blob_cli Bluetooth Mesh BLOB Transfer Client model API
|
||||
* @ingroup bt_mesh
|
||||
* @{
|
||||
*/
|
||||
|
||||
struct bt_mesh_blob_cli;
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief BLOB Transfer Client model Composition Data entry.
|
||||
*
|
||||
* @param _cli Pointer to a @ref bt_mesh_blob_cli instance.
|
||||
*/
|
||||
#define BT_MESH_MODEL_BLOB_CLI(_cli) \
|
||||
BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_BLOB_CLI, _bt_mesh_blob_cli_op, \
|
||||
NULL, _cli, &_bt_mesh_blob_cli_cb)
|
||||
|
||||
/** Target node's Pull mode (Pull BLOB Transfer Mode) context used
|
||||
* while sending chunks to the Target node.
|
||||
*/
|
||||
struct bt_mesh_blob_target_pull {
|
||||
/** Timestamp when the Block Report Timeout Timer expires for this Target node. */
|
||||
int64_t block_report_timestamp;
|
||||
|
||||
/** Missing chunks reported by this Target node. */
|
||||
uint8_t missing[DIV_ROUND_UP(CONFIG_BT_MESH_BLOB_CHUNK_COUNT_MAX, 8)];
|
||||
};
|
||||
|
||||
/** BLOB Transfer Client Target node. */
|
||||
struct bt_mesh_blob_target {
|
||||
/** Linked list node */
|
||||
sys_snode_t n;
|
||||
|
||||
/** Target node address. */
|
||||
uint16_t addr;
|
||||
|
||||
/** Target node's Pull mode context.
|
||||
* Needs to be initialized when sending a BLOB in Pull mode.
|
||||
*/
|
||||
struct bt_mesh_blob_target_pull *pull;
|
||||
|
||||
/** BLOB transfer status, see @ref bt_mesh_blob_status. */
|
||||
uint8_t status;
|
||||
|
||||
uint8_t procedure_complete:1, /* Procedure has been completed. */
|
||||
acked:1, /* Message has been acknowledged. Not used when sending. */
|
||||
timedout:1, /* Target node didn't respond after specified timeout. */
|
||||
skip:1; /* Skip Target node from broadcast. */
|
||||
};
|
||||
|
||||
/** BLOB transfer information.
|
||||
*
|
||||
* If @c phase is @ref BT_MESH_BLOB_XFER_PHASE_INACTIVE,
|
||||
* the fields below @c phase are not initialized.
|
||||
* If @c phase is @ref BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START,
|
||||
* the fields below @c id are not initialized.
|
||||
*/
|
||||
struct bt_mesh_blob_xfer_info {
|
||||
/** BLOB transfer status. */
|
||||
enum bt_mesh_blob_status status;
|
||||
|
||||
/** BLOB transfer mode. */
|
||||
enum bt_mesh_blob_xfer_mode mode;
|
||||
|
||||
/** BLOB transfer phase. */
|
||||
enum bt_mesh_blob_xfer_phase phase;
|
||||
|
||||
/** BLOB ID. */
|
||||
uint64_t id;
|
||||
|
||||
/** BLOB size in octets. */
|
||||
uint32_t size;
|
||||
|
||||
/** Logarithmic representation of the block size. */
|
||||
uint8_t block_size_log;
|
||||
|
||||
/** MTU size in octets. */
|
||||
uint16_t mtu_size;
|
||||
|
||||
/** Bit field indicating blocks that were not received. */
|
||||
const uint8_t *missing_blocks;
|
||||
};
|
||||
|
||||
/** BLOB Transfer Client transfer inputs. */
|
||||
struct bt_mesh_blob_cli_inputs {
|
||||
/** Linked list of Target nodes. Each node should point to @ref
|
||||
* bt_mesh_blob_target::n.
|
||||
*/
|
||||
sys_slist_t targets;
|
||||
|
||||
/** AppKey index to send with. */
|
||||
uint16_t app_idx;
|
||||
|
||||
/** Group address destination for the BLOB transfer, or @ref
|
||||
* BT_MESH_ADDR_UNASSIGNED to send every message to each Target
|
||||
* node individually.
|
||||
*/
|
||||
uint16_t group;
|
||||
|
||||
/** Time to live value of BLOB transfer messages. */
|
||||
uint8_t ttl;
|
||||
|
||||
/** Additional response time for the Target nodes, in 10-second increments.
|
||||
*
|
||||
* The extra time can be used to give the Target nodes more time to respond
|
||||
* to messages from the Client. The actual timeout will be calculated
|
||||
* according to the following formula:
|
||||
*
|
||||
* @verbatim
|
||||
* timeout = 20 seconds + (10 seconds * timeout_base) + (100 ms * TTL)
|
||||
* @endverbatim
|
||||
*
|
||||
* If a Target node fails to respond to a message from the Client within the
|
||||
* configured transfer timeout, the Target node is dropped.
|
||||
*/
|
||||
uint16_t timeout_base;
|
||||
};
|
||||
|
||||
/** Transfer capabilities of a Target node. */
|
||||
struct bt_mesh_blob_cli_caps {
|
||||
/** Max BLOB size. */
|
||||
size_t max_size;
|
||||
|
||||
/** Logarithmic representation of the minimum block size. */
|
||||
uint8_t min_block_size_log;
|
||||
|
||||
/** Logarithmic representation of the maximum block size. */
|
||||
uint8_t max_block_size_log;
|
||||
|
||||
/** Max number of chunks per block. */
|
||||
uint16_t max_chunks;
|
||||
|
||||
/** Max chunk size. */
|
||||
uint16_t max_chunk_size;
|
||||
|
||||
/** Max MTU size. */
|
||||
uint16_t mtu_size;
|
||||
|
||||
/** Supported transfer modes. */
|
||||
enum bt_mesh_blob_xfer_mode modes;
|
||||
};
|
||||
|
||||
/** BLOB Transfer Client state. */
|
||||
enum bt_mesh_blob_cli_state {
|
||||
/** No transfer is active. */
|
||||
BT_MESH_BLOB_CLI_STATE_NONE,
|
||||
/** Retrieving transfer capabilities. */
|
||||
BT_MESH_BLOB_CLI_STATE_CAPS_GET,
|
||||
/** Sending transfer start. */
|
||||
BT_MESH_BLOB_CLI_STATE_START,
|
||||
/** Sending block start. */
|
||||
BT_MESH_BLOB_CLI_STATE_BLOCK_START,
|
||||
/** Sending block chunks. */
|
||||
BT_MESH_BLOB_CLI_STATE_BLOCK_SEND,
|
||||
/** Checking block status. */
|
||||
BT_MESH_BLOB_CLI_STATE_BLOCK_CHECK,
|
||||
/** Checking transfer status. */
|
||||
BT_MESH_BLOB_CLI_STATE_XFER_CHECK,
|
||||
/** Cancelling transfer. */
|
||||
BT_MESH_BLOB_CLI_STATE_CANCEL,
|
||||
/** Transfer is suspended. */
|
||||
BT_MESH_BLOB_CLI_STATE_SUSPENDED,
|
||||
/** Checking transfer progress. */
|
||||
BT_MESH_BLOB_CLI_STATE_XFER_PROGRESS_GET,
|
||||
};
|
||||
|
||||
/** Event handler callbacks for the BLOB Transfer Client model.
|
||||
*
|
||||
* All handlers are optional.
|
||||
*/
|
||||
struct bt_mesh_blob_cli_cb {
|
||||
/** @brief Capabilities retrieval completion callback.
|
||||
*
|
||||
* Called when the capabilities retrieval procedure completes, indicating that
|
||||
* a common set of acceptable transfer parameters have been established
|
||||
* for the given list of Target nodes. All compatible Target nodes have
|
||||
* status code @ref BT_MESH_BLOB_SUCCESS.
|
||||
*
|
||||
* @param cli BLOB Transfer Client instance.
|
||||
* @param caps Safe transfer capabilities if the transfer capabilities
|
||||
* of at least one Target node has satisfied the Client, or NULL otherwise.
|
||||
*/
|
||||
void (*caps)(struct bt_mesh_blob_cli *cli,
|
||||
const struct bt_mesh_blob_cli_caps *caps);
|
||||
|
||||
/** @brief Target node loss callback.
|
||||
*
|
||||
* Called whenever a Target node has been lost due to some error in the
|
||||
* transfer. Losing a Target node is not considered a fatal error for
|
||||
* the Client until all Target nodes have been lost.
|
||||
*
|
||||
* @param cli BLOB Transfer Client instance.
|
||||
* @param target Target node that was lost.
|
||||
* @param reason Reason for the Target node loss.
|
||||
*/
|
||||
void (*lost_target)(struct bt_mesh_blob_cli *cli,
|
||||
struct bt_mesh_blob_target *target,
|
||||
enum bt_mesh_blob_status reason);
|
||||
|
||||
/** @brief Transfer is suspended.
|
||||
*
|
||||
* Called when the transfer is suspended due to response timeout from all Target nodes.
|
||||
*
|
||||
* @param cli BLOB Transfer Client instance.
|
||||
*/
|
||||
void (*suspended)(struct bt_mesh_blob_cli *cli);
|
||||
|
||||
/** @brief Transfer end callback.
|
||||
*
|
||||
* Called when the transfer ends.
|
||||
*
|
||||
* @param cli BLOB Transfer Client instance.
|
||||
* @param xfer Completed transfer.
|
||||
* @param success Status of the transfer.
|
||||
* Is @c true if at least one Target
|
||||
* node received the whole transfer.
|
||||
*/
|
||||
void (*end)(struct bt_mesh_blob_cli *cli,
|
||||
const struct bt_mesh_blob_xfer *xfer, bool success);
|
||||
|
||||
/** @brief Transfer progress callback
|
||||
*
|
||||
* The content of @c info is invalidated upon exit from the callback.
|
||||
* Therefore it needs to be copied if it is planned to be used later.
|
||||
*
|
||||
* @param cli BLOB Transfer Client instance.
|
||||
* @param target Target node that responded to the request.
|
||||
* @param info BLOB transfer information.
|
||||
*/
|
||||
void (*xfer_progress)(struct bt_mesh_blob_cli *cli,
|
||||
struct bt_mesh_blob_target *target,
|
||||
const struct bt_mesh_blob_xfer_info *info);
|
||||
|
||||
/** @brief End of Get Transfer Progress procedure.
|
||||
*
|
||||
* Called when all Target nodes have responded or the procedure timed-out.
|
||||
*
|
||||
* @param cli BLOB Transfer Client instance.
|
||||
*/
|
||||
void (*xfer_progress_complete)(struct bt_mesh_blob_cli *cli);
|
||||
};
|
||||
|
||||
/** @cond INTERNAL_HIDDEN */
|
||||
struct blob_cli_broadcast_ctx {
|
||||
/** Called for every Target node in unicast mode, or once in case of multicast mode. */
|
||||
void (*send)(struct bt_mesh_blob_cli *cli, uint16_t dst);
|
||||
/** Called after every @ref blob_cli_broadcast_ctx::send callback. */
|
||||
void (*send_complete)(struct bt_mesh_blob_cli *cli, uint16_t dst);
|
||||
/** If @ref blob_cli_broadcast_ctx::acked is true, called after all Target nodes
|
||||
* have confirmed reception by @ref blob_cli_broadcast_rsp. Otherwise, called
|
||||
* after transmission has been completed.
|
||||
*/
|
||||
void (*next)(struct bt_mesh_blob_cli *cli);
|
||||
/** If true, every transmission needs to be confirmed by @ref blob_cli_broadcast_rsp before
|
||||
* @ref blob_cli_broadcast_ctx::next is called.
|
||||
*/
|
||||
bool acked;
|
||||
/** If true, the message is always sent in a unicast way. */
|
||||
bool force_unicast;
|
||||
/** If true, non-responsive Target nodes won't be dropped after transfer has timed out. */
|
||||
bool optional;
|
||||
/** Set to true by the BLOB Transfer Client between blob_cli_broadcast
|
||||
* and broadcast_complete calls.
|
||||
*/
|
||||
bool is_inited;
|
||||
/* Defines a time in ms by which the broadcast API postpones sending the message to a next
|
||||
* target or completing the broadcast.
|
||||
*/
|
||||
uint32_t post_send_delay_ms;
|
||||
};
|
||||
/** INTERNAL_HIDDEN @endcond */
|
||||
|
||||
/** BLOB Transfer Client model instance. */
|
||||
struct bt_mesh_blob_cli {
|
||||
/** Event handler callbacks */
|
||||
const struct bt_mesh_blob_cli_cb *cb;
|
||||
|
||||
/* Runtime state */
|
||||
const struct bt_mesh_model *mod;
|
||||
|
||||
struct {
|
||||
struct bt_mesh_blob_target *target;
|
||||
struct blob_cli_broadcast_ctx ctx;
|
||||
struct k_work_delayable retry;
|
||||
/* Represents Client Timeout timer in a timestamp. Used in Pull mode only. */
|
||||
int64_t cli_timestamp;
|
||||
struct k_work_delayable complete;
|
||||
uint16_t pending;
|
||||
uint8_t retries;
|
||||
uint8_t sending : 1,
|
||||
cancelled : 1;
|
||||
} tx;
|
||||
|
||||
const struct bt_mesh_blob_io *io;
|
||||
const struct bt_mesh_blob_cli_inputs *inputs;
|
||||
const struct bt_mesh_blob_xfer *xfer;
|
||||
uint32_t chunk_interval_ms;
|
||||
uint16_t block_count;
|
||||
uint16_t chunk_idx;
|
||||
uint16_t mtu_size;
|
||||
enum bt_mesh_blob_cli_state state;
|
||||
struct bt_mesh_blob_block block;
|
||||
struct bt_mesh_blob_cli_caps caps;
|
||||
};
|
||||
|
||||
/** @brief Retrieve transfer capabilities for a list of Target nodes.
|
||||
*
|
||||
* Queries the availability and capabilities of all Target nodes, producing a
|
||||
* cumulative set of transfer capabilities for the Target nodes, and returning
|
||||
* it through the @ref bt_mesh_blob_cli_cb::caps callback.
|
||||
*
|
||||
* Retrieving the capabilities may take several seconds, depending on the
|
||||
* number of Target nodes and mesh network performance. The end of the procedure
|
||||
* is indicated through the @ref bt_mesh_blob_cli_cb::caps callback.
|
||||
*
|
||||
* This procedure is not required, but strongly recommended as a
|
||||
* preparation for a transfer to maximize performance and the chances of
|
||||
* success.
|
||||
*
|
||||
* @param cli BLOB Transfer Client instance.
|
||||
* @param inputs Statically allocated BLOB Transfer Client transfer inputs.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code otherwise.
|
||||
*/
|
||||
int bt_mesh_blob_cli_caps_get(struct bt_mesh_blob_cli *cli,
|
||||
const struct bt_mesh_blob_cli_inputs *inputs);
|
||||
|
||||
/** @brief Perform a BLOB transfer.
|
||||
*
|
||||
* Starts sending the transfer to the Target nodes. Only Target nodes with a
|
||||
* @c status of @ref BT_MESH_BLOB_SUCCESS will be considered.
|
||||
*
|
||||
* The transfer will keep going either until all Target nodes have been dropped, or
|
||||
* the full BLOB has been sent.
|
||||
*
|
||||
* The BLOB transfer may take several minutes, depending on the number of
|
||||
* Target nodes, size of the BLOB and mesh network performance. The end of the
|
||||
* transfer is indicated through the @ref bt_mesh_blob_cli_cb::end callback.
|
||||
*
|
||||
* A Client only supports one transfer at the time.
|
||||
*
|
||||
* @param cli BLOB Transfer Client instance.
|
||||
* @param inputs Statically allocated BLOB Transfer Client transfer inputs.
|
||||
* @param xfer Statically allocated transfer parameters.
|
||||
* @param io BLOB stream to read the transfer from.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code otherwise.
|
||||
*/
|
||||
int bt_mesh_blob_cli_send(struct bt_mesh_blob_cli *cli,
|
||||
const struct bt_mesh_blob_cli_inputs *inputs,
|
||||
const struct bt_mesh_blob_xfer *xfer,
|
||||
const struct bt_mesh_blob_io *io);
|
||||
|
||||
/** @brief Suspend the active transfer.
|
||||
*
|
||||
* @param cli BLOB Transfer Client instance.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code otherwise.
|
||||
*/
|
||||
int bt_mesh_blob_cli_suspend(struct bt_mesh_blob_cli *cli);
|
||||
|
||||
/** @brief Resume the suspended transfer.
|
||||
*
|
||||
* @param cli BLOB Transfer Client instance.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code otherwise.
|
||||
*/
|
||||
int bt_mesh_blob_cli_resume(struct bt_mesh_blob_cli *cli);
|
||||
|
||||
/** @brief Cancel an ongoing transfer.
|
||||
*
|
||||
* @param cli BLOB Transfer Client instance.
|
||||
*/
|
||||
void bt_mesh_blob_cli_cancel(struct bt_mesh_blob_cli *cli);
|
||||
|
||||
/** @brief Get the progress of BLOB transfer.
|
||||
*
|
||||
* This function can only be used if the BLOB Transfer Client is currently
|
||||
* not performing a BLOB transfer.
|
||||
* To get progress of the active BLOB transfer, use the
|
||||
* @ref bt_mesh_blob_cli_xfer_progress_active_get function.
|
||||
*
|
||||
* @param cli BLOB Transfer Client instance.
|
||||
* @param inputs Statically allocated BLOB Transfer Client transfer inputs.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code otherwise.
|
||||
*/
|
||||
int bt_mesh_blob_cli_xfer_progress_get(struct bt_mesh_blob_cli *cli,
|
||||
const struct bt_mesh_blob_cli_inputs *inputs);
|
||||
|
||||
/** @brief Get the current progress of the active transfer in percent.
|
||||
*
|
||||
* @param cli BLOB Transfer Client instance.
|
||||
*
|
||||
* @return The current transfer progress, or 0 if no transfer is active.
|
||||
*/
|
||||
uint8_t bt_mesh_blob_cli_xfer_progress_active_get(struct bt_mesh_blob_cli *cli);
|
||||
|
||||
/** @brief Get the current state of the BLOB Transfer Client.
|
||||
*
|
||||
* @param cli BLOB Transfer Client instance.
|
||||
*
|
||||
* @return true if the BLOB Transfer Client is currently participating in a transfer or
|
||||
* retrieving the capabilities and false otherwise.
|
||||
*/
|
||||
bool bt_mesh_blob_cli_is_busy(struct bt_mesh_blob_cli *cli);
|
||||
|
||||
/** @brief Set chunk sending interval in ms
|
||||
*
|
||||
* This function is optional, and can be used to define how fast chunks are sent in the BLOB Client
|
||||
* Model.
|
||||
* Without an added delay, for example a Bluetooth Mesh DFU can cause network blockage by
|
||||
* constantly sending the next chunks, especially if the chunks are sent to group addresses or
|
||||
* multiple unicast addresses.
|
||||
*
|
||||
* @note: Big intervals may cause timeouts. Increasing the @c timeout_base accordingly can
|
||||
* circumvent this.
|
||||
*
|
||||
* @param cli BLOB Transfer Client instance.
|
||||
* @param interval_ms the delay before each chunk is sent out in ms.
|
||||
*/
|
||||
void bt_mesh_blob_cli_set_chunk_interval_ms(struct bt_mesh_blob_cli *cli, uint32_t interval_ms);
|
||||
|
||||
/** @cond INTERNAL_HIDDEN */
|
||||
extern const struct bt_mesh_model_op _bt_mesh_blob_cli_op[];
|
||||
extern const struct bt_mesh_model_cb _bt_mesh_blob_cli_cb;
|
||||
/** @endcond */
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_BLOB_CLI_H_ */
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_BLOB_SRV_H_
|
||||
#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_BLOB_SRV_H_
|
||||
|
||||
#include <zephyr/bluetooth/mesh/access.h>
|
||||
#include <zephyr/bluetooth/mesh/blob.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @defgroup bt_mesh_blob_srv Bluetooth Mesh BLOB Transfer Server model API
|
||||
* @ingroup bt_mesh
|
||||
* @{
|
||||
*/
|
||||
|
||||
struct bt_mesh_blob_srv;
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Max number of blocks in a single transfer.
|
||||
*/
|
||||
#if defined(CONFIG_BT_MESH_BLOB_SRV)
|
||||
#define BT_MESH_BLOB_BLOCKS_MAX \
|
||||
(DIV_ROUND_UP(CONFIG_BT_MESH_BLOB_SIZE_MAX, \
|
||||
CONFIG_BT_MESH_BLOB_BLOCK_SIZE_MIN))
|
||||
#else
|
||||
#define BT_MESH_BLOB_BLOCKS_MAX 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief BLOB Transfer Server model composition data entry.
|
||||
*
|
||||
* @param _srv Pointer to a @ref bt_mesh_blob_srv instance.
|
||||
*/
|
||||
#define BT_MESH_MODEL_BLOB_SRV(_srv) \
|
||||
BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_BLOB_SRV, _bt_mesh_blob_srv_op, \
|
||||
NULL, _srv, &_bt_mesh_blob_srv_cb)
|
||||
|
||||
/** @brief BLOB Transfer Server model event handlers.
|
||||
*
|
||||
* All callbacks are optional.
|
||||
*/
|
||||
struct bt_mesh_blob_srv_cb {
|
||||
/** @brief Transfer start callback.
|
||||
*
|
||||
* Called when the transfer has started with the prepared BLOB ID.
|
||||
*
|
||||
* @param srv BLOB Transfer Server instance.
|
||||
* @param ctx Message context for the incoming start message. The
|
||||
* entire transfer will be sent from the same source
|
||||
* address.
|
||||
* @param xfer Transfer parameters.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code to reject the
|
||||
* transfer.
|
||||
*/
|
||||
int (*start)(struct bt_mesh_blob_srv *srv, struct bt_mesh_msg_ctx *ctx,
|
||||
struct bt_mesh_blob_xfer *xfer);
|
||||
|
||||
/** @brief Transfer end callback.
|
||||
*
|
||||
* Called when the transfer ends, either because it was cancelled, or
|
||||
* because it finished successfully. A new transfer may be prepared.
|
||||
*
|
||||
* @note The transfer may end before it's started if the start
|
||||
* parameters are invalid.
|
||||
*
|
||||
* @param srv BLOB Transfer Server instance.
|
||||
* @param id BLOB ID of the cancelled transfer.
|
||||
* @param success Whether the transfer was successful.
|
||||
*/
|
||||
void (*end)(struct bt_mesh_blob_srv *srv, uint64_t id, bool success);
|
||||
|
||||
/** @brief Transfer suspended callback.
|
||||
*
|
||||
* Called if the Server timed out while waiting for a transfer packet.
|
||||
* A suspended transfer may resume later from the start of the current
|
||||
* block. Any received chunks in the current block should be discarded,
|
||||
* they will be received again if the transfer resumes.
|
||||
*
|
||||
* The transfer will call @c resumed again when resuming.
|
||||
*
|
||||
* @note The BLOB Transfer Server does not run a timer in the suspended state,
|
||||
* and it's up to the application to determine whether the
|
||||
* transfer should be permanently cancelled. Without interaction,
|
||||
* the transfer will be suspended indefinitely, and the BLOB Transfer
|
||||
* Server will not accept any new transfers.
|
||||
*
|
||||
* @param srv BLOB Transfer Server instance.
|
||||
*/
|
||||
void (*suspended)(struct bt_mesh_blob_srv *srv);
|
||||
|
||||
/** @brief Transfer resume callback.
|
||||
*
|
||||
* Called if the transfer is resumed after being suspended.
|
||||
*
|
||||
* @param srv BLOB Transfer Server instance.
|
||||
*/
|
||||
void (*resume)(struct bt_mesh_blob_srv *srv);
|
||||
|
||||
/** @brief Transfer recovery callback.
|
||||
*
|
||||
* Called when the Bluetooth Mesh subsystem is started if the device is rebooted
|
||||
* in the middle of a transfer.
|
||||
*
|
||||
* Transfers will not be resumed after a reboot if this callback is not
|
||||
* defined.
|
||||
*
|
||||
* @param srv BLOB Transfer Server instance.
|
||||
* @param xfer Transfer to resume.
|
||||
* @param io BLOB stream return parameter. Must be set to a valid
|
||||
* BLOB stream by the callback.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code to abandon the
|
||||
* transfer.
|
||||
*/
|
||||
int (*recover)(struct bt_mesh_blob_srv *srv,
|
||||
struct bt_mesh_blob_xfer *xfer,
|
||||
const struct bt_mesh_blob_io **io);
|
||||
};
|
||||
|
||||
/** @brief BLOB Transfer Server model instance. */
|
||||
struct bt_mesh_blob_srv {
|
||||
/** Event handler callbacks. */
|
||||
const struct bt_mesh_blob_srv_cb *cb;
|
||||
|
||||
/* Runtime state: */
|
||||
const struct bt_mesh_blob_io *io;
|
||||
struct k_work_delayable rx_timeout;
|
||||
struct bt_mesh_blob_block block;
|
||||
const struct bt_mesh_model *mod;
|
||||
enum bt_mesh_blob_xfer_phase phase;
|
||||
|
||||
struct bt_mesh_blob_srv_state {
|
||||
struct bt_mesh_blob_xfer xfer;
|
||||
uint16_t cli;
|
||||
uint16_t app_idx;
|
||||
uint16_t timeout_base;
|
||||
uint16_t mtu_size;
|
||||
uint8_t ttl;
|
||||
|
||||
/* Bitfield of pending blocks. */
|
||||
ATOMIC_DEFINE(blocks, BT_MESH_BLOB_BLOCKS_MAX);
|
||||
} state;
|
||||
|
||||
/* Pull mode (Pull BLOB Transfer Mode) behavior. */
|
||||
struct {
|
||||
uint16_t chunk_idx;
|
||||
struct k_work_delayable report;
|
||||
} pull;
|
||||
};
|
||||
|
||||
/** @brief Prepare BLOB Transfer Server for an incoming transfer.
|
||||
*
|
||||
* Before a BLOB Transfer Server can receive a transfer, the transfer must be prepared
|
||||
* through some application level mechanism. The BLOB Transfer Server will only accept
|
||||
* incoming transfers with a matching BLOB ID.
|
||||
*
|
||||
* @param srv BLOB Transfer Server instance.
|
||||
* @param id BLOB ID to accept.
|
||||
* @param io BLOB stream to write the incoming BLOB to.
|
||||
* @param ttl Time to live value to use in responses to the BLOB Transfer Client.
|
||||
* @param timeout_base Extra time for the Client to respond in addition to the
|
||||
* base 10 seconds, in 10-second increments.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code on failure.
|
||||
*/
|
||||
int bt_mesh_blob_srv_recv(struct bt_mesh_blob_srv *srv, uint64_t id,
|
||||
const struct bt_mesh_blob_io *io, uint8_t ttl,
|
||||
uint16_t timeout_base);
|
||||
|
||||
/** @brief Cancel the current BLOB transfer.
|
||||
*
|
||||
* Tells the BLOB Transfer Client to drop this device from the list of Targets for the
|
||||
* current transfer. Note that the client may continue sending the transfer to
|
||||
* other Targets.
|
||||
*
|
||||
* @param srv BLOB Transfer Server instance.
|
||||
*
|
||||
* @return 0 on success, or (negative) error code on failure.
|
||||
*/
|
||||
int bt_mesh_blob_srv_cancel(struct bt_mesh_blob_srv *srv);
|
||||
|
||||
/** @brief Get the current state of the BLOB Transfer Server.
|
||||
*
|
||||
* @param srv BLOB Transfer Server instance.
|
||||
*
|
||||
* @return true if the BLOB Transfer Server is currently participating in a transfer,
|
||||
* false otherwise.
|
||||
*/
|
||||
bool bt_mesh_blob_srv_is_busy(const struct bt_mesh_blob_srv *srv);
|
||||
|
||||
/** @brief Get the current progress of the active transfer in percent.
|
||||
*
|
||||
* @param srv BLOB Transfer Server instance.
|
||||
*
|
||||
* @return The current transfer progress, or 0 if no transfer is active.
|
||||
*/
|
||||
uint8_t bt_mesh_blob_srv_progress(const struct bt_mesh_blob_srv *srv);
|
||||
|
||||
/** @cond INTERNAL_HIDDEN */
|
||||
extern const struct bt_mesh_model_op _bt_mesh_blob_srv_op[];
|
||||
extern const struct bt_mesh_model_cb _bt_mesh_blob_srv_cb;
|
||||
/** @endcond */
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_BLOB_SRV_H_ */
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
|
||||
#define BT_MESH_BLOB_OP_XFER_GET BT_MESH_MODEL_OP_2(0x83, 0x00)
|
||||
#define BT_MESH_BLOB_OP_XFER_START BT_MESH_MODEL_OP_2(0x83, 0x01)
|
||||
#define BT_MESH_BLOB_OP_XFER_CANCEL BT_MESH_MODEL_OP_2(0x83, 0x02)
|
||||
#define BT_MESH_BLOB_OP_XFER_STATUS BT_MESH_MODEL_OP_2(0x83, 0x03)
|
||||
#define BT_MESH_BLOB_OP_BLOCK_GET BT_MESH_MODEL_OP_2(0x83, 0x05)
|
||||
#define BT_MESH_BLOB_OP_BLOCK_START BT_MESH_MODEL_OP_2(0x83, 0x04)
|
||||
#define BT_MESH_BLOB_OP_CHUNK BT_MESH_MODEL_OP_1(0x66)
|
||||
#define BT_MESH_BLOB_OP_BLOCK_STATUS BT_MESH_MODEL_OP_1(0x67)
|
||||
#define BT_MESH_BLOB_OP_BLOCK_REPORT BT_MESH_MODEL_OP_1(0x68)
|
||||
#define BT_MESH_BLOB_OP_INFO_GET BT_MESH_MODEL_OP_2(0x83, 0x06)
|
||||
#define BT_MESH_BLOB_OP_INFO_STATUS BT_MESH_MODEL_OP_2(0x83, 0x07)
|
||||
|
||||
#define BLOB_BLOCK_NOT_SET 0xffff
|
||||
|
||||
#define BLOB_CHUNK_SDU_OVERHEAD \
|
||||
(BT_MESH_MODEL_OP_LEN(BT_MESH_BLOB_OP_CHUNK) + 2 + BT_MESH_MIC_SHORT)
|
||||
|
||||
#define BLOB_CHUNK_SIZE_MAX(sdu_max) ((sdu_max) - BLOB_CHUNK_SDU_OVERHEAD)
|
||||
#define BLOB_CHUNK_SDU_LEN(chunk_size) (BLOB_CHUNK_SDU_OVERHEAD + (chunk_size))
|
||||
|
||||
#if CONFIG_BT_MESH_ALIGN_CHUNK_SIZE_TO_MAX_SEGMENT || \
|
||||
CONFIG_BT_MESH_RX_BLOB_CHUNK_SIZE > BLOB_CHUNK_SIZE_MAX(BT_MESH_RX_SDU_MAX)
|
||||
#define BLOB_RX_CHUNK_SIZE BLOB_CHUNK_SIZE_MAX(BT_MESH_RX_SDU_MAX)
|
||||
#else
|
||||
#define BLOB_RX_CHUNK_SIZE CONFIG_BT_MESH_RX_BLOB_CHUNK_SIZE
|
||||
#endif
|
||||
|
||||
#if CONFIG_BT_MESH_ALIGN_CHUNK_SIZE_TO_MAX_SEGMENT || \
|
||||
CONFIG_BT_MESH_TX_BLOB_CHUNK_SIZE > BLOB_CHUNK_SIZE_MAX(BT_MESH_TX_SDU_MAX)
|
||||
#define BLOB_TX_CHUNK_SIZE BLOB_CHUNK_SIZE_MAX(BT_MESH_TX_SDU_MAX)
|
||||
#else
|
||||
#define BLOB_TX_CHUNK_SIZE CONFIG_BT_MESH_TX_BLOB_CHUNK_SIZE
|
||||
#endif
|
||||
|
||||
/* Utility macros for calculating log2 of a number at compile time.
|
||||
* Used to determine the log2 representation of the block size, which
|
||||
* is configured as a raw number, but encoded as log2.
|
||||
*
|
||||
* The macros expand to a series of ternary expressions, effectively
|
||||
* searching through power of twos until a match is found.
|
||||
* According to MshMBTv1.0, the block size cannot be larger than 2^20,
|
||||
* so we'll stop the search at 20.
|
||||
*/
|
||||
#define _BLOB_LOG_2_CEIL(l, x) ((x) <= (1U << l)) ? l :
|
||||
#define _BLOB_LOG_2_FLOOR(l, x) ((x) < (1U << (l + 1))) ? l :
|
||||
|
||||
#define BLOB_BLOCK_SIZE_LOG_CEIL(x) (LISTIFY(20, _BLOB_LOG_2_CEIL, (), x) 20)
|
||||
#define BLOB_BLOCK_SIZE_LOG_FLOOR(x) (LISTIFY(20, _BLOB_LOG_2_FLOOR, (), x) 20)
|
||||
|
||||
/* Log2 representation of the minimum block size */
|
||||
#define BLOB_BLOCK_SIZE_LOG_MIN BLOB_BLOCK_SIZE_LOG_CEIL(CONFIG_BT_MESH_BLOB_BLOCK_SIZE_MIN)
|
||||
/* Log2 representation of the maximum block size */
|
||||
#define BLOB_BLOCK_SIZE_LOG_MAX BLOB_BLOCK_SIZE_LOG_FLOOR(CONFIG_BT_MESH_BLOB_BLOCK_SIZE_MAX)
|
||||
|
||||
#if defined(CONFIG_BT_MESH_BLOB_SRV)
|
||||
#define BLOB_BLOCK_REPORT_STATUS_MSG_MAXLEN ( \
|
||||
MAX(sizeof(((struct bt_mesh_blob_block *)0)->missing), \
|
||||
CONFIG_BT_MESH_BLOB_SRV_PULL_REQ_COUNT * 3))
|
||||
#define BLOB_BLOCK_STATUS_MSG_MAXLEN (5 + \
|
||||
MAX(sizeof(((struct bt_mesh_blob_block *)0)->missing), \
|
||||
CONFIG_BT_MESH_BLOB_SRV_PULL_REQ_COUNT * 3))
|
||||
#else
|
||||
#define BLOB_BLOCK_REPORT_STATUS_MSG_MAXLEN sizeof(((struct bt_mesh_blob_srv *)0)->block.missing)
|
||||
#define BLOB_BLOCK_STATUS_MSG_MAXLEN (5 + sizeof(((struct bt_mesh_blob_srv *)0)->block.missing))
|
||||
#endif
|
||||
|
||||
#define BLOB_XFER_STATUS_MSG_MAXLEN (17 + sizeof(((struct bt_mesh_blob_srv *)0)->state.blocks))
|
||||
|
||||
enum bt_mesh_blob_chunks_missing {
|
||||
BT_MESH_BLOB_CHUNKS_MISSING_ALL,
|
||||
BT_MESH_BLOB_CHUNKS_MISSING_NONE,
|
||||
BT_MESH_BLOB_CHUNKS_MISSING_SOME,
|
||||
BT_MESH_BLOB_CHUNKS_MISSING_ENCODED,
|
||||
};
|
||||
|
||||
static inline size_t blob_block_size(size_t xfer_size, uint8_t block_size_log,
|
||||
uint32_t idx)
|
||||
{
|
||||
if (((idx + 1U) << block_size_log) <= xfer_size) {
|
||||
return (1U << block_size_log);
|
||||
}
|
||||
|
||||
return xfer_size & BIT_MASK(block_size_log);
|
||||
}
|
||||
|
||||
static inline void blob_chunk_missing_set(uint8_t *missing_chunks,
|
||||
int idx, bool missing)
|
||||
{
|
||||
WRITE_BIT(missing_chunks[idx / 8], idx % 8, missing);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
blob_chunk_missing_get(const uint8_t *missing_chunks, int idx)
|
||||
{
|
||||
return !!(missing_chunks[idx / 8] & BIT(idx % 8));
|
||||
}
|
||||
|
||||
static inline void blob_chunk_missing_set_all(struct bt_mesh_blob_block *block)
|
||||
{
|
||||
size_t bytes = block->chunk_count / 8;
|
||||
|
||||
memset(block->missing, 0xff, bytes);
|
||||
if (block->chunk_count % 8) {
|
||||
block->missing[bytes] = BIT_MASK(block->chunk_count % 8);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void blob_chunk_missing_set_none(struct bt_mesh_blob_block *block)
|
||||
{
|
||||
memset(block->missing, 0, sizeof(block->missing));
|
||||
}
|
||||
|
||||
/** @brief Perform a message broadcast to all BLOB Transfer Client Target nodes.
|
||||
*
|
||||
* Will send to a group or each Target node individually, repeating until
|
||||
* all Target nodes have responded or the retry time has run out.
|
||||
*
|
||||
* @param cli BLOB Transfer Client instance
|
||||
* @param ctx Broadcast context
|
||||
*/
|
||||
void blob_cli_broadcast(struct bt_mesh_blob_cli *cli,
|
||||
const struct blob_cli_broadcast_ctx *ctx);
|
||||
|
||||
/** @brief Register that a Target node responded to a broadcast.
|
||||
*
|
||||
* @param cli BLOB Transfer Client instance
|
||||
* @param target Target node that responded.
|
||||
*/
|
||||
void blob_cli_broadcast_rsp(struct bt_mesh_blob_cli *cli,
|
||||
struct bt_mesh_blob_target *target);
|
||||
|
||||
/** @brief Notify the BLOB Transfer Client that the requested transmission is complete.
|
||||
*
|
||||
* Should be called once for each call to the @ref blob_cli_broadcast_ctx.send
|
||||
* callback.
|
||||
*
|
||||
* @param cli BLOB Transfer Client instance.
|
||||
*/
|
||||
void blob_cli_broadcast_tx_complete(struct bt_mesh_blob_cli *cli);
|
||||
|
||||
/** @brief Aborts any ongoing BLOB Transfer Client operations.
|
||||
*
|
||||
* @param cli BLOB Transfer Client instance.
|
||||
*/
|
||||
void blob_cli_broadcast_abort(struct bt_mesh_blob_cli *cli);
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user