Linux Audio

Check our new training course

Embedded Linux Audio

Check our new training course
with Creative Commons CC-BY-SA
lecture materials

Bootlin logo

Elixir Cross Referencer

Loading...
/*
 * 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 <gatt/services.h>

#include "edtt_driver.h"
#include "bs_tracing.h"
#include "commands.h"

#define DEVICE_NAME		CONFIG_BT_DEVICE_NAME
#define DEVICE_NAME_LEN		(sizeof(DEVICE_NAME) - 1)

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,
		      0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12,
		      0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12),
};

static const struct bt_data sd[] = {
	BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
};

static int service_set;

static void connected(struct bt_conn *conn, uint8_t err)
{
	if (err) {
		printk("Connection failed (err %u)\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 security_changed(struct bt_conn *conn, bt_security_t level,
			     enum bt_security_err err)
{
	char addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	printk("Security changed: %s level %u\n", addr, level);
}

BT_CONN_CB_DEFINE(conn_callbacks) = {
	.connected = connected,
	.disconnected = disconnected,
	.security_changed = security_changed,
};

static void service_setup(int set)
{
	if (set == service_set) {
		printk("Ignored request to change GATT services set to #%d - "
			"already selected!\n", set);
		return;
	}
	switch (service_set) {
	case 0:
		break;
	case 1:
		service_c_2_1_remove();
		service_f_1_remove();
		service_c_1_1_remove();
		service_b_5_1_remove();
		service_b_2_1_remove();
		service_b_1_1_remove();
		service_b_3_1_remove();
		service_b_4_1_remove();
		service_a_1_remove();
		service_d_1_remove();
		break;
	case 2:
		service_e_2_remove();
		service_b_5_2_remove();
		service_b_2_2_remove();
		service_b_3_2_remove();
		service_a_2_remove();
		service_b_1_2_remove();
		service_d_2_remove();
		service_b_4_2_remove();
		service_c_1_2_remove();
		service_c_2_2_remove();
		break;
	case 3:
		service_e_3_remove();
		service_c_2_3_remove();
		service_b_2_3_remove();
		service_c_1_3_remove();
		service_a_3_remove();
		service_b_3_3_remove();
		service_b_4_3_remove();
		service_b_5_3_remove();
		service_d_3_remove();
		service_b_1_3_remove();
		break;
	default:
		break;
	}

	switch (set) {
	case 0:
		break;
	case 1:
		service_d_1_init();
		service_a_1_init();
		service_b_4_1_init();
		service_b_3_1_init();
		service_b_1_1_init();
		service_b_2_1_init();
		service_b_5_1_init();
		service_c_1_1_init();
		service_f_1_init();
		service_c_2_1_init();
		break;
	case 2:
		service_c_2_2_init();
		service_c_1_2_init();
		service_b_4_2_init();
		service_d_2_init();
		service_b_1_2_init();
		service_a_2_init();
		service_b_3_2_init();
		service_b_2_2_init();
		service_b_5_2_init();
		service_e_2_init();
		break;
	case 3:
		service_b_1_3_init();
		service_d_3_init();
		service_b_5_3_init();
		service_b_4_3_init();
		service_b_3_3_init();
		service_a_3_init();
		service_c_1_3_init();
		service_b_2_3_init();
		service_c_2_3_init();
		service_e_3_init();
		break;
	default:
		break;
	}
	service_set = set;
	printk("Switched to GATT services set to #%d\n", set);
}

static void service_notify(void)
{
	switch (service_set) {
	case 0:
		break;
	case 1:
		service_b_3_1_value_v6_notify();
		break;
	case 2:
		service_b_3_2_value_v6_notify();
		break;
	case 3:
		service_b_3_3_value_v6_notify();
		break;
	default:
		break;
	}
}

static void service_indicate(void)
{
	switch (service_set) {
	case 0:
		break;
	case 1:
		break;
	case 2:
		service_b_3_2_value_v6_indicate();
		break;
	case 3:
		service_b_3_3_value_v6_indicate();
		break;
	default:
		break;
	}
}

static void bt_ready(int err)
{
	if (err) {
		printk("Bluetooth init failed (err %d)\n", err);
		return;
	}

	printk("Bluetooth initialized\n");

	service_setup(1);

	printk("GATT Services initialized\n");

	if (IS_ENABLED(CONFIG_SETTINGS)) {
		settings_load();
	}

	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;
	}

	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,
};

/**
 * @brief Clean out excess bytes from the input buffer
 */
static void read_excess_bytes(uint16_t size)
{
	if (size > 0) {
		uint8_t buffer[size];

		edtt_read((uint8_t *)buffer, size, EDTTT_BLOCK);
		printk("command size wrong! (%u extra bytes removed)", size);
	}
}

/**
 * @brief Switch GATT Service Set
 */
static void switch_service_set(uint16_t size)
{
	uint16_t response = sys_cpu_to_le16(CMD_GATT_SERVICE_SET_RSP);
	uint8_t  set;

	if (size > 0) {
		edtt_read((uint8_t *)&set, sizeof(set), EDTTT_BLOCK);
		service_setup((int)set);
		size -= sizeof(set);
	}
	read_excess_bytes(size);
	size = 0;

	edtt_write((uint8_t *)&response, sizeof(response), EDTTT_BLOCK);
	edtt_write((uint8_t *)&size, sizeof(size), EDTTT_BLOCK);
}

/**
 * @brief Send Notifications from GATT Service Set
 */
static void handle_service_notify(uint16_t size)
{
	uint16_t response = sys_cpu_to_le16(CMD_GATT_SERVICE_NOTIFY_RSP);

	service_notify();
	read_excess_bytes(size);
	size = 0;

	edtt_write((uint8_t *)&response, sizeof(response), EDTTT_BLOCK);
	edtt_write((uint8_t *)&size, sizeof(size), EDTTT_BLOCK);
}

/**
 * @brief Send Indications from GATT Service Set
 */
static void handle_service_indicate(uint16_t size)
{
	uint16_t response = sys_cpu_to_le16(CMD_GATT_SERVICE_INDICATE_RSP);

	service_indicate();
	read_excess_bytes(size);
	size = 0;

	edtt_write((uint8_t *)&response, sizeof(response), EDTTT_BLOCK);
	edtt_write((uint8_t *)&size, sizeof(size), EDTTT_BLOCK);
}

void main(void)
{
	int err;
	uint16_t command;
	uint16_t size;

	err = bt_enable(bt_ready);
	if (err) {
		printk("Bluetooth init failed (err %d)\n", err);
		return;
	}

	bt_conn_auth_cb_register(&auth_cb_display);

	/**
	 * Initialize and start EDTT system
	 */
#if defined(CONFIG_ARCH_POSIX)
	enable_edtt_mode();
	set_edtt_autoshutdown(true);
#endif
	edtt_start();

	/* Implement notification. At the moment there is no suitable way
	 * of starting delayed work so we do it here
	 */
	while (1) {
		/**
		 * Wait for a command to arrive - then read and execute command
		 */
		edtt_read((uint8_t *)&command, sizeof(command), EDTTT_BLOCK);
		command = sys_le16_to_cpu(command);
		edtt_read((uint8_t *)&size, sizeof(size), EDTTT_BLOCK);
		size = sys_le16_to_cpu(size);
		bs_trace_raw_time(4, "command 0x%04X received (size %u)\n",
				command, size);

		switch (command) {
		case CMD_GATT_SERVICE_SET_REQ:
			switch_service_set(size);
			break;
		case CMD_GATT_SERVICE_NOTIFY_REQ:
			handle_service_notify(size);
			break;
		case CMD_GATT_SERVICE_INDICATE_REQ:
			handle_service_indicate(size);
			break;
		default:
			break;
		}
	}
}