/** @file * @brief Bluetooth Audio shell * */ /* * Copyright (c) 2020 Intel Corporation * Copyright (c) 2021 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #include #include #include "host/shell/bt.h" #include "host/shell/bt_shell_private.h" #if defined(CONFIG_BT_ISO_TX) #define DEFAULT_IO_QOS \ { \ .sdu = 40u, .phy = BT_GAP_LE_PHY_2M, .rtn = 2u, \ } #define TX_BUF_TIMEOUT K_SECONDS(1) static struct bt_iso_chan_io_qos iso_tx_qos = DEFAULT_IO_QOS; static uint32_t cis_sn_last; static uint32_t bis_sn_last; static int64_t cis_sn_last_updated_ticks; static int64_t bis_sn_last_updated_ticks; /** * @brief Get the next sequence number based on the last used values * * @param last_sn The last sequence number sent. * @param last_ticks The uptime ticks since the last sequence number increment. * @param interval_us The SDU interval in microseconds. * * @return The next sequence number to use */ static uint32_t get_next_sn(uint32_t last_sn, int64_t *last_ticks, uint32_t interval_us) { int64_t uptime_ticks, delta_ticks; uint64_t delta_us; uint64_t sn_incr; uint64_t next_sn; /* Note: This does not handle wrapping of ticks when they go above * 2^(62-1) */ uptime_ticks = k_uptime_ticks(); delta_ticks = uptime_ticks - *last_ticks; *last_ticks = uptime_ticks; delta_us = k_ticks_to_us_near64((uint64_t)delta_ticks); sn_incr = delta_us / interval_us; next_sn = (sn_incr + last_sn); return (uint32_t)next_sn; } #endif /* CONFIG_BT_ISO_TX */ #if defined(CONFIG_BT_ISO_RX) static void iso_recv(struct bt_iso_chan *chan, const struct bt_iso_recv_info *info, struct net_buf *buf) { if (info->flags & BT_ISO_FLAGS_VALID) { bt_shell_print("Incoming data channel %p len %u, seq: %d, ts: %d", chan, buf->len, info->seq_num, info->ts); } } #endif /* CONFIG_BT_ISO_RX */ static void iso_connected(struct bt_iso_chan *chan) { struct bt_iso_info iso_info; int err; bt_shell_print("ISO Channel %p connected", chan); err = bt_iso_chan_get_info(chan, &iso_info); if (err != 0) { printk("Failed to get ISO info: %d", err); return; } #if defined(CONFIG_BT_ISO_TX) if (iso_info.type == BT_ISO_CHAN_TYPE_CONNECTED) { cis_sn_last = 0U; cis_sn_last_updated_ticks = k_uptime_ticks(); } else { bis_sn_last = 0U; bis_sn_last_updated_ticks = k_uptime_ticks(); } #endif /* CONFIG_BT_ISO_TX */ } static void iso_disconnected(struct bt_iso_chan *chan, uint8_t reason) { bt_shell_print("ISO Channel %p disconnected with reason 0x%02x", chan, reason); } static struct bt_iso_chan_ops iso_ops = { #if defined(CONFIG_BT_ISO_RX) .recv = iso_recv, #endif /* CONFIG_BT_ISO_RX */ .connected = iso_connected, .disconnected = iso_disconnected, }; #if defined(CONFIG_BT_ISO_UNICAST) static uint32_t cis_sdu_interval_us; static struct bt_iso_chan_io_qos iso_rx_qos = DEFAULT_IO_QOS; static struct bt_iso_chan_qos cis_iso_qos = { .tx = &iso_tx_qos, .rx = &iso_rx_qos, }; #define CIS_ISO_CHAN_COUNT 1 struct bt_iso_chan iso_chan = { .ops = &iso_ops, .qos = &cis_iso_qos, }; NET_BUF_POOL_FIXED_DEFINE(tx_pool, 1, BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU), CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL); #if defined(CONFIG_BT_ISO_CENTRAL) static struct bt_iso_cig *cig; static long parse_interval(const struct shell *sh, const char *interval_str) { unsigned long interval; int err; err = 0; interval = shell_strtoul(interval_str, 0, &err); if (err != 0) { shell_error(sh, "Could not parse interval: %d", err); return -ENOEXEC; } if (!IN_RANGE(interval, BT_ISO_SDU_INTERVAL_MIN, BT_ISO_SDU_INTERVAL_MAX)) { shell_error(sh, "Invalid interval %lu", interval); return -ENOEXEC; } return interval; } static long parse_latency(const struct shell *sh, const char *latency_str) { unsigned long latency; int err; err = 0; latency = shell_strtoul(latency_str, 0, &err); if (err != 0) { shell_error(sh, "Could not parse latency: %d", err); return -ENOEXEC; } if (!IN_RANGE(latency, BT_ISO_LATENCY_MIN, BT_ISO_LATENCY_MAX)) { shell_error(sh, "Invalid latency %lu", latency); return -ENOEXEC; } return latency; } static int cmd_cig_create(const struct shell *sh, size_t argc, char *argv[]) { int err; struct bt_iso_cig_param param = {0}; struct bt_iso_chan *chans[CIS_ISO_CHAN_COUNT]; if (cig != NULL) { shell_error(sh, "Already created"); return -ENOEXEC; } chans[0] = &iso_chan; if (argc > 1) { if (!strcmp("tx", argv[1])) { chans[0]->qos->tx = &iso_tx_qos; chans[0]->qos->rx = NULL; } else if (!strcmp("rx", argv[1])) { chans[0]->qos->tx = NULL; chans[0]->qos->rx = &iso_rx_qos; } else if (!strcmp("txrx", argv[1])) { chans[0]->qos->tx = &iso_tx_qos; chans[0]->qos->rx = &iso_rx_qos; } } err = 0; if (argc > 2) { long interval; interval = shell_strtoul(argv[2], 0, &err); interval = parse_interval(sh, argv[2]); if (interval < 0) { return interval; } param.c_to_p_interval = interval; } else { param.c_to_p_interval = 10000; } if (argc > 3) { long interval; interval = parse_interval(sh, argv[3]); if (interval < 0) { return interval; } param.p_to_c_interval = interval; } else { param.p_to_c_interval = param.c_to_p_interval; } /* cis_sdu_interval_us is used to increase the sequence number. * cig_create can be called before an ACL is created, so the role * information may not be available. * Since we are central however we can safely set the cis_sdu_interval * to the Central to Peer interval */ cis_sdu_interval_us = param.c_to_p_interval; if (argc > 4) { unsigned long packing; packing = shell_strtoul(argv[4], 0, &err); if (err != 0) { shell_error(sh, "Could not parse packing: %d", err); return -ENOEXEC; } if (packing != BT_ISO_PACKING_SEQUENTIAL && packing != BT_ISO_PACKING_INTERLEAVED) { shell_error(sh, "Invalid packing %lu", packing); return -ENOEXEC; } param.packing = packing; } else { param.packing = 0; } if (argc > 5) { unsigned long framing; framing = shell_strtoul(argv[5], 0, &err); if (err != 0) { shell_error(sh, "Could not parse framing: %d", err); return -ENOEXEC; } if (framing != BT_ISO_FRAMING_UNFRAMED && framing != BT_ISO_FRAMING_FRAMED) { shell_error(sh, "Invalid framing %lu", framing); return -ENOEXEC; } param.framing = framing; } else { param.framing = 0; } if (argc > 6) { long latency; latency = parse_latency(sh, argv[6]); if (latency < 0) { return latency; } param.c_to_p_latency = latency; } else { param.c_to_p_latency = 10; } if (argc > 7) { long latency; latency = parse_latency(sh, argv[7]); if (latency < 0) { return latency; } param.p_to_c_latency = latency; } else { param.p_to_c_latency = param.c_to_p_latency; } if (argc > 7) { unsigned long sdu; sdu = shell_strtoul(argv[7], 0, &err); if (err != 0) { shell_error(sh, "Could not parse sdu: %d", err); return -ENOEXEC; } if (sdu > BT_ISO_MAX_SDU) { shell_error(sh, "Invalid sdu %lu", sdu); return -ENOEXEC; } if (chans[0]->qos->tx) { chans[0]->qos->tx->sdu = sdu; } if (chans[0]->qos->rx) { chans[0]->qos->rx->sdu = sdu; } } if (argc > 8) { unsigned long phy; phy = shell_strtoul(argv[8], 0, &err); if (err != 0) { shell_error(sh, "Could not parse phy: %d", err); return -ENOEXEC; } if (phy != BT_GAP_LE_PHY_1M && phy != BT_GAP_LE_PHY_2M && phy != BT_GAP_LE_PHY_CODED) { shell_error(sh, "Invalid phy %lu", phy); return -ENOEXEC; } if (chans[0]->qos->tx) { chans[0]->qos->tx->phy = phy; } if (chans[0]->qos->rx) { chans[0]->qos->rx->phy = phy; } } if (argc > 9) { unsigned long rtn; rtn = shell_strtoul(argv[9], 0, &err); if (err != 0) { shell_error(sh, "Could not parse rtn: %d", err); return -ENOEXEC; } if (rtn > BT_ISO_CONNECTED_RTN_MAX) { shell_error(sh, "Invalid rtn %lu", rtn); return -ENOEXEC; } if (chans[0]->qos->tx) { chans[0]->qos->tx->rtn = rtn; } if (chans[0]->qos->rx) { chans[0]->qos->rx->rtn = rtn; } } param.sca = BT_GAP_SCA_UNKNOWN; param.cis_channels = chans; param.num_cis = ARRAY_SIZE(chans); err = bt_iso_cig_create(¶m, &cig); if (err) { shell_error(sh, "Unable to create CIG (err %d)", err); return 0; } shell_print(sh, "CIG created"); return 0; } static int cmd_cig_term(const struct shell *sh, size_t argc, char *argv[]) { int err; if (cig == NULL) { shell_error(sh, "CIG not created"); return -ENOEXEC; } err = bt_iso_cig_terminate(cig); if (err) { shell_error(sh, "Unable to terminate CIG (err %d)", err); return 0; } shell_print(sh, "CIG terminated"); cig = NULL; return 0; } static int cmd_connect(const struct shell *sh, size_t argc, char *argv[]) { struct bt_iso_connect_param connect_param = { .acl = default_conn, .iso_chan = &iso_chan }; int err; if (iso_chan.iso == NULL) { shell_error(sh, "ISO channel not initialized in a CIG"); return 0; } #if defined(CONFIG_BT_SMP) if (argc > 1) { iso_chan.required_sec_level = *argv[1] - '0'; } #endif /* CONFIG_BT_SMP */ err = bt_iso_chan_connect(&connect_param, 1); if (err) { shell_error(sh, "Unable to connect (err %d)", err); return 0; } shell_print(sh, "ISO Connect pending..."); return 0; } #endif /* CONFIG_BT_ISO_CENTRAL */ #if defined(CONFIG_BT_ISO_PERIPHERAL) static int iso_accept(const struct bt_iso_accept_info *info, struct bt_iso_chan **chan) { bt_shell_print("Incoming request from %p with CIG ID 0x%02X and CIS ID 0x%02X", info->acl, info->cig_id, info->cis_id); if (iso_chan.iso) { bt_shell_print("No channels available"); return -ENOMEM; } *chan = &iso_chan; /* As the peripheral host we do not know the SDU interval, and thus we * cannot find the proper interval of incrementing the packet * sequence number (PSN). The only way to ensure that we correctly * increment the PSN, is by incrementing once per the minimum SDU * interval. This should be okay as the spec does not specify how much * the PSN may be incremented, and it is thus OK for us to increment * it faster than the SDU interval. */ cis_sdu_interval_us = BT_ISO_SDU_INTERVAL_MIN; return 0; } struct bt_iso_server iso_server = { #if defined(CONFIG_BT_SMP) .sec_level = BT_SECURITY_L1, #endif /* CONFIG_BT_SMP */ .accept = iso_accept, }; static int cmd_listen(const struct shell *sh, size_t argc, char *argv[]) { int err; static struct bt_iso_chan_io_qos *tx_qos, *rx_qos; if (!strcmp("tx", argv[1])) { tx_qos = &iso_tx_qos; rx_qos = NULL; } else if (!strcmp("rx", argv[1])) { tx_qos = NULL; rx_qos = &iso_rx_qos; } else if (!strcmp("txrx", argv[1])) { tx_qos = &iso_tx_qos; rx_qos = &iso_rx_qos; } else { shell_error(sh, "Invalid argument - use tx, rx or txrx"); return -ENOEXEC; } #if defined(CONFIG_BT_SMP) if (argc > 2) { iso_server.sec_level = *argv[2] - '0'; } #endif /* CONFIG_BT_SMP */ err = bt_iso_server_register(&iso_server); if (err) { shell_error(sh, "Unable to register ISO cap (err %d)", err); return err; } /* Setup peripheral iso data direction only if register is success */ iso_chan.qos->tx = tx_qos; iso_chan.qos->rx = rx_qos; return err; } #endif /* CONFIG_BT_ISO_PERIPHERAL */ static int cmd_send(const struct shell *sh, size_t argc, char *argv[]) { static uint8_t buf_data[CONFIG_BT_ISO_TX_MTU] = { [0 ... (CONFIG_BT_ISO_TX_MTU - 1)] = 0xff }; unsigned long count = 1; struct net_buf *buf; int ret = 0; int len; if (argc > 1) { count = shell_strtoul(argv[1], 0, &ret); if (ret != 0) { shell_error(sh, "Could not parse count: %d", ret); return -ENOEXEC; } } if (!iso_chan.iso) { shell_error(sh, "Not bound"); return 0; } if (!iso_chan.qos->tx) { shell_error(sh, "Transmission QoS disabled"); return -ENOEXEC; } len = MIN(iso_chan.qos->tx->sdu, CONFIG_BT_ISO_TX_MTU); cis_sn_last = get_next_sn(cis_sn_last, &cis_sn_last_updated_ticks, cis_sdu_interval_us); while (count--) { buf = net_buf_alloc(&tx_pool, TX_BUF_TIMEOUT); if (buf == NULL) { shell_error(sh, "Failed to get buffer..."); return -ENOEXEC; } net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE); net_buf_add_mem(buf, buf_data, len); shell_info(sh, "send: %d bytes of data with PSN %u", len, cis_sn_last); ret = bt_iso_chan_send(&iso_chan, buf, cis_sn_last); if (ret < 0) { shell_print(sh, "Unable to send: %d", -ret); net_buf_unref(buf); return -ENOEXEC; } } shell_print(sh, "ISO sending..."); return 0; } static int cmd_disconnect(const struct shell *sh, size_t argc, char *argv[]) { int err; err = bt_iso_chan_disconnect(&iso_chan); if (err) { shell_error(sh, "Unable to disconnect (err %d)", err); return 0; } shell_print(sh, "ISO Disconnect pending..."); return 0; } static int cmd_tx_sync_read_cis(const struct shell *sh, size_t argc, char *argv[]) { struct bt_iso_tx_info tx_info; int err; if (!iso_chan.iso) { shell_error(sh, "Not bound"); return 0; } err = bt_iso_chan_get_tx_sync(&iso_chan, &tx_info); if (err) { shell_error(sh, "Unable to read sync info (err %d)", err); return 0; } shell_print(sh, "TX sync info:\n\tTimestamp=%u\n\tOffset=%u\n\tSequence number=%u", tx_info.ts, tx_info.offset, tx_info.seq_num); return 0; } #endif /* CONFIG_BT_ISO_UNICAST */ #if defined(CONFIG_BT_ISO_BROADCAST) #define BIS_ISO_CHAN_COUNT 1 static struct bt_iso_big *big; 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 }; #if defined(CONFIG_BT_ISO_BROADCASTER) static uint32_t bis_sdu_interval_us; NET_BUF_POOL_FIXED_DEFINE(bis_tx_pool, BIS_ISO_CHAN_COUNT, BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU), CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL); static int cmd_broadcast(const struct shell *sh, size_t argc, char *argv[]) { static uint8_t buf_data[CONFIG_BT_ISO_TX_MTU] = { [0 ... (CONFIG_BT_ISO_TX_MTU - 1)] = 0xff }; unsigned long count = 1; struct net_buf *buf; int ret = 0; int len; if (argc > 1) { count = shell_strtoul(argv[1], 0, &ret); if (ret != 0) { shell_error(sh, "Could not parse count: %d", ret); return -ENOEXEC; } } if (!bis_iso_chan.iso) { shell_error(sh, "BIG not created"); return -ENOEXEC; } if (!bis_iso_qos.tx) { shell_error(sh, "BIG not setup as broadcaster"); return -ENOEXEC; } len = MIN(bis_iso_chan.qos->tx->sdu, CONFIG_BT_ISO_TX_MTU); bis_sn_last = get_next_sn(bis_sn_last, &bis_sn_last_updated_ticks, bis_sdu_interval_us); while (count--) { buf = net_buf_alloc(&bis_tx_pool, TX_BUF_TIMEOUT); if (buf == NULL) { shell_error(sh, "Failed to get buffer..."); return -ENOEXEC; } net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE); net_buf_add_mem(buf, buf_data, len); shell_info(sh, "send: %d bytes of data with PSN %u", len, bis_sn_last); ret = bt_iso_chan_send(&bis_iso_chan, buf, bis_sn_last); if (ret < 0) { shell_print(sh, "Unable to broadcast: %d", -ret); net_buf_unref(buf); return -ENOEXEC; } } shell_print(sh, "ISO broadcasting..."); return 0; } static int cmd_big_create(const struct shell *sh, size_t argc, char *argv[]) { int err; struct bt_iso_big_create_param param = {0}; struct bt_le_ext_adv *adv = adv_sets[selected_adv]; if (!adv) { shell_error(sh, "No (periodic) advertising set selected"); return -ENOEXEC; } /* TODO: Allow setting QOS from shell */ bis_iso_qos.tx = &iso_tx_qos; bis_iso_qos.tx->phy = BT_GAP_LE_PHY_2M; /* 2 MBit */ bis_iso_qos.tx->rtn = 2; bis_iso_qos.tx->sdu = CONFIG_BT_ISO_TX_MTU; bis_sdu_interval_us = param.interval = 10000; /* us */ param.latency = 20; /* ms */ param.bis_channels = bis_channels; param.num_bis = BIS_ISO_CHAN_COUNT; param.encryption = false; param.packing = BT_ISO_PACKING_SEQUENTIAL; param.framing = BT_ISO_FRAMING_UNFRAMED; 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(sh, "Invalid Broadcast Code Length"); return -ENOEXEC; } param.encryption = true; } else { shell_help(sh); return SHELL_CMD_HELP_PRINTED; } } else { memset(param.bcode, 0, sizeof(param.bcode)); } err = bt_iso_big_create(adv, ¶m, &big); if (err) { shell_error(sh, "Unable to create BIG (err %d)", err); return 0; } shell_print(sh, "BIG created"); return 0; } static int cmd_tx_sync_read_bis(const struct shell *sh, size_t argc, char *argv[]) { struct bt_iso_tx_info tx_info; int err; if (!bis_iso_chan.iso) { shell_error(sh, "BIG not created"); return -ENOEXEC; } err = bt_iso_chan_get_tx_sync(&bis_iso_chan, &tx_info); if (err) { shell_error(sh, "Unable to read sync info (err %d)", err); return 0; } shell_print(sh, "TX sync info:\n\tTimestamp=%u\n\tOffset=%u\n\tSequence number=%u", tx_info.ts, tx_info.offset, tx_info.seq_num); return 0; } #endif /* CONFIG_BT_ISO_BROADCASTER */ #if defined(CONFIG_BT_ISO_SYNC_RECEIVER) static int cmd_big_sync(const struct shell *sh, 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; unsigned long bis_bitfield; if (!pa_sync) { shell_error(sh, "No PA sync selected"); return -ENOEXEC; } err = 0; bis_bitfield = shell_strtoul(argv[1], 0, &err); if (err != 0) { shell_error(sh, "Could not parse bis_bitfield: %d", err); return -ENOEXEC; } if (bis_bitfield == 0U || bis_bitfield > BIT_MASK(BT_ISO_BIS_INDEX_MAX)) { shell_error(sh, "Invalid bis_bitfield: %lu", bis_bitfield); return -ENOEXEC; } bis_iso_qos.tx = NULL; bis_iso_qos.rx = &iso_rx_qos; param.bis_channels = bis_channels; param.num_bis = BIS_ISO_CHAN_COUNT; param.encryption = false; param.bis_bitfield = bis_bitfield; param.mse = 0; param.sync_timeout = 0xFF; for (size_t i = 2U; i < argc; i++) { if (!strcmp(argv[i], "mse")) { unsigned long mse; i++; if (i == argc) { shell_help(sh); return SHELL_CMD_HELP_PRINTED; } mse = shell_strtoul(argv[i], 0, &err); if (err != 0) { shell_error(sh, "Could not parse mse: %d", err); return -ENOEXEC; } if (mse != BT_ISO_SYNC_MSE_ANY && !IN_RANGE(mse, BT_ISO_SYNC_MSE_MIN, BT_ISO_SYNC_MSE_MAX)) { shell_error(sh, "Invalid mse %lu", mse); return -ENOEXEC; } param.mse = mse; } else if (!strcmp(argv[i], "timeout")) { unsigned long sync_timeout; i++; if (i == argc) { shell_help(sh); return SHELL_CMD_HELP_PRINTED; } sync_timeout = shell_strtoul(argv[i], 0, &err); if (err != 0) { shell_error(sh, "Could not parse sync_timeout: %d", err); return -ENOEXEC; } if (!IN_RANGE(sync_timeout, BT_ISO_SYNC_TIMEOUT_MIN, BT_ISO_SYNC_TIMEOUT_MAX)) { shell_error(sh, "Invalid sync_timeout %lu", sync_timeout); return -ENOEXEC; } param.sync_timeout = sync_timeout; } else if (!strcmp(argv[i], "enc")) { size_t bcode_len; i++; if (i == argc) { shell_help(sh); return SHELL_CMD_HELP_PRINTED; } memset(param.bcode, 0, sizeof(param.bcode)); bcode_len = hex2bin(argv[i], strlen(argv[i]), param.bcode, sizeof(param.bcode)); if (bcode_len == 0) { shell_error(sh, "Invalid Broadcast Code"); return -ENOEXEC; } param.encryption = true; } else { shell_help(sh); return SHELL_CMD_HELP_PRINTED; } } err = bt_iso_big_sync(pa_sync, ¶m, &big); if (err) { shell_error(sh, "Unable to sync to BIG (err %d)", err); return 0; } shell_print(sh, "BIG syncing"); return 0; } #endif /* CONFIG_BT_ISO_SYNC_RECEIVER */ static int cmd_big_term(const struct shell *sh, size_t argc, char *argv[]) { int err; err = bt_iso_big_terminate(big); if (err) { shell_error(sh, "Unable to terminate BIG (err %d)", err); return 0; } shell_print(sh, "BIG terminated"); return 0; } #endif /* CONFIG_BT_ISO_BROADCAST*/ SHELL_STATIC_SUBCMD_SET_CREATE(iso_cmds, #if defined(CONFIG_BT_ISO_UNICAST) #if defined(CONFIG_BT_ISO_CENTRAL) SHELL_CMD_ARG(cig_create, NULL, "[dir=tx,rx,txrx] [C to P interval] [P to C interval] " "[packing] [framing] [C to P latency] [P to C latency] [sdu] [phy] [rtn]", cmd_cig_create, 1, 10), SHELL_CMD_ARG(cig_term, NULL, "Terminate the CIG", cmd_cig_term, 1, 0), #if defined(CONFIG_BT_SMP) SHELL_CMD_ARG(connect, NULL, "Connect ISO Channel [security level]", cmd_connect, 1, 1), #else /* !CONFIG_BT_SMP */ SHELL_CMD_ARG(connect, NULL, "Connect ISO Channel", cmd_connect, 1, 0), #endif /* CONFIG_BT_SMP */ #endif /* CONFIG_BT_ISO_CENTRAL */ #if defined(CONFIG_BT_ISO_PERIPHERAL) #if defined(CONFIG_BT_SMP) SHELL_CMD_ARG(listen, NULL, " [security level]", cmd_listen, 2, 1), #else /* !CONFIG_BT_SMP */ SHELL_CMD_ARG(listen, NULL, "", cmd_listen, 2, 0), #endif /* CONFIG_BT_SMP */ #endif /* CONFIG_BT_ISO_PERIPHERAL */ #if defined(CONFIG_BT_ISO_TX) SHELL_CMD_ARG(send, NULL, "Send to ISO Channel [count]", cmd_send, 1, 1), #endif /* CONFIG_BT_ISO_TX */ SHELL_CMD_ARG(disconnect, NULL, "Disconnect ISO Channel", cmd_disconnect, 1, 0), SHELL_CMD_ARG(tx_sync_read_cis, NULL, "Read CIS TX sync info", cmd_tx_sync_read_cis, 1, 0), #endif /* CONFIG_BT_ISO_UNICAST */ #if defined(CONFIG_BT_ISO_BROADCASTER) SHELL_CMD_ARG(create-big, NULL, "Create a BIG as a broadcaster [enc ]", cmd_big_create, 1, 2), SHELL_CMD_ARG(broadcast, NULL, "Broadcast on ISO channels", cmd_broadcast, 1, 1), SHELL_CMD_ARG(tx_sync_read_bis, NULL, "Read BIS TX sync info", cmd_tx_sync_read_bis, 1, 0), #endif /* CONFIG_BT_ISO_BROADCASTER */ #if defined(CONFIG_BT_ISO_SYNC_RECEIVER) SHELL_CMD_ARG(sync-big, NULL, "Synchronize to a BIG as a receiver [mse] " "[timeout] [enc ]", cmd_big_sync, 2, 4), #endif /* CONFIG_BT_ISO_SYNC_RECEIVER */ #if defined(CONFIG_BT_ISO_BROADCAST) SHELL_CMD_ARG(term-big, NULL, "Terminate a BIG", cmd_big_term, 1, 0), #endif /* CONFIG_BT_ISO_BROADCAST */ SHELL_SUBCMD_SET_END ); static int cmd_iso(const struct shell *sh, size_t argc, char **argv) { if (argc == 1) { shell_help(sh); return SHELL_CMD_HELP_PRINTED; } shell_error(sh, "%s unknown parameter: %s", argv[0], argv[1]); return -EINVAL; } SHELL_CMD_ARG_REGISTER(iso, &iso_cmds, "Bluetooth ISO shell commands", cmd_iso, 1, 1);