diff --git a/include/mgmt/smp_bt.h b/include/mgmt/smp_bt.h new file mode 100644 index 00000000000..1e40bb7169e --- /dev/null +++ b/include/mgmt/smp_bt.h @@ -0,0 +1,32 @@ +/* + * Copyright Runtime.io 2018. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** @file + * @brief Bluetooth transport for the mcumgr SMP protocol. + */ + +#ifndef H_SMP_BT_ +#define H_SMP_BT_ + +#include +struct bt_conn; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Registers the SMP Bluetooth service. + * + * @return 0 on success; negative error code on failure. + */ +int smp_bt_register(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/subsys/mgmt/smp_bt.c b/subsys/mgmt/smp_bt.c new file mode 100644 index 00000000000..ae0c94cb8b3 --- /dev/null +++ b/subsys/mgmt/smp_bt.c @@ -0,0 +1,160 @@ +/* + * Copyright Runtime.io 2018. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** @file + * @brief Bluetooth transport for the mcumgr SMP protocol. + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include + +struct device; + +static struct zephyr_smp_transport smp_bt_transport; + +/* SMP service. + * {8D53DC1D-1DB7-4CD3-868B-8A527460AA84} + */ +static struct bt_uuid_128 smp_bt_svc_uuid = BT_UUID_INIT_128( + 0x84, 0xaa, 0x60, 0x74, 0x52, 0x8a, 0x8b, 0x86, + 0xd3, 0x4c, 0xb7, 0x1d, 0x1d, 0xdc, 0x53, 0x8d); + +/* SMP characteristic; used for both requests and responses. + * {DA2E7828-FBCE-4E01-AE9E-261174997C48} + */ +static struct bt_uuid_128 smp_bt_chr_uuid = BT_UUID_INIT_128( + 0x48, 0x7c, 0x99, 0x74, 0x11, 0x26, 0x9e, 0xae, + 0x01, 0x4e, 0xce, 0xfb, 0x28, 0x78, 0x2e, 0xda); + +/** + * Write handler for the SMP characteristic; processes an incoming SMP request. + */ +static ssize_t smp_bt_chr_write(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + const void *buf, u16_t len, u16_t offset, + u8_t flags) +{ + const bt_addr_le_t *addr; + struct net_buf *nb; + + nb = mcumgr_buf_alloc(); + net_buf_add_mem(nb, buf, len); + + addr = bt_conn_get_dst(conn); + memcpy(net_buf_user_data(nb), addr, sizeof(*addr)); + + zephyr_smp_rx_req(&smp_bt_transport, nb); + + return len; +} + +static void smp_bt_ccc_changed(const struct bt_gatt_attr *attr, u16_t value) +{ +} + +static struct bt_gatt_ccc_cfg smp_bt_ccc[BT_GATT_CCC_MAX] = {}; +static struct bt_gatt_attr smp_bt_attrs[] = { + /* SMP Primary Service Declaration */ + BT_GATT_PRIMARY_SERVICE(&smp_bt_svc_uuid), + + BT_GATT_CHARACTERISTIC(&smp_bt_chr_uuid.uuid, + BT_GATT_CHRC_WRITE_WITHOUT_RESP | + BT_GATT_CHRC_NOTIFY), + BT_GATT_DESCRIPTOR(&smp_bt_chr_uuid.uuid, + BT_GATT_PERM_WRITE, NULL, smp_bt_chr_write, NULL), + BT_GATT_CCC(smp_bt_ccc, smp_bt_ccc_changed), +}; + +static struct bt_gatt_service smp_bt_svc = BT_GATT_SERVICE(smp_bt_attrs); + +/** + * Transmits an SMP response over the specified Bluetooth connection. + */ +static int smp_bt_tx_rsp(struct bt_conn *conn, const void *data, u16_t len) +{ + return bt_gatt_notify(conn, smp_bt_attrs + 2, data, len); +} + +/** + * Extracts the peer address from a net_buf's user data and looks up the + * corresponding conection. + */ +static struct bt_conn *smp_bt_conn_from_pkt(const struct net_buf *nb) +{ + bt_addr_le_t addr; + + /* Cast away const. */ + memcpy(&addr, net_buf_user_data((void *)nb), sizeof(addr)); + return bt_conn_lookup_addr_le(&addr); +} + +/** + * Calculates the maximum fragment size to use when sending the specified + * response packet. + */ +static u16_t smp_bt_get_mtu(const struct net_buf *nb) +{ + struct bt_conn *conn; + u16_t mtu; + + conn = smp_bt_conn_from_pkt(nb); + if (conn == NULL) { + return 0; + } + + mtu = bt_gatt_get_mtu(conn); + bt_conn_unref(conn); + + /* Account for the three-byte notification header. */ + return mtu - 3; +} + +/** + * Transmits the specified SMP response. + */ +static int smp_bt_tx_pkt(struct zephyr_smp_transport *zst, struct net_buf *nb) +{ + struct bt_conn *conn; + int rc; + + conn = smp_bt_conn_from_pkt(nb); + if (conn == NULL) { + rc = -1; + } else { + rc = smp_bt_tx_rsp(conn, nb->data, nb->len); + bt_conn_unref(conn); + } + + mcumgr_buf_free(nb); + + return rc; +} + +int smp_bt_register(void) +{ + return bt_gatt_service_register(&smp_bt_svc); +} + +static int smp_bt_init(struct device *dev) +{ + ARG_UNUSED(dev); + + zephyr_smp_transport_init(&smp_bt_transport, smp_bt_tx_pkt, + smp_bt_get_mtu); + return 0; +} + +SYS_INIT(smp_bt_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);