From 4719db7047ce1b91d5a0d5c895cd3c6035dd91d5 Mon Sep 17 00:00:00 2001 From: xiongweichao Date: Thu, 21 Apr 2022 11:45:55 +0800 Subject: [PATCH 01/10] add l2cap api --- components/bt/CMakeLists.txt | 2 + components/bt/common/btc/core/btc_task.c | 6 + .../bt/common/btc/include/btc/btc_task.h | 1 + components/bt/host/bluedroid/Kconfig.in | 8 + .../bt/host/bluedroid/api/esp_l2cap_bt_api.c | 131 ++ .../api/include/api/esp_l2cap_bt_api.h | 251 ++++ .../bluedroid/bta/include/bta/bta_jv_api.h | 12 +- .../bt/host/bluedroid/bta/jv/bta_jv_act.c | 42 +- .../bt/host/bluedroid/bta/jv/bta_jv_api.c | 16 +- .../bluedroid/bta/jv/include/bta_jv_int.h | 2 + .../btc/profile/std/include/btc_l2cap.h | 72 + .../btc/profile/std/l2cap/btc_l2cap.c | 1233 +++++++++++++++++ .../bluedroid/btc/profile/std/spp/btc_spp.c | 2 +- .../include/common/bluedroid_user_config.h | 7 + .../common/include/common/bt_target.h | 90 +- 15 files changed, 1815 insertions(+), 60 deletions(-) create mode 100644 components/bt/host/bluedroid/api/esp_l2cap_bt_api.c create mode 100644 components/bt/host/bluedroid/api/include/api/esp_l2cap_bt_api.h create mode 100644 components/bt/host/bluedroid/btc/profile/std/include/btc_l2cap.h create mode 100644 components/bt/host/bluedroid/btc/profile/std/l2cap/btc_l2cap.c diff --git a/components/bt/CMakeLists.txt b/components/bt/CMakeLists.txt index 57bba0b881..34ea3ce564 100644 --- a/components/bt/CMakeLists.txt +++ b/components/bt/CMakeLists.txt @@ -113,6 +113,7 @@ if(CONFIG_BT_ENABLED) "host/bluedroid/api/esp_hf_ag_api.c" "host/bluedroid/api/esp_hf_client_api.c" "host/bluedroid/api/esp_spp_api.c" + "host/bluedroid/api/esp_l2cap_bt_api.c" "host/bluedroid/bta/ar/bta_ar.c" "host/bluedroid/bta/av/bta_av_aact.c" "host/bluedroid/bta/av/bta_av_act.c" @@ -214,6 +215,7 @@ if(CONFIG_BT_ENABLED) "host/bluedroid/btc/profile/std/gatt/btc_gattc.c" "host/bluedroid/btc/profile/std/gatt/btc_gatts.c" "host/bluedroid/btc/profile/std/spp/btc_spp.c" + "host/bluedroid/btc/profile/std/l2cap/btc_l2cap.c" "host/bluedroid/device/bdaddr.c" "host/bluedroid/device/controller.c" "host/bluedroid/device/interop.c" diff --git a/components/bt/common/btc/core/btc_task.c b/components/bt/common/btc/core/btc_task.c index 8df41d14a7..73063922a8 100644 --- a/components/bt/common/btc/core/btc_task.c +++ b/components/bt/common/btc/core/btc_task.c @@ -39,6 +39,9 @@ #if (BTC_SPP_INCLUDED == TRUE) #include "btc_spp.h" #endif /* #if (BTC_SPP_INCLUDED == TRUE) */ +#if (BTC_L2CAP_INCLUDED == TRUE) +#include "btc_l2cap.h" +#endif /* #if (BTC_L2CAP_INCLUDED == TRUE) */ #if BTC_HF_INCLUDED #include "btc_hf_ag.h" #endif/* #if BTC_HF_INCLUDED */ @@ -112,6 +115,9 @@ static const btc_func_t profile_tab[BTC_PID_NUM] = { #if (BTC_SPP_INCLUDED == TRUE) [BTC_PID_SPP] = {btc_spp_call_handler, btc_spp_cb_handler }, #endif /* #if (BTC_SPP_INCLUDED == TRUE) */ +#if (BTC_L2CAP_INCLUDED == TRUE) + [BTC_PID_L2CAP] = {btc_l2cap_call_handler, btc_l2cap_cb_handler }, +#endif /* #if (BTC_L2CAP_INCLUDED == TRUE) */ #if BTC_HF_INCLUDED [BTC_PID_HF] = {btc_hf_call_handler, btc_hf_cb_handler}, #endif /* #if BTC_HF_INCLUDED */ diff --git a/components/bt/common/btc/include/btc/btc_task.h b/components/bt/common/btc/include/btc/btc_task.h index 957d4e1a66..130208a81e 100644 --- a/components/bt/common/btc/include/btc/btc_task.h +++ b/components/bt/common/btc/include/btc/btc_task.h @@ -59,6 +59,7 @@ typedef enum { BTC_PID_SPP, BTC_PID_HD, BTC_PID_HH, + BTC_PID_L2CAP, #if (BTC_HF_INCLUDED == TRUE) BTC_PID_HF, #endif /* BTC_HF_INCLUDED */ diff --git a/components/bt/host/bluedroid/Kconfig.in b/components/bt/host/bluedroid/Kconfig.in index a1ed479b7f..c7e1f6f2c2 100644 --- a/components/bt/host/bluedroid/Kconfig.in +++ b/components/bt/host/bluedroid/Kconfig.in @@ -61,6 +61,14 @@ config BT_SPP_ENABLED help This enables the Serial Port Profile +config BT_L2CAP_ENABLED + bool "BT L2CAP" + depends on BT_CLASSIC_ENABLED + default n + help + This enables the Logical Link Control and Adaptation Layer Protocol. + Only supported classic bluetooth. + config BT_HFP_ENABLE bool "Hands Free/Handset Profile" depends on BT_CLASSIC_ENABLED diff --git a/components/bt/host/bluedroid/api/esp_l2cap_bt_api.c b/components/bt/host/bluedroid/api/esp_l2cap_bt_api.c new file mode 100644 index 0000000000..afc6c89a66 --- /dev/null +++ b/components/bt/host/bluedroid/api/esp_l2cap_bt_api.c @@ -0,0 +1,131 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "esp_bt_main.h" +#include "btc/btc_manage.h" + +#include "btc_l2cap.h" +#include "esp_l2cap_bt_api.h" +#include "common/bt_target.h" + +#if (defined BTC_L2CAP_INCLUDED && BTC_L2CAP_INCLUDED == TRUE) + +esp_err_t esp_bt_l2cap_register_callback(esp_bt_l2cap_cb_t callback) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (callback == NULL) { + return ESP_FAIL; + } + + btc_profile_cb_set(BTC_PID_L2CAP, callback); + return ESP_OK; +} + +esp_err_t esp_bt_l2cap_init(void) +{ + btc_msg_t msg; + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_L2CAP; + msg.act = BTC_L2CAP_ACT_INIT; + + return (btc_transfer_context(&msg, NULL, 0, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_bt_l2cap_deinit(void) +{ + btc_msg_t msg; + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_L2CAP; + msg.act = BTC_L2CAP_ACT_UNINIT; + + return (btc_transfer_context(&msg, NULL, 0, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_bt_l2cap_connect(esp_bt_l2cap_cntl_flags_t cntl_flag, uint16_t remote_psm, esp_bd_addr_t peer_bd_addr) +{ + btc_msg_t msg; + btc_l2cap_args_t arg; + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_L2CAP; + msg.act = BTC_L2CAP_ACT_CONNECT; + + arg.connect.sec_mask = (cntl_flag & 0xffff); + arg.connect.remote_psm = remote_psm; + memcpy(arg.connect.peer_bd_addr, peer_bd_addr, ESP_BD_ADDR_LEN); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_l2cap_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_bt_l2cap_start_srv(esp_bt_l2cap_cntl_flags_t cntl_flag, uint16_t local_psm) +{ + btc_msg_t msg; + btc_l2cap_args_t arg; + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_L2CAP; + msg.act = BTC_L2CAP_ACT_START_SRV; + + arg.start_srv.sec_mask = (cntl_flag & 0xffff); + arg.start_srv.local_psm = local_psm; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_l2cap_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_bt_l2cap_stop_all_srv(void) +{ + btc_msg_t msg; + btc_l2cap_args_t arg; + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_L2CAP; + msg.act = BTC_L2CAP_ACT_STOP_SRV; + + arg.stop_srv.psm = BTC_L2CAP_INVALID_PSM; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_l2cap_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_bt_l2cap_stop_srv(uint16_t local_psm) +{ + btc_msg_t msg; + btc_l2cap_args_t arg; + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_L2CAP; + msg.act = BTC_L2CAP_ACT_STOP_SRV; + + arg.stop_srv.psm = local_psm; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_l2cap_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_bt_l2cap_vfs_register(void) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + return btc_l2cap_vfs_register(); +} + +esp_err_t esp_bt_l2cap_vfs_unregister(void) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + return btc_l2cap_vfs_unregister(); +} + +#endif ///defined BTC_L2CAP_INCLUDED && BTC_L2CAP_INCLUDED == TRUE diff --git a/components/bt/host/bluedroid/api/include/api/esp_l2cap_bt_api.h b/components/bt/host/bluedroid/api/include/api/esp_l2cap_bt_api.h new file mode 100644 index 0000000000..1a3ee98bf7 --- /dev/null +++ b/components/bt/host/bluedroid/api/include/api/esp_l2cap_bt_api.h @@ -0,0 +1,251 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ESP_L2CAP_BT_API_H__ +#define __ESP_L2CAP_BT_API_H__ + +#include "esp_err.h" +#include "esp_bt_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief L2CAP operation success and failure codes + */ +typedef enum { + ESP_BT_L2CAP_SUCCESS = 0, /*!< Successful operation. */ + ESP_BT_L2CAP_FAILURE, /*!< Generic failure. */ + ESP_BT_L2CAP_BUSY, /*!< Temporarily can not handle this request. */ + ESP_BT_L2CAP_NO_RESOURCE, /*!< No more resource */ + ESP_BT_L2CAP_NEED_INIT, /*!< L2CAP module shall init first */ + ESP_BT_L2CAP_NEED_DEINIT, /*!< L2CAP module shall deinit first */ + ESP_BT_L2CAP_NO_CONNECTION, /*!< Connection may have been closed */ + ESP_BT_L2CAP_NO_SERVER, /*!< No server */ +} esp_bt_l2cap_status_t; + +/** + * @brief Security Setting Mask. Use these three mask mode: + * 1. ESP_BT_L2CAP_SEC_NONE + * 2. ESP_BT_L2CAP_SEC_AUTHENTICATE + * 3. (ESP_BT_L2CAP_SEC_ENCRYPT|ESP_BT_L2CAP_SEC_AUTHENTICATE) + */ +#define ESP_BT_L2CAP_SEC_NONE 0x0000 /*!< No security */ +#define ESP_BT_L2CAP_SEC_AUTHORIZE 0x0001 /*!< Authorization required */ +#define ESP_BT_L2CAP_SEC_AUTHENTICATE 0x0012 /*!< Authentication required */ +#define ESP_BT_L2CAP_SEC_ENCRYPT 0x0024 /*!< Encryption required */ +typedef uint32_t esp_bt_l2cap_cntl_flags_t; + +/** + * @brief L2CAP callback function events + */ +typedef enum { + ESP_BT_L2CAP_INIT_EVT = 0, /*!< When L2CAP is inited, the event comes */ + ESP_BT_L2CAP_UNINIT_EVT = 1, /*!< When L2CAP is uninited, the event comes */ + ESP_BT_L2CAP_OPEN_EVT = 16, /*!< When L2CAP Client connection open, the event comes */ + ESP_BT_L2CAP_CLOSE_EVT = 17, /*!< When L2CAP connection closed, the event comes */ + ESP_BT_L2CAP_START_EVT = 18, /*!< When L2CAP server started, the event comes */ + ESP_BT_L2CAP_CL_INIT_EVT = 19, /*!< When L2CAP client initiated a connection, the event comes */ + ESP_BT_L2CAP_SRV_STOP_EVT = 36, /*!< When L2CAP server stopped, the event comes */ +} esp_bt_l2cap_cb_event_t; + +/** + * @brief L2CAP callback parameters union + */ +typedef union { + /** + * @brief ESP_BT_L2CAP_INIT_EVT + */ + struct l2cap_init_evt_param { + esp_bt_l2cap_status_t status; /*!< status */ + } init; /*!< L2CAP callback param of ESP_BT_L2CAP_INIT_EVT */ + + /** + * @brief ESP_BT_L2CAP_UNINIT_EVT + */ + struct l2cap_uninit_evt_param { + esp_bt_l2cap_status_t status; /*!< status */ + } uninit; /*!< L2CAP callback param of ESP_BT_L2CAP_UNINIT_EVT */ + + /** + * @brief ESP_BT_L2CAP_OPEN_EVT + */ + struct l2cap_open_evt_param { + esp_bt_l2cap_status_t status; /*!< status */ + uint32_t handle; /*!< The connection handle */ + int fd; /*!< File descriptor */ + esp_bd_addr_t rem_bda; /*!< The peer address */ + int32_t tx_mtu; /*!< The transmit MTU */ + } open; /*!< L2CAP callback param of ESP_BT_L2CAP_OPEN_EVT */ + + /** + * @brief ESP_BT_L2CAP_CLOSE_EVT + */ + struct l2cap_close_evt_param { + esp_bt_l2cap_status_t status; /*!< status */ + uint32_t handle; /*!< The connection handle */ + bool async; /*!< FALSE, if local initiates disconnect */ + } close; /*!< L2CAP callback param of ESP_BT_L2CAP_CLOSE_EVT */ + + /** + * @brief ESP_BT_L2CAP_START_EVT + */ + struct l2cap_start_evt_param { + esp_bt_l2cap_status_t status; /*!< status */ + uint32_t handle; /*!< The connection handle */ + uint8_t sec_id; /*!< security ID used by this server */ + } start; /*!< L2CAP callback param of ESP_BT_L2CAP_START_EVT */ + + /** + * @brief ESP_BT_L2CAP_CL_INIT_EVT + */ + struct l2cap_cl_init_evt_param { + esp_bt_l2cap_status_t status; /*!< status */ + uint32_t handle; /*!< The connection handle */ + uint8_t sec_id; /*!< security ID used by this server */ + } cl_init; /*!< L2CAP callback param of ESP_BT_L2CAP_CL_INIT_EVT */ + + /** + * @brief ESP_BT_L2CAP_SRV_STOP_EVT + */ + struct l2cap_srv_stop_evt_param { + esp_bt_l2cap_status_t status; /*!< status */ + uint8_t psm; /*!< local psm */ + } srv_stop; /*!< L2CAP callback param of ESP_BT_L2CAP_SRV_STOP_EVT */ + +} esp_bt_l2cap_cb_param_t; + +/** + * @brief L2CAP callback function type. + * + * @param event: Event type + * @param param: Point to callback parameter, currently is union type + */ +typedef void (* esp_bt_l2cap_cb_t)(esp_bt_l2cap_cb_event_t event, esp_bt_l2cap_cb_param_t *param); + +/** + * @brief This function is called to init callbacks with L2CAP module. + * + * @param[in] callback: pointer to the init callback function. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_l2cap_register_callback(esp_bt_l2cap_cb_t callback); + +/** + * @brief This function is called to init L2CAP module. + * When the operation is completed, the callback function will be called with ESP_BT_L2CAP_INIT_EVT. + * This function should be called after esp_bluedroid_enable() completes successfully. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_l2cap_init(void); + +/** + * @brief This function is called to uninit l2cap module. + * The operation will close all active L2CAP connection first, then the callback function will be called + * with ESP_BT_L2CAP_CLOSE_EVT, and the number of ESP_BT_L2CAP_CLOSE_EVT is equal to the number of connection. + * When the operation is completed, the callback function will be called with ESP_BT_L2CAP_UNINIT_EVT. + * This function should be called after esp_bt_l2cap_init() completes successfully. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_l2cap_deinit(void); + +/** + * @brief This function makes an L2CAP connection to a remote BD Address. + * When the connection is initiated or failed to initiate, the callback is called with ESP_BT_L2CAP_CL_INIT_EVT. + * When the connection is established or failed, the callback is called with ESP_BT_L2CAP_OPEN_EVT. + * This funciton must be called after esp_bt_l2cap_init() successful and before esp_bt_l2cap_deinit(). + * + * @param[in] cntl_flag: Lower 16-bit security settings mask. + * @param[in] remote_psm: Remote device bluetooth Profile PSM. + * @param[in] peer_bd_addr: Remote device bluetooth device address. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_l2cap_connect(esp_bt_l2cap_cntl_flags_t cntl_flag, uint16_t remote_psm, esp_bd_addr_t peer_bd_addr); + +/** + * @brief This function create a L2CAP server and starts listening for an + * L2CAP connection request from a remote Bluetooth device. + * When the server is started successfully, the callback is called with ESP_BT_L2CAP_START_EVT. + * When the connection is established, the callback is called with ESP_BT_L2CAP_OPEN_EVT. + * This funciton must be called after esp_bt_l2cap_init() successful and before esp_bt_l2cap_deinit(). + * + * @param[in] cntl_flag: Lower 16-bit security settings mask. + * @param[in] local_psm: Dynamic PSM. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_l2cap_start_srv(esp_bt_l2cap_cntl_flags_t cntl_flag, uint16_t local_psm); + +/** + * @brief This function stops all L2CAP servers. + * The operation will close all active L2CAP connection first, then the callback function will be called + * with ESP_BT_L2CAP_CLOSE_EVT, and the number of ESP_BT_L2CAP_CLOSE_EVT is equal to the number of connection. + * When the operation is completed, the callback is called with ESP_BT_L2CAP_SRV_STOP_EVT. + * This funciton must be called after esp_bt_l2cap_init() successful and before esp_bt_l2cap_deinit(). + * + * @return + * - ESP_OK: success + * - other: failed + */ + +esp_err_t esp_bt_l2cap_stop_all_srv(void); + +/** + * @brief This function stops a specific L2CAP server. + * The operation will close all active L2CAP connection first on the specific L2CAP server, then the callback function will + * be called with ESP_BT_L2CAP_CLOSE_EVT, and the number of ESP_BT_L2CAP_CLOSE_EVT is equal to the number of connection. + * When the operation is completed, the callback is called with ESP_BT_L2CAP_SRV_STOP_EVT. + * This funciton must be called after esp_bt_l2cap_init() successful and before esp_bt_l2cap_deinit(). + * + * @param[in] local_psm: Dynamic PSM. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_l2cap_stop_srv(uint16_t local_psm); + +/** + * @brief This function is used to register VFS. + * Only supports write, read and close. + * This funciton must be called after esp_bt_l2cap_init() successful and before esp_bt_l2cap_deinit(). + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_l2cap_vfs_register(void); + +/** + * @brief This function is used to unregister VFS. + * This funciton must be called after esp_bt_l2cap_init() successful and before esp_bt_l2cap_deinit(). + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_l2cap_vfs_unregister(void); + +#ifdef __cplusplus +} +#endif + +#endif ///__ESP_L2CAP_BT_API_H__ diff --git a/components/bt/host/bluedroid/bta/include/bta/bta_jv_api.h b/components/bt/host/bluedroid/bta/include/bta/bta_jv_api.h index e2d5dd53c4..bdc9edbf5c 100644 --- a/components/bt/host/bluedroid/bta/include/bta/bta_jv_api.h +++ b/components/bt/host/bluedroid/bta/include/bta/bta_jv_api.h @@ -42,6 +42,7 @@ #define BTA_JV_BUSY 2 /* Temporarily can not handle this request. */ #define BTA_JV_NO_DATA 3 /* no data. */ #define BTA_JV_NO_RESOURCE 4 /* No more set pm control block */ +#define BTA_JV_ALREADY_DONE 5 /* already done, repeat the operation */ typedef UINT8 tBTA_JV_STATUS; #define BTA_JV_INTERNAL_ERR (-1) /* internal error. */ @@ -198,6 +199,7 @@ typedef struct { tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */ UINT32 handle; /* The connection handle */ BOOLEAN async; /* FALSE, if local initiates disconnect */ + void * user_data; /* piggyback caller's private data */ } tBTA_JV_L2CAP_CLOSE; /* data associated with BTA_JV_L2CAP_START_EVT */ @@ -428,7 +430,7 @@ typedef void *(tBTA_JV_RFCOMM_CBACK)(tBTA_JV_EVT event, tBTA_JV *p_data, void *u #if BTA_JV_L2CAP_INCLUDED /* JAVA L2CAP interface callback */ -typedef void (tBTA_JV_L2CAP_CBACK)(tBTA_JV_EVT event, tBTA_JV *p_data, void *user_Data); +typedef void *(tBTA_JV_L2CAP_CBACK)(tBTA_JV_EVT event, tBTA_JV *p_data, void *user_Data); #endif /* BTA_JV_L2CAP_INCLUDED */ /* JV configuration structure */ @@ -636,7 +638,7 @@ extern tBTA_JV_STATUS BTA_JvL2capConnect(tBTA_SEC sec_mask, tBTA_JV_ROLE role, ** BTA_JV_FAILURE, otherwise. ** *******************************************************************************/ -extern tBTA_JV_STATUS BTA_JvL2capClose(UINT32 handle); +extern tBTA_JV_STATUS BTA_JvL2capClose(UINT32 handle, tBTA_JV_L2CAP_CBACK *p_cback, void *user_data); /******************************************************************************* ** @@ -724,12 +726,10 @@ extern tBTA_JV_STATUS BTA_JvL2capStopServer(UINT16 local_psm, void *user_data); ** When the operation is complete, tBTA_JV_L2CAP_CBACK is ** called with BTA_JV_L2CAP_READ_EVT. ** -** Returns BTA_JV_SUCCESS, if the request is being processed. -** BTA_JV_FAILURE, otherwise. +** Returns Length of read data. ** *******************************************************************************/ -extern tBTA_JV_STATUS BTA_JvL2capRead(UINT32 handle, UINT32 req_id, - UINT8 *p_data, UINT16 len); +extern int BTA_JvL2capRead(UINT32 handle, UINT32 req_id, UINT8 *p_data, UINT16 len); /******************************************************************************* ** diff --git a/components/bt/host/bluedroid/bta/jv/bta_jv_act.c b/components/bt/host/bluedroid/bta/jv/bta_jv_act.c index 5f52176270..e3671ec6cb 100644 --- a/components/bt/host/bluedroid/bta/jv/bta_jv_act.c +++ b/components/bt/host/bluedroid/bta/jv/bta_jv_act.c @@ -865,10 +865,25 @@ void bta_jv_free_scn(tBTA_JV_MSG *p_data) bta_jv_cb.scn[scn - 1] = FALSE; BTM_FreeSCN(scn); } + if (fc->user_data) { + user_data = (tBTA_JV_FREE_SCN_USER_DATA *)fc->user_data; + evt_data.server_status = user_data->server_status; + if (user_data->server_status == BTA_JV_SERVER_RUNNING && find_rfc_pcb((void *)user_data->slot_id, &p_cb, &p_pcb)) { + /* if call bta_jv_rfcomm_stop_server successfully, find_rfc_pcb shall return false */ + evt_data.status = BTA_JV_FAILURE; + } + + if (fc->p_cback) { + fc->p_cback(BTA_JV_FREE_SCN_EVT, (tBTA_JV *)&evt_data, (void *)user_data); + } + } break; } case BTA_JV_CONN_TYPE_L2CAP: bta_jv_set_free_psm(scn); + if (fc->p_cback) { + fc->p_cback(BTA_JV_FREE_SCN_EVT, (tBTA_JV *)&evt_data, (void *)user_data); + } break; case BTA_JV_CONN_TYPE_L2CAP_LE: // TODO: Not yet implemented... @@ -876,20 +891,6 @@ void bta_jv_free_scn(tBTA_JV_MSG *p_data) default: break; } - - if (fc->user_data) - { - user_data = (tBTA_JV_FREE_SCN_USER_DATA *)fc->user_data; - evt_data.server_status = user_data->server_status; - if (user_data->server_status == BTA_JV_SERVER_RUNNING && find_rfc_pcb((void *)user_data->slot_id, &p_cb, &p_pcb)) { - /* if call bta_jv_rfcomm_stop_server successfully, find_rfc_pcb shall return false */ - evt_data.status = BTA_JV_FAILURE; - } - - if (fc->p_cback) { - fc->p_cback(BTA_JV_FREE_SCN_EVT, (tBTA_JV *)&evt_data, (void *)user_data); - } - } } static inline tBT_UUID shorten_sdp_uuid(const tBT_UUID *u) { @@ -1271,7 +1272,7 @@ void bta_jv_l2cap_connect(tBTA_JV_MSG *p_data) if (cc->has_cfg == TRUE) { cfg = cc->cfg; if (cfg.fcr_present && cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) { - chan_mode_mask = GAP_FCR_CHAN_OPT_ERTM; + chan_mode_mask |= GAP_FCR_CHAN_OPT_ERTM; } } @@ -1331,15 +1332,14 @@ void bta_jv_l2cap_close(tBTA_JV_MSG *p_data) { tBTA_JV_L2CAP_CLOSE evt_data; tBTA_JV_API_L2CAP_CLOSE *cc = &(p_data->l2cap_close); - tBTA_JV_L2CAP_CBACK *p_cback = cc->p_cb->p_cback; - void *user_data = cc->p_cb->user_data; + void *user_data = cc->user_data; evt_data.handle = cc->handle; evt_data.status = bta_jv_free_l2c_cb(cc->p_cb); evt_data.async = FALSE; - if (p_cback) { - p_cback(BTA_JV_L2CAP_CLOSE_EVT, (tBTA_JV *)&evt_data, user_data); + if (cc->p_cback) { + cc->p_cback(BTA_JV_L2CAP_CLOSE_EVT, (tBTA_JV *)&evt_data, user_data); } } @@ -1430,7 +1430,7 @@ void bta_jv_l2cap_start_server(tBTA_JV_MSG *p_data) if (ls->has_cfg == TRUE) { cfg = ls->cfg; if (cfg.fcr_present && cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) { - chan_mode_mask = GAP_FCR_CHAN_OPT_ERTM; + chan_mode_mask |= GAP_FCR_CHAN_OPT_ERTM; } } @@ -2854,7 +2854,7 @@ void bta_jv_l2cap_connect_le(tBTA_JV_MSG *p_data) id = t->id; t->init_called = FALSE; - if (L2CA_ConnectFixedChnl(t->chan, t->remote_addr, BLE_ADDR_UNKNOWN_TYPE)) { + if (L2CA_ConnectFixedChnl(t->chan, t->remote_addr, BLE_ADDR_UNKNOWN_TYPE, FALSE)) { evt.l2c_cl_init.status = BTA_JV_SUCCESS; evt.l2c_cl_init.handle = id; diff --git a/components/bt/host/bluedroid/bta/jv/bta_jv_api.c b/components/bt/host/bluedroid/bta/jv/bta_jv_api.c index 06a5ef740b..756eb17457 100644 --- a/components/bt/host/bluedroid/bta/jv/bta_jv_api.c +++ b/components/bt/host/bluedroid/bta/jv/bta_jv_api.c @@ -98,7 +98,7 @@ tBTA_JV_STATUS BTA_JvEnable(tBTA_JV_DM_CBACK *p_cback) APPL_TRACE_ERROR("No p_cback."); } else { APPL_TRACE_WARNING("No need to Init again."); - // status = BTA_JV_SUCCESS; + status = BTA_JV_ALREADY_DONE; } return (status); } @@ -467,7 +467,7 @@ tBTA_JV_STATUS BTA_JvL2capConnect(tBTA_SEC sec_mask, tBTA_JV_ROLE role, ** BTA_JV_FAILURE, otherwise. ** *******************************************************************************/ -tBTA_JV_STATUS BTA_JvL2capClose(UINT32 handle) +tBTA_JV_STATUS BTA_JvL2capClose(UINT32 handle, tBTA_JV_L2CAP_CBACK *p_cback, void *user_data) { tBTA_JV_STATUS status = BTA_JV_FAILURE; tBTA_JV_API_L2CAP_CLOSE *p_msg; @@ -479,6 +479,8 @@ tBTA_JV_STATUS BTA_JvL2capClose(UINT32 handle) p_msg->hdr.event = BTA_JV_API_L2CAP_CLOSE_EVT; p_msg->handle = handle; p_msg->p_cb = &bta_jv_cb.l2c_cb[handle]; + p_msg->p_cback = p_cback; + p_msg->user_data = user_data; bta_sys_sendmsg(p_msg); status = BTA_JV_SUCCESS; } @@ -684,20 +686,16 @@ on ** When the operation is complete, tBTA_JV_L2CAP_CBACK is ** called with BTA_JV_L2CAP_READ_EVT. ** -** Returns BTA_JV_SUCCESS, if the request is being processed. -** BTA_JV_FAILURE, otherwise. +** Returns Length of read data. ** *******************************************************************************/ -tBTA_JV_STATUS BTA_JvL2capRead(UINT32 handle, UINT32 req_id, UINT8 *p_data, UINT16 len) +int BTA_JvL2capRead(UINT32 handle, UINT32 req_id, UINT8 *p_data, UINT16 len) { - tBTA_JV_STATUS status = BTA_JV_FAILURE; tBTA_JV_L2CAP_READ evt_data; APPL_TRACE_API( "%s", __func__); - if (handle < BTA_JV_MAX_L2C_CONN && bta_jv_cb.l2c_cb[handle].p_cback) { - status = BTA_JV_SUCCESS; evt_data.status = BTA_JV_FAILURE; evt_data.handle = handle; evt_data.req_id = req_id; @@ -711,7 +709,7 @@ tBTA_JV_STATUS BTA_JvL2capRead(UINT32 handle, UINT32 req_id, UINT8 *p_data, UINT BTA_JV_L2CAP_READ_EVT, (tBTA_JV *)&evt_data, bta_jv_cb.l2c_cb[handle].user_data); } - return (status); + return (evt_data.len); } /******************************************************************************* diff --git a/components/bt/host/bluedroid/bta/jv/include/bta_jv_int.h b/components/bt/host/bluedroid/bta/jv/include/bta_jv_int.h index 5a0bf66faf..171ba72206 100644 --- a/components/bt/host/bluedroid/bta/jv/include/bta_jv_int.h +++ b/components/bt/host/bluedroid/bta/jv/include/bta_jv_int.h @@ -213,6 +213,8 @@ typedef struct { BT_HDR hdr; UINT32 handle; tBTA_JV_L2C_CB *p_cb; + tBTA_JV_L2CAP_CBACK *p_cback; + void *user_data; } tBTA_JV_API_L2CAP_CLOSE; /* data type for BTA_JV_API_L2CAP_READ_EVT */ diff --git a/components/bt/host/bluedroid/btc/profile/std/include/btc_l2cap.h b/components/bt/host/bluedroid/btc/profile/std/include/btc_l2cap.h new file mode 100644 index 0000000000..83708dbb03 --- /dev/null +++ b/components/bt/host/bluedroid/btc/profile/std/include/btc_l2cap.h @@ -0,0 +1,72 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __BTC_L2CAP_H__ +#define __BTC_L2CAP_H__ + +#include "btc/btc_task.h" +#include "esp_bt_defs.h" +#include "esp_l2cap_bt_api.h" +#include "common/bt_target.h" +#include "bta/bta_jv_api.h" + +#if (defined BTC_L2CAP_INCLUDED && BTC_L2CAP_INCLUDED == TRUE) + +#define BTC_L2CAP_INVALID_PSM 0x00 + +typedef enum { + BTC_L2CAP_ACT_INIT = 0, + BTC_L2CAP_ACT_UNINIT, + BTC_L2CAP_ACT_START_DISCOVERY, + BTC_L2CAP_ACT_CONNECT, + BTC_L2CAP_ACT_START_SRV, + BTC_L2CAP_ACT_STOP_SRV, +} btc_l2cap_act_t; + +/* btc_l2cap_args_t */ +typedef union { + //BTC_L2CAP_ACT_INIT + struct l2cap_init_arg { + } init; + + //BTC_L2CAP_ACT_UNINIT + struct l2cap_uninit_arg { + } uninit; + + //BTC_L2CAP_ACT_START_DISCOVERY + struct l2cap_start_discovery_arg { + BD_ADDR bd_addr; + UINT16 num_uuid; + tSDP_UUID *p_uuid_list; + } start_discovery; + + //BTC_L2CAP_ACT_CONNECT + struct l2cap_connect_arg { + UINT16 sec_mask; + UINT16 remote_psm; + esp_bd_addr_t peer_bd_addr; + } connect; + + //BTC_L2CAP_ACT_START_SRV + struct l2cap_start_srv_arg { + UINT16 sec_mask; + UINT16 local_psm; + } start_srv; + + //BTC_L2CAP_ACT_STOP_SRV + struct l2cap_stop_srv_arg { + UINT16 psm; + } stop_srv; + +} btc_l2cap_args_t; + +void btc_l2cap_call_handler(btc_msg_t *msg); +void btc_l2cap_cb_handler(btc_msg_t *msg); +esp_err_t btc_l2cap_vfs_register(void); +esp_err_t btc_l2cap_vfs_unregister(void); + +#endif ///defined BTC_L2CAP_INCLUDED && BTC_L2CAP_INCLUDED == TRUE +#endif ///__BTC_L2CAP_H__ diff --git a/components/bt/host/bluedroid/btc/profile/std/l2cap/btc_l2cap.c b/components/bt/host/bluedroid/btc/profile/std/l2cap/btc_l2cap.c new file mode 100644 index 0000000000..9ddb7a7d46 --- /dev/null +++ b/components/bt/host/bluedroid/btc/profile/std/l2cap/btc_l2cap.c @@ -0,0 +1,1233 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "btc_l2cap.h" +#include "stack/l2c_api.h" +#include "btc/btc_manage.h" +#include "btc/btc_task.h" +#include "bta/bta_jv_api.h" +#include "common/bt_trace.h" +#include "osi/allocator.h" +#include "esp_l2cap_bt_api.h" +#include "osi/list.h" +#include "freertos/ringbuf.h" +#include "osi/mutex.h" +#include "osi/alarm.h" +#include +#include +#include +#include "esp_vfs.h" +#include "esp_vfs_dev.h" +#include "stack/port_api.h" +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" +#include "btc_sdp.h" + +#include "btc/btc_task.h" +#include "stack/btu.h" + +#if (defined BTC_L2CAP_INCLUDED && BTC_L2CAP_INCLUDED == TRUE) + +#define SLOT_WRITE_BIT(i) (1UL << (i - 1)) +#define SLOT_CLOSE_BIT(i) (1UL << (i + BTA_JV_MAX_L2C_CONN - 1)) +#define VFS_WRITE_TIMEOUT (40 * 1000) +#define SLOT_TX_QUEUE_SIZE 10 +#define SLOT_TX_QUEUE_LOW_WM 4 +#define SLOT_TX_DATA_HIGH_WM (SLOT_TX_QUEUE_SIZE * BTA_JV_DEF_RFC_MTU) +#define VFS_CLOSE_TIMEOUT (20 * 1000) +#define BTC_L2CAP_ROLE_MASTER 0 +#define BTC_L2CAP_ROLE_SLAVE 1 + +typedef struct { + bool peer_fc; /* true if flow control is set based on peer's request */ + bool user_fc; /* true if flow control is set based on user's request */ + fixed_queue_t *queue; /* Queue of buffers waiting to be sent */ + uint32_t data_size; /* Number of data bytes in the queue */ +} slot_data_t; + +typedef struct { + bool connected; + bool is_server; + bool fix_chan; // unused + uint16_t psm; + uint8_t serial; + uint8_t max_session; + uint32_t id; + uint32_t handle; + int fd; + int tx_mtu; + uint8_t *write_data; + osi_alarm_t *close_alarm; + uint8_t role; + uint16_t security; + esp_bd_addr_t addr; + slot_data_t rx; + slot_data_t tx; + uint8_t service_uuid[16]; +} l2cap_slot_t; + + +typedef struct { + l2cap_slot_t *l2cap_slots[BTA_JV_MAX_L2C_CONN + 1]; + uint32_t l2cap_slot_id; + osi_mutex_t l2cap_slot_mutex; + EventGroupHandle_t tx_event_group; + esp_vfs_id_t l2cap_vfs_id; +} l2cap_local_param_t; + +static l2cap_local_param_t l2cap_local_param; + +/* L2CAP default options for OBEX connections */ +static const tL2CAP_FCR_OPTS obex_l2c_fcr_opts_def = +{ + L2CAP_FCR_ERTM_MODE, /* Mandatory for OBEX over l2cap */ + OBX_FCR_OPT_TX_WINDOW_SIZE_BR_EDR,/* Tx window size */ + OBX_FCR_OPT_MAX_TX_B4_DISCNT, /* Maximum transmissions before disconnecting */ + OBX_FCR_OPT_RETX_TOUT, /* Retransmission timeout (2 secs) */ + OBX_FCR_OPT_MONITOR_TOUT, /* Monitor timeout (12 secs) */ + OBX_FCR_OPT_MAX_PDU_SIZE /* MPS segment size */ +}; +static const tL2CAP_ERTM_INFO obex_l2c_etm_opt = +{ + L2CAP_FCR_ERTM_MODE, /* Mandatory for OBEX over l2cap */ + L2CAP_FCR_CHAN_OPT_ERTM, /* Mandatory for OBEX over l2cap */ + OBX_USER_RX_POOL_ID, + OBX_USER_TX_POOL_ID, + OBX_FCR_RX_POOL_ID, + OBX_FCR_TX_POOL_ID +}; + + +#if L2CAP_DYNAMIC_MEMORY == FALSE +#define is_l2cap_init() (l2cap_local_param.l2cap_slot_mutex != NULL) +#else +#define is_l2cap_init() (&l2cap_local_param != NULL && l2cap_local_param.l2cap_slot_mutex != NULL) +#endif + +static void l2cap_osi_free(void *p) +{ + osi_free(p); +} + +static l2cap_slot_t *l2cap_find_slot_by_handle(uint32_t handle) +{ + l2cap_slot_t *slot = NULL; + + for (size_t i = 1; i <= BTA_JV_MAX_L2C_CONN; i++) { + slot = l2cap_local_param.l2cap_slots[i]; + if (slot != NULL && slot->handle == handle) { + return slot; + } + } + return NULL; +} + +static l2cap_slot_t *l2cap_find_slot_by_id(uint32_t id) +{ + l2cap_slot_t *slot = NULL; + + for (size_t i = 1; i <= BTA_JV_MAX_L2C_CONN; i++) { + slot = l2cap_local_param.l2cap_slots[i]; + if (slot != NULL && slot->id == id) { + return slot; + } + } + return NULL; +} + +static l2cap_slot_t *l2cap_find_slot_by_fd(int fd) +{ + l2cap_slot_t *slot = NULL; + + for (size_t i = 1; i <= BTA_JV_MAX_L2C_CONN; i++) { + slot = l2cap_local_param.l2cap_slots[i]; + if (slot != NULL && slot->fd == fd) { + return slot; + } + } + return NULL; +} + +static int init_slot_data(slot_data_t *slot_data, size_t queue_size) +{ + memset(slot_data, 0, sizeof(slot_data_t)); + if ((slot_data->queue = fixed_queue_new(queue_size)) == NULL) { + return -1; + } + slot_data->data_size = 0; + return 0; +} + +static void free_slot_data(slot_data_t *slot_data) +{ + fixed_queue_free(slot_data->queue, l2cap_osi_free); + slot_data->queue = NULL; +} + +static l2cap_slot_t *l2cap_malloc_slot(void) +{ + uint8_t err_no = 0; + l2cap_slot_t **slot = NULL; + + if (++l2cap_local_param.l2cap_slot_id == 0) { + l2cap_local_param.l2cap_slot_id = 1; + } + for (size_t i = 1; i <= BTA_JV_MAX_L2C_CONN; i++) { + slot = &l2cap_local_param.l2cap_slots[i]; + if ((*slot) == NULL) { + if (((*slot) = (l2cap_slot_t *)osi_malloc(sizeof(l2cap_slot_t))) == NULL) { + return NULL; + } + (*slot)->id = l2cap_local_param.l2cap_slot_id; + (*slot)->psm = 0; + (*slot)->serial = i; + (*slot)->handle = 0xffff; + (*slot)->fd = -1; + (*slot)->connected = false; + (*slot)->is_server = false; + (*slot)->write_data = NULL; + (*slot)->close_alarm = NULL; + /* clear the old event bits */ + if (l2cap_local_param.tx_event_group) { + xEventGroupClearBits(l2cap_local_param.tx_event_group, SLOT_WRITE_BIT(i) | SLOT_CLOSE_BIT(i)); + } + + if (init_slot_data(&(*slot)->rx, QUEUE_SIZE_MAX)) { + BTC_TRACE_ERROR("%s unable to malloc rx queue!", __func__); + err_no = 1; + goto err; + } + if (init_slot_data(&(*slot)->tx, SLOT_TX_QUEUE_SIZE)) { + BTC_TRACE_ERROR("%s unable to malloc tx queue!", __func__); + err_no = 2; + goto err; + } + if (esp_vfs_register_fd(l2cap_local_param.l2cap_vfs_id, &(*slot)->fd) != ESP_OK) { + BTC_TRACE_ERROR("%s unable to register fd!", __func__); + err_no = 3; + goto err; + } + + return (*slot); + } + } + + return NULL; +err: + switch (err_no) { + case 3: + free_slot_data(&(*slot)->tx); + case 2: + free_slot_data(&(*slot)->rx); + case 1: + osi_free((*slot)); + (*slot) = NULL; + break; + default: + break; + } + return (*slot); +} + +static void l2cap_free_slot(l2cap_slot_t *slot) +{ + if (!slot) { + return; + } + l2cap_local_param.l2cap_slots[slot->serial] = NULL; + esp_vfs_unregister_fd(l2cap_local_param.l2cap_vfs_id, slot->fd); + xEventGroupSetBits(l2cap_local_param.tx_event_group, SLOT_CLOSE_BIT(slot->serial)); + free_slot_data(&slot->tx); + free_slot_data(&slot->rx); + if (slot->close_alarm) { + osi_alarm_free(slot->close_alarm); + } + osi_free(slot); +} + +static void close_timeout_handler(void *arg) +{ + btc_msg_t msg; + bt_status_t status; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_L2CAP; + msg.act = BTA_JV_L2CAP_CLOSE_EVT; + + status = btc_transfer_context(&msg, arg, sizeof(tBTA_JV), NULL); + + if (arg) { + free(arg); + } + + if (status != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed", __func__); + } +} + +static inline void btc_l2cap_cb_to_app(esp_bt_l2cap_cb_event_t event, esp_bt_l2cap_cb_param_t *param) +{ + esp_bt_l2cap_cb_t btc_l2cap_cb = (esp_bt_l2cap_cb_t)btc_profile_cb_get(BTC_PID_L2CAP); + if (btc_l2cap_cb) { + btc_l2cap_cb(event, param); + } +} + +static void *btc_l2cap_inter_cb(tBTA_JV_EVT event, tBTA_JV *p_data, void *user_data) +{ + bt_status_t status; + btc_msg_t msg; + uint32_t id = (uintptr_t)user_data; + l2cap_slot_t *slot = NULL; + + switch (event) { + case BTA_JV_L2CAP_OPEN_EVT: + slot = l2cap_find_slot_by_id(id); + if (!slot) { + BTC_TRACE_ERROR("%s unable to find L2CAP slot, event:%d!", __func__, event); + p_data->l2c_open.status = ESP_BT_L2CAP_NO_CONNECTION; + break; + } + slot->connected = TRUE; + slot->handle = p_data->l2c_open.handle; + slot->tx_mtu = p_data->l2c_open.tx_mtu; + BTA_JvSetPmProfile(p_data->l2c_open.handle, BTA_JV_PM_ID_1, BTA_JV_CONN_OPEN); + break; + case BTA_JV_L2CAP_START_EVT: + slot = l2cap_find_slot_by_id(id); + if (!slot) { + BTC_TRACE_ERROR("%s unable to find L2CAP slot, event:%d!", __func__, event); + p_data->l2c_open.status = ESP_BT_L2CAP_NO_CONNECTION; + break; + } + slot->handle = p_data->l2c_start.handle; + break; + case BTA_JV_L2CAP_CLOSE_EVT: + slot = l2cap_find_slot_by_id(id); + if (!slot) { + BTC_TRACE_ERROR("%s unable to find RFCOMM slot, event:%d!", __func__, event); + p_data->l2c_close.status = ESP_BT_L2CAP_NO_CONNECTION; + break; + } + p_data->l2c_close.status = BTA_JV_SUCCESS; + p_data->l2c_close.user_data = (void *)(uintptr_t)slot->id; + break; + case BTA_JV_L2CAP_CL_INIT_EVT: + slot = l2cap_find_slot_by_id(id); + if (!slot) { + BTC_TRACE_ERROR("%s unable to find L2CAP slot, event:%d!", __func__, event); + p_data->l2c_cl_init.status = ESP_BT_L2CAP_FAILURE; + break; + } + + if (p_data->l2c_cl_init.status == BTA_JV_SUCCESS) { + slot->handle = p_data->l2c_cl_init.handle; + } else { + l2cap_free_slot(slot); + } + break; + case BTA_JV_L2CAP_DATA_IND_EVT: + // to do + break; + case BTA_JV_FREE_SCN_EVT: + slot = l2cap_find_slot_by_id(id); + if (slot) { + l2cap_free_slot(slot); + } else { + BTC_TRACE_ERROR("%s unable to find L2CAP slot, event:%d!", __func__, event); + p_data->free_scn.status = ESP_BT_L2CAP_NO_CONNECTION; + } + break; + default: + break; + } + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_L2CAP; + msg.act = event; + + status = btc_transfer_context(&msg, p_data, sizeof(tBTA_JV), NULL); + if (status != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } + + return NULL; +} + +static void btc_l2cap_dm_inter_cb(tBTA_JV_EVT event, tBTA_JV *p_data, void *user_data) +{ + bt_status_t status; + btc_msg_t msg; + + switch (event) { + default: + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_L2CAP; + msg.act = event; + + status = btc_transfer_context(&msg, p_data, sizeof(tBTA_JV), NULL); + if (status != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } + break; + } + + return; +} + +static void btc_l2cap_init(void) +{ + esp_bt_l2cap_status_t ret = ESP_BT_L2CAP_SUCCESS; + esp_bt_l2cap_cb_param_t param; + + do { + if (is_l2cap_init()) { + BTC_TRACE_ERROR("%s L2CAP has been initiated, shall uninit first!", __func__); + ret = ESP_BT_L2CAP_NEED_DEINIT; + break; + } + +#if L2CAP_DYNAMIC_MEMORY == TRUE + if ((l2cap_local_param_ptr = (l2cap_local_param_t *)osi_malloc(sizeof(l2cap_local_param_t))) == NULL) { + BTC_TRACE_ERROR("%s malloc failed\n", __func__); + ret = ESP_BT_L2CAP_NO_RESOURCE; + break; + } + memset((void *)l2cap_local_param_ptr, 0, sizeof(l2cap_local_param_t)); +#endif + l2cap_local_param.l2cap_vfs_id = -1; + + if (osi_mutex_new(&l2cap_local_param.l2cap_slot_mutex) != 0) { +#if L2CAP_DYNAMIC_MEMORY == TRUE + osi_free(l2cap_local_param_ptr); + l2cap_local_param_ptr = NULL; +#endif + BTC_TRACE_ERROR("%s osi_mutex_new failed\n", __func__); + ret = ESP_BT_L2CAP_NO_RESOURCE; + break; + } + if ((l2cap_local_param.tx_event_group = xEventGroupCreate()) == NULL) { + BTC_TRACE_ERROR("%s create tx_event_group failed\n", __func__); + osi_mutex_free(&l2cap_local_param.l2cap_slot_mutex); +#if L2CAP_DYNAMIC_MEMORY == TRUE + osi_free(l2cap_local_param_ptr); + l2cap_local_param_ptr = NULL; +#endif + ret = ESP_BT_L2CAP_NO_RESOURCE; + break; + } + l2cap_local_param.l2cap_slot_id = 0; + ret = BTA_JvEnable((tBTA_JV_DM_CBACK *)btc_l2cap_dm_inter_cb); + if (BTA_JV_ALREADY_DONE == ret) { + ret = ESP_BT_L2CAP_SUCCESS; + param.init.status = ESP_BT_L2CAP_SUCCESS; + btc_l2cap_cb_to_app(ESP_BT_L2CAP_INIT_EVT, ¶m); + } + } while (0); + + if (ret != ESP_BT_L2CAP_SUCCESS) { + param.init.status = ret; + btc_l2cap_cb_to_app(ESP_BT_L2CAP_INIT_EVT, ¶m); + } +} + +static void btc_l2cap_uninit(void) +{ + esp_bt_l2cap_status_t ret = ESP_BT_L2CAP_SUCCESS; + + do { + if (!is_l2cap_init()) { + BTC_TRACE_ERROR("%s L2CAP has not been initiated, shall init first!", __func__); + ret = ESP_BT_L2CAP_NEED_INIT; + break; + } + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + // first, remove all connection + for (size_t i = 1; i <= BTA_JV_MAX_L2C_CONN; i++) { + if (l2cap_local_param.l2cap_slots[i] != NULL && !l2cap_local_param.l2cap_slots[i]->is_server) { + BTA_JvL2capClose(l2cap_local_param.l2cap_slots[i]->handle, (tBTA_JV_L2CAP_CBACK *)btc_l2cap_inter_cb, + (void *)l2cap_local_param.l2cap_slots[i]->id); + } + } + // second, remove all server + for (size_t i = 1; i <= BTA_JV_MAX_L2C_CONN; i++) { + if (l2cap_local_param.l2cap_slots[i] != NULL && l2cap_local_param.l2cap_slots[i]->is_server) { + if (l2cap_local_param.l2cap_slots[i]->handle != 0xffff) { + BTA_JvL2capStopServer(l2cap_local_param.l2cap_slots[i]->psm, + (void *)l2cap_local_param.l2cap_slots[i]->id); + } + + BTA_JvFreeChannel(l2cap_local_param.l2cap_slots[i]->psm, BTA_JV_CONN_TYPE_L2CAP, + (tBTA_JV_RFCOMM_CBACK *)btc_l2cap_inter_cb, (void *)l2cap_local_param.l2cap_slots[i]->id); + } + } + BTA_JvDisable((tBTA_JV_L2CAP_CBACK *)btc_l2cap_inter_cb); + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + } while(0); + + if (ret != ESP_BT_L2CAP_SUCCESS) { + esp_bt_l2cap_cb_param_t param; + param.uninit.status = ret; + btc_l2cap_cb_to_app(ESP_BT_L2CAP_UNINIT_EVT, ¶m); + } +} + +static void btc_l2cap_start_srv(btc_l2cap_args_t *arg) +{ + esp_bt_l2cap_status_t ret = ESP_BT_L2CAP_SUCCESS; + tL2CAP_CFG_INFO cfg; + + do { + if (!is_l2cap_init()) { + BTC_TRACE_ERROR("%s L2CAP have not been init\n", __func__); + ret = ESP_BT_L2CAP_NEED_INIT; + break; + } + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + l2cap_slot_t *slot = l2cap_malloc_slot(); + if (!slot) { + BTC_TRACE_ERROR("%s unable to malloc L2CAP slot!", __func__); + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + ret = ESP_BT_L2CAP_NO_RESOURCE; + break; + } + /** + * make this slot become a listening slot + */ + slot->is_server = true; + slot->security = arg->start_srv.sec_mask; + slot->role = BTC_L2CAP_ROLE_SLAVE; + slot->psm = arg->start_srv.local_psm; + + /* Setup ETM settings */ + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.fcr_present = TRUE; + cfg.fcr = obex_l2c_fcr_opts_def; + BTA_JvL2capStartServer(slot->security, slot->role, &obex_l2c_etm_opt, slot->psm, + L2CAP_MAX_SDU_LENGTH, &cfg, (tBTA_JV_L2CAP_CBACK *)btc_l2cap_inter_cb, (void *)slot->id); + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + } while(0); + + if (ret != ESP_BT_L2CAP_SUCCESS) { + esp_bt_l2cap_cb_param_t param; + param.start.status = ret; + param.start.handle = 0xffff; + param.start.sec_id = 0; + btc_l2cap_cb_to_app(ESP_BT_L2CAP_START_EVT, ¶m); + } + return; +} + +static void btc_l2cap_stop_srv(btc_l2cap_args_t *arg) +{ + esp_bt_l2cap_status_t ret = ESP_BT_L2CAP_SUCCESS; + bool is_remove_all = false; + uint8_t i, j, srv_cnt = 0; + uint8_t *srv_psm_arr = osi_malloc(BTA_JV_MAX_L2C_CONN); + + if (arg->stop_srv.psm == BTC_L2CAP_INVALID_PSM) { + is_remove_all = true; + } + + do { + if (!is_l2cap_init()) { + BTC_TRACE_ERROR("%s L2CAP have not been init\n", __func__); + ret = ESP_BT_L2CAP_NEED_INIT; + break; + } + if (srv_psm_arr == NULL) { + BTC_TRACE_ERROR("%s malloc srv_psm_arr failed\n", __func__); + ret = ESP_BT_L2CAP_NO_RESOURCE; + break; + } + + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + // [1] find all server + for (i = 1; i <= BTA_JV_MAX_L2C_CONN; i++) { + if (l2cap_local_param.l2cap_slots[i] != NULL && l2cap_local_param.l2cap_slots[i]->is_server && + l2cap_local_param.l2cap_slots[i]->handle != 0xffff) { + if (is_remove_all) { + srv_psm_arr[srv_cnt++] = l2cap_local_param.l2cap_slots[i]->psm; + } else if (l2cap_local_param.l2cap_slots[i]->psm == arg->stop_srv.psm) { + srv_psm_arr[srv_cnt++] = l2cap_local_param.l2cap_slots[i]->psm; + break; + } + } + } + if (srv_cnt == 0) { + if (is_remove_all) { + BTC_TRACE_ERROR("%s can not find any server!\n", __func__); + } else { + BTC_TRACE_ERROR("%s can not find server:%d!\n", __func__, arg->stop_srv.psm); + } + ret = ESP_BT_L2CAP_NO_SERVER; + break; + } + + // [2] remove all local related connection + for (j = 0; j < srv_cnt; j++) { + for (i = 1; i <= BTA_JV_MAX_L2C_CONN; i++) { + if (l2cap_local_param.l2cap_slots[i] != NULL && l2cap_local_param.l2cap_slots[i]->connected && + l2cap_local_param.l2cap_slots[i]->handle != 0xffff && + l2cap_local_param.l2cap_slots[i]->psm == srv_psm_arr[j]) { + BTA_JvL2capClose(l2cap_local_param.l2cap_slots[i]->handle, (tBTA_JV_L2CAP_CBACK *)btc_l2cap_inter_cb, + (void *)l2cap_local_param.l2cap_slots[i]->id); + } + } + } + + // [3] remove all server + for (j = 0; j < srv_cnt; j++) { + for (i = 1; i <= BTA_JV_MAX_L2C_CONN; i++) { + if (l2cap_local_param.l2cap_slots[i] != NULL && l2cap_local_param.l2cap_slots[i]->is_server && + l2cap_local_param.l2cap_slots[i]->handle != 0xffff && + l2cap_local_param.l2cap_slots[i]->psm == srv_psm_arr[j]) { + + if (l2cap_local_param.l2cap_slots[i]->handle > 0) { + BTA_JvL2capStopServer(l2cap_local_param.l2cap_slots[i]->psm, + (void *)l2cap_local_param.l2cap_slots[i]->id); + } + + BTA_JvFreeChannel(l2cap_local_param.l2cap_slots[i]->psm, BTA_JV_CONN_TYPE_L2CAP, + (tBTA_JV_RFCOMM_CBACK *)btc_l2cap_inter_cb, (void *)l2cap_local_param.l2cap_slots[i]->id); + } + } + } + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + } while (0); + + if (ret != ESP_BT_L2CAP_SUCCESS) { + esp_bt_l2cap_cb_param_t param; + param.srv_stop.status = ret; + param.srv_stop.psm = BTC_L2CAP_INVALID_PSM; + btc_l2cap_cb_to_app(ESP_BT_L2CAP_SRV_STOP_EVT, ¶m); + } + + if (srv_psm_arr) { + osi_free(srv_psm_arr); + srv_psm_arr = NULL; + } +} + +static void btc_l2cap_connect(btc_l2cap_args_t *arg) +{ + esp_bt_l2cap_status_t ret = ESP_BT_L2CAP_SUCCESS; + tL2CAP_CFG_INFO cfg; + + do { + if (!is_l2cap_init()) { + BTC_TRACE_ERROR("%s L2CAP have not been init\n", __func__); + ret = ESP_BT_L2CAP_NEED_INIT; + break; + } + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + l2cap_slot_t *slot = l2cap_malloc_slot(); + if (!slot) { + BTC_TRACE_ERROR("%s unable to malloc L2CAP slot!", __func__); + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + ret = ESP_BT_L2CAP_NO_RESOURCE; + break; + } + + slot->security = arg->connect.sec_mask; + slot->role = BTC_L2CAP_ROLE_MASTER; + slot->psm = arg->connect.remote_psm; + memcpy(slot->addr, arg->connect.peer_bd_addr, ESP_BD_ADDR_LEN); + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.fcr_present = TRUE; + cfg.fcr = obex_l2c_fcr_opts_def; + + BTA_JvL2capConnect(slot->security, slot->role, &obex_l2c_etm_opt, slot->psm, + L2CAP_MAX_SDU_LENGTH, &cfg, slot->addr, (tBTA_JV_L2CAP_CBACK *)btc_l2cap_inter_cb, (void *)slot->id); + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + } while (0); + + if (ret != ESP_BT_L2CAP_SUCCESS) { + esp_bt_l2cap_cb_param_t param; + param.open.status = ret; + param.open.handle = 0; + param.open.fd = -1; + param.open.tx_mtu = 0; + memset(param.open.rem_bda, 0, ESP_BD_ADDR_LEN); + btc_l2cap_cb_to_app(ESP_BT_L2CAP_OPEN_EVT, ¶m); + } +} + +static void btc_l2cap_write(uint32_t handle) +{ + do { + if (!is_l2cap_init()) { + BTC_TRACE_ERROR("%s L2CAP have not been init\n", __func__); + break; + } + l2cap_slot_t *slot = NULL; + slot = l2cap_find_slot_by_handle(handle); + if (!slot || (slot && !slot->connected)) { + if (!slot) { + BTC_TRACE_ERROR("%s unable to find l2cap slot!", __func__); + } else { + BTC_TRACE_ERROR("%s l2cap has been disconnected already!", __func__); + } + break; + } + + BT_HDR *p_buf; + if ((p_buf = fixed_queue_try_peek_first(slot->tx.queue)) != NULL && p_buf->layer_specific == 0) { + p_buf->event++; + p_buf->layer_specific = 1; + BTA_JvL2capWrite(handle, slot->id, p_buf->data + p_buf->offset, p_buf->len, (void *)slot->id); + } + } while (0); +} + +static void btc_l2cap_disconnect(uint32_t handle) +{ + esp_bt_l2cap_status_t ret = ESP_BT_L2CAP_SUCCESS; + + do { + if (!is_l2cap_init()) { + BTC_TRACE_ERROR("%s L2CAP have not been init\n", __func__); + ret = ESP_BT_L2CAP_NEED_INIT; + break; + } + l2cap_slot_t *slot = NULL; + slot = l2cap_find_slot_by_handle(handle); + if (!slot || (slot && !slot->connected)) { + if (!slot) { + BTC_TRACE_ERROR("%s unable to find L2CAP slot! disconnect fail!", __func__); + } else { + BTC_TRACE_ERROR("%s L2CAP has been disconnected already!", __func__); + } + ret = ESP_BT_L2CAP_NO_CONNECTION; + break; + } + BTA_JvL2capClose(handle, (tBTA_JV_L2CAP_CBACK *)btc_l2cap_inter_cb, (void *)slot->id); + } while(0); + + if (ret != ESP_BT_L2CAP_SUCCESS) { + esp_bt_l2cap_cb_param_t param; + param.close.status = ret; + param.close.handle = 0; + param.close.async = FALSE; + btc_l2cap_cb_to_app(ESP_BT_L2CAP_CLOSE_EVT, ¶m); + } +} + +void btc_l2cap_call_handler(btc_msg_t *msg) +{ + btc_l2cap_args_t *arg = (btc_l2cap_args_t *)(msg->arg); + switch (msg->act) { + case BTC_L2CAP_ACT_INIT: + btc_l2cap_init(); + break; + case BTC_L2CAP_ACT_UNINIT: + btc_l2cap_uninit(); + break; + case BTC_L2CAP_ACT_CONNECT: + btc_l2cap_connect(arg); + break; + case BTC_L2CAP_ACT_START_SRV: + btc_l2cap_start_srv(arg); + break; + case BTC_L2CAP_ACT_STOP_SRV: + btc_l2cap_stop_srv(arg); + break; + default: + BTC_TRACE_ERROR("%s: Unhandled event (%d)!\n", __FUNCTION__, msg->act); + break; + } +} + +void btc_l2cap_cb_handler(btc_msg_t *msg) +{ + esp_bt_l2cap_cb_param_t param; + tBTA_JV *p_data = (tBTA_JV *)msg->arg; + l2cap_slot_t *slot = NULL; + uint8_t event = msg->act; + uint8_t serial = 0; + uint32_t count = 0; + + switch (event) { + case BTA_JV_ENABLE_EVT: + param.init.status = p_data->status; + btc_l2cap_cb_to_app(ESP_BT_L2CAP_INIT_EVT, ¶m); + break; + case BTA_JV_DISABLE_EVT: + param.uninit.status = ESP_BT_L2CAP_SUCCESS; + BTA_JvFree(); + osi_mutex_free(&l2cap_local_param.l2cap_slot_mutex); + if (l2cap_local_param.tx_event_group) { + vEventGroupDelete(l2cap_local_param.tx_event_group); + l2cap_local_param.tx_event_group = NULL; + } + if (l2cap_local_param.l2cap_vfs_id != -1) { + esp_vfs_unregister_with_id(l2cap_local_param.l2cap_vfs_id); + l2cap_local_param.l2cap_vfs_id = -1; + } +#if L2CAP_DYNAMIC_MEMORY == TRUE + osi_free(l2cap_local_param_ptr); + l2cap_local_param_ptr = NULL; +#endif + btc_l2cap_cb_to_app(ESP_BT_L2CAP_UNINIT_EVT, ¶m); + break; + case BTA_JV_L2CAP_OPEN_EVT: + do { + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = l2cap_find_slot_by_handle(p_data->l2c_open.handle); + if (!slot) { + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + BTC_TRACE_ERROR("%s unable to find L2CAP slot, event:%d!", __func__, event); + param.open.status = ESP_BT_L2CAP_NO_CONNECTION; + break; + } + param.open.fd = slot->fd; + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + param.open.status = p_data->l2c_open.status; + } while (0); + param.open.handle = p_data->l2c_open.handle; + param.open.tx_mtu = p_data->l2c_open.tx_mtu; + memcpy(param.open.rem_bda, p_data->l2c_open.rem_bda, ESP_BD_ADDR_LEN); + btc_l2cap_cb_to_app(ESP_BT_L2CAP_OPEN_EVT, ¶m); + break; + case BTA_JV_L2CAP_CLOSE_EVT: + param.close.status = p_data->l2c_close.status; + param.close.handle = p_data->l2c_close.handle; + param.close.async = p_data->l2c_close.async; + bool need_call = true; + do { + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + uint32_t id = (uintptr_t)p_data->l2c_close.user_data; + slot = l2cap_find_slot_by_id(id); + if (!slot) { + param.close.status = ESP_BT_L2CAP_NO_CONNECTION; + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + BTC_TRACE_ERROR("%s unable to find RFCOMM slot, event:%d!", __func__, event); + break; + } + // if rx still has data, delay free slot + if (slot->close_alarm == NULL && slot->rx.queue && fixed_queue_length(slot->rx.queue) > 0) { + tBTA_JV *p_arg = NULL; + if ((p_arg = malloc(sizeof(tBTA_JV))) == NULL) { + param.close.status = ESP_BT_L2CAP_NO_RESOURCE; + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + BTC_TRACE_ERROR("%s unable to malloc slot close_alarm arg!", __func__); + break; + } + memcpy(p_arg, p_data, sizeof(tBTA_JV)); + if ((slot->close_alarm = + osi_alarm_new("slot", close_timeout_handler, (void *)p_arg, VFS_CLOSE_TIMEOUT)) == NULL) { + free(p_arg); + param.close.status = ESP_BT_L2CAP_NO_RESOURCE; + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + BTC_TRACE_ERROR("%s unable to malloc slot close_alarm!", __func__); + break; + } + if (osi_alarm_set(slot->close_alarm, VFS_CLOSE_TIMEOUT) != OSI_ALARM_ERR_PASS) { + free(p_arg); + osi_alarm_free(slot->close_alarm); + param.close.status = ESP_BT_L2CAP_BUSY; + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + BTC_TRACE_ERROR("%s set slot close_alarm failed!", __func__); + break; + } + BTC_TRACE_WARNING("%s slot rx data will be discard in %d milliseconds!", + __func__, VFS_CLOSE_TIMEOUT); + slot->connected = false; + need_call = false; + } + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + } while (0); + + if (need_call) { + btc_l2cap_cb_to_app(ESP_BT_L2CAP_CLOSE_EVT, ¶m); + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + l2cap_free_slot(slot); + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + } + break; + case BTA_JV_L2CAP_START_EVT: + param.start.status = p_data->l2c_start.status; + param.start.handle = p_data->l2c_start.handle; + param.start.sec_id = p_data->l2c_start.sec_id; + btc_l2cap_cb_to_app(ESP_BT_L2CAP_START_EVT, ¶m); + break; + case BTA_JV_L2CAP_CL_INIT_EVT: + param.cl_init.status = p_data->l2c_cl_init.status; + param.cl_init.handle = p_data->l2c_cl_init.handle; + param.cl_init.sec_id = p_data->l2c_cl_init.sec_id; + btc_l2cap_cb_to_app(ESP_BT_L2CAP_CL_INIT_EVT, ¶m); + break; + case BTA_JV_L2CAP_DATA_IND_EVT: + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = l2cap_find_slot_by_handle(p_data->data_ind.handle); + if (!slot) { + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + BTC_TRACE_ERROR("%s unable to find L2CAP slot, event:%d!", __func__, event); + break; + } + if (BTA_JvL2capReady(p_data->data_ind.handle, &count) == BTA_JV_SUCCESS && count > 0) { + BT_HDR *p_data_buf = osi_malloc(count + sizeof(BT_HDR)); + if (p_data_buf == NULL) { + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + BTC_TRACE_ERROR("%s, %d count = %d malloc failed!", __func__, __LINE__, count); + break; // to do disconnect + } + memset(p_data_buf, 0, count + sizeof(BT_HDR)); + p_data_buf->len = BTA_JvL2capRead(p_data->data_ind.handle, slot->id, p_data_buf->data, count); + if (p_data_buf->len > 0) { + fixed_queue_enqueue(slot->rx.queue, p_data_buf, FIXED_QUEUE_MAX_TIMEOUT); + } else { + osi_free(p_data_buf); + break; + } + } + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + break; + case BTA_JV_L2CAP_CONG_EVT: + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = l2cap_find_slot_by_handle(p_data->l2c_cong.handle); + if (!slot) { + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + BTC_TRACE_ERROR("%s unable to find L2CAP slot, event:%d!", __func__, event); + break; + } + if (!p_data->l2c_cong.cong) { + BT_HDR *p_buf; + if ((p_buf = fixed_queue_try_peek_first(slot->tx.queue)) != NULL && p_buf->layer_specific == 0) { + p_buf->event++; + p_buf->layer_specific = 1; + BTA_JvL2capWrite(p_data->l2c_cong.handle, slot->id, p_buf->data + p_buf->offset, p_buf->len, (void *)slot->id); + } + } + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + break; + case BTA_JV_L2CAP_READ_EVT: + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = l2cap_find_slot_by_handle(p_data->l2c_read.handle); + if (!slot) { + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + BTC_TRACE_ERROR("%s unable to find L2CAP slot, event:%d!", __func__, event); + break; + } + if (BTA_JvL2capReady(p_data->l2c_read.handle, &count) == BTA_JV_SUCCESS && count > 0) { + BT_HDR *p_data_buf = osi_malloc(count + sizeof(BT_HDR)); + if (p_data_buf == NULL) { + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + BTC_TRACE_ERROR("%s, %d count = %d malloc failed!", __func__, __LINE__, count); + break; // to do disconnect + } + memset(p_data_buf, 0, count + sizeof(BT_HDR)); + p_data_buf->len = BTA_JvL2capRead(p_data->data_ind.handle, slot->id, p_data_buf->data, count); + if (p_data_buf->len > 0) { + fixed_queue_enqueue(slot->rx.queue, p_data_buf, FIXED_QUEUE_MAX_TIMEOUT); + } else { + osi_free(p_data_buf); + break; + } + } + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + break; + case BTA_JV_L2CAP_WRITE_EVT: + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = l2cap_find_slot_by_handle(p_data->l2c_write.handle); + if (!slot) { + BTC_TRACE_ERROR("%s unable to find L2CAP slot!, handle:%d", __func__, p_data->l2c_write.handle); + } + if (slot) { + BT_HDR *p_buf; + serial = slot->serial; + if ((p_buf = fixed_queue_try_peek_first(slot->tx.queue)) == NULL) { + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + break; + } + if (p_data->l2c_write.status == BTA_JV_SUCCESS) { + p_buf->len -= p_data->l2c_write.len; + p_buf->offset += p_data->l2c_write.len; + p_buf->layer_specific = 0; + if (p_buf->len == 0) { + osi_free(fixed_queue_dequeue(slot->tx.queue, FIXED_QUEUE_MAX_TIMEOUT)); + if (fixed_queue_length(slot->tx.queue) <= SLOT_TX_QUEUE_LOW_WM) { + xEventGroupSetBits(l2cap_local_param.tx_event_group, SLOT_WRITE_BIT(serial)); + } + } + + if ((p_buf = fixed_queue_try_peek_first(slot->tx.queue)) != NULL && p_buf->layer_specific == 0 && + !p_data->l2c_write.cong) { + p_buf->layer_specific = 1; + p_buf->event++; + BTA_JvL2capWrite(p_data->l2c_write.handle, slot->id, p_buf->data + p_buf->offset, p_buf->len, (void *)slot->id); + } + } + } + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + break; + case BTA_JV_FREE_SCN_EVT: + param.srv_stop.status = p_data->free_scn.status; + param.srv_stop.psm = p_data->free_scn.scn; + btc_l2cap_cb_to_app(ESP_BT_L2CAP_SRV_STOP_EVT, ¶m); + break; + default: + break; + } + + return; +} + +static ssize_t l2cap_vfs_write(int fd, const void * data, size_t size) +{ + assert(data != NULL); + errno = 0; + if (size == 0) { + return 0; + } + if (!is_l2cap_init()) { + BTC_TRACE_ERROR("%s L2CAP have not been init\n", __func__); + errno = ESRCH; + return -1; + } + + l2cap_slot_t *slot = NULL; + uint8_t serial = 0; + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = l2cap_find_slot_by_fd(fd); + if (!slot) { + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + BTC_TRACE_ERROR("%s unable to find L2CAP slot!", __func__); + errno = ENOENT; + return -1; + } + serial = slot->serial; + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + + ssize_t sent = 0, write_size = 0; + size_t tx_len; + BT_HDR *p_buf = NULL; + bool enqueue_status= false; + EventBits_t tx_event_group_val = 0; + while (1) { + tx_event_group_val = 0; + if (size) { + if (p_buf == NULL) { + write_size = size < slot->tx_mtu ? size : slot->tx_mtu; + if ((p_buf = osi_malloc(sizeof(BT_HDR) + write_size)) == NULL) { + BTC_TRACE_ERROR("%s malloc failed!", __func__); + errno = ENOMEM; + sent = -1; + break; + } + p_buf->offset = 0; + p_buf->len = write_size; + p_buf->event = 0; // indicate the p_buf be sent count + p_buf->layer_specific = 0; // indicate the p_buf whether to be sent, 0 - ready to send; 1 - have sent + memcpy((UINT8 *)(p_buf + 1), data + sent, write_size); + } + } else { + break; + } + + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + if ((slot = l2cap_local_param.l2cap_slots[serial]) != NULL) { + tx_len = fixed_queue_length(slot->tx.queue); + enqueue_status = fixed_queue_enqueue(slot->tx.queue, p_buf, 0); + if (!enqueue_status) { + BTC_TRACE_DEBUG("%s tx_len:%d, fd:%d\n", __func__, fixed_queue_length(slot->tx.queue), fd); + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + //block untill under water level, be closed or time out + tx_event_group_val = + xEventGroupWaitBits(l2cap_local_param.tx_event_group, SLOT_WRITE_BIT(serial) | SLOT_CLOSE_BIT(serial), pdTRUE, + pdFALSE, VFS_WRITE_TIMEOUT / portTICK_PERIOD_MS); + if (tx_event_group_val & SLOT_CLOSE_BIT(serial)) { + BTC_TRACE_ERROR("%s exit for L2CAP close, fd:%d!", __func__, fd); + errno = EPIPE; + sent = -1; + break; + } else if (tx_event_group_val & SLOT_WRITE_BIT(serial)) { + continue; + } else if (tx_event_group_val == 0) { + BTC_TRACE_ERROR("%s exit for time out, fd:%d!", __func__, fd); + errno = EBUSY; + sent = -1; + break; + } + } + if (tx_len == 0) { + btc_l2cap_write(slot->handle); + } + sent += write_size; + size -= write_size; + p_buf = NULL; + } else { + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + errno = EPIPE; + sent = -1; + break; + } + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + } + + //errors occur, need to cleanup + if (p_buf) { + osi_free(p_buf); + p_buf = NULL; + } + + return sent; +} + +static int l2cap_vfs_close(int fd) +{ + errno = 0; + if (!is_l2cap_init()) { + BTC_TRACE_ERROR("%s L2CAP have not been init\n", __func__); + errno = ESRCH; + return -1; + } + l2cap_slot_t *slot = NULL; + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = l2cap_find_slot_by_fd(fd); + if (!slot) { + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + BTC_TRACE_ERROR("%s unable to find L2CAP slot!", __func__); + errno = ENOENT; + return -1; + } + btc_l2cap_disconnect(slot->handle); + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + return 0; +} + +static ssize_t l2cap_vfs_read(int fd, void * dst, size_t size) +{ + assert(dst != NULL); + errno = 0; + if (!is_l2cap_init()) { + BTC_TRACE_ERROR("%s L2CAP have not been init\n", __func__); + errno = ESRCH; + return -1; + } + + l2cap_slot_t *slot = NULL; + uint8_t serial = 0; + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = l2cap_find_slot_by_fd(fd); + if (!slot) { + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + BTC_TRACE_ERROR("%s unable to find L2CAP slot!", __func__); + errno = ENOENT; + return -1; + } + serial = slot->serial; + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + + ssize_t item_size = 0; + BT_HDR *p_buf; + while (1) { + osi_mutex_lock(&l2cap_local_param.l2cap_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + if ((slot = l2cap_local_param.l2cap_slots[serial]) != NULL) { + if (fixed_queue_length(slot->rx.queue) > 0) { + // free unused p_buf + if ((p_buf = (BT_HDR *)fixed_queue_try_peek_first(slot->rx.queue)) != NULL && p_buf->len == 0) { + osi_free(fixed_queue_dequeue(slot->rx.queue, FIXED_QUEUE_MAX_TIMEOUT)); + p_buf = NULL; + } + if (size == 0 || (p_buf = (BT_HDR *)fixed_queue_try_peek_first(slot->rx.queue)) == NULL) { + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + break; + } + } else { + /** + * If close_alarm is not NULL, it means that we have received the BTA_JV_L2CAP_CLOSE_EVT. + * And we can trigger close_alarm immediately. + */ + if (slot->close_alarm && osi_alarm_is_active(slot->close_alarm)) { + osi_alarm_cancel(slot->close_alarm); + osi_alarm_set(slot->close_alarm, 0); + } + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + break; + } + } else { + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + BTC_TRACE_ERROR("%s peer close, data will be discarded!\n", __func__); + errno = EPIPE; + item_size = -1; + break; + } + osi_mutex_unlock(&l2cap_local_param.l2cap_slot_mutex); + + if (p_buf->len <= size) { + memcpy(dst, p_buf->data + p_buf->offset, p_buf->len); + size -= p_buf->len; + item_size += p_buf->len; + dst += p_buf->len; + p_buf->offset += p_buf->len; + p_buf->len = 0; // indicate the p_buf is unused + } else { + memcpy(dst, p_buf->data + p_buf->offset, size); + item_size += size; + p_buf->offset += size; + p_buf->len -= size; + size = 0; + } + } + return item_size; +} + +esp_err_t btc_l2cap_vfs_register(void) +{ + esp_err_t ret = ESP_OK; + + do { + if (!is_l2cap_init()) { + BTC_TRACE_ERROR("%s L2CAP have not been init\n", __func__); + ret = ESP_FAIL; + break; + } + + esp_vfs_t vfs = { + .flags = ESP_VFS_FLAG_DEFAULT, + .write = l2cap_vfs_write, + .open = NULL, + .fstat = NULL, + .close = l2cap_vfs_close, + .read = l2cap_vfs_read, + .fcntl = NULL + }; + + // No FD range is registered here: l2cap_vfs_id is used to register/unregister + // file descriptors + if (esp_vfs_register_with_id(&vfs, NULL, &l2cap_local_param.l2cap_vfs_id) != ESP_OK) { + ret = ESP_FAIL; + break; + } + } while (0); + + return ret; +} + +esp_err_t btc_l2cap_vfs_unregister(void) +{ + esp_err_t ret = ESP_OK; + do { + if (!is_l2cap_init()) { + BTC_TRACE_ERROR("%s L2CAP have not been init\n", __func__); + ret = ESP_FAIL; + break; + } + + if (l2cap_local_param.l2cap_vfs_id != -1) { + if (esp_vfs_unregister_with_id(l2cap_local_param.l2cap_vfs_id) != ESP_OK) { + ret = ESP_FAIL; + } + } + l2cap_local_param.l2cap_vfs_id = -1; + } while (0); + + return ret; +} + +#endif ///defined BTC_L2CAP_INCLUDED && BTC_L2CAP_INCLUDED == TRUE diff --git a/components/bt/host/bluedroid/btc/profile/std/spp/btc_spp.c b/components/bt/host/bluedroid/btc/profile/std/spp/btc_spp.c index d5e2a604a3..adef8b25b9 100644 --- a/components/bt/host/bluedroid/btc/profile/std/spp/btc_spp.c +++ b/components/bt/host/bluedroid/btc/profile/std/spp/btc_spp.c @@ -106,7 +106,7 @@ static int init_slot_data(slot_data_t *slot_data, size_t queue_size) return 0; } -void free_slot_data(slot_data_t *slot_data) +static void free_slot_data(slot_data_t *slot_data) { fixed_queue_free(slot_data->queue, spp_osi_free); slot_data->queue = NULL; diff --git a/components/bt/host/bluedroid/common/include/common/bluedroid_user_config.h b/components/bt/host/bluedroid/common/include/common/bluedroid_user_config.h index 6f129f7fad..e6aae09935 100644 --- a/components/bt/host/bluedroid/common/include/common/bluedroid_user_config.h +++ b/components/bt/host/bluedroid/common/include/common/bluedroid_user_config.h @@ -45,6 +45,13 @@ #define UC_BT_SPP_ENABLED FALSE #endif +//L2CAP +#ifdef CONFIG_BT_L2CAP_ENABLED +#define UC_BT_L2CAP_ENABLED CONFIG_BT_L2CAP_ENABLED +#else +#define UC_BT_L2CAP_ENABLED FALSE +#endif + //HFP(AG) #ifdef CONFIG_BT_HFP_AG_ENABLE #define UC_BT_HFP_AG_ENABLED CONFIG_BT_HFP_AG_ENABLE diff --git a/components/bt/host/bluedroid/common/include/common/bt_target.h b/components/bt/host/bluedroid/common/include/common/bt_target.h index c16e4d5be8..ebc5df3e06 100644 --- a/components/bt/host/bluedroid/common/include/common/bt_target.h +++ b/components/bt/host/bluedroid/common/include/common/bt_target.h @@ -83,6 +83,13 @@ #define BTC_SPP_INCLUDED TRUE #endif /* UC_BT_SPP_ENABLED */ +#if (UC_BT_L2CAP_ENABLED == TRUE) +#define BTA_JV_INCLUDED TRUE +#define RFCOMM_INCLUDED TRUE +#define BTC_L2CAP_INCLUDED TRUE +#define VND_BT_JV_BTA_L2CAP TRUE +#endif /* UC_BT_L2CAP_ENABLED */ + #if (UC_BT_HFP_AG_ENABLED == TRUE) #define BTC_HF_INCLUDED TRUE #define BTA_AG_INCLUDED TRUE @@ -1594,51 +1601,88 @@ #define OBX_FCR_TX_BUF_SIZE BT_DEFAULT_BUFFER_SIZE #endif -/* This option is application when OBX_14_INCLUDED=TRUE -Size of the transmission window when using enhanced retransmission mode. Not used -in basic and streaming modes. Range: 1 - 63 -*/ +/* + * Size of the transmission window when using enhanced retransmission mode. Not used + * in basic and streaming modes. Range: 1 - 63 + */ #ifndef OBX_FCR_OPT_TX_WINDOW_SIZE_BR_EDR #define OBX_FCR_OPT_TX_WINDOW_SIZE_BR_EDR 20 #endif -/* This option is application when OBX_14_INCLUDED=TRUE -Number of transmission attempts for a single I-Frame before taking -Down the connection. Used In ERTM mode only. Value is Ignored in basic and -Streaming modes. -Range: 0, 1-0xFF -0 - infinite retransmissions -1 - single transmission -*/ +/* + * Number of transmission attempts for a single I-Frame before taking + * Down the connection. Used In ERTM mode only. Value is Ignored in basic and + * Streaming modes. + * Range: 0, 1-0xFF + * 0 - infinite retransmissions + * 1 - single transmission + */ #ifndef OBX_FCR_OPT_MAX_TX_B4_DISCNT #define OBX_FCR_OPT_MAX_TX_B4_DISCNT 20 #endif -/* This option is application when OBX_14_INCLUDED=TRUE -Retransmission Timeout -Range: Minimum 2000 (2 secs) on BR/EDR when supporting PBF. +/* + * Retransmission Timeout + * Range: Minimum 2000 (2 secs) on BR/EDR when supporting PBF. */ #ifndef OBX_FCR_OPT_RETX_TOUT #define OBX_FCR_OPT_RETX_TOUT 2000 #endif -/* This option is application when OBX_14_INCLUDED=TRUE -Monitor Timeout -Range: Minimum 12000 (12 secs) on BR/EDR when supporting PBF. -*/ +/* + * Monitor Timeout + * Range: Minimum 12000 (12 secs) on BR/EDR when supporting PBF. + */ #ifndef OBX_FCR_OPT_MONITOR_TOUT #define OBX_FCR_OPT_MONITOR_TOUT 12000 #endif -/* This option is application when OBX_14_INCLUDED=TRUE -Maximum PDU payload size. -Suggestion: The maximum amount of data that will fit into a 3-DH5 packet. -Range: 2 octets +/* + * Maximum PDU payload size. + * Suggestion: The maximum amount of data that will fit into a 3-DH5 packet. + * Range: 2 octets */ #ifndef OBX_FCR_OPT_MAX_PDU_SIZE #define OBX_FCR_OPT_MAX_PDU_SIZE L2CAP_MPS_OVER_BR_EDR #endif +/* + * Pool ID where to reassemble the SDU. + * This Pool will allow buffers to be used that are larger than + * the L2CAP_MAX_MTU. + */ +#ifndef OBX_USER_RX_POOL_ID +#define OBX_USER_RX_POOL_ID 4 +#endif + +/* + * Pool ID where to hold the SDU. + * This Pool will allow buffers to be used that are larger than + * the L2CAP_MAX_MTU. + */ +#ifndef OBX_USER_TX_POOL_ID +#define OBX_USER_TX_POOL_ID 4 +#endif + +/* + * GKI Buffer Pool ID used to hold MPS segments during SDU reassembly + */ +#ifndef OBX_FCR_RX_POOL_ID +#define OBX_FCR_RX_POOL_ID 3 +#endif + +/* + * Pool ID used to hold MPS segments used in (re)transmissions. + * L2CAP_DEFAULT_ERM_POOL_ID is specified to use the HCI ACL data pool. + * Note: This pool needs to have enough buffers to hold two times the window size negotiated + * in the L2CA_SetFCROptions (2 * tx_win_size) to allow for retransmissions. + * The size of each buffer must be able to hold the maximum MPS segment size passed in + * L2CA_SetFCROptions plus BT_HDR (8) + HCI preamble (4) + L2CAP_MIN_OFFSET (11 - as of BT 2.1 + EDR Spec). + */ +#ifndef OBX_FCR_TX_POOL_ID +#define OBX_FCR_TX_POOL_ID 3 +#endif + /****************************************************************************** ** From 1d9fd4707abd0a5caa4ab7b191bc183ed020b93b Mon Sep 17 00:00:00 2001 From: xiongweichao Date: Thu, 21 Apr 2022 14:26:29 +0800 Subject: [PATCH 02/10] add sdp api --- components/bt/CMakeLists.txt | 2 + components/bt/common/btc/core/btc_task.c | 6 + .../bt/common/btc/include/btc/btc_task.h | 1 + .../bt/host/bluedroid/api/esp_sdp_api.c | 129 +++ .../bluedroid/api/include/api/esp_sdp_api.h | 271 +++++ .../bluedroid/bta/include/bta/bta_sdp_api.h | 14 +- .../bt/host/bluedroid/bta/sdp/bta_sdp_act.c | 14 +- .../bt/host/bluedroid/bta/sdp/bta_sdp_api.c | 2 + .../btc/profile/std/include/bt_sdp.h | 5 +- .../btc/profile/std/include/btc_sdp.h | 53 + .../bluedroid/btc/profile/std/sdp/btc_sdp.c | 1023 +++++++++++++++++ .../common/include/common/bt_target.h | 3 +- 12 files changed, 1510 insertions(+), 13 deletions(-) create mode 100644 components/bt/host/bluedroid/api/esp_sdp_api.c create mode 100644 components/bt/host/bluedroid/api/include/api/esp_sdp_api.h create mode 100644 components/bt/host/bluedroid/btc/profile/std/include/btc_sdp.h create mode 100644 components/bt/host/bluedroid/btc/profile/std/sdp/btc_sdp.c diff --git a/components/bt/CMakeLists.txt b/components/bt/CMakeLists.txt index 34ea3ce564..7eb63851e9 100644 --- a/components/bt/CMakeLists.txt +++ b/components/bt/CMakeLists.txt @@ -113,6 +113,7 @@ if(CONFIG_BT_ENABLED) "host/bluedroid/api/esp_hf_ag_api.c" "host/bluedroid/api/esp_hf_client_api.c" "host/bluedroid/api/esp_spp_api.c" + "host/bluedroid/api/esp_sdp_api.c" "host/bluedroid/api/esp_l2cap_bt_api.c" "host/bluedroid/bta/ar/bta_ar.c" "host/bluedroid/bta/av/bta_av_aact.c" @@ -215,6 +216,7 @@ if(CONFIG_BT_ENABLED) "host/bluedroid/btc/profile/std/gatt/btc_gattc.c" "host/bluedroid/btc/profile/std/gatt/btc_gatts.c" "host/bluedroid/btc/profile/std/spp/btc_spp.c" + "host/bluedroid/btc/profile/std/sdp/btc_sdp.c" "host/bluedroid/btc/profile/std/l2cap/btc_l2cap.c" "host/bluedroid/device/bdaddr.c" "host/bluedroid/device/controller.c" diff --git a/components/bt/common/btc/core/btc_task.c b/components/bt/common/btc/core/btc_task.c index 73063922a8..1348fcbd2d 100644 --- a/components/bt/common/btc/core/btc_task.c +++ b/components/bt/common/btc/core/btc_task.c @@ -42,6 +42,9 @@ #if (BTC_L2CAP_INCLUDED == TRUE) #include "btc_l2cap.h" #endif /* #if (BTC_L2CAP_INCLUDED == TRUE) */ +#if (BTC_SDP_INCLUDED == TRUE) +#include "btc_sdp.h" +#endif /* #if (BTC_SDP_INCLUDED == TRUE) */ #if BTC_HF_INCLUDED #include "btc_hf_ag.h" #endif/* #if BTC_HF_INCLUDED */ @@ -118,6 +121,9 @@ static const btc_func_t profile_tab[BTC_PID_NUM] = { #if (BTC_L2CAP_INCLUDED == TRUE) [BTC_PID_L2CAP] = {btc_l2cap_call_handler, btc_l2cap_cb_handler }, #endif /* #if (BTC_L2CAP_INCLUDED == TRUE) */ +#if (BTC_SDP_INCLUDED == TRUE) + [BTC_PID_SDP] = {btc_sdp_call_handler, btc_sdp_cb_handler }, +#endif /* #if (BTC_SDP_INCLUDED == TRUE) */ #if BTC_HF_INCLUDED [BTC_PID_HF] = {btc_hf_call_handler, btc_hf_cb_handler}, #endif /* #if BTC_HF_INCLUDED */ diff --git a/components/bt/common/btc/include/btc/btc_task.h b/components/bt/common/btc/include/btc/btc_task.h index 130208a81e..13f51cfe50 100644 --- a/components/bt/common/btc/include/btc/btc_task.h +++ b/components/bt/common/btc/include/btc/btc_task.h @@ -60,6 +60,7 @@ typedef enum { BTC_PID_HD, BTC_PID_HH, BTC_PID_L2CAP, + BTC_PID_SDP, #if (BTC_HF_INCLUDED == TRUE) BTC_PID_HF, #endif /* BTC_HF_INCLUDED */ diff --git a/components/bt/host/bluedroid/api/esp_sdp_api.c b/components/bt/host/bluedroid/api/esp_sdp_api.c new file mode 100644 index 0000000000..7831b218ad --- /dev/null +++ b/components/bt/host/bluedroid/api/esp_sdp_api.c @@ -0,0 +1,129 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "esp_bt_main.h" +#include "btc/btc_manage.h" + +#include "btc_sdp.h" +#include "esp_sdp_api.h" +#include "common/bt_target.h" + +#if (defined BTC_SDP_INCLUDED && BTC_SDP_INCLUDED == TRUE) + +esp_err_t esp_sdp_register_callback(esp_sdp_cb_t callback) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (callback == NULL) { + return ESP_FAIL; + } + + btc_profile_cb_set(BTC_PID_SDP, callback); + return ESP_OK; +} + +esp_err_t esp_sdp_init(void) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + bt_status_t stat; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_SDP; + msg.act = BTC_SDP_ACT_INIT; + + /* Switch to BTC context */ + stat = btc_transfer_context(&msg, NULL, 0, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_sdp_deinit(void) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + bt_status_t stat; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_SDP; + msg.act = BTC_SDP_ACT_DEINIT; + + /* Switch to BTC context */ + stat = btc_transfer_context(&msg, NULL, 0, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_sdp_search_record(esp_bd_addr_t bd_addr, esp_bt_uuid_t uuid) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + bt_status_t stat; + btc_sdp_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_SDP; + msg.act = BTC_SDP_ACT_SEARCH; + + memset(&arg, 0, sizeof(btc_sdp_args_t)); + memcpy(arg.search.bd_addr, bd_addr, ESP_BD_ADDR_LEN); + arg.search.sdp_uuid.len = uuid.len; + memcpy(&arg.search.sdp_uuid.uu, &uuid.uuid, sizeof(uuid.uuid)); + + /* Switch to BTC context */ + stat = btc_transfer_context(&msg, &arg, sizeof(btc_sdp_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_sdp_create_record(esp_bluetooth_sdp_record_t *record) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (record == NULL || record->hdr.service_name_length > ESP_SDP_SERVER_NAME_MAX + || strlen(record->hdr.service_name)+1 != record->hdr.service_name_length) { + LOG_ERROR("Invalid server name!\n"); + return ESP_ERR_INVALID_ARG; + } + + btc_msg_t msg; + bt_status_t stat; + btc_sdp_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_SDP; + msg.act = BTC_SDP_ACT_CREATE_RECORD; + + memset(&arg, 0, sizeof(btc_sdp_args_t)); + arg.creat_record.record = (bluetooth_sdp_record *)record; + + /* Switch to BTC context */ + stat = btc_transfer_context(&msg, &arg, sizeof(btc_sdp_args_t), btc_sdp_arg_deep_copy); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_sdp_remove_record(int record_handle) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + bt_status_t stat; + btc_sdp_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_SDP; + msg.act = BTC_SDP_ACT_REMOVE_RECORD; + + arg.remove_record.record_handle = record_handle; + + /* Switch to BTC context */ + stat = btc_transfer_context(&msg, &arg, sizeof(btc_sdp_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +#endif ///defined BTC_SDP_INCLUDED && BTC_SDP_INCLUDED == TRUE diff --git a/components/bt/host/bluedroid/api/include/api/esp_sdp_api.h b/components/bt/host/bluedroid/api/include/api/esp_sdp_api.h new file mode 100644 index 0000000000..cb944a5ced --- /dev/null +++ b/components/bt/host/bluedroid/api/include/api/esp_sdp_api.h @@ -0,0 +1,271 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ESP_SDP_API_H__ +#define __ESP_SDP_API_H__ + +#include "esp_err.h" +#include "esp_bt_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ESP_SDP_SERVER_NAME_MAX 32 /*!< Service name max length */ +#define SDP_OPP_SUPPORTED_FORMATS_MAX_LENGTH 15 /*!< OPP supported format list maximum length */ + +typedef enum { + ESP_SDP_SUCCESS = 0, /*!< Successful operation. */ + ESP_SDP_FAILURE, /*!< Generic failure. */ + ESP_SDP_NO_RESOURCE, /*!< No more resource */ + ESP_SDP_NEED_INIT, /*!< SDP module shall init first */ + ESP_SDP_NEED_DEINIT, /*!< SDP module shall deinit first */ + ESP_SDP_NO_CREATE_RECORD, /*!< No record created */ +} esp_sdp_status_t; + +/** + * @brief SDP callback function events + */ +typedef enum { + ESP_SDP_INIT_EVT = 0, /*!< When SDP is inited, the event comes */ + ESP_SDP_DEINIT_EVT = 1, /*!< When SDP is deinited, the event comes */ + ESP_SDP_SEARCH_COMP_EVT = 2, /*!< When SDP search complete, the event comes */ + ESP_SDP_CREATE_RECORD_COMP_EVT = 3, /*!< When create SDP records complete, the event comes */ + ESP_SDP_REMOVE_RECORD_COMP_EVT = 4, /*!< When remove a SDP record complete, the event comes */ +} esp_sdp_cb_event_t; + +/** + * @brief SDP record type + */ +typedef enum { + ESP_SDP_TYPE_RAW, /*!< Used to carry raw SDP search data for unknown UUIDs */ + ESP_SDP_TYPE_MAP_MAS, /*!< Message Access Profile - Server */ + ESP_SDP_TYPE_MAP_MNS, /*!< Message Access Profile - Client (Notification Server) */ + ESP_SDP_TYPE_PBAP_PSE, /*!< Phone Book Profile - Server */ + ESP_SDP_TYPE_PBAP_PCE, /*!< Phone Book Profile - Client */ + ESP_SDP_TYPE_OPP_SERVER, /*!< Object Push Profile */ + ESP_SDP_TYPE_SAP_SERVER /*!< SIM Access Profile */ +} esp_bluetooth_sdp_types_t; + +/** + * @brief Some signals need additional pointers, hence we introduce a + * generic way to handle these pointers. + */ +typedef struct bluetooth_sdp_hdr_overlay { + esp_bluetooth_sdp_types_t type; /*!< SDP type */ + esp_bt_uuid_t uuid; /*!< UUID type, include uuid and uuid length */ + uint32_t service_name_length; /*!< Service name length */ + char *service_name; /*!< service name */ + int32_t rfcomm_channel_number; /*!< rfcomm channel number, if not used set to -1*/ + int32_t l2cap_psm; /*!< l2cap psm, if not used set to -1 */ + int32_t profile_version; /*!< profile version */ + + // User pointers, only used for some signals - see esp_bluetooth_sdp_ops_record_t + int user1_ptr_len; /*!< see esp_bluetooth_sdp_ops_record_t */ + uint8_t *user1_ptr; /*!< see esp_bluetooth_sdp_ops_record_t */ + int user2_ptr_len; /*!< see esp_bluetooth_sdp_ops_record_t */ + uint8_t *user2_ptr; /*!< see esp_bluetooth_sdp_ops_record_t */ +} esp_bluetooth_sdp_hdr_overlay_t; + +/** + * @brief Message Access Profile - Server parameters + */ +typedef struct bluetooth_sdp_mas_record { + esp_bluetooth_sdp_hdr_overlay_t hdr; /*!< General info */ + uint32_t mas_instance_id; /*!< MAS Instance ID */ + uint32_t supported_features; /*!< Map supported features */ + uint32_t supported_message_types; /*!< Supported message types */ +} esp_bluetooth_sdp_mas_record_t; + +/** + * @brief Message Access Profile - Client (Notification Server) parameters + */ +typedef struct bluetooth_sdp_mns_record { + esp_bluetooth_sdp_hdr_overlay_t hdr; /*!< General info */ + uint32_t supported_features; /*!< Supported features */ +} esp_bluetooth_sdp_mns_record_t; + +/** + * @brief Phone Book Profile - Server parameters + */ +typedef struct bluetooth_sdp_pse_record { + esp_bluetooth_sdp_hdr_overlay_t hdr; /*!< General info */ + uint32_t supported_features; /*!< Pbap Supported Features */ + uint32_t supported_repositories; /*!< Supported Repositories */ +} esp_bluetooth_sdp_pse_record_t; + +/** + * @brief Phone Book Profile - Client parameters + */ +typedef struct bluetooth_sdp_pce_record { + esp_bluetooth_sdp_hdr_overlay_t hdr; /*!< General info */ +} esp_bluetooth_sdp_pce_record_t; + +/** + * @brief Object Push Profile parameters + */ +typedef struct bluetooth_sdp_ops_record { + esp_bluetooth_sdp_hdr_overlay_t hdr; /*!< General info */ + int supported_formats_list_len; /*!< Supported formats list length */ + uint8_t supported_formats_list[SDP_OPP_SUPPORTED_FORMATS_MAX_LENGTH]; /*!< Supported formats list */ +} esp_bluetooth_sdp_ops_record_t; + +/** + * @brief SIM Access Profile parameters + */ +typedef struct bluetooth_sdp_sap_record { + esp_bluetooth_sdp_hdr_overlay_t hdr; /*!< General info */ +} esp_bluetooth_sdp_sap_record_t; + +/** + * @brief SDP record parameters union + */ +typedef union { + esp_bluetooth_sdp_hdr_overlay_t hdr; /*!< General info */ + esp_bluetooth_sdp_mas_record_t mas; /*!< Message Access Profile - Server */ + esp_bluetooth_sdp_mns_record_t mns; /*!< Message Access Profile - Client (Notification Server) */ + esp_bluetooth_sdp_pse_record_t pse; /*!< Phone Book Profile - Server */ + esp_bluetooth_sdp_pce_record_t pce; /*!< Phone Book Profile - Client */ + esp_bluetooth_sdp_ops_record_t ops; /*!< Object Push Profile */ + esp_bluetooth_sdp_sap_record_t sap; /*!< SIM Access Profile */ +} esp_bluetooth_sdp_record_t; + +/** + * @brief SDP callback parameters union + */ +typedef union { + /** + * @brief ESP_SDP_INIT_EVT + */ + struct sdp_init_evt_param { + esp_sdp_status_t status; /*!< status */ + } init; /*!< SDP callback param of ESP_SDP_INIT_EVT */ + + /** + * @brief ESP_SDP_DEINIT_EVT + */ + struct sdp_deinit_evt_param { + esp_sdp_status_t status; /*!< status */ + } deinit; /*!< SDP callback param of ESP_SDP_DEINIT_EVT */ + + /** + * @brief ESP_SDP_SEARCH_COMP_EVT + */ + struct sdp_search_evt_param { + esp_sdp_status_t status; /*!< status */ + esp_bd_addr_t remote_addr; /*!< remote device address */ + esp_bt_uuid_t sdp_uuid; /*!< service uuid */ + int record_count; /*!< Number of SDP records */ + esp_bluetooth_sdp_record_t *records;/*!< SDP records */ + } search; /*!< SDP callback param of ESP_SDP_SEARCH_COMP_EVT */ + + /** + * @brief ESP_SDP_CREATE_RECORD_COMP_EVT + */ + struct sdp_crate_record_evt_param { + esp_sdp_status_t status; /*!< status */ + int record_handle; /*!< SDP record handle */ + } create_record; /*!< SDP callback param of ESP_SDP_CREATE_RECORD_COMP_EVT */ + + /** + * @brief ESP_SDP_REMOVE_RECORD_COMP_EVT + */ + struct sdp_remove_record_evt_param { + esp_sdp_status_t status; /*!< status */ + } remove_record; /*!< SDP callback param of ESP_SDP_REMOVE_RECORD_COMP_EVT */ + +} esp_sdp_cb_param_t; /*!< SDP callback parameter union type */ + + +/** + * @brief SDP callback function type. + * + * @param event: Event type + * @param param: Point to callback parameter, currently is union type + */ +typedef void (* esp_sdp_cb_t)(esp_sdp_cb_event_t event, esp_sdp_cb_param_t *param); + +/** + * @brief This function is called to init callbacks with SDP module. + * + * @param[in] callback: pointer to the init callback function. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_sdp_register_callback(esp_sdp_cb_t callback); + +/** + * @brief This function is called to init SDP module. + * When the operation is completed, the callback function will be called with ESP_SDP_INIT_EVT. + * This function should be called after esp_bluedroid_enable() completes successfully. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_sdp_init(void); + +/** + * @brief This function is called to de-initialize SDP module. + * The operation will remove all SDP records, then the callback function will be called + * with ESP_SDP_REMOVE_RECORD_COMP_EVT, and the number of ESP_SDP_REMOVE_RECORD_COMP_EVT is + * equal to the number of SDP records.When the operation is completed, the callback function + * will be called with ESP_SDP_DEINIT_EVT. This function should be called after esp_sdp_init() + * completes successfully. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_sdp_deinit(void); + +/** + * @brief This function is called to performs service discovery for the services provided by the given peer device. + * When the operation is completed, the callback function will be called with ESP_SDP_SEARCH_COMP_EVT. + * This funciton must be called after esp_sdp_init() successful and before esp_sdp_deinit(). + * + * @param[in] bd_addr: Remote device bluetooth device address. + * @param[in] uuid: Service UUID of the remote device. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_sdp_search_record(esp_bd_addr_t bd_addr, esp_bt_uuid_t uuid); + +/** + * @brief This function is called to create SDP records. + * When the operation is completed, the callback function will be called with ESP_SDP_CREATE_RECORD_COMP_EVT. + * This funciton must be called after esp_sdp_init() successful and before esp_sdp_deinit(). + * + * @param[in] record: The SDP record to create. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_sdp_create_record(esp_bluetooth_sdp_record_t *record); + +/** + * @brief This function is called to remove a SDP record. + * When the operation is completed, the callback function will be called with ESP_SDP_REMOVE_RECORD_COMP_EVT. + * This funciton must be called after esp_sdp_init() successful and before esp_sdp_deinit(). + * + * @param[in] record_handle: The SDP record handle. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_sdp_remove_record(int record_handle); + +#ifdef __cplusplus +} +#endif + +#endif ///__ESP_SDP_API_H__ diff --git a/components/bt/host/bluedroid/bta/include/bta/bta_sdp_api.h b/components/bt/host/bluedroid/bta/include/bta/bta_sdp_api.h index 1d32b9204f..2d0d0eb4b7 100644 --- a/components/bt/host/bluedroid/bta/include/bta/bta_sdp_api.h +++ b/components/bt/host/bluedroid/bta/include/bta/bta_sdp_api.h @@ -40,12 +40,13 @@ typedef UINT8 tBTA_SDP_STATUS; /* SDP I/F callback events */ /* events received by tBTA_SDP_DM_CBACK */ -#define BTA_SDP_ENABLE_EVT 0 /* SDP service i/f enabled*/ -#define BTA_SDP_SEARCH_EVT 1 /* SDP Service started */ -#define BTA_SDP_SEARCH_COMP_EVT 2 /* SDP search complete */ -#define BTA_SDP_CREATE_RECORD_USER_EVT 3 /* SDP search complete */ -#define BTA_SDP_REMOVE_RECORD_USER_EVT 4 /* SDP search complete */ -#define BTA_SDP_MAX_EVT 5 /* max number of SDP events */ +#define BTA_SDP_ENABLE_EVT 0 /* SDP service enabled*/ +#define BTA_SDP_DISENABLE_EVT 1 /* SDP service disenabled*/ +#define BTA_SDP_SEARCH_EVT 2 /* SDP search started */ +#define BTA_SDP_SEARCH_COMP_EVT 3 /* SDP search complete */ +#define BTA_SDP_CREATE_RECORD_USER_EVT 4 /* SDP create record complete */ +#define BTA_SDP_REMOVE_RECORD_USER_EVT 5 /* SDP remove reocrd complete */ +#define BTA_SDP_MAX_EVT 6 /* max number of SDP events */ #define BTA_SDP_MAX_RECORDS 15 @@ -63,6 +64,7 @@ typedef struct { typedef union { tBTA_SDP_STATUS status; /* BTA_SDP_SEARCH_EVT */ tBTA_SDP_SEARCH_COMP sdp_search_comp; /* BTA_SDP_SEARCH_COMP_EVT */ + int handle; } tBTA_SDP; /* SDP DM Interface callback */ diff --git a/components/bt/host/bluedroid/bta/sdp/bta_sdp_act.c b/components/bt/host/bluedroid/bta/sdp/bta_sdp_act.c index 5ce91a5f03..8f06145ce5 100644 --- a/components/bt/host/bluedroid/bta/sdp/bta_sdp_act.c +++ b/components/bt/host/bluedroid/bta/sdp/bta_sdp_act.c @@ -439,9 +439,10 @@ static void bta_sdp_search_cback(UINT16 result, void *user_data) void bta_sdp_enable(tBTA_SDP_MSG *p_data) { APPL_TRACE_DEBUG("%s in, sdp_active:%d\n", __func__, bta_sdp_cb.sdp_active); - tBTA_SDP_STATUS status = BTA_SDP_SUCCESS; + tBTA_SDP bta_sdp; + bta_sdp.status = BTA_SDP_SUCCESS; bta_sdp_cb.p_dm_cback = p_data->enable.p_cback; - bta_sdp_cb.p_dm_cback(BTA_SDP_ENABLE_EVT, (tBTA_SDP *)&status, NULL); + bta_sdp_cb.p_dm_cback(BTA_SDP_ENABLE_EVT, (tBTA_SDP *)&bta_sdp, NULL); } /******************************************************************************* @@ -523,8 +524,11 @@ void bta_sdp_search(tBTA_SDP_MSG *p_data) void bta_sdp_create_record(tBTA_SDP_MSG *p_data) { APPL_TRACE_DEBUG("%s() event: %d\n", __func__, p_data->record.hdr.event); + tBTA_SDP bta_sdp; + bta_sdp.status = BTA_SDP_SUCCESS; + bta_sdp.handle = (int)p_data->record.user_data; if (bta_sdp_cb.p_dm_cback) { - bta_sdp_cb.p_dm_cback(BTA_SDP_CREATE_RECORD_USER_EVT, NULL, p_data->record.user_data); + bta_sdp_cb.p_dm_cback(BTA_SDP_CREATE_RECORD_USER_EVT, &bta_sdp, p_data->record.user_data); } } @@ -540,8 +544,10 @@ void bta_sdp_create_record(tBTA_SDP_MSG *p_data) void bta_sdp_remove_record(tBTA_SDP_MSG *p_data) { APPL_TRACE_DEBUG("%s() event: %d\n", __func__, p_data->record.hdr.event); + tBTA_SDP bta_sdp; + bta_sdp.status = BTA_SDP_SUCCESS; if (bta_sdp_cb.p_dm_cback) { - bta_sdp_cb.p_dm_cback(BTA_SDP_REMOVE_RECORD_USER_EVT, NULL, p_data->record.user_data); + bta_sdp_cb.p_dm_cback(BTA_SDP_REMOVE_RECORD_USER_EVT, &bta_sdp, p_data->record.user_data); } } diff --git a/components/bt/host/bluedroid/bta/sdp/bta_sdp_api.c b/components/bt/host/bluedroid/bta/sdp/bta_sdp_api.c index e26f93910c..cafb1a4a4c 100644 --- a/components/bt/host/bluedroid/bta/sdp/bta_sdp_api.c +++ b/components/bt/host/bluedroid/bta/sdp/bta_sdp_api.c @@ -102,6 +102,8 @@ tBTA_SDP_STATUS BTA_SdpEnable(tBTA_SDP_DM_CBACK *p_cback) tBTA_SDP_STATUS BTA_SdpDisable(void) { tBTA_SDP_STATUS status = BTA_SDP_SUCCESS; + + bta_sys_deregister(BTA_ID_SDP); #if BTA_DYNAMIC_MEMORY == TRUE /* Free buffer for SDP configuration structure */ osi_free(p_bta_sdp_cfg->p_sdp_db); diff --git a/components/bt/host/bluedroid/btc/profile/std/include/bt_sdp.h b/components/bt/host/bluedroid/btc/profile/std/include/bt_sdp.h index b2da0873a5..63ca09a80f 100644 --- a/components/bt/host/bluedroid/btc/profile/std/include/bt_sdp.h +++ b/components/bt/host/bluedroid/btc/profile/std/include/bt_sdp.h @@ -20,6 +20,7 @@ #include // #include "bluetooth.h" #include "common/bt_defs.h" +#include "esp_bt_defs.h" #define SDP_OPP_SUPPORTED_FORMATS_MAX_LENGTH 15 @@ -38,7 +39,7 @@ typedef enum { typedef struct _bluetooth_sdp_hdr { bluetooth_sdp_types type; - bt_uuid_t uuid; + esp_bt_uuid_t uuid; uint32_t service_name_length; char *service_name; int32_t rfcomm_channel_number; @@ -52,7 +53,7 @@ typedef struct _bluetooth_sdp_hdr { */ typedef struct _bluetooth_sdp_hdr_overlay { bluetooth_sdp_types type; - bt_uuid_t uuid; + esp_bt_uuid_t bt_uuid; uint32_t service_name_length; char *service_name; int32_t rfcomm_channel_number; diff --git a/components/bt/host/bluedroid/btc/profile/std/include/btc_sdp.h b/components/bt/host/bluedroid/btc/profile/std/include/btc_sdp.h new file mode 100644 index 0000000000..60772fa3c4 --- /dev/null +++ b/components/bt/host/bluedroid/btc/profile/std/include/btc_sdp.h @@ -0,0 +1,53 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __BTC_SDP_H__ +#define __BTC_SDP_H__ + +#include "btc/btc_task.h" +#include "esp_bt_defs.h" +#include "common/bt_target.h" +#include "bta/bta_sdp_api.h" +#include "bt_sdp.h" + +#if (defined BTC_SDP_INCLUDED && BTC_SDP_INCLUDED == TRUE) + +typedef enum { + BTC_SDP_ACT_INIT = 0, + BTC_SDP_ACT_DEINIT, + BTC_SDP_ACT_SEARCH, + BTC_SDP_ACT_CREATE_RECORD, + BTC_SDP_ACT_REMOVE_RECORD, +} btc_sdp_act_t; + +/* btc_sdp_args_t */ +typedef union { + //BTC_SDP_ACT_SEARCH + struct search_record_arg { + BD_ADDR bd_addr; + tSDP_UUID sdp_uuid; + } search; + + //BTC_SDP_ACT_CREATE_RECORD + struct creat_record_arg { + bluetooth_sdp_record *record; + } creat_record; + + //BTC_SDP_ACT_REMOVE_RECORD + struct remove_record_arg { + int record_handle; + } remove_record; + +} btc_sdp_args_t; + +void btc_sdp_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); +void btc_sdp_arg_deep_free(btc_msg_t *msg); + +void btc_sdp_call_handler(btc_msg_t *msg); +void btc_sdp_cb_handler(btc_msg_t *msg); + +#endif ///defined BTC_SDP_INCLUDED && BTC_SDP_INCLUDED == TRUE +#endif ///__BTC_SDP_H__ diff --git a/components/bt/host/bluedroid/btc/profile/std/sdp/btc_sdp.c b/components/bt/host/bluedroid/btc/profile/std/sdp/btc_sdp.c new file mode 100644 index 0000000000..41132739ce --- /dev/null +++ b/components/bt/host/bluedroid/btc/profile/std/sdp/btc_sdp.c @@ -0,0 +1,1023 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "btc_sdp.h" +#include "btc/btc_manage.h" +#include "btc/btc_task.h" +#include "bta/bta_sdp_api.h" +#include "bta/bta_sys.h" +#include "bta/utl.h" +#include "bt_sdp.h" +#include "osi/mutex.h" +#include "osi/allocator.h" +#include "esp_sdp_api.h" + +#if (defined BTC_SDP_INCLUDED && BTC_SDP_INCLUDED == TRUE) + +typedef enum { + SDP_RECORD_FREE = 0, + SDP_RECORD_ALLOCED, +} sdp_state_t; + +typedef struct { + sdp_state_t state; + int sdp_handle; + bluetooth_sdp_record* record_data; +} sdp_slot_t; + +typedef struct { + sdp_slot_t *sdp_slots[SDP_MAX_RECORDS]; + osi_mutex_t sdp_slot_mutex; +} sdp_local_param_t; + +#if SDP_DYNAMIC_MEMORY == FALSE +static sdp_local_param_t sdp_local_param; +#else +static sdp_local_param_t *sdp_local_param_ptr; +#define sdp_local_param (*sdp_local_param_ptr) +#endif + +#if SDP_DYNAMIC_MEMORY == FALSE +#define is_sdp_init() (sdp_local_param.sdp_slot_mutex != NULL) +#else +#define is_sdp_init() (&sdp_local_param != NULL && sdp_local_param.sdp_slot_mutex != NULL) +#endif + + +static inline void btc_sdp_cb_to_app(esp_sdp_cb_event_t event, esp_sdp_cb_param_t *param) +{ + esp_sdp_cb_t btc_sdp_cb = (esp_sdp_cb_t)btc_profile_cb_get(BTC_PID_SDP); + if (btc_sdp_cb) { + btc_sdp_cb(event, param); + } +} + +static void sdp_disable_handler(void) +{ + btc_msg_t msg; + bt_status_t status; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_SDP; + msg.act = BTA_SDP_DISENABLE_EVT; + + status = btc_transfer_context(&msg, NULL, 0, NULL); + + if (status != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed", __func__); + } +} + +static int get_sdp_records_size(bluetooth_sdp_record* in_record, int count) +{ + bluetooth_sdp_record* record = in_record; + int records_size = 0; + + for(int i = 0; i < count; i++) { + record = &in_record[i]; + records_size += sizeof(bluetooth_sdp_record); + records_size += record->hdr.service_name_length; + if(record->hdr.service_name_length > 0){ + records_size++; /* + '\0' termination of string */ + } + records_size += record->hdr.user1_ptr_len; + records_size += record->hdr.user2_ptr_len; + } + + return records_size; +} + +static void set_sdp_handle(int id, int handle) +{ + sdp_slot_t *slot = NULL; + + BTC_TRACE_DEBUG("%s() id=%d to handle=0x%08x", __func__, id, handle); + + if(id >= SDP_MAX_RECORDS) { + BTC_TRACE_ERROR("%s() failed - id %d is invalid", __func__, id); + return; + } + + osi_mutex_lock(&sdp_local_param.sdp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + slot = sdp_local_param.sdp_slots[id]; + if (slot == NULL) { + osi_mutex_unlock(&sdp_local_param.sdp_slot_mutex); + BTC_TRACE_ERROR("%s() id=%d to handle=0x%08x, set failed", __func__, id, handle); + return; + } + slot->sdp_handle = handle; + osi_mutex_unlock(&sdp_local_param.sdp_slot_mutex); +} + +static sdp_slot_t *start_create_sdp(int id) +{ + sdp_slot_t *sdp_slot = NULL; + + if(id >= SDP_MAX_RECORDS) { + BTC_TRACE_ERROR("%s() failed - id %d is invalid", __func__, id); + return NULL; + } + + osi_mutex_lock(&sdp_local_param.sdp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + sdp_slot = sdp_local_param.sdp_slots[id]; + if (sdp_slot == NULL) { + BTC_TRACE_ERROR("%s() id = %d ", __func__, id); + } else if(sdp_slot->state != SDP_RECORD_ALLOCED) { + BTC_TRACE_ERROR("%s() failed - state for id %d is state = %d expected %d", __func__, + id, sdp_local_param.sdp_slots[id]->state, SDP_RECORD_ALLOCED); + /* The record have been removed before this event occurred - e.g. deinit */ + sdp_slot = NULL; + } + osi_mutex_unlock(&sdp_local_param.sdp_slot_mutex); + + return sdp_slot; +} + +/* Deep copy all content of in_records into out_records. + * out_records must point to a chunk of memory large enough to contain all + * the data. Use getSdpRecordsSize() to calculate the needed size. */ +static void copy_sdp_records(bluetooth_sdp_record* in_records, bluetooth_sdp_record* out_records, int count) +{ + bluetooth_sdp_record *in_record; + bluetooth_sdp_record *out_record; + char *free_ptr = (char*)(&out_records[count]); /* set pointer to after the last entry */ + + for(int i = 0; i < count; i++) { + in_record = &in_records[i]; + out_record = &out_records[i]; + *out_record = *in_record; + + if(in_record->hdr.service_name == NULL || in_record->hdr.service_name_length == 0) { + out_record->hdr.service_name = NULL; + out_record->hdr.service_name_length = 0; + } else { + out_record->hdr.service_name = free_ptr; // Update service_name pointer + // Copy string + memcpy(free_ptr, in_record->hdr.service_name, in_record->hdr.service_name_length); + free_ptr += in_record->hdr.service_name_length; + *(free_ptr) = '\0'; // Set '\0' termination of string + free_ptr++; + } + if(in_record->hdr.user1_ptr != NULL) { + out_record->hdr.user1_ptr = (UINT8*)free_ptr; // Update pointer + memcpy(free_ptr, in_record->hdr.user1_ptr, in_record->hdr.user1_ptr_len); // Copy content + free_ptr += in_record->hdr.user1_ptr_len; + } + if(in_record->hdr.user2_ptr != NULL) { + out_record->hdr.user2_ptr = (UINT8*)free_ptr; // Update pointer + memcpy(free_ptr, in_record->hdr.user2_ptr, in_record->hdr.user2_ptr_len); // Copy content + free_ptr += in_record->hdr.user2_ptr_len; + } + } +} + +static int alloc_sdp_slot(bluetooth_sdp_record* in_record) +{ + int i; + int record_size = get_sdp_records_size(in_record, 1); + bluetooth_sdp_record *record = NULL; + sdp_slot_t **slot = NULL; + + record = osi_malloc(record_size); + if (record == NULL) { + BTC_TRACE_ERROR("%s() osi_malloc failed!", __func__); + return -1; + } + + copy_sdp_records(in_record, record, 1); + + osi_mutex_lock(&sdp_local_param.sdp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + for(i = 0; i < SDP_MAX_RECORDS; i++) + { + slot = &sdp_local_param.sdp_slots[i]; + if ((*slot) == NULL) { + if (((*slot) = (sdp_slot_t *)osi_malloc(sizeof(sdp_slot_t))) == NULL) { + osi_mutex_unlock(&sdp_local_param.sdp_slot_mutex); + BTC_TRACE_ERROR("%s() osi_malloc slot failed!", __func__); + osi_free(record); + return -1; + } + (*slot)->state = SDP_RECORD_ALLOCED; + (*slot)->record_data = record; + break; + } + } + osi_mutex_unlock(&sdp_local_param.sdp_slot_mutex); + if(i >= SDP_MAX_RECORDS) { + BTC_TRACE_ERROR("%s() failed - no more free slots!", __func__); + osi_free(record); + return -1; + } + + return i; +} + +static int free_sdp_slot(int id) +{ + int handle = -1; + bluetooth_sdp_record *record = NULL; + sdp_slot_t *slot = NULL; + + if(id >= SDP_MAX_RECORDS) { + APPL_TRACE_ERROR("%s() failed - id %d is invalid", __func__, id); + return handle; + } + slot = sdp_local_param.sdp_slots[id]; + if (slot == NULL) { + // already freed + return handle; + } + + osi_mutex_lock(&sdp_local_param.sdp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT); + handle = slot->sdp_handle; + if (slot->state != SDP_RECORD_FREE) { + /* safe a copy of the pointer, and free after unlock() */ + record = slot->record_data; + } + osi_mutex_unlock(&sdp_local_param.sdp_slot_mutex); + + if(record != NULL) { + osi_free(record); + } else { + // Record have already been freed + handle = -1; + } + osi_free(slot); + slot = NULL; + + return handle; +} + +/* Create a MAP MAS SDP record based on information stored in a bluetooth_sdp_mas_record */ +static int add_maps_sdp(const bluetooth_sdp_mas_record* rec) +{ + tSDP_PROTOCOL_ELEM protoList [3]; + UINT16 service = UUID_SERVCLASS_MESSAGE_ACCESS; + UINT16 browse = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP; + bool status = true; + UINT32 sdp_handle = 0; + UINT8 temp[4]; + UINT8* p_temp = temp; + + BTC_TRACE_DEBUG("%s(): MASID = 0x%02x, scn 0x%02x, psm = 0x%04x\n service name %s", __func__, + rec->mas_instance_id, rec->hdr.rfcomm_channel_number, + rec->hdr.l2cap_psm, rec->hdr.service_name); + + BTC_TRACE_DEBUG(" msg_types: 0x%02x, feature_bits: 0x%08x", + rec->supported_message_types, rec->supported_features); + + if ((sdp_handle = SDP_CreateRecord()) == 0) { + BTC_TRACE_ERROR("%s() - Unable to register MAPS Service", __func__); + return sdp_handle; + } + + /* add service class */ + status &= SDP_AddServiceClassIdList(sdp_handle, 1, &service); + memset( protoList, 0 , 3*sizeof(tSDP_PROTOCOL_ELEM) ); + + /* add protocol list, including RFCOMM scn */ + protoList[0].protocol_uuid = UUID_PROTOCOL_L2CAP; + protoList[0].num_params = 0; + protoList[1].protocol_uuid = UUID_PROTOCOL_RFCOMM; + protoList[1].num_params = 1; + protoList[1].params[0] = rec->hdr.rfcomm_channel_number; + protoList[2].protocol_uuid = UUID_PROTOCOL_OBEX; + protoList[2].num_params = 0; + status &= SDP_AddProtocolList(sdp_handle, 3, protoList); + + /* Add a name entry */ + status &= SDP_AddAttribute(sdp_handle, + (UINT16)ATTR_ID_SERVICE_NAME, + (UINT8)TEXT_STR_DESC_TYPE, + (UINT32)(rec->hdr.service_name_length + 1), + (UINT8 *)rec->hdr.service_name); + + /* Add in the Bluetooth Profile Descriptor List */ + status &= SDP_AddProfileDescriptorList(sdp_handle, + UUID_SERVCLASS_MAP_PROFILE, + rec->hdr.profile_version); + + /* Add MAS instance ID */ + status &= SDP_AddAttribute(sdp_handle, ATTR_ID_MAS_INSTANCE_ID, UINT_DESC_TYPE, + (UINT32)1, (UINT8*)&rec->mas_instance_id); + + /* Add supported message types */ + status &= SDP_AddAttribute(sdp_handle, ATTR_ID_SUPPORTED_MSG_TYPE, UINT_DESC_TYPE, + (UINT32)1, (UINT8*)&rec->supported_message_types); + + /* Add supported feature */ + UINT32_TO_BE_STREAM(p_temp, rec->supported_features); + status &= SDP_AddAttribute(sdp_handle, ATTR_ID_MAP_SUPPORTED_FEATURES, + UINT_DESC_TYPE, (UINT32)4, temp); + + /* Add the L2CAP PSM if present */ + if(rec->hdr.l2cap_psm != -1) { + p_temp = temp;// The macro modifies p_temp, hence rewind. + UINT16_TO_BE_STREAM(p_temp, rec->hdr.l2cap_psm); + status &= SDP_AddAttribute(sdp_handle, ATTR_ID_GOEP_L2CAP_PSM, + UINT_DESC_TYPE, (UINT32)2, temp); + } + + /* Make the service browseable */ + status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse); + + if (!status) { + SDP_DeleteRecord(sdp_handle); + sdp_handle = 0; + BTC_TRACE_ERROR("%s() FAILED", __func__); + } else { + bta_sys_add_uuid(service); /* UUID_SERVCLASS_MESSAGE_ACCESS */ + BTC_TRACE_DEBUG("%s(): SDP Registered (handle 0x%08x)", __func__, sdp_handle); + } + + return sdp_handle; +} + +/* Create a MAP MNS SDP record based on information stored in a bluetooth_sdp_mns_record */ +static int add_mapc_sdp(const bluetooth_sdp_mns_record* rec) +{ + tSDP_PROTOCOL_ELEM protoList [3]; + UINT16 service = UUID_SERVCLASS_MESSAGE_NOTIFICATION; + UINT16 browse = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP; + bool status = true; + UINT32 sdp_handle = 0; + UINT8 temp[4]; + UINT8* p_temp = temp; + + BTC_TRACE_DEBUG("%s(): scn 0x%02x, psm = 0x%04x\n service name %s", __func__, + rec->hdr.rfcomm_channel_number, rec->hdr.l2cap_psm, rec->hdr.service_name); + + BTC_TRACE_DEBUG(" feature_bits: 0x%08x", rec->supported_features); + + if ((sdp_handle = SDP_CreateRecord()) == 0) { + BTC_TRACE_ERROR("%s(): Unable to register MAP Notification Service", __func__); + return sdp_handle; + } + + /* add service class */ + status &= SDP_AddServiceClassIdList(sdp_handle, 1, &service); + memset( protoList, 0 , 3*sizeof(tSDP_PROTOCOL_ELEM) ); + + /* add protocol list, including RFCOMM scn */ + protoList[0].protocol_uuid = UUID_PROTOCOL_L2CAP; + protoList[0].num_params = 0; + protoList[1].protocol_uuid = UUID_PROTOCOL_RFCOMM; + protoList[1].num_params = 1; + protoList[1].params[0] = rec->hdr.rfcomm_channel_number; + protoList[2].protocol_uuid = UUID_PROTOCOL_OBEX; + protoList[2].num_params = 0; + status &= SDP_AddProtocolList(sdp_handle, 3, protoList); + + /* Add a name entry */ + status &= SDP_AddAttribute(sdp_handle, + (UINT16)ATTR_ID_SERVICE_NAME, + (UINT8)TEXT_STR_DESC_TYPE, + (UINT32)(rec->hdr.service_name_length + 1), + (UINT8 *)rec->hdr.service_name); + + /* Add in the Bluetooth Profile Descriptor List */ + status &= SDP_AddProfileDescriptorList(sdp_handle, + UUID_SERVCLASS_MAP_PROFILE, + rec->hdr.profile_version); + + /* Add supported feature */ + UINT32_TO_BE_STREAM(p_temp, rec->supported_features); + status &= SDP_AddAttribute(sdp_handle, ATTR_ID_MAP_SUPPORTED_FEATURES, + UINT_DESC_TYPE, (UINT32)4, temp); + + /* Add the L2CAP PSM if present */ + if(rec->hdr.l2cap_psm != -1) { + p_temp = temp;// The macro modifies p_temp, hence rewind. + UINT16_TO_BE_STREAM(p_temp, rec->hdr.l2cap_psm); + status &= SDP_AddAttribute(sdp_handle, ATTR_ID_GOEP_L2CAP_PSM, + UINT_DESC_TYPE, (UINT32)2, temp); + } + + /* Make the service browseable */ + status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse); + + if (!status) { + SDP_DeleteRecord(sdp_handle); + sdp_handle = 0; + BTC_TRACE_ERROR("%s() FAILED", __func__); + } else { + bta_sys_add_uuid(service); /* UUID_SERVCLASS_MESSAGE_ACCESS */ + BTC_TRACE_DEBUG("%s(): SDP Registered (handle 0x%08x)", __func__, sdp_handle); + } + + return sdp_handle; +} + +/* Create a PBAP Server SDP record based on information stored in a bluetooth_sdp_pse_record */ +static int add_pbaps_sdp(const bluetooth_sdp_pse_record* rec) +{ + tSDP_PROTOCOL_ELEM protoList [3]; + UINT16 service = UUID_SERVCLASS_PBAP_PSE; + UINT16 browse = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP; + bool status = true; + UINT32 sdp_handle = 0; + UINT8 temp[4]; + UINT8* p_temp = temp; + + BTC_TRACE_DEBUG("%s(): scn 0x%02x, psm = 0x%04x\n service name %s", __func__, + rec->hdr.rfcomm_channel_number, rec->hdr.l2cap_psm, rec->hdr.service_name); + + BTC_TRACE_DEBUG(" supported_repositories: 0x%08x, feature_bits: 0x%08x", + rec->supported_repositories, rec->supported_features); + + if ((sdp_handle = SDP_CreateRecord()) == 0) { + BTC_TRACE_ERROR("%s(): Unable to register PBAP Server Service", __func__); + return sdp_handle; + } + + /* add service class */ + status &= SDP_AddServiceClassIdList(sdp_handle, 1, &service); + memset( protoList, 0 , 3*sizeof(tSDP_PROTOCOL_ELEM) ); + + /* add protocol list, including RFCOMM scn */ + protoList[0].protocol_uuid = UUID_PROTOCOL_L2CAP; + protoList[0].num_params = 0; + protoList[1].protocol_uuid = UUID_PROTOCOL_RFCOMM; + protoList[1].num_params = 1; + protoList[1].params[0] = rec->hdr.rfcomm_channel_number; + protoList[2].protocol_uuid = UUID_PROTOCOL_OBEX; + protoList[2].num_params = 0; + status &= SDP_AddProtocolList(sdp_handle, 3, protoList); + + /* Add a name entry */ + status &= SDP_AddAttribute(sdp_handle, + (UINT16)ATTR_ID_SERVICE_NAME, + (UINT8)TEXT_STR_DESC_TYPE, + (UINT32)(rec->hdr.service_name_length + 1), + (UINT8 *)rec->hdr.service_name); + + /* Add in the Bluetooth Profile Descriptor List */ + status &= SDP_AddProfileDescriptorList(sdp_handle, + UUID_SERVCLASS_PHONE_ACCESS, + rec->hdr.profile_version); + + /* Add supported repositories 1 byte */ + status &= SDP_AddAttribute(sdp_handle, ATTR_ID_SUPPORTED_REPOSITORIES, + UINT_DESC_TYPE, (UINT32)1, (UINT8*)&rec->supported_repositories); + + /* Add supported feature 4 bytes*/ + UINT32_TO_BE_STREAM(p_temp, rec->supported_features); + status &= SDP_AddAttribute(sdp_handle, ATTR_ID_PBAP_SUPPORTED_FEATURES, + UINT_DESC_TYPE, (UINT32)4, temp); + + /* Add the L2CAP PSM if present */ + if(rec->hdr.l2cap_psm != -1) { + p_temp = temp;// The macro modifies p_temp, hence rewind. + UINT16_TO_BE_STREAM(p_temp, rec->hdr.l2cap_psm); + status &= SDP_AddAttribute(sdp_handle, ATTR_ID_GOEP_L2CAP_PSM, + UINT_DESC_TYPE, (UINT32)2, temp); + } + + /* Make the service browseable */ + status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse); + + if (!status) { + SDP_DeleteRecord(sdp_handle); + sdp_handle = 0; + BTC_TRACE_ERROR("%s() FAILED", __func__); + } else { + bta_sys_add_uuid(service); /* UUID_SERVCLASS_MESSAGE_ACCESS */ + BTC_TRACE_DEBUG("%s(): SDP Registered (handle 0x%08x)", __func__, sdp_handle); + } + + return sdp_handle; +} + + +/* Create a OPP Server SDP record based on information stored in a bluetooth_sdp_ops_record */ +static int add_opps_sdp(const bluetooth_sdp_ops_record* rec) +{ + tSDP_PROTOCOL_ELEM protoList [3]; + UINT16 service = UUID_SERVCLASS_OBEX_OBJECT_PUSH; + UINT16 browse = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP; + UINT8 type_len[rec->supported_formats_list_len]; + UINT8 desc_type[rec->supported_formats_list_len]; + UINT8 *type_value[rec->supported_formats_list_len]; + bool status = true; + UINT32 sdp_handle = 0; + UINT8 temp[4]; + UINT8* p_temp = temp; + tBTA_UTL_COD cod; + int i,j; + + BTC_TRACE_DEBUG("%s(): scn 0x%02x, psm = 0x%04x\n service name %s", __func__, + rec->hdr.rfcomm_channel_number, rec->hdr.l2cap_psm, rec->hdr.service_name); + + BTC_TRACE_DEBUG(" supported formats count: %d", + rec->supported_formats_list_len); + + if ((sdp_handle = SDP_CreateRecord()) == 0) { + BTC_TRACE_ERROR("%s(): Unable to register Object Push Server Service", __func__); + return sdp_handle; + } + + /* add service class */ + status &= SDP_AddServiceClassIdList(sdp_handle, 1, &service); + memset( protoList, 0 , 3*sizeof(tSDP_PROTOCOL_ELEM) ); + + /* add protocol list, including RFCOMM scn */ + protoList[0].protocol_uuid = UUID_PROTOCOL_L2CAP; + protoList[0].num_params = 0; + protoList[1].protocol_uuid = UUID_PROTOCOL_RFCOMM; + protoList[1].num_params = 1; + protoList[1].params[0] = rec->hdr.rfcomm_channel_number; + protoList[2].protocol_uuid = UUID_PROTOCOL_OBEX; + protoList[2].num_params = 0; + status &= SDP_AddProtocolList(sdp_handle, 3, protoList); + + /* Add a name entry */ + status &= SDP_AddAttribute(sdp_handle, + (UINT16)ATTR_ID_SERVICE_NAME, + (UINT8)TEXT_STR_DESC_TYPE, + (UINT32)(rec->hdr.service_name_length + 1), + (UINT8 *)rec->hdr.service_name); + + /* Add in the Bluetooth Profile Descriptor List */ + status &= SDP_AddProfileDescriptorList(sdp_handle, + UUID_SERVCLASS_OBEX_OBJECT_PUSH, + rec->hdr.profile_version); + + /* add sequence for supported types */ + for (i = 0, j = 0; i < rec->supported_formats_list_len; i++) { + type_value[j] = (UINT8 *) &rec->supported_formats_list[i]; + desc_type[j] = UINT_DESC_TYPE; + type_len[j++] = 1; + } + + status &= SDP_AddSequence(sdp_handle, (UINT16) ATTR_ID_SUPPORTED_FORMATS_LIST, + (UINT8) rec->supported_formats_list_len, desc_type, type_len, type_value); + + /* Add the L2CAP PSM if present */ + if(rec->hdr.l2cap_psm != -1) { + p_temp = temp;// The macro modifies p_temp, hence rewind. + UINT16_TO_BE_STREAM(p_temp, rec->hdr.l2cap_psm); + status &= SDP_AddAttribute(sdp_handle, ATTR_ID_GOEP_L2CAP_PSM, + UINT_DESC_TYPE, (UINT32)2, temp); + } + + /* Make the service browseable */ + status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse); + + if (!status) { + SDP_DeleteRecord(sdp_handle); + sdp_handle = 0; + BTC_TRACE_ERROR("%s() FAILED", __func__); + } else { + /* set class of device */ + cod.service = BTM_COD_SERVICE_OBJ_TRANSFER; + utl_set_device_class(&cod, BTA_UTL_SET_COD_SERVICE_CLASS); + + bta_sys_add_uuid(service); /* UUID_SERVCLASS_OBEX_OBJECT_PUSH */ + BTC_TRACE_DEBUG("%s(): SDP Registered (handle 0x%08x)", __func__, sdp_handle); + } + + return sdp_handle; +} + +// Create a Sim Access Profile SDP record based on information stored in a bluetooth_sdp_sap_record. +static int add_saps_sdp(const bluetooth_sdp_sap_record* rec) +{ + tSDP_PROTOCOL_ELEM protoList [2]; + UINT16 services[2]; + UINT16 browse = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP; + bool status = true; + UINT32 sdp_handle = 0; + + BTC_TRACE_DEBUG("%s(): scn 0x%02x, service name %s", __func__, + rec->hdr.rfcomm_channel_number, rec->hdr.service_name); + + if ((sdp_handle = SDP_CreateRecord()) == 0) { + BTC_TRACE_ERROR("%s(): Unable to register SAPS Service", __func__); + return sdp_handle; + } + + services[0] = UUID_SERVCLASS_SAP; + services[1] = UUID_SERVCLASS_GENERIC_TELEPHONY; + + // add service class + status &= SDP_AddServiceClassIdList(sdp_handle, 2, services); + memset(protoList, 0, 2 * sizeof(tSDP_PROTOCOL_ELEM)); + + // add protocol list, including RFCOMM scn + protoList[0].protocol_uuid = UUID_PROTOCOL_L2CAP; + protoList[0].num_params = 0; + protoList[1].protocol_uuid = UUID_PROTOCOL_RFCOMM; + protoList[1].num_params = 1; + protoList[1].params[0] = rec->hdr.rfcomm_channel_number; + status &= SDP_AddProtocolList(sdp_handle, 2, protoList); + + // Add a name entry + status &= SDP_AddAttribute(sdp_handle, + (UINT16)ATTR_ID_SERVICE_NAME, + (UINT8)TEXT_STR_DESC_TYPE, + (UINT32)(rec->hdr.service_name_length + 1), + (UINT8 *)rec->hdr.service_name); + + // Add in the Bluetooth Profile Descriptor List + status &= SDP_AddProfileDescriptorList(sdp_handle, + UUID_SERVCLASS_SAP, + rec->hdr.profile_version); + + // Make the service browseable + status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse); + + if (!status) { + SDP_DeleteRecord(sdp_handle); + sdp_handle = 0; + BTC_TRACE_ERROR("%s(): FAILED deleting record", __func__); + } else { + bta_sys_add_uuid(UUID_SERVCLASS_SAP); + BTC_TRACE_DEBUG("%s(): SDP Registered (handle 0x%08x)", __func__, sdp_handle); + } + + return sdp_handle; +} + +static int btc_handle_create_record_event(int id) +{ + int handle = -1; + const sdp_slot_t *sdp_slot = NULL; + + BTC_TRACE_DEBUG("Sdp Server %s", __func__); + + sdp_slot = start_create_sdp(id); + if(sdp_slot != NULL) { + bluetooth_sdp_record* record = sdp_slot->record_data; + switch(record->hdr.type) { + case SDP_TYPE_MAP_MAS: + handle = add_maps_sdp(&record->mas); + break; + case SDP_TYPE_MAP_MNS: + handle = add_mapc_sdp(&record->mns); + break; + case SDP_TYPE_PBAP_PSE: + handle = add_pbaps_sdp(&record->pse); + break; + case SDP_TYPE_OPP_SERVER: + handle = add_opps_sdp(&record->ops); + break; + case SDP_TYPE_SAP_SERVER: + handle = add_saps_sdp(&record->sap); + break; + case SDP_TYPE_PBAP_PCE: + // break; not yet supported + default: + BTC_TRACE_DEBUG("Record type %d is not supported",record->hdr.type); + break; + } + if(handle != -1) { + set_sdp_handle(id, handle); + } + } + + return handle; +} + +static bool btc_sdp_remove_record_event(int handle) +{ + bool result = false; + + BTC_TRACE_DEBUG("Sdp Server %s", __func__); + + if(handle != -1 && handle != 0) { + result = SDP_DeleteRecord(handle); + if(result == false) { + BTC_TRACE_ERROR(" Unable to remove handle 0x%08x", handle); + } + } + + return result; +} + +static void btc_sdp_dm_cback(tBTA_SDP_EVT event, tBTA_SDP* p_data, void* user_data) +{ + btc_msg_t msg; + bt_status_t status; + + switch (event) { + case BTA_SDP_CREATE_RECORD_USER_EVT: { + if (p_data->status == BTA_SDP_SUCCESS) { + if(btc_handle_create_record_event((int)user_data) < 0) { + p_data->status = BTA_SDP_FAILURE; + } + } + } + break; + case BTA_SDP_REMOVE_RECORD_USER_EVT: { + if (p_data->status == BTA_SDP_SUCCESS) { + if (btc_sdp_remove_record_event((int)user_data) == false) { + p_data->status = BTA_SDP_FAILURE; + } + } + } + break; + default: + break; + } + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_SDP; + msg.act = event; + + status = btc_transfer_context(&msg, p_data, sizeof(tBTA_SDP), NULL); + + if (status != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed", __func__); + } +} + +static void btc_sdp_init(void) +{ + esp_sdp_cb_param_t param; + esp_sdp_status_t ret = ESP_SDP_SUCCESS; + + do { + if (is_sdp_init()) { + BTC_TRACE_ERROR("%s SDP has been initiated, shall deinit first!", __func__); + ret = ESP_SDP_NEED_DEINIT; + break; + } + +#if SDP_DYNAMIC_MEMORY == TRUE + if ((sdp_local_param_ptr = (sdp_local_param_t *)osi_malloc(sizeof(sdp_local_param_t))) == NULL) { + BTC_TRACE_ERROR("%s malloc failed\n", __func__); + ret = ESP_SDP_NO_RESOURCE; + break; + } + memset((void *)sdp_local_param_ptr, 0, sizeof(sdp_local_param_t)); +#endif + + if (osi_mutex_new(&sdp_local_param.sdp_slot_mutex) != 0) { +#if SDP_DYNAMIC_MEMORY == TRUE + osi_free(sdp_local_param_ptr); + sdp_local_param_ptr = NULL; +#endif + BTC_TRACE_ERROR("%s osi_mutex_new failed\n", __func__); + ret = ESP_SDP_NO_RESOURCE; + break; + } + + ret = BTA_SdpEnable(btc_sdp_dm_cback); + } while(0); + + if (ret != ESP_SDP_SUCCESS) { + param.init.status = ret; + btc_sdp_cb_to_app(ESP_SDP_INIT_EVT, ¶m); + } +} + +static void btc_sdp_deinit(void) +{ + esp_sdp_cb_param_t param; + esp_sdp_status_t ret = ESP_SDP_SUCCESS; + int handle; + + do { + if (!is_sdp_init()) { + BTC_TRACE_ERROR("%s SDP has not been initiated, shall init first!", __func__); + ret = ESP_SDP_NEED_INIT; + break; + } + + for(int i = 0; i < SDP_MAX_RECORDS; i++) { + handle = free_sdp_slot(i); + if (handle > 0) { + BTA_SdpRemoveRecordByUser((void*)handle); + } + } + // todo + sdp_disable_handler(); + } while(0); + + if (ret != ESP_SDP_SUCCESS) { + param.deinit.status = ret; + btc_sdp_cb_to_app(ESP_SDP_DEINIT_EVT, ¶m); + } +} + +static void btc_sdp_create_record(btc_sdp_args_t *arg) +{ + int handle; + esp_sdp_cb_param_t param; + esp_sdp_status_t ret = ESP_SDP_SUCCESS; + + do { + if (!is_sdp_init()) { + BTC_TRACE_ERROR("%s SDP has not been initiated, shall init first!", __func__); + ret = ESP_SDP_NEED_INIT; + break; + } + + handle = alloc_sdp_slot(arg->creat_record.record); + if (handle < 0) { + ret = ESP_SDP_FAILURE; + break; + } + + BTA_SdpCreateRecordByUser((void *) handle); + } while(0); + + if (ret != ESP_SDP_SUCCESS) { + param.create_record.status = ret; + param.create_record.record_handle = -1; + btc_sdp_cb_to_app(ESP_SDP_CREATE_RECORD_COMP_EVT, ¶m); + } +} + +static void btc_sdp_remove_record(btc_sdp_args_t *arg) +{ + int handle; + esp_sdp_cb_param_t param; + esp_sdp_status_t ret = ESP_SDP_SUCCESS; + + do { + if (!is_sdp_init()) { + BTC_TRACE_ERROR("%s SDP has not been initiated, shall init first!", __func__); + ret = ESP_SDP_NEED_INIT; + break; + } + + /* Get the Record handle, and free the slot */ + /* The application layer record_handle is equivalent to the id of the btc layer */ + handle = free_sdp_slot(arg->remove_record.record_handle); + + BTC_TRACE_DEBUG("Sdp Server %s id=%d to handle=0x%08x", + __func__, arg->remove_record.record_handle, handle); + + /* Pass the actual record handle */ + if(handle > 0) { + BTA_SdpRemoveRecordByUser((void*) handle); + } else { + ret = ESP_SDP_NO_CREATE_RECORD; + break; + } + } while(0); + + if (ret != ESP_SDP_SUCCESS) { + param.create_record.status = ret; + btc_sdp_cb_to_app(ESP_SDP_REMOVE_RECORD_COMP_EVT, ¶m); + } +} + +static void btc_sdp_search(btc_sdp_args_t *arg) +{ + esp_sdp_cb_param_t param = {0}; + esp_sdp_status_t ret = ESP_SDP_SUCCESS; + + do { + if (!is_sdp_init()) { + BTC_TRACE_ERROR("%s SDP has not been initiated, shall init first!", __func__); + ret = ESP_SDP_NEED_INIT; + break; + } + + BTA_SdpSearch(arg->search.bd_addr, &arg->search.sdp_uuid); + } while(0); + + if (ret != ESP_SDP_SUCCESS) { + param.search.status = ret; + btc_sdp_cb_to_app(ESP_SDP_SEARCH_COMP_EVT, ¶m); + } +} + +void btc_sdp_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + btc_sdp_args_t *dst = (btc_sdp_args_t *)p_dest; + btc_sdp_args_t *src = (btc_sdp_args_t *)p_src; + + switch (msg->act) { + case BTC_SDP_ACT_CREATE_RECORD: + dst->creat_record.record = (bluetooth_sdp_record *)osi_calloc(sizeof(bluetooth_sdp_record)); + if (dst->creat_record.record) { + memcpy(dst->creat_record.record, src->creat_record.record, sizeof(bluetooth_sdp_record)); + } else { + BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act); + break; + } + + dst->creat_record.record->hdr.service_name = (char *)osi_calloc(src->creat_record.record->hdr.service_name_length); + if (dst->creat_record.record->hdr.service_name) { + strcpy(dst->creat_record.record->hdr.service_name, src->creat_record.record->hdr.service_name); + } else { + BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act); + } + break; + default: + break; + } +} + +void btc_sdp_arg_deep_free(btc_msg_t *msg) +{ + btc_sdp_args_t *arg = (btc_sdp_args_t *)msg->arg; + + switch (msg->act) { + case BTC_SDP_ACT_CREATE_RECORD: + if (arg->creat_record.record) { + osi_free(arg->creat_record.record); + } + if (arg->creat_record.record->hdr.service_name) { + osi_free(arg->creat_record.record->hdr.service_name); + } + break; + default: + break; + } +} + +void btc_sdp_call_handler(btc_msg_t *msg) +{ + btc_sdp_args_t *arg = (btc_sdp_args_t *)(msg->arg); + + BTC_TRACE_DEBUG("%s: event = %d\n", __func__, msg->act); + + switch (msg->act) { + case BTC_SDP_ACT_INIT: + btc_sdp_init(); + break; + case BTC_SDP_ACT_DEINIT: + btc_sdp_deinit(); + break; + case BTC_SDP_ACT_SEARCH: + btc_sdp_search(arg); + break; + case BTC_SDP_ACT_CREATE_RECORD: + btc_sdp_create_record(arg); + break; + case BTC_SDP_ACT_REMOVE_RECORD: + btc_sdp_remove_record(arg); + break; + default: + BTC_TRACE_ERROR("%s: Unhandled event (%d)!\n", __func__, msg->act); + break; + } + btc_sdp_arg_deep_free(msg); +} + +void btc_sdp_cb_handler(btc_msg_t *msg) +{ + esp_sdp_cb_param_t param = {0}; + uint8_t event = msg->act; + tBTA_SDP *p_data = (tBTA_SDP *)msg->arg; + + switch(event) { + case BTA_SDP_ENABLE_EVT: + param.init.status = p_data->status; + btc_sdp_cb_to_app(ESP_SDP_INIT_EVT, ¶m); + break; + case BTA_SDP_DISENABLE_EVT: + BTA_SdpDisable(); + osi_mutex_free(&sdp_local_param.sdp_slot_mutex); + #if SDP_DYNAMIC_MEMORY == TRUE + osi_free(sdp_local_param_ptr); + sdp_local_param_ptr = NULL; + #endif + param.deinit.status = ESP_SDP_SUCCESS; + btc_sdp_cb_to_app(ESP_SDP_DEINIT_EVT, ¶m); + break; + case BTA_SDP_SEARCH_COMP_EVT: + param.search.status = p_data->status; + if (param.search.status == ESP_SDP_SUCCESS) { + memcpy(param.search.remote_addr, p_data->sdp_search_comp.remote_addr, sizeof(BD_ADDR)); + memcpy(¶m.search.sdp_uuid, &p_data->sdp_search_comp.uuid, sizeof(tSDP_UUID)); + param.search.record_count = p_data->sdp_search_comp.record_count; + param.search.records = osi_malloc(sizeof(esp_bluetooth_sdp_record_t)*p_data->sdp_search_comp.record_count); + if (param.search.records != NULL) { + memcpy(param.search.records, p_data->sdp_search_comp.records, + sizeof(esp_bluetooth_sdp_record_t)*p_data->sdp_search_comp.record_count); + } else { + BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, event); + param.search.status = ESP_SDP_NO_RESOURCE; + } + } + btc_sdp_cb_to_app(ESP_SDP_SEARCH_COMP_EVT, ¶m); + if (param.search.records != NULL) { + osi_free(param.search.records); + } + break; + case BTA_SDP_CREATE_RECORD_USER_EVT: + param.create_record.status = p_data->status; + param.create_record.record_handle = p_data->handle; + btc_sdp_cb_to_app(ESP_SDP_CREATE_RECORD_COMP_EVT, ¶m); + break; + case BTA_SDP_REMOVE_RECORD_USER_EVT: + param.remove_record.status = p_data->status; + btc_sdp_cb_to_app(ESP_SDP_REMOVE_RECORD_COMP_EVT, ¶m); + break; + default: + BTC_TRACE_DEBUG("%s: Unhandled event (%d)!", __func__, msg->act); + break; + } +} + +#endif ///defined BTC_SDP_INCLUDED && BTC_SDP_INCLUDED == TRUE diff --git a/components/bt/host/bluedroid/common/include/common/bt_target.h b/components/bt/host/bluedroid/common/include/common/bt_target.h index ebc5df3e06..1373c2000b 100644 --- a/components/bt/host/bluedroid/common/include/common/bt_target.h +++ b/components/bt/host/bluedroid/common/include/common/bt_target.h @@ -87,6 +87,7 @@ #define BTA_JV_INCLUDED TRUE #define RFCOMM_INCLUDED TRUE #define BTC_L2CAP_INCLUDED TRUE +#define BTC_SDP_INCLUDED TRUE #define VND_BT_JV_BTA_L2CAP TRUE #endif /* UC_BT_L2CAP_ENABLED */ @@ -1412,7 +1413,7 @@ /* The maximum number of attributes in each record. */ #ifndef SDP_MAX_REC_ATTR -#if defined(HID_DEV_INCLUDED) && (HID_DEV_INCLUDED==TRUE) +#if (defined(HID_DEV_INCLUDED) && (HID_DEV_INCLUDED==TRUE)) || (defined(BTC_SDP_INCLUDED) && (BTC_SDP_INCLUDED==TRUE)) #define SDP_MAX_REC_ATTR 25 #else #define SDP_MAX_REC_ATTR 8 From 740dc8795da5687e44085f2c45f41ce2cb5ea12d Mon Sep 17 00:00:00 2001 From: xiongweichao Date: Thu, 21 Apr 2022 14:37:49 +0800 Subject: [PATCH 03/10] rfcomm supports the use of ERTM --- .../common/include/common/bt_target.h | 25 +++++++++++++++ .../bt/host/bluedroid/stack/l2cap/l2c_utils.c | 2 +- .../bluedroid/stack/rfcomm/rfc_l2cap_if.c | 18 +++++++++-- .../host/bluedroid/stack/rfcomm/rfc_mx_fsm.c | 31 +++++++++++++++++-- 4 files changed, 70 insertions(+), 6 deletions(-) diff --git a/components/bt/host/bluedroid/common/include/common/bt_target.h b/components/bt/host/bluedroid/common/include/common/bt_target.h index 1373c2000b..a0c20840b1 100644 --- a/components/bt/host/bluedroid/common/include/common/bt_target.h +++ b/components/bt/host/bluedroid/common/include/common/bt_target.h @@ -1565,6 +1565,31 @@ #define PORT_CREDIT_RX_LOW 8 #endif +/* ERTM Tx window size */ +#ifndef RFC_FCR_OPT_TX_WINDOW_SIZE +#define RFC_FCR_OPT_TX_WINDOW_SIZE 20 +#endif + +/* ERTM Maximum transmissions before disconnecting */ +#ifndef RFC_FCR_OPT_MAX_TX_B4_DISCNT +#define RFC_FCR_OPT_MAX_TX_B4_DISCNT 20 +#endif + +/* ERTM Retransmission timeout (2 secs) */ +#ifndef RFC_FCR_OPT_RETX_TOUT +#define RFC_FCR_OPT_RETX_TOUT 2000 +#endif + +/* ERTM Monitor timeout (12 secs) */ +#ifndef RFC_FCR_OPT_MONITOR_TOUT +#define RFC_FCR_OPT_MONITOR_TOUT 12000 +#endif + +/* ERTM ERTM MPS segment size */ +#ifndef RFC_FCR_OPT_MAX_PDU_SIZE +#define RFC_FCR_OPT_MAX_PDU_SIZE 1010 +#endif + /****************************************************************************** ** ** OBEX diff --git a/components/bt/host/bluedroid/stack/l2cap/l2c_utils.c b/components/bt/host/bluedroid/stack/l2cap/l2c_utils.c index f77ab9ee71..059abe7be1 100644 --- a/components/bt/host/bluedroid/stack/l2cap/l2c_utils.c +++ b/components/bt/host/bluedroid/stack/l2cap/l2c_utils.c @@ -1559,7 +1559,7 @@ tL2C_CCB *l2cu_allocate_ccb (tL2C_LCB *p_lcb, UINT16 cid) l2c_fcr_free_timer (p_ccb); #endif ///CLASSIC_BT_INCLUDED == TRUE p_ccb->ertm_info.preferred_mode = L2CAP_FCR_BASIC_MODE; /* Default mode for channel is basic mode */ - p_ccb->ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_BASIC; /* Default mode for channel is basic mode */ + p_ccb->ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_BASIC|L2CAP_FCR_CHAN_OPT_BASIC; p_ccb->ertm_info.fcr_rx_buf_size = L2CAP_FCR_RX_BUF_SIZE; p_ccb->ertm_info.fcr_tx_buf_size = L2CAP_FCR_TX_BUF_SIZE; p_ccb->ertm_info.user_rx_buf_size = L2CAP_USER_RX_BUF_SIZE; diff --git a/components/bt/host/bluedroid/stack/rfcomm/rfc_l2cap_if.c b/components/bt/host/bluedroid/stack/rfcomm/rfc_l2cap_if.c index 5b2fff17d4..a30bf25cf4 100644 --- a/components/bt/host/bluedroid/stack/rfcomm/rfc_l2cap_if.c +++ b/components/bt/host/bluedroid/stack/rfcomm/rfc_l2cap_if.c @@ -36,6 +36,17 @@ #include "osi/mutex.h" #include "osi/alarm.h" #if (defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE) + +static tL2CAP_ERTM_INFO rfc_l2c_etm_opt = +{ + L2CAP_FCR_ERTM_MODE, + L2CAP_FCR_CHAN_OPT_ERTM|L2CAP_FCR_CHAN_OPT_BASIC, /* Some devices do not support ERTM */ + L2CAP_USER_RX_BUF_SIZE, + L2CAP_USER_TX_BUF_SIZE, + L2CAP_FCR_RX_BUF_SIZE, + L2CAP_FCR_TX_BUF_SIZE +}; + /* ** Define Callback functions to be called by L2CAP */ @@ -117,7 +128,8 @@ void RFCOMM_ConnectInd (BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id) } if (p_mcb == NULL) { - L2CA_ConnectRsp (bd_addr, id, lcid, L2CAP_CONN_NO_RESOURCES, 0); + // L2CA_ConnectRsp (bd_addr, id, lcid, L2CAP_CONN_NO_RESOURCES, 0); + L2CA_ErtmConnectRsp (bd_addr, id, lcid, L2CAP_CONN_NO_RESOURCES, 0, &rfc_l2c_etm_opt); return; } p_mcb->lcid = lcid; @@ -178,7 +190,9 @@ void RFCOMM_ConnectCnf (UINT16 lcid, UINT16 result) RFCOMM_TRACE_DEBUG ("RFCOMM_ConnectCnf peer gave up pending LCID(0x%x)", p_mcb->pending_lcid); /* Peer gave up his connection request, make sure cleaning up L2CAP channel */ - L2CA_ConnectRsp (p_mcb->bd_addr, p_mcb->pending_id, p_mcb->pending_lcid, L2CAP_CONN_NO_RESOURCES, 0); + // L2CA_ConnectRsp (p_mcb->bd_addr, p_mcb->pending_id, p_mcb->pending_lcid, L2CAP_CONN_NO_RESOURCES, 0); + L2CA_ErtmConnectRsp (p_mcb->bd_addr, p_mcb->pending_id, p_mcb->pending_lcid, L2CAP_CONN_NO_RESOURCES, 0, + &rfc_l2c_etm_opt); p_mcb->pending_lcid = 0; } diff --git a/components/bt/host/bluedroid/stack/rfcomm/rfc_mx_fsm.c b/components/bt/host/bluedroid/stack/rfcomm/rfc_mx_fsm.c index 4ed65fc945..8dc4d1af11 100644 --- a/components/bt/host/bluedroid/stack/rfcomm/rfc_mx_fsm.c +++ b/components/bt/host/bluedroid/stack/rfcomm/rfc_mx_fsm.c @@ -39,6 +39,25 @@ #define L2CAP_SUCCESS 0 #define L2CAP_ERROR 1 +static tL2CAP_ERTM_INFO rfc_l2c_etm_opt = +{ + L2CAP_FCR_ERTM_MODE, + L2CAP_FCR_CHAN_OPT_ERTM|L2CAP_FCR_CHAN_OPT_BASIC, /* Some devices do not support ERTM */ + L2CAP_USER_RX_BUF_SIZE, + L2CAP_USER_TX_BUF_SIZE, + L2CAP_FCR_RX_BUF_SIZE, + L2CAP_FCR_TX_BUF_SIZE +}; + +static tL2CAP_FCR_OPTS rfc_l2c_fcr_opts_def = +{ + L2CAP_FCR_ERTM_MODE, + RFC_FCR_OPT_TX_WINDOW_SIZE, /* Tx window size */ + RFC_FCR_OPT_MAX_TX_B4_DISCNT, /* Maximum transmissions before disconnecting */ + RFC_FCR_OPT_RETX_TOUT, /* Retransmission timeout (2 secs) */ + RFC_FCR_OPT_MONITOR_TOUT, /* Monitor timeout (12 secs) */ + RFC_FCR_OPT_MAX_PDU_SIZE /* MPS segment size */ +}; /********************************************************************************/ /* L O C A L F U N C T I O N P R O T O T Y P E S */ @@ -124,7 +143,8 @@ void rfc_mx_sm_state_idle (tRFC_MCB *p_mcb, UINT16 event, void *p_data) /* Initialize L2CAP MTU */ p_mcb->peer_l2cap_mtu = L2CAP_DEFAULT_MTU - RFCOMM_MIN_OFFSET - 1; - if ((p_mcb->lcid = L2CA_ConnectReq (BT_PSM_RFCOMM, p_mcb->bd_addr)) == 0) { + // if ((p_mcb->lcid = L2CA_ConnectReq (BT_PSM_RFCOMM, p_mcb->bd_addr)) == 0) { + if ((p_mcb->lcid = L2CA_ErtmConnectReq (BT_PSM_RFCOMM, p_mcb->bd_addr, &rfc_l2c_etm_opt)) == 0) { PORT_StartCnf (p_mcb, RFCOMM_ERROR); return; } @@ -144,7 +164,8 @@ void rfc_mx_sm_state_idle (tRFC_MCB *p_mcb, UINT16 event, void *p_data) case RFC_MX_EVENT_CONN_IND: rfc_timer_start (p_mcb, RFCOMM_CONN_TIMEOUT); - L2CA_ConnectRsp (p_mcb->bd_addr, *((UINT8 *)p_data), p_mcb->lcid, L2CAP_CONN_OK, 0); + // L2CA_ConnectRsp (p_mcb->bd_addr, *((UINT8 *)p_data), p_mcb->lcid, L2CAP_CONN_OK, 0); + L2CA_ErtmConnectRsp (p_mcb->bd_addr, *((UINT8 *)p_data), p_mcb->lcid, L2CAP_CONN_OK, 0, &rfc_l2c_etm_opt); rfc_mx_send_config_req (p_mcb); @@ -482,7 +503,8 @@ void rfc_mx_sm_state_disc_wait_ua (tRFC_MCB *p_mcb, UINT16 event, void *p_data) if (p_mcb->restart_required) { /* Start Request was received while disconnecting. Execute it again */ - if ((p_mcb->lcid = L2CA_ConnectReq (BT_PSM_RFCOMM, p_mcb->bd_addr)) == 0) { + // if ((p_mcb->lcid = L2CA_ConnectReq (BT_PSM_RFCOMM, p_mcb->bd_addr)) == 0) { + if ((p_mcb->lcid = L2CA_ErtmConnectReq (BT_PSM_RFCOMM, p_mcb->bd_addr, &rfc_l2c_etm_opt)) == 0) { PORT_StartCnf (p_mcb, RFCOMM_ERROR); return; } @@ -554,6 +576,9 @@ static void rfc_mx_send_config_req (tRFC_MCB *p_mcb) cfg.mtu_present = TRUE; cfg.mtu = L2CAP_MTU_SIZE; + cfg.fcr_present = TRUE; + cfg.fcr = rfc_l2c_fcr_opts_def; + /* Defaults set by memset cfg.flush_to_present = FALSE; cfg.qos_present = FALSE; From a317cb1b0a4a7ac585772b195687d39b6da05844 Mon Sep 17 00:00:00 2001 From: xiongweichao Date: Thu, 28 Apr 2022 20:04:04 +0800 Subject: [PATCH 04/10] Split jv_rfcomm and jv_l2cap --- .../bluedroid/bta/include/bta/bta_jv_api.h | 44 +++++++++++-------- .../bt/host/bluedroid/bta/jv/bta_jv_act.c | 19 +++++++- .../bt/host/bluedroid/bta/jv/bta_jv_api.c | 2 + .../bt/host/bluedroid/bta/jv/bta_jv_main.c | 2 + .../bluedroid/bta/jv/include/bta_jv_int.h | 18 +++++++- .../common/include/common/bt_target.h | 7 ++- 6 files changed, 70 insertions(+), 22 deletions(-) diff --git a/components/bt/host/bluedroid/bta/include/bta/bta_jv_api.h b/components/bt/host/bluedroid/bta/include/bta/bta_jv_api.h index bdc9edbf5c..8564b07399 100644 --- a/components/bt/host/bluedroid/bta/include/bta/bta_jv_api.h +++ b/components/bt/host/bluedroid/bta/include/bta/bta_jv_api.h @@ -151,6 +151,7 @@ typedef UINT8 tBTA_JV_CONN_STATE; #endif /* BTA_JV_L2CAP_INCLUDED */ /* events received by tBTA_JV_RFCOMM_CBACK */ +#if BTA_JV_RFCOMM_INCLUDED #define BTA_JV_RFCOMM_OPEN_EVT 26 /* open status of RFCOMM Client connection */ #define BTA_JV_RFCOMM_CLOSE_EVT 27 /* RFCOMM connection closed */ #define BTA_JV_RFCOMM_START_EVT 28 /* RFCOMM server started */ @@ -160,6 +161,7 @@ typedef UINT8 tBTA_JV_CONN_STATE; #define BTA_JV_RFCOMM_READ_EVT 32 /* the result for BTA_JvRfcommRead */ #define BTA_JV_RFCOMM_WRITE_EVT 33 /* the result for BTA_JvRfcommWrite*/ #define BTA_JV_RFCOMM_SRV_OPEN_EVT 34 /* open status of Server RFCOMM connection */ +#endif /* BTA_JV_RFCOMM_INCLUDED */ #define BTA_JV_FREE_SCN_EVT 35 /* FREE an SCN */ #define BTA_JV_MAX_EVT 36 /* max number of JV events */ @@ -280,6 +282,7 @@ typedef struct { } tBTA_JV_L2CAP_WRITE_FIXED; #endif /* BTA_JV_L2CAP_INCLUDED */ +#if BTA_JV_RFCOMM_INCLUDED /* data associated with BTA_JV_RFCOMM_OPEN_EVT */ typedef struct { tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */ @@ -321,12 +324,6 @@ typedef struct { BOOLEAN use_co; /* TRUE to use co_rfc_data */ } tBTA_JV_RFCOMM_CL_INIT; -/*data associated with BTA_JV_L2CAP_DATA_IND_EVT & BTA_JV_RFCOMM_DATA_IND_EVT */ -typedef struct { - UINT32 handle; /* The connection handle */ - BT_HDR *p_buf; /* The incoming data */ -} tBTA_JV_DATA_IND; - /* data associated with BTA_JV_RFCOMM_CONG_EVT */ typedef struct { tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */ @@ -353,6 +350,13 @@ typedef struct { BOOLEAN cong; /* congestion status */ BOOLEAN old_cong; /* congestion status */ } tBTA_JV_RFCOMM_WRITE; +#endif /* BTA_JV_RFCOMM_INCLUDED */ + +/*data associated with BTA_JV_L2CAP_DATA_IND_EVT & BTA_JV_RFCOMM_DATA_IND_EVT */ +typedef struct { + UINT32 handle; /* The connection handle */ + BT_HDR *p_buf; /* The incoming data */ +} tBTA_JV_DATA_IND; /* data associated with BTA_JV_API_SET_PM_PROFILE_EVT */ typedef struct { @@ -404,6 +408,7 @@ typedef union { tBTA_JV_L2CAP_READ l2c_read; /* BTA_JV_L2CAP_READ_EVT */ tBTA_JV_L2CAP_WRITE l2c_write; /* BTA_JV_L2CAP_WRITE_EVT */ #endif /* BTA_JV_L2CAP_INCLUDED */ +#if BTA_JV_RFCOMM_INCLUDED tBTA_JV_RFCOMM_OPEN rfc_open; /* BTA_JV_RFCOMM_OPEN_EVT */ tBTA_JV_RFCOMM_SRV_OPEN rfc_srv_open; /* BTA_JV_RFCOMM_SRV_OPEN_EVT */ tBTA_JV_RFCOMM_CLOSE rfc_close; /* BTA_JV_RFCOMM_CLOSE_EVT */ @@ -412,6 +417,7 @@ typedef union { tBTA_JV_RFCOMM_CONG rfc_cong; /* BTA_JV_RFCOMM_CONG_EVT */ tBTA_JV_RFCOMM_READ rfc_read; /* BTA_JV_RFCOMM_READ_EVT */ tBTA_JV_RFCOMM_WRITE rfc_write; /* BTA_JV_RFCOMM_WRITE_EVT */ +#endif /* BTA_JV_RFCOMM_INCLUDED */ tBTA_JV_DATA_IND data_ind; /* BTA_JV_L2CAP_DATA_IND_EVT BTA_JV_RFCOMM_DATA_IND_EVT */ tBTA_JV_FREE_SCN free_scn; /* BTA_JV_FREE_SCN_EVT */ @@ -795,6 +801,7 @@ extern tBTA_JV_STATUS BTA_JvL2capWriteFixed(UINT16 channel, BD_ADDR *addr, UINT3 UINT8 *p_data, UINT16 len, void *user_data); #endif /* BTA_JV_L2CAP_INCLUDED */ +#if BTA_JV_RFCOMM_INCLUDED /******************************************************************************* ** ** Function BTA_JvRfcommConnect @@ -911,6 +918,19 @@ extern tBTA_JV_STATUS BTA_JvRfcommWrite(UINT32 handle, UINT32 req_id, int len, U *******************************************************************************/ extern tBTA_JV_STATUS BTA_JvRfcommFlowControl(UINT32 handle, UINT16 credits_given); +/******************************************************************************* +** +** Function BTA_JvRfcommGetPortHdl +** +** Description This function fetches the rfcomm port handle +** +** Returns BTA_JV_SUCCESS, if the request is being processed. +** BTA_JV_FAILURE, otherwise. +** +*******************************************************************************/ +UINT16 BTA_JvRfcommGetPortHdl(UINT32 handle); +#endif /* BTA_JV_RFCOMM_INCLUDED */ + /******************************************************************************* ** ** Function BTA_JVSetPmProfile @@ -932,17 +952,5 @@ extern tBTA_JV_STATUS BTA_JvRfcommFlowControl(UINT32 handle, UINT16 credits_give *******************************************************************************/ extern tBTA_JV_STATUS BTA_JvSetPmProfile(UINT32 handle, tBTA_JV_PM_ID app_id, tBTA_JV_CONN_STATE init_st); -/******************************************************************************* -** -** Function BTA_JvRfcommGetPortHdl -** -** Description This function fetches the rfcomm port handle -** -** Returns BTA_JV_SUCCESS, if the request is being processed. -** BTA_JV_FAILURE, otherwise. -** -*******************************************************************************/ -UINT16 BTA_JvRfcommGetPortHdl(UINT32 handle); - #endif ///defined BTA_JV_INCLUDED && BTA_JV_INCLUDED == TRUE #endif /* BTA_JV_API_H */ diff --git a/components/bt/host/bluedroid/bta/jv/bta_jv_act.c b/components/bt/host/bluedroid/bta/jv/bta_jv_act.c index e3671ec6cb..f3106600a1 100644 --- a/components/bt/host/bluedroid/bta/jv/bta_jv_act.c +++ b/components/bt/host/bluedroid/bta/jv/bta_jv_act.c @@ -107,17 +107,19 @@ static inline void logu(const char *title, const uint8_t *p_uuid) } -static tBTA_JV_PCB *bta_jv_add_rfc_port(tBTA_JV_RFC_CB *p_cb, tBTA_JV_PCB *p_pcb_open); static tBTA_JV_STATUS bta_jv_free_set_pm_profile_cb(UINT32 jv_handle); static void bta_jv_pm_conn_busy(tBTA_JV_PM_CB *p_cb); static void bta_jv_pm_conn_idle(tBTA_JV_PM_CB *p_cb); static void bta_jv_pm_state_change(tBTA_JV_PM_CB *p_cb, const tBTA_JV_CONN_STATE state); tBTA_JV_STATUS bta_jv_set_pm_conn_state(tBTA_JV_PM_CB *p_cb, const tBTA_JV_CONN_STATE new_st); +#if BTA_JV_RFCOMM_INCLUDED +static tBTA_JV_PCB *bta_jv_add_rfc_port(tBTA_JV_RFC_CB *p_cb, tBTA_JV_PCB *p_pcb_open); static int find_rfc_pcb(void *user_data, tBTA_JV_RFC_CB **cb, tBTA_JV_PCB **pcb); static void bta_jv_port_mgmt_sr_cback(UINT32 code, UINT16 port_handle, void* data); static void bta_jv_port_event_sr_cback(UINT32 code, UINT16 port_handle); static int bta_jv_port_data_co_cback(UINT16 port_handle, UINT8 *buf, UINT16 len, int type); +#endif /* BTA_JV_RFCOMM_INCLUDED */ /******************************************************************************* ** ** Function bta_jv_alloc_sec_id @@ -156,6 +158,7 @@ UNUSED_ATTR static int get_sec_id_used(void) } return used; } +#if BTA_JV_RFCOMM_INCLUDED UNUSED_ATTR static int get_rfc_cb_used(void) { int i; @@ -171,6 +174,7 @@ UNUSED_ATTR static int get_rfc_cb_used(void) } return used; } +#endif /* BTA_JV_RFCOMM_INCLUDED */ /******************************************************************************* ** @@ -191,6 +195,7 @@ static void bta_jv_free_sec_id(UINT8 *p_sec_id) } } +#if BTA_JV_RFCOMM_INCLUDED /******************************************************************************* ** ** Function bta_jv_alloc_rfc_cb @@ -431,6 +436,8 @@ static tBTA_JV_STATUS bta_jv_free_rfc_cb(tBTA_JV_RFC_CB *p_cb, tBTA_JV_PCB *p_pc } return status; } +#endif /* BTA_JV_RFCOMM_INCLUDED */ + #if BTA_JV_L2CAP_INCLUDED /******************************************************************************* ** @@ -525,6 +532,7 @@ static tBTA_JV_STATUS bta_jv_free_set_pm_profile_cb(UINT32 jv_handle) } if (BTA_JV_RFCOMM_MASK & jv_handle) { +#if BTA_JV_RFCOMM_INCLUDED UINT32 hi = ((jv_handle & BTA_JV_RFC_HDL_MASK) & ~BTA_JV_RFCOMM_MASK) - 1; UINT32 si = BTA_JV_RFC_HDL_TO_SIDX(jv_handle); if (hi < BTA_JV_MAX_RFC_CONN && bta_jv_cb.rfc_cb[hi].p_cback && si @@ -538,6 +546,7 @@ static tBTA_JV_STATUS bta_jv_free_set_pm_profile_cb(UINT32 jv_handle) p_cb = &p_pcb->p_pm_cb; } } +#endif /* BTA_JV_RFCOMM_INCLUDED */ } #if BTA_JV_L2CAP_INCLUDED else { @@ -582,6 +591,7 @@ static tBTA_JV_PM_CB *bta_jv_alloc_set_pm_profile_cb(UINT32 jv_handle, tBTA_JV_P if (bta_jv_cb.pm_cb[i].state == BTA_JV_PM_FREE_ST) { /* rfc handle bd addr retrieval requires core stack handle */ if (bRfcHandle) { +#if BTA_JV_RFCOMM_INCLUDED // UINT32 hi = ((jv_handle & BTA_JV_RFC_HDL_MASK) & ~BTA_JV_RFCOMM_MASK) - 1; // UINT32 si = BTA_JV_RFC_HDL_TO_SIDX(jv_handle); for (j = 0; j < BTA_JV_MAX_RFC_CONN; j++) { @@ -594,6 +604,7 @@ static tBTA_JV_PM_CB *bta_jv_alloc_set_pm_profile_cb(UINT32 jv_handle, tBTA_JV_P break; } } +#endif /* BTA_JV_RFCOMM_INCLUDED */ } #if BTA_JV_L2CAP_INCLUDED else { @@ -855,11 +866,14 @@ void bta_jv_free_scn(tBTA_JV_MSG *p_data) }; tBTA_JV_FREE_SCN_USER_DATA *user_data = NULL; +#if BTA_JV_RFCOMM_INCLUDED tBTA_JV_RFC_CB *p_cb = NULL; tBTA_JV_PCB *p_pcb = NULL; +#endif /* BTA_JV_RFCOMM_INCLUDED */ switch (p_data->free_channel.type) { case BTA_JV_CONN_TYPE_RFCOMM: { +#if BTA_JV_RFCOMM_INCLUDED if (scn > 0 && scn <= BTA_JV_MAX_SCN && bta_jv_cb.scn[scn - 1]) { /* this scn is used by JV */ bta_jv_cb.scn[scn - 1] = FALSE; @@ -877,6 +891,7 @@ void bta_jv_free_scn(tBTA_JV_MSG *p_data) fc->p_cback(BTA_JV_FREE_SCN_EVT, (tBTA_JV *)&evt_data, (void *)user_data); } } +#endif /* BTA_JV_RFCOMM_INCLUDED */ break; } case BTA_JV_CONN_TYPE_L2CAP: @@ -1615,6 +1630,7 @@ void bta_jv_l2cap_write_fixed(tBTA_JV_MSG *p_data) } #endif /* BTA_JV_L2CAP_INCLUDED */ +#if BTA_JV_RFCOMM_INCLUDED /******************************************************************************* ** ** Function bta_jv_port_data_co_cback @@ -2345,6 +2361,7 @@ void bta_jv_rfcomm_flow_control(tBTA_JV_MSG *p_data) tBTA_JV_PCB *p_pcb = fc->p_pcb; PORT_FlowControl_GiveCredit(p_pcb->port_handle, TRUE, fc->credits_given); } +#endif /* BTA_JV_RFCOMM_INCLUDED */ /******************************************************************************* ** diff --git a/components/bt/host/bluedroid/bta/jv/bta_jv_api.c b/components/bt/host/bluedroid/bta/jv/bta_jv_api.c index 756eb17457..dc060fcb6e 100644 --- a/components/bt/host/bluedroid/bta/jv/bta_jv_api.c +++ b/components/bt/host/bluedroid/bta/jv/bta_jv_api.c @@ -861,6 +861,7 @@ tBTA_JV_STATUS BTA_JvL2capWriteFixed(UINT16 channel, BD_ADDR *addr, UINT32 req_i } #endif /* BTA_JV_L2CAP_INCLUDED */ +#if BTA_JV_RFCOMM_INCLUDED /******************************************************************************* ** ** Function BTA_JvRfcommConnect @@ -1166,6 +1167,7 @@ tBTA_JV_STATUS BTA_JvRfcommFlowControl(UINT32 handle, UINT16 credits_given) } return (status); } +#endif /* BTA_JV_RFCOMM_INCLUDED */ /******************************************************************************* ** diff --git a/components/bt/host/bluedroid/bta/jv/bta_jv_main.c b/components/bt/host/bluedroid/bta/jv/bta_jv_main.c index 0cb30773c2..a71d884b92 100644 --- a/components/bt/host/bluedroid/bta/jv/bta_jv_main.c +++ b/components/bt/host/bluedroid/bta/jv/bta_jv_main.c @@ -62,6 +62,7 @@ const tBTA_JV_ACTION bta_jv_action[] = { bta_jv_l2cap_read, /* BTA_JV_API_L2CAP_READ_EVT */ bta_jv_l2cap_write, /* BTA_JV_API_L2CAP_WRITE_EVT */ #endif /* BTA_JV_L2CAP_INCLUDED */ +#if BTA_JV_RFCOMM_INCLUDED bta_jv_rfcomm_connect, /* BTA_JV_API_RFCOMM_CONNECT_EVT */ bta_jv_rfcomm_close, /* BTA_JV_API_RFCOMM_CLOSE_EVT */ bta_jv_rfcomm_start_server, /* BTA_JV_API_RFCOMM_START_SERVER_EVT */ @@ -69,6 +70,7 @@ const tBTA_JV_ACTION bta_jv_action[] = { bta_jv_rfcomm_read, /* BTA_JV_API_RFCOMM_READ_EVT */ bta_jv_rfcomm_write, /* BTA_JV_API_RFCOMM_WRITE_EVT */ bta_jv_rfcomm_flow_control, /* BTA_JV_API_RFCOMM_FLOW_CONTROL_EVT */ + #endif /* BTA_JV_RFCOMM_INCLUDED */ bta_jv_set_pm_profile, /* BTA_JV_API_SET_PM_PROFILE_EVT */ bta_jv_change_pm_state, /* BTA_JV_API_PM_STATE_CHANGE_EVT */ #if BTA_JV_L2CAP_INCLUDED diff --git a/components/bt/host/bluedroid/bta/jv/include/bta_jv_int.h b/components/bt/host/bluedroid/bta/jv/include/bta_jv_int.h index 171ba72206..320e1a8608 100644 --- a/components/bt/host/bluedroid/bta/jv/include/bta_jv_int.h +++ b/components/bt/host/bluedroid/bta/jv/include/bta_jv_int.h @@ -56,6 +56,7 @@ enum { BTA_JV_API_L2CAP_READ_EVT, BTA_JV_API_L2CAP_WRITE_EVT, #endif /* BTA_JV_L2CAP_INCLUDED */ +#if BTA_JV_RFCOMM_INCLUDED BTA_JV_API_RFCOMM_CONNECT_EVT, BTA_JV_API_RFCOMM_CLOSE_EVT, BTA_JV_API_RFCOMM_START_SERVER_EVT, @@ -63,6 +64,7 @@ enum { BTA_JV_API_RFCOMM_READ_EVT, BTA_JV_API_RFCOMM_WRITE_EVT, BTA_JV_API_RFCOMM_FLOW_CONTROL_EVT, +#endif /* BTA_JV_RFCOMM_INCLUDED */ BTA_JV_API_SET_PM_PROFILE_EVT, BTA_JV_API_PM_STATE_CHANGE_EVT, #if BTA_JV_L2CAP_INCLUDED @@ -159,6 +161,7 @@ typedef struct { tBTA_JV_PM_CB *p_pm_cb; /* ptr to pm control block, NULL: unused */ } tBTA_JV_PCB; +#if BTA_JV_RFCOMM_INCLUDED /* JV RFCOMM control block */ typedef struct { tBTA_JV_RFCOMM_CBACK *p_cback; /* the callback function */ @@ -169,6 +172,7 @@ typedef struct { UINT8 max_sess; /* max sessions */ int curr_sess; /* current sessions count*/ } tBTA_JV_RFC_CB; +#endif /* BTA_JV_RFCOMM_INCLUDED */ #if BTA_JV_L2CAP_INCLUDED /* data type for BTA_JV_API_L2CAP_CONNECT_EVT & BTA_JV_API_L2CAP_CONNECT_LE_EVT */ @@ -252,6 +256,7 @@ typedef struct { } tBTA_JV_API_L2CAP_WRITE_FIXED; #endif /* BTA_JV_L2CAP_INCLUDED */ +#if BTA_JV_RFCOMM_INCLUDED /* data type for BTA_JV_API_RFCOMM_CONNECT_EVT */ typedef struct { BT_HDR hdr; @@ -285,6 +290,7 @@ typedef struct { tBTA_JV_RFC_CB *p_cb; tBTA_JV_PCB *p_pcb; } tBTA_JV_API_RFCOMM_READ; +#endif /* BTA_JV_RFCOMM_INCLUDED */ /* data type for BTA_JV_API_SET_PM_PROFILE_EVT */ typedef struct { @@ -301,6 +307,7 @@ typedef struct { tBTA_JV_CONN_STATE state; } tBTA_JV_API_PM_STATE_CHANGE; +#if BTA_JV_RFCOMM_INCLUDED /* data type for BTA_JV_API_RFCOMM_WRITE_EVT */ typedef struct { BT_HDR hdr; @@ -329,6 +336,7 @@ typedef struct { tBTA_JV_RFCOMM_CBACK *p_cback; void *user_data; } tBTA_JV_API_RFCOMM_CLOSE; +#endif /* BTA_JV_RFCOMM_INCLUDED */ /* data type for BTA_JV_API_CREATE_RECORD_EVT */ typedef struct { @@ -383,14 +391,16 @@ typedef union { tBTA_JV_API_L2CAP_SERVER l2cap_server; tBTA_JV_API_L2CAP_WRITE_FIXED l2cap_write_fixed; #endif /* BTA_JV_L2CAP_INCLUDED */ +#if BTA_JV_RFCOMM_INCLUDED tBTA_JV_API_RFCOMM_CONNECT rfcomm_connect; tBTA_JV_API_RFCOMM_READ rfcomm_read; tBTA_JV_API_RFCOMM_WRITE rfcomm_write; tBTA_JV_API_RFCOMM_FLOW_CONTROL rfcomm_fc; - tBTA_JV_API_SET_PM_PROFILE set_pm; - tBTA_JV_API_PM_STATE_CHANGE change_pm_state; tBTA_JV_API_RFCOMM_CLOSE rfcomm_close; tBTA_JV_API_RFCOMM_SERVER rfcomm_server; +#endif /* BTA_JV_RFCOMM_INCLUDED */ + tBTA_JV_API_SET_PM_PROFILE set_pm; + tBTA_JV_API_PM_STATE_CHANGE change_pm_state; } tBTA_JV_MSG; /* JV control block */ @@ -404,7 +414,9 @@ typedef struct { #if BTA_JV_L2CAP_INCLUDED tBTA_JV_L2C_CB l2c_cb[BTA_JV_MAX_L2C_CONN]; /* index is GAP handle (index) */ #endif /* BTA_JV_L2CAP_INCLUDED */ +#if BTA_JV_RFCOMM_INCLUDED tBTA_JV_RFC_CB rfc_cb[BTA_JV_MAX_RFC_CONN]; +#endif /* BTA_JV_RFCOMM_INCLUDED */ tBTA_JV_PCB port_cb[MAX_RFC_PORTS]; /* index of this array is the port_handle, */ UINT8 sec_id[BTA_JV_NUM_SERVICE_ID]; /* service ID */ @@ -450,6 +462,7 @@ extern void bta_jv_l2cap_stop_server (tBTA_JV_MSG *p_data); extern void bta_jv_l2cap_read (tBTA_JV_MSG *p_data); extern void bta_jv_l2cap_write (tBTA_JV_MSG *p_data); #endif /* BTA_JV_L2CAP_INCLUDED */ +#if BTA_JV_RFCOMM_INCLUDED extern void bta_jv_rfcomm_connect (tBTA_JV_MSG *p_data); extern void bta_jv_rfcomm_close (tBTA_JV_MSG *p_data); extern void bta_jv_rfcomm_start_server (tBTA_JV_MSG *p_data); @@ -457,6 +470,7 @@ extern void bta_jv_rfcomm_stop_server (tBTA_JV_MSG *p_data); extern void bta_jv_rfcomm_read (tBTA_JV_MSG *p_data); extern void bta_jv_rfcomm_write (tBTA_JV_MSG *p_data); extern void bta_jv_rfcomm_flow_control(tBTA_JV_MSG *p_data); +#endif /* BTA_JV_RFCOMM_INCLUDED */ extern void bta_jv_set_pm_profile (tBTA_JV_MSG *p_data); extern void bta_jv_change_pm_state(tBTA_JV_MSG *p_data); #if BTA_JV_L2CAP_INCLUDED diff --git a/components/bt/host/bluedroid/common/include/common/bt_target.h b/components/bt/host/bluedroid/common/include/common/bt_target.h index a0c20840b1..f7cccf092d 100644 --- a/components/bt/host/bluedroid/common/include/common/bt_target.h +++ b/components/bt/host/bluedroid/common/include/common/bt_target.h @@ -80,12 +80,12 @@ #if (UC_BT_SPP_ENABLED == TRUE) #define RFCOMM_INCLUDED TRUE #define BTA_JV_INCLUDED TRUE +#define BTA_JV_RFCOMM_INCLUDED TRUE #define BTC_SPP_INCLUDED TRUE #endif /* UC_BT_SPP_ENABLED */ #if (UC_BT_L2CAP_ENABLED == TRUE) #define BTA_JV_INCLUDED TRUE -#define RFCOMM_INCLUDED TRUE #define BTC_L2CAP_INCLUDED TRUE #define BTC_SDP_INCLUDED TRUE #define VND_BT_JV_BTA_L2CAP TRUE @@ -95,6 +95,7 @@ #define BTC_HF_INCLUDED TRUE #define BTA_AG_INCLUDED TRUE #define PLC_INCLUDED TRUE +#define BTA_JV_RFCOMM_INCLUDED TRUE #ifndef RFCOMM_INCLUDED #define RFCOMM_INCLUDED TRUE #endif @@ -1488,6 +1489,10 @@ #define RFCOMM_INCLUDED FALSE #endif +#ifndef BTA_JV_RFCOMM_INCLUDED +#define BTA_JV_RFCOMM_INCLUDED FALSE +#endif + /* The maximum number of ports supported. */ #ifndef MAX_RFC_PORTS #define MAX_RFC_PORTS 8 /*max is 30*/ From 4faf4e760aadda6adde7f96ddc1a099656b4a3df Mon Sep 17 00:00:00 2001 From: xiongweichao Date: Sun, 8 May 2022 19:55:37 +0800 Subject: [PATCH 05/10] sdp support create and search PBAPC record --- .../bt/host/bluedroid/bta/sdp/bta_sdp_act.c | 28 ++++++++++ .../bluedroid/btc/profile/std/sdp/btc_sdp.c | 53 +++++++++++++++++-- 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/components/bt/host/bluedroid/bta/sdp/bta_sdp_act.c b/components/bt/host/bluedroid/bta/sdp/bta_sdp_act.c index 8f06145ce5..09268f12a6 100644 --- a/components/bt/host/bluedroid/bta/sdp/bta_sdp_act.c +++ b/components/bt/host/bluedroid/bta/sdp/bta_sdp_act.c @@ -50,6 +50,9 @@ static const uint8_t UUID_OBEX_OBJECT_PUSH[] = {0x00, 0x00, 0x11, 0x05, 0x00, 0 static const uint8_t UUID_PBAP_PSE[] = {0x00, 0x00, 0x11, 0x2F, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }; +static const uint8_t UUID_PBAP_PCE[] = {0x00, 0x00, 0x11, 0x2E, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB + }; static const uint8_t UUID_MAP_MAS[] = {0x00, 0x00, 0x11, 0x32, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }; @@ -223,6 +226,28 @@ static void bta_create_pse_sdp_record(bluetooth_sdp_record *record, tSDP_DISC_RE } } +static void bta_create_pce_sdp_record(bluetooth_sdp_record *record, tSDP_DISC_REC *p_rec) +{ + tSDP_DISC_ATTR *p_attr; + UINT16 pversion; + + record->pce.hdr.type = SDP_TYPE_PBAP_PCE; + record->pce.hdr.service_name_length = 0; + record->pce.hdr.service_name = NULL; + record->pce.hdr.rfcomm_channel_number = 0; // unused + record->pce.hdr.l2cap_psm = -1; // unused + record->pce.hdr.profile_version = 0; + + if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SERVICE_NAME)) != NULL) { + record->pce.hdr.service_name_length = SDP_DISC_ATTR_LEN(p_attr->attr_len_type); + record->pce.hdr.service_name = (char *)p_attr->attr_value.v.array; + } + + if (SDP_FindProfileVersionInRec(p_rec, UUID_SERVCLASS_PHONE_ACCESS, &pversion)) { + record->pce.hdr.profile_version = pversion; + } +} + static void bta_create_ops_sdp_record(bluetooth_sdp_record *record, tSDP_DISC_REC *p_rec) { tSDP_DISC_ATTR *p_attr, *p_sattr; @@ -395,6 +420,9 @@ static void bta_sdp_search_cback(UINT16 result, void *user_data) } else if (IS_UUID(UUID_PBAP_PSE, uuid->uu.uuid128)) { APPL_TRACE_DEBUG("%s() - found PBAP (PSE) uuid\n", __func__); bta_create_pse_sdp_record(&evt_data.records[count], p_rec); + } else if (IS_UUID(UUID_PBAP_PCE, uuid->uu.uuid128)) { + APPL_TRACE_DEBUG("%s() - found PBAP (PCE) uuid\n", __func__); + bta_create_pce_sdp_record(&evt_data.records[count], p_rec); } else if (IS_UUID(UUID_OBEX_OBJECT_PUSH, uuid->uu.uuid128)) { APPL_TRACE_DEBUG("%s() - found Object Push Server (OPS) uuid\n", __func__); bta_create_ops_sdp_record(&evt_data.records[count], p_rec); diff --git a/components/bt/host/bluedroid/btc/profile/std/sdp/btc_sdp.c b/components/bt/host/bluedroid/btc/profile/std/sdp/btc_sdp.c index 41132739ce..6ac494bc35 100644 --- a/components/bt/host/bluedroid/btc/profile/std/sdp/btc_sdp.c +++ b/components/bt/host/bluedroid/btc/profile/std/sdp/btc_sdp.c @@ -484,7 +484,53 @@ static int add_pbaps_sdp(const bluetooth_sdp_pse_record* rec) if (!status) { SDP_DeleteRecord(sdp_handle); sdp_handle = 0; - BTC_TRACE_ERROR("%s() FAILED", __func__); + BTC_TRACE_ERROR("%s() FAILED, status = %d", __func__, status); + } else { + bta_sys_add_uuid(service); /* UUID_SERVCLASS_MESSAGE_ACCESS */ + BTC_TRACE_DEBUG("%s(): SDP Registered (handle 0x%08x)", __func__, sdp_handle); + } + + return sdp_handle; +} + +/* Create a PBAP Client SDP record based on information stored in a bluetooth_sdp_pse_record */ +static int add_pbapc_sdp(const bluetooth_sdp_pce_record* rec) +{ + UINT16 service = UUID_SERVCLASS_PBAP_PCE; + UINT16 browse = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP; + bool status = true; + UINT32 sdp_handle = 0; + + BTC_TRACE_DEBUG("%s(): scn 0x%02x, psm = 0x%04x\n service name %s", __func__, + rec->hdr.rfcomm_channel_number, rec->hdr.l2cap_psm, rec->hdr.service_name); + + if ((sdp_handle = SDP_CreateRecord()) == 0) { + BTC_TRACE_ERROR("%s(): Unable to register PBAP Client Service", __func__); + return sdp_handle; + } + + /* add service class */ + status &= SDP_AddServiceClassIdList(sdp_handle, 1, &service); + + /* Add a name entry */ + status &= SDP_AddAttribute(sdp_handle, + (UINT16)ATTR_ID_SERVICE_NAME, + (UINT8)TEXT_STR_DESC_TYPE, + (UINT32)(rec->hdr.service_name_length + 1), + (UINT8 *)rec->hdr.service_name); + + /* Add in the Bluetooth Profile Descriptor List */ + status &= SDP_AddProfileDescriptorList(sdp_handle, + UUID_SERVCLASS_PHONE_ACCESS, + rec->hdr.profile_version); + + /* Make the service browseable */ + status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse); + + if (!status) { + SDP_DeleteRecord(sdp_handle); + sdp_handle = 0; + BTC_TRACE_ERROR("%s() FAILED, status = %d", __func__, status); } else { bta_sys_add_uuid(service); /* UUID_SERVCLASS_MESSAGE_ACCESS */ BTC_TRACE_DEBUG("%s(): SDP Registered (handle 0x%08x)", __func__, sdp_handle); @@ -663,14 +709,15 @@ static int btc_handle_create_record_event(int id) case SDP_TYPE_PBAP_PSE: handle = add_pbaps_sdp(&record->pse); break; + case SDP_TYPE_PBAP_PCE: + handle = add_pbapc_sdp(&record->pce); + break; case SDP_TYPE_OPP_SERVER: handle = add_opps_sdp(&record->ops); break; case SDP_TYPE_SAP_SERVER: handle = add_saps_sdp(&record->sap); break; - case SDP_TYPE_PBAP_PCE: - // break; not yet supported default: BTC_TRACE_DEBUG("Record type %d is not supported",record->hdr.type); break; From b777a53fe839f3db8d29c48c0fd30f265e1a9343 Mon Sep 17 00:00:00 2001 From: xiongweichao Date: Fri, 13 May 2022 19:20:22 +0800 Subject: [PATCH 06/10] support add and search raw sdp record --- .../bt/host/bluedroid/bta/sdp/bta_sdp_act.c | 4 + .../bluedroid/btc/profile/std/sdp/btc_sdp.c | 80 ++++++++++++++++++- 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/components/bt/host/bluedroid/bta/sdp/bta_sdp_act.c b/components/bt/host/bluedroid/bta/sdp/bta_sdp_act.c index 09268f12a6..1126187f97 100644 --- a/components/bt/host/bluedroid/bta/sdp/bta_sdp_act.c +++ b/components/bt/host/bluedroid/bta/sdp/bta_sdp_act.c @@ -367,6 +367,10 @@ static void bta_create_raw_sdp_record(bluetooth_sdp_record *record, tSDP_DISC_RE record->pse.hdr.service_name = (char *)p_attr->attr_value.v.array; } + if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_GOEP_L2CAP_PSM)) != NULL) { + record->hdr.l2cap_psm = p_attr->attr_value.v.u16; + } + /* Try to extract an RFCOMM channel */ if (SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_RFCOMM, &pe)) { record->pse.hdr.rfcomm_channel_number = pe.params[0]; diff --git a/components/bt/host/bluedroid/btc/profile/std/sdp/btc_sdp.c b/components/bt/host/bluedroid/btc/profile/std/sdp/btc_sdp.c index 6ac494bc35..78d7d7f5f3 100644 --- a/components/bt/host/bluedroid/btc/profile/std/sdp/btc_sdp.c +++ b/components/bt/host/bluedroid/btc/profile/std/sdp/btc_sdp.c @@ -253,6 +253,82 @@ static int free_sdp_slot(int id) return handle; } +/* Create a raw SDP record based on information stored in a bluetooth_sdp_raw_record */ +static int add_raw_sdp(const bluetooth_sdp_record* rec) +{ + tSDP_PROTOCOL_ELEM protoList [2]; + UINT16 service = 0; + UINT16 browse = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP; + bool status = true; + UINT8 temp[4]; + UINT8* p_temp = temp; + UINT32 sdp_handle = 0; + + BTC_TRACE_DEBUG("%s(): scn 0x%02x, psm = 0x%04x\n service name %s", __func__, + rec->hdr.rfcomm_channel_number, rec->hdr.l2cap_psm, rec->hdr.service_name); + + if ((sdp_handle = SDP_CreateRecord()) == 0) { + BTC_TRACE_ERROR("%s(): Unable to register raw sdp record", __func__); + return sdp_handle; + } + + if (rec->hdr.bt_uuid.len == 16) { + memcpy(&service, &rec->hdr.bt_uuid.uuid.uuid128[2], sizeof(service)); + } else if (rec->hdr.bt_uuid.len == 2) { + memcpy(&service, &rec->hdr.bt_uuid.uuid.uuid16, sizeof(service)); + } else if (rec->hdr.bt_uuid.len == 4) { + memcpy(&service, &rec->hdr.bt_uuid.uuid.uuid16, sizeof(service)); + } else { + SDP_DeleteRecord(sdp_handle); + sdp_handle = 0; + return sdp_handle; + } + /* add service class */ + status &= SDP_AddServiceClassIdList(sdp_handle, 1, &service); + memset( protoList, 0 , 2*sizeof(tSDP_PROTOCOL_ELEM) ); + + /* add protocol list, including RFCOMM scn */ + protoList[0].protocol_uuid = UUID_PROTOCOL_L2CAP; + protoList[0].num_params = 0; + if (rec->hdr.rfcomm_channel_number < 0) { + status &= SDP_AddProtocolList(sdp_handle, 1, protoList); + } else { + protoList[1].protocol_uuid = UUID_PROTOCOL_RFCOMM; + protoList[1].num_params = 1; + protoList[1].params[0] = rec->hdr.rfcomm_channel_number; + status &= SDP_AddProtocolList(sdp_handle, 2, protoList); + } + + /* Add a name entry */ + status &= SDP_AddAttribute(sdp_handle, + (UINT16)ATTR_ID_SERVICE_NAME, + (UINT8)TEXT_STR_DESC_TYPE, + (UINT32)(rec->hdr.service_name_length + 1), + (UINT8 *)rec->hdr.service_name); + + /* Add the L2CAP PSM if present */ + if(rec->hdr.l2cap_psm != -1) { + p_temp = temp;// The macro modifies p_temp, hence rewind. + UINT16_TO_BE_STREAM(p_temp, rec->hdr.l2cap_psm); + status &= SDP_AddAttribute(sdp_handle, ATTR_ID_GOEP_L2CAP_PSM, + UINT_DESC_TYPE, (UINT32)2, temp); + } + + /* Make the service browseable */ + status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse); + + if (!status) { + SDP_DeleteRecord(sdp_handle); + sdp_handle = 0; + BTC_TRACE_ERROR("%s() FAILED, status = %d", __func__, status); + } else { + bta_sys_add_uuid(service); + BTC_TRACE_DEBUG("%s(): SDP Registered (handle 0x%08x)", __func__, sdp_handle); + } + + return sdp_handle; +} + /* Create a MAP MAS SDP record based on information stored in a bluetooth_sdp_mas_record */ static int add_maps_sdp(const bluetooth_sdp_mas_record* rec) { @@ -700,6 +776,9 @@ static int btc_handle_create_record_event(int id) if(sdp_slot != NULL) { bluetooth_sdp_record* record = sdp_slot->record_data; switch(record->hdr.type) { + case SDP_TYPE_RAW: + handle = add_raw_sdp(record); + break; case SDP_TYPE_MAP_MAS: handle = add_maps_sdp(&record->mas); break; @@ -842,7 +921,6 @@ static void btc_sdp_deinit(void) BTA_SdpRemoveRecordByUser((void*)handle); } } - // todo sdp_disable_handler(); } while(0); From 52656ad582222731a3c394dad835be9af56de834 Mon Sep 17 00:00:00 2001 From: xiongweichao Date: Thu, 21 Apr 2022 15:54:45 +0800 Subject: [PATCH 07/10] add l2cap server demo --- .../classic_bt/bt_l2cap_server/CMakeLists.txt | 6 + .../classic_bt/bt_l2cap_server/README.md | 68 ++++ .../bt_l2cap_server/main/CMakeLists.txt | 3 + .../bt_l2cap_server/main/bt_app_core.c | 144 +++++++ .../bt_l2cap_server/main/bt_app_core.h | 77 ++++ .../classic_bt/bt_l2cap_server/main/main.c | 360 ++++++++++++++++++ .../bt_l2cap_server/sdkconfig.defaults | 9 + 7 files changed, 667 insertions(+) create mode 100644 examples/bluetooth/bluedroid/classic_bt/bt_l2cap_server/CMakeLists.txt create mode 100644 examples/bluetooth/bluedroid/classic_bt/bt_l2cap_server/README.md create mode 100644 examples/bluetooth/bluedroid/classic_bt/bt_l2cap_server/main/CMakeLists.txt create mode 100644 examples/bluetooth/bluedroid/classic_bt/bt_l2cap_server/main/bt_app_core.c create mode 100644 examples/bluetooth/bluedroid/classic_bt/bt_l2cap_server/main/bt_app_core.h create mode 100644 examples/bluetooth/bluedroid/classic_bt/bt_l2cap_server/main/main.c create mode 100644 examples/bluetooth/bluedroid/classic_bt/bt_l2cap_server/sdkconfig.defaults diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_server/CMakeLists.txt b/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_server/CMakeLists.txt new file mode 100644 index 0000000000..08374447b7 --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_server/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(bt_l2cap_server) diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_server/README.md b/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_server/README.md new file mode 100644 index 0000000000..a20da06876 --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_server/README.md @@ -0,0 +1,68 @@ +| Supported Targets | ESP32 | +| ----------------- | ----- | + +ESP-IDF BT-L2CAP-SERVER EXAMPLE +=================================== + +This example is to show how to use the APIs of **Logical Link Control and Adaptation Layer Protocol** (**L2CAP**) to create an L2CAP server. We also provide demo `bt_l2cap_client` to create L2CAP client. In fact, you can create L2CAP clients and L2CAP servers on a single device at the same time. + +## How to use example + +### Hardware Required + +This example is designed to run on commonly available ESP32 development board, e.g. ESP32-DevKitC. To operate the example, it should be connected to the L2CAP client running on another ESP32 board. + +### Configure the project + +1. Open the project configuration menu: + +``` +idf.py menuconfig +``` + +2. Enable the BT-L2CAP functionality by choosing the path as following: + +`Component config --> Bluetooth --> Bluedroid Options --> BT-L2CAP` + +3. If you want to limit the number of connected devices, please make sure set the `BT/BLE MAX ACL CONNECTIONS` and `BR/EDR ACL Max Connections` with same value you want. + +`Component config --> Bluetooth --> Bluedroid Options --> BT/BLE MAX ACL CONNECTIONS(1~7)` +and +`Component config --> Bluetooth --> Bluetooth --> Bluetooth controller --> BR/EDR ACL Max Connections` + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +idf.py -p PORT flash monitor +``` + +(Replace PORT with the name of the serial port to use.) + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +After the program is started, the example starts inquiry scan and page scan, awaiting being discovered and connected. Other bluetooth devices can discover a device named "ESP_BT_L2CAP_SERVER". ESP-IDF examples for other L2CAP client can be used to connect to the local device. + +When you run this example, the explain prints the following at the very begining: +``` +I (1398) L2CAP_TAG: ESP_BT_L2CAP_INIT_EVT: status:0 + +I (1398) L2CAP_TAG: ESP_BT_L2CAP_START_EVT: status:0, hdl:0x0, sec_id:0x37 + +I (1408) SDP_TAG: ESP_SDP_INIT_EVT: status:0 + +I (1408) SDP_TAG: ESP_SDP_CREATE_RECORD_COMP_EVT: status:0 + +I (1418) L2CAP_TAG: event: 10 +I (1428) L2CAP_TAG: Own address:[c4:dd:57:5b:e7:46] + +``` + +## Troubleshooting +* This example just demonstrates how to use the SDP and L2CAP APIs. This example cannot establish an l2cap connection with other devices (smartphones, computers, etc.) because the UUID is unknown and does not conform to the Bluetooth protocol standard. +* You can complete OBEX related profiles through SPP, SDP and L2CAP APIs. diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_server/main/CMakeLists.txt b/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_server/main/CMakeLists.txt new file mode 100644 index 0000000000..e2d9e2f26b --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_server/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "main.c" + "bt_app_core.c" + INCLUDE_DIRS ".") diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_server/main/bt_app_core.c b/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_server/main/bt_app_core.c new file mode 100644 index 0000000000..58b22dddea --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_server/main/bt_app_core.c @@ -0,0 +1,144 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include +#include +#include "freertos/xtensa_api.h" +#include "freertos/FreeRTOSConfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "bt_app_core.h" +#include "freertos/ringbuf.h" + +/******************************* + * STATIC FUNCTION DECLARATIONS + ******************************/ + +/* handler for application task */ +static void bt_app_task_handler(void *arg); +/* message sender */ +static bool bt_app_send_msg(bt_app_msg_t *msg); +/* handle dispatched messages */ +static void bt_app_work_dispatched(bt_app_msg_t *msg); + +/******************************* + * STATIC VARIABLE DEFINITIONS + ******************************/ + +static QueueHandle_t s_bt_app_task_queue = NULL; /* handle of work queue */ +static TaskHandle_t s_bt_app_task_handle = NULL; /* handle of application task */ + +/******************************* + * STATIC FUNCTION DEFINITIONS + ******************************/ + +static bool bt_app_send_msg(bt_app_msg_t *msg) +{ + if (msg == NULL) { + return false; + } + + /* send the message to work queue */ + if (xQueueSend(s_bt_app_task_queue, msg, 10 / portTICK_PERIOD_MS) != pdTRUE) { + ESP_LOGE(BT_APP_CORE_TAG, "%s xQueue send failed", __func__); + return false; + } + return true; +} + +static void bt_app_work_dispatched(bt_app_msg_t *msg) +{ + if (msg->cb) { + msg->cb(msg->event, msg->param); + } +} + +static void bt_app_task_handler(void *arg) +{ + bt_app_msg_t msg; + + for (;;) { + /* receive message from work queue and handle it */ + if (pdTRUE == xQueueReceive(s_bt_app_task_queue, &msg, (TickType_t)portMAX_DELAY)) { + ESP_LOGD(BT_APP_CORE_TAG, "%s, signal: 0x%x, event: 0x%x", __func__, msg.sig, msg.event); + + switch (msg.sig) { + case BT_APP_SIG_WORK_DISPATCH: + bt_app_work_dispatched(&msg); + break; + default: + ESP_LOGW(BT_APP_CORE_TAG, "%s, unhandled signal: %d", __func__, msg.sig); + break; + } /* switch (msg.sig) */ + + if (msg.param) { + free(msg.param); + } + } + } +} + +/******************************** + * EXTERNAL FUNCTION DEFINITIONS + *******************************/ + +bool bt_app_work_dispatch(bt_app_cb_t p_cback, uint16_t event, void *p_params, int param_len, bt_app_copy_cb_t p_copy_cback) +{ + ESP_LOGD(BT_APP_CORE_TAG, "%s event: 0x%x, param len: %d", __func__, event, param_len); + + bt_app_msg_t msg; + memset(&msg, 0, sizeof(bt_app_msg_t)); + + msg.sig = BT_APP_SIG_WORK_DISPATCH; + msg.event = event; + msg.cb = p_cback; + + if (param_len == 0) { + return bt_app_send_msg(&msg); + } else if (p_params && param_len > 0) { + if ((msg.param = malloc(param_len)) != NULL) { + memcpy(msg.param, p_params, param_len); + /* check if caller has provided a copy callback to do the deep copy */ + if (p_copy_cback) { + p_copy_cback(msg.param, p_params, param_len); + } + return bt_app_send_msg(&msg); + } + } + + return false; +} + +void bt_app_task_start_up(void) +{ + s_bt_app_task_queue = xQueueCreate(10, sizeof(bt_app_msg_t)); + xTaskCreate(bt_app_task_handler, "BtAppTask", 3072, NULL, configMAX_PRIORITIES - 3, &s_bt_app_task_handle); +} + +void bt_app_task_shut_down(void) +{ + if (s_bt_app_task_handle) { + vTaskDelete(s_bt_app_task_handle); + s_bt_app_task_handle = NULL; + } + if (s_bt_app_task_queue) { + vQueueDelete(s_bt_app_task_queue); + s_bt_app_task_queue = NULL; + } +} + +void l2cap_wr_task_start_up(l2cap_wr_task_cb_t p_cback, int fd) +{ + xTaskCreate(p_cback, "write_read", 2048, (void *)fd, 5, NULL); +} + +void l2cap_wr_task_shut_down(void) +{ + vTaskDelete(NULL); +} diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_server/main/bt_app_core.h b/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_server/main/bt_app_core.h new file mode 100644 index 0000000000..7627ae28d1 --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_server/main/bt_app_core.h @@ -0,0 +1,77 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#ifndef __BT_APP_CORE_H__ +#define __BT_APP_CORE_H__ + +#include +#include +#include + +/* log tag */ +#define BT_APP_CORE_TAG "BT_APP_CORE" + +/* signal for `bt_app_work_dispatch` */ +#define BT_APP_SIG_WORK_DISPATCH (0x01) + +/** + * @brief handler for the dispatched work + * + * @param [in] event event id + * @param [in] param handler parameter + */ +typedef void (* bt_app_cb_t) (uint16_t event, void *param); + +/** + * @brief handler for write and read + */ +typedef void (* l2cap_wr_task_cb_t) (void *fd); + +/* message to be sent */ +typedef struct { + uint16_t sig; /*!< signal to bt_app_task */ + uint16_t event; /*!< message event id */ + bt_app_cb_t cb; /*!< context switch callback */ + void *param; /*!< parameter area needs to be last */ +} bt_app_msg_t; + +/** + * @brief parameter deep-copy function to be customized + * + * @param [out] p_dest pointer to destination data + * @param [in] p_src pointer to source data + * @param [in] len data length in byte + */ +typedef void (* bt_app_copy_cb_t) (void *p_dest, void *p_src, int len); + +/** + * @brief work dispatcher for the application task + * + * @param [in] p_cback callback function + * @param [in] event event id + * @param [in] p_params callback paramters + * @param [in] param_len parameter length in byte + * @param [in] p_copy_cback parameter deep-copy function + * + * @return true if work dispatch successfully, false otherwise + */ +bool bt_app_work_dispatch(bt_app_cb_t p_cback, uint16_t event, void *p_params, int param_len, bt_app_copy_cb_t p_copy_cback); + +/** + * @brief start up the application task + */ +void bt_app_task_start_up(void); + +/** + * @brief shut down the application task + */ +void bt_app_task_shut_down(void); + +void l2cap_wr_task_start_up(l2cap_wr_task_cb_t p_cback, int fd); + +void l2cap_wr_task_shut_down(void); + +#endif /* __BT_APP_CORE_H__ */ diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_server/main/main.c b/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_server/main/main.c new file mode 100644 index 0000000000..5272ade7c7 --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_server/main/main.c @@ -0,0 +1,360 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include +#include +#include +#include +#include "nvs.h" +#include "nvs_flash.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "esp_bt.h" +#include "esp_bt_main.h" +#include "esp_gap_bt_api.h" +#include "esp_bt_device.h" +#include "esp_l2cap_bt_api.h" + +#include "time.h" +#include "sys/time.h" +#include "esp_vfs.h" +#include "esp_sdp_api.h" +#include "bt_app_core.h" + +#define L2CAP_TAG "L2CAP_TAG" +#define SDP_TAG "SDP_TAG" +#define EXAMPLE_DEVICE_NAME "ESP_BT_L2CAP_SERVER" +#define L2CAP_DATA_LEN 100 +#define BT_UNUSED_RFCOMM -1 +#define BT_L2CAP_DYNMIC_PSM 0x1001 +#define BT_UNKONWN_PROFILE_VERSION 0x0102 + +static esp_bt_l2cap_cntl_flags_t sec_mask = ESP_BT_L2CAP_SEC_AUTHENTICATE; +static char *sdp_service_name = "Unknown_profile"; +static const uint8_t UUID_UNKNOWN[] = {0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB + }; + +static void esp_bt_l2cap_cb(esp_bt_l2cap_cb_event_t event, esp_bt_l2cap_cb_param_t *param); +static void esp_hdl_bt_l2cap_cb_evt(uint16_t event, void *p_param); +static void esp_hdl_sdp_cb_evt(uint16_t event, void *p_param); + +static char *bda2str(esp_bd_addr_t bda, char *str, size_t size) +{ + if (bda == NULL || str == NULL || size < 18) { + return NULL; + } + + sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x", + bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); + return str; +} + +static void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) +{ + char bda_str[18] = {0}; + + switch (event) { + /* when Legacy Pairing pin code requested, this event comes */ + case ESP_BT_GAP_AUTH_CMPL_EVT:{ + if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) { + ESP_LOGI(L2CAP_TAG, "authentication success: %s bda:[%s]", param->auth_cmpl.device_name, + bda2str(param->auth_cmpl.bda, bda_str, sizeof(bda_str))); + } else { + ESP_LOGE(L2CAP_TAG, "authentication failed, status:%d", param->auth_cmpl.stat); + } + break; + } + /* when Security Simple Pairing user confirmation requested, this event comes */ + case ESP_BT_GAP_PIN_REQ_EVT:{ + ESP_LOGI(L2CAP_TAG, "ESP_BT_GAP_PIN_REQ_EVT min_16_digit:%d", param->pin_req.min_16_digit); + if (param->pin_req.min_16_digit) { + ESP_LOGI(L2CAP_TAG, "Input pin code: 0000 0000 0000 0000"); + esp_bt_pin_code_t pin_code = {0}; + esp_bt_gap_pin_reply(param->pin_req.bda, true, 16, pin_code); + } else { + ESP_LOGI(L2CAP_TAG, "Input pin code: 1234"); + esp_bt_pin_code_t pin_code; + pin_code[0] = '1'; + pin_code[1] = '2'; + pin_code[2] = '3'; + pin_code[3] = '4'; + esp_bt_gap_pin_reply(param->pin_req.bda, true, 4, pin_code); + } + break; + } + +#if (CONFIG_BT_SSP_ENABLED == true) + /* when Security Simple Pairing user confirmation requested, this event comes */ + case ESP_BT_GAP_CFM_REQ_EVT: + ESP_LOGI(L2CAP_TAG, "ESP_BT_GAP_CFM_REQ_EVT Please compare the numeric value: %d", param->cfm_req.num_val); + esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, true); + break; + /* when Security Simple Pairing passkey notified, this event comes */ + case ESP_BT_GAP_KEY_NOTIF_EVT: + ESP_LOGI(L2CAP_TAG, "ESP_BT_GAP_KEY_NOTIF_EVT passkey:%d", param->key_notif.passkey); + break; + /* when Security Simple Pairing passkey requested, this event comes */ + case ESP_BT_GAP_KEY_REQ_EVT: + ESP_LOGI(L2CAP_TAG, "ESP_BT_GAP_KEY_REQ_EVT Please enter passkey!"); + break; +#endif + + /* when GAP mode changed, this event comes */ + case ESP_BT_GAP_MODE_CHG_EVT: + ESP_LOGI(L2CAP_TAG, "ESP_BT_GAP_MODE_CHG_EVT mode:%d bda:[%s]", param->mode_chg.mode, + bda2str(param->mode_chg.bda, bda_str, sizeof(bda_str))); + break; + /* other */ + default: { + ESP_LOGI(L2CAP_TAG, "event: %d", event); + break; + } + } + return; +} + +static void l2cap_read_handle(void * param) +{ + int size = 0; + int fd = (int)param; + uint8_t *l2cap_data = NULL; + + l2cap_data = malloc(L2CAP_DATA_LEN); + if (!l2cap_data) { + ESP_LOGE(L2CAP_TAG, "malloc l2cap_data failed, fd:%d", fd); + goto done; + } + + do { + /* The frequency of calling this function also limits the speed at which the peer device can send data. */ + size = read(fd, l2cap_data, L2CAP_DATA_LEN); + if (size < 0) { + break; + } else if (size == 0) { + /* There is no data, retry after 500 ms */ + vTaskDelay(500 / portTICK_PERIOD_MS); + } else { + ESP_LOGI(L2CAP_TAG, "fd = %d data_len = %d", fd, size); + // esp_log_buffer_hex(L2CAP_TAG, l2cap_data, size); + /* To avoid task watchdog */ + vTaskDelay(10 / portTICK_PERIOD_MS); + } + } while (1); +done: + if (l2cap_data) { + free(l2cap_data); + } + l2cap_wr_task_shut_down(); +} + +static void esp_bt_l2cap_cb(esp_bt_l2cap_cb_event_t event, esp_bt_l2cap_cb_param_t *param) +{ + switch (event) { + case ESP_BT_L2CAP_INIT_EVT: + case ESP_BT_L2CAP_UNINIT_EVT: + case ESP_BT_L2CAP_OPEN_EVT: + case ESP_BT_L2CAP_CLOSE_EVT: + case ESP_BT_L2CAP_CL_INIT_EVT: + case ESP_BT_L2CAP_START_EVT: + case ESP_BT_L2CAP_SRV_STOP_EVT: { + bt_app_work_dispatch(esp_hdl_bt_l2cap_cb_evt, event, param, sizeof(esp_bt_l2cap_cb_param_t), NULL); + break; + } + default: + ESP_LOGE(L2CAP_TAG, "Invalid L2CAP event: %d", event); + break; + } +} + +static void esp_hdl_bt_l2cap_cb_evt(uint16_t event, void *p_param) +{ + char bda_str[18] = {0}; + esp_bt_l2cap_cb_param_t *l2cap_param = (esp_bt_l2cap_cb_param_t *)p_param; + + switch (event) { + case ESP_BT_L2CAP_INIT_EVT: + if (l2cap_param->init.status == ESP_BT_L2CAP_SUCCESS) { + ESP_LOGI(L2CAP_TAG, "ESP_BT_L2CAP_INIT_EVT: status:%d\n", l2cap_param->init.status); + esp_bt_l2cap_vfs_register(); + esp_bt_l2cap_start_srv(sec_mask, BT_L2CAP_DYNMIC_PSM); + } else { + ESP_LOGI(L2CAP_TAG, "ESP_BT_L2CAP_INIT_EVT: status:%d\n", l2cap_param->init.status); + } + break; + case ESP_BT_L2CAP_UNINIT_EVT: + ESP_LOGI(L2CAP_TAG, "ESP_BT_L2CAP_UNINIT_EVT: status:%d\n", l2cap_param->uninit.status); + break; + case ESP_BT_L2CAP_OPEN_EVT: + if (l2cap_param->open.status == ESP_BT_L2CAP_SUCCESS) { + ESP_LOGI(L2CAP_TAG, "ESP_BT_L2CAP_OPEN_EVT: status:%d, fd = %d, tx mtu = %d, remote_address:%s\n", l2cap_param->open.status, + l2cap_param->open.fd, l2cap_param->open.tx_mtu, bda2str(l2cap_param->open.rem_bda, bda_str, sizeof(bda_str))); + l2cap_wr_task_start_up(l2cap_read_handle, l2cap_param->open.fd); + } else { + ESP_LOGI(L2CAP_TAG, "ESP_BT_L2CAP_OPEN_EVT: status:%d\n", l2cap_param->open.status); + } + break; + case ESP_BT_L2CAP_CLOSE_EVT: + ESP_LOGI(L2CAP_TAG, "ESP_BT_L2CAP_CLOSE_EVT: status:%d\n", l2cap_param->close.status); + esp_bt_l2cap_start_srv(sec_mask, BT_L2CAP_DYNMIC_PSM); // bug, need to do fix + break; + case ESP_BT_L2CAP_CL_INIT_EVT: + ESP_LOGI(L2CAP_TAG, "ESP_BT_L2CAP_CL_INIT_EVT: status:%d\n", l2cap_param->cl_init.status); + break; + case ESP_BT_L2CAP_START_EVT: + if (l2cap_param->start.status == ESP_BT_L2CAP_SUCCESS) { + ESP_LOGI(L2CAP_TAG, "ESP_BT_L2CAP_START_EVT: status:%d, hdl:0x%x, sec_id:0x%x\n", + l2cap_param->start.status, l2cap_param->start.handle, l2cap_param->start.sec_id); + } else { + ESP_LOGI(L2CAP_TAG, "ESP_BT_L2CAP_START_EVT: status:%d\n", l2cap_param->start.status); + } + break; + case ESP_BT_L2CAP_SRV_STOP_EVT: + ESP_LOGI(L2CAP_TAG, "ESP_BT_L2CAP_SRV_STOP_EVT: status:%d, psm = 0x%x\n", l2cap_param->srv_stop.status, l2cap_param->srv_stop.psm); + break; + default: + break; + } + return; +} + +static void esp_sdp_cb(esp_sdp_cb_event_t event, esp_sdp_cb_param_t *param) +{ + switch (event) { + case ESP_SDP_INIT_EVT: + case ESP_SDP_DEINIT_EVT: + case ESP_SDP_SEARCH_COMP_EVT: + case ESP_SDP_CREATE_RECORD_COMP_EVT: + case ESP_SDP_REMOVE_RECORD_COMP_EVT: { + bt_app_work_dispatch(esp_hdl_sdp_cb_evt, event, param, sizeof(esp_sdp_cb_param_t), NULL); + break; + } + default: + ESP_LOGE(SDP_TAG, "Invalid SDP event: %d", event); + break; + } +} + +static void esp_hdl_sdp_cb_evt(uint16_t event, void *p_param) +{ + esp_bluetooth_sdp_record_t record = {0}; + esp_sdp_cb_param_t *sdp_param = (esp_sdp_cb_param_t *)p_param; + + switch (event) { + case ESP_SDP_INIT_EVT: + ESP_LOGI(SDP_TAG, "ESP_SDP_INIT_EVT: status:%d\n", sdp_param->init.status); + if (sdp_param->init.status == ESP_SDP_SUCCESS) { + record.hdr.type = ESP_SDP_TYPE_RAW; + record.hdr.uuid.len = sizeof(UUID_UNKNOWN); + memcpy(record.hdr.uuid.uuid.uuid128, UUID_UNKNOWN, sizeof(UUID_UNKNOWN)); + record.hdr.service_name_length = strlen(sdp_service_name) + 1; + record.hdr.service_name = sdp_service_name; + record.hdr.rfcomm_channel_number = BT_UNUSED_RFCOMM; + record.hdr.l2cap_psm = BT_L2CAP_DYNMIC_PSM; + record.hdr.profile_version = BT_UNKONWN_PROFILE_VERSION; + esp_sdp_create_record(&record); + } + break; + case ESP_SDP_DEINIT_EVT: + ESP_LOGI(SDP_TAG, "ESP_SDP_DEINIT_EVT: status:%d\n", sdp_param->deinit.status); + break; + case ESP_SDP_SEARCH_COMP_EVT: + ESP_LOGI(SDP_TAG, "ESP_SDP_SEARCH_COMP_EVT: status:%d\n", sdp_param->search.status); + break; + case ESP_SDP_CREATE_RECORD_COMP_EVT: + ESP_LOGI(SDP_TAG, "ESP_SDP_CREATE_RECORD_COMP_EVT: status:%d\n", sdp_param->create_record.status); + if (sdp_param->create_record.status == ESP_SDP_SUCCESS) { + esp_bt_dev_set_device_name(EXAMPLE_DEVICE_NAME); + esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); + } + break; + case ESP_SDP_REMOVE_RECORD_COMP_EVT: + ESP_LOGI(SDP_TAG, "ESP_SDP_REMOVE_RECORD_COMP_EVT: status:%d\n", sdp_param->remove_record.status); + break; + default: + break; + } +} + +void app_main(void) +{ + char bda_str[18] = {0}; + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK( ret ); + + ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_BLE)); + + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + if ((ret = esp_bt_controller_init(&bt_cfg)) != ESP_OK) { + ESP_LOGE(L2CAP_TAG, "%s initialize controller failed: %s\n", __func__, esp_err_to_name(ret)); + return; + } + + if ((ret = esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT)) != ESP_OK) { + ESP_LOGE(L2CAP_TAG, "%s enable controller failed: %s\n", __func__, esp_err_to_name(ret)); + return; + } + + if ((ret = esp_bluedroid_init()) != ESP_OK) { + ESP_LOGE(L2CAP_TAG, "%s initialize bluedroid failed: %s\n", __func__, esp_err_to_name(ret)); + return; + } + + if ((ret = esp_bluedroid_enable()) != ESP_OK) { + ESP_LOGE(L2CAP_TAG, "%s enable bluedroid failed: %s\n", __func__, esp_err_to_name(ret)); + return; + } + + bt_app_task_start_up(); + + if ((ret = esp_bt_gap_register_callback(esp_bt_gap_cb)) != ESP_OK) { + ESP_LOGE(L2CAP_TAG, "%s gap register failed: %s\n", __func__, esp_err_to_name(ret)); + return; + } + + if ((ret = esp_bt_l2cap_register_callback(esp_bt_l2cap_cb)) != ESP_OK) { + ESP_LOGE(L2CAP_TAG, "%s l2cap register failed: %s\n", __func__, esp_err_to_name(ret)); + return; + } + + if ((ret = esp_bt_l2cap_init()) != ESP_OK) { + ESP_LOGE(L2CAP_TAG, "%s l2cap init failed: %s\n", __func__, esp_err_to_name(ret)); + return; + } + + if ((ret = esp_sdp_register_callback(esp_sdp_cb)) != ESP_OK) { + ESP_LOGE(SDP_TAG, "%s sdp register failed: %s\n", __func__, esp_err_to_name(ret)); + return; + } + + if ((ret = esp_sdp_init()) != ESP_OK) { + ESP_LOGE(SDP_TAG, "%s sdp init failed: %s\n", __func__, esp_err_to_name(ret)); + return; + } + +#if (CONFIG_BT_SSP_ENABLED == true) + /* Set default parameters for Secure Simple Pairing */ + esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE; + esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_IO; + esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t)); +#endif + + /* + * Set default parameters for Legacy Pairing + * Use variable pin, input pin code when pairing + */ + esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_VARIABLE; + esp_bt_pin_code_t pin_code; + esp_bt_gap_set_pin(pin_type, 0, pin_code); + + ESP_LOGI(L2CAP_TAG, "Own address:[%s]", bda2str((uint8_t *)esp_bt_dev_get_address(), bda_str, sizeof(bda_str))); +} diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_server/sdkconfig.defaults b/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_server/sdkconfig.defaults new file mode 100644 index 0000000000..8fd8809a41 --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_server/sdkconfig.defaults @@ -0,0 +1,9 @@ +# Override some defaults so BT stack is enabled and +# Classic BT is enabled +CONFIG_BT_ENABLED=y +CONFIG_BTDM_CTRL_MODE_BLE_ONLY=n +CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=y +CONFIG_BTDM_CTRL_MODE_BTDM=n +CONFIG_BT_BLUEDROID_ENABLED=y +CONFIG_BT_CLASSIC_ENABLED=y +CONFIG_BT_L2CAP_ENABLED=y From 7624d5dd93596d36fedfdc64b94c98f4f9cefefe Mon Sep 17 00:00:00 2001 From: xiongweichao Date: Thu, 21 Apr 2022 19:28:51 +0800 Subject: [PATCH 08/10] add l2cap client demo --- .../classic_bt/bt_l2cap_client/CMakeLists.txt | 6 + .../classic_bt/bt_l2cap_client/README.md | 66 +++ .../bt_l2cap_client/main/CMakeLists.txt | 3 + .../bt_l2cap_client/main/bt_app_core.c | 144 ++++++ .../bt_l2cap_client/main/bt_app_core.h | 77 +++ .../classic_bt/bt_l2cap_client/main/main.c | 457 ++++++++++++++++++ .../bt_l2cap_client/sdkconfig.defaults | 9 + 7 files changed, 762 insertions(+) create mode 100644 examples/bluetooth/bluedroid/classic_bt/bt_l2cap_client/CMakeLists.txt create mode 100644 examples/bluetooth/bluedroid/classic_bt/bt_l2cap_client/README.md create mode 100644 examples/bluetooth/bluedroid/classic_bt/bt_l2cap_client/main/CMakeLists.txt create mode 100644 examples/bluetooth/bluedroid/classic_bt/bt_l2cap_client/main/bt_app_core.c create mode 100644 examples/bluetooth/bluedroid/classic_bt/bt_l2cap_client/main/bt_app_core.h create mode 100644 examples/bluetooth/bluedroid/classic_bt/bt_l2cap_client/main/main.c create mode 100644 examples/bluetooth/bluedroid/classic_bt/bt_l2cap_client/sdkconfig.defaults diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_client/CMakeLists.txt b/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_client/CMakeLists.txt new file mode 100644 index 0000000000..e1c8577f45 --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_client/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(bt_l2cap_client) diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_client/README.md b/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_client/README.md new file mode 100644 index 0000000000..69f7921251 --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_client/README.md @@ -0,0 +1,66 @@ +| Supported Targets | ESP32 | +| ----------------- | ----- | + +ESP-IDF BT-L2CAP-CLIENT EXAMPLE +=================================== + +This example is to show how to use the APIs of **Logical Link Control and Adaptation Layer Protocol** (**L2CAP**) to create an L2CAP client. We also provide demo `bt_l2cap_server` to create L2CAP server. In fact, you can create L2CAP clients and L2CAP servers on a single device at the same time. + +## How to use example + +### Hardware Required + +This example is designed to run on commonly available ESP32 development board, e.g. ESP32-DevKitC. To operate the example, it is supposed to connect to [bt_l2cap_server example](../bt_l2cap_server) in ESP-IDF. + +### Configure the project + +1. Open the project configuration menu: + +``` +idf.py menuconfig +``` + +2. Enable the BT-L2CAP functionality by choosing the path as following: + +`Component config --> Bluetooth --> Bluedroid Options --> BT-L2CAP` + +3. If you want to limit the number of connected devices, please make sure set the `BT/BLE MAX ACL CONNECTIONS` and `BR/EDR ACL Max Connections` with same value you want. + +`Component config --> Bluetooth --> Bluedroid Options --> BT/BLE MAX ACL CONNECTIONS(1~7)` +and +`Component config --> Bluetooth --> Bluetooth --> Bluetooth controller --> BR/EDR ACL Max Connections` + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +idf.py -p PORT flash monitor +``` + +(Replace PORT with the name of the serial port to use.) + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +For the first step, this example performs device discovery to search for a target device whose device name is "ESP_BT_L2CAP_SERVER". If a candidate target is found, the local device will initiate connection with it. + +When you run this example, the explain prints the following at the very begining: +``` +I (1383) L2CAP_TAG: ESP_BT_L2CAP_INIT_EVT: status:0 + +I (1393) SDP_TAG: ESP_SDP_INIT_EVT: status:0 + +I (1393) SDP_TAG: ESP_SDP_CREATE_RECORD_COMP_EVT: status:0 + +I (1403) L2CAP_TAG: event: 10 +I (1413) L2CAP_TAG: event: 1 +I (1403) L2CAP_TAG: Own address:[c4:dd:57:5b:e7:46] +``` + +## Troubleshooting +* This example just demonstrates how to use the SDP and L2CAP APIs. This example cannot establish an l2cap connection with other devices (smartphones, computers, etc.) because the UUID is unknown and does not conform to the Bluetooth protocol standard. +* You can complete OBEX related profiles through SPP, SDP and L2CAP APIs. diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_client/main/CMakeLists.txt b/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_client/main/CMakeLists.txt new file mode 100644 index 0000000000..e2d9e2f26b --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_client/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "main.c" + "bt_app_core.c" + INCLUDE_DIRS ".") diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_client/main/bt_app_core.c b/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_client/main/bt_app_core.c new file mode 100644 index 0000000000..58b22dddea --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_client/main/bt_app_core.c @@ -0,0 +1,144 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include +#include +#include "freertos/xtensa_api.h" +#include "freertos/FreeRTOSConfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "bt_app_core.h" +#include "freertos/ringbuf.h" + +/******************************* + * STATIC FUNCTION DECLARATIONS + ******************************/ + +/* handler for application task */ +static void bt_app_task_handler(void *arg); +/* message sender */ +static bool bt_app_send_msg(bt_app_msg_t *msg); +/* handle dispatched messages */ +static void bt_app_work_dispatched(bt_app_msg_t *msg); + +/******************************* + * STATIC VARIABLE DEFINITIONS + ******************************/ + +static QueueHandle_t s_bt_app_task_queue = NULL; /* handle of work queue */ +static TaskHandle_t s_bt_app_task_handle = NULL; /* handle of application task */ + +/******************************* + * STATIC FUNCTION DEFINITIONS + ******************************/ + +static bool bt_app_send_msg(bt_app_msg_t *msg) +{ + if (msg == NULL) { + return false; + } + + /* send the message to work queue */ + if (xQueueSend(s_bt_app_task_queue, msg, 10 / portTICK_PERIOD_MS) != pdTRUE) { + ESP_LOGE(BT_APP_CORE_TAG, "%s xQueue send failed", __func__); + return false; + } + return true; +} + +static void bt_app_work_dispatched(bt_app_msg_t *msg) +{ + if (msg->cb) { + msg->cb(msg->event, msg->param); + } +} + +static void bt_app_task_handler(void *arg) +{ + bt_app_msg_t msg; + + for (;;) { + /* receive message from work queue and handle it */ + if (pdTRUE == xQueueReceive(s_bt_app_task_queue, &msg, (TickType_t)portMAX_DELAY)) { + ESP_LOGD(BT_APP_CORE_TAG, "%s, signal: 0x%x, event: 0x%x", __func__, msg.sig, msg.event); + + switch (msg.sig) { + case BT_APP_SIG_WORK_DISPATCH: + bt_app_work_dispatched(&msg); + break; + default: + ESP_LOGW(BT_APP_CORE_TAG, "%s, unhandled signal: %d", __func__, msg.sig); + break; + } /* switch (msg.sig) */ + + if (msg.param) { + free(msg.param); + } + } + } +} + +/******************************** + * EXTERNAL FUNCTION DEFINITIONS + *******************************/ + +bool bt_app_work_dispatch(bt_app_cb_t p_cback, uint16_t event, void *p_params, int param_len, bt_app_copy_cb_t p_copy_cback) +{ + ESP_LOGD(BT_APP_CORE_TAG, "%s event: 0x%x, param len: %d", __func__, event, param_len); + + bt_app_msg_t msg; + memset(&msg, 0, sizeof(bt_app_msg_t)); + + msg.sig = BT_APP_SIG_WORK_DISPATCH; + msg.event = event; + msg.cb = p_cback; + + if (param_len == 0) { + return bt_app_send_msg(&msg); + } else if (p_params && param_len > 0) { + if ((msg.param = malloc(param_len)) != NULL) { + memcpy(msg.param, p_params, param_len); + /* check if caller has provided a copy callback to do the deep copy */ + if (p_copy_cback) { + p_copy_cback(msg.param, p_params, param_len); + } + return bt_app_send_msg(&msg); + } + } + + return false; +} + +void bt_app_task_start_up(void) +{ + s_bt_app_task_queue = xQueueCreate(10, sizeof(bt_app_msg_t)); + xTaskCreate(bt_app_task_handler, "BtAppTask", 3072, NULL, configMAX_PRIORITIES - 3, &s_bt_app_task_handle); +} + +void bt_app_task_shut_down(void) +{ + if (s_bt_app_task_handle) { + vTaskDelete(s_bt_app_task_handle); + s_bt_app_task_handle = NULL; + } + if (s_bt_app_task_queue) { + vQueueDelete(s_bt_app_task_queue); + s_bt_app_task_queue = NULL; + } +} + +void l2cap_wr_task_start_up(l2cap_wr_task_cb_t p_cback, int fd) +{ + xTaskCreate(p_cback, "write_read", 2048, (void *)fd, 5, NULL); +} + +void l2cap_wr_task_shut_down(void) +{ + vTaskDelete(NULL); +} diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_client/main/bt_app_core.h b/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_client/main/bt_app_core.h new file mode 100644 index 0000000000..7627ae28d1 --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_client/main/bt_app_core.h @@ -0,0 +1,77 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#ifndef __BT_APP_CORE_H__ +#define __BT_APP_CORE_H__ + +#include +#include +#include + +/* log tag */ +#define BT_APP_CORE_TAG "BT_APP_CORE" + +/* signal for `bt_app_work_dispatch` */ +#define BT_APP_SIG_WORK_DISPATCH (0x01) + +/** + * @brief handler for the dispatched work + * + * @param [in] event event id + * @param [in] param handler parameter + */ +typedef void (* bt_app_cb_t) (uint16_t event, void *param); + +/** + * @brief handler for write and read + */ +typedef void (* l2cap_wr_task_cb_t) (void *fd); + +/* message to be sent */ +typedef struct { + uint16_t sig; /*!< signal to bt_app_task */ + uint16_t event; /*!< message event id */ + bt_app_cb_t cb; /*!< context switch callback */ + void *param; /*!< parameter area needs to be last */ +} bt_app_msg_t; + +/** + * @brief parameter deep-copy function to be customized + * + * @param [out] p_dest pointer to destination data + * @param [in] p_src pointer to source data + * @param [in] len data length in byte + */ +typedef void (* bt_app_copy_cb_t) (void *p_dest, void *p_src, int len); + +/** + * @brief work dispatcher for the application task + * + * @param [in] p_cback callback function + * @param [in] event event id + * @param [in] p_params callback paramters + * @param [in] param_len parameter length in byte + * @param [in] p_copy_cback parameter deep-copy function + * + * @return true if work dispatch successfully, false otherwise + */ +bool bt_app_work_dispatch(bt_app_cb_t p_cback, uint16_t event, void *p_params, int param_len, bt_app_copy_cb_t p_copy_cback); + +/** + * @brief start up the application task + */ +void bt_app_task_start_up(void); + +/** + * @brief shut down the application task + */ +void bt_app_task_shut_down(void); + +void l2cap_wr_task_start_up(l2cap_wr_task_cb_t p_cback, int fd); + +void l2cap_wr_task_shut_down(void); + +#endif /* __BT_APP_CORE_H__ */ diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_client/main/main.c b/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_client/main/main.c new file mode 100644 index 0000000000..a3e27e9de8 --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_client/main/main.c @@ -0,0 +1,457 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include +#include +#include +#include +#include "nvs.h" +#include "nvs_flash.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "esp_bt.h" +#include "esp_bt_main.h" +#include "esp_gap_bt_api.h" +#include "esp_bt_device.h" +#include "esp_l2cap_bt_api.h" + +#include "time.h" +#include "sys/time.h" +#include "esp_vfs.h" +#include "esp_sdp_api.h" +#include "bt_app_core.h" + +#define L2CAP_TAG "L2CAP_TAG" +#define SDP_TAG "SDP_TAG" +#define EXAMPLE_DEVICE_NAME "ESP_BT_L2CAP_CLIENT" +#define TARGET_DEVICE_NAME "ESP_BT_L2CAP_SERVER" +#define L2CAP_DATA_LEN 100 +#define BT_L2CAP_DYNMIC_PSM 0x1001 +#define BT_UNUSED_RFCOMM -1 +#define BT_UNKONWN_PROFILE_VERSION 0x0102 + +static esp_bt_l2cap_cntl_flags_t sec_mask = ESP_BT_L2CAP_SEC_AUTHENTICATE; +static char *sdp_service_name = "Unknown_profile"; +static const uint8_t UUID_UNKNOWN[] = {0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB + }; + +static void esp_bt_l2cap_cb(esp_bt_l2cap_cb_event_t event, esp_bt_l2cap_cb_param_t *param); +static void esp_hdl_bt_l2cap_cb_evt(uint16_t event, void *p_param); +static void esp_hdl_sdp_cb_evt(uint16_t event, void *p_param); + +static char *bda2str(esp_bd_addr_t bda, char *str, size_t size) +{ + if (bda == NULL || str == NULL || size < 18) { + return NULL; + } + + sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x", + bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); + return str; +} + +static bool get_name_from_eir(uint8_t *eir, uint8_t *bdname, uint8_t *bdname_len) +{ + uint8_t *rmt_bdname = NULL; + uint8_t rmt_bdname_len = 0; + + if (!eir) { + return false; + } + + /* get complete or short local name from eir data */ + rmt_bdname = esp_bt_gap_resolve_eir_data(eir, ESP_BT_EIR_TYPE_CMPL_LOCAL_NAME, &rmt_bdname_len); + if (!rmt_bdname) { + rmt_bdname = esp_bt_gap_resolve_eir_data(eir, ESP_BT_EIR_TYPE_SHORT_LOCAL_NAME, &rmt_bdname_len); + } + + if (rmt_bdname) { + if (rmt_bdname_len > ESP_BT_GAP_MAX_BDNAME_LEN) { + rmt_bdname_len = ESP_BT_GAP_MAX_BDNAME_LEN; + } + + if (bdname) { + memcpy(bdname, rmt_bdname, rmt_bdname_len); + bdname[rmt_bdname_len] = '\0'; + } + if (bdname_len) { + *bdname_len = rmt_bdname_len; + } + return true; + } + + return false; +} + +static void filter_inquiry_scan_result(esp_bt_gap_cb_param_t *param) +{ + char bda_str[18]; + uint32_t cod = 0; /* class of device */ + int32_t rssi = -129; /* invalid value */ + uint8_t *eir = NULL; + uint8_t peer_bdname[ESP_BT_GAP_MAX_BDNAME_LEN + 1]; + esp_bt_gap_dev_prop_t *p; + esp_bt_uuid_t uuid = {0}; + + /* handle the discovery results */ + ESP_LOGI(L2CAP_TAG, "Scanned device: %s", bda2str(param->disc_res.bda, bda_str, 18)); + for (int i = 0; i < param->disc_res.num_prop; i++) { + p = param->disc_res.prop + i; + switch (p->type) { + case ESP_BT_GAP_DEV_PROP_COD: + cod = *(uint32_t *)(p->val); + ESP_LOGI(L2CAP_TAG, "--Class of Device: 0x%x", cod); + break; + case ESP_BT_GAP_DEV_PROP_RSSI: + rssi = *(int8_t *)(p->val); + ESP_LOGI(L2CAP_TAG, "--RSSI: %d", rssi); + break; + case ESP_BT_GAP_DEV_PROP_EIR: + eir = (uint8_t *)(p->val); + break; + case ESP_BT_GAP_DEV_PROP_BDNAME: + default: + break; + } + } + + /* search for target device in its Extended Inqury Response */ + if (eir) { + get_name_from_eir(eir, peer_bdname, NULL); + if (strcmp((char *)peer_bdname, TARGET_DEVICE_NAME) == 0) { + ESP_LOGI(L2CAP_TAG, "Found a target device, address %s, name %s", bda_str, peer_bdname); + ESP_LOGI(L2CAP_TAG, "Cancel device discovery ..."); + esp_bt_gap_cancel_discovery(); + + uuid.len = sizeof(UUID_UNKNOWN); + memcpy(uuid.uuid.uuid128, UUID_UNKNOWN, sizeof(UUID_UNKNOWN)); + esp_sdp_search_record(param->disc_res.bda, uuid); + } + } +} + +static void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) +{ + char bda_str[18] = {0}; + + switch (event) { + /* when device discovered a result, this event comes */ + case ESP_BT_GAP_DISC_RES_EVT: { + filter_inquiry_scan_result(param); + break; + } + /* when Legacy Pairing pin code requested, this event comes */ + case ESP_BT_GAP_AUTH_CMPL_EVT:{ + if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) { + ESP_LOGI(L2CAP_TAG, "authentication success: %s bda:[%s]", param->auth_cmpl.device_name, + bda2str(param->auth_cmpl.bda, bda_str, sizeof(bda_str))); + } else { + ESP_LOGE(L2CAP_TAG, "authentication failed, status:%d", param->auth_cmpl.stat); + } + break; + } + /* when Security Simple Pairing user confirmation requested, this event comes */ + case ESP_BT_GAP_PIN_REQ_EVT:{ + ESP_LOGI(L2CAP_TAG, "ESP_BT_GAP_PIN_REQ_EVT min_16_digit:%d", param->pin_req.min_16_digit); + if (param->pin_req.min_16_digit) { + ESP_LOGI(L2CAP_TAG, "Input pin code: 0000 0000 0000 0000"); + esp_bt_pin_code_t pin_code = {0}; + esp_bt_gap_pin_reply(param->pin_req.bda, true, 16, pin_code); + } else { + ESP_LOGI(L2CAP_TAG, "Input pin code: 1234"); + esp_bt_pin_code_t pin_code; + pin_code[0] = '1'; + pin_code[1] = '2'; + pin_code[2] = '3'; + pin_code[3] = '4'; + esp_bt_gap_pin_reply(param->pin_req.bda, true, 4, pin_code); + } + break; + } + +#if (CONFIG_BT_SSP_ENABLED == true) + /* when Security Simple Pairing user confirmation requested, this event comes */ + case ESP_BT_GAP_CFM_REQ_EVT: + ESP_LOGI(L2CAP_TAG, "ESP_BT_GAP_CFM_REQ_EVT Please compare the numeric value: %d", param->cfm_req.num_val); + esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, true); + break; + /* when Security Simple Pairing passkey notified, this event comes */ + case ESP_BT_GAP_KEY_NOTIF_EVT: + ESP_LOGI(L2CAP_TAG, "ESP_BT_GAP_KEY_NOTIF_EVT passkey: %d", param->key_notif.passkey); + break; + /* when Security Simple Pairing passkey requested, this event comes */ + case ESP_BT_GAP_KEY_REQ_EVT: + ESP_LOGI(L2CAP_TAG, "ESP_BT_GAP_KEY_REQ_EVT Please enter passkey!"); + break; +#endif + + /* when GAP mode changed, this event comes */ + case ESP_BT_GAP_MODE_CHG_EVT: + ESP_LOGI(L2CAP_TAG, "ESP_BT_GAP_MODE_CHG_EVT mode:%d bda:[%s]", param->mode_chg.mode, + bda2str(param->mode_chg.bda, bda_str, sizeof(bda_str))); + break; + /* other */ + default: { + ESP_LOGI(L2CAP_TAG, "event: %d", event); + break; + } + } + return; +} + +static void l2cap_write_handle(void * param) +{ + int size = 0; + int fd = (int)param; + uint8_t *l2cap_data = NULL; + uint16_t i = 0; + + l2cap_data = malloc(L2CAP_DATA_LEN); + if (!l2cap_data) { + ESP_LOGE(L2CAP_TAG, "malloc l2cap_data failed, fd:%d", fd); + goto done; + } + + for (i = 0; i < L2CAP_DATA_LEN; ++i) { + l2cap_data[i] = i; + } + + do { + /* + * The write function is blocked until all the target length of data has been sent to the lower layer + * successfully an error occurs. + */ + size = write(fd, l2cap_data, L2CAP_DATA_LEN); + if (size == -1) { + break; + } else if (size == 0) { + /*write fail due to ringbuf is full, retry after 500 ms*/ + vTaskDelay(500 / portTICK_PERIOD_MS); + } else { + ESP_LOGI(L2CAP_TAG, "fd = %d data_len = %d", fd, size); + vTaskDelay(50 / portTICK_PERIOD_MS); + } + } while (1); +done: + if (l2cap_data) { + free(l2cap_data); + } + l2cap_wr_task_shut_down(); +} + +static void esp_bt_l2cap_cb(esp_bt_l2cap_cb_event_t event, esp_bt_l2cap_cb_param_t *param) +{ + switch (event) { + case ESP_BT_L2CAP_INIT_EVT: + case ESP_BT_L2CAP_UNINIT_EVT: + case ESP_BT_L2CAP_OPEN_EVT: + case ESP_BT_L2CAP_CLOSE_EVT: + case ESP_BT_L2CAP_CL_INIT_EVT: + case ESP_BT_L2CAP_START_EVT: + case ESP_BT_L2CAP_SRV_STOP_EVT: { + bt_app_work_dispatch(esp_hdl_bt_l2cap_cb_evt, event, param, sizeof(esp_bt_l2cap_cb_param_t), NULL); + break; + } + default: + ESP_LOGE(L2CAP_TAG, "Invalid L2CAP event: %d", event); + break; + } +} + +static void esp_hdl_bt_l2cap_cb_evt(uint16_t event, void *p_param) +{ + char bda_str[18] = {0}; + esp_bt_l2cap_cb_param_t *l2cap_param = (esp_bt_l2cap_cb_param_t *)p_param; + + switch (event) { + case ESP_BT_L2CAP_INIT_EVT: + ESP_LOGI(L2CAP_TAG, "ESP_BT_L2CAP_INIT_EVT: status:%d\n", l2cap_param->init.status); + if (l2cap_param->init.status == ESP_BT_L2CAP_SUCCESS) { + esp_bt_l2cap_vfs_register(); + } + break; + case ESP_BT_L2CAP_UNINIT_EVT: + ESP_LOGI(L2CAP_TAG, "ESP_BT_L2CAP_UNINIT_EVT: status:%d\n", l2cap_param->uninit.status); + break; + case ESP_BT_L2CAP_OPEN_EVT: + if (l2cap_param->open.status == ESP_BT_L2CAP_SUCCESS) { + ESP_LOGI(L2CAP_TAG, "ESP_BT_L2CAP_OPEN_EVT: status:%d, fd = %d, tx mtu = %d, remote_address:%s\n", l2cap_param->open.status, + l2cap_param->open.fd, l2cap_param->open.tx_mtu, bda2str(l2cap_param->open.rem_bda, bda_str, sizeof(bda_str))); + l2cap_wr_task_start_up(l2cap_write_handle, l2cap_param->open.fd); + } else { + ESP_LOGI(L2CAP_TAG, "ESP_BT_L2CAP_OPEN_EVT: status:%d\n", l2cap_param->open.status); + } + break; + case ESP_BT_L2CAP_CLOSE_EVT: + ESP_LOGI(L2CAP_TAG, "ESP_BT_L2CAP_CLOSE_EVT: status:%d\n", l2cap_param->close.status); + break; + case ESP_BT_L2CAP_CL_INIT_EVT: + ESP_LOGI(L2CAP_TAG, "ESP_BT_L2CAP_CL_INIT_EVT: status:%d\n", l2cap_param->cl_init.status); + break; + case ESP_BT_L2CAP_START_EVT: + if (l2cap_param->start.status == ESP_BT_L2CAP_SUCCESS) { + ESP_LOGI(L2CAP_TAG, "ESP_BT_L2CAP_START_EVT: status:%d, hdl:0x%x, sec_id:0x%x\n", + l2cap_param->start.status, l2cap_param->start.handle, l2cap_param->start.sec_id); + } else { + ESP_LOGI(L2CAP_TAG, "ESP_BT_L2CAP_START_EVT: status:%d\n", l2cap_param->start.status); + } + break; + case ESP_BT_L2CAP_SRV_STOP_EVT: + ESP_LOGI(L2CAP_TAG, "ESP_BT_L2CAP_CLOSE_EVT: status:%d, psm = 0x%x\n", l2cap_param->srv_stop.status, l2cap_param->srv_stop.psm); + break; + default: + break; + } + return; +} + +static void esp_sdp_cb(esp_sdp_cb_event_t event, esp_sdp_cb_param_t *param) +{ + switch (event) { + case ESP_SDP_INIT_EVT: + case ESP_SDP_DEINIT_EVT: + case ESP_SDP_SEARCH_COMP_EVT: + case ESP_SDP_CREATE_RECORD_COMP_EVT: + case ESP_SDP_REMOVE_RECORD_COMP_EVT: { + bt_app_work_dispatch(esp_hdl_sdp_cb_evt, event, param, sizeof(esp_sdp_cb_param_t), NULL); + break; + } + default: + ESP_LOGE(SDP_TAG, "Invalid SDP event: %d", event); + break; + } +} + +static void esp_hdl_sdp_cb_evt(uint16_t event, void *p_param) +{ + esp_bluetooth_sdp_record_t record = {0}; + esp_sdp_cb_param_t *sdp_param = (esp_sdp_cb_param_t *)p_param; + char bda_str[18] = {0}; + + switch (event) { + case ESP_SDP_INIT_EVT: + ESP_LOGI(SDP_TAG, "ESP_SDP_INIT_EVT: status:%d\n", sdp_param->init.status); + if (sdp_param->init.status == ESP_SDP_SUCCESS) { + record.hdr.type = ESP_SDP_TYPE_RAW; + record.hdr.uuid.len = sizeof(UUID_UNKNOWN); + memcpy(record.hdr.uuid.uuid.uuid128, UUID_UNKNOWN, sizeof(UUID_UNKNOWN)); + record.hdr.service_name_length = strlen(sdp_service_name) + 1; + record.hdr.service_name = sdp_service_name; + record.hdr.rfcomm_channel_number = BT_UNUSED_RFCOMM; + record.hdr.l2cap_psm = BT_L2CAP_DYNMIC_PSM; + record.hdr.profile_version = BT_UNKONWN_PROFILE_VERSION; + esp_sdp_create_record(&record); + } + break; + case ESP_SDP_DEINIT_EVT: + ESP_LOGI(SDP_TAG, "ESP_SDP_DEINIT_EVT: status:%d\n", sdp_param->deinit.status); + break; + case ESP_SDP_SEARCH_COMP_EVT: + ESP_LOGI(SDP_TAG, "ESP_SDP_SEARCH_COMP_EVT: status:%d\n", sdp_param->search.status); + if (sdp_param->search.status == ESP_SDP_SUCCESS) { + ESP_LOGI(SDP_TAG, "Remote device address: %s\n", bda2str(sdp_param->search.remote_addr, bda_str, sizeof(bda_str))); + ESP_LOGI(SDP_TAG, "Remote device record count: %d\n", sdp_param->search.record_count); + ESP_LOGI(SDP_TAG, "Remote device rfcomm channel number: %d\n", sdp_param->search.records->hdr.rfcomm_channel_number); + ESP_LOGI(SDP_TAG, "Remote device l2cap psm: 0x%04x\n", sdp_param->search.records->hdr.l2cap_psm); + esp_bt_l2cap_connect(sec_mask, sdp_param->search.records->hdr.l2cap_psm, sdp_param->search.remote_addr); + } + break; + case ESP_SDP_CREATE_RECORD_COMP_EVT: + ESP_LOGI(SDP_TAG, "ESP_SDP_CREATE_RECORD_COMP_EVT: status:%d\n", sdp_param->create_record.status); + if (sdp_param->create_record.status == ESP_SDP_SUCCESS) { + esp_bt_dev_set_device_name(EXAMPLE_DEVICE_NAME); + esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); + esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0); + } + break; + case ESP_SDP_REMOVE_RECORD_COMP_EVT: + ESP_LOGI(SDP_TAG, "ESP_SDP_REMOVE_RECORD_COMP_EVT: status:%d\n", sdp_param->remove_record.status); + break; + default: + break; + } +} + +void app_main(void) +{ + char bda_str[18] = {0}; + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK( ret ); + + ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_BLE)); + + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + if ((ret = esp_bt_controller_init(&bt_cfg)) != ESP_OK) { + ESP_LOGE(L2CAP_TAG, "%s initialize controller failed: %s\n", __func__, esp_err_to_name(ret)); + return; + } + + if ((ret = esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT)) != ESP_OK) { + ESP_LOGE(L2CAP_TAG, "%s enable controller failed: %s\n", __func__, esp_err_to_name(ret)); + return; + } + + if ((ret = esp_bluedroid_init()) != ESP_OK) { + ESP_LOGE(L2CAP_TAG, "%s initialize bluedroid failed: %s\n", __func__, esp_err_to_name(ret)); + return; + } + + if ((ret = esp_bluedroid_enable()) != ESP_OK) { + ESP_LOGE(L2CAP_TAG, "%s enable bluedroid failed: %s\n", __func__, esp_err_to_name(ret)); + return; + } + + bt_app_task_start_up(); + + if ((ret = esp_bt_gap_register_callback(esp_bt_gap_cb)) != ESP_OK) { + ESP_LOGE(L2CAP_TAG, "%s gap register failed: %s\n", __func__, esp_err_to_name(ret)); + return; + } + + if ((ret = esp_bt_l2cap_register_callback(esp_bt_l2cap_cb)) != ESP_OK) { + ESP_LOGE(L2CAP_TAG, "%s l2cap register failed: %s\n", __func__, esp_err_to_name(ret)); + return; + } + + if ((ret = esp_bt_l2cap_init()) != ESP_OK) { + ESP_LOGE(L2CAP_TAG, "%s l2cap init failed: %s\n", __func__, esp_err_to_name(ret)); + return; + } + + if ((ret = esp_sdp_register_callback(esp_sdp_cb)) != ESP_OK) { + ESP_LOGE(SDP_TAG, "%s sdp register failed: %s\n", __func__, esp_err_to_name(ret)); + return; + } + + if ((ret = esp_sdp_init()) != ESP_OK) { + ESP_LOGE(SDP_TAG, "%s sdp init failed: %s\n", __func__, esp_err_to_name(ret)); + return; + } + +#if (CONFIG_BT_SSP_ENABLED == true) + /* Set default parameters for Secure Simple Pairing */ + esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE; + esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_IO; + esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t)); +#endif + + /* + * Set default parameters for Legacy Pairing + * Use variable pin, input pin code when pairing + */ + esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_VARIABLE; + esp_bt_pin_code_t pin_code; + esp_bt_gap_set_pin(pin_type, 0, pin_code); + + ESP_LOGI(L2CAP_TAG, "Own address:[%s]", bda2str((uint8_t *)esp_bt_dev_get_address(), bda_str, sizeof(bda_str))); +} diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_client/sdkconfig.defaults b/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_client/sdkconfig.defaults new file mode 100644 index 0000000000..8fd8809a41 --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/bt_l2cap_client/sdkconfig.defaults @@ -0,0 +1,9 @@ +# Override some defaults so BT stack is enabled and +# Classic BT is enabled +CONFIG_BT_ENABLED=y +CONFIG_BTDM_CTRL_MODE_BLE_ONLY=n +CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=y +CONFIG_BTDM_CTRL_MODE_BTDM=n +CONFIG_BT_BLUEDROID_ENABLED=y +CONFIG_BT_CLASSIC_ENABLED=y +CONFIG_BT_L2CAP_ENABLED=y From b2987c1e946703cb48d4fff5a123902113d3b51d Mon Sep 17 00:00:00 2001 From: xiongweichao Date: Wed, 18 May 2022 15:48:21 +0800 Subject: [PATCH 09/10] Added BT_L2CAP API reference document --- docs/conf_common.py | 1 + docs/doxygen/Doxyfile | 1 + docs/en/api-reference/bluetooth/classic_bt.rst | 1 + docs/en/api-reference/bluetooth/esp_l2cap_bt.rst | 15 +++++++++++++++ .../api-reference/bluetooth/esp_l2cap_bt.rst | 1 + 5 files changed, 19 insertions(+) create mode 100644 docs/en/api-reference/bluetooth/esp_l2cap_bt.rst create mode 100644 docs/zh_CN/api-reference/bluetooth/esp_l2cap_bt.rst diff --git a/docs/conf_common.py b/docs/conf_common.py index a098a9db68..b33ae14699 100644 --- a/docs/conf_common.py +++ b/docs/conf_common.py @@ -40,6 +40,7 @@ CLASSIC_BT_DOCS = ['api-reference/bluetooth/classic_bt.rst', 'api-reference/bluetooth/esp_a2dp.rst', 'api-reference/bluetooth/esp_avrc.rst', 'api-reference/bluetooth/esp_hidd.rst', + 'api-reference/bluetooth/esp_l2cap_bt.rst', 'api-reference/bluetooth/esp_hf_defs.rst', 'api-reference/bluetooth/esp_hf_client.rst', 'api-reference/bluetooth/esp_hf_ag.rst', diff --git a/docs/doxygen/Doxyfile b/docs/doxygen/Doxyfile index 11a187fedc..4e70c392ab 100644 --- a/docs/doxygen/Doxyfile +++ b/docs/doxygen/Doxyfile @@ -56,6 +56,7 @@ INPUT = \ $(PROJECT_PATH)/components/bt/host/bluedroid/api/include/api/esp_hf_defs.h \ $(PROJECT_PATH)/components/bt/host/bluedroid/api/include/api/esp_spp_api.h \ $(PROJECT_PATH)/components/bt/host/bluedroid/api/include/api/esp_hidd_api.h \ + $(PROJECT_PATH)/components/bt/host/bluedroid/api/include/api/esp_l2cap_bt_api.h \ $(PROJECT_PATH)/components/bt/host/nimble/esp-hci/include/esp_nimble_hci.h \ $(PROJECT_PATH)/components/bt/include/esp32/include/esp_bt.h \ $(PROJECT_PATH)/components/console/esp_console.h \ diff --git a/docs/en/api-reference/bluetooth/classic_bt.rst b/docs/en/api-reference/bluetooth/classic_bt.rst index d1d78fbf9c..cc0df3b93b 100644 --- a/docs/en/api-reference/bluetooth/classic_bt.rst +++ b/docs/en/api-reference/bluetooth/classic_bt.rst @@ -12,3 +12,4 @@ CLASSIC BT BT HFP Client BT HFP AG BT HID DEVICE + BT L2CAP diff --git a/docs/en/api-reference/bluetooth/esp_l2cap_bt.rst b/docs/en/api-reference/bluetooth/esp_l2cap_bt.rst new file mode 100644 index 0000000000..38d6d13318 --- /dev/null +++ b/docs/en/api-reference/bluetooth/esp_l2cap_bt.rst @@ -0,0 +1,15 @@ +Clissic Bluetooth L2CAP API +============================ + +Application Example +------------------- + +Check :example:`bluetooth/bluedroid/classic_bt` folder in ESP-IDF examples, which contains the following application: + +* This is a BT_L2CAP demo. This demo can connect, send and recive L2CAP data :example:`bluetooth/bluedroid/classic_bt/bt_l2cap_client`, :example:`bluetooth/bluedroid/classic_bt/bt_l2cap_server` + +API Reference +------------- + +.. include-build-file:: inc/esp_l2cap_bt_api.inc + diff --git a/docs/zh_CN/api-reference/bluetooth/esp_l2cap_bt.rst b/docs/zh_CN/api-reference/bluetooth/esp_l2cap_bt.rst new file mode 100644 index 0000000000..24031e6367 --- /dev/null +++ b/docs/zh_CN/api-reference/bluetooth/esp_l2cap_bt.rst @@ -0,0 +1 @@ +.. include:: ../../../en/api-reference/bluetooth/esp_l2cap_bt.rst \ No newline at end of file From 1423d02cb075bb74f993c681c767055b12d4564e Mon Sep 17 00:00:00 2001 From: wangmengyang Date: Sat, 28 May 2022 15:20:42 +0800 Subject: [PATCH 10/10] Added BT_SDP API reference document --- docs/conf_common.py | 1 + docs/doxygen/Doxyfile | 1 + docs/en/api-reference/bluetooth/classic_bt.rst | 1 + docs/en/api-reference/bluetooth/esp_sdp.rst | 14 ++++++++++++++ docs/zh_CN/api-reference/bluetooth/esp_sdp.rst | 1 + 5 files changed, 18 insertions(+) create mode 100644 docs/en/api-reference/bluetooth/esp_sdp.rst create mode 100644 docs/zh_CN/api-reference/bluetooth/esp_sdp.rst diff --git a/docs/conf_common.py b/docs/conf_common.py index b33ae14699..5d25b718c2 100644 --- a/docs/conf_common.py +++ b/docs/conf_common.py @@ -41,6 +41,7 @@ CLASSIC_BT_DOCS = ['api-reference/bluetooth/classic_bt.rst', 'api-reference/bluetooth/esp_avrc.rst', 'api-reference/bluetooth/esp_hidd.rst', 'api-reference/bluetooth/esp_l2cap_bt.rst', + 'api-reference/bluetooth/esp_sdp.rst', 'api-reference/bluetooth/esp_hf_defs.rst', 'api-reference/bluetooth/esp_hf_client.rst', 'api-reference/bluetooth/esp_hf_ag.rst', diff --git a/docs/doxygen/Doxyfile b/docs/doxygen/Doxyfile index 4e70c392ab..7393290ae3 100644 --- a/docs/doxygen/Doxyfile +++ b/docs/doxygen/Doxyfile @@ -57,6 +57,7 @@ INPUT = \ $(PROJECT_PATH)/components/bt/host/bluedroid/api/include/api/esp_spp_api.h \ $(PROJECT_PATH)/components/bt/host/bluedroid/api/include/api/esp_hidd_api.h \ $(PROJECT_PATH)/components/bt/host/bluedroid/api/include/api/esp_l2cap_bt_api.h \ + $(PROJECT_PATH)/components/bt/host/bluedroid/api/include/api/esp_sdp_api.h \ $(PROJECT_PATH)/components/bt/host/nimble/esp-hci/include/esp_nimble_hci.h \ $(PROJECT_PATH)/components/bt/include/esp32/include/esp_bt.h \ $(PROJECT_PATH)/components/console/esp_console.h \ diff --git a/docs/en/api-reference/bluetooth/classic_bt.rst b/docs/en/api-reference/bluetooth/classic_bt.rst index cc0df3b93b..0fbc223ab5 100644 --- a/docs/en/api-reference/bluetooth/classic_bt.rst +++ b/docs/en/api-reference/bluetooth/classic_bt.rst @@ -13,3 +13,4 @@ CLASSIC BT BT HFP AG BT HID DEVICE BT L2CAP + BT SDP diff --git a/docs/en/api-reference/bluetooth/esp_sdp.rst b/docs/en/api-reference/bluetooth/esp_sdp.rst new file mode 100644 index 0000000000..888d8b937a --- /dev/null +++ b/docs/en/api-reference/bluetooth/esp_sdp.rst @@ -0,0 +1,14 @@ +BT SDP APIs +============= + +Overview +-------- + +Bluetooth SDP reference APIs. + +API Reference +------------- + +.. include-build-file:: inc/esp_sdp_api.inc + + diff --git a/docs/zh_CN/api-reference/bluetooth/esp_sdp.rst b/docs/zh_CN/api-reference/bluetooth/esp_sdp.rst new file mode 100644 index 0000000000..7b18fc93bd --- /dev/null +++ b/docs/zh_CN/api-reference/bluetooth/esp_sdp.rst @@ -0,0 +1 @@ +.. include:: ../../../en/api-reference/bluetooth/esp_sdp.rst \ No newline at end of file