As both C and C++ standards require applications running under an OS to
return 'int', adapt that for Zephyr to align with those standard. This also
eliminates errors when building with clang when not using -ffreestanding,
and reduces the need for compiler flags to silence warnings for both clang
and gcc.
Most of these changes were automated using coccinelle with the following
script:
@@
@@
- void
+ int
main(...) {
...
- return;
+ return 0;
...
}
Approximately 40 files had to be edited by hand as coccinelle was unable to
fix them.
Signed-off-by: Keith Packard <keithp@keithp.com>
405 lines
10 KiB
C
405 lines
10 KiB
C
/* 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/kernel.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);
|
|
}
|
|
|
|
int 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 0;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|