blob: 7e78cc960724d1abf9001a85e9946722040f7d4c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "syscfg/syscfg.h"
#if MYNEWT_VAL(BLE_ISO)
#include "os/os_mbuf.h"
#include "host/ble_hs_log.h"
#include "host/ble_hs.h"
#include "host/ble_iso.h"
#include "nimble/hci_common.h"
#include "sys/queue.h"
#include "ble_hs_priv.h"
#include "ble_hs_hci_priv.h"
#include "ble_hs_mbuf_priv.h"
#define ble_iso_big_conn_handles_init(_big, _handles, _num_handles) \
do { \
struct ble_iso_conn *conn = SLIST_FIRST(&ble_iso_conns); \
\
for (uint8_t i = 0; i < (_num_handles); i++) { \
while (conn != NULL) { \
if (conn->type == BLE_ISO_CONN_BIS) { \
struct ble_iso_bis *bis; \
\
bis = CONTAINER_OF(conn, struct ble_iso_bis, conn); \
if (bis->big == (_big)) { \
conn->handle = le16toh((_handles)[i]); \
conn = SLIST_NEXT(conn, next); \
break; \
} \
} \
\
conn = SLIST_NEXT(conn, next); \
} \
} \
} while (0);
enum ble_iso_conn_type {
BLE_ISO_CONN_BIS,
};
struct ble_iso_big {
SLIST_ENTRY(ble_iso_big) next;
uint8_t handle;
uint16_t max_pdu;
uint8_t bis_cnt;
ble_iso_event_fn *cb;
void *cb_arg;
};
struct ble_iso_conn {
SLIST_ENTRY(ble_iso_conn) next;
enum ble_iso_conn_type type;
uint8_t handle;
struct ble_iso_rx_data_info rx_info;
struct os_mbuf *rx_buf;
ble_iso_event_fn *cb;
void *cb_arg;
};
struct ble_iso_bis {
struct ble_iso_conn conn;
struct ble_iso_big *big;
};
static SLIST_HEAD(, ble_iso_big) ble_iso_bigs;
static SLIST_HEAD(, ble_iso_conn) ble_iso_conns;
static struct os_mempool ble_iso_big_pool;
static os_membuf_t ble_iso_big_mem[
OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_ISO_MAX_BIGS), sizeof (struct ble_iso_big))];
static struct os_mempool ble_iso_bis_pool;
static os_membuf_t ble_iso_bis_mem[
OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_ISO_MAX_BISES), sizeof (struct ble_iso_bis))];
static void
ble_iso_conn_append(struct ble_iso_conn *conn)
{
struct ble_iso_conn *entry, *prev = NULL;
SLIST_FOREACH(entry, &ble_iso_conns, next) {
prev = entry;
}
if (prev == NULL) {
SLIST_INSERT_HEAD(&ble_iso_conns, conn, next);
} else {
SLIST_INSERT_AFTER(prev, conn, next);
}
}
static int
ble_iso_big_handle_set(struct ble_iso_big *big)
{
static uint8_t free_handle;
uint8_t i;
/* Set next free handle */
for (i = BLE_HCI_ISO_BIG_HANDLE_MIN; i < BLE_HCI_ISO_BIG_HANDLE_MAX; i++) {
struct ble_iso_big *node = NULL;
if (free_handle > BLE_HCI_ISO_BIG_HANDLE_MAX) {
free_handle = BLE_HCI_ISO_BIG_HANDLE_MIN;
}
big->handle = free_handle++;
SLIST_FOREACH(node, &ble_iso_bigs, next) {
if (node->handle == big->handle) {
break;
}
}
if (node == NULL || node->handle != big->handle) {
return 0;
}
}
BLE_HS_DBG_ASSERT(0);
return BLE_HS_EOS;
}
static struct ble_iso_big *
ble_iso_big_alloc(void)
{
struct ble_iso_big *new_big;
int rc;
new_big = os_memblock_get(&ble_iso_big_pool);
if (new_big == NULL) {
BLE_HS_LOG_ERROR("No more memory in pool\n");
/* Out of memory. */
return NULL;
}
memset(new_big, 0, sizeof *new_big);
rc = ble_iso_big_handle_set(new_big);
if (rc != 0) {
os_memblock_put(&ble_iso_big_pool, new_big);
return NULL;
}
SLIST_INSERT_HEAD(&ble_iso_bigs, new_big, next);
return new_big;
}
static struct ble_iso_bis *
ble_iso_bis_alloc(struct ble_iso_big *big)
{
struct ble_iso_bis *new_bis;
new_bis = os_memblock_get(&ble_iso_bis_pool);
if (new_bis == NULL) {
BLE_HS_LOG_ERROR("No more memory in pool\n");
/* Out of memory. */
return NULL;
}
memset(new_bis, 0, sizeof *new_bis);
new_bis->conn.type = BLE_ISO_CONN_BIS;
new_bis->big = big;
ble_iso_conn_append(&new_bis->conn);
return new_bis;
}
static struct ble_iso_big *
ble_iso_big_find_by_handle(uint8_t big_handle)
{
struct ble_iso_big *big;
SLIST_FOREACH(big, &ble_iso_bigs, next) {
if (big->handle == big_handle) {
return big;
}
}
return NULL;
}
static int
ble_iso_big_free(struct ble_iso_big *big)
{
struct ble_iso_conn *conn;
struct ble_iso_bis *rem_bis[MYNEWT_VAL(BLE_ISO_MAX_BISES)] = {
[0 ... MYNEWT_VAL(BLE_ISO_MAX_BISES) - 1] = NULL
};
uint8_t i = 0;
SLIST_FOREACH(conn, &ble_iso_conns, next) {
struct ble_iso_bis *bis;
if (conn->type != BLE_ISO_CONN_BIS) {
continue;
}
bis = CONTAINER_OF(conn, struct ble_iso_bis, conn);
if (bis->big == big) {
SLIST_REMOVE(&ble_iso_conns, conn, ble_iso_conn, next);
rem_bis[i++] = bis;
}
}
while (i > 0) {
os_memblock_put(&ble_iso_bis_pool, rem_bis[--i]);
}
SLIST_REMOVE(&ble_iso_bigs, big, ble_iso_big, next);
os_memblock_put(&ble_iso_big_pool, big);
return 0;
}
static struct ble_iso_conn *
ble_iso_conn_lookup_handle(uint16_t handle)
{
struct ble_iso_conn *conn;
SLIST_FOREACH(conn, &ble_iso_conns, next) {
if (conn->handle == handle) {
return conn;
}
}
return NULL;
}
#if MYNEWT_VAL(BLE_ISO_BROADCAST_SOURCE)
int
ble_iso_create_big(const struct ble_iso_create_big_params *create_params,
const struct ble_iso_big_params *big_params,
uint8_t *big_handle)
{
struct ble_hci_le_create_big_cp cp = { 0 };
struct ble_iso_big *big;
int rc;
cp.adv_handle = create_params->adv_handle;
if (create_params->bis_cnt > MYNEWT_VAL(BLE_ISO_MAX_BISES)) {
return BLE_HS_EINVAL;
}
big = ble_iso_big_alloc();
if (big == NULL) {
return BLE_HS_ENOMEM;
}
big->bis_cnt = create_params->bis_cnt;
big->cb = create_params->cb;
big->cb_arg = create_params->cb_arg;
for (uint8_t i = 0; i < create_params->bis_cnt; i++) {
struct ble_iso_bis *bis;
bis = ble_iso_bis_alloc(big);
if (bis == NULL) {
ble_iso_big_free(big);
return BLE_HS_ENOMEM;
}
}
cp.adv_handle = create_params->adv_handle;
cp.num_bis = create_params->bis_cnt;
put_le24(cp.sdu_interval, big_params->sdu_interval);
cp.max_sdu = big_params->max_sdu;
cp.max_transport_latency = big_params->max_transport_latency;
cp.rtn = big_params->rtn;
cp.phy = big_params->phy;
cp.packing = big_params->packing;
cp.framing = big_params->framing;
cp.encryption = big_params->encryption;
if (big_params->encryption) {
memcpy(cp.broadcast_code, big_params->broadcast_code, 16);
}
rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE,
BLE_HCI_OCF_LE_CREATE_BIG),
&cp, sizeof(cp),NULL, 0);
if (rc != 0) {
ble_iso_big_free(big);
} else {
*big_handle = big->handle;
}
return rc;
}
int
ble_iso_terminate_big(uint8_t big_handle)
{
struct ble_hci_le_terminate_big_cp cp;
struct ble_iso_big *big;
int rc;
big = ble_iso_big_find_by_handle(big_handle);
if (big == NULL) {
BLE_HS_LOG_ERROR("No BIG with handle=%d\n", big_handle);
return BLE_HS_ENOENT;
}
cp.big_handle = big->handle;
cp.reason = BLE_ERR_CONN_TERM_LOCAL;
rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE,
BLE_HCI_OCF_LE_TERMINATE_BIG),
&cp, sizeof(cp),NULL, 0);
return rc;
}
void
ble_iso_rx_create_big_complete(const struct ble_hci_ev_le_subev_create_big_complete *ev)
{
struct ble_iso_event event;
struct ble_iso_big *big;
big = ble_iso_big_find_by_handle(ev->big_handle);
if (big == NULL) {
BLE_HS_LOG_ERROR("No BIG with handle=%d\n", ev->big_handle);
return;
}
memset(&event, 0, sizeof(event));
event.type = BLE_ISO_EVENT_BIG_CREATE_COMPLETE;
event.big_created.status = ev->status;
if (event.big_created.status != 0) {
ble_iso_big_free(big);
} else {
if (big->bis_cnt != ev->num_bis) {
BLE_HS_LOG_ERROR("Unexpected num_bis=%d != bis_cnt=%d\n",
ev->num_bis, big->bis_cnt);
/* XXX: Should we destroy the group? */
}
ble_iso_big_conn_handles_init(big, ev->conn_handle, ev->num_bis);
big->max_pdu = ev->max_pdu;
event.big_created.desc.big_handle = ev->big_handle;
event.big_created.desc.big_sync_delay = get_le24(ev->big_sync_delay);
event.big_created.desc.transport_latency_big =
get_le24(ev->transport_latency_big);
event.big_created.desc.nse = ev->nse;
event.big_created.desc.bn = ev->bn;
event.big_created.desc.pto = ev->pto;
event.big_created.desc.irc = ev->irc;
event.big_created.desc.max_pdu = ev->max_pdu;
event.big_created.desc.iso_interval = ev->iso_interval;
event.big_created.desc.num_bis = ev->num_bis;
memcpy(event.big_created.desc.conn_handle, ev->conn_handle,
ev->num_bis * sizeof(uint16_t));
event.big_created.phy = ev->phy;
}
if (big->cb != NULL) {
big->cb(&event, big->cb_arg);
}
}
void
ble_iso_rx_terminate_big_complete(const struct ble_hci_ev_le_subev_terminate_big_complete *ev)
{
struct ble_iso_event event;
struct ble_iso_big *big;
big = ble_iso_big_find_by_handle(ev->big_handle);
if (big == NULL) {
BLE_HS_LOG_ERROR("No BIG with handle=%d\n", ev->big_handle);
return;
}
event.type = BLE_ISO_EVENT_BIG_TERMINATE_COMPLETE;
event.big_terminated.big_handle = ev->big_handle;
event.big_terminated.reason = ev->reason;
if (big->cb != NULL) {
big->cb(&event, big->cb_arg);
}
ble_iso_big_free(big);
}
static int
ble_iso_tx_complete(uint16_t conn_handle, const uint8_t *data,
uint16_t data_len)
{
struct os_mbuf *om;
int rc;
om = ble_hs_mbuf_bare_pkt();
if (!om) {
return BLE_HS_ENOMEM;
}
os_mbuf_extend(om, 8);
/* Connection_Handle, PB_Flag, TS_Flag */
put_le16(&om->om_data[0],
BLE_HCI_ISO_HANDLE(conn_handle, BLE_HCI_ISO_PB_COMPLETE, 0));
/* Data_Total_Length = Data length + Packet_Sequence_Number placeholder */
put_le16(&om->om_data[2], data_len + 4);
/* Packet_Sequence_Number placeholder */
put_le16(&om->om_data[4], 0);
/* ISO_SDU_Length */
put_le16(&om->om_data[6], data_len);
rc = os_mbuf_append(om, data, data_len);
if (rc) {
return rc;
}
return ble_transport_to_ll_iso(om);
}
static int
ble_iso_tx_segmented(uint16_t conn_handle, const uint8_t *data,
uint16_t data_len)
{
struct os_mbuf *om;
uint16_t data_left = data_len;
uint16_t packet_len;
uint16_t offset = 0;
uint8_t pb;
int rc;
while (data_left) {
packet_len = min(MYNEWT_VAL(BLE_TRANSPORT_ISO_SIZE), data_left);
if (data_left == data_len) {
pb = BLE_HCI_ISO_PB_FIRST;
} else if (packet_len == data_left) {
pb = BLE_HCI_ISO_PB_LAST;
} else {
pb = BLE_HCI_ISO_PB_CONTINUATION;
}
om = ble_hs_mbuf_bare_pkt();
if (!om) {
return BLE_HS_ENOMEM;
}
os_mbuf_extend(om, pb == BLE_HCI_ISO_PB_FIRST ? 8: 4);
/* Connection_Handle, PB_Flag, TS_Flag */
put_le16(&om->om_data[0],
BLE_HCI_ISO_HANDLE(conn_handle, pb, 0));
if (pb == BLE_HCI_ISO_PB_FIRST) {
/* Data_Total_Length = Data length +
* Packet_Sequence_Number placeholder*/
put_le16(&om->om_data[2], packet_len + 4);
/* Packet_Sequence_Number placeholder */
put_le16(&om->om_data[8], 0);
/* ISO_SDU_Length */
put_le16(&om->om_data[10], packet_len);
} else {
put_le16(&om->om_data[2], packet_len);
}
rc = os_mbuf_append(om, data + offset, packet_len);
if (rc) {
return rc;
}
ble_transport_to_ll_iso(om);
offset += packet_len;
data_left -= packet_len;
}
return 0;
}
int
ble_iso_tx(uint16_t conn_handle, void *data, uint16_t data_len)
{
int rc;
if (data_len <= MYNEWT_VAL(BLE_TRANSPORT_ISO_SIZE)) {
rc = ble_iso_tx_complete(conn_handle, data, data_len);
} else {
rc = ble_iso_tx_segmented(conn_handle, data, data_len);
}
return rc;
}
#endif /* BLE_ISO_BROADCAST_SOURCE */
#if MYNEWT_VAL(BLE_ISO_BROADCAST_SINK)
int
ble_iso_big_sync_create(const struct ble_iso_big_sync_create_params *param,
uint8_t *big_handle)
{
struct ble_hci_le_big_create_sync_cp *cp;
uint8_t buf[sizeof(*cp) + MYNEWT_VAL(BLE_ISO_MAX_BISES)];
struct ble_iso_big *big;
int rc;
big = ble_iso_big_alloc();
if (big == NULL) {
return BLE_HS_ENOMEM;
}
big->bis_cnt = param->bis_cnt;
big->cb = param->cb;
big->cb_arg = param->cb_arg;
cp = (void *)buf;
cp->big_handle = big->handle;
put_le16(&cp->sync_handle, param->sync_handle);
if (param->broadcast_code != NULL) {
cp->encryption = BLE_HCI_ISO_BIG_ENCRYPTION_ENCRYPTED;
memcpy(cp->broadcast_code, param->broadcast_code, sizeof(cp->broadcast_code));
} else {
cp->encryption = BLE_HCI_ISO_BIG_ENCRYPTION_UNENCRYPTED;
memset(cp->broadcast_code, 0, sizeof(cp->broadcast_code));
}
cp->mse = param->mse;
put_le16(&cp->sync_timeout, param->sync_timeout);
cp->num_bis = param->bis_cnt;
for (uint8_t i = 0; i < param->bis_cnt; i++) {
struct ble_iso_bis *bis;
bis = ble_iso_bis_alloc(big);
if (bis == NULL) {
ble_iso_big_free(big);
return BLE_HS_ENOMEM;
}
cp->bis[i] = param->bis_params[i].bis_index;
}
rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE,
BLE_HCI_OCF_LE_BIG_CREATE_SYNC),
cp, sizeof(*cp) + cp->num_bis, NULL, 0);
if (rc != 0) {
ble_iso_big_free(big);
} else {
*big_handle = big->handle;
}
return rc;
}
int
ble_iso_big_sync_terminate(uint8_t big_handle)
{
struct ble_hci_le_big_terminate_sync_cp cp;
struct ble_hci_le_big_terminate_sync_rp rp;
struct ble_iso_big *big;
int rc;
big = ble_iso_big_find_by_handle(big_handle);
if (big == NULL) {
BLE_HS_LOG_ERROR("No BIG with handle=%d\n", big_handle);
return BLE_HS_ENOENT;
}
cp.big_handle = big->handle;
rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE,
BLE_HCI_OCF_LE_BIG_TERMINATE_SYNC),
&cp, sizeof(cp), &rp, sizeof(rp));
if (rc == 0) {
struct ble_iso_event event;
ble_iso_event_fn *cb;
void *cb_arg;
event.type = BLE_ISO_EVENT_BIG_SYNC_TERMINATED;
event.big_terminated.big_handle = big_handle;
event.big_terminated.reason = BLE_ERR_CONN_TERM_LOCAL;
cb = big->cb;
cb_arg = big->cb_arg;
ble_iso_big_free(big);
if (cb != NULL) {
cb(&event, cb_arg);
}
}
return rc;
}
void
ble_iso_rx_big_sync_established(const struct ble_hci_ev_le_subev_big_sync_established *ev)
{
struct ble_iso_event event;
struct ble_iso_big *big;
ble_iso_event_fn *cb;
void *cb_arg;
big = ble_iso_big_find_by_handle(ev->big_handle);
if (big == NULL) {
return;
}
cb = big->cb;
cb_arg = big->cb_arg;
memset(&event, 0, sizeof(event));
event.type = BLE_ISO_EVENT_BIG_SYNC_ESTABLISHED;
event.big_sync_established.status = ev->status;
if (event.big_sync_established.status != 0) {
ble_iso_big_free(big);
} else {
if (big->bis_cnt != ev->num_bis) {
BLE_HS_LOG_ERROR("Unexpected num_bis=%d != bis_cnt=%d\n",
ev->num_bis, big->bis_cnt);
/* XXX: Should we destroy the group? */
}
ble_iso_big_conn_handles_init(big, ev->conn_handle, ev->num_bis);
event.big_sync_established.desc.big_handle = ev->big_handle;
event.big_sync_established.desc.transport_latency_big =
get_le24(ev->transport_latency_big);
event.big_sync_established.desc.nse = ev->nse;
event.big_sync_established.desc.bn = ev->bn;
event.big_sync_established.desc.pto = ev->pto;
event.big_sync_established.desc.irc = ev->irc;
event.big_sync_established.desc.max_pdu = le16toh(ev->max_pdu);
event.big_sync_established.desc.iso_interval = le16toh(ev->iso_interval);
event.big_sync_established.desc.num_bis = ev->num_bis;
memcpy(event.big_sync_established.desc.conn_handle, ev->conn_handle,
ev->num_bis * sizeof(uint16_t));
}
if (cb != NULL) {
cb(&event, cb_arg);
}
}
void
ble_iso_rx_big_sync_lost(const struct ble_hci_ev_le_subev_big_sync_lost *ev)
{
struct ble_iso_event event;
struct ble_iso_big *big;
ble_iso_event_fn *cb;
void *cb_arg;
big = ble_iso_big_find_by_handle(ev->big_handle);
if (big == NULL) {
BLE_HS_LOG_ERROR("No BIG with handle=%d\n", ev->big_handle);
return;
}
event.type = BLE_ISO_EVENT_BIG_SYNC_TERMINATED;
event.big_terminated.big_handle = ev->big_handle;
event.big_terminated.reason = ev->reason;
cb = big->cb;
cb_arg = big->cb_arg;
ble_iso_big_free(big);
if (cb != NULL) {
cb(&event, cb_arg);
}
}
static int
ble_iso_rx_data_info_parse(struct os_mbuf *om, bool ts_available,
struct ble_iso_rx_data_info *info)
{
struct ble_hci_iso_data *iso_data;
uint16_t u16;
if (ts_available) {
if (os_mbuf_len(om) < sizeof(info->ts)) {
BLE_HS_LOG_DEBUG("Data missing\n");
return BLE_HS_EMSGSIZE;
}
info->ts = get_le32(om->om_data);
os_mbuf_adj(om, sizeof(info->ts));
} else {
info->ts = 0;
}
if (os_mbuf_len(om) < sizeof(*iso_data)) {
BLE_HS_LOG_DEBUG("Data missing\n");
return BLE_HS_EMSGSIZE;
}
iso_data = (void *)(om->om_data);
info->seq_num = le16toh(iso_data->packet_seq_num);
u16 = le16toh(iso_data->sdu_len);
info->sdu_len = BLE_HCI_ISO_SDU_LENGTH(u16);
info->status = BLE_HCI_ISO_PKT_STATUS_FLAG(u16);
info->ts_valid = ts_available;
os_mbuf_adj(om, sizeof(*iso_data));
return 0;
}
static void
ble_iso_conn_rx_reset(struct ble_iso_conn *conn)
{
memset(&conn->rx_info, 0, sizeof(conn->rx_info));
conn->rx_buf = NULL;
}
static void
ble_iso_conn_rx_data_discard(struct ble_iso_conn *conn)
{
os_mbuf_free_chain(conn->rx_buf);
ble_iso_conn_rx_reset(conn);
}
static void
ble_iso_event_iso_rx_emit(struct ble_iso_conn *conn)
{
struct ble_iso_event event = {
.type = BLE_ISO_EVENT_ISO_RX,
.iso_rx.conn_handle = conn->handle,
.iso_rx.info = &conn->rx_info,
.iso_rx.om = conn->rx_buf,
};
if (conn->cb != NULL) {
conn->cb(&event, conn->cb_arg);
}
}
static int
ble_iso_conn_rx_data_load(struct ble_iso_conn *conn, struct os_mbuf *frag,
uint8_t pb_flag, bool ts_available, void *arg)
{
int len_remaining;
int rc;
switch (pb_flag) {
case BLE_HCI_ISO_PB_FIRST:
case BLE_HCI_ISO_PB_COMPLETE:
if (conn->rx_buf != NULL) {
/* Previous data packet never completed. Discard old packet. */
ble_iso_conn_rx_data_discard(conn);
}
rc = ble_iso_rx_data_info_parse(frag, ts_available, &conn->rx_info);
if (rc != 0) {
return rc;
}
conn->rx_buf = frag;
break;
case BLE_HCI_ISO_PB_CONTINUATION:
case BLE_HCI_ISO_PB_LAST:
if (conn->rx_buf == NULL) {
/* Last fragment without the start. Discard new packet. */
return BLE_HS_EBADDATA;
}
/* Determine whether the total length won't exceed the declared SDU length */
len_remaining = conn->rx_info.sdu_len - OS_MBUF_PKTLEN(conn->rx_buf);
if (len_remaining - os_mbuf_len(frag) < 0) {
/* SDU Length exceeded. Discard all packets. */
ble_iso_conn_rx_data_discard(conn);
return BLE_HS_EBADDATA;
}
os_mbuf_concat(conn->rx_buf, frag);
break;
default:
BLE_HS_LOG_ERROR("Invalid pb_flag %d\n", pb_flag);
return BLE_HS_EBADDATA;
}
if (pb_flag == BLE_HCI_ISO_PB_COMPLETE || pb_flag == BLE_HCI_ISO_PB_LAST) {
ble_iso_event_iso_rx_emit(conn);
ble_iso_conn_rx_reset(conn);
}
return 0;
}
int
ble_iso_rx_data(struct os_mbuf *om, void *arg)
{
struct ble_iso_conn *conn;
struct ble_hci_iso *hci_iso;
uint16_t conn_handle;
uint16_t length;
uint16_t pb_flag;
uint16_t ts_flag;
uint16_t u16;
int rc;
if (os_mbuf_len(om) < sizeof(*hci_iso)) {
BLE_HS_LOG_DEBUG("Data missing\n");
os_mbuf_free_chain(om);
return BLE_HS_EMSGSIZE;
}
hci_iso = (void *)om->om_data;
u16 = le16toh(hci_iso->handle);
conn_handle = BLE_HCI_ISO_CONN_HANDLE(u16);
pb_flag = BLE_HCI_ISO_PB_FLAG(u16);
ts_flag = BLE_HCI_ISO_TS_FLAG(u16);
length = BLE_HCI_ISO_LENGTH(le16toh(hci_iso->length));
os_mbuf_adj(om, sizeof(*hci_iso));
if (os_mbuf_len(om) < length) {
BLE_HS_LOG_DEBUG("Data missing\n");
os_mbuf_free_chain(om);
return BLE_HS_EMSGSIZE;
}
conn = ble_iso_conn_lookup_handle(conn_handle);
if (conn == NULL) {
BLE_HS_LOG_DEBUG("Unknown handle=%d\n", conn_handle);
os_mbuf_free_chain(om);
return BLE_HS_EMSGSIZE;
}
rc = ble_iso_conn_rx_data_load(conn, om, pb_flag, ts_flag > 0, arg);
if (rc != 0) {
os_mbuf_free_chain(om);
return rc;
}
return 0;
}
#endif /* BLE_ISO_BROADCAST_SINK */
int
ble_iso_data_path_setup(const struct ble_iso_data_path_setup_params *param)
{
struct ble_hci_le_setup_iso_data_path_rp rp;
struct ble_hci_le_setup_iso_data_path_cp *cp;
uint8_t buf[sizeof(*cp) + UINT8_MAX];
struct ble_iso_conn *conn;
int rc;
conn = ble_iso_conn_lookup_handle(param->conn_handle);
if (conn == NULL) {
BLE_HS_LOG_ERROR("invalid conn_handle\n");
return BLE_HS_ENOTCONN;
}
if (param->codec_config_len > 0 && param->codec_config == NULL) {
BLE_HS_LOG_ERROR("Missing codec_config\n");
return BLE_HS_EINVAL;
}
cp = (void *)buf;
put_le16(&cp->conn_handle, param->conn_handle);
cp->data_path_dir = 0;
if (param->data_path_dir & BLE_ISO_DATA_DIR_TX) {
/* Input (Host to Controller) */
cp->data_path_dir |= BLE_HCI_ISO_DATA_PATH_DIR_INPUT;
}
if (param->data_path_dir & BLE_ISO_DATA_DIR_RX) {
/* Output (Controller to Host) */
cp->data_path_dir |= BLE_HCI_ISO_DATA_PATH_DIR_OUTPUT;
if (cp->data_path_id == BLE_HCI_ISO_DATA_PATH_ID_HCI && param->cb == NULL) {
BLE_HS_LOG_ERROR("param->cb is NULL\n");
return BLE_HS_EINVAL;
}
conn->cb = param->cb;
conn->cb_arg = param->cb_arg;
}
cp->data_path_id = param->data_path_id;
cp->codec_id[0] = param->codec_id.format;
put_le16(&cp->codec_id[1], param->codec_id.company_id);
put_le16(&cp->codec_id[3], param->codec_id.vendor_specific);
put_le24(cp->controller_delay, param->ctrl_delay);
cp->codec_config_len = param->codec_config_len;
memcpy(cp->codec_config, param->codec_config, cp->codec_config_len);
rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE,
BLE_HCI_OCF_LE_SETUP_ISO_DATA_PATH),
cp, sizeof(*cp) + cp->codec_config_len, &rp,
sizeof(rp));
return rc;
}
int
ble_iso_data_path_remove(const struct ble_iso_data_path_remove_params *param)
{
struct ble_hci_le_remove_iso_data_path_rp rp;
struct ble_hci_le_remove_iso_data_path_cp cp = { 0 };
struct ble_iso_conn *conn;
int rc;
conn = ble_iso_conn_lookup_handle(param->conn_handle);
if (conn == NULL) {
BLE_HS_LOG_ERROR("invalid conn_handle\n");
return BLE_HS_ENOTCONN;
}
put_le16(&cp.conn_handle, param->conn_handle);
if (param->data_path_dir & BLE_ISO_DATA_DIR_TX) {
/* Input (Host to Controller) */
cp.data_path_dir |= BLE_HCI_ISO_DATA_PATH_DIR_INPUT;
}
if (param->data_path_dir & BLE_ISO_DATA_DIR_RX) {
/* Output (Controller to Host) */
cp.data_path_dir |= BLE_HCI_ISO_DATA_PATH_DIR_OUTPUT;
conn->cb = NULL;
conn->cb_arg = NULL;
}
rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE,
BLE_HCI_OCF_LE_REMOVE_ISO_DATA_PATH),
&cp, sizeof(cp), &rp, sizeof(rp));
return rc;
}
int
ble_iso_init(void)
{
int rc;
SLIST_INIT(&ble_iso_bigs);
rc = os_mempool_init(&ble_iso_big_pool,
MYNEWT_VAL(BLE_ISO_MAX_BIGS),
sizeof (struct ble_iso_big),
ble_iso_big_mem, "ble_iso_big_pool");
SYSINIT_PANIC_ASSERT(rc == 0);
rc = os_mempool_init(&ble_iso_bis_pool,
MYNEWT_VAL(BLE_ISO_MAX_BISES),
sizeof (struct ble_iso_bis),
ble_iso_bis_mem, "ble_iso_bis_pool");
SYSINIT_PANIC_ASSERT(rc == 0);
return 0;
}
#endif /* BLE_ISO */