200 lines
4.4 KiB
C
200 lines
4.4 KiB
C
/* Bluetooth Audio Unicast Server */
|
|
|
|
/*
|
|
* Copyright (c) 2021-2023 Nordic Semiconductor ASA
|
|
* Copyright (c) 2024 Demant A/S
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
#include <zephyr/autoconf.h>
|
|
#include <zephyr/bluetooth/audio/audio.h>
|
|
#include <zephyr/bluetooth/audio/bap.h>
|
|
#include <zephyr/bluetooth/conn.h>
|
|
#include <zephyr/bluetooth/iso.h>
|
|
#include <zephyr/logging/log.h>
|
|
#include <zephyr/sys/check.h>
|
|
|
|
#include "ascs_internal.h"
|
|
#include "bap_iso.h"
|
|
#include "bap_endpoint.h"
|
|
#include "pacs_internal.h"
|
|
|
|
LOG_MODULE_REGISTER(bt_bap_unicast_server, CONFIG_BT_BAP_UNICAST_SERVER_LOG_LEVEL);
|
|
|
|
static const struct bt_bap_unicast_server_cb *unicast_server_cb;
|
|
|
|
int bt_bap_unicast_server_register(const struct bt_bap_unicast_server_register_param *param)
|
|
{
|
|
if (param == NULL) {
|
|
LOG_DBG("param is NULL");
|
|
return -EINVAL;
|
|
}
|
|
|
|
return bt_ascs_register(param->snk_cnt, param->src_cnt);
|
|
}
|
|
|
|
int bt_bap_unicast_server_unregister(void)
|
|
{
|
|
if (unicast_server_cb != NULL) {
|
|
LOG_DBG("Callbacks are still registered");
|
|
return -EAGAIN;
|
|
}
|
|
|
|
return bt_ascs_unregister();
|
|
}
|
|
|
|
int bt_bap_unicast_server_register_cb(const struct bt_bap_unicast_server_cb *cb)
|
|
{
|
|
int err;
|
|
|
|
CHECKIF(cb == NULL) {
|
|
LOG_DBG("cb is NULL");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (unicast_server_cb != NULL) {
|
|
LOG_DBG("callback structure already registered");
|
|
return -EALREADY;
|
|
}
|
|
|
|
err = bt_ascs_init(cb);
|
|
if (err != 0) {
|
|
return err;
|
|
}
|
|
|
|
unicast_server_cb = cb;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_bap_unicast_server_unregister_cb(const struct bt_bap_unicast_server_cb *cb)
|
|
{
|
|
if (unicast_server_cb == NULL) {
|
|
LOG_DBG("no callback is registered");
|
|
return -EALREADY;
|
|
}
|
|
|
|
if (cb == NULL) {
|
|
LOG_DBG("cb is NULL");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (unicast_server_cb != cb) {
|
|
LOG_DBG("callback structure not registered");
|
|
return -EINVAL;
|
|
}
|
|
|
|
bt_ascs_cleanup();
|
|
unicast_server_cb = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_bap_unicast_server_reconfig(struct bt_bap_stream *stream,
|
|
const struct bt_audio_codec_cfg *codec_cfg)
|
|
{
|
|
struct bt_bap_ep *ep;
|
|
struct bt_bap_ascs_rsp rsp = BT_BAP_ASCS_RSP(BT_BAP_ASCS_RSP_CODE_SUCCESS,
|
|
BT_BAP_ASCS_REASON_NONE);
|
|
int err;
|
|
|
|
ep = stream->ep;
|
|
|
|
if (unicast_server_cb != NULL &&
|
|
unicast_server_cb->reconfig != NULL) {
|
|
err = unicast_server_cb->reconfig(stream, ep->dir, codec_cfg, &ep->qos_pref, &rsp);
|
|
} else {
|
|
err = -ENOTSUP;
|
|
}
|
|
|
|
if (err != 0) {
|
|
return err;
|
|
}
|
|
|
|
(void)memcpy(&ep->codec_cfg, codec_cfg, sizeof(*codec_cfg));
|
|
|
|
return ascs_ep_set_state(ep, BT_BAP_EP_STATE_CODEC_CONFIGURED);
|
|
}
|
|
|
|
int bt_bap_unicast_server_start(struct bt_bap_stream *stream)
|
|
{
|
|
struct bt_bap_ep *ep = stream->ep;
|
|
|
|
if (ep->dir != BT_AUDIO_DIR_SINK) {
|
|
LOG_DBG("Invalid operation for stream %p with dir %u",
|
|
stream, ep->dir);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* If ISO is connected to go streaming state,
|
|
* else wait for ISO to be connected
|
|
*/
|
|
if (ep->iso->chan.state == BT_ISO_STATE_CONNECTED) {
|
|
return ascs_ep_set_state(ep, BT_BAP_EP_STATE_STREAMING);
|
|
}
|
|
|
|
ep->receiver_ready = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_bap_unicast_server_metadata(struct bt_bap_stream *stream, const uint8_t meta[],
|
|
size_t meta_len)
|
|
{
|
|
struct bt_bap_ep *ep;
|
|
struct bt_bap_ascs_rsp rsp = BT_BAP_ASCS_RSP(BT_BAP_ASCS_RSP_CODE_SUCCESS,
|
|
BT_BAP_ASCS_REASON_NONE);
|
|
int err;
|
|
|
|
if (meta_len > sizeof(ep->codec_cfg.meta)) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (unicast_server_cb != NULL && unicast_server_cb->metadata != NULL) {
|
|
err = unicast_server_cb->metadata(stream, meta, meta_len, &rsp);
|
|
} else {
|
|
err = -ENOTSUP;
|
|
}
|
|
|
|
|
|
if (err) {
|
|
LOG_ERR("Metadata failed: err %d, code %u, reason %u", err, rsp.code, rsp.reason);
|
|
return err;
|
|
}
|
|
|
|
ep = stream->ep;
|
|
(void)memcpy(ep->codec_cfg.meta, meta, meta_len);
|
|
|
|
/* Set the state to the same state to trigger the notifications */
|
|
return ascs_ep_set_state(ep, ep->status.state);
|
|
}
|
|
|
|
int bt_bap_unicast_server_disable(struct bt_bap_stream *stream)
|
|
{
|
|
return bt_ascs_disable_ase(stream->ep);
|
|
}
|
|
|
|
int bt_bap_unicast_server_release(struct bt_bap_stream *stream)
|
|
{
|
|
return bt_ascs_release_ase(stream->ep);
|
|
}
|
|
|
|
int bt_bap_unicast_server_config_ase(struct bt_conn *conn, struct bt_bap_stream *stream,
|
|
struct bt_audio_codec_cfg *codec_cfg,
|
|
const struct bt_audio_codec_qos_pref *qos_pref)
|
|
{
|
|
return bt_ascs_config_ase(conn, stream, codec_cfg, qos_pref);
|
|
}
|
|
|
|
void bt_bap_unicast_server_foreach_ep(struct bt_conn *conn, bt_bap_ep_func_t func, void *user_data)
|
|
{
|
|
bt_ascs_foreach_ep(conn, func, user_data);
|
|
}
|