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