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, &notify_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