Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions include/bluetooth/bluetooth.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ struct bt_le_per_adv_sync;
/* Don't require everyone to include conn.h */
struct bt_conn;

/* Don't require everyone to include iso.h */
struct bt_iso_biginfo;

struct bt_le_ext_adv_sent_info {
/** The number of advertising events completed. */
uint8_t num_sent;
Expand Down Expand Up @@ -1191,6 +1194,17 @@ struct bt_le_per_adv_sync_cb {
void (*state_changed)(struct bt_le_per_adv_sync *sync,
const struct bt_le_per_adv_sync_state_info *info);

/**
* @brief BIGInfo advertising report received.
*
* This callback notifies the application of a BIGInfo advertising report.
* This is received if the advertiser is broadcasting isochronous streams in a BIG.
* See iso.h for more information.
*
* @param sync The advertising set object.
* @param biginfo The BIGInfo report.
*/
void (*biginfo)(struct bt_le_per_adv_sync *sync, const struct bt_iso_biginfo *biginfo);

sys_snode_t node;
};
Expand Down
2 changes: 2 additions & 0 deletions include/bluetooth/hci.h
Original file line number Diff line number Diff line change
Expand Up @@ -2396,6 +2396,7 @@ struct bt_hci_evt_le_big_complete {
uint8_t pto;
uint8_t irc;
uint16_t max_pdu;
uint16_t iso_interval;
uint8_t num_bis;
uint16_t handle[0];
} __packed;
Expand All @@ -2416,6 +2417,7 @@ struct bt_hci_evt_le_big_sync_established {
uint8_t pto;
uint8_t irc;
uint16_t max_pdu;
uint16_t iso_interval;
uint8_t num_bis;
uint16_t handle[0];
} __packed;
Expand Down
148 changes: 139 additions & 9 deletions include/bluetooth/iso.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,24 +79,25 @@ struct bt_iso_chan_qos {
/** @brief Channel direction
*
* Possible values: BT_ISO_CHAN_QOS_IN, BT_ISO_CHAN_QOS_OUT or
* BT_ISO_CHAN_QOS_INOUT.
* BT_ISO_CHAN_QOS_INOUT. Shall be BT_ISO_CHAN_QOS_IN for broadcast
* transmitting, and BT_ISO_CHAN_QOS_OUT for broadcast receiver.
*/
uint8_t dir;
/** Channel interval */
/** Channel interval in us. Value range 0x0000FF - 0x0FFFFFF. */
uint32_t interval;
/** Channel SCA */
/** Channel SCA - Only for CIS */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we should split this into it own struct and then have a union for broadcast and unicast specific parameters.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a bad idea.

Do you mean something like:

struct bt_iso_chan_qos {
    union {
        struct {
            <CIS fields>
        } cis;

        struct {
            <BIS fields>
        } bis;
    };
};

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to me.

uint8_t sca;
/** Channel packing mode */
/** Channel packing mode. 0 for unpacked, 1 for packed. */
uint8_t packing;
/** Channel framing mode */
/** Channel framing mode. 0 for unframed, 1 for framed. */
uint8_t framing;
/** Channel Latency */
/** Channel Latency in ms. Value range 0x0005 - 0x0FA0. */
uint16_t latency;
/** Channel SDU */
/** Channel SDU. Value range 0x0000 0 0x0FFF. */
uint8_t sdu;
/** Channel PHY */
/** Channel PHY - See BT_GAP_LE_PHY for values. Shall not be BT_GAP_LE_PHY_NONE. */
uint8_t phy;
/** Channel Retransmission Number */
/** Channel Retransmission Number. Value range 0x00 - 0x0F. */
uint8_t rtn;
};

Expand All @@ -118,6 +119,103 @@ struct bt_iso_chan_path {
uint8_t cc[0];
};


/** Opaque type representing an Broadcast Isochronous Group (BIG). */
struct bt_iso_big;

struct bt_iso_big_create_param {
/** Array of pointers to BIS channels */
struct bt_iso_chan **bis_channels;

/** Number channels in @p bis_channels */
uint8_t num_bis;

/** Whether or not to encrypt the streams. */
bool encryption;

/** @brief Broadcast code
*
* The code used to derive the session key that is used to encrypt and
* decrypt BIS payloads.
*/
uint8_t bcode[16];
};

struct bt_iso_big_sync_param {
/** Array of pointers to BIS channels */
struct bt_iso_chan **bis_channels;

/** Number channels in @p bis_channels */
uint8_t num_bis;

/** Bitfield of the BISes to sync to */
uint32_t bis_bitfield;

/** @brief Maximum subevents
*
* The MSE (Maximum Subevents) parameter is the maximum number of subevents that a
* Controller should use to receive data payloads in each interval for a BIS
*/
uint32_t mse;

/** Synchronization timeout for the BIG (N * 10 MS) */
uint16_t sync_timeout;

/** Whether or not the streams of the BIG are encrypted */
bool encryption;

/** @brief Broadcast code
*
* The code used to derive the session key that is used to encrypt and
* decrypt BIS payloads.
*/
uint8_t bcode[16];
};

struct bt_iso_biginfo {
/** Address of the advertiser */
const bt_addr_le_t *addr;

/** Advertiser SID */
uint8_t sid;

/** Number of BISes in the BIG */
uint8_t num_bis;

/** Maximum number of subevents in each isochronous event */
uint8_t sub_evt_count;

/** Interval between two BIG anchor point (N * 1.25 ms) */
uint16_t iso_interval;

/** The number of new payloads in each BIS event */
uint8_t burst_number;

/** Offset used for pre-transmissions */
uint8_t offset;

/** The number of times a payload is transmitted in a BIS event */
uint8_t rep_count;

/** Maximum size, in octets, of the payload */
uint16_t max_pdu;

/** The interval, in microseconds, of periodic SDUs. */
uint32_t sdu_interval;

/** Maximum size of an SDU, in octets. */
uint16_t max_sdu;

/** Channel PHY */
uint8_t phy;

/** Channel framing mode */
uint8_t framing;

/** Whether or not the BIG is encrypted */
bool encryption;
};

/** @brief ISO Channel operations structure. */
struct bt_iso_chan_ops {
/** @brief Channel connected callback
Expand Down Expand Up @@ -245,6 +343,38 @@ int bt_iso_chan_disconnect(struct bt_iso_chan *chan);
*/
int bt_iso_chan_send(struct bt_iso_chan *chan, struct net_buf *buf);

/** @brief Creates a BIG as a broadcaster
*
* @param[in] padv Pointer to the periodic advertising object the BIGInfo shall be sent on.
* @param[in] param The parameters used to create and enable the BIG. The QOS parameters are
* determined by the QOS field of the first BIS in the BIS list of this
* parameter.
* @param[out] out_big Broadcast Isochronous Group object on success.
*
* @return 0 in case of success or negative value in case of error.
*/
int bt_iso_big_create(struct bt_le_ext_adv *padv, struct bt_iso_big_create_param *param,
struct bt_iso_big **out_big);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would attemp to map the parameters to bt_iso_channel as well, so we can use function like bt_iso_disconnect to terminate, etc, also perhaps we should name this bt_iso_broadcast(channel, padv) then the channel would contain qos with the related field to create a the big. If this all can be arranged it would also be possible to use bt_iso_chan_send to transmit over broadcast.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bt_iso_chan_send is already used to send over broadcast.

Regarding bt_iso_terminate that should also be possible to implement. The reason why I haven't done that, is that I don't see a use case where you'd want to disconnect a single BIS in a BIG (actually I don't understand the usecase for disconnecting a single CIS in a CIG). Do you have an example of where you'd might want to do that?

I'm not sure what other parameters you are referring to here.

/** @brief Terminates a BIG as a broadcaster or receiver
*
* @param big Pointer to the BIG structure.
*
* @return 0 in case of success or negative value in case of error.
*/
int bt_iso_big_terminate(struct bt_iso_big *big);

/** @brief Creates a BIG as a receiver
*
* @param[in] sync Pointer to the periodic advertising sync object the BIGInfo was received on.
* @param[in] param The parameters used to create and enable the BIG sync.
* @param[out] out_big Broadcast Isochronous Group object on success.
*
* @return 0 in case of success or negative value in case of error.
*/
int bt_iso_big_sync(struct bt_le_per_adv_sync *sync, struct bt_iso_big_sync_param *param,
struct bt_iso_big **out_big);

#ifdef __cplusplus
}
#endif
Expand Down
21 changes: 21 additions & 0 deletions subsys/bluetooth/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,27 @@ config BT_ISO_RX_MTU
range 23 4095
help
Maximum MTU for Isochronous channels RX buffers.

