Merge branch 'master' into feature/esp32s2beta_merge

This commit is contained in:
Angus Gratton 2019-10-25 15:13:52 +11:00 committed by Angus Gratton
commit 7ce75a42c7
115 changed files with 15297 additions and 1267 deletions

View file

@ -23,6 +23,7 @@
#ifndef BOOTLOADER_BUILD
#include "esp_system.h"
#include "driver/periph_ctrl.h"
void bootloader_fill_random(void *buffer, size_t length)
{
@ -71,7 +72,11 @@ void bootloader_random_enable(void)
/* Ensure the hardware RNG is enabled following a soft reset. This should always be the case already (this clock is
never disabled while the CPU is running), this is a "belts and braces" type check.
*/
#ifdef BOOTLOADER_BUILD
DPORT_SET_PERI_REG_MASK(DPORT_WIFI_CLK_EN_REG, DPORT_WIFI_CLK_RNG_EN);
#else
periph_module_enable(PERIPH_RNG_MODULE);
#endif // BOOTLOADER_BUILD
/* Enable SAR ADC in test mode to feed ADC readings of the 1.1V
reference via I2S into the RNG entropy input.
@ -84,7 +89,11 @@ void bootloader_random_enable(void)
#if CONFIG_IDF_TARGET_ESP32
SET_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_SAR2_EN_TEST);
#ifdef BOOTLOADER_BUILD
DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_I2S0_CLK_EN);
#else
periph_module_enable(PERIPH_I2S0_MODULE);
#endif // BOOTLOADER_BUILD
CLEAR_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_ULP_CP_FORCE_START_TOP);
CLEAR_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_ULP_CP_START_TOP);
#elif CONFIG_IDF_TARGET_ESP32S2BETA
@ -138,8 +147,11 @@ void bootloader_random_enable(void)
void bootloader_random_disable(void)
{
/* Disable i2s clock */
#ifdef BOOTLOADER_BUILD
DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_I2S0_CLK_EN);
#else
periph_module_disable(PERIPH_I2S0_MODULE);
#endif // BOOTLOADER_BUILD
/* Reset some i2s configuration (possibly redundant as we reset entire
I2S peripheral further down). */
@ -174,8 +186,12 @@ void bootloader_random_disable(void)
#endif
/* Reset i2s peripheral */
#ifdef BOOTLOADER_BUILD
DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_I2S0_RST);
DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_I2S0_RST);
#else
periph_module_reset(PERIPH_I2S0_MODULE);
#endif
/* Disable pull supply voltage to SAR ADC */
CLEAR_PERI_REG_MASK(RTC_CNTL_TEST_MUX_REG, RTC_CNTL_ENT_RTC);

View file

@ -33,6 +33,7 @@ if(CONFIG_BT_ENABLED)
host/bluedroid/bta/av/include
host/bluedroid/bta/dm/include
host/bluedroid/bta/gatt/include
host/bluedroid/bta/hf_ag/include
host/bluedroid/bta/hf_client/include
host/bluedroid/bta/hh/include
host/bluedroid/bta/jv/include
@ -74,6 +75,7 @@ if(CONFIG_BT_ENABLED)
"host/bluedroid/api/esp_gatt_common_api.c"
"host/bluedroid/api/esp_gattc_api.c"
"host/bluedroid/api/esp_gatts_api.c"
"host/bluedroid/api/esp_hf_ag_api.c"
"host/bluedroid/api/esp_hf_client_api.c"
"host/bluedroid/api/esp_spp_api.c"
"host/bluedroid/bta/ar/bta_ar.c"
@ -116,6 +118,15 @@ if(CONFIG_BT_ENABLED)
"host/bluedroid/bta/jv/bta_jv_api.c"
"host/bluedroid/bta/jv/bta_jv_cfg.c"
"host/bluedroid/bta/jv/bta_jv_main.c"
"host/bluedroid/bta/hf_ag/bta_ag_act.c"
"host/bluedroid/bta/hf_ag/bta_ag_api.c"
"host/bluedroid/bta/hf_ag/bta_ag_at.c"
"host/bluedroid/bta/hf_ag/bta_ag_cfg.c"
"host/bluedroid/bta/hf_ag/bta_ag_cmd.c"
"host/bluedroid/bta/hf_ag/bta_ag_main.c"
"host/bluedroid/bta/hf_ag/bta_ag_rfc.c"
"host/bluedroid/bta/hf_ag/bta_ag_sco.c"
"host/bluedroid/bta/hf_ag/bta_ag_sdp.c"
"host/bluedroid/bta/hf_client/bta_hf_client_act.c"
"host/bluedroid/bta/hf_client/bta_hf_client_api.c"
"host/bluedroid/bta/hf_client/bta_hf_client_at.c"
@ -151,6 +162,8 @@ if(CONFIG_BT_ENABLED)
"host/bluedroid/btc/profile/std/a2dp/btc_av.c"
"host/bluedroid/btc/profile/std/avrc/btc_avrc.c"
"host/bluedroid/btc/profile/std/avrc/bta_avrc_co.c"
"host/bluedroid/btc/profile/std/hf_ag/bta_ag_co.c"
"host/bluedroid/btc/profile/std/hf_ag/btc_hf_ag.c"
"host/bluedroid/btc/profile/std/hf_client/btc_hf_client.c"
"host/bluedroid/btc/profile/std/hf_client/bta_hf_client_co.c"
"host/bluedroid/btc/profile/std/gap/btc_gap_ble.c"

View file

@ -47,6 +47,9 @@
#if (BTC_SPP_INCLUDED == TRUE)
#include "btc_spp.h"
#endif /* #if (BTC_SPP_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 */
@ -110,6 +113,9 @@ static const btc_func_t profile_tab[BTC_PID_NUM] = {
#if (BTC_SPP_INCLUDED == TRUE)
[BTC_PID_SPP] = {btc_spp_call_handler, btc_spp_cb_handler },
#endif /* #if (BTC_SPP_INCLUDED == TRUE) */
#if BTC_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 */

View file

@ -63,6 +63,9 @@ typedef enum {
BTC_PID_AVRC_CT,
BTC_PID_AVRC_TG,
BTC_PID_SPP,
#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 */

View file

@ -27,6 +27,7 @@ ifdef CONFIG_BT_BLUEDROID_ENABLED
COMPONENT_PRIV_INCLUDEDIRS += host/bluedroid/bta/include \
host/bluedroid/bta/ar/include \
host/bluedroid/bta/av/include \
host/bluedroid/bta/hf_ag/include \
host/bluedroid/bta/hf_client/include \
host/bluedroid/bta/dm/include \
host/bluedroid/bta/gatt/include \
@ -77,6 +78,7 @@ COMPONENT_SRCDIRS += host/bluedroid/bta/dm \
host/bluedroid/bta/ar \
host/bluedroid/bta/sys \
host/bluedroid/bta/jv \
host/bluedroid/bta/hf_ag \
host/bluedroid/bta/hf_client \
host/bluedroid/bta \
host/bluedroid/btif \
@ -94,6 +96,7 @@ COMPONENT_SRCDIRS += host/bluedroid/bta/dm \
host/bluedroid/btc/profile/std/a2dp \
host/bluedroid/btc/profile/std/avrc \
host/bluedroid/btc/profile/std/spp \
host/bluedroid/btc/profile/std/hf_ag \
host/bluedroid/btc/profile/std/hf_client \
host/bluedroid/btc/profile \
host/bluedroid/stack/btm \

@ -1 +1 @@
Subproject commit aaadbf2c26002ae85c175cb0e469a3b0bf57bf02
Subproject commit a904c792b1549edc7aedae20ac8c6a889d784b2b

View file

@ -807,8 +807,6 @@ int bt_le_adv_start(const struct bt_mesh_adv_param *param,
} else {
adv_params.conn_mode = BLE_GAP_CONN_MODE_NON;
adv_params.disc_mode = BLE_GAP_DISC_MODE_NON;
adv_params.itvl_min = 160;
adv_params.itvl_max = 160;
}
err = ble_gap_adv_start(BLE_OWN_ADDR_PUBLIC, NULL, BLE_HS_FOREVER, &adv_params,

View file

@ -72,6 +72,9 @@ choice BT_HFP_ROLE
config BT_HFP_CLIENT_ENABLE
bool "Hands Free Unit"
config BT_HFP_AG_ENABLE
bool "Audio Gateway"
endchoice
choice BT_HFP_AUDIO_DATA_PATH

View file

@ -0,0 +1,529 @@
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
//
// 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 <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include "bt_common.h"
#include "btc/btc_common.h"
#include "btc/btc_dm.h"
#include "btc_hf_ag.h"
#include "btc/btc_profile_queue.h"
#include "btc/btc_manage.h"
#include "btc/btc_util.h"
#include "bta/bta_ag_api.h"
#include "bta/bta_api.h"
#include "common/bt_target.h"
#include "common/bt_defs.h"
#include "device/bdaddr.h"
#include "esp_bt.h"
#include "esp_hf_ag_api.h"
#include "esp_err.h"
#include "esp_bt_main.h"
#include "osi/allocator.h"
#if (BTC_HF_INCLUDED == TRUE)
esp_err_t esp_bt_hf_register_callback(esp_hf_cb_t callback)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
if (callback == NULL) {
return ESP_FAIL;
}
btc_profile_cb_set(BTC_PID_HF, callback);
return ESP_OK;
}
esp_err_t esp_bt_hf_init(esp_bd_addr_t remote_addr)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_HF;
msg.act = BTC_HF_INIT_EVT;
btc_hf_args_t arg;
memset(&arg, 0, sizeof(btc_hf_args_t));
memcpy(&(arg.init), remote_addr, sizeof(esp_bd_addr_t));
/* Switch to BTC context */
bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL);
return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_bt_hf_deinit(esp_bd_addr_t remote_addr)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_HF;
msg.act = BTC_HF_DEINIT_EVT;
btc_hf_args_t arg;
memset(&arg, 0, sizeof(btc_hf_args_t));
memcpy(&(arg.deinit), remote_addr, sizeof(esp_bd_addr_t));
/* Switch to BTC context */
bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL);
return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_bt_hf_connect(esp_bd_addr_t remote_addr)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_HF;
msg.act = BTC_HF_CONNECT_EVT;
btc_hf_args_t arg;
memset(&arg, 0, sizeof(btc_hf_args_t));
memcpy(&(arg.connect), remote_addr, sizeof(esp_bd_addr_t));
/* Switch to BTC context */
bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL);
return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_bt_hf_disconnect(esp_bd_addr_t remote_addr)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_HF;
msg.act = BTC_HF_DISCONNECT_EVT;
btc_hf_args_t arg;
memset(&arg, 0, sizeof(btc_hf_args_t));
memcpy(&(arg.disconnect), remote_addr, sizeof(esp_bd_addr_t));
/* Switch to BTC context */
bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL);
return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_bt_hf_connect_audio(esp_bd_addr_t remote_addr)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_HF;
msg.act = BTC_HF_CONNECT_AUDIO_EVT;
btc_hf_args_t arg;
memset(&arg, 0, sizeof(btc_hf_args_t));
memcpy(&(arg.connect_audio), remote_addr, sizeof(esp_bd_addr_t));
/* Switch to BTC context */
bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL);
return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_bt_hf_disconnect_audio(esp_bd_addr_t remote_addr)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_HF;
msg.act = BTC_HF_DISCONNECT_AUDIO_EVT;
btc_hf_args_t arg;
memset(&arg, 0, sizeof(btc_hf_args_t));
memcpy(&(arg.disconnect_audio), remote_addr, sizeof(esp_bd_addr_t));
/* Switch to BTC context */
bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL);
return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_bt_hf_vra(esp_bd_addr_t remote_addr, esp_hf_vr_state_t value)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_HF;
msg.act = BTC_HF_VRA_EVT;
btc_hf_args_t arg;
memset(&arg, 0, sizeof(btc_hf_args_t));
arg.vra_rep.value = value;
memcpy(&(arg.volcon.remote_addr), remote_addr, sizeof(esp_bd_addr_t));
/* Switch to BTC context */
bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL);
return (status = BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_bt_hf_volume_control(esp_bd_addr_t remote_addr, esp_hf_volume_control_target_t type, int volume)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_HF;
msg.act = BTC_HF_VOLUME_CONTROL_EVT;
btc_hf_args_t arg;
memset(&arg, 0, sizeof(btc_hf_args_t));
arg.volcon.target_type = type;
arg.volcon.volume = volume;
memcpy(&(arg.volcon.remote_addr), remote_addr, sizeof(esp_bd_addr_t));
/* Switch to BTC context */
bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL);
return (status = BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_hf_unat_response(esp_bd_addr_t remote_addr, char *unat)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_HF;
msg.act = BTC_HF_UNAT_RESPONSE_EVT;
btc_hf_args_t arg;
memset(&arg, 0, sizeof(btc_hf_args_t));
arg.unat_rep.unat = unat;
memcpy(&(arg.unat_rep.remote_addr), remote_addr, sizeof(esp_bd_addr_t));
/* Switch to BTC context */
bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), btc_hf_arg_deep_copy);
return (status = BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_bt_hf_cmee_response(esp_bd_addr_t remote_addr, esp_hf_at_response_code_t response_code, esp_hf_cme_err_t error_code)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_HF;
msg.act = BTC_HF_CME_ERR_EVT;
btc_hf_args_t arg;
memset(&arg, 0, sizeof(btc_hf_args_t));
arg.ext_at.response_code = response_code;
arg.ext_at.error_code = error_code;
memcpy(&(arg.ext_at.remote_addr), remote_addr, sizeof(esp_bd_addr_t));
/* Switch to BTC context */
bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL);
return (status = BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_bt_hf_indchange_notification(esp_bd_addr_t remote_addr,
esp_hf_call_status_t call_state,
esp_hf_call_setup_status_t call_setup_state,
esp_hf_network_state_t ntk_state, int signal)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_HF;
msg.act = BTC_HF_IND_NOTIFICATION_EVT;
btc_hf_args_t arg;
memset(&arg, 0, sizeof(btc_hf_args_t));
memcpy(&(arg.ind_change.remote_addr), remote_addr, sizeof(esp_bd_addr_t));
arg.ind_change.call_state = call_state;
arg.ind_change.call_setup_state = call_setup_state;
arg.ind_change.ntk_state = ntk_state;
arg.ind_change.signal = signal;
/* Switch to BTC context */
bt_status_t state = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL);
return (state == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_bt_hf_cind_response(esp_bd_addr_t remote_addr,
esp_hf_call_status_t call_state,
esp_hf_call_setup_status_t call_setup_state,
esp_hf_network_state_t ntk_state, int signal, esp_hf_roaming_status_t roam, int batt_lev,
esp_hf_call_held_status_t call_held_status)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_HF;
msg.act = BTC_HF_CIND_RESPONSE_EVT;
btc_hf_args_t arg;
memset(&arg, 0, sizeof(btc_hf_args_t));
memcpy(&(arg.cind_rep.remote_addr), remote_addr, sizeof(esp_bd_addr_t));
arg.cind_rep.call_state = call_state;
arg.cind_rep.call_setup_state = call_setup_state;
arg.cind_rep.ntk_state = ntk_state;
arg.cind_rep.signal = signal;
arg.cind_rep.roam = roam;
arg.cind_rep.batt_lev = batt_lev;
arg.cind_rep.call_held_state = call_held_status;
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL);
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_bt_hf_cops_response(esp_bd_addr_t remote_addr, char *name)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_HF;
msg.act = BTC_HF_COPS_RESPONSE_EVT;
btc_hf_args_t arg;
memset(&arg, 0, sizeof(btc_hf_args_t));
memcpy(&(arg.cops_rep.remote_addr), remote_addr, sizeof(esp_bd_addr_t));
arg.cops_rep.name = name; //deep_copy
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), btc_hf_arg_deep_copy);
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_bt_hf_clcc_response(esp_bd_addr_t remote_addr, int index, esp_hf_current_call_direction_t dir,
esp_hf_current_call_status_t current_call_state, esp_hf_current_call_mode_t mode,
esp_hf_current_call_mpty_type_t mpty, char *number, esp_hf_call_addr_type_t type)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_HF;
msg.act = BTC_HF_CLCC_RESPONSE_EVT;
btc_hf_args_t arg;
memset(&arg, 0, sizeof(btc_hf_args_t));
memcpy(&(arg.clcc_rep.remote_addr), remote_addr, sizeof(esp_bd_addr_t));
//mandatory args
arg.clcc_rep.index = index;
arg.clcc_rep.dir = dir;
arg.clcc_rep.current_call_state = current_call_state;
arg.clcc_rep.mode = mode;
arg.clcc_rep.mpty = mpty;
// option args
arg.clcc_rep.number = number; //deep_copy
arg.clcc_rep.type = type;
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), btc_hf_arg_deep_copy);
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_bt_hf_cnum_response(esp_bd_addr_t remote_addr, char *number, esp_hf_subscriber_service_type_t type)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_HF;
msg.act = BTC_HF_CNUM_RESPONSE_EVT;
btc_hf_args_t arg;
memset(&arg, 0, sizeof(btc_hf_args_t));
memcpy(&(arg.cnum_rep), remote_addr, sizeof(esp_bd_addr_t));
arg.cnum_rep.number = number; //deep_copy
arg.cnum_rep.type = type;
/* Switch to BTC context */
bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), btc_hf_arg_deep_copy);
return (status = BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_bt_hf_bsir(esp_bd_addr_t remote_addr, esp_hf_in_band_ring_state_t state)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_HF;
msg.act = BTC_HF_INBAND_RING_EVT;
btc_hf_args_t arg;
memset(&arg, 0, sizeof(btc_hf_args_t));
memcpy(&(arg.bsir.remote_addr), remote_addr, sizeof(esp_bd_addr_t));
arg.bsir.state = state;
/* Switch to BTC context */
bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL);
return (status = BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_bt_hf_answer_call(esp_bd_addr_t remote_addr, int num_active, int num_held,
esp_hf_call_status_t call_state, esp_hf_call_setup_status_t call_setup_state,
char *number, esp_hf_call_addr_type_t call_addr_type)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_HF;
msg.act = BTC_HF_AC_INCALL_EVT;
btc_hf_args_t arg;
memset(&arg, 0, sizeof(btc_hf_args_t));
memcpy(&(arg.phone.remote_addr), remote_addr, sizeof(esp_bd_addr_t));
arg.phone.num_active = num_active;
arg.phone.num_held = num_held;
arg.phone.call_state = call_state;
arg.phone.call_setup_state = call_setup_state;
arg.phone.number = number; //deep_copy
arg.phone.call_addr_type = call_addr_type;
/* Switch to BTC context */
bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), btc_hf_arg_deep_copy);
return (status = BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_bt_hf_reject_call(esp_bd_addr_t remote_addr, int num_active, int num_held,
esp_hf_call_status_t call_state, esp_hf_call_setup_status_t call_setup_state,
char *number, esp_hf_call_addr_type_t call_addr_type)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_HF;
msg.act = BTC_HF_RJ_INCALL_EVT;
btc_hf_args_t arg;
memset(&arg, 0, sizeof(btc_hf_args_t));
memcpy(&(arg.phone.remote_addr), remote_addr, sizeof(esp_bd_addr_t));
arg.phone.num_active = num_active;
arg.phone.num_held = num_held;
arg.phone.call_state = call_state;
arg.phone.call_setup_state = call_setup_state;
arg.phone.number = number; //deep_copy
arg.phone.call_addr_type = call_addr_type;
/* Switch to BTC context */
bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), btc_hf_arg_deep_copy);
return (status = BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_bt_hf_end_call(esp_bd_addr_t remote_addr, int num_active, int num_held,
esp_hf_call_status_t call_state, esp_hf_call_setup_status_t call_setup_state,
char *number, esp_hf_call_addr_type_t call_addr_type)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_HF;
msg.act = BTC_HF_END_CALL_EVT;
btc_hf_args_t arg;
memset(&arg, 0, sizeof(btc_hf_args_t));
memcpy(&(arg.phone.remote_addr), remote_addr, sizeof(esp_bd_addr_t));
arg.phone.num_active = num_active;
arg.phone.num_held = num_held;
arg.phone.call_state = call_state;
arg.phone.call_setup_state = call_setup_state;
arg.phone.number = number; //deep_copy
arg.phone.call_addr_type = call_addr_type;
/* Switch to BTC context */
bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), btc_hf_arg_deep_copy);
return (status = BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_bt_hf_out_call(esp_bd_addr_t remote_addr, int num_active, int num_held,
esp_hf_call_status_t call_state, esp_hf_call_setup_status_t call_setup_state,
char *number, esp_hf_call_addr_type_t call_addr_type)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_HF;
msg.act = BTC_HF_OUT_CALL_EVT;
btc_hf_args_t arg;
memset(&arg, 0, sizeof(btc_hf_args_t));
memcpy(&(arg.phone.remote_addr), remote_addr, sizeof(esp_bd_addr_t));
arg.phone.num_active = num_active;
arg.phone.num_held = num_held;
arg.phone.call_state = call_state;
arg.phone.call_setup_state = call_setup_state;
arg.phone.number = number; //deep_copy
arg.phone.call_addr_type = call_addr_type;
/* Switch to BTC context */
bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), btc_hf_arg_deep_copy);
return (status = BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_bt_hf_register_data_callback(esp_hf_incoming_data_cb_t recv, esp_hf_outgoing_data_cb_t send)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_HF;
msg.act = BTC_HF_REGISTER_DATA_CALLBACK_EVT;
btc_hf_args_t arg;
memset(&arg, 0, sizeof(btc_hf_args_t));
arg.reg_data_cb.recv = recv;
arg.reg_data_cb.send = send;
/* Switch to BTC context */
bt_status_t status = btc_transfer_context(&msg, &arg, sizeof(btc_hf_args_t), NULL);
return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
#if (BTM_SCO_HCI_INCLUDED == TRUE)
void esp_hf_outgoing_data_ready(void)
{
BTA_AgCiData();
}
#endif /* #if (BTM_SCO_HCI_INCLUDED == TRUE ) */
#endif // BTC_HF_INCLUDED

View file

@ -0,0 +1,535 @@
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
//
// 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 __ESP_HF_AG_API_H__
#define __ESP_HF_AG_API_H__
#include "esp_err.h"
#include "esp_bt_defs.h"
#include "esp_hf_defs.h"
#ifdef __cplusplus
extern "C" {
#endif
/* features masks of HF AG */
#define ESP_HF_PEER_FEAT_3WAY 0x01 /* Three-way calling */
#define ESP_HF_PEER_FEAT_ECNR 0x02 /* Echo cancellation and/or noise reduction */
#define ESP_HF_PEER_FEAT_VREC 0x04 /* Voice recognition */
#define ESP_HF_PEER_FEAT_INBAND 0x08 /* In-band ring tone */
#define ESP_HF_PEER_FEAT_VTAG 0x10 /* Attach a phone number to a voice tag */
#define ESP_HF_PEER_FEAT_REJECT 0x20 /* Ability to reject incoming call */
#define ESP_HF_PEER_FEAT_ECS 0x40 /* Enhanced Call Status */
#define ESP_HF_PEER_FEAT_ECC 0x80 /* Enhanced Call Control */
#define ESP_HF_PEER_FEAT_EXTERR 0x100 /* Extended error codes */
#define ESP_HF_PEER_FEAT_CODEC 0x200 /* Codec Negotiation */
/* CHLD feature masks of HF AG */
#define ESP_HF_CHLD_FEAT_REL 0x01 /* 0 Release waiting call or held calls */
#define ESP_HF_CHLD_FEAT_REL_ACC 0x02 /* 1 Release active calls and accept other waiting or held call */
#define ESP_HF_CHLD_FEAT_REL_X 0x04 /* 1x Release specified active call only */
#define ESP_HF_CHLD_FEAT_HOLD_ACC 0x08 /* 2 Active calls on hold and accept other waiting or held call */
#define ESP_HF_CHLD_FEAT_PRIV_X 0x10 /* 2x Request private mode with specified call(put the rest on hold) */
#define ESP_HF_CHLD_FEAT_MERGE 0x20 /* 3 Add held call to multiparty */
#define ESP_HF_CHLD_FEAT_MERGE_DETACH 0x40 /* 4 Connect two calls and leave(disconnect from multiparty) */
/// HF callback events
typedef enum
{
ESP_HF_CONNECTION_STATE_EVT = 0, /*!< Connection state changed event */
ESP_HF_AUDIO_STATE_EVT, /*!< Audio connection state change event */
ESP_HF_BVRA_EVT, /*!< Voice recognition state change event */
ESP_HF_VOLUME_CONTROL_EVT, /*!< Audio volume control command from HF Client, provided by +VGM or +VGS message */
ESP_HF_UNAT_RESPONSE_EVT, /*!< Unknown AT cmd Response*/
ESP_HF_CIND_RESPONSE_EVT, /*!< Call And Device Indicator Response*/
ESP_HF_COPS_RESPONSE_EVT, /*!< Current operator information */
ESP_HF_CLCC_RESPONSE_EVT, /*!< List of current calls notification */
ESP_HF_CNUM_RESPONSE_EVT, /*!< Subscriber information response from HF Client */
ESP_HF_VTS_RESPONSE_EVT, /*!< Enable or not DTMF */
ESP_HF_NREC_RESPONSE_EVT, /*!< Enable or not NREC */
ESP_HF_ATA_RESPONSE_EVT, /*!< Answer an Incoming Call */
ESP_HF_CHUP_RESPONSE_EVT, /*!< Reject an Incoming Call */
ESP_HF_DIAL_EVT, /*!< Origin an outgoing call with specific number or the dial the last number */
ESP_HF_BAC_RESPONSE_EVT, /*!< Codec Negotiation */
ESP_HF_BCS_RESPONSE_EVT, /*!< Codec Negotiation */
} esp_hf_cb_event_t;
// HF callback parameters of corresponding esp event in esp_hf_cb_event_t
typedef union
{
/**
* @brief ESP_HS_CONNECTION_STATE_EVT
*/
struct hf_conn_stat_param {
esp_bd_addr_t remote_bda; /*!< remote bluetooth device address */
esp_hf_connection_state_t state; /*!< HF connection state */
uint32_t peer_feat; /*!< AG supported features */
uint32_t chld_feat; /*!< AG supported features on call hold and multiparty services */
} conn_stat; /*!< AG callback param of ESP_AG_CONNECTION_STATE_EVT */
/**
* @brief ESP_HF_AUDIO_STATE_EVT
*/
struct hf_audio_stat_param {
esp_bd_addr_t remote_addr; /*!< remote bluetooth device address */
esp_hf_audio_state_t state; /*!< audio connection state */
} audio_stat; /*!< AG callback param of ESP_AG_AUDIO_STATE_EVT */
/**
* @brief ESP_HF_BVRA_EVT
*/
struct hf_bvra_param {
esp_bd_addr_t remote_addr;
esp_hf_vr_state_t value; /*!< voice recognition state */
} vra_rep; /*!< AG callback param of ESP_AG_BVRA_EVT */
/**
* @brief ESP_HF_VOLUME_CONTROL_EVT
*/
struct hf_volume_control_param {
esp_hf_volume_type_t type; /*!< volume control target, speaker or microphone */
int volume; /*!< gain, ranges from 0 to 15 */
} volume_control; /*!< AG callback param of ESP_AG_VOLUME_CONTROL_EVT */
/**
* @brief ESP_HF_UNAT_RESPOSNE_EVT
*/
struct hf_unat_param {
char *unat;
}unat_rep;
/**
* @brief ESP_HF_AT_RESPONSE_EVT
*/
struct hf_at_code_param {
esp_hf_at_response_code_t code; /*!< AT response code */
esp_hf_cme_err_t cme; /*!< Extended Audio Gateway Error Result Code */
} at; /*!< AG callback param of ESP_HF_EXT_AT_EVT */
/**
* @brief ESP_HF_CIND_CALL_EVT
*/
struct hf_cind_param {
esp_hf_call_status_t call_status; /*!< call status indicator */
esp_hf_call_setup_status_t call_setup_status; /*!< call setup status indicator */
esp_hf_network_state_t svc; /*!< bluetooth proprietary call hold status indicator */
int signal_strength; /*!< bluetooth proprietary call hold status indicator */
esp_hf_roaming_status_t roam; /*!< bluetooth proprietary call hold status indicator */
int battery_level; /*!< battery charge value, ranges from 0 to 5 */
esp_hf_call_held_status_t call_held_status; /*!< bluetooth proprietary call hold status indicator */
} cind;
/**
* @brief ESP_HF_VTS_RESPOSNE_EVT
*/
struct hf_vts_param {
char *code;
}vts_rep;
/**
* @brief ESP_HF_NREC_RESPOSNE_EVT
*/
struct hf_nrec_param {
esp_hf_nrec_t state;
} nrec;
struct hf_outcall_param {
esp_bd_addr_t remote_addr;
char *num_or_loc;
} out_call;
/**
* @brief ESP_HF_BSIR_EVT
*/
struct hf_bsir_param {
esp_bd_addr_t remote_addr;
esp_hf_in_band_ring_state_t state; /*!< setting state of in-band ring tone */
} bsir;
/**
* @brief ESP_HF_BCS_RESPONSE_EVT
*/
struct hf_codec_param {
esp_hf_wbs_config_t mode;
} codec;
} esp_hf_cb_param_t;
/**
* @brief AG incoming data callback function, the callback is useful in case of
* Voice Over HCI.
* @param[in] buf : pointer to incoming data(payload of HCI synchronous data packet), the
* buffer is allocated inside bluetooth protocol stack and will be released after
* invoke of the callback is finished.
* @param[in] len : size(in bytes) in buf
*/
typedef void (* esp_hf_incoming_data_cb_t)(const uint8_t *buf, uint32_t len);
/**
* @brief AG outgoing data callback function, the callback is useful in case of
* Voice Over HCI. Once audio connection is set up and the application layer has
* prepared data to send, the lower layer will call this function to read data
* and then send. This callback is supposed to be implemented as non-blocking,
* and if data is not enough, return value 0 is supposed.
*
* @param[in] buf : pointer to incoming data(payload of HCI synchronous data packet), the
* buffer is allocated inside bluetooth protocol stack and will be released after
* invoke of the callback is finished.
* @param[in] len : size(in bytes) in buf
* @param[out] length of data successfully read
*/
typedef uint32_t (* esp_hf_outgoing_data_cb_t) (uint8_t *buf, uint32_t len);
/**
* @brief HF AG callback function type
*
* @param event : Event type
*
* @param param : Pointer to callback parameter
*/
typedef void (* esp_hf_cb_t) (esp_hf_cb_event_t event, esp_hf_cb_param_t *param);
/************************************************************************************
** ESP HF API
************************************************************************************/
/**
* @brief Register application callback function to HFP client module. This function should be called
* only after esp_bluedroid_enable() completes successfully, used by HFP client
*
* @param[in] callback: HFP AG event callback function
*
* @return
* - ESP_OK: success
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: if callback is a NULL function pointer
*
*/
esp_err_t esp_bt_hf_register_callback(esp_hf_cb_t callback);
/**
*
* @brief Initialize the bluetooth HF AG module. This function should be called
* after esp_bluedroid_enable() completes successfully
*
* @return
* - ESP_OK: if the initialization request is sent successfully
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*
*/
esp_err_t esp_bt_hf_init(esp_bd_addr_t remote_addr);
/**
*
* @brief De-initialize for HF AG module. This function
* should be called only after esp_bluedroid_enable() completes successfully
*
* @return
* - ESP_OK: success
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*
*/
esp_err_t esp_bt_hf_deinit(esp_bd_addr_t remote_addr);
/**
*
* @brief Connect to remote bluetooth HFP client device, must after esp_bt_hf_init()
*
* @param[in] remote_bda: remote bluetooth HFP client device address
*
* @return
* - ESP_OK: connect request is sent to lower layer
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*
*/
esp_err_t esp_bt_hf_connect(esp_bd_addr_t remote_bda);
/**
*
* @brief Disconnect from the remote HFP client
*
* @param[in] remote_bda: remote bluetooth device address
* @return
* - ESP_OK: disconnect request is sent to lower layer
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*
*/
esp_err_t esp_bt_hf_disconnect(esp_bd_addr_t remote_bda);
/**
*
* @brief Create audio connection with remote HFP client. As a precondition to use this API,
* Service Level Connection shall exist between HF client and AG.
*
* @param[in] remote_bda: remote bluetooth device address
* @return
* - ESP_OK: disconnect request is sent to lower layer
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*
*/
esp_err_t esp_bt_hf_connect_audio(esp_bd_addr_t remote_bda);
/**
*
* @brief Release the established audio connection with remote HFP client.
*
* @param[in] remote_bda: remote bluetooth device address
* @return
* - ESP_OK: disconnect request is sent to lower layer
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*
*/
esp_err_t esp_bt_hf_disconnect_audio(esp_bd_addr_t remote_bda);
/**
*
* @brief Response of Volume Recognition Command(AT+VRA) from HFP client. As a precondition to use this API,
* Service Level Connection shall exist with HFP client.
*
* @param[in] remote: volume control target, speaker or microphone
* @param[in] volume: gain of the speaker of microphone, ranges 0 to 15
*
* @return
* - ESP_OK: disconnect request is sent to lower layer
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*
*/
esp_err_t esp_bt_hf_vra(esp_bd_addr_t remote_bda, esp_hf_vr_state_t value);
/**
*
* @brief Volume synchronization with HFP client. As a precondition to use this API,
* Service Level Connection shall exist with HFP client
*
* @param[in] type: volume control target, speaker or microphone
* @param[in] volume: gain of the speaker of microphone, ranges 0 to 15
*
* @return
* - ESP_OK: disconnect request is sent to lower layer
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*
*/
esp_err_t esp_bt_hf_volume_control(esp_bd_addr_t remote_bda, esp_hf_volume_control_target_t type, int volume);
/**
*
* @brief Handle Unknown AT command from HFP Client.
* As a precondition to use this API, Service Level Connection shall exist between AG and HF Client
*
* @return
* - ESP_OK: disconnect request is sent to lower layer
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*
*/
esp_err_t esp_hf_unat_response(esp_bd_addr_t remote_addr, char *unat);
/**
*
* @brief Unsolicited send extend AT error code to HFP Client.
* As a precondition to use this API, Service Level Connection shall exist between AG and HF Client
*
* @return
* - ESP_OK: disconnect request is sent to lower layer
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*
*/
esp_err_t esp_bt_hf_cmee_response(esp_bd_addr_t remote_bda, esp_hf_at_response_code_t response_code, esp_hf_cme_err_t error_code);
/**
*
* @brief Usolicited send device status notificationto HFP Client.
* As a precondition to use this API, Service Level Connection shall exist between AG and HF Client
*
* @return
* - ESP_OK: disconnect request is sent to lower layer
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*
*/
esp_err_t esp_bt_hf_indchange_notification(esp_bd_addr_t remote_addr, esp_hf_call_status_t call_state,
esp_hf_call_setup_status_t call_setup_state,
esp_hf_network_state_t ntk_state, int signal);
/**
*
* @brief Response to device individual indicatiors to HFP Client.
* As a precondition to use this API, Service Level Connection shall exist between AG and HF Client.
*
* @return
* - ESP_OK: disconnect request is sent to lower layer
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*
*/
esp_err_t esp_bt_hf_cind_response(esp_bd_addr_t remote_addr,
esp_hf_call_status_t call_state,
esp_hf_call_setup_status_t call_setup_state,
esp_hf_network_state_t ntk_state, int signal, esp_hf_roaming_status_t roam, int batt_lev,
esp_hf_call_held_status_t call_held_status);
/**
*
* @brief Query the name of currently selected network operator in AG,
* As a precondition to use this API, Service Level Connection shall exist with HFP Client
*
* @return
* - ESP_OK: disconnect request is sent to lower layer
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*
*/
esp_err_t esp_bt_hf_cops_response(esp_bd_addr_t remote_addr, char *name);
/**
*
* @brief Response to Query list of current calls from HFP Client (use AT+CLCC command),
* As a precondition to use this API, Service Level Connection shall exist between AG and HF Client
*
* @return
* - ESP_OK: disconnect request is sent to lower layer
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*
*/
esp_err_t esp_bt_hf_clcc_response(esp_bd_addr_t remote_addr, int index, esp_hf_current_call_direction_t dir,
esp_hf_current_call_status_t current_call_state, esp_hf_current_call_mode_t mode,
esp_hf_current_call_mpty_type_t mpty, char *number, esp_hf_call_addr_type_t type);
/**
*
* @brief Get subscriber information number from HFP client(send AT+CNUM command),
* As a precondition to use this API, Service Level Connection shall exist with AG
*
* @return
* - ESP_OK: disconnect request is sent to lower layer
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*
*/
esp_err_t esp_bt_hf_cnum_response(esp_bd_addr_t remote_addr, char *number, esp_hf_subscriber_service_type_t type);
/**
*
* @brief Inform HF Client of Provided or not Inband Ring Tone.
* As a precondition to use this API, Service Level Connection shall exist with AG
*
* @return
* - ESP_OK: disconnect request is sent to lower layer
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*
*/
esp_err_t esp_bt_hf_bsir(esp_bd_addr_t remote_addr, esp_hf_in_band_ring_state_t state);
/**
*
* @brief Answer Incoming Call by AG or response to the AT+A command from Hands-Free Unit.
* As a precondition to use this API, Service Level Connection shall exist with AG.
*
* @return
* - ESP_OK: disconnect request is sent to lower layer
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*
*/
esp_err_t esp_bt_hf_answer_call(esp_bd_addr_t remote_addr, int num_active, int num_held,
esp_hf_call_status_t call_state, esp_hf_call_setup_status_t call_setup_state,
char *number, esp_hf_call_addr_type_t call_addr_type);
/**
*
* @brief Reject Incoming Call by AG or response to the AT+CHUP command from Hands-Free Unit.
* As a precondition to use this API, Service Level Connection shall exist with AG.
*
* @return
* - ESP_OK: disconnect request is sent to lower layer
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*
*/
esp_err_t esp_bt_hf_reject_call(esp_bd_addr_t remote_addr, int num_active, int num_held,
esp_hf_call_status_t call_state, esp_hf_call_setup_status_t call_setup_state,
char *number, esp_hf_call_addr_type_t call_addr_type);
/**
*
* @brief Reject Incoming Call by AG or response to the AT+CHUP command from Hands-Free Unit.
* As a precondition to use this API, Service Level Connection shall exist with AG.
*
* @return
* - ESP_OK: disconnect request is sent to lower layer
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*
*/
esp_err_t esp_bt_hf_out_call(esp_bd_addr_t remote_addr, int num_active, int num_held,
esp_hf_call_status_t call_state, esp_hf_call_setup_status_t call_setup_state,
char *number, esp_hf_call_addr_type_t call_addr_type);
/**
*
* @brief Reject Incoming Call by AG or response to the AT+CHUP command from Hands-Free Unit.
* As a precondition to use this API, Service Level Connection shall exist with AG.
*
* @return
* - ESP_OK: disconnect request is sent to lower layer
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: others
*
*/
esp_err_t esp_bt_hf_end_call(esp_bd_addr_t remote_addr, int num_active, int num_held,
esp_hf_call_status_t call_state, esp_hf_call_setup_status_t call_setup_state,
char *number, esp_hf_call_addr_type_t call_addr_type);
/**
* @brief Register AG data output function; the callback is only used in
* the case that Voice Over HCI is enabled.
*
* @param[in] recv: HFP client incoming data callback function
* @param[in] send: HFP client outgoing data callback function
*
* @return
* - ESP_OK: success
* - ESP_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: if callback is a NULL function pointer
*
*/
esp_err_t esp_bt_hf_register_data_callback(esp_hf_incoming_data_cb_t recv, esp_hf_outgoing_data_cb_t send);
/**
* @brief Trigger the lower-layer to fetch and send audio data. This function is only
* only used in the case that Voice Over HCI is enabled. Precondition is that
* the HFP audio connection is connected. After this function is called, lower
* layer will invoke esp_hf_client_outgoing_data_cb_t to fetch data
*
*/
void esp_hf_outgoing_data_ready(void);
#ifdef __cplusplus
}
#endif
#endif //__ESP_HF_AG_API_H__

View file

@ -126,7 +126,7 @@ typedef union {
* @brief ESP_HF_CLIENT_CIND_SERVICE_AVAILABILITY_EVT
*/
struct hf_client_service_availability_param {
esp_hf_service_availability_status_t status; /*!< service availability status */
esp_hf_network_state_t status; /*!< service availability status */
} service_availability; /*!< HF callback param of ESP_HF_CLIENT_CIND_SERVICE_AVAILABILITY_EVT */
/**

View file

@ -21,6 +21,44 @@
extern "C" {
#endif
#define ESP_BT_HF_NUMBER_LEN (32)
#define ESP_BT_HF_OPERATOR_NAME_LEN (16)
#ifndef BTC_HSAG_SERVICE_NAME
#define BTC_HSAG_SERVICE_NAME ("Headset Gateway")
#endif
#ifndef BTC_HFAG_SERVICE_NAME
#define BTC_HFAG_SERVICE_NAME ("Handsfree Gateway")
#endif
#ifndef BTC_HF_SERVICES
#define BTC_HF_SERVICES (BTA_HSP_SERVICE_MASK | BTA_HFP_SERVICE_MASK )
#endif
#ifndef BTC_HF_SERVICE_NAMES
#define BTC_HF_SERVICE_NAMES {BTC_HSAG_SERVICE_NAME , BTC_HFAG_SERVICE_NAME}
#endif
#ifndef BTC_HF_SECURITY
#define BTC_HF_SECURITY (BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT)
#endif
#define BTC_HF_CALL_END_TIMEOUT 6
#define BTC_HF_INVALID_IDX -1
/// in-band ring tone state
typedef enum {
ESP_HF_IN_BAND_RINGTONE_NOT_PROVIDED = 0,
ESP_HF_IN_BAND_RINGTONE_PROVIDED,
} esp_hf_in_band_ring_state_t;
/// voice recognition state
typedef enum {
ESP_HF_VR_STATE_DISABLED = 0, /*!< voice recognition disabled */
ESP_HF_VR_STATE_ENABLED, /*!< voice recognition enabled */
} esp_hf_vr_state_t;
/// Bluetooth HFP audio volume control target
typedef enum {
@ -28,11 +66,32 @@ typedef enum {
ESP_HF_VOLUME_CONTROL_TARGET_MIC, /*!< microphone */
} esp_hf_volume_control_target_t;
/// +CIND roaming status indicator values
/// Bluetooth HFP audio connection status
typedef enum {
ESP_HF_ROAMING_STATUS_INACTIVE = 0, /*!< roaming is not active */
ESP_HF_ROAMING_STATUS_ACTIVE, /*!< a roaming is active */
} esp_hf_roaming_status_t;
ESP_HF_AUDIO_STATE_DISCONNECTED = 0, /*!< audio connection released */
ESP_HF_AUDIO_STATE_CONNECTING, /*!< audio connection has been initiated */
ESP_HF_AUDIO_STATE_CONNECTED, /*!< audio connection is established */
ESP_HF_AUDIO_STATE_CONNECTED_MSBC, /*!< mSBC audio connection is established */
} esp_hf_audio_state_t;
typedef enum {
ESP_HF_VOLUME_TYPE_SPK = 0,
ESP_HF_VOLUME_TYPE_MIC
} esp_hf_volume_type_t;
/// +CIND network service availability status
typedef enum
{
ESP_HF_NETWORK_STATE_NOT_AVAILABLE = 0,
ESP_HF_NETWORK_STATE_AVAILABLE
} esp_hf_network_state_t;
/** +CIEV Service type */
typedef enum
{
ESP_HF_SERVICE_TYPE_HOME = 0,
ESP_HF_SERVICE_TYPE_ROAMING
} esp_hf_service_type_t;
/// +CIND call status indicator values
typedef enum {
@ -42,12 +101,18 @@ typedef enum {
/// +CIND call setup status indicator values
typedef enum {
ESP_HF_CALL_SETUP_STATUS_NONE = 0, /*!< no call setup in progress */
ESP_HF_CALL_SETUP_STATUS_IDLE = 0, /*!< no call setup in progress */
ESP_HF_CALL_SETUP_STATUS_INCOMING = 1, /*!< incoming call setup in progress */
ESP_HF_CALL_SETUP_STATUS_OUTGOING_DIALING = 2, /*!< outgoing call setup in dialing state */
ESP_HF_CALL_SETUP_STATUS_OUTGOING_ALERTING = 3, /*!< outgoing call setup in alerting state */
} esp_hf_call_setup_status_t;
/// +CIND roaming status indicator values
typedef enum {
ESP_HF_ROAMING_STATUS_INACTIVE = 0, /*!< roaming is not active */
ESP_HF_ROAMING_STATUS_ACTIVE, /*!< a roaming is active */
} esp_hf_roaming_status_t;
/// +CIND call held indicator values
typedef enum {
ESP_HF_CALL_HELD_STATUS_NONE = 0, /*!< no calls held */
@ -55,12 +120,6 @@ typedef enum {
ESP_HF_CALL_HELD_STATUS_HELD = 2, /*!< call on hold, no active call*/
} esp_hf_call_held_status_t;
/// +CIND network service availability status
typedef enum {
ESP_HF_SERVICE_AVAILABILITY_STATUS_UNAVAILABLE = 0, /*!< service not available */
ESP_HF_SERVICE_AVAILABILITY_STATUS_AVAILABLE, /*!< service available */
} esp_hf_service_availability_status_t;
/// +CLCC status of the call
typedef enum {
ESP_HF_CURRENT_CALL_STATUS_ACTIVE = 0, /*!< active */
@ -118,23 +177,35 @@ typedef enum {
ESP_HF_BTRH_CMD_REJECT = 2, /*!< reject a held incoming call */
} esp_hf_btrh_cmd_t;
/// response indication codes for AT commands
typedef enum {
ESP_HF_AT_RESPONSE_CODE_OK = 0, /*!< acknowledges execution of a command line */
ESP_HF_AT_RESPONSE_CODE_ERR, /*!< command not accepted */
ESP_HF_AT_RESPONSE_CODE_NO_CARRIER, /*!< connection terminated */
ESP_HF_AT_RESPONSE_CODE_BUSY, /*!< busy signal detected */
ESP_HF_AT_RESPONSE_CODE_NO_ANSWER, /*!< connection completion timeout */
ESP_HF_AT_RESPONSE_CODE_DELAYED, /*!< delayed */
ESP_HF_AT_RESPONSE_CODE_BLACKLISTED, /*!< blacklisted */
ESP_HF_AT_RESPONSE_CODE_CME, /*!< CME error */
} esp_hf_at_response_code_t;
/* +NREC */
typedef enum
{
ESP_HF_NREC_STOP = 0,
ESP_HF_NREC_START
} esp_hf_nrec_t;
/// voice recognition state
///+CCWA resposne status
typedef enum {
ESP_HF_VR_STATE_DISABLED = 0, /*!< voice recognition disabled */
ESP_HF_VR_STATE_ENABLED, /*!< voice recognition enabled */
} esp_hf_vr_state_t;
ESP_HF_CALL_WAITING_INACTIVE,
ESP_HF_CALL_WAITING_ACTIVE,
} esp_hf_call_waiting_status_t;
/* WBS codec setting */
typedef enum
{
ESP_HF_WBS_NONE,
ESP_HF_WBS_NO,
ESP_HF_WBS_YES
}esp_hf_wbs_config_t;
/// Bluetooth HFP RFCOMM connection and service level connection status
typedef enum {
ESP_HF_CONNECTION_STATE_DISCONNECTED = 0, /*!< RFCOMM data link channel released */
ESP_HF_CONNECTION_STATE_CONNECTING, /*!< connecting remote device on the RFCOMM data link*/
ESP_HF_CONNECTION_STATE_CONNECTED, /*!< RFCOMM connection established */
ESP_HF_CONNECTION_STATE_SLC_CONNECTED, /*!< service level connection established */
ESP_HF_CONNECTION_STATE_DISCONNECTING, /*!< disconnecting with remote device on the RFCOMM data link*/
} esp_hf_connection_state_t;
/// AT+CHLD command values
typedef enum {
@ -147,6 +218,24 @@ typedef enum {
ESP_HF_CHLD_TYPE_PRIV_X, /*!< <2x>, request private consultation mode with specified call */
} esp_hf_chld_type_t;
/* AT response code - OK/Error */
typedef enum {
ESP_HF_AT_RESPONSE_CODE_OK = 0, /*!< acknowledges execution of a command line */
ESP_HF_AT_RESPONSE_CODE_ERR, /*!< command not accepted */
ESP_HF_AT_RESPONSE_CODE_NO_CARRIER, /*!< connection terminated */
ESP_HF_AT_RESPONSE_CODE_BUSY, /*!< busy signal detected */
ESP_HF_AT_RESPONSE_CODE_NO_ANSWER, /*!< connection completion timeout */
ESP_HF_AT_RESPONSE_CODE_DELAYED, /*!< delayed */
ESP_HF_AT_RESPONSE_CODE_BLACKLISTED, /*!< blacklisted */
ESP_HF_AT_RESPONSE_CODE_CME, /*!< CME error */
} esp_hf_at_response_code_t;
/* AT response code - OK/Error */
typedef enum {
ESP_HF_AT_RESPONSE_ERROR = 0,
ESP_HF_AT_RESPONSE_OK
} esp_hf_at_response_t;
/// Extended Audio Gateway Error Result Code Response
typedef enum {
ESP_HF_CME_AG_FAILURE = 0, /*!< ag failure */
@ -174,6 +263,84 @@ typedef enum {
ESP_HF_CME_NETWORK_NOT_ALLOWED = 32, /*!< network not allowed --emergency calls only */
} esp_hf_cme_err_t;
/** Callback for connection state change.
* state will have one of the values from BtHfConnectionState
*/
typedef void (* esp_hf_connection_state_callback)(esp_hf_connection_state_t state, esp_bd_addr_t *bd_addr);
/** Callback for audio connection state change.
* state will have one of the values from BtHfAudioState
*/
typedef void (* esp_hf_audio_state_callback)(esp_hf_audio_state_t state, esp_bd_addr_t *bd_addr);
/** Callback for VR connection state change.
* state will have one of the values from BtHfVRState
*/
typedef void (* esp_hf_vr_cmd_callback)(esp_hf_vr_state_t state, esp_bd_addr_t *bd_addr);
/** Callback for answer incoming call (ATA)
*/
typedef void (* esp_hf_answer_call_cmd_callback)(esp_bd_addr_t *bd_addr);
/** Callback for disconnect call (AT+CHUP)
*/
typedef void (* esp_hf_hangup_call_cmd_callback)(esp_bd_addr_t *bd_addr);
/** Callback for disconnect call (AT+CHUP)
* type will denote Speaker/Mic gain (BtHfVolumeControl).
*/
typedef void (* esp_hf_volume_cmd_callback)(esp_hf_volume_control_target_t type, int volume, esp_bd_addr_t *bd_addr);
/** Callback for dialing an outgoing call
* If number is NULL, redial
*/
typedef void (* esp_hf_dial_call_cmd_callback)(char *number, esp_bd_addr_t *bd_addr);
/** Callback for sending DTMF tones
* tone contains the dtmf character to be sent
*/
typedef void (* esp_hf_dtmf_cmd_callback)(char tone, esp_bd_addr_t *bd_addr);
/** Callback for enabling/disabling noise reduction/echo cancellation
* value will be 1 to enable, 0 to disable
*/
typedef void (* esp_hf_nrec_cmd_callback)(esp_hf_nrec_t nrec, esp_bd_addr_t *bd_addr);
/** Callback for AT+BCS and event from BAC
* WBS enable, WBS disable
*/
typedef void (* esp_hf_wbs_callback)(esp_hf_wbs_config_t wbs, esp_bd_addr_t *bd_addr);
/** Callback for call hold handling (AT+CHLD)
* value will contain the call hold command (0, 1, 2, 3)
*/
typedef void (* esp_hf_chld_cmd_callback)(esp_hf_chld_type_t chld, esp_bd_addr_t *bd_addr);
/** Callback for CNUM (subscriber number)
*/
typedef void (* esp_hf_cnum_cmd_callback)(esp_bd_addr_t *bd_addr);
/** Callback for indicators (CIND)
*/
typedef void (* esp_hf_cind_cmd_callback)(esp_bd_addr_t *bd_addr);
/** Callback for operator selection (COPS)
*/
typedef void (* esp_hf_cops_cmd_callback)(esp_bd_addr_t *bd_addr);
/** Callback for call list (AT+CLCC)
*/
typedef void (* esp_hf_clcc_cmd_callback) (esp_bd_addr_t *bd_addr);
/** Callback for unknown AT command recd from AG
* at_string will contain the unparsed AT string
*/
typedef void (* esp_hf_unknown_at_cmd_callback)(char *at_string, esp_bd_addr_t *bd_addr);
/** Callback for keypressed (HSP) event.
*/
typedef void (* esp_hf_key_pressed_cmd_callback)(esp_bd_addr_t *bd_addr);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,818 @@
/******************************************************************************
*
* Copyright (C) 2003-2012 Broadcom Corporation
*
* 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.
*
******************************************************************************/
/******************************************************************************
*
* This file contains action functions for the audio gateway.
*
******************************************************************************/
#include <string.h>
#include "bta_ag_int.h"
#include "bta/bta_sys.h"
#include "bta/bta_ag_api.h"
#include "bta/bta_ag_co.h"
#include "stack/port_api.h"
#include "stack/l2c_api.h"
#include "bta_dm_int.h"
#include "bta/bta_sdp_api.h"
#include "bta/utl.h"
#include "common/bt_trace.h"
#include "osi/allocator.h"
#if (BTA_AG_INCLUDED == TRUE)
/*****************************************************************************
** Constants
*****************************************************************************/
/* maximum length of data to read from RFCOMM */
#define BTA_AG_RFC_READ_MAX 512
/* maximum AT command length */
#define BTA_AG_CMD_MAX 512
const UINT16 bta_ag_uuid[BTA_AG_NUM_IDX] =
{
UUID_SERVCLASS_HEADSET_AUDIO_GATEWAY,
UUID_SERVCLASS_AG_HANDSFREE
};
const UINT8 bta_ag_sec_id[BTA_AG_NUM_IDX] =
{
BTM_SEC_SERVICE_HEADSET_AG,
BTM_SEC_SERVICE_AG_HANDSFREE
};
const tBTA_SERVICE_ID bta_ag_svc_id[BTA_AG_NUM_IDX] =
{
BTA_HSP_SERVICE_ID,
BTA_HFP_SERVICE_ID
};
const tBTA_SERVICE_MASK bta_ag_svc_mask[BTA_AG_NUM_IDX] =
{
BTA_HSP_SERVICE_MASK,
BTA_HFP_SERVICE_MASK
};
typedef void (*tBTA_AG_ATCMD_CBACK)(tBTA_AG_SCB *p_scb, UINT16 cmd, UINT8 arg_type,
char *p_arg, INT16 int_arg);
const tBTA_AG_ATCMD_CBACK bta_ag_at_cback_tbl[BTA_AG_NUM_IDX] =
{
bta_ag_at_hsp_cback,
bta_ag_at_hfp_cback
};
/*******************************************************************************
**
** Function bta_ag_cback_open
**
** Description Send open callback event to application.
**
**
** Returns void
**
*******************************************************************************/
static void bta_ag_cback_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data, tBTA_AG_STATUS status)
{
tBTA_AG_OPEN open;
/* call app callback with open event */
open.hdr.handle = bta_ag_scb_to_idx(p_scb);
open.hdr.app_id = p_scb->app_id;
open.status = status;
open.service_id = bta_ag_svc_id[p_scb->conn_service];
if (p_data) {
/* if p_data is provided then we need to pick the bd address from the open api structure */
bdcpy(open.bd_addr, p_data->api_open.bd_addr);
} else {
bdcpy(open.bd_addr, p_scb->peer_addr);
}
(*bta_ag_cb.p_cback)(BTA_AG_OPEN_EVT, (tBTA_AG *) &open);
}
/*******************************************************************************
**
** Function bta_ag_register
**
** Description This function initializes values of the AG cb and sets up
** the SDP record for the services.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_register(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
{
tBTA_AG_REGISTER reg;
/* initialize control block */
p_scb->reg_services = p_data->api_register.services;
p_scb->serv_sec_mask = p_data->api_register.sec_mask;
p_scb->features = p_data->api_register.features;
p_scb->app_id = p_data->api_register.app_id;
/* create SDP records */
bta_ag_create_records(p_scb, p_data);
/* start RFCOMM servers */
bta_ag_start_servers(p_scb, p_scb->reg_services);
/* call app callback with register event */
reg.hdr.handle = bta_ag_scb_to_idx(p_scb);
reg.hdr.app_id = p_scb->app_id;
reg.status = BTA_AG_SUCCESS;
(*bta_ag_cb.p_cback)(BTA_AG_REGISTER_EVT, (tBTA_AG *) &reg);
}
/*******************************************************************************
**
** Function bta_ag_deregister
**
** Description This function removes the sdp records, closes the RFCOMM
** servers, and deallocates the service control block.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_deregister(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
{
/* set dealloc */
p_scb->dealloc = TRUE;
/* remove sdp records */
bta_ag_del_records(p_scb, p_data);
/* remove rfcomm servers */
bta_ag_close_servers(p_scb, p_scb->reg_services);
/* dealloc */
bta_ag_scb_dealloc(p_scb);
}
/*******************************************************************************
**
** Function bta_ag_start_dereg
**
** Description Start a deregister event.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_start_dereg(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
{
/* set dealloc */
p_scb->dealloc = TRUE;
/* remove sdp records */
bta_ag_del_records(p_scb, p_data);
}
/*******************************************************************************
**
** Function bta_ag_start_open
**
** Description This starts an AG open.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_start_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
{
BD_ADDR pending_bd_addr;
/* store parameters */
if (p_data) {
bdcpy(p_scb->peer_addr, p_data->api_open.bd_addr);
p_scb->open_services = p_data->api_open.services;
p_scb->cli_sec_mask = p_data->api_open.sec_mask;
}
/* Check if RFCOMM has any incoming connection to avoid collision. */
if (PORT_IsOpening (pending_bd_addr)) {
/* Let the incoming connection goes through. */
/* Issue collision for this scb for now. */
/* We will decide what to do when we find incoming connetion later. */
bta_ag_collision_cback (0, BTA_ID_AG, 0, p_scb->peer_addr);
return;
}
/* close servers */
bta_ag_close_servers(p_scb, p_scb->reg_services);
/* set role */
p_scb->role = BTA_AG_INT;
/* do service search */
bta_ag_do_disc(p_scb, p_scb->open_services);
}
/*******************************************************************************
**
** Function bta_ag_disc_int_res
**
** Description This function handles a discovery result when initiator.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_disc_int_res(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
{
UINT16 event = BTA_AG_DISC_FAIL_EVT;
APPL_TRACE_DEBUG ("bta_ag_disc_int_res: Status: %d", p_data->disc_result.status);
/* if found service */
if (p_data->disc_result.status == SDP_SUCCESS ||
p_data->disc_result.status == SDP_DB_FULL) {
/* get attributes */
if (bta_ag_sdp_find_attr(p_scb, p_scb->open_services)) {
/* set connected service */
p_scb->conn_service = bta_ag_service_to_idx(p_scb->open_services);
/* send ourselves sdp ok event */
event = BTA_AG_DISC_OK_EVT;
}
}
/* free discovery db */
bta_ag_free_db(p_scb, p_data);
/* if service not found check if we should search for other service */
if ((event == BTA_AG_DISC_FAIL_EVT) &&
(p_data->disc_result.status == SDP_SUCCESS ||
p_data->disc_result.status == SDP_DB_FULL ||
p_data->disc_result.status == SDP_NO_RECS_MATCH)) {
if ((p_scb->open_services & BTA_HFP_SERVICE_MASK) &&
(p_scb->open_services & BTA_HSP_SERVICE_MASK)) {
/* search for HSP */
p_scb->open_services &= ~BTA_HFP_SERVICE_MASK;
bta_ag_do_disc(p_scb, p_scb->open_services);
} else if ((p_scb->open_services & BTA_HSP_SERVICE_MASK) &&
(p_scb->hsp_version == HSP_VERSION_1_2)) {
/* search for UUID_SERVCLASS_HEADSET for HSP 1.0 device */
p_scb->hsp_version = HSP_VERSION_1_0;
bta_ag_do_disc(p_scb, p_scb->open_services);
} else {
/* send ourselves sdp ok/fail event */
bta_ag_sm_execute(p_scb, event, p_data);
}
} else {
/* send ourselves sdp ok/fail event */
bta_ag_sm_execute(p_scb, event, p_data);
}
}
/*******************************************************************************
**
** Function bta_ag_disc_acp_res
**
** Description This function handles a discovery result when acceptor.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_disc_acp_res(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
{
/* if found service */
if (p_data->disc_result.status == SDP_SUCCESS ||
p_data->disc_result.status == SDP_DB_FULL) {
/* get attributes */
bta_ag_sdp_find_attr(p_scb, bta_ag_svc_mask[p_scb->conn_service]);
}
/* free discovery db */
bta_ag_free_db(p_scb, p_data);
}
/*******************************************************************************
**
** Function bta_ag_disc_fail
**
** Description This function handles a discovery failure.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_disc_fail(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
{
UNUSED(p_data);
/* reopen registered servers */
bta_ag_start_servers(p_scb, p_scb->reg_services);
/* reinitialize stuff */
/* call open cback w. failure */
bta_ag_cback_open(p_scb, NULL, BTA_AG_FAIL_SDP);
}
/*******************************************************************************
**
** Function bta_ag_open_fail
**
** Description open connection failed.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_open_fail(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
{
/* call open cback w. failure */
bta_ag_cback_open(p_scb, p_data, BTA_AG_FAIL_RESOURCES);
}
/*******************************************************************************
**
** Function bta_ag_rfc_fail
**
** Description RFCOMM connection failed.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_rfc_fail(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
{
UNUSED(p_data);
/* reinitialize stuff */
p_scb->conn_handle = 0;
p_scb->conn_service = 0;
p_scb->peer_features = 0;
#if (BTM_WBS_INCLUDED == TRUE )
p_scb->peer_codecs = BTA_AG_CODEC_NONE;
p_scb->sco_codec = BTA_AG_CODEC_NONE;
#endif
p_scb->role = 0;
p_scb->svc_conn = FALSE;
p_scb->hsp_version = HSP_VERSION_1_2;
/*Clear the BD address*/
bdcpy(p_scb->peer_addr, bd_addr_null);
/* reopen registered servers */
bta_ag_start_servers(p_scb, p_scb->reg_services);
/* call open cback w. failure */
bta_ag_cback_open(p_scb, NULL, BTA_AG_FAIL_RFCOMM);
}
/*******************************************************************************
**
** Function bta_ag_rfc_close
**
** Description RFCOMM connection closed.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_rfc_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
{
tBTA_AG_CLOSE close;
tBTA_SERVICE_MASK services;
int i, num_active_conn = 0;
UNUSED(p_data);
/* reinitialize stuff */
p_scb->conn_service = 0;
p_scb->peer_features = 0;
#if (BTM_WBS_INCLUDED == TRUE )
p_scb->peer_codecs = BTA_AG_CODEC_NONE;
p_scb->sco_codec = BTA_AG_CODEC_NONE;
/* Clear these flags upon SLC teardown */
p_scb->codec_updated = FALSE;
p_scb->codec_fallback = FALSE;
p_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T2;
#endif
p_scb->role = 0;
p_scb->post_sco = BTA_AG_POST_SCO_NONE;
p_scb->svc_conn = FALSE;
p_scb->hsp_version = HSP_VERSION_1_2;
bta_ag_at_reinit(&p_scb->at_cb);
/* stop timers */
bta_sys_stop_timer(&p_scb->act_timer);
#if (BTM_WBS_INCLUDED == TRUE)
bta_sys_stop_timer(&p_scb->cn_timer);
#endif
close.hdr.handle = bta_ag_scb_to_idx(p_scb);
close.hdr.app_id = p_scb->app_id;
bdcpy(close.bd_addr, p_scb->peer_addr);
bta_sys_conn_close(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr);
/* call close call-out */
// bta_ag_sco_co_close(close.hdr.handle);
bta_ag_sco_co_close();
/* call close cback */
(*bta_ag_cb.p_cback)(BTA_AG_CLOSE_EVT, (tBTA_AG *) &close);
/* if not deregistering (deallocating) reopen registered servers */
if (p_scb->dealloc == FALSE) {
/* Clear peer bd_addr so instance can be reused */
bdcpy(p_scb->peer_addr, bd_addr_null);
/* start only unopened server */
services = p_scb->reg_services;
for (i = 0; i < BTA_AG_NUM_IDX && services != 0; i++) {
if (p_scb->serv_handle[i]) {
services &= ~((tBTA_SERVICE_MASK)1 << (BTA_HSP_SERVICE_ID + i));
}
}
bta_ag_start_servers(p_scb, services);
p_scb->conn_handle = 0;
/* Make sure SCO state is BTA_AG_SCO_SHUTDOWN_ST */
bta_ag_sco_shutdown(p_scb, NULL);
/* Check if all the SLCs are down */
for (i = 0; i < BTA_AG_NUM_SCB; i++) {
if (bta_ag_cb.scb[i].in_use && bta_ag_cb.scb[i].svc_conn) {
num_active_conn++;
}
}
if (!num_active_conn) {
bta_sys_sco_unuse(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr);
}
} else {
/* else close port and deallocate scb */
RFCOMM_RemoveServer(p_scb->conn_handle);
bta_ag_scb_dealloc(p_scb);
}
}
/*******************************************************************************
**
** Function bta_ag_rfc_open
**
** Description Handle RFCOMM channel open.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_rfc_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
{
/* initialize AT feature variables */
p_scb->clip_enabled = FALSE;
p_scb->ccwa_enabled = FALSE;
p_scb->cmer_enabled = FALSE;
p_scb->cmee_enabled = FALSE;
p_scb->inband_enabled = ((p_scb->features & BTA_AG_FEAT_INBAND) == BTA_AG_FEAT_INBAND);
/* set up AT command interpreter */
p_scb->at_cb.p_at_tbl = (tBTA_AG_AT_CMD *) bta_ag_at_tbl[p_scb->conn_service];
p_scb->at_cb.p_cmd_cback = (tBTA_AG_AT_CMD_CBACK *) bta_ag_at_cback_tbl[p_scb->conn_service];
p_scb->at_cb.p_err_cback = (tBTA_AG_AT_ERR_CBACK *) bta_ag_at_err_cback;
p_scb->at_cb.p_user = p_scb;
p_scb->at_cb.cmd_max_len = BTA_AG_CMD_MAX;
bta_ag_at_init(&p_scb->at_cb);
/* call app open call-out */
bta_ag_sco_co_open(bta_ag_scb_to_idx(p_scb), p_scb->air_mode, BTA_HFP_SCO_OUT_PKT_SIZE, bta_ag_svc_id[p_scb->conn_service]);
bta_sys_conn_open(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr);
bta_ag_cback_open(p_scb, NULL, BTA_AG_SUCCESS);
if (p_scb->conn_service == BTA_AG_HFP) {
/* if hfp start timer for service level conn */
bta_sys_start_timer(&p_scb->act_timer, BTA_AG_SVC_TOUT_EVT, p_bta_ag_cfg->conn_tout);
} else {
/* else service level conn is open */
bta_ag_svc_conn_open(p_scb, p_data);
}
}
/*******************************************************************************
**
** Function bta_ag_rfc_acp_open
**
** Description Handle RFCOMM channel open when accepting connection.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_rfc_acp_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
{
UINT16 lcid;
int i;
tBTA_AG_SCB *ag_scb, *other_scb;
BD_ADDR dev_addr;
int status;
/* set role */
p_scb->role = BTA_AG_ACP;
APPL_TRACE_DEBUG ("bta_ag_rfc_acp_open: serv_handle0 = %d serv_handle1 = %d",
p_scb->serv_handle[0], p_scb->serv_handle[1]);
/* get bd addr of peer */
if (PORT_SUCCESS != (status=PORT_CheckConnection(p_data->rfc.port_handle, dev_addr, &lcid))) {
APPL_TRACE_DEBUG ("bta_ag_rfc_acp_open error PORT_CheckConnection returned status %d", status);
}
/* Collision Handling */
for (i = 0, ag_scb = &bta_ag_cb.scb[0]; i < BTA_AG_NUM_SCB; i++, ag_scb++) {
if ((ag_scb->in_use) && (ag_scb->colli_tmr_on)) {
/* stop collision timer */
ag_scb->colli_tmr_on = FALSE;
bta_sys_stop_timer (&ag_scb->colli_timer);
if (bdcmp (dev_addr, ag_scb->peer_addr) == 0) {
/* If incoming and outgoing device are same, nothing more to do. */
/* Outgoing conn will be aborted because we have successful incoming conn. */
} else {
/* Resume outgoing connection. */
other_scb = bta_ag_get_other_idle_scb (p_scb);
if (other_scb) {
bdcpy(other_scb->peer_addr, ag_scb->peer_addr);
other_scb->open_services = ag_scb->open_services;
other_scb->cli_sec_mask = ag_scb->cli_sec_mask;
bta_ag_resume_open (other_scb);
}
}
break;
}
}
bdcpy (p_scb->peer_addr, dev_addr);
/* determine connected service from port handle */
for (i = 0; i < BTA_AG_NUM_IDX; i++) {
APPL_TRACE_DEBUG ("bta_ag_rfc_acp_open: i = %d serv_handle = %d port_handle = %d",
i, p_scb->serv_handle[i], p_data->rfc.port_handle);
if (p_scb->serv_handle[i] == p_data->rfc.port_handle) {
p_scb->conn_service = i;
p_scb->conn_handle = p_data->rfc.port_handle;
break;
}
}
APPL_TRACE_DEBUG ("bta_ag_rfc_acp_open: conn_service = %d conn_handle = %d",
p_scb->conn_service, p_scb->conn_handle);
/* close any unopened server */
bta_ag_close_servers(p_scb, (p_scb->reg_services & ~bta_ag_svc_mask[p_scb->conn_service]));
/* do service discovery to get features */
bta_ag_do_disc(p_scb, bta_ag_svc_mask[p_scb->conn_service]);
/* continue with common open processing */
bta_ag_rfc_open(p_scb, p_data);
}
/*******************************************************************************
**
** Function bta_ag_rfc_data
**
** Description Read and process data from RFCOMM.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_rfc_data(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
{
UINT16 len;
char buf[BTA_AG_RFC_READ_MAX];
UNUSED(p_data);
memset(buf, 0, BTA_AG_RFC_READ_MAX);
APPL_TRACE_DEBUG("bta_ag_rfc_data");
/* do the following */
for (;;) {
/* read data from rfcomm; if bad status, we're done */
if (PORT_ReadData(p_scb->conn_handle, buf, BTA_AG_RFC_READ_MAX, &len) != PORT_SUCCESS) {
break;
}
/* if no data, we're done */
if (len == 0) {
break;
}
/* run AT command interpreter on data */
bta_sys_busy(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr);
bta_ag_at_parse(&p_scb->at_cb, buf, len);
bta_sys_idle(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr);
/* no more data to read, we're done */
if (len < BTA_AG_RFC_READ_MAX) {
break;
}
}
}
/*******************************************************************************
**
** Function bta_ag_start_close
**
** Description Start the process of closing SCO and RFCOMM connection.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_start_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
{
/* Take the link out of sniff and set L2C idle time to 0 */
bta_dm_pm_active(p_scb->peer_addr);
L2CA_SetIdleTimeoutByBdAddr(p_scb->peer_addr, 0, BT_TRANSPORT_BR_EDR);
/* if SCO is open close SCO and wait on RFCOMM close */
if (bta_ag_sco_is_open(p_scb)) {
p_scb->post_sco = BTA_AG_POST_SCO_CLOSE_RFC;
} else {
p_scb->post_sco = BTA_AG_POST_SCO_NONE;
bta_ag_rfc_do_close(p_scb, p_data);
}
/* Always do SCO shutdown to handle all SCO corner cases */
bta_ag_sco_shutdown(p_scb, p_data);
}
/*******************************************************************************
**
** Function bta_ag_post_sco_open
**
** Description Perform post-SCO open action, if any
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_post_sco_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
{
switch (p_scb->post_sco) {
case BTA_AG_POST_SCO_RING:
bta_ag_send_ring(p_scb, p_data);
p_scb->post_sco = BTA_AG_POST_SCO_NONE;
break;
case BTA_AG_POST_SCO_CALL_CONN:
bta_ag_send_call_inds(p_scb, BTA_AG_IN_CALL_CONN_RES);
p_scb->post_sco = BTA_AG_POST_SCO_NONE;
break;
default:
break;
}
}
/*******************************************************************************
**
** Function bta_ag_post_sco_close
**
** Description Perform post-SCO close action, if any
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_post_sco_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
{
switch (p_scb->post_sco) {
case BTA_AG_POST_SCO_CLOSE_RFC:
bta_ag_rfc_do_close(p_scb, p_data);
p_scb->post_sco = BTA_AG_POST_SCO_NONE;
break;
case BTA_AG_POST_SCO_CALL_CONN:
bta_ag_send_call_inds(p_scb, BTA_AG_IN_CALL_CONN_RES);
p_scb->post_sco = BTA_AG_POST_SCO_NONE;
break;
case BTA_AG_POST_SCO_CALL_ORIG:
bta_ag_send_call_inds(p_scb, BTA_AG_OUT_CALL_ORIG_RES);
p_scb->post_sco = BTA_AG_POST_SCO_NONE;
break;
case BTA_AG_POST_SCO_CALL_END:
bta_ag_send_call_inds(p_scb, BTA_AG_END_CALL_RES);
p_scb->post_sco = BTA_AG_POST_SCO_NONE;
break;
case BTA_AG_POST_SCO_CALL_END_INCALL:
{
bta_ag_send_call_inds(p_scb, BTA_AG_END_CALL_RES);
/* Sending callsetup IND and Ring were defered to after SCO close. */
bta_ag_send_call_inds(p_scb, BTA_AG_IN_CALL_RES);
if (bta_ag_inband_enabled(p_scb) && !(p_scb->features & BTA_AG_FEAT_NOSCO)) {
p_scb->post_sco = BTA_AG_POST_SCO_RING;
bta_ag_sco_open(p_scb, p_data);
} else {
p_scb->post_sco = BTA_AG_POST_SCO_NONE;
bta_ag_send_ring(p_scb, p_data);
}
break;
}
default:
break;
}
}
/*******************************************************************************
**
** Function bta_ag_svc_conn_open
**
** Description Service level connection opened
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_svc_conn_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
{
tBTA_AG_CONN evt;
UNUSED(p_data);
if (!p_scb->svc_conn) {
/* set state variable */
p_scb->svc_conn = TRUE;
/* Clear AT+BIA mask from previous SLC if any. */
p_scb->bia_masked_out = 0;
/* stop timer */
bta_sys_stop_timer(&p_scb->act_timer);
/* call callback */
evt.hdr.handle = bta_ag_scb_to_idx(p_scb);
evt.hdr.app_id = p_scb->app_id;
evt.peer_feat = p_scb->peer_features;
bdcpy(evt.bd_addr, p_scb->peer_addr);
#if (BTM_WBS_INCLUDED == TRUE )
evt.peer_codec = p_scb->peer_codecs;
#endif
if ((p_scb->call_ind != BTA_AG_CALL_INACTIVE) ||
(p_scb->callsetup_ind != BTA_AG_CALLSETUP_NONE)) {
bta_sys_sco_use(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr);
}
(*bta_ag_cb.p_cback)(BTA_AG_CONN_EVT, (tBTA_AG *) &evt);
}
}
/*******************************************************************************
**
** Function bta_ag_ci_rx_data
**
** Description Send result code to RFCOMM
**
** Returns void
**
*******************************************************************************/
void bta_ag_ci_rx_data(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
{
UINT16 len;
tBTA_AG_CI_RX_WRITE *p_rx_write_msg = (tBTA_AG_CI_RX_WRITE *)p_data;
char *p_data_area = (char *)(p_rx_write_msg+1); /* Point to data area after header */
APPL_TRACE_DEBUG("bta_ag_ci_rx_data:");
/* send to RFCOMM */
bta_sys_busy(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr);
PORT_WriteData(p_scb->conn_handle, p_data_area, strlen(p_data_area), &len);
bta_sys_idle(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr);
}
/*******************************************************************************
**
** Function bta_ag_rcvd_slc_ready
**
** Description Handles SLC ready call-in in case of pass-through mode.
**
** Returns void
**
*******************************************************************************/
void bta_ag_rcvd_slc_ready(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
{
UNUSED(p_data);
APPL_TRACE_DEBUG("bta_ag_rcvd_slc_ready: handle = %d", bta_ag_scb_to_idx(p_scb));
if (bta_ag_cb.parse_mode == BTA_AG_PASS_THROUGH) {
/* In pass-through mode, BTA knows that SLC is ready only through call-in. */
bta_ag_svc_conn_open(p_scb, NULL);
}
}
/*******************************************************************************
**
** Function bta_ag_setcodec
**
** Description Handle API SetCodec
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_setcodec(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
{
#if (BTM_WBS_INCLUDED == TRUE )
tBTA_AG_PEER_CODEC codec_type = p_data->api_setcodec.codec;
tBTA_AG_VAL val;
/* Check if the requested codec type is valid */
if((codec_type != BTA_AG_CODEC_NONE) &&
(codec_type != BTA_AG_CODEC_CVSD) &&
(codec_type != BTA_AG_CODEC_MSBC)) {
val.num = codec_type;
val.hdr.status = BTA_AG_FAIL_RESOURCES;
APPL_TRACE_ERROR("bta_ag_setcodec error: unsupported codec type %d", codec_type);
(*bta_ag_cb.p_cback)(BTA_AG_WBS_EVT, (tBTA_AG *) &val);
return;
}
if((p_scb->peer_codecs & codec_type) || (codec_type == BTA_AG_CODEC_NONE) ||
(codec_type == BTA_AG_CODEC_CVSD)) {
p_scb->sco_codec = codec_type;
p_scb->codec_updated = TRUE;
val.num = codec_type;
val.hdr.status = BTA_AG_SUCCESS;
APPL_TRACE_DEBUG("bta_ag_setcodec: Updated codec type %d", codec_type);
} else {
val.num = codec_type;
val.hdr.status = BTA_AG_FAIL_RESOURCES;
APPL_TRACE_ERROR("bta_ag_setcodec error: unsupported codec type %d", codec_type);
}
(*bta_ag_cb.p_cback)(BTA_AG_WBS_EVT, (tBTA_AG *) &val);
#endif
}
#endif /* #if (BTA_AG_INCLUDED == TRUE) */

View file

@ -0,0 +1,322 @@
/******************************************************************************
*
* Copyright (C) 2003-2012 Broadcom Corporation
*
* 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.
*
******************************************************************************/
/******************************************************************************
*
* This is the implementation of the API for the audio gateway (AG)
* subsystem of BTA, Broadcom's Bluetooth application layer for mobile
* phones.
*
******************************************************************************/
#include <string.h>
#include "bta/bta_api.h"
#include "bta/bta_sys.h"
#include "bta/bta_ag_api.h"
#include "bta_ag_int.h"
#include "osi/allocator.h"
#if (BTA_AG_INCLUDED == TRUE)
/*****************************************************************************
** Constants
*****************************************************************************/
static const tBTA_SYS_REG bta_ag_reg =
{
bta_ag_hdl_event,
BTA_AgDisable
};
/*******************************************************************************
**
** Function BTA_AgEnable
**
** Description Enable the audio gateway service. When the enable
** operation is complete the callback function will be
** called with a BTA_AG_ENABLE_EVT. This function must
** be called before other function in the AG API are
** called.
**
** Returns BTA_SUCCESS if OK, BTA_FAILURE otherwise.
**
*******************************************************************************/
tBTA_STATUS BTA_AgEnable(tBTA_AG_PARSE_MODE parse_mode, tBTA_AG_CBACK *p_cback)
{
tBTA_AG_API_ENABLE *p_buf;
UINT8 idx;
/* Error if AG is already enabled, or AG is in the middle of disabling. */
for (idx = 0; idx < BTA_AG_NUM_SCB; idx++) {
if (bta_ag_cb.scb[idx].in_use) {
APPL_TRACE_ERROR ("BTA_AgEnable: FAILED, AG already enabled.");
return BTA_FAILURE;
}
}
/* register with BTA system manager */
bta_sys_register(BTA_ID_AG, &bta_ag_reg);
if ((p_buf = (tBTA_AG_API_ENABLE *) osi_malloc(sizeof(tBTA_AG_API_ENABLE))) != NULL) {
p_buf->hdr.event = BTA_AG_API_ENABLE_EVT;
p_buf->parse_mode = parse_mode;
p_buf->p_cback = p_cback;
bta_sys_sendmsg(p_buf);
}
return BTA_SUCCESS;
}
/*******************************************************************************
**
** Function BTA_AgDisable
**
** Description Disable the audio gateway service
**
**
** Returns void
**
*******************************************************************************/
void BTA_AgDisable(void)
{
BT_HDR *p_buf;
if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) {
p_buf->event = BTA_AG_API_DISABLE_EVT;
bta_sys_sendmsg(p_buf);
}
}
/*******************************************************************************
**
** Function BTA_AgRegister
**
** Description Register an Audio Gateway service.
**
**
** Returns void
**
*******************************************************************************/
void BTA_AgRegister(tBTA_SERVICE_MASK services, tBTA_SEC sec_mask,tBTA_AG_FEAT features,
char * p_service_names[], UINT8 app_id)
{
tBTA_AG_API_REGISTER *p_buf;
int i;
if ((p_buf = (tBTA_AG_API_REGISTER *) osi_malloc(sizeof(tBTA_AG_API_REGISTER))) != NULL) {
p_buf->hdr.event = BTA_AG_API_REGISTER_EVT;
p_buf->features = features;
p_buf->sec_mask = sec_mask;
p_buf->services = services;
p_buf->app_id = app_id;
for (i = 0; i < BTA_AG_NUM_IDX; i++) {
if(p_service_names[i]) {
BCM_STRNCPY_S(p_buf->p_name[i], BTA_SERVICE_NAME_LEN+1, p_service_names[i], BTA_SERVICE_NAME_LEN);
p_buf->p_name[i][BTA_SERVICE_NAME_LEN] = 0;
} else {
p_buf->p_name[i][0] = 0;
}
}
bta_sys_sendmsg(p_buf);
}
}
/*******************************************************************************
**
** Function BTA_AgDeregister
**
** Description Deregister an audio gateway service.
**
**
** Returns void
**
*******************************************************************************/
void BTA_AgDeregister(UINT16 handle)
{
BT_HDR *p_buf;
if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) {
p_buf->event = BTA_AG_API_DEREGISTER_EVT;
p_buf->layer_specific = handle;
bta_sys_sendmsg(p_buf);
}
}
/*******************************************************************************
**
** Function BTA_AgOpen
**
** Description Opens a connection to a headset or hands-free device.
** When connection is open callback function is called
** with a BTA_AG_OPEN_EVT. Only the data connection is
** opened. The audio connection is not opened.
**
**
** Returns void
**
*******************************************************************************/
void BTA_AgOpen(UINT16 handle, BD_ADDR bd_addr, tBTA_SEC sec_mask, tBTA_SERVICE_MASK services)
{
tBTA_AG_API_OPEN *p_buf;
if ((p_buf = (tBTA_AG_API_OPEN *) osi_malloc(sizeof(tBTA_AG_API_OPEN))) != NULL) {
p_buf->hdr.event = BTA_AG_API_OPEN_EVT;
p_buf->hdr.layer_specific = handle;
bdcpy(p_buf->bd_addr, bd_addr);
p_buf->services = services;
p_buf->sec_mask = sec_mask;
bta_sys_sendmsg(p_buf);
}
}
/*******************************************************************************
**
** Function BTA_AgClose
**
** Description Close the current connection to a headset or a handsfree
** Any current audio connection will also be closed.
**
**
** Returns void
**
*******************************************************************************/
void BTA_AgClose(UINT16 handle)
{
BT_HDR *p_buf;
if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) {
p_buf->event = BTA_AG_API_CLOSE_EVT;
p_buf->layer_specific = handle;
bta_sys_sendmsg(p_buf);
}
}
/*******************************************************************************
**
** Function BTA_AgAudioOpen
**
** Description Opens an audio connection to the currently connected
** headset or handsfree.
**
**
** Returns void
**
*******************************************************************************/
void BTA_AgAudioOpen(UINT16 handle)
{
BT_HDR *p_buf;
if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) {
p_buf->event = BTA_AG_API_AUDIO_OPEN_EVT;
p_buf->layer_specific = handle;
bta_sys_sendmsg(p_buf);
}
}
/*******************************************************************************
**
** Function BTA_AgAudioClose
**
** Description Close the currently active audio connection to a headset
** or handsfree. The data connection remains open
**
**
** Returns void
**
*******************************************************************************/
void BTA_AgAudioClose(UINT16 handle)
{
BT_HDR *p_buf;
if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) {
p_buf->event = BTA_AG_API_AUDIO_CLOSE_EVT;
p_buf->layer_specific = handle;
bta_sys_sendmsg(p_buf);
}
}
/*******************************************************************************
**
** Function BTA_AgResult
**
** Description Send an AT result code to a headset or hands-free device.
** This function is only used when the AG parse mode is set
** to BTA_AG_PARSE.
**
**
** Returns void
**
*******************************************************************************/
void BTA_AgResult(UINT16 handle, tBTA_AG_RES result, tBTA_AG_RES_DATA *p_data)
{
tBTA_AG_API_RESULT *p_buf;
// printf("BTA_AgReslut: %d\n",result);
if ((p_buf = (tBTA_AG_API_RESULT *) osi_malloc(sizeof(tBTA_AG_API_RESULT))) != NULL) {
p_buf->hdr.event = BTA_AG_API_RESULT_EVT;
p_buf->hdr.layer_specific = handle;
p_buf->result = result;
if(p_data) {
memcpy(&p_buf->data, p_data, sizeof(p_buf->data));
}
bta_sys_sendmsg(p_buf);
}
}
/*******************************************************************************
**
** Function BTA_AgSetCodec
**
** Description Specify the codec type to be used for the subsequent
** audio connection.
**
**
**
** Returns void
**
*******************************************************************************/
void BTA_AgSetCodec(UINT16 handle, tBTA_AG_PEER_CODEC codec)
{
tBTA_AG_API_SETCODEC *p_buf;
if ((p_buf = (tBTA_AG_API_SETCODEC *) osi_malloc(sizeof(tBTA_AG_API_SETCODEC))) != NULL) {
p_buf->hdr.event = BTA_AG_API_SETCODEC_EVT;
p_buf->hdr.layer_specific = handle;
p_buf->codec = codec;
bta_sys_sendmsg(p_buf);
}
}
#if (BTM_SCO_HCI_INCLUDED == TRUE )
/************************************************************************************************
* Function BTA_AgCiData
*
* Description Trigger the lower-layer to fetch and send audio data. This function is only
* only used in the case that Voice Over HCI is enabled. Precondition is that
* the HFP audio connection is connected. After this function is called, lower
* layer will invoke esp_hf_client_outgoing_data_cb_t to fetch data
*
***********************************************************************************************/
void BTA_AgCiData(void)
{
BT_HDR *p_buf;
if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) {
p_buf->event = BTA_AG_CI_SCO_DATA_EVT;
bta_sys_sendmsg(p_buf);
}
}
#endif /* #if (BTM_SCO_HCI_INCLUDED == TRUE ) */
#endif /* #if (BTA_AG_INCLUDED == TRUE)*/

View file

@ -0,0 +1,201 @@
/******************************************************************************
*
* Copyright (C) 2004-2012 Broadcom Corporation
*
* 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.
*
******************************************************************************/
/******************************************************************************
*
* BTA AG AT command interpreter.
*
******************************************************************************/
#include <string.h>
#include "osi/allocator.h"
#include "bta_ag_at.h"
#include "bta/utl.h"
#if (BTA_AG_INCLUDED == TRUE)
/******************************************************************************
**
** Function bta_ag_at_init
**
** Description Initialize the AT command parser control block.
**
**
** Returns void
**
******************************************************************************/
void bta_ag_at_init(tBTA_AG_AT_CB *p_cb)
{
p_cb->p_cmd_buf = NULL;
p_cb->cmd_pos = 0;
}
/******************************************************************************
**
** Function bta_ag_at_reinit
**
** Description Re-initialize the AT command parser control block. This
** function resets the AT command parser state and frees
** any GKI buffer.
**
**
** Returns void
**
******************************************************************************/
void bta_ag_at_reinit(tBTA_AG_AT_CB *p_cb)
{
if (p_cb->p_cmd_buf != NULL) {
osi_free(p_cb->p_cmd_buf);
p_cb->p_cmd_buf = NULL;
}
p_cb->cmd_pos = 0;
}
/******************************************************************************
**
** Function bta_ag_process_at
**
** Description Parse AT commands. This function will take the input
** character string and parse it for AT commands according to
** the AT command table passed in the control block.
**
**
** Returns void
**
******************************************************************************/
void bta_ag_process_at(tBTA_AG_AT_CB *p_cb)
{
UINT16 idx;
UINT8 arg_type;
char *p_arg;
INT16 int_arg = 0;
/* loop through at command table looking for match */
for (idx = 0; p_cb->p_at_tbl[idx].p_cmd[0] != 0; idx++) {
if (!utl_strucmp(p_cb->p_at_tbl[idx].p_cmd, p_cb->p_cmd_buf)) {
break;
}
}
/* if there is a match; verify argument type */
if (p_cb->p_at_tbl[idx].p_cmd[0] != 0) {
/* start of argument is p + strlen matching command */
p_arg = p_cb->p_cmd_buf + strlen(p_cb->p_at_tbl[idx].p_cmd);
/* if no argument */
if (p_arg[0] == 0) {
arg_type = BTA_AG_AT_NONE;
}
/* else if arg is '?' and it is last character */
else if (p_arg[0] == '?' && p_arg[1] == 0) {
arg_type = BTA_AG_AT_READ; /* we have a read */
}
/* else if arg is '=' */
else if (p_arg[0] == '=' && p_arg[1] != 0) {
if (p_arg[1] == '?' && p_arg[2] == 0) {
arg_type = BTA_AG_AT_TEST; /* we have a test */
} else {
arg_type = BTA_AG_AT_SET; /* we have a set */
p_arg++; /* skip past '=' */
}
}
/* else it is freeform argument */
else {
arg_type = BTA_AG_AT_FREE;
}
/* if arguments match command capabilities */
if ((arg_type & p_cb->p_at_tbl[idx].arg_type) != 0) {
/* if it's a set integer check max, min range */
if (arg_type == BTA_AG_AT_SET && p_cb->p_at_tbl[idx].fmt == BTA_AG_AT_INT) {
int_arg = utl_str2int(p_arg);
if (int_arg < (INT16) p_cb->p_at_tbl[idx].min ||
int_arg > (INT16) p_cb->p_at_tbl[idx].max) {
/* arg out of range; error */
(*p_cb->p_err_cback)(p_cb->p_user, FALSE, NULL);
} else {
(*p_cb->p_cmd_cback)(p_cb->p_user, idx, arg_type, p_arg, int_arg);
}
} else {
(*p_cb->p_cmd_cback)(p_cb->p_user, idx, arg_type, p_arg, int_arg);
}
} else {
/* else error */
(*p_cb->p_err_cback)(p_cb->p_user, FALSE, NULL);
}
} else {
/* else no match call error callback */
(*p_cb->p_err_cback)(p_cb->p_user, TRUE, p_cb->p_cmd_buf);
}
}
/******************************************************************************
**
** Function bta_ag_at_parse
**
** Description Parse AT commands. This function will take the input
** character string and parse it for AT commands according to
** the AT command table passed in the control block.
**
**
** Returns void
**
******************************************************************************/
void bta_ag_at_parse(tBTA_AG_AT_CB *p_cb, char *p_buf, UINT16 len)
{
int i = 0;
char* p_save;
if (p_cb->p_cmd_buf == NULL) {
if ((p_cb->p_cmd_buf = (char *) osi_malloc(p_cb->cmd_max_len)) == NULL) {
APPL_TRACE_ERROR("%s: osi_malloc() failed allocation", __func__);
return;
}
p_cb->cmd_pos = 0;
}
for (i = 0; i < len;) {
while (p_cb->cmd_pos < p_cb->cmd_max_len-1 && i < len) {
/* Skip null characters between AT commands. */
if ((p_cb->cmd_pos == 0) && (p_buf[i] == 0)) {
i++;
continue;
}
p_cb->p_cmd_buf[p_cb->cmd_pos] = p_buf[i++];
if ( p_cb->p_cmd_buf[p_cb->cmd_pos] == '\r' || p_cb->p_cmd_buf[p_cb->cmd_pos] == '\n') {
p_cb->p_cmd_buf[p_cb->cmd_pos] = 0;
if ((p_cb->cmd_pos > 2) &&
(p_cb->p_cmd_buf[0] == 'A' || p_cb->p_cmd_buf[0] == 'a') &&
(p_cb->p_cmd_buf[1] == 'T' || p_cb->p_cmd_buf[1] == 't')) {
p_save = p_cb->p_cmd_buf;
p_cb->p_cmd_buf += 2;
bta_ag_process_at(p_cb);
p_cb->p_cmd_buf = p_save;
}
p_cb->cmd_pos = 0;
} else if( p_cb->p_cmd_buf[p_cb->cmd_pos] == 0x1A || p_cb->p_cmd_buf[p_cb->cmd_pos] == 0x1B) {
p_cb->p_cmd_buf[++p_cb->cmd_pos] = 0;
(*p_cb->p_err_cback)(p_cb->p_user, TRUE, p_cb->p_cmd_buf);
p_cb->cmd_pos = 0;
} else {
++p_cb->cmd_pos;
}
}
if (i < len) {
p_cb->cmd_pos = 0;
}
}
}
#endif /* #if (BTA_AG_INCLUDED == TRUE) */

View file

@ -0,0 +1,60 @@
/******************************************************************************
*
* Copyright (C) 2003-2012 Broadcom Corporation
*
* 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.
*
******************************************************************************/
/******************************************************************************
*
* This file contains compile-time configurable constants for the audio
* gateway.
*
******************************************************************************/
#include "osi/allocator.h"
#include "bta/bta_api.h"
#include "bta/bta_ag_api.h"
#include "common/bt_target.h"
#if (BTA_AG_INCLUDED == TRUE)
#ifndef BTA_AG_CIND_INFO
#define BTA_AG_CIND_INFO "(\"call\",(0,1)),(\"callsetup\",(0-3)),(\"service\",(0-3)),(\"signal\",(0-6)),(\"roam\",(0,1)),(\"battchg\",(0-5)),(\"callheld\",(0-2))"
#endif
#ifndef BTA_AG_CONN_TIMEOUT
#define BTA_AG_CONN_TIMEOUT 5000
#endif
#ifndef BTA_AG_SCO_PKT_TYPES
/* S1 packet type setting from HFP 1.5 spec */
#define BTA_AG_SCO_PKT_TYPES /* BTM_SCO_LINK_ALL_PKT_MASK */ (BTM_SCO_LINK_ONLY_MASK | \
BTM_SCO_PKT_TYPES_MASK_EV3 | \
BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 | \
BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 | \
BTM_SCO_PKT_TYPES_MASK_NO_3_EV5)
#endif /* BTA_AG_SCO_PKT_TYPES */
const tBTA_AG_CFG bta_ag_cfg =
{
BTA_AG_CIND_INFO,
BTA_AG_CONN_TIMEOUT,
BTA_AG_SCO_PKT_TYPES,
BTA_AG_CHLD_VAL_ECC,
BTA_AG_CHLD_VAL
};
tBTA_AG_CFG *p_bta_ag_cfg = (tBTA_AG_CFG *) &bta_ag_cfg;
#endif /* #if (BTA_AG_INCLUDED == TRUE) */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,972 @@
/******************************************************************************
*
* Copyright (C) 2003-2012 Broadcom Corporation
*
* 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.
*
******************************************************************************/
/******************************************************************************
*
* This is the main implementation file for the BTA audio gateway.
*
******************************************************************************/
#include <string.h>
#include <stdlib.h>
#include "bta/bta_api.h"
#include "bta/bta_sys.h"
#include "bta/bta_ag_api.h"
#include "bta/bta_ag_co.h"
#include "bta/utl.h"
#include "common/bt_defs.h"
#include "common/bt_trace.h"
#include "osi/allocator.h"
#include "bta_ag_int.h"
#if (BTA_AG_INCLUDED == TRUE)
/*****************************************************************************
** Constants and types
*****************************************************************************/
#ifndef BTA_AG_DEBUG
#define BTA_AG_DEBUG TRUE
#endif
#if BTA_AG_DEBUG == TRUE
static char *bta_ag_evt_str(UINT16 event, tBTA_AG_RES result);
static char *bta_ag_state_str(UINT8 state);
#endif
/* state machine states */
enum
{
BTA_AG_INIT_ST,
BTA_AG_OPENING_ST,
BTA_AG_OPEN_ST,
BTA_AG_CLOSING_ST
};
/* state machine action enumeration list */
enum
{
BTA_AG_REGISTER,
BTA_AG_DEREGISTER,
BTA_AG_START_OPEN,
BTA_AG_RFC_DO_OPEN,
BTA_AG_RFC_DO_CLOSE,
BTA_AG_START_DEREG,
BTA_AG_START_CLOSE,
BTA_AG_RFC_OPEN,
BTA_AG_OPEN_FAIL,
BTA_AG_RFC_ACP_OPEN,
BTA_AG_RFC_CLOSE,
BTA_AG_RFC_FAIL,
BTA_AG_RFC_DATA,
BTA_AG_DISC_INT_RES,
BTA_AG_DISC_FAIL,
BTA_AG_DISC_ACP_RES,
BTA_AG_FREE_DB,
BTA_AG_SCO_CONN_OPEN,
BTA_AG_SCO_CONN_CLOSE,
BTA_AG_SCO_LISTEN,
BTA_AG_SCO_OPEN,
BTA_AG_SCO_CLOSE,
BTA_AG_SCO_SHUTDOWN,
BTA_AG_POST_SCO_OPEN,
BTA_AG_POST_SCO_CLOSE,
BTA_AG_SVC_CONN_OPEN,
BTA_AG_RESULT,
BTA_AG_SETCODEC,
BTA_AG_SEND_RING,
BTA_AG_CI_SCO_DATA,
BTA_AG_CI_RX_DATA,
BTA_AG_RCVD_SLC_READY,
BTA_AG_NUM_ACTIONS
};
#define BTA_AG_IGNORE BTA_AG_NUM_ACTIONS
/* type for action functions */
typedef void (*tBTA_AG_ACTION) (tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
/* action functions */
const tBTA_AG_ACTION bta_ag_action[] =
{
bta_ag_register,
bta_ag_deregister,
bta_ag_start_open,
bta_ag_rfc_do_open,
bta_ag_rfc_do_close,
bta_ag_start_dereg,
bta_ag_start_close,
bta_ag_rfc_open,
bta_ag_open_fail,
bta_ag_rfc_acp_open,
bta_ag_rfc_close,
bta_ag_rfc_fail,
bta_ag_rfc_data,
bta_ag_disc_int_res,
bta_ag_disc_fail,
bta_ag_disc_acp_res,
bta_ag_free_db,
bta_ag_sco_conn_open,
bta_ag_sco_conn_close,
bta_ag_sco_listen,
bta_ag_sco_open,
bta_ag_sco_close,
bta_ag_sco_shutdown,
bta_ag_post_sco_open,
bta_ag_post_sco_close,
bta_ag_svc_conn_open,
bta_ag_result,
bta_ag_setcodec,
bta_ag_send_ring,
bta_ag_ci_sco_data,
bta_ag_ci_rx_data,
bta_ag_rcvd_slc_ready
};
/* state table information */
#define BTA_AG_ACTIONS 2 /* number of actions */
#define BTA_AG_NEXT_STATE 2 /* position of next state */
#define BTA_AG_NUM_COLS 3 /* number of columns in state tables */
/* state table for init state */
const UINT8 bta_ag_st_init[][BTA_AG_NUM_COLS] =
{
/* Event Action 1 Action 2 Next state */
/* API_REGISTER_EVT */ {BTA_AG_REGISTER, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* API_DEREGISTER_EVT */ {BTA_AG_DEREGISTER, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* API_OPEN_EVT */ {BTA_AG_START_OPEN, BTA_AG_IGNORE, BTA_AG_OPENING_ST},
/* API_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* API_AUDIO_OPEN_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* API_AUDIO_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* API_RESULT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* API_SETCODEC_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* RFC_OPEN_EVT */ {BTA_AG_RFC_ACP_OPEN, BTA_AG_SCO_LISTEN, BTA_AG_OPEN_ST},
/* RFC_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* RFC_SRV_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* RFC_DATA_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* SCO_OPEN_EVT */ {BTA_AG_SCO_CONN_OPEN, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* SCO_CLOSE_EVT */ {BTA_AG_SCO_CONN_CLOSE, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* DISC_ACP_RES_EVT */ {BTA_AG_FREE_DB, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* DISC_INT_RES_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* DISC_OK_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* DISC_FAIL_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* CI_RX_WRITE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* RING_TOUT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* SVC_TOUT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* CI_SCO_DATA_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* CI_SLC_READY_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}
};
/* state table for opening state */
const UINT8 bta_ag_st_opening[][BTA_AG_NUM_COLS] =
{
/* Event Action 1 Action 2 Next state */
/* API_REGISTER_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST},
/* API_DEREGISTER_EVT */ {BTA_AG_RFC_DO_CLOSE, BTA_AG_START_DEREG, BTA_AG_CLOSING_ST},
/* API_OPEN_EVT */ {BTA_AG_OPEN_FAIL, BTA_AG_IGNORE, BTA_AG_OPENING_ST},
/* API_CLOSE_EVT */ {BTA_AG_RFC_DO_CLOSE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST},
/* API_AUDIO_OPEN_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST},
/* API_AUDIO_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST},
/* API_RESULT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST},
/* API_SETCODEC_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST},
/* RFC_OPEN_EVT */ {BTA_AG_RFC_OPEN, BTA_AG_SCO_LISTEN, BTA_AG_OPEN_ST},
/* RFC_CLOSE_EVT */ {BTA_AG_RFC_FAIL, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* RFC_SRV_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST},
/* RFC_DATA_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST},
/* SCO_OPEN_EVT */ {BTA_AG_SCO_CONN_OPEN, BTA_AG_IGNORE, BTA_AG_OPENING_ST},
/* SCO_CLOSE_EVT */ {BTA_AG_SCO_CONN_CLOSE, BTA_AG_IGNORE, BTA_AG_OPENING_ST},
/* DISC_ACP_RES_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST},
/* DISC_INT_RES_EVT */ {BTA_AG_DISC_INT_RES, BTA_AG_IGNORE, BTA_AG_OPENING_ST},
/* DISC_OK_EVT */ {BTA_AG_RFC_DO_OPEN, BTA_AG_IGNORE, BTA_AG_OPENING_ST},
/* DISC_FAIL_EVT */ {BTA_AG_DISC_FAIL, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* CI_RX_WRITE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST},
/* RING_TOUT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST},
/* SVC_TOUT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST},
/* CI_SCO_DATA_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST},
/* CI_SLC_READY_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}
};
/* state table for open state */
const UINT8 bta_ag_st_open[][BTA_AG_NUM_COLS] =
{
/* Event Action 1 Action 2 Next state */
/* API_REGISTER_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPEN_ST},
/* API_DEREGISTER_EVT */ {BTA_AG_START_CLOSE, BTA_AG_START_DEREG, BTA_AG_CLOSING_ST},
/* API_OPEN_EVT */ {BTA_AG_OPEN_FAIL, BTA_AG_IGNORE, BTA_AG_OPEN_ST},
/* API_CLOSE_EVT */ {BTA_AG_START_CLOSE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST},
/* API_AUDIO_OPEN_EVT */ {BTA_AG_SCO_OPEN, BTA_AG_IGNORE, BTA_AG_OPEN_ST},
/* API_AUDIO_CLOSE_EVT */ {BTA_AG_SCO_CLOSE, BTA_AG_IGNORE, BTA_AG_OPEN_ST},
/* API_RESULT_EVT */ {BTA_AG_RESULT, BTA_AG_IGNORE, BTA_AG_OPEN_ST},
/* API_SETCODEC_EVT */ {BTA_AG_SETCODEC, BTA_AG_IGNORE, BTA_AG_OPEN_ST},
/* RFC_OPEN_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPEN_ST},
/* RFC_CLOSE_EVT */ {BTA_AG_RFC_CLOSE, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* RFC_SRV_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPEN_ST},
/* RFC_DATA_EVT */ {BTA_AG_RFC_DATA, BTA_AG_IGNORE, BTA_AG_OPEN_ST},
/* SCO_OPEN_EVT */ {BTA_AG_SCO_CONN_OPEN, BTA_AG_POST_SCO_OPEN, BTA_AG_OPEN_ST},
/* SCO_CLOSE_EVT */ {BTA_AG_SCO_CONN_CLOSE, BTA_AG_POST_SCO_CLOSE, BTA_AG_OPEN_ST},
/* DISC_ACP_RES_EVT */ {BTA_AG_DISC_ACP_RES, BTA_AG_IGNORE, BTA_AG_OPEN_ST},
/* DISC_INT_RES_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPEN_ST},
/* DISC_OK_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPEN_ST},
/* DISC_FAIL_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPEN_ST},
/* CI_RX_WRITE_EVT */ {BTA_AG_CI_RX_DATA, BTA_AG_IGNORE, BTA_AG_OPEN_ST},
/* RING_TOUT_EVT */ {BTA_AG_SEND_RING, BTA_AG_IGNORE, BTA_AG_OPEN_ST},
/* SVC_TOUT_EVT */ {BTA_AG_START_CLOSE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST},
/* CI_SCO_DATA_EVT */ {BTA_AG_CI_SCO_DATA, BTA_AG_IGNORE, BTA_AG_OPEN_ST},
/* CI_SLC_READY_EVT */ {BTA_AG_RCVD_SLC_READY, BTA_AG_IGNORE, BTA_AG_OPEN_ST}
};
/* state table for closing state */
const UINT8 bta_ag_st_closing[][BTA_AG_NUM_COLS] =
{
/* Event Action 1 Action 2 Next state */
/* API_REGISTER_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST},
/* API_DEREGISTER_EVT */ {BTA_AG_START_DEREG, BTA_AG_IGNORE, BTA_AG_CLOSING_ST},
/* API_OPEN_EVT */ {BTA_AG_OPEN_FAIL, BTA_AG_IGNORE, BTA_AG_CLOSING_ST},
/* API_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST},
/* API_AUDIO_OPEN_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST},
/* API_AUDIO_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST},
/* API_RESULT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST},
/* API_SETCODEC_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST},
/* RFC_OPEN_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST},
/* RFC_CLOSE_EVT */ {BTA_AG_RFC_CLOSE, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* RFC_SRV_CLOSE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST},
/* RFC_DATA_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST},
/* SCO_OPEN_EVT */ {BTA_AG_SCO_CONN_OPEN, BTA_AG_IGNORE, BTA_AG_CLOSING_ST},
/* SCO_CLOSE_EVT */ {BTA_AG_SCO_CONN_CLOSE, BTA_AG_POST_SCO_CLOSE, BTA_AG_CLOSING_ST},
/* DISC_ACP_RES_EVT */ {BTA_AG_FREE_DB, BTA_AG_IGNORE, BTA_AG_CLOSING_ST},
/* DISC_INT_RES_EVT */ {BTA_AG_FREE_DB, BTA_AG_IGNORE, BTA_AG_INIT_ST},
/* DISC_OK_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST},
/* DISC_FAIL_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST},
/* CI_RX_WRITE_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST},
/* RING_TOUT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST},
/* SVC_TOUT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST},
/* CI_SCO_DATA_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST},
/* CI_SLC_READY_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}
};
/* type for state table */
typedef const UINT8 (*tBTA_AG_ST_TBL)[BTA_AG_NUM_COLS];
/* state table */
const tBTA_AG_ST_TBL bta_ag_st_tbl[] =
{
bta_ag_st_init,
bta_ag_st_opening,
bta_ag_st_open,
bta_ag_st_closing
};
/*****************************************************************************
** Global data
*****************************************************************************/
const char *bta_ag_version = "1.5"; //"1.6"
/* AG control block */
#if BTA_DYNAMIC_MEMORY == FALSE
tBTA_AG_CB bta_ag_cb;
#else
tBTA_AG_CB *bta_ag_cb_ptr;
#endif
#if BTA_AG_DEBUG == TRUE
static char *bta_ag_evt_str(UINT16 event, tBTA_AG_RES result)
{
switch (event)
{
case BTA_AG_API_REGISTER_EVT:
return "Register Request";
case BTA_AG_API_DEREGISTER_EVT:
return "Deregister Request";
case BTA_AG_API_OPEN_EVT:
return "Open SLC Request";
case BTA_AG_API_CLOSE_EVT:
return "Close SLC Request";
case BTA_AG_API_AUDIO_OPEN_EVT:
return "Open Audio Request";
case BTA_AG_API_AUDIO_CLOSE_EVT:
return "Close Audio Request";
case BTA_AG_API_RESULT_EVT:
switch (result) {
case BTA_AG_SPK_RES: return ("AT Result BTA_AG_SPK_RES");
case BTA_AG_MIC_RES: return ("AT Result BTA_AG_MIC_RES");
case BTA_AG_INBAND_RING_RES: return ("AT Result BTA_AG_INBAND_RING_RES");
case BTA_AG_CIND_RES: return ("AT Result BTA_AG_CIND_RES");
case BTA_AG_BINP_RES: return ("AT Result BTA_AG_BINP_RES");
case BTA_AG_IND_RES: return ("AT Result BTA_AG_IND_RES");
case BTA_AG_BVRA_RES: return ("AT Result BTA_AG_BVRA_RES");
case BTA_AG_CNUM_RES: return ("AT Result BTA_AG_CNUM_RES");
case BTA_AG_BTRH_RES: return ("AT Result BTA_AG_BTRH_RES");
case BTA_AG_CLCC_RES: return ("AT Result BTA_AG_CLCC_RES");
case BTA_AG_COPS_RES: return ("AT Result BTA_AG_COPS_RES");
case BTA_AG_IN_CALL_RES: return ("AT Result BTA_AG_IN_CALL_RES");
case BTA_AG_IN_CALL_CONN_RES: return ("AT Result BTA_AG_IN_CALL_CONN_RES");
case BTA_AG_CALL_WAIT_RES: return ("AT Result BTA_AG_CALL_WAIT_RES");
case BTA_AG_OUT_CALL_ORIG_RES: return ("AT Result BTA_AG_OUT_CALL_ORIG_RES");
case BTA_AG_OUT_CALL_ALERT_RES: return ("AT Result BTA_AG_OUT_CALL_ALERT_RES");
case BTA_AG_OUT_CALL_CONN_RES: return ("AT Result BTA_AG_OUT_CALL_CONN_RES");
case BTA_AG_CALL_CANCEL_RES: return ("AT Result BTA_AG_CALL_CANCEL_RES");
case BTA_AG_END_CALL_RES: return ("AT Result BTA_AG_END_CALL_RES");
case BTA_AG_UNAT_RES: return ("AT Result BTA_AG_UNAT_RES");
default: return ("Unknown AG Result");
}
case BTA_AG_API_SETCODEC_EVT:
return "Set Codec Request";
case BTA_AG_RFC_OPEN_EVT:
return "RFC Opened";
case BTA_AG_RFC_CLOSE_EVT:
return "RFC Closed";
case BTA_AG_RFC_SRV_CLOSE_EVT:
return "RFC SRV Closed";
case BTA_AG_RFC_DATA_EVT:
return "RFC Data";
case BTA_AG_SCO_OPEN_EVT:
return "Audio Opened";
case BTA_AG_SCO_CLOSE_EVT:
return "Audio Closed";
case BTA_AG_DISC_ACP_RES_EVT:
return "Discovery ACP Result";
case BTA_AG_DISC_INT_RES_EVT:
return "Discovery INT Result";
case BTA_AG_DISC_OK_EVT:
return "Discovery OK";
case BTA_AG_DISC_FAIL_EVT:
return "Discovery Failed";
case BTA_AG_CI_RX_WRITE_EVT:
return "CI RX Write";
case BTA_AG_RING_TOUT_EVT:
return "Ring Timeout";
case BTA_AG_SVC_TOUT_EVT:
return "Service Timeout";
case BTA_AG_API_ENABLE_EVT:
return "Enable AG";
case BTA_AG_API_DISABLE_EVT:
return "Disable AG";
case BTA_AG_CI_SCO_DATA_EVT:
return "SCO data Callin";
case BTA_AG_CI_SLC_READY_EVT:
return "SLC Ready Callin";
default:
return "Unknown AG Event";
}
}
static char *bta_ag_state_str(UINT8 state)
{
switch (state) {
case BTA_AG_INIT_ST:
return "Initial";
case BTA_AG_OPENING_ST:
return "Opening";
case BTA_AG_OPEN_ST:
return "Open";
case BTA_AG_CLOSING_ST:
return "Closing";
default:
return "Unknown AG State";
}
}
#endif
/*******************************************************************************
**
** Function bta_ag_timer_cback
**
** Description AG timer callback.
**
**
** Returns void
**
*******************************************************************************/
static void bta_ag_timer_cback(void *p)
{
BT_HDR *p_buf;
TIMER_LIST_ENT *p_tle = (TIMER_LIST_ENT *) p;
if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) {
p_buf->event = p_tle->event;
p_buf->layer_specific = bta_ag_scb_to_idx((tBTA_AG_SCB *) p_tle->param);
bta_sys_sendmsg(p_buf);
}
}
/*******************************************************************************
**
** Function bta_ag_scb_alloc
**
** Description Allocate an AG service control block.
**
**
** Returns pointer to the scb, or NULL if none could be allocated.
**
*******************************************************************************/
static tBTA_AG_SCB *bta_ag_scb_alloc(void)
{
tBTA_AG_SCB *p_scb = &bta_ag_cb.scb[0];
int i;
for (i = 0; i < BTA_AG_NUM_SCB; i++, p_scb++) {
if (!p_scb->in_use) {
/* initialize variables */
p_scb->in_use = TRUE;
p_scb->sco_idx = BTM_INVALID_SCO_INDEX;
#if (BTM_WBS_INCLUDED == TRUE )
p_scb->codec_updated = FALSE;
#endif
/* set up timers */
p_scb->act_timer.param = (UINT32) p_scb;
p_scb->act_timer.p_cback = bta_ag_timer_cback;
#if (BTM_WBS_INCLUDED == TRUE)
/* set eSCO mSBC setting to T2 as the preferred */
p_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T2;
#endif
APPL_TRACE_DEBUG("bta_ag_scb_alloc %d", bta_ag_scb_to_idx(p_scb));
break;
}
}
if (i == BTA_AG_NUM_SCB) {
p_scb = NULL; /* out of scbs */
APPL_TRACE_WARNING("Out of ag scbs");
}
return p_scb;
}
/*******************************************************************************
**
** Function bta_ag_scb_dealloc
**
** Description Deallocate a service control block.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_scb_dealloc(tBTA_AG_SCB *p_scb)
{
UINT8 idx;
BOOLEAN allocated = FALSE;
APPL_TRACE_DEBUG("bta_ag_scb_dealloc %d", bta_ag_scb_to_idx(p_scb));
/* stop timers */
bta_sys_stop_timer(&p_scb->act_timer);
#if (BTM_WBS_INCLUDED == TRUE)
bta_sys_stop_timer(&p_scb->cn_timer);
#endif
bta_sys_stop_timer(&p_scb->colli_timer);
/* initialize control block */
memset(p_scb, 0, sizeof(tBTA_AG_SCB));
p_scb->sco_idx = BTM_INVALID_SCO_INDEX;
/* If all scbs are deallocated, callback with disable event */
if (!bta_sys_is_register (BTA_ID_AG)) {
for (idx = 0; idx < BTA_AG_NUM_SCB; idx++) {
if (bta_ag_cb.scb[idx].in_use) {
allocated = TRUE;
break;
}
}
if (!allocated) {
(*bta_ag_cb.p_cback)(BTA_AG_DISABLE_EVT, NULL);
}
}
}
/*******************************************************************************
**
** Function bta_ag_scb_to_idx
**
** Description Given a pointer to an scb, return its index.
**
**
** Returns Index of scb.
**
*******************************************************************************/
UINT16 bta_ag_scb_to_idx(tBTA_AG_SCB *p_scb)
{
/* use array arithmetic to determine index */
return ((UINT16) (p_scb - bta_ag_cb.scb)) + 1;
}
/*******************************************************************************
**
** Function bta_ag_scb_by_idx
**
** Description Given an scb index return pointer to scb.
**
**
** Returns Pointer to scb or NULL if not allocated.
**
*******************************************************************************/
tBTA_AG_SCB *bta_ag_scb_by_idx(UINT16 idx)
{
tBTA_AG_SCB *p_scb;
/* verify index */
if (idx > 0 && idx <= BTA_AG_NUM_SCB) {
p_scb = &bta_ag_cb.scb[idx - 1];
if (!p_scb->in_use) {
p_scb = NULL;
APPL_TRACE_WARNING("ag scb idx %d not allocated", idx);
}
} else {
p_scb = NULL;
APPL_TRACE_DEBUG("ag scb idx %d out of range", idx);
}
return p_scb;
}
/*******************************************************************************
**
** Function bta_ag_service_to_idx
**
** Description Given a BTA service mask convert to profile index.
**
**
** Returns Profile ndex of scb.
**
*******************************************************************************/
UINT8 bta_ag_service_to_idx(tBTA_SERVICE_MASK services)
{
if (services & BTA_HFP_SERVICE_MASK) {
return BTA_AG_HFP;
} else {
return BTA_AG_HSP;
}
}
/*******************************************************************************
**
** Function bta_ag_idx_by_bdaddr
**
** Description Find SCB associated with peer BD address.
**
**
** Returns Index of SCB or zero if none found.
**
*******************************************************************************/
UINT16 bta_ag_idx_by_bdaddr(BD_ADDR peer_addr)
{
tBTA_AG_SCB *p_scb = &bta_ag_cb.scb[0];
UINT16 i;
if (peer_addr != NULL) {
for (i = 0; i < BTA_AG_NUM_SCB; i++, p_scb++) {
if (p_scb->in_use && !bdcmp(peer_addr, p_scb->peer_addr)) {
return (i + 1);
}
}
}
/* no scb found */
APPL_TRACE_WARNING("No ag scb for peer addr");
return 0;
}
/*******************************************************************************
**
** Function bta_ag_other_scb_open
**
** Description Check whether any other scb is in open state.
**
**
** Returns TRUE if another scb is in open state, FALSE otherwise.
**
*******************************************************************************/
BOOLEAN bta_ag_other_scb_open(tBTA_AG_SCB *p_curr_scb)
{
tBTA_AG_SCB *p_scb = &bta_ag_cb.scb[0];
for (int i = 0; i < BTA_AG_NUM_SCB; i++, p_scb++) {
if (p_scb->in_use && p_scb != p_curr_scb && p_scb->state == BTA_AG_OPEN_ST) {
return TRUE;
}
}
/* no other scb found */
APPL_TRACE_DEBUG("No other ag scb open");
return FALSE;
}
/*******************************************************************************
**
** Function bta_ag_scb_open
**
** Description Check whether given scb is in open state.
**
**
** Returns TRUE if scb is in open state, FALSE otherwise.
**
*******************************************************************************/
BOOLEAN bta_ag_scb_open(tBTA_AG_SCB *p_curr_scb)
{
if (p_curr_scb && p_curr_scb->in_use && p_curr_scb->state == BTA_AG_OPEN_ST) {
return TRUE;
}
return FALSE;
}
/*******************************************************************************
**
** Function bta_ag_get_other_idle_scb
**
** Description Return other scb if it is in INIT st.
**
**
** Returns Pointer to other scb if INIT st, NULL otherwise.
**
*******************************************************************************/
tBTA_AG_SCB *bta_ag_get_other_idle_scb (tBTA_AG_SCB *p_curr_scb)
{
tBTA_AG_SCB *p_scb = &bta_ag_cb.scb[0];
UINT8 xx;
for (xx = 0; xx < BTA_AG_NUM_SCB; xx++, p_scb++) {
if (p_scb->in_use && (p_scb != p_curr_scb) && (p_scb->state == BTA_AG_INIT_ST)) {
return p_scb;
}
}
/* no other scb found */
APPL_TRACE_DEBUG("bta_ag_get_other_idle_scb: No idle AG scb");
return NULL;
}
/*******************************************************************************
**
** Function bta_ag_colli_timer_cback
**
** Description AG connection collision timer callback
**
**
** Returns void
**
*******************************************************************************/
static void bta_ag_colli_timer_cback (TIMER_LIST_ENT *p_tle)
{
tBTA_AG_SCB *p_scb;
APPL_TRACE_DEBUG ("bta_ag_colli_timer_cback");
if (p_tle) {
p_scb = (tBTA_AG_SCB *)p_tle->param;
if (p_scb) {
p_scb->colli_tmr_on = FALSE;
/* If the peer haven't opened AG connection */
/* we will restart opening process. */
bta_ag_resume_open (p_scb);
}
}
}
/*******************************************************************************
**
** Function bta_ag_collision_cback
**
** Description Get notified about collision.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_collision_cback (tBTA_SYS_CONN_STATUS status, UINT8 id, UINT8 app_id, BD_ADDR peer_addr)
{
UINT16 handle;
tBTA_AG_SCB *p_scb;
UNUSED(status);
UNUSED(app_id);
/* Check if we have opening scb for the peer device. */
handle = bta_ag_idx_by_bdaddr (peer_addr);
p_scb = bta_ag_scb_by_idx (handle);
if (p_scb && (p_scb->state == BTA_AG_OPENING_ST)) {
if (id == BTA_ID_SYS) {
/* ACL collision */
APPL_TRACE_WARNING ("AG found collision (ACL) ...");
} else if (id == BTA_ID_AG) {
/* RFCOMM collision */
APPL_TRACE_WARNING ("AG found collision (RFCOMM) ...");
} else {
APPL_TRACE_WARNING ("AG found collision (\?\?\?) ...");
}
p_scb->state = BTA_AG_INIT_ST;
/* Cancel SDP if it had been started. */
if(p_scb->p_disc_db) {
(void)SDP_CancelServiceSearch (p_scb->p_disc_db);
bta_ag_free_db(p_scb, NULL);
}
/* reopen registered servers */
/* Collision may be detected before or after we close servers. */
if (bta_ag_is_server_closed (p_scb)) {
bta_ag_start_servers(p_scb, p_scb->reg_services);
}
/* Start timer to han */
p_scb->colli_timer.p_cback = (TIMER_CBACK*)&bta_ag_colli_timer_cback;
p_scb->colli_timer.param = (INT32)p_scb;
bta_sys_start_timer(&p_scb->colli_timer, 0, BTA_AG_COLLISION_TIMER);
p_scb->colli_tmr_on = TRUE;
}
}
/*******************************************************************************
**
** Function bta_ag_resume_open
**
** Description Resume opening process.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_resume_open (tBTA_AG_SCB *p_scb)
{
if (p_scb) {
APPL_TRACE_DEBUG ("bta_ag_resume_open, Handle(%d)", bta_ag_scb_to_idx(p_scb));
/* resume opening process. */
if (p_scb->state == BTA_AG_INIT_ST) {
p_scb->state = BTA_AG_OPENING_ST;
bta_ag_start_open (p_scb, NULL);
}
} else {
APPL_TRACE_ERROR ("bta_ag_resume_open, Null p_scb");
}
}
/*******************************************************************************
**
** Function bta_ag_api_enable
**
** Description Handle an API enable event.
**
**
** Returns void
**
*******************************************************************************/
static void bta_ag_api_enable(tBTA_AG_DATA *p_data)
{
/* initialize control block */
memset(&bta_ag_cb, 0, sizeof(tBTA_AG_CB));
/* store callback function */
bta_ag_cb.p_cback = p_data->api_enable.p_cback;
bta_ag_cb.parse_mode = p_data->api_enable.parse_mode;
/* check if mSBC support enabled */
if (strcmp(bta_ag_version, "1.6") == 0) {
bta_ag_cb.msbc_enabled = TRUE;
bta_ag_cb.scb->negotiated_codec = BTM_SCO_CODEC_MSBC;
} else{
bta_ag_cb.msbc_enabled = FALSE;
bta_ag_cb.scb->negotiated_codec = BTM_SCO_CODEC_CVSD;
}
/* set deault setting for eSCO/SCO */
BTM_WriteVoiceSettings(AG_VOICE_SETTINGS);
bta_sys_collision_register (BTA_ID_AG, bta_ag_collision_cback);
/* call callback with enable event */
(*bta_ag_cb.p_cback)(BTA_AG_ENABLE_EVT, NULL);
}
/*******************************************************************************
**
** Function bta_ag_api_disable
**
** Description Handle an API disable event.
**
**
** Returns void
**
*******************************************************************************/
static void bta_ag_api_disable(tBTA_AG_DATA *p_data)
{
/* deregister all scbs in use */
tBTA_AG_SCB *p_scb = &bta_ag_cb.scb[0];
BOOLEAN do_dereg = FALSE;
int i;
if (!bta_sys_is_register (BTA_ID_AG)) {
APPL_TRACE_ERROR("BTA AG is already disabled, ignoring ...");
return;
}
/* De-register with BTA system manager */
bta_sys_deregister(BTA_ID_AG);
for (i = 0; i < BTA_AG_NUM_SCB; i++, p_scb++) {
if (p_scb->in_use) {
bta_ag_sm_execute(p_scb, BTA_AG_API_DEREGISTER_EVT, p_data);
do_dereg = TRUE;
}
}
if (!do_dereg) {
/* Done, send callback evt to app */
(*bta_ag_cb.p_cback)(BTA_AG_DISABLE_EVT, NULL);
}
bta_sys_collision_register (BTA_ID_AG, NULL);
}
/*******************************************************************************
**
** Function bta_ag_api_register
**
** Description Handle an API event registers a new service.
**
**
** Returns void
**
*******************************************************************************/
static void bta_ag_api_register(tBTA_AG_DATA *p_data)
{
tBTA_AG_SCB *p_scb;
tBTA_AG_REGISTER reg;
/* allocate an scb */
if ((p_scb = bta_ag_scb_alloc()) != NULL) {
APPL_TRACE_DEBUG("bta_ag_api_register: p_scb 0x%08x ", (unsigned int)p_scb);
bta_ag_sm_execute(p_scb, p_data->hdr.event, p_data);
} else {
reg.status = BTA_AG_FAIL_RESOURCES;
(*bta_ag_cb.p_cback)(BTA_AG_REGISTER_EVT, (tBTA_AG *) &reg);
}
}
/*******************************************************************************
**
** Function bta_ag_api_result
**
** Description Handle an API result event.
**
**
** Returns void
**
*******************************************************************************/
static void bta_ag_api_result(tBTA_AG_DATA *p_data)
{
tBTA_AG_SCB *p_scb;
int i;
if (p_data->hdr.layer_specific != BTA_AG_HANDLE_ALL) {
if ((p_scb = bta_ag_scb_by_idx(p_data->hdr.layer_specific)) != NULL) {
APPL_TRACE_DEBUG("bta_ag_api_result: p_scb 0x%08x ", (unsigned int)p_scb);
bta_ag_sm_execute(p_scb, BTA_AG_API_RESULT_EVT, p_data);
}
} else {
for (i = 0, p_scb = &bta_ag_cb.scb[0]; i < BTA_AG_NUM_SCB; i++, p_scb++) {
if (p_scb->in_use && p_scb->svc_conn) {
APPL_TRACE_DEBUG("bta_ag_api_result p_scb 0x%08x ", (unsigned int)p_scb);
bta_ag_sm_execute(p_scb, BTA_AG_API_RESULT_EVT, p_data);
}
}
}
}
/*******************************************************************************
**
** Function bta_ag_sm_execute
**
** Description State machine event handling function for AG
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_sm_execute(tBTA_AG_SCB *p_scb, UINT16 event, tBTA_AG_DATA *p_data)
{
tBTA_AG_ST_TBL state_table;
UINT8 action;
#if BTA_AG_DEBUG == TRUE
UINT16 in_event = event;
UINT8 in_state = p_scb->state;
/* Ignore displaying of AT results when not connected (Ignored in state machine) */
if (in_event != BTA_AG_API_RESULT_EVT || p_scb->state == BTA_AG_OPEN_ST) {
APPL_TRACE_EVENT("AG evt (hdl 0x%04x): State %d (%s), Event 0x%04x (%s)",
bta_ag_scb_to_idx(p_scb),
p_scb->state, bta_ag_state_str(p_scb->state),
event, bta_ag_evt_str(event, p_data->api_result.result));
}
#else
APPL_TRACE_EVENT("AG evt (hdl 0x%04x): State %d, Event 0x%04x",
bta_ag_scb_to_idx(p_scb), p_scb->state, event);
#endif
event &= 0x00FF;
if (event >= (BTA_AG_MAX_EVT & 0x00FF)) {
APPL_TRACE_ERROR("AG evt out of range, ignoring...");
return;
}
/* look up the state table for the current state */
state_table = bta_ag_st_tbl[p_scb->state];
/* set next state */
p_scb->state = state_table[event][BTA_AG_NEXT_STATE];
/* execute action functions */
for (int i = 0; i < BTA_AG_ACTIONS; i++) {
if ((action = state_table[event][i]) != BTA_AG_IGNORE) {
(*bta_ag_action[action])(p_scb, p_data);
} else {
break;
}
}
#if BTA_AG_DEBUG == TRUE
if (p_scb->state != in_state) {
APPL_TRACE_EVENT("BTA AG State Change: [%s] -> [%s] after Event [%s]",
bta_ag_state_str(in_state),
bta_ag_state_str(p_scb->state),
bta_ag_evt_str(in_event, p_data->api_result.result));
}
#endif
}
/*******************************************************************************
**
** Function bta_ag_hdl_event
**
** Description Data gateway main event handling function.
**
**
** Returns BOOLEAN
**
*******************************************************************************/
BOOLEAN bta_ag_hdl_event(BT_HDR *p_msg)
{
tBTA_AG_SCB *p_scb;
APPL_TRACE_DEBUG("bta_ag_hdl_event: Event 0x%04x ", p_msg->event);
switch (p_msg->event) {
/* handle enable event */
case BTA_AG_API_ENABLE_EVT:
bta_ag_api_enable((tBTA_AG_DATA *) p_msg);
break;
/* handle disable event */
case BTA_AG_API_DISABLE_EVT:
bta_ag_api_disable((tBTA_AG_DATA *) p_msg);
break;
/* handle register event */
case BTA_AG_API_REGISTER_EVT:
bta_ag_api_register((tBTA_AG_DATA *) p_msg);
break;
/* handle result event */
case BTA_AG_API_RESULT_EVT:
bta_ag_api_result((tBTA_AG_DATA *) p_msg);
break;
/* all others reference scb by handle */
default:
if ((p_scb = bta_ag_scb_by_idx(p_msg->layer_specific)) != NULL) {
APPL_TRACE_DEBUG("bta_ag_hdl_event: p_scb 0x%08x ", (unsigned int)p_scb);
bta_ag_sm_execute(p_scb, p_msg->event, (tBTA_AG_DATA *) p_msg);
}
break;
}
return TRUE;
}
#endif /* #if (BTA_AG_INCLUDED == TRUE) */

View file

@ -0,0 +1,403 @@
/******************************************************************************
*
* Copyright (C) 2004-2012 Broadcom Corporation
*
* 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.
*
******************************************************************************/
/******************************************************************************
*
* This file contains the audio gateway functions controlling the RFCOMM
* connections.
*
******************************************************************************/
#include <string.h>
#include "bta_ag_int.h"
#include "bta/bta_api.h"
#include "bta/bta_sys.h"
#include "bta/bta_ag_api.h"
#include "bta/bta_ag_co.h"
#include "bta/utl.h"
#include "stack/btm_api.h"
#include "stack/port_api.h"
#include "stack/rfcdefs.h"
#include "common/bt_trace.h"
#include "osi/allocator.h"
#if (BTA_AG_INCLUDED == TRUE)
/* Event mask for RfCOMM port callback */
#define BTA_AG_PORT_EV_MASK PORT_EV_RXCHAR
/* each scb has its own rfcomm callbacks */
void bta_ag_port_cback_1(UINT32 code, UINT16 port_handle);
void bta_ag_port_cback_2(UINT32 code, UINT16 port_handle);
void bta_ag_port_cback_3(UINT32 code, UINT16 port_handle);
void bta_ag_mgmt_cback_1(UINT32 code, UINT16 port_handle);
void bta_ag_mgmt_cback_2(UINT32 code, UINT16 port_handle);
void bta_ag_mgmt_cback_3(UINT32 code, UINT16 port_handle);
int bta_ag_data_cback_1(UINT16 port_handle, void *p_data, UINT16 len);
int bta_ag_data_cback_2(UINT16 port_handle, void *p_data, UINT16 len);
int bta_ag_data_cback_3(UINT16 port_handle, void *p_data, UINT16 len);
/* rfcomm callback function tables */
typedef tPORT_CALLBACK *tBTA_AG_PORT_CBACK;
const tBTA_AG_PORT_CBACK bta_ag_port_cback_tbl[] =
{
bta_ag_port_cback_1,
bta_ag_port_cback_2,
bta_ag_port_cback_3
};
const tBTA_AG_PORT_CBACK bta_ag_mgmt_cback_tbl[] =
{
bta_ag_mgmt_cback_1,
bta_ag_mgmt_cback_2,
bta_ag_mgmt_cback_3
};
typedef tPORT_DATA_CALLBACK *tBTA_AG_DATA_CBACK;
const tBTA_AG_DATA_CBACK bta_ag_data_cback_tbl[] =
{
bta_ag_data_cback_1,
bta_ag_data_cback_2,
bta_ag_data_cback_3
};
/*******************************************************************************
**
** Function bta_ag_port_cback
**
** Description RFCOMM Port callback
**
**
** Returns void
**
*******************************************************************************/
static void bta_ag_port_cback(UINT32 code, UINT16 port_handle, UINT16 handle)
{
BT_HDR *p_buf;
tBTA_AG_SCB *p_scb;
UNUSED(code);
if ((p_scb = bta_ag_scb_by_idx(handle)) != NULL) {
/* ignore port events for port handles other than connected handle */
if (port_handle != p_scb->conn_handle) {
APPL_TRACE_DEBUG("ag_port_cback ignoring handle:%d conn_handle = %d other handle = %d",
port_handle, p_scb->conn_handle, handle);
return;
}
if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) {
p_buf->event = BTA_AG_RFC_DATA_EVT;
p_buf->layer_specific = handle;
bta_sys_sendmsg(p_buf);
}
}
}
/*******************************************************************************
**
** Function bta_ag_mgmt_cback
**
** Description RFCOMM management callback
**
**
** Returns void
**
*******************************************************************************/
static void bta_ag_mgmt_cback(UINT32 code, UINT16 port_handle, UINT16 handle)
{
tBTA_AG_RFC *p_buf;
tBTA_AG_SCB *p_scb;
UINT16 event;
UINT8 i;
BOOLEAN found_handle = FALSE;
APPL_TRACE_DEBUG("ag_mgmt_cback : code = %d, port_handle = %d, handle = %d",
code, port_handle, handle);
if ((p_scb = bta_ag_scb_by_idx(handle)) != NULL) {
/* ignore close event for port handles other than connected handle */
if ((code != PORT_SUCCESS) && (port_handle != p_scb->conn_handle)) {
APPL_TRACE_DEBUG("ag_mgmt_cback ignoring handle:%d", port_handle);
return;
}
if (code == PORT_SUCCESS) {
if (p_scb->conn_handle) {
/* Outgoing connection */
if (port_handle == p_scb->conn_handle)
found_handle = TRUE;
} else {
/* Incoming connection */
for (i = 0; i < BTA_AG_NUM_IDX; i++) {
if (port_handle == p_scb->serv_handle[i])
found_handle = TRUE;
}
}
if (!found_handle) {
APPL_TRACE_ERROR ("bta_ag_mgmt_cback: PORT_SUCCESS, ignoring handle = %d", port_handle);
return;
}
event = BTA_AG_RFC_OPEN_EVT;
} else if (port_handle == p_scb->conn_handle) {
/* distinguish server close events */
event = BTA_AG_RFC_CLOSE_EVT;
} else {
event = BTA_AG_RFC_SRV_CLOSE_EVT;
}
if ((p_buf = (tBTA_AG_RFC *) osi_malloc(sizeof(tBTA_AG_RFC))) != NULL) {
p_buf->hdr.event = event;
p_buf->hdr.layer_specific = handle;
p_buf->port_handle = port_handle;
bta_sys_sendmsg(p_buf);
}
}
}
/*******************************************************************************
**
** Function bta_ag_data_cback
**
** Description RFCOMM data callback
**
**
** Returns void
**
*******************************************************************************/
static int bta_ag_data_cback(UINT16 port_handle, void *p_data, UINT16 len, UINT16 handle)
{
UNUSED(port_handle);
/* call data call-out directly */
bta_ag_co_tx_write(handle, (UINT8 *) p_data, len);
return 0;
}
/*******************************************************************************
**
** Function bta_ag_port_cback_1 to 3
** bta_ag_mgmt_cback_1 to 3
**
** Description RFCOMM callback functions. This is an easy way to
** distinguish scb from the callback.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_mgmt_cback_1(UINT32 code, UINT16 handle) {bta_ag_mgmt_cback(code, handle, 1);}
void bta_ag_mgmt_cback_2(UINT32 code, UINT16 handle) {bta_ag_mgmt_cback(code, handle, 2);}
void bta_ag_mgmt_cback_3(UINT32 code, UINT16 handle) {bta_ag_mgmt_cback(code, handle, 3);}
void bta_ag_port_cback_1(UINT32 code, UINT16 handle) {bta_ag_port_cback(code, handle, 1);}
void bta_ag_port_cback_2(UINT32 code, UINT16 handle) {bta_ag_port_cback(code, handle, 2);}
void bta_ag_port_cback_3(UINT32 code, UINT16 handle) {bta_ag_port_cback(code, handle, 3);}
/*******************************************************************************
**
** Function bta_ag_data_cback_1 to 3
**
** Description RFCOMM data callback functions. This is an easy way to
** distinguish scb from the callback.
**
**
** Returns void
**
*******************************************************************************/
int bta_ag_data_cback_1(UINT16 port_handle, void *p_data, UINT16 len)
{
return bta_ag_data_cback(port_handle, p_data, len, 1);
}
int bta_ag_data_cback_2(UINT16 port_handle, void *p_data, UINT16 len)
{
return bta_ag_data_cback(port_handle, p_data, len, 2);
}
int bta_ag_data_cback_3(UINT16 port_handle, void *p_data, UINT16 len)
{
return bta_ag_data_cback(port_handle, p_data, len, 3);
}
/*******************************************************************************
**
** Function bta_ag_setup_port
**
** Description Setup RFCOMM port for use by AG.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_setup_port(tBTA_AG_SCB *p_scb, UINT16 handle)
{
UINT16 i = bta_ag_scb_to_idx(p_scb) - 1;
/* set up data callback if using pass through mode */
if (bta_ag_cb.parse_mode == BTA_AG_PASS_THROUGH) {
PORT_SetDataCallback(handle, bta_ag_data_cback_tbl[i]);
}
PORT_SetEventMask(handle, BTA_AG_PORT_EV_MASK);
PORT_SetEventCallback(handle, bta_ag_port_cback_tbl[i]);
}
/*******************************************************************************
**
** Function bta_ag_start_servers
**
** Description Setup RFCOMM servers for use by AG.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_start_servers(tBTA_AG_SCB *p_scb, tBTA_SERVICE_MASK services)
{
int bta_ag_port_status;
services >>= BTA_HSP_SERVICE_ID;
for (int i = 0; i < BTA_AG_NUM_IDX && services != 0; i++, services >>= 1) {
/* if service is set in mask */
if (services & 1) {
BTM_SetSecurityLevel(FALSE, "", bta_ag_sec_id[i], p_scb->serv_sec_mask,
BT_PSM_RFCOMM, BTM_SEC_PROTO_RFCOMM, bta_ag_cb.profile[i].scn);
bta_ag_port_status = RFCOMM_CreateConnection(bta_ag_uuid[i], bta_ag_cb.profile[i].scn,
TRUE, BTA_AG_MTU, (UINT8 *) bd_addr_any, &(p_scb->serv_handle[i]),
bta_ag_mgmt_cback_tbl[bta_ag_scb_to_idx(p_scb) - 1]);
if( bta_ag_port_status == PORT_SUCCESS ) {
bta_ag_setup_port(p_scb, p_scb->serv_handle[i]);
} else {
/* TODO: CR#137125 to handle to error properly */
APPL_TRACE_DEBUG("bta_ag_start_servers: RFCOMM_CreateConnection returned error:%d", bta_ag_port_status);
}
}
}
}
/*******************************************************************************
**
** Function bta_ag_close_servers
**
** Description Close RFCOMM servers port for use by AG.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_close_servers(tBTA_AG_SCB *p_scb, tBTA_SERVICE_MASK services)
{
int i;
services >>= BTA_HSP_SERVICE_ID;
for (i = 0; i < BTA_AG_NUM_IDX && services != 0; i++, services >>= 1) {
/* if service is set in mask */
if (services & 1) {
RFCOMM_RemoveServer(p_scb->serv_handle[i]);
p_scb->serv_handle[i] = 0;
}
}
}
/*******************************************************************************
**
** Function bta_ag_is_server_closed
**
** Description Returns TRUE if all servers are closed.
**
**
** Returns TRUE if all servers are closed, FALSE otherwise
**
*******************************************************************************/
BOOLEAN bta_ag_is_server_closed (tBTA_AG_SCB *p_scb)
{
UINT8 i;
BOOLEAN is_closed = TRUE;
for (i = 0; i < BTA_AG_NUM_IDX; i++) {
if (p_scb->serv_handle[i] != 0)
is_closed = FALSE;
}
return is_closed;
}
/*******************************************************************************
**
** Function bta_ag_rfc_do_open
**
** Description Open an RFCOMM connection to the peer device.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_rfc_do_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
{
BTM_SetSecurityLevel(TRUE, "", bta_ag_sec_id[p_scb->conn_service],
p_scb->cli_sec_mask, BT_PSM_RFCOMM, BTM_SEC_PROTO_RFCOMM, p_scb->peer_scn);
if (RFCOMM_CreateConnection(bta_ag_uuid[p_scb->conn_service], p_scb->peer_scn,
FALSE, BTA_AG_MTU, p_scb->peer_addr, &(p_scb->conn_handle),
bta_ag_mgmt_cback_tbl[bta_ag_scb_to_idx(p_scb) - 1]) == PORT_SUCCESS) {
bta_ag_setup_port(p_scb, p_scb->conn_handle);
APPL_TRACE_DEBUG("bta_ag_rfc_do_open : conn_handle = %d", p_scb->conn_handle);
} else {
/* RFCOMM create connection failed; send ourselves RFCOMM close event */
bta_ag_sm_execute(p_scb, BTA_AG_RFC_CLOSE_EVT, p_data);
}
}
/*******************************************************************************
**
** Function bta_ag_rfc_do_close
**
** Description Close RFCOMM connection.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_rfc_do_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
{
tBTA_AG_RFC *p_buf;
UNUSED(p_data);
if (p_scb->conn_handle) {
RFCOMM_RemoveConnection(p_scb->conn_handle);
} else {
/* Close API was called while AG is in Opening state. */
/* Need to trigger the state machine to send callback to the app */
/* and move back to INIT state. */
if ((p_buf = (tBTA_AG_RFC *) osi_malloc(sizeof(tBTA_AG_RFC))) != NULL) {
p_buf->hdr.event = BTA_AG_RFC_CLOSE_EVT;
p_buf->hdr.layer_specific = bta_ag_scb_to_idx(p_scb);
bta_sys_sendmsg(p_buf);
}
/* Cancel SDP if it had been started. */
/*
if(p_scb->p_disc_db)
{
(void)SDP_CancelServiceSearch (p_scb->p_disc_db);
}
*/
}
}
#endif /* #if (BTA_AG_INCLUDED == TRUE) */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,446 @@
/******************************************************************************
*
* Copyright (C) 2003-2012 Broadcom Corporation
*
* 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.
*
******************************************************************************/
/******************************************************************************
*
* This file contains the audio gateway functions performing SDP
* operations.
*
******************************************************************************/
#include <string.h>
#include "bta_ag_int.h"
#include "bta/bta_ag_api.h"
#include "bta/bta_sys.h"
#include "bta/bta_ag_api.h"
#include "bta/utl.h"
#include "stack/sdp_api.h"
#include "stack/btm_api.h"
#include "common/bt_trace.h"
#include "osi/allocator.h"
#if (BTA_AG_INCLUDED == TRUE)
/* Number of protocol elements in protocol element list. */
#define BTA_AG_NUM_PROTO_ELEMS 2
/* Number of elements in service class id list. */
#define BTA_AG_NUM_SVC_ELEMS 2
/* size of database for service discovery */
#ifndef BTA_AG_DISC_BUF_SIZE
#define BTA_AG_DISC_BUF_SIZE (4096+16)
#endif
/* declare sdp callback functions */
void bta_ag_sdp_cback_1(UINT16 status);
void bta_ag_sdp_cback_2(UINT16 status);
void bta_ag_sdp_cback_3(UINT16 status);
/* SDP callback function table */
typedef tSDP_DISC_CMPL_CB *tBTA_AG_SDP_CBACK;
const tBTA_AG_SDP_CBACK bta_ag_sdp_cback_tbl[] =
{
bta_ag_sdp_cback_1,
bta_ag_sdp_cback_2,
bta_ag_sdp_cback_3
};
/*******************************************************************************
**
** Function bta_ag_sdp_cback
**
** Description SDP callback function.
**
**
** Returns void
**
*******************************************************************************/
static void bta_ag_sdp_cback(UINT16 status, UINT8 idx)
{
tBTA_AG_DISC_RESULT *p_buf;
UINT16 event;
tBTA_AG_SCB *p_scb;
APPL_TRACE_DEBUG("bta_ag_sdp_cback status:0x%x", status);
if ((p_scb = bta_ag_scb_by_idx(idx)) != NULL) {
/* set event according to int/acp */
if (p_scb->role == BTA_AG_ACP) {
event = BTA_AG_DISC_ACP_RES_EVT;
} else {
event = BTA_AG_DISC_INT_RES_EVT;
}
if ((p_buf = (tBTA_AG_DISC_RESULT *) osi_malloc(sizeof(tBTA_AG_DISC_RESULT))) != NULL) {
p_buf->hdr.event = event;
p_buf->hdr.layer_specific = idx;
p_buf->status = status;
bta_sys_sendmsg(p_buf);
}
}
}
/*******************************************************************************
**
** Function bta_ag_sdp_cback_1 to 3
**
** Description SDP callback functions. Since there is no way to
** distinguish scb from the callback we need separate
** callbacks for each scb.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_sdp_cback_1(UINT16 status) {bta_ag_sdp_cback(status, 1);}
void bta_ag_sdp_cback_2(UINT16 status) {bta_ag_sdp_cback(status, 2);}
void bta_ag_sdp_cback_3(UINT16 status) {bta_ag_sdp_cback(status, 3);}
/******************************************************************************
**
** Function bta_ag_add_record
**
** Description This function is called by a server application to add
** HSP or HFP information to an SDP record. Prior to
** calling this function the application must call
** SDP_CreateRecord() to create an SDP record.
**
** Returns TRUE if function execution succeeded,
** FALSE if function execution failed.
**
******************************************************************************/
BOOLEAN bta_ag_add_record(UINT16 service_uuid, char *p_service_name, UINT8 scn,
tBTA_AG_FEAT features, UINT32 sdp_handle)
{
tSDP_PROTOCOL_ELEM proto_elem_list[BTA_AG_NUM_PROTO_ELEMS];
UINT16 svc_class_id_list[BTA_AG_NUM_SVC_ELEMS];
UINT16 browse_list[] = {UUID_SERVCLASS_PUBLIC_BROWSE_GROUP};
UINT16 version;
UINT16 profile_uuid;
UINT8 network;
BOOLEAN result = TRUE;
BOOLEAN codec_supported = FALSE;
UINT8 buf[2];
APPL_TRACE_DEBUG("bta_ag_add_record uuid: %x", service_uuid);
memset( proto_elem_list, 0 , BTA_AG_NUM_PROTO_ELEMS*sizeof(tSDP_PROTOCOL_ELEM));
/* add the protocol element sequence */
proto_elem_list[0].protocol_uuid = UUID_PROTOCOL_L2CAP;
proto_elem_list[0].num_params = 0;
proto_elem_list[1].protocol_uuid = UUID_PROTOCOL_RFCOMM;
proto_elem_list[1].num_params = 1;
proto_elem_list[1].params[0] = scn;
result &= SDP_AddProtocolList(sdp_handle, BTA_AG_NUM_PROTO_ELEMS, proto_elem_list);
/* add service class id list */
svc_class_id_list[0] = service_uuid;
svc_class_id_list[1] = UUID_SERVCLASS_GENERIC_AUDIO;
result &= SDP_AddServiceClassIdList(sdp_handle, BTA_AG_NUM_SVC_ELEMS, svc_class_id_list);
/* add profile descriptor list */
if (service_uuid == UUID_SERVCLASS_AG_HANDSFREE) {
profile_uuid = UUID_SERVCLASS_HF_HANDSFREE;
version = HFP_VERSION_1_6;
} else {
profile_uuid = UUID_SERVCLASS_HEADSET;
version = HSP_VERSION_1_2;
}
result &= SDP_AddProfileDescriptorList(sdp_handle, profile_uuid, version);
/* add service name */
if (p_service_name != NULL && p_service_name[0] != 0) {
result &= SDP_AddAttribute(sdp_handle, ATTR_ID_SERVICE_NAME, TEXT_STR_DESC_TYPE,
(UINT32)(strlen(p_service_name)+1), (UINT8 *) p_service_name);
}
/* add features and network */
if (service_uuid == UUID_SERVCLASS_AG_HANDSFREE) {
network = (features & BTA_AG_FEAT_REJECT) ? 1 : 0;
result &= SDP_AddAttribute(sdp_handle, ATTR_ID_DATA_STORES_OR_NETWORK,
UINT_DESC_TYPE, 1, &network);
if (features & BTA_AG_FEAT_CODEC) {
codec_supported = TRUE;
}
features &= BTA_AG_SDP_FEAT_SPEC;
/* Codec bit position is different in SDP and in BRSF */
if (codec_supported) {
features |= 0x0020;
}
UINT16_TO_BE_FIELD(buf, features);
result &= SDP_AddAttribute(sdp_handle, ATTR_ID_SUPPORTED_FEATURES, UINT_DESC_TYPE, 2, buf);
}
/* add browse group list */
result &= SDP_AddUuidSequence(sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, browse_list);
return result;
}
/*******************************************************************************
**
** Function bta_ag_create_records
**
** Description Create SDP records for registered services.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_create_records(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
{
tBTA_SERVICE_MASK services;
services = p_scb->reg_services >> BTA_HSP_SERVICE_ID;
for (int i = 0; i < BTA_AG_NUM_IDX && services != 0; i++, services >>= 1) {
/* if service is set in mask */
if (services & 1) {
/* add sdp record if not already registered */
if (bta_ag_cb.profile[i].sdp_handle == 0) {
bta_ag_cb.profile[i].sdp_handle = SDP_CreateRecord();
bta_ag_cb.profile[i].scn = BTM_AllocateSCN();
bta_ag_add_record(bta_ag_uuid[i], p_data->api_register.p_name[i],
bta_ag_cb.profile[i].scn, p_data->api_register.features,
bta_ag_cb.profile[i].sdp_handle);
bta_sys_add_uuid(bta_ag_uuid[i]);
}
}
}
p_scb->hsp_version = HSP_VERSION_1_2;
}
/*******************************************************************************
**
** Function bta_ag_del_records
**
** Description Delete SDP records for any registered services.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_del_records(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
{
tBTA_AG_SCB *p = &bta_ag_cb.scb[0];
tBTA_SERVICE_MASK services;
tBTA_SERVICE_MASK others = 0;
int i;
UNUSED(p_data);
/* get services of all other registered servers */
for (i = 0; i < BTA_AG_NUM_IDX; i++, p++) {
if (p_scb == p) {
continue;
}
if (p->in_use && p->dealloc == FALSE) {
others |= p->reg_services;
}
}
others >>= BTA_HSP_SERVICE_ID;
services = p_scb->reg_services >> BTA_HSP_SERVICE_ID;
for (i = 0; i < BTA_AG_NUM_IDX && services != 0; i++, services >>= 1, others >>= 1)
{
/* if service registered for this scb and not registered for any other scb */
if (((services & 1) == 1) && ((others & 1) == 0)) {
APPL_TRACE_DEBUG("bta_ag_del_records %d", i);
if (bta_ag_cb.profile[i].sdp_handle != 0) {
SDP_DeleteRecord(bta_ag_cb.profile[i].sdp_handle);
bta_ag_cb.profile[i].sdp_handle = 0;
}
BTM_FreeSCN(bta_ag_cb.profile[i].scn);
BTM_SecClrService(bta_ag_sec_id[i]);
bta_sys_remove_uuid(bta_ag_uuid[i]);
}
}
}
/*******************************************************************************
**
** Function bta_ag_sdp_find_attr
**
** Description Process SDP discovery results to find requested attributes
** for requested service.
**
**
** Returns TRUE if results found, FALSE otherwise.
**
*******************************************************************************/
BOOLEAN bta_ag_sdp_find_attr(tBTA_AG_SCB *p_scb, tBTA_SERVICE_MASK service)
{
tSDP_DISC_REC *p_rec = NULL;
tSDP_DISC_ATTR *p_attr;
tSDP_PROTOCOL_ELEM pe;
UINT16 uuid;
BOOLEAN result = FALSE;
if (service & BTA_HFP_SERVICE_MASK) {
uuid = UUID_SERVCLASS_HF_HANDSFREE;
p_scb->peer_version = HFP_VERSION_1_1; /* Default version */
} else if (service & BTA_HSP_SERVICE_MASK && p_scb->role == BTA_AG_INT) {
uuid = UUID_SERVCLASS_HEADSET_HS;
p_scb->peer_version = 0x0100; /* Default version */
} else {
return result;
}
/* loop through all records we found */
while (TRUE)
{
/* get next record; if none found, we're done */
if ((p_rec = SDP_FindServiceInDb(p_scb->p_disc_db, uuid, p_rec)) == NULL) {
if (uuid == UUID_SERVCLASS_HEADSET_HS) {
/* Search again in case the peer device is HSP v1.0 */
uuid = UUID_SERVCLASS_HEADSET;
if ((p_rec = SDP_FindServiceInDb(p_scb->p_disc_db, uuid, p_rec)) == NULL) {
break;
}
} else {
break;
}
}
/* get scn from proto desc list if initiator */
if (p_scb->role == BTA_AG_INT) {
if (SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_RFCOMM, &pe)) {
p_scb->peer_scn = (UINT8) pe.params[0];
} else {
continue;
}
}
/* get profile version (if failure, version parameter is not updated) */
SDP_FindProfileVersionInRec(p_rec, uuid, &p_scb->peer_version);
/* get features if HFP */
if (service & BTA_HFP_SERVICE_MASK) {
if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SUPPORTED_FEATURES)) != NULL) {
/* Found attribute. Get value. */
/* There might be race condition between SDP and BRSF. */
/* Do not update if we already received BRSF. */
if (p_scb->peer_features == 0)
p_scb->peer_features = p_attr->attr_value.v.u16;
}
} else {
/* HSP */
if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_REMOTE_AUDIO_VOLUME_CONTROL)) != NULL) {
/* Remote volume control of HSP */
if (p_attr->attr_value.v.u8) {
p_scb->peer_features |= BTA_AG_PEER_FEAT_VOL;
} else {
p_scb->peer_features &= ~BTA_AG_PEER_FEAT_VOL;
}
}
}
/* found what we needed */
result = TRUE;
break;
}
return result;
}
/*******************************************************************************
**
** Function bta_ag_do_disc
**
** Description Do service discovery.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_do_disc(tBTA_AG_SCB *p_scb, tBTA_SERVICE_MASK service)
{
tSDP_UUID uuid_list[2];
UINT16 num_uuid = 1;
UINT16 attr_list[4];
UINT8 num_attr;
BOOLEAN db_inited = FALSE;
/* HFP initiator; get proto list and features */
if (service & BTA_HFP_SERVICE_MASK && p_scb->role == BTA_AG_INT) {
attr_list[0] = ATTR_ID_SERVICE_CLASS_ID_LIST;
attr_list[1] = ATTR_ID_PROTOCOL_DESC_LIST;
attr_list[2] = ATTR_ID_BT_PROFILE_DESC_LIST;
attr_list[3] = ATTR_ID_SUPPORTED_FEATURES;
num_attr = 4;
uuid_list[0].uu.uuid16 = UUID_SERVCLASS_HF_HANDSFREE;
} else if (service & BTA_HFP_SERVICE_MASK && p_scb->role == BTA_AG_ACP) {
/* HFP acceptor; get features */
attr_list[0] = ATTR_ID_SERVICE_CLASS_ID_LIST;
attr_list[1] = ATTR_ID_BT_PROFILE_DESC_LIST;
attr_list[2] = ATTR_ID_SUPPORTED_FEATURES;
num_attr = 3;
uuid_list[0].uu.uuid16 = UUID_SERVCLASS_HF_HANDSFREE;
} else if (service & BTA_HSP_SERVICE_MASK && p_scb->role == BTA_AG_INT) {
/* HSP initiator; get proto list */
attr_list[0] = ATTR_ID_SERVICE_CLASS_ID_LIST;
attr_list[1] = ATTR_ID_PROTOCOL_DESC_LIST;
attr_list[2] = ATTR_ID_BT_PROFILE_DESC_LIST;
attr_list[3] = ATTR_ID_REMOTE_AUDIO_VOLUME_CONTROL;
num_attr = 4;
uuid_list[0].uu.uuid16 = UUID_SERVCLASS_HEADSET; /* Legacy from HSP v1.0 */
if (p_scb->hsp_version >= HSP_VERSION_1_2) {
uuid_list[1].uu.uuid16 = UUID_SERVCLASS_HEADSET_HS;
num_uuid = 2;
}
} else {
/* HSP acceptor; no discovery */
return;
}
/* allocate buffer for sdp database */
p_scb->p_disc_db = (tSDP_DISCOVERY_DB *) osi_malloc(BTA_AG_DISC_BUF_SIZE);
if(p_scb->p_disc_db) {
/* set up service discovery database; attr happens to be attr_list len */
uuid_list[0].len = LEN_UUID_16;
uuid_list[1].len = LEN_UUID_16;
db_inited = SDP_InitDiscoveryDb(p_scb->p_disc_db, BTA_AG_DISC_BUF_SIZE, num_uuid,
uuid_list, num_attr, attr_list);
}
if(db_inited) {
/*Service discovery not initiated */
db_inited = SDP_ServiceSearchAttributeRequest(p_scb->peer_addr, p_scb->p_disc_db,
bta_ag_sdp_cback_tbl[bta_ag_scb_to_idx(p_scb) - 1]);
}
if(!db_inited) {
/*free discover db */
bta_ag_free_db(p_scb, NULL);
/* sent failed event */
bta_ag_sm_execute(p_scb, BTA_AG_DISC_FAIL_EVT, NULL);
}
}
/*******************************************************************************
**
** Function bta_ag_free_db
**
** Description Free discovery database.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_free_db(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
{
UNUSED(p_data);
if (p_scb->p_disc_db != NULL) {
osi_free(p_scb->p_disc_db);
p_scb->p_disc_db = NULL;
}
}
#endif /* #if (BTA_AG_INCLUDED == TRUE) */

View file

@ -0,0 +1,126 @@
/******************************************************************************
*
* Copyright (C) 2004-2012 Broadcom Corporation
*
* 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.
*
******************************************************************************/
/******************************************************************************
*
* Interface file for BTA AG AT command interpreter.
*
******************************************************************************/
#ifndef BTA_AG_AT_H
#define BTA_AG_AT_H
#include "stack/bt_types.h"
#include "common/bt_target.h"
/*****************************************************************************
** Constants
*****************************************************************************/
#if (BTA_AG_INCLUDED == TRUE)
/* AT command argument capabilities */
#define BTA_AG_AT_NONE 0x01 /* no argument */
#define BTA_AG_AT_SET 0x02 /* set value */
#define BTA_AG_AT_READ 0x04 /* read value */
#define BTA_AG_AT_TEST 0x08 /* test value range */
#define BTA_AG_AT_FREE 0x10 /* freeform argument */
/* AT command argument format */
#define BTA_AG_AT_STR 0 /* string */
#define BTA_AG_AT_INT 1 /* integer */
/*****************************************************************************
** Data types
*****************************************************************************/
/* AT command table element */
typedef struct
{
const char *p_cmd; /* AT command string */
UINT8 arg_type; /* allowable argument type syntax */
UINT8 fmt; /* whether arg is int or string */
UINT8 min; /* minimum value for int arg */
INT16 max; /* maximum value for int arg */
} tBTA_AG_AT_CMD;
/* callback function executed when command is parsed */
typedef void (tBTA_AG_AT_CMD_CBACK)(void *p_user, UINT16 cmd, UINT8 arg_type,
char *p_arg, INT16 int_arg);
/* callback function executed to send "ERROR" result code */
typedef void (tBTA_AG_AT_ERR_CBACK)(void *p_user, BOOLEAN unknown, char *p_arg);
/* AT command parsing control block */
typedef struct
{
tBTA_AG_AT_CMD *p_at_tbl; /* AT command table */
tBTA_AG_AT_CMD_CBACK *p_cmd_cback; /* command callback */
tBTA_AG_AT_ERR_CBACK *p_err_cback; /* error callback */
void *p_user; /* user-defined data */
char *p_cmd_buf; /* temp parsing buffer */
UINT16 cmd_pos; /* position in temp buffer */
UINT16 cmd_max_len; /* length of temp buffer to allocate */
UINT8 state; /* parsing state */
} tBTA_AG_AT_CB;
/*****************************************************************************
** Function prototypes
*****************************************************************************/
/*****************************************************************************
**
** Function bta_ag_at_init
**
** Description Initialize the AT command parser control block.
**
**
** Returns void
**
*****************************************************************************/
extern void bta_ag_at_init(tBTA_AG_AT_CB *p_cb);
/*****************************************************************************
**
** Function bta_ag_at_reinit
**
** Description Re-initialize the AT command parser control block. This
** function resets the AT command parser state and frees
** any GKI buffer.
**
**
** Returns void
**
*****************************************************************************/
extern void bta_ag_at_reinit(tBTA_AG_AT_CB *p_cb);
/*****************************************************************************
**
** Function bta_ag_at_parse
**
** Description Parse AT commands. This function will take the input
** character string and parse it for AT commands according to
** the AT command table passed in the control block.
**
**
** Returns void
**
*****************************************************************************/
extern void bta_ag_at_parse(tBTA_AG_AT_CB *p_cb, char *p_buf, UINT16 len);
#endif /* #if (BTA_AG_INCLUDED == TRUE) */
#endif /* BTA_AG_AT_H */

View file

@ -0,0 +1,446 @@
/******************************************************************************
*
* Copyright (C) 2003-2012 Broadcom Corporation
*
* 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.
*
******************************************************************************/
/******************************************************************************
*
* This is the private interface file for the BTA audio gateway.
*
******************************************************************************/
#ifndef BTA_AG_INT_H
#define BTA_AG_INT_H
#include "bta_ag_at.h"
#include "bta/bta_sys.h"
#include "bta/bta_api.h"
#include "bta/bta_ag_api.h"
#include "stack/sdp_api.h"
#if (BTA_AG_INCLUDED == TRUE)
/* Send RING & CLIP in one AT cmd */
#ifndef BTA_AG_MULTI_RESULT_INCLUDED
#define BTA_AG_MULTI_RESULT_INCLUDED FALSE
#endif
/* Replace : in VGS and VGM for HSP */
#ifndef BTA_HSP_RESULT_REPLACE_COLON
#define BTA_HSP_RESULT_REPLACE_COLON TRUE
#endif
/*****************************************************************************
** Constants
*****************************************************************************/
#define HFP_VERSION_1_1 0x0101
#define HFP_VERSION_1_5 0x0105
#define HFP_VERSION_1_6 0x0106
#define HSP_VERSION_1_0 0x0100
#define HSP_VERSION_1_2 0x0102
/* Number of SCBs (AG service instances that can be registered) */
#ifndef BTA_AG_NUM_SCB
#define BTA_AG_NUM_SCB 1
#endif
/* Timer to wait for retry in case of collision */
#ifndef BTA_AG_COLLISION_TIMER
#define BTA_AG_COLLISION_TIMER 2000
#endif
/* RFCOMM MTU SIZE */
#define BTA_AG_MTU 256
/* Internal profile indexes */
#define BTA_AG_HSP 0 /* index for HSP */
#define BTA_AG_HFP 1 /* index for HFP */
#define BTA_AG_NUM_IDX 2 /* number of profile indexes */
/* profile role for connection */
#define BTA_AG_ACP 0 /* accepted connection */
#define BTA_AG_INT 1 /* initiating connection */
/* feature mask that matches spec */
#define BTA_AG_BSRF_FEAT_SPEC (BTA_AG_FEAT_3WAY | BTA_AG_FEAT_ECNR | \
BTA_AG_FEAT_VREC | BTA_AG_FEAT_INBAND | \
BTA_AG_FEAT_VTAG | BTA_AG_FEAT_REJECT | \
BTA_AG_FEAT_ECS | BTA_AG_FEAT_ECC | \
BTA_AG_FEAT_EXTERR | BTA_AG_FEAT_CODEC | \
BTA_AG_FEAT_VOIP)
#define BTA_AG_SDP_FEAT_SPEC (BTA_AG_FEAT_3WAY | BTA_AG_FEAT_ECNR | \
BTA_AG_FEAT_VREC | BTA_AG_FEAT_INBAND | \
BTA_AG_FEAT_VTAG)
enum
{
/* these events are handled by the state machine */
BTA_AG_API_REGISTER_EVT = BTA_SYS_EVT_START(BTA_ID_AG),
BTA_AG_API_DEREGISTER_EVT,
BTA_AG_API_OPEN_EVT,
BTA_AG_API_CLOSE_EVT,
BTA_AG_API_AUDIO_OPEN_EVT,
BTA_AG_API_AUDIO_CLOSE_EVT,
BTA_AG_API_RESULT_EVT,
BTA_AG_API_SETCODEC_EVT,
BTA_AG_RFC_OPEN_EVT,
BTA_AG_RFC_CLOSE_EVT,
BTA_AG_RFC_SRV_CLOSE_EVT,
BTA_AG_RFC_DATA_EVT,
BTA_AG_SCO_OPEN_EVT,
BTA_AG_SCO_CLOSE_EVT,
BTA_AG_DISC_ACP_RES_EVT,
BTA_AG_DISC_INT_RES_EVT,
BTA_AG_DISC_OK_EVT,
BTA_AG_DISC_FAIL_EVT,
BTA_AG_CI_RX_WRITE_EVT,
BTA_AG_RING_TOUT_EVT,
BTA_AG_SVC_TOUT_EVT,
#if (BTM_SCO_HCI_INCLUDED == TRUE )
BTA_AG_CI_SCO_DATA_EVT,
#endif /* (BTM_SCO_HCI_INCLUDED == TRUE ) */
BTA_AG_CI_SLC_READY_EVT,
BTA_AG_MAX_EVT,
/* these events are handled outside of the state machine */
BTA_AG_API_ENABLE_EVT,
BTA_AG_API_DISABLE_EVT
};
/* Actions to perform after a SCO event */
enum
{
BTA_AG_POST_SCO_NONE, /* no action */
BTA_AG_POST_SCO_CLOSE_RFC, /* close RFCOMM channel after SCO closes */
BTA_AG_POST_SCO_RING, /* send RING result code after SCO opens */
BTA_AG_POST_SCO_CALL_CONN, /* send call indicators after SCO opens/closes */
BTA_AG_POST_SCO_CALL_ORIG, /* send call indicators after SCO closes */
BTA_AG_POST_SCO_CALL_END, /* send call indicators after SCO closes */
BTA_AG_POST_SCO_CALL_END_INCALL /* send call indicators for end call & incoming call after SCO closes */
};
/* sco states */
enum
{
BTA_AG_SCO_SHUTDOWN_ST, /* no sco listening, all sco connections closed */
BTA_AG_SCO_LISTEN_ST, /* sco listening */
#if (BTM_WBS_INCLUDED == TRUE )
BTA_AG_SCO_CODEC_ST, /* sco codec negotiation */
#endif
BTA_AG_SCO_OPENING_ST, /* sco connection opening */
BTA_AG_SCO_OPEN_CL_ST, /* opening sco connection being closed */
BTA_AG_SCO_OPEN_XFER_ST, /* opening sco connection being transferred */
BTA_AG_SCO_OPEN_ST, /* sco open */
BTA_AG_SCO_CLOSING_ST, /* sco closing */
BTA_AG_SCO_CLOSE_OP_ST, /* closing sco being opened */
BTA_AG_SCO_CLOSE_XFER_ST, /* closing sco being transferred */
BTA_AG_SCO_SHUTTING_ST /* sco shutting down */
};
/*****************************************************************************
** Data types
*****************************************************************************/
/* data type for BTA_AG_API_ENABLE_EVT */
typedef struct
{
BT_HDR hdr;
tBTA_AG_PARSE_MODE parse_mode;
tBTA_AG_CBACK *p_cback;
} tBTA_AG_API_ENABLE;
/* data type for BTA_AG_API_REGISTER_EVT */
typedef struct
{
BT_HDR hdr;
char p_name[2][BTA_SERVICE_NAME_LEN+1];
tBTA_SERVICE_MASK services;
tBTA_SEC sec_mask;
tBTA_AG_FEAT features;
UINT8 app_id;
} tBTA_AG_API_REGISTER;
/* data type for BTA_AG_API_OPEN_EVT */
typedef struct
{
BT_HDR hdr;
BD_ADDR bd_addr;
tBTA_SERVICE_MASK services;
tBTA_SEC sec_mask;
} tBTA_AG_API_OPEN;
/* data type for BTA_AG_API_RESULT_EVT */
typedef struct
{
BT_HDR hdr;
tBTA_AG_RES result;
tBTA_AG_RES_DATA data;
} tBTA_AG_API_RESULT;
/* data type for BTA_AG_API_SETCODEC_EVT */
typedef struct
{
BT_HDR hdr;
tBTA_AG_PEER_CODEC codec;
} tBTA_AG_API_SETCODEC;
/* data type for BTA_AG_DISC_RESULT_EVT */
typedef struct
{
BT_HDR hdr;
UINT16 status;
} tBTA_AG_DISC_RESULT;
/* data type for RFCOMM events */
typedef struct
{
BT_HDR hdr;
UINT16 port_handle;
} tBTA_AG_RFC;
/* data type for BTA_AG_CI_RX_WRITE_EVT */
typedef struct
{
BT_HDR hdr;
char p_data[BTA_AG_MTU+1];
} tBTA_AG_CI_RX_WRITE;
/* union of all event datatypes */
typedef union
{
BT_HDR hdr;
tBTA_AG_API_ENABLE api_enable;
tBTA_AG_API_REGISTER api_register;
tBTA_AG_API_OPEN api_open;
tBTA_AG_API_RESULT api_result;
#if (BTM_WBS_INCLUDED == TRUE )
tBTA_AG_API_SETCODEC api_setcodec;
#endif
tBTA_AG_DISC_RESULT disc_result;
tBTA_AG_RFC rfc;
tBTA_AG_CI_RX_WRITE ci_rx_write;
} tBTA_AG_DATA;
/* type for each profile */
typedef struct
{
UINT32 sdp_handle;
UINT8 scn;
} tBTA_AG_PROFILE;
#if (BTM_WBS_INCLUDED == TRUE)
typedef enum
{
BTA_AG_SCO_MSBC_SETTINGS_T2 = 0, /* preferred/default when codec is mSBC */
BTA_AG_SCO_MSBC_SETTINGS_T1,
} tBTA_AG_SCO_MSBC_SETTINGS;
#endif
/* type for each service control block */
typedef struct
{
char clip[BTA_AG_AT_MAX_LEN+10]; /* number string used for CLIP */
UINT16 serv_handle[BTA_AG_NUM_IDX]; /* RFCOMM server handles */
tBTA_AG_AT_CB at_cb; /* AT command interpreter */
TIMER_LIST_ENT act_timer; /* ring timer */
BD_ADDR peer_addr; /* peer bd address */
tSDP_DISCOVERY_DB *p_disc_db; /* pointer to discovery database */
tBTA_SERVICE_MASK reg_services; /* services specified in register API */
tBTA_SERVICE_MASK open_services; /* services specified in open API */
UINT16 conn_handle; /* RFCOMM handle of connected service */
tBTA_SEC serv_sec_mask; /* server security mask */
tBTA_SEC cli_sec_mask; /* client security mask */
tBTA_AG_FEAT features; /* features registered by application */
tBTA_AG_PEER_FEAT peer_features; /* peer device features */
UINT16 peer_version; /* profile version of peer device */
UINT16 hsp_version; /* HSP profile version */
#if (BTM_WBS_INCLUDED == TRUE)
tBTA_AG_PEER_CODEC peer_codecs; /* codecs for eSCO supported by the peer */
tBTA_AG_PEER_CODEC sco_codec; /* codec to be used for eSCO connection */
tBTA_AG_PEER_CODEC inuse_codec; /* codec being used for the current SCO connection */
BOOLEAN codec_updated; /* set to TRUE whenever the app updates codec type */
BOOLEAN codec_fallback; /* If sco nego fails for mSBC, fallback to CVSD */
tBTA_AG_SCO_MSBC_SETTINGS codec_msbc_settings; /* settings to be used for the impending eSCO */
TIMER_LIST_ENT cn_timer; /* codec negotiation timer */
#endif
UINT16 sco_idx; /* SCO handle */
BOOLEAN in_use; /* scb in use */
BOOLEAN dealloc; /* TRUE if service shutting down */
BOOLEAN clip_enabled; /* set to TRUE if HF enables CLIP reporting */
BOOLEAN ccwa_enabled; /* set to TRUE if HF enables CCWA reporting */
BOOLEAN cmer_enabled; /* set to TRUE if HF enables CMER reporting */
BOOLEAN cmee_enabled; /* set to TRUE if HF enables CME ERROR reporting */
BOOLEAN inband_enabled; /* set to TRUE if inband ring enabled */
BOOLEAN svc_conn; /* set to TRUE when service level connection up */
TIMER_LIST_ENT colli_timer; /* Collision timer */
BOOLEAN colli_tmr_on; /* TRUE if collision timer is active */
UINT8 state; /* state machine state */
UINT8 conn_service; /* connected service */
UINT8 peer_scn; /* peer scn */
UINT8 app_id; /* application id */
UINT8 role; /* initiator/acceptor role */
tBTM_SCO_CODEC_TYPE negotiated_codec; /* negotiated codec */
UINT8 post_sco; /* action to perform after sco event */
UINT8 call_ind; /* CIEV call indicator value */
UINT8 callsetup_ind; /* CIEV callsetup indicator value */
UINT8 service_ind; /* CIEV service indicator value */
UINT8 signal_ind; /* CIEV signal indicator value */
UINT8 roam_ind; /* CIEV roam indicator value */
UINT8 battchg_ind; /* CIEV battery charge indicator value */
UINT8 callheld_ind; /* CIEV call held indicator value */
BOOLEAN retry_with_sco_only; /* indicator to try with SCO only when eSCO fails */
UINT32 bia_masked_out; /* indicators HF does not want us to send */
/* add */
UINT16 in_pkt_len;
UINT16 out_pkt_len;
UINT8 link_type; /* BTM_LINK_TYPE_SCO or BTM_LINK_TYPE_ESCO */
UINT8 tx_interval;
UINT8 retrans_window;
UINT8 air_mode;
} tBTA_AG_SCB;
/* type for sco data */
typedef struct
{
tBTM_ESCO_CONN_REQ_EVT_DATA conn_data; /* CO data for pending conn requestS */
tBTA_AG_SCB *p_curr_scb; /* SCB associated with SCO connection */
tBTA_AG_SCB *p_xfer_scb; /* SCB associated with SCO transfer */
UINT16 cur_idx; /* SCO handle */
UINT8 state; /* SCO state variable */
BOOLEAN param_updated; /* if params were updated to non-default */
tBTM_ESCO_PARAMS params; /* ESCO parameters */
tBTA_AG_DATA *p_data;
} tBTA_AG_SCO_CB;
/* type for AG control block */
typedef struct
{
tBTA_AG_SCB scb[BTA_AG_NUM_SCB]; /* service control blocks */
tBTA_AG_PROFILE profile[BTA_AG_NUM_IDX]; /* profile-specific data */
tBTA_AG_SCO_CB sco; /* SCO data */
tBTA_AG_CBACK *p_cback; /* application callback */
tBTA_AG_PARSE_MODE parse_mode; /* parse/pass-through mode */
BOOLEAN msbc_enabled;
} tBTA_AG_CB;
/*****************************************************************************
** Global data
*****************************************************************************/
/* constant lookup tables */
extern const UINT16 bta_ag_uuid[BTA_AG_NUM_IDX];
extern const UINT8 bta_ag_sec_id[BTA_AG_NUM_IDX];
extern const tBTA_AG_AT_CMD *bta_ag_at_tbl[BTA_AG_NUM_IDX];
/* control block declaration */
#if BTA_DYNAMIC_MEMORY == FALSE
extern tBTA_AG_CB bta_ag_cb;
#else
extern tBTA_AG_CB *bta_ag_cb_ptr;
#define bta_ag_cb (*bta_ag_cb_ptr)
#endif
/* config struct */
extern tBTA_AG_CFG *p_bta_ag_cfg;
/*****************************************************************************
** Function prototypes
*****************************************************************************/
/* main functions */
extern void bta_ag_scb_dealloc(tBTA_AG_SCB *p_scb);
extern UINT16 bta_ag_scb_to_idx(tBTA_AG_SCB *p_scb);
extern tBTA_AG_SCB *bta_ag_scb_by_idx(UINT16 idx);
extern UINT8 bta_ag_service_to_idx(tBTA_SERVICE_MASK services);
extern UINT16 bta_ag_idx_by_bdaddr(BD_ADDR peer_addr);
extern BOOLEAN bta_ag_other_scb_open(tBTA_AG_SCB *p_curr_scb);
extern BOOLEAN bta_ag_scb_open(tBTA_AG_SCB *p_curr_scb);
extern tBTA_AG_SCB *bta_ag_get_other_idle_scb (tBTA_AG_SCB *p_curr_scb);
extern void bta_ag_sm_execute(tBTA_AG_SCB *p_scb, UINT16 event, tBTA_AG_DATA *p_data);
extern BOOLEAN bta_ag_hdl_event(BT_HDR *p_msg);
extern void bta_ag_collision_cback(tBTA_SYS_CONN_STATUS status, UINT8 id, UINT8 app_id, BD_ADDR peer_addr);
extern void bta_ag_resume_open(tBTA_AG_SCB *p_scb);
/* SDP functions */
extern BOOLEAN bta_ag_add_record(UINT16 service_uuid, char *p_service_name, UINT8 scn, tBTA_AG_FEAT features, UINT32 sdp_handle);
extern void bta_ag_create_records(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_del_records(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern BOOLEAN bta_ag_sdp_find_attr(tBTA_AG_SCB *p_scb, tBTA_SERVICE_MASK service);
extern void bta_ag_do_disc(tBTA_AG_SCB *p_scb, tBTA_SERVICE_MASK service);
extern void bta_ag_free_db(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
/* RFCOMM functions */
extern void bta_ag_start_servers(tBTA_AG_SCB *p_scb, tBTA_SERVICE_MASK services);
extern void bta_ag_close_servers(tBTA_AG_SCB *p_scb, tBTA_SERVICE_MASK services);
extern BOOLEAN bta_ag_is_server_closed (tBTA_AG_SCB *p_scb);
extern void bta_ag_rfc_do_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_rfc_do_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
/* SCO functions */
extern BOOLEAN bta_ag_sco_is_open(tBTA_AG_SCB *p_scb);
extern BOOLEAN bta_ag_sco_is_opening(tBTA_AG_SCB *p_scb);
extern void bta_ag_sco_conn_rsp(tBTA_AG_SCB *p_scb, tBTM_ESCO_CONN_REQ_EVT_DATA *p_data);
/* AT command functions */
extern void bta_ag_at_hsp_cback(tBTA_AG_SCB *p_scb, UINT16 cmd, UINT8 arg_type, char *p_arg, INT16 int_arg);
extern void bta_ag_at_hfp_cback(tBTA_AG_SCB *p_scb, UINT16 cmd, UINT8 arg_type, char *p_arg, INT16 int_arg);
extern void bta_ag_at_err_cback(tBTA_AG_SCB *p_scb, BOOLEAN unknown, char *p_arg);
extern BOOLEAN bta_ag_inband_enabled(tBTA_AG_SCB *p_scb);
extern void bta_ag_send_call_inds(tBTA_AG_SCB *p_scb, tBTA_AG_RES result);
/* Action functions */
extern void bta_ag_register(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_deregister(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_start_dereg(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_start_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_start_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_disc_int_res(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_disc_acp_res(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_disc_fail(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_open_fail(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_rfc_fail(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_rfc_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_rfc_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_rfc_acp_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_rfc_data(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_sco_listen(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_sco_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_sco_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
#if (BTM_WBS_INCLUDED == TRUE)
extern void bta_ag_sco_codec_nego(tBTA_AG_SCB *p_scb, BOOLEAN result);
extern void bta_ag_codec_negotiate (tBTA_AG_SCB *p_scb);
#endif
extern void bta_ag_sco_shutdown(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_sco_conn_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_sco_conn_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_post_sco_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_post_sco_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_svc_conn_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_result(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_setcodec(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
#if (BTM_WBS_INCLUDED == TRUE)
extern void bta_ag_send_bcs(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
#endif
extern void bta_ag_send_ring(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_ci_sco_data(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_set_esco_param(BOOLEAN set_reset, tBTM_ESCO_PARAMS *param);
extern void bta_ag_ci_rx_data(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
extern void bta_ag_rcvd_slc_ready(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data);
#endif /* #if (BTA_AG_INCLUDED == TRUE) */
#endif /* BTA_AG_INT_H */

View file

@ -0,0 +1,594 @@
/******************************************************************************
*
* Copyright (C) 2003-2012 Broadcom Corporation
*
* 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.
*
******************************************************************************/
/******************************************************************************
*
* This is the public interface file for the audio gateway (AG) subsystem
* of BTA, Broadcom's Bluetooth application layer for mobile phones.
*
******************************************************************************/
#ifndef BTA_AG_API_H
#define BTA_AG_API_H
#include "bta_api.h"
#include "bta_hfp_defs.h"
#include "esp_hf_defs.h"
#if (BTA_AG_INCLUDED == TRUE)
/*****************************************************************************
** Constants and data types
*****************************************************************************/
/* AG feature masks */
#define BTA_AG_FEAT_3WAY 0x00000001 /* Three-way calling */
#define BTA_AG_FEAT_ECNR 0x00000002 /* Echo cancellation and/or noise reduction */
#define BTA_AG_FEAT_VREC 0x00000004 /* Voice recognition */
#define BTA_AG_FEAT_INBAND 0x00000008 /* In-band ring tone */
#define BTA_AG_FEAT_VTAG 0x00000010 /* Attach a phone number to a voice tag */
#define BTA_AG_FEAT_REJECT 0x00000020 /* Ability to reject incoming call */
#define BTA_AG_FEAT_ECS 0x00000040 /* Enhanced Call Status */
#define BTA_AG_FEAT_ECC 0x00000080 /* Enhanced Call Control */
#define BTA_AG_FEAT_EXTERR 0x00000100 /* Extended error codes */
#define BTA_AG_FEAT_CODEC 0x00000200 /* Codec Negotiation */
#define BTA_AG_FEAT_VOIP 0x00000400 /* VoIP call */
/* Proprietary features: using 31 ~ 16 bits */
#define BTA_AG_FEAT_BTRH 0x00010000 /* CCAP incoming call hold */
#define BTA_AG_FEAT_UNAT 0x00020000 /* Pass unknown AT commands to application */
#define BTA_AG_FEAT_NOSCO 0x00040000 /* No SCO control performed by BTA AG */
#define BTA_AG_FEAT_NO_ESCO 0x00080000 /* Do not allow or use eSCO */
typedef UINT32 tBTA_AG_FEAT;
/* HFP peer features */
#define BTA_AG_PEER_FEAT_ECNR 0x0001 /* Echo cancellation and/or noise reduction */
#define BTA_AG_PEER_FEAT_3WAY 0x0002 /* Call waiting and three-way calling */
#define BTA_AG_PEER_FEAT_CLI 0x0004 /* Caller ID presentation capability */
#define BTA_AG_PEER_FEAT_VREC 0x0008 /* Voice recognition activation */
#define BTA_AG_PEER_FEAT_VOL 0x0010 /* Remote volume control */
#define BTA_AG_PEER_FEAT_ECS 0x0020 /* Enhanced Call Status */
#define BTA_AG_PEER_FEAT_ECC 0x0040 /* Enhanced Call Control */
#define BTA_AG_PEER_FEAT_CODEC 0x0080 /* Codec Negotiation */
#define BTA_AG_PEER_FEAT_VOIP 0x0100 /* VoIP call */
typedef UINT16 tBTA_AG_PEER_FEAT;
/* AG extended call handling - masks not related to any spec */
#define BTA_AG_CLIENT_CHLD_REL 0x00000001 /* 0 Release waiting call or held calls */
#define BTA_AG_CLIENT_CHLD_REL_ACC 0x00000002 /* 1 Release active calls and accept other (waiting or held) cal */
#define BTA_AG_CLIENT_CHLD_REL_X 0x00000004 /* 1x Release x call*/
#define BTA_AG_CLIENT_CHLD_HOLD_ACC 0x00000008 /* 2 Active calls on hold and accept other call */
#define BTA_AG_CLIENT_CHLD_PRIV_X 0x00000010 /* 2x Active multiparty call on hold except call x */
#define BTA_AG_CLIENT_CHLD_MERGE 0x00000020 /* 3 Add held call to multiparty */
#define BTA_AG_CLIENT_CHLD_MERGE_DETACH 0x00000040 /* 4 Add held call to multiparty */
typedef UINT16 tBTA_AG_CHLD_FEAT;
/* HFP peer supported codec masks */
// TODO(google) This should use common definitions
// in hci/include/hci_audio.h
#define BTA_AG_CODEC_NONE BTM_SCO_CODEC_NONE
#define BTA_AG_CODEC_CVSD BTM_SCO_CODEC_CVSD /* CVSD */
#define BTA_AG_CODEC_MSBC BTM_SCO_CODEC_MSBC /* mSBC */
typedef UINT16 tBTA_AG_PEER_CODEC;
/* AG parse mode */
#define BTA_AG_PARSE 0 /* Perform AT command parsing in AG */
#define BTA_AG_PASS_THROUGH 1 /* Pass data directly to phones AT command interpreter */
typedef UINT8 tBTA_AG_PARSE_MODE;
/* AG open status */
#define BTA_AG_SUCCESS 0 /* Connection successfully opened */
#define BTA_AG_FAIL_SDP 1 /* Open failed due to SDP */
#define BTA_AG_FAIL_RFCOMM 2 /* Open failed due to RFCOMM */
#define BTA_AG_FAIL_RESOURCES 3 /* out of resources failure */
typedef UINT8 tBTA_AG_STATUS;
/* handle values used with BTA_AgResult */
#define BTA_AG_HANDLE_NONE 0
#define BTA_AG_HANDLE_ALL 0xFFFF
/* It is safe to use the same value as BTA_AG_HANDLE_ALL
* HANDLE_ALL is used for delivering indication
* SCO_NO_CHANGE is used for changing sco behavior
* They donot interfere with each other
*/
#define BTA_AG_HANDLE_SCO_NO_CHANGE 0xFFFF
/* AG result codes used with BTA_AgResult */
#define BTA_AG_SPK_RES 0 /* Update speaker volume */
#define BTA_AG_MIC_RES 1 /* Update microphone volume */
#define BTA_AG_INBAND_RING_RES 2 /* Update inband ring state AT+BSIR */
#define BTA_AG_CIND_RES 3 /* Send indicator response for AT+CIND */
#define BTA_AG_BINP_RES 4 /* Send phone number for voice tag for AT+BINP */
#define BTA_AG_IND_RES 5 /* Update an indicator value +CIEV<...> */
#define BTA_AG_BVRA_RES 6 /* Update voice recognition state for AT+BVRA */
#define BTA_AG_CNUM_RES 7 /* Send subscriber number response for AT+CNUM */
#define BTA_AG_BTRH_RES 8 /* Send CCAP incoming call hold */
#define BTA_AG_CLCC_RES 9 /* Query list of calls AT+CLCC */
#define BTA_AG_COPS_RES 10 /* Read network operator for AT+COPS */
#define BTA_AG_IN_CALL_RES 11 /* Indicate incoming phone call */
#define BTA_AG_IN_CALL_CONN_RES 12 /* Incoming phone call connected*/
#define BTA_AG_CALL_WAIT_RES 13 /* Call waiting notification for AT+CCWA */
#define BTA_AG_OUT_CALL_ORIG_RES 14 /* Outgoing phone call origination AT+ATD*/
#define BTA_AG_OUT_CALL_ALERT_RES 15 /* Outgoing phone call alerting remote party */
#define BTA_AG_OUT_CALL_CONN_RES 16 /* Outgoing phone call connected */
#define BTA_AG_CALL_CANCEL_RES 17 /* Incoming/outgoing 3-way canceled before connected */
#define BTA_AG_END_CALL_RES 18 /* End call AT+CHUP */
#define BTA_AG_IN_CALL_HELD_RES 19 /* Incoming call held AT+CHLD */
#define BTA_AG_UNAT_RES 20 /* Response to unknown AT command event AT+UNAT */
#define BTA_AG_MULTI_CALL_RES 21 /* SLC at three way call */
typedef UINT8 tBTA_AG_RES;
/* AG callback events */
#define BTA_AG_ENABLE_EVT 0 /* AG enabled */
#define BTA_AG_REGISTER_EVT 1 /* AG registered */
#define BTA_AG_OPEN_EVT 2 /* AG connection open */
#define BTA_AG_CLOSE_EVT 3 /* AG connection closed */
#define BTA_AG_CONN_EVT 4 /* Service level connection opened */
#define BTA_AG_AUDIO_OPEN_EVT 5 /* Audio connection open */
#define BTA_AG_AUDIO_CLOSE_EVT 6 /* Audio connection closed */
#define BTA_AG_SPK_EVT 7 /* Speaker volume changed */
#define BTA_AG_MIC_EVT 8 /* Microphone volume changed */
#define BTA_AG_AT_CKPD_EVT 9 /* CKPD from the HS */
#define BTA_AG_DISABLE_EVT 30 /* AG disabled */
#if (BTM_WBS_INCLUDED == TRUE )
#define BTA_AG_WBS_EVT 31 /* SCO codec nego */
#endif
/* Values below are for HFP only */
#define BTA_AG_AT_A_EVT 10 /* Answer a incoming call */
#define BTA_AG_AT_D_EVT 11 /* Place a call using number or memory dial */
#define BTA_AG_AT_CHLD_EVT 12 /* Call hold */
#define BTA_AG_AT_CHUP_EVT 13 /* Hang up a call */
#define BTA_AG_AT_CIND_EVT 14 /* Read indicator settings */
#define BTA_AG_AT_VTS_EVT 15 /* Transmit DTMF tone */
#define BTA_AG_AT_BINP_EVT 16 /* Retrieve number from voice tag */
#define BTA_AG_AT_BLDN_EVT 17 /* Place call to last dialed number */
#define BTA_AG_AT_BVRA_EVT 18 /* Enable/disable voice recognition */
#define BTA_AG_AT_NREC_EVT 19 /* Disable echo canceling */
#define BTA_AG_AT_CNUM_EVT 20 /* Retrieve subscriber number */
#define BTA_AG_AT_BTRH_EVT 21 /* CCAP-style incoming call hold */
#define BTA_AG_AT_CLCC_EVT 22 /* Query list of current calls */
#define BTA_AG_AT_COPS_EVT 23 /* Query Current Operator Name on AG */
#define BTA_AG_AT_UNAT_EVT 24 /* Unknown AT command */
#define BTA_AG_AT_CBC_EVT 25 /* Indicator Update */
#define BTA_AG_AT_BAC_EVT 26 /* avablable codec */
#define BTA_AG_AT_BCS_EVT 27 /* Codec select */
typedef UINT8 tBTA_AG_EVT;
/* HFP errcode - Set when BTA_AG_OK_ERROR is returned in 'ok_flag' */
#define BTA_AG_ERR_PHONE_FAILURE 0 /* Phone Failure */
#define BTA_AG_ERR_NO_CONN_PHONE 1 /* No connection to phone */
#define BTA_AG_ERR_OP_NOT_ALLOWED 3 /* Operation not allowed */
#define BTA_AG_ERR_OP_NOT_SUPPORTED 4 /* Operation not supported */
#define BTA_AG_ERR_PHSIM_PIN_REQ 5 /* PH-SIM PIN required */
#define BTA_AG_ERR_SIM_NOT_INSERTED 10 /* SIM not inserted */
#define BTA_AG_ERR_SIM_PIN_REQ 11 /* SIM PIN required */
#define BTA_AG_ERR_SIM_PUK_REQ 12 /* SIM PUK required */
#define BTA_AG_ERR_SIM_FAILURE 13 /* SIM failure */
#define BTA_AG_ERR_SIM_BUSY 14 /* SIM busy */
#define BTA_AG_ERR_INCORRECT_PWD 16 /* Incorrect password */
#define BTA_AG_ERR_SIM_PIN2_REQ 17 /* SIM PIN2 required */
#define BTA_AG_ERR_SIM_PUK2_REQ 18 /* SIM PUK2 required */
#define BTA_AG_ERR_MEMORY_FULL 20 /* Memory full */
#define BTA_AG_ERR_INVALID_INDEX 21 /* Invalid index */
#define BTA_AG_ERR_MEMORY_FAILURE 23 /* Memory failure */
#define BTA_AG_ERR_TEXT_TOO_LONG 24 /* Text string too long */
#define BTA_AG_ERR_INV_CHAR_IN_TSTR 25 /* Invalid characters in text string */
#define BTA_AG_ERR_DSTR_TOO_LONG 26 /* Dial string too long */
#define BTA_AG_ERR_INV_CHAR_IN_DSTR 27 /* Invalid characters in dial string */
#define BTA_AG_ERR_NO_NETWORK_SERV 30 /* No network service */
#define BTA_AG_ERR_NETWORK_TIME_OUT 31 /* Network timeout */
#define BTA_AG_ERR_NO_NET_EMG_ONLY 32 /* Network not allowed - emergency service only */
#define BTA_AG_ERR_VOIP_CS_CALLS 33 /* AG cannot create simultaneous VoIP and CS calls */
#define BTA_AG_ERR_NOT_FOR_VOIP 34 /* Not supported on this call type(VoIP) */
#define BTA_AG_ERR_SIP_RESP_CODE 35 /* SIP 3 digit response code */
typedef UINT8 tBTA_AG_ERR_TYPE;
#if 0 /* Not Used in Bluetooth HFP 1.5 Specification */
#define BTA_AG_ERR_PHADAP_LNK_RES 2 /* Phone-adapter link reserved */
#define BTA_AG_ERR_PHFSIM_PIN_REQ 6 /* PH-FSIM PIN required */
#define BTA_AG_ERR_PHFSIM_PUK_REQ 7 /* PH-FSIM PUK required */
#define BTA_AG_ERR_SIM_WRONG 15 /* SIM wrong */
#define BTA_AG_ERR_NOT_FOUND 22 /* Not found */
#define BTA_AG_ERR_NETWORK_TIMEOUT 31 /* Network timeout */
#define BTA_AG_ERR_NET_PIN_REQ 40 /* Network personalization PIN required */
#define BTA_AG_ERR_NET_PUK_REQ 41 /* Network personalization PUK required */
#define BTA_AG_ERR_SUBSET_PIN_REQ 42 /* Network subset personalization PIN required */
#define BTA_AG_ERR_SUBSET_PUK_REQ 43 /* Network subset personalization PUK required */
#define BTA_AG_ERR_SERVPRO_PIN_REQ 44 /* Service provider personalization PIN required */
#define BTA_AG_ERR_SERVPRO_PUK_REQ 45 /* Service provider personalization PUK required */
#define BTA_AG_ERR_CORP_PIN_REQ 46 /* Corporate personalization PIN required */
#define BTA_AG_ERR_CORP_PUK_REQ 47 /* Corporate personalization PUK required */
#define BTA_AG_ERR_UNKNOWN 100 /* Unknown error */
/* GPRS-related errors */
#define BTA_AG_ERR_ILL_MS 103 /* Illegal MS (#3) */
#define BTA_AG_ERR_ILL_ME 106 /* Illegal ME (#6) */
#define BTA_AG_ERR_GPRS_NOT_ALLOWED 107 /* GPRS services not allowed (#7) */
#define BTA_AG_ERR_PLMN_NOT_ALLOWED 111 /* PLMN services not allowed (#11) */
#define BTA_AG_ERR_LOC_NOT_ALLOWED 112 /* Location area not allowed (#12) */
#define BTA_AG_ERR_ROAM_NOT_ALLOWED 113 /* Roaming not allowed in this location area (#13) */
/* Errors related to a failure to Activate a Context */
#define BTA_AG_ERR_OPT_NOT_SUPP 132 /* Service option not supported (#32) */
#define BTA_AG_ERR_OPT_NOT_SUBSCR 133 /* Requested service option not subscribed (#33) */
#define BTA_AG_ERR_OPT_OUT_OF_ORDER 134 /* Service option temporarily out of order (#34) */
#define BTA_AG_ERR_PDP_AUTH_FAILURE 149 /* PDP authentication failure */
/* Other GPRS errors */
#define BTA_AG_ERR_INV_MOBILE_CLASS 150 /* Invalid mobile class */
#define BTA_AG_ERR_UNSPEC_GPRS_ERR 148 /* Unspecified GPRS error */
#endif /* Unused error codes */
/* HFP result data 'ok_flag' */
#define BTA_AG_OK_CONTINUE 0 /* Send out response (more responses coming) */
#define BTA_AG_OK_DONE 1 /* Send out response followed by OK (finished) */
#define BTA_AG_OK_ERROR 2 /* Error response */
typedef UINT8 tBTA_AG_AT_RESULT_TYPE;
/* BTRH values */
#define BTA_AG_BTRH_SET_HOLD 0 /* Put incoming call on hold */
#define BTA_AG_BTRH_SET_ACC 1 /* Accept incoming call on hold */
#define BTA_AG_BTRH_SET_REJ 2 /* Reject incoming call on hold */
#define BTA_AG_BTRH_READ 3 /* Read the current value */
#define BTA_AG_BTRH_NO_RESP 4 /* Not in RH States (reply to read) */
typedef UINT8 tBTA_AG_BTRH_TYPE;
/* ASCII character string of arguments to the AT command or result */
#ifndef BTA_AG_AT_MAX_LEN
#define BTA_AG_AT_MAX_LEN 256
#endif
/* indicator constants HFP 1.1 and later */
#define BTA_AG_IND_CALL 0 /* position of call indicator */
#define BTA_AG_IND_CALLSETUP 1 /* position of callsetup indicator */
#define BTA_AG_IND_SERVICE 2 /* position of service indicator */
/* indicator constants HFP 1.5 and later */
#define BTA_AG_IND_SIGNAL 3 /* position of signal strength indicator */
#define BTA_AG_IND_ROAM 4 /* position of roaming indicator */
#define BTA_AG_IND_BATTCHG 5 /* position of battery charge indicator */
#define BTA_AG_IND_CALLHELD 6 /* position of callheld indicator */
#define BTA_AG_IND_BEARER 7 /* position of bearer indicator */
typedef UINT16 tBTA_AG_IND_TYPE;
/* call indicator values */
#define BTA_AG_CALL_INACTIVE 0 /* Phone call inactive */
#define BTA_AG_CALL_ACTIVE 1 /* Phone call active */
/* callsetup indicator values */
#define BTA_AG_CALLSETUP_NONE 0 /* Not currently in call set up */
#define BTA_AG_CALLSETUP_INCOMING 1 /* Incoming call process ongoing */
#define BTA_AG_CALLSETUP_OUTGOING 2 /* Outgoing call set up is ongoing */
#define BTA_AG_CALLSETUP_ALERTING 3 /* Remote party being alerted in an outgoing call */
/* service indicator values */
#define BTA_AG_SERVICE_NONE 0 /* Neither CS nor VoIP service is available */
#define BTA_AG_SERVICE_CS 1 /* Only CS service is available */
#define BTA_AG_SERVICE_VOIP 2 /* Only VoIP service is available */
#define BTA_AG_SERVICE_CS_VOIP 3 /* Both CS and VoIP services available */
/* callheld indicator values */
#define BTA_AG_CALLHELD_INACTIVE 0 /* No held calls */
#define BTA_AG_CALLHELD_ACTIVE 1 /* Call held and call active */
#define BTA_AG_CALLHELD_NOACTIVE 2 /* Call held and no call active */
/* signal strength indicator values */
#define BTA_AG_ROAMING_INACTIVE 0 /* Phone call inactive */
#define BTA_AG_ROAMING_ACTIVE 1 /* Phone call active */
/* bearer indicator values */
#define BTA_AG_BEARER_WLAN 0 /* WLAN */
#define BTA_AG_BEARER_BLUETOOTH 1 /* Bluetooth */
#define BTA_AG_BEARER_WIRED 2 /* Wired */
#define BTA_AG_BEARER_2G3G 3 /* 2G 3G */
#define BTA_AG_BEARER_WIMAX 4 /* WIMAX */
#define BTA_AG_BEARER_RES1 5 /* Reserved */
#define BTA_AG_BEARER_RES2 6 /* Reserved */
#define BTA_AG_BEARER_RES3 7 /* Reserved */
/* data associated with BTA_AG_IND_RES */
typedef struct
{
tBTA_AG_IND_TYPE type;
UINT16 value;
} tBTA_AG_IND;
/* data type for BTA_AgResult() */
typedef struct
{
char str[BTA_AG_AT_MAX_LEN+1]; /* used for cops,clcc,cnum... */
tBTA_AG_IND ind; /* used for indicator type */
UINT16 num; /* used for codec state */
UINT16 audio_handle; /* used for audio path */
UINT16 errcode; /* Valid only if 'ok_flag' is set to BTA_AG_OK_ERROR */
UINT8 ok_flag; /* Indicates if response is finished, and if error occurred */
BOOLEAN state;
} tBTA_AG_RES_DATA;
/* data associated with most non-AT events */
typedef struct
{
UINT16 handle;
UINT8 app_id;
tBTA_AG_STATUS status;
} tBTA_AG_HDR;
/* data associated with BTA_AG_REGISTER_EVT */
typedef struct
{
tBTA_AG_HDR hdr;
UINT16 handle;
tBTA_AG_STATUS status;
} tBTA_AG_REGISTER;
/* data associated with BTA_AG_OPEN_EVT */
typedef struct
{
tBTA_AG_HDR hdr;
BD_ADDR bd_addr;
tBTA_SERVICE_ID service_id;
tBTA_AG_STATUS status;
} tBTA_AG_OPEN;
/* data associated with BTA_AG_CLOSE_EVT */
typedef struct
{
tBTA_AG_HDR hdr;
BD_ADDR bd_addr;
} tBTA_AG_CLOSE;
/* data associated with BTA_AG_CONN_EVT */
typedef struct
{
tBTA_AG_HDR hdr;
tBTA_AG_PEER_FEAT peer_feat;
BD_ADDR bd_addr;
tBTA_AG_PEER_CODEC peer_codec;
tBTA_AG_CHLD_FEAT chld_feat;
} tBTA_AG_CONN;
/* data associated with AT command event */
typedef struct
{
tBTA_AG_HDR hdr;
BD_ADDR bd_addr;
char str[BTA_AG_AT_MAX_LEN+1];
UINT16 num; /* voice recognition state*/
UINT8 idx; /* call number used by CLCC and CHLD */
UINT16 value;
} tBTA_AG_VAL;
/* data associated with BTA_AG_CLIP_EVT and BTA_AG_CCWA_EVT*/
#define BTA_AG_NUMBER_LEN 32
typedef struct {
char number[BTA_AG_NUMBER_LEN + 1];
} tBTA_AG_NUMBER;
/* data associated with BTA_HF_CLIENT_OPERATOR_NAME_EVT */
#define BTA_AG_COPS_LEN 16
typedef struct {
char name[BTA_AG_COPS_LEN + 1];
} tBTA_AG_COPS;
/* data associated with BTA_AG_AT_RESULT_EVT event */
typedef struct {
tBTA_AG_AT_RESULT_TYPE type;
UINT16 cme;
} tBTA_AG_AT_RESULT;
/* data associated with BTA_AG_CLCC_EVT event */
typedef struct {
UINT32 idx;
BOOLEAN inc;
UINT8 status;
BOOLEAN mpty;
BOOLEAN number_present;
char number[BTA_AG_NUMBER_LEN + 1];
} tBTA_AG_CLCC;
/* data associated with BTA_AG_CNUM_EVT event */
typedef struct {
UINT16 service;
char number[BTA_AG_NUMBER_LEN + 1];
} tBTA_AG_CNUM;
/* union of data associated with AG callback */
typedef union
{
tBTA_AG_HDR hdr;
tBTA_AG_REGISTER reg;
tBTA_AG_OPEN open;
tBTA_AG_CLOSE close;
tBTA_AG_CONN conn;
tBTA_AG_IND ind;
tBTA_AG_VAL val;
//add
tBTA_AG_COPS operator;
tBTA_AG_NUMBER number;
tBTA_AG_AT_RESULT result;
tBTA_AG_CLCC clcc;
tBTA_AG_CNUM cnum;
} tBTA_AG;
/* AG callback */
typedef void (tBTA_AG_CBACK)(tBTA_AG_EVT event, tBTA_AG *p_data);
/* AG configuration structure */
typedef struct
{
char *cind_info;
INT32 conn_tout;
UINT16 sco_pkt_types;
char *chld_val_ecc;
char *chld_val;
} tBTA_AG_CFG;
#ifdef __cplusplus
extern "C"
{
#endif
/*****************************************************************************
** External Function Declarations
*****************************************************************************/
/*******************************************************************************
**
** Function BTA_AgEnable
**
** Description Enable the audio gateway service. When the enable
** operation is complete the callback function will be
** called with a BTA_AG_ENABLE_EVT. This function must
** be called before other function in the AG API are
** called.
**
** Returns BTA_SUCCESS if OK, BTA_FAILURE otherwise.
**
*******************************************************************************/
tBTA_STATUS BTA_AgEnable(tBTA_AG_PARSE_MODE parse_mode, tBTA_AG_CBACK *p_cback);
/*******************************************************************************
**
** Function BTA_AgDisable
**
** Description Disable the audio gateway service
**
**
** Returns void
**
*******************************************************************************/
void BTA_AgDisable(void);
/*******************************************************************************
**
** Function BTA_AgRegister
**
** Description Register an Audio Gateway service.
**
**
** Returns void
**
*******************************************************************************/
void BTA_AgRegister(tBTA_SERVICE_MASK services, tBTA_SEC sec_mask,
tBTA_AG_FEAT features, char *p_service_names[], UINT8 app_id);
/*******************************************************************************
**
** Function BTA_AgDeregister
**
** Description Deregister an audio gateway service.
**
**
** Returns void
**
*******************************************************************************/
void BTA_AgDeregister(UINT16 handle);
/*******************************************************************************
**
** Function BTA_AgOpen
**
** Description Opens a connection to a headset or hands-free device.
** When connection is open callback function is called
** with a BTA_AG_OPEN_EVT. Only the data connection is
** opened. The audio connection is not opened.
**
**
** Returns void
**
*******************************************************************************/
void BTA_AgOpen(UINT16 handle, BD_ADDR bd_addr, tBTA_SEC sec_mask, tBTA_SERVICE_MASK services);
/*******************************************************************************
**
** Function BTA_AgClose
**
** Description Close the current connection to a headset or a handsfree
** Any current audio connection will also be closed
**
**
** Returns void
**
*******************************************************************************/
void BTA_AgClose(UINT16 handle);
/*******************************************************************************
**
** Function BTA_AgAudioOpen
**
** Description Opens an audio connection to the currently connected
** headset or hnadsfree
**
**
** Returns void
**
*******************************************************************************/
void BTA_AgAudioOpen(UINT16 handle);
/*******************************************************************************
**
** Function BTA_AgAudioClose
**
** Description Close the currently active audio connection to a headset
** or hnadsfree. The data connection remains open
**
**
** Returns void
**
*******************************************************************************/
void BTA_AgAudioClose(UINT16 handle);
/*******************************************************************************
**
** Function BTA_AgResult
**
** Description Send an AT result code to a headset or hands-free device.
** This function is only used when the AG parse mode is set
** to BTA_AG_PARSE.
**
**
** Returns void
**
*******************************************************************************/
void BTA_AgResult(UINT16 handle, tBTA_AG_RES result, tBTA_AG_RES_DATA *p_data);
/*******************************************************************************
**
** Function BTA_AgSetCodec
**
** Description Specify the codec type to be used for the subsequent
** audio connection.
**
**
**
** Returns void
**
*******************************************************************************/
void BTA_AgSetCodec(UINT16 handle, tBTA_AG_PEER_CODEC codec);
#if (BTM_SCO_HCI_INCLUDED == TRUE )
/*******************************************************************************
**
** Function BTA_AgCiData
**
** Description Give an EVT to BTA that tell outgoing data is ready.
**
**
** Returns void
**
*******************************************************************************/
void BTA_AgCiData(void);
#endif /*#if (BTM_SCO_HCI_INCLUDED == TRUE ) */
#ifdef __cplusplus
}
#endif
#endif /* #if (BTA_AG_INCLUDED == TRUE) */
#endif /* BTA_HF_API_H */

View file

@ -0,0 +1,160 @@
/******************************************************************************
*
* Copyright (C) 2003-2012 Broadcom Corporation
*
* 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.
*
******************************************************************************/
/******************************************************************************
*
* This is the interface file for audio gateway call-out and call-in functions.
*
******************************************************************************/
#ifndef BTA_AG_CIO_H
#define BTA_AG_CIO_H
#include "bta/bta_ag_api.h"
#include "hci/hci_audio.h"
#if (BTA_AG_INCLUDED == TRUE)
/*******************************************************************************
**
** Function bta_ag_sco_audio_state
**
** Description This function is called by the AG before the audio connection
** is brought up, after it comes up, and after it goes down.
**
** Parameters handle - handle of the AG instance
** state - Audio state
** codec - if WBS support is compiled in, codec to going to be used is provided
** and when in SCO_STATE_SETUP, BTM_I2SPCMConfig() must be called with
** the correct platform parameters.
** in the other states codec type should not be ignored
**
** Returns void
**
*******************************************************************************/
#if (BTM_WBS_INCLUDED == TRUE )
void bta_ag_sco_audio_state(UINT16 handle, UINT8 app_id, UINT8 state, tBTA_AG_PEER_CODEC codec);
#else
void bta_ag_sco_audio_state(UINT16 handle, UINT8 app_id, UINT8 state);
#endif
/*******************************************************************************
**
** Function bta_ag_sco_co_init
**
** Description Set default data path for SCO/eSCO.
** This callout function is executed by AG when it is
** started by calling BTA_AgEnable(). This function can be
** used by the phone to initialize audio paths or for other
** initialization purposes.
**
**
** Returns Void.
**
*******************************************************************************/
tBTA_HFP_SCO_ROUTE_TYPE bta_ag_sco_co_init(UINT32 rx_bw, UINT32 tx_bw, tBTA_HFP_CODEC_INFO *p_codec_info, UINT8 app_id);
/*******************************************************************************
**
** Function bta_ag_sco_co_open
**
** Description This function is executed by AG when a service level connection
** is opened.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_sco_co_open(UINT16 handle, tBTM_SCO_AIR_MODE_TYPE air_mode, UINT8 inout_pkt_size, UINT16 event);
/*******************************************************************************
**
** Function bta_ag_sco_co_close
**
** Description This function is called by AG when a service level
** connection is closed.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_sco_co_close(void);
/*******************************************************************************
**
** Function bta_ag_sco_co_out_data
**
** Description This function is called to send SCO data over HCI.
**
** Returns number of bytes got from application
**
*******************************************************************************/
uint32_t bta_ag_sco_co_out_data(UINT8 *p_buf);
/*******************************************************************************
**
** Function bta_hf_client_sco_co_in_data
**
** Description This function is called to send incoming SCO data to application.
**
** Returns void
**
*******************************************************************************/
void bta_ag_sco_co_in_data(BT_HDR *p_buf, tBTM_SCO_DATA_FLAG status);
/*******************************************************************************
**
** Function bta_ag_co_tx_write
**
** Description This function is called by the AG to send data to the
** phone when the AG is configured for AT command pass-through.
** The implementation of this function must copy the data to
** the phones memory.
**
** Returns void
**
*******************************************************************************/
void bta_ag_co_tx_write(UINT16 handle, UINT8 *p_data, UINT16 len);
/*******************************************************************************
**
** Function bta_ag_ci_rx_write
**
** Description This function is called to send data to the AG when the AG
** is configured for AT command pass-through. The function
** copies data to an event buffer and sends it.
**
** Returns void
**
*******************************************************************************/
extern void bta_ag_ci_rx_write(UINT16 handle, char *p_data, UINT16 len);
/******************************************************************************
**
** Function bta_ag_ci_slc_ready
**
** Description This function is called to notify AG that SLC is up at
** the application. This funcion is only used when the app
** is running in pass-through mode.
**
** Returns void
**
******************************************************************************/
extern void bta_ag_ci_slc_ready(UINT16 handle);
#endif /* #if (BTA_AG_INCLUDED == TRUE) */
#endif /* BTA_AG_CIO_H */

View file

@ -57,6 +57,9 @@ btc_dm_cb_t *btc_dm_cb_ptr;
extern bt_status_t btc_av_source_execute_service(BOOLEAN b_enable);
extern bt_status_t btc_av_sink_execute_service(BOOLEAN b_enable);
#endif
#if BTC_HF_INCLUDED
extern bt_status_t btc_hf_execute_service(BOOLEAN b_enable);
#endif
#if BTC_HF_CLIENT_INCLUDED
extern bt_status_t btc_hf_client_execute_service(BOOLEAN b_enable);
#endif
@ -510,6 +513,11 @@ static bt_status_t btc_in_execute_service_request(tBTA_SERVICE_ID service_id,
btc_av_sink_execute_service(b_enable);
break;
#endif
#if BTC_HF_INCLUDED
case BTA_HFP_SERVICE_ID:
btc_hf_execute_service(b_enable);
break;
#endif /* #if BTC_HF_INCLUDED */
#if BTC_HF_CLIENT_INCLUDED
case BTA_HFP_HS_SERVICE_ID:
btc_hf_client_execute_service(b_enable);

View file

@ -30,6 +30,11 @@
#if (BTA_AV_INCLUDED == TRUE)
#include "bta/bta_av_api.h"
#endif ///BTA_AV_INCLUDED == TRUE
#if (BTA_AG_INCLUDED == TRUE)
#include "bta/bta_ag_api.h"
#endif ///BTA_AG_INCLUDED == TRUE
#include "common/bt_defs.h"
#include "stack/btm_api.h"
#include "bta/bta_api.h"
@ -121,6 +126,89 @@ const char *dump_rc_pdu(UINT8 pdu)
}
#endif ///BTA_AV_INCLUDED == TRUE
#if (BTA_AG_INCLUDED == TRUE)
const char* dump_hf_conn_state(UINT16 event)
{
switch(event)
{
CASE_RETURN_STR(ESP_HF_CONNECTION_STATE_DISCONNECTED)
CASE_RETURN_STR(ESP_HF_CONNECTION_STATE_CONNECTING)
CASE_RETURN_STR(ESP_HF_CONNECTION_STATE_CONNECTED)
CASE_RETURN_STR(ESP_HF_CONNECTION_STATE_SLC_CONNECTED)
CASE_RETURN_STR(ESP_HF_CONNECTION_STATE_DISCONNECTING)
default:
return "UNKNOWN MSG ID";
}
}
const char* dump_hf_event(UINT16 event)
{
switch(event)
{
CASE_RETURN_STR(BTA_AG_ENABLE_EVT)
CASE_RETURN_STR(BTA_AG_REGISTER_EVT)
CASE_RETURN_STR(BTA_AG_OPEN_EVT)
CASE_RETURN_STR(BTA_AG_CLOSE_EVT)
CASE_RETURN_STR(BTA_AG_CONN_EVT)
CASE_RETURN_STR(BTA_AG_AUDIO_OPEN_EVT)
CASE_RETURN_STR(BTA_AG_AUDIO_CLOSE_EVT)
CASE_RETURN_STR(BTA_AG_SPK_EVT)
CASE_RETURN_STR(BTA_AG_MIC_EVT)
CASE_RETURN_STR(BTA_AG_AT_CKPD_EVT)
CASE_RETURN_STR(BTA_AG_DISABLE_EVT)
#if (BTM_WBS_INCLUDED == TRUE )
CASE_RETURN_STR(BTA_AG_WBS_EVT)
#endif
CASE_RETURN_STR(BTA_AG_AT_A_EVT)
CASE_RETURN_STR(BTA_AG_AT_D_EVT)
CASE_RETURN_STR(BTA_AG_AT_CHLD_EVT)
CASE_RETURN_STR(BTA_AG_AT_CHUP_EVT)
CASE_RETURN_STR(BTA_AG_AT_CIND_EVT)
CASE_RETURN_STR(BTA_AG_AT_VTS_EVT)
CASE_RETURN_STR(BTA_AG_AT_BINP_EVT)
CASE_RETURN_STR(BTA_AG_AT_BLDN_EVT)
CASE_RETURN_STR(BTA_AG_AT_BVRA_EVT)
CASE_RETURN_STR(BTA_AG_AT_NREC_EVT)
CASE_RETURN_STR(BTA_AG_AT_CNUM_EVT)
CASE_RETURN_STR(BTA_AG_AT_BTRH_EVT)
CASE_RETURN_STR(BTA_AG_AT_CLCC_EVT)
CASE_RETURN_STR(BTA_AG_AT_COPS_EVT)
CASE_RETURN_STR(BTA_AG_AT_UNAT_EVT)
CASE_RETURN_STR(BTA_AG_AT_CBC_EVT)
CASE_RETURN_STR(BTA_AG_AT_BAC_EVT)
CASE_RETURN_STR(BTA_AG_AT_BCS_EVT)
default:
return "UNKNOWN MSG ID";
}
}
const char* dump_hf_call_state(esp_hf_call_status_t call_state)
{
switch(call_state)
{
CASE_RETURN_STR(ESP_HF_CALL_STATUS_NO_CALLS)
CASE_RETURN_STR(ESP_HF_CALL_STATUS_CALL_IN_PROGRESS)
default:
return "UNKNOWN CALL STATE";
}
}
const char* dump_hf_call_setup_state(esp_hf_call_setup_status_t call_setup_state)
{
switch(call_setup_state)
{
CASE_RETURN_STR(ESP_HF_CALL_SETUP_STATUS_IDLE)
CASE_RETURN_STR(ESP_HF_CALL_SETUP_STATUS_INCOMING)
CASE_RETURN_STR(ESP_HF_CALL_SETUP_STATUS_OUTGOING_DIALING)
CASE_RETURN_STR(ESP_HF_CALL_SETUP_STATUS_OUTGOING_ALERTING)
default:
return "UNKNOWN CALL SETUP STATE";
}
}
#endif // #if (BTA_AG_INCLUDED == TRUE)
UINT32 devclass2uint(DEV_CLASS dev_class)
{
UINT32 cod = 0;

View file

@ -19,6 +19,7 @@
#include "stack/bt_types.h"
#include "common/bt_defs.h"
#include "esp_bt_defs.h"
#include "esp_hf_defs.h"
/*******************************************************************************
** Constants & Macros
@ -34,9 +35,18 @@ typedef char bdstr_t[18];
/*******************************************************************************
** Functions
********************************************************************************/
#if(BTA_AV_INCLUDED == TRUE)
const char *dump_rc_event(UINT8 event);
const char *dump_rc_notification_event_id(UINT8 event_id);
const char *dump_rc_pdu(UINT8 pdu);
#endif
#if(BTA_AG_INCLUDED == TRUE)
const char *dump_hf_conn_state(UINT16 event);
const char *dump_hf_event(UINT16 event);
const char *dump_hf_call_state(esp_hf_call_status_t call_state);
const char* dump_hf_call_setup_state(esp_hf_call_setup_status_t call_setup_state);
#endif
UINT32 devclass2uint(DEV_CLASS dev_class);
void uint2devclass(UINT32 dev, DEV_CLASS dev_class);

View file

@ -0,0 +1,590 @@
/******************************************************************************
*
* Copyright (C) 2009-2012 Broadcom Corporation
*
* 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.
*
******************************************************************************/
#define LOG_TAG "bt_btc_bta_ag"
#include "btc_hf_ag.h"
#include "bta_ag_int.h"
#include "bta/bta_api.h"
#include "bta/bta_ag_api.h"
#include "bta/bta_ag_co.h"
#include "bta/bta_dm_co.h"
#include "common/bt_target.h"
#include "hci/hci_audio.h"
#include "osi/allocator.h"
#include <string.h>
#if (BTA_AG_INCLUDED == TRUE)
/*******************************************************************************
* CONST
********************************************************************************/
#if (BTM_SCO_HCI_INCLUDED == TRUE)
#include "oi_codec_sbc.h"
#include "oi_status.h"
#include "sbc_encoder.h"
#if (PLC_INCLUDED == TRUE)
#include "sbc_plc.h"
typedef struct {
bool first_good_frame_found;
sbc_plc_state_t plc_state;
int16_t sbc_plc_out[SBC_FS];
} bta_hf_ct_plc_t;
#if HFP_DYNAMIC_MEMORY == FALSE
static bta_hf_ct_plc_t bta_hf_ct_plc;
#else
static bta_hf_ct_plc_t *bta_hf_ct_plc_ptr;
#define bta_hf_ct_plc (*bta_hf_ct_plc_ptr)
#endif ///HFP_DYNAMIC_MEMORY == FALSE
#endif ///(PLC_INCLUDED == TRUE)
#define HF_SBC_DEC_CONTEXT_DATA_LEN (CODEC_DATA_WORDS(1, SBC_CODEC_FAST_FILTER_BUFFERS))
#define HF_SBC_DEC_RAW_DATA_SIZE 240
#define HF_SBC_ENC_RAW_DATA_SIZE 240
/* BTA-AG-CO control block to map bdaddr to BTA handle */
typedef struct
{
OI_CODEC_SBC_DECODER_CONTEXT decoder_context;
OI_UINT32 decoder_context_data[HF_SBC_DEC_CONTEXT_DATA_LEN];
OI_INT16 decode_raw_data[HF_SBC_DEC_RAW_DATA_SIZE];
SBC_ENC_PARAMS encoder;
UINT8 sequence_number;
bool is_bad_frame;
bool decode_first_pkt;
OI_BYTE decode_msbc_data[BTM_MSBC_FRAME_SIZE];
bool encode_first_pkt;
OI_BYTE encode_msbc_data[BTM_MSBC_FRAME_SIZE];
} bta_ag_co_cb_t;
#if HFP_DYNAMIC_MEMORY == FALSE
static bta_ag_co_cb_t bta_ag_co_cb;
#else
static bta_ag_co_cb_t *bta_ag_co_cb_ptr;
#define bta_ag_co_cb (*bta_ag_co_cb_ptr)
#endif /* HFP_DYNAMIC_MEMORY == FALSE */
static UINT8 hf_air_mode = BTM_SCO_AIR_MODE_TRANSPNT;
static UINT8 hf_inout_pkt_size = 0;
/* =========================================================================
* AG pass-through mode handle
*===========================================================================*/
/*******************************************************************************
**
** Function bta_ag_co_tx_write
**
** Description This function is called by the AG to send data to the
** phone when the AG is configured for AT command pass-through.
** The implementation of this function must copy the data to
** the phones memory.
**
** Returns void
**
*******************************************************************************/
void bta_ag_co_tx_write(UINT16 handle, UNUSED_ATTR UINT8 * p_data, UINT16 len)
{
BTIF_TRACE_DEBUG( "bta_ag_co_tx_write: handle: %d, len: %d", handle, len );
}
/******************************************************************************
**
** Function bta_ag_ci_rx_write
**
** Description This function is called to send data to the AG when the AG
** is configured for AT command pass-through. The function
** copies data to an event buffer and sends it.
**
** Returns void
**
******************************************************************************/
void bta_ag_ci_rx_write(UINT16 handle, char *p_data, UINT16 len)
{
tBTA_AG_CI_RX_WRITE *p_buf;
UINT16 len_remaining = len;
char *p_data_area;
if (len > (BT_DEFAULT_BUFFER_SIZE - sizeof(tBTA_AG_CI_RX_WRITE) - 1)) {
len = BT_DEFAULT_BUFFER_SIZE - sizeof(tBTA_AG_CI_RX_WRITE) - 1;
}
while (len_remaining) {
if (len_remaining < len) {
len = len_remaining;
}
if ((p_buf = (tBTA_AG_CI_RX_WRITE *) osi_malloc((UINT16)(sizeof(tBTA_AG_CI_RX_WRITE) + len + 1))) != NULL) {
p_buf->hdr.event = BTA_AG_CI_RX_WRITE_EVT;
p_buf->hdr.layer_specific = handle;
p_data_area = (char *)(p_buf+1); /* Point to data area after header */
strncpy(p_data_area, p_data, len);
p_data_area[len] = 0;
bta_sys_sendmsg(p_buf);
} else {
APPL_TRACE_ERROR("ERROR: Unable to allocate buffer to hold AT response code. len=%i", len);
break;
}
len_remaining-=len;
p_data+=len;
}
}
/******************************************************************************
**
** Function bta_ag_ci_slc_ready
**
** Description This function is called to notify AG that SLC is up at
** the application. This funcion is only used when the app
** is running in pass-through mode.
**
** Returns void
**
******************************************************************************/
void bta_ag_ci_slc_ready(UINT16 handle)
{
tBTA_AG_DATA *p_buf;
if ((p_buf = (tBTA_AG_DATA *)osi_malloc(sizeof(tBTA_AG_DATA))) != NULL) {
p_buf->hdr.event = BTA_AG_CI_SLC_READY_EVT;
p_buf->hdr.layer_specific = handle;
bta_sys_sendmsg(p_buf);
}
}
/*******************************************************************************
* H2 & DEC
********************************************************************************/
/*******************************************************************************
**
** Function bta_ag_h2_header
**
** Description This function is called to fill in H2 header
**
** Returns void
**
*******************************************************************************/
static void bta_ag_h2_header(UINT16 *p_buf)
{
// H2: Header with synchronization word and sequence number
#define BTA_HF_H2_HEADER 0x0801
#define BTA_HF_H2_HEADER_BIT0_MASK (1 << 0)
#define BTA_HF_H2_HEADER_BIT1_MASK (1 << 1)
#define BTA_HF_H2_HEADER_SN0_BIT_OFFSET1 12
#define BTA_HF_H2_HEADER_SN0_BIT_OFFSET2 13
#define BTA_HF_H2_HEADER_SN1_BIT_OFFSET1 14
#define BTA_HF_H2_HEADER_SN1_BIT_OFFSET2 15
UINT16 h2_header = BTA_HF_H2_HEADER;
UINT8 h2_header_sn0 = bta_ag_co_cb.sequence_number & BTA_HF_H2_HEADER_BIT0_MASK;
UINT8 h2_header_sn1 = bta_ag_co_cb.sequence_number & BTA_HF_H2_HEADER_BIT1_MASK;
h2_header |= (h2_header_sn0 << BTA_HF_H2_HEADER_SN0_BIT_OFFSET1
| h2_header_sn0 << BTA_HF_H2_HEADER_SN0_BIT_OFFSET2
| h2_header_sn1 << (BTA_HF_H2_HEADER_SN1_BIT_OFFSET1 - 1)
| h2_header_sn1 << (BTA_HF_H2_HEADER_SN1_BIT_OFFSET2 - 1)
);
bta_ag_co_cb.sequence_number++;
*p_buf = h2_header;
}
/*******************************************************************************
**
** Function bta_hf_dec_init
**
** Description Initialize decoding task
**
** Returns void
**
*******************************************************************************/
static void bta_hf_dec_init(void)
{
#if (PLC_INCLUDED == TRUE)
sbc_plc_init(&(bta_hf_ct_plc.plc_state));
#endif ///(PLC_INCLUDED == TRUE)
OI_STATUS status = OI_CODEC_SBC_DecoderReset(&bta_ag_co_cb.decoder_context, bta_ag_co_cb.decoder_context_data,
HF_SBC_DEC_CONTEXT_DATA_LEN * sizeof(OI_UINT32), 1, 1, FALSE, TRUE);
if (!OI_SUCCESS(status)) {
APPL_TRACE_ERROR("OI_CODEC_SBC_DecoderReset failed with error code %d\n", status);
}
}
/*******************************************************************************
**
** Function bta_hf_enc_init
**
** Description Initialize encoding task for mSBC
**
** Returns void
**
*******************************************************************************/
static void bta_hf_enc_init(void)
{
bta_ag_co_cb.sequence_number = 0;
bta_ag_co_cb.decode_first_pkt = true;
bta_ag_co_cb.encode_first_pkt = true;
bta_ag_co_cb.is_bad_frame = false;
bta_ag_co_cb.encoder.sbc_mode = SBC_MODE_MSBC;
bta_ag_co_cb.encoder.s16NumOfBlocks = 15;
bta_ag_co_cb.encoder.s16NumOfSubBands = 8;
bta_ag_co_cb.encoder.s16AllocationMethod = SBC_LOUDNESS;
bta_ag_co_cb.encoder.s16BitPool = 26;
bta_ag_co_cb.encoder.s16ChannelMode = SBC_MONO;
bta_ag_co_cb.encoder.s16NumOfChannels = 1;
bta_ag_co_cb.encoder.s16SamplingFreq = SBC_sf16000;
SBC_Encoder_Init(&(bta_ag_co_cb.encoder));
}
/*******************************************************************************
**
** Function bta_ag_decode_msbc_frame
**
** Description This function is called decode a mSBC frame
**
** Returns void
**
*******************************************************************************/
static void bta_ag_decode_msbc_frame(UINT8 **data, UINT8 *length, BOOLEAN is_bad_frame)
{
OI_STATUS status;
const OI_BYTE *zero_signal_frame_data;
UINT8 zero_signal_frame_len = BTM_MSBC_FRAME_DATA_SIZE;
UINT32 sbc_raw_data_size = HF_SBC_DEC_RAW_DATA_SIZE;
if (is_bad_frame) {
status = OI_CODEC_SBC_CHECKSUM_MISMATCH;
} else {
status = OI_CODEC_SBC_DecodeFrame(&bta_ag_co_cb.decoder_context, (const OI_BYTE **)data,
(OI_UINT32 *)length,
(OI_INT16 *)bta_ag_co_cb.decode_raw_data,
(OI_UINT32 *)&sbc_raw_data_size);
}
// PLC_INCLUDED will be set to TRUE when enabling Wide Band Speech
#if (PLC_INCLUDED == TRUE)
switch(status) {
case OI_OK:
{
bta_hf_ct_plc.first_good_frame_found = TRUE;
sbc_plc_good_frame(&(bta_hf_ct_plc.plc_state), (int16_t *)bta_ag_co_cb.decode_raw_data, bta_hf_ct_plc.sbc_plc_out);
}
case OI_CODEC_SBC_NOT_ENOUGH_HEADER_DATA:
case OI_CODEC_SBC_NOT_ENOUGH_BODY_DATA:
case OI_CODEC_SBC_NOT_ENOUGH_AUDIO_DATA:
break;
case OI_CODEC_SBC_NO_SYNCWORD:
case OI_CODEC_SBC_CHECKSUM_MISMATCH:
{
if (!bta_hf_ct_plc.first_good_frame_found) {
break;
}
zero_signal_frame_data = sbc_plc_zero_signal_frame();
sbc_raw_data_size = HF_SBC_DEC_RAW_DATA_SIZE;
status = OI_CODEC_SBC_DecodeFrame(&bta_ag_co_cb.decoder_context, &zero_signal_frame_data,
(OI_UINT32 *)&zero_signal_frame_len,
(OI_INT16 *)bta_ag_co_cb.decode_raw_data,
(OI_UINT32 *)&sbc_raw_data_size);
sbc_plc_bad_frame(&(bta_hf_ct_plc.plc_state), bta_ag_co_cb.decode_raw_data, bta_hf_ct_plc.sbc_plc_out);
APPL_TRACE_DEBUG("bad frame, using PLC to fix it.");
break;
}
case OI_STATUS_INVALID_PARAMETERS:
{
// This caused by corrupt frames.
// The codec apparently does not recover from this.
// Re-initialize the codec.
APPL_TRACE_ERROR("Frame decode error: OI_STATUS_INVALID_PARAMETERS");
if (!OI_SUCCESS(OI_CODEC_SBC_DecoderReset(&bta_ag_co_cb.decoder_context, bta_ag_co_cb.decoder_context_data,
HF_SBC_DEC_CONTEXT_DATA_LEN * sizeof(OI_UINT32), 1, 1, FALSE, TRUE))) {
APPL_TRACE_ERROR("OI_CODEC_SBC_DecoderReset failed with error code %d\n", status);
}
break;
}
default:
APPL_TRACE_ERROR("Frame decode error: %d", status);
break;
}
#endif ///(PLC_INCLUDED == TRUE)
if (OI_SUCCESS(status)) {
btc_hf_incoming_data_cb_to_app((const uint8_t *)(bta_hf_ct_plc.sbc_plc_out), sbc_raw_data_size);
}
}
/*******************************************************************************
* BTA AG SCO CO FUNCITONS
********************************************************************************/
/*******************************************************************************
**
** Function bta_ag_sco_audio_state
**
** Description This function is called by the AG before the audio connection
** is brought up, after it comes up, and after it goes down.
**
** Parameters handle - handle of the AG instance
** state - Audio state
** codec - if WBS support is compiled in, codec to going to be used is provided
** and when in SCO_STATE_SETUP, BTM_I2SPCMConfig() must be called with
** the correct platform parameters.
** in the other states codec type should not be ignored
**
** Returns void
**
*******************************************************************************/
#if (BTM_WBS_INCLUDED == TRUE )
void bta_ag_sco_audio_state(UINT16 handle, UINT8 app_id, UINT8 state, tBTA_AG_PEER_CODEC codec)
#else
void bta_ag_sco_audio_state(UINT16 handle, UINT8 app_id, UINT8 state)
#endif
{
BTIF_TRACE_DEBUG("bta_ag_sco_audio_state: handle %d, state %d", handle, state);
switch (state) {
case SCO_STATE_ON:
case SCO_STATE_OFF:
case SCO_STATE_OFF_TRANSFER:
case SCO_STATE_SETUP:
default:
break;
}
}
/*******************************************************************************
**
** Function bta_ag_sco_co_init
**
** Description Set default data path for SCO/eSCO.
**
**
** Returns Void.
**
*******************************************************************************/
tBTA_HFP_SCO_ROUTE_TYPE bta_ag_sco_co_init(UINT32 rx_bw, UINT32 tx_bw, tBTA_HFP_CODEC_INFO *p_codec_info, UINT8 app_id)
{
APPL_TRACE_EVENT("%s rx_bw %d, tx_bw %d, codec %d", __FUNCTION__, rx_bw, tx_bw, p_codec_info->codec_type);
return BTA_HFP_SCO_ROUTE_HCI;
}
/*******************************************************************************
**
** Function bta_ag_sco_co_open
**
** Description This function is executed by AG when a service level connection
** is opened.
**
**
** Returns void
**
*******************************************************************************/
void bta_ag_sco_co_open(UINT16 handle, tBTM_SCO_AIR_MODE_TYPE air_mode, UINT8 inout_pkt_size, UINT16 event)
{
APPL_TRACE_EVENT("%s hdl %x, pkt_sz %u, event %u", __FUNCTION__, handle, inout_pkt_size, event);
hf_air_mode = air_mode;
hf_inout_pkt_size = inout_pkt_size;
if (air_mode == BTM_SCO_AIR_MODE_TRANSPNT) {
#if (HFP_DYNAMIC_MEMORY == TRUE)
bta_ag_co_cb_ptr = osi_calloc(sizeof(bta_ag_co_cb_t));
if (!bta_ag_co_cb_ptr) {
APPL_TRACE_ERROR("%s allocate failed", __FUNCTION__);
goto error_exit;
}
#if (PLC_INCLUDED == TRUE)
bta_hf_ct_plc_ptr = (bta_hf_ct_plc_t *)osi_calloc(sizeof(bta_hf_ct_plc_t));
if (!bta_hf_ct_plc_ptr) {
APPL_TRACE_ERROR("%s malloc fail.", __FUNCTION__);
goto error_exit;
}
#endif ///(PLC_INCLUDED == TRUE)
#endif /// (HFP_DYNAMIC_MEMORY == TRUE)
bta_hf_dec_init();
bta_hf_enc_init();
return;
} else {
return; // Nothing to do
}
#if (HFP_DYNAMIC_MEMORY == TRUE)
error_exit:;
hf_air_mode = BTM_SCO_AIR_MODE_UNKNOWN;
hf_inout_pkt_size = 0;
if (bta_ag_co_cb_ptr) {
osi_free(bta_ag_co_cb_ptr);
bta_ag_co_cb_ptr = NULL;
}
#if (PLC_INCLUDED == TRUE)
if (bta_hf_ct_plc_ptr) {
osi_free(bta_hf_ct_plc_ptr);
bta_hf_ct_plc_ptr = NULL;
}
#endif ///(PLC_INCLUDED == TRUE)
#endif /// (HFP_DYNAMIC_MEMORY == TRUE)
return;
}
/*******************************************************************************
**
** Function bta_ag_sco_co_close
**
** Description Nothing but print some log.
**
** Returns void
**
*******************************************************************************/
void bta_ag_sco_co_close(void)
{
APPL_TRACE_EVENT("%s", __FUNCTION__);
if (hf_air_mode == BTM_SCO_AIR_MODE_TRANSPNT) {
#if (PLC_INCLUDED == TRUE)
sbc_plc_deinit(&(bta_hf_ct_plc.plc_state));
bta_hf_ct_plc.first_good_frame_found = FALSE;
#if (HFP_DYNAMIC_MEMORY == TRUE)
osi_free(bta_hf_ct_plc_ptr);
bta_hf_ct_plc_ptr = NULL;
#endif /// (HFP_DYNAMIC_MEMORY == TRUE)
#endif ///(PLC_INCLUDED == TRUE)
#if (HFP_DYNAMIC_MEMORY == TRUE)
osi_free(bta_ag_co_cb_ptr);
bta_ag_co_cb_ptr = NULL;
#endif /* HFP_DYNAMIC_MEMORY == TRUE */
} else {
// Nothing to do
}
hf_air_mode = BTM_SCO_AIR_MODE_UNKNOWN;
hf_inout_pkt_size = 0;
}
/*******************************************************************************
**
** Function bta_ag_sco_co_out_data
**
** Description This function is called to send SCO data over HCI.
**
** Returns number of bytes got from application
**
*******************************************************************************/
uint32_t bta_ag_sco_co_out_data(UINT8 *p_buf)
{
if (hf_air_mode == BTM_SCO_AIR_MODE_CVSD) {
// CVSD
uint32_t hf_raw_pkt_size = hf_inout_pkt_size;
return btc_hf_outgoing_data_cb_to_app(p_buf, hf_raw_pkt_size);
} else if (hf_air_mode == BTM_SCO_AIR_MODE_TRANSPNT) {
// mSBC
if (hf_inout_pkt_size == BTM_MSBC_FRAME_SIZE / 2) {
if (bta_ag_co_cb.encode_first_pkt) {
UINT32 size = btc_hf_outgoing_data_cb_to_app((UINT8 *)bta_ag_co_cb.encoder.as16PcmBuffer, HF_SBC_ENC_RAW_DATA_SIZE);
if (size != HF_SBC_ENC_RAW_DATA_SIZE) {
return 0;
}
bta_ag_h2_header((UINT16 *)bta_ag_co_cb.encode_msbc_data);
bta_ag_co_cb.encoder.pu8Packet = bta_ag_co_cb.encode_msbc_data + 2;
SBC_Encoder(&bta_ag_co_cb.encoder);
memcpy(p_buf, bta_ag_co_cb.encode_msbc_data, hf_inout_pkt_size);
bta_ag_co_cb.encode_first_pkt = !bta_ag_co_cb.encode_first_pkt;
return hf_inout_pkt_size;
} else {
memcpy(p_buf, bta_ag_co_cb.encode_msbc_data + hf_inout_pkt_size, hf_inout_pkt_size);
bta_ag_co_cb.encode_first_pkt = !bta_ag_co_cb.encode_first_pkt;
return hf_inout_pkt_size;
}
} else if (hf_inout_pkt_size == BTM_MSBC_FRAME_SIZE) {
UINT32 size = btc_hf_outgoing_data_cb_to_app((UINT8 *)bta_ag_co_cb.encoder.as16PcmBuffer, HF_SBC_ENC_RAW_DATA_SIZE);
if (size != HF_SBC_ENC_RAW_DATA_SIZE) {
return 0;
}
bta_ag_h2_header((UINT16 *)p_buf);
bta_ag_co_cb.encoder.pu8Packet = p_buf + 2;
SBC_Encoder(&bta_ag_co_cb.encoder);
return hf_inout_pkt_size;
} else {
//Never run to here.
}
} else {
APPL_TRACE_ERROR("%s invaild air mode: %d", __FUNCTION__, hf_air_mode);
}
return 0;
}
/*******************************************************************************
**
** Function bta_ag_sco_co_in_data
**
** Description This function is called to send incoming SCO data to application.
**
** Returns void
**
*******************************************************************************/
void bta_ag_sco_co_in_data(BT_HDR *p_buf, tBTM_SCO_DATA_FLAG status)
{
UINT8 *p = (UINT8 *)(p_buf + 1) + p_buf->offset;
UINT8 pkt_size = 0;
STREAM_SKIP_UINT16(p);
STREAM_TO_UINT8(pkt_size, p);
if (hf_air_mode == BTM_SCO_AIR_MODE_CVSD) {
// CVSD
if(status != BTM_SCO_DATA_CORRECT) {
APPL_TRACE_DEBUG("%s: not a correct frame(%d).", __func__, status);
}
btc_hf_incoming_data_cb_to_app(p, pkt_size);
} else if (hf_air_mode == BTM_SCO_AIR_MODE_TRANSPNT) {
// mSBC
UINT8 *data = NULL;
if (pkt_size != hf_inout_pkt_size) {
bta_ag_co_cb.is_bad_frame = true;
}
if (status != BTM_SCO_DATA_CORRECT) {
bta_ag_co_cb.is_bad_frame = true;
}
if (hf_inout_pkt_size == BTM_MSBC_FRAME_SIZE / 2) {
if (bta_ag_co_cb.decode_first_pkt) {
if (!bta_ag_co_cb.is_bad_frame) {
memcpy(bta_ag_co_cb.decode_msbc_data, p, pkt_size);
}
} else {
if (!bta_ag_co_cb.is_bad_frame) {
memcpy(bta_ag_co_cb.decode_msbc_data + BTM_MSBC_FRAME_SIZE / 2, p, pkt_size);
}
data = bta_ag_co_cb.decode_msbc_data;
bta_ag_decode_msbc_frame(&data, &pkt_size, bta_ag_co_cb.is_bad_frame);
bta_ag_co_cb.is_bad_frame = false;
}
bta_ag_co_cb.decode_first_pkt = !bta_ag_co_cb.decode_first_pkt;
} else if (hf_inout_pkt_size == BTM_MSBC_FRAME_SIZE) {
data = p;
bta_ag_decode_msbc_frame(&data, &pkt_size, bta_ag_co_cb.is_bad_frame);
bta_ag_co_cb.is_bad_frame = false;
} else {
//Never run to here.
}
} else {
APPL_TRACE_ERROR("%s invaild air mode: %d", __FUNCTION__, hf_air_mode);
}
}
#endif /* #if (BTM_SCO_HCI_INCLUDED == TRUE) */
#endif /* #if (BTA_AG_INCLUDED == TRUE) */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,259 @@
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
//
// 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.
/*******************************************************************************
*
* Filename: btc_hf_ag.h
*
* Description: Main API header file for all BTC HF AG functions accessed
* from internal stack.
*
*******************************************************************************/
#ifndef __BTC_HF_AG_H__
#define __BTC_HF_AG_H__
#include "common/bt_target.h"
#include "btc/btc_task.h"
#include "btc/btc_common.h"
#include "bta/bta_ag_api.h"
#include "esp_hf_ag_api.h"
#if (BTC_HF_INCLUDED == TRUE)
/*******************************************************************************
** Type Defs
********************************************************************************/
/* btc_hf_act_t */
typedef enum
{
//INIT
BTC_HF_INIT_EVT,
BTC_HF_DEINIT_EVT,
BTC_HF_CONNECT_EVT,
BTC_HF_DISCONNECT_EVT,
BTC_HF_CONNECT_AUDIO_EVT,
BTC_HF_DISCONNECT_AUDIO_EVT,
BTC_HF_VRA_EVT,
BTC_HF_VOLUME_CONTROL_EVT,
//AT_RESPONSE
BTC_HF_UNAT_RESPONSE_EVT,
BTC_HF_CME_ERR_EVT,
BTC_HF_IND_NOTIFICATION_EVT,
BTC_HF_CIND_RESPONSE_EVT,
BTC_HF_COPS_RESPONSE_EVT,
BTC_HF_CLCC_RESPONSE_EVT,
BTC_HF_CNUM_RESPONSE_EVT,
BTC_HF_INBAND_RING_EVT,
//CALL_HANDLE
BTC_HF_AC_INCALL_EVT,
BTC_HF_RJ_INCALL_EVT,
BTC_HF_OUT_CALL_EVT,
BTC_HF_END_CALL_EVT,
//REG
BTC_HF_REGISTER_DATA_CALLBACK_EVT
} btc_hf_act_t;
/* btc_hf_args_t */
typedef union
{
// BTC_HF_INIT_EVT
bt_bdaddr_t init;
//BTC_HF_DEINIT_EVT
bt_bdaddr_t deinit;
// BTC_HF_CONNECT_EVT
bt_bdaddr_t connect;
// BTC_HF_DISCONNECT_EVT
bt_bdaddr_t disconnect;
// BTC_HF_CONNECT_AUDIO_EVT
bt_bdaddr_t connect_audio;
// BTC_HF_DISCONNECT_AUDIO_EVT
bt_bdaddr_t disconnect_audio;
//BTC_HF_VRA_EVT
struct vra_param {
bt_bdaddr_t remote_addr;
esp_hf_vr_state_t value;
} vra_rep;
// BTC_HF_VOLUME_CONTROL_EVT
struct volcon_args {
bt_bdaddr_t remote_addr;
esp_hf_volume_control_target_t target_type;
int volume;
} volcon;
//BTC_HF_UNAT_RESPONSE_EVT
struct unat_param {
bt_bdaddr_t remote_addr;
char *unat;
} unat_rep;
//BTC_HF_CME_ERR_EVT
struct at_ok_err_args {
bt_bdaddr_t remote_addr;
esp_hf_at_response_code_t response_code;
esp_hf_cme_err_t error_code;
} ext_at;
// BTC_HF_IND_NOTIFICATION_EVT
struct indchange_status {
bt_bdaddr_t remote_addr;
esp_hf_call_status_t call_state;
esp_hf_call_setup_status_t call_setup_state;
esp_hf_network_state_t ntk_state;
int signal;
} ind_change;
//BTC_HF_CIND_RESPONSE_EVT
struct cind_args {
bt_bdaddr_t remote_addr;
esp_hf_call_status_t call_state;
esp_hf_call_setup_status_t call_setup_state;
esp_hf_network_state_t ntk_state;
int signal;
esp_hf_roaming_status_t roam;
int batt_lev;
esp_hf_call_held_status_t call_held_state;
} cind_rep;
//BTC_HF_COPS_RESPONSE_EVT
struct cops_args {
bt_bdaddr_t remote_addr;
char *name;
} cops_rep;
// BTC_HF_CLCC_RESPONSE_EVT
struct clcc_args {
bt_bdaddr_t remote_addr;
int index;
esp_hf_current_call_direction_t dir;
esp_hf_current_call_status_t current_call_state;
esp_hf_current_call_mode_t mode;
esp_hf_current_call_mpty_type_t mpty;
char *number;
esp_hf_call_addr_type_t type;
} clcc_rep;
// BTC_HF_CNUM_RESPONSE_EVT
struct cnum_args {
bt_bdaddr_t remote_addr;
char *number;
esp_hf_subscriber_service_type_t type;
} cnum_rep;
//BTC_HF_NREC_RESPONSE_EVT
bt_bdaddr_t nrec_rep;
//BTC_HF_VTC_RESPONSE_EVT
struct bts_args {
bt_bdaddr_t remote_addr;
char *code;
} vts_rep;
//BTC_HF_INBAND_RING_EVT
struct bsir_args {
bt_bdaddr_t remote_addr;
esp_hf_in_band_ring_state_t state;
} bsir;
// BTC_HF_AC_INCALL_EVT
// BTC_HF_RJ_INCALL_EVT
// BTC_HF_OUT_CALL_EVT
// BTC_HF_END_CALL_EVT
struct phone_args {
bt_bdaddr_t remote_addr;
int num_active;
int num_held;
esp_hf_call_status_t call_state;
esp_hf_call_setup_status_t call_setup_state;
char *number;
esp_hf_call_addr_type_t call_addr_type;
} phone;
// BTC_HF_REGISTER_DATA_CALLBACK_EVT
struct reg_data_callback {
esp_hf_incoming_data_cb_t recv;
esp_hf_outgoing_data_cb_t send;
} reg_data_cb;
} btc_hf_args_t;
/************************************************************************************
** Local definitions
************************************************************************************/
/* Number of BTC-HF-AG control blocks */
#define BTC_HF_NUM_CB 1
/* Handsfree AG app ids for service registration */
/* APP ID definition*/
#define BTC_HF_ID_1 0
#if HFP_DYNAMIC_MEMORY == TRUE
extern hf_local_param_t *hf_local_param_ptr;
#define hf_local_param (*hf_local_param_ptr)
#endif
/* BTC-AG control block to map bdaddr to BTA handle */
typedef struct
{
bool initialized;
UINT16 handle;
bt_bdaddr_t connected_bda;
tBTA_AG_PEER_FEAT peer_feat;
tBTA_AG_CHLD_FEAT chld_feat;
struct timespec call_end_timestamp;
struct timespec connected_timestamp;
esp_hf_connection_state_t connection_state;
esp_hf_vr_state_t vr_state;
int num_active;
int num_held;
esp_hf_call_status_t call_state;
esp_hf_call_setup_status_t call_setup_state;
} btc_hf_cb_t;
typedef struct
{
int hf_idx;
UINT32 btc_hf_features;
btc_hf_cb_t btc_hf_cb;
esp_hf_incoming_data_cb_t btc_hf_incoming_data_cb;
esp_hf_outgoing_data_cb_t btc_hf_outgoing_data_cb;
} hf_local_param_t;
/*******************************************************************************
** BTC HF AG Handle Hub
********************************************************************************/
void btc_hf_call_handler(btc_msg_t *msg); // act the cmd from esp-application
void btc_hf_cb_handler(btc_msg_t *msg); //handle the event from bta
void btc_hf_incoming_data_cb_to_app(const uint8_t *data, uint32_t len);
uint32_t btc_hf_outgoing_data_cb_to_app(uint8_t *data, uint32_t len);
void btc_hf_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src);
void btc_hf_arg_deep_free(btc_msg_t *msg);
#endif // BTC_HF_INCLUDED == TRUE
#endif /* __BTC_HF_AG_H__ */

View file

@ -53,7 +53,14 @@
#define UC_BT_SPP_ENABLED FALSE
#endif
//HFP
//HFP(AG)
#ifdef CONFIG_BT_HFP_AG_ENABLE
#define UC_BT_HFP_AG_ENABLED CONFIG_BT_HFP_AG_ENABLE
#else
#define UC_BT_HFP_AG_ENABLED FALSE
#endif
//HFP(Client)
#ifdef CONFIG_BT_HFP_CLIENT_ENABLE
#define UC_BT_HFP_CLIENT_ENABLED CONFIG_BT_HFP_CLIENT_ENABLE
#else

View file

@ -80,6 +80,27 @@
#define BTC_SPP_INCLUDED TRUE
#endif /* UC_BT_SPP_ENABLED */
#if (UC_BT_HFP_AG_ENABLED == TRUE)
#define BTC_HF_INCLUDED TRUE
#define BTA_AG_INCLUDED TRUE
#define PLC_INCLUDED TRUE
#ifndef RFCOMM_INCLUDED
#define RFCOMM_INCLUDED TRUE
#endif
#ifndef BTM_SCO_INCLUDED
#define BTM_SCO_INCLUDED TRUE
#endif
#ifndef BTM_MAX_SCO_LINKS
#define BTM_MAX_SCO_LINKS (1)
#endif
#ifndef SBC_DEC_INCLUDED
#define SBC_DEC_INCLUDED TRUE
#endif
#ifndef SBC_ENC_INCLUDED
#define SBC_ENC_INCLUDED TRUE
#endif
#endif /* UC_BT_HFP_AG_ENABLED */
#if (UC_BT_HFP_CLIENT_ENABLED == TRUE)
#define BTC_HF_CLIENT_INCLUDED TRUE
#define BTA_HF_INCLUDED TRUE

View file

@ -215,7 +215,7 @@ static void btm_esco_conn_rsp (UINT16 sco_inx, UINT8 hci_status, BD_ADDR bda,
#if BTM_SCO_HCI_INCLUDED == TRUE
void btm_sco_process_num_bufs (UINT16 num_lm_sco_bufs)
{
BTM_TRACE_ERROR("%s, %d", __FUNCTION__, num_lm_sco_bufs);
BTM_TRACE_DEBUG("%s, %d", __FUNCTION__, num_lm_sco_bufs);
btm_cb.sco_cb.num_lm_sco_bufs = btm_cb.sco_cb.xmit_window_size = num_lm_sco_bufs;
}

View file

@ -152,6 +152,7 @@ static void btu_hci_msg_process(void *param)
case BT_EVT_TO_BTU_HCI_SCO:
#if BTM_SCO_INCLUDED == TRUE
btm_route_sco_data (p_msg);
osi_free(p_msg);
break;
#endif

@ -1 +1 @@
Subproject commit acaed3b6a860457b5cf61ecddce7d7be2c61a60a
Subproject commit b2ae70c37e544f93adc1f22c4954c93be7525bed

View file

@ -415,6 +415,14 @@
#define MYNEWT_VAL_BLE_HOST (1)
#endif
#ifndef MYNEWT_VAL_ESP_BLE_MESH
#ifdef CONFIG_BLE_MESH_HCI_5_0
#define MYNEWT_VAL_ESP_BLE_MESH (1)
#else
#define MYNEWT_VAL_ESP_BLE_MESH (0)
#endif
#endif
#ifndef MYNEWT_VAL_BLE_HS_DEBUG
#ifdef CONFIG_BT_NIMBLE_DEBUG
#define MYNEWT_VAL_BLE_HS_DEBUG (1)

View file

@ -390,7 +390,8 @@ static void can_intr_handler_err_warn(can_status_reg_t *status, int *alert_req)
can_alert_handler(CAN_ALERT_ABOVE_ERR_WARN, alert_req);
} else if (p_can_obj->control_flags & CTRL_FLAG_RECOVERING) {
//Bus recovery complete.
can_enter_reset_mode();
esp_err_t err = can_enter_reset_mode();
assert(err == ESP_OK);
//Reset and set flags to the equivalent of the stopped state
CAN_RESET_FLAG(p_can_obj->control_flags, CTRL_FLAG_RECOVERING | CTRL_FLAG_ERR_WARN |
CTRL_FLAG_ERR_PASSIVE | CTRL_FLAG_BUS_OFF |
@ -704,7 +705,7 @@ esp_err_t can_driver_install(const can_general_config_t *g_config, const can_tim
}
periph_module_reset(PERIPH_CAN_MODULE);
periph_module_enable(PERIPH_CAN_MODULE); //Enable APB CLK to CAN peripheral
esp_err_t err = can_exit_reset_mode(); //Must enter reset mode to write to config registers
esp_err_t err = can_enter_reset_mode(); //Must enter reset mode to write to config registers
assert(err == ESP_OK);
can_config_pelican(); //Use PeliCAN addresses
/* Note: REC is allowed to increase even in reset mode. Listen only mode
@ -766,7 +767,7 @@ esp_err_t can_driver_uninstall(void)
//Check state
CAN_CHECK_FROM_CRIT(p_can_obj != NULL, ESP_ERR_INVALID_STATE);
CAN_CHECK_FROM_CRIT(p_can_obj->control_flags & (CTRL_FLAG_STOPPED | CTRL_FLAG_BUS_OFF), ESP_ERR_INVALID_STATE);
esp_err_t err = can_exit_reset_mode(); //Enter reset mode to stop any CAN bus activity
esp_err_t err = can_enter_reset_mode(); //Enter reset mode to stop any CAN bus activity
assert(err == ESP_OK);
//Clear registers by reading
(void) can_get_interrupt_reason();
@ -805,7 +806,7 @@ esp_err_t can_start(void)
//Reset RX queue, and RX message count
xQueueReset(p_can_obj->rx_queue);
p_can_obj->rx_msg_count = 0;
esp_err_t err = can_exit_reset_mode(); //Should already be in bus-off mode, set again to make sure
esp_err_t err = can_enter_reset_mode(); //Should already be in bus-off mode, set again to make sure
assert(err == ESP_OK);
//Currently in listen only mode, need to set to mode specified by configuration
@ -835,7 +836,7 @@ esp_err_t can_stop(void)
CAN_CHECK_FROM_CRIT(!(p_can_obj->control_flags & (CTRL_FLAG_STOPPED | CTRL_FLAG_BUS_OFF)), ESP_ERR_INVALID_STATE);
//Clear interrupts and reset flags
esp_err_t err = can_exit_reset_mode();
esp_err_t err = can_enter_reset_mode();
assert(err == ESP_OK);
(void) can_get_interrupt_reason(); //Read interrupt register to clear interrupts
can_config_mode(CAN_MODE_LISTEN_ONLY); //Set to listen only mode to freeze REC

View file

@ -551,15 +551,32 @@ static ssize_t tcp_write(esp_tls_t *tls, const char *data, size_t datalen)
static ssize_t tls_write(esp_tls_t *tls, const char *data, size_t datalen)
{
ssize_t ret = mbedtls_ssl_write(&tls->ssl, (unsigned char*) data, datalen);
if (ret < 0) {
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_ESP, ESP_ERR_MBEDTLS_SSL_WRITE_FAILED);
ESP_LOGE(TAG, "write error :%d:", ret);
size_t written = 0;
size_t write_len = datalen;
while (written < datalen) {
if (write_len > MBEDTLS_SSL_OUT_CONTENT_LEN) {
write_len = MBEDTLS_SSL_OUT_CONTENT_LEN;
}
if (datalen > MBEDTLS_SSL_OUT_CONTENT_LEN) {
ESP_LOGD(TAG, "Fragmenting data of excessive size :%d, offset: %d, size %d", datalen, written, write_len);
}
ssize_t ret = mbedtls_ssl_write(&tls->ssl, (unsigned char*) data + written, write_len);
if (ret <= 0) {
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret != 0) {
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_ESP, ESP_ERR_MBEDTLS_SSL_WRITE_FAILED);
ESP_LOGE(TAG, "write error :%d:", ret);
return ret;
} else {
// Exitting the tls-write process as less than desired datalen are writable
ESP_LOGD(TAG, "mbedtls_ssl_write() returned %d, already written %d, exitting...", ret, written);
return written;
}
}
written += ret;
write_len = datalen - written;
}
return ret;
return written;
}
static int esp_tls_low_level_conn(const char *hostname, int hostlen, int port, const esp_tls_cfg_t *cfg, esp_tls_t *tls)

View file

@ -5,7 +5,7 @@
#include "esp32/rom/ets_sys.h"
#include "esp32/rom/lldesc.h"
#include "esp32/rom/gpio.h"
#include "driver/periph_ctrl.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
@ -33,8 +33,7 @@ the point where they happened to do what I want.
static void lcdIfaceInit(void)
{
DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_I2S0_CLK_EN);
DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_I2S0_RST);
periph_module_enable(PERIPH_I2S0_MODULE);
//Init pins to i2s functions
SET_PERI_REG_MASK(GPIO_ENABLE_W1TS_REG, (1 << 11) | (1 << 3) | (1 << 0) | (1 << 2) | (1 << 5) | (1 << 16) | (1 << 17) | (1 << 18) | (1 << 19) | (1 << 20)); //ENABLE GPIO oe_enable

View file

@ -6,7 +6,7 @@
#include "esp32/rom/ets_sys.h"
#include "esp32/rom/lldesc.h"
#include "esp32/rom/gpio.h"
#include "driver/periph_ctrl.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
@ -29,8 +29,7 @@ static volatile lldesc_t dmaDesc[2];
static void dmaMemcpy(void *in, void *out, int len)
{
volatile int i;
DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_I2S0_CLK_EN);
DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_I2S0_RST);
periph_module_enable(PERIPH_I2S0_MODULE);
//Init pins to i2s functions
SET_PERI_REG_MASK(GPIO_ENABLE_W1TS_REG, (1 << 11) | (1 << 3) | (1 << 0) | (1 << 2) | (1 << 5) | (1 << 16) | (1 << 17) | (1 << 18) | (1 << 19) | (1 << 20)); //ENABLE GPIO oe_enable

View file

@ -10,6 +10,10 @@
#include "esp_event.h"
#include "esp_eth.h"
#include "esp_log.h"
#include "lwip/inet.h"
#include "lwip/netdb.h"
#include "lwip/sockets.h"
#include "ping/ping_sock.h"
static const char *TAG = "esp_eth_test";
@ -17,11 +21,14 @@ static const char *TAG = "esp_eth_test";
#define ETH_STOP_BIT BIT(1)
#define ETH_CONNECT_BIT BIT(2)
#define ETH_GOT_IP_BIT BIT(3)
#define ETH_PING_END_BIT BIT(4)
#define ETH_START_TIMEOUT_MS (10000)
#define ETH_CONNECT_TIMEOUT_MS (40000)
#define ETH_STOP_TIMEOUT_MS (10000)
#define ETH_GET_IP_TIMEOUT_MS (60000)
#define ETH_PING_DURATION_MS (5000)
#define ETH_PING_END_TIMEOUT_MS (ETH_PING_DURATION_MS * 2)
/** Event handler for Ethernet events */
static void eth_event_handler(void *arg, esp_event_base_t event_base,
@ -66,6 +73,46 @@ static void got_ip_event_handler(void *arg, esp_event_base_t event_base,
xEventGroupSetBits(eth_event_group, ETH_GOT_IP_BIT);
}
static void test_on_ping_success(esp_ping_handle_t hdl, void *args)
{
uint8_t ttl;
uint16_t seqno;
uint32_t elapsed_time, recv_len;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_TTL, &ttl, sizeof(ttl));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len));
esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time));
printf("%d bytes from %s icmp_seq=%d ttl=%d time=%d ms\n",
recv_len, inet_ntoa(target_addr.u_addr.ip4), seqno, ttl, elapsed_time);
}
static void test_on_ping_timeout(esp_ping_handle_t hdl, void *args)
{
uint16_t seqno;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
printf("From %s icmp_seq=%d timeout\n", inet_ntoa(target_addr.u_addr.ip4), seqno);
}
static void test_on_ping_end(esp_ping_handle_t hdl, void *args)
{
EventGroupHandle_t eth_event_group = (EventGroupHandle_t)args;
uint32_t transmitted;
uint32_t received;
uint32_t total_time_ms;
esp_ping_get_profile(hdl, ESP_PING_PROF_REQUEST, &transmitted, sizeof(transmitted));
esp_ping_get_profile(hdl, ESP_PING_PROF_REPLY, &received, sizeof(received));
esp_ping_get_profile(hdl, ESP_PING_PROF_DURATION, &total_time_ms, sizeof(total_time_ms));
printf("%d packets transmitted, %d received, time %dms\n", transmitted, received, total_time_ms);
if (transmitted == received) {
xEventGroupSetBits(eth_event_group, ETH_PING_END_BIT);
}
}
TEST_CASE("esp32 ethernet io test", "[ethernet][test_env=UT_T2_Ethernet]")
{
TEST_ESP_OK(esp_event_loop_create_default());
@ -163,6 +210,83 @@ TEST_CASE("esp32 ethernet dhcp test", "[ethernet][test_env=UT_T2_Ethernet]")
vEventGroupDelete(eth_event_group);
}
TEST_CASE("esp32 ethernet icmp test", "[ethernet][test_env=UT_T2_Ethernet]")
{
EventBits_t bits = 0;
EventGroupHandle_t eth_event_group = xEventGroupCreate();
TEST_ASSERT(eth_event_group != NULL);
test_case_uses_tcpip();
TEST_ESP_OK(esp_event_loop_create_default());
TEST_ESP_OK(tcpip_adapter_set_default_eth_handlers());
TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, eth_event_group));
TEST_ESP_OK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, eth_event_group));
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config);
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config);
esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy);
esp_eth_handle_t eth_handle = NULL;
TEST_ESP_OK(esp_eth_driver_install(&eth_config, &eth_handle));
/* wait for IP lease */
bits = xEventGroupWaitBits(eth_event_group, ETH_GOT_IP_BIT, true, true, pdMS_TO_TICKS(ETH_GET_IP_TIMEOUT_MS));
TEST_ASSERT((bits & ETH_GOT_IP_BIT) == ETH_GOT_IP_BIT);
// Parse IP address
ip_addr_t target_addr;
struct addrinfo hint;
struct addrinfo *res = NULL;
memset(&hint, 0, sizeof(hint));
memset(&target_addr, 0, sizeof(target_addr));
/* convert URL to IP */
TEST_ASSERT(getaddrinfo("www.baidu.com", NULL, &hint, &res) == 0);
struct in_addr addr4 = ((struct sockaddr_in *) (res->ai_addr))->sin_addr;
inet_addr_to_ip4addr(ip_2_ip4(&target_addr), &addr4);
freeaddrinfo(res);
esp_ping_config_t ping_config = ESP_PING_DEFAULT_CONFIG();
ping_config.target_addr = target_addr;
ping_config.count = 0; // ping in infinite mode
/* set callback functions */
esp_ping_callbacks_t cbs;
cbs.on_ping_success = test_on_ping_success;
cbs.on_ping_timeout = test_on_ping_timeout;
cbs.on_ping_end = test_on_ping_end;
cbs.cb_args = eth_event_group;
esp_ping_handle_t ping;
TEST_ESP_OK(esp_ping_new_session(&ping_config, &cbs, &ping));
/* start ping */
TEST_ESP_OK(esp_ping_start(ping));
/* ping for a while */
vTaskDelay(pdMS_TO_TICKS(ETH_PING_DURATION_MS));
/* stop ping */
TEST_ESP_OK(esp_ping_stop(ping));
/* wait for end of ping */
bits = xEventGroupWaitBits(eth_event_group, ETH_PING_END_BIT, true, true, pdMS_TO_TICKS(ETH_PING_END_TIMEOUT_MS));
TEST_ASSERT((bits & ETH_PING_END_BIT) == ETH_PING_END_BIT);
/* restart ping */
TEST_ESP_OK(esp_ping_start(ping));
vTaskDelay(pdMS_TO_TICKS(ETH_PING_DURATION_MS));
TEST_ESP_OK(esp_ping_stop(ping));
bits = xEventGroupWaitBits(eth_event_group, ETH_PING_END_BIT, true, true, pdMS_TO_TICKS(ETH_PING_END_TIMEOUT_MS));
TEST_ASSERT((bits & ETH_PING_END_BIT) == ETH_PING_END_BIT);
/* de-initialize ping process */
TEST_ESP_OK(esp_ping_delete_session(ping));
TEST_ESP_OK(esp_eth_driver_uninstall(eth_handle));
/* wait for connection stop */
bits = xEventGroupWaitBits(eth_event_group, ETH_STOP_BIT, true, true, pdMS_TO_TICKS(ETH_STOP_TIMEOUT_MS));
TEST_ASSERT((bits & ETH_STOP_BIT) == ETH_STOP_BIT);
// "check link timer callback" might owned the reference of phy object, make sure it has release it
vTaskDelay(pdMS_TO_TICKS(2000));
TEST_ESP_OK(phy->del(phy));
TEST_ESP_OK(mac->del(mac));
TEST_ESP_OK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, got_ip_event_handler));
TEST_ESP_OK(tcpip_adapter_clear_default_eth_handlers());
TEST_ESP_OK(esp_event_loop_delete_default());
vEventGroupDelete(eth_event_group);
}
#if CONFIG_ETH_USE_SPI_ETHERNET
TEST_CASE("dm9051 io test", "[ethernet][ignore]")
{

View file

@ -466,3 +466,10 @@ void heap_caps_dump_all(void)
{
heap_caps_dump(MALLOC_CAP_INVALID);
}
size_t heap_caps_get_allocated_size( void *ptr )
{
heap_t *heap = find_containing_heap(ptr);
size_t size = multi_heap_get_allocated_size(heap->heap, ptr);
return size;
}

View file

@ -324,6 +324,19 @@ void heap_caps_dump(uint32_t caps);
*/
void heap_caps_dump_all(void);
/**
* @brief Return the size that a particular pointer was allocated with.
*
* @param ptr Pointer to currently allocated heap memory. Must be a pointer value previously
* returned by heap_caps_malloc,malloc,calloc, etc. and not yet freed.
*
* @note The app will crash with an assertion failure if the pointer is not valid.
*
* @return Size of the memory allocated at this block.
*
*/
size_t heap_caps_get_allocated_size( void *ptr );
#ifdef __cplusplus
}
#endif

View file

@ -10,6 +10,7 @@ set(srcs
"apps/dhcpserver/dhcpserver.c"
"apps/ping/esp_ping.c"
"apps/ping/ping.c"
"apps/ping/ping_sock.c"
"apps/sntp/sntp.c"
"lwip/src/api/api_lib.c"
"lwip/src/api/api_msg.c"

View file

@ -6,6 +6,14 @@ menu "LWIP"
help
The name this device will report to other devices on the network
config LWIP_DNS_SUPPORT_MDNS_QUERIES
bool "Enable mDNS queries in resolving host name"
default y
help
If this feature is enabled, standard API such as gethostbyname
support .local addresses by sending one shot multicast mDNS
query
config LWIP_L2_TO_L3_COPY
bool "Enable copy between Layer2 and Layer3 packets"
default n

View file

@ -5,7 +5,7 @@
*/
/*
* Redistribution and use in source and binary forms, with or without modification,
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@ -14,24 +14,24 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
*
*
*/
/**
/**
* This is an example of a "ping" sender (with raw API and socket API).
* It can be used as a start point to maintain opened a network connection, or
* like a network "watchdog" for your device.
@ -55,6 +55,7 @@
#if PING_USE_SOCKETS
#include "lwip/sockets.h"
#include "lwip/inet.h"
#include "ping/ping_sock.h"
#endif /* PING_USE_SOCKETS */
#ifdef ESP_PING
@ -98,20 +99,17 @@
#define PING_RESULT(ping_ok)
#endif
/* ping variables */
static u16_t ping_seq_num;
static struct timeval ping_time;
#if ESP_PING
static sys_sem_t ping_sem = NULL;
static bool ping_init_flag = false;
#endif
#if !PING_USE_SOCKETS
static struct raw_pcb *ping_pcb;
#endif /* PING_USE_SOCKETS */
#define PING_TIME_DIFF_MS(_end, _start) ((uint32_t)(((_end).tv_sec - (_start).tv_sec) * 1000 + (_end.tv_usec - _start.tv_usec)/1000))
#define PING_TIME_DIFF_SEC(_end, _start) ((uint32_t)((_end).tv_sec - (_start).tv_sec))
#if PING_USE_SOCKETS
/* ping handle */
static esp_ping_handle_t ping = NULL;
#else
static struct raw_pcb *ping_pcb;
static u16_t ping_seq_num;
static struct timeval ping_time;
/** Prepare a echo ICMP request */
static void
ping_prepare_echo( struct icmp_echo_hdr *iecho, u16_t len)
@ -133,213 +131,6 @@ ping_prepare_echo( struct icmp_echo_hdr *iecho, u16_t len)
iecho->chksum = inet_chksum(iecho, len);
}
#if PING_USE_SOCKETS
/* Ping using the socket ip */
static err_t
ping_send(int s, ip_addr_t *addr)
{
int err;
struct icmp_echo_hdr *iecho;
struct sockaddr_in to;
size_t ping_size;
#ifdef ESP_PING
size_t ping_data_len = 0;
esp_ping_get_target(PING_TARGET_DATA_LEN, &ping_data_len, sizeof(ping_data_len));
if (ping_data_len > 0) {
ping_size = sizeof(struct icmp_echo_hdr) + ping_data_len;
} else {
ping_size = sizeof(struct icmp_echo_hdr) + PING_DATA_SIZE;
}
#else
ping_size = sizeof(struct icmp_echo_hdr) + PING_DATA_SIZE;
#endif
LWIP_ASSERT("ping_size is too big", ping_size <= 0xffff);
LWIP_ASSERT("ping: expect IPv4 address", !IP_IS_V6(addr));
iecho = (struct icmp_echo_hdr *)mem_malloc((mem_size_t)ping_size);
if (!iecho) {
return ERR_MEM;
}
ping_prepare_echo(iecho, (u16_t)ping_size);
to.sin_len = sizeof(to);
to.sin_family = AF_INET;
inet_addr_from_ip4addr(&to.sin_addr, ip_2_ip4(addr));
err = sendto(s, iecho, ping_size, 0, (struct sockaddr*)&to, sizeof(to));
mem_free(iecho);
return (err ? ERR_OK : ERR_VAL);
}
static void
ping_recv(int s)
{
char buf[64];
int len;
struct sockaddr_in from;
struct ip_hdr *iphdr;
struct icmp_echo_hdr *iecho;
int fromlen = sizeof(from);
struct timeval now;
while((len = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr*)&from, (socklen_t*)&fromlen)) > 0) {
if (len >= (int)(sizeof(struct ip_hdr)+sizeof(struct icmp_echo_hdr))) {
if (from.sin_family != AF_INET) {
/* Ping is not IPv4 */
LWIP_DEBUGF( PING_DEBUG, ("ping: invalid sin_family\n"));
} else {
ip4_addr_t fromaddr;
inet_addr_to_ip4addr(&fromaddr, &from.sin_addr);
iphdr = (struct ip_hdr *)buf;
iecho = (struct icmp_echo_hdr *)(buf + (IPH_HL(iphdr) * 4));
LWIP_DEBUGF( PING_DEBUG, ("ping: recv seq=%d ", ntohs(iecho->seqno)));
ip4_addr_debug_print(PING_DEBUG, &fromaddr);
gettimeofday(&now, NULL);
LWIP_DEBUGF( PING_DEBUG, (" %"U32_F" ms\n", PING_TIME_DIFF_MS(now, ping_time)));
if ((iecho->id == PING_ID) && (iecho->seqno == htons(ping_seq_num))) {
/* do some ping result processing */
#ifdef ESP_PING
esp_ping_result((ICMPH_TYPE(iecho) == ICMP_ER), len, PING_TIME_DIFF_MS(now, ping_time));
#else
PING_RESULT((ICMPH_TYPE(iecho) == ICMP_ER));
#endif
return;
} else {
LWIP_DEBUGF( PING_DEBUG, ("ping: drop\n"));
}
}
}
fromlen = sizeof(from);
}
gettimeofday(&now, NULL);
if (len == 0) {
LWIP_DEBUGF( PING_DEBUG, ("ping: recv - %"U32_F" ms - timeout\n", PING_TIME_DIFF_MS(now, ping_time)));
}
/* do some ping result processing */
#ifdef ESP_PING
esp_ping_result(0, len, PING_TIME_DIFF_MS(now, ping_time));
#else
PING_RESULT(0);
#endif
}
static void
ping_thread(void *arg)
{
uint32_t ping_timeout = PING_RCV_TIMEO;
uint32_t ping_delay = PING_DELAY;
ip_addr_t ping_target;
int ret;
int s;
#ifdef ESP_PING
uint32_t ping_count_cur = 0;
uint32_t ping_count_max = 3;
ip4_addr_t ipaddr;
int lev;
esp_ping_get_target(PING_TARGET_IP_ADDRESS_COUNT, &ping_count_max, sizeof(ping_count_max));
esp_ping_get_target(PING_TARGET_RCV_TIMEO, &ping_timeout, sizeof(ping_timeout));
esp_ping_get_target(PING_TARGET_DELAY_TIME, &ping_delay, sizeof(ping_delay));
esp_ping_get_target(PING_TARGET_IP_ADDRESS, &ipaddr.addr, sizeof(uint32_t));
ip_addr_copy_from_ip4(ping_target, ipaddr);
#else
ip_addr_copy_from_ip4(ping_target, PING_TARGET);
#endif
#if LWIP_SO_SNDRCVTIMEO_NONSTANDARD
int timeout = ping_timeout;
#else
struct timeval timeout;
timeout.tv_sec = ping_timeout/1000;
timeout.tv_usec = (ping_timeout%1000)*1000;
#endif
LWIP_UNUSED_ARG(arg);
if ((s = socket(AF_INET, SOCK_RAW, IP_PROTO_ICMP)) < 0) {
goto _exit_new_socket_failed;
}
ret = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
LWIP_ASSERT("setting receive timeout failed", ret == 0);
LWIP_UNUSED_ARG(ret);
#ifdef ESP_PING
int tos = 0;
esp_ping_get_target(PING_TARGET_IP_TOS, &tos, sizeof(int));
if (tos > 0) {
tos <<= 5;
ret = setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
LWIP_ASSERT("setting IP_TOS failed", ret == 0);
LWIP_UNUSED_ARG(ret);
}
#endif
while (1) {
#ifdef ESP_PING
if (ping_count_cur++ >= ping_count_max) {
goto _exit;
}
if (ping_init_flag == false) {
goto _exit;
}
#endif
if (ping_send(s, &ping_target) == ERR_OK) {
LWIP_DEBUGF( PING_DEBUG, ("ping: send seq=%d ", ping_seq_num));
ip_addr_debug_print(PING_DEBUG, &ping_target);
LWIP_DEBUGF( PING_DEBUG, ("\n"));
gettimeofday(&ping_time, NULL);
ping_recv(s);
} else {
LWIP_DEBUGF( PING_DEBUG, ("ping: send "));
ip_addr_debug_print(PING_DEBUG, &ping_target);
LWIP_DEBUGF( PING_DEBUG, (" - error\n"));
}
sys_msleep(ping_delay);
}
#ifdef ESP_PING
_exit:
close(s);
_exit_new_socket_failed:
esp_ping_result(PING_RES_FINISH, 0, 0);
SYS_ARCH_PROTECT(lev);
if (ping_init_flag) { /* Ping closed by this thread */
LWIP_DEBUGF( PING_DEBUG, ("ping: closed by self "));
if (ping_sem) {
sys_sem_free(&ping_sem);
}
ping_sem = NULL;
ping_init_flag = false;
SYS_ARCH_UNPROTECT(lev);
} else { /* Ping closed by task calls ping_deinit */
LWIP_DEBUGF( PING_DEBUG, ("ping: closed by other"));
SYS_ARCH_UNPROTECT(lev);
if (ping_sem) {
sys_sem_signal(&ping_sem);
}
}
vTaskDelete(NULL);
#endif
}
#else /* PING_USE_SOCKETS */
/* Ping using the raw ip */
static u8_t
ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *addr)
@ -436,27 +227,72 @@ ping_send_now(void)
#endif /* PING_USE_SOCKETS */
/**
*
* Re-implement ping_init and ping_deinit with the APIs from ping_sock.h for back-ward compatibility sake.
* It's highly recommended that users should use the new APIs from ping_sock.h.
* ToDo: ping.h and esp_ping.h are deprecated now and should be removed in idf v5.x.
*
*/
static void
on_ping_success(esp_ping_handle_t hdl, void *args)
{
uint32_t elapsed_time, recv_len;
esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len));
esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time));
esp_ping_result(PING_RES_OK, recv_len, elapsed_time);
}
static void
on_ping_timeout(esp_ping_handle_t hdl, void *args)
{
uint32_t elapsed_time, recv_len;
esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len));
esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time));
esp_ping_result(PING_RES_TIMEOUT, recv_len, elapsed_time);
}
static void
on_ping_end(esp_ping_handle_t hdl, void *args)
{
esp_ping_result(PING_RES_FINISH, 0, 0);
esp_ping_delete_session(hdl);
}
int
ping_init(void)
{
int ret;
int lev;
SYS_ARCH_PROTECT(lev);
if (ping_init_flag) {
SYS_ARCH_UNPROTECT(lev);
/* Currently we only support one ping, call ping_deinit to kill the running ping before start new ping */
return ERR_INPROGRESS;
}
ret = sys_sem_new(&ping_sem, 0);
if (ERR_OK != ret) {
SYS_ARCH_UNPROTECT(lev);
return ERR_MEM;
}
ping_init_flag = true;
SYS_ARCH_UNPROTECT(lev);
#if PING_USE_SOCKETS
sys_thread_new("ping_thread", ping_thread, NULL, DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_PRIO);
uint32_t tos = 0;
uint32_t ping_timeout = PING_RCV_TIMEO;
uint32_t ping_delay = PING_DELAY;
uint32_t ping_count_max = 3;
ip_addr_t ping_target;
ip4_addr_t ipaddr;
esp_ping_get_target(PING_TARGET_IP_ADDRESS_COUNT, &ping_count_max, sizeof(ping_count_max));
esp_ping_get_target(PING_TARGET_RCV_TIMEO, &ping_timeout, sizeof(ping_timeout));
esp_ping_get_target(PING_TARGET_DELAY_TIME, &ping_delay, sizeof(ping_delay));
esp_ping_get_target(PING_TARGET_IP_ADDRESS, &ipaddr.addr, sizeof(uint32_t));
esp_ping_get_target(PING_TARGET_IP_TOS, &tos, sizeof(tos));
ip_addr_copy_from_ip4(ping_target, ipaddr);
esp_ping_config_t config = ESP_PING_DEFAULT_CONFIG();
config.count = ping_count_max;
config.timeout_ms = ping_timeout;
config.interval_ms = ping_delay;
config.target_addr = ping_target;
config.tos = tos;
esp_ping_callbacks_t cbs = {
.on_ping_end = on_ping_end,
.on_ping_success = on_ping_success,
.on_ping_timeout = on_ping_timeout,
};
esp_ping_new_session(&config, &cbs, &ping);
esp_ping_start(ping);
#else /* PING_USE_SOCKETS */
ping_raw_init();
#endif /* PING_USE_SOCKETS */
@ -466,24 +302,8 @@ ping_init(void)
void
ping_deinit(void)
{
int lev;
SYS_ARCH_PROTECT(lev);
if (ping_init_flag == false) {
SYS_ARCH_UNPROTECT(lev);
return;
}
ping_init_flag = false;
SYS_ARCH_UNPROTECT(lev);
if (ping_sem) {
sys_sem_wait(&ping_sem);
SYS_ARCH_PROTECT(lev);
sys_sem_free(&ping_sem);
ping_sem = NULL;
SYS_ARCH_UNPROTECT(lev);
}
esp_ping_stop(ping);
esp_ping_delete_session(ping);
}
#endif /* LWIP_IPV4 && LWIP_RAW */

View file

@ -0,0 +1,371 @@
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
//
// 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 <stdbool.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "lwip/opt.h"
#include "lwip/init.h"
#include "lwip/mem.h"
#include "lwip/icmp.h"
#include "lwip/netif.h"
#include "lwip/sys.h"
#include "lwip/timeouts.h"
#include "lwip/inet.h"
#include "lwip/inet_chksum.h"
#include "lwip/ip.h"
#include "lwip/netdb.h"
#include "lwip/sockets.h"
#include "esp_log.h"
#include "ping/ping_sock.h"
const static char *TAG = "ping_sock";
#define PING_CHECK(a, str, goto_tag, ret_value, ...) \
do \
{ \
if (!(a)) \
{ \
ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
ret = ret_value; \
goto goto_tag; \
} \
} while (0)
#define PING_TIME_DIFF_MS(_end, _start) ((uint32_t)(((_end).tv_sec - (_start).tv_sec) * 1000 + \
((_end).tv_usec - (_start).tv_usec) / 1000))
#define PING_CHECK_START_TIMEOUT_MS (1000)
#define PING_FLAGS_INIT (1 << 0)
#define PING_FLAGS_START (1 << 1)
typedef struct {
int sock;
struct sockaddr_storage target_addr;
TaskHandle_t ping_task_hdl;
struct icmp_echo_hdr *packet_hdr;
ip_addr_t recv_addr;
uint32_t recv_len;
uint32_t icmp_pkt_size;
uint32_t count;
uint32_t transmitted;
uint32_t received;
uint32_t interval_ms;
uint32_t elapsed_time_ms;
uint32_t total_time_ms;
uint8_t ttl;
uint32_t flags;
void (*on_ping_success)(esp_ping_handle_t hdl, void *args);
void (*on_ping_timeout)(esp_ping_handle_t hdl, void *args);
void (*on_ping_end)(esp_ping_handle_t hdl, void *args);
void *cb_args;
} esp_ping_t;
static esp_err_t esp_ping_send(esp_ping_t *ep)
{
esp_err_t ret = ESP_OK;
ep->packet_hdr->seqno++;
/* generate checksum since "seqno" has changed */
ep->packet_hdr->chksum = 0;
ep->packet_hdr->chksum = inet_chksum(ep->packet_hdr, ep->icmp_pkt_size);
int sent = sendto(ep->sock, ep->packet_hdr, ep->icmp_pkt_size, 0,
(struct sockaddr *)&ep->target_addr, sizeof(ep->target_addr));
if (sent != ep->icmp_pkt_size) {
int opt_val;
socklen_t opt_len = sizeof(opt_val);
getsockopt(ep->sock, SOL_SOCKET, SO_ERROR, &opt_val, &opt_len);
ESP_LOGE(TAG, "send error=%d", opt_val);
ret = ESP_FAIL;
} else {
ep->transmitted++;
}
return ret;
}
static int esp_ping_receive(esp_ping_t *ep)
{
char buf[64]; // 64 bytes are enough to cover IP header and ICMP header
int len = 0;
struct sockaddr_storage from;
int fromlen = sizeof(from);
while ((len = recvfrom(ep->sock, buf, sizeof(buf), 0, (struct sockaddr *)&from, (socklen_t *)&fromlen)) > 0) {
if (len >= (int)(sizeof(struct ip_hdr) + sizeof(struct icmp_echo_hdr))) {
ep->recv_len = (uint32_t)len;
if (from.ss_family == AF_INET) {
// IPv4
struct sockaddr_in *from4 = (struct sockaddr_in *)&from;
inet_addr_to_ip4addr(ip_2_ip4(&ep->recv_addr), &from4->sin_addr);
IP_SET_TYPE_VAL(ep->recv_addr, IPADDR_TYPE_V4);
} else {
// IPv6
struct sockaddr_in6 *from6 = (struct sockaddr_in6 *)&from;
inet6_addr_to_ip6addr(ip_2_ip6(&ep->recv_addr), &from6->sin6_addr);
IP_SET_TYPE_VAL(ep->recv_addr, IPADDR_TYPE_V6);
}
// Currently we only process IPv4
if (IP_IS_V4_VAL(ep->recv_addr)) {
struct ip_hdr *iphdr = (struct ip_hdr *)buf;
struct icmp_echo_hdr *iecho = (struct icmp_echo_hdr *)(buf + (IPH_HL(iphdr) * 4));
if ((iecho->id == ep->packet_hdr->id) && (iecho->seqno == ep->packet_hdr->seqno)) {
ep->received++;
ep->ttl = iphdr->_ttl;
return len;
}
}
}
fromlen = sizeof(from);
}
// if timeout, len will be -1
return len;
}
static void esp_ping_thread(void *args)
{
esp_ping_t *ep = (esp_ping_t *)(args);
TickType_t last_wake;
struct timeval start_time, end_time;
int recv_ret;
while (1) {
/* wait for ping start signal */
if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(PING_CHECK_START_TIMEOUT_MS))) {
/* initialize runtime statistics */
ep->packet_hdr->seqno = 0;
ep->transmitted = 0;
ep->received = 0;
ep->total_time_ms = 0;
last_wake = xTaskGetTickCount();
while ((ep->flags & PING_FLAGS_START) && ((ep->count == 0) || (ep->packet_hdr->seqno < ep->count))) {
esp_ping_send(ep);
gettimeofday(&start_time, NULL);
recv_ret = esp_ping_receive(ep);
gettimeofday(&end_time, NULL);
ep->elapsed_time_ms = PING_TIME_DIFF_MS(end_time, start_time);
ep->total_time_ms += ep->elapsed_time_ms;
if (recv_ret >= 0) {
if (ep->on_ping_success) {
ep->on_ping_success((esp_ping_handle_t)ep, ep->cb_args);
}
} else {
if (ep->on_ping_timeout) {
ep->on_ping_timeout((esp_ping_handle_t)ep, ep->cb_args);
}
}
vTaskDelayUntil(&last_wake, pdMS_TO_TICKS(ep->interval_ms)); // to get a more accurate delay
}
/* batch of ping operations finished */
if (ep->on_ping_end) {
ep->on_ping_end((esp_ping_handle_t)ep, ep->cb_args);
}
} else {
// check if ping has been de-initialized
if (!(ep->flags & PING_FLAGS_INIT)) {
break;
}
}
}
/* before exit task, free all resources */
if (ep->packet_hdr) {
free(ep->packet_hdr);
}
if (ep->sock > 0) {
close(ep->sock);
}
free(ep);
vTaskDelete(NULL);
}
esp_err_t esp_ping_new_session(const esp_ping_config_t *config, const esp_ping_callbacks_t *cbs, esp_ping_handle_t *hdl_out)
{
esp_err_t ret = ESP_OK;
esp_ping_t *ep = NULL;
PING_CHECK(config, "ping config can't be null", err, ESP_ERR_INVALID_ARG);
PING_CHECK(hdl_out, "ping handle can't be null", err, ESP_ERR_INVALID_ARG);
ep = mem_calloc(1, sizeof(esp_ping_t));
PING_CHECK(ep, "no memory for esp_ping object", err, ESP_ERR_NO_MEM);
/* set INIT flag, so that ping task won't exit (must set before create ping task) */
ep->flags |= PING_FLAGS_INIT;
/* create ping thread */
BaseType_t xReturned = xTaskCreate(esp_ping_thread, "ping", config->task_stack_size, ep,
config->task_prio, &ep->ping_task_hdl);
PING_CHECK(xReturned == pdTRUE, "create ping task failed", err, ESP_ERR_NO_MEM);
/* callback functions */
if (cbs) {
ep->cb_args = cbs->cb_args;
ep->on_ping_end = cbs->on_ping_end;
ep->on_ping_timeout = cbs->on_ping_timeout;
ep->on_ping_success = cbs->on_ping_success;
}
/* set parameters for ping */
ep->recv_addr = config->target_addr;
ep->count = config->count;
ep->interval_ms = config->interval_ms;
ep->icmp_pkt_size = sizeof(struct icmp_echo_hdr) + config->data_size;
ep->packet_hdr = mem_calloc(1, ep->icmp_pkt_size);
PING_CHECK(ep->packet_hdr, "no memory for echo packet", err, ESP_ERR_NO_MEM);
/* set ICMP type and code field */
ep->packet_hdr->type = ICMP_ECHO;
ep->packet_hdr->code = 0;
/* ping id should be unique, treat task handle as ping ID */
ep->packet_hdr->id = ((uint32_t)ep->ping_task_hdl) & 0xFFFF;
/* fill the additional data buffer with some data */
char *d = (char *)(ep->packet_hdr) + sizeof(struct icmp_echo_hdr);
for (uint32_t i = 0; i < config->data_size; i++) {
d[i] = 'A' + i;
}
/* create socket */
if (IP_IS_V4(&config->target_addr) || ip6_addr_isipv4mappedipv6(ip_2_ip6(&config->target_addr))) {
ep->sock = socket(AF_INET, SOCK_RAW, IP_PROTO_ICMP);
} else {
ep->sock = socket(AF_INET6, SOCK_RAW, IP6_NEXTH_ICMP6);
}
PING_CHECK(ep->sock > 0, "create socket failed: %d", err, ESP_FAIL, ep->sock);
struct timeval timeout;
timeout.tv_sec = config->timeout_ms / 1000;
timeout.tv_usec = (config->timeout_ms % 1000) * 1000;
/* set receive timeout */
setsockopt(ep->sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
/* set tos */
setsockopt(ep->sock, IPPROTO_IP, IP_TOS, &config->tos, sizeof(config->tos));
/* set socket address */
if (IP_IS_V4(&config->target_addr)) {
struct sockaddr_in *to4 = (struct sockaddr_in *)&ep->target_addr;
to4->sin_family = AF_INET;
inet_addr_from_ip4addr(&to4->sin_addr, ip_2_ip4(&config->target_addr));
}
if (IP_IS_V6(&config->target_addr)) {
struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&ep->target_addr;
to6->sin6_family = AF_INET6;
inet6_addr_from_ip6addr(&to6->sin6_addr, ip_2_ip6(&config->target_addr));
}
/* return ping handle to user */
*hdl_out = (esp_ping_handle_t)ep;
return ESP_OK;
err:
if (ep) {
if (ep->sock > 0) {
close(ep->sock);
}
if (ep->packet_hdr) {
free(ep->packet_hdr);
}
if (ep->ping_task_hdl) {
vTaskDelete(ep->ping_task_hdl);
}
free(ep);
}
return ret;
}
esp_err_t esp_ping_delete_session(esp_ping_handle_t hdl)
{
esp_err_t ret = ESP_OK;
esp_ping_t *ep = (esp_ping_t *)hdl;
PING_CHECK(ep, "ping handle can't be null", err, ESP_ERR_INVALID_ARG);
/* reset init flags, then ping task will exit */
ep->flags &= ~PING_FLAGS_INIT;
return ESP_OK;
err:
return ret;
}
esp_err_t esp_ping_start(esp_ping_handle_t hdl)
{
esp_err_t ret = ESP_OK;
esp_ping_t *ep = (esp_ping_t *)hdl;
PING_CHECK(ep, "ping handle can't be null", err, ESP_ERR_INVALID_ARG);
ep->flags |= PING_FLAGS_START;
xTaskNotifyGive(ep->ping_task_hdl);
return ESP_OK;
err:
return ret;
}
esp_err_t esp_ping_stop(esp_ping_handle_t hdl)
{
esp_err_t ret = ESP_OK;
esp_ping_t *ep = (esp_ping_t *)hdl;
PING_CHECK(ep, "ping handle can't be null", err, ESP_ERR_INVALID_ARG);
ep->flags &= ~PING_FLAGS_START;
return ESP_OK;
err:
return ret;
}
esp_err_t esp_ping_get_profile(esp_ping_handle_t hdl, esp_ping_profile_t profile, void *data, uint32_t size)
{
esp_err_t ret = ESP_OK;
esp_ping_t *ep = (esp_ping_t *)hdl;
const void *from = NULL;
uint32_t copy_size = 0;
PING_CHECK(ep, "ping handle can't be null", err, ESP_ERR_INVALID_ARG);
PING_CHECK(data, "profile data can't be null", err, ESP_ERR_INVALID_ARG);
switch (profile) {
case ESP_PING_PROF_SEQNO:
from = &ep->packet_hdr->seqno;
copy_size = sizeof(ep->packet_hdr->seqno);
break;
case ESP_PING_PROF_TTL:
from = &ep->ttl;
copy_size = sizeof(ep->ttl);
break;
case ESP_PING_PROF_REQUEST:
from = &ep->transmitted;
copy_size = sizeof(ep->transmitted);
break;
case ESP_PING_PROF_REPLY:
from = &ep->received;
copy_size = sizeof(ep->received);
break;
case ESP_PING_PROF_IPADDR:
from = &ep->recv_addr;
copy_size = sizeof(ep->recv_addr);
break;
case ESP_PING_PROF_SIZE:
from = &ep->recv_len;
copy_size = sizeof(ep->recv_len);
break;
case ESP_PING_PROF_TIMEGAP:
from = &ep->elapsed_time_ms;
copy_size = sizeof(ep->elapsed_time_ms);
break;
case ESP_PING_PROF_DURATION:
from = &ep->total_time_ms;
copy_size = sizeof(ep->total_time_ms);
break;
default:
PING_CHECK(false, "unknow profile: %d", err, ESP_ERR_INVALID_ARG, profile);
break;
}
PING_CHECK(size >= copy_size, "unmatched data size for profile %d", err, ESP_ERR_INVALID_SIZE, profile);
memcpy(data, from, copy_size);
return ESP_OK;
err:
return ret;
}

View file

@ -59,7 +59,7 @@ typedef enum {
typedef enum {
PING_RES_TIMEOUT = 0,
PING_RES_OK = 1,
PING_RES_OK = 1,
PING_RES_FINISH = 2,
} ping_res_t;

View file

@ -43,10 +43,10 @@ extern "C" {
#endif
int ping_init(void);
int ping_init(void) __attribute__ ((deprecated));
#ifdef ESP_PING
void ping_deinit(void);
void ping_deinit(void) __attribute__ ((deprecated));
#endif
#if !PING_USE_SOCKETS

View file

@ -0,0 +1,169 @@
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
//
// 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.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "esp_err.h"
#include "lwip/ip_addr.h"
/**
* @brief Type of "ping" session handle
*
*/
typedef void *esp_ping_handle_t;
/**
* @brief Type of "ping" callback functions
*
*/
typedef struct {
/**
* @brief arguments for callback functions
*
*/
void *cb_args;
/**
* @brief Invoked by internal ping thread when received ICMP echo reply packet
*
*/
void (*on_ping_success)(esp_ping_handle_t hdl, void *args);
/**
* @brief Invoked by internal ping thread when receive ICMP echo reply packet timeout
*
*/
void (*on_ping_timeout)(esp_ping_handle_t hdl, void *args);
/**
* @brief Invoked by internal ping thread when a ping session is finished
*
*/
void (*on_ping_end)(esp_ping_handle_t hdl, void *args);
} esp_ping_callbacks_t;
/**
* @brief Type of "ping" configuration
*
*/
typedef struct {
uint32_t count; /*!< A "ping" session contains count procedures */
uint32_t interval_ms; /*!< Milliseconds between each ping procedure */
uint32_t timeout_ms; /*!< Timeout value (in milliseconds) of each ping procedure */
uint32_t data_size; /*!< Size of the data next to ICMP packet header */
uint8_t tos; /*!< Type of Service, a field specified in the IP header */
ip_addr_t target_addr; /*!< Target IP address, either IPv4 or IPv6 */
uint32_t task_stack_size; /*!< Stack size of internal ping task */
uint32_t task_prio; /*!< Priority of internal ping task */
} esp_ping_config_t;
/**
* @brief Default ping configuration
*
*/
#define ESP_PING_DEFAULT_CONFIG() \
{ \
.count = 5, \
.interval_ms = 1000, \
.timeout_ms = 1000, \
.data_size = 56, \
.tos = 0, \
.target_addr = ip_addr_any_type, \
.task_stack_size = 2048, \
.task_prio = 2, \
}
#define ESP_PING_COUNT_INFINITE (0) /*!< Set ping count to zero will ping target infinitely */
/**
* @brief Profile of ping session
*
*/
typedef enum {
ESP_PING_PROF_SEQNO, /*!< Sequence number of a ping procedure */
ESP_PING_PROF_TTL, /*!< Time to live of a ping procedure */
ESP_PING_PROF_REQUEST, /*!< Number of request packets sent out */
ESP_PING_PROF_REPLY, /*!< Number of reply packets received */
ESP_PING_PROF_IPADDR, /*!< IP address of replied target */
ESP_PING_PROF_SIZE, /*!< Size of received packet */
ESP_PING_PROF_TIMEGAP, /*!< Elapsed time between request and reply packet */
ESP_PING_PROF_DURATION /*!< Elapsed time of the whole ping session */
} esp_ping_profile_t;
/**
* @brief Create a ping session
*
* @param config ping configuration
* @param cbs a bunch of callback functions invoked by internal ping task
* @param hdl_out handle of ping session
* @return
* - ESP_ERR_INVALID_ARG: invalid parameters (e.g. configuration is null, etc)
* - ESP_ERR_NO_MEM: out of memory
* - ESP_FAIL: other internal error (e.g. socket error)
* - ESP_OK: create ping session successfully, user can take the ping handle to do follow-on jobs
*/
esp_err_t esp_ping_new_session(const esp_ping_config_t *config, const esp_ping_callbacks_t *cbs, esp_ping_handle_t *hdl_out);
/**
* @brief Delete a ping session
*
* @param hdl handle of ping session
* @return
* - ESP_ERR_INVALID_ARG: invalid parameters (e.g. ping handle is null, etc)
* - ESP_OK: delete ping session successfully
*/
esp_err_t esp_ping_delete_session(esp_ping_handle_t hdl);
/**
* @brief Start the ping session
*
* @param hdl handle of ping session
* @return
* - ESP_ERR_INVALID_ARG: invalid parameters (e.g. ping handle is null, etc)
* - ESP_OK: start ping session successfully
*/
esp_err_t esp_ping_start(esp_ping_handle_t hdl);
/**
* @brief Stop the ping session
*
* @param hdl handle of ping session
* @return
* - ESP_ERR_INVALID_ARG: invalid parameters (e.g. ping handle is null, etc)
* - ESP_OK: stop ping session successfully
*/
esp_err_t esp_ping_stop(esp_ping_handle_t hdl);
/**
* @brief Get runtime profile of ping session
*
* @param hdl handle of ping session
* @param profile type of profile
* @param data profile data
* @param size profile data size
* @return
* - ESP_ERR_INVALID_ARG: invalid parameters (e.g. ping handle is null, etc)
* - ESP_ERR_INVALID_SIZE: the actual profile data size doesn't match the "size" parameter
* - ESP_OK: get profile successfully
*/
esp_err_t esp_ping_get_profile(esp_ping_handle_t hdl, esp_ping_profile_t profile, void *data, uint32_t size);
#ifdef __cplusplus
}
#endif

View file

@ -549,6 +549,12 @@
*/
#define SO_REUSE CONFIG_LWIP_SO_REUSE
/**
* LWIP_DNS_SUPPORT_MDNS_QUERIES==1: Enable mDNS queries in hostname resolution.
* This option is set via menuconfig.
*/
#define LWIP_DNS_SUPPORT_MDNS_QUERIES CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES
/**
* SO_REUSE_RXTOALL==1: Pass a copy of incoming broadcast/multicast packets
* to all local matches if SO_REUSEADDR is turned on.

View file

@ -2,21 +2,21 @@ SPIFFSGEN_PY:=$(COMPONENT_PATH)/spiffsgen.py
SPIFFSGEN_FLASH_IN_PROJECT=
ifdef CONFIG_SPIFFS_USE_MAGIC
USE_MAGIC = "--use-magic"
USE_MAGIC := "--use-magic"
else
USE_MAGIC = ""
USE_MAGIC :=
endif
ifdef CONFIG_SPIFFS_USE_MAGIC_LENGTH
USE_MAGIC_LEN = "--use-magic-len"
USE_MAGIC_LEN := "--use-magic-len"
else
USE_MAGIC_LEN = ""
USE_MAGIC_LEN :=
endif
ifdef CONFIG_SPIFFS_FOLLOW_SYMLINKS
FOLLOW_SYMLINKS = "--follow-symlinks"
FOLLOW_SYMLINKS := "--follow-symlinks"
else
FOLLOW_SYMLINKS = ""
FOLLOW_SYMLINKS :=
endif
# spiffs_create_partition_image

View file

@ -108,6 +108,10 @@ given VFS driver.
:cpp:func:`end_select` is called to stop/deinitialize/free the
environment which was setup by :cpp:func:`start_select`.
.. note::
:cpp:func:`end_select` might be called without a previous :cpp:func:`start_select` call in some rare
circumstances. :cpp:func:`end_select` should fail gracefully if this is the case.
Please refer to the
reference implementation for the UART peripheral in
:component_file:`vfs/vfs_uart.c` and most particularly to the functions
@ -155,6 +159,10 @@ Please see :component_file:`lwip/port/esp32/vfs_lwip.c` for a reference socket d
If you use :cpp:func:`select` for socket file descriptors only then you can enable the
:envvar:`CONFIG_LWIP_USE_ONLY_LWIP_SELECT` option to reduce the code size and improve performance.
.. note::
Don't change the socket driver during an active :cpp:func:`select` call or you might experience some undefined
behavior.
Paths
-----

View file

@ -21,6 +21,7 @@
#include "driver/uart.h"
#include "esp_vfs.h"
#include "esp_vfs_dev.h"
#include "esp_vfs_fat.h"
#include "lwip/sockets.h"
#include "lwip/netdb.h"
#include "test_utils.h"
@ -549,3 +550,65 @@ TEST_CASE("concurrent selects work", "[vfs]")
deinit(uart_fd, socket_fd);
close(dummy_socket_fd);
}
TEST_CASE("select() works with concurrent mount", "[vfs][fatfs]")
{
wl_handle_t test_wl_handle;
int uart_fd, socket_fd;
init(&uart_fd, &socket_fd);
const int dummy_socket_fd = open_dummy_socket();
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.format_if_mount_failed = true,
.max_files = 2
};
// select() will be waiting for a socket & UART and FATFS mount will occur in parallel
struct timeval tv = {
.tv_sec = 1,
.tv_usec = 0,
};
fd_set rdfds;
FD_ZERO(&rdfds);
FD_SET(uart_fd, &rdfds);
FD_SET(dummy_socket_fd, &rdfds);
test_select_task_param_t param = {
.rdfds = &rdfds,
.wrfds = NULL,
.errfds = NULL,
.maxfds = MAX(uart_fd, dummy_socket_fd) + 1,
.tv = &tv,
.select_ret = 0, // expected timeout
.sem = xSemaphoreCreateBinary(),
};
TEST_ASSERT_NOT_NULL(param.sem);
start_select_task(&param);
vTaskDelay(10 / portTICK_PERIOD_MS); //make sure the task has started and waits in select()
TEST_ESP_OK(esp_vfs_fat_spiflash_mount("/spiflash", NULL, &mount_config, &test_wl_handle));
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(param.sem, 1500 / portTICK_PERIOD_MS));
// select() will be waiting for a socket & UART and FATFS unmount will occur in parallel
FD_ZERO(&rdfds);
FD_SET(uart_fd, &rdfds);
FD_SET(dummy_socket_fd, &rdfds);
start_select_task(&param);
vTaskDelay(10 / portTICK_PERIOD_MS); //make sure the task has started and waits in select()
TEST_ESP_OK(esp_vfs_fat_spiflash_unmount("/spiflash", test_wl_handle));
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(param.sem, 1500 / portTICK_PERIOD_MS));
vSemaphoreDelete(param.sem);
deinit(uart_fd, socket_fd);
close(dummy_socket_fd);
}

View file

@ -877,8 +877,12 @@ int esp_vfs_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds
return -1;
}
// Capture s_vfs_count to a local variable in case a new driver is registered or removed during this actual select()
// call. s_vfs_count cannot be protected with a mutex during a select() call (which can be one without a timeout)
// because that could block the registration of new driver.
const size_t vfs_count = s_vfs_count;
fds_triple_t *vfs_fds_triple;
if ((vfs_fds_triple = calloc(s_vfs_count, sizeof(fds_triple_t))) == NULL) {
if ((vfs_fds_triple = calloc(vfs_count, sizeof(fds_triple_t))) == NULL) {
__errno_r(r) = ENOMEM;
ESP_LOGD(TAG, "calloc is unsuccessful");
return -1;
@ -952,7 +956,7 @@ int esp_vfs_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds
}
}
void **driver_args = calloc(s_vfs_count, sizeof(void *));
void **driver_args = calloc(vfs_count, sizeof(void *));
if (driver_args == NULL) {
free(vfs_fds_triple);
@ -961,7 +965,7 @@ int esp_vfs_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds
return -1;
}
for (int i = 0; i < s_vfs_count; ++i) {
for (int i = 0; i < vfs_count; ++i) {
const vfs_entry_t *vfs = get_vfs_for_index(i);
fds_triple_t *item = &vfs_fds_triple[i];
@ -977,7 +981,7 @@ int esp_vfs_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds
if (err != ESP_OK) {
call_end_selects(i, vfs_fds_triple, driver_args);
(void) set_global_fd_sets(vfs_fds_triple, s_vfs_count, readfds, writefds, errorfds);
(void) set_global_fd_sets(vfs_fds_triple, vfs_count, readfds, writefds, errorfds);
if (sel_sem.is_sem_local && sel_sem.sem) {
vSemaphoreDelete(sel_sem.sem);
sel_sem.sem = NULL;
@ -1022,9 +1026,9 @@ int esp_vfs_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds
xSemaphoreTake(sel_sem.sem, ticks_to_wait);
}
call_end_selects(s_vfs_count, vfs_fds_triple, driver_args); // for VFSs for start_select was called before
call_end_selects(vfs_count, vfs_fds_triple, driver_args); // for VFSs for start_select was called before
if (ret >= 0) {
ret += set_global_fd_sets(vfs_fds_triple, s_vfs_count, readfds, writefds, errorfds);
ret += set_global_fd_sets(vfs_fds_triple, vfs_count, readfds, writefds, errorfds);
}
if (sel_sem.is_sem_local && sel_sem.sem) {
vSemaphoreDelete(sel_sem.sem);
@ -1049,6 +1053,8 @@ void esp_vfs_select_triggered(esp_vfs_select_sem_t sem)
// which has a permanent FD. But in order to avoid to lock
// s_fd_table_lock we go through the VFS table.
for (int i = 0; i < s_vfs_count; ++i) {
// Note: s_vfs_count could have changed since the start of vfs_select() call. However, that change doesn't
// matter here stop_socket_select() will be called for only valid VFS drivers.
const vfs_entry_t *vfs = s_vfs[i];
if (vfs != NULL && vfs->vfs.stop_socket_select != NULL) {
vfs->vfs.stop_socket_select(sem.sem);
@ -1067,6 +1073,8 @@ void esp_vfs_select_triggered_isr(esp_vfs_select_sem_t sem, BaseType_t *woken)
// which has a permanent FD. But in order to avoid to lock
// s_fd_table_lock we go through the VFS table.
for (int i = 0; i < s_vfs_count; ++i) {
// Note: s_vfs_count could have changed since the start of vfs_select() call. However, that change doesn't
// matter here stop_socket_select() will be called for only valid VFS drivers.
const vfs_entry_t *vfs = s_vfs[i];
if (vfs != NULL && vfs->vfs.stop_socket_select_isr != NULL) {
vfs->vfs.stop_socket_select_isr(sem.sem, woken);

View file

@ -118,6 +118,8 @@ INPUT = \
../../components/esp-tls/esp_tls.h \
## MQTT
../../components/mqtt/esp-mqtt/include/mqtt_client.h \
## ICMP-ECHO
../../components/lwip/include/apps/ping/ping_sock.h \
## mDNS
../../components/mdns/include/mdns.h \
../../components/esp_http_client/include/esp_http_client.h \

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View file

@ -690,12 +690,15 @@ depending on the options selected in the project configuration.
``CMakeLists.txt``::
set(COMPONENT_SRCS "foo.c" "more_foo.c")
set(srcs "foo.c" "more_foo.c")
if(CONFIG_FOO_ENABLE_BAR)
list(APPEND COMPONENT_SRCS "bar.c")
list(APPEND srcs "bar.c")
endif()
idf_component_register(SRCS "${srcs}"
...)
This example makes use of the CMake `if <cmake if_>`_ function and `list APPEND <cmake list_>`_ function.
This can also be used to select or stub out an implementation, as such:
@ -724,16 +727,18 @@ This can also be used to select or stub out an implementation, as such:
``CMakeLists.txt``::
if(CONFIG_ENABLE_LCD_OUTPUT)
set(COMPONENT_SRCS lcd-real.c lcd-spi.c)
set(srcs lcd-real.c lcd-spi.c)
else()
set(COMPONENT_SRCS lcd-dummy.c)
set(srcs lcd-dummy.c)
endif()
# We need font if either console or plot is enabled
if(CONFIG_ENABLE_LCD_CONSOLE OR CONFIG_ENABLE_LCD_PLOT)
list(APPEND COMPONENT_SRCS "font.c")
list(APPEND srcs "font.c")
endif()
idf_component_register(SRCS "${srcs}"
...)
Conditions which depend on the target
-------------------------------------
@ -788,13 +793,13 @@ Embedding Binary Data
Sometimes you have a file with some binary or text data that you'd like to make available to your component - but you don't want to reformat the file as C source.
You can specify argument ``COMPONENT_EMBED_FILES`` in the component registration, giving space-delimited names of the files to embed::
You can specify argument ``EMBED_FILES`` in the component registration, giving space-delimited names of the files to embed::
idf_component_register(...
EMBED_FILES server_root_cert.der)
Or if the file is a string, you can use the variable ``COMPONENT_EMBED_TXTFILES``. This will embed the contents of the text file as a null-terminated string::
Or if the file is a string, you can use the variable ``EMBED_TXTFILES``. This will embed the contents of the text file as a null-terminated string::
idf_component_register(...
EMBED_TXTFILES server_root_cert.pem)
@ -806,7 +811,7 @@ The file's contents will be added to the .rodata section in flash, and are avail
extern const uint8_t server_root_cert_pem_start[] asm("_binary_server_root_cert_pem_start");
extern const uint8_t server_root_cert_pem_end[] asm("_binary_server_root_cert_pem_end");
The names are generated from the full name of the file, as given in ``COMPONENT_EMBED_FILES``. Characters /, ., etc. are replaced with underscores. The _binary prefix in the symbol name is added by objcopy and is the same for both text and binary files.
The names are generated from the full name of the file, as given in ``EMBED_FILES``. Characters /, ., etc. are replaced with underscores. The _binary prefix in the symbol name is added by objcopy and is the same for both text and binary files.
.. highlight:: cmake
@ -982,7 +987,7 @@ not yet be provided by a component, or use another library for the same function
Importing a library might look like this for a hypothetical library ``foo`` to be used in the ``main`` component::
# Register the component
idf_component_register()
idf_component_register(...)
# Set values of hypothetical variables that control the build of `foo`
set(FOO_BUILD_STATIC OFF)
@ -991,8 +996,8 @@ Importing a library might look like this for a hypothetical library ``foo`` to b
# Create and import the library targets
add_subdirectory(foo)
# Link `foo` to `main` component
target_link_libraries(main foo)
# Publicly link `foo` to `main` component
target_link_libraries(main PUBLIC foo)
For an actual example, take a look at :example:`build_system/cmake/import_lib`. Take note that what needs to be done in order to import
the library may vary. It is recommended to read up on the library's documentation for instructions on how to
@ -1278,7 +1283,7 @@ For project components (not part of ESP-IDF), there are a few different options:
- If keeping your project file in Git, ESP-IDF will automatically track the Git revision and re-run CMake if the revision changes.
- If some components are kept in a third git repository (not the project repository or ESP-IDF repository), you can add a call to the ``git_describe`` function in a component CMakeLists file in order to automatically trigger re-runs of CMake when the Git revision changes.
- If not using Git, remember to manually run ``idf.py reconfigure`` whenever a source file may change.
- To avoid this problem entirely, use ``COMPONENT_SRCS`` to list all source files in project components.
- To avoid this problem entirely, use ``SRCS`` argument to ``idf_component_register`` to list all source files in project components.
The best option will depend on your particular project and its users.
@ -1429,7 +1434,10 @@ Browse :idf_file:`/tools/cmake/project.cmake` for more details.
Migrating from ESP-IDF GNU Make System
======================================
Some aspects of the CMake-based ESP-IDF build system are very similar to the older GNU Make-based system. For example, to adapt a ``component.mk`` file to ``CMakeLists.txt`` variables like ``COMPONENT_ADD_INCLUDEDIRS`` and ``COMPONENT_SRCDIRS`` can stay the same and the syntax only needs changing to CMake syntax.
Some aspects of the CMake-based ESP-IDF build system are very similar to the older GNU Make-based system. The developer needs to provide values
the include directories, source files etc. There is a syntactical difference, however, as the developer needs to pass these as arguments
to the registration command, `idf_component_register`.
Automatic Conversion Tool
-------------------------
@ -1460,26 +1468,27 @@ Some features are significantly different or removed in the CMake-based system.
- ``COMPONENT_ADD_LDFLAGS``: Used to override linker flags. Use the CMake `target_link_libraries`_ command instead.
- ``COMPONENT_ADD_LINKER_DEPS``: List of files that linking should depend on. `target_link_libraries`_ will usually infer these dependencies automatically. For linker scripts, use the provided custom CMake function ``target_linker_scripts``.
- ``COMPONENT_SUBMODULES``: No longer used, the build system will automatically enumerate all submodules in the ESP-IDF repository.
- ``COMPONENT_EXTRA_INCLUDES``: Used to be an alternative to ``COMPONENT_PRIV_INCLUDEDIRS`` for absolute paths. Use ``COMPONENT_PRIV_INCLUDEDIRS`` for all cases now (can be relative or absolute).
- ``COMPONENT_OBJS``: Previously, component sources could be specified as a list of object files. Now they can be specified as an list of source files via ``COMPONENT_SRCS``.
- ``COMPONENT_OBJEXCLUDE``: Has been replaced with ``COMPONENT_SRCEXCLUDE``. Specify source files (as absolute paths or relative to component directory), instead.
- ``COMPONENT_EXTRA_INCLUDES``: Used to be an alternative to ``COMPONENT_PRIV_INCLUDEDIRS`` for absolute paths. Use ``PRIV_INCLUDE_DIRS`` argument to ``idf_component_register`` for all cases now (can be relative or absolute).
- ``COMPONENT_OBJS``: Previously, component sources could be specified as a list of object files. Now they can be specified as a list of source files via ``SRCS`` argument to `idf_component_register`.
- ``COMPONENT_OBJEXCLUDE``: Has been replaced with ``EXCLUDE_SRCS`` argument to ``idf_component_register``. Specify source files (as absolute paths or relative to component directory), instead.
- ``COMPONENT_EXTRA_CLEAN``: Set property ``ADDITIONAL_MAKE_CLEAN_FILES`` instead but note :ref:`CMake has some restrictions around this functionality <ADDITIONAL_MAKE_CLEAN_FILES_note>`.
- ``COMPONENT_OWNBUILDTARGET`` & ``COMPONENT_OWNCLEANTARGET``: Use CMake `ExternalProject`_ instead. See :ref:`component-build-full-override` for full details.
- ``COMPONENT_CONFIG_ONLY``: Call ``register_config_only_component()`` instead. See `Configuration-Only Components`_.
- ``COMPONENT_CONFIG_ONLY``: Call ``idf_component_register`` without any arguments instead. See `Configuration-Only Components`_.
- ``CFLAGS``, ``CPPFLAGS``, ``CXXFLAGS``: Use equivalent CMake commands instead. See `Controlling Component Compilation`_.
No Default Values
-----------------
The following variables no longer have default values:
Unlike in the legacy Make-based build system, the following have no default values:
- ``COMPONENT_SRCDIRS``
- ``COMPONENT_ADD_INCLUDEDIRS``
- Source directories (``COMPONENT_SRCDIRS`` variable in Make, ``SRC_DIRS`` argument to ``idf_component_register`` in CMake)
- Include directories (``COMPONENT_ADD_INCLUDEDIRS`` variable in Make, ``INCLUDE_DIRS`` argument to ``idf_component_register`` in CMake)
No Longer Necessary
-------------------
It is no longer necessary to set ``COMPONENT_SRCDIRS`` if setting ``COMPONENT_SRCS`` (in fact, in the CMake-based system ``COMPONENT_SRCS`` is ignored if ``COMPONENT_SRCDIRS`` is set).
- In the legacy Make-based build system, it is required to also set ``COMPONENT_SRCDIRS`` if ``COMPONENT_SRCS`` is set. In CMake, the equivalent is not necessary i.e. specifying ``SRC_DIRS`` to ``idf_component_register`` if ``SRCS`` is also specified (in fact, ``SRCS`` is ignored if ``SRC_DIRS`` is specified).
Flashing from make
------------------

View file

@ -28,12 +28,12 @@ To compile ULP code as part of a component, the following steps must be taken:
1. ULP code, written in assembly, must be added to one or more files with `.S` extension. These files must be placed into a separate directory inside component directory, for instance `ulp/`.
.. note: This directory should not be added to the ``COMPONENT_SRCDIRS`` environment variable. The logic behind this is that the ESP-IDF build system will compile files found in ``COMPONENT_SRCDIRS`` based on their extensions. For ``.S`` files, ``xtensa-esp32-elf-as`` assembler is used. This is not desirable for ULP assembly files, so the easiest way to achieve the distinction is by placing ULP assembly files into a separate directory. The ULP assembly source files should also **not** be added to ``COMPONENT_SRCS`` for the same reason. See the step below for how to properly add ULP assembly source files.
.. note: When registering the component (via ``idf_component_register``), this directory should not be added to the ``SRC_DIRS`` argument. The logic behind this is that the ESP-IDF build system will compile files found in ``SRC_DIRS`` based on their extensions. For ``.S`` files, ``xtensa-esp32-elf-as`` assembler is used. This is not desirable for ULP assembly files, so the easiest way to achieve the distinction is by placing ULP assembly files into a separate directory. The ULP assembly source files should also **not** be added to ``SRCS`` for the same reason. See the step below for how to properly add ULP assembly source files.
2. Call ``ulp_embed_binary`` from the component CMakeLists.txt after registration. For example::
...
register_component()
idf_component_register()
set(ulp_app_name ulp_${COMPONENT_NAME})
set(ulp_s_sources ulp/ulp_assembly_source_file.S)

View file

@ -29,7 +29,7 @@ There is no need to add a main function with ``UNITY_BEGIN()`` and ``UNITY_EN
The ``test`` subdirectory should contain a :ref:`component CMakeLists.txt <component-directories>`, since they are themselves,
components. ESP-IDF uses the test framework ``unity`` and should be specified as a requirement for the component. Normally, components
:ref:`should list their sources manually <cmake-file-globbing>`; for component tests however, this requirement is relaxed and the
use of ``COMPONENT_SRCDIRS`` is advised.
use of ``SRC_DIRS`` argument to ``idf_component_register`` is advised.
Overall, the minimal ``test`` subdirectory CMakeLists.txt file may look like as follows:

View file

@ -1,256 +1,246 @@
SPI Master driver
SPI Master Driver
=================
Overview
--------
SPI Master driver is a program that controls ESP32's SPI peripherals while they function as masters.
The ESP32 has four SPI peripheral devices, called SPI0, SPI1, HSPI and VSPI. SPI0 is entirely dedicated to
the flash cache the ESP32 uses to map the SPI flash device it is connected to into memory. SPI1 is
connected to the same hardware lines as SPI0 and is used to write to the flash chip. HSPI and VSPI
are free to use. SPI1, HSPI and VSPI all have three chip select lines, allowing them to drive up to
three SPI devices each as a master.
The spi_master driver
^^^^^^^^^^^^^^^^^^^^^
Overview of ESP32's SPI peripherals
-----------------------------------
The spi_master driver allows easy communicating with SPI slave devices, even in a multithreaded environment.
It fully transparently handles DMA transfers to read and write data and automatically takes care of
multiplexing between different SPI slaves on the same master.
ESP32 integrates four SPI peripherals.
.. note::
**Notes about thread safety**
The SPI driver API is thread safe when multiple SPI devices on the same bus are accessed from different tasks. However, the driver is not thread safe if the same SPI device is accessed from multiple tasks.
In this case, it is recommended to either refactor your application so only a single task accesses each SPI device, or to add mutex locking around access of the shared device.
- SPI0 and SPI1 are used internally to access the ESP32's attached flash memory and thus are currently not open to users. They share one signal bus via an arbiter.
- SPI2 and SPI3 are general purpose SPI controllers, sometimes referred to as HSPI and VSPI, respectively. They are open to users. SPI2 and SPI3 have independent signal buses with the same respective names. Each bus has three CS lines to drive up to three SPI slaves.
Terminology
^^^^^^^^^^^
-----------
The spi_master driver uses the following terms:
The terms used in relation to the SPI master driver are given in the table below.
* Host: The SPI peripheral inside the ESP32 initiating the SPI transmissions. One of SPI, HSPI or VSPI. (For
now, only HSPI or VSPI are actually supported in the driver; it will support all 3 peripherals
somewhere in the future.)
* Bus: The SPI bus, common to all SPI devices connected to one host. In general the bus consists of the
miso, mosi, sclk and optionally quadwp and quadhd signals. The SPI slaves are connected to these
signals in parallel.
================= =========================================================================================
Term Definition
================= =========================================================================================
**Host** The SPI controller peripheral inside ESP32 that initiates SPI transmissions over the bus, and acts as an SPI Master. This may be the SPI2 or SPI3 peripheral. (The driver will also support the SPI1 peripheral in the future.)
**Device** SPI slave device. An SPI bus may be connected to one or more Devices. Each Device shares the MOSI, MISO and SCLK signals but is only active on the bus when the Host asserts the Device's individual CS line.
**Bus** A signal bus, common to all Devices connected to one Host. In general, a bus includes the following lines: MISO, MOSI, SCLK, one or more CS lines, and, optionally, QUADWP and QUADHD. So Devices are connected to the same lines, with the exception that each Device has its own CS line. Several Devices can also share one CS line if connected in the daisy-chain manner.
- **MISO** Master In, Slave Out, a.k.a. Q. Data transmission from a Device to Host.
- **MOSI** Master Out, Slave In, a.k.a. D. Data transmission from a Host to Device.
- **SCLK** Serial Clock. Oscillating signal generated by a Host that keeps the transmission of data bits in sync.
- **CS** Chip Select. Allows a Host to select individual Device(s) connected to the bus in order to send or receive data.
- **QUADWP** Write Protect signal. Only used for 4-bit (qio/qout) transactions.
- **QUADHD** Hold signal. Only used for 4-bit (qio/qout) transactions.
- **Assertion** The action of activating a line. The opposite action of returning the line back to inactive (back to idle) is called *de-assertion*.
**Transaction** One instance of a Host asserting a CS line, transferring data to and from a Device, and de-asserting the CS line. Transactions are atomic, which means they can never be interrupted by another transaction.
**Launch edge** Edge of the clock at which the source register *launches* the signal onto the line.
**Latch edge** Edge of the clock at which the destination register *latches in* the signal.
================= =========================================================================================
- miso - Also known as q, this is the input of the serial stream into the ESP32
- mosi - Also known as d, this is the output of the serial stream from the ESP32
Driver Features
---------------
- sclk - Clock signal. Each data bit is clocked out or in on the positive or negative edge of this signal
The SPI master driver governs communications of Hosts with Devices. The driver supports the following features:
- quadwp - Write Protect signal. Only used for 4-bit (qio/qout) transactions.
- Multi-threaded environments
- Transparent handling of DMA transfers while reading and writing data
- Automatic time-division multiplexing of data coming from different Devices on the same signal bus
- quadhd - Hold signal. Only used for 4-bit (qio/qout) transactions.
.. warning::
* Device: A SPI slave. Each SPI slave has its own chip select (CS) line, which is made active when
a transmission to/from the SPI slave occurs.
* Transaction: One instance of CS going active, data transfer from and/or to a device happening, and
CS going inactive again. Transactions are atomic, as in they will never be interrupted by another
transaction.
The SPI master driver has the concept of multiple Devices connected to a single bus (sharing a single ESP32 SPI peripheral). As long as each Device is accessed by only one task, the driver is thread safe. However, if multiple tasks try to access the same SPI Device, the driver is **not thread-safe**. In this case, it is recommended to either:
SPI transactions
^^^^^^^^^^^^^^^^
- Refactor your application so that each SPI peripheral is only accessed by a single task at a time.
- Add a mutex lock around the shared Device using :c:macro:`xSemaphoreCreateMutex`.
A transaction on the SPI bus consists of five phases, any of which may be skipped:
* The command phase. In this phase, a command (0-16 bit) is clocked out.
* The address phase. In this phase, an address (0-64 bit) is clocked out.
* The write phase. The master sends data to the slave.
* The dummy phase. The phase is configurable, used to meet the timing requirements.
* The read phase. The slave sends data to the master.
SPI Transactions
----------------
In full duplex mode, the read and write phases are combined, and the SPI host reads and
writes data simultaneously. The total transaction length is decided by
``command_bits + address_bits + trans_conf.length``, while the ``trans_conf.rx_length``
only determins length of data received into the buffer.
An SPI bus transaction consists of five phases which can be found in the table below. Any of these phases can be skipped.
While in half duplex mode, the host have independent write and read phases. The length of write phase and read phase are
decided by ``trans_conf.length`` and ``trans_conf.rx_length`` respectively.
============== =========================================================================================================
Phase Description
============== =========================================================================================================
**Command** In this phase, a command (0-16 bit) is written to the bus by the Host.
**Address** In this phase, an address (0-64 bit) is transmitted over the bus by the Host.
**Write** Host sends data to a Device. This data follows the optional command and address phases and is indistinguishable from them at the electrical level.
**Dummy** This phase is configurable and is used to meet the timing requirements.
**Read** Device sends data to its Host.
============== =========================================================================================================
The command and address phase are optional in that not every SPI device will need to be sent a command
and/or address. This is reflected in the device configuration: when the ``command_bits`` or ``address_bits``
fields are set to zero, no command or address phase is done.
.. todo::
Something similar is true for the read and write phase: not every transaction needs both data to be written
as well as data to be read. When ``rx_buffer`` is NULL (and SPI_TRANS_USE_RXDATA) is not set) the read phase
is skipped. When ``tx_buffer`` is NULL (and SPI_TRANS_USE_TXDATA) is not set) the write phase is skipped.
Add a package diagram.
The attributes of a transaction are determined by the bus configuration structure :cpp:type:`spi_bus_config_t`, device configuration structure :cpp:type:`spi_device_interface_config_t`, and transaction configuration structure :cpp:type:`spi_transaction_t`.
An SPI Host can send full-duplex transactions, during which the read and write phases occur simultaneously. The total transaction length is determined by the sum of the following members:
- :cpp:member:`spi_device_interface_config_t::command_bits`
- :cpp:member:`spi_device_interface_config_t::address_bits`
- :cpp:member:`spi_transaction_t::length`
While the member :cpp:member:`spi_transaction_t::rxlength` only determines the length of data received into the buffer.
In half-duplex transactions, the read and write phases are not simultaneous (one direction at a time). The lengths of the write and read phases are determined by :cpp:member:`length` and :cpp:member:`rxlength` members of the struct :cpp:type:`spi_transaction_t` respectively.
The command and address phases are optional, as not every SPI device requires a command and/or address. This is reflected in the Device's configuration: if :cpp:member:`command_bits` and/or :cpp:member:`address_bits` are set to zero, no command or address phase will occur.
The read and write phases can also be optional, as not every transaction requires both writing and reading data. If :cpp:member:`rx_buffer` is NULL and :cpp:type:`SPI_TRANS_USE_RXDATA` is not set, the read phase is skipped. If :cpp:member:`tx_buffer` is NULL and :cpp:type:`SPI_TRANS_USE_TXDATA` is not set, the write phase is skipped.
The driver supports two types of transactions: the interrupt transactions and polling transactions. The programmer can choose to use a different transaction type per Device. If your Device requires both transaction types, see :ref:`mixed_transactions`.
The driver offers two different kinds of transactions: the interrupt
transactions and the polling transactions. Each device can choose one kind of
transaction to send. See :ref:`mixed_transactions` if your device do require
both kinds of transactions.
.. _interrupt_transactions:
Interrupt transactions
""""""""""""""""""""""""
Interrupt Transactions
^^^^^^^^^^^^^^^^^^^^^^
The interrupt transactions use an interrupt-driven logic when the
transactions are in-flight. The routine will get blocked, allowing the CPU to
run other tasks, while it is waiting for a transaction to be finished.
Interrupt transactions will block the transaction routine until the transaction completes, thus allowing the CPU to run other tasks.
An application task can queue multiple transactions, and the driver will automatically handle them one-by-one in the interrupt service routine (ISR). It allows the task to switch to other procedures until all the transactions complete.
Interrupt transactions can be queued into a device, the driver automatically
send them one-by-one in the ISR. A task can queue several transactions, and
then do something else before the transactions are finished.
.. _polling_transactions:
Polling transactions
""""""""""""""""""""
Polling Transactions
^^^^^^^^^^^^^^^^^^^^
The polling transactions don't rely on the interrupt, the routine keeps polling
the status bit of the SPI peripheral until the transaction is done.
Polling transactions do not use interrupts. The routine keeps polling the SPI Host's status bit until the transaction is finished.
All the tasks that do interrupt transactions may get blocked by the queue, at
which point they need to wait for the ISR to run twice before the transaction
is done. Polling transactions save the time spent on queue handling and
context switching, resulting in a smaller transaction interval smaller. The
disadvantage is that the the CPU is busy while these transactions are in
flight.
All the tasks that use interrupt transactions can be blocked by the queue. At this point, they will need to wait for the ISR to run twice before the transaction is finished. Polling transactions save time otherwise spent on queue handling and context switching, which results in smaller transaction intervals. The disadvantage is that the CPU is busy while these transactions are in progress.
The ``spi_device_polling_end`` routine spends at least 1us overhead to
unblock other tasks when the transaction is done. It is strongly recommended
to wrap a series of polling transactions inside of ``spi_device_acquire_bus``
and ``spi_device_release_bus`` to avoid the overhead. (See
:ref:`bus_acquiring`)
The :cpp:func:`spi_device_polling_end` routine needs an overhead of at least 1 us to unblock other tasks when the transaction is finished. It is strongly recommended to wrap a series of polling transactions using the functions :cpp:func:`spi_device_acquire_bus` and :cpp:func:`spi_device_release_bus` to avoid the overhead. For more information, see :ref:`bus_acquiring`.
Command and address phases
Command and Address Phases
^^^^^^^^^^^^^^^^^^^^^^^^^^
During the command and address phases, ``cmd`` and ``addr`` field in the
``spi_transaction_t`` struct are sent to the bus, while nothing is read at the
same time. The default length of command and address phase are set in the
``spi_device_interface_config_t`` and by ``spi_bus_add_device``. When the the
flag ``SPI_TRANS_VARIABLE_CMD`` and ``SPI_TRANS_VARIABLE_ADDR`` are not set in
the ``spi_transaction_t``,the driver automatically set the length of these
phases to the default value as set when the device is initialized respectively.
During the command and address phases, the members :cpp:member:`cmd` and :cpp:member:`addr` in the struct :cpp:type:`spi_transaction_t` are sent to the bus, nothing is read at this time. The default lengths of the command and address phases are set in :cpp:type:`spi_device_interface_config_t` by calling :cpp:func:`spi_bus_add_device`. If the flags :cpp:type:`SPI_TRANS_VARIABLE_CMD` and :cpp:type:`SPI_TRANS_VARIABLE_ADDR` in the member :cpp:member:`spi_transaction_t::flags` are not set, the driver automatically sets the length of these phases to default values during Device initialization.
If the length of command and address phases needs to be variable, declare a
``spi_transaction_ext_t`` descriptor, set the flag ``SPI_TRANS_VARIABLE_CMD``
or/and ``SPI_TRANS_VARIABLE_ADDR`` in the ``flags`` of ``base`` member and
configure the rest part of ``base`` as usual. Then the length of each phases
will be ``command_bits`` and ``address_bits`` set in the ``spi_transaction_ext_t``.
If the lengths of the command and address phases need to be variable, declare the struct :cpp:type:`spi_transaction_ext_t`, set the flags :cpp:type:`SPI_TRANS_VARIABLE_CMD` and/or :cpp:type:`SPI_TRANS_VARIABLE_ADDR` in the member :cpp:member:`spi_transaction_ext_t::base` and configure the rest of base as usual. Then the length of each phase will be equal to :cpp:member:`command_bits` and :cpp:member:`address_bits` set in the struct :cpp:type:`spi_transaction_ext_t`.
Write and read phases
Write and Read Phases
^^^^^^^^^^^^^^^^^^^^^
Normally, data to be transferred to or from a device will be read from or written to a chunk of memory
indicated by the ``rx_buffer`` and ``tx_buffer`` members of the transaction structure.
When DMA is enabled for transfers, these buffers are highly recommended to meet the requirements as below:
Normally, the data that needs to be transferred to or from a Device will be read from or written to a chunk of memory indicated by the members :cpp:member:`rx_buffer` and :cpp:member:`tx_buffer` of the structure :cpp:type:`spi_transaction_t`. If DMA is enabled for transfers, the buffers are required to be:
1. allocated in DMA-capable memory using ``pvPortMallocCaps(size, MALLOC_CAP_DMA)``;
2. 32-bit aligned (start from the boundary and have length of multiples of 4 bytes).
1. Allocated in DMA-capable internal memory. If :ref:`external PSRAM is enabled<dma-capable-memory>`, this means using ``pvPortMallocCaps(size, MALLOC_CAP_DMA)``.
2. 32-bit aligned (staring from a 32-bit boundary and having a length of multiples of 4 bytes).
If these requirements are not satisfied, efficiency of the transaction will suffer due to the allocation and
memcpy of temporary buffers.
If these requirements are not satisfied, the transaction efficiency will be affected due to the allocation and copying of temporary buffers.
.. note::
Half-duplex transactions with both read and write phases are not supported when using DMA. For details and workarounds, see :ref:`spi_known_issues`.
.. note:: Half duplex transactions with both read and write phases are not supported when using DMA. See
:ref:`spi_known_issues` for details and workarounds.
.. _bus_acquiring:
Bus acquiring
Bus Acquiring
^^^^^^^^^^^^^
Sometimes you may want to send spi transactions exclusively, continuously, to
make it as fast as possible. You may use ``spi_device_acquire_bus`` and
``spi_device_release_bus`` to realize this. When the bus is acquired,
transactions to other devices (no matter polling or interrupt) are pending
until the bus is released.
Sometimes you might want to send SPI transactions exclusively and continuously so that it takes as little time as possible. For this, you can use bus acquiring, which helps to suspend transactions (both polling or interrupt) to other devices until the bus is released. To acquire and release a bus, use the functions :cpp:func:`spi_device_acquire_bus` and :cpp:func:`spi_device_release_bus`.
Using the spi_master driver
^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Initialize a SPI bus by calling ``spi_bus_initialize``. Make sure to set the correct IO pins in
the ``bus_config`` struct. Take care to set signals that are not needed to -1.
Driver Usage
------------
- Tell the driver about a SPI slave device connected to the bus by calling spi_bus_add_device.
Make sure to configure any timing requirements the device has in the ``dev_config`` structure.
You should now have a handle for the device, to be used when sending it a transaction.
.. todo::
- To interact with the device, fill one or more spi_transaction_t structure with any transaction
parameters you need. Then send them either in a polling way or the interrupt way:
Organize the Driver Usage into subsections that will reflect the general usage experience of the users, e.g.,
Configuration
Add stuff about the configuration API here, and the various options in configuration (e.g., configure for interrupt vs. polling), and optional configuration
Transactions
Describe how to execute a normal transaction (i.e., where data is larger than 32 bits). Describe how to configure between big and little-endian.
- Add subsub section on how to optimize when transmitting less than 32 bits
- Add subsub section on how to transmit mixed transactions to the same device
- Initialize an SPI bus by calling the function :cpp:func:`spi_bus_initialize`. Make sure to set the correct I/O pins in the struct :cpp:type:`spi_bus_config_t`. Set the signals that are not needed to ``-1``.
- Register a Device connected to the bus with the driver by calling the function :cpp:func:`spi_bus_add_device`. Make sure to configure any timing requirements the device might need with the parameter ``dev_config``. You should now have obtained the Device's handle which will be used when sending a transaction to it.
- To interact with the Device, fill one or more :cpp:type:`spi_transaction_t` structs with any transaction parameters required. Then send the structs either using a polling transaction or an interrupt transaction:
- :ref:`Interrupt <interrupt_transactions>`
Either queue all transactions by calling ``spi_device_queue_trans``,
and at a later time query the result using
``spi_device_get_trans_result``, or handle all requests
synchroneously by feeding them into ``spi_device_transmit``.
Either queue all transactions by calling the function :cpp:func:`spi_device_queue_trans` and, at a later time, query the result using the function :cpp:func:`spi_device_get_trans_result`, or handle all requests synchronously by feeding them into :cpp:func:`spi_device_transmit`.
- :ref:`Polling <polling_transactions>`
Call the ``spi_device_polling_transmit`` to send polling
transactions. Alternatively, you can send a polling transaction by
``spi_device_polling_start`` and ``spi_device_polling_end`` if you
want to insert something between them.
Call the function :cpp:func:`spi_device_polling_transmit` to send polling transactions. Alternatively, if you want to insert something in between, send the transactions by using :cpp:func:`spi_device_polling_start` and :cpp:func:`spi_device_polling_end`.
- Optional: to do back-to-back transactions to a device, call
``spi_device_acquire_bus`` before and ``spi_device_release_bus`` after the
transactions.
- (Optional) To perform back-to-back transactions with a Device, call the function :cpp:func:`spi_device_acquire_bus` before sending transactions and :cpp:func:`spi_device_release_bus` after the transactions have been sent.
- Optional: to unload the driver for a device, call ``spi_bus_remove_device`` with the device
handle as an argument
- (Optional) To unload the driver for a certain Device, call :cpp:func:`spi_bus_remove_device` with the Device handle as an argument.
- Optional: to remove the driver for a bus, make sure no more drivers are attached and call
``spi_bus_free``.
- (Optional) To remove the driver for a bus, make sure no more drivers are attached and call :cpp:func:`spi_bus_free`.
Tips
""""
The example code for the SPI master driver can be found in the :example:`peripherals/spi_master` directory of ESP-IDF examples.
1. Transactions with small amount of data:
Sometimes, the amount of data is very small making it less than optimal allocating a separate buffer
for it. If the data to be transferred is 32 bits or less, it can be stored in the transaction struct
itself. For transmitted data, use the ``tx_data`` member for this and set the ``SPI_TRANS_USE_TXDATA`` flag
on the transmission. For received data, use ``rx_data`` and set ``SPI_TRANS_USE_RXDATA``. In both cases, do
not touch the ``tx_buffer`` or ``rx_buffer`` members, because they use the same memory locations
as ``tx_data`` and ``rx_data``.
2. Transactions with integers other than uint8_t
The SPI peripheral reads and writes the memory byte-by-byte. By default,
the SPI works at MSB first mode, each bytes are sent or received from the
MSB to the LSB. However, if you want to send data with length which is
not multiples of 8 bits, unused bits are sent.
Transactions with Data Not Exceeding 32 Bits
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E.g. you write ``uint8_t data = 0x15`` (00010101B), and set length to
only 5 bits, the sent data is ``00010B`` rather than expected ``10101B``.
When the transaction data size is equal to or less than 32 bits, it will be sub-optimal to allocate a buffer for the data. The data can be directly stored in the transaction struct instead. For transmitted data, it can be achieved by using the :cpp:member:`tx_data` member and setting the :cpp:type:`SPI_TRANS_USE_TXDATA` flag on the transmission. For received data, use :cpp:member:`rx_data` and set :cpp:type:`SPI_TRANS_USE_RXDATA`. In both cases, do not touch the :cpp:member:`tx_buffer` or :cpp:member:`rx_buffer` members, because they use the same memory locations as :cpp:member:`tx_data` and :cpp:member:`rx_data`.
Moreover, ESP32 is a little-endian chip whose lowest byte is stored at
the very beginning address for uint16_t and uint32_t variables. Hence if
a uint16_t is stored in the memory, it's bit 7 is first sent, then bit 6
to 0, then comes its bit 15 to bit 8.
To send data other than uint8_t arrays, macros ``SPI_SWAP_DATA_TX`` is
provided to shift your data to the MSB and swap the MSB to the lowest
address; while ``SPI_SWAP_DATA_RX`` can be used to swap received data
from the MSB to it's correct place.
Transactions with Integers Other Than ``uint8_t``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
GPIO matrix and IOMUX
^^^^^^^^^^^^^^^^^^^^^^^^^^^
An SPI Host reads and writes data into memory byte by byte. By default, data is sent with the most significant bit (MSB) first, as LSB first used in rare cases. If a value less than 8 bits needs to be sent, the bits should be written into memory in the MSB first manner.
Most peripheral signals in ESP32 can connect directly to a specific GPIO, which is called its IOMUX pin. When a
peripheral signal is routed to a pin other than its IOMUX pin, ESP32 uses the less direct GPIO matrix to make this
connection.
For example, if ``0b00010`` needs to be sent, it should be written into a ``uint8_t`` variable, and the length for reading should be set to 5 bits. The Device will still receive 8 bits with 3 additional "random" bits, so the reading must be performed correctly.
If the driver is configured with all SPI signals set to their specific IOMUX pins (or left unconnected), it will bypass
the GPIO matrix. If any SPI signal is configured to a pin other than its IOMUx pin, the driver will automatically route
all the signals via the GPIO Matrix. The GPIO matrix samples all signals at 80MHz and sends them between the GPIO and
the peripheral.
On top of that, ESP32 is a little-endian chip, which means that the least significant byte of ``uint16_t`` and ``uint32_t`` variables is stored at the smallest address. Hence, if ``uint16_t`` is stored in memory, bits [7:0] are sent first, followed by bits [15:8].
When the GPIO matrix is used, signals faster than 40MHz cannot propagate and the setup time of MISO is more easily
violated, since the input delay of MISO signal is increased. The maximum clock frequency with GPIO Matrix is 40MHz
or less, whereas using all IOMUX pins allows 80MHz.
For cases when the data to be transmitted has the size differing from ``uint8_t`` arrays, the following macros can be used to transform data to the format that can be sent by the SPI driver directly:
.. note:: More details about influence of input delay on the maximum clock frequency, see :ref:`timing_considerations` below.
- :c:macro:`SPI_SWAP_DATA_TX` for data to be transmitted
- :c:macro:`SPI_SWAP_DATA_RX` for data received
IOMUX pins for SPI controllers are as below:
.. _mixed_transactions:
Notes on Sending Mixed Transactions to the Same Device
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To reduce coding complexity, send only one type of transactions (interrupt or polling) to one Device. However, you still can send both interrupt and polling transactions alternately. The notes below explain how to do this.
The polling transactions should be initiated only after all the polling and interrupt transactions are finished.
Since an unfinished polling transaction blocks other transactions, please do not forget to call the function :cpp:func:`spi_device_polling_end` after :cpp:func:`spi_device_polling_start` to allow other transactions or to allow other Devices to use the bus. Remember that if there is no need to switch to other tasks during your polling transaction, you can initiate a transaction with :cpp:func:`spi_device_polling_transmit` so that it will be ended automatically.
In-flight polling transactions are disturbed by the ISR operation to accommodate interrupt transactions. Always make sure that all the interrupt transactions sent to the ISR are finished before you call :cpp:func:`spi_device_polling_start`. To do that, you can keep calling :cpp:func:`spi_device_get_trans_result` until all the transactions are returned.
To have better control of the calling sequence of functions, send mixed transactions to the same Device only within a single task.
GPIO Matrix and IO_MUX
----------------------
Most of ESP32's peripheral signals have direct connection to their dedicated IO_MUX pins. However, the signals can also be routed to any other available pins using the less direct GPIO matrix. If at least one signal is routed through the GPIO matrix, then all signals will be routed through it.
The GPIO matrix introduces flexibility of routing but also brings the following disadvantages:
- Increases the input delay of the MISO signal, which makes MISO setup time violations more likely. If SPI needs to operate at high speeds, use dedicated IO_MUX pins.
- Allows signals with clock frequencies only up to 40 MHz, as opposed to 80 MHz if IO_MUX pins are used.
.. note::
For more details about the influence of the MISO input delay on the maximum clock frequency, see :ref:`timing_considerations`.
The IO_MUX pins for SPI buses are given below.
+----------+------+------+
| Pin Name | HSPI | VSPI |
| Pin Name | SPI2 | SPI3 |
+ +------+------+
| | GPIO Number |
+==========+======+======+
@ -267,98 +257,64 @@ IOMUX pins for SPI controllers are as below:
| QUADHD | 4 | 21 |
+----------+------+------+
note * Only the first device attaching to the bus can use CS0 pin.
* Only the first Device attached to the bus can use the CS0 pin.
.. _mixed_transactions:
Notes to send mixed transactions to the same device
"""""""""""""""""""""""""""""""""""""""""""""""""""
Though we suggest to send only one type (interrupt or polling) of
transactions to one device to reduce coding complexity, it is supported to
send both interrupt and polling transactions alternately. Notes below is to
help you do this.
The polling transactions should be started when all the other transactions
are finished, no matter they are polling or interrupt.
An unfinished polling transaction forbid other transactions from being sent.
Always call ``spi_device_polling_end`` after ``spi_device_polling_start`` to
allow other device using the bus, or allow other transactions to be started
to the same device. You can use ``spi_device_polling_transmit`` to simplify
this if you don't need to do something during your polling transaction.
An in-flight polling transaction would get disturbed by the ISR operation
caused by interrupt transactions. Always make sure all the interrupt
transactions sent to the ISR are finished before you call
``spi_device_polling_start``. To do that, you can call
``spi_device_get_trans_result`` until all the transactions are returned.
It is strongly recommended to send mixed transactions to the same device in
only one task to control the calling sequence of functions.
Speed and Timing Considerations
-------------------------------
.. _speed_considerations:
Transferring speed
^^^^^^^^^^^^^^^^^^
Transfer Speed Considerations
-----------------------------
There're three factors limiting the transferring speed: (1) The transaction interval, (2) The SPI clock frequency used.
(3) The cache miss of SPI functions including callbacks.
When large transactions are used, the clock frequency determines the transferring speed; while the interval effects the
speed a lot if small transactions are used.
There are three factors limiting the transfer speed:
1. Transaction interval: It takes time for the software to setup spi
peripheral registers as well as copy data to FIFOs, or setup DMA links.
When the interrupt transactions are used, an extra overhead is appended,
from the cost of FreeRTOS queues and the time switching between tasks and
the ISR.
- Transaction interval
- SPI clock frequency
- Cache miss of SPI functions, including callbacks
1. For **interrupt transactions**, the CPU can switched to other
tasks when the transaction is in flight. This save the cpu time
but increase the interval (See :ref:`interrupt_transactions`).
For
**polling transactions**, it does not block the task but do
polling when the transaction is in flight. (See
:ref:`polling_transactions`).
The main parameter that determines the transfer speed for large transactions is clock frequency. For multiple small transactions, the transfer speed is mostly determined by the length of transaction intervals.
2. When the DMA is enabled, it needs about 2us per transaction to setup the linked list. When the master is
transferring, it automatically read data from the linked list. If the DMA is not enabled,
CPU has to write/read each byte to/from the FIFO by itself. Usually this is faster than 2us, but the
transaction length is limited to 64 bytes for both write and read.
Typical transaction interval with one byte data is as below:
Transaction Interval
^^^^^^^^^^^^^^^^^^^^
+--------+----------------+--------------+
| | Typical Transaction Time (us) |
+========+================+==============+
| | Interrupt | Polling |
+--------+----------------+--------------+
| DMA | 24 | 8 |
+--------+----------------+--------------+
| No DMA | 22 | 7 |
+--------+----------------+--------------+
Transaction interval is the time that software requires to set up SPI peripheral registers and to copy data to FIFOs, or to set up DMA links.
2. SPI clock frequency: Each byte transferred takes 8 times of the clock period *8/fspi*. If the clock frequency is
too high, some functions may be limited to use. See :ref:`timing_considerations`.
Interrupt transactions allow appending extra overhead to accommodate the cost of FreeRTOS queues and the time needed for switching between tasks and the ISR.
3. The cache miss: the default config puts only the ISR into the IRAM.
Other SPI related functions including the driver itself and the callback
may suffer from the cache miss and wait for some time while reading code
from the flash. Select :ref:`CONFIG_SPI_MASTER_IN_IRAM` to put the whole
SPI driver into IRAM, and put the entire callback(s) and its callee
functions into IRAM to prevent this.
For **interrupt transactions**, the CPU can switch to other tasks when a transaction is in progress. This saves the CPU time but increases the interval. See :ref:`interrupt_transactions`. For **polling transactions**, it does not block the task but allows to do polling when the transaction is in progress. For more information, see :ref:`polling_transactions`.
For an interrupt transaction, the overall cost is *20+8n/Fspi[MHz]* [us] for n bytes tranferred
in one transaction. Hence the transferring speed is : *n/(20+8n/Fspi)*. Example of transferring speed under 8MHz
clock speed:
If DMA is enabled, setting up the linked list requires about 2 us per transaction. When a master is transferring data, it automatically reads the data from the linked list. If DMA is not enabled, the CPU has to write and read each byte from the FIFO by itself. Usually, this is faster than 2 us, but the transaction length is limited to 64 bytes for both write and read.
Typical transaction interval timings for one byte of data are given below.
+--------+----------------+--------------+
| | Typical Transaction Time (us) |
+========+================+==============+
| | Interrupt | Polling |
+--------+----------------+--------------+
| DMA | 24 | 8 |
+--------+----------------+--------------+
| No DMA | 22 | 7 |
+--------+----------------+--------------+
SPI Clock Frequency
^^^^^^^^^^^^^^^^^^^
Transferring each byte takes eight times the clock period *8/fspi*. If the clock frequency is too high, the use of some functions might be limited. See :ref:`timing_considerations`.
Cache Miss
^^^^^^^^^^
The default config puts only the ISR into the IRAM. Other SPI related functions, including the driver itself and the callback, might suffer from the cache miss and will need to wait until the code is read from the flash. Select :ref:`CONFIG_SPI_MASTER_IN_IRAM` to put the whole SPI driver into IRAM and put the entire callback(s) and its callee functions into IRAM to prevent cache miss.
For an interrupt transaction, the overall cost is *20+8n/Fspi[MHz]* [us] for n bytes transferred in one transaction. Hence, the transferring speed is: *n/(20+8n/Fspi)*. An example of transferring speed at 8 MHz clock speed is given in the following table.
+-----------+----------------------+--------------------+------------+-------------+
| Frequency | Transaction Interval | Transaction Length | Total Time | Total Speed |
| | | | | |
| (MHz) | (us) | (bytes) | (us) | (kBps) |
| (MHz) | (us) | (bytes) | (us) | (KBps) |
+===========+======================+====================+============+=============+
| 8 | 25 | 1 | 26 | 38.5 |
+-----------+----------------------+--------------------+------------+-------------+
@ -371,129 +327,90 @@ clock speed:
| 8 | 25 | 128 | 153 | 836.6 |
+-----------+----------------------+--------------------+------------+-------------+
When the length of transaction is short, the cost of transaction interval is really high. Please try to squash data
into one transaction if possible to get higher transfer speed.
When a transaction length is short, the cost of transaction interval is high. If possible, try to squash several short transactions into one transaction to achieve a higher transfer speed.
Please note that the ISR is disabled during flash operation by default. To keep sending transactions during flash operations, enable :ref:`CONFIG_SPI_MASTER_ISR_IN_IRAM` and set :cpp:class:`ESP_INTR_FLAG_IRAM` in the member :cpp:member:`spi_bus_config_t::intr_flags`. In this case, all the transactions queued before starting flash operations will be handled by the ISR in parallel. Also note that the callback of each Device and their callee functions should be in IRAM, or your callback will crash due to cache miss. For more details, see :ref:`iram-safe-interrupt-handlers`.
BTW, the ISR is disabled during flash operation by default. To keep sending
transactions during flash operations, enable
:ref:`CONFIG_SPI_MASTER_ISR_IN_IRAM` and set :cpp:class:`ESP_INTR_FLAG_IRAM`
in the ``intr_flags`` member of :cpp:class:`spi_bus_config_t`. Then all the
transactions queued before the flash operations will be handled by the ISR
continuously during flash operation. Note that the callback of each devices,
and their callee functions, should be in the IRAM in this case, or your
callback will crash due to cache miss.
.. _timing_considerations:
Timing considerations
^^^^^^^^^^^^^^^^^^^^^
Timing Considerations
---------------------
As shown in the figure below, there is a delay on the MISO signal after SCLK
launch edge and before it's latched by the internal register. As a result,
the MISO pin setup time is the limiting factor for SPI clock speed. When the
delay is too large, setup slack is < 0 and the setup timing requirement is
violated, leads to the failure of reading correctly.
As shown in the figure below, there is a delay on the MISO line after the SCLK launch edge and before the signal is latched by the internal register. As a result, the MISO pin setup time is the limiting factor for the SPI clock speed. When the delay is too long, the setup slack is < 0, and the setup timing requirement is violated, which results in the failure to perform the reading correctly.
.. image:: /../_static/spi_miso.png
:scale: 40 %
:align: center
.. wavedrom don't support rendering pdflatex till now(1.3.1), so we use the png here
.. wavedrom does not support rendering pdflatex till now(1.3.1), so we use the png here
.. image:: /../_static/miso_timing_waveform.png
The maximum frequency allowed is related to the *input delay* (maximum valid
time after SCLK on the MISO bus), as well as the usage of GPIO matrix. The
maximum frequency allowed is reduced to about 33~77% (related to existing
*input delay*) when the GPIO matrix is used. To work at higher frequency, you
have to use the IOMUX pins or the *dummy bit workaround*. You can get the
maximum reading frequency of the master by ``spi_get_freq_limit``.
The maximum allowed frequency is dependent on:
- ``input_delay_ns`` - maximum data valid time on the MISO bus after a clock cycle on SCLK starts
- If the IO_MUX pin or the GPIO Matrix is used
When the GPIO matrix is used, the maximum allowed frequency is reduced to about 33~77% in comparison to the existing *input delay*. To retain a higher frequency, you have to use the IO_MUX pins or the *dummy bit workaround*. You can obtain the maximum reading frequency of the master by using the function :cpp:func:`spi_get_freq_limit`.
.. _dummy_bit_workaround:
**Dummy bit workaround:** We can insert dummy clocks (during which the host does not read data) before the read phase
actually begins. The slave still sees the dummy clocks and gives out data, but the host does not read until the read
phase. This compensates the lack of setup time of MISO required by the host, allowing the host reading at higher
frequency.
**Dummy bit workaround**: Dummy clocks, during which the Host does not read data, can be inserted before the read phase begins. The Device still sees the dummy clocks and sends out data, but the Host does not read until the read phase comes. This compensates for the lack of the MISO setup time required by the Host and allows the Host to do reading at a higher frequency.
In the ideal case (the slave is so fast that the input delay is shorter than an apb clock, 12.5ns), the maximum
frequency host can read (or read and write) under different conditions is as below:
In the ideal case, if the Device is so fast that the input delay is shorter than an APB clock cycle - 12.5 ns - the maximum frequency at which the Host can read (or read and write) in different conditions is as follows:
+-------------+-------------+------------+-----------------------------+
| Frequency Limit (MHz) | Dummy Bits | Comments |
+-------------+-------------+ Used + +
| GPIO matrix | IOMUX pins | By Driver | |
| GPIO matrix | IO_MUX pins | By Driver | |
+=============+=============+============+=============================+
| 26.6 | 80 | No | |
+-------------+-------------+------------+-----------------------------+
| 40 | -- | Yes | Half Duplex, no DMA allowed |
| 40 | -- | Yes | Half-duplex, no DMA allowed |
+-------------+-------------+------------+-----------------------------+
And if the host only writes, the *dummy bit workaround* is not used and the frequency limit is as below:
If the Host only writes data, the *dummy bit workaround* and the frequency check can be disabled by setting the bit `SPI_DEVICE_NO_DUMMY` in the member :cpp:member:`spi_device_interface_config_t::flags`. When disabled, the output frequency can be 80MHz, even if the GPIO matrix is used.
+-------------------+------------------+
| GPIO matrix (MHz) | IOMUX pins (MHz) |
+===================+==================+
| 40 | 80 |
+-------------------+------------------+
:cpp:member:`spi_device_interface_config_t::flags`
The spi master driver can work even if the *input delay* in the ``spi_device_interface_config_t`` is set to 0.
However, setting a accurate value helps to: (1) calculate the frequency limit in full duplex mode, and (2) compensate
the timing correctly by dummy bits in half duplex mode. You may find the maximum data valid time after the launch edge
of SPI clocks in the AC characteristics chapter of the device specifications, or measure the time on a oscilloscope or
logic analyzer.
The SPI master driver can work even if the :cpp:member:`input_delay_ns` in the structure :cpp:type:`spi_device_interface_config_t` is set to 0. However, setting an accurate value helps to:
.. wavedrom don't support rendering pdflatex till now(1.3.1), so we use the png here
- Calculate the frequency limit for full-duplex transactions
- Compensate the timing correctly with dummy bits for half-duplex transactions
.. image:: /../_static/miso_timing_waveform_async.png
You can approximate the maximum data valid time after the launch edge of SPI clocks by checking the statistics in the AC characteristics chapter of your Device's specification or measure the time on an oscilloscope or logic analyzer.
As shown in the figure above, the input delay is usually:
Please note that the actual PCB layout design and the excessive loads may increase the input delay. It means that non-optimal wiring and/or a load capacitor on the bus will most likely lead to the input delay values exceeding the values given in the Device specification or measured while the bus is floating.
*[input delay] = [sample delay] + [slave output delay]*
Some typical delay values are shown in the following table.
1. The sample delay is the maximum random delay due to the
asynchronization of SCLK and peripheral clock of the slave. It's usually
1 slave peripheral clock if the clock is asynchronize with SCLK, or 0 if
the slave just use the SCLK to latch the SCLK and launch MISO data. e.g.
for ESP32 slaves, the delay is 12.5ns (1 apb clock), while it is reduced
to 0 if the slave is in the same chip as the master.
+----------------------------------------+------------------+
| Device | Input delay (ns) |
+========================================+==================+
| Ideal Device | 0 |
+----------------------------------------+------------------+
| ESP32 slave using IO_MUX* | 50 |
+----------------------------------------+------------------+
| ESP32 slave using GPIO_MUX* | 75 |
+----------------------------------------+------------------+
| ESP32's slave device is on a different physical chip. |
+-----------------------------------------------------------+
2. The slave output delay is the time for the MOSI to be stable after the
launch edge. e.g. for ESP32 slaves, the output delay is 37.5ns (3 apb
clocks) when IOMUX pins in the slave is used, or 62.5ns (5 apb clocks) if
through the GPIO matrix.
The MISO path delay (valid time) consists of a slave's *input delay* plus master's *GPIO matrix delay*. This delay determines the frequency limit above which full-duplex transfers will not work as well as the dummy bits used in the half-duplex transactions. The frequency limit is:
Some typical delays are shown in the following table:
*Freq limit [MHz] = 80 / (floor(MISO delay[ns]/12.5) + 1)*
+--------------------+------------------+
| Device | Input delay (ns) |
+====================+==================+
| Ideal device | 0 |
+--------------------+------------------+
| ESP32 slave IOMUX* | 50 |
+--------------------+------------------+
| ESP32 slave GPIO* | 75 |
+--------------------+------------------+
| ESP32 slave is on an independent |
| chip, 12.5ns sample delay included. |
+---------------------------------------+
The MISO path delay(tv), consists of slave *input delay* and master *GPIO matrix delay*, finally determines the
frequency limit, above which the full duplex mode will not work, or dummy bits are used in the half duplex mode. The
frequency limit is:
*Freq limit[MHz] = 80 / (floor(MISO delay[ns]/12.5) + 1)*
The figure below shows the relations of frequency limit against the input delay. 2 extra apb clocks should be counted
into the MISO delay if the GPIO matrix in the master is used.
The figure below shows the relationship between frequency limit and input delay. Two extra APB clock cycle periods should be added to the MISO delay if the master uses the GPIO matrix.
.. image:: /../_static/spi_master_freq_tv.png
Corresponding frequency limit for different devices with different *input delay* are shown in the following
table:
Corresponding frequency limits for different Devices with different *input delay* times are shown in the table below.
+--------+------------------+----------------------+-------------------+
| Master | Input delay (ns) | MISO path delay (ns) | Freq. limit (MHz) |
+========+==================+======================+===================+
| IOMUX | 0 | 0 | 80 |
| IO_MUX | 0 | 0 | 80 |
+ (0ns) +------------------+----------------------+-------------------+
| | 50 | 50 | 16 |
+ +------------------+----------------------+-------------------+
@ -512,27 +429,27 @@ table:
Known Issues
------------
1. Half duplex mode is not compatible with DMA when both writing and reading phases exist.
1. Half-duplex transactions are not compatible with DMA when both writing and reading phases are used.
If such transactions are required, you have to use one of the alternative solutions:
1. use full-duplex mode instead.
2. disable the DMA by setting the last parameter to 0 in bus initialization function just as below:
1. Use full-duplex transactions instead.
2. Disable DMA by setting the bus initialization function's last parameter to 0 as follows:
``ret=spi_bus_initialize(VSPI_HOST, &buscfg, 0);``
this may prohibit you from transmitting and receiving data longer than 64 bytes.
3. try to use command and address field to replace the write phase.
This can prohibit you from transmitting and receiving data longer than 64 bytes.
3. Try using the command and address fields to replace the write phase.
2. Full duplex mode is not compatible with the *dummy bit workaround*, hence the frequency is limited. See :ref:`dummy
2. Full-duplex transactions are not compatible with the *dummy bit workaround*, hence the frequency is limited. See :ref:`dummy
bit speed-up workaround <dummy_bit_workaround>`.
3. ``cs_ena_pretrans`` is not compatible with command, address phases in full duplex mode.
3. ``cs_ena_pretrans`` is not compatible with the command and address phases of full-duplex transactions.
Application Example
-------------------
Display graphics on the 320x240 LCD of WROVER-Kits: :example:`peripherals/spi_master`.
The code example for displaying graphics on an ESP32-WROVER-KIT's 320x240 LCD screen can be found in the :example:`peripherals/spi_master` directory of ESP-IDF examples.
API Reference - SPI Common

View file

@ -0,0 +1,117 @@
ICMP Echo
=========
Overview
--------
ICMP (Internet Control Message Protocol) is used for diagnostic or control purposes or generated in response to errors in IP operations. The common network util ``ping`` is implemented based on the ICMP packets with the type field value of 0, also called ``Echo Reply``.
During a ping session, the source host firstly sends out an ICMP echo request packet and wait for an ICMP echo reply with specific times. In this way, it also measures the round-trip time for the messages. After receiving a valid ICMP echo reply, the source host will generate statistics about the IP link layer (e.g. packet loss, elapsed time, etc).
It is common that IoT device needs to check whether a remote server is alive or not. The device should show the warnings to users when it got offline. It can be achieved by creating a ping session and sending/parsing ICMP echo packets periodically.
To make this internal procedure much easier for users, ESP-IDF provides some out-of-box APIs.
Create a new ping session
^^^^^^^^^^^^^^^^^^^^^^^^^
To create a ping session, you need to fill in the ``esp_ping_config_t`` configuration structure firstly, specifying target IP address, interval times, and etc. Optionally, you can also register some callback functions with the `esp_ping_callbacks_t`` structure.
Example method to create a new ping session and register callbacks:
.. highlight:: c
::
static void test_on_ping_success(esp_ping_handle_t hdl, void *args)
{
// optionally, get callback arguments
// const char* str = (const char*) args;
// printf("%s\r\n", str); // "foo"
uint8_t ttl;
uint16_t seqno;
uint32_t elapsed_time, recv_len;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_TTL, &ttl, sizeof(ttl));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len));
esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time));
printf("%d bytes from %s icmp_seq=%d ttl=%d time=%d ms\n",
recv_len, inet_ntoa(target_addr.u_addr.ip4), seqno, ttl, elapsed_time);
}
static void test_on_ping_timeout(esp_ping_handle_t hdl, void *args)
{
uint16_t seqno;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
printf("From %s icmp_seq=%d timeout\n", inet_ntoa(target_addr.u_addr.ip4), seqno);
}
static void test_on_ping_end(esp_ping_handle_t hdl, void *args)
{
uint32_t transmitted;
uint32_t received;
uint32_t total_time_ms;
esp_ping_get_profile(hdl, ESP_PING_PROF_REQUEST, &transmitted, sizeof(transmitted));
esp_ping_get_profile(hdl, ESP_PING_PROF_REPLY, &received, sizeof(received));
esp_ping_get_profile(hdl, ESP_PING_PROF_DURATION, &total_time_ms, sizeof(total_time_ms));
printf("%d packets transmitted, %d received, time %dms\n", transmitted, received, total_time_ms);
}
void initialize_ping()
{
/* convert URL to IP address */
getaddrinfo("www.espressif.com", NULL, &hint, &res) == 0);
struct in_addr addr4 = ((struct sockaddr_in *) (res->ai_addr))->sin_addr;
inet_addr_to_ip4addr(ip_2_ip4(&target_addr), &addr4);
freeaddrinfo(res);
esp_ping_config_t ping_config = ESP_PING_DEFAULT_CONFIG();
ping_config.target_addr = target_addr; // target IP address
ping_config.count = ESP_PING_COUNT_INFINITE; // ping in infinite mode, esp_ping_stop can stop it
/* set callback functions */
esp_ping_callbacks_t cbs;
cbs.on_ping_success = test_on_ping_success;
cbs.on_ping_timeout = test_on_ping_timeout;
cbs.on_ping_end = test_on_ping_end;
cbs.cb_args = "foo"; // arguments that will feed to all callback functions, can be NULL
cbs.cb_args = eth_event_group;
esp_ping_handle_t ping;
esp_ping_new_session(&ping_config, &cbs, &ping);
}
Start and Stop ping session
^^^^^^^^^^^^^^^^^^^^^^^^^^^
You can start and stop ping session with the handle returned by ``esp_ping_new_session``. Note that, the ping session won't start automatically after creation. If the ping session is stopped, and restart again, the sequence number in ICMP packets will recount from zero again.
Delete a ping session
^^^^^^^^^^^^^^^^^^^^^
If a ping session won't be used any more, you can delete it with ``esp_ping_delete_session``. Please make sure the ping session is in stop state (i.e. you have called ``esp_ping_stop`` before or the ping session has finished all the procedures) when you call this function.
Get runtime statistics
^^^^^^^^^^^^^^^^^^^^^^
As the example code above, you can call ``esp_ping_get_profile`` to get different runtime statistics of ping session in the callback function.
Application Example
-------------------
ICMP echo example: :example:`protocols/icmp_echo`
API Reference
-------------
.. include:: /_build/inc/ping_sock.inc

View file

@ -11,6 +11,7 @@ Application Protocols
HTTP Client <esp_http_client>
HTTP Server <esp_http_server>
HTTPS Server <esp_https_server>
ICMP Echo <icmp_echo>
Local Control <esp_local_ctrl>
mDNS <mdns>
Modbus <modbus>

View file

@ -22,7 +22,7 @@ Unzip the zip file to ``C:\`` (or some other location, but this guide assumes ``
.. important::
If another toolchain location is used (different than the default `C:\msys32`), please ensure that the path where the all-in-one toolchain gets unzipped is a plain ASCII, contains no spaces, symlinks or accents.
If another toolchain location is used (different than the default ``C:\msys32``), please ensure that the path where the all-in-one toolchain gets unzipped is a plain ASCII, contains no spaces, symlinks or accents.
Check it Out

View file

@ -4,7 +4,7 @@ ESP32 Hardware Reference
:link_to_translation:`zh_CN:[中文]`
.. toctree::
:maxdepth: 2
:maxdepth: 3
Technical Reference Manual (PDF) <https://espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf>
Datasheet (PDF) <https://espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf>

View file

@ -4,14 +4,24 @@
Previous Versions of ESP32 Modules and Boards
*********************************************
This sections contains overview and links to documentation of previous version ESP32 Modules and Boards that have been replaced with newer versions or discontinued. It is maintained for convenience of users as several of these boards are still in use and some may still be available for purchase.
This sections contains overview and links to documentation of previous version ESP32 Modules and Boards that have been replaced with newer versions or discontinued. It is maintained for convenience of users as previous versions of some modules and boards are still in use and some may still be available for purchase.
Modules (No updated or discontinued modules)
============================================
So far, no modules have been updated or discontinued.
Development Boards
==================
To see the latest development boards, please refer to section :ref:`esp-modules-and-boards`.
.. _esp-modules-and-boards-esp32-pico-kit-v4:
ESP32-PICO-KIT V4
=================
-----------------
The smallest ESP32 development board with all the components required to connect it directly to a PC USB port, and pin headers to plug into a mini breadboard. It is equipped with ESP32-PICO-D4 module that integrates 4 MB flash memory, a crystal oscillator, filter capacitors and RF matching circuit in one single package. As result, the fully functional development board requires only a few external components that can easy fit on a 20 x 52 mm PCB including antenna, LDO, USB-UART bridge and two buttons to reset it and put into download mode.
@ -25,7 +35,7 @@ The smallest ESP32 development board with all the components required to connect
Comparing to ESP32-PICO-KIT V3, this version has revised printout and reduced number of exposed pins. Instead of 20, only 17 header pins are populated, so V4 can fit into a mini breadboard.
Documentation
-------------
^^^^^^^^^^^^^
* :doc:`../hw-reference/get-started-pico-kit`
* `ESP32-PICO-KIT V4 Schematic <https://dl.espressif.com/dl/schematics/esp32-pico-kit-v4_schematic.pdf>`_ (PDF)
@ -34,7 +44,7 @@ Documentation
.. _esp-modules-and-boards-esp32-pico-kit-v3:
ESP32-PICO-KIT V3
=================
-----------------
The first public release of Espressif's ESP32-PICO-D4 module on a mini development board. The board has a USB port for programming and debugging and two rows of 20 pin headers to plug into a breadboard. The ESP32-PICO-D4 module itself is small and requires only a few external components. Besides two core CPUs it integrates 4MB flash memory, a crystal oscillator and antenna matching components in one single 7 x 7 mm package. As a result the module and all the components making the complete development board fit into 20 x 52 mm PCB.
@ -46,7 +56,7 @@ The first public release of Espressif's ESP32-PICO-D4 module on a mini developme
ESP32-PICO-KIT V3 board
Documentation
-------------
^^^^^^^^^^^^^
* :doc:`../hw-reference/get-started-pico-kit-v3`
* `ESP32-PICO-KIT V3 Schematic <https://dl.espressif.com/dl/schematics/esp32-pico-kit-v3_schematic.pdf>`_ (PDF)
@ -56,7 +66,7 @@ Documentation
.. _esp-modules-and-boards-esp32-devkitc-v2:
ESP32 Core Board V2 / ESP32 DevKitC
===================================
-----------------------------------
Small and convenient development board with ESP-WROOM-32 module installed, break out pin headers and minimum additional components. Includes USB to serial programming interface, that also provides power supply for the board. Has pushbuttons to reset the board and put it in upload mode.
@ -68,7 +78,7 @@ Small and convenient development board with ESP-WROOM-32 module installed, break
ESP32 Core Board V2 / ESP32 DevKitC board
Documentation
-------------
^^^^^^^^^^^^^
* :doc:`../hw-reference/get-started-devkitc-v2`
* `ESP32 DevKitC V2 Schematic <https://dl.espressif.com/dl/schematics/ESP32-Core-Board-V2_sch.pdf>`__ (PDF)
@ -77,7 +87,7 @@ Documentation
.. _esp-modules-and-boards-esp-wrover-kit-v3:
ESP-WROVER-KIT V3
=================
-----------------
The ESP-WROVER-KIT V3 development board has dual port USB to serial converter for programming and JTAG interface for debugging. Power supply is provided by USB interface or from standard 5 mm power supply jack. Power supply selection is done with a jumper and may be put on/off with a separate switch. This board has MicroSD card slot, 3.2” SPI LCD screen and dedicated header to connect a camera. It provides RGB diode for diagnostics. Includes 32.768 kHz XTAL for internal RTC to operate it in low power modes.
@ -95,7 +105,7 @@ This is the first release of ESP-WROVER-KIT shipped with :ref:`esp-modules-and-b
The camera header has been changed from male back to female. The board soldermask is matte black. The board on picture above has :ref:`esp-modules-and-boards-esp32-wrover` is installed.
Documentation
-------------
^^^^^^^^^^^^^
* :doc:`../hw-reference/get-started-wrover-kit-v3`
* `ESP-WROVER-KIT V3 Schematic <https://dl.espressif.com/dl/schematics/ESP-WROVER-KIT_SCH-3.pdf>`__ (PDF)
@ -105,7 +115,7 @@ Documentation
.. _esp-modules-and-boards-esp-wrover-kit-v2:
ESP-WROVER-KIT V2
=================
-----------------
This is updated version of ESP32 DevKitJ V1 described above with design improvements identified when DevKitJ was in use, e.g. improved support for SD card. By default board has ESP-WROOM-32 module installed.
@ -119,7 +129,7 @@ This is updated version of ESP32 DevKitJ V1 described above with design improvem
Comparing to previous version, this board has a shiny black finish and a male camera header.
Documentation
-------------
^^^^^^^^^^^^^
* :doc:`../hw-reference/get-started-wrover-kit-v2`
* `ESP-WROVER-KIT V2 Schematic <https://dl.espressif.com/dl/schematics/ESP-WROVER-KIT_SCH-2.pdf>`__ (PDF)
@ -129,7 +139,7 @@ Documentation
.. _esp-modules-and-boards-esp-wrover-kit-v1:
ESP-WROVER-KIT V1 / ESP32 DevKitJ V1
====================================
------------------------------------
The first version of ESP-WROVER-KIT development board. Shipped with ESP-WROOM-32 on board.
@ -148,7 +158,7 @@ All versions of ESP-WROVER-KIT are ready to accommodate an ESP-WROOM-32 or ESP32
The board has red soldermask.
Documentation
-------------
^^^^^^^^^^^^^
* `ESP-WROVER-KIT V1 Schematic <https://dl.espressif.com/dl/schematics/ESP32-DevKitJ-v1_sch.pdf>`__ (PDF)
* :doc:`../api-guides/jtag-debugging/index`
@ -157,7 +167,7 @@ Documentation
.. _esp-modules-and-boards-esp32-demo-board:
ESP32 Demo Board V2
===================
-------------------
One of first feature rich evaluation boards that contains several pin headers, dip switches, USB to serial programming interface, reset and boot mode press buttons, power switch, 10 touch pads and separate header to connect LCD screen.
@ -170,7 +180,7 @@ One of first feature rich evaluation boards that contains several pin headers, d
Production of this board is discontinued.
Documentation
-------------
^^^^^^^^^^^^^
* `ESP32 Demo Board V2 Schematic <https://dl.espressif.com/dl/schematics/ESP32-Demo-Board-V2_sch.pdf>`__ (PDF)
* `FTDI Virtual COM Port Drivers`_

View file

@ -16,8 +16,8 @@ This document provides description of modules and development boards currently a
.. _esp-wroom-solo-wrover-modules:
WROOM, SOLO, WROVER, and PICO Modules
=====================================
Modules
=======
This is a family of ESP32-based modules with some integrated key components, including a crystal oscillator and an antenna matching circuit. The modules constitute ready-made solutions for integration into final products. If combined with a few extra components, such as a programming interface, bootstrapping resistors, and pin headers, these modules can also be used for evaluation of ESP32's functionality.

View file

@ -2,6 +2,8 @@
Flash Encryption
================
:link_to_translation:`zh_CN:[中文]`
This document provides introduction to Flash encryption concept on ESP32 and demonstrates how this feature can be used during development as well as production by the user using a sample example. The primary intention of the document is to act as a quick start guide to test and verify flash encryption operations. The details of the flash encryption block can be found in the `ESP32 Technical reference manual`_.
.. _ESP32 Technical Reference Manual: https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf
@ -17,7 +19,7 @@ With flash encryption enabled, following kinds of flash data are encrypted by de
- Partition Table
- All "app" type partitions
Other type of flash data are encrypted conditionally:
Other types of flash data are encrypted conditionally:
- Secure boot bootloader digest (if secure boot is enabled)
- Any partition marked with the "encrypted" flag in the partition table

View file

@ -0,0 +1 @@
.. include:: ../../../en/api-reference/protocols/icmp_echo.rst

View file

@ -11,6 +11,7 @@
Websocket Client <esp_websocket_client>
HTTP 服务器 <esp_http_server>
HTTPS Server <esp_https_server>
ICMP Echo <icmp_echo>
ASIO <asio>
ESP-MQTT <mqtt>
Modbus slave <modbus>

View file

@ -1,66 +1,65 @@
在用户配置文件中添加 IDF_PATH (传统 GNU Make)
=============================================
在用户配置文件中添加 IDF_PATH(传统 GNU Make
==============================================
:link_to_translation:`en:[English]`
.. include:: ../gnu-make-legacy.rst
为了在系统多次重新启动时保留 “IDF_PATH” 环境变量的设置,请按照以下说明将其添加到用户配置文件中
为了在系统多次启动时,保留 ``IDF_PATH`` 环境变量的设置,请按照以下说明将该变量的设置增加至用户配置文件
.. _add-idf_path-to-profile-windows-legacy:
Windows 操作系统
-----------------
Windows
-------
用户配置文件脚本保存在 ``C:/msys32/etc/profile.d/`` 路径下,每次打开 MSYS2 窗口时均会执行。
用户配置文件脚本存放在 ``C:/msys32/etc/profile.d/`` 目录中。每次打开 MSYS2 窗口时,系统都执行这些脚本。
#. 在 ``C:/msys32/etc/profile.d/`` 目录下创建一个新的脚本文件,并将其命名为 ``export_idf_path.sh``
#. 指定您的 ESP-IDF 保存路径,这通常与您的系统配置有关,比如 ``C:\msys32\home\user-name\esp\esp-idf``
#. 在 ``C:/msys32/etc/profile.d/`` 目录下创建一个新的脚本文件。将其命名为 ``export_idf_path.sh``
#. 在脚本文件中加入下方 ``export`` 命令行,例::
#. 确定 ESP-IDF 目录的路径。路径与系统配置有关,例如 ``C:\msys32\home\user-name\esp\esp-idf``
#. 在脚本中加入 ``export`` 命令e.g.::
export IDF_PATH="C:/msys32/home/user-name/esp/esp-idf"
请将原始 Windows 路径中将反斜杠替换为正斜杠。
#. 保存脚本。
#. 关闭 MSYS2 窗口并再次打开。输入以下命令检查是否设置了 ``IDF_PATH``::
printenv IDF_PATH
将此前在脚本文件中输入的路径打印出来。
如果您不想在用户配置文件中永久设置 ``IDF_PATH``,则应在打开 MSYS2 窗口时手动输入::
export IDF_PATH="C:/msys32/home/user-name/esp/esp-idf"
如您在安装用于 ESP32 开发的软件时,从 :ref:`get-started-setup-path-legacy` 小节跳转到了这里,请返回到 :ref:`get-started-start-project-legacy` 小节
注意请将反斜杠替换为 Windows 操作系统路径要求的正斜杠。
.. _add-idf_path-to-profile-linux-macos-legacy:
#. 保存脚本文件。
Linux and MacOS
---------------
``~/.profile`` 文件中加入以下指令,创建 ``IDF_PATH``
export IDF_PATH=~/esp/esp-idf
注销并重新登录以使此更改生效。
.. note::
如果将 ``/bin/bash`` 已设为登录 shell并且 ``.bash_profile````.profile`` 同时存在,则更新 ``.bash_profile``
运行以下命令以确保 ``IDF_PATH`` 已经设置好::
#. 关闭 MSYS2 窗口,并重新打开。运行以下命令,检查 ``IDF_PATH`` 是否成功设置::
printenv IDF_PATH
此前在 ``~/.profile`` 文件中输入(或者手动设置)的路径应该被打印出来
如果设置成功,则此时将打印之前在脚本中输入的路径。
如果不想永久设置 ``IDF_PATH``,每次重启或者注销时在终端窗口中手动输入::
如果您不希望永久设置 ``IDF_PATH`` 变量,也可以在每次打开 MSYS2 窗口时进行手动设置::
export IDF_PATH="C:/msys32/home/user-name/esp/esp-idf"
如果您是在安装 ESP32 软件的过程中从 :ref:`get-started-setup-path-legacy` 章节跳转至此,请返回 :ref:`get-started-start-project-legacy` 章节。
.. _add-idf_path-to-profile-linux-macos-legacy:
Linux 和 MacOS 操作系统
-------------------------
``~/.profile`` 文件中增加以下命令,设置 ``IDF_PATH`` 变量::
export IDF_PATH=~/esp/esp-idf
如果您从 :ref:`get-started-setup-path-legacy` 小节跳转到了这里,在安装用于 ESP32 开发的软件时,返回到 :ref:`get-started-start-project-legacy` 小节。
退出并重新登陆,检查设置是否生效。
.. note::
如果您已将 ``/bin/bash`` 设置为登录 shell且同时存在 ``.bash_profile````.profile``,则请更新 ``.bash_profile``
运行以下命令,检查 ``IDF_PATH`` 是否成功设置::
printenv IDF_PATH
如果设置成功,则此时将打印之前在 ``~/.profile`` 文件中输入(或手动设置)的路径。
如果您不希望永久设置 ``IDF_PATH`` 变量,也可以在每次重新打开终端窗口时进行手动设置::
export IDF_PATH=~/esp/esp-idf
如果您是在安装 ESP32 软件的过程中从 :ref:`get-started-setup-path-legacy` 章节跳转至此,请返回 :ref:`get-started-start-project-legacy` 章节。

View file

@ -1,6 +1,6 @@
********************************************
Eclipse IDE 的创建和烧录指南 (传统 GNU Make)
********************************************
**********************************************
Eclipse IDE 的创建和烧录指南(传统 GNU Make
**********************************************
:link_to_translation:`en:[English]`
.. include:: ../gnu-make-legacy.rst
@ -12,104 +12,99 @@ Eclipse IDE 的创建和烧录指南 (传统 GNU Make)
Eclipse IDE 是一个可视化的集成开发环境,可用于编写、编译和调试 ESP-IDF 项目。
* 首先,请在您的平台上安装相应的 ESP-IDF具体步骤请参考适用于 Windows、OS X 和 Linux 的相应安装步骤
* 首先,请在您的平台上安装相应的 ESP-IDF具体步骤请按照您的操作系统Windows、OS X 和 Linux对应的指示进行安装
* 我们建议,您首先使用命令行创建一个项目,大致熟悉项目的创建流程。此外,您还需要使用命令行 (``make menuconfig``) 对您的 ESP-IDF 项目进行配置。目前Eclipse 尚不支持对 ESP-IDF 项目进行配置
* 我们建议,您可以首先使用命令行创建一个项目,大致熟悉项目的创建流程。此外,您还需要使用命令行 (``make menuconfig``) 对您的 ESP-IDF 项目进行配置。目前Eclipse 还无法配置 ESP-IDF 项目
* 下载相应版本的 Eclipse Installer 至您的平台,点击 eclipse.org_
* 从 eclipse.org_ 下载对应操作系统的 Eclipse 安装器
* 运行 Eclipse Installer选择 “Eclipse for C/C++ Development”(有的版本也可能显示为 CDT
* 运行 Eclipse Installer选择 **"Eclipse for C/C++ Development"** (有的版本也可能显示为 CDT
配置 Eclipse
==============
配置 Eclipse IDE
=================
请打开安装好的 Eclipse IDE并按照以下步骤进行操作
打开安装好的 Eclipse IDE并按照以下步骤进行操作
导入新项目
----------
* Eclipse IDE 需使用 ESP-IDF 的 Makefile 功能。因此,在使用 Eclipse 前,您需要先创建一个 ESP-IDF 项目。在创建 ESP-IDF 项目时,您可以使用 GitHub 中的 idf-template 项目模版,或从 esp-idf 子目录中选择一个 example。
* Eclipse 需使用 ESP-IDF 的 Makefile 功能。因此,在使用 Eclipse 前,您需要先创建一个 ESP-IDF 项目。在创建 ESP-IDF 项目时,您可以使用 GitHub 中的 idf-template 项目模版,或从 ESP-IDF 子目录中复制一个 example。
* 运行 Eclipse选择 “File” -> “Import...”
* 运行 Eclipse选择 **"File"** -> **"Import..."**
* 在弹出的对话框中选择 “C/C++” -> “Existing Code as Makefile Project”然后点击 “Next”
* 在弹出的对话框中选择 **"C/C++"** -> **"Existing Code as Makefile Project"**,点击 **"Next"**
* 在下个界面中 “Existing Code Location” 位置输入您的 IDF 项目的路径。注意,这里应输入 ESP-IDF 项目的路径,而非 ESP-IDF 本身的路径(这个稍后再填)。此外,您指定的目标路径中应包含名为 ``Makefile`` (项目 Makefile的文件。
* 进入下一界面,在 **"Existing Code Location"** 处输入您的 IDF 项目路径。注意,这里应输入 ESP-IDF 项目的路径,而非 ESP-IDF 本身的路径(这个稍后再填)。此外,您指定的目标路径中应包含名为 ``Makefile`` (项目 Makefile的文件。
* 在本界面,找到 “Toolchain for Indexer Settings”选择 “Cross GCC”最后点击 “Finish”
* 本界面中,找到 **"Toolchain for Indexer Settings"**,选择 **"Cross GCC"**,最后点击 **"Finish"**
项目属性
--------
* 新项目将出现在 “Project Explorer” 下。请右键选择该项目,并在菜单中选择 “Properties”
* 新项目将出现在 **"Project Explorer"** 下。右键选择该项目,并在菜单中选择 **"Properties"**
* 点击 “C/C++ Build” 下的 “Environment” 属性页,选择 “Add...”,并在对应位置输入 ``BATCH_BUILD````1``
* 点击 **"C/C++ Build"** 下的 **"Environment"** 属性页,选择 **"Add..."**,并在对应位置输入 ``BATCH_BUILD````1``
* 再次点击 “Add...”,并在 “IDF_PATH” 中输入 ESP-IDF 所在的完整安装路径。
* 再次点击 **"Add..."**,并在 ``IDF_PATH`` 处输入 ESP-IDF 所在的完整安装路径。Windows 用户可以从 Windows 浏览器复制 ``IDF_PATH``
* 选择 “PATH” 环境变量,不要改变默认值。如果 Xtensa 工具链的路径尚不在 “PATH” 列表中,则应将该路径 (``something/xtensa-esp32-elf/bin``) 增加至列表,工具链的典型路径类似于 ``/home/user-name/esp/xtensa-esp32-elf/bin``。请注意您需要在附加路径前添加冒号 ``:``。Windows 用户需要将 ``C:\msys32\mingw32\bin;C:\msys32\opt\xtensa-esp32-elf\bin;C:\msys32\usr\bin`` 添加到 ``PATH`` 环境变量的靠前位置(如果您将 msys32 安装到了其它目录,则需要更改对应的路径以匹配您的本地环境)
* 编辑 ``PATH`` 环境变量。保留当前值,并将该路径增加至 Xtensa 工具链安装路径后,作为 IDF 设置的一部分(如已增加至 PATH则可忽略。工具链的常见路径为 ``/home/user-name/esp/xtensa-esp32-elf/bin`` (例)。注意,您需要在补充的路径前增加一个冒号(`:`。Windows 用户需要将 ``PATH`` 环境变量前增加 ``C:\msys32\mingw32\bin;C:\msys32\opt\xtensa-esp32-elf\bin;C:\msys32\usr\bin``。注意,请根据 msys32 的具体安装路径,修改 ``PATH`` 环境变量
* 在 macOS 平台上,增加一个 “PYTHONPATH” 环境变量,并将其设置为 ``/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages`` 保证系统中预先安装的 Python (需安装 pyserial 模块)可以覆盖 Eclipse 内置的任何 Python。
* MacOS 用户需要增加一个 ``PYTHONPATH`` 环境变量,并将其设置为 ``/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages``保证系统中预先安装的 Python (需安装 pyserial 模块)可以覆盖 Eclipse 内置的任何 Python。
* 前往 “C/C++ General” -> “Preprocessor Include Paths” 属性页面。
**特别说明**:如果您的 IDF_PATH 或项目不在 ``C:\msys32\home`` 之下,则应使用自定义编译命令:``python ${IDF_PATH}/tools/windows/eclipse_make.py`` (请注意,这种方法可能导致编译时间显著增加。)
* 点击 “Providers” 选项卡。
* 从 “Providers” 列表中选择 “CDT Cross GCC Built-in Compiler Settings”将 “Command to get compiler specs” 修改为 ``xtensa-esp32-elf-gcc ${FLAGS} -std=c++11 -E -P -v -dD "${INPUTS}"``
* 从 “Providers” 列表中选择 “CDT GCC Build Output Parser”将 “Compiler command pattern” 修改为 ``xtensa-esp32-elf-(gcc|g\+\+|c\+\+|cc|cpp|clang)``
* 前往 **"C/C++ General"** -> **"Preprocessor Include Paths"** 属性页面。点击 **"Providers"** 选项卡。
* 前往 “C/C++ General” -> “Indexer” 属性页面。
* 从 **"Providers"** 列表中选择 **"CDT Cross GCC Built-in Compiler Settings"**,将 **"Command to get compiler specs"** 修改为 ``xtensa-esp32-elf-gcc ${FLAGS} -std=c++11 -E -P -v -dD "${INPUTS}"``
* 去除 "Allow heuristic resolution of includes" 勾选。启用此选项时Eclipse 有时无法找到正确的头文件目录。
* 从 **"Providers"** 列表中选择 **"CDT GCC Build Output Parser"**,将 **"Compiler command pattern"** 修改为 ``xtensa-esp32-elf-(gcc|g\+\+|c\+\+|cc|cpp|clang)``
点击 “C/C++ General" -> "Indexer” 属性页
* 前往 **“C/C++ General”** -> **“Indexer”** 属性页面
* 选择 “Enable project specific settings” 以启用本页上的其他设置。
* 勾选 **“Enable project specific settings”**,启用本页面上的其他设置。
.. note::
* 取消勾选 **“Allow heuristic resolution of includes”**。勾选该选项有时会导致 Eclipse 无法找到正确的头文件目录。
取消选中 “Allow heuristic resolution of includes”。因为启用此选项时有时会导致 Eclipse 无法找到正确的头文件目录
* 前往 **“C/C++ Build”** -> **“Behavior”** 属性页面
点击 “C/C++ Build” -> “Behavior” 属性页。
* 选中 “Enable parallel build” 以启用多任务并行构建。
* 勾选 **“Enable parallel build”**,启用“并行编译”配置。
.. _eclipse-build-project-legacy:
在 Eclipse IDE 中创建项目
--------------------------
在 Eclipse 中构建项目
-----------------------
在首次创建项目前Eclipse IDE 可能会显示大量有关未定义值的错误和警告,主要原因在于项目编译过程中所需的一些源文件是在 ESP-IDF 项目创建过程中自动生成的。因此,这些错误和警告将在 ESP-IDF 项目生成完成后消失。
在首次创建项目前Eclipse IDE 可能会显示大量有关未定义值的错误和警告,这是因为 ESP-IDF 在编译过程中会自动生成一些源文件。因此,这些错误和警告均会在项目编译完成后消失。
* 点击 “OK”关闭 Eclipse IDE 中的 “Properties” 对话框。
* 点击 **“OK”**,关闭 Eclipse IDE 中的 **“Properties”** 对话框。
* 在 Eclipse IDE 界面外,打开命令管理器。进入项目目录,并通过 ``make menuconfig`` 命令对您的 ESP-IDF 项目进行配置。现阶段,您还无法在 Eclipse 中完成本操作。
* 在 Eclipse IDE 界面外,打开命令提示符窗口。进入项目目录,运行 ``make menuconfig`` 设置项目的 ESP-IDF 配置。现阶段,您还无法在 Eclipse 中完成本操作。
*如果您未进行最开始的配置步骤ESP-IDF 将提示在命令行中进行配置 - 但由于 Eclipse 暂时不支持相关功能,因此该项目将挂起或创建失败。*
.. note:
* 返回 Eclipse IDE 界面中,选择 “Project” -> “Build” 创建您的项目
如果您尝试跳过配置步骤直接开始编译ESP-IDF 将在命令行提示需完成配置,但 Eclipse 暂时无法处理这种情况,因此编译将挂起或失败
**提示**:如果您已经在 Eclipse IDE 环境外创建了项目,则可能需要选择 “Project” -> “Clean before choosing Project” -> “Build”允许 Eclipse 查看所有源文件的编译器参数,并借此确定头文件包含路径
* 回到 Eclipse IDE 界面,选择 **“Project”** -> **“Build”** 开始编译项目
在 Eclipse IDE 中烧录项目
--------------------------
**小提示**:如果您的项目不是通过 Eclipse 创建的,则在选择 **“Project”** -> **“Build”** 前还需进行 **“Project”** -> **“Clean”** 操作。如此操作后Eclipse 才能查看所有源文件的编译器参数,并借此确定头文件包含路径。
您可以将 ``make flash`` 目标放在 Eclipse 项目中,通过 Eclipse UI 调用 ``esptool.py`` 进行烧录:
在 Eclipse 中烧录项目
------------------------------------
* 打开 “Project Explorer”并右击您的项目请注意右击项目本身而非项目下的子文件否则 Eclipse 可能会找到错误的 ``Makefile``)。
您可以将 ``make flash`` 目标集成在 Eclipse 项目中,这样就可以通过 Eclipse UI 调用 ``esptool.py`` 进行烧录:
* 从菜单中选择 “Build Targets” -> “Create”。
* 打开 **“Project Explorer”**,并右击您的项目(请注意右击项目本身,而非项目下的子文件,否则 Eclipse 可能会找到错误的 ``Makefile``)。
* 输入 “flash” 为目标名称,其他选项使用默认值
* 从菜单中选择 **“Build Targets”** -> **“Create...”**
* 选择 “Project” -> “Build Target” -> “Build (快捷键Shift + F9创建自定义烧录目标用于编译、烧录项目
* 目标名称输入 “flash”其他选项使用默认值
注意,您将需要通过 ``make menuconfig``,设置串行端口和其他烧录选项。``make menuconfig`` 仍需通过命令行操作(请见平台的对应指南)
* 这时,您可以选择 **“Project”** -> **“Build Target”** -> **“Build”** (快捷键Shift + F9创建自定义烧录目标用于编译、烧录项目
如有需要,请按照相同步骤添加 ``bootloader````partition_table``
注意,您仍需要通过 ``make menuconfig`` 设置串行端口和其他烧录选项。``make menuconfig`` 仍需通过命令行操作(具体请见对应操作系统的指南)。
如有需要,请按照相同步骤添加 ``bootloader````partition_table`` 目标。
.. _eclipse.org: https://www.eclipse.org/

View file

@ -1,49 +1,70 @@
与 ESP32 创建串口连接 (传统 GNU Make)
=======================================
与 ESP32 创建串口连接(传统 GNU Make 系统)
================================================
:link_to_translation:`en:[English]`
.. include:: ../gnu-make-legacy.rst
本章节介绍如何在 ESP32 和 PC 之间建立串口连接。
本章节主要介绍如何创建 ESP32 和 PC 之间的串口连接。
连接 ESP32 和 PC
--------------------
用 USB 线将 ESP32 开发板连接到 PC。如果设备驱动程序没有自动安装确认 ESP32 开发板上的 USB 转串口芯片(或外部串口适配器)型号,在网上搜索驱动程序并进行安装。
用 USB 线将 ESP32 开发板连接到 PC。如果设备驱动程序没有自动安装请先确认 ESP32 开发板上的 USB 转串口芯片(或外部串口适配器)型号,然后在网上搜索驱动程序并进行手动安装。
以下是乐鑫 ESP32 开发板驱动程序的链接:
以下是乐鑫 ESP32 开发板驱动程序的链接:
* ESP32-PICO-KIT 和 ESP32-DevKitC - `CP210x USB to UART Bridge VCP Drivers <https://www.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers>`_
* ESP32-WROVER-KIT 和 ESP32 Demo Board - `FTDI Virtual COM Port Drivers <http://www.ftdichip.com/Drivers/VCP.htm>`_
.. csv-table::
:header: 开发板, USB 驱动, 备注
:widths: 40, 20, 40
以上驱动仅用于参考。当您将上述 ESP32 开发板与 PC 连接时,对应驱动程序应该已经被打包在操作系统中并自动安装。
:ref:`ESP32-DevKitC <esp-modules-and-boards-esp32-devkitc>`, `CP210x`_
`ESP32-LyraT <https://www.espressif.com/en/products/hardware/esp32-lyrat>`_, `CP210x`_
`ESP32-LyraTD-MSC <https://www.espressif.com/en/products/hardware/esp32-lyratd-msc>`_, `CP210x`_
:ref:`ESP32-PICO-KIT <esp-modules-and-boards-esp32-pico-kit>`, `CP210x`_
:ref:`ESP-WROVER-KIT <esp-modules-and-boards-esp-wrover-kit>`, `FTDI`_
:ref:`ESP32 Demo 板 <esp-modules-and-boards-esp32-demo-board>`, `FTDI`_
`ESP-Prog`_, `FTDI`_, 编程板 (w/o ESP32)
`ESP32-MeshKit-Sense <https://github.com/espressif/esp-iot-solution/blob/master/documents/evaluation_boards/ESP32-MeshKit-Sense_guide_en.md#esp32-meshkit-sense-hardware-design-guidelines>`_, n/a, 搭配 `ESP-Prog`_ 使用
`ESP32-Sense Kit <https://github.com/espressif/esp-iot-solution/blob/master/documents/evaluation_boards/esp32_sense_kit_guide_en.md#guide-for-esp32-sense-development-kit>`_, n/a, 搭配 `ESP-Prog`_ 使用
在 Windows 上查看端口
---------------------
.. _CP210x: https://www.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers
.. _FTDI: http://www.ftdichip.com/Drivers/VCP.htm
.. _ESP-Prog: https://github.com/espressif/esp-iot-solution/blob/master/documents/evaluation_boards/ESP-Prog_guide_en.md#introduction-to-the-esp-prog-board
检查 Windows 设备管理器中的 COM 端口列表。断开 ESP32 与 PC 的连接,然后重新连接,查看哪个端口从列表中消失,然后再次显示。
* CP210x: `CP210x USB to UART Bridge VCP Drivers <https://www.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers>`_
* FTDI: `FTDI Virtual COM Port Drivers <http://www.ftdichip.com/Drivers/VCP.htm>`_
以上驱动仅用作参考。正常情况下,当上述任一 ESP32 开发板与 PC 连接时,打包在操作系统中的驱动程序将会开始自动安装。
查看端口Windows 用户)
---------------------------
检查 Windows 设备管理器中的 COM 端口列表。断开 ESP32 与 PC 的连接,然后重连。查看从列表中消失后再次出现的是哪个端口。
以下为 ESP32 DevKitC 和 ESP32 WROVER KIT 串口:
.. figure:: ../../_static/esp32-devkitc-in-device-manager.png
:align: center
:alt: USB to UART bridge of ESP32-DevKitC in Windows Device Manager
:alt: Windows 设备管理器中 ESP32-DevKitC 的 USB 至 UART 桥
:figclass: align-center
设备管理器中 ESP32-DevKitC 的 USB 串口转换器
Windows 设备管理器中 ESP32-DevKitC 的 USB 至 UART 桥
.. figure:: ../../_static/esp32-wrover-kit-in-device-manager.png
:align: center
:alt: Two USB Serial Ports of ESP-WROVER-KIT in Windows Device Manager
:alt: Windows 设备管理器中 ESP-WROVER-KIT 的两个 USB 串行端口
:figclass: align-center
Windows 设备管理器中的两个 USB-WROVER-KIT 串行端口
Windows 设备管理器中 ESP-WROVER-KIT 的两个 USB 串行端口
在 Linux 和 MacOS 上检查串口
-----------------------------
要查看 ESP32 开发板(或外部串口适配器)的串口设备名称,运行以下命令两次,第一次先拔下开发板或适配器,第二次插入开发板或适配器之后再运行命令,第二次运行指令后出现的端口即是 ESP32 对应的串口:
查看端口Linux 和 MacOS 用户)
----------------------------------
查看 ESP32 开发板(或外部转串口适配器)的串口设备名称,请运行两次下述命令。首先,断开开发板或适配器,第一次运行命令;然后,连接开发板或适配器,第二次运行命令。其中,第二次运行命令后出现的端口即是 ESP32 对应的串口:
Linux ::
@ -53,13 +74,12 @@ MacOS ::
ls /dev/cu.*
.. _linux-dialout-group-legacy:
在 Linux 添加用户到 ``dialout``
-----------------------------------
将用户增加至 Linux 的 ``dialout``
-------------------------------------
当前登录用户可以通过 USB 读写串口。在大多数 Linux 发行版中,这是通过以下命令将用户添加到 ``dialout`` 组来完成的::
当前登录用户应当拥有通过 USB 对串口进行读写的权限。在多数 Linux 版本中,您都可以通过以下命令,将用户添加到 ``dialout`` 组,来获取读写权限::
sudo usermod -a -G dialout $USER
@ -67,31 +87,32 @@ MacOS ::
sudo usermod -a -G uucp $USER
重新登录以确保串行端口的读写权限被启用
请重新登录,确保串口读写权限可以生效
确认串口连接
------------------------
-------------
现在验证串口连接是可用的。您可以使用串口终端程序来执行此操作。在这个例子中,我们将使用 `PuTTY SSH Client <http://www.putty.org/>`_ ,它有 Windows 和 Linux 等平台的版本。您也可以使用其他串口程序并设置如下的通信参数
现在,请使用串口终端程序,验证串口连接是否可用。在本示例中,我们将使用 `PuTTY SSH Client <http://www.putty.org/>`_ 进行验证。该工具同时适用于 Windows 和 Linux 操作系统。您也可以使用其他串口程序,设置通信参数如下
运行终端,设置串口:波特率 = 115200数据位 = 8停止位 = 1奇偶校验 = N。以下是设置串口和在 Windows 和 Linux 上传输参数(如 115200-8-1-N的一些截屏示例。注意选择上述步骤中确认的串口进行设置。
运行终端,配置串口:波特率 = 115200数据位 = 8停止位 = 1奇偶校验 = N。在 Windows 和 Linux 中配置串口和通信参数(如 115200-8-1-N的截图如下。注意这里一定要选择在上述步骤中确认的串口进行配置。
.. figure:: ../../_static/putty-settings-windows.png
:align: center
:alt: Setting Serial Communication in PuTTY on Windows
:alt: 在 Windows 操作系统中使用 PuTTY 设置串口通信参数
:figclass: align-center
在 Windows 上的 PuTTY 设置串口传输。
在 Windows 操作系统中使用 PuTTY 设置串口通信参数
.. figure:: ../../_static/putty-settings-linux.png
:align: center
:alt: Setting Serial Communication in PuTTY on Linux
:alt: 在 Linux 操作系统中使用 PuTTY 设置串口通信参数
:figclass: align-center
在 Linux 上的 PuTTY 设置串口传输。
在 Linux 操作系统中使用 PuTTY 设置串口通信参数
在终端打开串口,检查是否有任何打印出来的日志。日志内容取决于加载到 ESP32 的应用程序。下图为 ESP32 的一个示例日志。
然后,请在终端打开串口,查看 ESP32 是否有任何打印,具体打印内容取决于加载至 ESP32 的程序。ESP32 打印示例
.. highlight:: none
@ -116,17 +137,18 @@ MacOS ::
...
如果您看到一些清晰的日志,则表示串行连接正常,您可以继续安装,最后将应用程序上载到 ESP32。
如果打印出的日志是可读的(而不是乱码),则表示串口连接正常。此时,您可以继续进行安装,并最终将应用程序下载到 ESP32。
.. note::
对于某些串口接线配置,在 ESP32 启动并产生串行输出之前,需要在终端程序中禁用串行 RTS DTR 引脚。这取决于串口适配器硬件本身,大多数开发板(包括所有乐鑫开发板)没有这个问题。此问题仅存在于将 RTS DTR 引脚直接连接到 EN GPIO0 引脚上的情况。更多详细信息,参见 `esptool documentation`_。
在某些串口接线方式下,在 ESP32 启动并开始打印串口日志前,需要在终端程序中禁用串口 RTS DTR 引脚。该问题仅存在于将 RTS DTR 引脚直接连接到 EN GPIO0 引脚上的情况,绝大多数开发板(包括乐鑫所有的开发板)都没有这个问题。更多详细信息,参见 `esptool 文档`_。
.. note::
验证通讯正常后关闭串口终端。下一步,我们将使用另一个应用程序来上传 ESP32。此应用程序在终端打开时将无法访问串口。
验证完成后,请关闭串口终端。我们将在后续步骤中向 ESP32 下载新的应用程序,如果未关闭终端,则该应用程序将无法访问串口。
您在安装用于 ESP32 开发的软件时,从 :ref:`get-started-connect-legacy` 小节跳转到了这里,请返回到 :ref:`get-started-configure-legacy` 小节继续阅读
果您是在安装 ESP32 软件的过程中从 :ref:`get-started-connect-legacy` 章节跳转至此,请返回 :ref:`get-started-configure-legacy` 章节
.. _esptool documentation: https://github.com/espressif/esptool/wiki/ESP32-Boot-Mode-Selection#automatic-bootloader
.. _esptool 文档: https://github.com/espressif/esptool/wiki/ESP32-Boot-Mode-Selection#automatic-bootloader

View file

@ -1,13 +1,12 @@
***************************
快速入门 (传统 GNU Make)
快速入门(传统 GNU Make
***************************
:link_to_translation:`en:[English]`
.. include:: ../gnu-make-legacy.rst
本文档旨在指导用户搭建 ESP32 硬件开发的软件环境,
通过一个简单的示例展示如何使用 ESP-IDF (Espressif IoT Development Framework) 配置菜单,并编译、下载固件至 ESP32 开发板等步骤。
本文档旨在指导用户搭建 ESP32 硬件开发的软件环境,通过一个简单的示例展示如何使用 ESP-IDF (Espressif IoT Development Framework) 配置菜单,并编译、下载固件至 ESP32 开发板等步骤。
.. include:: /_build/inc/version-note.inc
@ -17,7 +16,7 @@
ESP32 SoC 芯片支持以下功能:
* 2.4 GHz Wi-Fi
* 蓝牙 4.2 标准
* 蓝牙 4.2
* 高性能双核
* 超低功耗协处理器
* 多种外设
@ -32,13 +31,13 @@ ESP32 采用 40 nm 工艺制成,具有最佳的功耗性能、射频性能、
硬件:
* 一款 **ESP32** 开发板
* **USB 数据线** USB A/Micro USB B
* **USB 数据线** (A 转 Micro-B)
* PCWindows、Linux 或 Mac OS
软件:
* 设置 **工具链**,用于编译 ESP32 **应用程序**
* 获取 **ESP-IDF** 软件开发框架。该框架已经基本包含 ESP32 使用的 API软件库和源代码和运行 **工具链** 的脚本
* 设置 **工具链**,用于编译 ESP32 **应用程序**;
* 获取 **ESP-IDF** 软件开发框架。该框架已经基本包含 ESP32 使用的 API软件库和源代码和运行 **工具链** 的脚本;
* 安装 C 语言编程(**工程**)的 **文本编辑器**,例如 `Eclipse <https://www.eclipse.org/>`_
@ -74,7 +73,7 @@ ESP32 采用 40 nm 工艺制成,具有最佳的功耗性能、射频性能、
设置开发环境
~~~~~~~~~~~~
* :ref:`get-started-setup-toolchain-legacy`
* :doc:`Windows <windows-setup>`:doc:`Linux <linux-setup>`:doc:`macOS <macos-setup>`:ref:`get-started-setup-toolchain-legacy`
* :ref:`get-started-get-esp-idf-legacy`
* :ref:`get-started-setup-path-legacy`
* :ref:`get-started-get-packages-legacy`
@ -96,20 +95,20 @@ ESP32 采用 40 nm 工艺制成,具有最佳的功耗性能、射频性能、
工具链指一套用于编译代码和应用程序的程序。
为了加快开发进度,您可以直接使用乐鑫提供的预制工具链。请根据您的操作系统,点击下方对应的链接,并按照链接中的指导进行安装。
为了加快开发进度,您可以直接使用乐鑫提供的预制工具链。请根据您的操作系点击对应的链接,并按照链接中的指导进行安装。
.. toctree::
:hidden:
Windows <windows-setup>
Linux <linux-setup>
MacOS <macos-setup>
Linux <linux-setup>
macOS <macos-setup>
+-----------------------------+-------------------------+----------------------------------+
| |windows-logo| | |linux-logo| | |macos-logo| |
+-----------------------------+-------------------------+----------------------------------+
| `Windows <windows-legacy>`_ | `Linux <linux-legacy>`_ | `Mac OS <macos-legacy>`_ |
+-----------------------------+-------------------------+----------------------------------+
+-----------------+---------------+---------------+
| |windows-logo| | |linux-logo| | |macos-logo| |
+-----------------+---------------+---------------+
| Windows_ | Linux_ | `macOS`_ |
+-----------------+---------------+---------------+
.. |windows-logo| image:: ../../_static/windows-logo.png
:target: ../get-started-legacy/windows-setup.html
@ -120,15 +119,15 @@ ESP32 采用 40 nm 工艺制成,具有最佳的功耗性能、射频性能、
.. |macos-logo| image:: ../../_static/macos-logo.png
:target: ../get-started-legacy/macos-setup.html
.. _Windows-legacy: ../get-started-legacy/windows-setup.html
.. _Linux-legacy: ../get-started-legacy/linux-setup.html
.. _Mac OS-legacy: ../get-started-legacy/macos-setup.html
.. _Windows: ../get-started-legacy/windows-setup.html
.. _Linux: ../get-started-legacy/linux-setup.html
.. _macOS: ../get-started-legacy/macos-setup.html
.. note::
在本文档中Linux 和 MacOS 操作系统中 ESP-IDF 的默认安装路径为 ``~/esp``Windows 操作系统的默认路径为 ``%userprofile%\esp``。您也可以将 ESP-IDF 安装在任何其他路径下但请注意在使用命令行时进行相应替换。注意ESP-IDF 不支持带有空格的路径。
此外, 您也可以根据自身经验和实际需求,对环境进行个性化设置,而非使用预制工具链。此时,请前往 :ref:`工具链的个性化设置<get-started-customized-setup-legacy>` 章节获取更多信息。
此外, 您也可以根据自身经验和实际需求,对环境进行个性化设置,而非使用预制工具链。此时,请前往 :ref:`get-started-customized-setup-legacy` 章节获取更多信息。
.. _get-started-get-esp-idf-legacy:
@ -152,7 +151,7 @@ ESP-IDF 将下载至 ``~/esp/esp-idf``。
.. note::
在克隆远程仓库时,不要忘记加上 ``--recursive`` 选项。否则,请接着运行以下命令,获取所有子模块 ::
在克隆远程仓库时,不要忘记加上 ``--recursive`` 选项。否则,请接着运行以下命令,获取所有子模块::
cd esp-idf
git submodule update --init
@ -161,7 +160,7 @@ ESP-IDF 将下载至 ``~/esp/esp-idf``。
.. _get-started-setup-path-legacy:
第三步:设置环境变量
=====================
=======================
工具链通过环境变量 ``IDF_PATH`` 获得 ESP-IDF 的目录。因此,您需要在 PC 中设置该环境变量,否则无法编译工程。
@ -173,13 +172,13 @@ ESP-IDF 将下载至 ``~/esp/esp-idf``。
第四步:安装 Python 软件包
==========================
ESP-IDF 所需 Python 软件包位于 ``IDF_PATH/requirements.txt`` 中。您可以运行以下命令进行安装: ::
ESP-IDF 所需 Python 软件包位于 ``IDF_PATH/requirements.txt`` 中。您可以运行以下命令进行安装:您可以运行以下命令进行安装::
python -m pip install --user -r $IDF_PATH/requirements.txt
.. note::
请注意查询您所使用的 Python 解释器的版本(运行命令 ``python --version``),并根据查询结果将上方命令中的 ``python`` 替换为 ``python2``, ``python2.7``,例如 ::
请注意查询您所使用的 Python 解释器的版本(运行命令 ``python --version``),并根据查询结果将上方命令中的 ``python`` 替换为 ``python2``, ``python2.7``,例如::
python2.7 -m pip install --user -r $IDF_PATH/requirements.txt
@ -194,7 +193,7 @@ ESP-IDF 所需 Python 软件包位于 ``IDF_PATH/requirements.txt`` 中。您可
:example:`get-started/hello_world` 复制至您本地的 ``~/esp`` 目录下:
Linux 和 MacOS 操作系统
~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: bash
@ -216,9 +215,9 @@ ESP-IDF 的 :idf:`examples` 目录下有一系列示例工程,都可以按照
ESP-IDF 编译系统不支持带有空格的路径。
.. _get-started-connect-legacy:
第六步:连接设备
==================
===================
现在,请将您的 ESP32 开发板连接到 PC并查看开发板使用的串口。
@ -243,7 +242,7 @@ ESP-IDF 的 :idf:`examples` 目录下有一系列示例工程,都可以按照
请进入 :ref:`get-started-start-project-legacy` 中提到的 ``hello_world`` 目录,并运行工程配置工具 ``menuconfig``
Linux 和 MacOS 操作系统
~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: bash
@ -271,13 +270,13 @@ Windows 操作系统
``menuconfig`` 工具的常见操作见下。
* ``上下箭头``:移动
* 上下箭头:移动
* ``回车``:进入子菜单
* ``ESC 键``:返回上级菜单或退出
* ``英文问号``:调出帮助菜单(退出帮助菜单,请按回车键)。
* ``空格``、``Y 键``或``N 键``:使能/禁用 ``[*]`` 配置选项
* ``英文问号`` :调出有关高亮选项的帮助菜单
* ``/ 键``:寻找配置项目
* ``空格``或 ``Y 键``:选择 ``[*]`` 配置选项;``N 键``:禁用 ``[*]`` 配置选项
* ``英文问号`` (查询配置选项):调出有关该选项的帮助菜单
* ``/ 键``:寻找配置工程
.. note::
@ -292,7 +291,7 @@ Windows 操作系统
第八步:编译和烧录
====================
请使用以下命令,编译烧录工程 ::
请使用以下命令,编译烧录工程::
make flash
@ -315,7 +314,7 @@ Windows 操作系统
Changed.
Attaching SPI flash...
Configuring flash size...
Auto-detected Flash size:4MB
Auto-detected Flash size: 4MB
Flash params set to 0x0220
Compressed 11616 bytes to 6695...
Wrote 11616 bytes (6695 compressed) at 0x00001000 in 0.1 seconds (effective 920.5 kbit/s)...
@ -326,12 +325,12 @@ Windows 操作系统
Compressed 3072 bytes to 82...
Wrote 3072 bytes (82 compressed) at 0x00008000 in 0.0 seconds (effective 8297.4 kbit/s)...
Hash of data verified.
Leaving...
Hard resetting...
如果您希望使用 Eclipse IDE而非 ``make`` 编译系统,请参考 :doc:`Eclipse 指南 <eclipse-setup>`。
如果您希望使用 Eclipse IDE而非 ``make`` 编译系统,请参考 :doc:`Eclipse guide <eclipse-setup>`。
.. _get-started-monitor-legacy:
@ -341,12 +340,12 @@ Windows 操作系统
您可以使用 ``make monitor`` 命令,监视 “hello_world” 的运行情况。
运行该命令后,:doc:`IDF 监视器 <../api-guides/tools/idf-monitor>` 应用程序将启动 ::
运行该命令后,:doc:`IDF 监视器 <../api-guides/tools/idf-monitor>` 应用程序将启动::
$ make monitor
MONITOR
--- idf_monitor on /dev/ttyUSB0 115200 ---
--- Quit:Ctrl+] | Menu:Ctrl+T | Help:Ctrl+T followed by Ctrl+H ---
--- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
ets Jun 8 2016 00:22:57
rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
@ -360,14 +359,14 @@ Windows 操作系统
...
Hello world!
Restarting in 10 seconds...
I (211) cpu_start:Starting scheduler on APP CPU.
I (211) cpu_start: Starting scheduler on APP CPU.
Restarting in 9 seconds...
Restarting in 8 seconds...
Restarting in 7 seconds...
您可使用快捷键 ``Ctrl+]``,退出 IDF 监视器。
如果 IDF 监视器在烧录后很快发生错误,或打印信息全是乱码(见下),很有可能是因为您的开发板用了 26 MHz 晶振,而 ESP-IDF 默认支持大多数开发板使用的 40 MHz 晶振。
如果 IDF 监视器在烧录后很快发生错误,或打印信息全是乱码(见下),很有可能是因为您的开发板用了 26 MHz 晶振,而 ESP-IDF 默认支持大多数开发板使用的 40 MHz 晶振。
.. figure:: ../../_static/get-started-garbled-output.png
:align: center
@ -377,13 +376,13 @@ Windows 操作系统
此时,请您:
1. 退出监视器。
2. 打开 :ref:`menuconfig <get-started-configure>`
2. 打开 :ref:`menuconfig <get-started-configure-legacy>`
3. 进入 ``Component config`` --> ``ESP32-specific`` --> ``Main XTAL frequency`` 进行配置,将 :ref:`CONFIG_ESP32_XTAL_FREQ_SEL` 设置为 26 MHz。
4. 然后,请重新 :ref:`编译和烧录 <get-started-build-and-flash-legacy>` 应用程序。
.. note::
您也可以运行以下命令,一次性执行构建、烧录和监视过程 ::
您也可以运行以下命令,一次性执行构建、烧录和监视过程::
make flash monitor
@ -395,43 +394,40 @@ Windows 操作系统
环境变量
=========
========
用户可以在使用 ``make`` 命令时 **直接设置** 部分环境变量,而无需进入 ``make menuconfig`` 进行重新配置。这些变量包括:
+-----------------+-----------------------------------------------------------------------+
| 变量 | 描述与使用方式 |
+-----------------+-----------------------------------------------------------------------+
| ``ESPPORT`` | 覆盖 ``flash````monitor`` 命令使用的串口。 |
+ +-----------------------------------------------------------------------+
| | 例:``make flash ESPPORT=/dev/ttyUSB1``, ``make monitor ESPPORT=COM1``|
+-----------------+-----------------------------------------------------------------------+
| ``ESPBAUD`` | 覆盖烧录 ESP32 时使用的串口速率。 |
+ +-----------------------------------------------------------------------+
| | 例:``make flash ESPBAUD=9600`` |
+-----------------+-----------------------------------------------------------------------+
| ``MONITORBAUD`` | 覆盖监控时使用的串口速率。 |
+ +-----------------------------------------------------------------------+
| | 例:``make monitor MONITORBAUD=9600`` |
+-----------------+-----------------------------------------------------------------------+
.. list-table::
:widths: 25 75
:header-rows: 1
* - 变量
- 描述与使用方式
* - ``ESPPORT``
- 覆盖 ``flash````monitor`` 命令使用的串口。例:``make flash ESPPORT=/dev/ttyUSB1``, ``make monitor ESPPORT=COM1``
* - ``ESPBAUD``
- 覆盖烧录 ESP32 时使用的串口速率。例:``make flash ESPBAUD=9600``
* - ``MONITORBAUD``
- 覆盖监控时使用的串口速率。例:``make monitor MONITORBAUD=9600``
.. note::
您可导出环境变量(例:``export ESPPORT=/dev/ttyUSB1``)。
在同一会话窗口中,如果未被同步覆盖,所有 ``make`` 命令均会使用导出的环境变量值。
您可导出环境变量(例:``export ESPPORT=/dev/ttyUSB1``)。在同一会话窗口中,如果未被同步覆盖,所有 ``make`` 命令均会使用导出的环境变量值。
更新 ESP-IDF
=============
乐鑫会不时推出更新版本的 ESP-IDF修复 bug 或提出新的特性。因此,在使用时,您也应注意更新您本地的版本。最简单的方法是:直接删除您本地的 ``esp-idf`` 文件夹,然后按照 :ref:`get-started-get-esp-idf-legacy` 中的指示,重新完成克隆。
乐鑫会不时推出更新版本的 ESP-IDF修复 bug 或推出新的特性。因此,您在使用时,也应注意更新您本地的版本。最简单的方法是:直接删除您本地的 ``esp-idf`` 文件夹,然后按照 :ref:`get-started-get-esp-idf-legacy` 中的指示,重新完成克隆。
如果您希望将 ESP-IDF 克隆到新的路径下,请务必 :doc:`重新设置 IDF_PATH <add-idf_path-to-profile>`。否则,工具链将无法找到 ESP-IDF。
此外,您可以仅更新变更部分。具体方式,请前往 :ref:`更新 <updating>` 章节查看。
相关文档
=========
========
.. toctree::
:maxdepth: 1

View file

@ -1,21 +1,20 @@
*****************************************************
从零开始设置 Linux 环境下的工具链 (传统 GNU Make)
*****************************************************
****************************************************
从零开始设置 Linux 环境下的工具链(传统 GNU Make
****************************************************
:link_to_translation:`en:[English]`
.. include:: ../gnu-make-legacy.rst
.. note::
安装工具链的标准流程可以通过阅读文档 :doc:`Linux 平台工具链的标准设置 <linux-setup>` 来获得,:ref:`工具链的自定义设置 <get-started-customized-setup-legacy>` 章节会介绍哪些情况下我们必须要重新定义工具链
安装工具链的标准方法请见 :doc:`这里 <linux-setup>`。请参考 :ref:`工具链自定义设置 <get-started-customized-setup-legacy>` 章节,查看可能需要从头开始设置工具链的情况
安装准备
========
安装必要的工具
==============
编译 ESP-IDF 需要以下软件包:
要想使用 ESP-IDF 进行编译,您需要获取以下软件包:
- Ubuntu 和 Debian::
- Ubuntu and Debian::
sudo apt-get install gcc git wget make libncurses-dev flex bison gperf python python-pip python-setuptools python-serial python-cryptography python-future python-pyparsing python-pyelftools
@ -25,54 +24,53 @@
.. note::
一些旧的2014年之前Linux 发行版中使用的 ``pyserial`` 版本可能是 2.x ESP-IDF并不支持。
在这种情况下,请参考 :ref:`安装依赖的 Python 软件包 <get-started-get-packages-legacy>` 章节,通过 ``pip`` 工具来安装支持的版本。
一些旧的2014年之前Linux 发行版中使用的 ``pyserial`` 版本可能是 2.x ESP-IDF 并不支持。这种情况下,请按照 :ref:`get-started-get-packages-legacy` 章节的介绍,使用 ``pip`` 安装软件包。
从源代码编译工具链
==================
- 安装依赖:
- 安装依赖项:
- CentOS 7::
- CentOS 7::
sudo yum install gawk gperf grep gettext ncurses-devel python python-devel automake bison flex texinfo help2man libtool
- Ubuntu pre-16.04::
- Ubuntu pre-16.04::
sudo apt-get install gawk gperf grep gettext libncurses-dev python python-dev automake bison flex texinfo help2man libtool
- Ubuntu 16.04::
- Ubuntu 16.04 或以上 ::
sudo apt-get install gawk gperf grep gettext python python-dev automake bison flex texinfo help2man libtool libtool-bin
- Debian 9::
- Debian 9::
sudo apt-get install gawk gperf grep gettext libncurses-dev python python-dev automake bison flex texinfo help2man libtool libtool-bin
- Arch::
- Arch::
TODO
新建工作目录,然后进入::
- 创建工作目录,并进入该目录::
mkdir -p ~/esp
cd ~/esp
mkdir -p ~/esp
cd ~/esp
- 下载并编译 ``crosstool-NG``
下载 ``crosstool-NG`` 然后编译:
.. include:: /_build/inc/scratch-build-code.inc
.. include:: /_build/inc/scratch-build-code.inc
编译工具链::
- 编译工具链::
./ct-ng xtensa-esp32-elf
./ct-ng build
chmod -R u+w builds/xtensa-esp32-elf
编译得到的工具链会被保存到 ``~/esp/crosstool-NG/builds/xtensa-esp32-elf``根据 :ref:`Linux 下设置环境变量的标准方法 <setup-linux-toolchain-add-it-to-path-legacy>` 的介绍,将工具链添加到 ``PATH`` 中。
编译得到的工具链会被保存到 ``~/esp/crosstool-NG/builds/xtensa-esp32-elf``请按照 :ref:`标准设置指南 <setup-linux-toolchain-add-it-to-path-legacy>` 的介绍,将工具链添加到 ``PATH``
下一步
======
后续步骤
========
继续设置开发环境,请前往 :ref:`get-started-get-esp-idf-legacy` 章节。
继续设置开发环境,请前往 :ref:`获取 ESP-IDF <get-started-get-esp-idf-legacy>` 章节。

View file

@ -1,18 +1,18 @@
*******************************************
Linux 平台工具链的标准设置 (传统 GNU Make)
*******************************************
********************************************
Linux 平台工具链的标准设置(传统 GNU Make
********************************************
:link_to_translation:`en:[English]`
.. include:: ../gnu-make-legacy.rst
安装前提
=====================
安装准备
========
编译 ESP-IDF 需要以下软件包:
- CentOS 7::
sudo yum install gcc git wget make ncurses-devel flex bison gperf python pyserial python-pyelftools
sudo yum install gcc git wget make ncurses-devel flex bison gperf python python2-cryptography
- Ubuntu and Debian::
@ -24,84 +24,97 @@ Linux 平台工具链的标准设置 (传统 GNU Make)
.. note::
一些旧的2014年之前Linux 发行版中使用的 ``pyserial`` 版本可能是 2.x ESP-IDF并不支持。
在这种情况下,请参考 :ref:`安装依赖的 Python 软件包 <get-started-get-packages-legacy>` 章节,通过 ``pip`` 工具来安装支持的版本。
一些旧的2014年之前Linux 发行版中使用的 ``pyserial`` 版本可能是 2.x ESP-IDF 并不支持。这种情况下,请按照 :ref:`get-started-get-packages-legacy` 章节的介绍,使用 ``pip`` 安装软件包。
工具链设置
===============
工具链设置
==========
.. include:: /_build/inc/download-links.inc
Linux 版的 ESP32 工具链可以从 Espressif 的网站下载:
- 64-bit Linux
- 64 Linux
|download_link_linux64|
|download_link_linux64|
- 32-bit Linux
- 32 Linux
|download_link_linux32|
|download_link_linux32|
1. 下载完成后,将它解压到 ``~/esp`` 目录: :
1. 下载压缩文件之后,解压到 ``~/esp`` 目录中:
- 64-bit Linux:
- 64 位 Linux
.. include:: /_build/inc/unpack-code-linux64.inc
.. include:: /_build/inc/unpack-code-linux64.inc
- 32-bit Linux:
- 32 位 Linux
.. include:: /_build/inc/unpack-code-linux32.inc
.. include:: /_build/inc/unpack-code-linux32.inc
.. _setup-linux-toolchain-add-it-to-path-legacy:
2. 工具链将被解压到 ``~/esp/xtensa-esp32-elf/`` 目录
2. 工具链将被解压到 ``~/esp/xtensa-esp32-elf/`` 路径下
要使用工具链,你还需要在 ``~/.profile`` 文件中更新环境变量 ``PATH``。要使 ``xtensa-esp32-elf`` 在所有的终端会话中都有效,需要将下面这一行代码添加到你的 ``~/.profile`` 文件中: ::
为了正常使用工具链,您必须更新 ``~/.profile`` 文件中的 ``PATH`` 环境变量。此外,您还可以在 ``~/.profile`` 文件中增加以下代码,这样一来,所有终端窗口均可以使用 ``xtensa-esp32-elf``::
export PATH="$HOME/esp/xtensa-esp32-elf/bin:$PATH"
export PATH="$HOME/esp/xtensa-esp32-elf/bin:$PATH"
或者你也可以给上面的命令创建一个别名。这样做的好处是,你只在需要使用它的时候才获取工具链。将下面这行代码添加到 ``~/.profile`` 文件中即可: ::
或者,您可以为上述命令创建一个别名。这样,您只有在需要时才可以使用工具链。如需设置别名,请将以下代码增加至您的 ``〜/ .profile`` 文件中::
alias get_esp32='export PATH="$HOME/esp/xtensa-esp32-elf/bin:$PATH"'
alias get_esp32='export PATH="$HOME/esp/xtensa-esp32-elf/bin:$PATH"'
然后,当你需要使用工具链时,在命令行输入 ``get_esp32``,然后工具链会自动添加到你的 ``PATH`` 中。
这样,您可以在终端输入 ``get_esp32`` 命令将工具链添加至您的 ``PATH``,从而使用工具链。
.. note::
.. note::
如果您已将 ``/bin/bash`` 设置为登录 shell且同时存在 ``.bash_profile````.profile``,则请更新 ``.bash_profile`` 。在 CentOS 环境下, ``alias`` 需要添加到 ``.bashrc`` 文件中。
如果将 ``/bin/bash`` 设置为登录 shell且同时存在 ``.bash_profile````.profile``,则更新 ``.bash_profile`` 。在 CentOS 环境下, ``alias`` 需要添加到 ``.bashrc`` 文件中。
3. 退出并重新登录以使 ``.profile`` 更改生效。 运行以下命令来检查 ``PATH`` 设置是否正确: ::
3. 退出并重新登录以使 ``.profile`` 更改生效。运行以下命令来检查 ``PATH`` 设置是否正确 ::
printenv PATH
检查字符串的开头是否包含类似的工具链路径::
检查字符串的开头是否包含类似的工具链路径::
$ printenv PATH
/home/user-name/esp/xtensa-esp32-elf/bin:/home/user-name/bin:/home/user-name/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
$ printenv PATH
/home/user-name/esp/xtensa-esp32-elf/bin:/home/user-name/bin:/home/user-name/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
这里,您需要将 ``/home/user-name`` 替换为您的安装主目录。
除了 ``/home/user-name``,应该有具体的安装的主路径。
权限问题 /dev/ttyUSB0
------------------------------
某些 Linux 版本可能在烧写 ESP32 时会出现 ``Failed to open port /dev/ttyUSB0`` 错误消息。 :ref:`可以通过将当前用户添加到拨出组来解决<linux-dialout-group-legacy>`
某些 Linux 版本可能在烧写 ESP32 时会出现 ``Failed to open port /dev/ttyUSB0`` 错误消息,这可以通过 :ref:`将当前用户添加到 dialout 组 <linux-dialout-group-legacy>` 来解决。
Arch Linux 用户
----------------
在 Arch 中运行预编译的 gdb (xtensa-esp32-elf-gdb) 需要 ncurses 5但是 Arch 使用的是 ncurses 6。在 AUR_ 中向下兼容的库文件,可用于本地和 lib32 的配置:
在 Arch Linux 中运行预编译 gdb (xtensa-esp32-elf-gdb) 需要 ncurses 5但 Arch 会使用 ncurses 6。
不过AUR_ 中有针对原生和 lib32 配置的向下兼容库:
- https://aur.archlinux.org/packages/ncurses5-compat-libs/
- https://aur.archlinux.org/packages/lib32-ncurses5-compat-libs/
在安装这些软件包之前你可能需要将作者的公钥添加到你的钥匙圈中上面链接中的“Comments”部分有所叙述。
在安装这些软件包之前,您可能需要将作者的公钥添加到您的密钥环中,具体参考上方的 “注释” 部分。
此外,您也可以使用 crosstool-NG 编译一个链接到 ncurses 6 的 gdb。
或者,你也可以使用 crosstool-NG 编译一个链接 ncurses 6 的 gdb。
后续步骤
==========
要继续设置开发环境,请参考 :ref:`get-started-get-esp-idf-legacy` 一节。
继续设置开发环境,请前往 :ref:`get-started-get-esp-idf-legacy` 章节。
相关文档
=================
.. toctree::
:maxdepth: 1
linux-setup-scratch
.. _AUR: https://wiki.archlinux.org/index.php/Arch_User_Repository

View file

@ -1,16 +1,16 @@
**************************************************
从零开始设置 Mac OS 环境下的工具链 (传统 GNU Make)
**************************************************
********************************************************
从零开始设置 Mac OS 环境下的工具链(传统 GNU Make
********************************************************
:link_to_translation:`en:[English]`
.. include:: ../gnu-make-legacy.rst
.. note::
安装工具链的标准流程可以通过阅读文档 :doc:`在 MacOS 上安装 ESP32 工具链 <macos-setup>` 来获得, :ref:`工具链的自定义设置 <get-started-customized-setup-legacy>` 章节会介绍哪些情况下我们必须要重新定义工具链。
安装必要的工具
=====================
安装工具链的标准方法请见 :doc:`这里 <macos-setup>`。请参考 :ref:`工具链自定义设置 <get-started-customized-setup-legacy>` 章节,查看可能需要从头开始设置工具链的情况。
安装准备
========
- 安装 pip::
@ -21,54 +21,54 @@
``pip`` 稍后将用于安装 :ref:`必要的 Python 软件包 <get-started-get-packages-legacy>`
从源代码编译工具链
==================
===================
- 安装依赖:
- 安装依赖项:
- 安装 MacPorts_ 或 homebrew_ 安装包管理器。MacPorts 需要完整的 XCode 软件,而 homebrew 只需要安装 XCode 命令行工具即可。
- 安装 MacPorts_ 或者 homebrew_ 包管理器。MacPorts 需要安装完整的 XCode 软件,但是 homebrew 只需要安装 XCode 命令行工具即可。
.. _homebrew: https://brew.sh/
.. _MacPorts: https://www.macports.org/install.php
- 对于 MacPorts::
- 对于 MacPorts::
sudo port install gsed gawk binutils gperf grep gettext wget libtool autoconf automake
sudo port install gsed gawk binutils gperf grep gettext wget libtool autoconf automake
- 对于 homebrew::
- 对于 homebrew::
brew install gnu-sed gawk binutils gperftools gettext wget help2man libtool autoconf automake
brew install gnu-sed gawk binutils gperftools gettext wget help2man libtool autoconf automake
创建大小写敏感的文件系统镜像::
- 创建一个文件系统镜像(区分大小写)::
hdiutil create ~/esp/crosstool.dmg -volname "ctng" -size 10g -fs "Case-sensitive HFS+"
挂载::
- 挂载::
hdiutil mount ~/esp/crosstool.dmg
创建指向你工作目录的符号链接::
- 创建指向您工作目录的符号链接::
mkdir -p ~/esp
ln -s /Volumes/ctng ~/esp/ctng-volume
进入新创建的工作目录::
- 前往新创建的目录 ::
cd ~/esp/ctng-volume
下载 ``crosstool-NG`` 然后编译:
- 下载并编译 ``crosstool-NG``
.. include:: /_build/inc/scratch-build-code.inc
.. include:: /_build/inc/scratch-build-code.inc
编译工具链::
- 编译工具链::
./ct-ng xtensa-esp32-elf
./ct-ng build
chmod -R u+w builds/xtensa-esp32-elf
编译得到的工具链会被保存到 ``~/esp/ctng-volume/crosstool-NG/builds/xtensa-esp32-elf``根据 :ref:`Mac OS 下设置环境变量的标准方法 <setup-macos-toolchain-add-it-to-path-legacy>` 的介绍,将工具链添加到 ``PATH`` 中。
编译得到的工具链会被保存到 ``~/esp/ctng-volume/crosstool-NG/builds/xtensa-esp32-elf``请按照 :ref:`标准设置指南 <setup-macos-toolchain-add-it-to-path-legacy>` 的介绍,将工具链添加到 ``PATH``
下一步
======
后续步骤
========
继续设置开发环境,请前往 :ref:`获取 ESP-IDF <get-started-get-esp-idf-legacy>` 章节。
继续设置开发环境,请前往 :ref:`get-started-get-esp-idf-legacy` 章节。

View file

@ -1,12 +1,12 @@
************************************************
在 Mac OS 上安装 ESP32 工具链 (传统 GNU Make)
Mac OS 平台工具链的标准设置(传统 GNU Make
************************************************
:link_to_translation:`en:[English]`
.. include:: ../gnu-make-legacy.rst
安装准备
================
========
- 安装 pip::
@ -14,10 +14,10 @@
.. note::
``pip`` 稍后将用于安装 :ref:`必要的 Python 软件包 <get-started-get-packages-legacy>`。
``pip`` 稍后将用于安装 :ref:`所需 Python 包 <get-started-get-packages-legacy>`。
安装工具链
===============
工具链设置
==========
.. include:: /_build/inc/download-links.inc
@ -33,25 +33,25 @@ Mac OS 版本的 ESP32 工具链可以从以下地址下载:
工具链将被解压到 ``~/esp/xtensa-esp32-elf/`` 路径下。
``~/.profile`` 文件中更新 ``PATH`` 环境变量以使用工具链。为了使 ``xtensa-esp32-elf`` 在各种终端会话中都可用,在 ``~/.profile`` 文件中加上以下指令::
为了正常使用工具链,您必须更新 ``~/.profile`` 文件中的 ``PATH`` 环境变量。此外,您还可以在 ``~/.profile`` 文件中增加以下代码,这样一来,所有终端窗口均可以使用 ``xtensa-esp32-elf``::
export PATH=$HOME/esp/xtensa-esp32-elf/bin:$PATH
export PATH=$HOME/esp/xtensa-esp32-elf/bin:$PATH
或者,您可以为上述命令创建一个别名。这样只有执行以下指令时工具链才能被使用。将下面的指令添加到您的 ``〜/ .profile`` 文件中::
或者,您可以为上述命令创建一个别名。这样,您只有在需要时才可以使用工具链。如需设置别名,请将以下代码增加至您的 ``〜/ .profile`` 文件中::
alias get_esp32="export PATH=$HOME/esp/xtensa-esp32-elf/bin:$PATH"
当需要使用工具链时,在命令行里输入 ``get_esp32``,就可以将工具链添加到 ``PATH`` 中。
这样,您可以在终端输入 ``get_esp32`` 命令将工具链添加至您的 ``PATH``,从而使用工具链。
下一步
==========
后续步骤
========
前往 :ref:`get-started-get-esp-idf-legacy` 继续配置开发环境
继续设置开发环境,请前往 :ref:`get-started-get-esp-idf-legacy` 章节
相关文档
=================
========
.. toctree::
:maxdepth: 1

View file

@ -1,20 +1,20 @@
通过 make 指令创建和烧录项目 (传统 GNU Make)
============================================
通过 make 指令创建和烧录项目(传统 GNU Make
=================================================
:link_to_translation:`en:[English]`
.. include:: ../gnu-make-legacy.rst
寻找项目
-----------------
--------
`esp-idf-template <https://github.com/espressif/esp-idf-template>`_ 项目一样ESP-IDF 在 Github 上的 :idf:`examples` 目录下也有示例项目。
除了 `esp-idf-template <https://github.com/espressif/esp-idf-template>`_ 项目模版外ESP-IDF 还在 GitHub 仓库中的 :idf:`examples` 目录下提供多个示例项目。
找到需要的项目后,切换到其目录,然后可以对其进行配置和构建
请找到并进入您想要的项目,开始配置、构建该项目
配置项目
------------------------
--------
::
@ -22,53 +22,58 @@
编译项目
----------------------
--------
::
make all
... 该命令将配置 app 和 bootloader 并根据配置生成分区表。
该命令将配置 app 和 bootloader并根据配置生成分区表。
烧录项目
---------------------
--------
``make all`` 结束后,系统将打印一命令行提示您如何使用 esptool.py 烧录芯片。用户也可以通过以下指令进行烧录::
``make all`` 完成后将打印一行命令,提示您如何使用 esptool.py 烧录芯片。不过,您可以使用以下命令进行烧录::
make flash
这种方法将烧录整个项目(包括 app, bootloader 和分割表)到芯片中。通过命令 `make menuconfig` 可以配置串口。
该命令可以将整个项目(包括 app、bootloader 和分区表)烧录至新芯片。此外,如果分区表中存在 ota_data则该命令还会同时将初始 ota_data 烧录至芯片。
该命令允许您直接从 factory 分区中运行新加载的 app如果 factory 分区不存在,则从第一个 OTA 分区开始运行)。
您可以使用 ``make menuconfig`` 配置串口的烧录。
运行 ``make flash`` 之前无需运行 ``make all``。运行 ``make flash`` 将自动重建烧录所需的一切。
运行 ``make flash`` 之前无需单独运行 ``make all````make flash`` 命令本身就可以自动构建所需的文件
仅编译和烧录应用程序
---------------------------------
--------------------
最初的烧录之后,用户可以仅创建烧录 app不烧录 bootloader 和分区表:
首次烧录完成后,您可以只希望烧录并编译应用程序,而不再包括 bootloader 和分区表。这种情况可以使用以下命令:
* ``make app`` - 仅创建应用程序。
* ``make app-flash`` - 仅烧录应用程序
* ``make app`` -- 仅编译应用程序
* ``make app-flash`` -- 仅烧录应用程序
需要时 ``make app-flash`` 指令将自动重建 app。
``make app-flash`` -- 在有需要时自动重新编译应用程序
如果没有变化,每次都重新烧录、编译 bootloader 和分区表也没有坏处。
如果 bootloader 和分区表不变的话,对他们进行重新烧录并不会有负面影响。
分区表
-------------------
------
编译完项目后,"build" 目录将包含一个名为 "my_app.bin" 的二进制文件。这是一个可由 bootloader 加载的 ESP32 映像二进制文件。
项目编译完成后,"build" 目录下将出现一个名为 "my_app.bin" (例)的二进制文件。这是一个可由 bootloader 加载的 ESP32 映像文件。
一个 ESP32 flash 可以包含多个应用程序,以及多种数据(校准数据,文件系统,参数存储等)。因此,分区表烧录在 flash 偏移地址 0x8000 的地方
ESP32 的一块 flash 可以包含多个应用程序,以及多种数据(校准数据、文件系统、参数存储等)。因此,在向 flash 烧录分区表时,通常保留 0x8000 的偏移量
分区表中的每个条目都有一个名称标签类型app数据或其他子类型和闪存中分区表被存放的偏移量。
分区表中的每个条目都有名称标签、类型app、数据或其他、子类型及在 flash 中的偏移量。
使用分区表最简单的方法是 `make menuconfig` 并选择一个简单的预定义分区表:
使用分区表最简便的方法是:运行 ``make menuconfig``,选择一个预定义分区表:
* "Single factory app, no OTA"
* "Factory app, two OTA definitions"
在这两种情况下,出厂应用程序的烧录偏移为 0x10000。运行 `make partition_table`,可以打印分区表摘要。
上述两种情况中factory app 在 flash 中的烧录偏移地址均为 0x10000。运行 ``make partition_table`` 命令可以打印分区表摘要。
更多有关 :doc:`分区表 <../api-guides/partition-tables>` 及自定义分区表的内容,请见 :doc:`相关文档 <../api-guides/partition-tables>`
更多关于 :doc:`分区表 <../api-guides/partition-tables>` 的信息,以及如何创建自定义分区表,可以查看 :doc:`文档 <../api-guides/partition-tables>`

View file

@ -1 +1,28 @@
.. include:: ../../en/get-started-legacy/toolchain-setup-scratch.rst
.. _get-started-customized-setup-legacy:
***********************************************
工具链自定义设置(传统 GNU Make 系统)
***********************************************
除了从乐鑫官网(请见 :ref:`get-started-setup-toolchain-legacy`)下载二进制工具链外,您还可以自行编译工具链。
.. include:: ../gnu-make-legacy.rst
如无特殊需求,建议直接使用我们提供的预编译二进制工具链。不过,您可以在以下情况考虑自行编译工具链:
- 需要定制工具链编译配置
- 需要使用其他 GCC 版本(如 4.8.5
- 需要破解 gcc、newlib 或 libstdc++
- 有相关兴趣或时间充裕
- 不信任从网站下载的 bin 文件
如需自行编译工具链,请查看以下文档:
.. toctree::
:maxdepth: 1
windows-setup-scratch
linux-setup-scratch
macos-setup-scratch

View file

@ -1 +1,122 @@
.. include:: ../../en/get-started-legacy/windows-setup-scratch.rst
******************************************************
从零开始设置 Windows 环境下的工具链(传统 GNU Make
******************************************************
.. include:: ../gnu-make-legacy.rst
手动安装所有工具能更好地控制整个安装流程,同时也方便高阶用户进行自定义安装。此外,经验不足的用户还可以参考 :doc:`预编译环境 <windows-setup>` 中的步骤进行准备。
使用预编译环境对工具链进行快速标准设置,请参照 :doc:`windows-setup`
.. _configure-windows-toolchain-from-scratch-legacy:
从零开始配置工具链和环境
==============================================
本流程包括:首先,安装 MSYS2_其次安装 ESP-IDF 所需的 Python 工具包;最后,下载并安装 Xtensa 工具链。
* 请前往 MSYS2_ 安装器页面,并下载 ``msys2-i686-xxxxxxx.exe`` 安装器(我们仅支持 32-bit MSYS 环境32 位和 64 位 Window 均可使用)。截止至本文最新更新之时,最新版安装器为 ``msys2-i686-20161025.exe``
* 完成所有安装步骤。**最后一步时,请不要勾选 "Run MSYS2 32-bit now"。**
* 安装完成后,请从“开始”菜单中找到 "MSYS2 MinGW 32-bit",运行“命令提示符”窗口。
* 为什么要特别打开这个终端窗口MSYS2 会对不同环境进行区分。默认的 "MSYS" 环境与 Cygwin 相仿,会为 Windows 系统的 API 调用增加一个转换层。但为了使用支持 COM 端口的原生 Python我们需要准备好 "MinGW" 环境。
* GitHub 上的 ESP-IDF 仓库的 `tools` 目录下可以找到名为 ``windows_install_prerequisites.sh`` 的脚本。如果您还没有本地 ESP-IDF 副本,也可以从以下地址进行下载(请下载 raw 格式)::idf_raw:`tools/windows/windows_install_prerequisites.sh`,并将其保存至您的电脑。
* 请在 MSYS2 终端窗口中指定该脚本的保存路径。注意,路径格式与 Window 路径相同,但需使用正斜杠 (``/``) 而不是反斜杠 (``\``)。例 ``C:/Users/myuser/Downloads/windows_install_prerequisites.sh``。当然,您也可以直接打开查看该脚本的内容。
* ``windows_install_prerequisites.sh`` 脚本将帮您下载并安装支持 ESP-IDF 的软件包和 ESP32 工具链。
疑难解答
~~~~~~~~~~~~~~~
* MSYS 可能在脚本运行过程中进行自动升级,导致无法使用。此时,您会看到以下错误信息::
*** fatal error - cygheap base mismatch detected - 0x612E5408/0x612E4408
这个问题很大可能是由于 cygwin DLL 版本不兼容。
这种情况下,请完全关闭终端窗口(相当于终止所有进程),并重新打开一个窗口。然后,请在新窗口中重新运行 ``windows_install_prerequisites.sh`` (小技巧:您可以使用“向上箭头”找到之前运行的命令)。此时,更新流程将重启。
* 注意MSYS2 是一个“滚动”发行版,因此安装脚本可能会为您安装比“预编译环境”中更新的软件包。因此,如果遇到与 MSYS2 安装包有关的错误,请前往 `MSYS2-packages 问题列表`_ 页面寻找答案。如果未找到所需答案,请 `提交一个 IDF Issue`_
中国地区的 MSYS2 镜像
~~~~~~~~~~~~~~~~~~~~~~
中国地区有一些(非官方)的 MSYS2 镜像,这可以大幅提高中国地区的下载速度。
如需添加这些镜像,请在运行安装脚本前修改以下两个 MSYS2 镜像列表文件。镜像文件的保存路径为 ``/etc/pacman.d``,比如 ``c:\msys2\etc\pacman.d``
请在 ``mirrorlist.mingw32`` 最上方增加如下语句::
Server = https://mirrors.ustc.edu.cn/msys2/mingw/i686/
Server = http://mirror.bit.edu.cn/msys2/REPOS/MINGW/i686
请在 ``mirrorlist.msys`` 最上方增加如下语句::
Server = http://mirrors.ustc.edu.cn/msys2/msys/$arch
Server = http://mirror.bit.edu.cn/msys2/REPOS/MSYS2/$arch
HTTP 代理
~~~~~~~~~~
您可以在运行“设置脚本”之前,在终端中设置 ``http_proxy`` 变量,从而允许使用 HTTP 代理下载 MSYS 和 PIP::
export http_proxy='http://http.proxy.server:PORT'
或者修改证书::
export http_proxy='http://user:password@http.proxy.server:PORT'
如需始终用代理使用 MSYS请在 MSYS 目录中增加 ``/etc/profile``
其他设置:下载工具链
============================================
.. include:: /_build/inc/download-links.inc
如果您已经安装了 MSYS2或者想要以不同的方式执行操作您可以在此处下载工具链
|download_link_win32|
.. note::
如果您已完成了 :ref:`configure-windows-toolchain-from-scratch-legacy` 中介绍的所有步骤,则已经拥有了工具链,这里无需重复下载。
.. important::
仅拥有工具链 *尚无法* 让您在 Windows 中使用 ESP-IDF。除此之外您还至少需要安装 GNU make、bash 和 sed。上述环境已经包括这些配置此外还有一个主机编译器这样才能使用 menuconfig
后续步骤
==========
继续设置开发环境,请前往 :ref:`get-started-get-esp-idf-legacy` 章节。
.. _updating-existing-windows-environment-legacy:
更新环境
========================
当 IDF 有更新时,有时需要安装新的工具链或为 Windows MSYS2 环境添加新的系统要求。
此时,您无需重新配置环境,仅需更新现有 Windows 环境和工具链即可。
- 将 IDF 更新至您希望的版本。
- 请运行 IDF 仓库中的 ``tools/windows/windows_install_prerequisites.sh`` 脚本。该脚本将帮您安装所有新的软件包,并下载更新工具链至最新版本。
注意,该脚本在更新 MSYS2 时也会遇到 疑难解答_ 中介绍的问题。
如需要同时支持多个 IDF 版本,您可以在不同的目录下配置独立的 MSYS2 环境。或者,您还可以下载多个工具链,并将其解压缩到不同的目录下,然后使用 PATH 环境变量指定默认工具链。
.. _MSYS2: https://msys2.github.io/
.. _MSYS2-packages 问题列表: https://github.com/Alexpux/MSYS2-packages/issues/
.. _提交一个 IDF Issue: https://github.com/espressif/esp-idf/issues/new

View file

@ -1,63 +1,67 @@
**********************************************
Windows 平台工具链的标准设置 (传统 GNU Make)
Windows 平台工具链的标准设置(传统 GNU Make
**********************************************
:link_to_translation:`en:[English]`
.. include:: ../gnu-make-legacy.rst
引言
============
概述
====
Windows 没有内置的 "make" 环境,因此如果要安装工具链,你需要一个 GNU 兼容环境。我们这里使用 MSYS2_ 来提供该环境。你不需要一直使用这个环境(你可以使用 :doc:`Eclipse <eclipse-setup>` 或其它前端工具),但是它是在后台运行的
Windows 系统没有内置的 "make" 环境,所以除了安装工具链之外,您还需要一个兼容 GNU 的环境。这里,我们使用 MSYS2_ 环境兼容 GNU。您无需一直使用这个环境比如您可以使用 :doc:`Eclipse <eclipse-setup>` 或其他前端,仅需在后台运行 MSYS2_ 即可
工具链的设置
===============
快速设置的方法是从 dl.espressif.com 下载集成在一起的工具链和 MSYS2 压缩文件:
工具链设置
==========
https://dl.espressif.com/dl/esp32_win32_msys2_environment_and_toolchain-20181001.zip
最简便的工具链设置方法是从下方地址下载 Windows 多合一工具链和 MSYS2 压缩包文件:
将 zip 压缩文件解压到 ``C:\`` (或其它路径,这里假设是 ``C:\``),它会使用预先准备的环境创建一个 ``msys32`` 目录。
https://dl.espressif.com/dl/esp32_win32_msys2_environment_and_toolchain-20190611.zip
检出
============
请将压缩包文件解压至 ``C:\``(或其他目录,但本文档中以 ``C:\`` 为例),该文件将为您创建一个带有预配置环境的 ``msys32`` 目录。
运行 ``C:\msys32\mingw32.exe`` 打开一个 MSYS2 的终端窗口。该窗口的环境是一个 bash shell。创建一个 ``esp`` 目录作为开发 ESP32 应用的默认地址。运行指令 ::
mkdir -p ~/esp
输入 ``cd ~/esp`` 就进入到新创建的目录。如果没有错误信息出现则表明此步骤已完成。
开始尝试
========
请运行 ``C:\msys32\mingw32.exe`` 文件,打开一个 MSYS2 MINGW32 终端窗口。该窗口的环境为 bash shell。请创建一个名为 ``esp`` 的文件夹,作为 ESP32 应用程序开发的默认目录。您可以使用以下命令创建文件夹::
mkdir -p ~/esp
您还可以通过 ``cd ~/esp`` 命令,进入刚刚创建的文件夹。如无其他问题,本步骤到此结束。
.. figure:: ../../_static/msys2-terminal-window.png
:align: center
:alt: MSYS2 MINGW32 shell window
:alt: MSYS2 MINGW32 shell 窗口
:figclass: align-center
MSYS2 终端窗口
MSYS2 MINGW32 shell 窗口
请在后续步骤中,使用本窗口配置 ESP32 的开发环境。
后续步骤将会使用这个窗口来为 ESP32 设置开发环境。
后续步骤
==========
要继续设置开发环境,请参考 :ref:`get-started-get-esp-idf-legacy`节。
继续设置开发环境,请前往 :ref:`get-started-get-esp-idf-legacy`节。
更新环境
========================
========
当 IDF 更新时,有时需要新的工具链,或者将新的需求添加到 Windows MSYS2 环境中。要将旧版本的预编译环境中的数据移动到新版本
当 IDF 有更新时,有时需要安装新的工具链或为 Windows MSYS2 环境添加新的要求。如需将旧版本预编译环境中的数据迁移至新版本,您可以
- 把旧的 MSYS2 环境(即 ``C:\msys32``)移动/重命名为不同的目录(即 ``C:\msys32_old``)。
- 按照前文所述步骤下载新的预编译环境。
- 将新的 MSYS2 环境解压缩到 ``C:\msys32`` (或其他位置)。
- 找到旧的 ``C:\msys32_old\home`` 目录并把它移到 ``C:\msys32``
- 如果你不再需要 ``C:\msys32_old`` 可以将它删除
1. 复制旧的 MSYS2 环境(即 ``C:\msys32``),并将其移动/重命名到不同目录下(即 ``C:\msys32_old``)。
2. 使用上述步骤,下载新的预编译环境。
3. 将新的 MSYS2 环境解压缩至 ``C:\msys32`` (或您指定的其他位置)。
4. 找到旧的 ``C:\msys32_old\home`` 文件夹,并将其移动至 ``C:\msys32``
5. 此时,如无其他需要,您可以删除旧的 ``C:\msys32_old\home`` 文件夹
你可以在系统上拥有独立的不同的 MSYS2 环境,前提是在不同的目录中。
注意,您可以在电脑中安装多个不同的 MSYS2 环境,仅需将它们保存在不同的路径下即可。
此外,您还可以 :ref:`直接更新现有环境(无需下载新的版本)<updating-existing-windows-environment-legacy>`,但步骤更加复杂。
相关文档
=================
========
.. toctree::
:maxdepth: 1

View file

@ -1,3 +1,3 @@
.. note:: Since ESP-IDF V4.0, the default build system is based on CMake. This documentation is for the legacy build system based on GNU Make. Support for this build system may be removed in future major releases.
.. note:: ESP-IDF V4.0 及之后版本的默认构建系统为 CMake。本文档主要针对之前基于 GNU Make 的传统构建系统。请注意,未来,我们可能不会继续支持基于 GNU Make 的构建系统。

View file

@ -1 +1,690 @@
.. include:: ../../en/security/flash-encryption.rst
Flash 加密
============
:link_to_translation:`en:[English]`
本文档将介绍 ESP32 的 Flash 加密功能,并通过示例展示用户如何在开发及生产过程中使用此功能。本文档旨在引导用户快速入门如何测试及验证 Flash 加密的相关操作。有关 Flash 加密块的详细信息可参见 `ESP32 技术参考手册`_
.. _ESP32 技术参考手册: https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_cn.pdf
概述
------
Flash 加密功能用于加密与 ESP32 搭载使用的 SPI Flash 中的内容。启用 Flash 加密功能后,物理读取 SPI Flash 便无法恢复大部分 Flash 内容。通过明文数据烧录 ESP32 可应用加密功能,(若已启用加密功能)引导加载程序会在首次启动时对数据进行加密。
启用 Flash 加密后,系统将默认加密下列类型的 Flash 数据:
- 引导加载程序
- 分区表
- 所有 “app” 类型的分区
其他类型的 Flash 数据将视情况进行加密:
- 安全启动引导加载程序摘要(如果已启用安全启动)
- 分区表中标有“加密”标记的分区
Flash 加密与 :doc:`安全启动<secure-boot>` 功能各自独立,您可以在关闭安全启动的状态下使用 Flash 加密。但是,为了安全的计算机环境,二者应同时使用。在关闭安全启动的状态下,需运行其他配置来确保 Flash 加密的有效性。详细信息可参见 :ref:`flash-encryption-without-secure-boot`
.. important::
启用 Flash 加密将限制后续 ESP32 更新。请务必阅读本文档(包括 :ref:`flash-encryption-limitations`)了解启用 Flash 加密的影响。
.. _flash-encryption-efuse:
Flash 加密过程中使用的 eFuse
------------------------------
Flash 加密操作由 ESP32 上的多个 eFuse 控制。以下是这些 eFuse 列表及其描述:
::
eFuse 描述 是否可锁定为 默认
读取/写入 值
.. code-block:: none
编码方案 该 2 位宽 eFuse 控制 BLOCK1 中使用的实际 是 0
位数,从而获得最终的 256 位 AES 密钥。编码
方案值解码如下:
0: 256 bits
1: 192 bits
2: 128 bits
最终的 AES 密钥根据 FLASH_CRYPT_CONFIG 的
值产生。
BLOCK1 存储 AES 密钥的 256 位宽 eFuse 块 是 x
FLASH_CRYPT_CONFIG 4 位宽 eFuse控制 AES 加密进程 是 0xF
download_dis_encrypt 设置后,在 UART 下载模式运行时关闭 Flash 加 是 0
密操作
download_dis_decrypt 设置后,在 UART 下载模式运行时关闭 Flash 解 是 0
密操作
FLASH_CRYPT_CNT 7 位 eFuse在启动时启用/关闭加密功能 是 0
偶数位0246
启动时加密 Flash
奇数位1357
启动时不加密 Flash
对上述位的读写访问由 ``efuse_wr_disable````efuse_rd_disable`` 寄存器中的相应位控制。有关 ESP32 eFuse 的详细信息可参见 :doc:`eFuse 管理器 <../api-reference/system/efuse>`
Flash 的加密过程
------------------
假设 eFuse 值处于默认状态,且第二阶段的引导加载程序编译为支持 Flash 加密,则 Flash 加密过程执行如下:
- 首次上电复位时Flash 中的所有数据都是未加密形式(明文数据)。第一阶段加载器 (Rom) 将在 IRAM 中加载第二阶段加载器。
- 第二阶段引导加载程序将读取 flash_crypt_cnt (=00000000b) eFuse 值,因为该值为 0偶数位第二阶段引导加载程序将配置并启用 Flash 加密块,同时将 ``FLASH_CRYPT_CFG`` eFuse 的值编程为 0xF。
- Flash 加密块将生成 AES-256 位密钥,并将其储存于 BLOCK1 eFuse 中。该操作在硬件中执行,软件将无法访问此密钥。
- 接着Flash 加密块将加密 Flash 的内容(根据分区表的标记值)。原地加密可能会有耗时(取决于大分区的耗时)。
- 随后,第二阶段引导加载程序将在 flash_crypt_cnt (=00000001b) 中设置第一个可用位,从而标记已加密的 Flash 内容(偶数位)。
- 在 :ref:`flash_enc_release_mode` 下,第二阶段引导加载程序将把 ``download_dis_encrypt````download_dis_decrypt````download_dis_cache`` 的 eFuse 位改写为 1防止 UART 引导加载程序解密 Flash 的内容。同时也将写保护 ``FLASH_CRYPT_CNT`` 的 eFuse 位。
- 在 :ref:`flash_enc_development_mode` 下,第二阶段引导加载程序将仅改写 ``download_dis_decrypt````download_dis_cache`` 的 eFuse 位,从而允许 UART 引导加载程序重新烧录加密的二进制文件。同时将不会写保护 ``FLASH_CRYPT_CNT`` 的 eFuse 位。
- 然后,第二阶段引导加载程序重启设备并开始执行加密映像,同时将透明解密 Flash 的内容并将其加载至 IRAM。
在开发阶段常需编写不同的明文 Flash 映像,以及测试 Flash 的加密过程。这要求 UART 下载模式能够根据需求不断加载新的明文映像。但是在量产和生产过程中出于安全考虑UART 下载模式不应有权限访问 Flash 内容。因此需要有两种不同的 ESP32 配置:一种用于开发,另一种用于生产。以下章节介绍了 Flash 加密的 :ref:`flash_enc_development_mode`:ref:`flash_enc_release_mode` 及其使用指南。
.. important::
顾名思义,开发模式应 **仅开发过程** 使用,因为该模式可以修改和回读加密的 Flash 内容。
设置 Flash 加密的步骤
----------------------
.. _flash_enc_development_mode:
开发模式
^^^^^^^^^^
可使用 ESP32 内部生成的密钥或外部主机生成的密钥在开发中运行 Flash 加密。
使用 ESP32 生成的 Flash 加密密钥
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
正如上文所说,:ref:`flash_enc_development_mode` 允许用户使用 UART 下载模式多次下载明文映像。需完成以下步骤测试 Flash 加密过程:
- 确保您的 ESP32 设备有 :ref:`flash-encryption-efuse` 中所示的 Flash 加密 eFuse 的默认设置。
- 可在 ``$IDF_PATH/examples/security/flash_encryption`` 文件夹中找到 Flash 加密的示例应用程序。该示例应用程序中有显示 Flash 加密的状态(启用或关闭)以及 ``FLASH_CRYPT_CNT`` eFuse 值。
- 在第二阶段引导加载程序中启用 Flash 加密支持。请前往 :ref:`project-configuration-menu`,选择 "Security Features"。
- 选择 :ref:`Enable flash encryption on boot <CONFIG_SECURE_FLASH_ENC_ENABLED>`
- 默认设置模式为 **开发模式**
- 在引导加载程序 config 下选择适当详细程度的日志。
- 启用 Flash 加密将增大引导加载程序,因而可能需更新分区表偏移。请参见 :ref:`secure-boot-bootloader-size`
- 保存配置并退出。
构建并烧录完整的映像包括:引导加载程序、分区表和 app。这些分区最初以未加密形式写入 Flash。
::
idf.py flash monitor
一旦烧录完成,设备将重置,在下次启动时,第二阶段引导加载程序将加密 Flash 的 app 分区,然后重置该分区。现在,示例应用程序将在运行时解密并执行命令。以下是首次启用 Flash 加密后 ESP32 启动时的样例输出。
::
--- idf_monitor on /dev/cu.SLAB_USBtoUART 115200 ---
--- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
ets Jun 8 2016 00:22:57
rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0018,len:4
load:0x3fff001c,len:8452
load:0x40078000,len:13608
load:0x40080400,len:6664
entry 0x40080764
I (28) boot: ESP-IDF v4.0-dev-850-gc4447462d-dirty 2nd stage bootloader
I (29) boot: compile time 15:37:14
I (30) boot: Enabling RNG early entropy source...
I (35) boot: SPI Speed : 40MHz
I (39) boot: SPI Mode : DIO
I (43) boot: SPI Flash Size : 4MB
I (47) boot: Partition Table:
I (51) boot: ## Label Usage Type ST Offset Length
I (58) boot: 0 nvs WiFi data 01 02 0000a000 00006000
I (66) boot: 1 phy_init RF data 01 01 00010000 00001000
I (73) boot: 2 factory factory app 00 00 00020000 00100000
I (81) boot: End of partition table
I (85) esp_image: segment 0: paddr=0x00020020 vaddr=0x3f400020 size=0x0808c ( 32908) map
I (105) esp_image: segment 1: paddr=0x000280b4 vaddr=0x3ffb0000 size=0x01ea4 ( 7844) load
I (109) esp_image: segment 2: paddr=0x00029f60 vaddr=0x40080000 size=0x00400 ( 1024) load
0x40080000: _WindowOverflow4 at esp-idf/esp-idf/components/freertos/xtensa_vectors.S:1778
I (114) esp_image: segment 3: paddr=0x0002a368 vaddr=0x40080400 size=0x05ca8 ( 23720) load
I (132) esp_image: segment 4: paddr=0x00030018 vaddr=0x400d0018 size=0x126a8 ( 75432) map
0x400d0018: _flash_cache_start at ??:?
I (159) esp_image: segment 5: paddr=0x000426c8 vaddr=0x400860a8 size=0x01f4c ( 8012) load
0x400860a8: prvAddNewTaskToReadyList at esp-idf/esp-idf/components/freertos/tasks.c:4561
I (168) boot: Loaded app from partition at offset 0x20000
I (168) boot: Checking flash encryption...
I (168) flash_encrypt: Generating new flash encryption key...
I (187) flash_encrypt: Read & write protecting new key...
I (187) flash_encrypt: Setting CRYPT_CONFIG efuse to 0xF
W (188) flash_encrypt: Not disabling UART bootloader encryption
I (195) flash_encrypt: Disable UART bootloader decryption...
I (201) flash_encrypt: Disable UART bootloader MMU cache...
I (208) flash_encrypt: Disable JTAG...
I (212) flash_encrypt: Disable ROM BASIC interpreter fallback...
I (219) esp_image: segment 0: paddr=0x00001020 vaddr=0x3fff0018 size=0x00004 ( 4)
I (227) esp_image: segment 1: paddr=0x0000102c vaddr=0x3fff001c size=0x02104 ( 8452)
I (239) esp_image: segment 2: paddr=0x00003138 vaddr=0x40078000 size=0x03528 ( 13608)
I (249) esp_image: segment 3: paddr=0x00006668 vaddr=0x40080400 size=0x01a08 ( 6664)
I (657) esp_image: segment 0: paddr=0x00020020 vaddr=0x3f400020 size=0x0808c ( 32908) map
I (669) esp_image: segment 1: paddr=0x000280b4 vaddr=0x3ffb0000 size=0x01ea4 ( 7844)
I (672) esp_image: segment 2: paddr=0x00029f60 vaddr=0x40080000 size=0x00400 ( 1024)
0x40080000: _WindowOverflow4 at esp-idf/esp-idf/components/freertos/xtensa_vectors.S:1778
I (676) esp_image: segment 3: paddr=0x0002a368 vaddr=0x40080400 size=0x05ca8 ( 23720)
I (692) esp_image: segment 4: paddr=0x00030018 vaddr=0x400d0018 size=0x126a8 ( 75432) map
0x400d0018: _flash_cache_start at ??:?
I (719) esp_image: segment 5: paddr=0x000426c8 vaddr=0x400860a8 size=0x01f4c ( 8012)
0x400860a8: prvAddNewTaskToReadyList at esp-idf/esp-idf/components/freertos/tasks.c:4561
I (722) flash_encrypt: Encrypting partition 2 at offset 0x20000...
I (13229) flash_encrypt: Flash encryption completed
I (13229) boot: Resetting with flash encryption enabled...
启用 Flash 加密后,在下次启动时输出将显示已启用 Flash 加密。
::
rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0018,len:4
load:0x3fff001c,len:8452
load:0x40078000,len:13652
ho 0 tail 12 room 4
load:0x40080400,len:6664
entry 0x40080764
I (30) boot: ESP-IDF v4.0-dev-850-gc4447462d-dirty 2nd stage bootloader
I (30) boot: compile time 16:32:53
I (31) boot: Enabling RNG early entropy source...
I (37) boot: SPI Speed : 40MHz
I (41) boot: SPI Mode : DIO
I (45) boot: SPI Flash Size : 4MB
I (49) boot: Partition Table:
I (52) boot: ## Label Usage Type ST Offset Length
I (60) boot: 0 nvs WiFi data 01 02 0000a000 00006000
I (67) boot: 1 phy_init RF data 01 01 00010000 00001000
I (75) boot: 2 factory factory app 00 00 00020000 00100000
I (82) boot: End of partition table
I (86) esp_image: segment 0: paddr=0x00020020 vaddr=0x3f400020 size=0x0808c ( 32908) map
I (107) esp_image: segment 1: paddr=0x000280b4 vaddr=0x3ffb0000 size=0x01ea4 ( 7844) load
I (111) esp_image: segment 2: paddr=0x00029f60 vaddr=0x40080000 size=0x00400 ( 1024) load
0x40080000: _WindowOverflow4 at esp-idf/esp-idf/components/freertos/xtensa_vectors.S:1778
I (116) esp_image: segment 3: paddr=0x0002a368 vaddr=0x40080400 size=0x05ca8 ( 23720) load
I (134) esp_image: segment 4: paddr=0x00030018 vaddr=0x400d0018 size=0x126a8 ( 75432) map
0x400d0018: _flash_cache_start at ??:?
I (162) esp_image: segment 5: paddr=0x000426c8 vaddr=0x400860a8 size=0x01f4c ( 8012) load
0x400860a8: prvAddNewTaskToReadyList at esp-idf/esp-idf/components/freertos/tasks.c:4561
I (171) boot: Loaded app from partition at offset 0x20000
I (171) boot: Checking flash encryption...
I (171) flash_encrypt: flash encryption is enabled (3 plaintext flashes left)
I (178) boot: Disabling RNG early entropy source...
I (184) cpu_start: Pro cpu up.
I (188) cpu_start: Application information:
I (193) cpu_start: Project name: flash-encryption
I (198) cpu_start: App version: v4.0-dev-850-gc4447462d-dirty
I (205) cpu_start: Compile time: Jun 17 2019 16:32:52
I (211) cpu_start: ELF file SHA256: 8770c886bdf561a7...
I (217) cpu_start: ESP-IDF: v4.0-dev-850-gc4447462d-dirty
I (224) cpu_start: Starting app cpu, entry point is 0x40080e4c
0x40080e4c: call_start_cpu1 at esp-idf/esp-idf/components/esp32/cpu_start.c:265
I (0) cpu_start: App cpu up.
I (235) heap_init: Initializing. RAM available for dynamic allocation:
I (241) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
I (247) heap_init: At 3FFB2EC8 len 0002D138 (180 KiB): DRAM
I (254) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
I (260) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (266) heap_init: At 40087FF4 len 0001800C (96 KiB): IRAM
I (273) cpu_start: Pro cpu start user code
I (291) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
Sample program to check Flash Encryption
This is ESP32 chip with 2 CPU cores, WiFi/BT/BLE, silicon revision 1, 4MB external flash
Flash encryption feature is enabled
Flash encryption mode is DEVELOPMENT
Flash in encrypted mode with flash_crypt_cnt = 1
Halting...
在此阶段,如果用户希望以加密格式将已修改的明文应用程序映像更新到 Flash 中,可使用以下命令:
::
idf.py encrypted-app-flash monitor
.. _encrypt_partitions:
加密多重分区
^^^^^^^^^^^^^
如果所有分区都需以加密格式更新,则可使用以下命令:
::
idf.py encrypted-flash monitor
.. _pregenerated-flash-encryption-key:
使用主机生成的 Flash 加密密钥
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
可在主机中预生成 Flash 加密密钥,并将其烧录到 ESP32 的 eFuse 密钥块中。这样,无需明文 Flash 更新便可以在主机上预加密数据并将其烧录到 ESP32 中。该功能允许在 :ref:`flash_enc_development_mode`:ref:`flash_enc_release_mode` modes 两模式下加密烧录。
- 确保您的 ESP32 设备有 :ref:`flash-encryption-efuse` 中所示 Flash 加密 eFuse 的默认设置。
- 使用 espsecure.py 随机生成一个密钥::
espsecure.py generate_flash_encryption_key my_flash_encryption_key.bin
- 将该密钥烧录到设备上(一次性)。 **该步骤须在第一次加密启动前完成**,否则 ESP32 将随机生成一个软件无权限访问或修改的密钥::
espefuse.py --port PORT burn_key flash_encryption my_flash_encryption_key.bin
- 在第二阶段引导加载程序中启用 Flash 加密支持。请前往 :ref:`project-configuration-menu`,选择 “Security Features”。
- 选择 :ref:`Enable flash encryption on boot <CONFIG_SECURE_FLASH_ENC_ENABLED>`
- 模式默认设置为 **开发模式**
- 在引导加载程序 config 下选择适当详细程度的日志。
- 启用 Flash 加密将增大引导加载程序,因而可能需要更新分区表偏移。可参见 See :ref:`secure-boot-bootloader-size`
- 保存配置并退出。
构建并烧录完整的映像包括:引导加载程序、分区表和 app。这些分区最初以未加密形式写入 Flash
::
idf.py flash monitor
下次启动时,第二阶段引导加载程序将加密 Flash 的 app 分区并重置该分区。现在,示例应用程序将在运行时解密并执行命令。
在此阶段,如果用户希望将新的明文应用程序映像更新到 Flash应调用以下命令
::
idf.py encrypted-app-flash monitor
如何以加密格式重新编程所有分区,可参考 :ref:`encrypt_partitions`
.. _flash_enc_release_mode:
释放模式
^^^^^^^^^^
在释放模式下UART 引导加载程序无法执行 Flash 加密操作,**只能** 使用 OTA 方案下载新的明文映像,该方案将在写入 Flash 前加密明文映像。
- 确保您的 ESP32 设备有 :ref:`flash-encryption-efuse` 中所示 Flash 加密 eFuse 的默认设置。
- 在第二阶段引导加载程序中启用 Flash 加密支持。请前往 :ref:`project-configuration-menu`,选择 “Security Features”。
- 选择 :ref:`Enable flash encryption on boot <CONFIG_SECURE_FLASH_ENC_ENABLED>`
- 选择 **释放模式**,模式默认设置为 **开发模式**。请注意,**一旦选择了释放模式,``download_dis_encrypt`` 和 ``download_dis_decrypt`` eFuse 位将被编程为禁止 UART 引导加载程序访问 Flash 的内容**
- 在引导加载程序 config 下选择适当详细程度的日志。
- 启用 Flash 加密将增大引导加载程序,因而可能需要更新分区表偏移。可参见 See :ref:`secure-boot-bootloader-size`
- 保存配置并退出。
构建并烧录完整的映像包括:引导加载程序、分区表和 app。这些分区最初以未加密形式写入 Flash
::
idf.py flash monitor
下次启动时,第二阶段引导加载程序将加密 Flash app 分区并重置该分区。现在,示例应用程序应正确执行命令。
一旦在释放模式下启用 Flash 加密,引导加载程序将写保护 ``FLASH_CRYPT_CNT`` eFuse。
应使用 OTA 方案对字段中的明文进行后续更新。详情可参见 :doc:`OTA <../api-reference/system/ota>`
可能出现的错误
^^^^^^^^^^^^^^^^
启用 Flash 加密后,如果 ``FLASH_CRYPT_CNT`` eFuse 值中有奇数位,则所有(标有加密标志的)分区都应包含加密密文。以下为 ESP32 加载明文数据会产生的三种典型错误情况:
1. 如果通过明文引导加载程序映像重新更新了引导加载程序分区,则 ROM 加载器将无法加载 引导加载程序,并会显示以下错误类型:
::
rst:0x3 (SW_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
flash read err, 1000
ets_main.c 371
ets Jun 8 2016 00:22:57
rst:0x7 (TG0WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
flash read err, 1000
ets_main.c 371
ets Jun 8 2016 00:22:57
rst:0x7 (TG0WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
flash read err, 1000
ets_main.c 371
ets Jun 8 2016 00:22:57
rst:0x7 (TG0WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
flash read err, 1000
ets_main.c 371
ets Jun 8 2016 00:22:57
rst:0x7 (TG0WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
flash read err, 1000
ets_main.c 371
ets Jun 8 2016 00:22:57
2. 如果引导加载程序已加密,但使用明文分区表映像重新更新了分区表,则引导加载程序将无法读取分区表,并会显示以下错误类型:
::
rst:0x3 (SW_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0018,len:4
load:0x3fff001c,len:10464
ho 0 tail 12 room 4
load:0x40078000,len:19168
load:0x40080400,len:6664
entry 0x40080764
I (60) boot: ESP-IDF v4.0-dev-763-g2c55fae6c-dirty 2nd stage bootloader
I (60) boot: compile time 19:15:54
I (62) boot: Enabling RNG early entropy source...
I (67) boot: SPI Speed : 40MHz
I (72) boot: SPI Mode : DIO
I (76) boot: SPI Flash Size : 4MB
E (80) flash_parts: partition 0 invalid magic number 0x94f6
E (86) boot: Failed to verify partition table
E (91) boot: load partition table error!
3. 如果引导加载程序和分区表已加密,但使用明文应用程序映像重新更新了应用程序,则引导加载程序将无法加载新的应用程序,并会显示以下错误类型:
::
rst:0x3 (SW_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0018,len:4
load:0x3fff001c,len:8452
load:0x40078000,len:13616
load:0x40080400,len:6664
entry 0x40080764
I (56) boot: ESP-IDF v4.0-dev-850-gc4447462d-dirty 2nd stage bootloader
I (56) boot: compile time 15:37:14
I (58) boot: Enabling RNG early entropy source...
I (64) boot: SPI Speed : 40MHz
I (68) boot: SPI Mode : DIO
I (72) boot: SPI Flash Size : 4MB
I (76) boot: Partition Table:
I (79) boot: ## Label Usage Type ST Offset Length
I (87) boot: 0 nvs WiFi data 01 02 0000a000 00006000
I (94) boot: 1 phy_init RF data 01 01 00010000 00001000
I (102) boot: 2 factory factory app 00 00 00020000 00100000
I (109) boot: End of partition table
E (113) esp_image: image at 0x20000 has invalid magic byte
W (120) esp_image: image at 0x20000 has invalid SPI mode 108
W (126) esp_image: image at 0x20000 has invalid SPI size 11
E (132) boot: Factory app partition is not bootable
E (138) boot: No bootable app partitions in the partition table
Flash 加密的要点
-------------------
- 使用 AES-256 加密 Flash 的内容。Flash 加密密钥存储于 eFuse 内部的芯片中,并(默认)受保护防止软件访问。
- `flash 加密算法` 采用的是 AES-256其中密钥随着 Flash 的每个 32 字节块的偏移地址“调整”。这意味着,每个 32 字节块2 个连续的 16 字节 AES 块)使用从 Flash 加密密钥中产生的一个特殊密钥进行加密。
- 通过 ESP32 的 Flash 缓存映射功能Flash 可支持透明访问——读取任何映射到地址空间的 Flash 区域时,都将透明解密该区域。
为便于访问,某些数据分区最好保持未加密状态,或者也可使用对已加密数据无效的 Flash 友好型更新算法。由于 NVS 库无法与 Flash 加密直接兼容,因此无法加密非易失性存储器的 NVS 分区。详情可参见 :ref:`NVS 加密 <nvs_encryption>`
- 如果可能已启用 Flash 加密,则编写 :ref:`使用加密 flash <using-encrypted-flash>` 的代码时,编程人员须小心谨慎。
- 如果已启用安全启动,则重新烧录加密设备的引导加载程序则需要“可重新烧录”的安全启动摘要(可参见 :ref:`flash-encryption-and-secure-boot`)。
.. note:: 同时启用安全启动和 Flash 加密后,引导加载程序 app 二进制文件 ``bootloader.bin`` 可能会过大。参见 :ref:`secure-boot-bootloader-size`
.. important::
在首次启动加密过程中,请勿中断 ESP32 的电源。如果电源中断Flash 的内容将受到破坏,并需要重新烧录未加密数据。而这类重新烧录将不计入烧录限制次数。
.. _using-encrypted-flash:
使用加密的 Flash
-------------------
ESP32 app 代码可通过调用函数 :cpp:func:`esp_flash_encryption_enabled` 来确认当前是否已启用 Flash 加密。同时,设备可通过调用函数 :cpp:func:`esp_get_flash_encryption_mode` 来识别使用的 Flash 加密模式。
启用 Flash 加密后,使用代码访问 Flash 内容时需加注意。
Flash 加密的范围
^^^^^^^^^^^^^^^^^^
只要 ``FLASH_CRYPT_CNT`` eFuse 设置为奇数位的值,所有通过 MMU 的 Flash 缓存访问的 Flash 内容都将被透明解密。包括:
- Flash 中可执行的应用程序代码 (IROM)。
- 所有存储于 Flash 中的只读数据 (DROM)。
- 通过函数 :cpp:func:`spi_flash_mmap` 访问的任意数据。
- ROM 引导加载程序读取的软件引导加载程序映像。
.. important::
MMU Flash 缓存将无条件解密所有数据。Flash 中未加密存储的数据将通过 Flash 缓存“被透明解密”,并在软件中存储为随机垃圾数据。
读取加密的 Flash
^^^^^^^^^^^^^^^^^^
如在不使用 Flash 缓存 MMU 映射的情况下读取数据,推荐使用分区读取函数 :cpp:func:`esp_partition_read`。使用该函数时,只有从加密分区读取的数据才会被解密。其他分区的数据将以未加密形式读取。这样,软件便能同样访问加密和未加密的 Flash。
通过其他 SPI 读取 API 读取的数据均未解密:
- 通过函数 :cpp:func:`spi_flash_read` 读取的数据均未解密。
- 通过 ROM 函数 :cpp:func:`SPIRead` 读取的数据均未解密esp-idf app 不支持该函数)。
- 使用非易失性存储器 (NVS) API 存储的数据始终从 Flash 加密的角度进行存储和读取解密。如有需要,则由库提供加密功能。详情可参见 :ref:`NVS 加密 <nvs_encryption>`
写入加密的 Flash
^^^^^^^^^^^^^^^^^^
在可能的情况下,推荐使用分区写入函数 ``esp_partition_write``。使用该函数时,只有向加密分区写入的数据才会被加密。而写入其他分区的数据均未加密。这样,软件便可同样访问加密和未加密的 Flash。
当 write_encrypted 参数设置为“是”时,函数 ``esp_spi_flash_write`` 将写入数据。否则,数据将以未加密形式写入。
ROM 函数 ``esp_rom_spiflash_write_encrypted`` 将在 Flash 中写入加密数据,而 ROM 函数 ``SPIWrite`` 将在 Flash 中写入未加密数据esp-idf app 不支持上述函数)。
由于数据均采用块加密方式,加密数据最小的写入大小为 16 字节16字节对齐
.. _updating-encrypted-flash:
更新加密的 Flash
-------------------
.. _updating-encrypted-flash-ota:
OTA 更新
^^^^^^^^^^
只要使用了函数 ``esp_partition_write``,则加密分区的 OTA 更新将自动以加密形式写入。
.. _updating-encrypted-flash-serial:
关闭 Flash 加密
-----------------
若因某些原因意外启用了 Flash 加密,则接下来烧录明文数据时将使 ESP32 软砖(设备不断重启,并报错 ``flash read err, 1000``)。
可通过写入 ``FLASH_CRYPT_CNT`` eFuse 再次关闭 Flash 加密(仅适用于开发模式下):
- 首先,前往 :ref:`project-configuration-menu`,在“安全性能”目录下关闭 :ref:`启用 Flash 加密启动 <CONFIG_SECURE_FLASH_ENC_ENABLED>`
- 退出 menuconfig 并保存最新配置。
- 再次运行 ``idf.py menuconfig`` 并复核是否确认已关闭该选项!*如果该选项仍处于已启用状态,则引导加载程序会在启动后立即重新启用加密*
- 在未启用 Flash 加密的状态下,运行 ``idf.py flash`` 构建并烧录新的引导加载程序与 app。
- 运行 ``espefuse.py`` ``components/esptool_py/esptool`` 中)以关闭 FLASH_CRYPT_CNT::
espefuse.py burn_efuse FLASH_CRYPT_CNT
重置 ESP32Flash 加密应处于关闭状态,引导加载程序将正常启动。
.. _flash-encryption-limitations:
Flash 加密的局限性
--------------------
Flash 加密可防止从加密 Flash 中读取明文,从而保护固件防止未经授权的读取与修改。了解 Flash 加密系统的局限之处亦十分重要:
- Flash 加密功能与密钥同样稳固。因而,推荐您首次启动设备时在设备上生成密钥(默认行为)。如果在设备外生成密钥,请确保遵循正确的后续步骤。
- 并非所有数据都是加密存储。因而在 Flash 上存储数据时请检查您使用的存储方式库、API等是否支持 Flash 加密。
- Flash 加密无法防止攻击者获取 Flash 的高层次布局信息。这是因为同一个 AES 密钥要用于每对相邻的 16 字节 AES 块。当这些相邻的 16 字节块中包含相同内容时(如空白或填充区域),这些字节块将加密以产生匹配的加密块对。这可能使得攻击者可在加密设备间进行高层次对比(例如,确认两设备是否可能在运行相同的固件版本)。
- 出于相同原因,攻击者始终可获知一对相邻的 16 字节块32 字节对齐)何时包含相同内容。因此,在 Flash 上存储敏感数据时应牢记这点,并进行相关设置避免该情况发生(可使用计数器字节或每 16 字节设置不同的值即可)。
- 单独使用 Flash 加密可能无法防止攻击者修改本设备的固件。为防止设备上运行未经授权的固件,可搭配 Flash 加密使用 :doc:`安全启动 <secure-boot>`
.. _flash-encryption-and-secure-boot:
Flash 加密与安全启动
----------------------
推荐搭配使用 Flash 加密与安全启动。但是,如果已启用安全启动,则重新烧录设备时会受到其他限制:
- :ref:`updating-encrypted-flash-ota` 不受限制(如果新的 app 已使用安全启动签名密钥进行正确签名)。
- 只有当选择 :ref:`可再次烧录 <CONFIG_SECURE_BOOTLOADER_MODE>` 安全启动模式,且安全启动密钥已预生成并烧录至 ESP32可参见 :ref:`安全启动 <secure-boot-reflashable>`),则 :ref:`明文串行 flash 更新 <updating-encrypted-flash-serial>` 可实现。在该配置下,``idf.py bootloader`` 将生成简化的引导加载程序和安全启动摘要文件,用于在偏移量 0x0 处进行烧录。当进行明文串行重新烧录步骤时,须在烧录其他明文数据前重新烧录此文件。
- 假设未重新烧录引导加载程序,:ref:`使用预生成的 Flash 加密密钥重新烧录 <pregenerated-flash-encryption-key>` 仍可实现。重新烧录引导加载程序时,需在安全启动配置中启用相同的 :ref:`可重新烧录 <CONFIG_SECURE_BOOTLOADER_MODE>` 选项。
.. _flash-encryption-without-secure-boot:
使用无安全启动的 Flash 加密
-----------------------------
尽管 Flash 加密与安全启动可独立使用,但强烈建议您将这二者 **搭配使用** 以确保更高的安全性。
.. _flash-encryption-advanced-features:
Flash 加密的高级功能
----------------------
以下信息可帮助您使用 Flash 加密的高级功能:
加密分区标志
^^^^^^^^^^^^^
部分分区默认为已加密。除此之外,可将任意分区标记为需加密:
:doc:`分区表 <../api-guides/partition-tables>` 文档对 CSV 文件的描述中有标志字段。
该字段通常保留为空白。如果在字段中写入"encrypted"则这个分区将在分区表中标记为已加密此处写入的数据也视为加密数据app 分区同样适用)::
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x6000
phy_init, data, phy, 0xf000, 0x1000
factory, app, factory, 0x10000, 1M
secret_data, 0x40, 0x01, 0x20000, 256K, encrypted
- 默认分区表都不包含任何加密数据分区。
- "app"分区一般都视为加密分区,因此无需将其标记为已加密。
- 如果未启用 Flash 加密,则"encrypted"标记无效。
- 可将带有 ``phy_init`` 数据的可选 ``phy`` 分区标记为已加密,保护该数据防止物理访问读取或修改。
- ``nvs`` 分区无法标记为已加密。
.. _uart-bootloader-encryption:
启用 UART 引导加载程序加密/解密
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
默认情况下,首次启动 Flash 加密过程中将烧录 eFuse ``DISABLE_DL_ENCRYPT````DISABLE_DL_DECRYPT````DISABLE_DL_CACHE``
- ``DISABLE_DL_ENCRYPT`` 在 UART 引导加载程序启动模式下运行时,终止 Flash 加密操作。
- ``DISABLE_DL_DECRYPT`` 在 UART 引导加载程序模式下运行时,终止透明 Flash 解密(即使 FLASH_CRYPT_CNT 已设置为在正常操作中启用 Flash 透明解密)。
- ``DISABLE_DL_CACHE`` 在 UART 引导加载程序模式下运行时终止整个 MMU flash 缓存。
为了完整保存数据,可在首次启动前仅烧录部分 eFuse并写保护其他部分未设置值为 0。例如::
espefuse.py --port PORT burn_efuse DISABLE_DL_DECRYPT
espefuse.py --port PORT write_protect_efuse DISABLE_DL_ENCRYPT
(请注意,一个写保护位即可关闭这 3 个 eFuse因此写保护一个 eFuse 将写保护上述所有 eFuse。所以在写保护前须设置任意位
.. important::
由于 ``esptool.py`` 不支持读取加密的 Flash因此目前基本无法通过写保护这些 eFuse 来将其保持为未设状态。
.. important::
如果保留 ``DISABLE_DL_DECRYPT`` 未设置(为 0则实际上将使 Flash 加密无效,因为此时有物理访问权限的攻击者便可使用 UART 引导加载程序模式(使用自定义存根代码)读取 Flash 的内容。
.. _setting-flash-crypt-config:
设置 FLASH_CRYPT_CONFIG
^^^^^^^^^^^^^^^^^^^^^^^^^^^
``FLASH_CRYPT_CONFIG`` eFuse 决定 Flash 加密密钥中随块偏移“调整”的位数。详情可参见 :ref:`flash-encryption-algorithm`
首次启动 引导加载程序时,该值始终设置为最大 `0xF`
可手动写入这些 eFuse并在首次启动前对其写保护以便选择不同的调整值。但不推荐该操作。
``FLASH_CRYPT_CONFIG`` 的值为 0 时,强烈建议始终不对其进行写保护。如果该 eFuse 设置为 0则 Flash 加密密钥中无调整位,且 Flash 加密算法相当于 AES ECB 模式。
技术细节
-----------
下节将提供 Flash 加密操作的相关信息。
.. _flash-encryption-algorithm:
Flash 加密算法
^^^^^^^^^^^^^^^^
- AES-256 在 16 字节的数据块上运行。Flash 加密引擎在 32 字节的数据块和 2 个 串行 AES 块上加密或解密数据。
- Flash 加密的主密钥存储于 eFuse (BLOCK1) 中,默认受保护防止进一步写入或软件读取。
- AES-256 密钥大小为 256 位32 字节),从 eFuse block 1 中读取。硬件 AES 引擎使用反字节序密钥于 eFuse 块中存储的字节序。
- 如果 ``CODING_SCHEME`` eFuse 设置为 0默认“无”编码方案则 eFuse 密钥块为 256 位,且密钥按原方式存储(反字节序)。
- 如果 ``CODING_SCHEME`` eFuse 设置为 13/4 编码),则 eFuse 密钥块为 192 位(反字节序),信息熵总量减少。硬件 Flash 加密仍在 256 字节密钥上运行,在读取后(字节序未反向),密钥扩展为 ``key = key[0:255] + key[64:127]``
- Flash 加密中使用了逆向 AES 算法,因此 Flash 加密的“加密”操作相当于 AES 解密,而其“解密”操作则相当于 AES 加密。这是为了优化性能,不会影响算法的有效性。
- 每个 32 字节块2 个相邻的 16 字节 AES 块)都由一个特殊的密钥进行加密。该密钥由 eFuse 中 Flash 加密的主密钥产生,并随 Flash 中该字节块的偏移进行 XOR 运算(一次“密钥调整”)。
- 具体调整量取决于 ``FLASH_CRYPT_CONFIG`` eFuse 的设置。该 eFuse 共 4 位,每位可对特定范围的密钥位进行 XOR 运算:
- Bit 1对密钥的 0-66 位进行 XOR 运算。
- Bit 2对密钥的 67-131 位进行 XOR 运算。
- Bit 3对密钥的 132-194 位进行 XOR 运算。
- Bit 4对密钥的 195-256 位进行 XOR 运算。
建议将 ``FLASH_CRYPT_CONFIG`` 的值始终保留为默认值 `0xF`,这样所有密钥位都随块偏移进行 XOR 运算。详情可参见 :ref:`setting-flash-crypt-config`
- 块偏移的 19 个高位(第 5-23 位)由 Flash 加密的主密钥进行 XOR 运算。选定该范围的原因为Flash 的最大尺寸为 16MB24 位),每个块大小为 32 字节,因而 5 个最低有效位始终为 0。
- 从 19 个块偏移位中每个位到 Flash 加密密钥的 256 位都有一个特殊的映射,以决定与哪个位进行 XOR 运算。有关完整映射可参见 ``espsecure.py`` 源代码中的变量 ``_FLASH_ENCRYPTION_TWEAK_PATTERN``
- 有关在 Python 中实现的完整 Flash 加密算法,可参见 ``espsecure.py`` 源代码中的函数 `_flash_encryption_operation()`

View file

@ -20,10 +20,9 @@ STR_EXPECT = ("CAN Alert and Recovery: Driver installed", "CAN Alert and Recover
EXPECT_TIMEOUT = 20
@IDF.idf_example_test(env_tag='Example_CAN')
@IDF.idf_example_test(env_tag='Example_CAN1', ignore=True)
def test_can_alert_and_recovery_example(env, extra_data):
# Get device under test, flash and start example. "dut4" must be defined in EnvConfig
dut = env.get_dut('dut4', 'examples/peripherals/can/can_alert_and_recovery', dut_class=ESP32DUT)
dut = env.get_dut('dut1', 'examples/peripherals/can/can_alert_and_recovery', dut_class=ESP32DUT)
dut.start_app()
for string in STR_EXPECT:

View file

@ -18,7 +18,7 @@ except ImportError:
# Define tuple of strings to expect for each DUT.
master_expect = ("CAN Master: Driver installed", "CAN Master: Driver uninstalled")
slave_expect = ("CAN Slave: Driver installed", "CAN Slave: Driver uninstalled")
listen_only_expect = ("CAN Listen Only: Driver installed", "Listen Only: Driver uninstalled")
listen_only_expect = ("CAN Listen Only: Driver installed", "CAN Listen Only: Driver uninstalled")
def dut_thread_callback(**kwargs):
@ -37,7 +37,7 @@ def dut_thread_callback(**kwargs):
result[0] = True
@IDF.idf_example_test(env_tag='Example_CAN')
@IDF.idf_example_test(env_tag='Example_CAN2', ignore=True)
def test_can_network_example(env, extra_data):
# Get device under test. "dut1", "dut2", and "dut3" must be properly defined in EnvConfig

View file

@ -20,10 +20,10 @@ STR_EXPECT = ("CAN Self Test: Driver installed", "CAN Self Test: Driver uninstal
EXPECT_TIMEOUT = 20
@IDF.idf_example_test(env_tag='Example_CAN')
@IDF.idf_example_test(env_tag='Example_CAN1', ignore=True)
def test_can_self_test_example(env, extra_data):
# Get device under test, flash and start example. "dut4" must be defined in EnvConfig
dut = env.get_dut('dut4', 'examples/peripherals/can/can_self_test', dut_class=ESP32DUT)
dut = env.get_dut('dut1', 'examples/peripherals/can/can_self_test', dut_class=ESP32DUT)
dut.start_app()
for string in STR_EXPECT:

View file

@ -0,0 +1,10 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
# (Not part of the boilerplate)
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(icmp-echo)

View file

@ -0,0 +1,11 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := icmp-echo
EXTRA_COMPONENT_DIRS = $(IDF_PATH)/examples/common_components/protocol_examples_common
include $(IDF_PATH)/make/project.mk

View file

@ -0,0 +1,100 @@
# ICMP Echo-Reply (Ping) example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
Ping is a useful network utility used to test if a remote host is reachable on the IP network. It measures the round-trip time for messages sent from the source host to a destination target that are echoed back to the source.
Ping operates by sending Internet Control Message Protocol (ICMP) echo request packets to the target host and waiting for an ICMP echo reply.
This example implements a simple ping command line util based on the [console component](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/console.html).
**Notes:** Currently this example only supports IPv4.
## How to use example
### Hardware Required
This example should be able to run on any commonly available ESP32 development board.
### Configure the project
```
idf.py menuconfig
```
In the `Example Connection Configuration` menu:
* Choose the network interface (Wi-Fi or Ethernet) used by this example under `Connect using`.
* If Wi-Fi interface is selected, you also have to set:
* Wi-Fi SSID and Wi-Fi password that your board will connect to.
* If Ethernet interface is selected, you also have to set:
* Select Ethernet type under `Ethernet Type`, for example, `Internal EMAC` or `SPI Ethernet Module`.
* Select Ethernet PHY chip model under `Ethernet PHY Device`, for example, `IP101`.
* You might also have to set other Ethernet driver specific parameters under `Component Config > Ethernet`, for example, EMAC Clock mode, GPIO used by SMI, and etc.
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
* Run `help` to get manual of all supported commands.
```bash
esp32> help
help
Print the list of registered commands
ping [-W <t>] [-i <t>] [-s <n>] [-c <n>] [-Q <n>] <host>
send ICMP ECHO_REQUEST to network hosts
-W, --timeout=<t> Time to wait for a response, in seconds
-i, --interval=<t> Wait interval seconds between sending each packet
-s, --size=<n> Specify the number of data bytes to be sent
-c, --count=<n> Stop after sending count packets
-Q, --tos=<n> Set Type of Service related bits in IP datagrams
<host> Host address
```
* Run `ping` command to test reachable of remote server.
```bash
esp32> ping www.espressif.com
64 bytes from 119.9.92.99 icmp_seq=1 ttl=51 time=36 ms
64 bytes from 119.9.92.99 icmp_seq=2 ttl=51 time=34 ms
64 bytes from 119.9.92.99 icmp_seq=3 ttl=51 time=37 ms
64 bytes from 119.9.92.99 icmp_seq=4 ttl=51 time=36 ms
64 bytes from 119.9.92.99 icmp_seq=5 ttl=51 time=33 ms
--- 119.9.92.99 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 176ms
```
* Run `ping` with a wrong domain name
```bash
esp32> ping www.hello-world.io
ping: unknown host www.hello-world.io
Command returned non-zero error code: 0x1 (ERROR)
```
* Run `ping` with an unreachable server
```bash
esp32> ping www.zoom.us
From 69.171.230.18 icmp_seq=1 timeout
From 69.171.230.18 icmp_seq=2 timeout
From 69.171.230.18 icmp_seq=3 timeout
From 69.171.230.18 icmp_seq=4 timeout
From 69.171.230.18 icmp_seq=5 timeout
--- 69.171.230.18 ping statistics ---
5 packets transmitted, 0 received, 100% packet loss, time 4996ms
```

View file

@ -0,0 +1,2 @@
idf_component_register(SRCS "echo_example_main.c" "cmd_ping.c"
INCLUDE_DIRS ".")

View file

@ -0,0 +1,161 @@
/* Ping command
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include "sdkconfig.h"
#include "esp_log.h"
#include "cmd_ping.h"
#include "argtable3/argtable3.h"
#include "lwip/inet.h"
#include "lwip/netdb.h"
#include "lwip/sockets.h"
#include "esp_console.h"
#include "ping/ping_sock.h"
static void cmd_ping_on_ping_success(esp_ping_handle_t hdl, void *args)
{
uint8_t ttl;
uint16_t seqno;
uint32_t elapsed_time, recv_len;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_TTL, &ttl, sizeof(ttl));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len));
esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time));
printf("%d bytes from %s icmp_seq=%d ttl=%d time=%d ms\n",
recv_len, inet_ntoa(target_addr.u_addr.ip4), seqno, ttl, elapsed_time);
}
static void cmd_ping_on_ping_timeout(esp_ping_handle_t hdl, void *args)
{
uint16_t seqno;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
printf("From %s icmp_seq=%d timeout\n", inet_ntoa(target_addr.u_addr.ip4), seqno);
}
static void cmd_ping_on_ping_end(esp_ping_handle_t hdl, void *args)
{
ip_addr_t target_addr;
uint32_t transmitted;
uint32_t received;
uint32_t total_time_ms;
esp_ping_get_profile(hdl, ESP_PING_PROF_REQUEST, &transmitted, sizeof(transmitted));
esp_ping_get_profile(hdl, ESP_PING_PROF_REPLY, &received, sizeof(received));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
esp_ping_get_profile(hdl, ESP_PING_PROF_DURATION, &total_time_ms, sizeof(total_time_ms));
uint32_t loss = (uint32_t)((1 - ((float)received) / transmitted) * 100);
if (IP_IS_V4(&target_addr)) {
printf("\n--- %s ping statistics ---\n", inet_ntoa(*ip_2_ip4(&target_addr)));
} else {
printf("\n--- %s ping statistics ---\n", inet6_ntoa(*ip_2_ip6(&target_addr)));
}
printf("%d packets transmitted, %d received, %d%% packet loss, time %dms\n",
transmitted, received, loss, total_time_ms);
// delete the ping sessions, so that we clean up all resources and can create a new ping session
// we don't have to call delete function in the callback, instead we can call delete function from other tasks
esp_ping_delete_session(hdl);
}
static struct {
struct arg_dbl *timeout;
struct arg_dbl *interval;
struct arg_int *data_size;
struct arg_int *count;
struct arg_int *tos;
struct arg_str *host;
struct arg_end *end;
} ping_args;
static int do_ping_cmd(int argc, char **argv)
{
esp_ping_config_t config = ESP_PING_DEFAULT_CONFIG();
int nerrors = arg_parse(argc, argv, (void **)&ping_args);
if (nerrors != 0) {
arg_print_errors(stderr, ping_args.end, argv[0]);
return 1;
}
if (ping_args.timeout->count > 0) {
config.timeout_ms = (uint32_t)(ping_args.timeout->dval[0] * 1000);
}
if (ping_args.interval->count > 0) {
config.interval_ms = (uint32_t)(ping_args.interval->dval[0] * 1000);
}
if (ping_args.data_size->count > 0) {
config.data_size = (uint32_t)(ping_args.data_size->ival[0]);
}
if (ping_args.count->count > 0) {
config.count = (uint32_t)(ping_args.count->ival[0]);
}
if (ping_args.tos->count > 0) {
config.tos = (uint32_t)(ping_args.tos->ival[0]);
}
// parse IP address
ip_addr_t target_addr;
struct addrinfo hint;
struct addrinfo *res = NULL;
memset(&hint, 0, sizeof(hint));
memset(&target_addr, 0, sizeof(target_addr));
/* convert domain name to IP address */
if (getaddrinfo(ping_args.host->sval[0], NULL, &hint, &res) != 0) {
printf("ping: unknown host %s\n", ping_args.host->sval[0]);
return 1;
}
if (res->ai_family == AF_INET) {
struct in_addr addr4 = ((struct sockaddr_in *) (res->ai_addr))->sin_addr;
inet_addr_to_ip4addr(ip_2_ip4(&target_addr), &addr4);
} else {
struct in6_addr addr6 = ((struct sockaddr_in6 *) (res->ai_addr))->sin6_addr;
inet6_addr_to_ip6addr(ip_2_ip6(&target_addr), &addr6);
}
freeaddrinfo(res);
config.target_addr = target_addr;
/* set callback functions */
esp_ping_callbacks_t cbs = {
.on_ping_success = cmd_ping_on_ping_success,
.on_ping_timeout = cmd_ping_on_ping_timeout,
.on_ping_end = cmd_ping_on_ping_end,
.cb_args = NULL
};
esp_ping_handle_t ping;
esp_ping_new_session(&config, &cbs, &ping);
esp_ping_start(ping);
return 0;
}
void register_ping(void)
{
ping_args.timeout = arg_dbl0("W", "timeout", "<t>", "Time to wait for a response, in seconds");
ping_args.interval = arg_dbl0("i", "interval", "<t>", "Wait interval seconds between sending each packet");
ping_args.data_size = arg_int0("s", "size", "<n>", "Specify the number of data bytes to be sent");
ping_args.count = arg_int0("c", "count", "<n>", "Stop after sending count packets");
ping_args.tos = arg_int0("Q", "tos", "<n>", "Set Type of Service related bits in IP datagrams");
ping_args.host = arg_str1(NULL, NULL, "<host>", "Host address");
ping_args.end = arg_end(1);
const esp_console_cmd_t ping_cmd = {
.command = "ping",
.help = "send ICMP ECHO_REQUEST to network hosts",
.hint = NULL,
.func = &do_ping_cmd,
.argtable = &ping_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&ping_cmd));
}

View file

@ -0,0 +1,24 @@
/* Ping command
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Register Ping command
*
*/
void register_ping(void);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -0,0 +1,145 @@
/* ICMP echo example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include "sdkconfig.h"
#include "esp_system.h"
#include "esp_log.h"
#include "esp_console.h"
#include "driver/uart.h"
#include "esp_vfs_dev.h"
#include "esp_event.h"
#include "linenoise/linenoise.h"
#include "argtable3/argtable3.h"
#include "nvs_flash.h"
#include "protocol_examples_common.h"
#include "cmd_ping.h"
static void initialize_console(void)
{
/* Disable buffering on stdin */
setvbuf(stdin, NULL, _IONBF, 0);
/* Minicom, screen, idf_monitor send CR when ENTER key is pressed */
esp_vfs_dev_uart_set_rx_line_endings(ESP_LINE_ENDINGS_CR);
/* Move the caret to the beginning of the next line on '\n' */
esp_vfs_dev_uart_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF);
/* Configure UART. Note that REF_TICK is used so that the baud rate remains
* correct while APB frequency is changing in light sleep mode.
*/
const uart_config_t uart_config = {
.baud_rate = CONFIG_ESP_CONSOLE_UART_BAUDRATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.use_ref_tick = true
};
ESP_ERROR_CHECK(uart_param_config(CONFIG_ESP_CONSOLE_UART_NUM, &uart_config));
/* Install UART driver for interrupt-driven reads and writes */
ESP_ERROR_CHECK(uart_driver_install(CONFIG_ESP_CONSOLE_UART_NUM, 256, 0, 0, NULL, 0));
/* Tell VFS to use UART driver */
esp_vfs_dev_uart_use_driver(CONFIG_ESP_CONSOLE_UART_NUM);
/* Initialize the console */
esp_console_config_t console_config = {
.max_cmdline_args = 16,
.max_cmdline_length = 256,
#if CONFIG_LOG_COLORS
.hint_color = atoi(LOG_COLOR_CYAN)
#endif
};
ESP_ERROR_CHECK(esp_console_init(&console_config));
/* Configure linenoise line completion library */
/* Enable multiline editing. If not set, long commands will scroll within
* single line.
*/
linenoiseSetMultiLine(1);
/* Tell linenoise where to get command completions and hints */
linenoiseSetCompletionCallback(&esp_console_get_completion);
linenoiseSetHintsCallback((linenoiseHintsCallback *) &esp_console_get_hint);
/* Set command history size */
linenoiseHistorySetMaxLen(100);
}
void app_main(void)
{
initialize_console();
ESP_ERROR_CHECK(nvs_flash_init());
tcpip_adapter_init();
ESP_ERROR_CHECK(esp_event_loop_create_default());
/* wait for active network connection */
ESP_ERROR_CHECK(example_connect());
/* Register commands */
esp_console_register_help_command();
register_ping();
/* Prompt to be printed before each line.
* This can be customized, made dynamic, etc.
*/
const char *prompt = LOG_COLOR_I "esp32> " LOG_RESET_COLOR;
printf("\n"
"Type 'help' to get the list of commands.\n"
"Use UP/DOWN arrows to navigate through command history.\n"
"Press TAB when typing command name to auto-complete.\n");
/* Figure out if the terminal supports escape sequences */
int probe_status = linenoiseProbe();
if (probe_status) { /* zero indicates success */
printf("\n"
"Your terminal application does not support escape sequences.\n"
"Line editing and history features are disabled.\n"
"On Windows, try using Putty instead.\n");
linenoiseSetDumbMode(1);
#if CONFIG_LOG_COLORS
/* Since the terminal doesn't support escape sequences,
* don't use color codes in the prompt.
*/
prompt = "esp32> ";
#endif //CONFIG_LOG_COLORS
}
/* Main loop */
while (true) {
/* Get a line using linenoise.
* The line is returned when ENTER is pressed.
*/
char *line = linenoise(prompt);
if (line == NULL) { /* Ignore empty lines */
continue;
}
/* Add the command to the history */
linenoiseHistoryAdd(line);
/* Try to run the command */
int ret;
esp_err_t err = esp_console_run(line, &ret);
if (err == ESP_ERR_NOT_FOUND) {
printf("Unrecognized command\n");
} else if (err == ESP_ERR_INVALID_ARG) {
// command was empty
} else if (err == ESP_OK && ret != ESP_OK) {
printf("Command returned non-zero error code: 0x%x (%s)\n", ret, esp_err_to_name(ret));
} else if (err != ESP_OK) {
printf("Internal error: %s\n", esp_err_to_name(err));
}
/* linenoise allocates line buffer on the heap, so need to free it */
linenoiseFree(line);
}
}

View file

@ -29,6 +29,11 @@
static const char *TAG = "mdns-test";
static char* generate_hostname(void);
#if CONFIG_MDNS_RESOLVE_TEST_SERVICES == 1
static void query_mdns_host_with_gethostbyname(char * host);
static void query_mdns_host_with_getaddrinfo(char * host);
#endif
static void initialise_mdns(void)
{
char* hostname = generate_hostname();
@ -168,6 +173,8 @@ static void mdns_example_task(void *pvParameters)
#if CONFIG_MDNS_RESOLVE_TEST_SERVICES == 1
/* Send initial queries that are started by CI tester */
query_mdns_host("tinytester");
query_mdns_host_with_gethostbyname("tinytester-lwip.local");
query_mdns_host_with_getaddrinfo("tinytester-lwip.local");
#endif
while(1) {
@ -211,3 +218,45 @@ static char* generate_hostname(void)
return hostname;
#endif
}
#if CONFIG_MDNS_RESOLVE_TEST_SERVICES == 1
/**
* @brief Executes gethostbyname and displays list of resolved addresses.
* Note: This function is used only to test advertised mdns hostnames resolution
*/
static void query_mdns_host_with_gethostbyname(char * host)
{
struct hostent *res = gethostbyname(host);
if (res) {
unsigned int i = 0;
while (res->h_addr_list[i] != NULL) {
ESP_LOGI(TAG, "gethostbyname: %s resolved to: %s", host, inet_ntoa(*(struct in_addr *) (res->h_addr_list[i])));
i++;
}
}
}
/**
* @brief Executes getaddrinfo and displays list of resolved addresses.
* Note: This function is used only to test advertised mdns hostnames resolution
*/
static void query_mdns_host_with_getaddrinfo(char * host)
{
struct addrinfo hints;
struct addrinfo * res;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if (!getaddrinfo(host, NULL, &hints, &res)) {
while (res) {
ESP_LOGI(TAG, "getaddrinfo: %s resolved to: %s", host,
res->ai_family == AF_INET?
inet_ntoa(((struct sockaddr_in *) res->ai_addr)->sin_addr):
inet_ntoa(((struct sockaddr_in6 *) res->ai_addr)->sin6_addr));
res = res->ai_next;
}
}
}
#endif

View file

@ -6,7 +6,7 @@ import time
import struct
import dpkt
import dpkt.dns
from threading import Thread
from threading import Thread, Event
# this is a test case write with tiny-test-fw.
@ -25,52 +25,78 @@ except ImportError:
import DUT
g_run_server = True
g_done = False
# g_run_server = True
# g_done = False
stop_mdns_server = Event()
esp_answered = Event()
def get_dns_query_for_esp(esp_host):
dns = dpkt.dns.DNS(b'\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01')
dns.qd[0].name = esp_host + u'.local'
print("Created query for esp host: {} ".format(dns.__repr__()))
return dns.pack()
def get_dns_answer_to_mdns(tester_host):
dns = dpkt.dns.DNS(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
dns.op = dpkt.dns.DNS_QR | dpkt.dns.DNS_AA
dns.rcode = dpkt.dns.DNS_RCODE_NOERR
arr = dpkt.dns.DNS.RR()
arr.cls = dpkt.dns.DNS_IN
arr.type = dpkt.dns.DNS_A
arr.name = tester_host
arr.ip = socket.inet_aton('127.0.0.1')
dns. an.append(arr)
print("Created answer to mdns query: {} ".format(dns.__repr__()))
return dns.pack()
def get_dns_answer_to_mdns_lwip(tester_host, id):
dns = dpkt.dns.DNS(b"\x5e\x39\x84\x00\x00\x01\x00\x01\x00\x00\x00\x00\x0a\x64\x61\x76\x69\x64"
b"\x2d\x63\x6f\x6d\x70\x05\x6c\x6f\x63\x61\x6c\x00\x00\x01\x00\x01\xc0\x0c"
b"\x00\x01\x00\x01\x00\x00\x00\x0a\x00\x04\xc0\xa8\x0a\x6c")
dns.qd[0].name = tester_host
dns.an[0].name = tester_host
dns.an[0].ip = socket.inet_aton('127.0.0.1')
dns.an[0].rdata = socket.inet_aton('127.0.0.1')
dns.id = id
print("Created answer to mdns (lwip) query: {} ".format(dns.__repr__()))
return dns.pack()
def mdns_server(esp_host):
global g_done
global esp_answered
UDP_IP = "0.0.0.0"
UDP_PORT = 5353
MCAST_GRP = '224.0.0.251'
TESTER_NAME = u'tinytester.local'
TESTER_NAME_LWIP = u'tinytester-lwip.local'
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
sock.bind((UDP_IP,UDP_PORT))
mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
dns = dpkt.dns.DNS(b'\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01')
sock.settimeout(30)
resp_dns = dpkt.dns.DNS(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
resp_dns.op = dpkt.dns.DNS_QR | dpkt.dns.DNS_AA
resp_dns.rcode = dpkt.dns.DNS_RCODE_NOERR
arr = dpkt.dns.DNS.RR()
arr.cls = dpkt.dns.DNS_IN
arr.type = dpkt.dns.DNS_A
arr.name = u'tinytester.local'
arr.ip = socket.inet_aton('127.0.0.1')
resp_dns. an.append(arr)
sock.sendto(resp_dns.pack(),(MCAST_GRP,UDP_PORT))
while g_run_server:
while not stop_mdns_server.is_set():
try:
m = sock.recvfrom(1024)
dns = dpkt.dns.DNS(m[0])
if not esp_answered.is_set():
sock.sendto(get_dns_query_for_esp(esp_host), (MCAST_GRP,UDP_PORT))
time.sleep(0.2)
data, addr = sock.recvfrom(1024)
dns = dpkt.dns.DNS(data)
if len(dns.qd) > 0 and dns.qd[0].type == dpkt.dns.DNS_A:
if dns.qd[0].name == u'tinytester.local':
print(dns.__repr__(),dns.qd[0].name)
sock.sendto(resp_dns.pack(),(MCAST_GRP,UDP_PORT))
if dns.qd[0].name == TESTER_NAME:
print("Received query: {} ".format(dns.__repr__()))
sock.sendto(get_dns_answer_to_mdns(TESTER_NAME), (MCAST_GRP,UDP_PORT))
elif dns.qd[0].name == TESTER_NAME_LWIP:
print("Received query: {} ".format(dns.__repr__()))
sock.sendto(get_dns_answer_to_mdns_lwip(TESTER_NAME_LWIP, dns.id), addr)
if len(dns.an) > 0 and dns.an[0].type == dpkt.dns.DNS_A:
if dns.an[0].name == esp_host + u'.local':
print("Received answer esp32-mdns query")
g_done = True
print(dns.an[0].name)
dns = dpkt.dns.DNS(b'\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01')
dns.qd[0].name = esp_host + u'.local'
sock.sendto(dns.pack(),(MCAST_GRP,UDP_PORT))
print("Sending esp32-mdns query")
time.sleep(0.5)
sock.sendto(resp_dns.pack(),(MCAST_GRP,UDP_PORT))
print("Received answer to esp32-mdns query: {}".format(dns.__repr__()))
esp_answered.set()
except socket.timeout:
break
except dpkt.UnpackError:
@ -79,7 +105,7 @@ def mdns_server(esp_host):
@IDF.idf_example_test(env_tag="Example_WIFI")
def test_examples_protocol_mdns(env, extra_data):
global g_run_server
global stop_mdns_server
"""
steps: |
1. join AP + init mdns example
@ -103,22 +129,19 @@ def test_examples_protocol_mdns(env, extra_data):
try:
dut1.expect(re.compile(r" sta ip: ([^,]+),"), timeout=30)
except DUT.ExpectTimeout:
g_run_server = False
stop_mdns_server.set()
thread1.join()
raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
# 3. check the mdns name is accessible
start = time.time()
while (time.time() - start) <= 60:
if g_done:
break
time.sleep(0.5)
if g_done is False:
if not esp_answered.wait(timeout=30):
raise ValueError('Test has failed: did not receive mdns answer within timeout')
# 4. check DUT output if mdns advertized host is resolved
try:
dut1.expect(re.compile(r"mdns-test: Query A: tinytester.local resolved to: 127.0.0.1"), timeout=30)
dut1.expect(re.compile(r"mdns-test: gethostbyname: tinytester-lwip.local resolved to: 127.0.0.1"), timeout=30)
dut1.expect(re.compile(r"mdns-test: getaddrinfo: tinytester-lwip.local resolved to: 127.0.0.1"), timeout=30)
finally:
g_run_server = False
stop_mdns_server.set()
thread1.join()

View file

@ -1,2 +1,3 @@
CONFIG_MDNS_RESOLVE_TEST_SERVICES=y
CONFIG_MDNS_ADD_MAC_TO_HOSTNAME=y
CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y

View file

@ -11,25 +11,16 @@
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "esp_wifi.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "tcpip_adapter.h"
#include "protocol_examples_common.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "lwip/sockets.h"
#include "lwip/dns.h"
#include "lwip/netdb.h"
#include "esp_log.h"
#include "mqtt_client.h"
#include "esp_tls.h"
#include "esp_ota_ops.h"
static const char *TAG = "MQTTS_EXAMPLE";
@ -41,6 +32,20 @@ extern const uint8_t mqtt_eclipse_org_pem_start[] asm("_binary_mqtt_eclipse_or
#endif
extern const uint8_t mqtt_eclipse_org_pem_end[] asm("_binary_mqtt_eclipse_org_pem_end");
//
// Note: this function is for testing purposes only publishing the entire active partition
// (to be checked against the original binary)
//
static void send_binary(esp_mqtt_client_handle_t client)
{
spi_flash_mmap_handle_t out_handle;
const void *binary_address;
const esp_partition_t* partition = esp_ota_get_running_partition();
esp_partition_mmap(partition, 0, partition->size, SPI_FLASH_MMAP_DATA, &binary_address, &out_handle);
int msg_id = esp_mqtt_client_publish(client, "/topic/binary", binary_address, partition->size, 0, 0);
ESP_LOGI(TAG, "binary sent with msg_id=%d", msg_id);
}
static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event)
{
esp_mqtt_client_handle_t client = event->client;
@ -77,6 +82,10 @@ static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event)
ESP_LOGI(TAG, "MQTT_EVENT_DATA");
printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
printf("DATA=%.*s\r\n", event->data_len, event->data);
if (strncmp(event->data, "send binary please", event->data_len) == 0) {
ESP_LOGI(TAG, "Sending the binary");
send_binary(client);
}
break;
case MQTT_EVENT_ERROR:
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
@ -121,6 +130,7 @@ void app_main(void)
ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version());
esp_log_level_set("*", ESP_LOG_INFO);
esp_log_level_set("esp-tls", ESP_LOG_VERBOSE);
esp_log_level_set("MQTT_CLIENT", ESP_LOG_VERBOSE);
esp_log_level_set("MQTT_EXAMPLE", ESP_LOG_VERBOSE);
esp_log_level_set("TRANSPORT_TCP", ESP_LOG_VERBOSE);

Some files were not shown because too many files have changed in this diff Show more