193 lines
5.0 KiB
C
193 lines
5.0 KiB
C
/*
|
|
* Copyright 2023 NXP
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/sys/byteorder.h>
|
|
#include <zephyr/types.h>
|
|
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/init.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <zephyr/bluetooth/bluetooth.h>
|
|
#include <zephyr/bluetooth/conn.h>
|
|
#include <zephyr/bluetooth/gatt.h>
|
|
|
|
#include "audio_internal.h"
|
|
#include <zephyr/bluetooth/audio/tmap.h>
|
|
#include <zephyr/logging/log.h>
|
|
|
|
LOG_MODULE_REGISTER(bt_tmap, CONFIG_BT_TMAP_LOG_LEVEL);
|
|
|
|
/* Hex value if all TMAP role bits are set */
|
|
#define TMAP_ALL_ROLES 0x3F
|
|
|
|
static uint16_t tmap_role;
|
|
static const struct bt_tmap_cb *cb;
|
|
static bool tmas_found;
|
|
|
|
static struct bt_uuid_16 uuid[CONFIG_BT_MAX_CONN] = {BT_UUID_INIT_16(0)};
|
|
static struct bt_gatt_discover_params discover_params[CONFIG_BT_MAX_CONN];
|
|
static struct bt_gatt_read_params read_params[CONFIG_BT_MAX_CONN];
|
|
|
|
uint8_t tmap_char_read(struct bt_conn *conn, uint8_t err,
|
|
struct bt_gatt_read_params *params,
|
|
const void *data, uint16_t length)
|
|
{
|
|
uint16_t peer_role;
|
|
|
|
/* Check read request result */
|
|
if (err != BT_ATT_ERR_SUCCESS) {
|
|
if (cb->discovery_complete) {
|
|
cb->discovery_complete(0, conn, err);
|
|
}
|
|
|
|
return BT_GATT_ITER_STOP;
|
|
}
|
|
|
|
/* Check data length */
|
|
if (length != sizeof(peer_role)) {
|
|
if (cb->discovery_complete) {
|
|
cb->discovery_complete(0, conn, BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
|
|
}
|
|
|
|
return BT_GATT_ITER_STOP;
|
|
}
|
|
|
|
/* Extract the TMAP role of the peer and inform application fo the value found */
|
|
peer_role = sys_get_le16(data);
|
|
|
|
if ((peer_role > 0U) && (peer_role <= TMAP_ALL_ROLES)) {
|
|
if (cb->discovery_complete) {
|
|
cb->discovery_complete((enum bt_tmap_role)peer_role, conn, 0);
|
|
}
|
|
} else {
|
|
if (cb->discovery_complete) {
|
|
cb->discovery_complete(0, conn, BT_ATT_ERR_VALUE_NOT_ALLOWED);
|
|
}
|
|
}
|
|
|
|
return BT_GATT_ITER_STOP;
|
|
}
|
|
|
|
static uint8_t discover_func(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
|
struct bt_gatt_discover_params *params)
|
|
{
|
|
int err;
|
|
uint8_t conn_id = bt_conn_index(conn);
|
|
|
|
if (!attr) {
|
|
(void)memset(params, 0, sizeof(*params));
|
|
if (!tmas_found) {
|
|
/* TMAS not found on peer */
|
|
if (cb->discovery_complete) {
|
|
cb->discovery_complete(0, conn, BT_ATT_ERR_ATTRIBUTE_NOT_FOUND);
|
|
}
|
|
}
|
|
|
|
tmas_found = false;
|
|
|
|
return BT_GATT_ITER_STOP;
|
|
}
|
|
|
|
if (!bt_uuid_cmp(discover_params[conn_id].uuid, BT_UUID_TMAS)) {
|
|
LOG_DBG("Discovered TMAS\n");
|
|
tmas_found = true;
|
|
memcpy(&uuid[conn_id], BT_UUID_GATT_TMAPR, sizeof(uuid[conn_id]));
|
|
discover_params[conn_id].uuid = &uuid[conn_id].uuid;
|
|
discover_params[conn_id].start_handle = attr->handle + 1;
|
|
discover_params[conn_id].type = BT_GATT_DISCOVER_CHARACTERISTIC;
|
|
|
|
/* Discovered TMAS - Search for TMAP Role characteristic */
|
|
err = bt_gatt_discover(conn, &discover_params[conn_id]);
|
|
if (err) {
|
|
LOG_DBG("Discover failed (err %d)\n", err);
|
|
}
|
|
} else if (!bt_uuid_cmp(discover_params[conn_id].uuid, BT_UUID_GATT_TMAPR)) {
|
|
/* Use 0 for now, will expand later */
|
|
read_params[conn_id].func = tmap_char_read;
|
|
read_params[conn_id].handle_count = 1u;
|
|
read_params[conn_id].single.handle = bt_gatt_attr_value_handle(attr);
|
|
read_params[conn_id].single.offset = 0;
|
|
|
|
/* Discovered TMAP Role characteristic - read value */
|
|
err = bt_gatt_read(conn, &read_params[0]);
|
|
if (err != 0) {
|
|
printk("Could not read peer TMAP Role\n");
|
|
}
|
|
} else {
|
|
return BT_GATT_ITER_CONTINUE;
|
|
}
|
|
|
|
return BT_GATT_ITER_STOP;
|
|
}
|
|
|
|
static ssize_t read_role(struct bt_conn *conn,
|
|
const struct bt_gatt_attr *attr, void *buf,
|
|
uint16_t len, uint16_t offset)
|
|
{
|
|
uint16_t role;
|
|
|
|
role = sys_cpu_to_le16(tmap_role);
|
|
LOG_DBG("TMAP: role 0x%04X", role);
|
|
|
|
return bt_gatt_attr_read(conn, attr, buf, len, offset,
|
|
&role, sizeof(role));
|
|
}
|
|
|
|
/* Telephony and Media Audio Service attributes */
|
|
#define BT_TMAS_SERVICE_DEFINITION \
|
|
BT_GATT_PRIMARY_SERVICE(BT_UUID_TMAS), \
|
|
BT_GATT_CHARACTERISTIC(BT_UUID_GATT_TMAPR, \
|
|
BT_GATT_CHRC_READ, \
|
|
BT_GATT_PERM_READ, \
|
|
read_role, NULL, NULL)
|
|
|
|
static struct bt_gatt_attr svc_attrs[] = { BT_TMAS_SERVICE_DEFINITION };
|
|
static struct bt_gatt_service tmas;
|
|
|
|
int bt_tmap_register(enum bt_tmap_role role)
|
|
{
|
|
int err;
|
|
|
|
tmas = (struct bt_gatt_service)BT_GATT_SERVICE(svc_attrs);
|
|
|
|
err = bt_gatt_service_register(&tmas);
|
|
if (err) {
|
|
LOG_DBG("Could not register the TMAS service");
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
tmap_role = role;
|
|
tmas_found = false;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_tmap_discover(struct bt_conn *conn, const struct bt_tmap_cb *tmap_cb)
|
|
{
|
|
int err = 0;
|
|
uint8_t conn_id = bt_conn_index(conn);
|
|
|
|
cb = tmap_cb;
|
|
|
|
memcpy(&uuid[conn_id], BT_UUID_TMAS, sizeof(uuid[conn_id]));
|
|
discover_params[conn_id].uuid = &uuid[conn_id].uuid;
|
|
discover_params[conn_id].func = discover_func;
|
|
discover_params[conn_id].start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
|
|
discover_params[conn_id].end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
|
|
discover_params[conn_id].type = BT_GATT_DISCOVER_PRIMARY;
|
|
|
|
err = bt_gatt_discover(conn, &discover_params[conn_id]);
|
|
|
|
return err;
|
|
}
|
|
|
|
void bt_tmap_set_role(enum bt_tmap_role role)
|
|
{
|
|
tmap_role = role;
|
|
}
|