xref: /nrf52832-nimble/packages/NimBLE-latest/nimble/host/mesh/src/lpn.c (revision 042d53a763ad75cb1465103098bb88c245d95138)
1*042d53a7SEvalZero /*  Bluetooth Mesh */
2*042d53a7SEvalZero 
3*042d53a7SEvalZero /*
4*042d53a7SEvalZero  * Copyright (c) 2017 Intel Corporation
5*042d53a7SEvalZero  *
6*042d53a7SEvalZero  * SPDX-License-Identifier: Apache-2.0
7*042d53a7SEvalZero  */
8*042d53a7SEvalZero 
9*042d53a7SEvalZero #include "syscfg/syscfg.h"
10*042d53a7SEvalZero 
11*042d53a7SEvalZero #if MYNEWT_VAL(BLE_MESH_LOW_POWER) == 1
12*042d53a7SEvalZero 
13*042d53a7SEvalZero #include <stdint.h>
14*042d53a7SEvalZero 
15*042d53a7SEvalZero #define BT_DBG_ENABLED (MYNEWT_VAL(BLE_MESH_DEBUG_LOW_POWER))
16*042d53a7SEvalZero #include "host/ble_hs_log.h"
17*042d53a7SEvalZero 
18*042d53a7SEvalZero #include "mesh/mesh.h"
19*042d53a7SEvalZero #include "mesh_priv.h"
20*042d53a7SEvalZero #include "crypto.h"
21*042d53a7SEvalZero #include "adv.h"
22*042d53a7SEvalZero #include "net.h"
23*042d53a7SEvalZero #include "transport.h"
24*042d53a7SEvalZero #include "access.h"
25*042d53a7SEvalZero #include "beacon.h"
26*042d53a7SEvalZero #include "foundation.h"
27*042d53a7SEvalZero #include "lpn.h"
28*042d53a7SEvalZero 
29*042d53a7SEvalZero #if MYNEWT_VAL(BLE_MESH_LPN_AUTO)
30*042d53a7SEvalZero #define LPN_AUTO_TIMEOUT          K_SECONDS(MYNEWT_VAL(BLE_MESH_LPN_AUTO_TIMEOUT))
31*042d53a7SEvalZero #else
32*042d53a7SEvalZero #define LPN_AUTO_TIMEOUT          0
33*042d53a7SEvalZero #endif
34*042d53a7SEvalZero 
35*042d53a7SEvalZero #define LPN_RECV_DELAY            MYNEWT_VAL(BLE_MESH_LPN_RECV_DELAY)
36*042d53a7SEvalZero #define SCAN_LATENCY              min(MYNEWT_VAL(BLE_MESH_LPN_SCAN_LATENCY), \
37*042d53a7SEvalZero 				      LPN_RECV_DELAY)
38*042d53a7SEvalZero 
39*042d53a7SEvalZero #define FRIEND_REQ_RETRY_TIMEOUT  K_SECONDS(MYNEWT_VAL(BLE_MESH_LPN_RETRY_TIMEOUT))
40*042d53a7SEvalZero 
41*042d53a7SEvalZero #define FRIEND_REQ_WAIT           K_MSEC(100)
42*042d53a7SEvalZero #define FRIEND_REQ_SCAN           K_SECONDS(1)
43*042d53a7SEvalZero #define FRIEND_REQ_TIMEOUT        (FRIEND_REQ_WAIT + FRIEND_REQ_SCAN)
44*042d53a7SEvalZero 
45*042d53a7SEvalZero #define POLL_RETRY_TIMEOUT        K_MSEC(100)
46*042d53a7SEvalZero 
47*042d53a7SEvalZero #define REQ_RETRY_DURATION(lpn)  (4 * (LPN_RECV_DELAY + (lpn)->adv_duration + \
48*042d53a7SEvalZero 				       (lpn)->recv_win + POLL_RETRY_TIMEOUT))
49*042d53a7SEvalZero 
50*042d53a7SEvalZero #define POLL_TIMEOUT_INIT     (MYNEWT_VAL(BLE_MESH_LPN_INIT_POLL_TIMEOUT) * 100)
51*042d53a7SEvalZero #define POLL_TIMEOUT_MAX(lpn)   ((MYNEWT_VAL(BLE_MESH_LPN_POLL_TIMEOUT) * 100) - \
52*042d53a7SEvalZero 				 REQ_RETRY_DURATION(lpn))
53*042d53a7SEvalZero #define REQ_ATTEMPTS(lpn)     (POLL_TIMEOUT_MAX(lpn) < K_SECONDS(3) ? 2 : 4)
54*042d53a7SEvalZero 
55*042d53a7SEvalZero #define CLEAR_ATTEMPTS        2
56*042d53a7SEvalZero 
57*042d53a7SEvalZero #define LPN_CRITERIA ((MYNEWT_VAL(BLE_MESH_LPN_MIN_QUEUE_SIZE)) | \
58*042d53a7SEvalZero 		      (MYNEWT_VAL(BLE_MESH_LPN_RSSI_FACTOR) << 3) | \
59*042d53a7SEvalZero 		      (MYNEWT_VAL(BLE_MESH_LPN_RECV_WIN_FACTOR) << 5))
60*042d53a7SEvalZero 
61*042d53a7SEvalZero #define POLL_TO(to) { (u8_t)((to) >> 16), (u8_t)((to) >> 8), (u8_t)(to) }
62*042d53a7SEvalZero #define LPN_POLL_TO POLL_TO(MYNEWT_VAL(BLE_MESH_LPN_POLL_TIMEOUT))
63*042d53a7SEvalZero 
64*042d53a7SEvalZero /* 2 transmissions, 20ms interval */
65*042d53a7SEvalZero #define POLL_XMIT BT_MESH_TRANSMIT(1, 20)
66*042d53a7SEvalZero 
67*042d53a7SEvalZero static void (*lpn_cb)(u16_t friend_addr, bool established);
68*042d53a7SEvalZero 
69*042d53a7SEvalZero #if (MYNEWT_VAL(BLE_MESH_DEBUG_LOW_POWER))
state2str(int state)70*042d53a7SEvalZero static const char *state2str(int state)
71*042d53a7SEvalZero {
72*042d53a7SEvalZero 	switch (state) {
73*042d53a7SEvalZero 	case BT_MESH_LPN_DISABLED:
74*042d53a7SEvalZero 		return "disabled";
75*042d53a7SEvalZero 	case BT_MESH_LPN_CLEAR:
76*042d53a7SEvalZero 		return "clear";
77*042d53a7SEvalZero 	case BT_MESH_LPN_TIMER:
78*042d53a7SEvalZero 		return "timer";
79*042d53a7SEvalZero 	case BT_MESH_LPN_ENABLED:
80*042d53a7SEvalZero 		return "enabled";
81*042d53a7SEvalZero 	case BT_MESH_LPN_REQ_WAIT:
82*042d53a7SEvalZero 		return "req wait";
83*042d53a7SEvalZero 	case BT_MESH_LPN_WAIT_OFFER:
84*042d53a7SEvalZero 		return "wait offer";
85*042d53a7SEvalZero 	case BT_MESH_LPN_ESTABLISHED:
86*042d53a7SEvalZero 		return "established";
87*042d53a7SEvalZero 	case BT_MESH_LPN_RECV_DELAY:
88*042d53a7SEvalZero 		return "recv delay";
89*042d53a7SEvalZero 	case BT_MESH_LPN_WAIT_UPDATE:
90*042d53a7SEvalZero 		return "wait update";
91*042d53a7SEvalZero 	default:
92*042d53a7SEvalZero 		return "(unknown)";
93*042d53a7SEvalZero 	}
94*042d53a7SEvalZero }
95*042d53a7SEvalZero #endif /* CONFIG_BLUETOOTH_MESH_DEBUG_LPN */
96*042d53a7SEvalZero 
lpn_set_state(int state)97*042d53a7SEvalZero static inline void lpn_set_state(int state)
98*042d53a7SEvalZero {
99*042d53a7SEvalZero #if (MYNEWT_VAL(BLE_MESH_DEBUG_LOW_POWER))
100*042d53a7SEvalZero 	BT_DBG("%s -> %s", state2str(bt_mesh.lpn.state), state2str(state));
101*042d53a7SEvalZero #endif
102*042d53a7SEvalZero 	bt_mesh.lpn.state = state;
103*042d53a7SEvalZero }
104*042d53a7SEvalZero 
group_zero(atomic_t * target)105*042d53a7SEvalZero static inline void group_zero(atomic_t *target)
106*042d53a7SEvalZero {
107*042d53a7SEvalZero #if CONFIG_BT_MESH_LPN_GROUPS > 32
108*042d53a7SEvalZero 	int i;
109*042d53a7SEvalZero 
110*042d53a7SEvalZero 	for (i = 0; i < ARRAY_SIZE(bt_mesh.lpn.added); i++) {
111*042d53a7SEvalZero 		atomic_set(&target[i], 0);
112*042d53a7SEvalZero 	}
113*042d53a7SEvalZero #else
114*042d53a7SEvalZero 	atomic_set(target, 0);
115*042d53a7SEvalZero #endif
116*042d53a7SEvalZero }
117*042d53a7SEvalZero 
group_set(atomic_t * target,atomic_t * source)118*042d53a7SEvalZero static inline void group_set(atomic_t *target, atomic_t *source)
119*042d53a7SEvalZero {
120*042d53a7SEvalZero #if CONFIG_BT_MESH_LPN_GROUPS > 32
121*042d53a7SEvalZero 	int i;
122*042d53a7SEvalZero 
123*042d53a7SEvalZero 	for (i = 0; i < ARRAY_SIZE(bt_mesh.lpn.added); i++) {
124*042d53a7SEvalZero 		atomic_or(&target[i], atomic_get(&source[i]));
125*042d53a7SEvalZero 	}
126*042d53a7SEvalZero #else
127*042d53a7SEvalZero 	atomic_or(target, atomic_get(source));
128*042d53a7SEvalZero #endif
129*042d53a7SEvalZero }
130*042d53a7SEvalZero 
group_clear(atomic_t * target,atomic_t * source)131*042d53a7SEvalZero static inline void group_clear(atomic_t *target, atomic_t *source)
132*042d53a7SEvalZero {
133*042d53a7SEvalZero #if CONFIG_BT_MESH_LPN_GROUPS > 32
134*042d53a7SEvalZero 	int i;
135*042d53a7SEvalZero 
136*042d53a7SEvalZero 	for (i = 0; i < ARRAY_SIZE(bt_mesh.lpn.added); i++) {
137*042d53a7SEvalZero 		atomic_and(&target[i], ~atomic_get(&source[i]));
138*042d53a7SEvalZero 	}
139*042d53a7SEvalZero #else
140*042d53a7SEvalZero 	atomic_and(target, ~atomic_get(source));
141*042d53a7SEvalZero #endif
142*042d53a7SEvalZero }
143*042d53a7SEvalZero 
144*042d53a7SEvalZero static void clear_friendship(bool force, bool disable);
145*042d53a7SEvalZero 
friend_clear_sent(int err,void * user_data)146*042d53a7SEvalZero static void friend_clear_sent(int err, void *user_data)
147*042d53a7SEvalZero {
148*042d53a7SEvalZero 	struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
149*042d53a7SEvalZero 
150*042d53a7SEvalZero 	/* We're switching away from Low Power behavior, so permanently
151*042d53a7SEvalZero 	 * enable scanning.
152*042d53a7SEvalZero 	 */
153*042d53a7SEvalZero 	bt_mesh_scan_enable();
154*042d53a7SEvalZero 
155*042d53a7SEvalZero 	lpn->req_attempts++;
156*042d53a7SEvalZero 
157*042d53a7SEvalZero 	if (err) {
158*042d53a7SEvalZero 		BT_ERR("Sending Friend Request failed (err %d)", err);
159*042d53a7SEvalZero 		lpn_set_state(BT_MESH_LPN_ENABLED);
160*042d53a7SEvalZero 		clear_friendship(false, lpn->disable);
161*042d53a7SEvalZero 		return;
162*042d53a7SEvalZero 	}
163*042d53a7SEvalZero 
164*042d53a7SEvalZero 	lpn_set_state(BT_MESH_LPN_CLEAR);
165*042d53a7SEvalZero 	k_delayed_work_submit(&lpn->timer, FRIEND_REQ_TIMEOUT);
166*042d53a7SEvalZero }
167*042d53a7SEvalZero 
168*042d53a7SEvalZero static const struct bt_mesh_send_cb clear_sent_cb = {
169*042d53a7SEvalZero 	.end = friend_clear_sent,
170*042d53a7SEvalZero };
171*042d53a7SEvalZero 
send_friend_clear(void)172*042d53a7SEvalZero static int send_friend_clear(void)
173*042d53a7SEvalZero {
174*042d53a7SEvalZero 	struct bt_mesh_msg_ctx ctx = {
175*042d53a7SEvalZero 		.net_idx     = bt_mesh.sub[0].net_idx,
176*042d53a7SEvalZero 		.app_idx     = BT_MESH_KEY_UNUSED,
177*042d53a7SEvalZero 		.addr        = bt_mesh.lpn.frnd,
178*042d53a7SEvalZero 		.send_ttl    = 0,
179*042d53a7SEvalZero 	};
180*042d53a7SEvalZero 	struct bt_mesh_net_tx tx = {
181*042d53a7SEvalZero 		.sub = &bt_mesh.sub[0],
182*042d53a7SEvalZero 		.ctx = &ctx,
183*042d53a7SEvalZero 		.src = bt_mesh_primary_addr(),
184*042d53a7SEvalZero 		.xmit = bt_mesh_net_transmit_get(),
185*042d53a7SEvalZero 	};
186*042d53a7SEvalZero 	struct bt_mesh_ctl_friend_clear req = {
187*042d53a7SEvalZero 		.lpn_addr    = sys_cpu_to_be16(tx.src),
188*042d53a7SEvalZero 		.lpn_counter = sys_cpu_to_be16(bt_mesh.lpn.counter),
189*042d53a7SEvalZero 	};
190*042d53a7SEvalZero 
191*042d53a7SEvalZero 	BT_DBG("");
192*042d53a7SEvalZero 
193*042d53a7SEvalZero 	return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_CLEAR, &req,
194*042d53a7SEvalZero 				sizeof(req), NULL, &clear_sent_cb, NULL);
195*042d53a7SEvalZero }
196*042d53a7SEvalZero 
clear_friendship(bool force,bool disable)197*042d53a7SEvalZero static void clear_friendship(bool force, bool disable)
198*042d53a7SEvalZero {
199*042d53a7SEvalZero 	struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
200*042d53a7SEvalZero 
201*042d53a7SEvalZero 	BT_DBG("force %u disable %u", force, disable);
202*042d53a7SEvalZero 
203*042d53a7SEvalZero 	if (!force && lpn->established && !lpn->clear_success &&
204*042d53a7SEvalZero 	    lpn->req_attempts < CLEAR_ATTEMPTS) {
205*042d53a7SEvalZero 		send_friend_clear();
206*042d53a7SEvalZero 		lpn->disable = disable;
207*042d53a7SEvalZero 		return;
208*042d53a7SEvalZero 	}
209*042d53a7SEvalZero 
210*042d53a7SEvalZero 	bt_mesh_rx_reset();
211*042d53a7SEvalZero 
212*042d53a7SEvalZero 	k_delayed_work_cancel(&lpn->timer);
213*042d53a7SEvalZero 
214*042d53a7SEvalZero 	friend_cred_del(bt_mesh.sub[0].net_idx, lpn->frnd);
215*042d53a7SEvalZero 
216*042d53a7SEvalZero 	if (lpn->clear_success) {
217*042d53a7SEvalZero 		lpn->old_friend = BT_MESH_ADDR_UNASSIGNED;
218*042d53a7SEvalZero 	} else {
219*042d53a7SEvalZero 		lpn->old_friend = lpn->frnd;
220*042d53a7SEvalZero 	}
221*042d53a7SEvalZero 
222*042d53a7SEvalZero 	if (lpn_cb && lpn->frnd != BT_MESH_ADDR_UNASSIGNED) {
223*042d53a7SEvalZero 		lpn_cb(lpn->frnd, false);
224*042d53a7SEvalZero 	}
225*042d53a7SEvalZero 
226*042d53a7SEvalZero 	lpn->frnd = BT_MESH_ADDR_UNASSIGNED;
227*042d53a7SEvalZero 	lpn->fsn = 0;
228*042d53a7SEvalZero 	lpn->req_attempts = 0;
229*042d53a7SEvalZero 	lpn->recv_win = 0;
230*042d53a7SEvalZero 	lpn->queue_size = 0;
231*042d53a7SEvalZero 	lpn->disable = 0;
232*042d53a7SEvalZero 	lpn->sent_req = 0;
233*042d53a7SEvalZero 	lpn->established = 0;
234*042d53a7SEvalZero 	lpn->clear_success = 0;
235*042d53a7SEvalZero 
236*042d53a7SEvalZero 	group_zero(lpn->added);
237*042d53a7SEvalZero 	group_zero(lpn->pending);
238*042d53a7SEvalZero 	group_zero(lpn->to_remove);
239*042d53a7SEvalZero 
240*042d53a7SEvalZero 	/* Set this to 1 to force group subscription when the next
241*042d53a7SEvalZero 	 * Friendship is created, in case lpn->groups doesn't get
242*042d53a7SEvalZero 	 * modified meanwhile.
243*042d53a7SEvalZero 	 */
244*042d53a7SEvalZero 	lpn->groups_changed = 1;
245*042d53a7SEvalZero 
246*042d53a7SEvalZero 	if (disable) {
247*042d53a7SEvalZero 		lpn_set_state(BT_MESH_LPN_DISABLED);
248*042d53a7SEvalZero 		return;
249*042d53a7SEvalZero 	}
250*042d53a7SEvalZero 
251*042d53a7SEvalZero 	lpn_set_state(BT_MESH_LPN_ENABLED);
252*042d53a7SEvalZero 	k_delayed_work_submit(&lpn->timer, FRIEND_REQ_RETRY_TIMEOUT);
253*042d53a7SEvalZero }
254*042d53a7SEvalZero 
friend_req_sent(u16_t duration,int err,void * user_data)255*042d53a7SEvalZero static void friend_req_sent(u16_t duration, int err, void *user_data)
256*042d53a7SEvalZero {
257*042d53a7SEvalZero 	struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
258*042d53a7SEvalZero 
259*042d53a7SEvalZero 	if (err) {
260*042d53a7SEvalZero 		BT_ERR("Sending Friend Request failed (err %d)", err);
261*042d53a7SEvalZero 		return;
262*042d53a7SEvalZero 	}
263*042d53a7SEvalZero 
264*042d53a7SEvalZero 	lpn->adv_duration = duration;
265*042d53a7SEvalZero 
266*042d53a7SEvalZero 	if (IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) {
267*042d53a7SEvalZero 		k_delayed_work_submit(&lpn->timer, FRIEND_REQ_WAIT);
268*042d53a7SEvalZero 		lpn_set_state(BT_MESH_LPN_REQ_WAIT);
269*042d53a7SEvalZero 	} else {
270*042d53a7SEvalZero 		k_delayed_work_submit(&lpn->timer,
271*042d53a7SEvalZero 				      duration + FRIEND_REQ_TIMEOUT);
272*042d53a7SEvalZero 		lpn_set_state(BT_MESH_LPN_WAIT_OFFER);
273*042d53a7SEvalZero 	}
274*042d53a7SEvalZero }
275*042d53a7SEvalZero 
276*042d53a7SEvalZero static const struct bt_mesh_send_cb friend_req_sent_cb = {
277*042d53a7SEvalZero 	.start = friend_req_sent,
278*042d53a7SEvalZero };
279*042d53a7SEvalZero 
send_friend_req(struct bt_mesh_lpn * lpn)280*042d53a7SEvalZero static int send_friend_req(struct bt_mesh_lpn *lpn)
281*042d53a7SEvalZero {
282*042d53a7SEvalZero 	const struct bt_mesh_comp *comp = bt_mesh_comp_get();
283*042d53a7SEvalZero 	struct bt_mesh_msg_ctx ctx = {
284*042d53a7SEvalZero 		.net_idx  = bt_mesh.sub[0].net_idx,
285*042d53a7SEvalZero 		.app_idx  = BT_MESH_KEY_UNUSED,
286*042d53a7SEvalZero 		.addr     = BT_MESH_ADDR_FRIENDS,
287*042d53a7SEvalZero 		.send_ttl = 0,
288*042d53a7SEvalZero 	};
289*042d53a7SEvalZero 	struct bt_mesh_net_tx tx = {
290*042d53a7SEvalZero 		.sub = &bt_mesh.sub[0],
291*042d53a7SEvalZero 		.ctx = &ctx,
292*042d53a7SEvalZero 		.src = bt_mesh_primary_addr(),
293*042d53a7SEvalZero 		.xmit = POLL_XMIT,
294*042d53a7SEvalZero 	};
295*042d53a7SEvalZero 	struct bt_mesh_ctl_friend_req req = {
296*042d53a7SEvalZero 		.criteria    = LPN_CRITERIA,
297*042d53a7SEvalZero 		.recv_delay  = LPN_RECV_DELAY,
298*042d53a7SEvalZero 		.poll_to     = LPN_POLL_TO,
299*042d53a7SEvalZero 		.prev_addr   = lpn->old_friend,
300*042d53a7SEvalZero 		.num_elem    = comp->elem_count,
301*042d53a7SEvalZero 		.lpn_counter = sys_cpu_to_be16(lpn->counter),
302*042d53a7SEvalZero 	};
303*042d53a7SEvalZero 
304*042d53a7SEvalZero 	BT_DBG("");
305*042d53a7SEvalZero 
306*042d53a7SEvalZero 	return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_REQ, &req,
307*042d53a7SEvalZero 				sizeof(req), NULL, &friend_req_sent_cb, NULL);
308*042d53a7SEvalZero }
309*042d53a7SEvalZero 
req_sent(u16_t duration,int err,void * user_data)310*042d53a7SEvalZero static void req_sent(u16_t duration, int err, void *user_data)
311*042d53a7SEvalZero {
312*042d53a7SEvalZero 	struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
313*042d53a7SEvalZero 
314*042d53a7SEvalZero #if BT_DBG_ENABLED
315*042d53a7SEvalZero 	BT_DBG("req 0x%02x duration %u err %d state %s",
316*042d53a7SEvalZero 	       lpn->sent_req, duration, err, state2str(lpn->state));
317*042d53a7SEvalZero #endif
318*042d53a7SEvalZero 
319*042d53a7SEvalZero 	if (err) {
320*042d53a7SEvalZero 		BT_ERR("Sending request failed (err %d)", err);
321*042d53a7SEvalZero 		lpn->sent_req = 0;
322*042d53a7SEvalZero 		group_zero(lpn->pending);
323*042d53a7SEvalZero 		return;
324*042d53a7SEvalZero 	}
325*042d53a7SEvalZero 
326*042d53a7SEvalZero 	lpn->req_attempts++;
327*042d53a7SEvalZero 	lpn->adv_duration = duration;
328*042d53a7SEvalZero 
329*042d53a7SEvalZero 	if (lpn->established || IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) {
330*042d53a7SEvalZero 		lpn_set_state(BT_MESH_LPN_RECV_DELAY);
331*042d53a7SEvalZero 		/* We start scanning a bit early to elimitate risk of missing
332*042d53a7SEvalZero 		 * response data due to HCI and other latencies.
333*042d53a7SEvalZero 		 */
334*042d53a7SEvalZero 		k_delayed_work_submit(&lpn->timer,
335*042d53a7SEvalZero 				      LPN_RECV_DELAY - SCAN_LATENCY);
336*042d53a7SEvalZero 	} else {
337*042d53a7SEvalZero 		k_delayed_work_submit(&lpn->timer,
338*042d53a7SEvalZero 				      LPN_RECV_DELAY + duration +
339*042d53a7SEvalZero 				      lpn->recv_win);
340*042d53a7SEvalZero 	}
341*042d53a7SEvalZero }
342*042d53a7SEvalZero 
343*042d53a7SEvalZero static const struct bt_mesh_send_cb req_sent_cb = {
344*042d53a7SEvalZero 	.start = req_sent,
345*042d53a7SEvalZero };
346*042d53a7SEvalZero 
send_friend_poll(void)347*042d53a7SEvalZero static int send_friend_poll(void)
348*042d53a7SEvalZero {
349*042d53a7SEvalZero 	struct bt_mesh_msg_ctx ctx = {
350*042d53a7SEvalZero 		.net_idx     = bt_mesh.sub[0].net_idx,
351*042d53a7SEvalZero 		.app_idx     = BT_MESH_KEY_UNUSED,
352*042d53a7SEvalZero 		.addr        = bt_mesh.lpn.frnd,
353*042d53a7SEvalZero 		.send_ttl    = 0,
354*042d53a7SEvalZero 	};
355*042d53a7SEvalZero 	struct bt_mesh_net_tx tx = {
356*042d53a7SEvalZero 		.sub = &bt_mesh.sub[0],
357*042d53a7SEvalZero 		.ctx = &ctx,
358*042d53a7SEvalZero 		.src = bt_mesh_primary_addr(),
359*042d53a7SEvalZero 		.xmit = POLL_XMIT,
360*042d53a7SEvalZero 		.friend_cred = true,
361*042d53a7SEvalZero 	};
362*042d53a7SEvalZero 	struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
363*042d53a7SEvalZero 	u8_t fsn = lpn->fsn;
364*042d53a7SEvalZero 	int err;
365*042d53a7SEvalZero 
366*042d53a7SEvalZero 	BT_DBG("lpn->sent_req 0x%02x", lpn->sent_req);
367*042d53a7SEvalZero 
368*042d53a7SEvalZero 	if (lpn->sent_req) {
369*042d53a7SEvalZero 		if (lpn->sent_req != TRANS_CTL_OP_FRIEND_POLL) {
370*042d53a7SEvalZero 			lpn->pending_poll = 1;
371*042d53a7SEvalZero 		}
372*042d53a7SEvalZero 
373*042d53a7SEvalZero 		return 0;
374*042d53a7SEvalZero 	}
375*042d53a7SEvalZero 
376*042d53a7SEvalZero 	err = bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_POLL, &fsn, 1,
377*042d53a7SEvalZero 			       NULL, &req_sent_cb, NULL);
378*042d53a7SEvalZero 	if (err == 0) {
379*042d53a7SEvalZero 		lpn->pending_poll = 0;
380*042d53a7SEvalZero 		lpn->sent_req = TRANS_CTL_OP_FRIEND_POLL;
381*042d53a7SEvalZero 	}
382*042d53a7SEvalZero 
383*042d53a7SEvalZero 	return err;
384*042d53a7SEvalZero }
385*042d53a7SEvalZero 
bt_mesh_lpn_disable(bool force)386*042d53a7SEvalZero void bt_mesh_lpn_disable(bool force)
387*042d53a7SEvalZero {
388*042d53a7SEvalZero 	if (bt_mesh.lpn.state == BT_MESH_LPN_DISABLED) {
389*042d53a7SEvalZero 		return;
390*042d53a7SEvalZero 	}
391*042d53a7SEvalZero 
392*042d53a7SEvalZero 	clear_friendship(force, true);
393*042d53a7SEvalZero }
394*042d53a7SEvalZero 
bt_mesh_lpn_set(bool enable)395*042d53a7SEvalZero int bt_mesh_lpn_set(bool enable)
396*042d53a7SEvalZero {
397*042d53a7SEvalZero 	struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
398*042d53a7SEvalZero 
399*042d53a7SEvalZero 	if (enable) {
400*042d53a7SEvalZero 		if (lpn->state != BT_MESH_LPN_DISABLED) {
401*042d53a7SEvalZero 			return 0;
402*042d53a7SEvalZero 		}
403*042d53a7SEvalZero 	} else {
404*042d53a7SEvalZero 		if (lpn->state == BT_MESH_LPN_DISABLED) {
405*042d53a7SEvalZero 			return 0;
406*042d53a7SEvalZero 		}
407*042d53a7SEvalZero 	}
408*042d53a7SEvalZero 
409*042d53a7SEvalZero 	if (!bt_mesh_is_provisioned()) {
410*042d53a7SEvalZero 		if (enable) {
411*042d53a7SEvalZero 			lpn_set_state(BT_MESH_LPN_ENABLED);
412*042d53a7SEvalZero 		} else {
413*042d53a7SEvalZero 			lpn_set_state(BT_MESH_LPN_DISABLED);
414*042d53a7SEvalZero 		}
415*042d53a7SEvalZero 
416*042d53a7SEvalZero 		return 0;
417*042d53a7SEvalZero 	}
418*042d53a7SEvalZero 
419*042d53a7SEvalZero 	if (enable) {
420*042d53a7SEvalZero 		lpn_set_state(BT_MESH_LPN_ENABLED);
421*042d53a7SEvalZero 
422*042d53a7SEvalZero 		if (IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) {
423*042d53a7SEvalZero 			bt_mesh_scan_disable();
424*042d53a7SEvalZero 		}
425*042d53a7SEvalZero 
426*042d53a7SEvalZero 		send_friend_req(lpn);
427*042d53a7SEvalZero 	} else {
428*042d53a7SEvalZero 		if (IS_ENABLED(CONFIG_BT_MESH_LPN_AUTO) &&
429*042d53a7SEvalZero 		    lpn->state == BT_MESH_LPN_TIMER) {
430*042d53a7SEvalZero 			k_delayed_work_cancel(&lpn->timer);
431*042d53a7SEvalZero 			lpn_set_state(BT_MESH_LPN_DISABLED);
432*042d53a7SEvalZero 		} else {
433*042d53a7SEvalZero 			bt_mesh_lpn_disable(false);
434*042d53a7SEvalZero 		}
435*042d53a7SEvalZero 	}
436*042d53a7SEvalZero 
437*042d53a7SEvalZero 	return 0;
438*042d53a7SEvalZero }
439*042d53a7SEvalZero 
friend_response_received(struct bt_mesh_lpn * lpn)440*042d53a7SEvalZero static void friend_response_received(struct bt_mesh_lpn *lpn)
441*042d53a7SEvalZero {
442*042d53a7SEvalZero 	BT_DBG("lpn->sent_req 0x%02x", lpn->sent_req);
443*042d53a7SEvalZero 
444*042d53a7SEvalZero 	if (lpn->sent_req == TRANS_CTL_OP_FRIEND_POLL) {
445*042d53a7SEvalZero 		lpn->fsn++;
446*042d53a7SEvalZero 	}
447*042d53a7SEvalZero 
448*042d53a7SEvalZero 	k_delayed_work_cancel(&lpn->timer);
449*042d53a7SEvalZero 	bt_mesh_scan_disable();
450*042d53a7SEvalZero 	lpn_set_state(BT_MESH_LPN_ESTABLISHED);
451*042d53a7SEvalZero 	lpn->req_attempts = 0;
452*042d53a7SEvalZero 	lpn->sent_req = 0;
453*042d53a7SEvalZero }
454*042d53a7SEvalZero 
bt_mesh_lpn_msg_received(struct bt_mesh_net_rx * rx)455*042d53a7SEvalZero void bt_mesh_lpn_msg_received(struct bt_mesh_net_rx *rx)
456*042d53a7SEvalZero {
457*042d53a7SEvalZero 	struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
458*042d53a7SEvalZero 
459*042d53a7SEvalZero 	if (lpn->state == BT_MESH_LPN_TIMER) {
460*042d53a7SEvalZero 		BT_DBG("Restarting establishment timer");
461*042d53a7SEvalZero 		k_delayed_work_submit(&lpn->timer, LPN_AUTO_TIMEOUT);
462*042d53a7SEvalZero 		return;
463*042d53a7SEvalZero 	}
464*042d53a7SEvalZero 
465*042d53a7SEvalZero 	if (lpn->sent_req != TRANS_CTL_OP_FRIEND_POLL) {
466*042d53a7SEvalZero 		BT_WARN("Unexpected message withouth a preceding Poll");
467*042d53a7SEvalZero 		return;
468*042d53a7SEvalZero 	}
469*042d53a7SEvalZero 
470*042d53a7SEvalZero 	friend_response_received(lpn);
471*042d53a7SEvalZero 
472*042d53a7SEvalZero 	BT_DBG("Requesting more messages from Friend");
473*042d53a7SEvalZero 
474*042d53a7SEvalZero 	send_friend_poll();
475*042d53a7SEvalZero }
476*042d53a7SEvalZero 
bt_mesh_lpn_friend_offer(struct bt_mesh_net_rx * rx,struct os_mbuf * buf)477*042d53a7SEvalZero int bt_mesh_lpn_friend_offer(struct bt_mesh_net_rx *rx,
478*042d53a7SEvalZero 			     struct os_mbuf *buf)
479*042d53a7SEvalZero {
480*042d53a7SEvalZero 	struct bt_mesh_ctl_friend_offer *msg = (void *)buf->om_data;
481*042d53a7SEvalZero 	struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
482*042d53a7SEvalZero 	struct bt_mesh_subnet *sub = rx->sub;
483*042d53a7SEvalZero 	struct friend_cred *cred;
484*042d53a7SEvalZero 	u16_t frnd_counter;
485*042d53a7SEvalZero 	int err;
486*042d53a7SEvalZero 
487*042d53a7SEvalZero 	if (buf->om_len < sizeof(*msg)) {
488*042d53a7SEvalZero 		BT_WARN("Too short Friend Offer");
489*042d53a7SEvalZero 		return -EINVAL;
490*042d53a7SEvalZero 	}
491*042d53a7SEvalZero 
492*042d53a7SEvalZero 	if (lpn->state != BT_MESH_LPN_WAIT_OFFER) {
493*042d53a7SEvalZero 		BT_WARN("Ignoring unexpected Friend Offer");
494*042d53a7SEvalZero 		return 0;
495*042d53a7SEvalZero 	}
496*042d53a7SEvalZero 
497*042d53a7SEvalZero 	if (!msg->recv_win) {
498*042d53a7SEvalZero 		BT_WARN("Prohibited ReceiveWindow value");
499*042d53a7SEvalZero 		return -EINVAL;
500*042d53a7SEvalZero 	}
501*042d53a7SEvalZero 
502*042d53a7SEvalZero 	frnd_counter = sys_be16_to_cpu(msg->frnd_counter);
503*042d53a7SEvalZero 
504*042d53a7SEvalZero 	BT_DBG("recv_win %u queue_size %u sub_list_size %u rssi %d counter %u",
505*042d53a7SEvalZero 	       msg->recv_win, msg->queue_size, msg->sub_list_size, msg->rssi,
506*042d53a7SEvalZero 	       frnd_counter);
507*042d53a7SEvalZero 
508*042d53a7SEvalZero 	lpn->frnd = rx->ctx.addr;
509*042d53a7SEvalZero 
510*042d53a7SEvalZero 	cred = friend_cred_create(sub, lpn->frnd, lpn->counter, frnd_counter);
511*042d53a7SEvalZero 	if (!cred) {
512*042d53a7SEvalZero 		lpn->frnd = BT_MESH_ADDR_UNASSIGNED;
513*042d53a7SEvalZero 		return -ENOMEM;
514*042d53a7SEvalZero 	}
515*042d53a7SEvalZero 
516*042d53a7SEvalZero 	/* TODO: Add offer acceptance criteria check */
517*042d53a7SEvalZero 
518*042d53a7SEvalZero 	k_delayed_work_cancel(&lpn->timer);
519*042d53a7SEvalZero 
520*042d53a7SEvalZero 	lpn->recv_win = msg->recv_win;
521*042d53a7SEvalZero 	lpn->queue_size = msg->queue_size;
522*042d53a7SEvalZero 
523*042d53a7SEvalZero 	err = send_friend_poll();
524*042d53a7SEvalZero 	if (err) {
525*042d53a7SEvalZero 		friend_cred_clear(cred);
526*042d53a7SEvalZero 		lpn->frnd = BT_MESH_ADDR_UNASSIGNED;
527*042d53a7SEvalZero 		lpn->recv_win = 0;
528*042d53a7SEvalZero 		lpn->queue_size = 0;
529*042d53a7SEvalZero 		return err;
530*042d53a7SEvalZero 	}
531*042d53a7SEvalZero 
532*042d53a7SEvalZero 	lpn->counter++;
533*042d53a7SEvalZero 
534*042d53a7SEvalZero 	return 0;
535*042d53a7SEvalZero }
536*042d53a7SEvalZero 
bt_mesh_lpn_friend_clear_cfm(struct bt_mesh_net_rx * rx,struct os_mbuf * buf)537*042d53a7SEvalZero int bt_mesh_lpn_friend_clear_cfm(struct bt_mesh_net_rx *rx,
538*042d53a7SEvalZero 				 struct os_mbuf *buf)
539*042d53a7SEvalZero {
540*042d53a7SEvalZero 	struct bt_mesh_ctl_friend_clear_confirm *msg = (void *)buf->om_data;
541*042d53a7SEvalZero 	struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
542*042d53a7SEvalZero 	u16_t addr, counter;
543*042d53a7SEvalZero 
544*042d53a7SEvalZero 	if (buf->om_len < sizeof(*msg)) {
545*042d53a7SEvalZero 		BT_WARN("Too short Friend Clear Confirm");
546*042d53a7SEvalZero 		return -EINVAL;
547*042d53a7SEvalZero 	}
548*042d53a7SEvalZero 
549*042d53a7SEvalZero 	if (lpn->state != BT_MESH_LPN_CLEAR) {
550*042d53a7SEvalZero 		BT_WARN("Ignoring unexpected Friend Clear Confirm");
551*042d53a7SEvalZero 		return 0;
552*042d53a7SEvalZero 	}
553*042d53a7SEvalZero 
554*042d53a7SEvalZero 	addr = sys_be16_to_cpu(msg->lpn_addr);
555*042d53a7SEvalZero 	counter = sys_be16_to_cpu(msg->lpn_counter);
556*042d53a7SEvalZero 
557*042d53a7SEvalZero 	BT_DBG("LPNAddress 0x%04x LPNCounter 0x%04x", addr, counter);
558*042d53a7SEvalZero 
559*042d53a7SEvalZero 	if (addr != bt_mesh_primary_addr() || counter != lpn->counter) {
560*042d53a7SEvalZero 		BT_WARN("Invalid parameters in Friend Clear Confirm");
561*042d53a7SEvalZero 		return 0;
562*042d53a7SEvalZero 	}
563*042d53a7SEvalZero 
564*042d53a7SEvalZero 	lpn->clear_success = 1;
565*042d53a7SEvalZero 	clear_friendship(false, lpn->disable);
566*042d53a7SEvalZero 
567*042d53a7SEvalZero 	return 0;
568*042d53a7SEvalZero }
569*042d53a7SEvalZero 
lpn_group_add(u16_t group)570*042d53a7SEvalZero static void lpn_group_add(u16_t group)
571*042d53a7SEvalZero {
572*042d53a7SEvalZero 	struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
573*042d53a7SEvalZero 	u16_t *free_slot = NULL;
574*042d53a7SEvalZero 	int i;
575*042d53a7SEvalZero 
576*042d53a7SEvalZero 	for (i = 0; i < ARRAY_SIZE(lpn->groups); i++) {
577*042d53a7SEvalZero 		if (lpn->groups[i] == group) {
578*042d53a7SEvalZero 			atomic_clear_bit(lpn->to_remove, i);
579*042d53a7SEvalZero 			return;
580*042d53a7SEvalZero 		}
581*042d53a7SEvalZero 
582*042d53a7SEvalZero 		if (!free_slot && lpn->groups[i] == BT_MESH_ADDR_UNASSIGNED) {
583*042d53a7SEvalZero 			free_slot = &lpn->groups[i];
584*042d53a7SEvalZero 		}
585*042d53a7SEvalZero 	}
586*042d53a7SEvalZero 
587*042d53a7SEvalZero 	if (!free_slot) {
588*042d53a7SEvalZero 		BT_WARN("Friend Subscription List exceeded!");
589*042d53a7SEvalZero 		return;
590*042d53a7SEvalZero 	}
591*042d53a7SEvalZero 
592*042d53a7SEvalZero 	*free_slot = group;
593*042d53a7SEvalZero 	lpn->groups_changed = 1;
594*042d53a7SEvalZero }
595*042d53a7SEvalZero 
lpn_group_del(u16_t group)596*042d53a7SEvalZero static void lpn_group_del(u16_t group)
597*042d53a7SEvalZero {
598*042d53a7SEvalZero 	struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
599*042d53a7SEvalZero 	int i;
600*042d53a7SEvalZero 
601*042d53a7SEvalZero 	for (i = 0; i < ARRAY_SIZE(lpn->groups); i++) {
602*042d53a7SEvalZero 		if (lpn->groups[i] == group) {
603*042d53a7SEvalZero 			if (atomic_test_bit(lpn->added, i) ||
604*042d53a7SEvalZero 			    atomic_test_bit(lpn->pending, i)) {
605*042d53a7SEvalZero 				atomic_set_bit(lpn->to_remove, i);
606*042d53a7SEvalZero 				lpn->groups_changed = 1;
607*042d53a7SEvalZero 			} else {
608*042d53a7SEvalZero 				lpn->groups[i] = BT_MESH_ADDR_UNASSIGNED;
609*042d53a7SEvalZero 			}
610*042d53a7SEvalZero 		}
611*042d53a7SEvalZero 	}
612*042d53a7SEvalZero }
613*042d53a7SEvalZero 
group_popcount(atomic_t * target)614*042d53a7SEvalZero static inline int group_popcount(atomic_t *target)
615*042d53a7SEvalZero {
616*042d53a7SEvalZero #if CONFIG_BT_MESH_LPN_GROUPS > 32
617*042d53a7SEvalZero 	int i, count = 0;
618*042d53a7SEvalZero 
619*042d53a7SEvalZero 	for (i = 0; i < ARRAY_SIZE(bt_mesh.lpn.added); i++) {
620*042d53a7SEvalZero 		count += popcount(atomic_get(&target[i]));
621*042d53a7SEvalZero 	}
622*042d53a7SEvalZero #else
623*042d53a7SEvalZero 	return popcount(atomic_get(target));
624*042d53a7SEvalZero #endif
625*042d53a7SEvalZero }
626*042d53a7SEvalZero 
sub_update(u8_t op)627*042d53a7SEvalZero static bool sub_update(u8_t op)
628*042d53a7SEvalZero {
629*042d53a7SEvalZero 	struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
630*042d53a7SEvalZero 	int added_count = group_popcount(lpn->added);
631*042d53a7SEvalZero 	struct bt_mesh_msg_ctx ctx = {
632*042d53a7SEvalZero 		.net_idx     = bt_mesh.sub[0].net_idx,
633*042d53a7SEvalZero 		.app_idx     = BT_MESH_KEY_UNUSED,
634*042d53a7SEvalZero 		.addr        = lpn->frnd,
635*042d53a7SEvalZero 		.send_ttl    = 0,
636*042d53a7SEvalZero 	};
637*042d53a7SEvalZero 	struct bt_mesh_net_tx tx = {
638*042d53a7SEvalZero 		.sub = &bt_mesh.sub[0],
639*042d53a7SEvalZero 		.ctx = &ctx,
640*042d53a7SEvalZero 		.src = bt_mesh_primary_addr(),
641*042d53a7SEvalZero 		.xmit = POLL_XMIT,
642*042d53a7SEvalZero 		.friend_cred = true,
643*042d53a7SEvalZero 	};
644*042d53a7SEvalZero 	struct bt_mesh_ctl_friend_sub req;
645*042d53a7SEvalZero 	size_t i, g;
646*042d53a7SEvalZero 
647*042d53a7SEvalZero 	BT_DBG("op 0x%02x sent_req 0x%02x", op, lpn->sent_req);
648*042d53a7SEvalZero 
649*042d53a7SEvalZero 	if (lpn->sent_req) {
650*042d53a7SEvalZero 		return false;
651*042d53a7SEvalZero 	}
652*042d53a7SEvalZero 
653*042d53a7SEvalZero 	for (i = 0, g = 0; i < ARRAY_SIZE(lpn->groups); i++) {
654*042d53a7SEvalZero 		if (lpn->groups[i] == BT_MESH_ADDR_UNASSIGNED) {
655*042d53a7SEvalZero 			continue;
656*042d53a7SEvalZero 		}
657*042d53a7SEvalZero 
658*042d53a7SEvalZero 		if (op == TRANS_CTL_OP_FRIEND_SUB_ADD) {
659*042d53a7SEvalZero 			if (atomic_test_bit(lpn->added, i)) {
660*042d53a7SEvalZero 				continue;
661*042d53a7SEvalZero 			}
662*042d53a7SEvalZero 		} else {
663*042d53a7SEvalZero 			if (!atomic_test_bit(lpn->to_remove, i)) {
664*042d53a7SEvalZero 				continue;
665*042d53a7SEvalZero 			}
666*042d53a7SEvalZero 		}
667*042d53a7SEvalZero 
668*042d53a7SEvalZero 		if (added_count + g >= lpn->queue_size) {
669*042d53a7SEvalZero 			BT_WARN("Friend Queue Size exceeded");
670*042d53a7SEvalZero 			break;
671*042d53a7SEvalZero 		}
672*042d53a7SEvalZero 
673*042d53a7SEvalZero 		req.addr_list[g++] = sys_cpu_to_be16(lpn->groups[i]);
674*042d53a7SEvalZero 		atomic_set_bit(lpn->pending, i);
675*042d53a7SEvalZero 
676*042d53a7SEvalZero 		if (g == ARRAY_SIZE(req.addr_list)) {
677*042d53a7SEvalZero 			break;
678*042d53a7SEvalZero 		}
679*042d53a7SEvalZero 	}
680*042d53a7SEvalZero 
681*042d53a7SEvalZero 	if (g == 0) {
682*042d53a7SEvalZero 		group_zero(lpn->pending);
683*042d53a7SEvalZero 		return false;
684*042d53a7SEvalZero 	}
685*042d53a7SEvalZero 
686*042d53a7SEvalZero 	req.xact = lpn->xact_next++;
687*042d53a7SEvalZero 
688*042d53a7SEvalZero 	if (bt_mesh_ctl_send(&tx, op, &req, 1 + g * 2, NULL,
689*042d53a7SEvalZero 			     &req_sent_cb, NULL) < 0) {
690*042d53a7SEvalZero 		group_zero(lpn->pending);
691*042d53a7SEvalZero 		return false;
692*042d53a7SEvalZero 	}
693*042d53a7SEvalZero 
694*042d53a7SEvalZero 	lpn->xact_pending = req.xact;
695*042d53a7SEvalZero 	lpn->sent_req = op;
696*042d53a7SEvalZero 	return true;
697*042d53a7SEvalZero }
698*042d53a7SEvalZero 
update_timeout(struct bt_mesh_lpn * lpn)699*042d53a7SEvalZero static void update_timeout(struct bt_mesh_lpn *lpn)
700*042d53a7SEvalZero {
701*042d53a7SEvalZero 	if (lpn->established) {
702*042d53a7SEvalZero 		BT_WARN("No response from Friend during ReceiveWindow");
703*042d53a7SEvalZero 		bt_mesh_scan_disable();
704*042d53a7SEvalZero 		lpn_set_state(BT_MESH_LPN_ESTABLISHED);
705*042d53a7SEvalZero 		k_delayed_work_submit(&lpn->timer, POLL_RETRY_TIMEOUT);
706*042d53a7SEvalZero 	} else {
707*042d53a7SEvalZero 		if (IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) {
708*042d53a7SEvalZero 			bt_mesh_scan_disable();
709*042d53a7SEvalZero 		}
710*042d53a7SEvalZero 
711*042d53a7SEvalZero 		if (lpn->req_attempts < 6) {
712*042d53a7SEvalZero 			BT_WARN("Retrying first Friend Poll");
713*042d53a7SEvalZero 			lpn->sent_req = 0;
714*042d53a7SEvalZero 			if (send_friend_poll() == 0) {
715*042d53a7SEvalZero 				return;
716*042d53a7SEvalZero 			}
717*042d53a7SEvalZero 		}
718*042d53a7SEvalZero 
719*042d53a7SEvalZero 		BT_ERR("Timed out waiting for first Friend Update");
720*042d53a7SEvalZero 		clear_friendship(false, false);
721*042d53a7SEvalZero 	}
722*042d53a7SEvalZero }
723*042d53a7SEvalZero 
lpn_timeout(struct ble_npl_event * work)724*042d53a7SEvalZero static void lpn_timeout(struct ble_npl_event *work)
725*042d53a7SEvalZero {
726*042d53a7SEvalZero 	struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
727*042d53a7SEvalZero 
728*042d53a7SEvalZero #if (MYNEWT_VAL(BLE_MESH_DEBUG_LOW_POWER))
729*042d53a7SEvalZero 	BT_DBG("state: %s", state2str(lpn->state));
730*042d53a7SEvalZero #endif
731*042d53a7SEvalZero 
732*042d53a7SEvalZero 	switch (lpn->state) {
733*042d53a7SEvalZero 	case BT_MESH_LPN_DISABLED:
734*042d53a7SEvalZero 		break;
735*042d53a7SEvalZero 	case BT_MESH_LPN_CLEAR:
736*042d53a7SEvalZero 		clear_friendship(false, bt_mesh.lpn.disable);
737*042d53a7SEvalZero 		break;
738*042d53a7SEvalZero 	case BT_MESH_LPN_TIMER:
739*042d53a7SEvalZero 		BT_DBG("Starting to look for Friend nodes");
740*042d53a7SEvalZero 		lpn_set_state(BT_MESH_LPN_ENABLED);
741*042d53a7SEvalZero 		if (IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) {
742*042d53a7SEvalZero 			bt_mesh_scan_disable();
743*042d53a7SEvalZero 		}
744*042d53a7SEvalZero 		/* fall through */
745*042d53a7SEvalZero 	case BT_MESH_LPN_ENABLED:
746*042d53a7SEvalZero 		send_friend_req(lpn);
747*042d53a7SEvalZero 		break;
748*042d53a7SEvalZero 	case BT_MESH_LPN_REQ_WAIT:
749*042d53a7SEvalZero 		bt_mesh_scan_enable();
750*042d53a7SEvalZero 		k_delayed_work_submit(&lpn->timer,
751*042d53a7SEvalZero 				      lpn->adv_duration + FRIEND_REQ_SCAN);
752*042d53a7SEvalZero 		lpn_set_state(BT_MESH_LPN_WAIT_OFFER);
753*042d53a7SEvalZero 		break;
754*042d53a7SEvalZero 	case BT_MESH_LPN_WAIT_OFFER:
755*042d53a7SEvalZero 		BT_WARN("No acceptable Friend Offers received");
756*042d53a7SEvalZero 		if (IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) {
757*042d53a7SEvalZero 			bt_mesh_scan_disable();
758*042d53a7SEvalZero 		}
759*042d53a7SEvalZero 		lpn->counter++;
760*042d53a7SEvalZero 		lpn_set_state(BT_MESH_LPN_ENABLED);
761*042d53a7SEvalZero 		k_delayed_work_submit(&lpn->timer, FRIEND_REQ_RETRY_TIMEOUT);
762*042d53a7SEvalZero 		break;
763*042d53a7SEvalZero 	case BT_MESH_LPN_ESTABLISHED:
764*042d53a7SEvalZero 		if (lpn->req_attempts < REQ_ATTEMPTS(lpn)) {
765*042d53a7SEvalZero 			u8_t req = lpn->sent_req;
766*042d53a7SEvalZero 
767*042d53a7SEvalZero 			lpn->sent_req = 0;
768*042d53a7SEvalZero 
769*042d53a7SEvalZero 			if (!req || req == TRANS_CTL_OP_FRIEND_POLL) {
770*042d53a7SEvalZero 				send_friend_poll();
771*042d53a7SEvalZero 			} else {
772*042d53a7SEvalZero 				sub_update(req);
773*042d53a7SEvalZero 			}
774*042d53a7SEvalZero 
775*042d53a7SEvalZero 			break;
776*042d53a7SEvalZero 		}
777*042d53a7SEvalZero 
778*042d53a7SEvalZero 		BT_ERR("No response from Friend after %u retries",
779*042d53a7SEvalZero 		       lpn->req_attempts);
780*042d53a7SEvalZero 		lpn->req_attempts = 0;
781*042d53a7SEvalZero 		clear_friendship(false, false);
782*042d53a7SEvalZero 		break;
783*042d53a7SEvalZero 	case BT_MESH_LPN_RECV_DELAY:
784*042d53a7SEvalZero 		k_delayed_work_submit(&lpn->timer,
785*042d53a7SEvalZero 				      lpn->adv_duration + SCAN_LATENCY +
786*042d53a7SEvalZero 				      lpn->recv_win);
787*042d53a7SEvalZero 		bt_mesh_scan_enable();
788*042d53a7SEvalZero 		lpn_set_state(BT_MESH_LPN_WAIT_UPDATE);
789*042d53a7SEvalZero 		break;
790*042d53a7SEvalZero 	case BT_MESH_LPN_WAIT_UPDATE:
791*042d53a7SEvalZero 		update_timeout(lpn);
792*042d53a7SEvalZero 		break;
793*042d53a7SEvalZero 	default:
794*042d53a7SEvalZero 		__ASSERT(0, "Unhandled LPN state");
795*042d53a7SEvalZero 		break;
796*042d53a7SEvalZero 	}
797*042d53a7SEvalZero }
798*042d53a7SEvalZero 
bt_mesh_lpn_group_add(u16_t group)799*042d53a7SEvalZero void bt_mesh_lpn_group_add(u16_t group)
800*042d53a7SEvalZero {
801*042d53a7SEvalZero 	BT_DBG("group 0x%04x", group);
802*042d53a7SEvalZero 
803*042d53a7SEvalZero 	lpn_group_add(group);
804*042d53a7SEvalZero 
805*042d53a7SEvalZero 	if (!bt_mesh_lpn_established() || bt_mesh.lpn.sent_req) {
806*042d53a7SEvalZero 		return;
807*042d53a7SEvalZero 	}
808*042d53a7SEvalZero 
809*042d53a7SEvalZero 	sub_update(TRANS_CTL_OP_FRIEND_SUB_ADD);
810*042d53a7SEvalZero }
811*042d53a7SEvalZero 
bt_mesh_lpn_group_del(u16_t * groups,size_t group_count)812*042d53a7SEvalZero void bt_mesh_lpn_group_del(u16_t *groups, size_t group_count)
813*042d53a7SEvalZero {
814*042d53a7SEvalZero 	int i;
815*042d53a7SEvalZero 
816*042d53a7SEvalZero 	for (i = 0; i < group_count; i++) {
817*042d53a7SEvalZero 		if (groups[i] != BT_MESH_ADDR_UNASSIGNED) {
818*042d53a7SEvalZero 			BT_DBG("group 0x%04x", groups[i]);
819*042d53a7SEvalZero 			lpn_group_del(groups[i]);
820*042d53a7SEvalZero 		}
821*042d53a7SEvalZero 	}
822*042d53a7SEvalZero 
823*042d53a7SEvalZero 	if (!bt_mesh_lpn_established() || bt_mesh.lpn.sent_req) {
824*042d53a7SEvalZero 		return;
825*042d53a7SEvalZero 	}
826*042d53a7SEvalZero 
827*042d53a7SEvalZero 	sub_update(TRANS_CTL_OP_FRIEND_SUB_REM);
828*042d53a7SEvalZero }
829*042d53a7SEvalZero 
poll_timeout(struct bt_mesh_lpn * lpn)830*042d53a7SEvalZero static s32_t poll_timeout(struct bt_mesh_lpn *lpn)
831*042d53a7SEvalZero {
832*042d53a7SEvalZero 	/* If we're waiting for segment acks keep polling at high freq */
833*042d53a7SEvalZero 	if (bt_mesh_tx_in_progress()) {
834*042d53a7SEvalZero 		return min(POLL_TIMEOUT_MAX(lpn), K_SECONDS(1));
835*042d53a7SEvalZero 	}
836*042d53a7SEvalZero 
837*042d53a7SEvalZero 	if (lpn->poll_timeout < POLL_TIMEOUT_MAX(lpn)) {
838*042d53a7SEvalZero 		lpn->poll_timeout *= 2;
839*042d53a7SEvalZero 		lpn->poll_timeout = min(lpn->poll_timeout,
840*042d53a7SEvalZero 					POLL_TIMEOUT_MAX(lpn));
841*042d53a7SEvalZero 	}
842*042d53a7SEvalZero 
843*042d53a7SEvalZero 	BT_DBG("Poll Timeout is %ums", lpn->poll_timeout);
844*042d53a7SEvalZero 
845*042d53a7SEvalZero 	return lpn->poll_timeout;
846*042d53a7SEvalZero }
847*042d53a7SEvalZero 
bt_mesh_lpn_friend_sub_cfm(struct bt_mesh_net_rx * rx,struct os_mbuf * buf)848*042d53a7SEvalZero int bt_mesh_lpn_friend_sub_cfm(struct bt_mesh_net_rx *rx,
849*042d53a7SEvalZero 			       struct os_mbuf *buf)
850*042d53a7SEvalZero {
851*042d53a7SEvalZero 	struct bt_mesh_ctl_friend_sub_confirm *msg = (void *)buf->om_data;
852*042d53a7SEvalZero 	struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
853*042d53a7SEvalZero 
854*042d53a7SEvalZero 	if (buf->om_len < sizeof(*msg)) {
855*042d53a7SEvalZero 		BT_WARN("Too short Friend Subscription Confirm");
856*042d53a7SEvalZero 		return -EINVAL;
857*042d53a7SEvalZero 	}
858*042d53a7SEvalZero 
859*042d53a7SEvalZero 	BT_DBG("xact 0x%02x", msg->xact);
860*042d53a7SEvalZero 
861*042d53a7SEvalZero 	if (!lpn->sent_req) {
862*042d53a7SEvalZero 		BT_WARN("No pending subscription list message");
863*042d53a7SEvalZero 		return 0;
864*042d53a7SEvalZero 	}
865*042d53a7SEvalZero 
866*042d53a7SEvalZero 	if (msg->xact != lpn->xact_pending) {
867*042d53a7SEvalZero 		BT_WARN("Transaction mismatch (0x%02x != 0x%02x)",
868*042d53a7SEvalZero 			msg->xact, lpn->xact_pending);
869*042d53a7SEvalZero 		return 0;
870*042d53a7SEvalZero 	}
871*042d53a7SEvalZero 
872*042d53a7SEvalZero 	if (lpn->sent_req == TRANS_CTL_OP_FRIEND_SUB_ADD) {
873*042d53a7SEvalZero 		group_set(lpn->added, lpn->pending);
874*042d53a7SEvalZero 		group_zero(lpn->pending);
875*042d53a7SEvalZero 	} else if (lpn->sent_req == TRANS_CTL_OP_FRIEND_SUB_REM) {
876*042d53a7SEvalZero 		int i;
877*042d53a7SEvalZero 
878*042d53a7SEvalZero 		group_clear(lpn->added, lpn->pending);
879*042d53a7SEvalZero 
880*042d53a7SEvalZero 		for (i = 0; i < ARRAY_SIZE(lpn->groups); i++) {
881*042d53a7SEvalZero 			if (atomic_test_and_clear_bit(lpn->pending, i) &&
882*042d53a7SEvalZero 			    atomic_test_and_clear_bit(lpn->to_remove, i)) {
883*042d53a7SEvalZero 				lpn->groups[i] = BT_MESH_ADDR_UNASSIGNED;
884*042d53a7SEvalZero 			}
885*042d53a7SEvalZero 		}
886*042d53a7SEvalZero 	} else {
887*042d53a7SEvalZero 		BT_WARN("Unexpected Friend Subscription Confirm");
888*042d53a7SEvalZero 		return 0;
889*042d53a7SEvalZero 	}
890*042d53a7SEvalZero 
891*042d53a7SEvalZero 	friend_response_received(lpn);
892*042d53a7SEvalZero 
893*042d53a7SEvalZero 	if (lpn->groups_changed) {
894*042d53a7SEvalZero 		sub_update(TRANS_CTL_OP_FRIEND_SUB_ADD);
895*042d53a7SEvalZero 		sub_update(TRANS_CTL_OP_FRIEND_SUB_REM);
896*042d53a7SEvalZero 
897*042d53a7SEvalZero 		if (!lpn->sent_req) {
898*042d53a7SEvalZero 			lpn->groups_changed = 0;
899*042d53a7SEvalZero 		}
900*042d53a7SEvalZero 	}
901*042d53a7SEvalZero 
902*042d53a7SEvalZero 	if (lpn->pending_poll) {
903*042d53a7SEvalZero 		send_friend_poll();
904*042d53a7SEvalZero 	}
905*042d53a7SEvalZero 
906*042d53a7SEvalZero 	if (!lpn->sent_req) {
907*042d53a7SEvalZero 		k_delayed_work_submit(&lpn->timer, poll_timeout(lpn));
908*042d53a7SEvalZero 	}
909*042d53a7SEvalZero 
910*042d53a7SEvalZero 	return 0;
911*042d53a7SEvalZero }
912*042d53a7SEvalZero 
bt_mesh_lpn_friend_update(struct bt_mesh_net_rx * rx,struct os_mbuf * buf)913*042d53a7SEvalZero int bt_mesh_lpn_friend_update(struct bt_mesh_net_rx *rx,
914*042d53a7SEvalZero 			      struct os_mbuf *buf)
915*042d53a7SEvalZero {
916*042d53a7SEvalZero 	struct bt_mesh_ctl_friend_update *msg = (void *)buf->om_data;
917*042d53a7SEvalZero 	struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
918*042d53a7SEvalZero 	struct bt_mesh_subnet *sub = rx->sub;
919*042d53a7SEvalZero 	u32_t iv_index;
920*042d53a7SEvalZero 
921*042d53a7SEvalZero 	if (buf->om_len < sizeof(*msg)) {
922*042d53a7SEvalZero 		BT_WARN("Too short Friend Update");
923*042d53a7SEvalZero 		return -EINVAL;
924*042d53a7SEvalZero 	}
925*042d53a7SEvalZero 
926*042d53a7SEvalZero 	if (lpn->sent_req != TRANS_CTL_OP_FRIEND_POLL) {
927*042d53a7SEvalZero 		BT_WARN("Unexpected friend update");
928*042d53a7SEvalZero 		return 0;
929*042d53a7SEvalZero 	}
930*042d53a7SEvalZero 
931*042d53a7SEvalZero 	if (sub->kr_phase == BT_MESH_KR_PHASE_2 && !rx->new_key) {
932*042d53a7SEvalZero 		BT_WARN("Ignoring Phase 2 KR Update secured using old key");
933*042d53a7SEvalZero 		return 0;
934*042d53a7SEvalZero 	}
935*042d53a7SEvalZero 
936*042d53a7SEvalZero 	if (bt_mesh.ivu_initiator &&
937*042d53a7SEvalZero 	    bt_mesh.iv_update == BT_MESH_IV_UPDATE(msg->flags)) {
938*042d53a7SEvalZero 		bt_mesh_beacon_ivu_initiator(false);
939*042d53a7SEvalZero 	}
940*042d53a7SEvalZero 
941*042d53a7SEvalZero 	if (!lpn->established) {
942*042d53a7SEvalZero 		/* This is normally checked on the transport layer, however
943*042d53a7SEvalZero 		 * in this state we're also still accepting master
944*042d53a7SEvalZero 		 * credentials so we need to ensure the right ones (Friend
945*042d53a7SEvalZero 		 * Credentials) were used for this message.
946*042d53a7SEvalZero 		 */
947*042d53a7SEvalZero 		if (!rx->friend_cred) {
948*042d53a7SEvalZero 			BT_WARN("Friend Update with wrong credentials");
949*042d53a7SEvalZero 			return -EINVAL;
950*042d53a7SEvalZero 		}
951*042d53a7SEvalZero 
952*042d53a7SEvalZero 		lpn->established = 1;
953*042d53a7SEvalZero 
954*042d53a7SEvalZero 		BT_INFO("Friendship established with 0x%04x", lpn->frnd);
955*042d53a7SEvalZero 
956*042d53a7SEvalZero 		if (lpn_cb) {
957*042d53a7SEvalZero 			lpn_cb(lpn->frnd, true);
958*042d53a7SEvalZero 		}
959*042d53a7SEvalZero 
960*042d53a7SEvalZero 		/* Set initial poll timeout */
961*042d53a7SEvalZero 		lpn->poll_timeout = min(POLL_TIMEOUT_MAX(lpn),
962*042d53a7SEvalZero 					POLL_TIMEOUT_INIT);
963*042d53a7SEvalZero 	}
964*042d53a7SEvalZero 
965*042d53a7SEvalZero 	friend_response_received(lpn);
966*042d53a7SEvalZero 
967*042d53a7SEvalZero 	iv_index = sys_be32_to_cpu(msg->iv_index);
968*042d53a7SEvalZero 
969*042d53a7SEvalZero 	BT_DBG("flags 0x%02x iv_index 0x%08x md %u", msg->flags, iv_index,
970*042d53a7SEvalZero 	       msg->md);
971*042d53a7SEvalZero 
972*042d53a7SEvalZero 	if (bt_mesh_kr_update(sub, BT_MESH_KEY_REFRESH(msg->flags),
973*042d53a7SEvalZero 			      rx->new_key)) {
974*042d53a7SEvalZero 		bt_mesh_net_beacon_update(sub);
975*042d53a7SEvalZero 	}
976*042d53a7SEvalZero 
977*042d53a7SEvalZero 	bt_mesh_net_iv_update(iv_index, BT_MESH_IV_UPDATE(msg->flags));
978*042d53a7SEvalZero 
979*042d53a7SEvalZero 	if (lpn->groups_changed) {
980*042d53a7SEvalZero 		sub_update(TRANS_CTL_OP_FRIEND_SUB_ADD);
981*042d53a7SEvalZero 		sub_update(TRANS_CTL_OP_FRIEND_SUB_REM);
982*042d53a7SEvalZero 
983*042d53a7SEvalZero 		if (!lpn->sent_req) {
984*042d53a7SEvalZero 			lpn->groups_changed = 0;
985*042d53a7SEvalZero 		}
986*042d53a7SEvalZero 	}
987*042d53a7SEvalZero 
988*042d53a7SEvalZero 	if (msg->md) {
989*042d53a7SEvalZero 		BT_DBG("Requesting for more messages");
990*042d53a7SEvalZero 		send_friend_poll();
991*042d53a7SEvalZero 	}
992*042d53a7SEvalZero 
993*042d53a7SEvalZero 	if (!lpn->sent_req) {
994*042d53a7SEvalZero 		k_delayed_work_submit(&lpn->timer, poll_timeout(lpn));
995*042d53a7SEvalZero 	}
996*042d53a7SEvalZero 
997*042d53a7SEvalZero 	return 0;
998*042d53a7SEvalZero }
999*042d53a7SEvalZero 
bt_mesh_lpn_poll(void)1000*042d53a7SEvalZero int bt_mesh_lpn_poll(void)
1001*042d53a7SEvalZero {
1002*042d53a7SEvalZero 	if (!bt_mesh.lpn.established) {
1003*042d53a7SEvalZero 		return -EAGAIN;
1004*042d53a7SEvalZero 	}
1005*042d53a7SEvalZero 
1006*042d53a7SEvalZero 	BT_DBG("Requesting more messages");
1007*042d53a7SEvalZero 
1008*042d53a7SEvalZero 	return send_friend_poll();
1009*042d53a7SEvalZero }
1010*042d53a7SEvalZero 
bt_mesh_lpn_set_cb(void (* cb)(u16_t friend_addr,bool established))1011*042d53a7SEvalZero void bt_mesh_lpn_set_cb(void (*cb)(u16_t friend_addr, bool established))
1012*042d53a7SEvalZero {
1013*042d53a7SEvalZero 	lpn_cb = cb;
1014*042d53a7SEvalZero }
1015*042d53a7SEvalZero 
bt_mesh_lpn_init(void)1016*042d53a7SEvalZero int bt_mesh_lpn_init(void)
1017*042d53a7SEvalZero {
1018*042d53a7SEvalZero 	struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
1019*042d53a7SEvalZero 
1020*042d53a7SEvalZero 	BT_DBG("");
1021*042d53a7SEvalZero 
1022*042d53a7SEvalZero 	k_delayed_work_init(&lpn->timer, lpn_timeout);
1023*042d53a7SEvalZero 
1024*042d53a7SEvalZero 	if (lpn->state == BT_MESH_LPN_ENABLED) {
1025*042d53a7SEvalZero 		if (IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) {
1026*042d53a7SEvalZero 			bt_mesh_scan_disable();
1027*042d53a7SEvalZero 		} else {
1028*042d53a7SEvalZero 			bt_mesh_scan_enable();
1029*042d53a7SEvalZero 		}
1030*042d53a7SEvalZero 
1031*042d53a7SEvalZero 		send_friend_req(lpn);
1032*042d53a7SEvalZero 	} else {
1033*042d53a7SEvalZero 		bt_mesh_scan_enable();
1034*042d53a7SEvalZero 
1035*042d53a7SEvalZero 		if (IS_ENABLED(CONFIG_BT_MESH_LPN_AUTO)) {
1036*042d53a7SEvalZero 			BT_DBG("Waiting %u ms for messages", LPN_AUTO_TIMEOUT);
1037*042d53a7SEvalZero 			lpn_set_state(BT_MESH_LPN_TIMER);
1038*042d53a7SEvalZero 			k_delayed_work_submit(&lpn->timer, LPN_AUTO_TIMEOUT);
1039*042d53a7SEvalZero 		}
1040*042d53a7SEvalZero 	}
1041*042d53a7SEvalZero 
1042*042d53a7SEvalZero 	return 0;
1043*042d53a7SEvalZero }
1044*042d53a7SEvalZero 
1045*042d53a7SEvalZero #endif //MYNEWT_VAL(BLE_MESH_LOW_POWER) == 1
1046