zephyr/samples/bluetooth/tmap_central/src/main.c
Lingao Meng 786b9a0ad4 Bluetooth: Host: Add const prefix for UUID
Add const prefix for service uuid and char uuid.

Since Service UUID and Char UUID should not change in the service
definition, they are most reasonably defined as rodata, also for
save some ram footprint.

The field `attr->user_data` type is `void *`, as this PR change
all Service UUID to rodata, so there must add (void *) to avoid warning.

Signed-off-by: Lingao Meng <menglingao@xiaomi.com>
2023-12-07 16:16:43 +00:00

337 lines
7.2 KiB
C

/*
* Copyright 2023 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/types.h>
#include <stddef.h>
#include <errno.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/printk.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/audio/audio.h>
#include <zephyr/bluetooth/audio/bap.h>
#include <zephyr/bluetooth/audio/bap_lc3_preset.h>
#include <zephyr/bluetooth/audio/tmap.h>
#include <zephyr/bluetooth/audio/cap.h>
#include "tmap_central.h"
static struct bt_conn *default_conn;
static K_SEM_DEFINE(sem_connected, 0, 1);
static K_SEM_DEFINE(sem_security_updated, 0, 1);
static K_SEM_DEFINE(sem_disconnected, 0, 1);
static K_SEM_DEFINE(sem_mtu_exchanged, 0, 1);
static K_SEM_DEFINE(sem_discovery_done, 0, 1);
static void att_mtu_updated(struct bt_conn *conn, uint16_t tx, uint16_t rx)
{
printk("MTU exchanged: %u/%u\n", tx, rx);
k_sem_give(&sem_mtu_exchanged);
}
static struct bt_gatt_cb gatt_callbacks = {
.att_mtu_updated = att_mtu_updated
};
void tmap_discovery_complete(enum bt_tmap_role role, struct bt_conn *conn, int err)
{
if (conn != default_conn) {
return;
}
if (err) {
printk("TMAS discovery failed! (err %d)\n", err);
return;
}
printk("TMAS discovery done\n");
k_sem_give(&sem_discovery_done);
}
static struct bt_tmap_cb tmap_callbacks = {
.discovery_complete = tmap_discovery_complete
};
static void start_scan(void);
static int init(void)
{
int err;
err = bt_enable(NULL);
if (err != 0) {
printk("Bluetooth enable failed (err %d)\n", err);
return err;
}
printk("Bluetooth initialized\n");
bt_gatt_cb_register(&gatt_callbacks);
return 0;
}
static void connected(struct bt_conn *conn, uint8_t err)
{
char addr[BT_ADDR_LE_STR_LEN];
(void)bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
if (err != 0) {
printk("Failed to connect to %s (%u)\n", addr, err);
bt_conn_unref(default_conn);
default_conn = NULL;
start_scan();
return;
}
if (conn != default_conn) {
return;
}
printk("Connected: %s\n", addr);
k_sem_give(&sem_connected);
}
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
char addr[BT_ADDR_LE_STR_LEN];
if (conn != default_conn) {
return;
}
(void)bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
printk("Disconnected: %s (reason 0x%02x)\n", addr, reason);
bt_conn_unref(default_conn);
default_conn = NULL;
k_sem_give(&sem_disconnected);
}
static void security_changed(struct bt_conn *conn, bt_security_t level,
enum bt_security_err err)
{
if (err == 0) {
printk("Security changed: %u\n", err);
k_sem_give(&sem_security_updated);
} else {
printk("Failed to set security level: %u\n", err);
}
}
BT_CONN_CB_DEFINE(conn_callbacks) = {
.connected = connected,
.disconnected = disconnected,
.security_changed = security_changed
};
static bool check_audio_support_and_connect(struct bt_data *data, void *user_data)
{
bt_addr_le_t *addr = user_data;
struct net_buf_simple tmas_svc_data;
const struct bt_uuid *uuid;
uint16_t uuid_val;
uint16_t peer_tmap_role = 0;
int err;
printk("[AD]: %u data_len %u\n", data->type, data->data_len);
if (data->type != BT_DATA_SVC_DATA16) {
return true; /* Continue parsing to next AD data type */
}
if (data->data_len < sizeof(uuid_val)) {
printk("AD invalid size %u\n", data->data_len);
return true; /* Continue parsing to next AD data type */
}
net_buf_simple_init_with_data(&tmas_svc_data, (void *)data->data, data->data_len);
uuid_val = net_buf_simple_pull_le16(&tmas_svc_data);
uuid = BT_UUID_DECLARE_16(sys_le16_to_cpu(uuid_val));
if (bt_uuid_cmp(uuid, BT_UUID_TMAS) != 0) {
/* We are looking for the TMAS service data */
return true; /* Continue parsing to next AD data type */
}
printk("Found TMAS in peer adv data!\n");
if (tmas_svc_data.len < sizeof(peer_tmap_role)) {
printk("AD invalid size %u\n", data->data_len);
return false; /* Stop parsing */
}
peer_tmap_role = net_buf_simple_pull_le16(&tmas_svc_data);
if (!(sys_le16_to_cpu(peer_tmap_role) & BT_TMAP_ROLE_UMR)) {
printk("No TMAS UMR support!\n");
return false; /* Stop parsing */
}
printk("Attempt to connect!\n");
err = bt_le_scan_stop();
if (err != 0) {
printk("Failed to stop scan: %d\n", err);
return false;
}
err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN,
BT_LE_CONN_PARAM_DEFAULT,
&default_conn);
if (err != 0) {
printk("Create conn to failed (%u)\n", err);
start_scan();
}
return false; /* Stop parsing */
}
static void scan_recv(const struct bt_le_scan_recv_info *info,
struct net_buf_simple *buf)
{
char le_addr[BT_ADDR_LE_STR_LEN];
if (default_conn != NULL) {
/* Already connected */
return;
}
/* Check for connectable, extended advertising */
if (((info->adv_props & BT_GAP_ADV_PROP_EXT_ADV) != 0) ||
((info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE)) != 0) {
bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
printk("[DEVICE]: %s, ", le_addr);
/* Check for TMAS support in advertising data */
bt_data_parse(buf, check_audio_support_and_connect, (void *)info->addr);
}
}
static struct bt_le_scan_cb scan_callbacks = {
.recv = scan_recv,
};
static void start_scan(void)
{
int err;
err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL);
if (err != 0) {
printk("Scanning failed to start (err %d)\n", err);
return;
}
printk("Scanning successfully started\n");
}
static int scan_and_connect(void)
{
int err;
start_scan();
err = k_sem_take(&sem_connected, K_FOREVER);
if (err != 0) {
printk("failed to take sem_connected (err %d)\n", err);
return err;
}
err = k_sem_take(&sem_mtu_exchanged, K_FOREVER);
if (err != 0) {
printk("failed to take sem_mtu_exchanged (err %d)\n", err);
return err;
}
err = bt_conn_set_security(default_conn, BT_SECURITY_L2);
if (err != 0) {
printk("failed to set security (err %d)\n", err);
return err;
}
err = k_sem_take(&sem_security_updated, K_FOREVER);
if (err != 0) {
printk("failed to take sem_security_updated (err %d)\n", err);
return err;
}
return 0;
}
int main(void)
{
int err;
err = init();
if (err != 0) {
return err;
}
printk("Initializing TMAP and setting role\n");
/* Initialize TMAP */
err = bt_tmap_register(BT_TMAP_ROLE_CG | BT_TMAP_ROLE_UMS);
if (err != 0) {
return err;
}
/* Initialize CAP Initiator */
err = cap_initiator_init();
if (err != 0) {
return err;
}
printk("CAP initialized\n");
/* Initialize VCP Volume Controller */
err = vcp_vol_ctlr_init();
if (err != 0) {
return err;
}
printk("VCP initialized\n");
/* Initialize MCP Server */
err = mcp_server_init();
if (err != 0) {
return err;
}
printk("MCP initialized\n");
/* Initialize CCP Server */
err = ccp_server_init();
if (err != 0) {
return err;
}
printk("CCP initialized\n");
/* Register scan callback and start scanning */
bt_le_scan_cb_register(&scan_callbacks);
err = scan_and_connect();
if (err != 0) {
return err;
}
err = bt_tmap_discover(default_conn, &tmap_callbacks);
if (err != 0) {
return err;
}
k_sem_take(&sem_discovery_done, K_FOREVER);
/* Send a VCP command */
err = vcp_vol_ctlr_mute();
if (err != 0) {
printk("Error sending mute command!\n");
}
/* Discover and configure unicast streams */
err = cap_initiator_setup(default_conn);
if (err != 0) {
return err;
}
return 0;
}