/* Bluetooth Mesh */ /* * Copyright (c) 2017 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #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; }