# TODO: Split between broadcaster and observer for optimization
config BT_ISO_BROADCAST
bool "Bluetooth ISO Broadcast Channels supported"
select BT_EXT_ADV
select BT_PER_ADV
select BT_PER_ADV_SYNC
help
This option enables support for Bluetooth Broadcast
Isochronous channels.

if BT_ISO_BROADCAST

config BT_ISO_MAX_BIG
int "Maximum number of Broadcast Isochronous Groups (BIGs) to support"
default 1
help
Maximmum number of BIGs that are supported by the host. A BIG can be
used for either transmitting or receiving, but not at the same time.

endif # BT_ISO_BROADCAST
endif # BT_ISO

# Workaround for not being able to have commas in macro arguments
Expand Down
9 changes: 9 additions & 0 deletions subsys/bluetooth/audio/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ config BT_AUDIO_UNICAST
This option enables support for Bluetooth Unicast Audio using
Isochronous channels.

# TODO: Make BT_AUDIO_BROADCAST not depend on BT_CONN
config BT_AUDIO_BROADCAST
bool "Bluetooth Broadcast Audio Support"
select BT_ISO
select BT_ISO_BROADCAST
help
This option enables support for Bluetooth Broadcast Audio using
Isochronous channels.

endif # BT_CONN

config BT_AUDIO_DEBUG
Expand Down
21 changes: 17 additions & 4 deletions subsys/bluetooth/host/conn_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,23 @@ struct bt_conn_sco {
struct bt_conn_iso {
/* Reference to ACL Connection */
struct bt_conn *acl;
/* CIG ID */
uint8_t cig_id;
/* CIS ID */
uint8_t cis_id;
union {
/* CIG ID */
uint8_t cig_id;
/* BIG handle */
uint8_t big_handle;
};

union {
/* CIS ID */
uint8_t cis_id;

/* BIS ID */
uint8_t bis_id;
};

/** If true, this is a ISO for a BIS, else it is a ISO for a CIS */
bool is_bis;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could use the presence of an ACL to indicate if this is a broadcast or a unicast ISO handle.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Took me a while to figure out why I didn't just do that. It looks like we can be in a case where acl is NULL, but while it is still a CIS (e.g. when creating CIS'es fails), and this value is currently only being used in the cleanup. So if binding a conn and a iso channel fails, the acl pointer cannot be used.

};

