OVMS3-idf/components/bt/esp_ble_mesh/mesh_models/client/client_common.c
lly 04fc15a4f3 ble_mesh: Fix memory leak when node is reset
When node is being reset, the init functions of each sig-defined
models will be invoked again, this will cause memory leak because
some model internal data will be allocated again.
Hence before trying to allocate memory for them, we add some check
to make sure no memory has been allocated previously.
And for client model, when the init functions are invoked again,
we will clear the list items.
2020-02-12 12:03:31 +00:00

384 lines
11 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 "osi/allocator.h"
#include "osi/mutex.h"
#include "mesh_access.h"
#include "mesh_buf.h"
#include "mesh_slist.h"
#include "mesh_main.h"
#include "mesh.h"
#include "client_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;
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 *)osi_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__);
osi_free(node);
return -EINVAL;
}
if ((err = bt_mesh_model_send(model, ctx, msg, cb, cb_data)) != 0) {
osi_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 osi_mutex_t client_model_mutex;
static void bt_mesh_client_model_mutex_new(void)
{
static bool init;
if (!init) {
osi_mutex_new(&client_model_mutex);
init = true;
}
}
void bt_mesh_client_model_lock(void)
{
osi_mutex_lock(&client_model_mutex, OSI_MUTEX_MAX_TIMEOUT);
}
void bt_mesh_client_model_unlock(void)
{
osi_mutex_unlock(&client_model_mutex);
}
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 = osi_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_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
osi_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);
osi_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;
}