| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138 |
- /* Bluetooth Mesh */
- /*
- * SPDX-FileCopyrightText: 2017 Intel Corporation
- *
- * SPDX-License-Identifier: Apache-2.0
- */
- #include <stdint.h>
- #include <errno.h>
- #include "crypto.h"
- #include "adv.h"
- #include "scan.h"
- #include "mesh.h"
- #include "transport.h"
- #include "access.h"
- #include "beacon.h"
- #include "lpn.h"
- #include "rpl.h"
- #include "foundation.h"
- #include "mesh/main.h"
- #include "mesh/cfg_srv.h"
- #include "heartbeat.h"
- #include "mesh_v1.1/utils.h"
- #ifdef CONFIG_BLE_MESH_LOW_POWER
- #if CONFIG_BLE_MESH_LPN_AUTO
- #define LPN_AUTO_TIMEOUT K_SECONDS(CONFIG_BLE_MESH_LPN_AUTO_TIMEOUT)
- #else
- #define LPN_AUTO_TIMEOUT 0
- #endif
- #define LPN_RECV_DELAY CONFIG_BLE_MESH_LPN_RECV_DELAY
- #define SCAN_LATENCY MIN(CONFIG_BLE_MESH_LPN_SCAN_LATENCY, LPN_RECV_DELAY)
- #define FRIEND_REQ_RETRY_TIMEOUT K_SECONDS(CONFIG_BLE_MESH_LPN_RETRY_TIMEOUT)
- #define FRIEND_REQ_WAIT K_MSEC(100)
- #define FRIEND_REQ_SCAN K_SECONDS(1)
- #define FRIEND_REQ_TIMEOUT (FRIEND_REQ_WAIT + FRIEND_REQ_SCAN)
- #define POLL_RETRY_TIMEOUT K_MSEC(100)
- #define REQ_RETRY_DURATION(lpn) (4 * (LPN_RECV_DELAY + (lpn)->adv_duration + \
- (lpn)->recv_win + POLL_RETRY_TIMEOUT))
- #define POLL_TIMEOUT_INIT (CONFIG_BLE_MESH_LPN_INIT_POLL_TIMEOUT * 100)
- #define POLL_TIMEOUT_MAX(lpn) ((CONFIG_BLE_MESH_LPN_POLL_TIMEOUT * 100) - \
- REQ_RETRY_DURATION(lpn))
- /**
- * 1. Should use 20 attempts for BQB test case MESH/NODE/FRND/LPM/BI-02-C.
- * 2. We should use more specific value for each PollTimeout range.
- */
- #define REQ_ATTEMPTS(lpn) (POLL_TIMEOUT_MAX(lpn) < K_SECONDS(3) ? 2 : 6)
- #define CLEAR_ATTEMPTS 2
- #define LPN_CRITERIA ((CONFIG_BLE_MESH_LPN_MIN_QUEUE_SIZE) | \
- (CONFIG_BLE_MESH_LPN_RSSI_FACTOR << 3) | \
- (CONFIG_BLE_MESH_LPN_RECV_WIN_FACTOR << 5))
- #define POLL_TO(to) { (uint8_t)((to) >> 16), (uint8_t)((to) >> 8), (uint8_t)(to) }
- #define LPN_POLL_TO POLL_TO(CONFIG_BLE_MESH_LPN_POLL_TIMEOUT)
- /* 2 transmissions, 20ms interval */
- #define POLL_XMIT BLE_MESH_TRANSMIT(1, 20)
- #define FIRST_POLL_ATTEMPTS 6
- static void (*lpn_cb)(uint16_t friend_addr, bool established);
- #if !CONFIG_BLE_MESH_NO_LOG
- static const char *state2str(int state)
- {
- switch (state) {
- case BLE_MESH_LPN_DISABLED:
- return "disabled";
- case BLE_MESH_LPN_CLEAR:
- return "clear";
- case BLE_MESH_LPN_TIMER:
- return "timer";
- case BLE_MESH_LPN_ENABLED:
- return "enabled";
- case BLE_MESH_LPN_REQ_WAIT:
- return "req wait";
- case BLE_MESH_LPN_WAIT_OFFER:
- return "wait offer";
- case BLE_MESH_LPN_ESTABLISHED:
- return "established";
- case BLE_MESH_LPN_RECV_DELAY:
- return "recv delay";
- case BLE_MESH_LPN_WAIT_UPDATE:
- return "wait update";
- case BLE_MESH_LPN_OFFER_RECV:
- return "offer recv";
- default:
- return "(unknown)";
- }
- }
- #endif
- static inline void lpn_set_state(int state)
- {
- BT_DBG("%s -> %s", state2str(bt_mesh.lpn.state), state2str(state));
- bt_mesh.lpn.state = state;
- }
- static inline void group_zero(bt_mesh_atomic_t *target)
- {
- #if CONFIG_BLE_MESH_LPN_GROUPS > 32
- int i;
- for (i = 0; i < ARRAY_SIZE(bt_mesh.lpn.added); i++) {
- bt_mesh_atomic_set(&target[i], 0);
- }
- #else
- bt_mesh_atomic_set(target, 0);
- #endif
- }
- static inline void group_set(bt_mesh_atomic_t *target, bt_mesh_atomic_t *source)
- {
- #if CONFIG_BLE_MESH_LPN_GROUPS > 32
- int i;
- for (i = 0; i < ARRAY_SIZE(bt_mesh.lpn.added); i++) {
- (void)bt_mesh_atomic_or(&target[i], bt_mesh_atomic_get(&source[i]));
- }
- #else
- (void)bt_mesh_atomic_or(target, bt_mesh_atomic_get(source));
- #endif
- }
- static inline void group_clear(bt_mesh_atomic_t *target, bt_mesh_atomic_t *source)
- {
- #if CONFIG_BLE_MESH_LPN_GROUPS > 32
- int i;
- for (i = 0; i < ARRAY_SIZE(bt_mesh.lpn.added); i++) {
- (void)bt_mesh_atomic_and(&target[i], ~bt_mesh_atomic_get(&source[i]));
- }
- #else
- (void)bt_mesh_atomic_and(target, ~bt_mesh_atomic_get(source));
- #endif
- }
- static void clear_friendship(bool force, bool disable);
- static bool scan_after_clear;
- static void friend_clear_sent(int err, void *user_data)
- {
- struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
- /* We're switching away from Low Power behavior, so permanently
- * enable scanning.
- */
- if (scan_after_clear == false) {
- bt_mesh_scan_enable();
- scan_after_clear = true;
- }
- lpn->req_attempts++;
- if (err) {
- BT_ERR("Sending Friend Clear failed (err %d)", err);
- lpn_set_state(BLE_MESH_LPN_ENABLED);
- clear_friendship(false, lpn->disable);
- return;
- }
- lpn_set_state(BLE_MESH_LPN_CLEAR);
- k_delayed_work_submit(&lpn->timer, FRIEND_REQ_TIMEOUT);
- }
- static const struct bt_mesh_send_cb clear_sent_cb = {
- .end = friend_clear_sent,
- };
- static int send_friend_clear(void)
- {
- struct bt_mesh_msg_ctx ctx = {
- .net_idx = bt_mesh.sub[0].net_idx,
- .app_idx = BLE_MESH_KEY_UNUSED,
- .addr = bt_mesh.lpn.frnd,
- .send_ttl = 0,
- .send_cred = BLE_MESH_FLOODING_CRED,
- /* Tag with immutable-credentials */
- .send_tag = BLE_MESH_TAG_IMMUTABLE_CRED,
- };
- 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(tx.src),
- .lpn_counter = sys_cpu_to_be16(bt_mesh.lpn.counter),
- };
- return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_CLEAR, &req,
- sizeof(req), &clear_sent_cb, NULL);
- }
- static void clear_friendship(bool force, bool disable)
- {
- struct bt_mesh_cfg_srv *cfg = bt_mesh_cfg_get();
- struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
- BT_DBG("force %u disable %u", force, disable);
- if (!force && lpn->established && !lpn->clear_success &&
- lpn->req_attempts < CLEAR_ATTEMPTS) {
- send_friend_clear();
- lpn->disable = disable;
- return;
- }
- bt_mesh_rx_reset();
- bt_mesh_rpl_reset(true);
- k_delayed_work_cancel(&lpn->timer);
- friend_cred_del(bt_mesh.sub[0].net_idx, lpn->frnd);
- if (lpn->clear_success) {
- lpn->old_friend = BLE_MESH_ADDR_UNASSIGNED;
- } else {
- lpn->old_friend = lpn->frnd;
- }
- if (lpn_cb && lpn->frnd != BLE_MESH_ADDR_UNASSIGNED) {
- lpn_cb(lpn->frnd, false);
- }
- /* If the Low Power node supports directed forwarding functionality when
- * the friendship is established in a subnet, the Low Power node shall
- * store the current value of the Directed Forwarding state and shall set
- * the state to 0x00 for that subnet. When that friendship is terminated,
- * the Low Power node shall set the Directed Forwarding state to the stored
- * value.
- */
- #if CONFIG_BLE_MESH_DF_SRV
- if (lpn->established) {
- bt_mesh_restore_directed_forwarding_state(bt_mesh.sub[0].net_idx,
- lpn->old_directed_forwarding);
- }
- #endif
- lpn->frnd = BLE_MESH_ADDR_UNASSIGNED;
- lpn->fsn = 0U;
- lpn->req_attempts = 0U;
- lpn->recv_win = 0U;
- lpn->queue_size = 0U;
- lpn->disable = 0U;
- lpn->sent_req = 0U;
- lpn->established = 0U;
- lpn->clear_success = 0U;
- group_zero(lpn->added);
- group_zero(lpn->pending);
- group_zero(lpn->to_remove);
- /* Set this to 1 to force group subscription when the next
- * Friendship is created, in case lpn->groups doesn't get
- * modified meanwhile.
- */
- lpn->groups_changed = 1U;
- if (cfg->hb_pub.feat & BLE_MESH_FEAT_LOW_POWER) {
- bt_mesh_heartbeat_send();
- }
- if (disable) {
- lpn_set_state(BLE_MESH_LPN_DISABLED);
- return;
- }
- lpn_set_state(BLE_MESH_LPN_ENABLED);
- k_delayed_work_submit(&lpn->timer, FRIEND_REQ_RETRY_TIMEOUT);
- scan_after_clear = false;
- if (IS_ENABLED(CONFIG_BLE_MESH_LPN_ESTABLISHMENT)) {
- bt_mesh_scan_disable();
- }
- }
- static void friend_req_sent(uint16_t duration, int err, void *user_data)
- {
- struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
- if (err) {
- BT_ERR("Sending Friend Request failed (err %d)", err);
- if (IS_ENABLED(CONFIG_BLE_MESH_LPN_ESTABLISHMENT)) {
- bt_mesh_scan_enable();
- }
- return;
- }
- lpn->adv_duration = duration;
- if (IS_ENABLED(CONFIG_BLE_MESH_LPN_ESTABLISHMENT)) {
- k_delayed_work_submit(&lpn->timer, FRIEND_REQ_WAIT);
- lpn_set_state(BLE_MESH_LPN_REQ_WAIT);
- } else {
- k_delayed_work_submit(&lpn->timer,
- duration + FRIEND_REQ_TIMEOUT);
- lpn_set_state(BLE_MESH_LPN_WAIT_OFFER);
- }
- }
- static const struct bt_mesh_send_cb friend_req_sent_cb = {
- .start = friend_req_sent,
- };
- static int send_friend_req(struct bt_mesh_lpn *lpn)
- {
- const struct bt_mesh_comp *comp = bt_mesh_comp_get();
- struct bt_mesh_msg_ctx ctx = {
- .net_idx = bt_mesh.sub[0].net_idx,
- .app_idx = BLE_MESH_KEY_UNUSED,
- .addr = BLE_MESH_ADDR_FRIENDS,
- .send_ttl = 0,
- .send_cred = BLE_MESH_FLOODING_CRED,
- /* Tag with immutable-credentials and as a friendship PDU */
- .send_tag = BLE_MESH_TAG_IMMUTABLE_CRED | BLE_MESH_TAG_FRIENDSHIP,
- };
- struct bt_mesh_net_tx tx = {
- .sub = &bt_mesh.sub[0],
- .ctx = &ctx,
- .src = bt_mesh_primary_addr(),
- .xmit = POLL_XMIT,
- };
- struct bt_mesh_ctl_friend_req req = {
- .criteria = LPN_CRITERIA,
- .recv_delay = LPN_RECV_DELAY,
- .poll_to = LPN_POLL_TO,
- .prev_addr = sys_cpu_to_be16(lpn->old_friend),
- .num_elem = comp->elem_count,
- .lpn_counter = sys_cpu_to_be16(lpn->counter),
- };
- return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_REQ, &req,
- sizeof(req), &friend_req_sent_cb, NULL);
- }
- static void req_sent(uint16_t duration, int err, void *user_data)
- {
- struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
- BT_DBG("req 0x%02x duration %u err %d state %s",
- lpn->sent_req, duration, err, state2str(lpn->state));
- if (err) {
- BT_ERR("Sending request failed (err %d)", err);
- lpn->sent_req = 0U;
- group_zero(lpn->pending);
- return;
- }
- lpn->req_attempts++;
- lpn->adv_duration = duration;
- if (lpn->established || IS_ENABLED(CONFIG_BLE_MESH_LPN_ESTABLISHMENT)) {
- lpn_set_state(BLE_MESH_LPN_RECV_DELAY);
- /* We start scanning a bit early to eliminate risk of missing
- * response data due to HCI and other latencies.
- */
- k_delayed_work_submit(&lpn->timer,
- LPN_RECV_DELAY - SCAN_LATENCY);
- } else {
- lpn_set_state(BLE_MESH_LPN_OFFER_RECV);
- /**
- * Friend Update is replied by Friend Node with TTL set to 0 and Network
- * Transmit set to 30ms which will cause the packet easy to be missed.
- * Regarding this situation, here we can reduce the duration of receiving
- * the first Friend Update.
- */
- k_delayed_work_submit(&lpn->timer,
- LPN_RECV_DELAY + duration +
- lpn->recv_win);
- }
- }
- static const struct bt_mesh_send_cb req_sent_cb = {
- .start = req_sent,
- };
- static int send_friend_poll(void)
- {
- struct bt_mesh_msg_ctx ctx = {
- .net_idx = bt_mesh.sub[0].net_idx,
- .app_idx = BLE_MESH_KEY_UNUSED,
- .addr = bt_mesh.lpn.frnd,
- .send_ttl = 0,
- .send_cred = BLE_MESH_FRIENDSHIP_CRED,
- /* Tag with immutable-credentials and as a friendship PDU */
- .send_tag = BLE_MESH_TAG_IMMUTABLE_CRED | BLE_MESH_TAG_FRIENDSHIP,
- };
- struct bt_mesh_net_tx tx = {
- .sub = &bt_mesh.sub[0],
- .ctx = &ctx,
- .src = bt_mesh_primary_addr(),
- .xmit = POLL_XMIT,
- };
- struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
- uint8_t fsn = lpn->fsn;
- int err = 0;
- BT_DBG("lpn->sent_req 0x%02x", lpn->sent_req);
- if (lpn->sent_req) {
- if (lpn->sent_req != TRANS_CTL_OP_FRIEND_POLL) {
- lpn->pending_poll = 1U;
- }
- return 0;
- }
- err = bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_POLL, &fsn, 1,
- &req_sent_cb, NULL);
- if (err == 0) {
- lpn->pending_poll = 0U;
- lpn->sent_req = TRANS_CTL_OP_FRIEND_POLL;
- }
- return err;
- }
- void bt_mesh_lpn_disable(bool force)
- {
- if (bt_mesh.lpn.state == BLE_MESH_LPN_DISABLED) {
- return;
- }
- clear_friendship(force, true);
- }
- int bt_mesh_lpn_set(bool enable, bool force)
- {
- struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
- if (enable) {
- if (lpn->state != BLE_MESH_LPN_DISABLED) {
- return 0;
- }
- } else {
- if (lpn->state == BLE_MESH_LPN_DISABLED) {
- return 0;
- }
- }
- if (!bt_mesh_is_provisioned()) {
- if (enable) {
- lpn_set_state(BLE_MESH_LPN_ENABLED);
- } else {
- lpn_set_state(BLE_MESH_LPN_DISABLED);
- }
- return 0;
- }
- if (enable) {
- lpn_set_state(BLE_MESH_LPN_ENABLED);
- if (IS_ENABLED(CONFIG_BLE_MESH_LPN_ESTABLISHMENT)) {
- bt_mesh_scan_disable();
- }
- send_friend_req(lpn);
- } else {
- if (IS_ENABLED(CONFIG_BLE_MESH_LPN_AUTO) &&
- lpn->state == BLE_MESH_LPN_TIMER) {
- k_delayed_work_cancel(&lpn->timer);
- lpn_set_state(BLE_MESH_LPN_DISABLED);
- } else {
- bt_mesh_lpn_disable(force);
- }
- }
- return 0;
- }
- static void friend_response_received(struct bt_mesh_lpn *lpn)
- {
- BT_DBG("lpn->sent_req 0x%02x", lpn->sent_req);
- if (lpn->sent_req == TRANS_CTL_OP_FRIEND_POLL) {
- lpn->fsn++;
- }
- k_delayed_work_cancel(&lpn->timer);
- bt_mesh_scan_disable();
- lpn_set_state(BLE_MESH_LPN_ESTABLISHED);
- lpn->req_attempts = 0U;
- lpn->sent_req = 0U;
- }
- void bt_mesh_lpn_msg_received(struct bt_mesh_net_rx *rx)
- {
- struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
- if (lpn->state == BLE_MESH_LPN_TIMER) {
- BT_DBG("Restarting establishment timer");
- k_delayed_work_submit(&lpn->timer, LPN_AUTO_TIMEOUT);
- return;
- }
- if (lpn->sent_req != TRANS_CTL_OP_FRIEND_POLL) {
- BT_WARN("Unexpected message without a preceding Poll");
- return;
- }
- friend_response_received(lpn);
- BT_DBG("Requesting more messages from Friend");
- send_friend_poll();
- }
- int bt_mesh_lpn_friend_offer(struct bt_mesh_net_rx *rx,
- struct net_buf_simple *buf)
- {
- struct bt_mesh_ctl_friend_offer *msg = (void *)buf->data;
- struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
- struct bt_mesh_subnet *sub = rx->sub;
- struct friend_cred *cred = NULL;
- uint16_t frnd_counter = 0U;
- int err = 0;
- if (buf->len < sizeof(*msg)) {
- BT_WARN("Too short Friend Offer");
- return -EINVAL;
- }
- if (lpn->state != BLE_MESH_LPN_WAIT_OFFER) {
- BT_WARN("Ignoring unexpected Friend Offer");
- return 0;
- }
- if (!msg->recv_win) {
- BT_WARN("Prohibited ReceiveWindow value");
- return -EINVAL;
- }
- frnd_counter = sys_be16_to_cpu(msg->frnd_counter);
- BT_INFO("recv_win %u queue_size %u sub_list_size %u rssi %d counter %u",
- msg->recv_win, msg->queue_size, msg->sub_list_size, msg->rssi,
- frnd_counter);
- lpn->frnd = rx->ctx.addr;
- cred = friend_cred_create(sub, lpn->frnd, lpn->counter, frnd_counter);
- if (!cred) {
- lpn->frnd = BLE_MESH_ADDR_UNASSIGNED;
- return -ENOMEM;
- }
- /* TODO: Add offer acceptance criteria check */
- k_delayed_work_cancel(&lpn->timer);
- lpn->recv_win = msg->recv_win;
- lpn->queue_size = msg->queue_size;
- err = send_friend_poll();
- if (err) {
- friend_cred_clear(cred);
- lpn->frnd = BLE_MESH_ADDR_UNASSIGNED;
- lpn->recv_win = 0U;
- lpn->queue_size = 0U;
- return err;
- }
- lpn->counter++;
- if (IS_ENABLED(CONFIG_BLE_MESH_LPN_ESTABLISHMENT)) {
- bt_mesh_scan_disable();
- }
- return 0;
- }
- int bt_mesh_lpn_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_lpn *lpn = &bt_mesh.lpn;
- uint16_t addr = 0U, counter = 0U;
- if (buf->len < sizeof(*msg)) {
- BT_WARN("Too short Friend Clear Confirm");
- return -EINVAL;
- }
- if (lpn->state != BLE_MESH_LPN_CLEAR) {
- BT_WARN("Ignoring unexpected Friend Clear Confirm");
- return 0;
- }
- addr = sys_be16_to_cpu(msg->lpn_addr);
- counter = sys_be16_to_cpu(msg->lpn_counter);
- BT_DBG("LPNAddress 0x%04x LPNCounter 0x%04x", addr, counter);
- if (addr != bt_mesh_primary_addr() || counter != lpn->counter) {
- BT_WARN("Invalid parameters in Friend Clear Confirm");
- return 0;
- }
- lpn->clear_success = 1U;
- clear_friendship(false, lpn->disable);
- return 0;
- }
- static void lpn_group_add(uint16_t group)
- {
- struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
- uint16_t *free_slot = NULL;
- int i;
- for (i = 0; i < ARRAY_SIZE(lpn->groups); i++) {
- if (lpn->groups[i] == group) {
- bt_mesh_atomic_clear_bit(lpn->to_remove, i);
- return;
- }
- if (!free_slot && lpn->groups[i] == BLE_MESH_ADDR_UNASSIGNED) {
- free_slot = &lpn->groups[i];
- }
- }
- if (!free_slot) {
- BT_WARN("Friend Subscription List exceeded!");
- return;
- }
- *free_slot = group;
- lpn->groups_changed = 1U;
- }
- static void lpn_group_del(uint16_t group)
- {
- struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
- int i;
- for (i = 0; i < ARRAY_SIZE(lpn->groups); i++) {
- if (lpn->groups[i] == group) {
- if (bt_mesh_atomic_test_bit(lpn->added, i) ||
- bt_mesh_atomic_test_bit(lpn->pending, i)) {
- bt_mesh_atomic_set_bit(lpn->to_remove, i);
- lpn->groups_changed = 1U;
- } else {
- lpn->groups[i] = BLE_MESH_ADDR_UNASSIGNED;
- }
- }
- }
- }
- static inline int group_popcount(bt_mesh_atomic_t *target)
- {
- #if CONFIG_BLE_MESH_LPN_GROUPS > 32
- int i, count = 0;
- for (i = 0; i < ARRAY_SIZE(bt_mesh.lpn.added); i++) {
- count += popcount(bt_mesh_atomic_get(&target[i]));
- }
- #else
- return popcount(bt_mesh_atomic_get(target));
- #endif
- }
- static bool sub_update(uint8_t op)
- {
- struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
- int added_count = group_popcount(lpn->added);
- struct bt_mesh_msg_ctx ctx = {
- .net_idx = bt_mesh.sub[0].net_idx,
- .app_idx = BLE_MESH_KEY_UNUSED,
- .addr = lpn->frnd,
- .send_ttl = 0,
- .send_cred = BLE_MESH_FRIENDSHIP_CRED,
- /* Tag with immutable-credentials and as a friendship PDU */
- .send_tag = BLE_MESH_TAG_IMMUTABLE_CRED | BLE_MESH_TAG_FRIENDSHIP,
- };
- struct bt_mesh_net_tx tx = {
- .sub = &bt_mesh.sub[0],
- .ctx = &ctx,
- .src = bt_mesh_primary_addr(),
- .xmit = POLL_XMIT,
- };
- struct bt_mesh_ctl_friend_sub req = {0};
- size_t i = 0U, g = 0U;
- BT_DBG("op 0x%02x sent_req 0x%02x", op, lpn->sent_req);
- if (lpn->sent_req) {
- return false;
- }
- for (i = 0U, g = 0U; i < ARRAY_SIZE(lpn->groups); i++) {
- if (lpn->groups[i] == BLE_MESH_ADDR_UNASSIGNED) {
- continue;
- }
- if (op == TRANS_CTL_OP_FRIEND_SUB_ADD) {
- if (bt_mesh_atomic_test_bit(lpn->added, i)) {
- continue;
- }
- } else {
- if (!bt_mesh_atomic_test_bit(lpn->to_remove, i)) {
- continue;
- }
- }
- if (added_count + g >= lpn->queue_size) {
- BT_WARN("Friend Queue Size exceeded");
- break;
- }
- req.addr_list[g++] = sys_cpu_to_be16(lpn->groups[i]);
- bt_mesh_atomic_set_bit(lpn->pending, i);
- if (g == ARRAY_SIZE(req.addr_list)) {
- break;
- }
- }
- if (g == 0U) {
- group_zero(lpn->pending);
- return false;
- }
- req.xact = lpn->xact_next++;
- if (bt_mesh_ctl_send(&tx, op, &req, 1 + g * 2,
- &req_sent_cb, NULL) < 0) {
- group_zero(lpn->pending);
- return false;
- }
- lpn->xact_pending = req.xact;
- lpn->sent_req = op;
- return true;
- }
- static void update_timeout(struct bt_mesh_lpn *lpn)
- {
- if (lpn->established) {
- BT_WARN("No response from Friend during ReceiveWindow");
- bt_mesh_scan_disable();
- lpn_set_state(BLE_MESH_LPN_ESTABLISHED);
- k_delayed_work_submit(&lpn->timer, POLL_RETRY_TIMEOUT);
- } else {
- if (IS_ENABLED(CONFIG_BLE_MESH_LPN_ESTABLISHMENT)) {
- bt_mesh_scan_disable();
- }
- if (lpn->req_attempts < FIRST_POLL_ATTEMPTS) {
- BT_WARN("Retrying first Friend Poll");
- lpn->sent_req = 0U;
- if (send_friend_poll() == 0) {
- return;
- }
- }
- BT_ERR("Timed out waiting for first Friend Update");
- clear_friendship(false, false);
- }
- }
- static void lpn_timeout(struct k_work *work)
- {
- struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
- BT_DBG("state: %s", state2str(lpn->state));
- switch (lpn->state) {
- case BLE_MESH_LPN_DISABLED:
- break;
- case BLE_MESH_LPN_CLEAR:
- clear_friendship(false, bt_mesh.lpn.disable);
- break;
- case BLE_MESH_LPN_TIMER:
- BT_DBG("Starting to look for Friend nodes");
- lpn_set_state(BLE_MESH_LPN_ENABLED);
- if (IS_ENABLED(CONFIG_BLE_MESH_LPN_ESTABLISHMENT)) {
- bt_mesh_scan_disable();
- }
- /* fall through */
- case BLE_MESH_LPN_ENABLED:
- send_friend_req(lpn);
- break;
- case BLE_MESH_LPN_REQ_WAIT:
- bt_mesh_scan_enable();
- k_delayed_work_submit(&lpn->timer,
- lpn->adv_duration + FRIEND_REQ_SCAN);
- lpn_set_state(BLE_MESH_LPN_WAIT_OFFER);
- break;
- case BLE_MESH_LPN_WAIT_OFFER:
- BT_WARN("No acceptable Friend Offers received");
- if (IS_ENABLED(CONFIG_BLE_MESH_LPN_ESTABLISHMENT)) {
- bt_mesh_scan_disable();
- }
- lpn->counter++;
- lpn_set_state(BLE_MESH_LPN_ENABLED);
- lpn->sent_req = 0U;
- k_delayed_work_submit(&lpn->timer, FRIEND_REQ_RETRY_TIMEOUT);
- break;
- case BLE_MESH_LPN_OFFER_RECV:
- if (lpn->req_attempts < FIRST_POLL_ATTEMPTS) {
- BT_WARN("Retrying the first Friend Poll, %d attempts", lpn->req_attempts);
- lpn->sent_req = 0U;
- send_friend_poll();
- break;
- }
- BT_ERR("Timeout waiting for the first Friend Update");
- clear_friendship(true, false);
- break;
- case BLE_MESH_LPN_ESTABLISHED:
- if (lpn->req_attempts < REQ_ATTEMPTS(lpn)) {
- uint8_t req = lpn->sent_req;
- lpn->sent_req = 0U;
- if (!req || req == TRANS_CTL_OP_FRIEND_POLL) {
- send_friend_poll();
- } else {
- sub_update(req);
- }
- break;
- }
- BT_ERR("No response from Friend after %u retries",
- lpn->req_attempts);
- lpn->req_attempts = 0U;
- clear_friendship(false, false);
- break;
- case BLE_MESH_LPN_RECV_DELAY:
- k_delayed_work_submit(&lpn->timer,
- lpn->adv_duration + SCAN_LATENCY +
- lpn->recv_win);
- bt_mesh_scan_enable();
- lpn_set_state(BLE_MESH_LPN_WAIT_UPDATE);
- break;
- case BLE_MESH_LPN_WAIT_UPDATE:
- update_timeout(lpn);
- break;
- default:
- BT_ERR("Unhandled LPN state");
- break;
- }
- }
- void bt_mesh_lpn_group_add(uint16_t group)
- {
- BT_DBG("group 0x%04x", group);
- lpn_group_add(group);
- if (!bt_mesh_lpn_established() || bt_mesh.lpn.sent_req) {
- return;
- }
- sub_update(TRANS_CTL_OP_FRIEND_SUB_ADD);
- }
- void bt_mesh_lpn_group_del(uint16_t *groups, size_t group_count)
- {
- int i;
- for (i = 0; i < group_count; i++) {
- if (groups[i] != BLE_MESH_ADDR_UNASSIGNED) {
- BT_DBG("group 0x%04x", groups[i]);
- lpn_group_del(groups[i]);
- }
- }
- if (!bt_mesh_lpn_established() || bt_mesh.lpn.sent_req) {
- return;
- }
- sub_update(TRANS_CTL_OP_FRIEND_SUB_REM);
- }
- static int32_t poll_timeout(struct bt_mesh_lpn *lpn)
- {
- /* If we're waiting for segment acks keep polling at high freq */
- if (bt_mesh_tx_in_progress()) {
- return MIN(POLL_TIMEOUT_MAX(lpn), K_SECONDS(1));
- }
- if (lpn->poll_timeout < POLL_TIMEOUT_MAX(lpn)) {
- lpn->poll_timeout *= 2;
- lpn->poll_timeout = MIN(lpn->poll_timeout,
- POLL_TIMEOUT_MAX(lpn));
- }
- BT_DBG("Poll Timeout is %ums", lpn->poll_timeout);
- return lpn->poll_timeout;
- }
- int bt_mesh_lpn_friend_sub_cfm(struct bt_mesh_net_rx *rx,
- struct net_buf_simple *buf)
- {
- struct bt_mesh_ctl_friend_sub_confirm *msg = (void *)buf->data;
- struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
- if (buf->len < sizeof(*msg)) {
- BT_WARN("Too short Friend Subscription Confirm");
- return -EINVAL;
- }
- BT_DBG("xact 0x%02x", msg->xact);
- if (!lpn->sent_req) {
- BT_WARN("No pending subscription list message");
- return 0;
- }
- if (msg->xact != lpn->xact_pending) {
- BT_WARN("Transaction mismatch (0x%02x != 0x%02x)",
- msg->xact, lpn->xact_pending);
- return 0;
- }
- if (lpn->sent_req == TRANS_CTL_OP_FRIEND_SUB_ADD) {
- group_set(lpn->added, lpn->pending);
- group_zero(lpn->pending);
- } else if (lpn->sent_req == TRANS_CTL_OP_FRIEND_SUB_REM) {
- int i;
- group_clear(lpn->added, lpn->pending);
- for (i = 0; i < ARRAY_SIZE(lpn->groups); i++) {
- if (bt_mesh_atomic_test_and_clear_bit(lpn->pending, i) &&
- bt_mesh_atomic_test_and_clear_bit(lpn->to_remove, i)) {
- lpn->groups[i] = BLE_MESH_ADDR_UNASSIGNED;
- }
- }
- } else {
- BT_WARN("Unexpected Friend Subscription Confirm");
- return 0;
- }
- friend_response_received(lpn);
- if (lpn->groups_changed) {
- sub_update(TRANS_CTL_OP_FRIEND_SUB_ADD);
- sub_update(TRANS_CTL_OP_FRIEND_SUB_REM);
- if (!lpn->sent_req) {
- lpn->groups_changed = 0U;
- }
- }
- if (lpn->pending_poll) {
- send_friend_poll();
- }
- if (!lpn->sent_req) {
- k_delayed_work_submit(&lpn->timer, poll_timeout(lpn));
- }
- return 0;
- }
- int bt_mesh_lpn_friend_update(struct bt_mesh_net_rx *rx,
- struct net_buf_simple *buf)
- {
- struct bt_mesh_ctl_friend_update *msg = (void *)buf->data;
- struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
- struct bt_mesh_subnet *sub = rx->sub;
- uint32_t iv_index = 0U;
- if (buf->len < sizeof(*msg)) {
- BT_WARN("Too short Friend Update");
- return -EINVAL;
- }
- if (lpn->sent_req != TRANS_CTL_OP_FRIEND_POLL) {
- BT_WARN("Unexpected friend update");
- return 0;
- }
- if (sub->kr_phase == BLE_MESH_KR_PHASE_2 && !rx->new_key) {
- BT_WARN("Ignoring Phase 2 KR Update secured using old key");
- return 0;
- }
- if (bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_IVU_INITIATOR) &&
- (bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_IVU_IN_PROGRESS) ==
- BLE_MESH_IV_UPDATE(msg->flags))) {
- bt_mesh_beacon_ivu_initiator(false);
- }
- if (!lpn->established) {
- struct bt_mesh_cfg_srv *cfg = bt_mesh_cfg_get();
- /* This is normally checked on the transport layer, however
- * in this state we're also still accepting master
- * credentials so we need to ensure the right ones (Friend
- * Credentials) were used for this message.
- */
- if (rx->ctx.recv_cred != BLE_MESH_FRIENDSHIP_CRED) {
- BT_WARN("Friend Update with wrong credentials");
- return -EINVAL;
- }
- lpn->established = 1U;
- BT_INFO("Friendship established with 0x%04x", lpn->frnd);
- if (cfg->hb_pub.feat & BLE_MESH_FEAT_LOW_POWER) {
- bt_mesh_heartbeat_send();
- }
- if (lpn_cb) {
- lpn_cb(lpn->frnd, true);
- }
- /* Set initial poll timeout */
- lpn->poll_timeout = MIN(POLL_TIMEOUT_MAX(lpn),
- POLL_TIMEOUT_INIT);
- /* If the Low Power node supports directed forwarding functionality when
- * the friendship is established in a subnet, the Low Power node shall
- * store the current value of the Directed Forwarding state and shall set
- * the state to 0x00 for that subnet. When that friendship is terminated,
- * the Low Power node shall set the Directed Forwarding state to the stored
- * value.
- */
- #if CONFIG_BLE_MESH_DF_SRV
- lpn->old_directed_forwarding = bt_mesh_get_and_disable_directed_forwarding_state(sub);
- #endif
- }
- friend_response_received(lpn);
- iv_index = sys_be32_to_cpu(msg->iv_index);
- BT_INFO("flags 0x%02x iv_index 0x%08x md %u", msg->flags, iv_index,
- msg->md);
- if (bt_mesh_kr_update(sub, BLE_MESH_KEY_REFRESH(msg->flags),
- rx->new_key)) {
- bt_mesh_net_secure_beacon_update(sub);
- }
- bt_mesh_net_iv_update(iv_index, BLE_MESH_IV_UPDATE(msg->flags));
- if (lpn->groups_changed) {
- sub_update(TRANS_CTL_OP_FRIEND_SUB_ADD);
- sub_update(TRANS_CTL_OP_FRIEND_SUB_REM);
- if (!lpn->sent_req) {
- lpn->groups_changed = 0U;
- }
- }
- if (msg->md) {
- BT_DBG("Requesting for more messages");
- send_friend_poll();
- }
- if (!lpn->sent_req) {
- k_delayed_work_submit(&lpn->timer, poll_timeout(lpn));
- }
- return 0;
- }
- int bt_mesh_lpn_poll(void)
- {
- if (!bt_mesh.lpn.established) {
- return -EAGAIN;
- }
- BT_DBG("Requesting more messages");
- return send_friend_poll();
- }
- void bt_mesh_lpn_set_cb(void (*cb)(uint16_t friend_addr, bool established))
- {
- lpn_cb = cb;
- }
- int bt_mesh_lpn_init(void)
- {
- struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
- k_delayed_work_init(&lpn->timer, lpn_timeout);
- if (lpn->state == BLE_MESH_LPN_ENABLED) {
- if (!IS_ENABLED(CONFIG_BLE_MESH_LPN_ESTABLISHMENT)) {
- bt_mesh_scan_enable();
- }
- send_friend_req(lpn);
- } else {
- bt_mesh_scan_enable();
- if (IS_ENABLED(CONFIG_BLE_MESH_LPN_AUTO)) {
- BT_DBG("Waiting %u ms for messages", LPN_AUTO_TIMEOUT);
- lpn_set_state(BLE_MESH_LPN_TIMER);
- k_delayed_work_submit(&lpn->timer, LPN_AUTO_TIMEOUT);
- }
- }
- return 0;
- }
- #if CONFIG_BLE_MESH_DEINIT
- int bt_mesh_lpn_deinit(void)
- {
- struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
- bt_mesh_lpn_disable(true);
- k_delayed_work_free(&lpn->timer);
- return 0;
- }
- #endif /* CONFIG_BLE_MESH_DEINIT */
- #endif /* CONFIG_BLE_MESH_LOW_POWER */
|