typedef void (*bt_conn_tx_cb_t)(struct bt_conn *conn, void *user_data);
Expand Down
72 changes: 72 additions & 0 deletions subsys/bluetooth/host/hci_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -4737,6 +4737,47 @@ static void le_past_received(struct net_buf *buf)
}
}
#endif /* CONFIG_BT_CONN */

#if defined(CONFIG_BT_ISO_BROADCAST)
static void hci_le_biginfo_adv_report(struct net_buf *buf)
{
struct bt_hci_evt_le_biginfo_adv_report *evt;
struct bt_le_per_adv_sync *per_adv_sync;
struct bt_le_per_adv_sync_cb *listener;
struct bt_iso_biginfo biginfo;

evt = net_buf_pull_mem(buf, sizeof(*evt));

per_adv_sync = get_per_adv_sync(sys_le16_to_cpu(evt->sync_handle));

if (!per_adv_sync) {
BT_ERR("Unknown handle 0x%04X for periodic advertising report",
sys_le16_to_cpu(evt->sync_handle));
return;
}

biginfo.addr = &per_adv_sync->addr;
biginfo.sid = per_adv_sync->sid;
biginfo.num_bis = evt->num_bis;
biginfo.sub_evt_count = evt->nse;
biginfo.iso_interval = sys_le16_to_cpu(evt->iso_interval);
biginfo.burst_number = evt->bn;
biginfo.offset = evt->pto;
biginfo.rep_count = evt->irc;
biginfo.max_pdu = sys_le16_to_cpu(evt->max_pdu);
biginfo.sdu_interval = sys_get_le24(evt->sdu_interval);
biginfo.max_sdu = sys_le16_to_cpu(evt->max_sdu);
biginfo.phy = evt->phy;
biginfo.framing = evt->framing;
biginfo.encryption = evt->encryption ? true : false;

SYS_SLIST_FOR_EACH_CONTAINER(&pa_sync_cbs, listener, node) {
if (listener->biginfo) {
listener->biginfo(per_adv_sync, &biginfo);
}
}
}
#endif /* defined(CONFIG_BT_ISO_BROADCAST) */
#endif /* defined(CONFIG_BT_PER_ADV_SYNC) */
#endif /* defined(CONFIG_BT_EXT_ADV) */

