966 lines
27 KiB
C
966 lines
27 KiB
C
|
/*
|
||
|
* Copyright (c) 2020 Nordic Semiconductor ASA
|
||
|
*
|
||
|
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||
|
*/
|
||
|
|
||
|
#include "btstack/bluetooth.h"
|
||
|
#include "system/includes.h"
|
||
|
#include "bt_common.h"
|
||
|
#include "api/sig_mesh_api.h"
|
||
|
#include "api/scene_srv.h"
|
||
|
#include "misc/slist.h"
|
||
|
#include "misc/list_gen.h"
|
||
|
#include "misc/byteorder.h"
|
||
|
#include "model_api.h"
|
||
|
#include <access.h>
|
||
|
#include "model_utils.h"
|
||
|
#include "model_types.h"
|
||
|
#include "adaptation.h"
|
||
|
|
||
|
#if (CONFIG_BT_MESH_SCENE_SRV)
|
||
|
|
||
|
#define LOG_TAG "[Gen_Scene_srv]"
|
||
|
#define LOG_ERROR_ENABLE
|
||
|
#define LOG_DEBUG_ENABLE
|
||
|
#define LOG_INFO_ENABLE
|
||
|
/* #define LOG_DUMP_ENABLE */
|
||
|
#define LOG_CLI_ENABLE
|
||
|
#include "debug.h"
|
||
|
|
||
|
#define SCENE_PAGE_SIZE SETTINGS_MAX_VAL_LEN
|
||
|
/* Account for company ID in data: */
|
||
|
#define VND_MODEL_SCENE_DATA_OVERHEAD sizeof(uint16_t)
|
||
|
|
||
|
struct __attribute__((__packed__)) scene_data {
|
||
|
uint8_t len;
|
||
|
uint8_t elem_idx;
|
||
|
uint16_t id;
|
||
|
uint8_t data[];
|
||
|
};
|
||
|
|
||
|
static sys_slist_t scene_servers;
|
||
|
|
||
|
static char *scene_path(char *buf, uint16_t scene, bool vnd, uint8_t page)
|
||
|
{
|
||
|
sprintf(buf, "%x/%c%x", scene, vnd ? 'v' : 's', page);
|
||
|
return buf;
|
||
|
}
|
||
|
|
||
|
static inline void update_page_count(struct bt_mesh_scene_srv *srv, bool vnd,
|
||
|
uint8_t page)
|
||
|
{
|
||
|
if (vnd) {
|
||
|
srv->vndpages = MAX(page + 1, srv->vndpages);
|
||
|
} else {
|
||
|
srv->sigpages = MAX(page + 1, srv->sigpages);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static const struct bt_mesh_scene_entry *
|
||
|
entry_find(const struct bt_mesh_model *mod, bool vnd)
|
||
|
{
|
||
|
const struct bt_mesh_scene_entry *it;
|
||
|
|
||
|
if (vnd) {
|
||
|
extern const struct bt_mesh_scene_entry
|
||
|
_bt_mesh_scene_entry_vnd_list_start[];
|
||
|
extern const struct bt_mesh_scene_entry
|
||
|
_bt_mesh_scene_entry_vnd_list_end[];
|
||
|
|
||
|
for (it = _bt_mesh_scene_entry_vnd_list_start;
|
||
|
it != _bt_mesh_scene_entry_vnd_list_end; it++) {
|
||
|
if (it->id.vnd.id == mod->vnd.id &&
|
||
|
it->id.vnd.company == mod->vnd.company) {
|
||
|
return it;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
extern const struct bt_mesh_scene_entry
|
||
|
_bt_mesh_scene_entry_sig_list_start[];
|
||
|
extern const struct bt_mesh_scene_entry
|
||
|
_bt_mesh_scene_entry_sig_list_end[];
|
||
|
|
||
|
for (it = _bt_mesh_scene_entry_sig_list_start;
|
||
|
it != _bt_mesh_scene_entry_sig_list_end; it++) {
|
||
|
if (it->id.sig == mod->id) {
|
||
|
return it;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static struct bt_mesh_scene_srv *srv_find(uint16_t elem_idx)
|
||
|
{
|
||
|
struct bt_mesh_scene_srv *srv = scene_servers.head;
|
||
|
|
||
|
// 遍历链表
|
||
|
while (srv != NULL) {
|
||
|
/* Scene servers are added to the link list in reverse
|
||
|
* composition data order. The first scene server that isn't
|
||
|
* after this element will be the right one:
|
||
|
*/
|
||
|
if (srv->model->rt->elem_idx <= elem_idx) {
|
||
|
return srv;
|
||
|
}
|
||
|
srv = (struct bt_mesh_scene_srv *)srv->n.next;
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static uint16_t current_scene(const struct bt_mesh_scene_srv *srv)
|
||
|
{
|
||
|
if (srv->next && k_work_delayable_is_pending(&srv->work)) {
|
||
|
/* When we're in a transition, the current scene should be NONE (see the Bluetooth
|
||
|
* mesh model specification, section 5.1.3.2.1). Check that we're not just in a
|
||
|
* delay phase:
|
||
|
*/
|
||
|
if (!srv->transition.delay ||
|
||
|
(k_ticks_to_ms_near64(k_work_delayable_remaining_get(&srv->work)) <=
|
||
|
srv->transition.time)) {
|
||
|
return BT_MESH_SCENE_NONE;
|
||
|
} else {
|
||
|
return srv->prev;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return srv->prev;
|
||
|
}
|
||
|
|
||
|
static uint16_t target_scene(const struct bt_mesh_scene_srv *srv)
|
||
|
{
|
||
|
return srv->next;
|
||
|
}
|
||
|
|
||
|
static void scene_status_encode(struct bt_mesh_scene_srv *srv, struct net_buf_simple *buf,
|
||
|
const struct bt_mesh_scene_state *state)
|
||
|
{
|
||
|
bt_mesh_model_msg_init(buf, BT_MESH_SCENE_OP_STATUS);
|
||
|
net_buf_simple_add_u8(buf, state->status);
|
||
|
if (state->target != BT_MESH_SCENE_NONE) {
|
||
|
net_buf_simple_add_le16(buf, state->current);
|
||
|
net_buf_simple_add_le16(buf, state->target);
|
||
|
net_buf_simple_add_u8(buf, model_transition_encode(state->remaining_time));
|
||
|
} else {
|
||
|
net_buf_simple_add_le16(buf, state->current);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int scene_status_send(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
|
||
|
const struct bt_mesh_scene_state *state)
|
||
|
{
|
||
|
struct bt_mesh_scene_srv *srv = model->rt->user_data;
|
||
|
|
||
|
BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_SCENE_OP_STATUS,
|
||
|
BT_MESH_SCENE_MSG_MAXLEN_STATUS);
|
||
|
|
||
|
scene_status_encode(srv, &buf, state);
|
||
|
|
||
|
return bt_mesh_msg_send(model, ctx, &buf);
|
||
|
}
|
||
|
|
||
|
static void curr_scene_state_get(struct bt_mesh_scene_srv *srv, struct bt_mesh_scene_state *state)
|
||
|
{
|
||
|
state->current = current_scene(srv);
|
||
|
state->target = target_scene(srv);
|
||
|
state->remaining_time = k_ticks_to_ms_near64(k_work_delayable_remaining_get(&srv->work));
|
||
|
state->status = BT_MESH_SCENE_SUCCESS;
|
||
|
}
|
||
|
|
||
|
static int handle_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
|
||
|
struct net_buf_simple *buf)
|
||
|
{
|
||
|
struct bt_mesh_scene_srv *srv = model->rt->user_data;
|
||
|
struct bt_mesh_scene_state state;
|
||
|
|
||
|
curr_scene_state_get(srv, &state);
|
||
|
scene_status_send(model, ctx, &state);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int scene_recall(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
|
||
|
struct net_buf_simple *buf, bool ack)
|
||
|
{
|
||
|
struct bt_mesh_scene_srv *srv = model->rt->user_data;
|
||
|
struct bt_mesh_model_transition transition;
|
||
|
struct bt_mesh_scene_state state;
|
||
|
uint16_t scene;
|
||
|
bool has_trans;
|
||
|
uint8_t tid;
|
||
|
int err;
|
||
|
|
||
|
scene = net_buf_simple_pull_le16(buf);
|
||
|
if (scene == BT_MESH_SCENE_NONE) {
|
||
|
return -EINVAL; /* Prohibited */
|
||
|
}
|
||
|
|
||
|
tid = net_buf_simple_pull_u8(buf);
|
||
|
has_trans = !!model_transition_get(srv->model, &transition, buf);
|
||
|
|
||
|
if (tid_check_and_update(&srv->tid, tid, ctx)) {
|
||
|
log_debug("Duplicate TID");
|
||
|
curr_scene_state_get(srv, &state);
|
||
|
scene_status_send(model, ctx, &state);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
err = bt_mesh_scene_srv_set(srv, scene, has_trans ? &transition : NULL);
|
||
|
if (err) {
|
||
|
curr_scene_state_get(srv, &state);
|
||
|
if (err == -ENOENT) {
|
||
|
state.status = BT_MESH_SCENE_NOT_FOUND;
|
||
|
}
|
||
|
} else {
|
||
|
state.status = BT_MESH_SCENE_SUCCESS;
|
||
|
state.current = srv->prev;
|
||
|
state.target = srv->next;
|
||
|
state.remaining_time = transition.time;
|
||
|
}
|
||
|
|
||
|
if (ack) {
|
||
|
scene_status_send(model, ctx, &state);
|
||
|
}
|
||
|
|
||
|
if (state.status == BT_MESH_SCENE_SUCCESS) {
|
||
|
/* Publish */
|
||
|
scene_status_send(model, NULL, &state);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int handle_recall(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
|
||
|
struct net_buf_simple *buf)
|
||
|
{
|
||
|
return scene_recall(model, ctx, buf, true);
|
||
|
}
|
||
|
|
||
|
static int handle_recall_unack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
|
||
|
struct net_buf_simple *buf)
|
||
|
{
|
||
|
return scene_recall(model, ctx, buf, false);
|
||
|
}
|
||
|
|
||
|
static int scene_register_status_send(struct bt_mesh_model *model,
|
||
|
struct bt_mesh_msg_ctx *ctx,
|
||
|
enum bt_mesh_scene_status status)
|
||
|
{
|
||
|
struct bt_mesh_scene_srv *srv = model->rt->user_data;
|
||
|
|
||
|
BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_SCENE_OP_REGISTER_STATUS,
|
||
|
BT_MESH_SCENE_MSG_MINLEN_REGISTER_STATUS +
|
||
|
2 * CONFIG_BT_MESH_SCENES_MAX);
|
||
|
bt_mesh_model_msg_init(&buf, BT_MESH_SCENE_OP_REGISTER_STATUS);
|
||
|
net_buf_simple_add_u8(&buf, status);
|
||
|
net_buf_simple_add_le16(&buf, current_scene(srv));
|
||
|
|
||
|
for (int i = 0; i < srv->count; i++) {
|
||
|
net_buf_simple_add_le16(&buf, srv->all[i]);
|
||
|
}
|
||
|
|
||
|
return bt_mesh_msg_send(model, ctx, &buf);
|
||
|
}
|
||
|
|
||
|
static int handle_register_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
|
||
|
struct net_buf_simple *buf)
|
||
|
{
|
||
|
(void)scene_register_status_send(model, ctx, BT_MESH_SCENE_SUCCESS);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
const struct bt_mesh_model_op _bt_mesh_scene_srv_op[] = {
|
||
|
{
|
||
|
BT_MESH_SCENE_OP_GET,
|
||
|
BT_MESH_LEN_EXACT(BT_MESH_SCENE_MSG_LEN_GET),
|
||
|
handle_get,
|
||
|
},
|
||
|
{
|
||
|
BT_MESH_SCENE_OP_RECALL,
|
||
|
BT_MESH_LEN_MIN(BT_MESH_SCENE_MSG_MINLEN_RECALL),
|
||
|
handle_recall,
|
||
|
},
|
||
|
{
|
||
|
BT_MESH_SCENE_OP_RECALL_UNACK,
|
||
|
BT_MESH_LEN_MIN(BT_MESH_SCENE_MSG_MINLEN_RECALL),
|
||
|
handle_recall_unack,
|
||
|
},
|
||
|
{
|
||
|
BT_MESH_SCENE_OP_REGISTER_GET,
|
||
|
BT_MESH_LEN_EXACT(BT_MESH_SCENE_MSG_LEN_REGISTER_GET),
|
||
|
handle_register_get,
|
||
|
},
|
||
|
BT_MESH_MODEL_OP_END,
|
||
|
};
|
||
|
|
||
|
static uint16_t *scene_find(struct bt_mesh_scene_srv *srv, uint16_t scene)
|
||
|
{
|
||
|
for (int i = 0; i < srv->count; i++) {
|
||
|
if (srv->all[i] == scene) {
|
||
|
return &srv->all[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static void entry_recover(struct bt_mesh_scene_srv *srv, bool vnd,
|
||
|
const struct scene_data *data)
|
||
|
{
|
||
|
const struct bt_mesh_elem *elem = &bt_mesh_comp_get()->elem[data->elem_idx];
|
||
|
const size_t overhead = vnd ? VND_MODEL_SCENE_DATA_OVERHEAD : 0;
|
||
|
const struct bt_mesh_scene_entry *entry;
|
||
|
struct bt_mesh_model *mod;
|
||
|
|
||
|
if (vnd) {
|
||
|
mod = bt_mesh_model_find_vnd(elem, sys_get_le16(data->data), data->id);
|
||
|
} else {
|
||
|
mod = bt_mesh_model_find(elem, data->id);
|
||
|
}
|
||
|
|
||
|
if (!mod) {
|
||
|
log_error("No model @%s", bt_hex(&data->elem_idx, vnd ? 5 : 3));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* MeshMDL1.0.1, section 5.1.3.1.1:
|
||
|
* If a model is extending another model, the extending model shall determine
|
||
|
* the Stored with Scene behavior of that model.
|
||
|
*/
|
||
|
if (bt_mesh_model_is_extended(mod)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
entry = entry_find(mod, vnd);
|
||
|
if (!entry) {
|
||
|
log_error("No scene entry for %s", bt_hex(&data->elem_idx, vnd ? 5 : 3));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
entry->recall(mod, &data->data[overhead], data->len - overhead,
|
||
|
&srv->transition);
|
||
|
}
|
||
|
|
||
|
static void page_recover(struct bt_mesh_scene_srv *srv, bool vnd,
|
||
|
const uint8_t buf[], size_t len)
|
||
|
{
|
||
|
for (struct scene_data *data = (struct scene_data *)&buf[0];
|
||
|
data < (struct scene_data *)&buf[len];
|
||
|
data = (struct scene_data *)&data->data[data->len]) {
|
||
|
entry_recover(srv, vnd, data);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static ssize_t entry_store(struct bt_mesh_model *mod,
|
||
|
const struct bt_mesh_scene_entry *entry, bool vnd,
|
||
|
uint8_t buf[])
|
||
|
{
|
||
|
struct scene_data *data = (struct scene_data *)buf;
|
||
|
ssize_t size;
|
||
|
|
||
|
data->elem_idx = mod->rt->elem_idx;
|
||
|
|
||
|
if (vnd) {
|
||
|
data->id = mod->vnd.id;
|
||
|
sys_put_le16(mod->vnd.company, data->data);
|
||
|
size = entry->store(mod,
|
||
|
&data->data[VND_MODEL_SCENE_DATA_OVERHEAD]);
|
||
|
data->len = size + VND_MODEL_SCENE_DATA_OVERHEAD;
|
||
|
} else {
|
||
|
data->id = mod->id;
|
||
|
size = entry->store(mod, &data->data[0]);
|
||
|
data->len = size;
|
||
|
}
|
||
|
|
||
|
if (size > entry->maxlen) {
|
||
|
log_error("Entry %s:%u:%u: data too large (%u bytes)",
|
||
|
vnd ? "vnd" : "sig", mod->rt->elem_idx, mod->rt->mod_idx, size);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
if (size < 0) {
|
||
|
log_error("Failed storing %s:%u:%u (%d)", vnd ? "vnd" : "sig",
|
||
|
mod->rt->elem_idx, mod->rt->mod_idx, size);
|
||
|
return size;
|
||
|
}
|
||
|
|
||
|
if (size == 0) {
|
||
|
/* Silently ignore this entry */
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
return sizeof(struct scene_data) + data->len;
|
||
|
}
|
||
|
|
||
|
/** Store a single page of the Scene.
|
||
|
*
|
||
|
* To accommodate large scene data, each scene is stored in pages of up to 256
|
||
|
* bytes.
|
||
|
*/
|
||
|
static void page_store(struct bt_mesh_scene_srv *srv, uint16_t scene,
|
||
|
uint8_t page, bool vnd, uint8_t buf[], size_t len)
|
||
|
{
|
||
|
char path[9];
|
||
|
int err;
|
||
|
|
||
|
scene_path(path, scene, vnd, page);
|
||
|
update_page_count(srv, vnd, page);
|
||
|
|
||
|
err = bt_mesh_model_data_store(srv->model, false, path, buf, len);
|
||
|
if (err) {
|
||
|
log_error("Failed storing %s: %d", path, err);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** @brief Get the end of the Scene server's controlled elements.
|
||
|
*
|
||
|
* A Scene Server controls all elements whose index is equal to or larger than
|
||
|
* its own, and smaller than the return value of this function.
|
||
|
*
|
||
|
* @param[in] srv Scene Server to find control boundary of.
|
||
|
*
|
||
|
* @return The element index of the next Scene Server, or the total element
|
||
|
* count.
|
||
|
*/
|
||
|
static uint16_t srv_elem_end(const struct bt_mesh_scene_srv *srv)
|
||
|
{
|
||
|
uint16_t end = bt_mesh_elem_count();
|
||
|
struct bt_mesh_scene_srv *it = scene_servers.head;
|
||
|
|
||
|
// 遍历链表
|
||
|
while (it != NULL) {
|
||
|
if (it == srv) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
end = it->model->rt->elem_idx;
|
||
|
it = (struct bt_mesh_scene_srv *)it->n.next;
|
||
|
}
|
||
|
|
||
|
return end;
|
||
|
}
|
||
|
|
||
|
static void scene_recall_complete_mod(struct bt_mesh_scene_srv *srv, struct bt_mesh_model *models,
|
||
|
int model_count, bool vnd)
|
||
|
{
|
||
|
for (int j = 0; j < model_count; j++) {
|
||
|
const struct bt_mesh_scene_entry *entry;
|
||
|
struct bt_mesh_model *mod = &models[j];
|
||
|
|
||
|
if (mod == srv->model) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/* MeshMDL1.0.1, section 5.1.3.1.1:
|
||
|
* If a model is extending another model, the extending
|
||
|
* model shall determine the Stored with Scene behavior
|
||
|
* of that model.
|
||
|
*/
|
||
|
if (bt_mesh_model_is_extended(mod)) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
entry = entry_find(mod, vnd);
|
||
|
if (!entry || !entry->recall_complete) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
entry->recall_complete(mod);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void scene_recall_complete(struct bt_mesh_scene_srv *srv)
|
||
|
{
|
||
|
const struct bt_mesh_comp *comp = bt_mesh_comp_get();
|
||
|
uint16_t elem_end = srv_elem_end(srv);
|
||
|
|
||
|
for (int i = srv->model->rt->elem_idx; i < elem_end; i++) {
|
||
|
const struct bt_mesh_elem *elem = &comp->elem[i];
|
||
|
|
||
|
scene_recall_complete_mod(srv, elem->models, elem->model_count, false);
|
||
|
scene_recall_complete_mod(srv, elem->vnd_models, elem->vnd_model_count, true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void scene_store_mod(struct bt_mesh_scene_srv *srv, uint16_t scene,
|
||
|
bool vnd)
|
||
|
{
|
||
|
const size_t data_overhead = sizeof(struct scene_data) + (vnd ? 2 : 0);
|
||
|
const struct bt_mesh_comp *comp = bt_mesh_comp_get();
|
||
|
uint16_t elem_end = srv_elem_end(srv);
|
||
|
uint8_t buf[SCENE_PAGE_SIZE];
|
||
|
uint8_t page = 0;
|
||
|
size_t len = 0;
|
||
|
for (int i = srv->model->rt->elem_idx; i < elem_end; i++) {
|
||
|
const struct bt_mesh_elem *elem = &comp->elem[i];
|
||
|
struct bt_mesh_model *models = vnd ? elem->vnd_models : elem->models;
|
||
|
int model_count = vnd ? elem->vnd_model_count : elem->model_count;
|
||
|
|
||
|
for (int j = 0; j < model_count; j++) {
|
||
|
const struct bt_mesh_scene_entry *entry;
|
||
|
struct bt_mesh_model *mod = &models[j];
|
||
|
ssize_t size;
|
||
|
|
||
|
if (mod == srv->model) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/* MeshMDL1.0.1, section 5.1.3.1.1:
|
||
|
* If a model is extending another model, the extending
|
||
|
* model shall determine the Stored with Scene behavior
|
||
|
* of that model.
|
||
|
*/
|
||
|
if (bt_mesh_model_is_extended(mod)) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
entry = entry_find(mod, vnd);
|
||
|
if (!entry) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (len + data_overhead + entry->maxlen >= SCENE_PAGE_SIZE) {
|
||
|
page_store(srv, scene, page++, vnd, buf, len);
|
||
|
len = 0;
|
||
|
}
|
||
|
|
||
|
size = entry_store(mod, entry, vnd, &buf[len]);
|
||
|
len += MAX(0, size);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (len) {
|
||
|
page_store(srv, scene, page, vnd, buf, len);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static enum bt_mesh_scene_status scene_store(struct bt_mesh_scene_srv *srv,
|
||
|
uint16_t scene)
|
||
|
{
|
||
|
uint16_t *existing = scene_find(srv, scene);
|
||
|
|
||
|
if (!existing) {
|
||
|
if (srv->count == ARRAY_SIZE(srv->all)) {
|
||
|
log_error("Out of space");
|
||
|
return BT_MESH_SCENE_REGISTER_FULL;
|
||
|
}
|
||
|
|
||
|
srv->all[srv->count++] = scene;
|
||
|
}
|
||
|
|
||
|
scene_store_mod(srv, scene, false);
|
||
|
scene_store_mod(srv, scene, true);
|
||
|
|
||
|
srv->prev = scene;
|
||
|
srv->next = BT_MESH_SCENE_NONE;
|
||
|
/* We're checking srv->next in the handler, so failure to cancel is okay: */
|
||
|
(void)k_work_cancel_delayable(&srv->work);
|
||
|
|
||
|
return BT_MESH_SCENE_SUCCESS;
|
||
|
}
|
||
|
|
||
|
static void scene_delete(struct bt_mesh_scene_srv *srv, uint16_t *scene)
|
||
|
{
|
||
|
uint8_t path[9];
|
||
|
|
||
|
log_debug("0x%x", *scene);
|
||
|
|
||
|
for (int i = 0; i < srv->sigpages; i++) {
|
||
|
scene_path(path, *scene, false, i);
|
||
|
(void)bt_mesh_model_data_store(srv->model, false, path, NULL, 0);
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < srv->vndpages; i++) {
|
||
|
scene_path(path, *scene, true, i);
|
||
|
(void)bt_mesh_model_data_store(srv->model, false, path, NULL, 0);
|
||
|
}
|
||
|
|
||
|
uint16_t target = target_scene(srv);
|
||
|
uint16_t current = current_scene(srv);
|
||
|
|
||
|
if (target == *scene ||
|
||
|
(current == *scene && target == BT_MESH_SCENE_NONE)) {
|
||
|
srv->next = BT_MESH_SCENE_NONE;
|
||
|
srv->prev = BT_MESH_SCENE_NONE;
|
||
|
|
||
|
/* Cancel failure checked in work handler. */
|
||
|
(void)k_work_cancel_delayable(&srv->work);
|
||
|
} else if (current == *scene && target != *scene) {
|
||
|
srv->prev = BT_MESH_SCENE_NONE;
|
||
|
}
|
||
|
|
||
|
*scene = srv->all[--srv->count];
|
||
|
}
|
||
|
|
||
|
static int handle_store(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
|
||
|
struct net_buf_simple *buf)
|
||
|
{
|
||
|
struct bt_mesh_scene_srv *srv = model->rt->user_data;
|
||
|
enum bt_mesh_scene_status status;
|
||
|
uint16_t scene_number;
|
||
|
|
||
|
scene_number = net_buf_simple_pull_le16(buf);
|
||
|
if (scene_number == BT_MESH_SCENE_NONE) {
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
status = scene_store(srv, scene_number);
|
||
|
scene_register_status_send(model, ctx, status);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int handle_store_unack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
|
||
|
struct net_buf_simple *buf)
|
||
|
{
|
||
|
struct bt_mesh_scene_srv *srv = model->rt->user_data;
|
||
|
uint16_t scene_number;
|
||
|
|
||
|
scene_number = net_buf_simple_pull_le16(buf);
|
||
|
if (scene_number == BT_MESH_SCENE_NONE) {
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
(void)scene_store(srv, scene_number);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int delete_scene(struct bt_mesh_scene_srv *srv, struct bt_mesh_msg_ctx *ctx,
|
||
|
struct net_buf_simple *buf)
|
||
|
{
|
||
|
uint16_t *scene;
|
||
|
|
||
|
scene = scene_find(srv, net_buf_simple_pull_le16(buf));
|
||
|
if (scene != BT_MESH_SCENE_NONE) {
|
||
|
scene_delete(srv, scene);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int handle_delete(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
|
||
|
struct net_buf_simple *buf)
|
||
|
{
|
||
|
struct bt_mesh_scene_srv *srv = model->rt->user_data;
|
||
|
int err;
|
||
|
|
||
|
err = delete_scene(srv, ctx, buf);
|
||
|
if (err) {
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
scene_register_status_send(model, ctx, BT_MESH_SCENE_SUCCESS);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int handle_delete_unack(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
|
||
|
struct net_buf_simple *buf)
|
||
|
{
|
||
|
struct bt_mesh_scene_srv *srv = model->rt->user_data;
|
||
|
|
||
|
return delete_scene(srv, ctx, buf);
|
||
|
}
|
||
|
|
||
|
const struct bt_mesh_model_op _bt_mesh_scene_setup_srv_op[] = {
|
||
|
{
|
||
|
BT_MESH_SCENE_OP_STORE,
|
||
|
BT_MESH_LEN_EXACT(BT_MESH_SCENE_MSG_LEN_STORE),
|
||
|
handle_store,
|
||
|
},
|
||
|
{
|
||
|
BT_MESH_SCENE_OP_STORE_UNACK,
|
||
|
BT_MESH_LEN_EXACT(BT_MESH_SCENE_MSG_LEN_STORE),
|
||
|
handle_store_unack,
|
||
|
},
|
||
|
{
|
||
|
BT_MESH_SCENE_OP_DELETE,
|
||
|
BT_MESH_LEN_EXACT(BT_MESH_SCENE_MSG_LEN_DELETE),
|
||
|
handle_delete,
|
||
|
},
|
||
|
{
|
||
|
BT_MESH_SCENE_OP_DELETE_UNACK,
|
||
|
BT_MESH_LEN_EXACT(BT_MESH_SCENE_MSG_LEN_DELETE),
|
||
|
handle_delete_unack,
|
||
|
},
|
||
|
BT_MESH_MODEL_OP_END,
|
||
|
};
|
||
|
|
||
|
// static void scene_srv_transition_end(struct k_work *work)
|
||
|
// {
|
||
|
// struct bt_mesh_scene_srv *srv =
|
||
|
// CONTAINER_OF(k_work_delayable_from_work(work), struct bt_mesh_scene_srv, work);
|
||
|
|
||
|
// if (srv->next == BT_MESH_SCENE_NONE) {
|
||
|
// /* This is already done */
|
||
|
// return;
|
||
|
// }
|
||
|
|
||
|
// srv->prev = srv->next;
|
||
|
// srv->next = BT_MESH_SCENE_NONE;
|
||
|
|
||
|
// /* Publish at the end of the transition */
|
||
|
// if (bt_mesh_model_transition_time(&srv->transition)) {
|
||
|
// bt_mesh_scene_srv_pub(srv, NULL);
|
||
|
// }
|
||
|
// }
|
||
|
|
||
|
static int scene_srv_pub_update(struct bt_mesh_model *model)
|
||
|
{
|
||
|
struct bt_mesh_scene_srv *srv = model->rt->user_data;
|
||
|
struct bt_mesh_scene_state state;
|
||
|
|
||
|
curr_scene_state_get(srv, &state);
|
||
|
scene_status_encode(srv, &srv->pub_msg, &state);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int scene_srv_init(struct bt_mesh_model *model)
|
||
|
{
|
||
|
struct bt_mesh_scene_srv *srv = model->rt->user_data;
|
||
|
|
||
|
sys_slist_prepend(&scene_servers, &srv->n);
|
||
|
|
||
|
srv->model = model;
|
||
|
|
||
|
// k_work_init_delayable(&srv->work, scene_srv_transition_end);
|
||
|
|
||
|
net_buf_simple_init_with_data(&srv->pub_msg, srv->buf,
|
||
|
sizeof(srv->buf));
|
||
|
srv->pub.msg = &srv->pub_msg;
|
||
|
srv->pub.update = scene_srv_pub_update;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int scene_srv_set(struct bt_mesh_model *model, const char *path,
|
||
|
size_t len_rd, settings_read_cb read_cb, void *cb_arg)
|
||
|
{
|
||
|
#if 0 // not adapter
|
||
|
struct bt_mesh_scene_srv *srv = model->rt->user_data;
|
||
|
uint8_t buf[SCENE_PAGE_SIZE];
|
||
|
uint16_t scene;
|
||
|
ssize_t size;
|
||
|
uint8_t page;
|
||
|
bool vnd;
|
||
|
|
||
|
log_debug("path: %s", path);
|
||
|
|
||
|
/* The entire model data tree is loaded in this callback. Depending on
|
||
|
* the path and whether we have started the mesh, we'll handle the data
|
||
|
* differently:
|
||
|
*
|
||
|
* - Path "XXXX/vYY": Scene XXXX vendor model page YY
|
||
|
* - Path "XXXX/sYY": Scene XXXX sig model page YY
|
||
|
*/
|
||
|
scene = strtol(path, NULL, 16);
|
||
|
if (scene == BT_MESH_SCENE_NONE) {
|
||
|
log_error("Unknown data %s", path);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
settings_name_next(path, &path);
|
||
|
if (!path) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
vnd = path[0] == 'v';
|
||
|
page = strtol(&path[1], NULL, 16);
|
||
|
update_page_count(srv, vnd, page);
|
||
|
|
||
|
/* Before starting the mesh, we'll just register that the scene exists:
|
||
|
* Once the mesh starts, we'll load the current scene, and end up in
|
||
|
* this callback again, but bt_mesh_is_provisioned() will be true.
|
||
|
*/
|
||
|
if (!bt_mesh_is_provisioned()) {
|
||
|
if (scene_find(srv, scene)) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (srv->count == ARRAY_SIZE(srv->all)) {
|
||
|
log_error("No room for scene 0x%x", scene);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
log_debug("Recovered scene 0x%x", scene);
|
||
|
srv->all[srv->count++] = scene;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
size = read_cb(cb_arg, &buf, sizeof(buf));
|
||
|
if (size < 0) {
|
||
|
log_error("Failed loading scene 0x%x", scene);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
log_debug("0x%x: %s", scene, bt_hex(buf, size));
|
||
|
page_recover(srv, vnd, buf, size);
|
||
|
#endif
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void scene_srv_reset(struct bt_mesh_model *model)
|
||
|
{
|
||
|
struct bt_mesh_scene_srv *srv = model->rt->user_data;
|
||
|
|
||
|
srv->next = BT_MESH_SCENE_NONE;
|
||
|
|
||
|
while (srv->count) {
|
||
|
scene_delete(srv, &srv->all[0]);
|
||
|
}
|
||
|
|
||
|
srv->prev = BT_MESH_SCENE_NONE;
|
||
|
/* We're checking srv->next in the handler, so failure to cancel is okay: */
|
||
|
(void)k_work_cancel_delayable(&srv->work);
|
||
|
srv->sigpages = 0;
|
||
|
srv->vndpages = 0;
|
||
|
}
|
||
|
|
||
|
const struct bt_mesh_model_cb _bt_mesh_scene_srv_cb = {
|
||
|
.init = scene_srv_init,
|
||
|
.settings_set = scene_srv_set,
|
||
|
.reset = scene_srv_reset,
|
||
|
};
|
||
|
|
||
|
static int scene_setup_srv_init(struct bt_mesh_model *model)
|
||
|
{
|
||
|
struct bt_mesh_scene_srv *srv = model->rt->user_data;
|
||
|
struct bt_mesh_dtt_srv *dtt_srv = NULL;
|
||
|
int err;
|
||
|
|
||
|
if (!srv) {
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
srv->setup_mod = model;
|
||
|
//for compiler, not used now.
|
||
|
// if (IS_ENABLED(CONFIG_BT_MESH_DTT_SRV)) {
|
||
|
// dtt_srv = bt_mesh_dtt_srv_get(bt_mesh_model_elem(model));
|
||
|
// }
|
||
|
|
||
|
// if (!dtt_srv) {
|
||
|
// log_error("Failed to find Generic DTT Server on element");
|
||
|
// return -EINVAL;
|
||
|
// }
|
||
|
|
||
|
// err = bt_mesh_model_extend(srv->setup_mod, dtt_srv->model);
|
||
|
// if (err) {
|
||
|
// return err;
|
||
|
// }
|
||
|
//for compiler, not used now.
|
||
|
|
||
|
#if CONFIG_BT_MESH_COMP_PAGE_1
|
||
|
err = bt_mesh_model_correspond(srv->setup_mod, srv->model);
|
||
|
if (err) {
|
||
|
return err;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#if CONFIG_BT_MESH_MODEL_EXTENSIONS
|
||
|
return bt_mesh_model_extend(srv->setup_mod, srv->model);
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
const struct bt_mesh_model_cb _bt_mesh_scene_setup_srv_cb = {
|
||
|
.init = scene_setup_srv_init,
|
||
|
};
|
||
|
|
||
|
void bt_mesh_scene_invalidate(struct bt_mesh_model *mod)
|
||
|
{
|
||
|
struct bt_mesh_scene_srv *srv = srv_find(mod->rt->elem_idx);
|
||
|
|
||
|
if (!srv) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
srv->prev = BT_MESH_SCENE_NONE;
|
||
|
/* We're checking srv->next in the handler, so failure to cancel is okay: */
|
||
|
(void)k_work_cancel_delayable(&srv->work);
|
||
|
srv->next = BT_MESH_SCENE_NONE;
|
||
|
}
|
||
|
|
||
|
int bt_mesh_scene_srv_set(struct bt_mesh_scene_srv *srv, uint16_t scene,
|
||
|
struct bt_mesh_model_transition *transition)
|
||
|
{
|
||
|
int32_t transition_time;
|
||
|
uint16_t curr;
|
||
|
char path[25];
|
||
|
int err;
|
||
|
|
||
|
if (scene == BT_MESH_SCENE_NONE ||
|
||
|
model_transition_is_invalid(transition)) {
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
if (!scene_find(srv, scene)) {
|
||
|
log_error("Unknown scene 0x%x", scene);
|
||
|
return -ENOENT;
|
||
|
}
|
||
|
|
||
|
curr = current_scene(srv);
|
||
|
if (scene == curr) {
|
||
|
srv->prev = scene;
|
||
|
srv->next = BT_MESH_SCENE_NONE;
|
||
|
srv->transition.time = 0;
|
||
|
srv->transition.delay = 0;
|
||
|
/* We're checking srv->next in the handler, so failure to cancel is okay: */
|
||
|
(void)k_work_cancel_delayable(&srv->work);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
transition_time = bt_mesh_model_transition_time(transition);
|
||
|
if (transition_time) {
|
||
|
srv->transition = *transition;
|
||
|
srv->prev = curr;
|
||
|
srv->next = scene;
|
||
|
k_work_reschedule(&srv->work, K_MSEC(transition_time));
|
||
|
} else {
|
||
|
srv->prev = scene;
|
||
|
srv->next = BT_MESH_SCENE_NONE;
|
||
|
srv->transition.time = 0;
|
||
|
srv->transition.delay = 0;
|
||
|
/* We're checking srv->next in the handler, so failure to cancel is okay: */
|
||
|
(void)k_work_cancel_delayable(&srv->work);
|
||
|
}
|
||
|
|
||
|
sprintf(path, "bt/mesh/s/%x/data/%x",
|
||
|
(srv->model->rt->elem_idx << 8) | srv->model->rt->mod_idx, scene);
|
||
|
|
||
|
log_debug("Loading %s", path);
|
||
|
|
||
|
//for compiler, not used now.
|
||
|
// err = settings_load_subtree(path);
|
||
|
// if (!err) {
|
||
|
// scene_recall_complete(srv);
|
||
|
// }
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
int bt_mesh_scene_srv_pub(struct bt_mesh_scene_srv *srv,
|
||
|
struct bt_mesh_msg_ctx *ctx)
|
||
|
{
|
||
|
struct bt_mesh_scene_state state;
|
||
|
|
||
|
curr_scene_state_get(srv, &state);
|
||
|
return scene_status_send(srv->model, ctx, &state);
|
||
|
}
|
||
|
|
||
|
uint16_t bt_mesh_scene_srv_current_scene_get(const struct bt_mesh_scene_srv *srv)
|
||
|
{
|
||
|
return current_scene(srv);
|
||
|
}
|
||
|
|
||
|
uint16_t bt_mesh_scene_srv_target_scene_get(const struct bt_mesh_scene_srv *srv)
|
||
|
{
|
||
|
return target_scene(srv);
|
||
|
}
|
||
|
|
||
|
#endif /* CONFIG_BT_MESH_SCENE_SRV */
|