text: /*
   * 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;
  
  struct smp_bt_user_data {
  	struct bt_conn *conn;
  };
  
  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)
  {
  	struct smp_bt_user_data *ud;
  	struct net_buf *nb;
  
  	nb = mcumgr_buf_alloc();
  	net_buf_add_mem(nb, buf, len);
  
  	ud = net_buf_user_data(nb);
  	ud->conn = bt_conn_ref(conn);
  
  	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_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 Bluetooth connection from a net_buf's user data.
   */
  static struct bt_conn *smp_bt_conn_from_pkt(const struct net_buf *nb)
  {
  	struct smp_bt_user_data *ud = net_buf_user_data(nb);
  
  	if (!ud->conn) {
  		return NULL;
  	}
  
  	return bt_conn_ref(ud->conn);
  }
  
  /**
   * 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;
  }
  
  static void smp_bt_buf_free(struct net_buf *nb)
  {
  	struct smp_bt_user_data *ud = net_buf_user_data(nb);
  
  	if (ud->conn) {
  		bt_conn_unref(ud->conn);
  		ud->conn = NULL;
  	}
  
  	mcumgr_buf_free(nb);
  }
  
  /**
   * 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);
  	}
  
  	smp_bt_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);
  