Expand Down Expand Up @@ -5025,6 +5066,23 @@ static const struct event_handler meta_events[] = {
sizeof(struct bt_hci_evt_le_cis_established)),
EVENT_HANDLER(BT_HCI_EVT_LE_CIS_REQ, hci_le_cis_req,
sizeof(struct bt_hci_evt_le_cis_req)),
#if defined(CONFIG_BT_ISO_BROADCAST)
EVENT_HANDLER(BT_HCI_EVT_LE_BIG_COMPLETE,
hci_le_big_complete,
sizeof(struct bt_hci_evt_le_big_complete)),
EVENT_HANDLER(BT_HCI_EVT_LE_BIG_TERMINATE,
hci_le_big_termimate,
sizeof(struct bt_hci_evt_le_big_terminate)),
EVENT_HANDLER(BT_HCI_EVT_LE_BIG_SYNC_ESTABLISHED,
hci_le_big_sync_established,
sizeof(struct bt_hci_evt_le_big_sync_established)),
EVENT_HANDLER(BT_HCI_EVT_LE_BIG_SYNC_LOST,
hci_le_big_sync_lost,
sizeof(struct bt_hci_evt_le_big_sync_lost)),
EVENT_HANDLER(BT_HCI_EVT_LE_BIGINFO_ADV_REPORT,
hci_le_biginfo_adv_report,
sizeof(struct bt_hci_evt_le_biginfo_adv_report)),
#endif /* (CONFIG_BT_ISO_BROADCAST) */
#endif /* (CONFIG_BT_ISO) */
};

Expand Down Expand Up @@ -5578,6 +5636,20 @@ static int le_set_event_mask(void)
}
}

/* Enable BIS events for broadcaster and/or receiver */
if (IS_ENABLED(CONFIG_BT_ISO_BROADCAST) &&
BT_FEAT_LE_BIS(bt_dev.le.features)) {
if (BT_FEAT_LE_ISO_BROADCASTER(bt_dev.le.features)) {
mask |= BT_EVT_MASK_LE_BIG_COMPLETE;
mask |= BT_EVT_MASK_LE_BIG_TERMINATED;
}
if (BT_FEAT_LE_SYNC_RECEIVER(bt_dev.le.features)) {
mask |= BT_EVT_MASK_LE_BIG_SYNC_ESTABLISHED;
mask |= BT_EVT_MASK_LE_BIG_SYNC_LOST;
mask |= BT_EVT_MASK_LE_BIGINFO_ADV_REPORT;
}
}

sys_put_le64(mask, cp_mask->events);
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_EVENT_MASK, buf, NULL);
}
Expand Down
Loading