text: /* main.c - Application main entry point */
  
  /*
   * Copyright (c) 2015-2016 Intel Corporation
   *
   * SPDX-License-Identifier: Apache-2.0
   */
  
  #include <zephyr/types.h>
  #include <stddef.h>
  #include <string.h>
  #include <errno.h>
  #include <zephyr/sys/printk.h>
  #include <zephyr/sys/byteorder.h>
  #include <zephyr/zephyr.h>
  
  #include <zephyr/settings/settings.h>
  
  #include <zephyr/bluetooth/bluetooth.h>
  #include <zephyr/bluetooth/hci.h>
  #include <zephyr/bluetooth/conn.h>
  #include <zephyr/bluetooth/uuid.h>
  #include <zephyr/bluetooth/gatt.h>
  #include <zephyr/bluetooth/services/bas.h>
  #include <zephyr/bluetooth/services/hrs.h>
  #include <zephyr/bluetooth/services/ias.h>
  
  #include "cts.h"
  
  /* Custom Service Variables */
  #define BT_UUID_CUSTOM_SERVICE_VAL \
  	BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef0)
  
  static struct bt_uuid_128 vnd_uuid = BT_UUID_INIT_128(
  	BT_UUID_CUSTOM_SERVICE_VAL);
  
  static struct bt_uuid_128 vnd_enc_uuid = BT_UUID_INIT_128(
  	BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef1));
  
  static struct bt_uuid_128 vnd_auth_uuid = BT_UUID_INIT_128(
  	BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef2));
  
  #define VND_MAX_LEN 20
  
  static uint8_t vnd_value[VND_MAX_LEN + 1] = { 'V', 'e', 'n', 'd', 'o', 'r'};
  static uint8_t vnd_auth_value[VND_MAX_LEN + 1] = { 'V', 'e', 'n', 'd', 'o', 'r'};
  static uint8_t vnd_wwr_value[VND_MAX_LEN + 1] = { 'V', 'e', 'n', 'd', 'o', 'r' };
  
  static ssize_t read_vnd(struct bt_conn *conn, const struct bt_gatt_attr *attr,
  			void *buf, uint16_t len, uint16_t offset)
  {
  	const char *value = attr->user_data;
  
  	return bt_gatt_attr_read(conn, attr, buf, len, offset, value,
  				 strlen(value));
  }
  
  static ssize_t write_vnd(struct bt_conn *conn, const struct bt_gatt_attr *attr,
  			 const void *buf, uint16_t len, uint16_t offset,
  			 uint8_t flags)
  {
  	uint8_t *value = attr->user_data;
  
  	if (offset + len > VND_MAX_LEN) {
  		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
  	}
  
  	memcpy(value + offset, buf, len);
  	value[offset + len] = 0;
  
  	return len;
  }
  
  static uint8_t simulate_vnd;
  static uint8_t indicating;
  static struct bt_gatt_indicate_params ind_params;
  
  static void vnd_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
  {
  	simulate_vnd = (value == BT_GATT_CCC_INDICATE) ? 1 : 0;
  }
  
  static void indicate_cb(struct bt_conn *conn,
  			struct bt_gatt_indicate_params *params, uint8_t err)
  {
  	printk("Indication %s\n", err != 0U ? "fail" : "success");
  }
  
  static void indicate_destroy(struct bt_gatt_indicate_params *params)
  {
  	printk("Indication complete\n");
  	indicating = 0U;
  }
  
  #define VND_LONG_MAX_LEN 74
  static uint8_t vnd_long_value[VND_LONG_MAX_LEN + 1] = {
  		  'V', 'e', 'n', 'd', 'o', 'r', ' ', 'd', 'a', 't', 'a', '1',
  		  'V', 'e', 'n', 'd', 'o', 'r', ' ', 'd', 'a', 't', 'a', '2',
  		  'V', 'e', 'n', 'd', 'o', 'r', ' ', 'd', 'a', 't', 'a', '3',
  		  'V', 'e', 'n', 'd', 'o', 'r', ' ', 'd', 'a', 't', 'a', '4',
  		  'V', 'e', 'n', 'd', 'o', 'r', ' ', 'd', 'a', 't', 'a', '5',
  		  'V', 'e', 'n', 'd', 'o', 'r', ' ', 'd', 'a', 't', 'a', '6',
  		  '.', ' ' };
  
  static ssize_t write_long_vnd(struct bt_conn *conn,
  			      const struct bt_gatt_attr *attr, const void *buf,
  			      uint16_t len, uint16_t offset, uint8_t flags)
  {
  	uint8_t *value = attr->user_data;
  
  	if (flags & BT_GATT_WRITE_FLAG_PREPARE) {
  		return 0;
  	}
  
  	if (offset + len > VND_LONG_MAX_LEN) {
  		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
  	}
  
  	memcpy(value + offset, buf, len);
  	value[offset + len] = 0;
  
  	return len;
  }
  
  static const struct bt_uuid_128 vnd_long_uuid = BT_UUID_INIT_128(
  	BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef3));
  
  static struct bt_gatt_cep vnd_long_cep = {
  	.properties = BT_GATT_CEP_RELIABLE_WRITE,
  };
  
  static int signed_value;
  
  static ssize_t read_signed(struct bt_conn *conn, const struct bt_gatt_attr *attr,
  			   void *buf, uint16_t len, uint16_t offset)
  {
  	const char *value = attr->user_data;
  
  	return bt_gatt_attr_read(conn, attr, buf, len, offset, value,
  				 sizeof(signed_value));
  }
  
  static ssize_t write_signed(struct bt_conn *conn, const struct bt_gatt_attr *attr,
  			    const void *buf, uint16_t len, uint16_t offset,
  			    uint8_t flags)
  {
  	uint8_t *value = attr->user_data;
  
  	if (offset + len > sizeof(signed_value)) {
  		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
  	}
  
  	memcpy(value + offset, buf, len);
  
  	return len;
  }
  
  static const struct bt_uuid_128 vnd_signed_uuid = BT_UUID_INIT_128(
  	BT_UUID_128_ENCODE(0x13345678, 0x1234, 0x5678, 0x1334, 0x56789abcdef3));
  
  static const struct bt_uuid_128 vnd_write_cmd_uuid = BT_UUID_INIT_128(
  	BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef4));
  
  static ssize_t write_without_rsp_vnd(struct bt_conn *conn,
  				     const struct bt_gatt_attr *attr,
  				     const void *buf, uint16_t len, uint16_t offset,
  				     uint8_t flags)
  {
  	uint8_t *value = attr->user_data;
  
  	if (!(flags & BT_GATT_WRITE_FLAG_CMD)) {
  		/* Write Request received. Reject it since this Characteristic
  		 * only accepts Write Without Response.
  		 */
  		return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
  	}
  
  	if (offset + len > VND_MAX_LEN) {
  		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
  	}
  
  	memcpy(value + offset, buf, len);
  	value[offset + len] = 0;
  
  	return len;
  }
  
  /* Vendor Primary Service Declaration */
  BT_GATT_SERVICE_DEFINE(vnd_svc,
  	BT_GATT_PRIMARY_SERVICE(&vnd_uuid),
  	BT_GATT_CHARACTERISTIC(&vnd_enc_uuid.uuid,
  			       BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE |
  			       BT_GATT_CHRC_INDICATE,
  			       BT_GATT_PERM_READ_ENCRYPT |
  			       BT_GATT_PERM_WRITE_ENCRYPT,
  			       read_vnd, write_vnd, vnd_value),
  	BT_GATT_CCC(vnd_ccc_cfg_changed,
  		    BT_GATT_PERM_READ | BT_GATT_PERM_WRITE_ENCRYPT),
  	BT_GATT_CHARACTERISTIC(&vnd_auth_uuid.uuid,
  			       BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
  			       BT_GATT_PERM_READ_AUTHEN |
  			       BT_GATT_PERM_WRITE_AUTHEN,
  			       read_vnd, write_vnd, vnd_auth_value),
  	BT_GATT_CHARACTERISTIC(&vnd_long_uuid.uuid, BT_GATT_CHRC_READ |
  			       BT_GATT_CHRC_WRITE | BT_GATT_CHRC_EXT_PROP,
  			       BT_GATT_PERM_READ | BT_GATT_PERM_WRITE |
  			       BT_GATT_PERM_PREPARE_WRITE,
  			       read_vnd, write_long_vnd, &vnd_long_value),
  	BT_GATT_CEP(&vnd_long_cep),
  	BT_GATT_CHARACTERISTIC(&vnd_signed_uuid.uuid, BT_GATT_CHRC_READ |
  			       BT_GATT_CHRC_WRITE | BT_GATT_CHRC_AUTH,
  			       BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
  			       read_signed, write_signed, &signed_value),
  	BT_GATT_CHARACTERISTIC(&vnd_write_cmd_uuid.uuid,
  			       BT_GATT_CHRC_WRITE_WITHOUT_RESP,
  			       BT_GATT_PERM_WRITE, NULL,
  			       write_without_rsp_vnd, &vnd_wwr_value),
  );
  
  static const struct bt_data ad[] = {
  	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
  	BT_DATA_BYTES(BT_DATA_UUID16_ALL,
  		      BT_UUID_16_ENCODE(BT_UUID_HRS_VAL),
  		      BT_UUID_16_ENCODE(BT_UUID_BAS_VAL),
  		      BT_UUID_16_ENCODE(BT_UUID_CTS_VAL)),
  	BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_CUSTOM_SERVICE_VAL),
  };
  
  void mtu_updated(struct bt_conn *conn, uint16_t tx, uint16_t rx)
  {
  	printk("Updated MTU: TX: %d RX: %d bytes\n", tx, rx);
  }
  
  static struct bt_gatt_cb gatt_callbacks = {
  	.att_mtu_updated = mtu_updated
  };
  
  static void connected(struct bt_conn *conn, uint8_t err)
  {
  	if (err) {
  		printk("Connection failed (err 0x%02x)\n", err);
  	} else {
  		printk("Connected\n");
  	}
  }
  
  static void disconnected(struct bt_conn *conn, uint8_t reason)
  {
  	printk("Disconnected (reason 0x%02x)\n", reason);
  }
  
  static void alert_stop(void)
  {
  	printk("Alert stopped\n");
  }
  
  static void alert_start(void)
  {
  	printk("Mild alert started\n");
  }
  
  static void alert_high_start(void)
  {
  	printk("High alert started\n");
  }
  
  BT_CONN_CB_DEFINE(conn_callbacks) = {
  	.connected = connected,
  	.disconnected = disconnected,
  };
  
  BT_IAS_CB_DEFINE(ias_callbacks) = {
  	.no_alert = alert_stop,
  	.mild_alert = alert_start,
  	.high_alert = alert_high_start,
  };
  
  static void bt_ready(void)
  {
  	int err;
  
  	printk("Bluetooth initialized\n");
  
  	cts_init();
  
  	if (IS_ENABLED(CONFIG_SETTINGS)) {
  		settings_load();
  	}
  
  	err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0);
  	if (err) {
  		printk("Advertising failed to start (err %d)\n", err);
  		return;
  	}
  
  	printk("Advertising successfully started\n");
  }
  
  static void auth_passkey_display(struct bt_conn *conn, unsigned int passkey)
  {
  	char addr[BT_ADDR_LE_STR_LEN];
  
  	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
  
  	printk("Passkey for %s: %06u\n", addr, passkey);
  }
  
  static void auth_cancel(struct bt_conn *conn)
  {
  	char addr[BT_ADDR_LE_STR_LEN];
  
  	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
  
  	printk("Pairing cancelled: %s\n", addr);
  }
  
  static struct bt_conn_auth_cb auth_cb_display = {
  	.passkey_display = auth_passkey_display,
  	.passkey_entry = NULL,
  	.cancel = auth_cancel,
  };
  
  static void bas_notify(void)
  {
  	uint8_t battery_level = bt_bas_get_battery_level();
  
  	battery_level--;
  
  	if (!battery_level) {
  		battery_level = 100U;
  	}
  
  	bt_bas_set_battery_level(battery_level);
  }
  
  static void hrs_notify(void)
  {
  	static uint8_t heartrate = 90U;
  
  	/* Heartrate measurements simulation */
  	heartrate++;
  	if (heartrate == 160U) {
  		heartrate = 90U;
  	}
  
  	bt_hrs_notify(heartrate);
  }
  
  void main(void)
  {
  	struct bt_gatt_attr *vnd_ind_attr;
  	char str[BT_UUID_STR_LEN];
  	int err;
  
  	err = bt_enable(NULL);
  	if (err) {
  		printk("Bluetooth init failed (err %d)\n", err);
  		return;
  	}
  
  	bt_ready();
  
  	bt_gatt_cb_register(&gatt_callbacks);
  	bt_conn_auth_cb_register(&auth_cb_display);
  
  	vnd_ind_attr = bt_gatt_find_by_uuid(vnd_svc.attrs, vnd_svc.attr_count,
  					    &vnd_enc_uuid.uuid);
  	bt_uuid_to_str(&vnd_enc_uuid.uuid, str, sizeof(str));
  	printk("Indicate VND attr %p (UUID %s)\n", vnd_ind_attr, str);
  
  	/* Implement notification. At the moment there is no suitable way
  	 * of starting delayed work so we do it here
  	 */
  	while (1) {
  		k_sleep(K_SECONDS(1));
  
  		/* Current Time Service updates only when time is changed */
  		cts_notify();
  
  		/* Heartrate measurements simulation */
  		hrs_notify();
  
  		/* Battery level simulation */
  		bas_notify();
  
  		/* Vendor indication simulation */
  		if (simulate_vnd && vnd_ind_attr) {
  			if (indicating) {
  				continue;
  			}
  
  			ind_params.attr = vnd_ind_attr;
  			ind_params.func = indicate_cb;
  			ind_params.destroy = indicate_destroy;
  			ind_params.data = &indicating;
  			ind_params.len = sizeof(indicating);
  
  			if (bt_gatt_indicate(NULL, &ind_params) == 0) {
  				indicating = 1U;
  			}
  		}
  	}
  }
  