Bluetooth: Mesh: Implement the Friend Clear procedure

When a Friend Node receives a Friend Request with a unicast
PreviousAddress that's not an element on the Friend Node, it needs to
start the Friend Clear procedure. This procedure involves sending
periodic Friend Clear messages to the old Friend of the LPN.

Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
This commit is contained in:
Johan Hedberg 2017-11-09 23:45:40 +02:00 committed by Johan Hedberg
commit ed8ed9ea7d
4 changed files with 152 additions and 6 deletions

View file

@ -550,10 +550,137 @@ int bt_mesh_friend_poll(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf)
return 0;
}
static void send_friend_clear(u16_t addr)
static struct bt_mesh_friend *find_clear(u16_t prev_friend)
{
/* TODO: Send Friend Clear message to the old Friend */
BT_WARN("Sending Friend Clear not yet implemented");
int i;
for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
struct bt_mesh_friend *frnd = &bt_mesh.frnd[i];
if (frnd->clear.frnd == prev_friend) {
return frnd;
}
}
return NULL;
}
static void friend_clear_sent(struct net_buf *buf, u16_t duration, int err)
{
struct bt_mesh_friend *frnd;
BT_DBG("addr 0x%02x", BT_MESH_ADV(buf)->addr);
frnd = find_clear(BT_MESH_ADV(buf)->addr);
if (!frnd) {
BT_WARN("No matching clear procedure found for 0x%02x",
BT_MESH_ADV(buf)->addr);
return;
}
k_delayed_work_submit(&frnd->clear.timer,
duration + K_SECONDS(frnd->clear.repeat_sec));
frnd->clear.repeat_sec *= 2;
}
static void send_friend_clear(struct bt_mesh_friend *frnd)
{
struct bt_mesh_msg_ctx ctx = {
.net_idx = frnd->net_idx,
.app_idx = BT_MESH_KEY_UNUSED,
.addr = frnd->clear.frnd,
.send_ttl = BT_MESH_TTL_MAX,
};
struct bt_mesh_net_tx tx = {
.sub = &bt_mesh.sub[0],
.ctx = &ctx,
.src = bt_mesh_primary_addr(),
.xmit = bt_mesh_net_transmit_get(),
};
struct bt_mesh_ctl_friend_clear req = {
.lpn_addr = sys_cpu_to_be16(frnd->lpn),
.lpn_counter = sys_cpu_to_be16(frnd->lpn_counter),
};
BT_DBG("");
bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_CLEAR, &req,
sizeof(req), NULL, friend_clear_sent);
}
static void clear_timeout(struct k_work *work)
{
struct bt_mesh_friend *frnd = CONTAINER_OF(work, struct bt_mesh_friend,
clear.timer.work);
u32_t now, duration;
BT_DBG("LPN 0x%04x (old) Friend 0x%04x", frnd->lpn, frnd->clear.frnd);
now = k_uptime_get_32();
/* Handle time wrap-around due to 32-bit limit */
if (now < frnd->clear.start) {
duration = (UINT32_MAX - frnd->clear.start) + now;
} else {
duration = now - frnd->clear.start;
}
if (duration > 2 * frnd->poll_to) {
BT_DBG("Clear Procedure timer expired");
frnd->clear.frnd = BT_MESH_ADDR_UNASSIGNED;
return;
}
send_friend_clear(frnd);
}
static void clear_procedure_start(struct bt_mesh_friend *frnd)
{
BT_DBG("LPN 0x%04x (old) Friend 0x%04x", frnd->lpn, frnd->clear.frnd);
frnd->clear.start = k_uptime_get_32() + (2 * frnd->poll_to);
frnd->clear.repeat_sec = 1;
send_friend_clear(frnd);
}
int bt_mesh_friend_clear_cfm(struct bt_mesh_net_rx *rx,
struct net_buf_simple *buf)
{
struct bt_mesh_ctl_friend_clear_confirm *msg = (void *)buf->data;
struct bt_mesh_friend *frnd;
u16_t lpn_addr, lpn_counter;
BT_DBG("");
if (buf->len < sizeof(*msg)) {
BT_WARN("Too short Friend Clear Confirm");
return -EINVAL;
}
frnd = find_clear(rx->ctx.addr);
if (!frnd) {
BT_WARN("No pending clear procedure for 0x%02x", rx->ctx.addr);
return 0;
}
lpn_addr = sys_be16_to_cpu(msg->lpn_addr);
if (lpn_addr != frnd->lpn) {
BT_WARN("LPN address mismatch (0x%04x != 0x%04x)",
lpn_addr, frnd->lpn);
return 0;
}
lpn_counter = sys_be16_to_cpu(msg->lpn_counter);
if (lpn_counter != frnd->lpn_counter) {
BT_WARN("LPN counter mismatch (0x%04x != 0x%04x)",
lpn_counter, frnd->lpn_counter);
return 0;
}
k_delayed_work_cancel(&frnd->clear.timer);
frnd->clear.frnd = BT_MESH_ADDR_UNASSIGNED;
return 0;
}
static void enqueue_offer(struct bt_mesh_friend *frnd, s8_t rssi)
@ -674,8 +801,6 @@ int bt_mesh_friend_req(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf)
BT_WARN("Existing LPN re-requesting Friendship");
friend_clear(frnd);
goto init_friend;
} else if (BT_MESH_ADDR_IS_UNICAST(old_friend)) {
send_friend_clear(old_friend);
}
for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
@ -696,10 +821,16 @@ init_friend:
frnd->recv_delay = msg->recv_delay;
frnd->poll_to = poll_to * 100;
frnd->lpn_counter = sys_be16_to_cpu(msg->lpn_counter);
frnd->clear.frnd = sys_be16_to_cpu(msg->prev_addr);
BT_DBG("LPN 0x%04x rssi %d recv_delay %u poll_to %ums",
frnd->lpn, rx->rssi, frnd->recv_delay, frnd->poll_to);
if (BT_MESH_ADDR_IS_UNICAST(old_friend) &&
!bt_mesh_elem_find(old_friend)) {
clear_procedure_start(frnd);
}
k_delayed_work_submit(&frnd->timer,
offer_delay(frnd, rx->rssi, msg->criteria));
@ -866,6 +997,7 @@ int bt_mesh_friend_init(void)
sys_slist_init(&frnd->queue);
k_delayed_work_init(&frnd->timer, friend_timeout);
k_delayed_work_init(&frnd->clear.timer, clear_timeout);
for (j = 0; j < ARRAY_SIZE(frnd->seg); j++) {
sys_slist_init(&frnd->seg[j].queue);

View file

@ -34,6 +34,8 @@ void bt_mesh_friend_clear_net_idx(u16_t net_idx);
int bt_mesh_friend_poll(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf);
int bt_mesh_friend_req(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf);
int bt_mesh_friend_clear(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf);
int bt_mesh_friend_clear_cfm(struct bt_mesh_net_rx *rx,
struct net_buf_simple *buf);
int bt_mesh_friend_sub_add(struct bt_mesh_net_rx *rx,
struct net_buf_simple *buf);
int bt_mesh_friend_sub_rem(struct bt_mesh_net_rx *rx,

View file

@ -113,6 +113,14 @@ struct bt_mesh_friend {
sys_slist_t queue;
u32_t queue_size;
/* Friend Clear Procedure */
struct {
u32_t start; /* Clear Procedure start */
u16_t frnd; /* Previous Friend's address */
u16_t repeat_sec; /* Repeat timeout in seconds */
struct k_delayed_work timer; /* Repeat timer */
} clear;
};
#if defined(CONFIG_BT_MESH_LOW_POWER)

View file

@ -722,7 +722,7 @@ static int ctl_recv(struct bt_mesh_net_rx *rx, u8_t hdr,
return 0;
}
if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) {
if (IS_ENABLED(CONFIG_BT_MESH_FRIEND) && !bt_mesh_lpn_established()) {
switch (ctl_op) {
case TRANS_CTL_OP_FRIEND_POLL:
return bt_mesh_friend_poll(rx, buf);
@ -730,6 +730,8 @@ static int ctl_recv(struct bt_mesh_net_rx *rx, u8_t hdr,
return bt_mesh_friend_req(rx, buf);
case TRANS_CTL_OP_FRIEND_CLEAR:
return bt_mesh_friend_clear(rx, buf);
case TRANS_CTL_OP_FRIEND_CLEAR_CFM:
return bt_mesh_friend_clear_cfm(rx, buf);
case TRANS_CTL_OP_FRIEND_SUB_ADD:
return bt_mesh_friend_sub_add(rx, buf);
case TRANS_CTL_OP_FRIEND_SUB_REM:
@ -832,6 +834,8 @@ int bt_mesh_ctl_send(struct bt_mesh_net_tx *tx, u8_t ctl_op, void *data,
return -ENOBUFS;
}
BT_MESH_ADV(buf)->addr = tx->ctx->addr;
net_buf_reserve(buf, BT_MESH_NET_HDR_LEN);
net_buf_add_u8(buf, TRANS_CTL_HDR(ctl_op, 0));