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 + /****************************************************************************** **