diff options
Diffstat (limited to 'lib/bt/common')
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, ¶m, 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, ¶m, 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, ¶m, 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, ¶m, 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, ¶m, 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, ¶m, 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, ¶m, 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, ¶m, 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, ¶m, 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, ¶m, 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, ¶m, 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, ¶m, 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, ¶m, 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, ¶m, 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, ¶m, 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, ¶m, 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, ¶m, 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, ¶m, 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, ¶m, 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, ¶m, 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, ¶m, 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, ¶m, 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, ¶m, 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, ¶m); + 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, ¶m, 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, ¶m, 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, ¶m, 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; +} |
