zephyr/subsys/bluetooth/shell/iso.c
Emil Gydesen d48b040a78 Bluetooth: Host: Updated ISO SCA description
The SCA (sca) field of bt_iso_chan_qos did not give much
information about the expected values or what they meant,
nor any information about what the value perhaps should be.

Updated the description and the ISO shell.

Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
2021-03-11 06:38:08 -05:00

453 lines
10 KiB
C

/** @file
* @brief Bluetooth Audio shell
*
*/
/*
* Copyright (c) 2020 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <ctype.h>
#include <zephyr.h>
#include <shell/shell.h>
#include <sys/printk.h>
#include <sys/byteorder.h>
#include <sys/util.h>
#include <bluetooth/hci.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/conn.h>
#include <bluetooth/iso.h>
#include "bt.h"
static void iso_recv(struct bt_iso_chan *chan, struct net_buf *buf)
{
printk("Incoming data channel %p len %u\n", chan, buf->len);
}
static void iso_connected(struct bt_iso_chan *chan)
{
printk("ISO Channel %p connected\n", chan);
}
static void iso_disconnected(struct bt_iso_chan *chan)
{
printk("ISO Channel %p disconnected\n", chan);
}
static struct bt_iso_chan_ops iso_ops = {
.recv = iso_recv,
.connected = iso_connected,
.disconnected = iso_disconnected,
};
static struct bt_iso_chan_qos iso_qos = {
.sca = BT_GAP_SCA_UNKNOWN,
};
struct bt_iso_chan iso_chan = {
.ops = &iso_ops,
.qos = &iso_qos,
};
static int iso_accept(struct bt_conn *conn, struct bt_iso_chan **chan)
{
printk("Incoming conn %p\n", conn);
if (iso_chan.conn) {
printk("No channels available\n");
return -ENOMEM;
}
*chan = &iso_chan;
return 0;
}
struct bt_iso_server iso_server = {
.sec_level = BT_SECURITY_L1,
.accept = iso_accept,
};
static int cmd_listen(const struct shell *shell, size_t argc, char *argv[])
{
int err;
if (argc > 1) {
iso_server.sec_level = *argv[1] - '0';
}
err = bt_iso_server_register(&iso_server);
if (err) {
shell_error(shell, "Unable to register ISO cap (err %d)",
err);
}
return err;
}
static int cmd_bind(const struct shell *shell, size_t argc, char *argv[])
{
struct bt_conn *conns[1];
struct bt_iso_chan *chans[1];
int err;
if (!default_conn) {
shell_error(shell, "Not connected");
return 0;
}
if (iso_chan.conn) {
shell_error(shell, "Already bound");
return 0;
}
conns[0] = default_conn;
chans[0] = &iso_chan;
if (argc > 1) {
chans[0]->qos->dir = strtol(argv[1], NULL, 0);
}
if (argc > 2) {
chans[0]->qos->interval = strtol(argv[2], NULL, 0);
}
if (argc > 3) {
chans[0]->qos->packing = strtol(argv[3], NULL, 0);
}
if (argc > 4) {
chans[0]->qos->framing = strtol(argv[4], NULL, 0);
}
if (argc > 5) {
chans[0]->qos->latency = strtol(argv[5], NULL, 0);
}
if (argc > 6) {
chans[0]->qos->sdu = strtol(argv[6], NULL, 0);
}
if (argc > 7) {
chans[0]->qos->phy = strtol(argv[7], NULL, 0);
}
if (argc > 8) {
chans[0]->qos->rtn = strtol(argv[8], NULL, 0);
}
err = bt_iso_chan_bind(conns, 1, chans);
if (err) {
shell_error(shell, "Unable to bind (err %d)", err);
return 0;
}
shell_print(shell, "ISO Channel bound");
return 0;
}
static int cmd_connect(const struct shell *shell, size_t argc, char *argv[])
{
struct bt_iso_chan *chans[1];
int err;
if (!iso_chan.conn) {
shell_error(shell, "Not bound");
return 0;
}
chans[0] = &iso_chan;
err = bt_iso_chan_connect(chans, 1);
if (err) {
shell_error(shell, "Unable to connect (err %d)", err);
return 0;
}
shell_print(shell, "ISO Connect pending...");
return 0;
}
#define DATA_MTU CONFIG_BT_ISO_TX_MTU
NET_BUF_POOL_FIXED_DEFINE(tx_pool, 1, DATA_MTU, NULL);
static int cmd_send(const struct shell *shell, size_t argc, char *argv[])
{
static uint8_t buf_data[DATA_MTU] = { [0 ... (DATA_MTU - 1)] = 0xff };
int ret, len, count = 1;
struct net_buf *buf;
if (argc > 1) {
count = strtoul(argv[1], NULL, 10);
}
if (!iso_chan.conn) {
shell_error(shell, "Not bound");
return 0;
}
len = MIN(iso_chan.qos->sdu, DATA_MTU - BT_ISO_CHAN_SEND_RESERVE);
while (count--) {
buf = net_buf_alloc(&tx_pool, K_FOREVER);
net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE);
net_buf_add_mem(buf, buf_data, len);
ret = bt_iso_chan_send(&iso_chan, buf);
if (ret < 0) {
shell_print(shell, "Unable to send: %d", -ret);
net_buf_unref(buf);
return -ENOEXEC;
}
}
shell_print(shell, "ISO sending...");
return 0;
}
static int cmd_disconnect(const struct shell *shell, size_t argc,
char *argv[])
{
int err;
err = bt_iso_chan_disconnect(&iso_chan);
if (err) {
shell_error(shell, "Unable to disconnect (err %d)", err);
return 0;
}
shell_print(shell, "ISO Disconnect pending...");
return 0;
}
#if defined(CONFIG_BT_ISO_BROADCAST)
#define BIS_ISO_CHAN_COUNT 1
static struct bt_iso_chan_qos bis_iso_qos;
static struct bt_iso_chan bis_iso_chan = {
.ops = &iso_ops,
.qos = &bis_iso_qos,
};
static struct bt_iso_chan *bis_channels[BIS_ISO_CHAN_COUNT] = { &bis_iso_chan };
static struct bt_iso_big *big;
NET_BUF_POOL_FIXED_DEFINE(bis_tx_pool, BIS_ISO_CHAN_COUNT, DATA_MTU, NULL);
static int cmd_broadcast(const struct shell *shell, size_t argc, char *argv[])
{
static uint8_t buf_data[DATA_MTU] = { [0 ... (DATA_MTU - 1)] = 0xff };
int ret, len, count = 1;
struct net_buf *buf;
if (argc > 1) {
count = strtoul(argv[1], NULL, 10);
}
if (!bis_iso_chan.conn) {
shell_error(shell, "BIG not created");
return -ENOEXEC;
}
if (bis_iso_qos.dir != BT_ISO_CHAN_QOS_IN) {
shell_error(shell, "BIG not setup as broadcaster");
return -ENOEXEC;
}
len = MIN(iso_chan.qos->sdu, DATA_MTU - BT_ISO_CHAN_SEND_RESERVE);
while (count--) {
for (int i = 0; i < BIS_ISO_CHAN_COUNT; i++) {
buf = net_buf_alloc(&tx_pool, K_FOREVER);
net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE);
net_buf_add_mem(buf, buf_data, len);
ret = bt_iso_chan_send(&bis_iso_chan, buf);
if (ret < 0) {
shell_print(shell, "[%i]: Unable to broadcast: %d", i, -ret);
net_buf_unref(buf);
return -ENOEXEC;
}
}
}
shell_print(shell, "ISO broadcasting...");
return 0;
}
static int cmd_big_create(const struct shell *shell, size_t argc, char *argv[])
{
int err;
struct bt_iso_big_create_param param;
struct bt_le_ext_adv *adv = adv_sets[selected_adv];
uint8_t original_dir = bis_iso_qos.dir;
if (!adv) {
shell_error(shell, "No (periodic) advertising set selected");
return -ENOEXEC;
}
bis_iso_qos.dir = BT_ISO_CHAN_QOS_IN;
/* TODO: Allow setting QOS from shell */
bis_iso_qos.interval = 10000; /* us */
bis_iso_qos.latency = 20; /* ms */
bis_iso_qos.phy = BT_GAP_LE_PHY_2M; /* 2 MBit */
bis_iso_qos.rtn = 2;
bis_iso_qos.sdu = CONFIG_BT_ISO_TX_MTU;
param.bis_channels = bis_channels;
param.num_bis = BIS_ISO_CHAN_COUNT;
param.encryption = false;
if (argc > 1) {
if (!strcmp(argv[1], "enc")) {
uint8_t bcode_len = hex2bin(argv[1], strlen(argv[1]), param.bcode,
sizeof(param.bcode));
if (!bcode_len || bcode_len != sizeof(param.bcode)) {
shell_error(shell, "Invalid Broadcast Code Length");
return -ENOEXEC;
}
param.encryption = true;
} else {
shell_help(shell);
return SHELL_CMD_HELP_PRINTED;
}
}
err = bt_iso_big_create(adv, &param, &big);
if (err) {
bis_iso_qos.dir = original_dir;
shell_error(shell, "Unable to create BIG (err %d)", err);
return 0;
}
shell_print(shell, "BIG created");
return 0;
}
static int cmd_big_sync(const struct shell *shell, size_t argc, char *argv[])
{
int err;
/* TODO: Add support to select which PA sync to BIG sync to */
struct bt_le_per_adv_sync *pa_sync = per_adv_syncs[0];
struct bt_iso_big_sync_param param;
uint8_t original_dir = bis_iso_qos.dir;
if (!pa_sync) {
shell_error(shell, "No PA sync selected");
return -ENOEXEC;
}
bis_iso_qos.dir = BT_ISO_CHAN_QOS_OUT;
param.bis_channels = bis_channels;
param.num_bis = BIS_ISO_CHAN_COUNT;
param.encryption = false;
param.bis_bitfield = strtoul(argv[1], NULL, 16);
param.mse = 0;
param.sync_timeout = 0xFF;
for (int i = 2; i < argc; i++) {
if (!strcmp(argv[i], "mse")) {
param.mse = strtoul(argv[i], NULL, 16);
} else if (!strcmp(argv[i], "timeout")) {
param.sync_timeout = strtoul(argv[i], NULL, 16);
} else if (!strcmp(argv[i], "enc")) {
uint8_t bcode_len;
i++;
if (i == argc) {
shell_help(shell);
return SHELL_CMD_HELP_PRINTED;
}
bcode_len = hex2bin(argv[i], strlen(argv[i]), param.bcode,
sizeof(param.bcode));
if (!bcode_len || bcode_len != sizeof(param.bcode)) {
shell_error(shell, "Invalid Broadcast Code Length");
return -ENOEXEC;
}
param.encryption = true;
} else {
shell_help(shell);
return SHELL_CMD_HELP_PRINTED;
}
}
err = bt_iso_big_sync(pa_sync, &param, &big);
if (err) {
bis_iso_qos.dir = original_dir;
shell_error(shell, "Unable to sync to BIG (err %d)", err);
return 0;
}
shell_print(shell, "BIG syncing");
return 0;
}
static int cmd_big_term(const struct shell *shell, size_t argc, char *argv[])
{
int err;
err = bt_iso_big_terminate(big);
if (err) {
shell_error(shell, "Unable to terminate BIG", err);
return 0;
}
shell_print(shell, "BIG terminated");
return 0;
}
#endif /* CONFIG_BT_ISO_BROADCAST */
SHELL_STATIC_SUBCMD_SET_CREATE(iso_cmds,
SHELL_CMD_ARG(bind, NULL, "[dir] [interval] [packing] [framing] "
"[latency] [sdu] [phy] [rtn]", cmd_bind, 1, 8),
SHELL_CMD_ARG(connect, NULL, "Connect ISO Channel", cmd_connect, 1, 0),
SHELL_CMD_ARG(listen, NULL, "[security level]", cmd_listen, 1, 1),
SHELL_CMD_ARG(send, NULL, "Send to ISO Channel [count]",
cmd_send, 1, 1),
SHELL_CMD_ARG(disconnect, NULL, "Disconnect ISO Channel",
cmd_disconnect, 1, 0),
#if defined(CONFIG_BT_ISO_BROADCAST)
SHELL_CMD_ARG(create-big, NULL, "Create a BIG as a broadcaster [enc <broadcast code>]",
cmd_big_create, 1, 2),
SHELL_CMD_ARG(sync-big, NULL, "Synchronize to a BIG as a receiver <BIS bitfield> [mse] "
"[timeout] [enc <broadcast code>]", cmd_big_sync, 2, 4),
SHELL_CMD_ARG(term-big, NULL, "Terminate a BIG", cmd_big_term, 1, 0),
SHELL_CMD_ARG(broadcast, NULL, "Broadcast on ISO channels", cmd_broadcast, 1, 1),
#endif /* CONFIG_BT_ISO_BROADCAST */
SHELL_SUBCMD_SET_END
);
static int cmd_iso(const struct shell *shell, size_t argc, char **argv)
{
if (argc > 1) {
shell_error(shell, "%s unknown parameter: %s",
argv[0], argv[1]);
} else {
shell_error(shell, "%s Missing subcommand", argv[0]);
}
return -ENOEXEC;
}
SHELL_CMD_ARG_REGISTER(iso, &iso_cmds, "Bluetooth ISO shell commands",
cmd_iso, 1, 1);