1 /*
2 * Copyright (c) 2021 Xiaomi Corporation
3 * Copyright (c) 2018 Nordic Semiconductor ASA
4 * Copyright (c) 2017 Intel Corporation
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 #include <zephyr/kernel.h>
10 #include <zephyr/debug/stack.h>
11 #include <zephyr/sys/iterable_sections.h>
12 #include <zephyr/net_buf.h>
13 #include <zephyr/bluetooth/bluetooth.h>
14 #include <zephyr/bluetooth/hci.h>
15 #include <zephyr/bluetooth/mesh.h>
16
17 #include "common/bt_str.h"
18
19 #include "host/hci_core.h"
20
21 #include "net.h"
22 #include "proxy.h"
23 #include "solicitation.h"
24
25 #define LOG_LEVEL CONFIG_BT_MESH_ADV_LOG_LEVEL
26 #include <zephyr/logging/log.h>
27 LOG_MODULE_REGISTER(bt_mesh_adv_ext);
28
29 /* Convert from ms to 0.625ms units */
30 #define ADV_INT_FAST_MS 20
31
32 #ifndef CONFIG_BT_MESH_RELAY_ADV_SETS
33 #define CONFIG_BT_MESH_RELAY_ADV_SETS 0
34 #endif
35
36 #ifdef CONFIG_BT_MESH_ADV_STACK_SIZE
37 #define MESH_WORKQ_PRIORITY CONFIG_BT_MESH_ADV_PRIO
38 #define MESH_WORKQ_STACK_SIZE CONFIG_BT_MESH_ADV_STACK_SIZE
39 #else
40 #define MESH_WORKQ_PRIORITY 0
41 #define MESH_WORKQ_STACK_SIZE 0
42 #endif
43
44 enum {
45 /** Controller is currently advertising */
46 ADV_FLAG_ACTIVE,
47 /** Advertising sending completed */
48 ADV_FLAG_SENT,
49 /** Currently performing proxy advertising */
50 ADV_FLAG_PROXY,
51 /** Custom adv params have been set, we need to update the parameters on
52 * the next send.
53 */
54 ADV_FLAG_UPDATE_PARAMS,
55
56 /** The advertiser is suspending. */
57 ADV_FLAG_SUSPENDING,
58
59 /* Number of adv flags. */
60 ADV_FLAGS_NUM
61 };
62
63 struct bt_mesh_ext_adv {
64 const enum bt_mesh_adv_tag_bit tags;
65 ATOMIC_DEFINE(flags, ADV_FLAGS_NUM);
66 struct bt_le_ext_adv *instance;
67 struct bt_mesh_adv *adv;
68 uint32_t timestamp;
69 struct k_work work;
70 struct bt_le_adv_param adv_param;
71 };
72
73 static void send_pending_adv(struct k_work *work);
74 static bool schedule_send(struct bt_mesh_ext_adv *ext_adv);
75
76 static struct k_work_q bt_mesh_workq;
77 static K_KERNEL_STACK_DEFINE(thread_stack, MESH_WORKQ_STACK_SIZE);
78
79 #if defined(CONFIG_BT_MESH_WORKQ_MESH)
80 #define MESH_WORKQ &bt_mesh_workq
81 #else /* CONFIG_BT_MESH_WORKQ_SYS */
82 #define MESH_WORKQ &k_sys_work_q
83 #endif /* CONFIG_BT_MESH_WORKQ_MESH */
84
85 static struct bt_mesh_ext_adv advs[] = {
86 [0] = {
87 .tags = (
88 #if !defined(CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE)
89 BT_MESH_ADV_TAG_BIT_FRIEND |
90 #endif
91 #if !defined(CONFIG_BT_MESH_ADV_EXT_GATT_SEPARATE)
92 BT_MESH_ADV_TAG_BIT_PROXY |
93 #endif /* !CONFIG_BT_MESH_ADV_EXT_GATT_SEPARATE */
94 #if defined(CONFIG_BT_MESH_ADV_EXT_RELAY_USING_MAIN_ADV_SET)
95 BT_MESH_ADV_TAG_BIT_RELAY |
96 #endif /* CONFIG_BT_MESH_ADV_EXT_RELAY_USING_MAIN_ADV_SET */
97 #if defined(CONFIG_BT_MESH_PB_ADV)
98 BT_MESH_ADV_TAG_BIT_PROV |
99 #endif /* CONFIG_BT_MESH_PB_ADV */
100 BT_MESH_ADV_TAG_BIT_LOCAL
101 ),
102 .work = Z_WORK_INITIALIZER(send_pending_adv),
103 },
104 #if CONFIG_BT_MESH_RELAY_ADV_SETS
105 [1 ... CONFIG_BT_MESH_RELAY_ADV_SETS] = {
106 .tags = (
107 #if (defined(CONFIG_BT_MESH_RELAY) || defined(CONFIG_BT_MESH_BRG_CFG_SRV))
108 BT_MESH_ADV_TAG_BIT_RELAY |
109 #endif /* CONFIG_BT_MESH_RELAY || CONFIG_BT_MESH_BRG_CFG_SRV */
110 #if defined(CONFIG_BT_MESH_PB_ADV_USE_RELAY_SETS)
111 BT_MESH_ADV_TAG_BIT_PROV |
112 #endif /* CONFIG_BT_MESH_PB_ADV_USE_RELAY_SETS */
113 0),
114 .work = Z_WORK_INITIALIZER(send_pending_adv),
115 },
116 #endif /* CONFIG_BT_MESH_RELAY_ADV_SETS */
117 #if defined(CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE)
118 {
119 .tags = BT_MESH_ADV_TAG_BIT_FRIEND,
120 .work = Z_WORK_INITIALIZER(send_pending_adv),
121 },
122 #endif /* CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE */
123 #if defined(CONFIG_BT_MESH_ADV_EXT_GATT_SEPARATE)
124 {
125 .tags = BT_MESH_ADV_TAG_BIT_PROXY,
126 .work = Z_WORK_INITIALIZER(send_pending_adv),
127 },
128 #endif /* CONFIG_BT_MESH_ADV_EXT_GATT_SEPARATE */
129 };
130
131 BUILD_ASSERT(ARRAY_SIZE(advs) <= CONFIG_BT_EXT_ADV_MAX_ADV_SET,
132 "Insufficient adv instances");
133
relay_adv_get(void)134 static inline struct bt_mesh_ext_adv *relay_adv_get(void)
135 {
136 if (!!(CONFIG_BT_MESH_RELAY_ADV_SETS)) {
137 return &advs[1];
138 } else {
139 return &advs[0];
140 }
141 }
142
gatt_adv_get(void)143 static inline struct bt_mesh_ext_adv *gatt_adv_get(void)
144 {
145 if (IS_ENABLED(CONFIG_BT_MESH_ADV_EXT_GATT_SEPARATE)) {
146 return &advs[ARRAY_SIZE(advs) - 1];
147 } else {
148 return &advs[0];
149 }
150 }
151
adv_start(struct bt_mesh_ext_adv * ext_adv,const struct bt_le_adv_param * param,struct bt_le_ext_adv_start_param * start,const struct bt_data * ad,size_t ad_len,const struct bt_data * sd,size_t sd_len)152 static int adv_start(struct bt_mesh_ext_adv *ext_adv,
153 const struct bt_le_adv_param *param,
154 struct bt_le_ext_adv_start_param *start,
155 const struct bt_data *ad, size_t ad_len,
156 const struct bt_data *sd, size_t sd_len)
157 {
158 int err;
159
160 if (!ext_adv->instance) {
161 LOG_ERR("Mesh advertiser not enabled");
162 return -ENODEV;
163 }
164
165 if (atomic_test_and_set_bit(ext_adv->flags, ADV_FLAG_ACTIVE)) {
166 LOG_ERR("Advertiser is busy");
167 return -EBUSY;
168 }
169
170 if (atomic_test_bit(ext_adv->flags, ADV_FLAG_UPDATE_PARAMS)) {
171 err = bt_le_ext_adv_update_param(ext_adv->instance, param);
172 if (err) {
173 LOG_ERR("Failed updating adv params: %d", err);
174 atomic_clear_bit(ext_adv->flags, ADV_FLAG_ACTIVE);
175 return err;
176 }
177
178 atomic_set_bit_to(ext_adv->flags, ADV_FLAG_UPDATE_PARAMS,
179 param != &ext_adv->adv_param);
180 }
181
182 err = bt_le_ext_adv_set_data(ext_adv->instance, ad, ad_len, sd, sd_len);
183 if (err) {
184 LOG_ERR("Failed setting adv data: %d", err);
185 atomic_clear_bit(ext_adv->flags, ADV_FLAG_ACTIVE);
186 return err;
187 }
188
189 ext_adv->timestamp = k_uptime_get_32();
190
191 err = bt_le_ext_adv_start(ext_adv->instance, start);
192 if (err) {
193 LOG_ERR("Advertising failed: err %d", err);
194 atomic_clear_bit(ext_adv->flags, ADV_FLAG_ACTIVE);
195 }
196
197 return err;
198 }
199
bt_data_send(struct bt_mesh_ext_adv * ext_adv,uint8_t num_events,uint16_t adv_interval,const struct bt_data * ad,size_t ad_len)200 static int bt_data_send(struct bt_mesh_ext_adv *ext_adv, uint8_t num_events, uint16_t adv_interval,
201 const struct bt_data *ad, size_t ad_len)
202 {
203 struct bt_le_ext_adv_start_param start = {
204 .num_events = num_events,
205 };
206
207 adv_interval = MAX(ADV_INT_FAST_MS, adv_interval);
208
209 /* Only update advertising parameters if they're different */
210 if (ext_adv->adv_param.interval_min != BT_MESH_ADV_SCAN_UNIT(adv_interval)) {
211 ext_adv->adv_param.interval_min = BT_MESH_ADV_SCAN_UNIT(adv_interval);
212 ext_adv->adv_param.interval_max = ext_adv->adv_param.interval_min;
213 atomic_set_bit(ext_adv->flags, ADV_FLAG_UPDATE_PARAMS);
214 }
215
216 return adv_start(ext_adv, &ext_adv->adv_param, &start, ad, ad_len, NULL, 0);
217 }
218
adv_send(struct bt_mesh_ext_adv * ext_adv,struct bt_mesh_adv * adv)219 static int adv_send(struct bt_mesh_ext_adv *ext_adv, struct bt_mesh_adv *adv)
220 {
221 uint8_t num_events = BT_MESH_TRANSMIT_COUNT(adv->ctx.xmit) + 1;
222 uint16_t duration, adv_int;
223 struct bt_data ad;
224 int err;
225
226 adv_int = BT_MESH_TRANSMIT_INT(adv->ctx.xmit);
227 /* Upper boundary estimate: */
228 duration = num_events * (adv_int + 10);
229
230 LOG_DBG("type %u len %u: %s", adv->ctx.type,
231 adv->b.len, bt_hex(adv->b.data, adv->b.len));
232 LOG_DBG("count %u interval %ums duration %ums",
233 num_events, adv_int, duration);
234
235 ad.type = bt_mesh_adv_type[adv->ctx.type];
236 ad.data_len = adv->b.len;
237 ad.data = adv->b.data;
238
239 err = bt_data_send(ext_adv, num_events, adv_int, &ad, 1);
240 if (!err) {
241 ext_adv->adv = bt_mesh_adv_ref(adv);
242 }
243
244 bt_mesh_adv_send_start(duration, err, &adv->ctx);
245
246 return err;
247 }
248
stop_proxy_adv(struct bt_mesh_ext_adv * ext_adv)249 static int stop_proxy_adv(struct bt_mesh_ext_adv *ext_adv)
250 {
251 int err;
252
253 if (atomic_test_bit(ext_adv->flags, ADV_FLAG_PROXY)) {
254 err = bt_le_ext_adv_stop(ext_adv->instance);
255 if (err) {
256 LOG_ERR("Failed to stop proxy advertising: %d", err);
257 return err;
258 }
259
260 atomic_clear_bit(ext_adv->flags, ADV_FLAG_PROXY);
261 atomic_clear_bit(ext_adv->flags, ADV_FLAG_ACTIVE);
262 }
263
264 return 0;
265 }
266
adv_queue_send_process(struct bt_mesh_ext_adv * ext_adv)267 static int adv_queue_send_process(struct bt_mesh_ext_adv *ext_adv)
268 {
269 struct bt_mesh_adv *adv;
270 int err = -ENOENT;
271
272 while ((adv = bt_mesh_adv_get_by_tag(ext_adv->tags, K_NO_WAIT))) {
273 /* busy == 0 means this was canceled */
274 if (!adv->ctx.busy) {
275 bt_mesh_adv_unref(adv);
276 continue;
277 }
278
279 if (stop_proxy_adv(ext_adv)) {
280 LOG_WRN("Advertising %p canceled due to proxy adv failed to stop", adv);
281 bt_mesh_adv_send_start(0, -ECANCELED, &adv->ctx);
282 bt_mesh_adv_unref(adv);
283 continue;
284 }
285
286 adv->ctx.busy = 0U;
287 err = adv_send(ext_adv, adv);
288
289 bt_mesh_adv_unref(adv);
290
291 if (!err) {
292 return 0; /* Wait for advertising to finish */
293 }
294 }
295
296 return err;
297 }
298
start_proxy_sol_or_proxy_adv(struct bt_mesh_ext_adv * ext_adv)299 static void start_proxy_sol_or_proxy_adv(struct bt_mesh_ext_adv *ext_adv)
300 {
301 if (ext_adv->instance == NULL) {
302 LOG_DBG("Advertiser is suspended or deleted");
303 return;
304 }
305
306 if (!(ext_adv->tags & BT_MESH_ADV_TAG_BIT_PROXY)) {
307 return;
308 }
309
310 if (IS_ENABLED(CONFIG_BT_MESH_PROXY_SOLICITATION)) {
311 if (stop_proxy_adv(ext_adv)) {
312 return;
313 }
314
315 if (!bt_mesh_sol_send()) {
316 return;
317 }
318 }
319
320 if (IS_ENABLED(CONFIG_BT_MESH_GATT_SERVER) &&
321 !atomic_test_and_set_bit(ext_adv->flags, ADV_FLAG_PROXY)) {
322 if (bt_mesh_adv_gatt_send()) {
323 atomic_clear_bit(ext_adv->flags, ADV_FLAG_PROXY);
324 return;
325 }
326 }
327 }
328
send_pending_adv(struct k_work * work)329 static void send_pending_adv(struct k_work *work)
330 {
331 static const char * const adv_tag_to_str[] = {
332 [BT_MESH_ADV_TAG_LOCAL] = "local",
333 [BT_MESH_ADV_TAG_RELAY] = "relay",
334 [BT_MESH_ADV_TAG_PROXY] = "proxy",
335 [BT_MESH_ADV_TAG_FRIEND] = "friend",
336 [BT_MESH_ADV_TAG_PROV] = "prov",
337 };
338 struct bt_mesh_ext_adv *ext_adv;
339 int err;
340
341 ext_adv = CONTAINER_OF(work, struct bt_mesh_ext_adv, work);
342
343 if (atomic_test_bit(ext_adv->flags, ADV_FLAG_SUSPENDING)) {
344 LOG_DBG("Advertiser is suspending");
345 return;
346 }
347
348 if (atomic_test_and_clear_bit(ext_adv->flags, ADV_FLAG_SENT)) {
349 LOG_DBG("Advertising stopped after %u ms for %s adv",
350 k_uptime_get_32() - ext_adv->timestamp,
351 ext_adv->adv ? adv_tag_to_str[ext_adv->adv->ctx.tag]
352 : adv_tag_to_str[BT_MESH_ADV_TAG_PROXY]);
353
354 atomic_clear_bit(ext_adv->flags, ADV_FLAG_ACTIVE);
355 atomic_clear_bit(ext_adv->flags, ADV_FLAG_PROXY);
356
357 if (ext_adv->adv) {
358 struct bt_mesh_adv_ctx ctx = ext_adv->adv->ctx;
359
360 ext_adv->adv->ctx.started = 0;
361 bt_mesh_adv_unref(ext_adv->adv);
362 bt_mesh_adv_send_end(0, &ctx);
363
364 ext_adv->adv = NULL;
365 }
366 }
367
368 err = adv_queue_send_process(ext_adv);
369 if (!err) {
370 return;
371 }
372
373 start_proxy_sol_or_proxy_adv(ext_adv);
374 }
375
schedule_send(struct bt_mesh_ext_adv * ext_adv)376 static bool schedule_send(struct bt_mesh_ext_adv *ext_adv)
377 {
378 if (atomic_test_bit(ext_adv->flags, ADV_FLAG_ACTIVE)) {
379 /* We don't need to resubmit the `send_pending_adv` work if the mesh advertiser
380 * is sending a mesh packet. The `send_pending_adv` work is resubmitted when the
381 * current advertising is finished, which is done through the `adv_sent` callback.
382 *
383 * The proxy advertisement in turns doesn't timeout or stop quickly and has less
384 * priority than regular mesh messages, thus needs to be stopped immeditaly.
385 */
386 if (!atomic_test_bit(ext_adv->flags, ADV_FLAG_PROXY)) {
387 return false;
388 }
389 }
390
391 bt_mesh_wq_submit(&ext_adv->work);
392
393 return true;
394 }
395
bt_mesh_adv_gatt_update(void)396 void bt_mesh_adv_gatt_update(void)
397 {
398 (void)schedule_send(gatt_adv_get());
399 }
400
bt_mesh_adv_local_ready(void)401 void bt_mesh_adv_local_ready(void)
402 {
403 (void)schedule_send(advs);
404 }
405
bt_mesh_adv_relay_ready(void)406 void bt_mesh_adv_relay_ready(void)
407 {
408 struct bt_mesh_ext_adv *ext_adv = relay_adv_get();
409
410 for (int i = 0; i < CONFIG_BT_MESH_RELAY_ADV_SETS; i++) {
411 if (schedule_send(&ext_adv[i])) {
412 return;
413 }
414 }
415
416 /* Use the main adv set for the sending of relay messages. */
417 if (IS_ENABLED(CONFIG_BT_MESH_ADV_EXT_RELAY_USING_MAIN_ADV_SET) ||
418 CONFIG_BT_MESH_RELAY_ADV_SETS == 0) {
419 (void)schedule_send(advs);
420 }
421 }
422
bt_mesh_adv_friend_ready(void)423 void bt_mesh_adv_friend_ready(void)
424 {
425 if (IS_ENABLED(CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE)) {
426 schedule_send(&advs[1 + CONFIG_BT_MESH_RELAY_ADV_SETS]);
427 } else {
428 schedule_send(&advs[0]);
429 }
430 }
431
bt_mesh_adv_terminate(struct bt_mesh_adv * adv)432 int bt_mesh_adv_terminate(struct bt_mesh_adv *adv)
433 {
434 int err;
435
436 for (int i = 0; i < ARRAY_SIZE(advs); i++) {
437 struct bt_mesh_ext_adv *ext_adv = &advs[i];
438
439 if (ext_adv->adv != adv) {
440 continue;
441 }
442
443 if (!atomic_test_bit(ext_adv->flags, ADV_FLAG_ACTIVE)) {
444 return 0;
445 }
446
447 err = bt_le_ext_adv_stop(ext_adv->instance);
448 if (err) {
449 LOG_ERR("Failed to stop adv %d", err);
450 return err;
451 }
452
453 /* Do not call `cb:end`, since this user action */
454 adv->ctx.cb = NULL;
455
456 atomic_set_bit(ext_adv->flags, ADV_FLAG_SENT);
457
458 bt_mesh_wq_submit(&ext_adv->work);
459
460 return 0;
461 }
462
463 return -EINVAL;
464 }
465
bt_mesh_adv_init(void)466 void bt_mesh_adv_init(void)
467 {
468 struct bt_le_adv_param adv_param = {
469 .id = BT_ID_DEFAULT,
470 .interval_min = BT_MESH_ADV_SCAN_UNIT(ADV_INT_FAST_MS),
471 .interval_max = BT_MESH_ADV_SCAN_UNIT(ADV_INT_FAST_MS),
472 #if defined(CONFIG_BT_MESH_DEBUG_USE_ID_ADDR)
473 .options = BT_LE_ADV_OPT_USE_IDENTITY,
474 #endif
475 };
476
477 for (int i = 0; i < ARRAY_SIZE(advs); i++) {
478 (void)memcpy(&advs[i].adv_param, &adv_param, sizeof(adv_param));
479 }
480
481 if (IS_ENABLED(CONFIG_BT_MESH_WORKQ_MESH)) {
482 k_work_queue_init(&bt_mesh_workq);
483 k_work_queue_start(&bt_mesh_workq, thread_stack, MESH_WORKQ_STACK_SIZE,
484 K_PRIO_COOP(MESH_WORKQ_PRIORITY), NULL);
485 k_thread_name_set(&bt_mesh_workq.thread, "BT MESH WQ");
486 }
487 }
488
adv_instance_find(struct bt_le_ext_adv * instance)489 static struct bt_mesh_ext_adv *adv_instance_find(struct bt_le_ext_adv *instance)
490 {
491 for (int i = 0; i < ARRAY_SIZE(advs); i++) {
492 if (advs[i].instance == instance) {
493 return &advs[i];
494 }
495 }
496
497 return NULL;
498 }
499
adv_sent(struct bt_le_ext_adv * instance,struct bt_le_ext_adv_sent_info * info)500 static void adv_sent(struct bt_le_ext_adv *instance,
501 struct bt_le_ext_adv_sent_info *info)
502 {
503 struct bt_mesh_ext_adv *ext_adv = adv_instance_find(instance);
504
505 if (!ext_adv) {
506 LOG_WRN("Unexpected adv instance");
507 return;
508 }
509
510 if (!atomic_test_bit(ext_adv->flags, ADV_FLAG_ACTIVE)) {
511 LOG_DBG("Advertiser %p ADV_FLAG_ACTIVE not set", ext_adv);
512 return;
513 }
514
515 atomic_set_bit(ext_adv->flags, ADV_FLAG_SENT);
516
517 bt_mesh_wq_submit(&ext_adv->work);
518 }
519
bt_mesh_adv_enable(void)520 int bt_mesh_adv_enable(void)
521 {
522 int err;
523
524 static const struct bt_le_ext_adv_cb adv_cb = {
525 .sent = adv_sent,
526 };
527
528 if (advs[0].instance) {
529 /* Already initialized */
530 return 0;
531 }
532
533 for (int i = 0; i < ARRAY_SIZE(advs); i++) {
534 err = bt_le_ext_adv_create(&advs[i].adv_param, &adv_cb,
535 &advs[i].instance);
536 if (err) {
537 return err;
538 }
539 }
540
541 return 0;
542 }
543
bt_mesh_adv_disable(void)544 int bt_mesh_adv_disable(void)
545 {
546 struct k_work_sync sync;
547 int err;
548
549 for (int i = 0; i < ARRAY_SIZE(advs); i++) {
550 atomic_set_bit(advs[i].flags, ADV_FLAG_SUSPENDING);
551
552 if (k_current_get() != k_work_queue_thread_get(MESH_WORKQ) ||
553 (k_work_busy_get(&advs[i].work) & K_WORK_RUNNING) == 0) {
554 k_work_flush(&advs[i].work, &sync);
555 }
556
557 err = bt_le_ext_adv_stop(advs[i].instance);
558 if (err) {
559 LOG_ERR("Failed to stop adv %d", err);
560 return err;
561 }
562
563 /* `adv_sent` is called to finish transmission of an adv buffer that was pushed to
564 * the host before the advertiser was stopped, but did not finish.
565 */
566 adv_sent(advs[i].instance, NULL);
567
568 err = bt_le_ext_adv_delete(advs[i].instance);
569 if (err) {
570 LOG_ERR("Failed to delete adv %d", err);
571 return err;
572 }
573
574 advs[i].instance = NULL;
575
576 atomic_clear_bit(advs[i].flags, ADV_FLAG_SUSPENDING);
577 }
578
579 return 0;
580 }
581
bt_mesh_adv_gatt_start(const struct bt_le_adv_param * param,int32_t duration,const struct bt_data * ad,size_t ad_len,const struct bt_data * sd,size_t sd_len)582 int bt_mesh_adv_gatt_start(const struct bt_le_adv_param *param,
583 int32_t duration,
584 const struct bt_data *ad, size_t ad_len,
585 const struct bt_data *sd, size_t sd_len)
586 {
587 struct bt_mesh_ext_adv *ext_adv = gatt_adv_get();
588 struct bt_le_ext_adv_start_param start = {
589 /* Timeout is set in 10 ms steps, with 0 indicating "forever" */
590 .timeout = (duration == SYS_FOREVER_MS) ? 0 : MAX(1, duration / 10),
591 };
592
593 LOG_DBG("Start advertising %d ms", duration);
594
595 atomic_set_bit(ext_adv->flags, ADV_FLAG_UPDATE_PARAMS);
596
597 return adv_start(ext_adv, param, &start, ad, ad_len, sd, sd_len);
598 }
599
bt_mesh_adv_bt_data_send(uint8_t num_events,uint16_t adv_interval,const struct bt_data * ad,size_t ad_len)600 int bt_mesh_adv_bt_data_send(uint8_t num_events, uint16_t adv_interval,
601 const struct bt_data *ad, size_t ad_len)
602 {
603 return bt_data_send(advs, num_events, adv_interval, ad, ad_len);
604 }
605
bt_mesh_wq_submit(struct k_work * work)606 int bt_mesh_wq_submit(struct k_work *work)
607 {
608 return k_work_submit_to_queue(MESH_WORKQ, work);
609 }
610