1 /*
2 * Copyright Runtime.io 2018. All rights reserved.
3 * Copyright (c) 2022 Nordic Semiconductor ASA
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 /** @file
9 * @brief Bluetooth transport for the mcumgr SMP protocol.
10 */
11
12 #include <zephyr/kernel.h>
13 #include <zephyr/init.h>
14 #include <zephyr/bluetooth/bluetooth.h>
15 #include <zephyr/bluetooth/uuid.h>
16 #include <zephyr/bluetooth/gatt.h>
17 #include <zephyr/mgmt/mcumgr/mgmt/mgmt.h>
18 #include <zephyr/mgmt/mcumgr/smp/smp.h>
19 #include <zephyr/mgmt/mcumgr/transport/smp.h>
20 #include <zephyr/mgmt/mcumgr/transport/smp_bt.h>
21 #include <zephyr/mgmt/mcumgr/mgmt/handlers.h>
22 #include <errno.h>
23
24 #include <mgmt/mcumgr/transport/smp_internal.h>
25 #include <mgmt/mcumgr/transport/smp_reassembly.h>
26
27 #include <zephyr/logging/log.h>
28 LOG_MODULE_DECLARE(mcumgr_smp, CONFIG_MCUMGR_TRANSPORT_LOG_LEVEL);
29
30 #define RESTORE_TIME COND_CODE_1(CONFIG_MCUMGR_TRANSPORT_BT_CONN_PARAM_CONTROL, \
31 (CONFIG_MCUMGR_TRANSPORT_BT_CONN_PARAM_CONTROL_RESTORE_TIME), \
32 (0))
33 #define RETRY_TIME COND_CODE_1(CONFIG_MCUMGR_TRANSPORT_BT_CONN_PARAM_CONTROL, \
34 (CONFIG_MCUMGR_TRANSPORT_BT_CONN_PARAM_CONTROL_RETRY_TIME), \
35 (0))
36
37 #define CONN_PARAM_SMP COND_CODE_1(CONFIG_MCUMGR_TRANSPORT_BT_CONN_PARAM_CONTROL, \
38 BT_LE_CONN_PARAM( \
39 CONFIG_MCUMGR_TRANSPORT_BT_CONN_PARAM_CONTROL_MIN_INT, \
40 CONFIG_MCUMGR_TRANSPORT_BT_CONN_PARAM_CONTROL_MAX_INT, \
41 CONFIG_MCUMGR_TRANSPORT_BT_CONN_PARAM_CONTROL_LATENCY, \
42 CONFIG_MCUMGR_TRANSPORT_BT_CONN_PARAM_CONTROL_TIMEOUT), \
43 (NULL))
44 #define CONN_PARAM_PREF COND_CODE_1(CONFIG_MCUMGR_TRANSPORT_BT_CONN_PARAM_CONTROL, \
45 BT_LE_CONN_PARAM( \
46 CONFIG_BT_PERIPHERAL_PREF_MIN_INT, \
47 CONFIG_BT_PERIPHERAL_PREF_MAX_INT, \
48 CONFIG_BT_PERIPHERAL_PREF_LATENCY, \
49 CONFIG_BT_PERIPHERAL_PREF_TIMEOUT), \
50 (NULL))
51
52 /* Permission levels for GATT characteristics of the SMP service. */
53 #ifndef CONFIG_MCUMGR_TRANSPORT_BT_PERM_RW_AUTHEN
54 #define CONFIG_MCUMGR_TRANSPORT_BT_PERM_RW_AUTHEN 0
55 #endif
56 #ifndef CONFIG_MCUMGR_TRANSPORT_BT_PERM_RW_ENCRYPT
57 #define CONFIG_MCUMGR_TRANSPORT_BT_PERM_RW_ENCRYPT 0
58 #endif
59 #ifndef CONFIG_MCUMGR_TRANSPORT_BT_PERM_RW
60 #define CONFIG_MCUMGR_TRANSPORT_BT_PERM_RW 0
61 #endif
62
63 #define SMP_GATT_PERM ( \
64 CONFIG_MCUMGR_TRANSPORT_BT_PERM_RW_AUTHEN ? \
65 (BT_GATT_PERM_READ_AUTHEN | BT_GATT_PERM_WRITE_AUTHEN) : \
66 CONFIG_MCUMGR_TRANSPORT_BT_PERM_RW_ENCRYPT ? \
67 (BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT) : \
68 (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)) \
69
70 #define SMP_GATT_PERM_WRITE_MASK \
71 (BT_GATT_PERM_WRITE | BT_GATT_PERM_WRITE_ENCRYPT | BT_GATT_PERM_WRITE_AUTHEN)
72
73 /* Minimum number of bytes that must be able to be sent with a notification to a target device
74 * before giving up
75 */
76 #define SMP_BT_MINIMUM_MTU_SEND_FAILURE 20
77
78 #ifdef CONFIG_MCUMGR_TRANSPORT_BT_CONN_PARAM_CONTROL
79 /* Verification of SMP Connection Parameters configuration that is not possible in the Kconfig. */
80 BUILD_ASSERT((CONFIG_MCUMGR_TRANSPORT_BT_CONN_PARAM_CONTROL_TIMEOUT * 4U) >
81 ((1U + CONFIG_MCUMGR_TRANSPORT_BT_CONN_PARAM_CONTROL_LATENCY) *
82 CONFIG_MCUMGR_TRANSPORT_BT_CONN_PARAM_CONTROL_MAX_INT));
83 #endif
84
85 struct smp_bt_user_data {
86 struct bt_conn *conn;
87 uint8_t id;
88 };
89
90 /* Verification of user data being able to fit */
91 BUILD_ASSERT(sizeof(struct smp_bt_user_data) <= CONFIG_MCUMGR_TRANSPORT_NETBUF_USER_DATA_SIZE,
92 "CONFIG_MCUMGR_TRANSPORT_NETBUF_USER_DATA_SIZE not large enough to fit Bluetooth"
93 " user data");
94
95 enum {
96 CONN_PARAM_SMP_REQUESTED = BIT(0),
97 };
98
99 struct conn_param_data {
100 struct bt_conn *conn;
101 struct k_work_delayable dwork;
102 struct k_work_delayable ework;
103 uint8_t state;
104 uint8_t id;
105 struct k_sem smp_notify_sem;
106 };
107
108 static uint8_t next_id;
109 static struct smp_transport smp_bt_transport;
110 static struct conn_param_data conn_data[CONFIG_BT_MAX_CONN];
111
112 static void connected(struct bt_conn *conn, uint8_t err);
113 static void disconnected(struct bt_conn *conn, uint8_t reason);
114
115 /* Bluetooth connection callback handlers */
116 BT_CONN_CB_DEFINE(mcumgr_bt_callbacks) = {
117 .connected = connected,
118 .disconnected = disconnected,
119 };
120
121 #ifdef CONFIG_SMP_CLIENT
122 static struct smp_client_transport_entry smp_client_transport;
123 #endif
124
125 /* Helper function that allocates conn_param_data for a conn. */
conn_param_data_alloc(struct bt_conn * conn)126 static struct conn_param_data *conn_param_data_alloc(struct bt_conn *conn)
127 {
128 for (size_t i = 0; i < ARRAY_SIZE(conn_data); i++) {
129 if (conn_data[i].conn == NULL) {
130 bool valid = false;
131
132 conn_data[i].conn = conn;
133
134 /* Generate an ID for this connection and reset semaphore */
135 while (!valid) {
136 valid = true;
137 conn_data[i].id = next_id;
138 ++next_id;
139
140 if (next_id == 0) {
141 /* Avoid use of 0 (invalid ID) */
142 ++next_id;
143 }
144
145 for (size_t l = 0; l < ARRAY_SIZE(conn_data); l++) {
146 if (l != i && conn_data[l].conn != NULL &&
147 conn_data[l].id == conn_data[i].id) {
148 valid = false;
149 break;
150 }
151 }
152 }
153
154 k_sem_reset(&conn_data[i].smp_notify_sem);
155
156 return &conn_data[i];
157 }
158 }
159
160 /* Conn data must exists. */
161 __ASSERT_NO_MSG(false);
162 return NULL;
163 }
164
165 /* Helper function that returns conn_param_data associated with a conn. */
conn_param_data_get(const struct bt_conn * conn)166 static struct conn_param_data *conn_param_data_get(const struct bt_conn *conn)
167 {
168 for (size_t i = 0; i < ARRAY_SIZE(conn_data); i++) {
169 if (conn_data[i].conn == conn) {
170 return &conn_data[i];
171 }
172 }
173
174 return NULL;
175 }
176
177 /* SMP Bluetooth notification sent callback */
smp_notify_finished(struct bt_conn * conn,void * user_data)178 static void smp_notify_finished(struct bt_conn *conn, void *user_data)
179 {
180 struct conn_param_data *cpd = conn_param_data_get(conn);
181
182 if (cpd != NULL) {
183 k_sem_give(&cpd->smp_notify_sem);
184 }
185 }
186
187 /* Sets connection parameters for a given conn. */
conn_param_set(struct bt_conn * conn,struct bt_le_conn_param * param)188 static void conn_param_set(struct bt_conn *conn, struct bt_le_conn_param *param)
189 {
190 int ret = 0;
191 struct conn_param_data *cpd = conn_param_data_get(conn);
192
193 if (cpd != NULL) {
194 ret = bt_conn_le_param_update(conn, param);
195 if (ret && (ret != -EALREADY)) {
196 /* Try again to avoid being stuck with incorrect connection parameters. */
197 (void)k_work_reschedule(&cpd->ework, K_MSEC(RETRY_TIME));
198 } else {
199 (void)k_work_cancel_delayable(&cpd->ework);
200 }
201 }
202 }
203
204 /* Work handler function for restoring the preferred connection parameters for the connection. */
conn_param_on_pref_restore(struct k_work * work)205 static void conn_param_on_pref_restore(struct k_work *work)
206 {
207 struct k_work_delayable *dwork = k_work_delayable_from_work(work);
208 struct conn_param_data *cpd = CONTAINER_OF(dwork, struct conn_param_data, dwork);
209
210 if (cpd != NULL) {
211 conn_param_set(cpd->conn, CONN_PARAM_PREF);
212 cpd->state &= ~CONN_PARAM_SMP_REQUESTED;
213 }
214 }
215
216 /* Work handler function for retrying on conn negotiation API error. */
conn_param_on_error_retry(struct k_work * work)217 static void conn_param_on_error_retry(struct k_work *work)
218 {
219 struct k_work_delayable *ework = k_work_delayable_from_work(work);
220 struct conn_param_data *cpd = CONTAINER_OF(ework, struct conn_param_data, ework);
221 struct bt_le_conn_param *param = (cpd->state & CONN_PARAM_SMP_REQUESTED) ?
222 CONN_PARAM_SMP : CONN_PARAM_PREF;
223
224 conn_param_set(cpd->conn, param);
225 }
226
conn_param_smp_enable(struct bt_conn * conn)227 static void conn_param_smp_enable(struct bt_conn *conn)
228 {
229 struct conn_param_data *cpd = conn_param_data_get(conn);
230
231 if (cpd != NULL) {
232 if (!(cpd->state & CONN_PARAM_SMP_REQUESTED)) {
233 conn_param_set(conn, CONN_PARAM_SMP);
234 cpd->state |= CONN_PARAM_SMP_REQUESTED;
235 }
236
237 /* SMP characteristic in use; refresh the restore timeout. */
238 (void)k_work_reschedule(&cpd->dwork, K_MSEC(RESTORE_TIME));
239 }
240 }
241
242 /**
243 * Write handler for the SMP characteristic; processes an incoming SMP request.
244 */
smp_bt_chr_write(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)245 static ssize_t smp_bt_chr_write(struct bt_conn *conn,
246 const struct bt_gatt_attr *attr,
247 const void *buf, uint16_t len, uint16_t offset,
248 uint8_t flags)
249 {
250 struct conn_param_data *cpd = conn_param_data_get(conn);
251 #ifdef CONFIG_MCUMGR_TRANSPORT_BT_REASSEMBLY
252 int ret;
253 bool started;
254
255 if (cpd == NULL) {
256 LOG_ERR("Null cpd object for connection %p", (void *)conn);
257 return BT_GATT_ERR(BT_ATT_ERR_INSUFFICIENT_RESOURCES);
258 }
259
260 started = (smp_reassembly_expected(&smp_bt_transport) >= 0);
261
262 LOG_DBG("started = %s, buf len = %d", started ? "true" : "false", len);
263 LOG_HEXDUMP_DBG(buf, len, "buf = ");
264
265 ret = smp_reassembly_collect(&smp_bt_transport, buf, len);
266 LOG_DBG("collect = %d", ret);
267
268 /*
269 * Collection can fail only due to failing to allocate memory or by receiving
270 * more data than expected.
271 */
272 if (ret == -ENOMEM) {
273 /* Failed to collect the buffer */
274 return BT_GATT_ERR(BT_ATT_ERR_INSUFFICIENT_RESOURCES);
275 } else if (ret < 0) {
276 /* Failed operation on already allocated buffer, drop the packet and report
277 * error.
278 */
279 struct smp_bt_user_data *ud =
280 (struct smp_bt_user_data *)smp_reassembly_get_ud(&smp_bt_transport);
281
282 if (ud != NULL) {
283 ud->conn = NULL;
284 ud->id = 0;
285 }
286
287 smp_reassembly_drop(&smp_bt_transport);
288 return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED);
289 }
290
291 if (!started) {
292 /*
293 * Transport context is attached to the buffer after first fragment
294 * has been collected.
295 */
296 struct smp_bt_user_data *ud = smp_reassembly_get_ud(&smp_bt_transport);
297
298 if (IS_ENABLED(CONFIG_MCUMGR_TRANSPORT_BT_CONN_PARAM_CONTROL)) {
299 conn_param_smp_enable(conn);
300 }
301
302 ud->conn = conn;
303 ud->id = cpd->id;
304 }
305
306 /* No more bytes are expected for this packet */
307 if (ret == 0) {
308 smp_reassembly_complete(&smp_bt_transport, false);
309 }
310
311 /* BT expects entire len to be consumed */
312 return len;
313 #else
314 struct smp_bt_user_data *ud;
315 struct net_buf *nb;
316
317 if (cpd == NULL) {
318 LOG_ERR("Null cpd object for connection %p", (void *)conn);
319 return BT_GATT_ERR(BT_ATT_ERR_INSUFFICIENT_RESOURCES);
320 }
321
322 nb = smp_packet_alloc();
323 if (!nb) {
324 LOG_DBG("failed net_buf alloc for SMP packet");
325 return BT_GATT_ERR(BT_ATT_ERR_INSUFFICIENT_RESOURCES);
326 }
327
328 if (net_buf_tailroom(nb) < len) {
329 LOG_DBG("SMP packet len (%" PRIu16 ") > net_buf len (%zu)",
330 len, net_buf_tailroom(nb));
331 smp_packet_free(nb);
332 return BT_GATT_ERR(BT_ATT_ERR_INSUFFICIENT_RESOURCES);
333 }
334
335 net_buf_add_mem(nb, buf, len);
336
337 ud = net_buf_user_data(nb);
338 ud->conn = conn;
339 ud->id = cpd->id;
340
341 if (IS_ENABLED(CONFIG_MCUMGR_TRANSPORT_BT_CONN_PARAM_CONTROL)) {
342 conn_param_smp_enable(conn);
343 }
344
345 smp_rx_req(&smp_bt_transport, nb);
346
347 return len;
348 #endif
349 }
350
smp_bt_ccc_changed(const struct bt_gatt_attr * attr,uint16_t value)351 static void smp_bt_ccc_changed(const struct bt_gatt_attr *attr, uint16_t value)
352 {
353 #ifdef CONFIG_MCUMGR_TRANSPORT_BT_REASSEMBLY
354 if (smp_reassembly_expected(&smp_bt_transport) >= 0 && value == 0) {
355 struct smp_bt_user_data *ud = smp_reassembly_get_ud(&smp_bt_transport);
356
357 ud->conn = NULL;
358 ud->id = 0;
359
360 smp_reassembly_drop(&smp_bt_transport);
361 }
362 #endif
363 }
364
365 #define SMP_BT_ATTRS \
366 BT_GATT_PRIMARY_SERVICE(SMP_BT_SVC_UUID), \
367 BT_GATT_CHARACTERISTIC(SMP_BT_CHR_UUID, \
368 BT_GATT_CHRC_WRITE_WITHOUT_RESP | \
369 BT_GATT_CHRC_NOTIFY, \
370 SMP_GATT_PERM & SMP_GATT_PERM_WRITE_MASK, \
371 NULL, smp_bt_chr_write, NULL), \
372 BT_GATT_CCC(smp_bt_ccc_changed, \
373 SMP_GATT_PERM),
374
375
376 #ifdef CONFIG_MCUMGR_TRANSPORT_BT_DYNAMIC_SVC_REGISTRATION
377 static struct bt_gatt_attr attr_smp_bt_svc[] = {SMP_BT_ATTRS};
378 static struct bt_gatt_service smp_bt_svc = BT_GATT_SERVICE(attr_smp_bt_svc);
379 #else
380 BT_GATT_SERVICE_DEFINE(smp_bt_svc, SMP_BT_ATTRS);
381 #endif
382
smp_bt_notify(struct bt_conn * conn,const void * data,uint16_t len)383 int smp_bt_notify(struct bt_conn *conn, const void *data, uint16_t len)
384 {
385 return bt_gatt_notify(conn, attr_smp_bt_svc + 2, data, len);
386 }
387
388 /**
389 * Extracts the Bluetooth connection from a net_buf's user data.
390 */
smp_bt_conn_from_pkt(const struct net_buf * nb)391 static struct bt_conn *smp_bt_conn_from_pkt(const struct net_buf *nb)
392 {
393 struct smp_bt_user_data *ud = net_buf_user_data(nb);
394
395 if (!ud->conn) {
396 return NULL;
397 }
398
399 return ud->conn;
400 }
401
402 /**
403 * Calculates the maximum fragment size to use when sending the specified
404 * response packet.
405 */
smp_bt_get_mtu(const struct net_buf * nb)406 static uint16_t smp_bt_get_mtu(const struct net_buf *nb)
407 {
408 struct bt_conn *conn;
409 uint16_t mtu;
410
411 conn = smp_bt_conn_from_pkt(nb);
412 if (conn == NULL) {
413 return 0;
414 }
415
416 mtu = bt_gatt_get_mtu(conn);
417
418 /* Account for the three-byte notification header. */
419 return mtu - 3;
420 }
421
smp_bt_ud_free(void * ud)422 static void smp_bt_ud_free(void *ud)
423 {
424 struct smp_bt_user_data *user_data = ud;
425
426 if (user_data->conn) {
427 user_data->conn = NULL;
428 user_data->id = 0;
429 }
430 }
431
smp_bt_ud_copy(struct net_buf * dst,const struct net_buf * src)432 static int smp_bt_ud_copy(struct net_buf *dst, const struct net_buf *src)
433 {
434 struct smp_bt_user_data *src_ud = net_buf_user_data(src);
435 struct smp_bt_user_data *dst_ud = net_buf_user_data(dst);
436
437 if (src_ud->conn) {
438 dst_ud->conn = src_ud->conn;
439 dst_ud->id = src_ud->id;
440 }
441
442 return 0;
443 }
444
445 /**
446 * Transmits the specified SMP response.
447 */
smp_bt_tx_pkt(struct net_buf * nb)448 static int smp_bt_tx_pkt(struct net_buf *nb)
449 {
450 struct bt_conn *conn;
451 int rc = MGMT_ERR_EOK;
452 uint16_t off = 0;
453 uint16_t mtu_size;
454 struct bt_gatt_notify_params notify_param = {
455 .attr = attr_smp_bt_svc + 2,
456 .func = smp_notify_finished,
457 .data = nb->data,
458 };
459 bool sent = false;
460 struct bt_conn_info info;
461 struct conn_param_data *cpd;
462 struct smp_bt_user_data *ud;
463
464 conn = smp_bt_conn_from_pkt(nb);
465 if (conn == NULL) {
466 rc = MGMT_ERR_ENOENT;
467 goto cleanup;
468 }
469
470 /* Verify that the device is connected, the necessity for this check is that the remote
471 * device might have sent a command and disconnected before the command has been processed
472 * completely, if this happens then the connection details will still be valid due to
473 * the incremented connection reference count, but the connection has actually been
474 * dropped, this avoids waiting for a semaphore that will never be given which would
475 * otherwise cause a deadlock.
476 */
477 rc = bt_conn_get_info(conn, &info);
478
479 if (rc != 0 || info.state != BT_CONN_STATE_CONNECTED) {
480 /* Remote device has disconnected */
481 rc = MGMT_ERR_ENOENT;
482 goto cleanup;
483 }
484
485 /* Send data in chunks of the MTU size */
486 mtu_size = smp_bt_get_mtu(nb);
487
488 if (mtu_size == 0U) {
489 /* The transport cannot support a transmission right now. */
490 rc = MGMT_ERR_EUNKNOWN;
491 goto cleanup;
492 }
493
494 cpd = conn_param_data_get(conn);
495 ud = net_buf_user_data(nb);
496
497 if (cpd == NULL || cpd->id == 0 || cpd->id != ud->id) {
498 /* The device that sent this packet has disconnected or is not the same active
499 * connection, drop the outgoing data
500 */
501 rc = MGMT_ERR_ENOENT;
502 goto cleanup;
503 }
504
505 k_sem_reset(&cpd->smp_notify_sem);
506
507 while (off < nb->len) {
508 if (cpd->id == 0 || cpd->id != ud->id) {
509 /* The device that sent this packet has disconnected or is not the same
510 * active connection, drop the outgoing data
511 */
512 rc = MGMT_ERR_ENOENT;
513 goto cleanup;
514 }
515
516 if ((off + mtu_size) > nb->len) {
517 /* Final packet, limit size */
518 mtu_size = nb->len - off;
519 }
520
521 notify_param.len = mtu_size;
522 rc = bt_gatt_notify_cb(conn, ¬ify_param);
523
524 if (rc == -ENOMEM) {
525 if (sent == false) {
526 /* Failed to send a packet thus far, try reducing the MTU size
527 * as perhaps the buffer size is limited to a value which is
528 * less than the MTU or there is a configuration error in the
529 * project
530 */
531 if (mtu_size < SMP_BT_MINIMUM_MTU_SEND_FAILURE) {
532 /* If unable to send a 20 byte message, something is
533 * amiss, no point in continuing
534 */
535 rc = MGMT_ERR_ENOMEM;
536 break;
537 }
538
539 mtu_size /= 2;
540 }
541
542 /* No buffers available, wait until the next loop for them to become
543 * available
544 */
545 rc = MGMT_ERR_EOK;
546 k_yield();
547 } else if (rc == 0) {
548 off += mtu_size;
549 notify_param.data = &nb->data[off];
550 sent = true;
551
552 /* Wait for the completion (or disconnect) semaphore before
553 * continuing, allowing other parts of the system to run.
554 */
555 k_sem_take(&cpd->smp_notify_sem, K_FOREVER);
556 } else {
557 /* No connection, cannot continue */
558 rc = MGMT_ERR_EUNKNOWN;
559 break;
560 }
561 }
562
563 cleanup:
564 smp_bt_ud_free(net_buf_user_data(nb));
565 smp_packet_free(nb);
566
567 return rc;
568 }
569
570 #ifdef CONFIG_MCUMGR_TRANSPORT_BT_DYNAMIC_SVC_REGISTRATION
smp_bt_register(void)571 int smp_bt_register(void)
572 {
573 return bt_gatt_service_register(&smp_bt_svc);
574 }
575
smp_bt_unregister(void)576 int smp_bt_unregister(void)
577 {
578 return bt_gatt_service_unregister(&smp_bt_svc);
579 }
580 #endif
581
582 /* BT connected callback. */
connected(struct bt_conn * conn,uint8_t err)583 static void connected(struct bt_conn *conn, uint8_t err)
584 {
585 if (err == 0) {
586 (void)conn_param_data_alloc(conn);
587 }
588 }
589
590 /* BT disconnected callback. */
disconnected(struct bt_conn * conn,uint8_t reason)591 static void disconnected(struct bt_conn *conn, uint8_t reason)
592 {
593 struct conn_param_data *cpd = conn_param_data_get(conn);
594
595 /* Remove all pending requests from this device which have yet to be processed from the
596 * FIFO (for this specific connection).
597 */
598 smp_rx_remove_invalid(&smp_bt_transport, (void *)conn);
599
600 /* Force giving the notification semaphore here, this is only needed if there is a pending
601 * outgoing packet when the device has disconnected, as in this case the notification
602 * callback will not be called and this is needed to prevent a deadlock.
603 */
604 if (cpd != NULL) {
605 /* Clear cpd. */
606 cpd->id = 0;
607 cpd->conn = NULL;
608
609 if (IS_ENABLED(CONFIG_MCUMGR_TRANSPORT_BT_CONN_PARAM_CONTROL)) {
610 /* Cancel work if ongoing. */
611 (void)k_work_cancel_delayable(&cpd->dwork);
612 (void)k_work_cancel_delayable(&cpd->ework);
613
614 /* Clear cpd. */
615 cpd->state = 0;
616 }
617
618 k_sem_give(&cpd->smp_notify_sem);
619 } else {
620 LOG_ERR("Null cpd object for connection %p", (void *)conn);
621 }
622 }
623
conn_param_control_init(void)624 static void conn_param_control_init(void)
625 {
626 for (size_t i = 0; i < ARRAY_SIZE(conn_data); i++) {
627 k_work_init_delayable(&conn_data[i].dwork, conn_param_on_pref_restore);
628 k_work_init_delayable(&conn_data[i].ework, conn_param_on_error_retry);
629 }
630 }
631
smp_bt_query_valid_check(struct net_buf * nb,void * arg)632 static bool smp_bt_query_valid_check(struct net_buf *nb, void *arg)
633 {
634 const struct bt_conn *conn = (struct bt_conn *)arg;
635 struct smp_bt_user_data *ud = net_buf_user_data(nb);
636 struct conn_param_data *cpd;
637
638 if (conn == NULL || ud == NULL) {
639 return false;
640 }
641
642 cpd = conn_param_data_get(conn);
643
644 if (cpd == NULL || (ud->conn == conn && cpd->id != ud->id)) {
645 return false;
646 }
647
648 return true;
649 }
650
smp_bt_setup(void)651 static void smp_bt_setup(void)
652 {
653 int rc;
654 uint8_t i = 0;
655
656 next_id = 1;
657
658 if (IS_ENABLED(CONFIG_MCUMGR_TRANSPORT_BT_CONN_PARAM_CONTROL)) {
659 conn_param_control_init();
660 }
661
662 while (i < CONFIG_BT_MAX_CONN) {
663 k_sem_init(&conn_data[i].smp_notify_sem, 0, 1);
664 ++i;
665 }
666
667 smp_bt_transport.functions.output = smp_bt_tx_pkt;
668 smp_bt_transport.functions.get_mtu = smp_bt_get_mtu;
669 smp_bt_transport.functions.ud_copy = smp_bt_ud_copy;
670 smp_bt_transport.functions.ud_free = smp_bt_ud_free;
671 smp_bt_transport.functions.query_valid_check = smp_bt_query_valid_check;
672
673 rc = smp_transport_init(&smp_bt_transport);
674
675 if (IS_ENABLED(CONFIG_MCUMGR_TRANSPORT_BT_DYNAMIC_SVC_REGISTRATION) && rc == 0) {
676 rc = smp_bt_register();
677 }
678
679 #ifdef CONFIG_SMP_CLIENT
680 if (rc == 0) {
681 smp_client_transport.smpt = &smp_bt_transport;
682 smp_client_transport.smpt_type = SMP_BLUETOOTH_TRANSPORT;
683 smp_client_transport_register(&smp_client_transport);
684 }
685 #endif
686
687 if (rc != 0) {
688 LOG_ERR("Bluetooth SMP transport register failed (err %d)", rc);
689 }
690 }
691
692 MCUMGR_HANDLER_DEFINE(smp_bt, smp_bt_setup);
693