1. Add an API to set Provisioner static oob value 2. Add an API to deinit BLE Mesh stack 3. Add an API to set Provisioner unicast address 4. Add an API to provision devices with fixed address 5. Add an API to store node composition data 6. Add an API to get node with device uuid 7. Add an API to get node with unicast address 8. Add an API to delete node with device uuid 9. Add an API to delete node with unicast address 10. Add an API for Provisioner to update local AppKey 11. Add an API for Provisioner to update local NetKey 12. Support Provisioner persistent functionality 13. Fix Provisioner entering IV Update procedure 14. Fix an issue which may cause client failing to send msg 15. Use bt_mesh.flags to indicate device role 16. Remove several useless macros 17. Callback RSSI of received mesh provisioning packets 18. Modify the Provisioner disable function 19. Change some log level from debug to info 20. Add parameters to Provisioner bind AppKey completion event 21. Fix node ignoring relay messages issue 22. Support using a specific partition for BLE Mesh 23. Fix compile warning when proxy related macros are disabled 24. Clean up BLE Mesh stack included header files 25. NULL can be input if client message needs no parameters 26. Fix compile warning when BT log is disabled 27. Initilize BLE Mesh stack local variables 28. Support using PSRAM for BLE Mesh mutex, queue and task 29. Add a menuconfig option to enable using memory from PSRAM 30. Clean up sdkconfig.defaults of BLE Mesh examples
408 lines
12 KiB
C
408 lines
12 KiB
C
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#include "mesh.h"
|
|
#include "mesh_main.h"
|
|
#include "client_common.h"
|
|
#include "mesh_common.h"
|
|
|
|
static bt_mesh_client_node_t *bt_mesh_client_pick_node(sys_slist_t *list, u16_t tx_dst)
|
|
{
|
|
bt_mesh_client_node_t *node = NULL;
|
|
sys_snode_t *cur = NULL;
|
|
|
|
bt_mesh_list_lock();
|
|
if (sys_slist_is_empty(list)) {
|
|
bt_mesh_list_unlock();
|
|
return NULL;
|
|
}
|
|
|
|
for (cur = sys_slist_peek_head(list);
|
|
cur != NULL; cur = sys_slist_peek_next(cur)) {
|
|
node = (bt_mesh_client_node_t *)cur;
|
|
if (node->ctx.addr == tx_dst) {
|
|
bt_mesh_list_unlock();
|
|
return node;
|
|
}
|
|
}
|
|
|
|
bt_mesh_list_unlock();
|
|
return NULL;
|
|
}
|
|
|
|
bt_mesh_client_node_t *bt_mesh_is_client_recv_publish_msg(
|
|
struct bt_mesh_model *model,
|
|
struct bt_mesh_msg_ctx *ctx,
|
|
struct net_buf_simple *buf, bool need_pub)
|
|
{
|
|
bt_mesh_client_internal_data_t *data = NULL;
|
|
bt_mesh_client_user_data_t *cli = NULL;
|
|
bt_mesh_client_node_t *node = NULL;
|
|
|
|
if (!model || !ctx || !buf) {
|
|
BT_ERR("%s, Invalid parameter", __func__);
|
|
return NULL;
|
|
}
|
|
|
|
cli = (bt_mesh_client_user_data_t *)model->user_data;
|
|
if (!cli) {
|
|
BT_ERR("%s, Clinet user_data is NULL", __func__);
|
|
return NULL;
|
|
}
|
|
|
|
/** If the received message address is not a unicast address,
|
|
* the address may be a group/virtual address, and we push
|
|
* this message to the application layer.
|
|
*/
|
|
if (!BLE_MESH_ADDR_IS_UNICAST(ctx->recv_dst)) {
|
|
BT_DBG("Unexpected status message 0x%x", ctx->recv_op);
|
|
if (cli->publish_status && need_pub) {
|
|
cli->publish_status(ctx->recv_op, model, ctx, buf);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/** If the source address of the received status message is
|
|
* different with the destination address of the sending
|
|
* message, then the message is from another element and
|
|
* push it to application layer.
|
|
*/
|
|
data = (bt_mesh_client_internal_data_t *)cli->internal_data;
|
|
if (!data) {
|
|
BT_ERR("%s, Client internal_data is NULL", __func__);
|
|
return NULL;
|
|
}
|
|
|
|
if ((node = bt_mesh_client_pick_node(&data->queue, ctx->addr)) == NULL) {
|
|
BT_DBG("Unexpected status message 0x%x", ctx->recv_op);
|
|
if (cli->publish_status && need_pub) {
|
|
cli->publish_status(ctx->recv_op, model, ctx, buf);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
if (node->op_pending != ctx->recv_op) {
|
|
BT_DBG("Unexpected status message 0x%x", ctx->recv_op);
|
|
if (cli->publish_status && need_pub) {
|
|
cli->publish_status(ctx->recv_op, model, ctx, buf);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
if (k_delayed_work_remaining_get(&node->timer) == 0) {
|
|
BT_DBG("Unexpected status message 0x%x", ctx->recv_op);
|
|
if (cli->publish_status && need_pub) {
|
|
cli->publish_status(ctx->recv_op, model, ctx, buf);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
static bool bt_mesh_client_check_node_in_list(sys_slist_t *list, u16_t tx_dst)
|
|
{
|
|
bt_mesh_client_node_t *node = NULL;
|
|
sys_snode_t *cur = NULL;
|
|
|
|
bt_mesh_list_lock();
|
|
if (sys_slist_is_empty(list)) {
|
|
bt_mesh_list_unlock();
|
|
return false;
|
|
}
|
|
|
|
for (cur = sys_slist_peek_head(list);
|
|
cur != NULL; cur = sys_slist_peek_next(cur)) {
|
|
node = (bt_mesh_client_node_t *)cur;
|
|
if (node->ctx.addr == tx_dst) {
|
|
bt_mesh_list_unlock();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bt_mesh_list_unlock();
|
|
return false;
|
|
}
|
|
|
|
static u32_t bt_mesh_client_get_status_op(const bt_mesh_client_op_pair_t *op_pair,
|
|
int size, u32_t opcode)
|
|
{
|
|
if (!op_pair || size == 0) {
|
|
return 0;
|
|
}
|
|
|
|
const bt_mesh_client_op_pair_t *op = op_pair;
|
|
for (int i = 0; i < size; i++) {
|
|
if (op->cli_op == opcode) {
|
|
return op->status_op;
|
|
}
|
|
op++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_mesh_client_send_msg(struct bt_mesh_model *model,
|
|
u32_t opcode,
|
|
struct bt_mesh_msg_ctx *ctx,
|
|
struct net_buf_simple *msg,
|
|
k_work_handler_t timer_handler,
|
|
s32_t timeout, bool need_ack,
|
|
const struct bt_mesh_send_cb *cb,
|
|
void *cb_data)
|
|
{
|
|
bt_mesh_client_internal_data_t *internal = NULL;
|
|
bt_mesh_client_user_data_t *cli = NULL;
|
|
bt_mesh_client_node_t *node = NULL;
|
|
int err = 0;
|
|
|
|
if (!model || !ctx || !msg) {
|
|
BT_ERR("%s, Invalid parameter", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
cli = (bt_mesh_client_user_data_t *)model->user_data;
|
|
__ASSERT(cli, "Invalid client value when sent client msg.");
|
|
internal = (bt_mesh_client_internal_data_t *)cli->internal_data;
|
|
__ASSERT(internal, "Invalid internal value when sent client msg.");
|
|
|
|
if (!need_ack) {
|
|
/* If this is an unack message, send it directly. */
|
|
return bt_mesh_model_send(model, ctx, msg, cb, cb_data);
|
|
}
|
|
|
|
if (bt_mesh_client_check_node_in_list(&internal->queue, ctx->addr)) {
|
|
BT_ERR("%s, Busy sending message to DST 0x%04x", __func__, ctx->addr);
|
|
err = -EBUSY;
|
|
} else {
|
|
/* Don't forget to free the node in the timeout (timer_handler) function. */
|
|
node = (bt_mesh_client_node_t *)bt_mesh_calloc(sizeof(bt_mesh_client_node_t));
|
|
if (!node) {
|
|
BT_ERR("%s, Failed to allocate memory", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
memcpy(&node->ctx, ctx, sizeof(struct bt_mesh_msg_ctx));
|
|
node->ctx.model = model;
|
|
node->opcode = opcode;
|
|
if ((node->op_pending = bt_mesh_client_get_status_op(cli->op_pair, cli->op_pair_size, opcode)) == 0) {
|
|
BT_ERR("%s, Not found the status opcode in the op_pair list", __func__);
|
|
bt_mesh_free(node);
|
|
return -EINVAL;
|
|
}
|
|
if ((err = bt_mesh_model_send(model, ctx, msg, cb, cb_data)) != 0) {
|
|
bt_mesh_free(node);
|
|
} else {
|
|
bt_mesh_list_lock();
|
|
sys_slist_append(&internal->queue, &node->client_node);
|
|
bt_mesh_list_unlock();
|
|
k_delayed_work_init(&node->timer, timer_handler);
|
|
k_delayed_work_submit(&node->timer, timeout ? timeout : CONFIG_BLE_MESH_CLIENT_MSG_TIMEOUT);
|
|
}
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static bt_mesh_mutex_t client_model_lock;
|
|
|
|
static void bt_mesh_client_model_mutex_new(void)
|
|
{
|
|
if (!client_model_lock.mutex) {
|
|
bt_mesh_mutex_create(&client_model_lock);
|
|
}
|
|
}
|
|
|
|
static void bt_mesh_client_model_mutex_free(void)
|
|
{
|
|
bt_mesh_mutex_free(&client_model_lock);
|
|
}
|
|
|
|
void bt_mesh_client_model_lock(void)
|
|
{
|
|
bt_mesh_mutex_lock(&client_model_lock);
|
|
}
|
|
|
|
void bt_mesh_client_model_unlock(void)
|
|
{
|
|
bt_mesh_mutex_unlock(&client_model_lock);
|
|
}
|
|
|
|
int bt_mesh_client_init(struct bt_mesh_model *model)
|
|
{
|
|
bt_mesh_client_internal_data_t *data = NULL;
|
|
bt_mesh_client_user_data_t *cli = NULL;
|
|
|
|
if (!model) {
|
|
BT_ERR("%s, Invalid parameter", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!model->op) {
|
|
BT_ERR("%s, Client model op is NULL", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
cli = model->user_data;
|
|
if (!cli) {
|
|
BT_ERR("%s, Client user_data is NULL", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!cli->internal_data) {
|
|
data = bt_mesh_calloc(sizeof(bt_mesh_client_internal_data_t));
|
|
if (!data) {
|
|
BT_ERR("%s, Failed to allocate memory", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Init the client data queue */
|
|
sys_slist_init(&data->queue);
|
|
|
|
cli->model = model;
|
|
cli->internal_data = data;
|
|
} else {
|
|
bt_mesh_client_clear_list(cli->internal_data);
|
|
}
|
|
|
|
bt_mesh_client_model_mutex_new();
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_mesh_client_deinit(struct bt_mesh_model *model)
|
|
{
|
|
bt_mesh_client_user_data_t *client = NULL;
|
|
|
|
if (!model) {
|
|
BT_ERR("%s, Invalid parameter", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
client = (bt_mesh_client_user_data_t *)model->user_data;
|
|
if (!client) {
|
|
BT_ERR("%s, Client user_data is NULL", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (client->internal_data) {
|
|
/* Remove items from the list */
|
|
bt_mesh_client_clear_list(client->internal_data);
|
|
|
|
/* Free the allocated internal data */
|
|
bt_mesh_free(client->internal_data);
|
|
client->internal_data = NULL;
|
|
}
|
|
|
|
bt_mesh_client_model_mutex_free();
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_mesh_client_free_node(bt_mesh_client_node_t *node)
|
|
{
|
|
bt_mesh_client_internal_data_t *internal = NULL;
|
|
bt_mesh_client_user_data_t *client = NULL;
|
|
|
|
if (!node || !node->ctx.model) {
|
|
BT_ERR("%s, Client model list item is NULL", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
client = (bt_mesh_client_user_data_t *)node->ctx.model->user_data;
|
|
if (!client) {
|
|
BT_ERR("%s, Client model user data is NULL", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
internal = (bt_mesh_client_internal_data_t *)client->internal_data;
|
|
if (!internal) {
|
|
BT_ERR("%s, Client model internal data is NULL", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
// Release the client node from the queue
|
|
bt_mesh_list_lock();
|
|
sys_slist_find_and_remove(&internal->queue, &node->client_node);
|
|
bt_mesh_list_unlock();
|
|
// Free the node
|
|
bt_mesh_free(node);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_mesh_client_clear_list(void *data)
|
|
{
|
|
bt_mesh_client_internal_data_t *internal = NULL;
|
|
bt_mesh_client_node_t *node = NULL;
|
|
|
|
if (!data) {
|
|
BT_ERR("%s, Invalid parameter", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
internal = (bt_mesh_client_internal_data_t *)data;
|
|
|
|
bt_mesh_list_lock();
|
|
while (!sys_slist_is_empty(&internal->queue)) {
|
|
node = (void *)sys_slist_get_not_empty(&internal->queue);
|
|
bt_mesh_free(node);
|
|
}
|
|
bt_mesh_list_unlock();
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_mesh_set_client_model_role(bt_mesh_role_param_t *common)
|
|
{
|
|
bt_mesh_client_user_data_t *client = NULL;
|
|
|
|
if (!common || !common->model || !common->model->user_data) {
|
|
BT_ERR("%s, Invalid parameter", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
client = (bt_mesh_client_user_data_t *)common->model->user_data;
|
|
|
|
switch (common->role) {
|
|
#if CONFIG_BLE_MESH_NODE
|
|
case NODE:
|
|
/* no matter if provisioner is enabled/disabled , node role can be used to send messages */
|
|
client->msg_role = NODE;
|
|
break;
|
|
#endif
|
|
#if CONFIG_BLE_MESH_PROVISIONER
|
|
case PROVISIONER:
|
|
/* if provisioner is not enabled, provisioner role can't be used to send messages */
|
|
if (!bt_mesh_is_provisioner_en()) {
|
|
BT_ERR("%s, Provisioner is disabled", __func__);
|
|
return -EINVAL;
|
|
}
|
|
client->msg_role = PROVISIONER;
|
|
break;
|
|
#endif
|
|
#if CONFIG_BLE_MESH_FAST_PROV
|
|
case FAST_PROV:
|
|
client->msg_role = FAST_PROV;
|
|
break;
|
|
#endif
|
|
default:
|
|
BT_WARN("%s, Unknown model role %x", __func__, common->role);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|