summaryrefslogtreecommitdiff
path: root/lib/bt/common
diff options
context:
space:
mode:
Diffstat (limited to 'lib/bt/common')
-rw-r--r--lib/bt/common/api/esp_blufi_api.c126
-rw-r--r--lib/bt/common/api/include/api/esp_blufi_api.h436
-rw-r--r--lib/bt/common/btc/core/btc_alarm.c20
-rw-r--r--lib/bt/common/btc/core/btc_manage.c44
-rw-r--r--lib/bt/common/btc/core/btc_task.c549
-rw-r--r--lib/bt/common/btc/include/btc/btc_alarm.h22
-rw-r--r--lib/bt/common/btc/include/btc/btc_manage.h23
-rw-r--r--lib/bt/common/btc/include/btc/btc_task.h160
-rw-r--r--lib/bt/common/btc/profile/esp/blufi/bluedroid_host/esp_blufi.c422
-rw-r--r--lib/bt/common/btc/profile/esp/blufi/blufi_prf.c901
-rw-r--r--lib/bt/common/btc/profile/esp/blufi/blufi_protocol.c253
-rw-r--r--lib/bt/common/btc/profile/esp/blufi/include/blufi_int.h197
-rw-r--r--lib/bt/common/btc/profile/esp/blufi/include/esp_blufi.h99
-rw-r--r--lib/bt/common/btc/profile/esp/blufi/nimble_host/esp_blufi.c543
-rw-r--r--lib/bt/common/btc/profile/esp/include/btc_blufi_prf.h103
-rw-r--r--lib/bt/common/include/bt_common.h215
-rw-r--r--lib/bt/common/include/bt_user_config.h101
-rw-r--r--lib/bt/common/osi/alarm.c331
-rw-r--r--lib/bt/common/osi/allocator.c260
-rw-r--r--lib/bt/common/osi/buffer.c102
-rw-r--r--lib/bt/common/osi/config.c740
-rw-r--r--lib/bt/common/osi/fixed_pkt_queue.c161
-rw-r--r--lib/bt/common/osi/fixed_queue.c256
-rw-r--r--lib/bt/common/osi/future.c97
-rw-r--r--lib/bt/common/osi/hash_functions.c63
-rw-r--r--lib/bt/common/osi/hash_map.c270
-rw-r--r--lib/bt/common/osi/include/osi/alarm.h84
-rw-r--r--lib/bt/common/osi/include/osi/allocator.h145
-rw-r--r--lib/bt/common/osi/include/osi/buffer.h59
-rw-r--r--lib/bt/common/osi/include/osi/config.h145
-rw-r--r--lib/bt/common/osi/include/osi/fixed_pkt_queue.h79
-rw-r--r--lib/bt/common/osi/include/osi/fixed_queue.h125
-rw-r--r--lib/bt/common/osi/include/osi/future.h53
-rw-r--r--lib/bt/common/osi/include/osi/hash_functions.h37
-rw-r--r--lib/bt/common/osi/include/osi/hash_map.h110
-rw-r--r--lib/bt/common/osi/include/osi/list.h121
-rw-r--r--lib/bt/common/osi/include/osi/mutex.h52
-rw-r--r--lib/bt/common/osi/include/osi/osi.h16
-rw-r--r--lib/bt/common/osi/include/osi/pkt_queue.h88
-rw-r--r--lib/bt/common/osi/include/osi/semaphore.h43
-rw-r--r--lib/bt/common/osi/include/osi/thread.h120
-rw-r--r--lib/bt/common/osi/list.c312
-rw-r--r--lib/bt/common/osi/mutex.c99
-rw-r--r--lib/bt/common/osi/osi.c25
-rw-r--r--lib/bt/common/osi/pkt_queue.c144
-rw-r--r--lib/bt/common/osi/semaphore.c77
-rw-r--r--lib/bt/common/osi/thread.c453
47 files changed, 8881 insertions, 0 deletions
diff --git a/lib/bt/common/api/esp_blufi_api.c b/lib/bt/common/api/esp_blufi_api.c
new file mode 100644
index 00000000..b2475537
--- /dev/null
+++ b/lib/bt/common/api/esp_blufi_api.c
@@ -0,0 +1,126 @@
+/*
+ * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+
+#include "esp_blufi_api.h"
+#include "btc/btc_task.h"
+#include "btc_blufi_prf.h"
+#include "btc/btc_manage.h"
+#include "osi/future.h"
+#if (BLUFI_INCLUDED == TRUE)
+esp_err_t esp_blufi_register_callbacks(esp_blufi_callbacks_t *callbacks)
+{
+ ESP_BLE_HOST_STATUS_CHECK(ESP_BLE_HOST_STATUS_ENABLED);
+
+ if (callbacks == NULL) {
+ return ESP_FAIL;
+ }
+
+ btc_blufi_set_callbacks(callbacks);
+ return (btc_profile_cb_set(BTC_PID_BLUFI, callbacks->event_cb) == 0 ? ESP_OK : ESP_FAIL);
+}
+
+esp_err_t esp_blufi_send_wifi_conn_report(wifi_mode_t opmode, esp_blufi_sta_conn_state_t sta_conn_state, uint8_t softap_conn_num, esp_blufi_extra_info_t *extra_info)
+{
+ btc_msg_t msg;
+ btc_blufi_args_t arg;
+
+ ESP_BLE_HOST_STATUS_CHECK(ESP_BLE_HOST_STATUS_ENABLED);
+
+ msg.sig = BTC_SIG_API_CALL;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = BTC_BLUFI_ACT_SEND_CFG_REPORT;
+ arg.wifi_conn_report.opmode = opmode;
+ arg.wifi_conn_report.sta_conn_state = sta_conn_state;
+ arg.wifi_conn_report.softap_conn_num = softap_conn_num;
+ arg.wifi_conn_report.extra_info = extra_info;
+
+ return (btc_transfer_context(&msg, &arg, sizeof(btc_blufi_args_t), btc_blufi_call_deep_copy,
+ btc_blufi_call_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
+}
+
+esp_err_t esp_blufi_send_wifi_list(uint16_t apCount, esp_blufi_ap_record_t *list)
+{
+ btc_msg_t msg;
+ btc_blufi_args_t arg;
+
+ ESP_BLE_HOST_STATUS_CHECK(ESP_BLE_HOST_STATUS_ENABLED);
+
+ msg.sig = BTC_SIG_API_CALL;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = BTC_BLUFI_ACT_SEND_WIFI_LIST;
+ arg.wifi_list.apCount = apCount;
+ arg.wifi_list.list = list;
+
+ return (btc_transfer_context(&msg, &arg, sizeof(btc_blufi_args_t), btc_blufi_call_deep_copy,
+ btc_blufi_call_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
+}
+
+esp_err_t esp_blufi_profile_init(void)
+{
+ btc_msg_t msg;
+
+ ESP_BLE_HOST_STATUS_CHECK(ESP_BLE_HOST_STATUS_ENABLED);
+
+ msg.sig = BTC_SIG_API_CALL;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = BTC_BLUFI_ACT_INIT;
+
+ return (btc_transfer_context(&msg, NULL, 0, NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
+}
+
+esp_err_t esp_blufi_profile_deinit(void)
+{
+ btc_msg_t msg;
+
+ ESP_BLE_HOST_STATUS_CHECK(ESP_BLE_HOST_STATUS_ENABLED);
+
+ msg.sig = BTC_SIG_API_CALL;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = BTC_BLUFI_ACT_DEINIT;
+
+ return (btc_transfer_context(&msg, NULL, 0, NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
+}
+
+uint16_t esp_blufi_get_version(void)
+{
+ return btc_blufi_get_version();
+}
+
+esp_err_t esp_blufi_send_error_info(esp_blufi_error_state_t state)
+{
+ btc_msg_t msg;
+ btc_blufi_args_t arg;
+
+ ESP_BLE_HOST_STATUS_CHECK(ESP_BLE_HOST_STATUS_ENABLED);
+
+ msg.sig = BTC_SIG_API_CALL;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = BTC_BLUFI_ACT_SEND_ERR_INFO;
+ arg.blufi_err_infor.state = state;
+
+ return (btc_transfer_context(&msg, &arg, sizeof(btc_blufi_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
+}
+
+esp_err_t esp_blufi_send_custom_data(uint8_t *data, uint32_t data_len)
+{
+ btc_msg_t msg;
+ btc_blufi_args_t arg;
+ if(data == NULL || data_len == 0) {
+ return ESP_ERR_INVALID_ARG;
+ }
+ ESP_BLE_HOST_STATUS_CHECK(ESP_BLE_HOST_STATUS_ENABLED);
+
+ msg.sig = BTC_SIG_API_CALL;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = BTC_BLUFI_ACT_SEND_CUSTOM_DATA;
+ arg.custom_data.data = data;
+ arg.custom_data.data_len = data_len;
+
+ return (btc_transfer_context(&msg, &arg, sizeof(btc_blufi_args_t), btc_blufi_call_deep_copy,
+ btc_blufi_call_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
+}
+#endif ///BLUFI_INCLUDED == TRUE
diff --git a/lib/bt/common/api/include/api/esp_blufi_api.h b/lib/bt/common/api/include/api/esp_blufi_api.h
new file mode 100644
index 00000000..8e1807f5
--- /dev/null
+++ b/lib/bt/common/api/include/api/esp_blufi_api.h
@@ -0,0 +1,436 @@
+/*
+ * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __ESP_BLUFI_API_H__
+#define __ESP_BLUFI_API_H__
+
+#include "esp_err.h"
+#include "esp_wifi_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ ESP_BLUFI_EVENT_INIT_FINISH = 0, /*<! When BLUFI init complete, this event happen */
+ ESP_BLUFI_EVENT_DEINIT_FINISH, /*<! When BLUFI deinit complete, this event happen */
+ ESP_BLUFI_EVENT_SET_WIFI_OPMODE, /*<! When Phone set ESP32 wifi operation mode(AP/STA/AP_STA), this event happen */
+ ESP_BLUFI_EVENT_BLE_CONNECT, /*<! When Phone connect to ESP32 with BLE, this event happen */
+ ESP_BLUFI_EVENT_BLE_DISCONNECT, /*<! When Phone disconnect with BLE, this event happen */
+ ESP_BLUFI_EVENT_REQ_CONNECT_TO_AP, /*<! When Phone request ESP32's STA connect to AP, this event happen */
+ ESP_BLUFI_EVENT_REQ_DISCONNECT_FROM_AP, /*<! When Phone request ESP32's STA disconnect from AP, this event happen */
+ ESP_BLUFI_EVENT_GET_WIFI_STATUS, /*<! When Phone get ESP32 wifi status, this event happen */
+ ESP_BLUFI_EVENT_DEAUTHENTICATE_STA, /*<! When Phone deauthenticate sta from SOFTAP, this event happen */
+ /* recv data */
+ ESP_BLUFI_EVENT_RECV_STA_BSSID, /*<! When Phone send STA BSSID to ESP32 to connect, this event happen */
+ ESP_BLUFI_EVENT_RECV_STA_SSID, /*<! When Phone send STA SSID to ESP32 to connect, this event happen */
+ ESP_BLUFI_EVENT_RECV_STA_PASSWD, /*<! When Phone send STA PASSWORD to ESP32 to connect, this event happen */
+ ESP_BLUFI_EVENT_RECV_SOFTAP_SSID, /*<! When Phone send SOFTAP SSID to ESP32 to start SOFTAP, this event happen */
+ ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD, /*<! When Phone send SOFTAP PASSWORD to ESP32 to start SOFTAP, this event happen */
+ ESP_BLUFI_EVENT_RECV_SOFTAP_MAX_CONN_NUM, /*<! When Phone send SOFTAP max connection number to ESP32 to start SOFTAP, this event happen */
+ ESP_BLUFI_EVENT_RECV_SOFTAP_AUTH_MODE, /*<! When Phone send SOFTAP authentication mode to ESP32 to start SOFTAP, this event happen */
+ ESP_BLUFI_EVENT_RECV_SOFTAP_CHANNEL, /*<! When Phone send SOFTAP channel to ESP32 to start SOFTAP, this event happen */
+ ESP_BLUFI_EVENT_RECV_USERNAME, /*<! When Phone send username to ESP32, this event happen */
+ ESP_BLUFI_EVENT_RECV_CA_CERT, /*<! When Phone send CA certificate to ESP32, this event happen */
+ ESP_BLUFI_EVENT_RECV_CLIENT_CERT, /*<! When Phone send Client certificate to ESP32, this event happen */
+ ESP_BLUFI_EVENT_RECV_SERVER_CERT, /*<! When Phone send Server certificate to ESP32, this event happen */
+ ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY, /*<! When Phone send Client Private key to ESP32, this event happen */
+ ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY, /*<! When Phone send Server Private key to ESP32, this event happen */
+ ESP_BLUFI_EVENT_RECV_SLAVE_DISCONNECT_BLE, /*<! When Phone send Disconnect key to ESP32, this event happen */
+ ESP_BLUFI_EVENT_GET_WIFI_LIST, /*<! When Phone send get wifi list command to ESP32, this event happen */
+ ESP_BLUFI_EVENT_REPORT_ERROR, /*<! When Blufi report error, this event happen */
+ ESP_BLUFI_EVENT_RECV_CUSTOM_DATA, /*<! When Phone send custom data to ESP32, this event happen */
+} esp_blufi_cb_event_t;
+
+/// BLUFI config status
+typedef enum {
+ ESP_BLUFI_STA_CONN_SUCCESS = 0x00,
+ ESP_BLUFI_STA_CONN_FAIL = 0x01,
+ ESP_BLUFI_STA_CONNECTING = 0x02,
+ ESP_BLUFI_STA_NO_IP = 0x03,
+} esp_blufi_sta_conn_state_t;
+
+/// BLUFI init status
+typedef enum {
+ ESP_BLUFI_INIT_OK = 0,
+ ESP_BLUFI_INIT_FAILED,
+} esp_blufi_init_state_t;
+
+/// BLUFI deinit status
+typedef enum {
+ ESP_BLUFI_DEINIT_OK = 0,
+ ESP_BLUFI_DEINIT_FAILED,
+} esp_blufi_deinit_state_t;
+
+typedef enum {
+ ESP_BLUFI_SEQUENCE_ERROR = 0,
+ ESP_BLUFI_CHECKSUM_ERROR,
+ ESP_BLUFI_DECRYPT_ERROR,
+ ESP_BLUFI_ENCRYPT_ERROR,
+ ESP_BLUFI_INIT_SECURITY_ERROR,
+ ESP_BLUFI_DH_MALLOC_ERROR,
+ ESP_BLUFI_DH_PARAM_ERROR,
+ ESP_BLUFI_READ_PARAM_ERROR,
+ ESP_BLUFI_MAKE_PUBLIC_ERROR,
+ ESP_BLUFI_DATA_FORMAT_ERROR,
+ ESP_BLUFI_CALC_MD5_ERROR,
+ ESP_BLUFI_WIFI_SCAN_FAIL,
+ ESP_BLUFI_MSG_STATE_ERROR,
+} esp_blufi_error_state_t;
+
+/**
+ * @brief BLUFI extra information structure
+ */
+typedef struct {
+ //station
+ uint8_t sta_bssid[6]; /*!< BSSID of station interface */
+ bool sta_bssid_set; /*!< is BSSID of station interface set */
+ uint8_t *sta_ssid; /*!< SSID of station interface */
+ int sta_ssid_len; /*!< length of SSID of station interface */
+ uint8_t *sta_passwd; /*!< password of station interface */
+ int sta_passwd_len; /*!< length of password of station interface */
+ uint8_t *softap_ssid; /*!< SSID of softap interface */
+ int softap_ssid_len; /*!< length of SSID of softap interface */
+ uint8_t *softap_passwd; /*!< password of station interface */
+ int softap_passwd_len; /*!< length of password of station interface */
+ uint8_t softap_authmode; /*!< authentication mode of softap interface */
+ bool softap_authmode_set; /*!< is authentication mode of softap interface set */
+ uint8_t softap_max_conn_num; /*!< max connection number of softap interface */
+ bool softap_max_conn_num_set; /*!< is max connection number of softap interface set */
+ uint8_t softap_channel; /*!< channel of softap interface */
+ bool softap_channel_set; /*!< is channel of softap interface set */
+ uint8_t sta_max_conn_retry; /*!< max retry of sta establish connection */
+ bool sta_max_conn_retry_set; /*!< is max retry of sta establish connection set */
+ uint8_t sta_conn_end_reason; /*!< reason of sta connection end */
+ bool sta_conn_end_reason_set; /*!< is reason of sta connection end set */
+ int8_t sta_conn_rssi; /*!< rssi of sta connection */
+ bool sta_conn_rssi_set; /*!< is rssi of sta connection set */
+} esp_blufi_extra_info_t;
+
+/** @brief Description of an WiFi AP */
+typedef struct {
+ uint8_t ssid[33]; /**< SSID of AP */
+ int8_t rssi; /**< signal strength of AP */
+} esp_blufi_ap_record_t;
+
+/// Bluetooth address length
+#define ESP_BLUFI_BD_ADDR_LEN 6
+/// Bluetooth device address
+typedef uint8_t esp_blufi_bd_addr_t[ESP_BLUFI_BD_ADDR_LEN];
+
+/**
+ * @brief BLUFI callback parameters union
+ */
+typedef union {
+ /**
+ * @brief ESP_BLUFI_EVENT_INIT_FINISH
+ */
+ struct blufi_init_finish_evt_param {
+ esp_blufi_init_state_t state; /*!< Initial status */
+ } init_finish; /*!< Blufi callback param of ESP_BLUFI_EVENT_INIT_FINISH */
+
+ /**
+ * @brief ESP_BLUFI_EVENT_DEINIT_FINISH
+ */
+ struct blufi_deinit_finish_evt_param {
+ esp_blufi_deinit_state_t state; /*!< De-initial status */
+ } deinit_finish; /*!< Blufi callback param of ESP_BLUFI_EVENT_DEINIT_FINISH */
+
+ /**
+ * @brief ESP_BLUFI_EVENT_SET_WIFI_MODE
+ */
+ struct blufi_set_wifi_mode_evt_param {
+ wifi_mode_t op_mode; /*!< Wifi operation mode */
+ } wifi_mode; /*!< Blufi callback param of ESP_BLUFI_EVENT_INIT_FINISH */
+
+ /**
+ * @brief ESP_BLUFI_EVENT_CONNECT
+ */
+ struct blufi_connect_evt_param {
+ esp_blufi_bd_addr_t remote_bda; /*!< Blufi Remote bluetooth device address */
+ uint8_t server_if; /*!< server interface */
+ uint16_t conn_id; /*!< Connection id */
+ } connect; /*!< Blufi callback param of ESP_BLUFI_EVENT_CONNECT */
+
+ /**
+ * @brief ESP_BLUFI_EVENT_DISCONNECT
+ */
+ struct blufi_disconnect_evt_param {
+ esp_blufi_bd_addr_t remote_bda; /*!< Blufi Remote bluetooth device address */
+ } disconnect; /*!< Blufi callback param of ESP_BLUFI_EVENT_DISCONNECT */
+
+ /* ESP_BLUFI_EVENT_REQ_WIFI_CONNECT */ /* No callback param */
+ /* ESP_BLUFI_EVENT_REQ_WIFI_DISCONNECT */ /* No callback param */
+
+ /**
+ * @brief ESP_BLUFI_EVENT_RECV_STA_BSSID
+ */
+ struct blufi_recv_sta_bssid_evt_param {
+ uint8_t bssid[6]; /*!< BSSID */
+ } sta_bssid; /*!< Blufi callback param of ESP_BLUFI_EVENT_RECV_STA_BSSID */
+
+ /**
+ * @brief ESP_BLUFI_EVENT_RECV_STA_SSID
+ */
+ struct blufi_recv_sta_ssid_evt_param {
+ uint8_t *ssid; /*!< SSID */
+ int ssid_len; /*!< SSID length */
+ } sta_ssid; /*!< Blufi callback param of ESP_BLUFI_EVENT_RECV_STA_SSID */
+
+ /**
+ * @brief
+ * ESP_BLUFI_EVENT_RECV_STA_PASSWD
+ */
+ struct blufi_recv_sta_passwd_evt_param {
+ uint8_t *passwd; /*!< Password */
+ int passwd_len; /*!< Password Length */
+ } sta_passwd; /*!< Blufi callback param of ESP_BLUFI_EVENT_RECV_STA_PASSWD */
+
+ /**
+ * @brief ESP_BLUFI_EVENT_RECV_SOFTAP_SSID
+ */
+ struct blufi_recv_softap_ssid_evt_param {
+ uint8_t *ssid; /*!< SSID */
+ int ssid_len; /*!< SSID length */
+ } softap_ssid; /*!< Blufi callback param of ESP_BLUFI_EVENT_RECV_SOFTAP_SSID */
+
+ /**
+ * @brief
+ * ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD
+ */
+ struct blufi_recv_softap_passwd_evt_param {
+ uint8_t *passwd; /*!< Password */
+ int passwd_len; /*!< Password Length */
+ } softap_passwd; /*!< Blufi callback param of ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD */
+
+ /**
+ * @brief ESP_BLUFI_EVENT_RECV_SOFTAP_MAX_CONN_NUM
+ */
+ struct blufi_recv_softap_max_conn_num_evt_param {
+ int max_conn_num; /*!< SSID */
+ } softap_max_conn_num; /*!< Blufi callback param of ESP_BLUFI_EVENT_RECV_SOFTAP_MAX_CONN_NUM */
+
+ /**
+ * @brief
+ * ESP_BLUFI_EVENT_RECV_SOFTAP_AUTH_MODE
+ */
+ struct blufi_recv_softap_auth_mode_evt_param {
+ wifi_auth_mode_t auth_mode; /*!< Authentication mode */
+ } softap_auth_mode; /*!< Blufi callback param of ESP_BLUFI_EVENT_RECV_SOFTAP_AUTH_MODE */
+
+ /**
+ * @brief
+ * ESP_BLUFI_EVENT_RECV_SOFTAP_CHANNEL
+ */
+ struct blufi_recv_softap_channel_evt_param {
+ uint8_t channel; /*!< Authentication mode */
+ } softap_channel; /*!< Blufi callback param of ESP_BLUFI_EVENT_RECV_SOFTAP_CHANNEL */
+
+ /**
+ * @brief ESP_BLUFI_EVENT_RECV_USERNAME
+ */
+ struct blufi_recv_username_evt_param {
+ uint8_t *name; /*!< Username point */
+ int name_len; /*!< Username length */
+ } username; /*!< Blufi callback param of ESP_BLUFI_EVENT_RECV_USERNAME*/
+
+ /**
+ * @brief ESP_BLUFI_EVENT_RECV_CA_CERT
+ */
+ struct blufi_recv_ca_evt_param {
+ uint8_t *cert; /*!< CA certificate point */
+ int cert_len; /*!< CA certificate length */
+ } ca; /*!< Blufi callback param of ESP_BLUFI_EVENT_RECV_CA_CERT */
+
+ /**
+ * ESP_BLUFI_EVENT_RECV_CLIENT_CERT
+ */
+ struct blufi_recv_client_cert_evt_param {
+ uint8_t *cert; /*!< Client certificate point */
+ int cert_len; /*!< Client certificate length */
+ } client_cert; /*!< Blufi callback param of ESP_BLUFI_EVENT_RECV_CLIENT_CERT */
+
+ /**
+ * ESP_BLUFI_EVENT_RECV_SERVER_CERT
+ */
+ struct blufi_recv_server_cert_evt_param {
+ uint8_t *cert; /*!< Client certificate point */
+ int cert_len; /*!< Client certificate length */
+ } server_cert; /*!< Blufi callback param of ESP_BLUFI_EVENT_RECV_SERVER_CERT */
+
+ /**
+ * ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY
+ */
+ struct blufi_recv_client_pkey_evt_param {
+ uint8_t *pkey; /*!< Client Private Key point, if Client certificate not contain Key */
+ int pkey_len; /*!< Client Private key length */
+ } client_pkey; /*!< Blufi callback param of ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY */
+ /**
+ * ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY
+ */
+ struct blufi_recv_server_pkey_evt_param {
+ uint8_t *pkey; /*!< Client Private Key point, if Client certificate not contain Key */
+ int pkey_len; /*!< Client Private key length */
+ } server_pkey; /*!< Blufi callback param of ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY */
+ /**
+ * @brief
+ * ESP_BLUFI_EVENT_REPORT_ERROR
+ */
+ struct blufi_get_error_evt_param {
+ esp_blufi_error_state_t state; /*!< Blufi error state */
+ } report_error; /*!< Blufi callback param of ESP_BLUFI_EVENT_REPORT_ERROR */
+ /**
+ * @brief
+ * ESP_BLUFI_EVENT_RECV_CUSTOM_DATA
+ */
+ struct blufi_recv_custom_data_evt_param {
+ uint8_t *data; /*!< Custom data */
+ uint32_t data_len; /*!< Custom data Length */
+ } custom_data; /*!< Blufi callback param of ESP_BLUFI_EVENT_RECV_CUSTOM_DATA */
+} esp_blufi_cb_param_t;
+
+/**
+ * @brief BLUFI event callback function type
+ * @param event : Event type
+ * @param param : Point to callback parameter, currently is union type
+ */
+typedef void (* esp_blufi_event_cb_t)(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param);
+
+/* security function declare */
+
+/**
+ * @brief BLUFI negotiate data handler
+ * @param data : data from phone
+ * @param len : length of data from phone
+ * @param output_data : data want to send to phone
+ * @param output_len : length of data want to send to phone
+ * @param need_free : output reporting if memory needs to be freed or not *
+ */
+typedef void (*esp_blufi_negotiate_data_handler_t)(uint8_t *data, int len, uint8_t **output_data, int *output_len, bool *need_free);
+
+/**
+ * @brief BLUFI encrypt the data after negotiate a share key
+ * @param iv8 : initial vector(8bit), normally, blufi core will input packet sequence number
+ * @param crypt_data : plain text and encrypted data, the encrypt function must support autochthonous encrypt
+ * @param crypt_len : length of plain text
+ * @return Nonnegative number is encrypted length, if error, return negative number;
+ */
+typedef int (* esp_blufi_encrypt_func_t)(uint8_t iv8, uint8_t *crypt_data, int crypt_len);
+
+/**
+ * @brief BLUFI decrypt the data after negotiate a share key
+ * @param iv8 : initial vector(8bit), normally, blufi core will input packet sequence number
+ * @param crypt_data : encrypted data and plain text, the encrypt function must support autochthonous decrypt
+ * @param crypt_len : length of encrypted text
+ * @return Nonnegative number is decrypted length, if error, return negative number;
+ */
+typedef int (* esp_blufi_decrypt_func_t)(uint8_t iv8, uint8_t *crypt_data, int crypt_len);
+
+/**
+ * @brief BLUFI checksum
+ * @param iv8 : initial vector(8bit), normally, blufi core will input packet sequence number
+ * @param data : data need to checksum
+ * @param len : length of data
+ */
+typedef uint16_t (*esp_blufi_checksum_func_t)(uint8_t iv8, uint8_t *data, int len);
+
+/**
+ * @brief BLUFI callback functions type
+ */
+typedef struct {
+ esp_blufi_event_cb_t event_cb; /*!< BLUFI event callback */
+ esp_blufi_negotiate_data_handler_t negotiate_data_handler; /*!< BLUFI negotiate data function for negotiate share key */
+ esp_blufi_encrypt_func_t encrypt_func; /*!< BLUFI encrypt data function with share key generated by negotiate_data_handler */
+ esp_blufi_decrypt_func_t decrypt_func; /*!< BLUFI decrypt data function with share key generated by negotiate_data_handler */
+ esp_blufi_checksum_func_t checksum_func; /*!< BLUFI check sum function (FCS) */
+} esp_blufi_callbacks_t;
+
+/**
+ *
+ * @brief This function is called to receive blufi callback event
+ *
+ * @param[in] callbacks: callback functions
+ *
+ * @return ESP_OK - success, other - failed
+ *
+ */
+esp_err_t esp_blufi_register_callbacks(esp_blufi_callbacks_t *callbacks);
+
+/**
+ *
+ * @brief This function is called to initialize blufi_profile
+ *
+ * @return ESP_OK - success, other - failed
+ *
+ */
+esp_err_t esp_blufi_profile_init(void);
+
+/**
+ *
+ * @brief This function is called to de-initialize blufi_profile
+ *
+ * @return ESP_OK - success, other - failed
+ *
+ */
+esp_err_t esp_blufi_profile_deinit(void);
+
+/**
+ *
+ * @brief This function is called to send wifi connection report
+ * @param opmode : wifi opmode
+ * @param sta_conn_state : station is already in connection or not
+ * @param softap_conn_num : softap connection number
+ * @param extra_info : extra information, such as sta_ssid, softap_ssid and etc.
+ *
+ * @return ESP_OK - success, other - failed
+ *
+ */
+esp_err_t esp_blufi_send_wifi_conn_report(wifi_mode_t opmode, esp_blufi_sta_conn_state_t sta_conn_state, uint8_t softap_conn_num, esp_blufi_extra_info_t *extra_info);
+
+/**
+ *
+ * @brief This function is called to send wifi list
+ * @param apCount : wifi list count
+ * @param list : wifi list
+ *
+ * @return ESP_OK - success, other - failed
+ *
+ */
+esp_err_t esp_blufi_send_wifi_list(uint16_t apCount, esp_blufi_ap_record_t *list);
+
+/**
+ *
+ * @brief Get BLUFI profile version
+ *
+ * @return Most 8bit significant is Great version, Least 8bit is Sub version
+ *
+ */
+uint16_t esp_blufi_get_version(void);
+
+/**
+ *
+ * @brief This function is called to send blufi error information
+ * @param state : error state
+ *
+ * @return ESP_OK - success, other - failed
+ *
+ */
+esp_err_t esp_blufi_send_error_info(esp_blufi_error_state_t state);
+/**
+ *
+ * @brief This function is called to custom data
+ * @param data : custom data value
+ * @param data_len : the length of custom data
+ *
+ * @return ESP_OK - success, other - failed
+ *
+ */
+esp_err_t esp_blufi_send_custom_data(uint8_t *data, uint32_t data_len);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ESP_BLUFI_API_ */
diff --git a/lib/bt/common/btc/core/btc_alarm.c b/lib/bt/common/btc/core/btc_alarm.c
new file mode 100644
index 00000000..6c6177f7
--- /dev/null
+++ b/lib/bt/common/btc/core/btc_alarm.c
@@ -0,0 +1,20 @@
+/*
+ * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "btc/btc_task.h"
+#include "btc/btc_alarm.h"
+#include "esp_log.h"
+
+void btc_alarm_handler(btc_msg_t *msg)
+{
+ btc_alarm_args_t *arg = (btc_alarm_args_t *)msg->arg;
+
+ BTC_TRACE_DEBUG("%s act %d\n", __FUNCTION__, msg->act);
+
+ if (arg->cb) {
+ arg->cb(arg->cb_data);
+ }
+}
diff --git a/lib/bt/common/btc/core/btc_manage.c b/lib/bt/common/btc/core/btc_manage.c
new file mode 100644
index 00000000..52aa837b
--- /dev/null
+++ b/lib/bt/common/btc/core/btc_manage.c
@@ -0,0 +1,44 @@
+/*
+ * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+
+#include "btc/btc_task.h"
+#include "osi/thread.h"
+
+#if BTC_DYNAMIC_MEMORY == FALSE
+void *btc_profile_cb_tab[BTC_PID_NUM] = {};
+#else
+void **btc_profile_cb_tab;
+#endif
+
+void esp_profile_cb_reset(void)
+{
+ int i;
+
+ for (i = 0; i < BTC_PID_NUM; i++) {
+ btc_profile_cb_tab[i] = NULL;
+ }
+}
+
+int btc_profile_cb_set(btc_pid_t profile_id, void *cb)
+{
+ if (profile_id < 0 || profile_id >= BTC_PID_NUM) {
+ return -1;
+ }
+
+ btc_profile_cb_tab[profile_id] = cb;
+
+ return 0;
+}
+
+void *btc_profile_cb_get(btc_pid_t profile_id)
+{
+ if (profile_id < 0 || profile_id >= BTC_PID_NUM) {
+ return NULL;
+ }
+
+ return btc_profile_cb_tab[profile_id];
+}
diff --git a/lib/bt/common/btc/core/btc_task.c b/lib/bt/common/btc/core/btc_task.c
new file mode 100644
index 00000000..179d501e
--- /dev/null
+++ b/lib/bt/common/btc/core/btc_task.c
@@ -0,0 +1,549 @@
+/*
+ * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "btc/btc_task.h"
+#include "osi/thread.h"
+#include "esp_log.h"
+#include "bt_common.h"
+#include "osi/allocator.h"
+#include "btc/btc_alarm.h"
+
+#include "btc/btc_manage.h"
+#include "btc_blufi_prf.h"
+#include "blufi_int.h"
+#ifdef CONFIG_BT_BLUEDROID_ENABLED
+#include "common/bt_target.h"
+#include "btc/btc_main.h"
+#include "btc/btc_dev.h"
+#include "btc_gatts.h"
+#include "btc_gattc.h"
+#include "btc_gatt_common.h"
+#include "btc_gap_ble.h"
+#include "btc/btc_dm.h"
+#include "bta/bta_gatt_api.h"
+#if CLASSIC_BT_INCLUDED
+#include "btc/btc_profile_queue.h"
+#if (BTC_GAP_BT_INCLUDED == TRUE)
+#include "btc_gap_bt.h"
+#endif /* BTC_GAP_BT_INCLUDED == TRUE */
+#if BTC_AV_INCLUDED
+#include "btc_av.h"
+#include "btc_avrc.h"
+#include "btc_av_co.h"
+#endif /* #if BTC_AV_INCLUDED */
+#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_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 */
+#if BTC_HF_CLIENT_INCLUDED
+#include "btc_hf_client.h"
+#endif /* #if BTC_HF_CLIENT_INCLUDED */
+#if BTC_HD_INCLUDED == TRUE
+#include "btc_hd.h"
+#endif /* BTC_HD_INCLUDED */
+#if BTC_HH_INCLUDED == TRUE
+#include "btc_hh.h"
+#endif /* BTC_HH_INCLUDED */
+#endif /* #if CLASSIC_BT_INCLUDED */
+#endif
+
+#if (BLE_INCLUDED == TRUE)
+#include "btc_gap_ble.h"
+#endif
+
+#if CONFIG_BLE_MESH
+#include "btc_ble_mesh_ble.h"
+#include "btc_ble_mesh_prov.h"
+#include "btc_ble_mesh_health_model.h"
+#include "btc_ble_mesh_config_model.h"
+#include "btc_ble_mesh_agg_model.h"
+#include "btc_ble_mesh_brc_model.h"
+#include "btc_ble_mesh_df_model.h"
+#include "btc_ble_mesh_lcd_model.h"
+#include "btc_ble_mesh_odp_model.h"
+#include "btc_ble_mesh_prb_model.h"
+#include "btc_ble_mesh_rpr_model.h"
+#include "btc_ble_mesh_sar_model.h"
+#include "btc_ble_mesh_srpl_model.h"
+#include "btc_ble_mesh_generic_model.h"
+#include "btc_ble_mesh_lighting_model.h"
+#include "btc_ble_mesh_sensor_model.h"
+#include "btc_ble_mesh_time_scene_model.h"
+#include "btc_ble_mesh_mbt_model.h"
+#endif /* #if CONFIG_BLE_MESH */
+
+#define BTC_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE)
+#define BTC_TASK_STACK_SIZE (BT_BTC_TASK_STACK_SIZE + BT_TASK_EXTRA_STACK_SIZE) //by menuconfig
+#define BTC_TASK_NAME "BTC_TASK"
+#define BTC_TASK_PRIO (BT_TASK_MAX_PRIORITIES - 6)
+#define BTC_TASK_WORKQUEUE_NUM (2)
+#define BTC_TASK_WORKQUEUE0_LEN (0)
+#define BTC_TASK_WORKQUEUE1_LEN (5)
+
+osi_thread_t *btc_thread;
+
+static const btc_func_t profile_tab[BTC_PID_NUM] = {
+#ifdef CONFIG_BT_BLUEDROID_ENABLED
+ [BTC_PID_MAIN_INIT] = {btc_main_call_handler, NULL },
+ [BTC_PID_DEV] = {btc_dev_call_handler, NULL },
+#if (GATTS_INCLUDED == TRUE)
+ [BTC_PID_GATTS] = {btc_gatts_call_handler, btc_gatts_cb_handler },
+#endif ///GATTS_INCLUDED == TRUE
+#if (GATTC_INCLUDED == TRUE)
+ [BTC_PID_GATTC] = {btc_gattc_call_handler, btc_gattc_cb_handler },
+#endif ///GATTC_INCLUDED == TRUE
+#if (GATTS_INCLUDED == TRUE || GATTC_INCLUDED == TRUE)
+ [BTC_PID_GATT_COMMON] = {btc_gatt_com_call_handler, NULL },
+#endif //GATTC_INCLUDED == TRUE || GATTS_INCLUDED == TRUE
+#if (BLE_INCLUDED == TRUE)
+ [BTC_PID_GAP_BLE] = {btc_gap_ble_call_handler, btc_gap_ble_cb_handler },
+#else
+ [BTC_PID_GAP_BLE] = {NULL, NULL},
+#endif ///BLE_INCLUDED == TRUE
+ [BTC_PID_BLE_HID] = {NULL, NULL},
+ [BTC_PID_SPPLIKE] = {NULL, NULL},
+ [BTC_PID_DM_SEC] = {NULL, btc_dm_sec_cb_handler },
+#endif
+#if (BLUFI_INCLUDED == TRUE)
+ [BTC_PID_BLUFI] = {btc_blufi_call_handler, btc_blufi_cb_handler },
+#endif ///BLUFI_INCLUDED == TRUE
+ [BTC_PID_ALARM] = {btc_alarm_handler, NULL },
+#ifdef CONFIG_BT_BLUEDROID_ENABLED
+#if CLASSIC_BT_INCLUDED
+#if (BTC_GAP_BT_INCLUDED == TRUE)
+ [BTC_PID_GAP_BT] = {btc_gap_bt_call_handler, btc_gap_bt_cb_handler },
+#endif /* (BTC_GAP_BT_INCLUDED == TRUE) */
+ [BTC_PID_PRF_QUE] = {btc_profile_queue_handler, NULL },
+#if BTC_AV_INCLUDED
+ [BTC_PID_A2DP] = {btc_a2dp_call_handler, btc_a2dp_cb_handler },
+ [BTC_PID_AVRC_CT] = {btc_avrc_ct_call_handler, NULL },
+ [BTC_PID_AVRC_TG] = {btc_avrc_tg_call_handler, NULL },
+#endif /* #if BTC_AV_INCLUDED */
+#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_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 */
+#if BTC_HF_CLIENT_INCLUDED
+ [BTC_PID_HF_CLIENT] = {btc_hf_client_call_handler, btc_hf_client_cb_handler},
+#endif /* #if BTC_HF_CLIENT_INCLUDED */
+#if BTC_HD_INCLUDED
+ [BTC_PID_HD] = {btc_hd_call_handler, btc_hd_cb_handler },
+#endif
+#if BTC_HH_INCLUDED
+ [BTC_PID_HH] = {btc_hh_call_handler, btc_hh_cb_handler },
+#endif
+#endif /* #if CLASSIC_BT_INCLUDED */
+#endif
+#if CONFIG_BLE_MESH
+ [BTC_PID_PROV] = {btc_ble_mesh_prov_call_handler, btc_ble_mesh_prov_cb_handler },
+ [BTC_PID_MODEL] = {btc_ble_mesh_model_call_handler, btc_ble_mesh_model_cb_handler },
+#if CONFIG_BLE_MESH_HEALTH_CLI
+ [BTC_PID_HEALTH_CLIENT] = {btc_ble_mesh_health_client_call_handler, btc_ble_mesh_health_client_cb_handler },
+#endif /* CONFIG_BLE_MESH_HEALTH_CLI */
+#if CONFIG_BLE_MESH_HEALTH_SRV
+ [BTC_PID_HEALTH_SERVER] = {btc_ble_mesh_health_server_call_handler, btc_ble_mesh_health_server_cb_handler },
+#endif /* CONFIG_BLE_MESH_HEALTH_SRV */
+#if CONFIG_BLE_MESH_CFG_CLI
+ [BTC_PID_CONFIG_CLIENT] = {btc_ble_mesh_config_client_call_handler, btc_ble_mesh_config_client_cb_handler },
+#endif /* CONFIG_BLE_MESH_CFG_CLI */
+ [BTC_PID_CONFIG_SERVER] = {NULL, btc_ble_mesh_config_server_cb_handler },
+#if CONFIG_BLE_MESH_AGG_CLI
+ [BTC_PID_AGG_CLIENT] = {btc_ble_mesh_agg_client_call_handler, btc_ble_mesh_agg_client_cb_handler },
+#endif /* CONFIG_BLE_MESH_AGG_CLI */
+#if CONFIG_BLE_MESH_AGG_SRV
+ [BTC_PID_AGG_SERVER] = {NULL, btc_ble_mesh_agg_server_cb_handler },
+#endif /* CONFIG_BLE_MESH_AGG_SRV */
+#if CONFIG_BLE_MESH_BRC_CLI
+ [BTC_PID_BRC_CLIENT] = {btc_ble_mesh_brc_client_call_handler, btc_ble_mesh_brc_client_cb_handler },
+#endif /* CONFIG_BLE_MESH_BRC_CLI */
+#if CONFIG_BLE_MESH_BRC_SRV
+ [BTC_PID_BRC_SERVER] = {NULL, btc_ble_mesh_brc_server_cb_handler },
+#endif /* CONFIG_BLE_MESH_BRC_SRV */
+#if CONFIG_BLE_MESH_DF_CLI
+ [BTC_PID_DF_CLIENT] = {btc_ble_mesh_df_client_call_handler, btc_ble_mesh_df_client_cb_handler },
+#endif /* CONFIG_BLE_MESH_DF_CLI */
+#if CONFIG_BLE_MESH_DF_SRV
+ [BTC_PID_DF_SERVER] = {NULL, btc_ble_mesh_df_server_cb_handler },
+#endif /* CONFIG_BLE_MESH_DF_SRV */
+#if CONFIG_BLE_MESH_LCD_CLI
+ [BTC_PID_LCD_CLIENT] = {btc_ble_mesh_lcd_client_call_handler, btc_ble_mesh_lcd_client_cb_handler },
+#endif /* CONFIG_BLE_MESH_LCD_CLI */
+#if CONFIG_BLE_MESH_LCD_SRV
+ [BTC_PID_LCD_SERVER] = {NULL, btc_ble_mesh_lcd_server_cb_handler },
+#endif /* CONFIG_BLE_MESH_LCD_SRV */
+#if CONFIG_BLE_MESH_ODP_CLI
+ [BTC_PID_ODP_CLIENT] = {btc_ble_mesh_odp_client_call_handler, btc_ble_mesh_odp_client_cb_handler },
+#endif /* CONFIG_BLE_MESH_ODP_CLI */
+#if CONFIG_BLE_MESH_ODP_SRV
+ [BTC_PID_ODP_SERVER] = {NULL, btc_ble_mesh_odp_server_cb_handler },
+#endif /* CONFIG_BLE_MESH_ODP_SRV */
+#if CONFIG_BLE_MESH_PRB_CLI
+ [BTC_PID_PRB_CLIENT] = {btc_ble_mesh_prb_client_call_handler, btc_ble_mesh_prb_client_cb_handler },
+#endif /* CONFIG_BLE_MESH_PRB_CLI */
+#if CONFIG_BLE_MESH_PRB_SRV
+ [BTC_PID_PRB_SERVER] = {NULL, btc_ble_mesh_prb_server_cb_handler },
+#endif /*CONFIG_BLE_MESH_PRB_SRV*/
+#if CONFIG_BLE_MESH_RPR_CLI
+ [BTC_PID_RPR_CLIENT] = {btc_ble_mesh_rpr_client_call_handler, btc_ble_mesh_rpr_client_cb_handler },
+#endif /* CONFIG_BLE_MESH_RPR_CLI */
+#if CONFIG_BLE_MESH_RPR_SRV
+ [BTC_PID_RPR_SERVER] = {NULL, btc_ble_mesh_rpr_server_cb_handler },
+#endif /* CONFIG_BLE_MESH_RPR_SRV */
+#if CONFIG_BLE_MESH_SAR_CLI
+ [BTC_PID_SAR_CLIENT] = {btc_ble_mesh_sar_client_call_handler, btc_ble_mesh_sar_client_cb_handler },
+#endif /* CONFIG_BLE_MESH_SAR_CLI */
+#if CONFIG_BLE_MESH_SAR_SRV
+ [BTC_PID_SAR_SERVER] = {NULL, btc_ble_mesh_sar_server_cb_handler },
+#endif /* CONFIG_BLE_MESH_SAR_SRV */
+#if CONFIG_BLE_MESH_SRPL_CLI
+ [BTC_PID_SRPL_CLIENT] = {btc_ble_mesh_srpl_client_call_handler, btc_ble_mesh_srpl_client_cb_handler },
+#endif /* CONFIG_BLE_MESH_SRPL_CLI */
+#if CONFIG_BLE_MESH_SRPL_SRV
+ [BTC_PID_SRPL_SERVER] = {NULL, btc_ble_mesh_srpl_server_cb_handler },
+#endif /* CONFIG_BLE_MESH_SRPL_SRV */
+#if CONFIG_BLE_MESH_GENERIC_CLIENT
+ [BTC_PID_GENERIC_CLIENT] = {btc_ble_mesh_generic_client_call_handler, btc_ble_mesh_generic_client_cb_handler },
+#endif /* CONFIG_BLE_MESH_GENERIC_CLIENT */
+#if CONFIG_BLE_MESH_LIGHTING_CLIENT
+ [BTC_PID_LIGHTING_CLIENT] = {btc_ble_mesh_lighting_client_call_handler, btc_ble_mesh_lighting_client_cb_handler },
+#endif /* CONFIG_BLE_MESH_LIGHTING_CLIENT */
+#if CONFIG_BLE_MESH_SENSOR_CLI
+ [BTC_PID_SENSOR_CLIENT] = {btc_ble_mesh_sensor_client_call_handler, btc_ble_mesh_sensor_client_cb_handler },
+#endif /* CONFIG_BLE_MESH_SENSOR_CLI */
+#if CONFIG_BLE_MESH_TIME_SCENE_CLIENT
+ [BTC_PID_TIME_SCENE_CLIENT] = {btc_ble_mesh_time_scene_client_call_handler, btc_ble_mesh_time_scene_client_cb_handler},
+#endif /* CONFIG_BLE_MESH_TIME_SCENE_CLIENT */
+#if CONFIG_BLE_MESH_GENERIC_SERVER
+ [BTC_PID_GENERIC_SERVER] = {NULL, btc_ble_mesh_generic_server_cb_handler },
+#endif /* CONFIG_BLE_MESH_GENERIC_SERVER */
+#if CONFIG_BLE_MESH_LIGHTING_SERVER
+ [BTC_PID_LIGHTING_SERVER] = {NULL, btc_ble_mesh_lighting_server_cb_handler },
+#endif /* CONFIG_BLE_MESH_LIGHTING_SERVER */
+#if CONFIG_BLE_MESH_SENSOR_SERVER
+ [BTC_PID_SENSOR_SERVER] = {NULL, btc_ble_mesh_sensor_server_cb_handler },
+#endif /* CONFIG_BLE_MESH_SENSOR_SERVER */
+#if CONFIG_BLE_MESH_TIME_SCENE_SERVER
+ [BTC_PID_TIME_SCENE_SERVER] = {NULL, btc_ble_mesh_time_scene_server_cb_handler},
+#endif /* CONFIG_BLE_MESH_TIME_SCENE_SERVER */
+#if CONFIG_BLE_MESH_MBT_CLI
+ [BTC_PID_MBT_CLIENT] = {btc_ble_mesh_mbt_client_call_handler, btc_ble_mesh_mbt_client_cb_handler },
+#endif /* CONFIG_BLE_MESH_MBT_CLI */
+#if CONFIG_BLE_MESH_MBT_SRV
+ [BTC_PID_MBT_SERVER] = {btc_ble_mesh_mbt_server_call_handler, btc_ble_mesh_mbt_server_cb_handler },
+#endif /* CONFIG_BLE_MESH_MBT_SRV */
+#if CONFIG_BLE_MESH_BLE_COEX_SUPPORT
+ [BTC_PID_BLE_MESH_BLE_COEX] = {btc_ble_mesh_ble_call_handler, btc_ble_mesh_ble_cb_handler },
+#endif /* CONFIG_BLE_MESH_BLE_COEX_SUPPORT */
+#endif /* #if CONFIG_BLE_MESH */
+};
+
+/*****************************************************************************
+**
+** Function btc_task
+**
+** Description Process profile Task Thread.
+******************************************************************************/
+static void btc_thread_handler(void *arg)
+{
+ btc_msg_t *msg = (btc_msg_t *)arg;
+
+ BTC_TRACE_DEBUG("%s msg %u %u %u %p\n", __func__, msg->sig, msg->pid, msg->act, msg->arg);
+ switch (msg->sig) {
+ case BTC_SIG_API_CALL:
+ profile_tab[msg->pid].btc_call(msg);
+ break;
+ case BTC_SIG_API_CB:
+ profile_tab[msg->pid].btc_cb(msg);
+ break;
+ default:
+ break;
+ }
+
+ osi_free(msg);
+}
+
+static bt_status_t btc_task_post(btc_msg_t *msg, uint32_t timeout)
+{
+ if (osi_thread_post(btc_thread, btc_thread_handler, msg, 0, timeout) == false) {
+ return BT_STATUS_BUSY;
+ }
+
+ return BT_STATUS_SUCCESS;
+}
+
+/**
+ * transfer an message to another module in the different task.
+ * @param msg message
+ * @param arg paramter
+ * @param arg_len length of paramter
+ * @param copy_func deep copy function
+ * @param free_func deep free function
+ * @return BT_STATUS_SUCCESS: success
+ * others: fail
+ */
+bt_status_t btc_transfer_context(btc_msg_t *msg, void *arg, int arg_len, btc_arg_deep_copy_t copy_func,
+ btc_arg_deep_free_t free_func)
+{
+ btc_msg_t* lmsg;
+ bt_status_t ret;
+ // arg XOR arg_len
+ if ((msg == NULL) || ((arg == NULL) == !(arg_len == 0))) {
+ BTC_TRACE_WARNING("%s Invalid parameters\n", __func__);
+ return BT_STATUS_PARM_INVALID;
+ }
+
+ BTC_TRACE_DEBUG("%s msg %u %u %u %p\n", __func__, msg->sig, msg->pid, msg->act, arg);
+
+ lmsg = (btc_msg_t *)osi_malloc(sizeof(btc_msg_t) + arg_len);
+ if (lmsg == NULL) {
+ BTC_TRACE_WARNING("%s No memory\n", __func__);
+ return BT_STATUS_NOMEM;
+ }
+
+ memcpy(lmsg, msg, sizeof(btc_msg_t));
+ if (arg) {
+ memset(lmsg->arg, 0x00, arg_len); //important, avoid arg which have no length
+ memcpy(lmsg->arg, arg, arg_len);
+ if (copy_func) {
+ copy_func(lmsg, lmsg->arg, arg);
+ }
+ }
+
+ ret = btc_task_post(lmsg, OSI_THREAD_MAX_TIMEOUT);
+ if (ret != BT_STATUS_SUCCESS) {
+ if (copy_func && free_func) {
+ free_func(lmsg);
+ }
+ osi_free(lmsg);
+ }
+
+ return ret;
+}
+
+/**
+ * transfer an message to another module in tha same task.
+ * @param msg message
+ * @return BT_STATUS_SUCCESS: success
+ * others: fail
+ */
+bt_status_t btc_inter_profile_call(btc_msg_t *msg)
+{
+ if (msg == NULL) {
+ return BT_STATUS_PARM_INVALID;
+ }
+
+ switch (msg->sig) {
+ case BTC_SIG_API_CALL:
+ profile_tab[msg->pid].btc_call(msg);
+ break;
+ case BTC_SIG_API_CB:
+ profile_tab[msg->pid].btc_cb(msg);
+ break;
+ default:
+ break;
+ }
+ return BT_STATUS_SUCCESS;
+}
+
+
+#if BTC_DYNAMIC_MEMORY
+
+static void btc_deinit_mem(void) {
+ if (btc_dm_cb_ptr) {
+ osi_free(btc_dm_cb_ptr);
+ btc_dm_cb_ptr = NULL;
+ }
+
+ if (btc_profile_cb_tab) {
+ osi_free(btc_profile_cb_tab);
+ btc_profile_cb_tab = NULL;
+ }
+
+#if (BLE_INCLUDED == TRUE)
+ if (gl_bta_adv_data_ptr) {
+ osi_free(gl_bta_adv_data_ptr);
+ gl_bta_adv_data_ptr = NULL;
+ }
+
+ if (gl_bta_scan_rsp_data_ptr) {
+ osi_free(gl_bta_scan_rsp_data_ptr);
+ gl_bta_scan_rsp_data_ptr = NULL;
+ }
+#endif ///BLE_INCLUDED == TRUE
+
+#if GATTS_INCLUDED == TRUE && GATT_DYNAMIC_MEMORY == TRUE
+ if (btc_creat_tab_env_ptr) {
+ osi_free(btc_creat_tab_env_ptr);
+ btc_creat_tab_env_ptr = NULL;
+ }
+#if (BLUFI_INCLUDED == TRUE)
+ if (blufi_env_ptr) {
+ osi_free(blufi_env_ptr);
+ blufi_env_ptr = NULL;
+ }
+#endif
+#endif
+
+#if BTC_HF_CLIENT_INCLUDED == TRUE && HFP_DYNAMIC_MEMORY == TRUE
+ if (hf_client_local_param_ptr) {
+ osi_free(hf_client_local_param_ptr);
+ hf_client_local_param_ptr = NULL;
+ }
+#endif
+
+#if BTC_AV_INCLUDED == TRUE && AVRC_DYNAMIC_MEMORY == TRUE
+ if (btc_rc_cb_ptr) {
+ osi_free(btc_rc_cb_ptr);
+ btc_rc_cb_ptr = NULL;
+ }
+ if (bta_av_co_cb_ptr) {
+ osi_free(bta_av_co_cb_ptr);
+ bta_av_co_cb_ptr = NULL;
+ }
+#endif
+}
+
+static bt_status_t btc_init_mem(void) {
+ if ((btc_dm_cb_ptr = (btc_dm_cb_t *)osi_malloc(sizeof(btc_dm_cb_t))) == NULL) {
+ goto error_exit;
+ }
+ memset((void *)btc_dm_cb_ptr, 0, sizeof(btc_dm_cb_t));
+
+ if ((btc_profile_cb_tab = (void **)osi_malloc(sizeof(void *) * BTC_PID_NUM)) == NULL) {
+ goto error_exit;
+ }
+ memset((void *)btc_profile_cb_tab, 0, sizeof(void *) * BTC_PID_NUM);
+
+#if (BLE_INCLUDED == TRUE)
+ if ((gl_bta_adv_data_ptr = (tBTA_BLE_ADV_DATA *)osi_malloc(sizeof(tBTA_BLE_ADV_DATA))) == NULL) {
+ goto error_exit;
+ }
+ memset((void *)gl_bta_adv_data_ptr, 0, sizeof(tBTA_BLE_ADV_DATA));
+
+ if ((gl_bta_scan_rsp_data_ptr = (tBTA_BLE_ADV_DATA *)osi_malloc(sizeof(tBTA_BLE_ADV_DATA))) == NULL) {
+ goto error_exit;
+ }
+ memset((void *)gl_bta_scan_rsp_data_ptr, 0, sizeof(tBTA_BLE_ADV_DATA));
+#endif ///BLE_INCLUDED == TRUE
+
+#if GATTS_INCLUDED == TRUE && GATT_DYNAMIC_MEMORY == TRUE
+ if ((btc_creat_tab_env_ptr = (esp_btc_creat_tab_t *)osi_malloc(sizeof(esp_btc_creat_tab_t))) == NULL) {
+ goto error_exit;
+ }
+ memset((void *)btc_creat_tab_env_ptr, 0, sizeof(esp_btc_creat_tab_t));
+#if (BLUFI_INCLUDED == TRUE)
+ if ((blufi_env_ptr = (tBLUFI_ENV *)osi_malloc(sizeof(tBLUFI_ENV))) == NULL) {
+ goto error_exit;
+ }
+ memset((void *)blufi_env_ptr, 0, sizeof(tBLUFI_ENV));
+#endif
+#endif
+
+#if BTC_HF_CLIENT_INCLUDED == TRUE && HFP_DYNAMIC_MEMORY == TRUE
+ if ((hf_client_local_param_ptr = (hf_client_local_param_t *)osi_malloc(sizeof(hf_client_local_param_t))) == NULL) {
+ goto error_exit;
+ }
+ memset((void *)hf_client_local_param_ptr, 0, sizeof(hf_client_local_param_t));
+#endif
+
+#if BTC_AV_INCLUDED == TRUE && AVRC_DYNAMIC_MEMORY == TRUE
+ if ((btc_rc_cb_ptr = (btc_rc_cb_t *)osi_malloc(sizeof(btc_rc_cb_t))) == NULL) {
+ goto error_exit;
+ }
+ memset((void *)btc_rc_cb_ptr, 0, sizeof(btc_rc_cb_t));
+ if ((bta_av_co_cb_ptr = (tBTA_AV_CO_CB *)osi_malloc(sizeof(tBTA_AV_CO_CB))) == NULL) {
+ goto error_exit;
+ }
+ memset((void *)bta_av_co_cb_ptr, 0, sizeof(tBTA_AV_CO_CB));
+#endif
+
+ return BT_STATUS_SUCCESS;
+
+error_exit:;
+ btc_deinit_mem();
+ return BT_STATUS_NOMEM;
+}
+#endif ///BTC_DYNAMIC_MEMORY
+
+bt_status_t btc_init(void)
+{
+ const size_t workqueue_len[] = {BTC_TASK_WORKQUEUE0_LEN, BTC_TASK_WORKQUEUE1_LEN};
+ btc_thread = osi_thread_create(BTC_TASK_NAME, BTC_TASK_STACK_SIZE, BTC_TASK_PRIO, BTC_TASK_PINNED_TO_CORE,
+ BTC_TASK_WORKQUEUE_NUM, workqueue_len);
+ if (btc_thread == NULL) {
+ return BT_STATUS_NOMEM;
+ }
+
+#if BTC_DYNAMIC_MEMORY
+ if (btc_init_mem() != BT_STATUS_SUCCESS){
+ return BT_STATUS_NOMEM;
+ }
+#endif
+
+#if (BLE_INCLUDED == TRUE)
+ btc_gap_callback_init();
+ btc_gap_ble_init();
+#endif ///BLE_INCLUDED == TRUE
+
+#if SCAN_QUEUE_CONGEST_CHECK
+ btc_adv_list_init();
+#endif
+ /* TODO: initial the profile_tab */
+ return BT_STATUS_SUCCESS;
+}
+
+void btc_deinit(void)
+{
+#if BTC_DYNAMIC_MEMORY
+ btc_deinit_mem();
+#endif
+
+ osi_thread_free(btc_thread);
+ btc_thread = NULL;
+#if (BLE_INCLUDED == TRUE)
+ btc_gap_ble_deinit();
+#endif ///BLE_INCLUDED == TRUE
+#if SCAN_QUEUE_CONGEST_CHECK
+ btc_adv_list_deinit();
+#endif
+}
+
+bool btc_check_queue_is_congest(void)
+{
+ if (osi_thread_queue_wait_size(btc_thread, 0) >= BT_QUEUE_CONGEST_SIZE) {
+ return true;
+ }
+
+ return false;
+}
+
+int get_btc_work_queue_size(void)
+{
+ return osi_thread_queue_wait_size(btc_thread, 0);
+}
+
+osi_thread_t *btc_get_current_thread(void)
+{
+ return btc_thread;
+}
diff --git a/lib/bt/common/btc/include/btc/btc_alarm.h b/lib/bt/common/btc/include/btc/btc_alarm.h
new file mode 100644
index 00000000..20c35f28
--- /dev/null
+++ b/lib/bt/common/btc/include/btc/btc_alarm.h
@@ -0,0 +1,22 @@
+/*
+ * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+
+#ifndef __BTC_ALARM_H__
+#define __BTC_ALARM_H__
+
+#include <stdint.h>
+#include "osi/alarm.h"
+
+/* btc_alarm_args_t */
+typedef struct {
+ osi_alarm_callback_t cb;
+ void *cb_data;
+} btc_alarm_args_t;
+
+void btc_alarm_handler(btc_msg_t *msg);
+
+#endif /* __BTC_ALARM_H__ */
diff --git a/lib/bt/common/btc/include/btc/btc_manage.h b/lib/bt/common/btc/include/btc/btc_manage.h
new file mode 100644
index 00000000..b55c1a3a
--- /dev/null
+++ b/lib/bt/common/btc/include/btc/btc_manage.h
@@ -0,0 +1,23 @@
+/*
+ * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __BTC_MANAGE_H__
+#define __BTC_MANAGE_H__
+
+#include "btc/btc_task.h"
+
+#if BTC_DYNAMIC_MEMORY == FALSE
+extern void *btc_profile_cb_tab[BTC_PID_NUM];
+#else
+extern void **btc_profile_cb_tab;
+#endif
+/* reset gatt callback table */
+void esp_profile_cb_reset(void);
+
+int btc_profile_cb_set(btc_pid_t profile_id, void *cb);
+void *btc_profile_cb_get(btc_pid_t profile_id);
+
+#endif /* __BTC_MANAGE_H__ */
diff --git a/lib/bt/common/btc/include/btc/btc_task.h b/lib/bt/common/btc/include/btc/btc_task.h
new file mode 100644
index 00000000..232186b5
--- /dev/null
+++ b/lib/bt/common/btc/include/btc/btc_task.h
@@ -0,0 +1,160 @@
+/*
+ * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __BTC_TASK_H__
+#define __BTC_TASK_H__
+
+#include <stdint.h>
+#include "bt_common.h"
+#include "osi/thread.h"
+
+#if CONFIG_BT_BLUEDROID_ENABLED
+#include "common/bt_target.h"
+#endif
+
+typedef struct btc_msg {
+ uint8_t sig; //event signal
+ uint8_t aid; //application id
+ uint8_t pid; //profile id
+ uint8_t act; //profile action, defined in seprerate header files
+ UINT8 arg[0]; //param for btc function or function param
+} btc_msg_t;
+
+typedef struct btc_adv_packet {
+ uint8_t addr[6];
+ uint8_t addr_type;
+} btc_adv_packet_t;
+
+typedef enum {
+ BTC_SIG_API_CALL = 0, // APP TO STACK
+ BTC_SIG_API_CB, // STACK TO APP
+ BTC_SIG_NUM,
+} btc_sig_t; //btc message type
+
+typedef enum {
+ BTC_PID_MAIN_INIT = 0,
+ BTC_PID_DEV,
+ BTC_PID_GATTS,
+#if (GATTC_INCLUDED == TRUE)
+ BTC_PID_GATTC,
+#endif ///GATTC_INCLUDED == TRUE
+ BTC_PID_GATT_COMMON,
+ BTC_PID_GAP_BLE,
+ BTC_PID_BLE_HID,
+ BTC_PID_SPPLIKE,
+#if (BLUFI_INCLUDED == TRUE)
+ BTC_PID_BLUFI,
+#endif ///BLUFI_INCLUDED == TRUE
+ BTC_PID_DM_SEC,
+ BTC_PID_ALARM,
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ BTC_PID_GAP_BT,
+ BTC_PID_PRF_QUE,
+ BTC_PID_A2DP,
+ BTC_PID_AVRC_CT,
+ BTC_PID_AVRC_TG,
+ BTC_PID_SPP,
+ BTC_PID_HD,
+ BTC_PID_HH,
+ BTC_PID_L2CAP,
+ BTC_PID_SDP,
+#if (BTC_HF_INCLUDED == TRUE)
+ BTC_PID_HF,
+#endif /* BTC_HF_INCLUDED */
+#if (BTC_HF_CLIENT_INCLUDED == TRUE)
+ BTC_PID_HF_CLIENT,
+#endif /* BTC_HF_CLIENT_INCLUDED */
+#endif /* CLASSIC_BT_INCLUDED */
+#if CONFIG_BLE_MESH
+ BTC_PID_PROV,
+ BTC_PID_MODEL,
+ BTC_PID_HEALTH_CLIENT,
+ BTC_PID_HEALTH_SERVER,
+ BTC_PID_CONFIG_CLIENT,
+ BTC_PID_CONFIG_SERVER,
+ BTC_PID_AGG_CLIENT,
+ BTC_PID_AGG_SERVER,
+ BTC_PID_BRC_CLIENT,
+ BTC_PID_BRC_SERVER,
+ BTC_PID_DF_CLIENT,
+ BTC_PID_DF_SERVER,
+ BTC_PID_LCD_CLIENT,
+ BTC_PID_LCD_SERVER,
+ BTC_PID_ODP_CLIENT,
+ BTC_PID_ODP_SERVER,
+ BTC_PID_PRB_CLIENT,
+ BTC_PID_PRB_SERVER,
+ BTC_PID_RPR_CLIENT,
+ BTC_PID_RPR_SERVER,
+ BTC_PID_SAR_CLIENT,
+ BTC_PID_SAR_SERVER,
+ BTC_PID_SRPL_CLIENT,
+ BTC_PID_SRPL_SERVER,
+ BTC_PID_GENERIC_CLIENT,
+ BTC_PID_LIGHTING_CLIENT,
+ BTC_PID_SENSOR_CLIENT,
+ BTC_PID_TIME_SCENE_CLIENT,
+ BTC_PID_GENERIC_SERVER,
+ BTC_PID_LIGHTING_SERVER,
+ BTC_PID_SENSOR_SERVER,
+ BTC_PID_TIME_SCENE_SERVER,
+ BTC_PID_MBT_CLIENT,
+ BTC_PID_MBT_SERVER,
+ BTC_PID_BLE_MESH_BLE_COEX,
+#endif /* CONFIG_BLE_MESH */
+ BTC_PID_NUM,
+} btc_pid_t; //btc profile id
+
+typedef struct {
+ void (* btc_call)(btc_msg_t *msg);
+ void (* btc_cb)(btc_msg_t *msg);
+} btc_func_t;
+
+typedef void (* btc_arg_deep_copy_t)(btc_msg_t *msg, void *dst, void *src);
+typedef void (* btc_arg_deep_free_t)(btc_msg_t *msg);
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * transfer an message to another module in the different task.
+ * @param msg message
+ * @param arg paramter
+ * @param arg_len length of paramter
+ * @param copy_func deep copy function
+ * @param free_func deep free function
+ * @return BT_STATUS_SUCCESS: success
+ * others: fail
+ */
+bt_status_t btc_transfer_context(btc_msg_t *msg, void *arg, int arg_len, btc_arg_deep_copy_t copy_func,
+ btc_arg_deep_free_t free_func);
+
+/**
+ * transfer an message to another module in tha same task.
+ * @param msg message
+ * @return BT_STATUS_SUCCESS: success
+ * others: fail
+ */
+bt_status_t btc_inter_profile_call(btc_msg_t *msg);
+
+bt_status_t btc_init(void);
+void btc_deinit(void);
+bool btc_check_queue_is_congest(void);
+int get_btc_work_queue_size(void);
+
+/**
+ * get the BTC thread handle
+ * @return NULL: fail
+ * others: pointer of osi_thread structure of BTC
+ */
+osi_thread_t *btc_get_current_thread(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BTC_TASK_H__ */
diff --git a/lib/bt/common/btc/profile/esp/blufi/bluedroid_host/esp_blufi.c b/lib/bt/common/btc/profile/esp/blufi/bluedroid_host/esp_blufi.c
new file mode 100644
index 00000000..a0da30f4
--- /dev/null
+++ b/lib/bt/common/btc/profile/esp/blufi/bluedroid_host/esp_blufi.c
@@ -0,0 +1,422 @@
+/*
+ * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include "osi/allocator.h"
+#include "btc/btc_task.h"
+#include "btc/btc_manage.h"
+#include "blufi_int.h"
+#include "btc_blufi_prf.h"
+#include "esp_log.h"
+#include "esp_blufi_api.h"
+
+#include "common/bt_target.h"
+#include "common/bt_trace.h"
+#include "stack/bt_types.h"
+#include "stack/gatt_api.h"
+#include "bta/bta_api.h"
+#include "bta/bta_gatt_api.h"
+#include "bta_gatts_int.h"
+#include "btc_gatt_util.h"
+#include "btc_gatts.h"
+
+#include "esp_bt_defs.h"
+#include "esp_gap_ble_api.h"
+#include "esp_gatt_common_api.h"
+#include "esp_bt_main.h"
+#include "esp_bt_device.h"
+#include "esp_err.h"
+#include "esp_blufi.h"
+
+#if (BLUFI_INCLUDED == TRUE)
+
+static uint8_t server_if;
+static uint16_t conn_id;
+static uint8_t blufi_service_uuid128[32] = {
+ /* LSB <--------------------------------------------------------------------------------> MSB */
+ //first uuid, 16bit, [12],[13] is the value
+ 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+};
+
+static esp_ble_adv_data_t blufi_adv_data = {
+ .set_scan_rsp = false,
+ .include_name = true,
+ .include_txpower = true,
+ .min_interval = 0x0006, //slave connection min interval, Time = min_interval * 1.25 msec
+ .max_interval = 0x0010, //slave connection max interval, Time = max_interval * 1.25 msec
+ .appearance = 0x00,
+ .manufacturer_len = 0,
+ .p_manufacturer_data = NULL,
+ .service_data_len = 0,
+ .p_service_data = NULL,
+ .service_uuid_len = 16,
+ .p_service_uuid = blufi_service_uuid128,
+ .flag = 0x6,
+};
+
+static esp_ble_adv_params_t blufi_adv_params = {
+ .adv_int_min = 0x100,
+ .adv_int_max = 0x100,
+ .adv_type = ADV_TYPE_IND,
+ .own_addr_type = BLE_ADDR_TYPE_PUBLIC,
+ //.peer_addr =
+ //.peer_addr_type =
+ .channel_map = ADV_CHNL_ALL,
+ .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
+};
+
+void esp_blufi_gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
+{
+ switch (event) {
+ case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
+ esp_ble_gap_start_advertising(&blufi_adv_params);
+ break;
+ default:
+ break;
+ }
+}
+
+// static functions declare
+static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data);
+
+void blufi_create_service(void)
+{
+ if (!blufi_env.enabled) {
+ BTC_TRACE_ERROR("blufi service added error.");
+ return;
+ }
+
+ blufi_env.srvc_inst = 0x00;
+ BTA_GATTS_CreateService(blufi_env.gatt_if, &blufi_srvc_uuid, blufi_env.srvc_inst, BLUFI_HDL_NUM, true);
+}
+
+uint8_t esp_blufi_init(void)
+{
+
+ /* register the BLUFI profile to the BTA_GATTS module*/
+ BTA_GATTS_AppRegister(&blufi_app_uuid, blufi_profile_cb);
+ return GATT_SUCCESS;
+}
+
+static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data)
+{
+ tBTA_GATTS_RSP rsp;
+ BLUFI_TRACE_DEBUG("blufi profile cb event = %x\n", event);
+
+ switch (event) {
+ case BTA_GATTS_REG_EVT:
+ BLUFI_TRACE_DEBUG("REG: status %d, app_uuid %04x, gatt_if %d\n", p_data->reg_oper.status, p_data->reg_oper.uuid.uu.uuid16, p_data->reg_oper.server_if);
+
+ if (p_data->reg_oper.status != BTA_GATT_OK) {
+ BLUFI_TRACE_ERROR("BLUFI profile register failed\n");
+ return;
+ }
+
+ blufi_env.gatt_if = p_data->reg_oper.server_if;
+ blufi_env.enabled = true;
+
+ //create the blufi service to the service data base.
+ if (p_data->reg_oper.uuid.uu.uuid16 == BLUFI_APP_UUID) {
+ BLUFI_TRACE_DEBUG("%s %d\n", __func__, __LINE__);
+ blufi_create_service();
+ }
+ break;
+ case BTA_GATTS_DEREG_EVT: {
+ esp_blufi_cb_param_t param;
+ btc_msg_t msg;
+
+ BLUFI_TRACE_DEBUG("DEREG: status %d, gatt_if %d\n", p_data->reg_oper.status, p_data->reg_oper.server_if);
+
+ if (p_data->reg_oper.status != BTA_GATT_OK) {
+ BLUFI_TRACE_ERROR("BLUFI profile unregister failed\n");
+ return;
+ }
+
+ blufi_env.enabled = false;
+
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_DEINIT_FINISH;
+ param.deinit_finish.state = ESP_BLUFI_DEINIT_OK;
+
+ btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
+
+ break;
+ }
+ case BTA_GATTS_READ_EVT:
+ memset(&rsp, 0, sizeof(tBTA_GATTS_API_RSP));
+ rsp.attr_value.handle = p_data->req_data.p_data->read_req.handle;
+ rsp.attr_value.len = 1;
+ rsp.attr_value.value[0] = 0x00;
+ BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id,
+ p_data->req_data.status, &rsp);
+ break;
+ case BTA_GATTS_WRITE_EVT: {
+ if (p_data->req_data.p_data->write_req.is_prep) {
+ tBTA_GATT_STATUS status = GATT_SUCCESS;
+
+ if (blufi_env.prepare_buf == NULL) {
+ blufi_env.prepare_buf = osi_malloc(BLUFI_PREPAIR_BUF_MAX_SIZE);
+ blufi_env.prepare_len = 0;
+ if (blufi_env.prepare_buf == NULL) {
+ BLUFI_TRACE_ERROR("Blufi prep no mem\n");
+ status = GATT_NO_RESOURCES;
+ }
+ } else {
+ if (p_data->req_data.p_data->write_req.offset > BLUFI_PREPAIR_BUF_MAX_SIZE) {
+ status = GATT_INVALID_OFFSET;
+ } else if ((p_data->req_data.p_data->write_req.offset + p_data->req_data.p_data->write_req.len) > BLUFI_PREPAIR_BUF_MAX_SIZE) {
+ status = GATT_INVALID_ATTR_LEN;
+ }
+ }
+
+ memset(&rsp, 0, sizeof(tGATTS_RSP));
+ rsp.attr_value.handle = p_data->req_data.p_data->write_req.handle;
+ rsp.attr_value.len = p_data->req_data.p_data->write_req.len;
+ rsp.attr_value.offset = p_data->req_data.p_data->write_req.offset;
+ memcpy(rsp.attr_value.value, p_data->req_data.p_data->write_req.value, p_data->req_data.p_data->write_req.len);
+
+ BLUFI_TRACE_DEBUG("prep write, len=%d, offset=%d\n", p_data->req_data.p_data->write_req.len, p_data->req_data.p_data->write_req.offset);
+
+ BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id,
+ status, &rsp);
+
+ if (status != GATT_SUCCESS) {
+ if (blufi_env.prepare_buf) {
+ osi_free(blufi_env.prepare_buf);
+ blufi_env.prepare_buf = NULL;
+ blufi_env.prepare_len = 0;
+ }
+ BLUFI_TRACE_ERROR("write data error , error code 0x%x\n", status);
+ return;
+ }
+ memcpy(blufi_env.prepare_buf + p_data->req_data.p_data->write_req.offset,
+ p_data->req_data.p_data->write_req.value,
+ p_data->req_data.p_data->write_req.len);
+ blufi_env.prepare_len += p_data->req_data.p_data->write_req.len;
+
+ return;
+ } else {
+ BLUFI_TRACE_DEBUG("norm write, len=%d, offset=%d\n", p_data->req_data.p_data->write_req.len, p_data->req_data.p_data->write_req.offset);
+ BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id,
+ p_data->req_data.status, NULL);
+ }
+
+ if (p_data->req_data.p_data->write_req.handle == blufi_env.handle_char_p2e) {
+ btc_blufi_recv_handler(&p_data->req_data.p_data->write_req.value[0],
+ p_data->req_data.p_data->write_req.len);
+ }
+ break;
+ }
+ case BTA_GATTS_EXEC_WRITE_EVT:
+ BLUFI_TRACE_DEBUG("exec write exec %d\n", p_data->req_data.p_data->exec_write);
+
+ BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id,
+ GATT_SUCCESS, NULL);
+
+ if (blufi_env.prepare_buf && p_data->req_data.p_data->exec_write == GATT_PREP_WRITE_EXEC) {
+ btc_blufi_recv_handler(blufi_env.prepare_buf, blufi_env.prepare_len);
+ }
+
+ if (blufi_env.prepare_buf) {
+ osi_free(blufi_env.prepare_buf);
+ blufi_env.prepare_buf = NULL;
+ blufi_env.prepare_len = 0;
+ }
+
+ break;
+ case BTA_GATTS_MTU_EVT:
+ BLUFI_TRACE_DEBUG("MTU size %d\n", p_data->req_data.p_data->mtu);
+ blufi_env.frag_size = (p_data->req_data.p_data->mtu < BLUFI_MAX_DATA_LEN ? p_data->req_data.p_data->mtu : BLUFI_MAX_DATA_LEN) - BLUFI_MTU_RESERVED_SIZE;
+ break;
+ case BTA_GATTS_CONF_EVT:
+ BLUFI_TRACE_DEBUG("CONFIRM EVT\n");
+ /* Nothing */
+ break;
+ case BTA_GATTS_CREATE_EVT:
+ blufi_env.handle_srvc = p_data->create.service_id;
+
+ //add the frist blufi characteristic --> write characteristic
+ BTA_GATTS_AddCharacteristic(blufi_env.handle_srvc, &blufi_char_uuid_p2e,
+ (GATT_PERM_WRITE),
+ (GATT_CHAR_PROP_BIT_WRITE),
+ NULL, NULL);
+ break;
+ case BTA_GATTS_ADD_CHAR_EVT:
+ switch (p_data->add_result.char_uuid.uu.uuid16) {
+ case BLUFI_CHAR_P2E_UUID: /* Phone to ESP32 */
+ blufi_env.handle_char_p2e = p_data->add_result.attr_id;
+
+ BTA_GATTS_AddCharacteristic(blufi_env.handle_srvc, &blufi_char_uuid_e2p,
+ (GATT_PERM_READ),
+ (GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY),
+ NULL, NULL);
+ break;
+ case BLUFI_CHAR_E2P_UUID: /* ESP32 to Phone */
+ blufi_env.handle_char_e2p = p_data->add_result.attr_id;
+
+ BTA_GATTS_AddCharDescriptor (blufi_env.handle_srvc,
+ (GATT_PERM_READ | GATT_PERM_WRITE),
+ &blufi_descr_uuid_e2p,
+ NULL, NULL);
+ break;
+ default:
+ break;
+ }
+ break;
+ case BTA_GATTS_ADD_CHAR_DESCR_EVT: {
+ /* call init finish */
+ esp_blufi_cb_param_t param;
+ btc_msg_t msg;
+
+ blufi_env.handle_descr_e2p = p_data->add_result.attr_id;
+ //start the blufi service after created
+ BTA_GATTS_StartService(blufi_env.handle_srvc, BTA_GATT_TRANSPORT_LE);
+
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_INIT_FINISH;
+ param.init_finish.state = ESP_BLUFI_INIT_OK;
+
+ btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
+ break;
+ }
+ case BTA_GATTS_CONNECT_EVT: {
+ btc_msg_t msg;
+ esp_blufi_cb_param_t param;
+
+ //set the connection flag to true
+ BLUFI_TRACE_API("\ndevice is connected "BT_BD_ADDR_STR", server_if=%d,reason=0x%x,connect_id=%d\n",
+ BT_BD_ADDR_HEX(p_data->conn.remote_bda), p_data->conn.server_if,
+ p_data->conn.reason, p_data->conn.conn_id);
+
+ memcpy(blufi_env.remote_bda, p_data->conn.remote_bda, ESP_BLUFI_BD_ADDR_LEN);
+ blufi_env.conn_id = p_data->conn.conn_id;
+ blufi_env.is_connected = true;
+ blufi_env.recv_seq = blufi_env.send_seq = 0;
+
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_BLE_CONNECT;
+ memcpy(param.connect.remote_bda, p_data->conn.remote_bda, ESP_BLUFI_BD_ADDR_LEN);
+ param.connect.conn_id = BTC_GATT_GET_CONN_ID(p_data->conn.conn_id);
+ conn_id = param.connect.conn_id;
+ server_if = p_data->conn.server_if;
+ btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
+ break;
+ }
+ case BTA_GATTS_DISCONNECT_EVT: {
+ btc_msg_t msg;
+ esp_blufi_cb_param_t param;
+
+ blufi_env.is_connected = false;
+ //set the connection flag to true
+ BLUFI_TRACE_API("\ndevice is disconnected "BT_BD_ADDR_STR", server_if=%d,reason=0x%x,connect_id=%d\n",
+ BT_BD_ADDR_HEX(p_data->conn.remote_bda), p_data->conn.server_if,
+ p_data->conn.reason, p_data->conn.conn_id);
+
+ memcpy(blufi_env.remote_bda, p_data->conn.remote_bda, ESP_BLUFI_BD_ADDR_LEN);
+ blufi_env.conn_id = p_data->conn.conn_id;
+ blufi_env.recv_seq = blufi_env.send_seq = 0;
+ blufi_env.sec_mode = 0x0;
+ blufi_env.offset = 0;
+
+ if (blufi_env.aggr_buf != NULL) {
+ osi_free(blufi_env.aggr_buf);
+ blufi_env.aggr_buf = NULL;
+ }
+
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_BLE_DISCONNECT;
+ memcpy(param.disconnect.remote_bda, p_data->conn.remote_bda, ESP_BLUFI_BD_ADDR_LEN);
+ btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
+ break;
+ }
+ case BTA_GATTS_OPEN_EVT:
+ break;
+ case BTA_GATTS_CLOSE_EVT:
+ break;
+ case BTA_GATTS_CONGEST_EVT:
+ break;
+ default:
+ break;
+ }
+}
+
+void esp_blufi_send_notify(void *arg)
+{
+ struct pkt_info *pkts = (struct pkt_info *) arg;
+ uint16_t conn_id = blufi_env.conn_id;
+ uint16_t attr_id = blufi_env.handle_char_e2p;
+ bool rsp = false;
+ BTA_GATTS_HandleValueIndication(conn_id, attr_id, pkts->pkt_len,
+ pkts->pkt, rsp);
+
+}
+
+void esp_blufi_deinit(void)
+{
+ BTA_GATTS_StopService(blufi_env.handle_srvc);
+ BTA_GATTS_DeleteService(blufi_env.handle_srvc);
+ /* register the BLUFI profile to the BTA_GATTS module*/
+ BTA_GATTS_AppDeregister(blufi_env.gatt_if);
+}
+
+void esp_blufi_adv_start(void)
+{
+ esp_ble_gap_set_device_name(BLUFI_DEVICE_NAME);
+ esp_ble_gap_config_adv_data(&blufi_adv_data);
+}
+
+void esp_blufi_adv_stop(void)
+{
+ esp_ble_gap_stop_advertising();
+}
+
+void esp_blufi_send_encap(void *arg)
+{
+ struct blufi_hdr *hdr = (struct blufi_hdr *)arg;
+retry:
+ if (blufi_env.is_connected == false) {
+ BTC_TRACE_WARNING("%s ble connection is broken\n", __func__);
+ return;
+ }
+ if (esp_ble_get_cur_sendable_packets_num(BTC_GATT_GET_CONN_ID(blufi_env.conn_id)) > 0) {
+ btc_blufi_send_notify((uint8_t *)hdr,
+ ((hdr->fc & BLUFI_FC_CHECK) ?
+ hdr->data_len + sizeof(struct blufi_hdr) + 2 :
+ hdr->data_len + sizeof(struct blufi_hdr)));
+ } else {
+ BTC_TRACE_WARNING("%s wait to send blufi custom data\n", __func__);
+ vTaskDelay(pdMS_TO_TICKS(10));
+ goto retry;
+ }
+}
+
+esp_err_t esp_blufi_close(esp_gatt_if_t gatts_if, uint16_t conn_id)
+{
+ ESP_BLE_HOST_STATUS_CHECK(ESP_BLE_HOST_STATUS_ENABLED);
+ btc_msg_t msg;
+ btc_ble_gatts_args_t arg;
+ msg.sig = BTC_SIG_API_CALL;
+ msg.pid = BTC_PID_GATTS;
+ msg.act = BTC_GATTS_ACT_CLOSE;
+ arg.close.conn_id = BTC_GATT_CREATE_CONN_ID(gatts_if, conn_id);
+ return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL, NULL)
+ == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
+}
+
+void esp_blufi_disconnect()
+{
+ int rc;
+ rc = esp_blufi_close(server_if, conn_id);
+ assert (rc == 0);
+}
+
+#endif
diff --git a/lib/bt/common/btc/profile/esp/blufi/blufi_prf.c b/lib/bt/common/btc/profile/esp/blufi/blufi_prf.c
new file mode 100644
index 00000000..2c7be212
--- /dev/null
+++ b/lib/bt/common/btc/profile/esp/blufi/blufi_prf.c
@@ -0,0 +1,901 @@
+/*
+ * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <stdint.h>
+
+#include <string.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+
+#include "osi/allocator.h"
+
+#include "btc_blufi_prf.h"
+#include "btc/btc_task.h"
+#include "btc/btc_manage.h"
+
+#include "blufi_int.h"
+
+#include "esp_log.h"
+#include "esp_blufi_api.h"
+#include "esp_blufi.h"
+
+#if (BLUFI_INCLUDED == TRUE)
+
+#if GATT_DYNAMIC_MEMORY == FALSE
+tBLUFI_ENV blufi_env;
+#else
+tBLUFI_ENV *blufi_env_ptr;
+#endif
+
+// static functions declare
+static void btc_blufi_send_ack(uint8_t seq);
+
+inline void btc_blufi_cb_to_app(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param)
+{
+ esp_blufi_event_cb_t btc_blufi_cb = (esp_blufi_event_cb_t)btc_profile_cb_get(BTC_PID_BLUFI);
+ if (btc_blufi_cb) {
+ btc_blufi_cb(event, param);
+ }
+}
+
+static uint8_t btc_blufi_profile_init(void)
+{
+ esp_blufi_callbacks_t *store_p = blufi_env.cbs;
+
+ uint8_t rc;
+ if (blufi_env.enabled) {
+ BLUFI_TRACE_ERROR("BLUFI already initialized");
+ return ESP_BLUFI_ERROR;
+ }
+
+ memset(&blufi_env, 0x0, sizeof(blufi_env));
+ blufi_env.cbs = store_p; /* if set callback prior, restore the point */
+ blufi_env.frag_size = BLUFI_FRAG_DATA_DEFAULT_LEN;
+ rc = esp_blufi_init();
+ if(rc != 0 ){
+ return rc;
+ }
+
+ return ESP_BLUFI_SUCCESS;
+}
+
+static uint8_t btc_blufi_profile_deinit(void)
+{
+ if (!blufi_env.enabled) {
+ BTC_TRACE_ERROR("BLUFI already de-initialized");
+ return ESP_BLUFI_ERROR;
+ }
+
+ esp_blufi_deinit();
+ return ESP_BLUFI_SUCCESS;
+}
+
+void btc_blufi_send_notify(uint8_t *pkt, int pkt_len)
+{
+ struct pkt_info pkts;
+ pkts.pkt = pkt;
+ pkts.pkt_len = pkt_len;
+ esp_blufi_send_notify(&pkts);
+}
+
+void btc_blufi_report_error(esp_blufi_error_state_t state)
+{
+ btc_msg_t msg;
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_REPORT_ERROR;
+ esp_blufi_cb_param_t param;
+ param.report_error.state = state;
+ btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
+}
+
+void btc_blufi_recv_handler(uint8_t *data, int len)
+{
+ struct blufi_hdr *hdr = (struct blufi_hdr *)data;
+ uint16_t checksum, checksum_pkt;
+ int ret;
+
+ if (hdr->seq != blufi_env.recv_seq) {
+ BTC_TRACE_ERROR("%s seq %d is not expect %d\n", __func__, hdr->seq, blufi_env.recv_seq + 1);
+ btc_blufi_report_error(ESP_BLUFI_SEQUENCE_ERROR);
+ return;
+ }
+
+ blufi_env.recv_seq++;
+
+ // first step, decrypt
+ if (BLUFI_FC_IS_ENC(hdr->fc)
+ && (blufi_env.cbs && blufi_env.cbs->decrypt_func)) {
+ ret = blufi_env.cbs->decrypt_func(hdr->seq, hdr->data, hdr->data_len);
+ if (ret != hdr->data_len) { /* enc must be success and enc len must equal to plain len */
+ BTC_TRACE_ERROR("%s decrypt error %d\n", __func__, ret);
+ btc_blufi_report_error(ESP_BLUFI_DECRYPT_ERROR);
+ return;
+ }
+ }
+
+ // second step, check sum
+ if (BLUFI_FC_IS_CHECK(hdr->fc)
+ && (blufi_env.cbs && blufi_env.cbs->checksum_func)) {
+ checksum = blufi_env.cbs->checksum_func(hdr->seq, &hdr->seq, hdr->data_len + 2);
+ checksum_pkt = hdr->data[hdr->data_len] | (((uint16_t) hdr->data[hdr->data_len + 1]) << 8);
+ if (checksum != checksum_pkt) {
+ BTC_TRACE_ERROR("%s checksum error %04x, pkt %04x\n", __func__, checksum, checksum_pkt);
+ btc_blufi_report_error(ESP_BLUFI_CHECKSUM_ERROR);
+ return;
+ }
+ }
+
+ if (BLUFI_FC_IS_REQ_ACK(hdr->fc)) {
+ btc_blufi_send_ack(hdr->seq);
+ }
+
+ if (BLUFI_FC_IS_FRAG(hdr->fc)) {
+ if (blufi_env.offset == 0) {
+ /*
+ blufi_env.aggr_buf should be NULL if blufi_env.offset is 0.
+ It is possible that the process of sending fragment packet
+ has not been completed
+ */
+ if (blufi_env.aggr_buf) {
+ BTC_TRACE_ERROR("%s msg error, blufi_env.aggr_buf is not freed\n", __func__);
+ btc_blufi_report_error(ESP_BLUFI_MSG_STATE_ERROR);
+ return;
+ }
+ blufi_env.total_len = hdr->data[0] | (((uint16_t) hdr->data[1]) << 8);
+ blufi_env.aggr_buf = osi_malloc(blufi_env.total_len);
+ if (blufi_env.aggr_buf == NULL) {
+ BTC_TRACE_ERROR("%s no mem, len %d\n", __func__, blufi_env.total_len);
+ btc_blufi_report_error(ESP_BLUFI_DH_MALLOC_ERROR);
+ return;
+ }
+ }
+ if (blufi_env.offset + hdr->data_len - 2 <= blufi_env.total_len){
+ memcpy(blufi_env.aggr_buf + blufi_env.offset, hdr->data + 2, hdr->data_len - 2);
+ blufi_env.offset += (hdr->data_len - 2);
+ } else {
+ BTC_TRACE_ERROR("%s payload is longer than packet length, len %d \n", __func__, blufi_env.total_len);
+ btc_blufi_report_error(ESP_BLUFI_DATA_FORMAT_ERROR);
+ return;
+ }
+
+ } else {
+ if (blufi_env.offset > 0) { /* if previous pkt is frag */
+ /* blufi_env.aggr_buf should not be NULL */
+ if (blufi_env.aggr_buf == NULL) {
+ BTC_TRACE_ERROR("%s buffer is NULL\n", __func__);
+ btc_blufi_report_error(ESP_BLUFI_DH_MALLOC_ERROR);
+ return;
+ }
+ /* payload length should be equal to total_len */
+ if ((blufi_env.offset + hdr->data_len) != blufi_env.total_len) {
+ BTC_TRACE_ERROR("%s payload is longer than packet length, len %d \n", __func__, blufi_env.total_len);
+ btc_blufi_report_error(ESP_BLUFI_DATA_FORMAT_ERROR);
+ return;
+ }
+ memcpy(blufi_env.aggr_buf + blufi_env.offset, hdr->data, hdr->data_len);
+
+ btc_blufi_protocol_handler(hdr->type, blufi_env.aggr_buf, blufi_env.total_len);
+ blufi_env.offset = 0;
+ osi_free(blufi_env.aggr_buf);
+ blufi_env.aggr_buf = NULL;
+ } else {
+ btc_blufi_protocol_handler(hdr->type, hdr->data, hdr->data_len);
+ blufi_env.offset = 0;
+ }
+ }
+}
+void btc_blufi_send_encap(uint8_t type, uint8_t *data, int total_data_len)
+{
+ struct blufi_hdr *hdr = NULL;
+ int remain_len = total_data_len;
+ uint16_t checksum;
+ int ret;
+
+ if (blufi_env.is_connected == false) {
+ BTC_TRACE_ERROR("blufi connection has been disconnected \n");
+ return;
+ }
+
+ while (remain_len > 0) {
+ if (remain_len > blufi_env.frag_size) {
+ hdr = osi_malloc(sizeof(struct blufi_hdr) + 2 + blufi_env.frag_size + 2);
+ if (hdr == NULL) {
+ BTC_TRACE_ERROR("%s no mem\n", __func__);
+ return;
+ }
+ hdr->fc = 0x0;
+ hdr->data_len = blufi_env.frag_size + 2;
+ hdr->data[0] = remain_len & 0xff;
+ hdr->data[1] = (remain_len >> 8) & 0xff;
+ memcpy(hdr->data + 2, &data[total_data_len - remain_len], blufi_env.frag_size); //copy first, easy for check sum
+ hdr->fc |= BLUFI_FC_FRAG;
+ } else {
+ hdr = osi_malloc(sizeof(struct blufi_hdr) + remain_len + 2);
+ if (hdr == NULL) {
+ BTC_TRACE_ERROR("%s no mem\n", __func__);
+ return;
+ }
+ hdr->fc = 0x0;
+ hdr->data_len = remain_len;
+ memcpy(hdr->data, &data[total_data_len - remain_len], hdr->data_len); //copy first, easy for check sum
+ }
+
+ hdr->type = type;
+ hdr->fc |= BLUFI_FC_DIR_E2P;
+ hdr->seq = blufi_env.send_seq++;
+
+ if (BLUFI_TYPE_IS_CTRL(hdr->type)) {
+ if ((blufi_env.sec_mode & BLUFI_CTRL_SEC_MODE_CHECK_MASK)
+ && (blufi_env.cbs && blufi_env.cbs->checksum_func)) {
+ hdr->fc |= BLUFI_FC_CHECK;
+ checksum = blufi_env.cbs->checksum_func(hdr->seq, &hdr->seq, hdr->data_len + 2);
+ memcpy(&hdr->data[hdr->data_len], &checksum, 2);
+ }
+ } else if (!BLUFI_TYPE_IS_DATA_NEG(hdr->type) && !BLUFI_TYPE_IS_DATA_ERROR_INFO(hdr->type)) {
+ if ((blufi_env.sec_mode & BLUFI_DATA_SEC_MODE_CHECK_MASK)
+ && (blufi_env.cbs && blufi_env.cbs->checksum_func)) {
+ hdr->fc |= BLUFI_FC_CHECK;
+ checksum = blufi_env.cbs->checksum_func(hdr->seq, &hdr->seq, hdr->data_len + 2);
+ memcpy(&hdr->data[hdr->data_len], &checksum, 2);
+ }
+
+ if ((blufi_env.sec_mode & BLUFI_DATA_SEC_MODE_ENC_MASK)
+ && (blufi_env.cbs && blufi_env.cbs->encrypt_func)) {
+ ret = blufi_env.cbs->encrypt_func(hdr->seq, hdr->data, hdr->data_len);
+ if (ret == hdr->data_len) { /* enc must be success and enc len must equal to plain len */
+ hdr->fc |= BLUFI_FC_ENC;
+ } else {
+ BTC_TRACE_ERROR("%s encrypt error %d\n", __func__, ret);
+ btc_blufi_report_error(ESP_BLUFI_ENCRYPT_ERROR);
+ osi_free(hdr);
+ return;
+ }
+ }
+ }
+
+ if (hdr->fc & BLUFI_FC_FRAG) {
+ remain_len -= (hdr->data_len - 2);
+ } else {
+ remain_len -= hdr->data_len;
+ }
+
+ esp_blufi_send_encap(hdr);
+
+ osi_free(hdr);
+ hdr = NULL;
+ }
+}
+
+static void btc_blufi_wifi_conn_report(uint8_t opmode, uint8_t sta_conn_state, uint8_t softap_conn_num, esp_blufi_extra_info_t *info, int info_len)
+{
+ uint8_t type;
+ uint8_t *data;
+ int data_len;
+ uint8_t *p;
+
+ data_len = info_len + 3;
+ p = data = osi_malloc(data_len);
+ if (data == NULL) {
+ BTC_TRACE_ERROR("%s no mem\n", __func__);
+ return;
+ }
+
+ type = BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_WIFI_REP);
+ *p++ = opmode;
+ *p++ = sta_conn_state;
+ *p++ = softap_conn_num;
+
+ if (info) {
+ if (info->sta_bssid_set) {
+ *p++ = BLUFI_TYPE_DATA_SUBTYPE_STA_BSSID;
+ *p++ = 6;
+ memcpy(p, info->sta_bssid, 6);
+ p += 6;
+ }
+ if (info->sta_ssid) {
+ *p++ = BLUFI_TYPE_DATA_SUBTYPE_STA_SSID;
+ *p++ = info->sta_ssid_len;
+ memcpy(p, info->sta_ssid, info->sta_ssid_len);
+ p += info->sta_ssid_len;
+ }
+ if (info->sta_passwd) {
+ *p++ = BLUFI_TYPE_DATA_SUBTYPE_STA_PASSWD;
+ *p++ = info->sta_passwd_len;
+ memcpy(p, info->sta_passwd, info->sta_passwd_len);
+ p += info->sta_passwd_len;
+ }
+ if (info->softap_ssid) {
+ *p++ = BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_SSID;
+ *p++ = info->softap_ssid_len;
+ memcpy(p, info->softap_ssid, info->softap_ssid_len);
+ p += info->softap_ssid_len;
+ }
+ if (info->softap_passwd) {
+ *p++ = BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_PASSWD;
+ *p++ = info->softap_passwd_len;
+ memcpy(p, info->softap_passwd, info->softap_passwd_len);
+ p += info->softap_passwd_len;
+ }
+ if (info->softap_authmode_set) {
+ *p++ = BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_AUTH_MODE;
+ *p++ = 1;
+ *p++ = info->softap_authmode;
+ }
+ if (info->softap_max_conn_num_set) {
+ *p++ = BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_MAX_CONN_NUM;
+ *p++ = 1;
+ *p++ = info->softap_max_conn_num;
+ }
+ if (info->softap_channel_set) {
+ *p++ = BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_CHANNEL;
+ *p++ = 1;
+ *p++ = info->softap_channel;
+ }
+ if (info->sta_max_conn_retry_set) {
+ *p++ = BLUFI_TYPE_DATA_SUBTYPE_STA_MAX_CONN_RETRY;
+ *p++ = 1;
+ *p++ = info->sta_max_conn_retry;
+ }
+ if (info->sta_conn_end_reason_set) {
+ *p++ = BLUFI_TYPE_DATA_SUBTYPE_STA_CONN_END_REASON;
+ *p++ = 1;
+ *p++ = info->sta_conn_end_reason;
+ }
+ if (info->sta_conn_rssi_set) {
+ *p++ = BLUFI_TYPE_DATA_SUBTYPE_STA_CONN_RSSI;
+ *p++ = 1;
+ *p++ = info->sta_conn_rssi;
+ }
+ }
+ if (p - data > data_len) {
+ BTC_TRACE_ERROR("%s len error %d %d\n", __func__, (int)(p - data), data_len);
+ }
+
+ btc_blufi_send_encap(type, data, data_len);
+ osi_free(data);
+}
+
+void btc_blufi_send_wifi_list(uint16_t apCount, esp_blufi_ap_record_t *list)
+{
+ uint8_t type;
+ uint8_t *data;
+ int data_len;
+ uint8_t *p;
+ // malloc size: (len + RSSI + ssid buffer) * apCount;
+ uint32_t malloc_size = (1 + 1 + sizeof(list->ssid)) * apCount;
+ p = data = osi_malloc(malloc_size);
+ if (data == NULL) {
+ BTC_TRACE_ERROR("malloc error\n");
+ return;
+ }
+ type = BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_WIFI_LIST);
+ for (int i = 0; i < apCount; ++i)
+ {
+ uint32_t len = strlen((const char *)list[i].ssid);
+ data_len = (p - data);
+ //current_len + ssid + rssi + total_len_value
+ if((data_len + len + 1 + 1) > malloc_size) {
+ BTC_TRACE_ERROR("%s len error", __func__);
+ osi_free(data);
+ return;
+ }
+ *p++ = len + 1; // length of ssid + rssi
+ *p++ = list[i].rssi;
+ memcpy(p, list[i].ssid, len);
+ p = p + len;
+ }
+ data_len = (p - data);
+ btc_blufi_send_encap(type, data, data_len);
+ osi_free(data);
+}
+
+static void btc_blufi_send_ack(uint8_t seq)
+{
+ uint8_t type;
+ uint8_t data;
+
+ type = BLUFI_BUILD_TYPE(BLUFI_TYPE_CTRL, BLUFI_TYPE_CTRL_SUBTYPE_ACK);
+ data = seq;
+
+ btc_blufi_send_encap(type, &data, 1);
+}
+static void btc_blufi_send_error_info(uint8_t state)
+{
+ uint8_t type;
+ uint8_t *data;
+ int data_len;
+ uint8_t *p;
+
+ data_len = 1;
+ p = data = osi_malloc(data_len);
+ if (data == NULL) {
+ BTC_TRACE_ERROR("%s no mem\n", __func__);
+ return;
+ }
+
+ type = BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_ERROR_INFO);
+ *p++ = state;
+ if (p - data > data_len) {
+ BTC_TRACE_ERROR("%s len error %d %d\n", __func__, (int)(p - data), data_len);
+ }
+
+ btc_blufi_send_encap(type, data, data_len);
+ osi_free(data);
+}
+
+static void btc_blufi_send_custom_data(uint8_t *value, uint32_t value_len)
+{
+ if(value == NULL || value_len == 0) {
+ BTC_TRACE_ERROR("%s value or value len error", __func__);
+ return;
+ }
+ uint8_t *data = osi_malloc(value_len);
+ if (data == NULL) {
+ BTC_TRACE_ERROR("%s mem malloc error", __func__);
+ return;
+ }
+ uint8_t type = BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_CUSTOM_DATA);
+ memcpy(data, value, value_len);
+ btc_blufi_send_encap(type, data, value_len);
+ osi_free(data);
+}
+
+void btc_blufi_cb_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src)
+{
+ esp_blufi_cb_param_t *dst = (esp_blufi_cb_param_t *) p_dest;
+ esp_blufi_cb_param_t *src = (esp_blufi_cb_param_t *) p_src;
+
+ switch (msg->act) {
+ case ESP_BLUFI_EVENT_RECV_STA_SSID:
+ dst->sta_ssid.ssid = osi_malloc(src->sta_ssid.ssid_len);
+ if (dst->sta_ssid.ssid == NULL) {
+ BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act);
+ }
+ memcpy(dst->sta_ssid.ssid, src->sta_ssid.ssid, src->sta_ssid.ssid_len);
+ break;
+ case ESP_BLUFI_EVENT_RECV_STA_PASSWD:
+ dst->sta_passwd.passwd = osi_malloc(src->sta_passwd.passwd_len);
+ if (dst->sta_passwd.passwd == NULL) {
+ BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act);
+ }
+ memcpy(dst->sta_passwd.passwd, src->sta_passwd.passwd, src->sta_passwd.passwd_len);
+ break;
+ case ESP_BLUFI_EVENT_RECV_SOFTAP_SSID:
+ dst->softap_ssid.ssid = osi_malloc(src->softap_ssid.ssid_len);
+ if (dst->softap_ssid.ssid == NULL) {
+ BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act);
+ }
+ memcpy(dst->softap_ssid.ssid, src->softap_ssid.ssid, src->softap_ssid.ssid_len);
+ break;
+ case ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD:
+ dst->softap_passwd.passwd = osi_malloc(src->softap_passwd.passwd_len);
+ if (dst->softap_passwd.passwd == NULL) {
+ BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act);
+ }
+ memcpy(dst->softap_passwd.passwd, src->softap_passwd.passwd, src->softap_passwd.passwd_len);
+ break;
+ case ESP_BLUFI_EVENT_RECV_USERNAME:
+ dst->username.name = osi_malloc(src->username.name_len);
+ if (dst->username.name == NULL) {
+ BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act);
+ }
+ memcpy(dst->username.name, src->username.name, src->username.name_len);
+ break;
+ case ESP_BLUFI_EVENT_RECV_CA_CERT:
+ dst->ca.cert = osi_malloc(src->ca.cert_len);
+ if (dst->ca.cert == NULL) {
+ BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act);
+ }
+ memcpy(dst->ca.cert, src->ca.cert, src->ca.cert_len);
+ break;
+ case ESP_BLUFI_EVENT_RECV_CLIENT_CERT:
+ dst->client_cert.cert = osi_malloc(src->client_cert.cert_len);
+ if (dst->client_cert.cert == NULL) {
+ BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act);
+ }
+ memcpy(dst->client_cert.cert, src->client_cert.cert, src->client_cert.cert_len);
+ break;
+ case ESP_BLUFI_EVENT_RECV_SERVER_CERT:
+ dst->server_cert.cert = osi_malloc(src->server_cert.cert_len);
+ if (dst->server_cert.cert == NULL) {
+ BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act);
+ }
+ memcpy(dst->server_cert.cert, src->server_cert.cert, src->server_cert.cert_len);
+ break;
+ case ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY:
+ dst->client_pkey.pkey = osi_malloc(src->client_pkey.pkey_len);
+ if (dst->client_pkey.pkey == NULL) {
+ BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act);
+ }
+ memcpy(dst->client_pkey.pkey, src->client_pkey.pkey, src->client_pkey.pkey_len);
+ break;
+ case ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY:
+ dst->server_pkey.pkey = osi_malloc(src->server_pkey.pkey_len);
+ if (dst->server_pkey.pkey == NULL) {
+ BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act);
+ }
+ memcpy(dst->server_pkey.pkey, src->server_pkey.pkey, src->server_pkey.pkey_len);
+ break;
+ case ESP_BLUFI_EVENT_RECV_CUSTOM_DATA:
+ dst->custom_data.data = osi_malloc(src->custom_data.data_len);
+ if (dst->custom_data.data == NULL) {
+ BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act);
+ break;
+ }
+ memcpy(dst->custom_data.data, src->custom_data.data, src->custom_data.data_len);
+ break;
+ default:
+ break;
+ }
+}
+
+void btc_blufi_cb_deep_free(btc_msg_t *msg)
+{
+ esp_blufi_cb_param_t *param = (esp_blufi_cb_param_t *)msg->arg;
+
+ switch (msg->act) {
+ case ESP_BLUFI_EVENT_RECV_STA_SSID:
+ osi_free(param->sta_ssid.ssid);
+ break;
+ case ESP_BLUFI_EVENT_RECV_STA_PASSWD:
+ osi_free(param->sta_passwd.passwd);
+ break;
+ case ESP_BLUFI_EVENT_RECV_SOFTAP_SSID:
+ osi_free(param->softap_ssid.ssid);
+ break;
+ case ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD:
+ osi_free(param->softap_passwd.passwd);
+ break;
+ case ESP_BLUFI_EVENT_RECV_USERNAME:
+ osi_free(param->username.name);
+ break;
+ case ESP_BLUFI_EVENT_RECV_CA_CERT:
+ osi_free(param->ca.cert);
+ break;
+ case ESP_BLUFI_EVENT_RECV_CLIENT_CERT:
+ osi_free(param->client_cert.cert);
+ break;
+ case ESP_BLUFI_EVENT_RECV_SERVER_CERT:
+ osi_free(param->server_cert.cert);
+ break;
+ case ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY:
+ osi_free(param->client_pkey.pkey);
+ break;
+ case ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY:
+ osi_free(param->server_pkey.pkey);
+ break;
+ case ESP_BLUFI_EVENT_RECV_CUSTOM_DATA:
+ osi_free(param->custom_data.data);
+ break;
+ default:
+ break;
+ }
+}
+
+void btc_blufi_cb_handler(btc_msg_t *msg)
+{
+ esp_blufi_cb_param_t *param = (esp_blufi_cb_param_t *)msg->arg;
+
+ switch (msg->act) {
+ case ESP_BLUFI_EVENT_INIT_FINISH: {
+ btc_blufi_cb_to_app(ESP_BLUFI_EVENT_INIT_FINISH, param);
+ break;
+ }
+ case ESP_BLUFI_EVENT_DEINIT_FINISH: {
+ btc_blufi_cb_to_app(ESP_BLUFI_EVENT_DEINIT_FINISH, param);
+ break;
+ }
+ case ESP_BLUFI_EVENT_BLE_CONNECT:
+ btc_blufi_cb_to_app(ESP_BLUFI_EVENT_BLE_CONNECT, param);
+ break;
+ case ESP_BLUFI_EVENT_BLE_DISCONNECT:
+ btc_blufi_cb_to_app(ESP_BLUFI_EVENT_BLE_DISCONNECT, param);
+ break;
+ case ESP_BLUFI_EVENT_SET_WIFI_OPMODE:
+ btc_blufi_cb_to_app(ESP_BLUFI_EVENT_SET_WIFI_OPMODE, param);
+ break;
+ case ESP_BLUFI_EVENT_REQ_CONNECT_TO_AP:
+ btc_blufi_cb_to_app(ESP_BLUFI_EVENT_REQ_CONNECT_TO_AP, NULL);
+ break;
+ case ESP_BLUFI_EVENT_REQ_DISCONNECT_FROM_AP:
+ btc_blufi_cb_to_app(ESP_BLUFI_EVENT_REQ_DISCONNECT_FROM_AP, NULL);
+ break;
+ case ESP_BLUFI_EVENT_GET_WIFI_STATUS:
+ btc_blufi_cb_to_app(ESP_BLUFI_EVENT_GET_WIFI_STATUS, NULL);
+ break;
+ case ESP_BLUFI_EVENT_GET_WIFI_LIST:
+ btc_blufi_cb_to_app(ESP_BLUFI_EVENT_GET_WIFI_LIST, NULL);
+ break;
+ case ESP_BLUFI_EVENT_DEAUTHENTICATE_STA:
+ btc_blufi_cb_to_app(ESP_BLUFI_EVENT_DEAUTHENTICATE_STA, NULL);
+ break;
+ case ESP_BLUFI_EVENT_RECV_STA_BSSID:
+ btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_STA_BSSID, param);
+ break;
+ case ESP_BLUFI_EVENT_RECV_STA_SSID:
+ btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_STA_SSID, param);
+ break;
+ case ESP_BLUFI_EVENT_RECV_STA_PASSWD:
+ btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_STA_PASSWD, param);
+ break;
+ case ESP_BLUFI_EVENT_RECV_SOFTAP_SSID:
+ btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_SOFTAP_SSID, param);
+ break;
+ case ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD:
+ btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD, param);
+ break;
+ case ESP_BLUFI_EVENT_RECV_SOFTAP_MAX_CONN_NUM:
+ btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_SOFTAP_MAX_CONN_NUM, param);
+ break;
+ case ESP_BLUFI_EVENT_RECV_SOFTAP_AUTH_MODE:
+ btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_SOFTAP_AUTH_MODE, param);
+ break;
+ case ESP_BLUFI_EVENT_RECV_SOFTAP_CHANNEL:
+ btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_SOFTAP_CHANNEL, param);
+ break;
+ case ESP_BLUFI_EVENT_RECV_USERNAME:
+ btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_USERNAME, param);
+ break;
+ case ESP_BLUFI_EVENT_RECV_CA_CERT:
+ btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_CA_CERT, param);
+ break;
+ case ESP_BLUFI_EVENT_RECV_CLIENT_CERT:
+ btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_CLIENT_CERT, param);
+ break;
+ case ESP_BLUFI_EVENT_RECV_SERVER_CERT:
+ btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_SERVER_CERT, param);
+ break;
+ case ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY:
+ btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY, param);
+ break;
+ case ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY:
+ btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY, param);
+ break;
+ case ESP_BLUFI_EVENT_RECV_SLAVE_DISCONNECT_BLE:
+ btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_SLAVE_DISCONNECT_BLE, param);
+ break;
+ case ESP_BLUFI_EVENT_REPORT_ERROR:
+ btc_blufi_cb_to_app(ESP_BLUFI_EVENT_REPORT_ERROR, param);
+ break;
+ case ESP_BLUFI_EVENT_RECV_CUSTOM_DATA:
+ btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_CUSTOM_DATA, param);
+ break;
+ default:
+ BTC_TRACE_ERROR("%s UNKNOWN %d\n", __func__, msg->act);
+ break;
+ }
+
+ btc_blufi_cb_deep_free(msg);
+}
+
+void btc_blufi_call_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src)
+{
+ btc_blufi_args_t *dst = (btc_blufi_args_t *) p_dest;
+ btc_blufi_args_t *src = (btc_blufi_args_t *) p_src;
+
+ switch (msg->act) {
+ case BTC_BLUFI_ACT_SEND_CFG_REPORT: {
+ esp_blufi_extra_info_t *src_info = src->wifi_conn_report.extra_info;
+ dst->wifi_conn_report.extra_info_len = 0;
+ dst->wifi_conn_report.extra_info = NULL;
+
+ if (src_info == NULL) {
+ return;
+ }
+
+ dst->wifi_conn_report.extra_info = osi_calloc(sizeof(esp_blufi_extra_info_t));
+ if (dst->wifi_conn_report.extra_info == NULL) {
+ BTC_TRACE_ERROR("%s no mem line %d\n", __func__, __LINE__);
+ return;
+ }
+
+ if (src_info->sta_bssid_set) {
+ memcpy(dst->wifi_conn_report.extra_info->sta_bssid, src_info->sta_bssid, 6);
+ dst->wifi_conn_report.extra_info->sta_bssid_set = src_info->sta_bssid_set;
+ dst->wifi_conn_report.extra_info_len += (6 + 2);
+ }
+ if (src_info->sta_ssid) {
+ dst->wifi_conn_report.extra_info->sta_ssid = osi_malloc(src_info->sta_ssid_len);
+ if (dst->wifi_conn_report.extra_info->sta_ssid) {
+ memcpy(dst->wifi_conn_report.extra_info->sta_ssid, src_info->sta_ssid, src_info->sta_ssid_len);
+ dst->wifi_conn_report.extra_info->sta_ssid_len = src_info->sta_ssid_len;
+ dst->wifi_conn_report.extra_info_len += (dst->wifi_conn_report.extra_info->sta_ssid_len + 2);
+ } else {
+ BTC_TRACE_ERROR("%s no mem line %d\n", __func__, __LINE__);
+ return;
+ }
+ }
+ if (src_info->sta_passwd) {
+ dst->wifi_conn_report.extra_info->sta_passwd = osi_malloc(src_info->sta_passwd_len);
+ if (dst->wifi_conn_report.extra_info->sta_passwd) {
+ memcpy(dst->wifi_conn_report.extra_info->sta_passwd, src_info->sta_passwd, src_info->sta_passwd_len);
+ dst->wifi_conn_report.extra_info->sta_passwd_len = src_info->sta_passwd_len;
+ dst->wifi_conn_report.extra_info_len += (dst->wifi_conn_report.extra_info->sta_passwd_len + 2);
+ } else {
+ BTC_TRACE_ERROR("%s no mem line %d\n", __func__, __LINE__);
+ return;
+ }
+ }
+ if (src_info->softap_ssid) {
+ dst->wifi_conn_report.extra_info->softap_ssid = osi_malloc(src_info->softap_ssid_len);
+ if (dst->wifi_conn_report.extra_info->softap_ssid) {
+ memcpy(dst->wifi_conn_report.extra_info->softap_ssid, src_info->softap_ssid, src_info->softap_ssid_len);
+ dst->wifi_conn_report.extra_info->softap_ssid_len = src_info->softap_ssid_len;
+ dst->wifi_conn_report.extra_info_len += (dst->wifi_conn_report.extra_info->softap_ssid_len + 2);
+ } else {
+ BTC_TRACE_ERROR("%s no mem line %d\n", __func__, __LINE__);
+ return;
+ }
+ }
+ if (src_info->softap_passwd) {
+ dst->wifi_conn_report.extra_info->softap_passwd = osi_malloc(src_info->softap_passwd_len);
+ if (dst->wifi_conn_report.extra_info->softap_passwd) {
+ memcpy(dst->wifi_conn_report.extra_info->softap_passwd, src_info->softap_passwd, src_info->softap_passwd_len);
+ dst->wifi_conn_report.extra_info->softap_passwd_len = src_info->softap_passwd_len;
+ dst->wifi_conn_report.extra_info_len += (dst->wifi_conn_report.extra_info->softap_passwd_len + 2);
+ } else {
+ BTC_TRACE_ERROR("%s no mem line %d\n", __func__, __LINE__);
+ return;
+ }
+ }
+ if (src_info->softap_authmode_set) {
+ dst->wifi_conn_report.extra_info->softap_authmode_set = src_info->softap_authmode_set;
+ dst->wifi_conn_report.extra_info->softap_authmode = src_info->softap_authmode;
+ dst->wifi_conn_report.extra_info_len += (1 + 2);
+ }
+ if (src_info->softap_max_conn_num_set) {
+ dst->wifi_conn_report.extra_info->softap_max_conn_num_set = src_info->softap_max_conn_num_set;
+ dst->wifi_conn_report.extra_info->softap_max_conn_num = src_info->softap_max_conn_num;
+ dst->wifi_conn_report.extra_info_len += (1 + 2);
+ }
+ if (src_info->softap_channel_set) {
+ dst->wifi_conn_report.extra_info->softap_channel_set = src_info->softap_channel_set;
+ dst->wifi_conn_report.extra_info->softap_channel = src_info->softap_channel;
+ dst->wifi_conn_report.extra_info_len += (1 + 2);
+ }
+ if (src_info->sta_max_conn_retry_set) {
+ dst->wifi_conn_report.extra_info->sta_max_conn_retry_set = src_info->sta_max_conn_retry_set;
+ dst->wifi_conn_report.extra_info->sta_max_conn_retry = src_info->sta_max_conn_retry;
+ dst->wifi_conn_report.extra_info_len += (1 + 2);
+ }
+ if (src_info->sta_conn_end_reason_set) {
+ dst->wifi_conn_report.extra_info->sta_conn_end_reason_set = src_info->sta_conn_end_reason_set;
+ dst->wifi_conn_report.extra_info->sta_conn_end_reason = src_info->sta_conn_end_reason;
+ dst->wifi_conn_report.extra_info_len += (1 + 2);
+ }
+ if (src_info->sta_conn_rssi_set) {
+ dst->wifi_conn_report.extra_info->sta_conn_rssi_set = src_info->sta_conn_rssi_set;
+ dst->wifi_conn_report.extra_info->sta_conn_rssi = src_info->sta_conn_rssi;
+ dst->wifi_conn_report.extra_info_len += (1 + 2);
+ }
+ break;
+ }
+ case BTC_BLUFI_ACT_SEND_WIFI_LIST:{
+ esp_blufi_ap_record_t *list = src->wifi_list.list;
+ src->wifi_list.list = NULL;
+ if (list == NULL || src->wifi_list.apCount <= 0) {
+ break;
+ }
+ dst->wifi_list.list = (esp_blufi_ap_record_t *)osi_malloc(sizeof(esp_blufi_ap_record_t) * src->wifi_list.apCount);
+ if (dst->wifi_list.list == NULL) {
+ BTC_TRACE_ERROR("%s no mem line %d\n", __func__, __LINE__);
+ break;
+ }
+ memcpy(dst->wifi_list.list, list, sizeof(esp_blufi_ap_record_t) * src->wifi_list.apCount);
+ break;
+ }
+ case BTC_BLUFI_ACT_SEND_CUSTOM_DATA:{
+ uint8_t *data = src->custom_data.data;
+ if(data == NULL) {
+ BTC_TRACE_ERROR("custom data is NULL\n");
+ break;
+ }
+ dst->custom_data.data = osi_malloc(src->custom_data.data_len);
+ if(dst->custom_data.data == NULL) {
+ BTC_TRACE_ERROR("custom data malloc error\n");
+ break;
+ }
+ memcpy(dst->custom_data.data, src->custom_data.data, src->custom_data.data_len);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void btc_blufi_call_deep_free(btc_msg_t *msg)
+{
+ btc_blufi_args_t *arg = (btc_blufi_args_t *)msg->arg;
+
+ switch (msg->act) {
+ case BTC_BLUFI_ACT_SEND_CFG_REPORT: {
+ esp_blufi_extra_info_t *info = (esp_blufi_extra_info_t *)arg->wifi_conn_report.extra_info;
+
+ if (info == NULL) {
+ return;
+ }
+ if (info->sta_ssid) {
+ osi_free(info->sta_ssid);
+ }
+ if (info->sta_passwd) {
+ osi_free(info->sta_passwd);
+ }
+ if (info->softap_ssid) {
+ osi_free(info->softap_ssid);
+ }
+ if (info->softap_passwd) {
+ osi_free(info->softap_passwd);
+ }
+ osi_free(info);
+ break;
+ }
+ case BTC_BLUFI_ACT_SEND_WIFI_LIST:{
+ esp_blufi_ap_record_t *list = (esp_blufi_ap_record_t *)arg->wifi_list.list;
+ if (list){
+ osi_free(list);
+ }
+ break;
+ }
+ case BTC_BLUFI_ACT_SEND_CUSTOM_DATA:{
+ uint8_t *data = arg->custom_data.data;
+ if(data) {
+ osi_free(data);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void btc_blufi_call_handler(btc_msg_t *msg)
+{
+ btc_blufi_args_t *arg = (btc_blufi_args_t *)msg->arg;
+
+ switch (msg->act) {
+ case BTC_BLUFI_ACT_INIT:
+ btc_blufi_profile_init();
+ break;
+ case BTC_BLUFI_ACT_DEINIT:
+ btc_blufi_profile_deinit();
+ break;
+ case BTC_BLUFI_ACT_SEND_CFG_REPORT:
+ btc_blufi_wifi_conn_report(arg->wifi_conn_report.opmode,
+ arg->wifi_conn_report.sta_conn_state,
+ arg->wifi_conn_report.softap_conn_num,
+ arg->wifi_conn_report.extra_info,
+ arg->wifi_conn_report.extra_info_len);
+ break;
+ case BTC_BLUFI_ACT_SEND_WIFI_LIST:{
+ btc_blufi_send_wifi_list(arg->wifi_list.apCount, arg->wifi_list.list);
+ break;
+ }
+ case BTC_BLUFI_ACT_SEND_ERR_INFO:
+ btc_blufi_send_error_info(arg->blufi_err_infor.state);
+ break;
+ case BTC_BLUFI_ACT_SEND_CUSTOM_DATA:
+ btc_blufi_send_custom_data(arg->custom_data.data, arg->custom_data.data_len);
+ break;
+ default:
+ BTC_TRACE_ERROR("%s UNKNOWN %d\n", __func__, msg->act);
+ break;
+ }
+ btc_blufi_call_deep_free(msg);
+}
+
+void btc_blufi_set_callbacks(esp_blufi_callbacks_t *callbacks)
+{
+ blufi_env.cbs = callbacks;
+}
+
+uint16_t btc_blufi_get_version(void)
+{
+ return BTC_BLUFI_VERSION;
+}
+
+#endif ///BLUFI_INCLUDED == TRUE
diff --git a/lib/bt/common/btc/profile/esp/blufi/blufi_protocol.c b/lib/bt/common/btc/profile/esp/blufi/blufi_protocol.c
new file mode 100644
index 00000000..1b84253b
--- /dev/null
+++ b/lib/bt/common/btc/profile/esp/blufi/blufi_protocol.c
@@ -0,0 +1,253 @@
+/*
+ * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <stdint.h>
+
+#include <string.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#include "btc_blufi_prf.h"
+#include "btc/btc_task.h"
+#include "btc/btc_manage.h"
+
+#include "blufi_int.h"
+
+//#include "esp_wifi.h"
+#if (BLUFI_INCLUDED == TRUE)
+
+void btc_blufi_protocol_handler(uint8_t type, uint8_t *data, int len)
+{
+ btc_msg_t msg;
+ esp_blufi_cb_param_t param;
+ uint8_t *output_data = NULL;
+ int output_len = 0;
+ bool need_free = false;
+
+ switch (BLUFI_GET_TYPE(type)) {
+ case BLUFI_TYPE_CTRL:
+ switch (BLUFI_GET_SUBTYPE(type)) {
+ case BLUFI_TYPE_CTRL_SUBTYPE_ACK:
+ /* TODO: check sequence */
+ break;
+ case BLUFI_TYPE_CTRL_SUBTYPE_SET_SEC_MODE:
+ blufi_env.sec_mode = data[0];
+ break;
+ case BLUFI_TYPE_CTRL_SUBTYPE_SET_WIFI_OPMODE:
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_SET_WIFI_OPMODE;
+ param.wifi_mode.op_mode = data[0];
+
+ btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
+ break;
+ case BLUFI_TYPE_CTRL_SUBTYPE_CONN_TO_AP:
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_REQ_CONNECT_TO_AP;
+
+ btc_transfer_context(&msg, NULL, 0, NULL, NULL);
+ break;
+ case BLUFI_TYPE_CTRL_SUBTYPE_DISCONN_FROM_AP:
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_REQ_DISCONNECT_FROM_AP;
+
+ btc_transfer_context(&msg, NULL, 0, NULL, NULL);
+ break;
+ case BLUFI_TYPE_CTRL_SUBTYPE_GET_WIFI_STATUS:
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_GET_WIFI_STATUS;
+
+ btc_transfer_context(&msg, NULL, 0, NULL, NULL);
+ break;
+ case BLUFI_TYPE_CTRL_SUBTYPE_DEAUTHENTICATE_STA:
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_DEAUTHENTICATE_STA;
+
+ btc_transfer_context(&msg, NULL, 0, NULL,NULL);
+ break;
+ case BLUFI_TYPE_CTRL_SUBTYPE_GET_VERSION: {
+ uint8_t type = BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_REPLY_VERSION);
+ uint8_t data[2];
+
+ data[0] = BTC_BLUFI_GREAT_VER;
+ data[1] = BTC_BLUFI_SUB_VER;
+ btc_blufi_send_encap(type, &data[0], sizeof(data));
+ break;
+ }
+ case BLUFI_TYPE_CTRL_SUBTYPE_DISCONNECT_BLE:
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_RECV_SLAVE_DISCONNECT_BLE;
+ btc_transfer_context(&msg, NULL, 0, NULL, NULL);
+ break;
+ case BLUFI_TYPE_CTRL_SUBTYPE_GET_WIFI_LIST:
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_GET_WIFI_LIST;
+ btc_transfer_context(&msg, NULL, 0, NULL, NULL);
+ break;
+ default:
+ BTC_TRACE_ERROR("%s Unkown Ctrl pkt %02x\n", __func__, type);
+ break;
+ }
+ break;
+ case BLUFI_TYPE_DATA:
+ switch (BLUFI_GET_SUBTYPE(type)) {
+ case BLUFI_TYPE_DATA_SUBTYPE_NEG:
+ if (blufi_env.cbs && blufi_env.cbs->negotiate_data_handler) {
+ blufi_env.cbs->negotiate_data_handler(data, len, &output_data, &output_len, &need_free);
+ }
+
+ if (output_data && output_len > 0) {
+ btc_blufi_send_encap(BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_NEG),
+ output_data, output_len);
+ }
+ break;
+ case BLUFI_TYPE_DATA_SUBTYPE_STA_BSSID:
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_RECV_STA_BSSID;
+ memcpy(param.sta_bssid.bssid, &data[0], 6);
+
+ btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
+ break;
+ case BLUFI_TYPE_DATA_SUBTYPE_STA_SSID:
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_RECV_STA_SSID;
+ param.sta_ssid.ssid = &data[0];
+ param.sta_ssid.ssid_len = len;
+
+ btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy, btc_blufi_cb_deep_free);
+ break;
+ case BLUFI_TYPE_DATA_SUBTYPE_STA_PASSWD:
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_RECV_STA_PASSWD;
+ param.sta_passwd.passwd = &data[0];
+ param.sta_passwd.passwd_len = len;
+
+ btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy, btc_blufi_cb_deep_free);
+ break;
+ case BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_SSID:
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_RECV_SOFTAP_SSID;
+ param.softap_ssid.ssid = &data[0];
+ param.softap_ssid.ssid_len = len;
+
+ btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy, btc_blufi_cb_deep_free);
+ break;
+ case BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_PASSWD:
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD;
+ param.softap_passwd.passwd = &data[0];
+ param.softap_passwd.passwd_len = len;
+
+ btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy, btc_blufi_cb_deep_free);
+ break;
+ case BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_MAX_CONN_NUM:
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_RECV_SOFTAP_MAX_CONN_NUM;
+ param.softap_max_conn_num.max_conn_num = data[0];
+
+ btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
+ break;
+ case BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_AUTH_MODE:
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_RECV_SOFTAP_AUTH_MODE;
+ param.softap_auth_mode.auth_mode = data[0];
+
+ btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
+ break;
+ case BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_CHANNEL:
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_RECV_SOFTAP_CHANNEL;
+ param.softap_channel.channel = data[0];
+
+ btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
+ break;
+ case BLUFI_TYPE_DATA_SUBTYPE_USERNAME:
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_RECV_USERNAME;
+ param.username.name = &data[0];
+ param.username.name_len = len;
+
+ btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy, btc_blufi_cb_deep_free);
+ break;
+ case BLUFI_TYPE_DATA_SUBTYPE_CA:
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_RECV_CA_CERT;
+ param.ca.cert = &data[0];
+ param.ca.cert_len = len;
+
+ btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy, btc_blufi_cb_deep_free);
+ break;
+ case BLUFI_TYPE_DATA_SUBTYPE_CLIENT_CERT:
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_RECV_CLIENT_CERT;
+ param.client_cert.cert = &data[0];
+ param.client_cert.cert_len = len;
+
+ btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy, btc_blufi_cb_deep_free);
+ break;
+ case BLUFI_TYPE_DATA_SUBTYPE_SERVER_CERT:
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_RECV_SERVER_CERT;
+ param.client_cert.cert = &data[0];
+ param.client_cert.cert_len = len;
+
+ btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy, btc_blufi_cb_deep_free);
+ break;
+ case BLUFI_TYPE_DATA_SUBTYPE_CLIENT_PRIV_KEY:
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY;
+ param.client_pkey.pkey = &data[0];
+ param.client_pkey.pkey_len = len;
+
+ btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy, btc_blufi_cb_deep_free);
+ break;
+ case BLUFI_TYPE_DATA_SUBTYPE_SERVER_PRIV_KEY:
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY;
+ param.client_pkey.pkey = &data[0];
+ param.client_pkey.pkey_len = len;
+
+ btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy, btc_blufi_cb_deep_free);
+ break;
+ case BLUFI_TYPE_DATA_SUBTYPE_CUSTOM_DATA:
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_RECV_CUSTOM_DATA;
+ param.custom_data.data = &data[0];
+ param.custom_data.data_len = len;
+ btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy, btc_blufi_cb_deep_free);
+ break;
+ default:
+ BTC_TRACE_ERROR("%s Unkown Ctrl pkt %02x\n", __func__, type);
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+#endif ///BLUFI_INCLUDED == TRUE
diff --git a/lib/bt/common/btc/profile/esp/blufi/include/blufi_int.h b/lib/bt/common/btc/profile/esp/blufi/include/blufi_int.h
new file mode 100644
index 00000000..5730f143
--- /dev/null
+++ b/lib/bt/common/btc/profile/esp/blufi/include/blufi_int.h
@@ -0,0 +1,197 @@
+/*
+ * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __BLUFI_INT_H__
+#define __BLUFI_INT_H__
+
+#include "btc/btc_task.h"
+#include "esp_blufi_api.h"
+#if (BLUFI_INCLUDED == TRUE)
+
+#define BTC_BLUFI_GREAT_VER 0x01 //Version + Subversion
+#define BTC_BLUFI_SUB_VER 0x03 //Version + Subversion
+#define BTC_BLUFI_VERSION ((BTC_BLUFI_GREAT_VER<<8)|BTC_BLUFI_SUB_VER) //Version + Subversion
+
+typedef UINT8 tGATT_IF;
+/* service engine control block */
+typedef struct {
+ /* Protocol reference */
+ tGATT_IF gatt_if;
+ UINT8 srvc_inst;
+ UINT16 handle_srvc;
+ UINT16 handle_char_p2e;
+ UINT16 handle_char_e2p;
+ UINT16 handle_descr_e2p;
+ UINT16 conn_id;
+ BOOLEAN is_connected;
+ BD_ADDR remote_bda;
+ UINT32 trans_id;
+ UINT8 congest;
+ UINT16 frag_size;
+#define BLUFI_PREPAIR_BUF_MAX_SIZE 1024
+ uint8_t *prepare_buf;
+ int prepare_len;
+ /* Control reference */
+ esp_blufi_callbacks_t *cbs;
+ BOOLEAN enabled;
+ uint8_t send_seq;
+ uint8_t recv_seq;
+ uint8_t sec_mode;
+ uint8_t *aggr_buf;
+ uint16_t total_len;
+ uint16_t offset;
+} tBLUFI_ENV;
+
+/* BLUFI protocol */
+struct blufi_hdr{
+ uint8_t type;
+ uint8_t fc;
+ uint8_t seq;
+ uint8_t data_len;
+ uint8_t data[0];
+};
+typedef struct blufi_hdr blufi_hd_t;
+
+struct blufi_frag_hdr {
+ uint8_t type;
+ uint8_t fc;
+ uint8_t seq;
+ uint8_t data_len;
+ uint16_t total_len;
+ uint8_t content[0];
+};
+typedef struct blufi_frag_hdr blufi_frag_hdr_t;
+
+#if GATT_DYNAMIC_MEMORY == FALSE
+extern tBLUFI_ENV blufi_env;
+#else
+extern tBLUFI_ENV *blufi_env_ptr;
+#define blufi_env (*blufi_env_ptr)
+#endif
+
+#define BLUFI_DATA_SEC_MODE_CHECK_MASK 0x01
+#define BLUFI_DATA_SEC_MODE_ENC_MASK 0x02
+#define BLUFI_CTRL_SEC_MODE_CHECK_MASK 0x10
+#define BLUFI_CTRL_SEC_MODE_ENC_MASK 0x20
+#define BLUFI_MAX_DATA_LEN 255
+
+// packet type
+#define BLUFI_TYPE_MASK 0x03
+#define BLUFI_TYPE_SHIFT 0
+#define BLUFI_SUBTYPE_MASK 0xFC
+#define BLUFI_SUBTYPE_SHIFT 2
+
+#define BLUFI_GET_TYPE(type) ((type) & BLUFI_TYPE_MASK)
+#define BLUFI_GET_SUBTYPE(type) (((type) & BLUFI_SUBTYPE_MASK) >>BLUFI_SUBTYPE_SHIFT)
+#define BLUFI_BUILD_TYPE(type, subtype) (((type) & BLUFI_TYPE_MASK) | ((subtype)<<BLUFI_SUBTYPE_SHIFT))
+
+#define BLUFI_TYPE_CTRL 0x0
+#define BLUFI_TYPE_CTRL_SUBTYPE_ACK 0x00
+#define BLUFI_TYPE_CTRL_SUBTYPE_SET_SEC_MODE 0x01
+#define BLUFI_TYPE_CTRL_SUBTYPE_SET_WIFI_OPMODE 0x02
+#define BLUFI_TYPE_CTRL_SUBTYPE_CONN_TO_AP 0x03
+#define BLUFI_TYPE_CTRL_SUBTYPE_DISCONN_FROM_AP 0x04
+#define BLUFI_TYPE_CTRL_SUBTYPE_GET_WIFI_STATUS 0x05
+#define BLUFI_TYPE_CTRL_SUBTYPE_DEAUTHENTICATE_STA 0x06
+#define BLUFI_TYPE_CTRL_SUBTYPE_GET_VERSION 0x07
+#define BLUFI_TYPE_CTRL_SUBTYPE_DISCONNECT_BLE 0x08
+#define BLUFI_TYPE_CTRL_SUBTYPE_GET_WIFI_LIST 0x09
+
+#define BLUFI_TYPE_DATA 0x1
+#define BLUFI_TYPE_DATA_SUBTYPE_NEG 0x00
+#define BLUFI_TYPE_DATA_SUBTYPE_STA_BSSID 0x01
+#define BLUFI_TYPE_DATA_SUBTYPE_STA_SSID 0x02
+#define BLUFI_TYPE_DATA_SUBTYPE_STA_PASSWD 0x03
+#define BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_SSID 0x04
+#define BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_PASSWD 0x05
+#define BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_MAX_CONN_NUM 0x06
+#define BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_AUTH_MODE 0x07
+#define BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_CHANNEL 0x08
+#define BLUFI_TYPE_DATA_SUBTYPE_USERNAME 0x09
+#define BLUFI_TYPE_DATA_SUBTYPE_CA 0x0a
+#define BLUFI_TYPE_DATA_SUBTYPE_CLIENT_CERT 0x0b
+#define BLUFI_TYPE_DATA_SUBTYPE_SERVER_CERT 0x0c
+#define BLUFI_TYPE_DATA_SUBTYPE_CLIENT_PRIV_KEY 0x0d
+#define BLUFI_TYPE_DATA_SUBTYPE_SERVER_PRIV_KEY 0x0e
+#define BLUFI_TYPE_DATA_SUBTYPE_WIFI_REP 0x0f
+#define BLUFI_TYPE_DATA_SUBTYPE_REPLY_VERSION 0x10
+#define BLUFI_TYPE_DATA_SUBTYPE_WIFI_LIST 0x11
+#define BLUFI_TYPE_DATA_SUBTYPE_ERROR_INFO 0x12
+#define BLUFI_TYPE_DATA_SUBTYPE_CUSTOM_DATA 0x13
+#define BLUFI_TYPE_DATA_SUBTYPE_STA_MAX_CONN_RETRY 0x14
+#define BLUFI_TYPE_DATA_SUBTYPE_STA_CONN_END_REASON 0x15
+#define BLUFI_TYPE_DATA_SUBTYPE_STA_CONN_RSSI 0x16
+#define BLUFI_TYPE_IS_CTRL(type) (BLUFI_GET_TYPE((type)) == BLUFI_TYPE_CTRL)
+#define BLUFI_TYPE_IS_DATA(type) (BLUFI_GET_TYPE((type)) == BLUFI_TYPE_DATA)
+
+#define BLUFI_TYPE_IS_CTRL_ACK(type) (BLUFI_TYPE_IS_CTRL((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_CTRL_SUBTYPE_ACK)
+#define BLUFI_TYPE_IS_CTRL_START_NEG(type) (BLUFI_TYPE_IS_CTRL((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_CTRL_SUBTYPE_START_NEG)
+#define BLUFI_TYPE_IS_CTRL_STOP_NEG(type) (BLUFI_TYPE_IS_CTRL((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_CTRL_SUBTYPE_STOP_NEG)
+#define BLUFI_TYPE_IS_CTRL_SET_WIFI_OPMODE(type) (BLUFI_TYPE_IS_CTRL((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_CTRL_SUBTYPE_SET_WIFI_OPMODE)
+#define BLUFI_TYPE_IS_CTRL_CONN_WIFI(type) (BLUFI_TYPE_IS_CTRL((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_CTRL_SUBTYPE_CONN_TO_AP)
+#define BLUFI_TYPE_IS_CTRL_DISCONN_WIFI(type) (BLUFI_TYPE_IS_CTRL((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_CTRL_SUBTYPE_DISCONN_FROM_AP)
+#define BLUFI_TYPE_IS_CTRL_GET_WIFI_STATUS(type) (BLUFI_TYPE_IS_CTRL((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_CTRL_SUBTYPE_GET_WIFI_STATUS)
+#define BLUFI_TYPE_IS_CTRL_DEAUTHENTICATE_STA(type) (BLUFI_TYPE_IS_CTRL((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_CTRL_SUBTYPE_DEAUTHENTICATE_STA)
+#define BLUFI_TYPE_IS_CTRL_GET_VERSION(type) (BLUFI_TYPE_IS_CTRL((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_CTRL_SUBTYPE_GET_VERSION)
+
+#define BLUFI_TYPE_IS_DATA_NEG(type) (BLUFI_TYPE_IS_DATA((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_DATA_SUBTYPE_NEG)
+#define BLUFI_TYPE_IS_DATA_STA_BSSID(type) (BLUFI_TYPE_IS_DATA((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_DATA_SUBTYPE_STA_BSSID)
+#define BLUFI_TYPE_IS_DATA_STA_SSID(type) (BLUFI_TYPE_IS_DATA((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_DATA_SUBTYPE_STA_SSID)
+#define BLUFI_TYPE_IS_DATA_STA_PASSWD(type) (BLUFI_TYPE_IS_DATA((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_DATA_SUBTYPE_STA_PASSWD)
+#define BLUFI_TYPE_IS_DATA_SOFTAP_SSID(type) (BLUFI_TYPE_IS_DATA((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_SSID)
+#define BLUFI_TYPE_IS_DATA_SOFTAP_PASSWD(type) (BLUFI_TYPE_IS_DATA((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_PASSWD)
+#define BLUFI_TYPE_IS_DATA_SOFTAP_MAX_CONN_NUM(type) (BLUFI_TYPE_IS_DATA((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_MAX_CONN_NUM)
+#define BLUFI_TYPE_IS_DATA_SOFTAP_AUTH_MODE(type) (BLUFI_TYPE_IS_DATA((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_AUTH_MODE)
+#define BLUFI_TYPE_IS_DATA_SOFTAP_CHANNEL(type) (BLUFI_TYPE_IS_DATA((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_CHANNEL)
+#define BLUFI_TYPE_IS_DATA_USERNAME(type) (BLUFI_TYPE_IS_DATA((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_DATA_SUBTYPE_USERNAME)
+#define BLUFI_TYPE_IS_DATA_CA(type) (BLUFI_TYPE_IS_DATA((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_DATA_SUBTYPE_CA)
+#define BLUFI_TYPE_IS_DATA_CLEINT_CERT(type) (BLUFI_TYPE_IS_DATA((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_DATA_SUBTYPE_CLIENT_CERT)
+#define BLUFI_TYPE_IS_DATA_SERVER_CERT(type) (BLUFI_TYPE_IS_DATA((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_DATA_SUBTYPE_SERVER_CERT)
+#define BLUFI_TYPE_IS_DATA_CLIENT_PRIV_KEY(type) (BLUFI_TYPE_IS_DATA((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_DATA_SUBTYPE_CLIENT_PRIV_KEY)
+#define BLUFI_TYPE_IS_DATA_SERVER_PRIV_KEY(type) (BLUFI_TYPE_IS_DATA((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_DATA_SUBTYPE_SERVER_PRIV_KEY)
+#define BLUFI_TYPE_IS_DATA_ERROR_INFO(type) (BLUFI_TYPE_IS_DATA((type)) && BLUFI_GET_SUBTYPE((type)) == BLUFI_TYPE_DATA_SUBTYPE_ERROR_INFO)
+
+// packet frame control
+#define BLUFI_FC_ENC_MASK 0x01
+#define BLUFI_FC_CHECK_MASK 0x02
+#define BLUFI_FC_DIR_MASK 0x04
+#define BLUFI_FC_REQ_ACK_MASK 0x08
+#define BLUFI_FC_FRAG_MASK 0x10
+
+#define BLUFI_FC_ENC 0x01
+#define BLUFI_FC_CHECK 0x02
+#define BLUFI_FC_DIR_P2E 0x00
+#define BLUFI_FC_DIR_E2P 0x04
+#define BLUFI_FC_REQ_ACK 0x08
+#define BLUFI_FC_FRAG 0x10
+
+#define BLUFI_FC_IS_ENC(fc) ((fc) & BLUFI_FC_ENC_MASK)
+#define BLUFI_FC_IS_CHECK(fc) ((fc) & BLUFI_FC_CHECK_MASK)
+#define BLUFI_FC_IS_DIR_P2E(fc) ((fc) & BLUFI_FC_DIR_P2E_MASK)
+#define BLUFI_FC_IS_DIR_E2P(fc) (!((fc) & BLUFI_DIR_P2E_MASK))
+#define BLUFI_FC_IS_REQ_ACK(fc) ((fc) & BLUFI_FC_REQ_ACK_MASK)
+#define BLUFI_FC_IS_FRAG(fc) ((fc) & BLUFI_FC_FRAG_MASK)
+
+/* default GATT MTU size over LE link
+*/
+#define GATT_DEF_BLE_MTU_SIZE 23
+/* BLUFI HEADER + TOTAL(REMAIN) LENGTH + CRC + L2CAP RESERVED */
+#define BLUFI_MTU_RESERVED_SIZE (sizeof(struct blufi_hdr) + 2 + 2 + 3)
+#define BLUFI_FRAG_DATA_DEFAULT_LEN (GATT_DEF_BLE_MTU_SIZE - BLUFI_MTU_RESERVED_SIZE)
+
+//function declare
+void btc_blufi_protocol_handler(uint8_t type, uint8_t *data, int len);
+
+void btc_blufi_send_encap(uint8_t type, uint8_t *data, int data_len);
+
+void btc_blufi_set_callbacks(esp_blufi_callbacks_t *callbacks);
+
+void btc_blufi_cb_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src);
+
+void btc_blufi_cb_deep_free(btc_msg_t *msg);
+
+#endif /* __BLUFI_INT_H__ */
+#endif ///BLUFI_INCLUDED == TRUE
diff --git a/lib/bt/common/btc/profile/esp/blufi/include/esp_blufi.h b/lib/bt/common/btc/profile/esp/blufi/include/esp_blufi.h
new file mode 100644
index 00000000..a368325d
--- /dev/null
+++ b/lib/bt/common/btc/profile/esp/blufi/include/esp_blufi.h
@@ -0,0 +1,99 @@
+/*
+ * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __ESP_BLUFI_H__
+#define __ESP_BLUFI_H__
+
+#include "esp_blufi_api.h"
+#include "esp_err.h"
+
+#ifdef CONFIG_BT_NIMBLE_ENABLED
+#include "nimble/ble.h"
+#include "host/ble_gap.h"
+#include "modlog/modlog.h"
+#endif
+
+#ifdef CONFIG_BT_BLUEDROID_ENABLED
+#include "esp_gap_ble_api.h"
+#endif
+
+#define BLUFI_APP_UUID 0xFFFF
+#define BLUFI_DEVICE_NAME "BLUFI_DEVICE"
+
+#ifdef CONFIG_BT_NIMBLE_ENABLED
+struct ble_hs_cfg;
+struct ble_gatt_register_ctxt;
+struct gatt_value {
+ struct os_mbuf *buf;
+ uint16_t val_handle;
+ uint8_t type;
+ void *ptr;
+};
+#define SERVER_MAX_VALUES 3
+#define MAX_VAL_SIZE 512
+extern struct gatt_value gatt_values[SERVER_MAX_VALUES];
+/* GATT server callback */
+void esp_blufi_gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg);
+
+/* Initialise gatt server */
+int esp_blufi_gatt_svr_init(void);
+void esp_blufi_btc_init(void);
+void esp_blufi_btc_deinit(void);
+#endif
+
+#ifdef CONFIG_BT_BLUEDROID_ENABLED
+/**
+ * @brief Close a connection a remote device.
+ *
+ * @param[in] gatts_if: GATT server access interface
+ * @param[in] conn_id: connection ID to be closed.
+ *
+ * @return
+ * - ESP_OK : success
+ * - other : failed
+ *
+ */
+esp_err_t esp_blufi_close(uint8_t gatts_if, uint16_t conn_id);
+void esp_blufi_gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
+#endif
+
+/* Initialise blufi profile */
+uint8_t esp_blufi_init(void);
+
+/* start advertising */
+void bleprph_advertise(void);
+
+/* send notifications */
+void esp_blufi_send_notify(void *arg);
+
+/* Deinitialise blufi */
+void esp_blufi_deinit(void);
+/* disconnect */
+void esp_blufi_disconnect(void);
+
+/* Stop advertisement */
+void esp_blufi_adv_stop(void);
+
+/* Start advertisement */
+void esp_blufi_adv_start(void);
+
+void esp_blufi_send_encap(void *arg);
+
+#ifdef CONFIG_BT_NIMBLE_ENABLED
+/**
+ * @brief Handle gap event for BluFi.
+ * This function can be called inside custom use gap event handler.
+ * It provide minimal event management for BluFi purpose.
+ *
+ * @param[in] event The type of event being signalled.
+ * @param[in] arg Application-specified argument. Currently unused
+ * @return int 0 in case of success.
+ * Other in case of failure.
+ */
+int esp_blufi_handle_gap_events(struct ble_gap_event *event, void *arg);
+#endif
+
+#endif/* _ESP_BLUFI_ */
diff --git a/lib/bt/common/btc/profile/esp/blufi/nimble_host/esp_blufi.c b/lib/bt/common/btc/profile/esp/blufi/nimble_host/esp_blufi.c
new file mode 100644
index 00000000..67a83951
--- /dev/null
+++ b/lib/bt/common/btc/profile/esp/blufi/nimble_host/esp_blufi.c
@@ -0,0 +1,543 @@
+/*
+ * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "btc_blufi_prf.h"
+#include "blufi_int.h"
+#include "esp_log.h"
+#include "esp_blufi_api.h"
+#include "esp_err.h"
+#include "btc/btc_task.h"
+#include "esp_blufi.h"
+#include "osi/allocator.h"
+#include "console/console.h"
+
+/*nimBLE Host*/
+#include "nimble/nimble_port.h"
+#include "nimble/nimble_port_freertos.h"
+#include "host/ble_hs.h"
+#include "host/util/util.h"
+#include "host/ble_uuid.h"
+#include "host/ble_gatt.h"
+
+#include "services/gap/ble_svc_gap.h"
+#include "services/gatt/ble_svc_gatt.h"
+
+#if (BLUFI_INCLUDED == TRUE)
+
+static uint8_t own_addr_type;
+
+struct gatt_value gatt_values[SERVER_MAX_VALUES];
+const static char *TAG = "BLUFI_EXAMPLE";
+
+enum {
+ GATT_VALUE_TYPE_CHR,
+ GATT_VALUE_TYPE_DSC,
+};
+
+static int gatt_svr_access_cb(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg);
+
+static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
+ {
+ /*** Service: Blufi */
+ .type = BLE_GATT_SVC_TYPE_PRIMARY,
+ .uuid = BLE_UUID16_DECLARE(BLUFI_SERVICE_UUID),
+ .characteristics = (struct ble_gatt_chr_def[])
+ { {
+ /*** Characteristic: P2E */
+ .uuid = BLE_UUID16_DECLARE(BLUFI_CHAR_P2E_UUID),
+ .access_cb = gatt_svr_access_cb,
+ .flags = BLE_GATT_CHR_F_WRITE,
+ .arg = &gatt_values[0],
+ .val_handle = &gatt_values[0].val_handle,
+ }, {
+ /*** Characteristic: E2P */
+ .uuid = BLE_UUID16_DECLARE(BLUFI_CHAR_E2P_UUID),
+ .access_cb = gatt_svr_access_cb,
+ .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY,
+ .arg = &gatt_values[1],
+ .val_handle = &gatt_values[1].val_handle,
+ }, {
+ 0, /* No more characteristics in this service. */
+ }
+ },
+ },
+
+ {
+ 0, /* No more services. */
+ },
+};
+
+void esp_blufi_gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg)
+{
+ char buf[BLE_UUID_STR_LEN];
+ switch (ctxt->op) {
+ case BLE_GATT_REGISTER_OP_SVC:
+ ESP_LOGI(TAG, "registered service %s with handle=%d",
+ ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf),
+ ctxt->svc.handle);
+ break;
+
+ case BLE_GATT_REGISTER_OP_CHR:
+ ESP_LOGI(TAG, "registering characteristic %s with "
+ "def_handle=%d val_handle=%d\n",
+ ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf),
+ ctxt->chr.def_handle,
+ ctxt->chr.val_handle);
+ break;
+
+ case BLE_GATT_REGISTER_OP_DSC:
+ ESP_LOGI(TAG, "registering descriptor %s with handle=%d",
+ ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf),
+ ctxt->dsc.handle);
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+}
+
+static size_t write_value(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg)
+{
+ struct gatt_value *value = (struct gatt_value *)arg;
+ uint16_t len;
+ int rc;
+ if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
+ if (ctxt->chr->flags & BLE_GATT_CHR_F_WRITE_AUTHOR) {
+ return BLE_ATT_ERR_INSUFFICIENT_AUTHOR;
+ }
+ } else {
+ if (ctxt->dsc->att_flags & BLE_ATT_F_WRITE_AUTHOR) {
+ return BLE_ATT_ERR_INSUFFICIENT_AUTHOR;
+ }
+ }
+
+ btc_blufi_recv_handler(&ctxt->om->om_data[0], ctxt->om->om_len);
+ rc = ble_hs_mbuf_to_flat(ctxt->om, value->buf->om_data,
+ value->buf->om_len, &len);
+ if (rc != 0) {
+ return BLE_ATT_ERR_UNLIKELY;
+ }
+ /* Maximum attribute value size is 512 bytes */
+ assert(value->buf->om_len < MAX_VAL_SIZE);
+
+ return 0;
+}
+
+static size_t read_value(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg)
+{
+ const struct gatt_value *value = (const struct gatt_value *) arg;
+ char str[BLE_UUID_STR_LEN];
+ int rc;
+
+ memset(str, '\0', sizeof(str));
+
+ if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
+ if (ctxt->chr->flags & BLE_GATT_CHR_F_READ_AUTHOR) {
+ return BLE_ATT_ERR_INSUFFICIENT_AUTHOR;
+ }
+
+ ble_uuid_to_str(ctxt->chr->uuid, str);
+ } else {
+ if (ctxt->dsc->att_flags & BLE_ATT_F_READ_AUTHOR) {
+ return BLE_ATT_ERR_INSUFFICIENT_AUTHOR;
+ }
+
+ ble_uuid_to_str(ctxt->dsc->uuid, str);
+ }
+
+ rc = os_mbuf_append(ctxt->om, value->buf->om_data, value->buf->om_len);
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+}
+
+static int gatt_svr_access_cb(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg)
+{
+ switch (ctxt->op) {
+ case BLE_GATT_ACCESS_OP_READ_CHR:
+ return read_value(conn_handle, attr_handle,
+ ctxt, arg);
+ case BLE_GATT_ACCESS_OP_WRITE_CHR:
+ return write_value(conn_handle, attr_handle,
+ ctxt, arg);
+ default:
+ assert(0);
+ return BLE_ATT_ERR_UNLIKELY;
+ }
+
+ /* Unknown characteristic; the nimble stack should not have called this
+ * function.
+ */
+ assert(0);
+ return BLE_ATT_ERR_UNLIKELY;
+}
+
+static void init_gatt_values(void)
+{
+ int i = 0;
+ const struct ble_gatt_svc_def *svc;
+ const struct ble_gatt_chr_def *chr;
+ const struct ble_gatt_dsc_def *dsc;
+
+ for (svc = gatt_svr_svcs; svc && svc->uuid; svc++) {
+ for (chr = svc->characteristics; chr && chr->uuid; chr++) {
+ assert(i < SERVER_MAX_VALUES);
+ gatt_values[i].type = GATT_VALUE_TYPE_CHR;
+ gatt_values[i].ptr = (void *)chr;
+ gatt_values[i].buf = os_msys_get(0, 0);
+ os_mbuf_extend(gatt_values[i].buf, 1);
+ ++i;
+
+ for (dsc = chr->descriptors; dsc && dsc->uuid; dsc++) {
+ assert(i < SERVER_MAX_VALUES);
+ gatt_values[i].type = GATT_VALUE_TYPE_DSC;
+ gatt_values[i].ptr = (void *)dsc;
+ gatt_values[i].buf = os_msys_get(0, 0);
+ os_mbuf_extend(gatt_values[i].buf, 1);
+ ++i;
+ }
+ }
+ }
+
+}
+
+int esp_blufi_gatt_svr_init(void)
+{
+ int rc;
+ ble_svc_gap_init();
+ ble_svc_gatt_init();
+
+ rc = ble_gatts_count_cfg(gatt_svr_svcs);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = ble_gatts_add_svcs(gatt_svr_svcs);
+ if (rc != 0) {
+ return rc;
+ }
+
+ init_gatt_values();
+ return 0;
+}
+
+static int
+esp_blufi_gap_event(struct ble_gap_event *event, void *arg)
+{
+ struct ble_gap_conn_desc desc;
+ int rc;
+
+ switch (event->type) {
+ case BLE_GAP_EVENT_CONNECT:
+ /* A new connection was established or a connection attempt failed. */
+ ESP_LOGI(TAG, "connection %s; status=%d",
+ event->connect.status == 0 ? "established" : "failed",
+ event->connect.status);
+ if (event->connect.status == 0) {
+
+ blufi_env.is_connected = true;
+ blufi_env.recv_seq = blufi_env.send_seq = 0;
+ btc_msg_t msg;
+ esp_blufi_cb_param_t param;
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_BLE_CONNECT;
+
+ rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
+ assert(rc == 0);
+ memcpy(param.connect.remote_bda, desc.peer_id_addr.val, ESP_BLUFI_BD_ADDR_LEN);
+
+ param.connect.conn_id = event->connect.conn_handle;
+ /* save connection handle */
+ blufi_env.conn_id = event->connect.conn_handle;
+ btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
+ }
+ if (event->connect.status != 0) {
+ /* Connection failed; resume advertising. */
+ esp_blufi_adv_start();
+ }
+ return 0;
+ case BLE_GAP_EVENT_DISCONNECT:
+ ESP_LOGI(TAG, "disconnect; reason=%d", event->disconnect.reason);
+ memcpy(blufi_env.remote_bda, event->disconnect.conn.peer_id_addr.val, ESP_BLUFI_BD_ADDR_LEN);
+ blufi_env.is_connected = false;
+ blufi_env.recv_seq = blufi_env.send_seq = 0;
+ blufi_env.sec_mode = 0x0;
+ blufi_env.offset = 0;
+
+ if (blufi_env.aggr_buf != NULL) {
+ osi_free(blufi_env.aggr_buf);
+ blufi_env.aggr_buf = NULL;
+ }
+
+ btc_msg_t msg;
+ esp_blufi_cb_param_t param;
+
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_BLE_DISCONNECT;
+ memcpy(param.disconnect.remote_bda, event->disconnect.conn.peer_id_addr.val, ESP_BLUFI_BD_ADDR_LEN);
+ btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
+
+ return 0;
+ case BLE_GAP_EVENT_CONN_UPDATE:
+ /* The central has updated the connection parameters. */
+ ESP_LOGI(TAG, "connection updated; status=%d",
+ event->conn_update.status);
+ return 0;
+
+ case BLE_GAP_EVENT_ADV_COMPLETE:
+ ESP_LOGI(TAG, "advertise complete; reason=%d",
+ event->adv_complete.reason);
+ esp_blufi_adv_start();
+ return 0;
+
+ case BLE_GAP_EVENT_SUBSCRIBE:
+ ESP_LOGI(TAG, "subscribe event; conn_handle=%d attr_handle=%d "
+ "reason=%d prevn=%d curn=%d previ=%d curi=%d\n",
+ event->subscribe.conn_handle,
+ event->subscribe.attr_handle,
+ event->subscribe.reason,
+ event->subscribe.prev_notify,
+ event->subscribe.cur_notify,
+ event->subscribe.prev_indicate,
+ event->subscribe.cur_indicate);
+ return 0;
+
+ case BLE_GAP_EVENT_MTU:
+ ESP_LOGI(TAG, "mtu update event; conn_handle=%d cid=%d mtu=%d",
+ event->mtu.conn_handle,
+ event->mtu.channel_id,
+ event->mtu.value);
+ blufi_env.frag_size = (event->mtu.value < BLUFI_MAX_DATA_LEN ? event->mtu.value : BLUFI_MAX_DATA_LEN) - BLUFI_MTU_RESERVED_SIZE;
+ return 0;
+
+ }
+ return 0;
+}
+
+void esp_blufi_adv_start(void)
+{
+ int rc;
+
+ rc = ble_hs_util_ensure_addr(0);
+ assert(rc == 0);
+
+ /* Figure out address to use while advertising (no privacy for now) */
+ rc = ble_hs_id_infer_auto(0, &own_addr_type);
+ if (rc != 0) {
+ ESP_LOGI(TAG, "error determining address type; rc=%d ", rc);
+ return;
+ }
+
+ /* Printing ADDR */
+ uint8_t addr_val[6] = {0};
+ rc = ble_hs_id_copy_addr(own_addr_type, addr_val, NULL);
+
+ /* Begin advertising. */
+ struct ble_gap_adv_params adv_params;
+ struct ble_hs_adv_fields fields;
+ const char *name;
+
+ /**
+ * Set the advertisement data included in our advertisements:
+ * o Flags (indicates advertisement type and other general info).
+ * o Advertising tx power.
+ * o Device name.
+ * o 16-bit service UUIDs (alert notifications).
+ */
+
+ memset(&fields, 0, sizeof fields);
+
+ /* Advertise two flags:
+ * o Discoverability in forthcoming advertisement (general)
+ * o BLE-only (BR/EDR unsupported).
+ */
+ fields.flags = BLE_HS_ADV_F_DISC_GEN |
+ BLE_HS_ADV_F_BREDR_UNSUP;
+
+ /* Indicate that the TX power level field should be included; have the
+ * stack fill this value automatically. This is done by assigning the
+ * special value BLE_HS_ADV_TX_PWR_LVL_AUTO.
+ */
+ fields.tx_pwr_lvl_is_present = 1;
+ fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
+
+ name = ble_svc_gap_device_name();
+ fields.name = (uint8_t *)name;
+ fields.name_len = strlen(name);
+ fields.name_is_complete = 1;
+
+ fields.uuids16 = (ble_uuid16_t[]) {
+ BLE_UUID16_INIT(BLUFI_APP_UUID)
+ };
+ fields.num_uuids16 = 1;
+ fields.uuids16_is_complete = 1;
+ rc = ble_gap_adv_set_fields(&fields);
+ if (rc != 0) {
+ ESP_LOGE(TAG, "error setting advertisement data; rc=%d", rc);
+ return;
+ }
+
+ /* Begin advertising. */
+ memset(&adv_params, 0, sizeof adv_params);
+ adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
+ adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
+ rc = ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER,
+ &adv_params, esp_blufi_gap_event, NULL);
+ if (rc != 0) {
+ ESP_LOGE(TAG, "error enabling advertisement; rc=%d", rc);
+ return;
+ }
+}
+
+uint8_t esp_blufi_init(void)
+{
+ blufi_env.enabled = true;
+ esp_blufi_cb_param_t param;
+ param.init_finish.state = ESP_BLUFI_INIT_OK;
+ btc_blufi_cb_to_app(ESP_BLUFI_EVENT_INIT_FINISH, &param);
+ return ESP_BLUFI_ERROR;
+}
+
+void esp_blufi_deinit(void)
+{
+ blufi_env.enabled = false;
+ btc_msg_t msg;
+ esp_blufi_cb_param_t param;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_DEINIT_FINISH;
+ param.deinit_finish.state = ESP_BLUFI_DEINIT_OK;
+ btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
+}
+
+void esp_blufi_send_notify(void *arg)
+{
+ struct pkt_info *pkts = (struct pkt_info *) arg;
+ struct os_mbuf *om;
+ om = ble_hs_mbuf_from_flat(pkts->pkt, pkts->pkt_len);
+ if (om == NULL) {
+ ESP_LOGE(TAG, "Error in allocating memory");
+ return;
+ }
+ int rc = 0;
+ rc = ble_gatts_notify_custom(blufi_env.conn_id, gatt_values[1].val_handle, om);
+ if (rc != 0) {
+ ESP_LOGE(TAG, "Error in sending notification");
+ }
+}
+
+void esp_blufi_disconnect(void)
+{
+ ble_gap_terminate(blufi_env.conn_id, BLE_ERR_REM_USER_CONN_TERM);
+}
+
+void esp_blufi_adv_stop(void) {}
+
+void esp_blufi_send_encap(void *arg)
+{
+ struct blufi_hdr *hdr = (struct blufi_hdr *)arg;
+ if (blufi_env.is_connected == false) {
+ BTC_TRACE_WARNING("%s ble connection is broken\n", __func__);
+ return;
+ }
+ btc_blufi_send_notify((uint8_t *)hdr,
+ ((hdr->fc & BLUFI_FC_CHECK) ?
+ hdr->data_len + sizeof(struct blufi_hdr) + 2 :
+ hdr->data_len + sizeof(struct blufi_hdr)));
+}
+
+void esp_blufi_btc_init(void)
+{
+ int rc;
+ rc = btc_init();
+ assert(rc == 0);
+}
+
+void esp_blufi_btc_deinit(void)
+{
+ btc_deinit();
+}
+
+int esp_blufi_handle_gap_events(struct ble_gap_event *event, void *arg)
+{
+ struct ble_gap_conn_desc desc;
+ int rc;
+
+ if (event != NULL) {
+ switch (event->type) {
+ case BLE_GAP_EVENT_CONNECT:
+ if (event->connect.status == 0) {
+ btc_msg_t msg;
+ esp_blufi_cb_param_t param;
+
+ blufi_env.is_connected = true;
+ blufi_env.recv_seq = blufi_env.send_seq = 0;
+ blufi_env.conn_id = event->connect.conn_handle;
+
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_BLE_CONNECT;
+
+ rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
+ assert(rc == 0);
+ memcpy(param.connect.remote_bda, desc.peer_id_addr.val, ESP_BLUFI_BD_ADDR_LEN);
+
+ param.connect.conn_id = event->connect.conn_handle;
+ btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
+ }
+ return 0;
+ case BLE_GAP_EVENT_DISCONNECT: {
+ btc_msg_t msg;
+ esp_blufi_cb_param_t param;
+
+ blufi_env.is_connected = false;
+ blufi_env.recv_seq = blufi_env.send_seq = 0;
+ blufi_env.sec_mode = 0x0;
+ blufi_env.offset = 0;
+
+ memcpy(blufi_env.remote_bda,
+ event->disconnect.conn.peer_id_addr.val,
+ ESP_BLUFI_BD_ADDR_LEN);
+
+ if (blufi_env.aggr_buf != NULL) {
+ osi_free(blufi_env.aggr_buf);
+ blufi_env.aggr_buf = NULL;
+ }
+
+ msg.sig = BTC_SIG_API_CB;
+ msg.pid = BTC_PID_BLUFI;
+ msg.act = ESP_BLUFI_EVENT_BLE_DISCONNECT;
+ memcpy(param.disconnect.remote_bda,
+ event->disconnect.conn.peer_id_addr.val,
+ ESP_BLUFI_BD_ADDR_LEN);
+ btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
+ return 0;
+ }
+
+ case BLE_GAP_EVENT_MTU:
+ blufi_env.frag_size = (event->mtu.value < BLUFI_MAX_DATA_LEN ? event->mtu.value :
+ BLUFI_MAX_DATA_LEN) - BLUFI_MTU_RESERVED_SIZE;
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/lib/bt/common/btc/profile/esp/include/btc_blufi_prf.h b/lib/bt/common/btc/profile/esp/include/btc_blufi_prf.h
new file mode 100644
index 00000000..347c4428
--- /dev/null
+++ b/lib/bt/common/btc/profile/esp/include/btc_blufi_prf.h
@@ -0,0 +1,103 @@
+/*
+ * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __BTC_BLUFI_PRF_H__
+#define __BTC_BLUFI_PRF_H__
+
+#include "blufi_int.h"
+#include "btc/btc_task.h"
+#include "esp_blufi_api.h"
+#include "esp_err.h"
+
+#ifdef CONFIG_BT_BLUEDROID_ENABLED
+#include "stack/gatt_api.h"
+#define ESP_BLUFI_ERROR GATT_ERROR
+#define ESP_BLUFI_SUCCESS GATT_SUCCESS
+#else
+#define ESP_BLUFI_ERROR 0x85
+#define ESP_BLUFI_SUCCESS 0x00
+#endif
+
+#define BT_BD_ADDR_STR "%02x:%02x:%02x:%02x:%02x:%02x"
+#define BT_BD_ADDR_HEX(addr) addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]
+
+#define GATT_UUID_CHAR_CLIENT_CONFIG 0x2902 /* Client Characteristic Configuration */
+//define the blufi serivce uuid
+#define BLUFI_SERVICE_UUID 0xFFFF
+//define the blufi Char uuid (PHONE to ESP32)
+#define BLUFI_CHAR_P2E_UUID 0xFF01
+//define the blufi Char uuid (ESP32 to PHONE)
+#define BLUFI_CHAR_E2P_UUID 0xFF02
+//define the blufi Descriptor uuid (ESP32 to PHONE)
+#define BLUFI_DESCR_E2P_UUID GATT_UUID_CHAR_CLIENT_CONFIG
+//define the blufi APP ID
+#define BLUFI_APP_UUID 0xFFFF
+
+#define BLUFI_HDL_NUM 6
+
+struct pkt_info{
+ uint8_t *pkt;
+ int pkt_len;
+};
+
+static const tBT_UUID blufi_srvc_uuid = {LEN_UUID_16, {BLUFI_SERVICE_UUID}};
+static const tBT_UUID blufi_char_uuid_p2e = {LEN_UUID_16, {BLUFI_CHAR_P2E_UUID}};
+static const tBT_UUID blufi_char_uuid_e2p = {LEN_UUID_16, {BLUFI_CHAR_E2P_UUID}};
+static const tBT_UUID blufi_descr_uuid_e2p = {LEN_UUID_16, {BLUFI_DESCR_E2P_UUID}};
+static const tBT_UUID blufi_app_uuid = {LEN_UUID_16, {BLUFI_APP_UUID}};
+
+typedef enum {
+ BTC_BLUFI_ACT_INIT = 0,
+ BTC_BLUFI_ACT_DEINIT,
+ BTC_BLUFI_ACT_SEND_CFG_REPORT,
+ BTC_BLUFI_ACT_SEND_WIFI_LIST,
+ BTC_BLUFI_ACT_SEND_ERR_INFO,
+ BTC_BLUFI_ACT_SEND_CUSTOM_DATA,
+} btc_blufi_act_t;
+
+typedef union {
+ struct blufi_cfg_report {
+ wifi_mode_t opmode;
+ esp_blufi_sta_conn_state_t sta_conn_state;
+ uint8_t softap_conn_num;
+ esp_blufi_extra_info_t *extra_info;
+ int extra_info_len;
+ } wifi_conn_report;
+ /*
+ BTC_BLUFI_ACT_SEND_WIFI_LIST
+ */
+ struct blufi_wifi_list {
+ uint16_t apCount;
+ esp_blufi_ap_record_t *list;
+ } wifi_list;
+ /*
+ BTC_BLUFI_ACT_SEND_ERR_INFO
+ */
+ struct blufi_error_infor {
+ esp_blufi_error_state_t state;
+ } blufi_err_infor;
+ /*
+ BTC_BLUFI_ACT_SEND_CUSTOM_DATA
+ */
+ struct blufi_custom_data {
+ uint8_t *data;
+ uint32_t data_len;
+ } custom_data;
+} btc_blufi_args_t;
+
+void btc_blufi_cb_to_app(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param);
+void btc_blufi_cb_handler(btc_msg_t *msg);
+void btc_blufi_call_handler(btc_msg_t *msg);
+void btc_blufi_set_callbacks(esp_blufi_callbacks_t *callbacks);
+
+void btc_blufi_recv_handler(uint8_t *data, int len);
+void btc_blufi_send_notify(uint8_t *pkt, int pkt_len);
+void btc_blufi_call_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src);
+void btc_blufi_call_deep_free(btc_msg_t *msg);
+
+uint16_t btc_blufi_get_version(void);
+
+#endif /* __BTC_BLUFI_PRF_H__ */
diff --git a/lib/bt/common/include/bt_common.h b/lib/bt/common/include/bt_common.h
new file mode 100644
index 00000000..134cf33c
--- /dev/null
+++ b/lib/bt/common/include/bt_common.h
@@ -0,0 +1,215 @@
+/*
+ * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef _BT_COMMON_H_
+#define _BT_COMMON_H_
+
+#include <assert.h>
+#include <stdbool.h>
+#include "bt_user_config.h"
+#include "esp_log.h"
+
+#ifndef FALSE
+#define FALSE false
+#endif
+
+#ifndef TRUE
+#define TRUE true
+#endif
+
+
+#if (UC_BT_BLUFI_ENABLE)
+#define BLUFI_INCLUDED TRUE
+#else
+#define BLUFI_INCLUDED FALSE
+#endif
+
+#ifdef CONFIG_BT_BLUEDROID_ENABLED
+#include "esp_bt_defs.h"
+#include "esp_bt_main.h"
+#include "esp_gatt_defs.h"
+#define ESP_BLE_HOST_STATUS_ENABLED ESP_BLUEDROID_STATUS_ENABLED
+#define ESP_BLE_HOST_STATUS_CHECK(status) ESP_BLUEDROID_STATUS_CHECK(status)
+#else
+#define ESP_BLE_HOST_STATUS_ENABLED 0
+#define ESP_BLE_HOST_STATUS_CHECK(status) do {} while (0)
+#endif
+
+#ifndef BT_QUEUE_CONGEST_SIZE
+#define BT_QUEUE_CONGEST_SIZE 40
+#endif
+
+#define BTC_INITIAL_TRACE_LEVEL UC_BT_LOG_BTC_TRACE_LEVEL
+#define OSI_INITIAL_TRACE_LEVEL UC_BT_LOG_OSI_TRACE_LEVEL
+#define BLUFI_INITIAL_TRACE_LEVEL UC_BT_LOG_BLUFI_TRACE_LEVEL
+
+#if UC_BT_BLE_DYNAMIC_ENV_MEMORY
+#define BT_BLE_DYNAMIC_ENV_MEMORY TRUE
+#define BTC_DYNAMIC_MEMORY TRUE
+#else
+#define BT_BLE_DYNAMIC_ENV_MEMORY FALSE
+#define BTC_DYNAMIC_MEMORY FALSE
+#endif
+
+#if UC_BT_BLUEDROID_MEM_DEBUG
+#define HEAP_MEMORY_DEBUG TRUE
+#else
+#define HEAP_MEMORY_DEBUG FALSE
+#endif
+
+#ifndef BT_BLE_DYNAMIC_ENV_MEMORY
+#define BT_BLE_DYNAMIC_ENV_MEMORY FALSE
+#endif
+
+/* OS Configuration from User config (eg: sdkconfig) */
+#define TASK_PINNED_TO_CORE UC_TASK_PINNED_TO_CORE
+#define BT_TASK_MAX_PRIORITIES configMAX_PRIORITIES
+#define BT_BTC_TASK_STACK_SIZE UC_BTC_TASK_STACK_SIZE
+
+/* Define trace levels */
+#define BT_TRACE_LEVEL_NONE UC_TRACE_LEVEL_NONE /* No trace messages to be generated */
+#define BT_TRACE_LEVEL_ERROR UC_TRACE_LEVEL_ERROR /* Error condition trace messages */
+#define BT_TRACE_LEVEL_WARNING UC_TRACE_LEVEL_WARNING /* Warning condition trace messages */
+#define BT_TRACE_LEVEL_API UC_TRACE_LEVEL_API /* API traces */
+#define BT_TRACE_LEVEL_EVENT UC_TRACE_LEVEL_EVENT /* Debug messages for events */
+#define BT_TRACE_LEVEL_DEBUG UC_TRACE_LEVEL_DEBUG /* Full debug messages */
+#define BT_TRACE_LEVEL_VERBOSE UC_TRACE_LEVEL_VERBOSE /* Verbose debug messages */
+
+#define MAX_TRACE_LEVEL UC_TRACE_LEVEL_VERBOSE
+
+#ifndef LOG_LOCAL_LEVEL
+#ifndef BOOTLOADER_BUILD
+#define LOG_LOCAL_LEVEL UC_LOG_DEFAULT_LEVEL
+#else
+#define LOG_LOCAL_LEVEL UC_BOOTLOADER_LOG_LEVEL
+#endif
+#endif
+
+// Mapping between ESP_LOG_LEVEL and BT_TRACE_LEVEL
+#if (LOG_LOCAL_LEVEL >= 4)
+#define LOG_LOCAL_LEVEL_MAPPING (LOG_LOCAL_LEVEL+1)
+#else
+#define LOG_LOCAL_LEVEL_MAPPING LOG_LOCAL_LEVEL
+#endif
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+#define BT_LOG_LEVEL_CHECK(LAYER, LEVEL) (MAX(LAYER##_INITIAL_TRACE_LEVEL, LOG_LOCAL_LEVEL_MAPPING) >= BT_TRACE_LEVEL_##LEVEL)
+
+#define BT_PRINT_E(tag, format, ...) {esp_log_write(ESP_LOG_ERROR, tag, LOG_FORMAT(E, format), esp_log_timestamp(), tag, ##__VA_ARGS__); }
+#define BT_PRINT_W(tag, format, ...) {esp_log_write(ESP_LOG_WARN, tag, LOG_FORMAT(W, format), esp_log_timestamp(), tag, ##__VA_ARGS__); }
+#define BT_PRINT_I(tag, format, ...) {esp_log_write(ESP_LOG_INFO, tag, LOG_FORMAT(I, format), esp_log_timestamp(), tag, ##__VA_ARGS__); }
+#define BT_PRINT_D(tag, format, ...) {esp_log_write(ESP_LOG_DEBUG, tag, LOG_FORMAT(D, format), esp_log_timestamp(), tag, ##__VA_ARGS__); }
+#define BT_PRINT_V(tag, format, ...) {esp_log_write(ESP_LOG_VERBOSE, tag, LOG_FORMAT(V, format), esp_log_timestamp(), tag, ##__VA_ARGS__); }
+
+
+#if !UC_BT_STACK_NO_LOG
+/* define traces for BTC */
+#define BTC_TRACE_ERROR(fmt, args...) {if (BTC_INITIAL_TRACE_LEVEL >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(BTC, ERROR)) BT_PRINT_E("BT_BTC", fmt, ## args);}
+#define BTC_TRACE_WARNING(fmt, args...) {if (BTC_INITIAL_TRACE_LEVEL >= BT_TRACE_LEVEL_WARNING && BT_LOG_LEVEL_CHECK(BTC, WARNING)) BT_PRINT_W("BT_BTC", fmt, ## args);}
+#define BTC_TRACE_API(fmt, args...) {if (BTC_INITIAL_TRACE_LEVEL >= BT_TRACE_LEVEL_API && BT_LOG_LEVEL_CHECK(BTC,API)) BT_PRINT_I("BT_BTC", fmt, ## args);}
+#define BTC_TRACE_EVENT(fmt, args...) {if (BTC_INITIAL_TRACE_LEVEL >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(BTC,EVENT)) BT_PRINT_D("BT_BTC", fmt, ## args);}
+#define BTC_TRACE_DEBUG(fmt, args...) {if (BTC_INITIAL_TRACE_LEVEL >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(BTC,DEBUG)) BT_PRINT_D("BT_BTC", fmt, ## args);}
+#define BTC_TRACE_VERBOSE(fmt, args...) {if (BTC_INITIAL_TRACE_LEVEL >= BT_TRACE_LEVEL_VERBOSE && BT_LOG_LEVEL_CHECK(BTC,VERBOSE)) BT_PRINT_V("BT_BTC", fmt, ## args);}
+
+/* define traces for OSI */
+#define OSI_TRACE_ERROR(fmt, args...) {if (OSI_INITIAL_TRACE_LEVEL >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(OSI, ERROR)) BT_PRINT_E("BT_OSI", fmt, ## args);}
+#define OSI_TRACE_WARNING(fmt, args...) {if (OSI_INITIAL_TRACE_LEVEL >= BT_TRACE_LEVEL_WARNING && BT_LOG_LEVEL_CHECK(OSI, WARNING)) BT_PRINT_W("BT_OSI", fmt, ## args);}
+#define OSI_TRACE_API(fmt, args...) {if (OSI_INITIAL_TRACE_LEVEL >= BT_TRACE_LEVEL_API && BT_LOG_LEVEL_CHECK(OSI,API)) BT_PRINT_I("BT_OSI", fmt, ## args);}
+#define OSI_TRACE_EVENT(fmt, args...) {if (OSI_INITIAL_TRACE_LEVEL >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(OSI,EVENT)) BT_PRINT_D("BT_OSI", fmt, ## args);}
+#define OSI_TRACE_DEBUG(fmt, args...) {if (OSI_INITIAL_TRACE_LEVEL >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(OSI,DEBUG)) BT_PRINT_D("BT_OSI", fmt, ## args);}
+#define OSI_TRACE_VERBOSE(fmt, args...) {if (OSI_INITIAL_TRACE_LEVEL >= BT_TRACE_LEVEL_VERBOSE && BT_LOG_LEVEL_CHECK(OSI,VERBOSE)) BT_PRINT_V("BT_OSI", fmt, ## args);}
+
+/* define traces for BLUFI */
+#define BLUFI_TRACE_ERROR(fmt, args...) {if (BLUFI_INITIAL_TRACE_LEVEL >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(BLUFI, ERROR)) BT_PRINT_E("BT_BLUFI", fmt, ## args);}
+#define BLUFI_TRACE_WARNING(fmt, args...) {if (BLUFI_INITIAL_TRACE_LEVEL >= BT_TRACE_LEVEL_WARNING && BT_LOG_LEVEL_CHECK(BLUFI, WARNING)) BT_PRINT_W("BT_BLUFI", fmt, ## args);}
+#define BLUFI_TRACE_API(fmt, args...) {if (BLUFI_INITIAL_TRACE_LEVEL >= BT_TRACE_LEVEL_API && BT_LOG_LEVEL_CHECK(BLUFI,API)) BT_PRINT_I("BT_BLUFI", fmt, ## args);}
+#define BLUFI_TRACE_EVENT(fmt, args...) {if (BLUFI_INITIAL_TRACE_LEVEL >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(BLUFI,EVENT)) BT_PRINT_D("BT_BLUFI", fmt, ## args);}
+#define BLUFI_TRACE_DEBUG(fmt, args...) {if (BLUFI_INITIAL_TRACE_LEVEL >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(BLUFI,DEBUG)) BT_PRINT_D("BT_BLUFI", fmt, ## args);}
+#define BLUFI_TRACE_VERBOSE(fmt, args...) {if (BLUFI_INITIAL_TRACE_LEVEL >= BT_TRACE_LEVEL_VERBOSE && BT_LOG_LEVEL_CHECK(BLUFI,VERBOSE)) BT_PRINT_V("BT_BLUFI", fmt, ## args);}
+
+#else
+
+/* define traces for BTC */
+#define BTC_TRACE_ERROR(fmt, args...)
+#define BTC_TRACE_WARNING(fmt, args...)
+#define BTC_TRACE_API(fmt, args...)
+#define BTC_TRACE_EVENT(fmt, args...)
+#define BTC_TRACE_DEBUG(fmt, args...)
+#define BTC_TRACE_VERBOSE(fmt, args...)
+
+/* define traces for OSI */
+#define OSI_TRACE_ERROR(fmt, args...)
+#define OSI_TRACE_WARNING(fmt, args...)
+#define OSI_TRACE_API(fmt, args...)
+#define OSI_TRACE_EVENT(fmt, args...)
+#define OSI_TRACE_DEBUG(fmt, args...)
+#define OSI_TRACE_VERBOSE(fmt, args...)
+
+/* define traces for BLUFI */
+#define BLUFI_TRACE_ERROR(fmt, args...)
+#define BLUFI_TRACE_WARNING(fmt, args...)
+#define BLUFI_TRACE_API(fmt, args...)
+#define BLUFI_TRACE_EVENT(fmt, args...)
+#define BLUFI_TRACE_DEBUG(fmt, args...)
+#define BLUFI_TRACE_VERBOSE(fmt, args...)
+
+#endif
+
+/** Bluetooth Error Status */
+/** We need to build on this */
+
+/* relate to ESP_BT_STATUS_xxx in esp_bt_defs.h */
+typedef enum {
+ BT_STATUS_SUCCESS = 0,
+ BT_STATUS_FAIL,
+ BT_STATUS_NOT_READY,
+ BT_STATUS_NOMEM,
+ BT_STATUS_BUSY,
+ BT_STATUS_DONE, /* request already completed */
+ BT_STATUS_UNSUPPORTED,
+ BT_STATUS_PARM_INVALID,
+ BT_STATUS_UNHANDLED,
+ BT_STATUS_AUTH_FAILURE,
+ BT_STATUS_RMT_DEV_DOWN,
+ BT_STATUS_AUTH_REJECTED,
+ BT_STATUS_INVALID_STATIC_RAND_ADDR,
+ BT_STATUS_PENDING,
+ BT_STATUS_UNACCEPT_CONN_INTERVAL,
+ BT_STATUS_PARAM_OUT_OF_RANGE,
+ BT_STATUS_TIMEOUT,
+ BT_STATUS_MEMORY_FULL,
+ BT_STATUS_EIR_TOO_LARGE,
+} bt_status_t;
+
+typedef uint8_t UINT8;
+typedef uint16_t UINT16;
+typedef uint32_t UINT32;
+typedef uint64_t UINT64;
+typedef bool BOOLEAN;
+/* Maximum UUID size - 16 bytes, and structure to hold any type of UUID. */
+#define MAX_UUID_SIZE 16
+
+typedef struct {
+#define LEN_UUID_16 2
+#define LEN_UUID_32 4
+#define LEN_UUID_128 16
+
+ UINT16 len;
+
+ union {
+ UINT16 uuid16;
+ UINT32 uuid32;
+ UINT8 uuid128[MAX_UUID_SIZE];
+ } uu;
+
+} tBT_UUID;
+
+/* Common Bluetooth field definitions */
+#define BD_ADDR_LEN 6 /* Device address length */
+typedef UINT8 BD_ADDR[BD_ADDR_LEN]; /* Device address */
+
+#endif /* _BT_COMMON_H_ */
diff --git a/lib/bt/common/include/bt_user_config.h b/lib/bt/common/include/bt_user_config.h
new file mode 100644
index 00000000..738f4f2c
--- /dev/null
+++ b/lib/bt/common/include/bt_user_config.h
@@ -0,0 +1,101 @@
+/*
+ * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __BT_USER_CONFIG_H__
+#define __BT_USER_CONFIG_H__
+#include "sdkconfig.h"
+
+#define UC_TRACE_LEVEL_NONE 0 /* No trace messages to be generated */
+#define UC_TRACE_LEVEL_ERROR 1 /* Error condition trace messages */
+#define UC_TRACE_LEVEL_WARNING 2 /* Warning condition trace messages */
+#define UC_TRACE_LEVEL_API 3 /* API traces */
+#define UC_TRACE_LEVEL_EVENT 4 /* Debug messages for events */
+#define UC_TRACE_LEVEL_DEBUG 5 /* Full debug messages */
+#define UC_TRACE_LEVEL_VERBOSE 6 /* Verbose debug messages */
+
+//DYNAMIC ENV ALLOCATOR
+#ifdef CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY
+#define UC_BT_BLE_DYNAMIC_ENV_MEMORY CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY
+#else
+#define UC_BT_BLE_DYNAMIC_ENV_MEMORY FALSE
+#endif
+
+#ifdef CONFIG_BT_STACK_NO_LOG
+#define UC_BT_STACK_NO_LOG CONFIG_BT_STACK_NO_LOG
+#else
+#define UC_BT_STACK_NO_LOG FALSE
+#endif
+
+#ifdef CONFIG_BT_CONTROLLER_ENABLED
+#define UC_BT_CONTROLLER_INCLUDED TRUE
+#else
+#define UC_BT_CONTROLLER_INCLUDED FALSE
+#endif
+
+/**********************************************************
+ * Thread/Task reference
+ **********************************************************/
+#ifdef CONFIG_BT_BLUEDROID_PINNED_TO_CORE
+#define UC_TASK_PINNED_TO_CORE (CONFIG_BT_BLUEDROID_PINNED_TO_CORE < portNUM_PROCESSORS ? CONFIG_BT_BLUEDROID_PINNED_TO_CORE : tskNO_AFFINITY)
+#else
+#define UC_TASK_PINNED_TO_CORE (0)
+#endif
+
+#ifdef CONFIG_BT_BTC_TASK_STACK_SIZE
+#define UC_BTC_TASK_STACK_SIZE CONFIG_BT_BTC_TASK_STACK_SIZE
+#else
+#define UC_BTC_TASK_STACK_SIZE 4096
+#endif
+
+/**********************************************************
+ * Trace reference
+ **********************************************************/
+
+#ifdef CONFIG_LOG_DEFAULT_LEVEL
+#define UC_LOG_DEFAULT_LEVEL CONFIG_LOG_DEFAULT_LEVEL
+#else
+#define UC_LOG_DEFAULT_LEVEL 3
+#endif
+
+#ifdef CONFIG_BOOTLOADER_LOG_LEVEL
+#define UC_BOOTLOADER_LOG_LEVEL CONFIG_BOOTLOADER_LOG_LEVEL
+#else
+#define UC_BOOTLOADER_LOG_LEVEL 3
+#endif
+
+#ifdef CONFIG_BT_LOG_BTC_TRACE_LEVEL
+#define UC_BT_LOG_BTC_TRACE_LEVEL CONFIG_BT_LOG_BTC_TRACE_LEVEL
+#else
+#define UC_BT_LOG_BTC_TRACE_LEVEL UC_TRACE_LEVEL_WARNING
+#endif
+
+#ifdef CONFIG_BT_LOG_OSI_TRACE_LEVEL
+#define UC_BT_LOG_OSI_TRACE_LEVEL CONFIG_BT_LOG_OSI_TRACE_LEVEL
+#else
+#define UC_BT_LOG_OSI_TRACE_LEVEL UC_TRACE_LEVEL_WARNING
+#endif
+
+#ifdef CONFIG_BT_LOG_BLUFI_TRACE_LEVEL
+#define UC_BT_LOG_BLUFI_TRACE_LEVEL CONFIG_BT_LOG_BLUFI_TRACE_LEVEL
+#else
+#define UC_BT_LOG_BLUFI_TRACE_LEVEL UC_TRACE_LEVEL_WARNING
+#endif
+
+//BLUFI
+#if defined(CONFIG_BT_BLE_BLUFI_ENABLE) || defined(CONFIG_BT_NIMBLE_BLUFI_ENABLE)
+#define UC_BT_BLUFI_ENABLE TRUE
+#else
+#define UC_BT_BLUFI_ENABLE FALSE
+#endif
+
+//MEMORY DEBUG
+#ifdef CONFIG_BT_BLUEDROID_MEM_DEBUG
+#define UC_BT_BLUEDROID_MEM_DEBUG TRUE
+#else
+#define UC_BT_BLUEDROID_MEM_DEBUG FALSE
+#endif
+
+#endif /* __BT_USER_CONFIG_H__ */
diff --git a/lib/bt/common/osi/alarm.c b/lib/bt/common/osi/alarm.c
new file mode 100644
index 00000000..464bbc2c
--- /dev/null
+++ b/lib/bt/common/osi/alarm.c
@@ -0,0 +1,331 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include "osi/alarm.h"
+#include "osi/allocator.h"
+#include "osi/list.h"
+#include "esp_timer.h"
+#include "btc/btc_task.h"
+#include "btc/btc_alarm.h"
+#include "osi/mutex.h"
+#include "bt_common.h"
+
+typedef struct alarm_t {
+ /* timer id point to here */
+ esp_timer_handle_t alarm_hdl;
+ osi_alarm_callback_t cb;
+ void *cb_data;
+ int64_t deadline_us;
+} osi_alarm_t;
+
+enum {
+ ALARM_STATE_IDLE,
+ ALARM_STATE_OPEN,
+};
+
+static osi_mutex_t alarm_mutex;
+static int alarm_state;
+
+#if (BT_BLE_DYNAMIC_ENV_MEMORY == FALSE)
+static struct alarm_t alarm_cbs[ALARM_CBS_NUM];
+#else
+static struct alarm_t *alarm_cbs;
+#endif
+
+static osi_alarm_err_t alarm_free(osi_alarm_t *alarm);
+static osi_alarm_err_t alarm_set(osi_alarm_t *alarm, period_ms_t timeout, bool is_periodic);
+
+int osi_alarm_create_mux(void)
+{
+ if (alarm_state != ALARM_STATE_IDLE) {
+ OSI_TRACE_WARNING("%s, invalid state %d\n", __func__, alarm_state);
+ return -1;
+ }
+ osi_mutex_new(&alarm_mutex);
+ return 0;
+}
+
+int osi_alarm_delete_mux(void)
+{
+ if (alarm_state != ALARM_STATE_IDLE) {
+ OSI_TRACE_WARNING("%s, invalid state %d\n", __func__, alarm_state);
+ return -1;
+ }
+ osi_mutex_free(&alarm_mutex);
+ return 0;
+}
+
+void osi_alarm_init(void)
+{
+ assert(alarm_mutex != NULL);
+
+ osi_mutex_lock(&alarm_mutex, OSI_MUTEX_MAX_TIMEOUT);
+ if (alarm_state != ALARM_STATE_IDLE) {
+ OSI_TRACE_WARNING("%s, invalid state %d\n", __func__, alarm_state);
+ goto end;
+ }
+#if (BT_BLE_DYNAMIC_ENV_MEMORY == TRUE)
+ if ((alarm_cbs = (osi_alarm_t *)osi_malloc(sizeof(osi_alarm_t) * ALARM_CBS_NUM)) == NULL) {
+ OSI_TRACE_ERROR("%s, malloc failed\n", __func__);
+ goto end;
+ }
+#endif
+
+ memset(alarm_cbs, 0x00, sizeof(osi_alarm_t) * ALARM_CBS_NUM);
+ alarm_state = ALARM_STATE_OPEN;
+
+end:
+ osi_mutex_unlock(&alarm_mutex);
+}
+
+void osi_alarm_deinit(void)
+{
+ assert(alarm_mutex != NULL);
+
+ osi_mutex_lock(&alarm_mutex, OSI_MUTEX_MAX_TIMEOUT);
+ if (alarm_state != ALARM_STATE_OPEN) {
+ OSI_TRACE_WARNING("%s, invalid state %d\n", __func__, alarm_state);
+ goto end;
+ }
+
+ for (int i = 0; i < ALARM_CBS_NUM; i++) {
+ if (alarm_cbs[i].alarm_hdl != NULL) {
+ alarm_free(&alarm_cbs[i]);
+ }
+ }
+
+#if (BT_BLE_DYNAMIC_ENV_MEMORY == TRUE)
+ osi_free(alarm_cbs);
+ alarm_cbs = NULL;
+#endif
+
+ alarm_state = ALARM_STATE_IDLE;
+
+end:
+ osi_mutex_unlock(&alarm_mutex);
+}
+
+static struct alarm_t *alarm_cbs_lookfor_available(void)
+{
+ int i;
+
+ for (i = 0; i < ALARM_CBS_NUM; i++) {
+ if (alarm_cbs[i].alarm_hdl == NULL) { //available
+ OSI_TRACE_DEBUG("%s %d %p\n", __func__, i, &alarm_cbs[i]);
+ return &alarm_cbs[i];
+ }
+ }
+
+ return NULL;
+}
+
+static void alarm_cb_handler(struct alarm_t *alarm)
+{
+ OSI_TRACE_DEBUG("TimerID %p\n", alarm);
+ if (alarm_state != ALARM_STATE_OPEN) {
+ OSI_TRACE_WARNING("%s, invalid state %d\n", __func__, alarm_state);
+ return;
+ }
+ btc_msg_t msg = {0};
+ btc_alarm_args_t arg;
+ msg.sig = BTC_SIG_API_CALL;
+ msg.pid = BTC_PID_ALARM;
+ arg.cb = alarm->cb;
+ arg.cb_data = alarm->cb_data;
+ btc_transfer_context(&msg, &arg, sizeof(btc_alarm_args_t), NULL, NULL);
+}
+
+osi_alarm_t *osi_alarm_new(const char *alarm_name, osi_alarm_callback_t callback, void *data, period_ms_t timer_expire)
+{
+ assert(alarm_mutex != NULL);
+
+ struct alarm_t *timer_id = NULL;
+
+ osi_mutex_lock(&alarm_mutex, OSI_MUTEX_MAX_TIMEOUT);
+ if (alarm_state != ALARM_STATE_OPEN) {
+ OSI_TRACE_ERROR("%s, invalid state %d\n", __func__, alarm_state);
+ timer_id = NULL;
+ goto end;
+ }
+
+ timer_id = alarm_cbs_lookfor_available();
+
+ if (!timer_id) {
+ OSI_TRACE_ERROR("%s alarm_cbs exhausted\n", __func__);
+ timer_id = NULL;
+ goto end;
+ }
+
+ esp_timer_create_args_t tca = {0};
+ tca.callback = (esp_timer_cb_t)alarm_cb_handler;
+ tca.arg = timer_id;
+ tca.dispatch_method = ESP_TIMER_TASK;
+ tca.name = alarm_name;
+
+ timer_id->cb = callback;
+ timer_id->cb_data = data;
+ timer_id->deadline_us = 0;
+
+ esp_err_t stat = esp_timer_create(&tca, &timer_id->alarm_hdl);
+ if (stat != ESP_OK) {
+ OSI_TRACE_ERROR("%s failed to create timer, err 0x%x\n", __func__, stat);
+ timer_id = NULL;
+ goto end;
+ }
+
+end:
+ osi_mutex_unlock(&alarm_mutex);
+ return timer_id;
+}
+
+static osi_alarm_err_t alarm_free(osi_alarm_t *alarm)
+{
+ if (!alarm || alarm->alarm_hdl == NULL) {
+ OSI_TRACE_ERROR("%s null\n", __func__);
+ return OSI_ALARM_ERR_INVALID_ARG;
+ }
+ esp_timer_stop(alarm->alarm_hdl);
+ esp_err_t stat = esp_timer_delete(alarm->alarm_hdl);
+ if (stat != ESP_OK) {
+ OSI_TRACE_ERROR("%s failed to delete timer, err 0x%x\n", __func__, stat);
+ return OSI_ALARM_ERR_FAIL;
+ }
+
+ memset(alarm, 0, sizeof(osi_alarm_t));
+ return OSI_ALARM_ERR_PASS;
+}
+
+void osi_alarm_free(osi_alarm_t *alarm)
+{
+ assert(alarm_mutex != NULL);
+
+ osi_mutex_lock(&alarm_mutex, OSI_MUTEX_MAX_TIMEOUT);
+ if (alarm_state != ALARM_STATE_OPEN) {
+ OSI_TRACE_ERROR("%s, invalid state %d\n", __func__, alarm_state);
+ goto end;
+ }
+ alarm_free(alarm);
+
+end:
+ osi_mutex_unlock(&alarm_mutex);
+ return;
+}
+
+static osi_alarm_err_t alarm_set(osi_alarm_t *alarm, period_ms_t timeout, bool is_periodic)
+{
+ assert(alarm_mutex != NULL);
+
+ osi_alarm_err_t ret = OSI_ALARM_ERR_PASS;
+ osi_mutex_lock(&alarm_mutex, OSI_MUTEX_MAX_TIMEOUT);
+ if (alarm_state != ALARM_STATE_OPEN) {
+ OSI_TRACE_ERROR("%s, invalid state %d\n", __func__, alarm_state);
+ ret = OSI_ALARM_ERR_INVALID_STATE;
+ goto end;
+ }
+
+ if (!alarm || alarm->alarm_hdl == NULL) {
+ OSI_TRACE_ERROR("%s null\n", __func__);
+ ret = OSI_ALARM_ERR_INVALID_ARG;
+ goto end;
+ }
+
+ int64_t timeout_us = 1000 * (int64_t)timeout;
+ esp_err_t stat;
+ if (is_periodic) {
+ stat = esp_timer_start_periodic(alarm->alarm_hdl, (uint64_t)timeout_us);
+ } else {
+ stat = esp_timer_start_once(alarm->alarm_hdl, (uint64_t)timeout_us);
+ }
+ if (stat != ESP_OK) {
+ OSI_TRACE_ERROR("%s failed to start timer, err 0x%x\n", __func__, stat);
+ ret = OSI_ALARM_ERR_FAIL;
+ goto end;
+ }
+ alarm->deadline_us = is_periodic ? 0 : (timeout_us + esp_timer_get_time());
+
+end:
+ osi_mutex_unlock(&alarm_mutex);
+ return ret;
+}
+
+osi_alarm_err_t osi_alarm_set(osi_alarm_t *alarm, period_ms_t timeout)
+{
+ return alarm_set(alarm, timeout, FALSE);
+}
+
+osi_alarm_err_t osi_alarm_set_periodic(osi_alarm_t *alarm, period_ms_t period)
+{
+ return alarm_set(alarm, period, TRUE);
+}
+
+osi_alarm_err_t osi_alarm_cancel(osi_alarm_t *alarm)
+{
+ int ret = OSI_ALARM_ERR_PASS;
+ osi_mutex_lock(&alarm_mutex, OSI_MUTEX_MAX_TIMEOUT);
+ if (alarm_state != ALARM_STATE_OPEN) {
+ OSI_TRACE_ERROR("%s, invalid state %d\n", __func__, alarm_state);
+ ret = OSI_ALARM_ERR_INVALID_STATE;
+ goto end;
+ }
+
+ if (!alarm || alarm->alarm_hdl == NULL) {
+ OSI_TRACE_ERROR("%s null\n", __func__);
+ ret = OSI_ALARM_ERR_INVALID_ARG;
+ goto end;
+ }
+
+ esp_err_t stat = esp_timer_stop(alarm->alarm_hdl);
+ if (stat != ESP_OK) {
+ OSI_TRACE_DEBUG("%s failed to stop timer, err 0x%x\n", __func__, stat);
+ ret = OSI_ALARM_ERR_FAIL;
+ goto end;
+ }
+end:
+ osi_mutex_unlock(&alarm_mutex);
+ return ret;
+}
+
+period_ms_t osi_alarm_get_remaining_ms(const osi_alarm_t *alarm)
+{
+ assert(alarm_mutex != NULL);
+ int64_t dt_us = 0;
+
+ osi_mutex_lock(&alarm_mutex, OSI_MUTEX_MAX_TIMEOUT);
+ dt_us = alarm->deadline_us - esp_timer_get_time();
+ osi_mutex_unlock(&alarm_mutex);
+
+ return (dt_us > 0) ? (period_ms_t)(dt_us / 1000) : 0;
+}
+
+uint32_t osi_time_get_os_boottime_ms(void)
+{
+ return (uint32_t)(esp_timer_get_time() / 1000);
+}
+
+bool osi_alarm_is_active(osi_alarm_t *alarm)
+{
+ assert(alarm != NULL);
+
+ if (alarm->alarm_hdl != NULL) {
+ return esp_timer_is_active(alarm->alarm_hdl);
+ }
+
+ return false;
+}
diff --git a/lib/bt/common/osi/allocator.c b/lib/bt/common/osi/allocator.c
new file mode 100644
index 00000000..4d10e10f
--- /dev/null
+++ b/lib/bt/common/osi/allocator.c
@@ -0,0 +1,260 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+#include <stdlib.h>
+#include <string.h>
+
+#include "bt_common.h"
+#include "osi/allocator.h"
+
+extern void *pvPortZalloc(size_t size);
+extern void vPortFree(void *pv);
+
+
+#if HEAP_MEMORY_DEBUG
+
+#define OSI_MEM_DBG_INFO_MAX 1024*3
+typedef struct {
+ void *p;
+ int size;
+ const char *func;
+ int line;
+} osi_mem_dbg_info_t;
+
+static uint32_t mem_dbg_count = 0;
+static osi_mem_dbg_info_t mem_dbg_info[OSI_MEM_DBG_INFO_MAX];
+static uint32_t mem_dbg_current_size = 0;
+static uint32_t mem_dbg_max_size = 0;
+
+#define OSI_MEM_DBG_MAX_SECTION_NUM 5
+typedef struct {
+ bool used;
+ uint32_t max_size;
+} osi_mem_dbg_max_size_section_t;
+static osi_mem_dbg_max_size_section_t mem_dbg_max_size_section[OSI_MEM_DBG_MAX_SECTION_NUM];
+
+void osi_mem_dbg_init(void)
+{
+ int i;
+
+ for (i = 0; i < OSI_MEM_DBG_INFO_MAX; i++) {
+ mem_dbg_info[i].p = NULL;
+ mem_dbg_info[i].size = 0;
+ mem_dbg_info[i].func = NULL;
+ mem_dbg_info[i].line = 0;
+ }
+ mem_dbg_count = 0;
+ mem_dbg_current_size = 0;
+ mem_dbg_max_size = 0;
+
+ for (i = 0; i < OSI_MEM_DBG_MAX_SECTION_NUM; i++){
+ mem_dbg_max_size_section[i].used = false;
+ mem_dbg_max_size_section[i].max_size = 0;
+ }
+}
+
+void osi_mem_dbg_record(void *p, int size, const char *func, int line)
+{
+ int i;
+
+ if (!p || size == 0) {
+ OSI_TRACE_ERROR("%s invalid !!\n", __func__);
+ return;
+ }
+
+ for (i = 0; i < OSI_MEM_DBG_INFO_MAX; i++) {
+ if (mem_dbg_info[i].p == NULL) {
+ mem_dbg_info[i].p = p;
+ mem_dbg_info[i].size = size;
+ mem_dbg_info[i].func = func;
+ mem_dbg_info[i].line = line;
+ mem_dbg_count++;
+ break;
+ }
+ }
+
+ if (i >= OSI_MEM_DBG_INFO_MAX) {
+ OSI_TRACE_ERROR("%s full %s %d !!\n", __func__, func, line);
+ }
+
+ mem_dbg_current_size += size;
+ if(mem_dbg_max_size < mem_dbg_current_size) {
+ mem_dbg_max_size = mem_dbg_current_size;
+ }
+
+ for (i = 0; i < OSI_MEM_DBG_MAX_SECTION_NUM; i++){
+ if (mem_dbg_max_size_section[i].used) {
+ if(mem_dbg_max_size_section[i].max_size < mem_dbg_current_size) {
+ mem_dbg_max_size_section[i].max_size = mem_dbg_current_size;
+ }
+ }
+ }
+}
+
+void osi_mem_dbg_clean(void *p, const char *func, int line)
+{
+ int i;
+
+ if (!p) {
+ OSI_TRACE_ERROR("%s invalid\n", __func__);
+ return;
+ }
+
+ for (i = 0; i < OSI_MEM_DBG_INFO_MAX; i++) {
+ if (mem_dbg_info[i].p == p) {
+ mem_dbg_current_size -= mem_dbg_info[i].size;
+ mem_dbg_info[i].p = NULL;
+ mem_dbg_info[i].size = 0;
+ mem_dbg_info[i].func = NULL;
+ mem_dbg_info[i].line = 0;
+ mem_dbg_count--;
+ break;
+ }
+ }
+
+ if (i >= OSI_MEM_DBG_INFO_MAX) {
+ OSI_TRACE_ERROR("%s full %s %d !!\n", __func__, func, line);
+ }
+}
+
+void osi_mem_dbg_show(void)
+{
+ int i;
+
+ for (i = 0; i < OSI_MEM_DBG_INFO_MAX; i++) {
+ if (mem_dbg_info[i].p || mem_dbg_info[i].size != 0 ) {
+ OSI_TRACE_ERROR("--> p %p, s %d, f %s, l %d\n", mem_dbg_info[i].p, mem_dbg_info[i].size, mem_dbg_info[i].func, mem_dbg_info[i].line);
+ }
+ }
+ OSI_TRACE_ERROR("--> count %d\n", mem_dbg_count);
+ OSI_TRACE_ERROR("--> size %dB\n--> max size %dB\n", mem_dbg_current_size, mem_dbg_max_size);
+}
+
+uint32_t osi_mem_dbg_get_max_size(void)
+{
+ return mem_dbg_max_size;
+}
+
+uint32_t osi_mem_dbg_get_current_size(void)
+{
+ return mem_dbg_current_size;
+}
+
+void osi_men_dbg_set_section_start(uint8_t index)
+{
+ if (index >= OSI_MEM_DBG_MAX_SECTION_NUM) {
+ OSI_TRACE_ERROR("Then range of index should be between 0 and %d, current index is %d.\n",
+ OSI_MEM_DBG_MAX_SECTION_NUM - 1, index);
+ return;
+ }
+
+ if (mem_dbg_max_size_section[index].used) {
+ OSI_TRACE_WARNING("This index(%d) has been started, restart it.\n", index);
+ }
+
+ mem_dbg_max_size_section[index].used = true;
+ mem_dbg_max_size_section[index].max_size = mem_dbg_current_size;
+}
+
+void osi_men_dbg_set_section_end(uint8_t index)
+{
+ if (index >= OSI_MEM_DBG_MAX_SECTION_NUM) {
+ OSI_TRACE_ERROR("Then range of index should be between 0 and %d, current index is %d.\n",
+ OSI_MEM_DBG_MAX_SECTION_NUM - 1, index);
+ return;
+ }
+
+ if (!mem_dbg_max_size_section[index].used) {
+ OSI_TRACE_ERROR("This index(%d) has not been started.\n", index);
+ return;
+ }
+
+ mem_dbg_max_size_section[index].used = false;
+}
+
+uint32_t osi_mem_dbg_get_max_size_section(uint8_t index)
+{
+ if (index >= OSI_MEM_DBG_MAX_SECTION_NUM){
+ OSI_TRACE_ERROR("Then range of index should be between 0 and %d, current index is %d.\n",
+ OSI_MEM_DBG_MAX_SECTION_NUM - 1, index);
+ return 0;
+ }
+
+ return mem_dbg_max_size_section[index].max_size;
+}
+#endif
+
+char *osi_strdup(const char *str)
+{
+ size_t size = strlen(str) + 1; // + 1 for the null terminator
+ char *new_string = (char *)osi_calloc(size);
+
+ if (!new_string) {
+ return NULL;
+ }
+
+ memcpy(new_string, str, size);
+ return new_string;
+}
+
+void *osi_malloc_func(size_t size)
+{
+#if HEAP_MEMORY_DEBUG
+ void *p;
+#if HEAP_ALLOCATION_FROM_SPIRAM_FIRST
+ p = heap_caps_malloc_prefer(size, 2, MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL);
+#else
+ p = malloc(size);
+#endif /* #if HEAP_ALLOCATION_FROM_SPIRAM_FIRST */
+ osi_mem_dbg_record(p, size, __func__, __LINE__);
+ return p;
+#else
+#if HEAP_ALLOCATION_FROM_SPIRAM_FIRST
+ return heap_caps_malloc_prefer(size, 2, MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL);
+#else
+ return malloc(size);
+#endif /* #if HEAP_ALLOCATION_FROM_SPIRAM_FIRST */
+#endif /* #if HEAP_MEMORY_DEBUG */
+}
+
+void *osi_calloc_func(size_t size)
+{
+#if HEAP_MEMORY_DEBUG
+ void *p;
+#if HEAP_ALLOCATION_FROM_SPIRAM_FIRST
+ p = heap_caps_calloc_prefer(1, size, 2, MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL);
+#else
+ p = calloc(1, size);
+#endif /* #if HEAP_ALLOCATION_FROM_SPIRAM_FIRST */
+ osi_mem_dbg_record(p, size, __func__, __LINE__);
+ return p;
+#else
+#if HEAP_ALLOCATION_FROM_SPIRAM_FIRST
+ return heap_caps_calloc_prefer(1, size, 2, MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL);
+#else
+ return calloc(1, size);
+#endif /* #if HEAP_ALLOCATION_FROM_SPIRAM_FIRST */
+#endif /* #if HEAP_MEMORY_DEBUG */
+}
+
+void osi_free_func(void *ptr)
+{
+#if HEAP_MEMORY_DEBUG
+ osi_mem_dbg_clean(ptr, __func__, __LINE__);
+#endif
+ free(ptr);
+}
diff --git a/lib/bt/common/osi/buffer.c b/lib/bt/common/osi/buffer.c
new file mode 100644
index 00000000..55936752
--- /dev/null
+++ b/lib/bt/common/osi/buffer.c
@@ -0,0 +1,102 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+#include <stdint.h>
+#include "bt_common.h"
+#include "osi/allocator.h"
+#include "osi/buffer.h"
+
+struct buffer_t {
+ buffer_t *root;
+ size_t refcount;
+ size_t length;
+ uint8_t data[];
+};
+
+buffer_t *buffer_new(size_t size)
+{
+ assert(size > 0);
+
+ buffer_t *buffer = osi_malloc(sizeof(buffer_t) + size);
+ if (!buffer) {
+ OSI_TRACE_ERROR("%s unable to allocate buffer of %zu bytes.", __func__, size);
+ return NULL;
+ }
+
+ buffer->root = buffer;
+ buffer->refcount = 1;
+ buffer->length = size;
+
+ return buffer;
+}
+
+buffer_t *buffer_new_ref(const buffer_t *buf)
+{
+ assert(buf != NULL);
+ return buffer_new_slice(buf, buf->length);
+}
+
+buffer_t *buffer_new_slice(const buffer_t *buf, size_t slice_size)
+{
+ assert(buf != NULL);
+ assert(slice_size > 0);
+ assert(slice_size <= buf->length);
+
+ buffer_t *ret = osi_calloc(sizeof(buffer_t));
+ if (!ret) {
+ OSI_TRACE_ERROR("%s unable to allocate new buffer for slice of length %zu.", __func__, slice_size);
+ return NULL;
+ }
+
+ ret->root = buf->root;
+ ret->refcount = SIZE_MAX;
+ ret->length = slice_size;
+
+ ++buf->root->refcount;
+
+ return ret;
+}
+
+void buffer_free(buffer_t *buffer)
+{
+ if (!buffer) {
+ return;
+ }
+
+ if (buffer->root != buffer) {
+ // We're a leaf node. Delete the root node if we're the last referent.
+ if (--buffer->root->refcount == 0) {
+ osi_free(buffer->root);
+ }
+ osi_free(buffer);
+ } else if (--buffer->refcount == 0) {
+ // We're a root node. Roots are only deleted when their refcount goes to 0.
+ osi_free(buffer);
+ }
+}
+
+void *buffer_ptr(const buffer_t *buf)
+{
+ assert(buf != NULL);
+ return buf->root->data + buf->root->length - buf->length;
+}
+
+size_t buffer_length(const buffer_t *buf)
+{
+ assert(buf != NULL);
+ return buf->length;
+}
diff --git a/lib/bt/common/osi/config.c b/lib/bt/common/osi/config.c
new file mode 100644
index 00000000..fb6727a7
--- /dev/null
+++ b/lib/bt/common/osi/config.c
@@ -0,0 +1,740 @@
+/*
+ * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define LOG_TAG "bt_osi_config"
+#include "esp_system.h"
+#include "nvs_flash.h"
+#include "nvs.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "bt_common.h"
+#include "osi/allocator.h"
+#include "osi/config.h"
+#include "osi/list.h"
+
+#define CONFIG_FILE_MAX_SIZE (1536)//1.5k
+#define CONFIG_FILE_DEFAULE_LENGTH (2048)
+#define CONFIG_KEY "bt_cfg_key"
+typedef struct {
+ char *key;
+ char *value;
+} entry_t;
+
+typedef struct {
+ char *name;
+ list_t *entries;
+} section_t;
+
+struct config_t {
+ list_t *sections;
+};
+
+// Empty definition; this type is aliased to list_node_t.
+struct config_section_iter_t {};
+
+static void config_parse(nvs_handle_t fp, config_t *config);
+
+static section_t *section_new(const char *name);
+static void section_free(void *ptr);
+static section_t *section_find(const config_t *config, const char *section);
+
+static entry_t *entry_new(const char *key, const char *value);
+static void entry_free(void *ptr);
+static entry_t *entry_find(const config_t *config, const char *section, const char *key);
+
+config_t *config_new_empty(void)
+{
+ config_t *config = osi_calloc(sizeof(config_t));
+ if (!config) {
+ OSI_TRACE_ERROR("%s unable to allocate memory for config_t.\n", __func__);
+ goto error;
+ }
+
+ config->sections = list_new(section_free);
+ if (!config->sections) {
+ OSI_TRACE_ERROR("%s unable to allocate list for sections.\n", __func__);
+ goto error;
+ }
+
+ return config;
+
+error:;
+ config_free(config);
+ return NULL;
+}
+
+config_t *config_new(const char *filename)
+{
+ assert(filename != NULL);
+
+ config_t *config = config_new_empty();
+ if (!config) {
+ return NULL;
+ }
+
+ esp_err_t err;
+ nvs_handle_t fp;
+ err = nvs_open(filename, NVS_READWRITE, &fp);
+ if (err != ESP_OK) {
+ if (err == ESP_ERR_NVS_NOT_INITIALIZED) {
+ OSI_TRACE_ERROR("%s: NVS not initialized. "
+ "Call nvs_flash_init before initializing bluetooth.", __func__);
+ } else {
+ OSI_TRACE_ERROR("%s unable to open NVS namespace '%s'\n", __func__, filename);
+ }
+ config_free(config);
+ return NULL;
+ }
+
+ config_parse(fp, config);
+ nvs_close(fp);
+ return config;
+}
+
+void config_free(config_t *config)
+{
+ if (!config) {
+ return;
+ }
+
+ list_free(config->sections);
+ osi_free(config);
+}
+
+bool config_has_section(const config_t *config, const char *section)
+{
+ assert(config != NULL);
+ assert(section != NULL);
+
+ return (section_find(config, section) != NULL);
+}
+
+bool config_has_key(const config_t *config, const char *section, const char *key)
+{
+ assert(config != NULL);
+ assert(section != NULL);
+ assert(key != NULL);
+
+ return (entry_find(config, section, key) != NULL);
+}
+
+bool config_has_key_in_section(config_t *config, const char *key, char *key_value)
+{
+ OSI_TRACE_DEBUG("key = %s, value = %s", key, key_value);
+ for (const list_node_t *node = list_begin(config->sections); node != list_end(config->sections); node = list_next(node)) {
+ const section_t *section = (const section_t *)list_node(node);
+
+ for (const list_node_t *node = list_begin(section->entries); node != list_end(section->entries); node = list_next(node)) {
+ entry_t *entry = list_node(node);
+ OSI_TRACE_DEBUG("entry->key = %s, entry->value = %s", entry->key, entry->value);
+ if (!strcmp(entry->key, key) && !strcmp(entry->value, key_value)) {
+ OSI_TRACE_DEBUG("%s, the irk aready in the flash.", __func__);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+int config_get_int(const config_t *config, const char *section, const char *key, int def_value)
+{
+ assert(config != NULL);
+ assert(section != NULL);
+ assert(key != NULL);
+
+ entry_t *entry = entry_find(config, section, key);
+ if (!entry) {
+ return def_value;
+ }
+
+ char *endptr;
+ int ret = strtol(entry->value, &endptr, 0);
+ return (*endptr == '\0') ? ret : def_value;
+}
+
+bool config_get_bool(const config_t *config, const char *section, const char *key, bool def_value)
+{
+ assert(config != NULL);
+ assert(section != NULL);
+ assert(key != NULL);
+
+ entry_t *entry = entry_find(config, section, key);
+ if (!entry) {
+ return def_value;
+ }
+
+ if (!strcmp(entry->value, "true")) {
+ return true;
+ }
+ if (!strcmp(entry->value, "false")) {
+ return false;
+ }
+
+ return def_value;
+}
+
+const char *config_get_string(const config_t *config, const char *section, const char *key, const char *def_value)
+{
+ assert(config != NULL);
+ assert(section != NULL);
+ assert(key != NULL);
+
+ entry_t *entry = entry_find(config, section, key);
+ if (!entry) {
+ return def_value;
+ }
+
+ return entry->value;
+}
+
+void config_set_int(config_t *config, const char *section, const char *key, int value)
+{
+ assert(config != NULL);
+ assert(section != NULL);
+ assert(key != NULL);
+
+ char value_str[32] = { 0 };
+ sprintf(value_str, "%d", value);
+ config_set_string(config, section, key, value_str, false);
+}
+
+void config_set_bool(config_t *config, const char *section, const char *key, bool value)
+{
+ assert(config != NULL);
+ assert(section != NULL);
+ assert(key != NULL);
+
+ config_set_string(config, section, key, value ? "true" : "false", false);
+}
+
+void config_set_string(config_t *config, const char *section, const char *key, const char *value, bool insert_back)
+{
+ section_t *sec = section_find(config, section);
+ if (!sec) {
+ sec = section_new(section);
+ if (insert_back) {
+ list_append(config->sections, sec);
+ } else {
+ list_prepend(config->sections, sec);
+ }
+ }
+
+ for (const list_node_t *node = list_begin(sec->entries); node != list_end(sec->entries); node = list_next(node)) {
+ entry_t *entry = list_node(node);
+ if (!strcmp(entry->key, key)) {
+ osi_free(entry->value);
+ entry->value = osi_strdup(value);
+ return;
+ }
+ }
+
+ entry_t *entry = entry_new(key, value);
+ list_append(sec->entries, entry);
+}
+
+bool config_remove_section(config_t *config, const char *section)
+{
+ assert(config != NULL);
+ assert(section != NULL);
+
+ section_t *sec = section_find(config, section);
+ if (!sec) {
+ return false;
+ }
+
+ return list_remove(config->sections, sec);
+}
+
+bool config_update_newest_section(config_t *config, const char *section)
+{
+ assert(config != NULL);
+ assert(section != NULL);
+
+ list_node_t *first_node = list_begin(config->sections);
+ if (first_node == NULL) {
+ return false;
+ }
+ section_t *first_sec = list_node(first_node);
+ if (strcmp(first_sec->name, section) == 0) {
+ return true;
+ }
+
+ for (const list_node_t *node = list_begin(config->sections); node != list_end(config->sections); node = list_next(node)) {
+ section_t *sec = list_node(node);
+ if (strcmp(sec->name, section) == 0) {
+ list_delete(config->sections, sec);
+ list_prepend(config->sections, sec);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool config_remove_key(config_t *config, const char *section, const char *key)
+{
+ assert(config != NULL);
+ assert(section != NULL);
+ assert(key != NULL);
+ bool ret;
+
+ section_t *sec = section_find(config, section);
+ entry_t *entry = entry_find(config, section, key);
+ if (!sec || !entry) {
+ return false;
+ }
+
+ ret = list_remove(sec->entries, entry);
+ if (list_length(sec->entries) == 0) {
+ OSI_TRACE_DEBUG("%s remove section name:%s",__func__, section);
+ ret &= config_remove_section(config, section);
+ }
+ return ret;
+}
+
+const config_section_node_t *config_section_begin(const config_t *config)
+{
+ assert(config != NULL);
+ return (const config_section_node_t *)list_begin(config->sections);
+}
+
+const config_section_node_t *config_section_end(const config_t *config)
+{
+ assert(config != NULL);
+ return (const config_section_node_t *)list_end(config->sections);
+}
+
+const config_section_node_t *config_section_next(const config_section_node_t *node)
+{
+ assert(node != NULL);
+ return (const config_section_node_t *)list_next((const list_node_t *)node);
+}
+
+const char *config_section_name(const config_section_node_t *node)
+{
+ assert(node != NULL);
+ const list_node_t *lnode = (const list_node_t *)node;
+ const section_t *section = (const section_t *)list_node(lnode);
+ return section->name;
+}
+
+static int get_config_size(const config_t *config)
+{
+ assert(config != NULL);
+
+ int w_len = 0, total_size = 0;
+
+ for (const list_node_t *node = list_begin(config->sections); node != list_end(config->sections); node = list_next(node)) {
+ const section_t *section = (const section_t *)list_node(node);
+ w_len = strlen(section->name) + strlen("[]\n");// format "[section->name]\n"
+ total_size += w_len;
+
+ for (const list_node_t *enode = list_begin(section->entries); enode != list_end(section->entries); enode = list_next(enode)) {
+ const entry_t *entry = (const entry_t *)list_node(enode);
+ w_len = strlen(entry->key) + strlen(entry->value) + strlen(" = \n");// format "entry->key = entry->value\n"
+ total_size += w_len;
+ }
+
+ // Only add a separating newline if there are more sections.
+ if (list_next(node) != list_end(config->sections)) {
+ total_size ++; //'\n'
+ } else {
+ break;
+ }
+ }
+ total_size ++; //'\0'
+ return total_size;
+}
+
+static int get_config_size_from_flash(nvs_handle_t fp)
+{
+ assert(fp != 0);
+
+ esp_err_t err;
+ const size_t keyname_bufsz = sizeof(CONFIG_KEY) + 5 + 1; // including log10(sizeof(i))
+ char *keyname = osi_calloc(keyname_bufsz);
+ if (!keyname){
+ OSI_TRACE_ERROR("%s, malloc error\n", __func__);
+ return 0;
+ }
+ size_t length = CONFIG_FILE_DEFAULE_LENGTH;
+ size_t total_length = 0;
+ uint16_t i = 0;
+ snprintf(keyname, keyname_bufsz, "%s%d", CONFIG_KEY, 0);
+ err = nvs_get_blob(fp, keyname, NULL, &length);
+ if (err == ESP_ERR_NVS_NOT_FOUND) {
+ osi_free(keyname);
+ return 0;
+ }
+ if (err != ESP_OK) {
+ OSI_TRACE_ERROR("%s, error %d\n", __func__, err);
+ osi_free(keyname);
+ return 0;
+ }
+ total_length += length;
+ while (length == CONFIG_FILE_MAX_SIZE) {
+ length = CONFIG_FILE_DEFAULE_LENGTH;
+ snprintf(keyname, keyname_bufsz, "%s%d", CONFIG_KEY, ++i);
+ err = nvs_get_blob(fp, keyname, NULL, &length);
+
+ if (err == ESP_ERR_NVS_NOT_FOUND) {
+ break;
+ }
+ if (err != ESP_OK) {
+ OSI_TRACE_ERROR("%s, error %d\n", __func__, err);
+ osi_free(keyname);
+ return 0;
+ }
+ total_length += length;
+ }
+ osi_free(keyname);
+ return total_length;
+}
+
+bool config_save(const config_t *config, const char *filename)
+{
+ assert(config != NULL);
+ assert(filename != NULL);
+ assert(*filename != '\0');
+
+ esp_err_t err;
+ int err_code = 0;
+ nvs_handle_t fp;
+ char *line = osi_calloc(1024);
+ const size_t keyname_bufsz = sizeof(CONFIG_KEY) + 5 + 1; // including log10(sizeof(i))
+ char *keyname = osi_calloc(keyname_bufsz);
+ int config_size = get_config_size(config);
+ char *buf = osi_calloc(config_size);
+ if (!line || !buf || !keyname) {
+ err_code |= 0x01;
+ goto error;
+ }
+
+ err = nvs_open(filename, NVS_READWRITE, &fp);
+ if (err != ESP_OK) {
+ if (err == ESP_ERR_NVS_NOT_INITIALIZED) {
+ OSI_TRACE_ERROR("%s: NVS not initialized. "
+ "Call nvs_flash_init before initializing bluetooth.", __func__);
+ }
+ err_code |= 0x02;
+ goto error;
+ }
+
+ int w_cnt, w_cnt_total = 0;
+ for (const list_node_t *node = list_begin(config->sections); node != list_end(config->sections); node = list_next(node)) {
+ const section_t *section = (const section_t *)list_node(node);
+ w_cnt = snprintf(line, 1024, "[%s]\n", section->name);
+ if(w_cnt < 0) {
+ OSI_TRACE_ERROR("snprintf error w_cnt %d.",w_cnt);
+ err_code |= 0x10;
+ goto error;
+ }
+ if(w_cnt_total + w_cnt > config_size) {
+ OSI_TRACE_ERROR("%s, memcpy size (w_cnt + w_cnt_total = %d) is larger than buffer size (config_size = %d).", __func__, (w_cnt + w_cnt_total), config_size);
+ err_code |= 0x20;
+ goto error;
+ }
+ OSI_TRACE_DEBUG("section name: %s, w_cnt + w_cnt_total = %d\n", section->name, w_cnt + w_cnt_total);
+ memcpy(buf + w_cnt_total, line, w_cnt);
+ w_cnt_total += w_cnt;
+
+ for (const list_node_t *enode = list_begin(section->entries); enode != list_end(section->entries); enode = list_next(enode)) {
+ const entry_t *entry = (const entry_t *)list_node(enode);
+ OSI_TRACE_DEBUG("(key, val): (%s, %s)\n", entry->key, entry->value);
+ w_cnt = snprintf(line, 1024, "%s = %s\n", entry->key, entry->value);
+ if(w_cnt < 0) {
+ OSI_TRACE_ERROR("snprintf error w_cnt %d.",w_cnt);
+ err_code |= 0x10;
+ goto error;
+ }
+ if(w_cnt_total + w_cnt > config_size) {
+ OSI_TRACE_ERROR("%s, memcpy size (w_cnt + w_cnt_total = %d) is larger than buffer size.(config_size = %d)", __func__, (w_cnt + w_cnt_total), config_size);
+ err_code |= 0x20;
+ goto error;
+ }
+ OSI_TRACE_DEBUG("%s, w_cnt + w_cnt_total = %d", __func__, w_cnt + w_cnt_total);
+ memcpy(buf + w_cnt_total, line, w_cnt);
+ w_cnt_total += w_cnt;
+ }
+
+ // Only add a separating newline if there are more sections.
+ if (list_next(node) != list_end(config->sections)) {
+ buf[w_cnt_total] = '\n';
+ w_cnt_total += 1;
+ } else {
+ break;
+ }
+ }
+ buf[w_cnt_total] = '\0';
+ if (w_cnt_total < CONFIG_FILE_MAX_SIZE) {
+ snprintf(keyname, keyname_bufsz, "%s%d", CONFIG_KEY, 0);
+ err = nvs_set_blob(fp, keyname, buf, w_cnt_total);
+ if (err != ESP_OK) {
+ nvs_close(fp);
+ err_code |= 0x04;
+ goto error;
+ }
+ }else {
+ int count = (w_cnt_total / CONFIG_FILE_MAX_SIZE);
+ assert(count <= 0xFF);
+ for (uint8_t i = 0; i <= count; i++)
+ {
+ snprintf(keyname, keyname_bufsz, "%s%d", CONFIG_KEY, i);
+ if (i == count) {
+ err = nvs_set_blob(fp, keyname, buf + i*CONFIG_FILE_MAX_SIZE, w_cnt_total - i*CONFIG_FILE_MAX_SIZE);
+ OSI_TRACE_DEBUG("save keyname = %s, i = %d, %d\n", keyname, i, w_cnt_total - i*CONFIG_FILE_MAX_SIZE);
+ }else {
+ err = nvs_set_blob(fp, keyname, buf + i*CONFIG_FILE_MAX_SIZE, CONFIG_FILE_MAX_SIZE);
+ OSI_TRACE_DEBUG("save keyname = %s, i = %d, %d\n", keyname, i, CONFIG_FILE_MAX_SIZE);
+ }
+ if (err != ESP_OK) {
+ nvs_close(fp);
+ err_code |= 0x04;
+ goto error;
+ }
+ }
+ }
+
+ err = nvs_commit(fp);
+ if (err != ESP_OK) {
+ nvs_close(fp);
+ err_code |= 0x08;
+ goto error;
+ }
+
+ nvs_close(fp);
+ osi_free(line);
+ osi_free(buf);
+ osi_free(keyname);
+ return true;
+
+error:
+ if (buf) {
+ osi_free(buf);
+ }
+ if (line) {
+ osi_free(line);
+ }
+ if (keyname) {
+ osi_free(keyname);
+ }
+ if (err_code) {
+ OSI_TRACE_ERROR("%s, err_code: 0x%x\n", __func__, err_code);
+ }
+ return false;
+}
+
+static char *trim(char *str)
+{
+ while (isspace((unsigned char)(*str))) {
+ ++str;
+ }
+
+ if (!*str) {
+ return str;
+ }
+
+ char *end_str = str + strlen(str) - 1;
+ while (end_str > str && isspace((unsigned char)(*end_str))) {
+ --end_str;
+ }
+
+ end_str[1] = '\0';
+ return str;
+}
+
+static void config_parse(nvs_handle_t fp, config_t *config)
+{
+ assert(fp != 0);
+ assert(config != NULL);
+
+ esp_err_t err;
+ int line_num = 0;
+ int err_code = 0;
+ uint16_t i = 0;
+ size_t length = CONFIG_FILE_DEFAULE_LENGTH;
+ size_t total_length = 0;
+ char *line = osi_calloc(1024);
+ char *section = osi_calloc(1024);
+ const size_t keyname_bufsz = sizeof(CONFIG_KEY) + 5 + 1; // including log10(sizeof(i))
+ char *keyname = osi_calloc(keyname_bufsz);
+ int buf_size = get_config_size_from_flash(fp);
+ char *buf = NULL;
+
+ if(buf_size == 0) { //First use nvs
+ goto error;
+ }
+ buf = osi_calloc(buf_size);
+ if (!line || !section || !buf || !keyname) {
+ err_code |= 0x01;
+ goto error;
+ }
+ snprintf(keyname, keyname_bufsz, "%s%d", CONFIG_KEY, 0);
+ err = nvs_get_blob(fp, keyname, buf, &length);
+ if (err == ESP_ERR_NVS_NOT_FOUND) {
+ goto error;
+ }
+ if (err != ESP_OK) {
+ err_code |= 0x02;
+ goto error;
+ }
+ total_length += length;
+ while (length == CONFIG_FILE_MAX_SIZE) {
+ length = CONFIG_FILE_DEFAULE_LENGTH;
+ snprintf(keyname, keyname_bufsz, "%s%d", CONFIG_KEY, ++i);
+ err = nvs_get_blob(fp, keyname, buf + CONFIG_FILE_MAX_SIZE * i, &length);
+
+ if (err == ESP_ERR_NVS_NOT_FOUND) {
+ break;
+ }
+ if (err != ESP_OK) {
+ err_code |= 0x02;
+ goto error;
+ }
+ total_length += length;
+ }
+ char *p_line_end;
+ char *p_line_bgn = buf;
+ strcpy(section, CONFIG_DEFAULT_SECTION);
+
+ while ( (p_line_bgn < buf + total_length - 1) && (p_line_end = strchr(p_line_bgn, '\n'))) {
+
+ // get one line
+ int line_len = p_line_end - p_line_bgn;
+ if (line_len > 1023) {
+ OSI_TRACE_WARNING("%s exceed max line length on line %d.\n", __func__, line_num);
+ break;
+ }
+ memcpy(line, p_line_bgn, line_len);
+ line[line_len] = '\0';
+ p_line_bgn = p_line_end + 1;
+ char *line_ptr = trim(line);
+ ++line_num;
+
+ // Skip blank and comment lines.
+ if (*line_ptr == '\0' || *line_ptr == '#') {
+ continue;
+ }
+
+ if (*line_ptr == '[') {
+ size_t len = strlen(line_ptr);
+ if (line_ptr[len - 1] != ']') {
+ OSI_TRACE_WARNING("%s unterminated section name on line %d.\n", __func__, line_num);
+ continue;
+ }
+ strncpy(section, line_ptr + 1, len - 2);
+ section[len - 2] = '\0';
+ } else {
+ char *split = strchr(line_ptr, '=');
+ if (!split) {
+ OSI_TRACE_DEBUG("%s no key/value separator found on line %d.\n", __func__, line_num);
+ continue;
+ }
+ *split = '\0';
+ config_set_string(config, section, trim(line_ptr), trim(split + 1), true);
+ }
+ }
+
+error:
+ if (buf) {
+ osi_free(buf);
+ }
+ if (line) {
+ osi_free(line);
+ }
+ if (section) {
+ osi_free(section);
+ }
+ if (keyname) {
+ osi_free(keyname);
+ }
+ if (err_code) {
+ OSI_TRACE_ERROR("%s returned with err code: %d\n", __func__, err_code);
+ }
+}
+
+static section_t *section_new(const char *name)
+{
+ section_t *section = osi_calloc(sizeof(section_t));
+ if (!section) {
+ return NULL;
+ }
+
+ section->name = osi_strdup(name);
+ section->entries = list_new(entry_free);
+ return section;
+}
+
+static void section_free(void *ptr)
+{
+ if (!ptr) {
+ return;
+ }
+
+ section_t *section = ptr;
+ osi_free(section->name);
+ list_free(section->entries);
+ osi_free(section);
+}
+
+static section_t *section_find(const config_t *config, const char *section)
+{
+ for (const list_node_t *node = list_begin(config->sections); node != list_end(config->sections); node = list_next(node)) {
+ section_t *sec = list_node(node);
+ if (!strcmp(sec->name, section)) {
+ return sec;
+ }
+ }
+
+ return NULL;
+}
+
+static entry_t *entry_new(const char *key, const char *value)
+{
+ entry_t *entry = osi_calloc(sizeof(entry_t));
+ if (!entry) {
+ return NULL;
+ }
+
+ entry->key = osi_strdup(key);
+ entry->value = osi_strdup(value);
+ return entry;
+}
+
+static void entry_free(void *ptr)
+{
+ if (!ptr) {
+ return;
+ }
+
+ entry_t *entry = ptr;
+ osi_free(entry->key);
+ osi_free(entry->value);
+ osi_free(entry);
+}
+
+static entry_t *entry_find(const config_t *config, const char *section, const char *key)
+{
+ section_t *sec = section_find(config, section);
+ if (!sec) {
+ return NULL;
+ }
+
+ for (const list_node_t *node = list_begin(sec->entries); node != list_end(sec->entries); node = list_next(node)) {
+ entry_t *entry = list_node(node);
+ if (!strcmp(entry->key, key)) {
+ return entry;
+ }
+ }
+
+ return NULL;
+}
diff --git a/lib/bt/common/osi/fixed_pkt_queue.c b/lib/bt/common/osi/fixed_pkt_queue.c
new file mode 100644
index 00000000..e98d4548
--- /dev/null
+++ b/lib/bt/common/osi/fixed_pkt_queue.c
@@ -0,0 +1,161 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "osi/allocator.h"
+#include "osi/pkt_queue.h"
+#include "osi/fixed_pkt_queue.h"
+#include "osi/osi.h"
+#include "osi/semaphore.h"
+
+typedef struct fixed_pkt_queue_t {
+ struct pkt_queue *pkt_list;
+ osi_sem_t enqueue_sem;
+ osi_sem_t dequeue_sem;
+ size_t capacity;
+ fixed_pkt_queue_cb dequeue_ready;
+} fixed_pkt_queue_t;
+
+fixed_pkt_queue_t *fixed_pkt_queue_new(size_t capacity)
+{
+ fixed_pkt_queue_t *ret = osi_calloc(sizeof(fixed_pkt_queue_t));
+ if (!ret) {
+ goto error;
+ }
+
+ ret->capacity = capacity;
+ ret->pkt_list = pkt_queue_create();
+ if (!ret->pkt_list) {
+ goto error;
+ }
+
+ osi_sem_new(&ret->enqueue_sem, capacity, capacity);
+ if (!ret->enqueue_sem) {
+ goto error;
+ }
+
+ osi_sem_new(&ret->dequeue_sem, capacity, 0);
+ if (!ret->dequeue_sem) {
+ goto error;
+ }
+
+ return ret;
+
+error:
+ fixed_pkt_queue_free(ret, NULL);
+ return NULL;
+}
+
+void fixed_pkt_queue_free(fixed_pkt_queue_t *queue, fixed_pkt_queue_free_cb free_cb)
+{
+ if (queue == NULL) {
+ return;
+ }
+
+ fixed_pkt_queue_unregister_dequeue(queue);
+
+ pkt_queue_destroy(queue->pkt_list, (pkt_queue_free_cb)free_cb);
+ queue->pkt_list = NULL;
+
+ if (queue->enqueue_sem) {
+ osi_sem_free(&queue->enqueue_sem);
+ }
+ if (queue->dequeue_sem) {
+ osi_sem_free(&queue->dequeue_sem);
+ }
+ osi_free(queue);
+}
+
+bool fixed_pkt_queue_is_empty(fixed_pkt_queue_t *queue)
+{
+ if (queue == NULL) {
+ return true;
+ }
+
+ return pkt_queue_is_empty(queue->pkt_list);
+}
+
+size_t fixed_pkt_queue_length(fixed_pkt_queue_t *queue)
+{
+ if (queue == NULL) {
+ return 0;
+ }
+ return pkt_queue_length(queue->pkt_list);
+}
+
+size_t fixed_pkt_queue_capacity(fixed_pkt_queue_t *queue)
+{
+ assert(queue != NULL);
+
+ return queue->capacity;
+}
+
+bool fixed_pkt_queue_enqueue(fixed_pkt_queue_t *queue, pkt_linked_item_t *linked_pkt, uint32_t timeout)
+{
+ bool ret = false;
+
+ assert(queue != NULL);
+ assert(linked_pkt != NULL);
+
+ if (osi_sem_take(&queue->enqueue_sem, timeout) != 0) {
+ return false;
+ }
+
+ ret = pkt_queue_enqueue(queue->pkt_list, linked_pkt);
+
+ assert(ret == true);
+ osi_sem_give(&queue->dequeue_sem);
+
+ return ret;
+}
+
+pkt_linked_item_t *fixed_pkt_queue_dequeue(fixed_pkt_queue_t *queue, uint32_t timeout)
+{
+ pkt_linked_item_t *ret = NULL;
+
+ assert(queue != NULL);
+
+ if (osi_sem_take(&queue->dequeue_sem, timeout) != 0) {
+ return NULL;
+ }
+ ret = pkt_queue_dequeue(queue->pkt_list);
+
+ osi_sem_give(&queue->enqueue_sem);
+
+ return ret;
+}
+
+pkt_linked_item_t *fixed_pkt_queue_try_peek_first(fixed_pkt_queue_t *queue)
+{
+ if (queue == NULL) {
+ return NULL;
+ }
+
+ return pkt_queue_try_peek_first(queue->pkt_list);
+}
+
+void fixed_pkt_queue_register_dequeue(fixed_pkt_queue_t *queue, fixed_pkt_queue_cb ready_cb)
+{
+ assert(queue != NULL);
+ assert(ready_cb != NULL);
+
+ queue->dequeue_ready = ready_cb;
+}
+
+void fixed_pkt_queue_unregister_dequeue(fixed_pkt_queue_t *queue)
+{
+ assert(queue != NULL);
+
+ queue->dequeue_ready = NULL;
+}
+
+void fixed_pkt_queue_process(fixed_pkt_queue_t *queue)
+{
+ assert(queue != NULL);
+
+ if (queue->dequeue_ready) {
+ queue->dequeue_ready(queue);
+ }
+}
diff --git a/lib/bt/common/osi/fixed_queue.c b/lib/bt/common/osi/fixed_queue.c
new file mode 100644
index 00000000..0f1ba9b7
--- /dev/null
+++ b/lib/bt/common/osi/fixed_queue.c
@@ -0,0 +1,256 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+#include "osi/allocator.h"
+#include "osi/fixed_queue.h"
+#include "osi/list.h"
+#include "osi/osi.h"
+#include "osi/mutex.h"
+#include "osi/semaphore.h"
+
+typedef struct fixed_queue_t {
+
+ list_t *list;
+ osi_sem_t enqueue_sem;
+ osi_sem_t dequeue_sem;
+ osi_mutex_t lock;
+ size_t capacity;
+
+ fixed_queue_cb dequeue_ready;
+} fixed_queue_t;
+
+
+fixed_queue_t *fixed_queue_new(size_t capacity)
+{
+ fixed_queue_t *ret = osi_calloc(sizeof(fixed_queue_t));
+ if (!ret) {
+ goto error;
+ }
+
+ osi_mutex_new(&ret->lock);
+ ret->capacity = capacity;
+
+ ret->list = list_new(NULL);
+ if (!ret->list) {
+ goto error;
+ }
+
+
+ osi_sem_new(&ret->enqueue_sem, capacity, capacity);
+ if (!ret->enqueue_sem) {
+ goto error;
+ }
+
+ osi_sem_new(&ret->dequeue_sem, capacity, 0);
+ if (!ret->dequeue_sem) {
+ goto error;
+ }
+
+ return ret;
+
+error:;
+ fixed_queue_free(ret, NULL);
+ return NULL;
+}
+
+void fixed_queue_free(fixed_queue_t *queue, fixed_queue_free_cb free_cb)
+{
+ const list_node_t *node;
+
+ if (queue == NULL) {
+ return;
+ }
+
+ fixed_queue_unregister_dequeue(queue);
+
+ if (free_cb) {
+ for (node = list_begin(queue->list); node != list_end(queue->list); node = list_next(node)) {
+ free_cb(list_node(node));
+ }
+ }
+
+ list_free(queue->list);
+ osi_sem_free(&queue->enqueue_sem);
+ osi_sem_free(&queue->dequeue_sem);
+ osi_mutex_free(&queue->lock);
+ osi_free(queue);
+}
+
+bool fixed_queue_is_empty(fixed_queue_t *queue)
+{
+ bool is_empty = false;
+
+ if (queue == NULL) {
+ return true;
+ }
+
+ osi_mutex_lock(&queue->lock, OSI_MUTEX_MAX_TIMEOUT);
+ is_empty = list_is_empty(queue->list);
+ osi_mutex_unlock(&queue->lock);
+
+ return is_empty;
+}
+
+size_t fixed_queue_length(fixed_queue_t *queue)
+{
+ size_t length;
+
+ if (queue == NULL) {
+ return 0;
+ }
+
+ osi_mutex_lock(&queue->lock, OSI_MUTEX_MAX_TIMEOUT);
+ length = list_length(queue->list);
+ osi_mutex_unlock(&queue->lock);
+
+ return length;
+}
+size_t fixed_queue_capacity(fixed_queue_t *queue)
+{
+ assert(queue != NULL);
+
+ return queue->capacity;
+}
+
+bool fixed_queue_enqueue(fixed_queue_t *queue, void *data, uint32_t timeout)
+{
+ bool status=false; //Flag whether enqueued success
+
+ assert(queue != NULL);
+ assert(data != NULL);
+
+ if (osi_sem_take(&queue->enqueue_sem, timeout) != 0) {
+ return false;
+ }
+
+ osi_mutex_lock(&queue->lock, OSI_MUTEX_MAX_TIMEOUT);
+ status = list_append(queue->list, data); //Check whether enqueued success
+ osi_mutex_unlock(&queue->lock);
+
+ if(status == true )
+ osi_sem_give(&queue->dequeue_sem);
+
+ return status;
+}
+
+void *fixed_queue_dequeue(fixed_queue_t *queue, uint32_t timeout)
+{
+ void *ret = NULL;
+
+ assert(queue != NULL);
+
+ if (osi_sem_take(&queue->dequeue_sem, timeout) != 0) {
+ return NULL;
+ }
+
+ osi_mutex_lock(&queue->lock, OSI_MUTEX_MAX_TIMEOUT);
+ ret = list_front(queue->list);
+ list_remove(queue->list, ret);
+ osi_mutex_unlock(&queue->lock);
+
+ osi_sem_give(&queue->enqueue_sem);
+
+ return ret;
+}
+
+void *fixed_queue_try_peek_first(fixed_queue_t *queue)
+{
+ void *ret = NULL;
+
+ if (queue == NULL) {
+ return NULL;
+ }
+
+ osi_mutex_lock(&queue->lock, OSI_MUTEX_MAX_TIMEOUT);
+ ret = list_is_empty(queue->list) ? NULL : list_front(queue->list);
+ osi_mutex_unlock(&queue->lock);
+
+ return ret;
+}
+
+void *fixed_queue_try_peek_last(fixed_queue_t *queue)
+{
+ void *ret = NULL;
+
+ if (queue == NULL) {
+ return NULL;
+ }
+
+ osi_mutex_lock(&queue->lock, OSI_MUTEX_MAX_TIMEOUT);
+ ret = list_is_empty(queue->list) ? NULL : list_back(queue->list);
+ osi_mutex_unlock(&queue->lock);
+
+ return ret;
+}
+
+void *fixed_queue_try_remove_from_queue(fixed_queue_t *queue, void *data)
+{
+ bool removed = false;
+
+ if (queue == NULL) {
+ return NULL;
+ }
+
+ osi_mutex_lock(&queue->lock, OSI_MUTEX_MAX_TIMEOUT);
+ if (list_contains(queue->list, data) &&
+ osi_sem_take(&queue->dequeue_sem, 0) == 0) {
+ removed = list_remove(queue->list, data);
+ assert(removed);
+ }
+ osi_mutex_unlock(&queue->lock);
+
+ if (removed) {
+ osi_sem_give(&queue->enqueue_sem);
+ return data;
+ }
+
+ return NULL;
+}
+
+list_t *fixed_queue_get_list(fixed_queue_t *queue)
+{
+ assert(queue != NULL);
+
+ // NOTE: This function is not thread safe, and there is no point for
+ // calling osi_mutex_lock() / osi_mutex_unlock()
+ return queue->list;
+}
+
+void fixed_queue_register_dequeue(fixed_queue_t *queue, fixed_queue_cb ready_cb)
+{
+ assert(queue != NULL);
+ assert(ready_cb != NULL);
+
+ queue->dequeue_ready = ready_cb;
+}
+
+void fixed_queue_unregister_dequeue(fixed_queue_t *queue)
+{
+ assert(queue != NULL);
+
+ queue->dequeue_ready = NULL;
+}
+
+void fixed_queue_process(fixed_queue_t *queue)
+{
+ assert(queue != NULL);
+
+ if (queue->dequeue_ready) {
+ queue->dequeue_ready(queue);
+ }
+}
diff --git a/lib/bt/common/osi/future.c b/lib/bt/common/osi/future.c
new file mode 100644
index 00000000..aee33b1c
--- /dev/null
+++ b/lib/bt/common/osi/future.c
@@ -0,0 +1,97 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+#include "bt_common.h"
+#include "osi/allocator.h"
+#include "osi/future.h"
+#include "osi/osi.h"
+
+void future_free(future_t *future);
+
+future_t *future_new(void)
+{
+ future_t *ret = osi_calloc(sizeof(future_t));
+ if (!ret) {
+ OSI_TRACE_ERROR("%s unable to allocate memory for return value.", __func__);
+ goto error;
+ }
+
+ if (osi_sem_new(&ret->semaphore, 1, 0) != 0) {
+ OSI_TRACE_ERROR("%s unable to allocate memory for the semaphore.", __func__);
+ goto error;
+ }
+
+ ret->ready_can_be_called = true;
+ return ret;
+error:;
+ future_free(ret);
+ return NULL;
+}
+
+future_t *future_new_immediate(void *value)
+{
+ future_t *ret = osi_calloc(sizeof(future_t));
+ if (!ret) {
+ OSI_TRACE_ERROR("%s unable to allocate memory for return value.", __func__);
+ goto error;
+ }
+
+ ret->result = value;
+ ret->ready_can_be_called = false;
+ return ret;
+error:;
+ future_free(ret);
+ return NULL;
+}
+
+void future_ready(future_t *future, void *value)
+{
+ assert(future != NULL);
+ assert(future->ready_can_be_called);
+
+ future->ready_can_be_called = false;
+ future->result = value;
+ osi_sem_give(&future->semaphore);
+}
+
+void *future_await(future_t *future)
+{
+ assert(future != NULL);
+
+ // If the future is immediate, it will not have a semaphore
+ if (future->semaphore) {
+ osi_sem_take(&future->semaphore, OSI_SEM_MAX_TIMEOUT);
+ }
+
+ void *result = future->result;
+ future_free(future);
+ return result;
+}
+
+void future_free(future_t *future)
+{
+ if (!future) {
+ return;
+ }
+
+ if (future->semaphore) {
+ osi_sem_free(&future->semaphore);
+ }
+
+ osi_free(future);
+}
diff --git a/lib/bt/common/osi/hash_functions.c b/lib/bt/common/osi/hash_functions.c
new file mode 100644
index 00000000..d4a39ed4
--- /dev/null
+++ b/lib/bt/common/osi/hash_functions.c
@@ -0,0 +1,63 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+#include <string.h>
+
+#include "osi/hash_functions.h"
+
+hash_index_t hash_function_naive(const void *key)
+{
+ return (hash_index_t)key;
+}
+
+hash_index_t hash_function_integer(const void *key)
+{
+ return ((hash_index_t)key) * 2654435761;
+}
+
+hash_index_t hash_function_pointer(const void *key)
+{
+ return ((hash_index_t)key) * 2654435761;
+}
+
+hash_index_t hash_function_string(const void *key)
+{
+ hash_index_t hash = 5381;
+ const char *name = (const char *)key;
+ size_t string_len = strlen(name);
+ for (size_t i = 0; i < string_len; ++i) {
+ hash = ((hash << 5) + hash ) + name[i];
+ }
+ return hash;
+}
+
+void hash_function_blob(const unsigned char *s, unsigned int len, hash_key_t h)
+{
+ size_t j;
+
+ while (len--) {
+ j = sizeof(hash_key_t)-1;
+
+ while (j) {
+ h[j] = ((h[j] << 7) | (h[j-1] >> 1)) + h[j];
+ --j;
+ }
+
+ h[0] = (h[0] << 7) + h[0] + *s++;
+ }
+}
diff --git a/lib/bt/common/osi/hash_map.c b/lib/bt/common/osi/hash_map.c
new file mode 100644
index 00000000..59942bfd
--- /dev/null
+++ b/lib/bt/common/osi/hash_map.c
@@ -0,0 +1,270 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+#include "bt_common.h"
+#include "osi/list.h"
+#include "osi/hash_map.h"
+#include "osi/allocator.h"
+
+struct hash_map_t;
+
+typedef struct hash_map_bucket_t {
+ list_t *list;
+} hash_map_bucket_t;
+
+typedef struct hash_map_t {
+ hash_map_bucket_t *bucket;
+ size_t num_bucket;
+ size_t hash_size;
+ hash_index_fn hash_fn;
+ key_free_fn key_fn;
+ data_free_fn data_fn;
+ key_equality_fn keys_are_equal;
+} hash_map_t;
+
+// Hidden constructor for list, only to be used by us.
+list_t *list_new_internal(list_free_cb callback);
+
+static void bucket_free_(void *data);
+static bool default_key_equality(const void *x, const void *y);
+static hash_map_entry_t *find_bucket_entry_(list_t *hash_bucket_list,
+ const void *key);
+
+// Hidden constructor, only to be used by the allocation tracker. Behaves the same as
+// |hash_map_new|, except you get to specify the allocator.
+hash_map_t *hash_map_new_internal(
+ size_t num_bucket,
+ hash_index_fn hash_fn,
+ key_free_fn key_fn,
+ data_free_fn data_fn,
+ key_equality_fn equality_fn)
+{
+ assert(hash_fn != NULL);
+ assert(num_bucket > 0);
+ hash_map_t *hash_map = osi_calloc(sizeof(hash_map_t));
+ if (hash_map == NULL) {
+ return NULL;
+ }
+
+ hash_map->hash_fn = hash_fn;
+ hash_map->key_fn = key_fn;
+ hash_map->data_fn = data_fn;
+ hash_map->keys_are_equal = equality_fn ? equality_fn : default_key_equality;
+
+ hash_map->num_bucket = num_bucket;
+ hash_map->bucket = osi_calloc(sizeof(hash_map_bucket_t) * num_bucket);
+ if (hash_map->bucket == NULL) {
+ osi_free(hash_map);
+ return NULL;
+ }
+ return hash_map;
+}
+
+hash_map_t *hash_map_new(
+ size_t num_bucket,
+ hash_index_fn hash_fn,
+ key_free_fn key_fn,
+ data_free_fn data_fn,
+ key_equality_fn equality_fn)
+{
+ return hash_map_new_internal(num_bucket, hash_fn, key_fn, data_fn, equality_fn);
+}
+
+void hash_map_free(hash_map_t *hash_map)
+{
+ if (hash_map == NULL) {
+ return;
+ }
+ hash_map_clear(hash_map);
+ osi_free(hash_map->bucket);
+ osi_free(hash_map);
+}
+
+/*
+bool hash_map_is_empty(const hash_map_t *hash_map) {
+ assert(hash_map != NULL);
+ return (hash_map->hash_size == 0);
+}
+
+size_t hash_map_size(const hash_map_t *hash_map) {
+ assert(hash_map != NULL);
+ return hash_map->hash_size;
+}
+
+size_t hash_map_num_buckets(const hash_map_t *hash_map) {
+ assert(hash_map != NULL);
+ return hash_map->num_bucket;
+}
+*/
+
+bool hash_map_has_key(const hash_map_t *hash_map, const void *key)
+{
+ assert(hash_map != NULL);
+
+ hash_index_t hash_key = hash_map->hash_fn(key) % hash_map->num_bucket;
+ list_t *hash_bucket_list = hash_map->bucket[hash_key].list;
+
+ hash_map_entry_t *hash_map_entry = find_bucket_entry_(hash_bucket_list, key);
+ return (hash_map_entry != NULL);
+}
+
+bool hash_map_set(hash_map_t *hash_map, const void *key, void *data)
+{
+ assert(hash_map != NULL);
+ assert(data != NULL);
+
+ hash_index_t hash_key = hash_map->hash_fn(key) % hash_map->num_bucket;
+
+ if (hash_map->bucket[hash_key].list == NULL) {
+ hash_map->bucket[hash_key].list = list_new_internal(bucket_free_);
+ if (hash_map->bucket[hash_key].list == NULL) {
+ return false;
+ }
+ }
+ list_t *hash_bucket_list = hash_map->bucket[hash_key].list;
+
+ hash_map_entry_t *hash_map_entry = find_bucket_entry_(hash_bucket_list, key);
+
+ if (hash_map_entry) {
+ // Calls hash_map callback to delete the hash_map_entry.
+ bool rc = list_remove(hash_bucket_list, hash_map_entry);
+ assert(rc == true);
+ (void)rc;
+ } else {
+ hash_map->hash_size++;
+ }
+ hash_map_entry = osi_calloc(sizeof(hash_map_entry_t));
+ if (hash_map_entry == NULL) {
+ return false;
+ }
+
+ hash_map_entry->key = key;
+ hash_map_entry->data = data;
+ hash_map_entry->hash_map = hash_map;
+
+ return list_append(hash_bucket_list, hash_map_entry);
+}
+
+bool hash_map_erase(hash_map_t *hash_map, const void *key)
+{
+ assert(hash_map != NULL);
+
+ hash_index_t hash_key = hash_map->hash_fn(key) % hash_map->num_bucket;
+ list_t *hash_bucket_list = hash_map->bucket[hash_key].list;
+
+ hash_map_entry_t *hash_map_entry = find_bucket_entry_(hash_bucket_list, key);
+ if (hash_map_entry == NULL) {
+ return false;
+ }
+
+ hash_map->hash_size--;
+ bool remove = list_remove(hash_bucket_list, hash_map_entry);
+ if(list_is_empty(hash_map->bucket[hash_key].list)) {
+ list_free(hash_map->bucket[hash_key].list);
+ hash_map->bucket[hash_key].list = NULL;
+ }
+
+ return remove;
+}
+
+void *hash_map_get(const hash_map_t *hash_map, const void *key)
+{
+ assert(hash_map != NULL);
+
+ hash_index_t hash_key = hash_map->hash_fn(key) % hash_map->num_bucket;
+ list_t *hash_bucket_list = hash_map->bucket[hash_key].list;
+
+ hash_map_entry_t *hash_map_entry = find_bucket_entry_(hash_bucket_list, key);
+ if (hash_map_entry != NULL) {
+ return hash_map_entry->data;
+ }
+
+ return NULL;
+}
+
+void hash_map_clear(hash_map_t *hash_map)
+{
+ assert(hash_map != NULL);
+
+ for (hash_index_t i = 0; i < hash_map->num_bucket; i++) {
+ if (hash_map->bucket[i].list == NULL) {
+ continue;
+ }
+ list_free(hash_map->bucket[i].list);
+ hash_map->bucket[i].list = NULL;
+ }
+}
+
+void hash_map_foreach(hash_map_t *hash_map, hash_map_iter_cb callback, void *context)
+{
+ assert(hash_map != NULL);
+ assert(callback != NULL);
+
+ for (hash_index_t i = 0; i < hash_map->num_bucket; ++i) {
+ if (hash_map->bucket[i].list == NULL) {
+ continue;
+ }
+ for (const list_node_t *iter = list_begin(hash_map->bucket[i].list);
+ iter != list_end(hash_map->bucket[i].list);
+ iter = list_next(iter)) {
+ hash_map_entry_t *hash_map_entry = (hash_map_entry_t *)list_node(iter);
+ if (!callback(hash_map_entry, context)) {
+ return;
+ }
+ }
+ }
+}
+
+static void bucket_free_(void *data)
+{
+ assert(data != NULL);
+ hash_map_entry_t *hash_map_entry = (hash_map_entry_t *)data;
+ const hash_map_t *hash_map = hash_map_entry->hash_map;
+
+ if (hash_map->key_fn) {
+ hash_map->key_fn((void *)hash_map_entry->key);
+ }
+ if (hash_map->data_fn) {
+ hash_map->data_fn(hash_map_entry->data);
+ }
+ osi_free(hash_map_entry);
+}
+
+static hash_map_entry_t *find_bucket_entry_(list_t *hash_bucket_list,
+ const void *key)
+{
+
+ if (hash_bucket_list == NULL) {
+ return NULL;
+ }
+
+ for (const list_node_t *iter = list_begin(hash_bucket_list);
+ iter != list_end(hash_bucket_list);
+ iter = list_next(iter)) {
+ hash_map_entry_t *hash_map_entry = (hash_map_entry_t *)list_node(iter);
+ if (hash_map_entry->hash_map->keys_are_equal(hash_map_entry->key, key)) {
+ return hash_map_entry;
+ }
+ }
+ return NULL;
+}
+
+static bool default_key_equality(const void *x, const void *y)
+{
+ return x == y;
+}
diff --git a/lib/bt/common/osi/include/osi/alarm.h b/lib/bt/common/osi/include/osi/alarm.h
new file mode 100644
index 00000000..fe8344cd
--- /dev/null
+++ b/lib/bt/common/osi/include/osi/alarm.h
@@ -0,0 +1,84 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+#ifndef _ALARM_H_
+#define _ALARM_H_
+
+#include <stdint.h>
+#include "esp_timer.h"
+
+typedef struct alarm_t osi_alarm_t;
+typedef uint64_t period_ms_t;
+typedef esp_timer_cb_t osi_alarm_callback_t;
+
+typedef enum {
+ OSI_ALARM_ERR_PASS = 0,
+ OSI_ALARM_ERR_FAIL = -1,
+ OSI_ALARM_ERR_INVALID_ARG = -2,
+ OSI_ALARM_ERR_INVALID_STATE = -3,
+} osi_alarm_err_t;
+
+#define ALARM_CBS_NUM 50
+#define ALARM_ID_BASE 1000
+
+int osi_alarm_create_mux(void);
+int osi_alarm_delete_mux(void);
+void osi_alarm_init(void);
+void osi_alarm_deinit(void);
+
+// Creates a new alarm object. The returned object must be freed by calling
+// |alarm_free|. Returns NULL on failure.
+osi_alarm_t *osi_alarm_new(const char *alarm_name, osi_alarm_callback_t callback, void *data, period_ms_t timer_expire);
+
+// Frees an alarm object created by |alarm_new|. |alarm| may be NULL. If the
+// alarm is pending, it will be cancelled. It is not safe to call |alarm_free|
+// from inside the callback of |alarm|.
+void osi_alarm_free(osi_alarm_t *alarm);
+
+// Sets an alarm to fire |cb| after the given |deadline|. Note that |deadline| is the
+// number of milliseconds relative to the current time. |data| is a context variable
+// for the callback and may be NULL. |cb| will be called back in the context of an
+// unspecified thread (i.e. it will not be called back in the same thread as the caller).
+// |alarm| and |cb| may not be NULL.
+osi_alarm_err_t osi_alarm_set(osi_alarm_t *alarm, period_ms_t timeout);
+
+// Sets an periodic alarm to fire |cb| each given |period|.
+osi_alarm_err_t osi_alarm_set_periodic(osi_alarm_t *alarm, period_ms_t period);
+
+// This function cancels the |alarm| if it was previously set. When this call
+// returns, the caller has a guarantee that the callback is not in progress and
+// will not be called if it hasn't already been called. This function is idempotent.
+// |alarm| may not be NULL.
+osi_alarm_err_t osi_alarm_cancel(osi_alarm_t *alarm);
+
+// Figure out how much time until next expiration.
+// Returns 0 if not armed. |alarm| may not be NULL.
+// only for oneshot alarm, not for periodic alarm
+// TODO: Remove this function once PM timers can be re-factored
+period_ms_t osi_alarm_get_remaining_ms(const osi_alarm_t *alarm);
+
+// Alarm-related state cleanup
+//void alarm_cleanup(void);
+
+uint32_t osi_time_get_os_boottime_ms(void);
+
+// This function returns whether the given |alarm| is active or not.
+// Return true if active, false otherwise.
+bool osi_alarm_is_active(osi_alarm_t *alarm);
+
+#endif /*_ALARM_H_*/
diff --git a/lib/bt/common/osi/include/osi/allocator.h b/lib/bt/common/osi/include/osi/allocator.h
new file mode 100644
index 00000000..579f2b2b
--- /dev/null
+++ b/lib/bt/common/osi/include/osi/allocator.h
@@ -0,0 +1,145 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+#ifndef _ALLOCATOR_H_
+#define _ALLOCATOR_H_
+
+#include <stddef.h>
+#include <stdlib.h>
+#include "esp_heap_caps.h"
+
+char *osi_strdup(const char *str);
+
+void *osi_malloc_func(size_t size);
+void *osi_calloc_func(size_t size);
+void osi_free_func(void *ptr);
+
+#if HEAP_MEMORY_DEBUG
+
+void osi_mem_dbg_init(void);
+void osi_mem_dbg_record(void *p, int size, const char *func, int line);
+void osi_mem_dbg_clean(void *p, const char *func, int line);
+void osi_mem_dbg_show(void);
+uint32_t osi_mem_dbg_get_max_size(void);
+uint32_t osi_mem_dbg_get_current_size(void);
+void osi_men_dbg_set_section_start(uint8_t index);
+void osi_men_dbg_set_section_end(uint8_t index);
+uint32_t osi_mem_dbg_get_max_size_section(uint8_t index);
+
+#if HEAP_ALLOCATION_FROM_SPIRAM_FIRST
+#define osi_malloc(size) \
+({ \
+ void *p; \
+ p = heap_caps_malloc_prefer(size, 2, \
+ MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, \
+ MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL); \
+ osi_mem_dbg_record(p, size, __func__, __LINE__); \
+ (void *)p; \
+})
+
+#define osi_calloc(size) \
+({ \
+ void *p; \
+ p = heap_caps_calloc_prefer(1, size, 2, \
+ MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, \
+ MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL); \
+ osi_mem_dbg_record(p, size, __func__, __LINE__); \
+ (void *)p; \
+})
+
+#else
+
+#define osi_malloc(size) \
+({ \
+ void *p; \
+ p = malloc((size)); \
+ osi_mem_dbg_record(p, size, __func__, __LINE__); \
+ (void *)p; \
+})
+
+#define osi_calloc(size) \
+({ \
+ void *p; \
+ p = calloc(1, (size)); \
+ osi_mem_dbg_record(p, size, __func__, __LINE__); \
+ (void *)p; \
+})
+
+#endif /* #if HEAP_ALLOCATION_FROM_SPIRAM_FIRST */
+
+
+#if 0
+#define osi_malloc(size) \
+do { \
+ void *p; \
+ \
+#if HEAP_ALLOCATION_FROM_SPIRAM_FIRST \
+ p = heap_caps_malloc_prefer(size, 2, MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL); \
+#else \
+ p = malloc((size)); \
+#endif /* #if HEAP_ALLOCATION_FROM_SPIRAM_FIRST */ \
+ osi_mem_dbg_record(p, size, __func__, __LINE__); \
+ (void *)p; \
+}while(0)
+
+#define osi_calloc(size) \
+do { \
+ void *p; \
+ \
+#if HEAP_ALLOCATION_FROM_SPIRAM_FIRST \
+ p = heap_caps_calloc_prefer(1, size, 2, \
+ MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, \
+ MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL); \
+#else \
+ p = calloc(1, (size)); \
+#endif /* #if HEAP_ALLOCATION_FROM_SPIRAM_FIRST */ \
+ osi_mem_dbg_record(p, size, __func__, __LINE__); \
+ (void *)p; \
+} while(0)
+#endif
+
+#define osi_free(ptr) \
+do { \
+ void *tmp_point = (void *)(ptr); \
+ osi_mem_dbg_clean(tmp_point, __func__, __LINE__); \
+ free(tmp_point); \
+} while (0)
+
+#else
+
+#if HEAP_ALLOCATION_FROM_SPIRAM_FIRST
+#define osi_malloc(size) heap_caps_malloc_prefer(size, 2, MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL)
+#define osi_calloc(size) heap_caps_calloc_prefer(1, size, 2, MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL)
+#else
+#define osi_malloc(size) malloc((size))
+#define osi_calloc(size) calloc(1, (size))
+#endif /* #if HEAP_ALLOCATION_FROM_SPIRAM_FIRST */
+#define osi_free(p) free((p))
+
+#endif /* HEAP_MEMORY_DEBUG */
+
+#define FREE_AND_RESET(a) \
+do { \
+ if (a) { \
+ osi_free(a); \
+ a = NULL; \
+ } \
+}while (0)
+
+
+#endif /* _ALLOCATOR_H_ */
diff --git a/lib/bt/common/osi/include/osi/buffer.h b/lib/bt/common/osi/include/osi/buffer.h
new file mode 100644
index 00000000..fd1b2fa3
--- /dev/null
+++ b/lib/bt/common/osi/include/osi/buffer.h
@@ -0,0 +1,59 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+#ifndef _BUFFER_H_
+#define _BUFFER_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+
+typedef struct buffer_t buffer_t;
+
+// Returns a new buffer of |size| bytes. Returns NULL if a buffer could not be
+// allocated. |size| must be non-zero. The caller must release this buffer with
+// |buffer_free|.
+buffer_t *buffer_new(size_t size);
+
+// Creates a new reference to the buffer |buf|. A reference is indistinguishable
+// from the original: writes to the original will be reflected in the reference
+// and vice versa. In other words, this function creates an alias to |buf|. The
+// caller must release the returned buffer with |buffer_free|. Note that releasing
+// the returned buffer does not release |buf|. |buf| must not be NULL.
+buffer_t *buffer_new_ref(const buffer_t *buf);
+
+// Creates a new reference to the last |slice_size| bytes of |buf|. See
+// |buffer_new_ref| for a description of references. |slice_size| must be
+// greater than 0 and may be at most |buffer_length|
+// (0 < slice_size <= buffer_length). |buf| must not be NULL.
+buffer_t *buffer_new_slice(const buffer_t *buf, size_t slice_size);
+
+// Frees a buffer object. |buf| may be NULL.
+void buffer_free(buffer_t *buf);
+
+// Returns a pointer to a writeable memory region for |buf|. All references
+// and slices that share overlapping bytes will also be written to when
+// writing to the returned pointer. The caller may safely write up to
+// |buffer_length| consecutive bytes starting at the address returned by
+// this function. |buf| must not be NULL.
+void *buffer_ptr(const buffer_t *buf);
+
+// Returns the length of the writeable memory region referred to by |buf|.
+// |buf| must not be NULL.
+size_t buffer_length(const buffer_t *buf);
+
+#endif /*_BUFFER_H_*/
diff --git a/lib/bt/common/osi/include/osi/config.h b/lib/bt/common/osi/include/osi/config.h
new file mode 100644
index 00000000..ce538282
--- /dev/null
+++ b/lib/bt/common/osi/include/osi/config.h
@@ -0,0 +1,145 @@
+/*
+ * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __CONFIG_H__
+#define __CONFIG_H__
+
+// This module implements a configuration parser. Clients can query the
+// contents of a configuration file through the interface provided here.
+// The current implementation is read-only; mutations are only kept in
+// memory. This parser supports the INI file format.
+
+// Implementation notes:
+// - Key/value pairs that are not within a section are assumed to be under
+// the |CONFIG_DEFAULT_SECTION| section.
+// - Multiple sections with the same name will be merged as if they were in
+// a single section.
+// - Empty sections with no key/value pairs will be treated as if they do
+// not exist. In other words, |config_has_section| will return false for
+// empty sections.
+// - Duplicate keys in a section will overwrite previous values.
+// - All strings are case sensitive.
+
+#include <stdbool.h>
+
+// The default section name to use if a key/value pair is not defined within
+// a section.
+#define CONFIG_DEFAULT_SECTION "Global"
+
+typedef struct config_t config_t;
+typedef struct config_section_node_t config_section_node_t;
+
+// Creates a new config object with no entries (i.e. not backed by a file).
+// This function returns a config object or NULL on error. Clients must call
+// |config_free| on the returned handle when it is no longer required.
+config_t *config_new_empty(void);
+
+// Loads the specified file and returns a handle to the config file. If there
+// was a problem loading the file or allocating memory, this function returns
+// NULL. Clients must call |config_free| on the returned handle when it is no
+// longer required. |filename| must not be NULL and must point to a readable
+// file on the filesystem.
+config_t *config_new(const char *filename);
+
+// Frees resources associated with the config file. No further operations may
+// be performed on the |config| object after calling this function. |config|
+// may be NULL.
+void config_free(config_t *config);
+
+// Returns true if the config file contains a section named |section|. If
+// the section has no key/value pairs in it, this function will return false.
+// |config| and |section| must not be NULL.
+bool config_has_section(const config_t *config, const char *section);
+
+// Returns true if the config file has a key named |key| under |section|.
+// Returns false otherwise. |config|, |section|, and |key| must not be NULL.
+bool config_has_key(const config_t *config, const char *section, const char *key);
+
+// Returns true if the config file has a key named |key| and the key_value.
+// Returns false otherwise. |config|, |key|, and |key_value| must not be NULL.
+bool config_has_key_in_section(config_t *config, const char *key, char *key_value);
+
+// Returns the integral value for a given |key| in |section|. If |section|
+// or |key| do not exist, or the value cannot be fully converted to an integer,
+// this function returns |def_value|. |config|, |section|, and |key| must not
+// be NULL.
+int config_get_int(const config_t *config, const char *section, const char *key, int def_value);
+
+// Returns the boolean value for a given |key| in |section|. If |section|
+// or |key| do not exist, or the value cannot be converted to a boolean, this
+// function returns |def_value|. |config|, |section|, and |key| must not be NULL.
+bool config_get_bool(const config_t *config, const char *section, const char *key, bool def_value);
+
+// Returns the string value for a given |key| in |section|. If |section| or
+// |key| do not exist, this function returns |def_value|. The returned string
+// is owned by the config module and must not be freed. |config|, |section|,
+// and |key| must not be NULL. |def_value| may be NULL.
+const char *config_get_string(const config_t *config, const char *section, const char *key, const char *def_value);
+
+// Sets an integral value for the |key| in |section|. If |key| or |section| do
+// not already exist, this function creates them. |config|, |section|, and |key|
+// must not be NULL.
+void config_set_int(config_t *config, const char *section, const char *key, int value);
+
+// Sets a boolean value for the |key| in |section|. If |key| or |section| do
+// not already exist, this function creates them. |config|, |section|, and |key|
+// must not be NULL.
+void config_set_bool(config_t *config, const char *section, const char *key, bool value);
+
+// Sets a string value for the |key| in |section|. If |key| or |section| do
+// not already exist, this function creates them. |config|, |section|, |key|, and
+// |value| must not be NULL.
+void config_set_string(config_t *config, const char *section, const char *key, const char *value, bool insert_back);
+
+// Removes |section| from the |config| (and, as a result, all keys in the section).
+// Returns true if |section| was found and removed from |config|, false otherwise.
+// Neither |config| nor |section| may be NULL.
+bool config_remove_section(config_t *config, const char *section);
+
+// Updates |section| to be the first section in |config|. Return true if |section| is in
+// |config| and updated successfully, false otherwise.
+// Neither |config| nor |section| may be NULL.
+bool config_update_newest_section(config_t *config, const char *section);
+
+// Removes one specific |key| residing in |section| of the |config|. Returns true
+// if the section and key were found and the key was removed, false otherwise.
+// None of |config|, |section|, or |key| may be NULL.
+bool config_remove_key(config_t *config, const char *section, const char *key);
+
+// Returns an iterator to the first section in the config file. If there are no
+// sections, the iterator will equal the return value of |config_section_end|.
+// The returned pointer must be treated as an opaque handle and must not be freed.
+// The iterator is invalidated on any config mutating operation. |config| may not
+// be NULL.
+const config_section_node_t *config_section_begin(const config_t *config);
+
+// Returns an iterator to one past the last section in the config file. It does not
+// represent a valid section, but can be used to determine if all sections have been
+// iterated over. The returned pointer must be treated as an opaque handle and must
+// not be freed and must not be iterated on (must not call |config_section_next| on
+// it). |config| may not be NULL.
+const config_section_node_t *config_section_end(const config_t *config);
+
+// Moves |iter| to the next section. If there are no more sections, |iter| will
+// equal the value of |config_section_end|. |iter| may not be NULL and must be
+// a pointer returned by either |config_section_begin| or |config_section_next|.
+const config_section_node_t *config_section_next(const config_section_node_t *iter);
+
+// Returns the name of the section referred to by |iter|. The returned pointer is
+// owned by the config module and must not be freed by the caller. The pointer will
+// remain valid until |config_free| is called. |iter| may not be NULL and must not
+// equal the value returned by |config_section_end|.
+const char *config_section_name(const config_section_node_t *iter);
+
+// Saves |config| to a file given by |filename|. Note that this could be a destructive
+// operation: if |filename| already exists, it will be overwritten. The config
+// module does not preserve comments or formatting so if a config file was opened
+// with |config_new| and subsequently overwritten with |config_save|, all comments
+// and special formatting in the original file will be lost. Neither |config| nor
+// |filename| may be NULL.
+bool config_save(const config_t *config, const char *filename);
+
+#endif /* #ifndef __CONFIG_H__ */
diff --git a/lib/bt/common/osi/include/osi/fixed_pkt_queue.h b/lib/bt/common/osi/include/osi/fixed_pkt_queue.h
new file mode 100644
index 00000000..f4235ca5
--- /dev/null
+++ b/lib/bt/common/osi/include/osi/fixed_pkt_queue.h
@@ -0,0 +1,79 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef _FIXED_PKT_QUEUE_H_
+#define _FIXED_PKT_QUEUE_H_
+
+
+#include "osi/pkt_queue.h"
+#include "osi/semaphore.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef FIXED_PKT_QUEUE_SIZE_MAX
+#define FIXED_PKT_QUEUE_SIZE_MAX 254
+#endif
+
+#define FIXED_PKT_QUEUE_MAX_TIMEOUT OSI_SEM_MAX_TIMEOUT
+
+struct fixed_pkt_queue_t;
+
+typedef struct fixed_pkt_queue_t fixed_pkt_queue_t;
+
+typedef void (*fixed_pkt_queue_free_cb)(pkt_linked_item_t *data);
+typedef void (*fixed_pkt_queue_cb)(fixed_pkt_queue_t *queue);
+
+// Creates a new fixed queue with the given |capacity|. If more elements than
+// |capacity| are added to the queue, the caller is blocked until space is
+// made available in the queue. Returns NULL on failure. The caller must free
+// the returned queue with |fixed_pkt_queue_free|.
+fixed_pkt_queue_t *fixed_pkt_queue_new(size_t capacity);
+
+// Freeing a queue that is currently in use (i.e. has waiters
+// blocked on it) results in undefined behaviour.
+void fixed_pkt_queue_free(fixed_pkt_queue_t *queue, fixed_pkt_queue_free_cb free_cb);
+
+// Returns a value indicating whether the given |queue| is empty. If |queue|
+// is NULL, the return value is true.
+bool fixed_pkt_queue_is_empty(fixed_pkt_queue_t *queue);
+
+// Returns the length of the |queue|. If |queue| is NULL, the return value
+// is 0.
+size_t fixed_pkt_queue_length(fixed_pkt_queue_t *queue);
+
+// Returns the maximum number of elements this queue may hold. |queue| may
+// not be NULL.
+size_t fixed_pkt_queue_capacity(fixed_pkt_queue_t *queue);
+
+// Enqueues the given |data| into the |queue|. The caller will be blocked or immediately return or wait for timeout according to the parameter timeout.
+// If enqueue failed, it will return false, otherwise return true
+bool fixed_pkt_queue_enqueue(fixed_pkt_queue_t *queue, pkt_linked_item_t *linked_pkt, uint32_t timeout);
+
+// Dequeues the next element from |queue|. If the queue is currently empty,
+// this function will block the caller until an item is enqueued or immediately return or wait for timeout according to the parameter timeout.
+// If dequeue failed, it will return NULL, otherwise return a point.
+pkt_linked_item_t *fixed_pkt_queue_dequeue(fixed_pkt_queue_t *queue, uint32_t timeout);
+
+// Returns the first element from |queue|, if present, without dequeuing it.
+// This function will never block the caller. Returns NULL if there are no
+// elements in the queue or |queue| is NULL.
+pkt_linked_item_t *fixed_pkt_queue_try_peek_first(fixed_pkt_queue_t *queue);
+
+// Registers |queue| with |reactor| for dequeue operations. When there is an element
+// in the queue, ready_cb will be called. The |context| parameter is passed, untouched,
+// to the callback routine. Neither |queue|, nor |reactor|, nor |read_cb| may be NULL.
+// |context| may be NULL.
+void fixed_pkt_queue_register_dequeue(fixed_pkt_queue_t *queue, fixed_pkt_queue_cb ready_cb);
+
+// Unregisters the dequeue ready callback for |queue| from whichever reactor
+// it is registered with, if any. This function is idempotent.
+void fixed_pkt_queue_unregister_dequeue(fixed_pkt_queue_t *queue);
+
+void fixed_pkt_queue_process(fixed_pkt_queue_t *queue);
+
+#endif
diff --git a/lib/bt/common/osi/include/osi/fixed_queue.h b/lib/bt/common/osi/include/osi/fixed_queue.h
new file mode 100644
index 00000000..a25e6039
--- /dev/null
+++ b/lib/bt/common/osi/include/osi/fixed_queue.h
@@ -0,0 +1,125 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+#ifndef _FIXED_QUEUE_H_
+#define _FIXED_QUEUE_H_
+
+#include <stdbool.h>
+#include "osi/list.h"
+#include "osi/semaphore.h"
+
+#ifndef QUEUE_SIZE_MAX
+#define QUEUE_SIZE_MAX 254
+#endif
+
+#define FIXED_QUEUE_MAX_TIMEOUT OSI_SEM_MAX_TIMEOUT
+
+struct fixed_queue_t;
+
+typedef struct fixed_queue_t fixed_queue_t;
+//typedef struct reactor_t reactor_t;
+
+typedef void (*fixed_queue_free_cb)(void *data);
+typedef void (*fixed_queue_cb)(fixed_queue_t *queue);
+
+// Creates a new fixed queue with the given |capacity|. If more elements than
+// |capacity| are added to the queue, the caller is blocked until space is
+// made available in the queue. Returns NULL on failure. The caller must free
+// the returned queue with |fixed_queue_free|.
+fixed_queue_t *fixed_queue_new(size_t capacity);
+
+// Freeing a queue that is currently in use (i.e. has waiters
+// blocked on it) results in undefined behaviour.
+void fixed_queue_free(fixed_queue_t *queue, fixed_queue_free_cb free_cb);
+
+// Returns a value indicating whether the given |queue| is empty. If |queue|
+// is NULL, the return value is true.
+bool fixed_queue_is_empty(fixed_queue_t *queue);
+
+// Returns the length of the |queue|. If |queue| is NULL, the return value
+// is 0.
+size_t fixed_queue_length(fixed_queue_t *queue);
+
+// Returns the maximum number of elements this queue may hold. |queue| may
+// not be NULL.
+size_t fixed_queue_capacity(fixed_queue_t *queue);
+
+// Enqueues the given |data| into the |queue|. The caller will be blocked or immediately return or wait for timeout according to the parameter timeout.
+// If enqueue failed, it will return false, otherwise return true
+bool fixed_queue_enqueue(fixed_queue_t *queue, void *data, uint32_t timeout);
+
+// Dequeues the next element from |queue|. If the queue is currently empty,
+// this function will block the caller until an item is enqueued or immediately return or wait for timeout according to the parameter timeout.
+// If dequeue failed, it will return NULL, otherwise return a point.
+void *fixed_queue_dequeue(fixed_queue_t *queue, uint32_t timeout);
+
+// Returns the first element from |queue|, if present, without dequeuing it.
+// This function will never block the caller. Returns NULL if there are no
+// elements in the queue or |queue| is NULL.
+void *fixed_queue_try_peek_first(fixed_queue_t *queue);
+
+// Returns the last element from |queue|, if present, without dequeuing it.
+// This function will never block the caller. Returns NULL if there are no
+// elements in the queue or |queue| is NULL.
+void *fixed_queue_try_peek_last(fixed_queue_t *queue);
+
+// Tries to remove a |data| element from the middle of the |queue|. This
+// function will never block the caller. If the queue is empty or NULL, this
+// function returns NULL immediately. |data| may not be NULL. If the |data|
+// element is found in the queue, a pointer to the removed data is returned,
+// otherwise NULL.
+void *fixed_queue_try_remove_from_queue(fixed_queue_t *queue, void *data);
+
+// Returns the iterateable list with all entries in the |queue|. This function
+// will never block the caller. |queue| may not be NULL.
+//
+// NOTE: The return result of this function is not thread safe: the list could
+// be modified by another thread, and the result would be unpredictable.
+// TODO: The usage of this function should be refactored, and the function
+// itself should be removed.
+list_t *fixed_queue_get_list(fixed_queue_t *queue);
+
+// This function returns a valid file descriptor. Callers may perform one
+// operation on the fd: select(2). If |select| indicates that the file
+// descriptor is readable, the caller may call |fixed_queue_enqueue| without
+// blocking. The caller must not close the returned file descriptor. |queue|
+// may not be NULL.
+//int fixed_queue_get_enqueue_fd(const fixed_queue_t *queue);
+
+// This function returns a valid file descriptor. Callers may perform one
+// operation on the fd: select(2). If |select| indicates that the file
+// descriptor is readable, the caller may call |fixed_queue_dequeue| without
+// blocking. The caller must not close the returned file descriptor. |queue|
+// may not be NULL.
+//int fixed_queue_get_dequeue_fd(const fixed_queue_t *queue);
+
+// Registers |queue| with |reactor| for dequeue operations. When there is an element
+// in the queue, ready_cb will be called. The |context| parameter is passed, untouched,
+// to the callback routine. Neither |queue|, nor |reactor|, nor |read_cb| may be NULL.
+// |context| may be NULL.
+void fixed_queue_register_dequeue(fixed_queue_t *queue, fixed_queue_cb ready_cb);
+
+// Unregisters the dequeue ready callback for |queue| from whichever reactor
+// it is registered with, if any. This function is idempotent.
+void fixed_queue_unregister_dequeue(fixed_queue_t *queue);
+
+void fixed_queue_process(fixed_queue_t *queue);
+
+list_t *fixed_queue_get_list(fixed_queue_t *queue);
+
+#endif
diff --git a/lib/bt/common/osi/include/osi/future.h b/lib/bt/common/osi/include/osi/future.h
new file mode 100644
index 00000000..9d1cb521
--- /dev/null
+++ b/lib/bt/common/osi/include/osi/future.h
@@ -0,0 +1,53 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+#ifndef __FUTURE_H__
+#define __FUTURE_H__
+
+#include "osi/semaphore.h"
+
+struct future {
+ bool ready_can_be_called;
+ osi_sem_t semaphore; // NULL semaphore means immediate future
+ void *result;
+};
+typedef struct future future_t;
+
+#define FUTURE_SUCCESS ((void *)1)
+#define FUTURE_FAIL ((void *)0)
+
+// Constructs a new future_t object. Returns NULL on failure.
+future_t *future_new(void);
+
+// Constructs a new future_t object with an immediate |value|. No waiting will
+// occur in the call to |future_await| because the value is already present.
+// Returns NULL on failure.
+future_t *future_new_immediate(void *value);
+
+// Signals that the |future| is ready, passing |value| back to the context
+// waiting for the result. Must only be called once for every future.
+// |future| may not be NULL.
+void future_ready(future_t *future, void *value);
+
+// Waits for the |future| to be ready. Returns the value set in |future_ready|.
+// Frees the future before return. |future| may not be NULL.
+void *future_await(future_t *async_result);
+
+//Free the future if this "future" is not used
+void future_free(future_t *future);
+#endif /* __FUTURE_H__ */
diff --git a/lib/bt/common/osi/include/osi/hash_functions.h b/lib/bt/common/osi/include/osi/hash_functions.h
new file mode 100644
index 00000000..8102a0c1
--- /dev/null
+++ b/lib/bt/common/osi/include/osi/hash_functions.h
@@ -0,0 +1,37 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+#ifndef _HASH_FUNCTIONS_H_
+#define _HASH_FUNCTIONS_H_
+
+#include "osi/hash_map.h"
+
+typedef unsigned char hash_key_t[4];
+
+hash_index_t hash_function_naive(const void *key);
+
+hash_index_t hash_function_integer(const void *key);
+
+// Hashes a pointer based only on its address value
+hash_index_t hash_function_pointer(const void *key);
+
+hash_index_t hash_function_string(const void *key);
+
+void hash_function_blob(const unsigned char *s, unsigned int len, hash_key_t h);
+
+#endif /* _HASH_FUNCTIONS_H_ */
diff --git a/lib/bt/common/osi/include/osi/hash_map.h b/lib/bt/common/osi/include/osi/hash_map.h
new file mode 100644
index 00000000..fea1e021
--- /dev/null
+++ b/lib/bt/common/osi/include/osi/hash_map.h
@@ -0,0 +1,110 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+#ifndef _HASH_MAP_H_
+#define _HASH_MAP_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+struct hash_map_t;
+typedef struct hash_map_t hash_map_t;
+
+typedef struct hash_map_entry_t {
+ const void *key;
+ void *data;
+ const hash_map_t *hash_map;
+} hash_map_entry_t;
+
+typedef size_t hash_index_t;
+
+// Takes a key structure and returns a hash value.
+typedef hash_index_t (*hash_index_fn)(const void *key);
+typedef bool (*hash_map_iter_cb)(hash_map_entry_t *hash_entry, void *context);
+
+typedef bool (*key_equality_fn)(const void *x, const void *y);
+
+typedef void (*key_free_fn)(void *data);
+typedef void (*data_free_fn)(void *data);
+
+// Returns a new, empty hash_map. Returns NULL if not enough memory could be allocated
+// for the hash_map structure. The returned hash_map must be freed with |hash_map_free|.
+// The |num_bucket| specifies the number of hashable buckets for the map and must not
+// be zero. The |hash_fn| specifies a hash function to be used and must not be NULL.
+// The |key_fn| and |data_fn| are called whenever a hash_map element is removed from
+// the hash_map. They can be used to release resources held by the hash_map element,
+// e.g. memory or file descriptor. |key_fn| and |data_fn| may be NULL if no cleanup
+// is necessary on element removal. |equality_fn| is used to check for key equality.
+// If |equality_fn| is NULL, default pointer equality is used.
+hash_map_t *hash_map_new(
+ size_t size,
+ hash_index_fn hash_fn,
+ key_free_fn key_fn,
+ data_free_fn data_fn,
+ key_equality_fn equality_fn);
+
+// Frees the hash_map. This function accepts NULL as an argument, in which case it
+// behaves like a no-op.
+void hash_map_free(hash_map_t *hash_map);
+
+// Returns true if the hash_map is empty (has no elements), false otherwise.
+// Note that a NULL |hash_map| is not the same as an empty |hash_map|. This function
+// does not accept a NULL |hash_map|.
+//bool hash_map_is_empty(const hash_map_t *hash_map);
+
+// Returns the number of elements in the hash map. This function does not accept a
+// NULL |hash_map|.
+//size_t hash_map_size(const hash_map_t *hash_map);
+
+// Returns the number of buckets in the hash map. This function does not accept a
+// NULL |hash_map|.
+//size_t hash_map_num_buckets(const hash_map_t *hash_map);
+
+// Returns true if the hash_map has a valid entry for the presented key.
+// This function does not accept a NULL |hash_map|.
+bool hash_map_has_key(const hash_map_t *hash_map, const void *key);
+
+// Returns the element indexed by |key| in the hash_map without removing it. |hash_map|
+// may not be NULL. Returns NULL if no entry indexed by |key|.
+void *hash_map_get(const hash_map_t *hash_map, const void *key);
+
+// Sets the value |data| indexed by |key| into the |hash_map|. Neither |data| nor
+// |hash_map| may be NULL. This function does not make copies of |data| nor |key|
+// so the pointers must remain valid at least until the element is removed from the
+// hash_map or the hash_map is freed. Returns true if |data| could be set, false
+// otherwise (e.g. out of memory).
+bool hash_map_set(hash_map_t *hash_map, const void *key, void *data);
+
+// Removes data indexed by |key| from the hash_map. |hash_map| may not be NULL.
+// If |key_fn| or |data_fn| functions were specified in |hash_map_new|, they
+// will be called back with |key| or |data| respectively. This function returns true
+// if |key| was found in the hash_map and removed, false otherwise.
+bool hash_map_erase(hash_map_t *hash_map, const void *key);
+
+// Removes all elements in the hash_map. Calling this function will return the hash_map
+// to the same state it was in after |hash_map_new|. |hash_map| may not be NULL.
+void hash_map_clear(hash_map_t *hash_map);
+
+// Iterates through the entire |hash_map| and calls |callback| for each data
+// element and passes through the |context| argument. If the hash_map is
+// empty, |callback| will never be called. It is not safe to mutate the
+// hash_map inside the callback. Neither |hash_map| nor |callback| may be NULL.
+// If |callback| returns false, the iteration loop will immediately exit.
+void hash_map_foreach(hash_map_t *hash_map, hash_map_iter_cb callback, void *context);
+
+#endif /* _HASH_MAP_H_ */
diff --git a/lib/bt/common/osi/include/osi/list.h b/lib/bt/common/osi/include/osi/list.h
new file mode 100644
index 00000000..f066a1f2
--- /dev/null
+++ b/lib/bt/common/osi/include/osi/list.h
@@ -0,0 +1,121 @@
+#ifndef _LIST_H_
+#define _LIST_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+struct list_node_t;
+typedef struct list_node_t list_node_t;
+
+struct list_t;
+typedef struct list_t list_t;
+
+typedef void (*list_free_cb)(void *data);
+typedef bool (*list_iter_cb)(void *data, void *context);
+
+// Returns a new, empty list. Returns NULL if not enough memory could be allocated
+// for the list structure. The returned list must be freed with |list_free|. The
+// |callback| specifies a function to be called whenever a list element is removed
+// from the list. It can be used to release resources held by the list element, e.g.
+// memory or file descriptor. |callback| may be NULL if no cleanup is necessary on
+// element removal.
+list_t *list_new(list_free_cb callback);
+
+
+list_node_t *list_free_node(list_t *list, list_node_t *node);
+
+// similar with list_free_node, this function doesn't free the node data
+list_node_t *list_delete_node(list_t *list, list_node_t *node);
+
+// Frees the list. This function accepts NULL as an argument, in which case it
+// behaves like a no-op.
+void list_free(list_t *list);
+
+// Returns true if |list| is empty (has no elements), false otherwise.
+// |list| may not be NULL.
+bool list_is_empty(const list_t *list);
+
+// Returns true if the list contains |data|, false otherwise.
+// |list| may not be NULL.
+bool list_contains(const list_t *list, const void *data);
+
+// Returns list_node which contains |data|, NULL otherwise.
+// |list| may not be NULL.
+list_node_t *list_get_node(const list_t *list, const void *data);
+
+// Returns the length of the |list|. |list| may not be NULL.
+size_t list_length(const list_t *list);
+
+// Returns the first element in the list without removing it. |list| may not
+// be NULL or empty.
+void *list_front(const list_t *list);
+
+// Returns the last element in the list without removing it. |list| may not
+// be NULL or empty.
+void *list_back(const list_t *list);
+list_node_t *list_back_node(const list_t *list);
+
+// Inserts |data| after |prev_node| in |list|. |data|, |list|, and |prev_node|
+// may not be NULL. This function does not make a copy of |data| so the pointer
+// must remain valid at least until the element is removed from the list or the
+// list is freed. Returns true if |data| could be inserted, false otherwise
+// (e.g. out of memory).
+bool list_insert_after(list_t *list, list_node_t *prev_node, void *data);
+
+// Inserts |data| at the beginning of |list|. Neither |data| nor |list| may be NULL.
+// This function does not make a copy of |data| so the pointer must remain valid
+// at least until the element is removed from the list or the list is freed.
+// Returns true if |data| could be inserted, false otherwise (e.g. out of memory).
+bool list_prepend(list_t *list, void *data);
+
+// Inserts |data| at the end of |list|. Neither |data| nor |list| may be NULL.
+// This function does not make a copy of |data| so the pointer must remain valid
+// at least until the element is removed from the list or the list is freed.
+// Returns true if |data| could be inserted, false otherwise (e.g. out of memory).
+bool list_append(list_t *list, void *data);
+
+// Removes |data| from the list. Neither |list| nor |data| may be NULL. If |data|
+// is inserted multiple times in the list, this function will only remove the first
+// instance. If a free function was specified in |list_new|, it will be called back
+// with |data|. This function returns true if |data| was found in the list and removed,
+// false otherwise.
+//list_node_t list_remove_node(list_t *list, list_node_t *prev_node, list_node_t *node);
+//list_node_t list_insert_node(list_t *list, list_node_t *prev_node, list_node_t *node);
+
+bool list_remove(list_t *list, void *data);
+
+// similar with list_remove, but do not free the node data
+bool list_delete(list_t *list, void *data);
+
+// Removes all elements in the list. Calling this function will return the list to the
+// same state it was in after |list_new|. |list| may not be NULL.
+void list_clear(list_t *list);
+
+// Iterates through the entire |list| and calls |callback| for each data element.
+// If the list is empty, |callback| will never be called. It is safe to mutate the
+// list inside the callback. If an element is added before the node being visited,
+// there will be no callback for the newly-inserted node. Neither |list| nor
+// |callback| may be NULL.
+list_node_t *list_foreach(const list_t *list, list_iter_cb callback, void *context);
+
+// Returns an iterator to the first element in |list|. |list| may not be NULL.
+// The returned iterator is valid as long as it does not equal the value returned
+// by |list_end|.
+list_node_t *list_begin(const list_t *list);
+
+// Returns an iterator that points past the end of the list. In other words,
+// this function returns the value of an invalid iterator for the given list.
+// When an iterator has the same value as what's returned by this function, you
+// may no longer call |list_next| with the iterator. |list| may not be NULL.
+list_node_t *list_end(const list_t *list);
+
+// Given a valid iterator |node|, this function returns the next value for the
+// iterator. If the returned value equals the value returned by |list_end|, the
+// iterator has reached the end of the list and may no longer be used for any
+// purpose.
+list_node_t *list_next(const list_node_t *node);
+
+// Returns the value stored at the location pointed to by the iterator |node|.
+// |node| must not equal the value returned by |list_end|.
+void *list_node(const list_node_t *node);
+
+#endif /* _LIST_H_ */
diff --git a/lib/bt/common/osi/include/osi/mutex.h b/lib/bt/common/osi/include/osi/mutex.h
new file mode 100644
index 00000000..d0fde11e
--- /dev/null
+++ b/lib/bt/common/osi/include/osi/mutex.h
@@ -0,0 +1,52 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+#ifndef __MUTEX_H__
+#define __MUTEX_H__
+
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/queue.h"
+#include "freertos/semphr.h"
+#include "osi/semaphore.h"
+
+#define OSI_MUTEX_MAX_TIMEOUT OSI_SEM_MAX_TIMEOUT
+
+#define osi_mutex_valid( x ) ( ( ( *x ) == NULL) ? pdFALSE : pdTRUE )
+#define osi_mutex_set_invalid( x ) ( ( *x ) = NULL )
+
+typedef SemaphoreHandle_t osi_mutex_t;
+
+int osi_mutex_new(osi_mutex_t *mutex);
+
+int osi_mutex_lock(osi_mutex_t *mutex, uint32_t timeout);
+
+void osi_mutex_unlock(osi_mutex_t *mutex);
+
+void osi_mutex_free(osi_mutex_t *mutex);
+
+/* Just for a global mutex */
+int osi_mutex_global_init(void);
+
+void osi_mutex_global_deinit(void);
+
+void osi_mutex_global_lock(void);
+
+void osi_mutex_global_unlock(void);
+
+#endif /* __MUTEX_H__ */
diff --git a/lib/bt/common/osi/include/osi/osi.h b/lib/bt/common/osi/include/osi/osi.h
new file mode 100644
index 00000000..3bd217af
--- /dev/null
+++ b/lib/bt/common/osi/include/osi/osi.h
@@ -0,0 +1,16 @@
+
+#ifndef _OSI_H_
+#define _OSI_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define UNUSED_ATTR __attribute__((unused))
+
+#define CONCAT(a, b) a##b
+#define COMPILE_ASSERT(x)
+
+int osi_init(void);
+void osi_deinit(void);
+
+#endif /*_OSI_H_*/
diff --git a/lib/bt/common/osi/include/osi/pkt_queue.h b/lib/bt/common/osi/include/osi/pkt_queue.h
new file mode 100644
index 00000000..96277c3e
--- /dev/null
+++ b/lib/bt/common/osi/include/osi/pkt_queue.h
@@ -0,0 +1,88 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef _PKT_LIST_H_
+#define _PKT_LIST_H_
+
+#include "sys/queue.h"
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct pkt_queue;
+
+typedef struct pkt_linked_item {
+ STAILQ_ENTRY(pkt_linked_item) next;
+ uint8_t data[];
+} pkt_linked_item_t;
+
+#define BT_PKT_LINKED_HDR_SIZE (sizeof (pkt_linked_item_t))
+
+typedef void (*pkt_queue_free_cb)(pkt_linked_item_t *item);
+
+/*
+ * brief: create a pkt_queue instance. pkt_queue is a wrapper class of a FIFO implemented by single linked list.
+ * The enqueue and dequeue operations of the FIFO are protected against race conditions of multiple tasks
+ * return: NULL if not enough memory, otherwise a valid pointer
+ */
+struct pkt_queue *pkt_queue_create(void);
+
+/*
+ * brief: enqueue one item to the FIFO
+ * param queue: pkt_queue instance created using pkt_queue_create
+ * param item: the item to be enqueued to the FIFO
+ * return: true if enqueued successfully, false when the arguments passed in are invalid
+ */
+bool pkt_queue_enqueue(struct pkt_queue *queue, pkt_linked_item_t *item);
+
+/*
+ * brief: dequeue one item for the FIFO
+ * param queue: pkt_queue instance created using pkt_queue_create
+ * return: pointer of type pkt_linked_item_t dequeued, NULL if the queue is empty or upon exception
+ */
+pkt_linked_item_t *pkt_queue_dequeue(struct pkt_queue *queue);
+
+/*
+ * brief: get the pointer of the first item from the FIFO but not get it dequeued
+ * param queue: pkt_queue instance created using pkt_queue_create
+ * return: pointer of the first item in the FIFO, NULL if the FIFO is empty
+ */
+pkt_linked_item_t *pkt_queue_try_peek_first(struct pkt_queue *queue);
+
+/*
+ * brief: retrieve the number of items existing in the FIFO
+ * param queue: pkt_queue instance created using pkt_queue_create
+ * return: total number of items in the FIFO
+ */
+size_t pkt_queue_length(const struct pkt_queue *queue);
+
+/*
+ * brief: retrieve the status whether the FIFO is empty
+ * param queue: pkt_queue instance created using pkt_queue_create
+ * return: false if the FIFO is not empty, otherwise true
+ */
+bool pkt_queue_is_empty(const struct pkt_queue *queue);
+
+/*
+ * brief: delete the item in the FIFO one by one
+ * param free_cb: destructor function for each item in the FIFO, if set to NULL, will use osi_free_func by default
+ */
+void pkt_queue_flush(struct pkt_queue *queue, pkt_queue_free_cb free_cb);
+
+/*
+ * brief: delete the items in the FIFO and then destroy the pkt_queue instance.
+ * param free_cb: destructor function for each item in the FIFO, if set to NULL, will use osi_free_func by default
+ */
+void pkt_queue_destroy(struct pkt_queue *queue, pkt_queue_free_cb free_cb);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/bt/common/osi/include/osi/semaphore.h b/lib/bt/common/osi/include/osi/semaphore.h
new file mode 100644
index 00000000..c9be9079
--- /dev/null
+++ b/lib/bt/common/osi/include/osi/semaphore.h
@@ -0,0 +1,43 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+#ifndef __SEMAPHORE_H__
+#define __SEMAPHORE_H__
+
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/queue.h"
+#include "freertos/semphr.h"
+
+#define OSI_SEM_MAX_TIMEOUT 0xffffffffUL
+
+typedef SemaphoreHandle_t osi_sem_t;
+
+#define osi_sem_valid( x ) ( ( ( *x ) == NULL) ? pdFALSE : pdTRUE )
+#define osi_sem_set_invalid( x ) ( ( *x ) = NULL )
+
+int osi_sem_new(osi_sem_t *sem, uint32_t max_count, uint32_t init_count);
+
+void osi_sem_free(osi_sem_t *sem);
+
+int osi_sem_take(osi_sem_t *sem, uint32_t timeout);
+
+void osi_sem_give(osi_sem_t *sem);
+
+
+#endif /* __SEMAPHORE_H__ */
diff --git a/lib/bt/common/osi/include/osi/thread.h b/lib/bt/common/osi/include/osi/thread.h
new file mode 100644
index 00000000..85ba611f
--- /dev/null
+++ b/lib/bt/common/osi/include/osi/thread.h
@@ -0,0 +1,120 @@
+/*
+ * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __THREAD_H__
+#define __THREAD_H__
+
+#include "freertos/FreeRTOSConfig.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/queue.h"
+#include "freertos/task.h"
+#include "osi/semaphore.h"
+#include "esp_task.h"
+#include "bt_common.h"
+
+#define OSI_THREAD_MAX_TIMEOUT OSI_SEM_MAX_TIMEOUT
+
+struct osi_thread;
+struct osi_event;
+
+typedef struct osi_thread osi_thread_t;
+
+typedef void (*osi_thread_func_t)(void *context);
+
+typedef enum {
+ OSI_THREAD_CORE_0 = 0,
+ OSI_THREAD_CORE_1,
+ OSI_THREAD_CORE_AFFINITY,
+} osi_thread_core_t;
+
+/*
+ * brief: Create a thread or task
+ * param name: thread name
+ * param stack_size: thread stack size
+ * param priority: thread priority
+ * param core: the CPU core which this thread run, OSI_THREAD_CORE_AFFINITY means unspecific CPU core
+ * param work_queue_num: speicify queue number, the queue[0] has highest priority, and the priority is decrease by index
+ * return : if create successfully, return thread handler; otherwise return NULL.
+ */
+osi_thread_t *osi_thread_create(const char *name, size_t stack_size, int priority, osi_thread_core_t core, uint8_t work_queue_num, const size_t work_queue_len[]);
+
+/*
+ * brief: Destroy a thread or task
+ * param thread: point of thread handler
+ */
+void osi_thread_free(osi_thread_t *thread);
+
+/*
+ * brief: Post an msg to a thread and told the thread call the function
+ * param thread: point of thread handler
+ * param func: callback function that called by target thread
+ * param context: argument of callback function
+ * param queue_idx: the queue which the msg send to
+ * param timeout: post timeout, OSI_THREAD_MAX_TIMEOUT means blocking forever, 0 means never blocking, others means block millisecond
+ * return : if post successfully, return true, otherwise return false
+ */
+bool osi_thread_post(osi_thread_t *thread, osi_thread_func_t func, void *context, int queue_idx, uint32_t timeout);
+
+/*
+ * brief: Set the priority of thread
+ * param thread: point of thread handler
+ * param priority: priority
+ * return : if set successfully, return true, otherwise return false
+ */
+bool osi_thread_set_priority(osi_thread_t *thread, int priority);
+
+/* brief: Get thread name
+ * param thread: point of thread handler
+ * return: constant point of thread name
+ */
+const char *osi_thread_name(osi_thread_t *thread);
+
+/* brief: Get the size of the specified queue
+ * param thread: point of thread handler
+ * param wq_idx: the queue index of the thread
+ * return: queue size
+ */
+int osi_thread_queue_wait_size(osi_thread_t *thread, int wq_idx);
+
+/*
+ * brief: Create an osi_event struct and register the handler function and its argument
+ * An osi_event is a kind of work that can be posted to the workqueue of osi_thread to process,
+ * but the work can have at most one instance the thread workqueue before it is processed. This
+ * allows the "single post, multiple data processing" jobs.
+ * param func: the handler to process the job
+ * param context: the argument to be passed to the handler function when the job is being processed
+ * return: NULL if no memory, otherwise a valid struct pointer
+ */
+struct osi_event *osi_event_create(osi_thread_func_t func, void *context);
+
+/*
+ * brief: Bind an osi_event to a specific work queue for an osi_thread.
+ * After binding is completed, a function call of API osi_thread_post_event will send a work
+ * to the workqueue of the thread, with specified queue index.
+ * param func: event: the pointer to osi_event that is created using osi_event_create
+ * param thread: the pointer to osi_thread that is created using osi_thread_create
+ * param queue_idx: the index of the workqueue of the specified osi_thread, with range starting from 0 to work_queue_num - 1
+ * return: true if osi_event binds to the thread's workqueue successfully, otherwise false
+ */
+bool osi_event_bind(struct osi_event* event, osi_thread_t *thread, int queue_idx);
+
+/*
+ * brief: Destroy the osi_event struct created by osi_event_create and free the allocated memory
+ * param event: the pointer to osi_event
+ */
+void osi_event_delete(struct osi_event* event);
+
+/*
+ * brief: try sending a work to the binded thread's workqueue, so that it can be handled by the worker thread
+ * param event: pointer to osi_event, created by osi_event_create
+ * param timeout: post timeout, OSI_THREAD_MAX_TIMEOUT means blocking forever, 0 means never blocking, others means block millisecond
+ * return: true if the message is enqueued to the thread workqueue, otherwise failed
+ * note: if the return value of function is false, it is the case that the workqueue of the thread is full, and users
+ * are expected to post the event sometime later to get the work handled.
+ */
+bool osi_thread_post_event(struct osi_event *event, uint32_t timeout);
+
+#endif /* __THREAD_H__ */
diff --git a/lib/bt/common/osi/list.c b/lib/bt/common/osi/list.c
new file mode 100644
index 00000000..c951d212
--- /dev/null
+++ b/lib/bt/common/osi/list.c
@@ -0,0 +1,312 @@
+
+#include "bt_common.h"
+#include "osi/allocator.h"
+#include "osi/list.h"
+#include "osi/osi.h"
+
+struct list_node_t {
+ struct list_node_t *next;
+ void *data;
+};
+
+typedef struct list_t {
+ list_node_t *head;
+ list_node_t *tail;
+ size_t length;
+ list_free_cb free_cb;
+} list_t;
+
+//static list_node_t *list_free_node_(list_t *list, list_node_t *node);
+
+// Hidden constructor, only to be used by the hash map for the allocation tracker.
+// Behaves the same as |list_new|, except you get to specify the allocator.
+list_t *list_new_internal(list_free_cb callback)
+{
+ list_t *list = (list_t *) osi_calloc(sizeof(list_t));
+ if (!list) {
+ return NULL;
+ }
+
+ list->head = list->tail = NULL;
+ list->length = 0;
+ list->free_cb = callback;
+ return list;
+}
+
+list_t *list_new(list_free_cb callback)
+{
+ return list_new_internal(callback);
+}
+
+void list_free(list_t *list)
+{
+ if (!list) {
+ return;
+ }
+
+ list_clear(list);
+ osi_free(list);
+}
+
+bool list_is_empty(const list_t *list)
+{
+ assert(list != NULL);
+ return (list->length == 0);
+}
+
+bool list_contains(const list_t *list, const void *data)
+{
+ assert(list != NULL);
+ assert(data != NULL);
+
+ for (const list_node_t *node = list_begin(list); node != list_end(list); node = list_next(node)) {
+ if (list_node(node) == data) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+list_node_t *list_get_node(const list_t *list, const void *data)
+{
+ assert(list != NULL);
+ assert(data != NULL);
+ list_node_t *p_node_ret = NULL;
+ for (list_node_t *node = list_begin(list); node != list_end(list); node = list_next(node)) {
+ if (list_node(node) == data) {
+ p_node_ret = node;
+ break;
+ }
+ }
+
+ return p_node_ret;
+}
+
+size_t list_length(const list_t *list)
+{
+ assert(list != NULL);
+ return list->length;
+}
+
+void *list_front(const list_t *list)
+{
+ assert(list != NULL);
+ assert(!list_is_empty(list));
+
+ return list->head->data;
+}
+
+void *list_back(const list_t *list) {
+ assert(list != NULL);
+ assert(!list_is_empty(list));
+
+ return list->tail->data;
+}
+
+list_node_t *list_back_node(const list_t *list) {
+ assert(list != NULL);
+ assert(!list_is_empty(list));
+
+ return list->tail;
+}
+
+bool list_insert_after(list_t *list, list_node_t *prev_node, void *data) {
+ assert(list != NULL);
+ assert(prev_node != NULL);
+ assert(data != NULL);
+ list_node_t *node = (list_node_t *)osi_calloc(sizeof(list_node_t));
+ if (!node) {
+ OSI_TRACE_ERROR("%s osi_calloc failed.\n", __FUNCTION__ );
+ return false;
+ }
+ node->next = prev_node->next;
+ node->data = data;
+ prev_node->next = node;
+ if (list->tail == prev_node) {
+ list->tail = node;
+ }
+ ++list->length;
+ return true;
+}
+
+bool list_prepend(list_t *list, void *data)
+{
+ assert(list != NULL);
+ assert(data != NULL);
+ list_node_t *node = (list_node_t *)osi_calloc(sizeof(list_node_t));
+ if (!node) {
+ OSI_TRACE_ERROR("%s osi_calloc failed.\n", __FUNCTION__ );
+ return false;
+ }
+ node->next = list->head;
+ node->data = data;
+ list->head = node;
+ if (list->tail == NULL) {
+ list->tail = list->head;
+ }
+ ++list->length;
+ return true;
+}
+
+bool list_append(list_t *list, void *data)
+{
+ assert(list != NULL);
+ assert(data != NULL);
+ list_node_t *node = (list_node_t *)osi_calloc(sizeof(list_node_t));
+ if (!node) {
+ OSI_TRACE_ERROR("%s osi_calloc failed.\n", __FUNCTION__ );
+ return false;
+ }
+ node->next = NULL;
+ node->data = data;
+ if (list->tail == NULL) {
+ list->head = node;
+ list->tail = node;
+ } else {
+ list->tail->next = node;
+ list->tail = node;
+ }
+ ++list->length;
+ return true;
+}
+
+bool list_remove(list_t *list, void *data)
+{
+ assert(list != NULL);
+ assert(data != NULL);
+
+ if (list_is_empty(list)) {
+ return false;
+ }
+
+ if (list->head->data == data) {
+ list_node_t *next = list_free_node(list, list->head);
+ if (list->tail == list->head) {
+ list->tail = next;
+ }
+ list->head = next;
+ return true;
+ }
+
+ for (list_node_t *prev = list->head, *node = list->head->next; node; prev = node, node = node->next)
+ if (node->data == data) {
+ prev->next = list_free_node(list, node);
+ if (list->tail == node) {
+ list->tail = prev;
+ }
+ return true;
+ }
+
+ return false;
+}
+
+bool list_delete(list_t *list, void *data)
+{
+ assert(list != NULL);
+ assert(data != NULL);
+
+ if (list_is_empty(list)) {
+ return false;
+ }
+
+ if (list->head->data == data) {
+ list_node_t *next = list_delete_node(list, list->head);
+ if (list->tail == list->head) {
+ list->tail = next;
+ }
+ list->head = next;
+ return true;
+ }
+
+ for (list_node_t *prev = list->head, *node = list->head->next; node; prev = node, node = node->next)
+ if (node->data == data) {
+ prev->next = list_delete_node(list, node);
+ if (list->tail == node) {
+ list->tail = prev;
+ }
+ return true;
+ }
+
+ return false;
+}
+
+void list_clear(list_t *list)
+{
+ assert(list != NULL);
+ for (list_node_t *node = list->head; node; ) {
+ node = list_free_node(list, node);
+ }
+ list->head = NULL;
+ list->tail = NULL;
+ list->length = 0;
+}
+
+list_node_t *list_foreach(const list_t *list, list_iter_cb callback, void *context)
+{
+ assert(list != NULL);
+ assert(callback != NULL);
+
+ for (list_node_t *node = list->head; node; ) {
+ list_node_t *next = node->next;
+ if (!callback(node->data, context)) {
+ return node;
+ }
+ node = next;
+ }
+ return NULL;
+}
+
+list_node_t *list_begin(const list_t *list)
+{
+ assert(list != NULL);
+ return list->head;
+}
+
+list_node_t *list_end(UNUSED_ATTR const list_t *list)
+{
+ assert(list != NULL);
+ return NULL;
+}
+
+list_node_t *list_next(const list_node_t *node)
+{
+ assert(node != NULL);
+ return node->next;
+}
+
+void *list_node(const list_node_t *node)
+{
+ assert(node != NULL);
+ return node->data;
+}
+
+list_node_t *list_free_node(list_t *list, list_node_t *node)
+{
+ assert(list != NULL);
+ assert(node != NULL);
+
+ list_node_t *next = node->next;
+
+ if (list->free_cb) {
+ list->free_cb(node->data);
+ }
+ osi_free(node);
+ --list->length;
+
+ return next;
+}
+
+// remove the element from list but do not free the node data
+list_node_t *list_delete_node(list_t *list, list_node_t *node)
+{
+ assert(list != NULL);
+ assert(node != NULL);
+
+ list_node_t *next = node->next;
+
+ osi_free(node);
+ --list->length;
+
+ return next;
+}
diff --git a/lib/bt/common/osi/mutex.c b/lib/bt/common/osi/mutex.c
new file mode 100644
index 00000000..b7fe38a1
--- /dev/null
+++ b/lib/bt/common/osi/mutex.c
@@ -0,0 +1,99 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+#include "osi/mutex.h"
+
+
+/* static section */
+static osi_mutex_t gl_mutex; /* Recursive Type */
+
+
+/** Create a new mutex
+ * @param mutex pointer to the mutex to create
+ * @return a new mutex */
+int osi_mutex_new(osi_mutex_t *mutex)
+{
+ int xReturn = -1;
+
+ *mutex = xSemaphoreCreateMutex();
+
+ if (*mutex != NULL) {
+ xReturn = 0;
+ }
+
+ return xReturn;
+}
+
+/** Lock a mutex
+ * @param mutex the mutex to lock */
+int osi_mutex_lock(osi_mutex_t *mutex, uint32_t timeout)
+{
+ int ret = 0;
+
+ if (timeout == OSI_MUTEX_MAX_TIMEOUT) {
+ if (xSemaphoreTake(*mutex, portMAX_DELAY) != pdTRUE) {
+ ret = -1;
+ }
+ } else {
+ if (xSemaphoreTake(*mutex, timeout / portTICK_PERIOD_MS) != pdTRUE) {
+ ret = -2;
+ }
+ }
+
+ return ret;
+}
+
+/** Unlock a mutex
+ * @param mutex the mutex to unlock */
+void osi_mutex_unlock(osi_mutex_t *mutex)
+{
+ xSemaphoreGive(*mutex);
+}
+
+/** Delete a semaphore
+ * @param mutex the mutex to delete */
+void osi_mutex_free(osi_mutex_t *mutex)
+{
+ vSemaphoreDelete(*mutex);
+ *mutex = NULL;
+}
+
+int osi_mutex_global_init(void)
+{
+ gl_mutex = xSemaphoreCreateRecursiveMutex();
+ if (gl_mutex == NULL) {
+ return -1;
+ }
+
+ return 0;
+}
+
+void osi_mutex_global_deinit(void)
+{
+ vSemaphoreDelete(gl_mutex);
+}
+
+void osi_mutex_global_lock(void)
+{
+ xSemaphoreTakeRecursive(gl_mutex, portMAX_DELAY);
+}
+
+void osi_mutex_global_unlock(void)
+{
+ xSemaphoreGiveRecursive(gl_mutex);
+}
diff --git a/lib/bt/common/osi/osi.c b/lib/bt/common/osi/osi.c
new file mode 100644
index 00000000..caf4154c
--- /dev/null
+++ b/lib/bt/common/osi/osi.c
@@ -0,0 +1,25 @@
+/*
+ * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+
+#include "osi/osi.h"
+#include "osi/mutex.h"
+
+int osi_init(void)
+{
+ int ret = 0;
+
+ if (osi_mutex_global_init() != 0) {
+ ret = -1;
+ }
+
+ return ret;
+}
+
+void osi_deinit(void)
+{
+ osi_mutex_global_deinit();
+}
diff --git a/lib/bt/common/osi/pkt_queue.c b/lib/bt/common/osi/pkt_queue.c
new file mode 100644
index 00000000..81abd1f0
--- /dev/null
+++ b/lib/bt/common/osi/pkt_queue.c
@@ -0,0 +1,144 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "osi/pkt_queue.h"
+#include "osi/allocator.h"
+#include "osi/mutex.h"
+
+
+STAILQ_HEAD(pkt_queue_header, pkt_linked_item);
+
+struct pkt_queue {
+ osi_mutex_t lock;
+ size_t length;
+ struct pkt_queue_header header;
+} pkt_queue_t;
+
+struct pkt_queue *pkt_queue_create(void)
+{
+ struct pkt_queue *queue = calloc(1, sizeof(struct pkt_queue));
+ if (queue == NULL) {
+ return NULL;
+ }
+ if (osi_mutex_new(&queue->lock) != 0) {
+ osi_free(queue);
+ }
+ struct pkt_queue_header *p = &queue->header;
+ STAILQ_INIT(p);
+
+ return queue;
+}
+
+static void pkt_queue_cleanup(struct pkt_queue *queue, pkt_queue_free_cb free_cb)
+{
+ if (queue == NULL) {
+ return;
+ }
+
+ struct pkt_queue_header *header = &queue->header;
+ pkt_linked_item_t *item = STAILQ_FIRST(header);
+ pkt_linked_item_t *tmp;
+
+ pkt_queue_free_cb free_func = (free_cb != NULL) ? free_cb : (pkt_queue_free_cb)osi_free_func;
+
+ while (item != NULL) {
+ tmp = STAILQ_NEXT(item, next);
+ free_func(item);
+ item = tmp;
+ queue->length--;
+ }
+ STAILQ_INIT(header);
+ queue->length = 0;
+}
+
+void pkt_queue_flush(struct pkt_queue *queue, pkt_queue_free_cb free_cb)
+{
+ if (queue == NULL) {
+ return;
+ }
+ osi_mutex_lock(&queue->lock, OSI_MUTEX_MAX_TIMEOUT);
+ pkt_queue_cleanup(queue, free_cb);
+ osi_mutex_unlock(&queue->lock);
+}
+
+void pkt_queue_destroy(struct pkt_queue *queue, pkt_queue_free_cb free_cb)
+{
+ if (queue == NULL) {
+ return;
+ }
+ osi_mutex_lock(&queue->lock, OSI_MUTEX_MAX_TIMEOUT);
+ pkt_queue_cleanup(queue, free_cb);
+ osi_mutex_unlock(&queue->lock);
+
+ osi_mutex_free(&queue->lock);
+ osi_free(queue);
+}
+
+pkt_linked_item_t *pkt_queue_dequeue(struct pkt_queue *queue)
+{
+ if (queue == NULL || queue->length == 0) {
+ return NULL;
+ }
+
+ struct pkt_linked_item *item;
+ struct pkt_queue_header *header;
+ osi_mutex_lock(&queue->lock, OSI_MUTEX_MAX_TIMEOUT);
+ header = &queue->header;
+ item = STAILQ_FIRST(header);
+ if (item != NULL) {
+ STAILQ_REMOVE_HEAD(header, next);
+ if (queue->length > 0) {
+ queue->length--;
+ }
+ }
+ osi_mutex_unlock(&queue->lock);
+
+ return item;
+}
+
+bool pkt_queue_enqueue(struct pkt_queue *queue, pkt_linked_item_t *item)
+{
+ if (queue == NULL || item == NULL) {
+ return false;
+ }
+
+ struct pkt_queue_header *header;
+ osi_mutex_lock(&queue->lock, OSI_MUTEX_MAX_TIMEOUT);
+ header = &queue->header;
+ STAILQ_INSERT_TAIL(header, item, next);
+ queue->length++;
+ osi_mutex_unlock(&queue->lock);
+
+ return true;
+}
+
+size_t pkt_queue_length(const struct pkt_queue *queue)
+{
+ if (queue == NULL) {
+ return 0;
+ }
+ return queue->length;
+}
+
+bool pkt_queue_is_empty(const struct pkt_queue *queue)
+{
+ return pkt_queue_length(queue) == 0;
+}
+
+pkt_linked_item_t *pkt_queue_try_peek_first(struct pkt_queue *queue)
+{
+ if (queue == NULL) {
+ return NULL;
+ }
+
+ struct pkt_queue_header *header = &queue->header;
+ pkt_linked_item_t *item;
+ osi_mutex_lock(&queue->lock, OSI_MUTEX_MAX_TIMEOUT);
+ item = STAILQ_FIRST(header);
+ osi_mutex_unlock(&queue->lock);
+
+ return item;
+}
diff --git a/lib/bt/common/osi/semaphore.c b/lib/bt/common/osi/semaphore.c
new file mode 100644
index 00000000..14823160
--- /dev/null
+++ b/lib/bt/common/osi/semaphore.c
@@ -0,0 +1,77 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+
+#include "osi/semaphore.h"
+
+/*-----------------------------------------------------------------------------------*/
+// Creates and returns a new semaphore. The "init_count" argument specifies
+// the initial state of the semaphore, "max_count" specifies the maximum value
+// that can be reached.
+int osi_sem_new(osi_sem_t *sem, uint32_t max_count, uint32_t init_count)
+{
+ int ret = -1;
+
+ if (sem) {
+ *sem = xSemaphoreCreateCounting(max_count, init_count);
+ if ((*sem) != NULL) {
+ ret = 0;
+ }
+ }
+
+ return ret;
+}
+
+/*-----------------------------------------------------------------------------------*/
+// Give a semaphore
+void osi_sem_give(osi_sem_t *sem)
+{
+ xSemaphoreGive(*sem);
+}
+
+/*
+ Blocks the thread while waiting for the semaphore to be
+ signaled. If the "timeout" argument is non-zero, the thread should
+ only be blocked for the specified time (measured in
+ milliseconds).
+
+*/
+int
+osi_sem_take(osi_sem_t *sem, uint32_t timeout)
+{
+ int ret = 0;
+
+ if (timeout == OSI_SEM_MAX_TIMEOUT) {
+ if (xSemaphoreTake(*sem, portMAX_DELAY) != pdTRUE) {
+ ret = -1;
+ }
+ } else {
+ if (xSemaphoreTake(*sem, timeout / portTICK_PERIOD_MS) != pdTRUE) {
+ ret = -2;
+ }
+ }
+
+ return ret;
+}
+
+// Deallocates a semaphore
+void osi_sem_free(osi_sem_t *sem)
+{
+ vSemaphoreDelete(*sem);
+ *sem = NULL;
+}
diff --git a/lib/bt/common/osi/thread.c b/lib/bt/common/osi/thread.c
new file mode 100644
index 00000000..f53eadb6
--- /dev/null
+++ b/lib/bt/common/osi/thread.c
@@ -0,0 +1,453 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+#include <string.h>
+
+#include "osi/allocator.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/queue.h"
+#include "osi/semaphore.h"
+#include "osi/thread.h"
+#include "osi/mutex.h"
+
+struct work_item {
+ osi_thread_func_t func;
+ void *context;
+};
+
+struct work_queue {
+ QueueHandle_t queue;
+ size_t capacity;
+};
+
+struct osi_thread {
+ TaskHandle_t thread_handle; /*!< Store the thread object */
+ int thread_id; /*!< May for some OS, such as Linux */
+ bool stop;
+ uint8_t work_queue_num; /*!< Work queue number */
+ struct work_queue **work_queues; /*!< Point to queue array, and the priority inverse array index */
+ osi_sem_t work_sem;
+ osi_sem_t stop_sem;
+};
+
+struct osi_thread_start_arg {
+ osi_thread_t *thread;
+ osi_sem_t start_sem;
+ int error;
+};
+
+struct osi_event {
+ struct work_item item;
+ osi_mutex_t lock;
+ uint16_t is_queued;
+ uint16_t queue_idx;
+ osi_thread_t *thread;
+};
+
+static const size_t DEFAULT_WORK_QUEUE_CAPACITY = 100;
+
+static struct work_queue *osi_work_queue_create(size_t capacity)
+{
+ if (capacity == 0) {
+ return NULL;
+ }
+
+ struct work_queue *wq = (struct work_queue *)osi_malloc(sizeof(struct work_queue));
+ if (wq != NULL) {
+ wq->queue = xQueueCreate(capacity, sizeof(struct work_item));
+ if (wq->queue != 0) {
+ wq->capacity = capacity;
+ return wq;
+ } else {
+ osi_free(wq);
+ }
+ }
+
+ return NULL;
+}
+
+static void osi_work_queue_delete(struct work_queue *wq)
+{
+ if (wq != NULL) {
+ if (wq->queue != 0) {
+ vQueueDelete(wq->queue);
+ }
+ wq->queue = 0;
+ wq->capacity = 0;
+ osi_free(wq);
+ }
+ return;
+}
+
+static bool osi_thead_work_queue_get(struct work_queue *wq, struct work_item *item)
+{
+ assert (wq != NULL);
+ assert (wq->queue != 0);
+ assert (item != NULL);
+
+ if (pdTRUE == xQueueReceive(wq->queue, item, 0)) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static bool osi_thead_work_queue_put(struct work_queue *wq, const struct work_item *item, uint32_t timeout)
+{
+ assert (wq != NULL);
+ assert (wq->queue != 0);
+ assert (item != NULL);
+
+ bool ret = true;
+ if (timeout == OSI_SEM_MAX_TIMEOUT) {
+ if (xQueueSend(wq->queue, item, portMAX_DELAY) != pdTRUE) {
+ ret = false;
+ }
+ } else {
+ if (xQueueSend(wq->queue, item, timeout / portTICK_PERIOD_MS) != pdTRUE) {
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static size_t osi_thead_work_queue_len(struct work_queue *wq)
+{
+ assert (wq != NULL);
+ assert (wq->queue != 0);
+ assert (wq->capacity != 0);
+
+ size_t available_spaces = (size_t)uxQueueSpacesAvailable(wq->queue);
+
+ if (available_spaces <= wq->capacity) {
+ return wq->capacity - available_spaces;
+ } else {
+ assert (0);
+ }
+ return 0;
+}
+
+static void osi_thread_run(void *arg)
+{
+ struct osi_thread_start_arg *start = (struct osi_thread_start_arg *)arg;
+ osi_thread_t *thread = start->thread;
+
+ osi_sem_give(&start->start_sem);
+
+ while (1) {
+ int idx = 0;
+
+ osi_sem_take(&thread->work_sem, OSI_SEM_MAX_TIMEOUT);
+
+ if (thread->stop) {
+ break;
+ }
+
+ struct work_item item;
+ while (!thread->stop && idx < thread->work_queue_num) {
+ if (osi_thead_work_queue_get(thread->work_queues[idx], &item) == true) {
+ item.func(item.context);
+ idx = 0;
+ continue;
+ } else {
+ idx++;
+ }
+ }
+ }
+
+ thread->thread_handle = NULL;
+ osi_sem_give(&thread->stop_sem);
+
+ vTaskDelete(NULL);
+}
+
+static int osi_thread_join(osi_thread_t *thread, uint32_t wait_ms)
+{
+ assert(thread != NULL);
+ return osi_sem_take(&thread->stop_sem, wait_ms);
+}
+
+static void osi_thread_stop(osi_thread_t *thread)
+{
+ int ret;
+
+ assert(thread != NULL);
+
+ //stop the thread
+ thread->stop = true;
+ osi_sem_give(&thread->work_sem);
+
+ //join
+ ret = osi_thread_join(thread, 1000); //wait 1000ms
+
+ //if join failed, delete the task here
+ if (ret != 0 && thread->thread_handle) {
+ vTaskDelete(thread->thread_handle);
+ }
+}
+
+//in linux, the stack_size, priority and core may not be set here, the code will be ignore the arguments
+osi_thread_t *osi_thread_create(const char *name, size_t stack_size, int priority, osi_thread_core_t core, uint8_t work_queue_num, const size_t work_queue_len[])
+{
+ int ret;
+ struct osi_thread_start_arg start_arg = {0};
+
+ if (stack_size <= 0 ||
+ core < OSI_THREAD_CORE_0 || core > OSI_THREAD_CORE_AFFINITY ||
+ work_queue_num <= 0 || work_queue_len == NULL) {
+ return NULL;
+ }
+
+ osi_thread_t *thread = (osi_thread_t *)osi_calloc(sizeof(osi_thread_t));
+ if (thread == NULL) {
+ goto _err;
+ }
+
+ thread->stop = false;
+ thread->work_queues = (struct work_queue **)osi_calloc(sizeof(struct work_queue *) * work_queue_num);
+ if (thread->work_queues == NULL) {
+ goto _err;
+ }
+ thread->work_queue_num = work_queue_num;
+
+ for (int i = 0; i < thread->work_queue_num; i++) {
+ size_t queue_len = work_queue_len[i] ? work_queue_len[i] : DEFAULT_WORK_QUEUE_CAPACITY;
+ thread->work_queues[i] = osi_work_queue_create(queue_len);
+ if (thread->work_queues[i] == NULL) {
+ goto _err;
+ }
+ }
+
+ ret = osi_sem_new(&thread->work_sem, 1, 0);
+ if (ret != 0) {
+ goto _err;
+ }
+
+ ret = osi_sem_new(&thread->stop_sem, 1, 0);
+ if (ret != 0) {
+ goto _err;
+ }
+
+ start_arg.thread = thread;
+ ret = osi_sem_new(&start_arg.start_sem, 1, 0);
+ if (ret != 0) {
+ goto _err;
+ }
+
+ if (xTaskCreatePinnedToCore(osi_thread_run, name, stack_size, &start_arg, priority, &thread->thread_handle, core) != pdPASS) {
+ goto _err;
+ }
+
+ osi_sem_take(&start_arg.start_sem, OSI_SEM_MAX_TIMEOUT);
+ osi_sem_free(&start_arg.start_sem);
+
+ return thread;
+
+_err:
+
+ if (thread) {
+ if (start_arg.start_sem) {
+ osi_sem_free(&start_arg.start_sem);
+ }
+
+ if (thread->thread_handle) {
+ vTaskDelete(thread->thread_handle);
+ }
+
+ for (int i = 0; i < thread->work_queue_num; i++) {
+ if (thread->work_queues[i]) {
+ osi_work_queue_delete(thread->work_queues[i]);
+ }
+ thread->work_queues[i] = NULL;
+ }
+
+ if (thread->work_queues) {
+ osi_free(thread->work_queues);
+ thread->work_queues = NULL;
+ }
+
+ if (thread->work_sem) {
+ osi_sem_free(&thread->work_sem);
+ }
+
+ if (thread->stop_sem) {
+ osi_sem_free(&thread->stop_sem);
+ }
+
+ osi_free(thread);
+ }
+
+ return NULL;
+}
+
+void osi_thread_free(osi_thread_t *thread)
+{
+ if (!thread)
+ return;
+
+ osi_thread_stop(thread);
+
+ for (int i = 0; i < thread->work_queue_num; i++) {
+ if (thread->work_queues[i]) {
+ osi_work_queue_delete(thread->work_queues[i]);
+ thread->work_queues[i] = NULL;
+ }
+ }
+
+ if (thread->work_queues) {
+ osi_free(thread->work_queues);
+ thread->work_queues = NULL;
+ }
+
+ if (thread->work_sem) {
+ osi_sem_free(&thread->work_sem);
+ }
+
+ if (thread->stop_sem) {
+ osi_sem_free(&thread->stop_sem);
+ }
+
+
+ osi_free(thread);
+}
+
+bool osi_thread_post(osi_thread_t *thread, osi_thread_func_t func, void *context, int queue_idx, uint32_t timeout)
+{
+ assert(thread != NULL);
+ assert(func != NULL);
+
+ if (queue_idx >= thread->work_queue_num) {
+ return false;
+ }
+
+ struct work_item item;
+
+ item.func = func;
+ item.context = context;
+
+ if (osi_thead_work_queue_put(thread->work_queues[queue_idx], &item, timeout) == false) {
+ return false;
+ }
+
+ osi_sem_give(&thread->work_sem);
+
+ return true;
+}
+
+bool osi_thread_set_priority(osi_thread_t *thread, int priority)
+{
+ assert(thread != NULL);
+
+ vTaskPrioritySet(thread->thread_handle, priority);
+ return true;
+}
+
+const char *osi_thread_name(osi_thread_t *thread)
+{
+ assert(thread != NULL);
+
+ return pcTaskGetName(thread->thread_handle);
+}
+
+int osi_thread_queue_wait_size(osi_thread_t *thread, int wq_idx)
+{
+ if (wq_idx < 0 || wq_idx >= thread->work_queue_num) {
+ return -1;
+ }
+
+ return (int)(osi_thead_work_queue_len(thread->work_queues[wq_idx]));
+}
+
+
+struct osi_event *osi_event_create(osi_thread_func_t func, void *context)
+{
+ struct osi_event *event = osi_calloc(sizeof(struct osi_event));
+ if (event != NULL) {
+ if (osi_mutex_new(&event->lock) == 0) {
+ event->item.func = func;
+ event->item.context = context;
+ return event;
+ }
+ osi_free(event);
+ }
+
+ return NULL;
+}
+
+void osi_event_delete(struct osi_event* event)
+{
+ if (event != NULL) {
+ osi_mutex_free(&event->lock);
+ memset(event, 0, sizeof(struct osi_event));
+ osi_free(event);
+ }
+}
+
+bool osi_event_bind(struct osi_event* event, osi_thread_t *thread, int queue_idx)
+{
+ if (event == NULL || event->thread != NULL) {
+ return false;
+ }
+
+ if (thread == NULL || queue_idx >= thread->work_queue_num) {
+ return false;
+ }
+
+ event->thread = thread;
+ event->queue_idx = queue_idx;
+
+ return true;
+}
+
+static void osi_thread_generic_event_handler(void *context)
+{
+ struct osi_event *event = (struct osi_event *)context;
+ if (event != NULL && event->item.func != NULL) {
+ osi_mutex_lock(&event->lock, OSI_MUTEX_MAX_TIMEOUT);
+ event->is_queued = 0;
+ osi_mutex_unlock(&event->lock);
+ event->item.func(event->item.context);
+ }
+}
+
+bool osi_thread_post_event(struct osi_event *event, uint32_t timeout)
+{
+ assert(event != NULL && event->thread != NULL);
+ assert(event->queue_idx >= 0 && event->queue_idx < event->thread->work_queue_num);
+ bool ret = false;
+ if (event->is_queued == 0) {
+ uint16_t acquire_cnt = 0;
+ osi_mutex_lock(&event->lock, OSI_MUTEX_MAX_TIMEOUT);
+ event->is_queued += 1;
+ acquire_cnt = event->is_queued;
+ osi_mutex_unlock(&event->lock);
+
+ if (acquire_cnt == 1) {
+ ret = osi_thread_post(event->thread, osi_thread_generic_event_handler, event, event->queue_idx, timeout);
+ if (!ret) {
+ // clear "is_queued" when post failure, to allow for following event posts
+ osi_mutex_lock(&event->lock, OSI_MUTEX_MAX_TIMEOUT);
+ event->is_queued = 0;
+ osi_mutex_unlock(&event->lock);
+ }
+ }
+ }
+
+ return ret;
+}