Bluetooth: ISO: Add advanced broadcast ISO parameters
Add support for setting advanced broadcast ISO parameters using the ISO test commands. This allows the host to set ISO parameters that the controller normally would handle. Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
This commit is contained in:
parent
2187f6b29c
commit
0081ecd626
2 changed files with 219 additions and 22 deletions
|
@ -109,6 +109,16 @@ extern "C" {
|
||||||
#define BT_ISO_BIS_INDEX_MIN 0x01
|
#define BT_ISO_BIS_INDEX_MIN 0x01
|
||||||
/** Highest BIS index */
|
/** Highest BIS index */
|
||||||
#define BT_ISO_BIS_INDEX_MAX 0x1F
|
#define BT_ISO_BIS_INDEX_MAX 0x1F
|
||||||
|
/** Minimum Immediate Repetition Count */
|
||||||
|
#define BT_ISO_IRC_MIN 0x01U
|
||||||
|
/** Maximum Immediate Repetition Count */
|
||||||
|
#define BT_ISO_IRC_MAX 0x0FU
|
||||||
|
/** Minimum pre-transmission offset */
|
||||||
|
#define BT_ISO_PTO_MIN 0x00U
|
||||||
|
/** Maximum pre-transmission offset */
|
||||||
|
#define BT_ISO_PTO_MAX 0x0FU
|
||||||
|
|
||||||
|
|
||||||
/** Omit time stamp when sending to controller
|
/** Omit time stamp when sending to controller
|
||||||
*
|
*
|
||||||
* Using this value will enqueue the ISO SDU in a FIFO manner, instead of
|
* Using this value will enqueue the ISO SDU in a FIFO manner, instead of
|
||||||
|
@ -425,6 +435,8 @@ struct bt_iso_big_create_param {
|
||||||
/** @brief Channel Latency in ms.
|
/** @brief Channel Latency in ms.
|
||||||
*
|
*
|
||||||
* Value range BT_ISO_LATENCY_MIN - BT_ISO_LATENCY_MAX.
|
* Value range BT_ISO_LATENCY_MIN - BT_ISO_LATENCY_MAX.
|
||||||
|
*
|
||||||
|
* This value is ignored if any advanced ISO parameters are set.
|
||||||
*/
|
*/
|
||||||
uint16_t latency;
|
uint16_t latency;
|
||||||
|
|
||||||
|
@ -457,6 +469,34 @@ struct bt_iso_big_create_param {
|
||||||
* [42 72 6F 61 64 63 61 73 74 20 43 6F 64 65 00 00]
|
* [42 72 6F 61 64 63 61 73 74 20 43 6F 64 65 00 00]
|
||||||
*/
|
*/
|
||||||
uint8_t bcode[BT_ISO_BROADCAST_CODE_SIZE];
|
uint8_t bcode[BT_ISO_BROADCAST_CODE_SIZE];
|
||||||
|
|
||||||
|
#if defined(CONFIG_BT_ISO_ADVANCED)
|
||||||
|
/** @brief Immediate Repetition Count
|
||||||
|
*
|
||||||
|
* The number of times the scheduled payloads are transmitted in a
|
||||||
|
* given event.
|
||||||
|
*
|
||||||
|
* Value range from @ref BT_ISO_MIN_IRC to @ref BT_ISO_MAX_IRC.
|
||||||
|
*/
|
||||||
|
uint8_t irc;
|
||||||
|
|
||||||
|
/** @brief Pre-transmission offset
|
||||||
|
*
|
||||||
|
* Offset used for pre-transmissions.
|
||||||
|
*
|
||||||
|
* Value range from @ref BT_ISO_MIN_PTO to @ref BT_ISO_MAX_PTO.
|
||||||
|
*/
|
||||||
|
uint8_t pto;
|
||||||
|
|
||||||
|
/** @brief ISO interval
|
||||||
|
*
|
||||||
|
* Time between consecutive BIS anchor points.
|
||||||
|
*
|
||||||
|
* Value range from @ref BT_ISO_ISO_INTERVAL_MIN to
|
||||||
|
* @ref BT_ISO_ISO_INTERVAL_MAX.
|
||||||
|
*/
|
||||||
|
uint16_t iso_interval;
|
||||||
|
#endif /* CONFIG_BT_ISO_ADVANCED */
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @brief Broadcast Isochronous Group (BIG) Sync Parameters */
|
/** @brief Broadcast Isochronous Group (BIG) Sync Parameters */
|
||||||
|
|
|
@ -2569,25 +2569,113 @@ static int hci_le_create_big(struct bt_le_ext_adv *padv, struct bt_iso_big *big,
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
int bt_iso_big_create(struct bt_le_ext_adv *padv, struct bt_iso_big_create_param *param,
|
#if defined(CONFIG_BT_ISO_ADVANCED)
|
||||||
struct bt_iso_big **out_big)
|
static int hci_le_create_big_test(const struct bt_le_ext_adv *padv,
|
||||||
|
struct bt_iso_big *big,
|
||||||
|
const struct bt_iso_big_create_param *param)
|
||||||
{
|
{
|
||||||
|
struct bt_hci_cp_le_create_big_test *req;
|
||||||
|
struct bt_hci_cmd_state_set state;
|
||||||
|
const struct bt_iso_chan_qos *qos;
|
||||||
|
struct bt_iso_chan *bis;
|
||||||
|
struct net_buf *buf;
|
||||||
int err;
|
int err;
|
||||||
struct bt_iso_big *big;
|
|
||||||
|
|
||||||
if (!atomic_test_bit(padv->flags, BT_PER_ADV_PARAMS_SET)) {
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_CREATE_BIG_TEST, sizeof(*req));
|
||||||
LOG_DBG("PA params not set; invalid adv object");
|
|
||||||
return -EINVAL;
|
if (!buf) {
|
||||||
|
return -ENOBUFS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bis = SYS_SLIST_PEEK_HEAD_CONTAINER(&big->bis_channels, bis, node);
|
||||||
|
__ASSERT(bis != NULL, "bis was NULL");
|
||||||
|
|
||||||
|
/* All BIS will share the same QOS */
|
||||||
|
qos = bis->qos;
|
||||||
|
|
||||||
|
req = net_buf_add(buf, sizeof(*req));
|
||||||
|
req->big_handle = big->handle;
|
||||||
|
req->adv_handle = padv->handle;
|
||||||
|
req->num_bis = big->num_bis;
|
||||||
|
sys_put_le24(param->interval, req->sdu_interval);
|
||||||
|
req->iso_interval = sys_cpu_to_le16(param->iso_interval);
|
||||||
|
req->nse = qos->num_subevents;
|
||||||
|
req->max_sdu = sys_cpu_to_le16(qos->tx->sdu);
|
||||||
|
req->max_pdu = sys_cpu_to_le16(qos->tx->max_pdu);
|
||||||
|
req->phy = qos->tx->phy;
|
||||||
|
req->packing = param->packing;
|
||||||
|
req->framing = param->framing;
|
||||||
|
req->bn = qos->tx->burst_number;
|
||||||
|
req->irc = param->irc;
|
||||||
|
req->pto = param->pto;
|
||||||
|
req->encryption = param->encryption;
|
||||||
|
if (req->encryption) {
|
||||||
|
memcpy(req->bcode, param->bcode, sizeof(req->bcode));
|
||||||
|
} else {
|
||||||
|
memset(req->bcode, 0, sizeof(req->bcode));
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("BIG handle %u, adv handle %u, num_bis %u, SDU interval %u, "
|
||||||
|
"ISO interval %u, NSE %u, SDU %u, PDU %u, PHY %u, packing %u, "
|
||||||
|
"framing %u, BN %u, IRC %u, PTO %u, encryption %u",
|
||||||
|
req->big_handle, req->adv_handle, req->num_bis, param->interval,
|
||||||
|
param->iso_interval, req->nse, req->max_sdu, req->max_pdu,
|
||||||
|
req->phy, req->packing, req->framing, req->bn, req->irc,
|
||||||
|
req->pto, req->encryption);
|
||||||
|
|
||||||
|
bt_hci_cmd_state_set_init(buf, &state, big->flags, BT_BIG_PENDING, true);
|
||||||
|
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_CREATE_BIG_TEST, buf, NULL);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
SYS_SLIST_FOR_EACH_CONTAINER(&big->bis_channels, bis, node) {
|
||||||
|
bt_iso_chan_set_state(bis, BT_ISO_STATE_CONNECTING);
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_advanced_big_param(const struct bt_iso_big_create_param *param)
|
||||||
|
{
|
||||||
|
if (param->irc != 0U ||
|
||||||
|
param->iso_interval != 0U) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if any of the CIS contain any test-param-only values */
|
||||||
|
for (uint8_t i = 0U; i < param->num_bis; i++) {
|
||||||
|
const struct bt_iso_chan *bis = param->bis_channels[i];
|
||||||
|
const struct bt_iso_chan_qos *qos = bis->qos;
|
||||||
|
|
||||||
|
if (qos->num_subevents > 0U) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
__ASSERT(qos->tx != NULL, "TX cannot be NULL for broadcaster");
|
||||||
|
|
||||||
|
if (qos->tx->max_pdu > 0U || qos->tx->burst_number > 0U) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_BT_ISO_ADVANCED */
|
||||||
|
|
||||||
|
static bool valid_big_param(const struct bt_iso_big_create_param *param,
|
||||||
|
bool advanced)
|
||||||
|
{
|
||||||
CHECKIF(!param->bis_channels) {
|
CHECKIF(!param->bis_channels) {
|
||||||
LOG_DBG("NULL BIS channels");
|
LOG_DBG("NULL BIS channels");
|
||||||
return -EINVAL;
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECKIF(!param->num_bis) {
|
CHECKIF(!param->num_bis) {
|
||||||
LOG_DBG("Invalid number of BIS %u", param->num_bis);
|
LOG_DBG("Invalid number of BIS %u", param->num_bis);
|
||||||
return -EINVAL;
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint8_t i = 0; i < param->num_bis; i++) {
|
for (uint8_t i = 0; i < param->num_bis; i++) {
|
||||||
|
@ -2595,54 +2683,116 @@ int bt_iso_big_create(struct bt_le_ext_adv *padv, struct bt_iso_big_create_param
|
||||||
|
|
||||||
CHECKIF(bis == NULL) {
|
CHECKIF(bis == NULL) {
|
||||||
LOG_DBG("bis_channels[%u]: NULL channel", i);
|
LOG_DBG("bis_channels[%u]: NULL channel", i);
|
||||||
return -EINVAL;
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bis->iso) {
|
if (bis->iso) {
|
||||||
LOG_DBG("bis_channels[%u]: already allocated", i);
|
LOG_DBG("bis_channels[%u]: already allocated", i);
|
||||||
return -EALREADY;
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECKIF(bis->qos == NULL) {
|
CHECKIF(bis->qos == NULL) {
|
||||||
LOG_DBG("bis_channels[%u]: qos is NULL", i);
|
LOG_DBG("bis_channels[%u]: qos is NULL", i);
|
||||||
return -EINVAL;
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECKIF(bis->qos->tx == NULL ||
|
CHECKIF(bis->qos->tx == NULL ||
|
||||||
!valid_chan_io_qos(bis->qos->tx, true, true, false)) {
|
!valid_chan_io_qos(bis->qos->tx, true, true,
|
||||||
|
advanced)) {
|
||||||
LOG_DBG("bis_channels[%u]: Invalid QOS", i);
|
LOG_DBG("bis_channels[%u]: Invalid QOS", i);
|
||||||
return -EINVAL;
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECKIF(param->framing != BT_ISO_FRAMING_UNFRAMED &&
|
CHECKIF(param->framing != BT_ISO_FRAMING_UNFRAMED &&
|
||||||
param->framing != BT_ISO_FRAMING_FRAMED) {
|
param->framing != BT_ISO_FRAMING_FRAMED) {
|
||||||
LOG_DBG("Invalid framing parameter: %u", param->framing);
|
LOG_DBG("Invalid framing parameter: %u", param->framing);
|
||||||
return -EINVAL;
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECKIF(param->packing != BT_ISO_PACKING_SEQUENTIAL &&
|
CHECKIF(param->packing != BT_ISO_PACKING_SEQUENTIAL &&
|
||||||
param->packing != BT_ISO_PACKING_INTERLEAVED) {
|
param->packing != BT_ISO_PACKING_INTERLEAVED) {
|
||||||
LOG_DBG("Invalid packing parameter: %u", param->packing);
|
LOG_DBG("Invalid packing parameter: %u", param->packing);
|
||||||
return -EINVAL;
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECKIF(param->num_bis > BT_ISO_MAX_GROUP_ISO_COUNT ||
|
CHECKIF(param->num_bis > BT_ISO_MAX_GROUP_ISO_COUNT ||
|
||||||
param->num_bis > CONFIG_BT_ISO_MAX_CHAN) {
|
param->num_bis > CONFIG_BT_ISO_MAX_CHAN) {
|
||||||
LOG_DBG("num_bis (%u) shall be lower than: %u", param->num_bis,
|
LOG_DBG("num_bis (%u) shall be lower than: %u", param->num_bis,
|
||||||
MAX(CONFIG_BT_ISO_MAX_CHAN, BT_ISO_MAX_GROUP_ISO_COUNT));
|
MAX(CONFIG_BT_ISO_MAX_CHAN, BT_ISO_MAX_GROUP_ISO_COUNT));
|
||||||
return -EINVAL;
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECKIF(param->interval < BT_ISO_SDU_INTERVAL_MIN ||
|
CHECKIF(!IN_RANGE(param->interval,
|
||||||
param->interval > BT_ISO_SDU_INTERVAL_MAX) {
|
BT_ISO_SDU_INTERVAL_MIN,
|
||||||
|
BT_ISO_SDU_INTERVAL_MAX)) {
|
||||||
LOG_DBG("Invalid interval: %u", param->interval);
|
LOG_DBG("Invalid interval: %u", param->interval);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECKIF(!advanced &&
|
||||||
|
!IN_RANGE(param->latency,
|
||||||
|
BT_ISO_LATENCY_MIN,
|
||||||
|
BT_ISO_LATENCY_MAX)) {
|
||||||
|
LOG_DBG("Invalid latency: %u", param->latency);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_BT_ISO_ADVANCED)
|
||||||
|
if (advanced) {
|
||||||
|
CHECKIF(!IN_RANGE(param->irc, BT_ISO_IRC_MIN, BT_ISO_IRC_MAX)) {
|
||||||
|
LOG_DBG("Invalid IRC %u", param->irc);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECKIF(!IN_RANGE(param->pto, BT_ISO_PTO_MIN, BT_ISO_PTO_MAX)) {
|
||||||
|
LOG_DBG("Invalid PTO %u", param->pto);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECKIF(!IN_RANGE(param->iso_interval,
|
||||||
|
BT_ISO_ISO_INTERVAL_MIN,
|
||||||
|
BT_ISO_ISO_INTERVAL_MAX)) {
|
||||||
|
LOG_DBG("Invalid ISO interval %u", param->iso_interval);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_BT_ISO_ADVANCED */
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bt_iso_big_create(struct bt_le_ext_adv *padv, struct bt_iso_big_create_param *param,
|
||||||
|
struct bt_iso_big **out_big)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct bt_iso_big *big;
|
||||||
|
bool advanced = false;
|
||||||
|
|
||||||
|
if (!atomic_test_bit(padv->flags, BT_PER_ADV_PARAMS_SET)) {
|
||||||
|
LOG_DBG("PA params not set; invalid adv object");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECKIF(param->latency < BT_ISO_LATENCY_MIN ||
|
#if defined(CONFIG_BT_ISO_ADVANCED)
|
||||||
param->latency > BT_ISO_LATENCY_MAX) {
|
advanced = is_advanced_big_param(param);
|
||||||
LOG_DBG("Invalid latency: %u", param->latency);
|
#endif /* CONFIG_BT_ISO_ADVANCED */
|
||||||
|
|
||||||
|
if (!valid_big_param(param, advanced)) {
|
||||||
|
LOG_DBG("Invalid BIG parameters");
|
||||||
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2660,7 +2810,14 @@ int bt_iso_big_create(struct bt_le_ext_adv *padv, struct bt_iso_big_create_param
|
||||||
}
|
}
|
||||||
big->num_bis = param->num_bis;
|
big->num_bis = param->num_bis;
|
||||||
|
|
||||||
err = hci_le_create_big(padv, big, param);
|
if (!advanced) {
|
||||||
|
err = hci_le_create_big(padv, big, param);
|
||||||
|
#if defined(CONFIG_BT_ISO_ADVANCED)
|
||||||
|
} else {
|
||||||
|
err = hci_le_create_big_test(padv, big, param);
|
||||||
|
#endif /* CONFIG_BT_ISO_ADVANCED */
|
||||||
|
}
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
LOG_DBG("Could not create BIG %d", err);
|
LOG_DBG("Could not create BIG %d", err);
|
||||||
cleanup_big(big);
|
cleanup_big(big);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue