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 <misc/printk.h>
  #include <misc/util.h>
  #include <misc/byteorder.h>
  #include <zephyr.h>
  
  #include <bluetooth/bluetooth.h>
  #include <bluetooth/hci.h>
  #include <bluetooth/conn.h>
  #include <bluetooth/uuid.h>
  #include <bluetooth/gatt.h>
  
  #define DEVICE_NAME CONFIG_BT_DEVICE_NAME
  #define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
  #define NUMBER_OF_SLOTS 1
  #define EDS_VERSION 0x00
  #define EDS_URL_READ_OFFSET 2
  #define EDS_URL_WRITE_OFFSET 4
  #define EDS_IDLE_TIMEOUT K_SECONDS(30)
  
  /* Idle timer */
  struct k_delayed_work idle_work;
  
  static const struct bt_data ad[] = {
  	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
  	/* Eddystone Service UUID a3c87500-8ed3-4bdf-8a39-a01bebede295 */
  	BT_DATA_BYTES(BT_DATA_UUID128_ALL,
  		      0x95, 0xe2, 0xed, 0xeb, 0x1b, 0xa0, 0x39, 0x8a,
  		      0xdf, 0x4b, 0xd3, 0x8e, 0x00, 0x75, 0xc8, 0xa3),
  };
  
  /* Set Scan Response data */
  static const struct bt_data sd[] = {
  	BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
  };
  
  /* Eddystone Service Variables */
  /* Service UUID a3c87500-8ed3-4bdf-8a39-a01bebede295 */
  static struct bt_uuid_128 eds_uuid = BT_UUID_INIT_128(
  	0x95, 0xe2, 0xed, 0xeb, 0x1b, 0xa0, 0x39, 0x8a,
  	0xdf, 0x4b, 0xd3, 0x8e, 0x00, 0x75, 0xc8, 0xa3);
  
  /* Characteristic UUID a3c87501-8ed3-4bdf-8a39-a01bebede295 */
  static struct bt_uuid_128 eds_caps_uuid = BT_UUID_INIT_128(
  	0x95, 0xe2, 0xed, 0xeb, 0x1b, 0xa0, 0x39, 0x8a,
  	0xdf, 0x4b, 0xd3, 0x8e, 0x01, 0x75, 0xc8, 0xa3);
  
  /* Characteristic UUID a3c87502-8ed3-4bdf-8a39-a01bebede295 */
  static struct bt_uuid_128 eds_slot_uuid = BT_UUID_INIT_128(
  	0x95, 0xe2, 0xed, 0xeb, 0x1b, 0xa0, 0x39, 0x8a,
  	0xdf, 0x4b, 0xd3, 0x8e, 0x02, 0x75, 0xc8, 0xa3);
  
  /* Characteristic UUID a3c87503-8ed3-4bdf-8a39-a01bebede295 */
  static struct bt_uuid_128 eds_intv_uuid = BT_UUID_INIT_128(
  	0x95, 0xe2, 0xed, 0xeb, 0x1b, 0xa0, 0x39, 0x8a,
  	0xdf, 0x4b, 0xd3, 0x8e, 0x03, 0x75, 0xc8, 0xa3);
  
  /* Characteristic UUID a3c87504-8ed3-4bdf-8a39-a01bebede295 */
  static struct bt_uuid_128 eds_tx_uuid = BT_UUID_INIT_128(
  	0x95, 0xe2, 0xed, 0xeb, 0x1b, 0xa0, 0x39, 0x8a,
  	0xdf, 0x4b, 0xd3, 0x8e, 0x04, 0x75, 0xc8, 0xa3);
  
  /* Characteristic UUID a3c87505-8ed3-4bdf-8a39-a01bebede295 */
  static struct bt_uuid_128 eds_adv_tx_uuid = BT_UUID_INIT_128(
  	0x95, 0xe2, 0xed, 0xeb, 0x1b, 0xa0, 0x39, 0x8a,
  	0xdf, 0x4b, 0xd3, 0x8e, 0x05, 0x75, 0xc8, 0xa3);
  
  /* Characteristic UUID a3c87506-8ed3-4bdf-8a39-a01bebede295 */
  static struct bt_uuid_128 eds_lock_uuid = BT_UUID_INIT_128(
  	0x95, 0xe2, 0xed, 0xeb, 0x1b, 0xa0, 0x39, 0x8a,
  	0xdf, 0x4b, 0xd3, 0x8e, 0x06, 0x75, 0xc8, 0xa3);
  
  /* Characteristic UUID a3c87507-8ed3-4bdf-8a39-a01bebede295 */
  static struct bt_uuid_128 eds_unlock_uuid = BT_UUID_INIT_128(
  	0x95, 0xe2, 0xed, 0xeb, 0x1b, 0xa0, 0x39, 0x8a,
  	0xdf, 0x4b, 0xd3, 0x8e, 0x07, 0x75, 0xc8, 0xa3);
  
  /* Characteristic UUID a3c87508-8ed3-4bdf-8a39-a01bebede295 */
  static struct bt_uuid_128 eds_ecdh_uuid = BT_UUID_INIT_128(
  	0x95, 0xe2, 0xed, 0xeb, 0x1b, 0xa0, 0x39, 0x8a,
  	0xdf, 0x4b, 0xd3, 0x8e, 0x08, 0x75, 0xc8, 0xa3);
  
  /* Characteristic UUID a3c87509-8ed3-4bdf-8a39-a01bebede295 */
  static struct bt_uuid_128 eds_eid_uuid = BT_UUID_INIT_128(
  	0x95, 0xe2, 0xed, 0xeb, 0x1b, 0xa0, 0x39, 0x8a,
  	0xdf, 0x4b, 0xd3, 0x8e, 0x09, 0x75, 0xc8, 0xa3);
  
  /* Characteristic UUID a3c8750a-8ed3-4bdf-8a39-a01bebede295 */
  static struct bt_uuid_128 eds_data_uuid = BT_UUID_INIT_128(
  	0x95, 0xe2, 0xed, 0xeb, 0x1b, 0xa0, 0x39, 0x8a,
  	0xdf, 0x4b, 0xd3, 0x8e, 0x0a, 0x75, 0xc8, 0xa3);
  
  /* Characteristic UUID a3c8750b-8ed3-4bdf-8a39-a01bebede295 */
  static struct bt_uuid_128 eds_reset_uuid = BT_UUID_INIT_128(
  	0x95, 0xe2, 0xed, 0xeb, 0x1b, 0xa0, 0x39, 0x8a,
  	0xdf, 0x4b, 0xd3, 0x8e, 0x0b, 0x75, 0xc8, 0xa3);
  
  /* Characteristic UUID a3c8750c-8ed3-4bdf-8a39-a01bebede295 */
  static struct bt_uuid_128 eds_connectable_uuid = BT_UUID_INIT_128(
  	0x95, 0xe2, 0xed, 0xeb, 0x1b, 0xa0, 0x39, 0x8a,
  	0xdf, 0x4b, 0xd3, 0x8e, 0x0c, 0x75, 0xc8, 0xa3);
  
  enum {
  	EDS_TYPE_UID = 0x00,
  	EDS_TYPE_URL = 0x10,
  	EDS_TYPE_TLM = 0x20,
  	EDS_TYPE_EID = 0x30,
  	EDS_TYPE_NONE = 0xff,
  };
  
  enum {
  	EDS_SLOT_UID = sys_cpu_to_be16(BIT(0)),
  	EDS_SLOT_URL = sys_cpu_to_be16(BIT(1)),
  	EDS_SLOT_TLM = sys_cpu_to_be16(BIT(2)),
  	EDS_SLOT_EID = sys_cpu_to_be16(BIT(3)),
  };
  
  struct eds_capabilities {
  	u8_t version;
  	u8_t slots;
  	u8_t uids;
  	u8_t adv_types;
  	u16_t slot_types;
  	u8_t tx_power;
  } __packed;
  
  static struct eds_capabilities eds_caps = {
  	.version = EDS_VERSION,
  	.slots = NUMBER_OF_SLOTS,
  	.slot_types = EDS_SLOT_URL, /* TODO: Add support for other slot types */
  };
  
  u8_t eds_active_slot;
  
  enum {
  	EDS_LOCKED = 0x00,
  	EDS_UNLOCKED = 0x01,
  	EDS_UNLOCKED_NO_RELOCKING = 0x02,
  };
  
  struct eds_slot {
  	u8_t type;
  	u8_t state;
  	u8_t connectable;
  	u16_t interval;
  	u8_t tx_power;
  	u8_t adv_tx_power;
  	u8_t lock[16];
  	u8_t challenge[16];
  	struct bt_data ad[3];
  };
  
  static struct eds_slot eds_slots[NUMBER_OF_SLOTS] = {
  	[0 ... (NUMBER_OF_SLOTS - 1)] = {
  		.type = EDS_TYPE_NONE,  /* Start as disabled */
  		.state = EDS_UNLOCKED, /* Start unlocked */
  		.interval = sys_cpu_to_be16(BT_GAP_ADV_FAST_INT_MIN_2),
  		.lock = { 'Z', 'e', 'p', 'h', 'y', 'r', ' ', 'E', 'd', 'd',
  			  'y', 's', 't', 'o', 'n', 'e' },
  		.challenge = {},
  		.ad = {
  			BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_NO_BREDR),
  			BT_DATA_BYTES(BT_DATA_UUID16_ALL, 0xaa, 0xfe),
  			BT_DATA_BYTES(BT_DATA_SVC_DATA16,
  				      0xaa, 0xfe, /* Eddystone UUID */
  				      0x10, /* Eddystone-URL frame type */
  				      0x00, /* Calibrated Tx power at 0m */
  				      0x00, /* URL Scheme Prefix http://www. */
  				      'z', 'e', 'p', 'h', 'y', 'r',
  				      'p', 'r', 'o', 'j', 'e', 'c', 't',
  				      0x08) /* .org */
  		},
  	},
  };
  
  static ssize_t read_caps(struct bt_conn *conn, const struct bt_gatt_attr *attr,
  			 void *buf, u16_t len, u16_t offset)
  {
  	const struct eds_capabilities *caps = attr->user_data;
  
  	return bt_gatt_attr_read(conn, attr, buf, len, offset, caps,
  				 sizeof(*caps));
  }
  
  static ssize_t read_slot(struct bt_conn *conn, const struct bt_gatt_attr *attr,
  			 void *buf, u16_t len, u16_t offset)
  {
  	return bt_gatt_attr_read(conn, attr, buf, len, offset,
  				 &eds_active_slot, sizeof(eds_active_slot));
  }
  
  static ssize_t write_slot(struct bt_conn *conn,
  			  const struct bt_gatt_attr *attr, const void *buf,
  			  u16_t len, u16_t offset, u8_t flags)
  {
  	u8_t value;
  
  	if (offset + len > sizeof(value)) {
  		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
  	}
  
  	memcpy(&value, buf, len);
  
  	if (value + 1 > NUMBER_OF_SLOTS) {
  		return BT_GATT_ERR(BT_ATT_ERR_WRITE_NOT_PERMITTED);
  	}
  
  	eds_active_slot = value;
  
  	return len;
  }
  
  static ssize_t read_tx_power(struct bt_conn *conn,
  			     const struct bt_gatt_attr *attr,
  			     void *buf, u16_t len, u16_t offset)
  {
  	struct eds_slot *slot = &eds_slots[eds_active_slot];
  
  	if (slot->state == EDS_LOCKED) {
  		return BT_GATT_ERR(BT_ATT_ERR_READ_NOT_PERMITTED);
  	}
  
  	return bt_gatt_attr_read(conn, attr, buf, len, offset, &slot->tx_power,
  				 sizeof(slot->tx_power));
  }
  
  static ssize_t write_tx_power(struct bt_conn *conn,
  			      const struct bt_gatt_attr *attr,
  			      const void *buf, u16_t len, u16_t offset,
  			      u8_t flags)
  {
  	struct eds_slot *slot = &eds_slots[eds_active_slot];
  
  	if (slot->state == EDS_LOCKED) {
  		return BT_GATT_ERR(BT_ATT_ERR_WRITE_NOT_PERMITTED);
  	}
  
  	if (offset + len > sizeof(slot->tx_power)) {
  		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
  	}
  
  	memcpy(&slot->tx_power, buf, len);
  
  	return len;
  }
  
  static ssize_t read_adv_tx_power(struct bt_conn *conn,
  				 const struct bt_gatt_attr *attr,
  				 void *buf, u16_t len, u16_t offset)
  {
  	struct eds_slot *slot = &eds_slots[eds_active_slot];
  
  	if (slot->state == EDS_LOCKED) {
  		return BT_GATT_ERR(BT_ATT_ERR_READ_NOT_PERMITTED);
  	}
  
  	return bt_gatt_attr_read(conn, attr, buf, len, offset, &slot->tx_power,
  				 sizeof(slot->tx_power));
  }
  
  static ssize_t write_adv_tx_power(struct bt_conn *conn,
  				  const struct bt_gatt_attr *attr,
  				  const void *buf, u16_t len,
  				  u16_t offset,
  				  u8_t flags)
  {
  	struct eds_slot *slot = &eds_slots[eds_active_slot];
  
  	if (slot->state == EDS_LOCKED) {
  		return BT_GATT_ERR(BT_ATT_ERR_WRITE_NOT_PERMITTED);
  	}
  
  	if (offset + len > sizeof(slot->adv_tx_power)) {
  		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
  	}
  
  	memcpy(&slot->adv_tx_power, buf, len);
  
  	return len;
  }
  
  static ssize_t read_interval(struct bt_conn *conn,
  			     const struct bt_gatt_attr *attr,
  			     void *buf, u16_t len, u16_t offset)
  {
  	struct eds_slot *slot = &eds_slots[eds_active_slot];
  
  	if (slot->state == EDS_LOCKED) {
  		return BT_GATT_ERR(BT_ATT_ERR_WRITE_NOT_PERMITTED);
  	}
  
  	return bt_gatt_attr_read(conn, attr, buf, len, offset, &slot->interval,
  				 sizeof(slot->interval));
  }
  
  static ssize_t read_lock(struct bt_conn *conn, const struct bt_gatt_attr *attr,
  			 void *buf, u16_t len, u16_t offset)
  {
  	struct eds_slot *slot = &eds_slots[eds_active_slot];
  
  	return bt_gatt_attr_read(conn, attr, buf, len, offset, &slot->state,
  				 sizeof(slot->state));
  }
  
  static ssize_t write_lock(struct bt_conn *conn,
  			  const struct bt_gatt_attr *attr, const void *buf,
  			  u16_t len, u16_t offset, u8_t flags)
  {
  	struct eds_slot *slot = &eds_slots[eds_active_slot];
  	u8_t value;
  
  	if (slot->state == EDS_LOCKED) {
  		return BT_GATT_ERR(BT_ATT_ERR_WRITE_NOT_PERMITTED);
  	}
  
  	if (offset) {
  		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
  	}
  
  	/* Write 1 byte to lock or 17 bytes to transition to a new lock state */
  	if (len != 1) {
  		/* TODO: Allow setting new lock code, using AES-128-ECB to
  		 * decrypt with the existing lock code and set the unencrypted
  		 * value as the new code.
  		 */
  		return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
  	}
  
  	memcpy(&value, buf, sizeof(value));
  
  	if (value > EDS_UNLOCKED_NO_RELOCKING) {
  		return BT_GATT_ERR(BT_ATT_ERR_WRITE_NOT_PERMITTED);
  	}
  
  	slot->state = value;
  
  	return len;
  }
  
  static ssize_t read_unlock(struct bt_conn *conn,
  			   const struct bt_gatt_attr *attr,
  			   void *buf, u16_t len, u16_t offset)
  {
  	struct eds_slot *slot = &eds_slots[eds_active_slot];
  
  	if (slot->state != EDS_LOCKED) {
  		return BT_GATT_ERR(BT_ATT_ERR_READ_NOT_PERMITTED);
  	}
  
  	/* returns a 128-bit challenge token. This token is for one-time use
  	 * and cannot be replayed.
  	 */
  	if (bt_rand(slot->challenge, sizeof(slot->challenge))) {
  		return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY);
  	}
  
  	return bt_gatt_attr_read(conn, attr, buf, len, offset, slot->challenge,
  				 sizeof(slot->challenge));
  }
  
  static ssize_t write_unlock(struct bt_conn *conn,
  			    const struct bt_gatt_attr *attr, const void *buf,
  			    u16_t len, u16_t offset, u8_t flags)
  {
  	struct eds_slot *slot = &eds_slots[eds_active_slot];
  
  	if (slot->state != EDS_LOCKED) {
  		return BT_GATT_ERR(BT_ATT_ERR_READ_NOT_PERMITTED);
  	}
  
  	/* TODO: accepts a 128-bit encrypted value that verifies the client
  	 * knows the beacon's lock code.
  	 */
  
  	return BT_GATT_ERR(BT_ATT_ERR_NOT_SUPPORTED);
  }
  
  static u8_t eds_ecdh[32] = {}; /* TODO: Add ECDH key */
  
  static ssize_t read_ecdh(struct bt_conn *conn, const struct bt_gatt_attr *attr,
  			 void *buf, u16_t len, u16_t offset)
  {
  	u8_t *value = attr->user_data;
  
  	return bt_gatt_attr_read(conn, attr, buf, len, offset, value,
  				 sizeof(eds_ecdh));
  }
  
  static u8_t eds_eid[16] = {}; /* TODO: Add EID key */
  
  static ssize_t read_eid(struct bt_conn *conn, const struct bt_gatt_attr *attr,
  			void *buf, u16_t len, u16_t offset)
  {
  	u8_t *value = attr->user_data;
  
  	return bt_gatt_attr_read(conn, attr, buf, len, offset, value,
  				 sizeof(eds_eid));
  }
  
  static ssize_t read_adv_data(struct bt_conn *conn,
  			     const struct bt_gatt_attr *attr, void *buf,
  			     u16_t len, u16_t offset)
  {
  	struct eds_slot *slot = &eds_slots[eds_active_slot];
  
  	if (slot->state == EDS_LOCKED) {
  		return BT_GATT_ERR(BT_ATT_ERR_READ_NOT_PERMITTED);
  	}
  
  	/* If the slot is currently not broadcasting, reading the slot data
  	 * shall return either an empty array or a single byte of 0x00.
  	 */
  	if (slot->type == EDS_TYPE_NONE) {
  		return 0;
  	}
  
  	return bt_gatt_attr_read(conn, attr, buf, len, offset,
  				 slot->ad[2].data + EDS_URL_READ_OFFSET,
  				 slot->ad[2].data_len - EDS_URL_READ_OFFSET);
  }
  
  static int eds_slot_restart(struct eds_slot *slot, u8_t type)
  {
  	int err;
  
  	/* Restart advertising */
  	bt_le_adv_stop();
  
  	if (type == EDS_TYPE_NONE) {
  		/* Restore connectable if slot */
  		err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad),
  				      sd, ARRAY_SIZE(sd));
  	} else {
  		err = bt_le_adv_start(BT_LE_ADV_NCONN, slot->ad,
  				      ARRAY_SIZE(slot->ad), sd, ARRAY_SIZE(sd));
  	}
  
  	if (err) {
  		printk("Advertising failed to start (err %d)\n", err);
  		return err;
  	}
  
  	slot->type = type;
  
  	return 0;
  }
  
  static ssize_t write_adv_data(struct bt_conn *conn,
  			      const struct bt_gatt_attr *attr,
  			      const void *buf, u16_t len, u16_t offset,
  			      u8_t flags)
  {
  	struct eds_slot *slot = &eds_slots[eds_active_slot];
  	u8_t type;
  
  	if (slot->state == EDS_LOCKED) {
  		return BT_GATT_ERR(BT_ATT_ERR_READ_NOT_PERMITTED);
  	}
  
  	if (offset) {
  		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
  	}
  
  	/* Writing an empty array, clears the slot and stops Tx. */
  	if (!len) {
  		eds_slot_restart(slot, EDS_TYPE_NONE);
  		return len;
  	}
  
  	/* Write length: 17 bytes (UID), 19 bytes (URL), 1 byte (TLM), 34 or
  	 * 18 bytes (EID)
  	 */
  	if (len > 19) {
  		return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
  	}
  
  	memcpy(&type, buf, sizeof(type));
  
  	switch (type) {
  	case EDS_TYPE_URL:
  		/* written data is just the frame type and any ID-related
  		 * information, and doesn't include the Tx power since that is
  		 * controlled by characteristics 4 (Radio Tx Power) and
  		 * 5 (Advertised Tx Power).
  		 */
  		slot->ad[2].data_len = min(slot->ad[2].data_len,
  					   len + EDS_URL_WRITE_OFFSET);
  		memcpy(&slot->ad[2].data + EDS_URL_WRITE_OFFSET, buf,
  		       slot->ad[2].data_len - EDS_URL_WRITE_OFFSET);
  
  		/* Restart slot */
  		if (eds_slot_restart(slot, type) < 0) {
  			return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY);
  		}
  
  		return len;
  	case EDS_TYPE_UID:
  	case EDS_TYPE_TLM:
  	case EDS_TYPE_EID:
  	default:
  		/* TODO: Add support for other types. */
  		return BT_GATT_ERR(BT_ATT_ERR_WRITE_NOT_PERMITTED);
  	}
  }
  
  static ssize_t write_reset(struct bt_conn *conn,
  			   const struct bt_gatt_attr *attr,
  			   const void *buf, u16_t len, u16_t offset,
  			   u8_t flags)
  {
  	/* TODO: Power cycle or reload for storage the values */
  	return BT_GATT_ERR(BT_ATT_ERR_WRITE_NOT_PERMITTED);
  }
  
  static ssize_t read_connectable(struct bt_conn *conn,
  			     const struct bt_gatt_attr *attr, void *buf,
  			     u16_t len, u16_t offset)
  {
  	u8_t connectable = 0x01;
  
  	/* Returning a non-zero value indicates that the beacon is capable
  	 * of becoming non-connectable
  	 */
  	return bt_gatt_attr_read(conn, attr, buf, len, offset,
  				 &connectable, sizeof(connectable));
  }
  
  static ssize_t write_connectable(struct bt_conn *conn,
  				 const struct bt_gatt_attr *attr,
  				 const void *buf, u16_t len, u16_t offset,
  				 u8_t flags)
  {
  	struct eds_slot *slot = &eds_slots[eds_active_slot];
  
  	if (slot->state == EDS_LOCKED) {
  		return BT_GATT_ERR(BT_ATT_ERR_WRITE_NOT_PERMITTED);
  	}
  
  	if (offset) {
  		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
  	}
  
  	if (len > sizeof(slot->connectable)) {
  		return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
  	}
  
  	/* If any non-zero value is written, the beacon shall remain in its
  	 * connectable state until any other value is written.
  	 */
  	memcpy(&slot->connectable, buf, len);
  
  	return len;
  }
  
  /* Eddystone Configuration Service Declaration */
  static struct bt_gatt_attr eds_attrs[] = {
  	BT_GATT_PRIMARY_SERVICE(&eds_uuid),
  	/* Capabilities: Readable only when unlocked. Never writable. */
  	BT_GATT_CHARACTERISTIC(&eds_caps_uuid.uuid, BT_GATT_CHRC_READ,
  			       BT_GATT_PERM_READ, read_caps, NULL, &eds_caps),
  	/* Active slot: Must be unlocked for both read and write. */
  	BT_GATT_CHARACTERISTIC(&eds_slot_uuid.uuid,
  			       BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
  			       BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
  			       read_slot, write_slot, NULL),
  	/* Advertising Interval: Must be unlocked for both read and write. */
  	BT_GATT_CHARACTERISTIC(&eds_intv_uuid.uuid, BT_GATT_CHRC_READ,
  			       BT_GATT_PERM_READ, read_interval, NULL, NULL),
  	/* Radio TX Power: Must be unlocked for both read and write. */
  	BT_GATT_CHARACTERISTIC(&eds_tx_uuid.uuid,
  			       BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
  			       BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
  			       read_tx_power, write_tx_power, NULL),
  	/* Advertised TX Power: Must be unlocked for both read and write. */
  	BT_GATT_CHARACTERISTIC(&eds_adv_tx_uuid.uuid,
  			       BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
  			       BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
  			       read_adv_tx_power, write_adv_tx_power, NULL),
  	/* Lock State:
  	 * Readable in locked or unlocked state.
  	 * Writeable only in unlocked state.
  	 */
  	BT_GATT_CHARACTERISTIC(&eds_lock_uuid.uuid,
  			       BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
  			       BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
  			       read_lock, write_lock, NULL),
  	/* Unlock:
  	 * Readable only in locked state.
  	 * Writeable only in locked state.
  	 */
  	BT_GATT_CHARACTERISTIC(&eds_unlock_uuid.uuid,
  			       BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
  			       BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
  			       read_unlock, write_unlock, NULL),
  	/* Public ECDH Key: Readable only in unlocked state. Never writable. */
  	BT_GATT_CHARACTERISTIC(&eds_ecdh_uuid.uuid, BT_GATT_CHRC_READ,
  			       BT_GATT_PERM_READ, read_ecdh, NULL, &eds_ecdh),
  	/* EID Identity Key:Readable only in unlocked state. Never writable. */
  	BT_GATT_CHARACTERISTIC(&eds_eid_uuid.uuid, BT_GATT_CHRC_READ,
  			       BT_GATT_PERM_READ, read_eid, NULL, eds_eid),
  	/* ADV Slot Data: Must be unlocked for both read and write. */
  	BT_GATT_CHARACTERISTIC(&eds_data_uuid.uuid,
  			       BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
  			       BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
  			       read_adv_data, write_adv_data, NULL),
  	/* ADV Factory Reset: Must be unlocked for write. */
  	BT_GATT_CHARACTERISTIC(&eds_reset_uuid.uuid,  BT_GATT_CHRC_WRITE,
  			       BT_GATT_PERM_WRITE, NULL, write_reset, NULL),
  	/* ADV Remain Connectable: Must be unlocked for write. */
  	BT_GATT_CHARACTERISTIC(&eds_connectable_uuid.uuid,
  			       BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
  			       BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
  			       read_connectable, write_connectable, NULL),
  };
  
  static struct bt_gatt_service eds_svc = BT_GATT_SERVICE(eds_attrs);
  
  static void bt_ready(int err)
  {
  	if (err) {
  		printk("Bluetooth init failed (err %d)\n", err);
  		return;
  	}
  
  	printk("Bluetooth initialized\n");
  
  	bt_gatt_service_register(&eds_svc);
  
  	/* Start advertising */
  	err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad),
  			      sd, ARRAY_SIZE(sd));
  	if (err) {
  		printk("Advertising failed to start (err %d)\n", err);
  		return;
  	}
  
  	k_delayed_work_submit(&idle_work, EDS_IDLE_TIMEOUT);
  
  	printk("Configuration mode: waiting connections...\n");
  }
  
  static void idle_timeout(struct k_work *work)
  {
  	if (eds_slots[eds_active_slot].type == EDS_TYPE_NONE) {
  		printk("Switching to Beacon mode.\n");
  		eds_slot_restart(&eds_slots[eds_active_slot], EDS_TYPE_URL);
  	}
  }
  
  static void connected(struct bt_conn *conn, u8_t err)
  {
  	if (err) {
  		printk("Connection failed (err %u)\n", err);
  	} else {
  		printk("Connected\n");
  		k_delayed_work_cancel(&idle_work);
  	}
  }
  
  static void disconnected(struct bt_conn *conn, u8_t reason)
  {
  	struct eds_slot *slot = &eds_slots[eds_active_slot];
  
  	printk("Disconnected (reason %u)\n", reason);
  
  	if (!slot->connectable) {
  		k_delayed_work_submit(&idle_work, 0);
  	}
  }
  
  static struct bt_conn_cb conn_callbacks = {
  	.connected = connected,
  	.disconnected = disconnected,
  };
  
  void main(void)
  {
  	int err;
  
  	bt_conn_cb_register(&conn_callbacks);
  	k_delayed_work_init(&idle_work, idle_timeout);
  
  	/* Initialize the Bluetooth Subsystem */
  	err = bt_enable(bt_ready);
  	if (err) {
  		printk("Bluetooth init failed (err %d)\n", err);
  	}
  }
  