From 70b415dab60a0acb00877b4ca88bedae15d8cd8c Mon Sep 17 00:00:00 2001 From: Zihao Gao Date: Wed, 30 Oct 2024 10:45:02 +0800 Subject: [PATCH] Bluetooth: AVRCP: Implemation of AVRCP. This patch implementing avrcp.c New Kconfig BT_AVRCP is provided to enable this layer. BT_AVRCP_TARGET and BT_AVRCP_CONTROLLER are then provided to enable one of the two roles independently. avrcp.h shows the APIs for the upper layer. Only connection and disconnection interfaces are provided in this patch. Signed-off-by: Zihao Gao --- include/zephyr/bluetooth/classic/avrcp.h | 78 +++++++++ subsys/bluetooth/Kconfig.logging | 4 + subsys/bluetooth/host/classic/CMakeLists.txt | 1 + subsys/bluetooth/host/classic/Kconfig | 21 +++ subsys/bluetooth/host/classic/avrcp.c | 158 ++++++++++++++++++ .../bluetooth/host/classic/avrcp_internal.h | 12 ++ subsys/bluetooth/host/classic/l2cap_br.c | 5 + 7 files changed, 279 insertions(+) create mode 100644 include/zephyr/bluetooth/classic/avrcp.h create mode 100644 subsys/bluetooth/host/classic/avrcp.c create mode 100644 subsys/bluetooth/host/classic/avrcp_internal.h diff --git a/include/zephyr/bluetooth/classic/avrcp.h b/include/zephyr/bluetooth/classic/avrcp.h new file mode 100644 index 00000000000..b9087bb0113 --- /dev/null +++ b/include/zephyr/bluetooth/classic/avrcp.h @@ -0,0 +1,78 @@ +/** @file + * @brief Audio Video Remote Control Profile header. + */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * Copyright (C) 2024 Xiaomi Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_AVRCP_H_ +#define ZEPHYR_INCLUDE_BLUETOOTH_AVRCP_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief AVRCP structure */ +struct bt_avrcp; + +struct bt_avrcp_cb { + /** @brief An AVRCP connection has been established. + * + * This callback notifies the application of an avrcp connection, + * i.e., an AVCTP L2CAP connection. + * + * @param avrcp AVRCP connection object. + */ + void (*connected)(struct bt_avrcp *avrcp); + /** @brief An AVRCP connection has been disconnected. + * + * This callback notifies the application that an avrcp connection + * has been disconnected. + * + * @param avrcp AVRCP connection object. + */ + void (*disconnected)(struct bt_avrcp *avrcp); +}; + +/** @brief Connect AVRCP. + * + * This function is to be called after the conn parameter is obtained by + * performing a GAP procedure. The API is to be used to establish AVRCP + * connection between devices. + * + * @param conn Pointer to bt_conn structure. + * + * @return pointer to struct bt_avrcp in case of success or NULL in case + * of error. + */ +struct bt_avrcp *bt_avrcp_connect(struct bt_conn *conn); + +/** @brief Disconnect AVRCP. + * + * This function close AVCTP L2CAP connection. + * + * @param avrcp The AVRCP instance. + * + * @return 0 in case of success or error code in case of error. + */ +int bt_avrcp_disconnect(struct bt_avrcp *avrcp); + +/** @brief Register callback. + * + * Register AVRCP callbacks to monitor the state and interact with the remote device. + * + * @param cb The callback function. + * + * @return 0 in case of success or error code in case of error. + */ +int bt_avrcp_register_cb(const struct bt_avrcp_cb *cb); + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_AVRCP_H_ */ diff --git a/subsys/bluetooth/Kconfig.logging b/subsys/bluetooth/Kconfig.logging index aacd3059c69..7ccaf55c2d6 100644 --- a/subsys/bluetooth/Kconfig.logging +++ b/subsys/bluetooth/Kconfig.logging @@ -391,6 +391,10 @@ module = BT_AVCTP module-str = "Bluetooth AVCTP" source "subsys/logging/Kconfig.template.log_config_inherit" +module = BT_AVRCP +module-str = "Bluetooth AVRCP" +source "subsys/logging/Kconfig.template.log_config_inherit" + module = BT_SDP module-str = "Bluetooth Service Discovery Protocol (SDP)" source "subsys/logging/Kconfig.template.log_config_inherit" diff --git a/subsys/bluetooth/host/classic/CMakeLists.txt b/subsys/bluetooth/host/classic/CMakeLists.txt index 6b0934f02d8..0f7f01d5c00 100644 --- a/subsys/bluetooth/host/classic/CMakeLists.txt +++ b/subsys/bluetooth/host/classic/CMakeLists.txt @@ -7,6 +7,7 @@ zephyr_library_link_libraries(subsys__bluetooth) zephyr_library_sources_ifdef(CONFIG_BT_A2DP a2dp.c a2dp_codec_sbc.c) zephyr_library_sources_ifdef(CONFIG_BT_AVDTP avdtp.c) +zephyr_library_sources_ifdef(CONFIG_BT_AVRCP avrcp.c) zephyr_library_sources_ifdef(CONFIG_BT_AVCTP avctp.c) zephyr_library_sources_ifdef(CONFIG_BT_RFCOMM rfcomm.c) diff --git a/subsys/bluetooth/host/classic/Kconfig b/subsys/bluetooth/host/classic/Kconfig index da2b4d7428f..5c084f03609 100644 --- a/subsys/bluetooth/host/classic/Kconfig +++ b/subsys/bluetooth/host/classic/Kconfig @@ -184,6 +184,27 @@ config BT_AVCTP help This option enables Bluetooth AVCTP support +config BT_AVRCP + bool "Bluetooth AVRCP Profile [EXPERIMENTAL]" + select BT_AVCTP + select EXPERIMENTAL + help + This option enables the AVRCP profile + +if BT_AVRCP + +config BT_AVRCP_TARGET + bool "Bluetooth AVRCP Profile Target Function" + help + This option enables the AVRCP profile target function + +config BT_AVRCP_CONTROLLER + bool "Bluetooth AVRCP Profile Controller Function" + help + This option enables the AVRCP profile controller function + +endif # BT_AVRCP + config BT_PAGE_TIMEOUT hex "Bluetooth Page Timeout" default 0x2000 diff --git a/subsys/bluetooth/host/classic/avrcp.c b/subsys/bluetooth/host/classic/avrcp.c new file mode 100644 index 00000000000..b6474b37867 --- /dev/null +++ b/subsys/bluetooth/host/classic/avrcp.c @@ -0,0 +1,158 @@ +/** @file + * @brief Audio Video Remote Control Profile + */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * Copyright (C) 2024 Xiaomi Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "host/hci_core.h" +#include "host/conn_internal.h" +#include "host/l2cap_internal.h" +#include "avctp_internal.h" +#include "avrcp_internal.h" + +#define LOG_LEVEL CONFIG_BT_AVRCP_LOG_LEVEL +#include +LOG_MODULE_REGISTER(bt_avrcp); + +struct bt_avrcp { + struct bt_avctp session; +}; + +#define AVRCP_AVCTP(_avctp) CONTAINER_OF(_avctp, struct bt_avrcp, session) + +static const struct bt_avrcp_cb *avrcp_cb; +static struct bt_avrcp avrcp_connection[CONFIG_BT_MAX_CONN]; + +static struct bt_avrcp *get_new_connection(struct bt_conn *conn) +{ + struct bt_avrcp *avrcp; + + if (!conn) { + LOG_ERR("Invalid Input (err: %d)", -EINVAL); + return NULL; + } + + avrcp = &avrcp_connection[bt_conn_index(conn)]; + memset(avrcp, 0, sizeof(struct bt_avrcp)); + return avrcp; +} + +/* The AVCTP L2CAP channel established */ +static void avrcp_connected(struct bt_avctp *session) +{ + struct bt_avrcp *avrcp = AVRCP_AVCTP(session); + + if ((avrcp_cb != NULL) && (avrcp_cb->connected != NULL)) { + avrcp_cb->connected(avrcp); + } +} + +/* The AVCTP L2CAP channel released */ +static void avrcp_disconnected(struct bt_avctp *session) +{ + struct bt_avrcp *avrcp = AVRCP_AVCTP(session); + + if ((avrcp_cb != NULL) && (avrcp_cb->disconnected != NULL)) { + avrcp_cb->disconnected(avrcp); + } +} + +static const struct bt_avctp_ops_cb avctp_ops = { + .connected = avrcp_connected, + .disconnected = avrcp_disconnected, +}; + +static int avrcp_accept(struct bt_conn *conn, struct bt_avctp **session) +{ + struct bt_avrcp *avrcp; + + avrcp = get_new_connection(conn); + if (!avrcp) { + return -ENOMEM; + } + + *session = &(avrcp->session); + avrcp->session.ops = &avctp_ops; + + LOG_DBG("session: %p", &(avrcp->session)); + + return 0; +} + +static struct bt_avctp_event_cb avctp_cb = { + .accept = avrcp_accept, +}; + +int bt_avrcp_init(void) +{ + int err; + + /* Register event handlers with AVCTP */ + err = bt_avctp_register(&avctp_cb); + if (err < 0) { + LOG_ERR("AVRCP registration failed"); + return err; + } + + LOG_DBG("AVRCP Initialized successfully."); + return 0; +} + +struct bt_avrcp *bt_avrcp_connect(struct bt_conn *conn) +{ + struct bt_avrcp *avrcp; + int err; + + avrcp = get_new_connection(conn); + if (!avrcp) { + LOG_ERR("Cannot allocate memory"); + return NULL; + } + + avrcp->session.ops = &avctp_ops; + err = bt_avctp_connect(conn, &(avrcp->session)); + if (err < 0) { + /* If error occurs, undo the saving and return the error */ + memset(avrcp, 0, sizeof(struct bt_avrcp)); + LOG_DBG("AVCTP Connect failed"); + return NULL; + } + + LOG_DBG("Connection request sent"); + return avrcp; +} + +int bt_avrcp_disconnect(struct bt_avrcp *avrcp) +{ + int err; + + err = bt_avctp_disconnect(&(avrcp->session)); + if (err < 0) { + LOG_DBG("AVCTP Disconnect failed"); + return err; + } + + return err; +} + +int bt_avrcp_register_cb(const struct bt_avrcp_cb *cb) +{ + avrcp_cb = cb; + return 0; +} diff --git a/subsys/bluetooth/host/classic/avrcp_internal.h b/subsys/bluetooth/host/classic/avrcp_internal.h new file mode 100644 index 00000000000..8e2454a6e23 --- /dev/null +++ b/subsys/bluetooth/host/classic/avrcp_internal.h @@ -0,0 +1,12 @@ +/** @file + * @brief Audio Video Remote Control Profile internal header. + */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * Copyright (C) 2024 Xiaomi Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +int bt_avrcp_init(void); diff --git a/subsys/bluetooth/host/classic/l2cap_br.c b/subsys/bluetooth/host/classic/l2cap_br.c index 76c33e02704..d1e558a7b3d 100644 --- a/subsys/bluetooth/host/classic/l2cap_br.c +++ b/subsys/bluetooth/host/classic/l2cap_br.c @@ -26,6 +26,7 @@ #include "avdtp_internal.h" #include "a2dp_internal.h" #include "avctp_internal.h" +#include "avrcp_internal.h" #include "rfcomm_internal.h" #include "sdp_internal.h" @@ -2084,4 +2085,8 @@ void bt_l2cap_br_init(void) if (IS_ENABLED(CONFIG_BT_A2DP)) { bt_a2dp_init(); } + + if (IS_ENABLED(CONFIG_BT_AVRCP)) { + bt_avrcp_init(); + } }