| /* 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_BT_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_BT_MESH_FRIEND_RECV_WIN, |
| .queue_size = CONFIG_BT_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; |
| } |