Fork ESP-IDF's bluetooth component

i want better sbc encoding, and no cla will stop me
This commit is contained in:
jacqueline
2024-03-28 14:32:49 +11:00
parent 239e6d8950
commit ee29c25b29
1761 changed files with 737738 additions and 0 deletions
+126
View File
@@ -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
@@ -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_ */
+20
View File
@@ -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);
}
}
+44
View File
@@ -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];
}
+549
View File
@@ -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;
}
+22
View File
@@ -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__ */
@@ -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__ */
+160
View File
@@ -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__ */
@@ -0,0 +1,422 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include "osi/allocator.h"
#include "btc/btc_task.h"
#include "btc/btc_manage.h"
#include "blufi_int.h"
#include "btc_blufi_prf.h"
#include "esp_log.h"
#include "esp_blufi_api.h"
#include "common/bt_target.h"
#include "common/bt_trace.h"
#include "stack/bt_types.h"
#include "stack/gatt_api.h"
#include "bta/bta_api.h"
#include "bta/bta_gatt_api.h"
#include "bta_gatts_int.h"
#include "btc_gatt_util.h"
#include "btc_gatts.h"
#include "esp_bt_defs.h"
#include "esp_gap_ble_api.h"
#include "esp_gatt_common_api.h"
#include "esp_bt_main.h"
#include "esp_bt_device.h"
#include "esp_err.h"
#include "esp_blufi.h"
#if (BLUFI_INCLUDED == TRUE)
static uint8_t server_if;
static uint16_t conn_id;
static uint8_t blufi_service_uuid128[32] = {
/* LSB <--------------------------------------------------------------------------------> MSB */
//first uuid, 16bit, [12],[13] is the value
0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
};
static esp_ble_adv_data_t blufi_adv_data = {
.set_scan_rsp = false,
.include_name = true,
.include_txpower = true,
.min_interval = 0x0006, //slave connection min interval, Time = min_interval * 1.25 msec
.max_interval = 0x0010, //slave connection max interval, Time = max_interval * 1.25 msec
.appearance = 0x00,
.manufacturer_len = 0,
.p_manufacturer_data = NULL,
.service_data_len = 0,
.p_service_data = NULL,
.service_uuid_len = 16,
.p_service_uuid = blufi_service_uuid128,
.flag = 0x6,
};
static esp_ble_adv_params_t blufi_adv_params = {
.adv_int_min = 0x100,
.adv_int_max = 0x100,
.adv_type = ADV_TYPE_IND,
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
//.peer_addr =
//.peer_addr_type =
.channel_map = ADV_CHNL_ALL,
.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
};
void esp_blufi_gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
{
switch (event) {
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
esp_ble_gap_start_advertising(&blufi_adv_params);
break;
default:
break;
}
}
// static functions declare
static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data);
void blufi_create_service(void)
{
if (!blufi_env.enabled) {
BTC_TRACE_ERROR("blufi service added error.");
return;
}
blufi_env.srvc_inst = 0x00;
BTA_GATTS_CreateService(blufi_env.gatt_if, &blufi_srvc_uuid, blufi_env.srvc_inst, BLUFI_HDL_NUM, true);
}
uint8_t esp_blufi_init(void)
{
/* register the BLUFI profile to the BTA_GATTS module*/
BTA_GATTS_AppRegister(&blufi_app_uuid, blufi_profile_cb);
return GATT_SUCCESS;
}
static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data)
{
tBTA_GATTS_RSP rsp;
BLUFI_TRACE_DEBUG("blufi profile cb event = %x\n", event);
switch (event) {
case BTA_GATTS_REG_EVT:
BLUFI_TRACE_DEBUG("REG: status %d, app_uuid %04x, gatt_if %d\n", p_data->reg_oper.status, p_data->reg_oper.uuid.uu.uuid16, p_data->reg_oper.server_if);
if (p_data->reg_oper.status != BTA_GATT_OK) {
BLUFI_TRACE_ERROR("BLUFI profile register failed\n");
return;
}
blufi_env.gatt_if = p_data->reg_oper.server_if;
blufi_env.enabled = true;
//create the blufi service to the service data base.
if (p_data->reg_oper.uuid.uu.uuid16 == BLUFI_APP_UUID) {
BLUFI_TRACE_DEBUG("%s %d\n", __func__, __LINE__);
blufi_create_service();
}
break;
case BTA_GATTS_DEREG_EVT: {
esp_blufi_cb_param_t param;
btc_msg_t msg;
BLUFI_TRACE_DEBUG("DEREG: status %d, gatt_if %d\n", p_data->reg_oper.status, p_data->reg_oper.server_if);
if (p_data->reg_oper.status != BTA_GATT_OK) {
BLUFI_TRACE_ERROR("BLUFI profile unregister failed\n");
return;
}
blufi_env.enabled = false;
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_DEINIT_FINISH;
param.deinit_finish.state = ESP_BLUFI_DEINIT_OK;
btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
break;
}
case BTA_GATTS_READ_EVT:
memset(&rsp, 0, sizeof(tBTA_GATTS_API_RSP));
rsp.attr_value.handle = p_data->req_data.p_data->read_req.handle;
rsp.attr_value.len = 1;
rsp.attr_value.value[0] = 0x00;
BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id,
p_data->req_data.status, &rsp);
break;
case BTA_GATTS_WRITE_EVT: {
if (p_data->req_data.p_data->write_req.is_prep) {
tBTA_GATT_STATUS status = GATT_SUCCESS;
if (blufi_env.prepare_buf == NULL) {
blufi_env.prepare_buf = osi_malloc(BLUFI_PREPAIR_BUF_MAX_SIZE);
blufi_env.prepare_len = 0;
if (blufi_env.prepare_buf == NULL) {
BLUFI_TRACE_ERROR("Blufi prep no mem\n");
status = GATT_NO_RESOURCES;
}
} else {
if (p_data->req_data.p_data->write_req.offset > BLUFI_PREPAIR_BUF_MAX_SIZE) {
status = GATT_INVALID_OFFSET;
} else if ((p_data->req_data.p_data->write_req.offset + p_data->req_data.p_data->write_req.len) > BLUFI_PREPAIR_BUF_MAX_SIZE) {
status = GATT_INVALID_ATTR_LEN;
}
}
memset(&rsp, 0, sizeof(tGATTS_RSP));
rsp.attr_value.handle = p_data->req_data.p_data->write_req.handle;
rsp.attr_value.len = p_data->req_data.p_data->write_req.len;
rsp.attr_value.offset = p_data->req_data.p_data->write_req.offset;
memcpy(rsp.attr_value.value, p_data->req_data.p_data->write_req.value, p_data->req_data.p_data->write_req.len);
BLUFI_TRACE_DEBUG("prep write, len=%d, offset=%d\n", p_data->req_data.p_data->write_req.len, p_data->req_data.p_data->write_req.offset);
BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id,
status, &rsp);
if (status != GATT_SUCCESS) {
if (blufi_env.prepare_buf) {
osi_free(blufi_env.prepare_buf);
blufi_env.prepare_buf = NULL;
blufi_env.prepare_len = 0;
}
BLUFI_TRACE_ERROR("write data error , error code 0x%x\n", status);
return;
}
memcpy(blufi_env.prepare_buf + p_data->req_data.p_data->write_req.offset,
p_data->req_data.p_data->write_req.value,
p_data->req_data.p_data->write_req.len);
blufi_env.prepare_len += p_data->req_data.p_data->write_req.len;
return;
} else {
BLUFI_TRACE_DEBUG("norm write, len=%d, offset=%d\n", p_data->req_data.p_data->write_req.len, p_data->req_data.p_data->write_req.offset);
BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id,
p_data->req_data.status, NULL);
}
if (p_data->req_data.p_data->write_req.handle == blufi_env.handle_char_p2e) {
btc_blufi_recv_handler(&p_data->req_data.p_data->write_req.value[0],
p_data->req_data.p_data->write_req.len);
}
break;
}
case BTA_GATTS_EXEC_WRITE_EVT:
BLUFI_TRACE_DEBUG("exec write exec %d\n", p_data->req_data.p_data->exec_write);
BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id,
GATT_SUCCESS, NULL);
if (blufi_env.prepare_buf && p_data->req_data.p_data->exec_write == GATT_PREP_WRITE_EXEC) {
btc_blufi_recv_handler(blufi_env.prepare_buf, blufi_env.prepare_len);
}
if (blufi_env.prepare_buf) {
osi_free(blufi_env.prepare_buf);
blufi_env.prepare_buf = NULL;
blufi_env.prepare_len = 0;
}
break;
case BTA_GATTS_MTU_EVT:
BLUFI_TRACE_DEBUG("MTU size %d\n", p_data->req_data.p_data->mtu);
blufi_env.frag_size = (p_data->req_data.p_data->mtu < BLUFI_MAX_DATA_LEN ? p_data->req_data.p_data->mtu : BLUFI_MAX_DATA_LEN) - BLUFI_MTU_RESERVED_SIZE;
break;
case BTA_GATTS_CONF_EVT:
BLUFI_TRACE_DEBUG("CONFIRM EVT\n");
/* Nothing */
break;
case BTA_GATTS_CREATE_EVT:
blufi_env.handle_srvc = p_data->create.service_id;
//add the frist blufi characteristic --> write characteristic
BTA_GATTS_AddCharacteristic(blufi_env.handle_srvc, &blufi_char_uuid_p2e,
(GATT_PERM_WRITE),
(GATT_CHAR_PROP_BIT_WRITE),
NULL, NULL);
break;
case BTA_GATTS_ADD_CHAR_EVT:
switch (p_data->add_result.char_uuid.uu.uuid16) {
case BLUFI_CHAR_P2E_UUID: /* Phone to ESP32 */
blufi_env.handle_char_p2e = p_data->add_result.attr_id;
BTA_GATTS_AddCharacteristic(blufi_env.handle_srvc, &blufi_char_uuid_e2p,
(GATT_PERM_READ),
(GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY),
NULL, NULL);
break;
case BLUFI_CHAR_E2P_UUID: /* ESP32 to Phone */
blufi_env.handle_char_e2p = p_data->add_result.attr_id;
BTA_GATTS_AddCharDescriptor (blufi_env.handle_srvc,
(GATT_PERM_READ | GATT_PERM_WRITE),
&blufi_descr_uuid_e2p,
NULL, NULL);
break;
default:
break;
}
break;
case BTA_GATTS_ADD_CHAR_DESCR_EVT: {
/* call init finish */
esp_blufi_cb_param_t param;
btc_msg_t msg;
blufi_env.handle_descr_e2p = p_data->add_result.attr_id;
//start the blufi service after created
BTA_GATTS_StartService(blufi_env.handle_srvc, BTA_GATT_TRANSPORT_LE);
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_INIT_FINISH;
param.init_finish.state = ESP_BLUFI_INIT_OK;
btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
break;
}
case BTA_GATTS_CONNECT_EVT: {
btc_msg_t msg;
esp_blufi_cb_param_t param;
//set the connection flag to true
BLUFI_TRACE_API("\ndevice is connected "BT_BD_ADDR_STR", server_if=%d,reason=0x%x,connect_id=%d\n",
BT_BD_ADDR_HEX(p_data->conn.remote_bda), p_data->conn.server_if,
p_data->conn.reason, p_data->conn.conn_id);
memcpy(blufi_env.remote_bda, p_data->conn.remote_bda, ESP_BLUFI_BD_ADDR_LEN);
blufi_env.conn_id = p_data->conn.conn_id;
blufi_env.is_connected = true;
blufi_env.recv_seq = blufi_env.send_seq = 0;
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_BLE_CONNECT;
memcpy(param.connect.remote_bda, p_data->conn.remote_bda, ESP_BLUFI_BD_ADDR_LEN);
param.connect.conn_id = BTC_GATT_GET_CONN_ID(p_data->conn.conn_id);
conn_id = param.connect.conn_id;
server_if = p_data->conn.server_if;
btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
break;
}
case BTA_GATTS_DISCONNECT_EVT: {
btc_msg_t msg;
esp_blufi_cb_param_t param;
blufi_env.is_connected = false;
//set the connection flag to true
BLUFI_TRACE_API("\ndevice is disconnected "BT_BD_ADDR_STR", server_if=%d,reason=0x%x,connect_id=%d\n",
BT_BD_ADDR_HEX(p_data->conn.remote_bda), p_data->conn.server_if,
p_data->conn.reason, p_data->conn.conn_id);
memcpy(blufi_env.remote_bda, p_data->conn.remote_bda, ESP_BLUFI_BD_ADDR_LEN);
blufi_env.conn_id = p_data->conn.conn_id;
blufi_env.recv_seq = blufi_env.send_seq = 0;
blufi_env.sec_mode = 0x0;
blufi_env.offset = 0;
if (blufi_env.aggr_buf != NULL) {
osi_free(blufi_env.aggr_buf);
blufi_env.aggr_buf = NULL;
}
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_BLE_DISCONNECT;
memcpy(param.disconnect.remote_bda, p_data->conn.remote_bda, ESP_BLUFI_BD_ADDR_LEN);
btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
break;
}
case BTA_GATTS_OPEN_EVT:
break;
case BTA_GATTS_CLOSE_EVT:
break;
case BTA_GATTS_CONGEST_EVT:
break;
default:
break;
}
}
void esp_blufi_send_notify(void *arg)
{
struct pkt_info *pkts = (struct pkt_info *) arg;
uint16_t conn_id = blufi_env.conn_id;
uint16_t attr_id = blufi_env.handle_char_e2p;
bool rsp = false;
BTA_GATTS_HandleValueIndication(conn_id, attr_id, pkts->pkt_len,
pkts->pkt, rsp);
}
void esp_blufi_deinit(void)
{
BTA_GATTS_StopService(blufi_env.handle_srvc);
BTA_GATTS_DeleteService(blufi_env.handle_srvc);
/* register the BLUFI profile to the BTA_GATTS module*/
BTA_GATTS_AppDeregister(blufi_env.gatt_if);
}
void esp_blufi_adv_start(void)
{
esp_ble_gap_set_device_name(BLUFI_DEVICE_NAME);
esp_ble_gap_config_adv_data(&blufi_adv_data);
}
void esp_blufi_adv_stop(void)
{
esp_ble_gap_stop_advertising();
}
void esp_blufi_send_encap(void *arg)
{
struct blufi_hdr *hdr = (struct blufi_hdr *)arg;
retry:
if (blufi_env.is_connected == false) {
BTC_TRACE_WARNING("%s ble connection is broken\n", __func__);
return;
}
if (esp_ble_get_cur_sendable_packets_num(BTC_GATT_GET_CONN_ID(blufi_env.conn_id)) > 0) {
btc_blufi_send_notify((uint8_t *)hdr,
((hdr->fc & BLUFI_FC_CHECK) ?
hdr->data_len + sizeof(struct blufi_hdr) + 2 :
hdr->data_len + sizeof(struct blufi_hdr)));
} else {
BTC_TRACE_WARNING("%s wait to send blufi custom data\n", __func__);
vTaskDelay(pdMS_TO_TICKS(10));
goto retry;
}
}
esp_err_t esp_blufi_close(esp_gatt_if_t gatts_if, uint16_t conn_id)
{
ESP_BLE_HOST_STATUS_CHECK(ESP_BLE_HOST_STATUS_ENABLED);
btc_msg_t msg;
btc_ble_gatts_args_t arg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_GATTS;
msg.act = BTC_GATTS_ACT_CLOSE;
arg.close.conn_id = BTC_GATT_CREATE_CONN_ID(gatts_if, conn_id);
return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL, NULL)
== BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
}
void esp_blufi_disconnect()
{
int rc;
rc = esp_blufi_close(server_if, conn_id);
assert (rc == 0);
}
#endif
@@ -0,0 +1,901 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#include "osi/allocator.h"
#include "btc_blufi_prf.h"
#include "btc/btc_task.h"
#include "btc/btc_manage.h"
#include "blufi_int.h"
#include "esp_log.h"
#include "esp_blufi_api.h"
#include "esp_blufi.h"
#if (BLUFI_INCLUDED == TRUE)
#if GATT_DYNAMIC_MEMORY == FALSE
tBLUFI_ENV blufi_env;
#else
tBLUFI_ENV *blufi_env_ptr;
#endif
// static functions declare
static void btc_blufi_send_ack(uint8_t seq);
inline void btc_blufi_cb_to_app(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param)
{
esp_blufi_event_cb_t btc_blufi_cb = (esp_blufi_event_cb_t)btc_profile_cb_get(BTC_PID_BLUFI);
if (btc_blufi_cb) {
btc_blufi_cb(event, param);
}
}
static uint8_t btc_blufi_profile_init(void)
{
esp_blufi_callbacks_t *store_p = blufi_env.cbs;
uint8_t rc;
if (blufi_env.enabled) {
BLUFI_TRACE_ERROR("BLUFI already initialized");
return ESP_BLUFI_ERROR;
}
memset(&blufi_env, 0x0, sizeof(blufi_env));
blufi_env.cbs = store_p; /* if set callback prior, restore the point */
blufi_env.frag_size = BLUFI_FRAG_DATA_DEFAULT_LEN;
rc = esp_blufi_init();
if(rc != 0 ){
return rc;
}
return ESP_BLUFI_SUCCESS;
}
static uint8_t btc_blufi_profile_deinit(void)
{
if (!blufi_env.enabled) {
BTC_TRACE_ERROR("BLUFI already de-initialized");
return ESP_BLUFI_ERROR;
}
esp_blufi_deinit();
return ESP_BLUFI_SUCCESS;
}
void btc_blufi_send_notify(uint8_t *pkt, int pkt_len)
{
struct pkt_info pkts;
pkts.pkt = pkt;
pkts.pkt_len = pkt_len;
esp_blufi_send_notify(&pkts);
}
void btc_blufi_report_error(esp_blufi_error_state_t state)
{
btc_msg_t msg;
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_REPORT_ERROR;
esp_blufi_cb_param_t param;
param.report_error.state = state;
btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
}
void btc_blufi_recv_handler(uint8_t *data, int len)
{
struct blufi_hdr *hdr = (struct blufi_hdr *)data;
uint16_t checksum, checksum_pkt;
int ret;
if (hdr->seq != blufi_env.recv_seq) {
BTC_TRACE_ERROR("%s seq %d is not expect %d\n", __func__, hdr->seq, blufi_env.recv_seq + 1);
btc_blufi_report_error(ESP_BLUFI_SEQUENCE_ERROR);
return;
}
blufi_env.recv_seq++;
// first step, decrypt
if (BLUFI_FC_IS_ENC(hdr->fc)
&& (blufi_env.cbs && blufi_env.cbs->decrypt_func)) {
ret = blufi_env.cbs->decrypt_func(hdr->seq, hdr->data, hdr->data_len);
if (ret != hdr->data_len) { /* enc must be success and enc len must equal to plain len */
BTC_TRACE_ERROR("%s decrypt error %d\n", __func__, ret);
btc_blufi_report_error(ESP_BLUFI_DECRYPT_ERROR);
return;
}
}
// second step, check sum
if (BLUFI_FC_IS_CHECK(hdr->fc)
&& (blufi_env.cbs && blufi_env.cbs->checksum_func)) {
checksum = blufi_env.cbs->checksum_func(hdr->seq, &hdr->seq, hdr->data_len + 2);
checksum_pkt = hdr->data[hdr->data_len] | (((uint16_t) hdr->data[hdr->data_len + 1]) << 8);
if (checksum != checksum_pkt) {
BTC_TRACE_ERROR("%s checksum error %04x, pkt %04x\n", __func__, checksum, checksum_pkt);
btc_blufi_report_error(ESP_BLUFI_CHECKSUM_ERROR);
return;
}
}
if (BLUFI_FC_IS_REQ_ACK(hdr->fc)) {
btc_blufi_send_ack(hdr->seq);
}
if (BLUFI_FC_IS_FRAG(hdr->fc)) {
if (blufi_env.offset == 0) {
/*
blufi_env.aggr_buf should be NULL if blufi_env.offset is 0.
It is possible that the process of sending fragment packet
has not been completed
*/
if (blufi_env.aggr_buf) {
BTC_TRACE_ERROR("%s msg error, blufi_env.aggr_buf is not freed\n", __func__);
btc_blufi_report_error(ESP_BLUFI_MSG_STATE_ERROR);
return;
}
blufi_env.total_len = hdr->data[0] | (((uint16_t) hdr->data[1]) << 8);
blufi_env.aggr_buf = osi_malloc(blufi_env.total_len);
if (blufi_env.aggr_buf == NULL) {
BTC_TRACE_ERROR("%s no mem, len %d\n", __func__, blufi_env.total_len);
btc_blufi_report_error(ESP_BLUFI_DH_MALLOC_ERROR);
return;
}
}
if (blufi_env.offset + hdr->data_len - 2 <= blufi_env.total_len){
memcpy(blufi_env.aggr_buf + blufi_env.offset, hdr->data + 2, hdr->data_len - 2);
blufi_env.offset += (hdr->data_len - 2);
} else {
BTC_TRACE_ERROR("%s payload is longer than packet length, len %d \n", __func__, blufi_env.total_len);
btc_blufi_report_error(ESP_BLUFI_DATA_FORMAT_ERROR);
return;
}
} else {
if (blufi_env.offset > 0) { /* if previous pkt is frag */
/* blufi_env.aggr_buf should not be NULL */
if (blufi_env.aggr_buf == NULL) {
BTC_TRACE_ERROR("%s buffer is NULL\n", __func__);
btc_blufi_report_error(ESP_BLUFI_DH_MALLOC_ERROR);
return;
}
/* payload length should be equal to total_len */
if ((blufi_env.offset + hdr->data_len) != blufi_env.total_len) {
BTC_TRACE_ERROR("%s payload is longer than packet length, len %d \n", __func__, blufi_env.total_len);
btc_blufi_report_error(ESP_BLUFI_DATA_FORMAT_ERROR);
return;
}
memcpy(blufi_env.aggr_buf + blufi_env.offset, hdr->data, hdr->data_len);
btc_blufi_protocol_handler(hdr->type, blufi_env.aggr_buf, blufi_env.total_len);
blufi_env.offset = 0;
osi_free(blufi_env.aggr_buf);
blufi_env.aggr_buf = NULL;
} else {
btc_blufi_protocol_handler(hdr->type, hdr->data, hdr->data_len);
blufi_env.offset = 0;
}
}
}
void btc_blufi_send_encap(uint8_t type, uint8_t *data, int total_data_len)
{
struct blufi_hdr *hdr = NULL;
int remain_len = total_data_len;
uint16_t checksum;
int ret;
if (blufi_env.is_connected == false) {
BTC_TRACE_ERROR("blufi connection has been disconnected \n");
return;
}
while (remain_len > 0) {
if (remain_len > blufi_env.frag_size) {
hdr = osi_malloc(sizeof(struct blufi_hdr) + 2 + blufi_env.frag_size + 2);
if (hdr == NULL) {
BTC_TRACE_ERROR("%s no mem\n", __func__);
return;
}
hdr->fc = 0x0;
hdr->data_len = blufi_env.frag_size + 2;
hdr->data[0] = remain_len & 0xff;
hdr->data[1] = (remain_len >> 8) & 0xff;
memcpy(hdr->data + 2, &data[total_data_len - remain_len], blufi_env.frag_size); //copy first, easy for check sum
hdr->fc |= BLUFI_FC_FRAG;
} else {
hdr = osi_malloc(sizeof(struct blufi_hdr) + remain_len + 2);
if (hdr == NULL) {
BTC_TRACE_ERROR("%s no mem\n", __func__);
return;
}
hdr->fc = 0x0;
hdr->data_len = remain_len;
memcpy(hdr->data, &data[total_data_len - remain_len], hdr->data_len); //copy first, easy for check sum
}
hdr->type = type;
hdr->fc |= BLUFI_FC_DIR_E2P;
hdr->seq = blufi_env.send_seq++;
if (BLUFI_TYPE_IS_CTRL(hdr->type)) {
if ((blufi_env.sec_mode & BLUFI_CTRL_SEC_MODE_CHECK_MASK)
&& (blufi_env.cbs && blufi_env.cbs->checksum_func)) {
hdr->fc |= BLUFI_FC_CHECK;
checksum = blufi_env.cbs->checksum_func(hdr->seq, &hdr->seq, hdr->data_len + 2);
memcpy(&hdr->data[hdr->data_len], &checksum, 2);
}
} else if (!BLUFI_TYPE_IS_DATA_NEG(hdr->type) && !BLUFI_TYPE_IS_DATA_ERROR_INFO(hdr->type)) {
if ((blufi_env.sec_mode & BLUFI_DATA_SEC_MODE_CHECK_MASK)
&& (blufi_env.cbs && blufi_env.cbs->checksum_func)) {
hdr->fc |= BLUFI_FC_CHECK;
checksum = blufi_env.cbs->checksum_func(hdr->seq, &hdr->seq, hdr->data_len + 2);
memcpy(&hdr->data[hdr->data_len], &checksum, 2);
}
if ((blufi_env.sec_mode & BLUFI_DATA_SEC_MODE_ENC_MASK)
&& (blufi_env.cbs && blufi_env.cbs->encrypt_func)) {
ret = blufi_env.cbs->encrypt_func(hdr->seq, hdr->data, hdr->data_len);
if (ret == hdr->data_len) { /* enc must be success and enc len must equal to plain len */
hdr->fc |= BLUFI_FC_ENC;
} else {
BTC_TRACE_ERROR("%s encrypt error %d\n", __func__, ret);
btc_blufi_report_error(ESP_BLUFI_ENCRYPT_ERROR);
osi_free(hdr);
return;
}
}
}
if (hdr->fc & BLUFI_FC_FRAG) {
remain_len -= (hdr->data_len - 2);
} else {
remain_len -= hdr->data_len;
}
esp_blufi_send_encap(hdr);
osi_free(hdr);
hdr = NULL;
}
}
static void btc_blufi_wifi_conn_report(uint8_t opmode, uint8_t sta_conn_state, uint8_t softap_conn_num, esp_blufi_extra_info_t *info, int info_len)
{
uint8_t type;
uint8_t *data;
int data_len;
uint8_t *p;
data_len = info_len + 3;
p = data = osi_malloc(data_len);
if (data == NULL) {
BTC_TRACE_ERROR("%s no mem\n", __func__);
return;
}
type = BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_WIFI_REP);
*p++ = opmode;
*p++ = sta_conn_state;
*p++ = softap_conn_num;
if (info) {
if (info->sta_bssid_set) {
*p++ = BLUFI_TYPE_DATA_SUBTYPE_STA_BSSID;
*p++ = 6;
memcpy(p, info->sta_bssid, 6);
p += 6;
}
if (info->sta_ssid) {
*p++ = BLUFI_TYPE_DATA_SUBTYPE_STA_SSID;
*p++ = info->sta_ssid_len;
memcpy(p, info->sta_ssid, info->sta_ssid_len);
p += info->sta_ssid_len;
}
if (info->sta_passwd) {
*p++ = BLUFI_TYPE_DATA_SUBTYPE_STA_PASSWD;
*p++ = info->sta_passwd_len;
memcpy(p, info->sta_passwd, info->sta_passwd_len);
p += info->sta_passwd_len;
}
if (info->softap_ssid) {
*p++ = BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_SSID;
*p++ = info->softap_ssid_len;
memcpy(p, info->softap_ssid, info->softap_ssid_len);
p += info->softap_ssid_len;
}
if (info->softap_passwd) {
*p++ = BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_PASSWD;
*p++ = info->softap_passwd_len;
memcpy(p, info->softap_passwd, info->softap_passwd_len);
p += info->softap_passwd_len;
}
if (info->softap_authmode_set) {
*p++ = BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_AUTH_MODE;
*p++ = 1;
*p++ = info->softap_authmode;
}
if (info->softap_max_conn_num_set) {
*p++ = BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_MAX_CONN_NUM;
*p++ = 1;
*p++ = info->softap_max_conn_num;
}
if (info->softap_channel_set) {
*p++ = BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_CHANNEL;
*p++ = 1;
*p++ = info->softap_channel;
}
if (info->sta_max_conn_retry_set) {
*p++ = BLUFI_TYPE_DATA_SUBTYPE_STA_MAX_CONN_RETRY;
*p++ = 1;
*p++ = info->sta_max_conn_retry;
}
if (info->sta_conn_end_reason_set) {
*p++ = BLUFI_TYPE_DATA_SUBTYPE_STA_CONN_END_REASON;
*p++ = 1;
*p++ = info->sta_conn_end_reason;
}
if (info->sta_conn_rssi_set) {
*p++ = BLUFI_TYPE_DATA_SUBTYPE_STA_CONN_RSSI;
*p++ = 1;
*p++ = info->sta_conn_rssi;
}
}
if (p - data > data_len) {
BTC_TRACE_ERROR("%s len error %d %d\n", __func__, (int)(p - data), data_len);
}
btc_blufi_send_encap(type, data, data_len);
osi_free(data);
}
void btc_blufi_send_wifi_list(uint16_t apCount, esp_blufi_ap_record_t *list)
{
uint8_t type;
uint8_t *data;
int data_len;
uint8_t *p;
// malloc size: (len + RSSI + ssid buffer) * apCount;
uint32_t malloc_size = (1 + 1 + sizeof(list->ssid)) * apCount;
p = data = osi_malloc(malloc_size);
if (data == NULL) {
BTC_TRACE_ERROR("malloc error\n");
return;
}
type = BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_WIFI_LIST);
for (int i = 0; i < apCount; ++i)
{
uint32_t len = strlen((const char *)list[i].ssid);
data_len = (p - data);
//current_len + ssid + rssi + total_len_value
if((data_len + len + 1 + 1) > malloc_size) {
BTC_TRACE_ERROR("%s len error", __func__);
osi_free(data);
return;
}
*p++ = len + 1; // length of ssid + rssi
*p++ = list[i].rssi;
memcpy(p, list[i].ssid, len);
p = p + len;
}
data_len = (p - data);
btc_blufi_send_encap(type, data, data_len);
osi_free(data);
}
static void btc_blufi_send_ack(uint8_t seq)
{
uint8_t type;
uint8_t data;
type = BLUFI_BUILD_TYPE(BLUFI_TYPE_CTRL, BLUFI_TYPE_CTRL_SUBTYPE_ACK);
data = seq;
btc_blufi_send_encap(type, &data, 1);
}
static void btc_blufi_send_error_info(uint8_t state)
{
uint8_t type;
uint8_t *data;
int data_len;
uint8_t *p;
data_len = 1;
p = data = osi_malloc(data_len);
if (data == NULL) {
BTC_TRACE_ERROR("%s no mem\n", __func__);
return;
}
type = BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_ERROR_INFO);
*p++ = state;
if (p - data > data_len) {
BTC_TRACE_ERROR("%s len error %d %d\n", __func__, (int)(p - data), data_len);
}
btc_blufi_send_encap(type, data, data_len);
osi_free(data);
}
static void btc_blufi_send_custom_data(uint8_t *value, uint32_t value_len)
{
if(value == NULL || value_len == 0) {
BTC_TRACE_ERROR("%s value or value len error", __func__);
return;
}
uint8_t *data = osi_malloc(value_len);
if (data == NULL) {
BTC_TRACE_ERROR("%s mem malloc error", __func__);
return;
}
uint8_t type = BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_CUSTOM_DATA);
memcpy(data, value, value_len);
btc_blufi_send_encap(type, data, value_len);
osi_free(data);
}
void btc_blufi_cb_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src)
{
esp_blufi_cb_param_t *dst = (esp_blufi_cb_param_t *) p_dest;
esp_blufi_cb_param_t *src = (esp_blufi_cb_param_t *) p_src;
switch (msg->act) {
case ESP_BLUFI_EVENT_RECV_STA_SSID:
dst->sta_ssid.ssid = osi_malloc(src->sta_ssid.ssid_len);
if (dst->sta_ssid.ssid == NULL) {
BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act);
}
memcpy(dst->sta_ssid.ssid, src->sta_ssid.ssid, src->sta_ssid.ssid_len);
break;
case ESP_BLUFI_EVENT_RECV_STA_PASSWD:
dst->sta_passwd.passwd = osi_malloc(src->sta_passwd.passwd_len);
if (dst->sta_passwd.passwd == NULL) {
BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act);
}
memcpy(dst->sta_passwd.passwd, src->sta_passwd.passwd, src->sta_passwd.passwd_len);
break;
case ESP_BLUFI_EVENT_RECV_SOFTAP_SSID:
dst->softap_ssid.ssid = osi_malloc(src->softap_ssid.ssid_len);
if (dst->softap_ssid.ssid == NULL) {
BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act);
}
memcpy(dst->softap_ssid.ssid, src->softap_ssid.ssid, src->softap_ssid.ssid_len);
break;
case ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD:
dst->softap_passwd.passwd = osi_malloc(src->softap_passwd.passwd_len);
if (dst->softap_passwd.passwd == NULL) {
BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act);
}
memcpy(dst->softap_passwd.passwd, src->softap_passwd.passwd, src->softap_passwd.passwd_len);
break;
case ESP_BLUFI_EVENT_RECV_USERNAME:
dst->username.name = osi_malloc(src->username.name_len);
if (dst->username.name == NULL) {
BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act);
}
memcpy(dst->username.name, src->username.name, src->username.name_len);
break;
case ESP_BLUFI_EVENT_RECV_CA_CERT:
dst->ca.cert = osi_malloc(src->ca.cert_len);
if (dst->ca.cert == NULL) {
BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act);
}
memcpy(dst->ca.cert, src->ca.cert, src->ca.cert_len);
break;
case ESP_BLUFI_EVENT_RECV_CLIENT_CERT:
dst->client_cert.cert = osi_malloc(src->client_cert.cert_len);
if (dst->client_cert.cert == NULL) {
BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act);
}
memcpy(dst->client_cert.cert, src->client_cert.cert, src->client_cert.cert_len);
break;
case ESP_BLUFI_EVENT_RECV_SERVER_CERT:
dst->server_cert.cert = osi_malloc(src->server_cert.cert_len);
if (dst->server_cert.cert == NULL) {
BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act);
}
memcpy(dst->server_cert.cert, src->server_cert.cert, src->server_cert.cert_len);
break;
case ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY:
dst->client_pkey.pkey = osi_malloc(src->client_pkey.pkey_len);
if (dst->client_pkey.pkey == NULL) {
BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act);
}
memcpy(dst->client_pkey.pkey, src->client_pkey.pkey, src->client_pkey.pkey_len);
break;
case ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY:
dst->server_pkey.pkey = osi_malloc(src->server_pkey.pkey_len);
if (dst->server_pkey.pkey == NULL) {
BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act);
}
memcpy(dst->server_pkey.pkey, src->server_pkey.pkey, src->server_pkey.pkey_len);
break;
case ESP_BLUFI_EVENT_RECV_CUSTOM_DATA:
dst->custom_data.data = osi_malloc(src->custom_data.data_len);
if (dst->custom_data.data == NULL) {
BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act);
break;
}
memcpy(dst->custom_data.data, src->custom_data.data, src->custom_data.data_len);
break;
default:
break;
}
}
void btc_blufi_cb_deep_free(btc_msg_t *msg)
{
esp_blufi_cb_param_t *param = (esp_blufi_cb_param_t *)msg->arg;
switch (msg->act) {
case ESP_BLUFI_EVENT_RECV_STA_SSID:
osi_free(param->sta_ssid.ssid);
break;
case ESP_BLUFI_EVENT_RECV_STA_PASSWD:
osi_free(param->sta_passwd.passwd);
break;
case ESP_BLUFI_EVENT_RECV_SOFTAP_SSID:
osi_free(param->softap_ssid.ssid);
break;
case ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD:
osi_free(param->softap_passwd.passwd);
break;
case ESP_BLUFI_EVENT_RECV_USERNAME:
osi_free(param->username.name);
break;
case ESP_BLUFI_EVENT_RECV_CA_CERT:
osi_free(param->ca.cert);
break;
case ESP_BLUFI_EVENT_RECV_CLIENT_CERT:
osi_free(param->client_cert.cert);
break;
case ESP_BLUFI_EVENT_RECV_SERVER_CERT:
osi_free(param->server_cert.cert);
break;
case ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY:
osi_free(param->client_pkey.pkey);
break;
case ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY:
osi_free(param->server_pkey.pkey);
break;
case ESP_BLUFI_EVENT_RECV_CUSTOM_DATA:
osi_free(param->custom_data.data);
break;
default:
break;
}
}
void btc_blufi_cb_handler(btc_msg_t *msg)
{
esp_blufi_cb_param_t *param = (esp_blufi_cb_param_t *)msg->arg;
switch (msg->act) {
case ESP_BLUFI_EVENT_INIT_FINISH: {
btc_blufi_cb_to_app(ESP_BLUFI_EVENT_INIT_FINISH, param);
break;
}
case ESP_BLUFI_EVENT_DEINIT_FINISH: {
btc_blufi_cb_to_app(ESP_BLUFI_EVENT_DEINIT_FINISH, param);
break;
}
case ESP_BLUFI_EVENT_BLE_CONNECT:
btc_blufi_cb_to_app(ESP_BLUFI_EVENT_BLE_CONNECT, param);
break;
case ESP_BLUFI_EVENT_BLE_DISCONNECT:
btc_blufi_cb_to_app(ESP_BLUFI_EVENT_BLE_DISCONNECT, param);
break;
case ESP_BLUFI_EVENT_SET_WIFI_OPMODE:
btc_blufi_cb_to_app(ESP_BLUFI_EVENT_SET_WIFI_OPMODE, param);
break;
case ESP_BLUFI_EVENT_REQ_CONNECT_TO_AP:
btc_blufi_cb_to_app(ESP_BLUFI_EVENT_REQ_CONNECT_TO_AP, NULL);
break;
case ESP_BLUFI_EVENT_REQ_DISCONNECT_FROM_AP:
btc_blufi_cb_to_app(ESP_BLUFI_EVENT_REQ_DISCONNECT_FROM_AP, NULL);
break;
case ESP_BLUFI_EVENT_GET_WIFI_STATUS:
btc_blufi_cb_to_app(ESP_BLUFI_EVENT_GET_WIFI_STATUS, NULL);
break;
case ESP_BLUFI_EVENT_GET_WIFI_LIST:
btc_blufi_cb_to_app(ESP_BLUFI_EVENT_GET_WIFI_LIST, NULL);
break;
case ESP_BLUFI_EVENT_DEAUTHENTICATE_STA:
btc_blufi_cb_to_app(ESP_BLUFI_EVENT_DEAUTHENTICATE_STA, NULL);
break;
case ESP_BLUFI_EVENT_RECV_STA_BSSID:
btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_STA_BSSID, param);
break;
case ESP_BLUFI_EVENT_RECV_STA_SSID:
btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_STA_SSID, param);
break;
case ESP_BLUFI_EVENT_RECV_STA_PASSWD:
btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_STA_PASSWD, param);
break;
case ESP_BLUFI_EVENT_RECV_SOFTAP_SSID:
btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_SOFTAP_SSID, param);
break;
case ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD:
btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD, param);
break;
case ESP_BLUFI_EVENT_RECV_SOFTAP_MAX_CONN_NUM:
btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_SOFTAP_MAX_CONN_NUM, param);
break;
case ESP_BLUFI_EVENT_RECV_SOFTAP_AUTH_MODE:
btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_SOFTAP_AUTH_MODE, param);
break;
case ESP_BLUFI_EVENT_RECV_SOFTAP_CHANNEL:
btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_SOFTAP_CHANNEL, param);
break;
case ESP_BLUFI_EVENT_RECV_USERNAME:
btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_USERNAME, param);
break;
case ESP_BLUFI_EVENT_RECV_CA_CERT:
btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_CA_CERT, param);
break;
case ESP_BLUFI_EVENT_RECV_CLIENT_CERT:
btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_CLIENT_CERT, param);
break;
case ESP_BLUFI_EVENT_RECV_SERVER_CERT:
btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_SERVER_CERT, param);
break;
case ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY:
btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY, param);
break;
case ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY:
btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY, param);
break;
case ESP_BLUFI_EVENT_RECV_SLAVE_DISCONNECT_BLE:
btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_SLAVE_DISCONNECT_BLE, param);
break;
case ESP_BLUFI_EVENT_REPORT_ERROR:
btc_blufi_cb_to_app(ESP_BLUFI_EVENT_REPORT_ERROR, param);
break;
case ESP_BLUFI_EVENT_RECV_CUSTOM_DATA:
btc_blufi_cb_to_app(ESP_BLUFI_EVENT_RECV_CUSTOM_DATA, param);
break;
default:
BTC_TRACE_ERROR("%s UNKNOWN %d\n", __func__, msg->act);
break;
}
btc_blufi_cb_deep_free(msg);
}
void btc_blufi_call_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src)
{
btc_blufi_args_t *dst = (btc_blufi_args_t *) p_dest;
btc_blufi_args_t *src = (btc_blufi_args_t *) p_src;
switch (msg->act) {
case BTC_BLUFI_ACT_SEND_CFG_REPORT: {
esp_blufi_extra_info_t *src_info = src->wifi_conn_report.extra_info;
dst->wifi_conn_report.extra_info_len = 0;
dst->wifi_conn_report.extra_info = NULL;
if (src_info == NULL) {
return;
}
dst->wifi_conn_report.extra_info = osi_calloc(sizeof(esp_blufi_extra_info_t));
if (dst->wifi_conn_report.extra_info == NULL) {
BTC_TRACE_ERROR("%s no mem line %d\n", __func__, __LINE__);
return;
}
if (src_info->sta_bssid_set) {
memcpy(dst->wifi_conn_report.extra_info->sta_bssid, src_info->sta_bssid, 6);
dst->wifi_conn_report.extra_info->sta_bssid_set = src_info->sta_bssid_set;
dst->wifi_conn_report.extra_info_len += (6 + 2);
}
if (src_info->sta_ssid) {
dst->wifi_conn_report.extra_info->sta_ssid = osi_malloc(src_info->sta_ssid_len);
if (dst->wifi_conn_report.extra_info->sta_ssid) {
memcpy(dst->wifi_conn_report.extra_info->sta_ssid, src_info->sta_ssid, src_info->sta_ssid_len);
dst->wifi_conn_report.extra_info->sta_ssid_len = src_info->sta_ssid_len;
dst->wifi_conn_report.extra_info_len += (dst->wifi_conn_report.extra_info->sta_ssid_len + 2);
} else {
BTC_TRACE_ERROR("%s no mem line %d\n", __func__, __LINE__);
return;
}
}
if (src_info->sta_passwd) {
dst->wifi_conn_report.extra_info->sta_passwd = osi_malloc(src_info->sta_passwd_len);
if (dst->wifi_conn_report.extra_info->sta_passwd) {
memcpy(dst->wifi_conn_report.extra_info->sta_passwd, src_info->sta_passwd, src_info->sta_passwd_len);
dst->wifi_conn_report.extra_info->sta_passwd_len = src_info->sta_passwd_len;
dst->wifi_conn_report.extra_info_len += (dst->wifi_conn_report.extra_info->sta_passwd_len + 2);
} else {
BTC_TRACE_ERROR("%s no mem line %d\n", __func__, __LINE__);
return;
}
}
if (src_info->softap_ssid) {
dst->wifi_conn_report.extra_info->softap_ssid = osi_malloc(src_info->softap_ssid_len);
if (dst->wifi_conn_report.extra_info->softap_ssid) {
memcpy(dst->wifi_conn_report.extra_info->softap_ssid, src_info->softap_ssid, src_info->softap_ssid_len);
dst->wifi_conn_report.extra_info->softap_ssid_len = src_info->softap_ssid_len;
dst->wifi_conn_report.extra_info_len += (dst->wifi_conn_report.extra_info->softap_ssid_len + 2);
} else {
BTC_TRACE_ERROR("%s no mem line %d\n", __func__, __LINE__);
return;
}
}
if (src_info->softap_passwd) {
dst->wifi_conn_report.extra_info->softap_passwd = osi_malloc(src_info->softap_passwd_len);
if (dst->wifi_conn_report.extra_info->softap_passwd) {
memcpy(dst->wifi_conn_report.extra_info->softap_passwd, src_info->softap_passwd, src_info->softap_passwd_len);
dst->wifi_conn_report.extra_info->softap_passwd_len = src_info->softap_passwd_len;
dst->wifi_conn_report.extra_info_len += (dst->wifi_conn_report.extra_info->softap_passwd_len + 2);
} else {
BTC_TRACE_ERROR("%s no mem line %d\n", __func__, __LINE__);
return;
}
}
if (src_info->softap_authmode_set) {
dst->wifi_conn_report.extra_info->softap_authmode_set = src_info->softap_authmode_set;
dst->wifi_conn_report.extra_info->softap_authmode = src_info->softap_authmode;
dst->wifi_conn_report.extra_info_len += (1 + 2);
}
if (src_info->softap_max_conn_num_set) {
dst->wifi_conn_report.extra_info->softap_max_conn_num_set = src_info->softap_max_conn_num_set;
dst->wifi_conn_report.extra_info->softap_max_conn_num = src_info->softap_max_conn_num;
dst->wifi_conn_report.extra_info_len += (1 + 2);
}
if (src_info->softap_channel_set) {
dst->wifi_conn_report.extra_info->softap_channel_set = src_info->softap_channel_set;
dst->wifi_conn_report.extra_info->softap_channel = src_info->softap_channel;
dst->wifi_conn_report.extra_info_len += (1 + 2);
}
if (src_info->sta_max_conn_retry_set) {
dst->wifi_conn_report.extra_info->sta_max_conn_retry_set = src_info->sta_max_conn_retry_set;
dst->wifi_conn_report.extra_info->sta_max_conn_retry = src_info->sta_max_conn_retry;
dst->wifi_conn_report.extra_info_len += (1 + 2);
}
if (src_info->sta_conn_end_reason_set) {
dst->wifi_conn_report.extra_info->sta_conn_end_reason_set = src_info->sta_conn_end_reason_set;
dst->wifi_conn_report.extra_info->sta_conn_end_reason = src_info->sta_conn_end_reason;
dst->wifi_conn_report.extra_info_len += (1 + 2);
}
if (src_info->sta_conn_rssi_set) {
dst->wifi_conn_report.extra_info->sta_conn_rssi_set = src_info->sta_conn_rssi_set;
dst->wifi_conn_report.extra_info->sta_conn_rssi = src_info->sta_conn_rssi;
dst->wifi_conn_report.extra_info_len += (1 + 2);
}
break;
}
case BTC_BLUFI_ACT_SEND_WIFI_LIST:{
esp_blufi_ap_record_t *list = src->wifi_list.list;
src->wifi_list.list = NULL;
if (list == NULL || src->wifi_list.apCount <= 0) {
break;
}
dst->wifi_list.list = (esp_blufi_ap_record_t *)osi_malloc(sizeof(esp_blufi_ap_record_t) * src->wifi_list.apCount);
if (dst->wifi_list.list == NULL) {
BTC_TRACE_ERROR("%s no mem line %d\n", __func__, __LINE__);
break;
}
memcpy(dst->wifi_list.list, list, sizeof(esp_blufi_ap_record_t) * src->wifi_list.apCount);
break;
}
case BTC_BLUFI_ACT_SEND_CUSTOM_DATA:{
uint8_t *data = src->custom_data.data;
if(data == NULL) {
BTC_TRACE_ERROR("custom data is NULL\n");
break;
}
dst->custom_data.data = osi_malloc(src->custom_data.data_len);
if(dst->custom_data.data == NULL) {
BTC_TRACE_ERROR("custom data malloc error\n");
break;
}
memcpy(dst->custom_data.data, src->custom_data.data, src->custom_data.data_len);
break;
}
default:
break;
}
}
void btc_blufi_call_deep_free(btc_msg_t *msg)
{
btc_blufi_args_t *arg = (btc_blufi_args_t *)msg->arg;
switch (msg->act) {
case BTC_BLUFI_ACT_SEND_CFG_REPORT: {
esp_blufi_extra_info_t *info = (esp_blufi_extra_info_t *)arg->wifi_conn_report.extra_info;
if (info == NULL) {
return;
}
if (info->sta_ssid) {
osi_free(info->sta_ssid);
}
if (info->sta_passwd) {
osi_free(info->sta_passwd);
}
if (info->softap_ssid) {
osi_free(info->softap_ssid);
}
if (info->softap_passwd) {
osi_free(info->softap_passwd);
}
osi_free(info);
break;
}
case BTC_BLUFI_ACT_SEND_WIFI_LIST:{
esp_blufi_ap_record_t *list = (esp_blufi_ap_record_t *)arg->wifi_list.list;
if (list){
osi_free(list);
}
break;
}
case BTC_BLUFI_ACT_SEND_CUSTOM_DATA:{
uint8_t *data = arg->custom_data.data;
if(data) {
osi_free(data);
}
break;
}
default:
break;
}
}
void btc_blufi_call_handler(btc_msg_t *msg)
{
btc_blufi_args_t *arg = (btc_blufi_args_t *)msg->arg;
switch (msg->act) {
case BTC_BLUFI_ACT_INIT:
btc_blufi_profile_init();
break;
case BTC_BLUFI_ACT_DEINIT:
btc_blufi_profile_deinit();
break;
case BTC_BLUFI_ACT_SEND_CFG_REPORT:
btc_blufi_wifi_conn_report(arg->wifi_conn_report.opmode,
arg->wifi_conn_report.sta_conn_state,
arg->wifi_conn_report.softap_conn_num,
arg->wifi_conn_report.extra_info,
arg->wifi_conn_report.extra_info_len);
break;
case BTC_BLUFI_ACT_SEND_WIFI_LIST:{
btc_blufi_send_wifi_list(arg->wifi_list.apCount, arg->wifi_list.list);
break;
}
case BTC_BLUFI_ACT_SEND_ERR_INFO:
btc_blufi_send_error_info(arg->blufi_err_infor.state);
break;
case BTC_BLUFI_ACT_SEND_CUSTOM_DATA:
btc_blufi_send_custom_data(arg->custom_data.data, arg->custom_data.data_len);
break;
default:
BTC_TRACE_ERROR("%s UNKNOWN %d\n", __func__, msg->act);
break;
}
btc_blufi_call_deep_free(msg);
}
void btc_blufi_set_callbacks(esp_blufi_callbacks_t *callbacks)
{
blufi_env.cbs = callbacks;
}
uint16_t btc_blufi_get_version(void)
{
return BTC_BLUFI_VERSION;
}
#endif ///BLUFI_INCLUDED == TRUE
@@ -0,0 +1,253 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#include "btc_blufi_prf.h"
#include "btc/btc_task.h"
#include "btc/btc_manage.h"
#include "blufi_int.h"
//#include "esp_wifi.h"
#if (BLUFI_INCLUDED == TRUE)
void btc_blufi_protocol_handler(uint8_t type, uint8_t *data, int len)
{
btc_msg_t msg;
esp_blufi_cb_param_t param;
uint8_t *output_data = NULL;
int output_len = 0;
bool need_free = false;
switch (BLUFI_GET_TYPE(type)) {
case BLUFI_TYPE_CTRL:
switch (BLUFI_GET_SUBTYPE(type)) {
case BLUFI_TYPE_CTRL_SUBTYPE_ACK:
/* TODO: check sequence */
break;
case BLUFI_TYPE_CTRL_SUBTYPE_SET_SEC_MODE:
blufi_env.sec_mode = data[0];
break;
case BLUFI_TYPE_CTRL_SUBTYPE_SET_WIFI_OPMODE:
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_SET_WIFI_OPMODE;
param.wifi_mode.op_mode = data[0];
btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
break;
case BLUFI_TYPE_CTRL_SUBTYPE_CONN_TO_AP:
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_REQ_CONNECT_TO_AP;
btc_transfer_context(&msg, NULL, 0, NULL, NULL);
break;
case BLUFI_TYPE_CTRL_SUBTYPE_DISCONN_FROM_AP:
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_REQ_DISCONNECT_FROM_AP;
btc_transfer_context(&msg, NULL, 0, NULL, NULL);
break;
case BLUFI_TYPE_CTRL_SUBTYPE_GET_WIFI_STATUS:
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_GET_WIFI_STATUS;
btc_transfer_context(&msg, NULL, 0, NULL, NULL);
break;
case BLUFI_TYPE_CTRL_SUBTYPE_DEAUTHENTICATE_STA:
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_DEAUTHENTICATE_STA;
btc_transfer_context(&msg, NULL, 0, NULL,NULL);
break;
case BLUFI_TYPE_CTRL_SUBTYPE_GET_VERSION: {
uint8_t type = BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_REPLY_VERSION);
uint8_t data[2];
data[0] = BTC_BLUFI_GREAT_VER;
data[1] = BTC_BLUFI_SUB_VER;
btc_blufi_send_encap(type, &data[0], sizeof(data));
break;
}
case BLUFI_TYPE_CTRL_SUBTYPE_DISCONNECT_BLE:
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_RECV_SLAVE_DISCONNECT_BLE;
btc_transfer_context(&msg, NULL, 0, NULL, NULL);
break;
case BLUFI_TYPE_CTRL_SUBTYPE_GET_WIFI_LIST:
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_GET_WIFI_LIST;
btc_transfer_context(&msg, NULL, 0, NULL, NULL);
break;
default:
BTC_TRACE_ERROR("%s Unkown Ctrl pkt %02x\n", __func__, type);
break;
}
break;
case BLUFI_TYPE_DATA:
switch (BLUFI_GET_SUBTYPE(type)) {
case BLUFI_TYPE_DATA_SUBTYPE_NEG:
if (blufi_env.cbs && blufi_env.cbs->negotiate_data_handler) {
blufi_env.cbs->negotiate_data_handler(data, len, &output_data, &output_len, &need_free);
}
if (output_data && output_len > 0) {
btc_blufi_send_encap(BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_NEG),
output_data, output_len);
}
break;
case BLUFI_TYPE_DATA_SUBTYPE_STA_BSSID:
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_RECV_STA_BSSID;
memcpy(param.sta_bssid.bssid, &data[0], 6);
btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
break;
case BLUFI_TYPE_DATA_SUBTYPE_STA_SSID:
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_RECV_STA_SSID;
param.sta_ssid.ssid = &data[0];
param.sta_ssid.ssid_len = len;
btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy, btc_blufi_cb_deep_free);
break;
case BLUFI_TYPE_DATA_SUBTYPE_STA_PASSWD:
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_RECV_STA_PASSWD;
param.sta_passwd.passwd = &data[0];
param.sta_passwd.passwd_len = len;
btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy, btc_blufi_cb_deep_free);
break;
case BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_SSID:
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_RECV_SOFTAP_SSID;
param.softap_ssid.ssid = &data[0];
param.softap_ssid.ssid_len = len;
btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy, btc_blufi_cb_deep_free);
break;
case BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_PASSWD:
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD;
param.softap_passwd.passwd = &data[0];
param.softap_passwd.passwd_len = len;
btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy, btc_blufi_cb_deep_free);
break;
case BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_MAX_CONN_NUM:
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_RECV_SOFTAP_MAX_CONN_NUM;
param.softap_max_conn_num.max_conn_num = data[0];
btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
break;
case BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_AUTH_MODE:
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_RECV_SOFTAP_AUTH_MODE;
param.softap_auth_mode.auth_mode = data[0];
btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
break;
case BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_CHANNEL:
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_RECV_SOFTAP_CHANNEL;
param.softap_channel.channel = data[0];
btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
break;
case BLUFI_TYPE_DATA_SUBTYPE_USERNAME:
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_RECV_USERNAME;
param.username.name = &data[0];
param.username.name_len = len;
btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy, btc_blufi_cb_deep_free);
break;
case BLUFI_TYPE_DATA_SUBTYPE_CA:
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_RECV_CA_CERT;
param.ca.cert = &data[0];
param.ca.cert_len = len;
btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy, btc_blufi_cb_deep_free);
break;
case BLUFI_TYPE_DATA_SUBTYPE_CLIENT_CERT:
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_RECV_CLIENT_CERT;
param.client_cert.cert = &data[0];
param.client_cert.cert_len = len;
btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy, btc_blufi_cb_deep_free);
break;
case BLUFI_TYPE_DATA_SUBTYPE_SERVER_CERT:
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_RECV_SERVER_CERT;
param.client_cert.cert = &data[0];
param.client_cert.cert_len = len;
btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy, btc_blufi_cb_deep_free);
break;
case BLUFI_TYPE_DATA_SUBTYPE_CLIENT_PRIV_KEY:
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY;
param.client_pkey.pkey = &data[0];
param.client_pkey.pkey_len = len;
btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy, btc_blufi_cb_deep_free);
break;
case BLUFI_TYPE_DATA_SUBTYPE_SERVER_PRIV_KEY:
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY;
param.client_pkey.pkey = &data[0];
param.client_pkey.pkey_len = len;
btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy, btc_blufi_cb_deep_free);
break;
case BLUFI_TYPE_DATA_SUBTYPE_CUSTOM_DATA:
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_RECV_CUSTOM_DATA;
param.custom_data.data = &data[0];
param.custom_data.data_len = len;
btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy, btc_blufi_cb_deep_free);
break;
default:
BTC_TRACE_ERROR("%s Unkown Ctrl pkt %02x\n", __func__, type);
break;
}
break;
default:
break;
}
}
#endif ///BLUFI_INCLUDED == TRUE
@@ -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
@@ -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_ */
@@ -0,0 +1,543 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include "btc_blufi_prf.h"
#include "blufi_int.h"
#include "esp_log.h"
#include "esp_blufi_api.h"
#include "esp_err.h"
#include "btc/btc_task.h"
#include "esp_blufi.h"
#include "osi/allocator.h"
#include "console/console.h"
/*nimBLE Host*/
#include "nimble/nimble_port.h"
#include "nimble/nimble_port_freertos.h"
#include "host/ble_hs.h"
#include "host/util/util.h"
#include "host/ble_uuid.h"
#include "host/ble_gatt.h"
#include "services/gap/ble_svc_gap.h"
#include "services/gatt/ble_svc_gatt.h"
#if (BLUFI_INCLUDED == TRUE)
static uint8_t own_addr_type;
struct gatt_value gatt_values[SERVER_MAX_VALUES];
const static char *TAG = "BLUFI_EXAMPLE";
enum {
GATT_VALUE_TYPE_CHR,
GATT_VALUE_TYPE_DSC,
};
static int gatt_svr_access_cb(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg);
static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
{
/*** Service: Blufi */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = BLE_UUID16_DECLARE(BLUFI_SERVICE_UUID),
.characteristics = (struct ble_gatt_chr_def[])
{ {
/*** Characteristic: P2E */
.uuid = BLE_UUID16_DECLARE(BLUFI_CHAR_P2E_UUID),
.access_cb = gatt_svr_access_cb,
.flags = BLE_GATT_CHR_F_WRITE,
.arg = &gatt_values[0],
.val_handle = &gatt_values[0].val_handle,
}, {
/*** Characteristic: E2P */
.uuid = BLE_UUID16_DECLARE(BLUFI_CHAR_E2P_UUID),
.access_cb = gatt_svr_access_cb,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY,
.arg = &gatt_values[1],
.val_handle = &gatt_values[1].val_handle,
}, {
0, /* No more characteristics in this service. */
}
},
},
{
0, /* No more services. */
},
};
void esp_blufi_gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg)
{
char buf[BLE_UUID_STR_LEN];
switch (ctxt->op) {
case BLE_GATT_REGISTER_OP_SVC:
ESP_LOGI(TAG, "registered service %s with handle=%d",
ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf),
ctxt->svc.handle);
break;
case BLE_GATT_REGISTER_OP_CHR:
ESP_LOGI(TAG, "registering characteristic %s with "
"def_handle=%d val_handle=%d\n",
ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf),
ctxt->chr.def_handle,
ctxt->chr.val_handle);
break;
case BLE_GATT_REGISTER_OP_DSC:
ESP_LOGI(TAG, "registering descriptor %s with handle=%d",
ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf),
ctxt->dsc.handle);
break;
default:
assert(0);
break;
}
}
static size_t write_value(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg)
{
struct gatt_value *value = (struct gatt_value *)arg;
uint16_t len;
int rc;
if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
if (ctxt->chr->flags & BLE_GATT_CHR_F_WRITE_AUTHOR) {
return BLE_ATT_ERR_INSUFFICIENT_AUTHOR;
}
} else {
if (ctxt->dsc->att_flags & BLE_ATT_F_WRITE_AUTHOR) {
return BLE_ATT_ERR_INSUFFICIENT_AUTHOR;
}
}
btc_blufi_recv_handler(&ctxt->om->om_data[0], ctxt->om->om_len);
rc = ble_hs_mbuf_to_flat(ctxt->om, value->buf->om_data,
value->buf->om_len, &len);
if (rc != 0) {
return BLE_ATT_ERR_UNLIKELY;
}
/* Maximum attribute value size is 512 bytes */
assert(value->buf->om_len < MAX_VAL_SIZE);
return 0;
}
static size_t read_value(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg)
{
const struct gatt_value *value = (const struct gatt_value *) arg;
char str[BLE_UUID_STR_LEN];
int rc;
memset(str, '\0', sizeof(str));
if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
if (ctxt->chr->flags & BLE_GATT_CHR_F_READ_AUTHOR) {
return BLE_ATT_ERR_INSUFFICIENT_AUTHOR;
}
ble_uuid_to_str(ctxt->chr->uuid, str);
} else {
if (ctxt->dsc->att_flags & BLE_ATT_F_READ_AUTHOR) {
return BLE_ATT_ERR_INSUFFICIENT_AUTHOR;
}
ble_uuid_to_str(ctxt->dsc->uuid, str);
}
rc = os_mbuf_append(ctxt->om, value->buf->om_data, value->buf->om_len);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
static int gatt_svr_access_cb(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg)
{
switch (ctxt->op) {
case BLE_GATT_ACCESS_OP_READ_CHR:
return read_value(conn_handle, attr_handle,
ctxt, arg);
case BLE_GATT_ACCESS_OP_WRITE_CHR:
return write_value(conn_handle, attr_handle,
ctxt, arg);
default:
assert(0);
return BLE_ATT_ERR_UNLIKELY;
}
/* Unknown characteristic; the nimble stack should not have called this
* function.
*/
assert(0);
return BLE_ATT_ERR_UNLIKELY;
}
static void init_gatt_values(void)
{
int i = 0;
const struct ble_gatt_svc_def *svc;
const struct ble_gatt_chr_def *chr;
const struct ble_gatt_dsc_def *dsc;
for (svc = gatt_svr_svcs; svc && svc->uuid; svc++) {
for (chr = svc->characteristics; chr && chr->uuid; chr++) {
assert(i < SERVER_MAX_VALUES);
gatt_values[i].type = GATT_VALUE_TYPE_CHR;
gatt_values[i].ptr = (void *)chr;
gatt_values[i].buf = os_msys_get(0, 0);
os_mbuf_extend(gatt_values[i].buf, 1);
++i;
for (dsc = chr->descriptors; dsc && dsc->uuid; dsc++) {
assert(i < SERVER_MAX_VALUES);
gatt_values[i].type = GATT_VALUE_TYPE_DSC;
gatt_values[i].ptr = (void *)dsc;
gatt_values[i].buf = os_msys_get(0, 0);
os_mbuf_extend(gatt_values[i].buf, 1);
++i;
}
}
}
}
int esp_blufi_gatt_svr_init(void)
{
int rc;
ble_svc_gap_init();
ble_svc_gatt_init();
rc = ble_gatts_count_cfg(gatt_svr_svcs);
if (rc != 0) {
return rc;
}
rc = ble_gatts_add_svcs(gatt_svr_svcs);
if (rc != 0) {
return rc;
}
init_gatt_values();
return 0;
}
static int
esp_blufi_gap_event(struct ble_gap_event *event, void *arg)
{
struct ble_gap_conn_desc desc;
int rc;
switch (event->type) {
case BLE_GAP_EVENT_CONNECT:
/* A new connection was established or a connection attempt failed. */
ESP_LOGI(TAG, "connection %s; status=%d",
event->connect.status == 0 ? "established" : "failed",
event->connect.status);
if (event->connect.status == 0) {
blufi_env.is_connected = true;
blufi_env.recv_seq = blufi_env.send_seq = 0;
btc_msg_t msg;
esp_blufi_cb_param_t param;
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_BLE_CONNECT;
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
assert(rc == 0);
memcpy(param.connect.remote_bda, desc.peer_id_addr.val, ESP_BLUFI_BD_ADDR_LEN);
param.connect.conn_id = event->connect.conn_handle;
/* save connection handle */
blufi_env.conn_id = event->connect.conn_handle;
btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
}
if (event->connect.status != 0) {
/* Connection failed; resume advertising. */
esp_blufi_adv_start();
}
return 0;
case BLE_GAP_EVENT_DISCONNECT:
ESP_LOGI(TAG, "disconnect; reason=%d", event->disconnect.reason);
memcpy(blufi_env.remote_bda, event->disconnect.conn.peer_id_addr.val, ESP_BLUFI_BD_ADDR_LEN);
blufi_env.is_connected = false;
blufi_env.recv_seq = blufi_env.send_seq = 0;
blufi_env.sec_mode = 0x0;
blufi_env.offset = 0;
if (blufi_env.aggr_buf != NULL) {
osi_free(blufi_env.aggr_buf);
blufi_env.aggr_buf = NULL;
}
btc_msg_t msg;
esp_blufi_cb_param_t param;
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_BLE_DISCONNECT;
memcpy(param.disconnect.remote_bda, event->disconnect.conn.peer_id_addr.val, ESP_BLUFI_BD_ADDR_LEN);
btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
return 0;
case BLE_GAP_EVENT_CONN_UPDATE:
/* The central has updated the connection parameters. */
ESP_LOGI(TAG, "connection updated; status=%d",
event->conn_update.status);
return 0;
case BLE_GAP_EVENT_ADV_COMPLETE:
ESP_LOGI(TAG, "advertise complete; reason=%d",
event->adv_complete.reason);
esp_blufi_adv_start();
return 0;
case BLE_GAP_EVENT_SUBSCRIBE:
ESP_LOGI(TAG, "subscribe event; conn_handle=%d attr_handle=%d "
"reason=%d prevn=%d curn=%d previ=%d curi=%d\n",
event->subscribe.conn_handle,
event->subscribe.attr_handle,
event->subscribe.reason,
event->subscribe.prev_notify,
event->subscribe.cur_notify,
event->subscribe.prev_indicate,
event->subscribe.cur_indicate);
return 0;
case BLE_GAP_EVENT_MTU:
ESP_LOGI(TAG, "mtu update event; conn_handle=%d cid=%d mtu=%d",
event->mtu.conn_handle,
event->mtu.channel_id,
event->mtu.value);
blufi_env.frag_size = (event->mtu.value < BLUFI_MAX_DATA_LEN ? event->mtu.value : BLUFI_MAX_DATA_LEN) - BLUFI_MTU_RESERVED_SIZE;
return 0;
}
return 0;
}
void esp_blufi_adv_start(void)
{
int rc;
rc = ble_hs_util_ensure_addr(0);
assert(rc == 0);
/* Figure out address to use while advertising (no privacy for now) */
rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) {
ESP_LOGI(TAG, "error determining address type; rc=%d ", rc);
return;
}
/* Printing ADDR */
uint8_t addr_val[6] = {0};
rc = ble_hs_id_copy_addr(own_addr_type, addr_val, NULL);
/* Begin advertising. */
struct ble_gap_adv_params adv_params;
struct ble_hs_adv_fields fields;
const char *name;
/**
* Set the advertisement data included in our advertisements:
* o Flags (indicates advertisement type and other general info).
* o Advertising tx power.
* o Device name.
* o 16-bit service UUIDs (alert notifications).
*/
memset(&fields, 0, sizeof fields);
/* Advertise two flags:
* o Discoverability in forthcoming advertisement (general)
* o BLE-only (BR/EDR unsupported).
*/
fields.flags = BLE_HS_ADV_F_DISC_GEN |
BLE_HS_ADV_F_BREDR_UNSUP;
/* Indicate that the TX power level field should be included; have the
* stack fill this value automatically. This is done by assigning the
* special value BLE_HS_ADV_TX_PWR_LVL_AUTO.
*/
fields.tx_pwr_lvl_is_present = 1;
fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
name = ble_svc_gap_device_name();
fields.name = (uint8_t *)name;
fields.name_len = strlen(name);
fields.name_is_complete = 1;
fields.uuids16 = (ble_uuid16_t[]) {
BLE_UUID16_INIT(BLUFI_APP_UUID)
};
fields.num_uuids16 = 1;
fields.uuids16_is_complete = 1;
rc = ble_gap_adv_set_fields(&fields);
if (rc != 0) {
ESP_LOGE(TAG, "error setting advertisement data; rc=%d", rc);
return;
}
/* Begin advertising. */
memset(&adv_params, 0, sizeof adv_params);
adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
rc = ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER,
&adv_params, esp_blufi_gap_event, NULL);
if (rc != 0) {
ESP_LOGE(TAG, "error enabling advertisement; rc=%d", rc);
return;
}
}
uint8_t esp_blufi_init(void)
{
blufi_env.enabled = true;
esp_blufi_cb_param_t param;
param.init_finish.state = ESP_BLUFI_INIT_OK;
btc_blufi_cb_to_app(ESP_BLUFI_EVENT_INIT_FINISH, &param);
return ESP_BLUFI_ERROR;
}
void esp_blufi_deinit(void)
{
blufi_env.enabled = false;
btc_msg_t msg;
esp_blufi_cb_param_t param;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_DEINIT_FINISH;
param.deinit_finish.state = ESP_BLUFI_DEINIT_OK;
btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
}
void esp_blufi_send_notify(void *arg)
{
struct pkt_info *pkts = (struct pkt_info *) arg;
struct os_mbuf *om;
om = ble_hs_mbuf_from_flat(pkts->pkt, pkts->pkt_len);
if (om == NULL) {
ESP_LOGE(TAG, "Error in allocating memory");
return;
}
int rc = 0;
rc = ble_gatts_notify_custom(blufi_env.conn_id, gatt_values[1].val_handle, om);
if (rc != 0) {
ESP_LOGE(TAG, "Error in sending notification");
}
}
void esp_blufi_disconnect(void)
{
ble_gap_terminate(blufi_env.conn_id, BLE_ERR_REM_USER_CONN_TERM);
}
void esp_blufi_adv_stop(void) {}
void esp_blufi_send_encap(void *arg)
{
struct blufi_hdr *hdr = (struct blufi_hdr *)arg;
if (blufi_env.is_connected == false) {
BTC_TRACE_WARNING("%s ble connection is broken\n", __func__);
return;
}
btc_blufi_send_notify((uint8_t *)hdr,
((hdr->fc & BLUFI_FC_CHECK) ?
hdr->data_len + sizeof(struct blufi_hdr) + 2 :
hdr->data_len + sizeof(struct blufi_hdr)));
}
void esp_blufi_btc_init(void)
{
int rc;
rc = btc_init();
assert(rc == 0);
}
void esp_blufi_btc_deinit(void)
{
btc_deinit();
}
int esp_blufi_handle_gap_events(struct ble_gap_event *event, void *arg)
{
struct ble_gap_conn_desc desc;
int rc;
if (event != NULL) {
switch (event->type) {
case BLE_GAP_EVENT_CONNECT:
if (event->connect.status == 0) {
btc_msg_t msg;
esp_blufi_cb_param_t param;
blufi_env.is_connected = true;
blufi_env.recv_seq = blufi_env.send_seq = 0;
blufi_env.conn_id = event->connect.conn_handle;
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_BLE_CONNECT;
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
assert(rc == 0);
memcpy(param.connect.remote_bda, desc.peer_id_addr.val, ESP_BLUFI_BD_ADDR_LEN);
param.connect.conn_id = event->connect.conn_handle;
btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
}
return 0;
case BLE_GAP_EVENT_DISCONNECT: {
btc_msg_t msg;
esp_blufi_cb_param_t param;
blufi_env.is_connected = false;
blufi_env.recv_seq = blufi_env.send_seq = 0;
blufi_env.sec_mode = 0x0;
blufi_env.offset = 0;
memcpy(blufi_env.remote_bda,
event->disconnect.conn.peer_id_addr.val,
ESP_BLUFI_BD_ADDR_LEN);
if (blufi_env.aggr_buf != NULL) {
osi_free(blufi_env.aggr_buf);
blufi_env.aggr_buf = NULL;
}
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_BLE_DISCONNECT;
memcpy(param.disconnect.remote_bda,
event->disconnect.conn.peer_id_addr.val,
ESP_BLUFI_BD_ADDR_LEN);
btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
return 0;
}
case BLE_GAP_EVENT_MTU:
blufi_env.frag_size = (event->mtu.value < BLUFI_MAX_DATA_LEN ? event->mtu.value :
BLUFI_MAX_DATA_LEN) - BLUFI_MTU_RESERVED_SIZE;
return 0;
}
}
return 0;
}
#endif
@@ -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__ */
+215
View File
@@ -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_ */
+101
View File
@@ -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__ */
+331
View File
@@ -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;
}
+260
View File
@@ -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);
}
+102
View File
@@ -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;
}
+740
View File
@@ -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;
}
+161
View File
@@ -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);
}
}
+256
View File
@@ -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);
}
}
+97
View File
@@ -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);
}
+63
View File
@@ -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++;
}
}
+270
View File
@@ -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;
}
+84
View File
@@ -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_*/
+145
View File
@@ -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_ */
+59
View File
@@ -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_*/
+145
View File
@@ -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__ */
@@ -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
+125
View File
@@ -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
+53
View File
@@ -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__ */
@@ -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_ */
+110
View File
@@ -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_ */
+121
View File
@@ -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_ */
+52
View File
@@ -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__ */
+16
View File
@@ -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_*/
+88
View File
@@ -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
+43
View File
@@ -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__ */
+120
View File
@@ -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__ */
+312
View File
@@ -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;
}
+99
View File
@@ -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);
}
+25
View File
@@ -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();
}
+144
View File
@@ -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;
}
+77
View File
@@ -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;
}
+453
View File
@@ -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;
}