zephyr/subsys/bluetooth/host/mesh/friend.c

213 lines
4.6 KiB
C

/* Bluetooth Mesh */
/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <zephyr.h>
#include <misc/byteorder.h>
#include <net/buf.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/mesh.h>
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BLUETOOTH_MESH_DEBUG_FRIEND)
#include "common/log.h"
#include "crypto.h"
#include "adv.h"
#include "mesh.h"
#include "net.h"
#include "transport.h"
#include "access.h"
#include "friend.h"
static int send_friend_update(void)
{
struct bt_mesh_msg_ctx ctx = {
.net_idx = bt_mesh.sub[0].net_idx,
.app_idx = BT_MESH_KEY_UNUSED,
.addr = bt_mesh.frnd.lpn,
.send_ttl = 0,
.friend_cred = 1,
};
struct bt_mesh_net_tx tx = {
.sub = &bt_mesh.sub[0],
.ctx = &ctx,
.src = bt_mesh_primary_addr(),
};
struct bt_mesh_ctl_friend_update upd = {
.flags = 0,
.iv_index = sys_cpu_to_be32(bt_mesh.iv_index),
.md = !k_fifo_is_empty(&bt_mesh.frnd.queue),
};
BT_DBG("");
return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_UPDATE, &upd,
sizeof(upd), NULL);
}
int bt_mesh_friend_poll(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf)
{
struct bt_mesh_ctl_friend_poll *msg = (void *)buf->data;
struct bt_mesh_friend *frnd = &bt_mesh.frnd;
if (buf->len < sizeof(*msg)) {
BT_WARN("Too short Friend Update");
return -EINVAL;
}
BT_DBG("msg->fsn 0x%02x frnd->fsn 0x%02x", msg->fsn, frnd->fsn);
if (msg->fsn != frnd->fsn) {
frnd->send_last = 1;
}
frnd->fsn++;
frnd->send_update = 1;
k_delayed_work_submit(&frnd->timer, frnd->recv_delay);
return 0;
}
static int send_friend_offer(s8_t rssi)
{
struct bt_mesh_msg_ctx ctx = {
.net_idx = bt_mesh.sub[0].net_idx,
.app_idx = BT_MESH_KEY_UNUSED,
.addr = bt_mesh.frnd.lpn,
.send_ttl = 0,
};
struct bt_mesh_net_tx tx = {
.sub = &bt_mesh.sub[0],
.ctx = &ctx,
.src = bt_mesh_primary_addr(),
};
struct bt_mesh_ctl_friend_offer off = {
.recv_win = CONFIG_BLUETOOTH_MESH_FRIEND_RECV_WIN,
.queue_size = CONFIG_BLUETOOTH_MESH_FRIEND_QUEUE_SIZE,
.rssi = rssi,
.frnd_counter = bt_mesh.frnd.counter++,
};
BT_DBG("");
return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_OFFER, &off,
sizeof(off), NULL);
}
int bt_mesh_friend_req(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf)
{
struct bt_mesh_ctl_friend_req *msg = (void *)buf->data;
struct bt_mesh_friend *frnd = &bt_mesh.frnd;
struct bt_mesh_subnet *sub = rx->sub;
if (buf->len < sizeof(*msg)) {
BT_WARN("Too short Friend Request");
return -EINVAL;
}
frnd->lpn = rx->ctx.addr;
frnd->rssi = rx->rssi;
frnd->recv_delay = msg->recv_delay;
frnd->poll_to = (((u32_t)msg->poll_to[0] << 16) |
((u32_t)msg->poll_to[1] << 8) |
((u32_t)msg->poll_to[2]));
frnd->poll_to *= 100;
frnd->lpn_counter = sys_be16_to_cpu(msg->lpn_counter);
BT_DBG("LPN 0x%04x rssi %d recv_delay %u poll_to %ums",
frnd->lpn, frnd->rssi, frnd->recv_delay, frnd->poll_to);
bt_mesh_friend_cred_add(sub->net_idx, sub->keys[0].net, 0,
frnd->lpn, frnd->lpn_counter, frnd->counter);
frnd->send_offer = 1;
k_delayed_work_submit(&frnd->timer, K_MSEC(100));
return 0;
}
static void friend_timeout(struct k_work *work)
{
struct bt_mesh_friend *frnd = &bt_mesh.frnd;
BT_DBG("send_offer %u send_update %u", frnd->send_offer,
frnd->send_update);
if (frnd->send_offer) {
frnd->send_offer = 0;
send_friend_offer(frnd->rssi);
return;
}
if (!frnd->send_update) {
BT_WARN("Friendship lost");
bt_mesh_friend_cred_del(bt_mesh.sub[0].net_idx, frnd->lpn);
return;
}
frnd->send_update = 0;
if (frnd->send_last && frnd->last) {
frnd->send_last = 0;
bt_mesh_adv_send(frnd->last, NULL);
return;
}
if (frnd->last) {
net_buf_unref(frnd->last);
}
frnd->last = net_buf_get(&frnd->queue, K_NO_WAIT);
if (frnd->last) {
bt_mesh_adv_send(frnd->last, NULL);
} else {
send_friend_update();
}
k_delayed_work_submit(&frnd->timer, frnd->poll_to);
}
int bt_mesh_friend_init(void)
{
struct bt_mesh_friend *frnd = &bt_mesh.frnd;
k_fifo_init(&frnd->queue);
k_delayed_work_init(&frnd->timer, friend_timeout);
return 0;
}
bool bt_mesh_friend_enqueue(struct net_buf *buf, u16_t dst)
{
/* FIXME: Add support for multiple LPNs and group addresses */
if (!bt_mesh_friend_dst_is_lpn(dst)) {
return false;
}
if (BT_MESH_ADDR_IS_UNICAST(dst)) {
net_buf_put(&bt_mesh.frnd.queue, net_buf_ref(buf));
} else {
struct net_buf *clone = net_buf_clone(buf, K_NO_WAIT);
if (clone) {
net_buf_put(&bt_mesh.frnd.queue, clone);
} else {
BT_WARN("Unable to allocate buffer for friend queue");
return false;
}
}
BT_DBG("Queued message for LPN");
return true;
}