blob: 1806fde57d6a8686bf017a91914fd295f671ef8e [file] [log] [blame]
/* main.c - Application main entry point */
/*
* Copyright (c) 2015 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <misc/printk.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 "Test peripheral"
#define HEART_RATE_APPEARANCE 0x0341
static struct bt_uuid gap_uuid = {
.type = BT_UUID_16,
.u16 = BT_UUID_GAP,
};
static struct bt_uuid device_name_uuid = {
.type = BT_UUID_16,
.u16 = BT_UUID_GAP_DEVICE_NAME,
};
static struct bt_gatt_chrc name_chrc = {
.properties = BT_GATT_CHRC_READ,
.value_handle = 0x0003,
.uuid = &device_name_uuid,
};
static int read_name(struct bt_conn *conn, const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset)
{
const char *name = attr->user_data;
return bt_gatt_attr_read(conn, attr, buf, len, offset, name,
strlen(name));
}
static struct bt_uuid appeareance_uuid = {
.type = BT_UUID_16,
.u16 = BT_UUID_GAP_APPEARANCE,
};
static struct bt_gatt_chrc appearance_chrc = {
.properties = BT_GATT_CHRC_READ,
.value_handle = 0x0005,
.uuid = &appeareance_uuid,
};
static int read_appearance(struct bt_conn *conn,
const struct bt_gatt_attr *attr, void *buf,
uint16_t len, uint16_t offset)
{
uint16_t appearance = sys_cpu_to_le16(HEART_RATE_APPEARANCE);
return bt_gatt_attr_read(conn, attr, buf, len, offset, &appearance,
sizeof(appearance));
}
static struct bt_uuid hrs_uuid = {
.type = BT_UUID_16,
.u16 = BT_UUID_HRS,
};
static struct bt_uuid hrmc_uuid = {
.type = BT_UUID_16,
.u16 = BT_UUID_HRS_MEASUREMENT,
};
static struct bt_gatt_chrc hrmc_chrc = {
.properties = BT_GATT_CHRC_NOTIFY,
.value_handle = 0x0008,
.uuid = &hrmc_uuid,
};
static struct bt_uuid bslc_uuid = {
.type = BT_UUID_16,
.u16 = BT_UUID_HRS_BODY_SENSOR,
};
static struct bt_gatt_chrc bslc_chrc = {
.properties = BT_GATT_CHRC_READ,
.value_handle = 0x000b,
.uuid = &bslc_uuid,
};
static struct bt_uuid hrcpc_uuid = {
.type = BT_UUID_16,
.u16 = BT_UUID_HRS_CONTROL_POINT,
};
static struct bt_gatt_chrc hrcpc_chrc = {
.properties = BT_GATT_CHRC_WRITE,
.value_handle = 0x000d,
.uuid = &hrcpc_uuid,
};
static struct bt_gatt_ccc_cfg hrmc_ccc_cfg[CONFIG_BLUETOOTH_MAX_PAIRED] = {};
static uint8_t simulate_hrm = 0;
static void hrmc_ccc_cfg_changed(uint16_t value)
{
simulate_hrm = (value == BT_GATT_CCC_NOTIFY) ? 1 : 0;
}
static int read_blsc(struct bt_conn *conn, const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset)
{
uint8_t value = 0x01;
return bt_gatt_attr_read(conn, attr, buf, len, offset, &value,
sizeof(value));
}
/* Battery Service Variables */
static struct bt_uuid bas_uuid = {
.type = BT_UUID_16,
.u16 = BT_UUID_BAS,
};
static struct bt_uuid blvl_uuid = {
.type = BT_UUID_16,
.u16 = BT_UUID_BAS_BATTERY_LEVEL,
};
static struct bt_gatt_chrc blvl_chrc = {
.properties = BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
.value_handle = 0x0010,
.uuid = &blvl_uuid,
};
static struct bt_gatt_ccc_cfg blvl_ccc_cfg[CONFIG_BLUETOOTH_MAX_PAIRED] = {};
static uint8_t simulate_blvl = 0;
static uint8_t battery = 100;
static void blvl_ccc_cfg_changed(uint16_t value)
{
simulate_blvl = (value == BT_GATT_CCC_NOTIFY) ? 1 : 0;
}
static int read_blvl(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(*value));
}
/* Current Time Service Variables */
static struct bt_uuid cts_uuid = {
.type = BT_UUID_16,
.u16 = BT_UUID_CTS,
};
static struct bt_uuid ct_uuid = {
.type = BT_UUID_16,
.u16 = BT_UUID_CTS_CURRENT_TIME,
};
static struct bt_gatt_chrc ct_chrc = {
.properties = BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY |
BT_GATT_CHRC_WRITE,
.value_handle = 0x0014,
.uuid = &ct_uuid,
};
static void generate_current_time(uint8_t *buf)
{
uint16_t year;
/* 'Exact Time 256' contains 'Day Date Time' which contains
* 'Date Time' - characteristic contains fields for:
* year, month, day, hours, minutes and seconds.
*/
year = sys_cpu_to_le16(2015);
memcpy(buf, &year, 2); /* year */
buf[2] = 5; /* months starting from 1 */
buf[3] = 30; /* day */
buf[4] = 12; /* hours */
buf[5] = 45; /* minutes */
buf[6] = 30; /* seconds */
/* 'Day of Week' part of 'Day Date Time' */
buf[7] = 1; /* day of week starting from 1 */
/* 'Fractions 256 part of 'Exact Time 256' */
buf[8] = 0;
/* Adjust reason */
buf[9] = 0; /* No update, change, etc */
}
static struct bt_gatt_ccc_cfg ct_ccc_cfg[CONFIG_BLUETOOTH_MAX_PAIRED] = {};
static void ct_ccc_cfg_changed(uint16_t value)
{
/* TODO: Handle value */
}
static uint8_t ct[10];
static uint8_t ct_update = 0;
static int read_ct(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(ct));
}
static int write_ct(struct bt_conn *conn, const struct bt_gatt_attr *attr,
const void *buf, uint16_t len, uint16_t offset)
{
uint8_t *value = attr->user_data;
if (offset + len > sizeof(ct)) {
return -EINVAL;
}
memcpy(value + offset, buf, len);
ct_update = 1;
return len;
}
/* Device Information Service Variables */
static struct bt_uuid dis_uuid = {
.type = BT_UUID_16,
.u16 = BT_UUID_DIS,
};
static struct bt_uuid model_uuid = {
.type = BT_UUID_16,
.u16 = BT_UUID_DIS_MODEL_NUMBER_STRING,
};
static struct bt_gatt_chrc model_chrc = {
.properties = BT_GATT_CHRC_READ,
.value_handle = 0x0018,
.uuid = &model_uuid,
};
static int read_model(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 struct bt_uuid manuf_uuid = {
.type = BT_UUID_16,
.u16 = BT_UUID_DIS_MANUFACTURER_NAME_STRING,
};
static struct bt_gatt_chrc manuf_chrc = {
.properties = BT_GATT_CHRC_READ,
.value_handle = 0x001a,
.uuid = &manuf_uuid,
};
static int read_manuf(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));
}
/* Custom Service Variables */
static struct bt_uuid vnd_uuid = {
.type = BT_UUID_128,
.u128 = { 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12,
0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12 },
};
static struct bt_uuid vnd_enc_uuid = {
.type = BT_UUID_128,
.u128 = { 0xf1, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12,
0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12 },
};
static struct bt_gatt_chrc vnd_enc_chrc = {
.properties = BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
.value_handle = 0x001d,
.uuid = &vnd_enc_uuid,
};
static struct bt_uuid vnd_auth_uuid = {
.type = BT_UUID_128,
.u128 = { 0xf2, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12,
0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12 },
};
static struct bt_gatt_chrc vnd_auth_chrc = {
.properties = BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
.value_handle = 0x001f,
.uuid = &vnd_auth_uuid,
};
static uint8_t vnd_value[] = { 'V', 'e', 'n', 'd', 'o', 'r' };
static int 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 int write_vnd(struct bt_conn *conn, const struct bt_gatt_attr *attr,
const void *buf, uint16_t len, uint16_t offset)
{
uint8_t *value = attr->user_data;
if (offset + len > sizeof(vnd_value)) {
return -EINVAL;
}
memcpy(value + offset, buf, len);
return len;
}
static struct vnd_long_value {
/* TODO: buffer needs to be per connection */
uint8_t buf[BT_BUF_MAX_DATA];
uint8_t data[BT_BUF_MAX_DATA];
} vnd_long_value = {
.buf = { 'V', 'e', 'n', 'd', 'o', 'r' },
.data = { 'V', 'e', 'n', 'd', 'o', 'r' },
};
static int read_long_vnd(struct bt_conn *conn,
const struct bt_gatt_attr *attr, void *buf,
uint16_t len, uint16_t offset)
{
struct vnd_long_value *value = attr->user_data;
return bt_gatt_attr_read(conn, attr, buf, len, offset, value->data,
sizeof(value->data));
}
static int write_long_vnd(struct bt_conn *conn,
const struct bt_gatt_attr *attr, const void *buf,
uint16_t len, uint16_t offset)
{
struct vnd_long_value *value = attr->user_data;
if (offset + len > sizeof(value->buf)) {
return -EINVAL;
}
/* Copy to buffer */
memcpy(value->buf + offset, buf, len);
return len;
}
static int flush_long_vnd(struct bt_conn *conn,
const struct bt_gatt_attr *attr, uint8_t flags)
{
struct vnd_long_value *value = attr->user_data;
switch (flags) {
case BT_GATT_FLUSH_DISCARD:
/* Discard buffer reseting it back with data */
memcpy(value->buf, value->data, sizeof(value->buf));
return 0;
case BT_GATT_FLUSH_SYNC:
/* Sync buffer to data */
memcpy(value->data, value->buf, sizeof(value->data));
return 0;
}
return -EINVAL;
}
static const struct bt_uuid vnd_long_uuid = {
.type = BT_UUID_128,
.u128 = { 0xf3, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12,
0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12 },
};
static struct bt_gatt_chrc vnd_long_chrc = {
.properties = BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE |
BT_GATT_CHRC_EXT_PROP,
.value_handle = 0x0021,
.uuid = &vnd_long_uuid,
};
static struct bt_gatt_cep vnd_long_cep = {
.properties = BT_GATT_CEP_RELIABLE_WRITE,
};
static int signed_value;
static int 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 int write_signed(struct bt_conn *conn, const struct bt_gatt_attr *attr,
const void *buf, uint16_t len, uint16_t offset)
{
uint8_t *value = attr->user_data;
if (offset + len > sizeof(signed_value)) {
return -EINVAL;
}
memcpy(value + offset, buf, len);
return len;
}
static const struct bt_uuid vnd_signed_uuid = {
.type = BT_UUID_128,
.u128 = { 0xf3, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x13,
0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x13 },
};
static struct bt_gatt_chrc vnd_signed_chrc = {
.properties = BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE |
BT_GATT_CHRC_AUTH,
.value_handle = 0x0024,
.uuid = &vnd_signed_uuid,
};
static const struct bt_gatt_attr attrs[] = {
BT_GATT_PRIMARY_SERVICE(0x0001, &gap_uuid),
BT_GATT_CHARACTERISTIC(0x0002, &name_chrc),
BT_GATT_DESCRIPTOR(0x0003, &device_name_uuid, BT_GATT_PERM_READ,
read_name, NULL, DEVICE_NAME),
BT_GATT_CHARACTERISTIC(0x0004, &appearance_chrc),
BT_GATT_DESCRIPTOR(0x0005, &appeareance_uuid, BT_GATT_PERM_READ,
read_appearance, NULL, NULL),
/* Heart Rate Service Declaration */
BT_GATT_PRIMARY_SERVICE(0x0006, &hrs_uuid),
BT_GATT_CHARACTERISTIC(0x0007, &hrmc_chrc),
BT_GATT_DESCRIPTOR(0x0008, &hrmc_uuid, BT_GATT_PERM_READ, NULL, NULL,
NULL),
BT_GATT_CCC(0x0009, 0x0008, hrmc_ccc_cfg, hrmc_ccc_cfg_changed),
BT_GATT_CHARACTERISTIC(0x000a, &bslc_chrc),
BT_GATT_DESCRIPTOR(0x000b, &bslc_uuid, BT_GATT_PERM_READ, read_blsc,
NULL, NULL),
BT_GATT_CHARACTERISTIC(0x000c, &hrcpc_chrc),
/* TODO: Add write permission and callback */
BT_GATT_DESCRIPTOR(0x000d, &hrcpc_uuid, BT_GATT_PERM_READ, NULL, NULL,
NULL),
/* Battery Service Declaration */
BT_GATT_PRIMARY_SERVICE(0x000e, &bas_uuid),
BT_GATT_CHARACTERISTIC(0x000f, &blvl_chrc),
BT_GATT_DESCRIPTOR(0x0010, &blvl_uuid, BT_GATT_PERM_READ, read_blvl,
NULL, &battery),
BT_GATT_CCC(0x0011, 0x0010, blvl_ccc_cfg, blvl_ccc_cfg_changed),
/* Current Time Service Declaration */
BT_GATT_PRIMARY_SERVICE(0x0012, &cts_uuid),
BT_GATT_CHARACTERISTIC(0x0013, &ct_chrc),
BT_GATT_DESCRIPTOR(0x0014, &ct_uuid,
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
read_ct, write_ct, ct),
BT_GATT_CCC(0x0015, 0x0014, ct_ccc_cfg, ct_ccc_cfg_changed),
/* Device Information Service Declaration */
BT_GATT_PRIMARY_SERVICE(0x0016, &dis_uuid),
BT_GATT_CHARACTERISTIC(0x0017, &model_chrc),
BT_GATT_DESCRIPTOR(0x0018, &model_uuid, BT_GATT_PERM_READ,
read_model, NULL, CONFIG_PLATFORM),
BT_GATT_CHARACTERISTIC(0x0019, &manuf_chrc),
BT_GATT_DESCRIPTOR(0x001a, &manuf_uuid, BT_GATT_PERM_READ,
read_manuf, NULL, "Manufacturer"),
/* Vendor Primary Service Declaration */
BT_GATT_PRIMARY_SERVICE(0x001b, &vnd_uuid),
BT_GATT_CHARACTERISTIC(0x001c, &vnd_enc_chrc),
BT_GATT_DESCRIPTOR(0x001d, &vnd_enc_uuid,
BT_GATT_PERM_READ | BT_GATT_PERM_READ_ENCRYPT |
BT_GATT_PERM_WRITE | BT_GATT_PERM_WRITE_ENCRYPT,
read_vnd, write_vnd, vnd_value),
BT_GATT_CHARACTERISTIC(0x001f, &vnd_auth_chrc),
BT_GATT_DESCRIPTOR(0x001f, &vnd_auth_uuid,
BT_GATT_PERM_READ | BT_GATT_PERM_READ_AUTHEN |
BT_GATT_PERM_WRITE | BT_GATT_PERM_WRITE_AUTHEN,
read_vnd, write_vnd, vnd_value),
BT_GATT_CHARACTERISTIC(0x0020, &vnd_long_chrc),
BT_GATT_LONG_DESCRIPTOR(0x0021, &vnd_long_uuid,
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
read_long_vnd, write_long_vnd, flush_long_vnd,
&vnd_long_value),
BT_GATT_CEP(0x0022, &vnd_long_cep),
BT_GATT_CHARACTERISTIC(0x0023, &vnd_signed_chrc),
BT_GATT_DESCRIPTOR(0x0024, &vnd_signed_uuid,
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
read_signed, write_signed, &signed_value),
};
static const struct bt_eir ad[] = {
{
.len = 2,
.type = BT_EIR_FLAGS,
.data = { BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR },
},
{
.len = 7,
.type = BT_EIR_UUID16_ALL,
.data = { 0x0d, 0x18, 0x0f, 0x18, 0x05, 0x18 },
},
{
.len = 17,
.type = BT_EIR_UUID128_ALL,
.data = { 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12,
0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12 },
},
{ }
};
static const struct bt_eir sd[] = {
{
.len = 16,
.type = BT_EIR_NAME_COMPLETE,
.data = DEVICE_NAME,
},
{ }
};
static void connected(struct bt_conn *conn)
{
printk("Connected\n");
}
static void disconnected(struct bt_conn *conn)
{
printk("Disconnected\n");
}
static struct bt_conn_cb conn_callbacks = {
.connected = connected,
.disconnected = disconnected,
};
static void bt_ready(int err)
{
if (err) {
printk("Bluetooth init failed (err %d)\n", err);
return;
}
printk("Bluetooth initialized\n");
err = bt_start_advertising(BT_LE_ADV_IND, ad, sd);
if (err) {
printk("Advertising failed to start (err %d)\n", err);
return;
}
printk("Advertising successfully started\n");
}
#ifdef CONFIG_MICROKERNEL
void mainloop(void)
#else
void main(void)
#endif
{
int err;
err = bt_enable(bt_ready);
if (err) {
printk("Bluetooth init failed (err %d)\n", err);
return;
}
/* Simulate current time for Current Time Service */
generate_current_time(ct);
bt_gatt_register(attrs, ARRAY_SIZE(attrs));
bt_conn_cb_register(&conn_callbacks);
/* Implement notification. At the moment there is no suitable way
* of starting delayed work so we do it here
*/
while (1) {
static uint8_t hrm[2];
task_sleep(sys_clock_ticks_per_sec);
/* Current Time Service updates only when time is changed */
if (ct_update) {
ct_update = 0;
bt_gatt_notify(0x0014, &ct, sizeof(ct));
}
/* Heartrate measurements simulation */
if (simulate_hrm) {
hrm[0] = 0x06; /* uint8, sensor contact */
hrm[1] = 90 + (sys_rand32_get() % 20);
bt_gatt_notify(0x0008, &hrm, sizeof(hrm));
}
/* Battery level simulation */
if (simulate_blvl) {
battery -= 1;
if (!battery) {
/* Software eco battery charger */
battery = 100;
}
bt_gatt_notify(0x0010, &battery, sizeof(battery));
}
}
}