| /* |
| * Copyright Runtime.io 2018. All rights reserved. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| /** @file |
| * @brief Bluetooth transport for the mcumgr SMP protocol. |
| */ |
| |
| #include <errno.h> |
| |
| #include <zephyr.h> |
| #include <init.h> |
| #include <bluetooth/bluetooth.h> |
| #include <bluetooth/uuid.h> |
| #include <bluetooth/gatt.h> |
| |
| #include <mgmt/smp_bt.h> |
| #include <mgmt/buf.h> |
| |
| #include <mgmt/smp.h> |
| |
| 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); |