Merge branch 'master' into 'refactor/btdm_spp_client_demo'

# Conflicts:
#   examples/bluetooth/ble_spp_client/main/spp_client_demo.c
This commit is contained in:
Xie Wen Xiang 2017-11-06 14:38:01 +08:00
commit 7a423392c3
103 changed files with 3595 additions and 336 deletions

View file

@ -133,6 +133,7 @@ build_esp_idf_tests:
- build_examples/*/*/*/build/*.bin
- build_examples/*/*/*/build/*.elf
- build_examples/*/*/*/build/*.map
- build_examples/*/*/*/build/download.config
- build_examples/*/*/*/build/bootloader/*.bin
expire_in: 1 week
variables:
@ -411,17 +412,19 @@ check_submodule_sync:
assign_test:
<<: *build_template
stage: assign_test
dependencies:
- build_esp_idf_tests
- build_ssc
# gitlab ci do not support match job with RegEx or wildcard now in dependencies.
# we have a lot build example jobs. now we don't use dependencies, just download all artificats of build stage.
variables:
UT_BIN_PATH: "tools/unit-test-app/output"
OUTPUT_BIN_PATH: "test_bins/ESP32_IDF"
TEST_FW_PATH: "$CI_PROJECT_DIR/tools/tiny-test-fw"
EXAMPLE_CONFIG_OUTPUT_PATH: "$CI_PROJECT_DIR/examples/test_configs"
artifacts:
paths:
- test_bins
- components/idf_test/*/CIConfigs
- components/idf_test/*/TC.sqlite
- $EXAMPLE_CONFIG_OUTPUT_PATH
expire_in: 1 mos
before_script: *add_gitlab_key_before
script:
@ -430,6 +433,8 @@ assign_test:
# copy and rename folder name to "UT_config"
- for CONFIG in $(ls $UT_BIN_PATH); do cp -r "$UT_BIN_PATH/$CONFIG" "$OUTPUT_BIN_PATH/UT_$CONFIG"; done
- cp -r SSC/ssc_bin/* $OUTPUT_BIN_PATH
# assign example tests
- python $TEST_FW_PATH/CIAssignExampleTest.py $IDF_PATH/examples $IDF_PATH/.gitlab-ci.yml $EXAMPLE_CONFIG_OUTPUT_PATH
# clone test script to assign tests
- git clone $TEST_SCRIPT_REPOSITORY
- cd auto_test_script
@ -439,6 +444,34 @@ assign_test:
# assgin integration test cases
- python CIAssignTestCases.py -t $IDF_PATH/components/idf_test/integration_test -c $IDF_PATH/.gitlab-ci.yml -b $IDF_PATH/test_bins
.example_test_template: &example_test_template
stage: test
when: on_success
only:
- master
- /^release\/v/
- /^v\d+\.\d+(\.\d+)?($|-)/
- triggers
# gitlab ci do not support match job with RegEx or wildcard now in dependencies.
# we have a lot build example jobs and the binaries them exceed the limitation of artifacts.
# we can't artifact them in one job. For example test jobs, download all artifacts from previous stages.
artifacts:
when: always
paths:
- $LOG_PATH
expire_in: 6 mos
variables:
TEST_FW_PATH: "$CI_PROJECT_DIR/tools/tiny-test-fw"
TEST_CASE_PATH: "$CI_PROJECT_DIR/examples"
CONFIG_FILE: "$CI_PROJECT_DIR/examples/test_configs/$CI_JOB_NAME.yml"
LOG_PATH: "$CI_PROJECT_DIR/TEST_LOGS"
script:
# first test if config file exists, if not exist, exit 0
- test -e $CONFIG_FILE || exit 0
- cd $TEST_FW_PATH
# run test
- python Runner.py $TEST_CASE_PATH -c $CONFIG_FILE
.test_template: &test_template
stage: test
when: on_success
@ -509,6 +542,12 @@ nvs_compatible_test:
# run test
- python CIRunner.py -l "$LOG_PATH/$CI_JOB_NAME" -c $CONFIG_FILE -e $LOCAL_ENV_CONFIG_PATH -t $TEST_CASE_FILE_PATH -m $MODULE_UPDATE_FILE
example_test_001_01:
<<: *example_test_template
tags:
- ESP32
- Example_WIFI
UT_001_01:
<<: *unit_test_template
tags:
@ -670,6 +709,13 @@ UT_003_07:
- UT_T1_1
- UT_single_core
UT_003_08:
<<: *unit_test_template
tags:
- ESP32_IDF
- UT_T1_1
- UT_single_core
IT_001_01:
<<: *test_template
tags:

View file

@ -67,7 +67,7 @@ static void esp_apptrace_test_timer_init(int timer_group, int timer_idx, uint32_
config.alarm_en = 1;
config.auto_reload = 1;
config.counter_dir = TIMER_COUNT_UP;
config.divider = 1;
config.divider = 2; //Range is 2 to 65536
config.intr_type = TIMER_INTR_LEVEL;
config.counter_en = TIMER_PAUSE;
/*Configure timer*/
@ -403,7 +403,7 @@ static void esp_apptrace_test_ts_init(int timer_group, int timer_idx)
config.alarm_en = 0;
config.auto_reload = 0;
config.counter_dir = TIMER_COUNT_UP;
config.divider = 1;
config.divider = 2; //Range is 2 to 65536
config.counter_en = 0;
/*Configure timer*/
timer_init(timer_group, timer_idx, &config);
@ -420,7 +420,7 @@ static void esp_apptrace_test_ts_cleanup()
config.alarm_en = 0;
config.auto_reload = 0;
config.counter_dir = TIMER_COUNT_UP;
config.divider = 1;
config.divider = 2; //Range is 2 to 65536
config.counter_en = 0;
/*Configure timer*/
timer_init(s_ts_timer_group, s_ts_timer_idx, &config);

View file

@ -43,6 +43,16 @@ config BOOTLOADER_SPI_WP_PIN
The default value (GPIO 7) is correct for WP pin on ESP32-D2WD integrated flash.
config BOOTLOADER_VDDSDIO_BOOST
bool "Increase VDDSDIO LDO voltage to 1.9V"
default y
help
If this option is enabled, and VDDSDIO LDO is set to 1.8V (using EFUSE
or MTDI bootstrapping pin), bootloader will change LDO settings to
output 1.9V instead. This helps prevent flash chip from browning out
during flash programming operations.
For 3.3V flash, this option has no effect.
endmenu # Bootloader

View file

@ -73,6 +73,8 @@ static void set_cache_and_start_app(uint32_t drom_addr,
uint32_t irom_size,
uint32_t entry_addr);
static void update_flash_config(const esp_image_header_t* pfhdr);
static void vddsdio_configure();
static void flash_gpio_configure();
static void clock_configure(void);
static void uart_console_configure(void);
static void wdt_reset_check(void);
@ -443,6 +445,8 @@ static bool load_boot_image(const bootloader_state_t *bs, int start_index, esp_i
void bootloader_main()
{
vddsdio_configure();
flash_gpio_configure();
clock_configure();
uart_console_configure();
wdt_reset_check();
@ -737,6 +741,105 @@ static void print_flash_info(const esp_image_header_t* phdr)
}
static void vddsdio_configure()
{
#if CONFIG_BOOTLOADER_VDDSDIO_BOOST
rtc_vddsdio_config_t cfg = rtc_vddsdio_get_config();
if (cfg.tieh == 0) { // 1.8V is used
cfg.drefh = 3;
cfg.drefm = 3;
cfg.drefl = 3;
cfg.force = 1;
cfg.enable = 1;
rtc_vddsdio_set_config(cfg);
ets_delay_us(10); // wait for regulator to become stable
}
#endif // CONFIG_BOOTLOADER_VDDSDIO_BOOST
}
#define FLASH_CLK_IO 6
#define FLASH_CS_IO 11
#define FLASH_SPIQ_IO 7
#define FLASH_SPID_IO 8
#define FLASH_SPIWP_IO 10
#define FLASH_SPIHD_IO 9
#define FLASH_IO_MATRIX_DUMMY_40M 1
#define FLASH_IO_MATRIX_DUMMY_80M 2
static void IRAM_ATTR flash_gpio_configure()
{
int spi_cache_dummy = 0;
int drv = 2;
#if CONFIG_FLASHMODE_QIO
spi_cache_dummy = SPI0_R_QIO_DUMMY_CYCLELEN; //qio 3
#elif CONFIG_FLASHMODE_QOUT
spi_cache_dummy = SPI0_R_FAST_DUMMY_CYCLELEN; //qout 7
#elif CONFIG_FLASHMODE_DIO
spi_cache_dummy = SPI0_R_DIO_DUMMY_CYCLELEN; //dio 3
#elif CONFIG_FLASHMODE_DOUT
spi_cache_dummy = SPI0_R_FAST_DUMMY_CYCLELEN; //dout 7
#endif
/* dummy_len_plus values defined in ROM for SPI flash configuration */
extern uint8_t g_rom_spiflash_dummy_len_plus[];
#if CONFIG_ESPTOOLPY_FLASHFREQ_40M
g_rom_spiflash_dummy_len_plus[0] = FLASH_IO_MATRIX_DUMMY_40M;
g_rom_spiflash_dummy_len_plus[1] = FLASH_IO_MATRIX_DUMMY_40M;
SET_PERI_REG_BITS(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN_V, spi_cache_dummy + FLASH_IO_MATRIX_DUMMY_40M, SPI_USR_DUMMY_CYCLELEN_S); //DUMMY
#elif CONFIG_ESPTOOLPY_FLASHFREQ_80M
g_rom_spiflash_dummy_len_plus[0] = FLASH_IO_MATRIX_DUMMY_80M;
g_rom_spiflash_dummy_len_plus[1] = FLASH_IO_MATRIX_DUMMY_80M;
SET_PERI_REG_BITS(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN_V, spi_cache_dummy + FLASH_IO_MATRIX_DUMMY_80M, SPI_USR_DUMMY_CYCLELEN_S); //DUMMY
drv = 3;
#endif
uint32_t chip_ver = REG_GET_FIELD(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_VER_PKG);
uint32_t pkg_ver = chip_ver & 0x7;
if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5) {
// For ESP32D2WD the SPI pins are already configured
ESP_LOGI(TAG, "Detected ESP32D2WD");
//flash clock signal should come from IO MUX.
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, FUNC_SD_CLK_SPICLK);
SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, drv, FUN_DRV_S);
} else if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD2) {
// For ESP32PICOD2 the SPI pins are already configured
ESP_LOGI(TAG, "Detected ESP32PICOD2");
//flash clock signal should come from IO MUX.
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, FUNC_SD_CLK_SPICLK);
SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, drv, FUN_DRV_S);
} else if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4) {
// For ESP32PICOD4 the SPI pins are already configured
ESP_LOGI(TAG, "Detected ESP32PICOD4");
//flash clock signal should come from IO MUX.
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, FUNC_SD_CLK_SPICLK);
SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, drv, FUN_DRV_S);
} else {
ESP_LOGI(TAG, "Detected ESP32");
const uint32_t spiconfig = ets_efuse_get_spiconfig();
if (spiconfig == EFUSE_SPICONFIG_SPI_DEFAULTS) {
gpio_matrix_out(FLASH_CS_IO, SPICS0_OUT_IDX, 0, 0);
gpio_matrix_out(FLASH_SPIQ_IO, SPIQ_OUT_IDX, 0, 0);
gpio_matrix_in(FLASH_SPIQ_IO, SPIQ_IN_IDX, 0);
gpio_matrix_out(FLASH_SPID_IO, SPID_OUT_IDX, 0, 0);
gpio_matrix_in(FLASH_SPID_IO, SPID_IN_IDX, 0);
gpio_matrix_out(FLASH_SPIWP_IO, SPIWP_OUT_IDX, 0, 0);
gpio_matrix_in(FLASH_SPIWP_IO, SPIWP_IN_IDX, 0);
gpio_matrix_out(FLASH_SPIHD_IO, SPIHD_OUT_IDX, 0, 0);
gpio_matrix_in(FLASH_SPIHD_IO, SPIHD_IN_IDX, 0);
//select pin function gpio
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA0_U, PIN_FUNC_GPIO);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA1_U, PIN_FUNC_GPIO);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA2_U, PIN_FUNC_GPIO);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA3_U, PIN_FUNC_GPIO);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CMD_U, PIN_FUNC_GPIO);
// flash clock signal should come from IO MUX.
// set drive ability for clock
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, FUNC_SD_CLK_SPICLK);
SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, drv, FUN_DRV_S);
}
}
}
static void clock_configure(void)
{
/* Set CPU to 80MHz. Keep other clocks unmodified. */

View file

@ -1,14 +1,96 @@
menuconfig BT_ENABLED
menu Bluetooth
config BT_ENABLED
bool "Bluetooth"
help
Select this option to enable Bluetooth and show the submenu with Bluetooth configuration choices.
menuconfig BLUEDROID_ENABLED
bool "Bluedroid Bluetooth stack enabled"
choice BTDM_CONTROLLER_PINNED_TO_CORE_CHOICE
prompt "The cpu core which bluetooth controller run"
depends on BT_ENABLED && !FREERTOS_UNICORE
help
Specify the cpu core to run bluetooth controller.
Can not specify no-affinity.
config BTDM_CONTROLLER_PINNED_TO_CORE_0
bool "Core 0 (PRO CPU)"
config BTDM_CONTROLLER_PINNED_TO_CORE_1
bool "Core 1 (APP CPU)"
depends on !FREERTOS_UNICORE
endchoice
config BTDM_CONTROLLER_PINNED_TO_CORE
int
default 0 if BTDM_CONTROLLER_PINNED_TO_CORE_0
default 1 if BTDM_CONTROLLER_PINNED_TO_CORE_1
default 0
choice BTDM_CONTROLLER_HCI_MODE_CHOICE
prompt "HCI mode"
depends on BT_ENABLED
default y
help
This enables the default Bluedroid Bluetooth stack
help
Speicify HCI mode as VHCI or UART(H4)
config BTDM_CONTROLLER_HCI_MODE_VHCI
bool "VHCI"
help
Normal option. Mostly, choose this VHCI when bluetooth host run on ESP32, too.
config BTDM_CONTROLLER_HCI_MODE_UART_H4
bool "UART(H4)"
help
If use external bluetooth host which run on other hardware and use UART as the HCI interface,
choose this option.
endchoice
menu "HCI UART(H4) Options"
visible if BTDM_CONTROLLER_HCI_MODE_UART_H4
config BT_HCI_UART_NO
int "UART Number for HCI"
depends on BTDM_CONTROLLER_HCI_MODE_UART_H4
range 1 2
default 1
help
Uart number for HCI. The available uart is UART1 and UART2.
config BT_HCI_UART_BAUDRATE
int "UART Baudrate for HCI"
depends on BTDM_CONTROLLER_HCI_MODE_UART_H4
range 115200 921600
default 921600
help
UART Baudrate for HCI. Please use standard baudrate.
endmenu
menuconfig BLUEDROID_ENABLED
bool "Bluedroid Enable"
depends on BTDM_CONTROLLER_HCI_MODE_VHCI
default y
help
This enables the default Bluedroid Bluetooth stack
choice BLUEDROID_PINNED_TO_CORE_CHOICE
prompt "The cpu core which Bluedroid run"
depends on BLUEDROID_ENABLED && !FREERTOS_UNICORE
help
Which the cpu core to run Bluedroid. Can choose core0 and core1.
Can not specify no-affinity.
config BLUEDROID_PINNED_TO_CORE_0
bool "Core 0 (PRO CPU)"
config BLUEDROID_PINNED_TO_CORE_1
bool "Core 1 (APP CPU)"
depends on !FREERTOS_UNICORE
endchoice
config BLUEDROID_PINNED_TO_CORE
int
depends on BLUEDROID_ENABLED
default 0 if BLUEDROID_PINNED_TO_CORE_0
default 1 if BLUEDROID_PINNED_TO_CORE_1
default 0
config BTC_TASK_STACK_SIZE
int "Bluetooth event (callback to application) task stack size"
@ -67,44 +149,6 @@ config BT_ACL_CONNECTIONS
help
Maximum BT/BLE connection count
#disable now for app cpu due to a known issue
config BTDM_CONTROLLER_RUN_APP_CPU
bool "Run controller on APP CPU"
depends on BT_ENABLED && !FREERTOS_UNICORE && 0
default n
help
Run controller on APP CPU.
config BTDM_CONTROLLER_RUN_CPU
int
depends on BT_ENABLED
default 1 if BTDM_CONTROLLER_RUN_APP_CPU
default 0
menuconfig BT_HCI_UART
bool "HCI use UART as IO"
depends on BT_ENABLED && !BLUEDROID_ENABLED
default n
help
Default HCI use VHCI, if this option choose, HCI will use UART(0/1/2) as IO.
Besides, it can set uart number and uart baudrate.
config BT_HCI_UART_NO
int "UART Number for HCI"
depends on BT_HCI_UART
range 1 2
default 1
help
Uart number for HCI.
config BT_HCI_UART_BAUDRATE
int "UART Baudrate for HCI"
depends on BT_HCI_UART
range 115200 921600
default 921600
help
UART Baudrate for HCI. Please use standard baudrate.
config SMP_ENABLE
bool
depends on BLUEDROID_ENABLED
@ -115,3 +159,5 @@ config BT_RESERVE_DRAM
hex
default 0x10000 if BT_ENABLED
default 0
endmenu

View file

@ -36,7 +36,9 @@ esp_err_t esp_bt_dev_set_device_name(const char *name)
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
if (!name){
return ESP_ERR_INVALID_ARG;
}
if (strlen(name) > ESP_DEV_DEVICE_NAME_MAX) {
return ESP_ERR_INVALID_ARG;
}

View file

@ -179,10 +179,20 @@ typedef enum {
/* relate to BTA_DM_BLE_SEC_xxx in bta_api.h */
typedef enum {
ESP_BLE_SEC_NONE = 0, /* relate to BTA_DM_BLE_SEC_NONE in bta_api.h */
ESP_BLE_SEC_ENCRYPT, /* relate to BTA_DM_BLE_SEC_ENCRYPT in bta_api.h */
ESP_BLE_SEC_ENCRYPT_NO_MITM, /* relate to BTA_DM_BLE_SEC_ENCRYPT_NO_MITM in bta_api.h */
ESP_BLE_SEC_ENCRYPT_MITM, /* relate to BTA_DM_BLE_SEC_ENCRYPT_MITM in bta_api.h */
ESP_BLE_SEC_ENCRYPT = 1, /* relate to BTA_DM_BLE_SEC_ENCRYPT in bta_api.h. If the device has already
bonded, the stack will used LTK to encrypt with the remote device directly.
Else if the device hasn't bonded, the stack will used the default authentication request
used the esp_ble_gap_set_security_param function set by the user. */
ESP_BLE_SEC_ENCRYPT_NO_MITM, /* relate to BTA_DM_BLE_SEC_ENCRYPT_NO_MITM in bta_api.h. If the device has already
bonded, the stack will check the LTK Whether the authentication request has been met, if met, used the LTK
to encrypt with the remote device directly, else Re-pair with the remote device.
Else if the device hasn't bonded, the stack will used NO MITM authentication request in the current link instead of
used the authreq in the esp_ble_gap_set_security_param function set by the user. */
ESP_BLE_SEC_ENCRYPT_MITM, /* relate to BTA_DM_BLE_SEC_ENCRYPT_MITM in bta_api.h. If the device has already
bonded, the stack will check the LTK Whether the authentication request has been met, if met, used the LTK
to encrypt with the remote device directly, else Re-pair with the remote device.
Else if the device hasn't bonded, the stack will used MITM authentication request in the current link instead of
used the authreq in the esp_ble_gap_set_security_param function set by the user. */
}esp_ble_sec_act_t;
typedef enum {

View file

@ -201,7 +201,6 @@ typedef union {
* @brief ESP_GATTC_CONNECT_EVT
*/
struct gattc_connect_evt_param {
esp_gatt_status_t status; /*!< Operation status */
uint16_t conn_id; /*!< Connection id */
esp_bd_addr_t remote_bda; /*!< Remote bluetooth device address */
} connect; /*!< Gatt client callback param of ESP_GATTC_CONNECT_EVT */
@ -210,7 +209,7 @@ typedef union {
* @brief ESP_GATTC_DISCONNECT_EVT
*/
struct gattc_disconnect_evt_param {
esp_gatt_status_t status; /*!< Operation status */
esp_gatt_conn_reason_t reason; /*!< disconnection reason */
uint16_t conn_id; /*!< Connection id */
esp_bd_addr_t remote_bda; /*!< Remote bluetooth device address */
} disconnect; /*!< Gatt client callback param of ESP_GATTC_DISCONNECT_EVT */

View file

@ -119,6 +119,8 @@ typedef union {
struct gatts_conf_evt_param {
esp_gatt_status_t status; /*!< Operation status */
uint16_t conn_id; /*!< Connection id */
uint16_t len; /*!< The indication or notification value length, len is valid when send notification or indication failed */
uint8_t *value; /*!< The indication or notification value , value is valid when send notification or indication failed */
} conf; /*!< Gatt server callback param of ESP_GATTS_CONF_EVT (confirm) */
/**
@ -193,7 +195,6 @@ typedef union {
struct gatts_connect_evt_param {
uint16_t conn_id; /*!< Connection id */
esp_bd_addr_t remote_bda; /*!< Remote bluetooth device address */
bool is_connected; /*!< Indicate it is connected or not */
} connect; /*!< Gatt server callback param of ESP_GATTS_CONNECT_EVT */
/**
@ -202,7 +203,7 @@ typedef union {
struct gatts_disconnect_evt_param {
uint16_t conn_id; /*!< Connection id */
esp_bd_addr_t remote_bda; /*!< Remote bluetooth device address */
bool is_connected; /*!< Indicate it is connected or not */
esp_gatt_conn_reason_t reason; /*!< Indicate the reason of disconnection */
} disconnect; /*!< Gatt server callback param of ESP_GATTS_DISCONNECT_EVT */
/**

View file

@ -711,7 +711,6 @@ void bta_gattc_conncback(tBTA_GATTC_RCB *p_rcb, tBTA_GATTC_DATA *p_data)
{
if (p_rcb) {
bta_gattc_send_connect_cback(p_rcb,
BTA_GATT_OK,
p_data->int_conn.remote_bda,
p_data->int_conn.hdr.layer_specific);
@ -730,7 +729,7 @@ void bta_gattc_disconncback(tBTA_GATTC_RCB *p_rcb, tBTA_GATTC_DATA *p_data)
{
if (p_rcb) {
bta_gattc_send_disconnect_cback(p_rcb,
BTA_GATT_OK,
p_data->int_conn.reason,
p_data->int_conn.remote_bda,
p_data->int_conn.hdr.layer_specific);
@ -793,7 +792,7 @@ void bta_gattc_close(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
if (p_data->hdr.event == BTA_GATTC_API_CLOSE_EVT) {
cb_data.close.status = GATT_Disconnect(p_data->hdr.layer_specific);
} else if (p_data->hdr.event == BTA_GATTC_INT_DISCONN_EVT) {
cb_data.close.status = p_data->int_conn.reason;
cb_data.close.status = BTA_GATT_OK;
cb_data.close.reason = p_data->int_conn.reason;
}

View file

@ -653,15 +653,13 @@ void bta_gattc_send_open_cback( tBTA_GATTC_RCB *p_clreg, tBTA_GATT_STATUS status
** Returns
**
*******************************************************************************/
void bta_gattc_send_connect_cback( tBTA_GATTC_RCB *p_clreg, tBTA_GATT_STATUS status,
BD_ADDR remote_bda, UINT16 conn_id)
void bta_gattc_send_connect_cback( tBTA_GATTC_RCB *p_clreg, BD_ADDR remote_bda, UINT16 conn_id)
{
tBTA_GATTC cb_data;
if (p_clreg->p_cback) {
memset(&cb_data, 0, sizeof(tBTA_GATTC));
cb_data.connect.status = status;
cb_data.connect.client_if = p_clreg->client_if;
cb_data.connect.conn_id = conn_id;
bdcpy(cb_data.connect.remote_bda, remote_bda);
@ -679,7 +677,7 @@ void bta_gattc_send_connect_cback( tBTA_GATTC_RCB *p_clreg, tBTA_GATT_STATUS sta
** Returns
**
*******************************************************************************/
void bta_gattc_send_disconnect_cback( tBTA_GATTC_RCB *p_clreg, tBTA_GATT_STATUS status,
void bta_gattc_send_disconnect_cback( tBTA_GATTC_RCB *p_clreg, tGATT_DISCONN_REASON reason,
BD_ADDR remote_bda, UINT16 conn_id)
{
tBTA_GATTC cb_data;
@ -687,7 +685,7 @@ void bta_gattc_send_disconnect_cback( tBTA_GATTC_RCB *p_clreg, tBTA_GATT_STATUS
if (p_clreg->p_cback) {
memset(&cb_data, 0, sizeof(tBTA_GATTC));
cb_data.disconnect.status = status;
cb_data.disconnect.reason = reason;
cb_data.disconnect.client_if = p_clreg->client_if;
cb_data.disconnect.conn_id = conn_id;
bdcpy(cb_data.disconnect.remote_bda, remote_bda);

View file

@ -692,6 +692,15 @@ void bta_gatts_indicate_handle (tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg)
cb_data.req_data.status = status;
cb_data.req_data.conn_id = p_msg->api_indicate.hdr.layer_specific;
cb_data.req_data.value =(uint8_t *)osi_malloc(p_msg->api_indicate.len);
if (cb_data.req_data.value != NULL){
memset(cb_data.req_data.value, 0, p_msg->api_indicate.len);
cb_data.req_data.data_len = p_msg->api_indicate.len;
memcpy(cb_data.req_data.value, p_msg->api_indicate.value, p_msg->api_indicate.len);
}else{
cb_data.req_data.data_len = 0;
APPL_TRACE_ERROR("%s, malloc failed", __func__);
}
(*p_rcb->p_cback)(BTA_GATTS_CONF_EVT, &cb_data);
}
} else {

View file

@ -367,14 +367,13 @@ typedef struct {
} tBTA_GATTC_ENC_CMPL_CB;
typedef struct {
tBTA_GATT_STATUS status;
UINT16 conn_id;
tBTA_GATTC_IF client_if;
BD_ADDR remote_bda;
} tBTA_GATTC_CONNECT;
typedef struct {
tBTA_GATT_STATUS status;
tGATT_DISCONN_REASON reason;
UINT16 conn_id;
tBTA_GATTC_IF client_if;
BD_ADDR remote_bda;
@ -523,6 +522,8 @@ typedef struct {
UINT32 trans_id;
UINT16 conn_id;
tBTA_GATTS_REQ_DATA *p_data;
UINT16 data_len;
UINT8 *value;
} tBTA_GATTS_REQ;
typedef struct {

View file

@ -438,9 +438,8 @@ extern void bta_gattc_init_bk_conn(tBTA_GATTC_API_OPEN *p_data, tBTA_GATTC_RCB *
extern void bta_gattc_cancel_bk_conn(tBTA_GATTC_API_CANCEL_OPEN *p_data);
extern void bta_gattc_send_open_cback( tBTA_GATTC_RCB *p_clreg, tBTA_GATT_STATUS status,
BD_ADDR remote_bda, UINT16 conn_id, tBTA_TRANSPORT transport, UINT16 mtu);
extern void bta_gattc_send_connect_cback( tBTA_GATTC_RCB *p_clreg, tBTA_GATT_STATUS status,
BD_ADDR remote_bda, UINT16 conn_id);
extern void bta_gattc_send_disconnect_cback( tBTA_GATTC_RCB *p_clreg, tBTA_GATT_STATUS status,
extern void bta_gattc_send_connect_cback( tBTA_GATTC_RCB *p_clreg, BD_ADDR remote_bda, UINT16 conn_id);
extern void bta_gattc_send_disconnect_cback( tBTA_GATTC_RCB *p_clreg, tGATT_DISCONN_REASON reason,
BD_ADDR remote_bda, UINT16 conn_id);
extern void bta_gattc_process_api_refresh(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg);
extern void bta_gattc_cfg_mtu(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data);

View file

@ -139,8 +139,8 @@ bt_status_t btc_transfer_context(btc_msg_t *msg, void *arg, int arg_len, btc_arg
int btc_init(void)
{
xBtcQueue = xQueueCreate(BTC_TASK_QUEUE_NUM, sizeof(btc_msg_t));
xTaskCreatePinnedToCore(btc_task, "Btc_task", BTC_TASK_STACK_SIZE, NULL, BTC_TASK_PRIO, &xBtcTaskHandle, 0);
xBtcQueue = xQueueCreate(BTC_TASK_QUEUE_LEN, sizeof(btc_msg_t));
xTaskCreatePinnedToCore(btc_task, "Btc_task", BTC_TASK_STACK_SIZE, NULL, BTC_TASK_PRIO, &xBtcTaskHandle, BTC_TASK_PINNED_TO_CORE);
btc_gap_callback_init();
/* TODO: initial the profile_tab */

View file

@ -112,10 +112,6 @@ enum {
/* 5 frames is equivalent to 6.89*5*2.9 ~= 100 ms @ 44.1 khz, 20 ms mediatick */
#define MAX_OUTPUT_A2DP_FRAME_QUEUE_SZ (5)
#define MEDIA_DATA_Q_LEN (1)
#define MEDIA_CTRL_Q_LEN (5)
#define COMBINED_MEDIA_Q_LEN (MEDIA_DATA_Q_LEN + MEDIA_CTRL_Q_LEN)
typedef struct {
UINT16 num_frames_to_be_processed;
UINT16 len;
@ -276,13 +272,13 @@ bool btc_a2dp_start_media_task(void)
APPL_TRACE_EVENT("## A2DP START MEDIA THREAD ##");
xBtcMediaQueueSet = xQueueCreateSet(COMBINED_MEDIA_Q_LEN);
xBtcMediaQueueSet = xQueueCreateSet(BTC_MEDIA_TASK_QUEUE_SET_LEN);
configASSERT(xBtcMediaQueueSet);
xBtcMediaDataQueue = xQueueCreate(MEDIA_DATA_Q_LEN, sizeof(void *));
xBtcMediaDataQueue = xQueueCreate(BTC_MEDIA_DATA_QUEUE_LEN, sizeof(void *));
configASSERT(xBtcMediaDataQueue);
xQueueAddToSet(xBtcMediaDataQueue, xBtcMediaQueueSet);
xBtcMediaCtrlQueue = xQueueCreate(MEDIA_CTRL_Q_LEN, sizeof(void *));
xBtcMediaCtrlQueue = xQueueCreate(BTC_MEDIA_CTRL_QUEUE_LEN, sizeof(void *));
configASSERT(xBtcMediaCtrlQueue);
xQueueAddToSet(xBtcMediaCtrlQueue, xBtcMediaQueueSet);

View file

@ -858,7 +858,6 @@ void btc_gattc_cb_handler(btc_msg_t *msg)
tBTA_GATTC_CONNECT *connect = &arg->connect;
gattc_if = connect->client_if;
param.connect.status = connect->status;
param.connect.conn_id = BTC_GATT_GET_CONN_ID(connect->conn_id);
memcpy(param.connect.remote_bda, connect->remote_bda, sizeof(esp_bd_addr_t));
btc_gattc_cb_to_app(ESP_GATTC_CONNECT_EVT, gattc_if, &param);
@ -879,7 +878,7 @@ void btc_gattc_cb_handler(btc_msg_t *msg)
tBTA_GATTC_DISCONNECT *disconnect = &arg->disconnect;
gattc_if = disconnect->client_if;
param.disconnect.status = disconnect->status;
param.disconnect.reason = disconnect->reason;
param.disconnect.conn_id = BTC_GATT_GET_CONN_ID(disconnect->conn_id);
memcpy(param.disconnect.remote_bda, disconnect->remote_bda, sizeof(esp_bd_addr_t));
btc_gattc_cb_to_app(ESP_GATTC_DISCONNECT_EVT, gattc_if, &param);

View file

@ -527,6 +527,11 @@ static void btc_gatts_cb_param_copy_free(btc_msg_t *msg, tBTA_GATTS *p_data)
osi_free(p_data->req_data.p_data);
}
break;
case BTA_GATTS_CONF_EVT:
if (p_data && p_data->req_data.value){
osi_free(p_data->req_data.value);
}
break;
default:
break;
}
@ -787,6 +792,12 @@ void btc_gatts_cb_handler(btc_msg_t *msg)
param.conf.conn_id = BTC_GATT_GET_CONN_ID(p_data->req_data.conn_id);
param.conf.status = p_data->req_data.status;
if (p_data->req_data.status != ESP_GATT_OK && p_data->req_data.value){
param.conf.len = p_data->req_data.data_len;
param.conf.value = p_data->req_data.value;
}else{
param.conf.len = 0;
}
btc_gatts_cb_to_app(ESP_GATTS_CONF_EVT, gatts_if, &param);
break;
case BTA_GATTS_CREATE_EVT:
@ -849,7 +860,6 @@ void btc_gatts_cb_handler(btc_msg_t *msg)
case BTA_GATTS_CONNECT_EVT:
gatts_if = p_data->conn.server_if;
param.connect.conn_id = BTC_GATT_GET_CONN_ID(p_data->conn.conn_id);
param.connect.is_connected = true;
memcpy(param.connect.remote_bda, p_data->conn.remote_bda, ESP_BD_ADDR_LEN);
btc_gatts_cb_to_app(ESP_GATTS_CONNECT_EVT, gatts_if, &param);
@ -857,7 +867,7 @@ void btc_gatts_cb_handler(btc_msg_t *msg)
case BTA_GATTS_DISCONNECT_EVT:
gatts_if = p_data->conn.server_if;
param.disconnect.conn_id = BTC_GATT_GET_CONN_ID(p_data->conn.conn_id);
param.disconnect.is_connected = false;
param.disconnect.reason = p_data->conn.reason;
memcpy(param.disconnect.remote_bda, p_data->conn.remote_bda, ESP_BD_ADDR_LEN);
btc_gatts_cb_to_app(ESP_GATTS_DISCONNECT_EVT, gatts_if, &param);

View file

@ -101,8 +101,8 @@ static bool hal_open(const hci_hal_callbacks_t *upper_callbacks)
hci_hal_env_init(HCI_HAL_SERIAL_BUFFER_SIZE, SIZE_MAX);
xHciH4Queue = xQueueCreate(HCI_H4_QUEUE_NUM, sizeof(BtTaskEvt_t));
xTaskCreatePinnedToCore(hci_hal_h4_rx_handler, HCI_H4_TASK_NAME, HCI_H4_TASK_STACK_SIZE, NULL, HCI_H4_TASK_PRIO, &xHciH4TaskHandle, 0);
xHciH4Queue = xQueueCreate(HCI_H4_QUEUE_LEN, sizeof(BtTaskEvt_t));
xTaskCreatePinnedToCore(hci_hal_h4_rx_handler, HCI_H4_TASK_NAME, HCI_H4_TASK_STACK_SIZE, NULL, HCI_H4_TASK_PRIO, &xHciH4TaskHandle, HCI_H4_TASK_PINNED_TO_CORE);
//register vhci host cb
esp_vhci_host_register_callback(&vhci_host_cb);

View file

@ -107,8 +107,8 @@ int hci_start_up(void)
goto error;
}
xHciHostQueue = xQueueCreate(HCI_HOST_QUEUE_NUM, sizeof(BtTaskEvt_t));
xTaskCreatePinnedToCore(hci_host_thread_handler, HCI_HOST_TASK_NAME, HCI_HOST_TASK_STACK_SIZE, NULL, HCI_HOST_TASK_PRIO, &xHciHostTaskHandle, 0);
xHciHostQueue = xQueueCreate(HCI_HOST_QUEUE_LEN, sizeof(BtTaskEvt_t));
xTaskCreatePinnedToCore(hci_host_thread_handler, HCI_HOST_TASK_NAME, HCI_HOST_TASK_STACK_SIZE, NULL, HCI_HOST_TASK_PRIO, &xHciHostTaskHandle, HCI_HOST_TASK_PINNED_TO_CORE);
packet_fragmenter->init(&packet_fragmenter_callbacks);
hal->open(&hal_callbacks);

View file

@ -681,11 +681,15 @@
/* The maximum number of simultaneous channels that L2CAP can support. Up to 16*/
#ifndef MAX_L2CAP_CHANNELS
#if (CLASSIC_BT_INCLUDED == TRUE)
#define MAX_L2CAP_CHANNELS 8
#define MAX_L2CAP_CHANNELS 16
#else
#if (SMP_INCLUDED == FALSE)
#define MAX_L2CAP_CHANNELS MAX_ACL_CONNECTIONS //This is used in the BLE client when start connected with the peer device
#else
#define MAX_L2CAP_CHANNELS (MAX_ACL_CONNECTIONS * 2) //This is used in the BLE client when start connected with the peer device and in SMP
#endif ///SMP_INCLUDED == FALSE
#endif ///CLASSIC_BT_INCLUDED == TRUE
#endif
#endif ///MAX_L2CAP_CHANNELS
/* The maximum number of simultaneous applications that can register with L2CAP. */
#ifndef MAX_L2CAP_CLIENTS

View file

@ -57,26 +57,39 @@ typedef enum {
SIG_BTU_NUM,
} SIG_BTU_t;
#define TASK_PINNED_TO_CORE (CONFIG_BLUEDROID_PINNED_TO_CORE < portNUM_PROCESSORS ? CONFIG_BLUEDROID_PINNED_TO_CORE : tskNO_AFFINITY)
#define HCI_HOST_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE)
#define HCI_HOST_TASK_STACK_SIZE (2048 + BT_TASK_EXTRA_STACK_SIZE)
#define HCI_HOST_TASK_PRIO (configMAX_PRIORITIES - 3)
#define HCI_HOST_TASK_NAME "hciHostT"
#define HCI_HOST_QUEUE_NUM 40
#define HCI_HOST_QUEUE_LEN 40
#define HCI_H4_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE)
#define HCI_H4_TASK_STACK_SIZE (2048 + BT_TASK_EXTRA_STACK_SIZE)
#define HCI_H4_TASK_PRIO (configMAX_PRIORITIES - 4)
#define HCI_H4_TASK_NAME "hciH4T"
#define HCI_H4_QUEUE_NUM 60
#define HCI_H4_QUEUE_LEN 60
#define BTU_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE)
#define BTU_TASK_STACK_SIZE (4096 + BT_TASK_EXTRA_STACK_SIZE)
#define BTU_TASK_PRIO (configMAX_PRIORITIES - 5)
#define BTU_TASK_NAME "btuT"
#define BTU_QUEUE_NUM 50
#define BTU_QUEUE_LEN 50
#define BTC_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE)
#define BTC_TASK_STACK_SIZE (CONFIG_BTC_TASK_STACK_SIZE + BT_TASK_EXTRA_STACK_SIZE) //by menuconfig
#define BTC_TASK_NAME "btcT"
#define BTC_TASK_PRIO (configMAX_PRIORITIES - 6)
#define BTC_TASK_QUEUE_NUM 60
#define BTC_TASK_QUEUE_LEN 60
#define BTC_MEDIA_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE)
#define BTC_MEDIA_TASK_STACK_SIZE (CONFIG_BTC_TASK_STACK_SIZE + BT_TASK_EXTRA_STACK_SIZE)
#define BTC_MEDIA_TASK_NAME "BtcMediaT"
#define BTC_MEDIA_TASK_PRIO (configMAX_PRIORITIES - 3)
#define BTC_MEDIA_DATA_QUEUE_LEN (1)
#define BTC_MEDIA_CTRL_QUEUE_LEN (5)
#define BTC_MEDIA_TASK_QUEUE_SET_LEN (BTC_MEDIA_DATA_QUEUE_LEN + BTC_MEDIA_CTRL_QUEUE_LEN)
#define TASK_POST_NON_BLOCKING (0)
#define TASK_POST_BLOCKING (portMAX_DELAY)

View file

@ -1417,7 +1417,7 @@ tBTM_STATUS btm_ble_set_encryption (BD_ADDR bd_addr, void *p_ref_data, UINT8 lin
switch (sec_act) {
case BTM_BLE_SEC_ENCRYPT:
if (link_role == BTM_ROLE_MASTER) {
if (link_role == BTM_ROLE_MASTER && (p_rec->ble.key_type & BTM_LE_KEY_PENC)) {
/* start link layer encryption using the security info stored */
cmd = btm_ble_start_encrypt(bd_addr, FALSE, NULL);
break;
@ -1426,7 +1426,7 @@ tBTM_STATUS btm_ble_set_encryption (BD_ADDR bd_addr, void *p_ref_data, UINT8 lin
sec_request to request the master to encrypt the link */
case BTM_BLE_SEC_ENCRYPT_NO_MITM:
case BTM_BLE_SEC_ENCRYPT_MITM:
if (link_role == BTM_ROLE_MASTER) {
if ((link_role == BTM_ROLE_MASTER) && (sec_act != BTM_BLE_SEC_ENCRYPT)) {
auth_req = (sec_act == BTM_BLE_SEC_ENCRYPT_NO_MITM)
? SMP_AUTH_GEN_BOND : (SMP_AUTH_GEN_BOND | SMP_AUTH_YN_BIT);
btm_ble_link_sec_check (bd_addr, auth_req, &sec_req_act);

View file

@ -168,8 +168,8 @@ void BTU_StartUp(void)
osi_mutex_new(&btu_l2cap_alarm_lock);
xBtuQueue = xQueueCreate(BTU_QUEUE_NUM, sizeof(BtTaskEvt_t));
xTaskCreatePinnedToCore(btu_task_thread_handler, BTU_TASK_NAME, BTU_TASK_STACK_SIZE, NULL, BTU_TASK_PRIO, &xBtuTaskHandle, 0);
xBtuQueue = xQueueCreate(BTU_QUEUE_LEN, sizeof(BtTaskEvt_t));
xTaskCreatePinnedToCore(btu_task_thread_handler, BTU_TASK_NAME, BTU_TASK_STACK_SIZE, NULL, BTU_TASK_PRIO, &xBtuTaskHandle, BTU_TASK_PINNED_TO_CORE);
btu_task_post(SIG_BTU_START_UP, NULL, TASK_POST_BLOCKING);

View file

@ -567,7 +567,6 @@ tGATT_STATUS GATTS_HandleValueIndication (UINT16 conn_id, UINT16 attr_handle, U
tGATT_VALUE indication;
BT_HDR *p_msg;
tGATT_VALUE *p_buf;
tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id);
UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id);
tGATT_REG *p_reg = gatt_get_regcb(gatt_if);
@ -591,12 +590,16 @@ tGATT_STATUS GATTS_HandleValueIndication (UINT16 conn_id, UINT16 attr_handle, U
indication.auth_req = GATT_AUTH_REQ_NONE;
if (GATT_HANDLE_IS_VALID(p_tcb->indicate_handle)) {
/* TODO: need to further check whether deleting pending queue here cause reducing transport performance */
/*
GATT_TRACE_DEBUG ("Add a pending indication");
if ((p_buf = gatt_add_pending_ind(p_tcb, &indication)) != NULL) {
cmd_status = GATT_SUCCESS;
} else {
cmd_status = GATT_NO_RESOURCES;
}
*/
return GATT_BUSY;
} else {
if ( (p_msg = attp_build_sr_msg (p_tcb, GATT_HANDLE_VALUE_IND, (tGATT_SR_MSG *)&indication)) != NULL) {

View file

@ -421,6 +421,8 @@ void gatt_process_exec_write_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U
if (is_prepare_write_valid){
if((queue_data->p_attr->p_value != NULL) && (queue_data->p_attr->p_value->attr_val.attr_val != NULL)){
memcpy(queue_data->p_attr->p_value->attr_val.attr_val+queue_data->offset, queue_data->value, queue_data->len);
//don't forget to increase the attribute value length in the gatts database.
queue_data->p_attr->p_value->attr_val.attr_len += queue_data->len;
}
}
osi_free(queue_data);

View file

@ -1469,7 +1469,8 @@ typedef struct {
#define HCI_FEATURE_SWITCH_MASK 0x20
#define HCI_FEATURE_SWITCH_OFF 0
#define HCI_SWITCH_SUPPORTED(x) ((x)[HCI_FEATURE_SWITCH_OFF] & HCI_FEATURE_SWITCH_MASK)
// temporarily disable ROLE_SWITCH since there is an issue to be fixed
#define HCI_SWITCH_SUPPORTED(x) (0 & ((x)[HCI_FEATURE_SWITCH_OFF] & HCI_FEATURE_SWITCH_MASK))
#define HCI_FEATURE_HOLD_MODE_MASK 0x40
#define HCI_FEATURE_HOLD_MODE_OFF 0

View file

@ -1221,25 +1221,16 @@ void smp_decide_association_model(tSMP_CB *p_cb, tSMP_INT_DATA *p_data)
switch (p_cb->selected_association_model) {
case SMP_MODEL_ENCRYPTION_ONLY: /* TK = 0, go calculate Confirm */
if (p_cb->role == HCI_ROLE_MASTER &&
((p_cb->peer_auth_req & SMP_AUTH_YN_BIT) != 0) &&
((p_cb->loc_auth_req & SMP_AUTH_YN_BIT) == 0)) {
SMP_TRACE_ERROR ("IO capability does not meet authentication requirement\n");
failure = SMP_PAIR_AUTH_FAIL;
p = (tSMP_INT_DATA *)&failure;
int_evt = SMP_AUTH_CMPL_EVT;
} else {
p_cb->sec_level = SMP_SEC_UNAUTHENTICATE;
SMP_TRACE_EVENT ("p_cb->sec_level =%d (SMP_SEC_UNAUTHENTICATE) \n", p_cb->sec_level );
p_cb->sec_level = SMP_SEC_UNAUTHENTICATE;
SMP_TRACE_EVENT ("p_cb->sec_level =%d (SMP_SEC_UNAUTHENTICATE) \n", p_cb->sec_level );
key.key_type = SMP_KEY_TYPE_TK;
key.p_data = p_cb->tk;
p = (tSMP_INT_DATA *)&key;
key.key_type = SMP_KEY_TYPE_TK;
key.p_data = p_cb->tk;
p = (tSMP_INT_DATA *)&key;
memset(p_cb->tk, 0, BT_OCTET16_LEN);
/* TK, ready */
int_evt = SMP_KEY_READY_EVT;
}
memset(p_cb->tk, 0, BT_OCTET16_LEN);
/* TK, ready */
int_evt = SMP_KEY_READY_EVT;
break;
case SMP_MODEL_PASSKEY:

View file

@ -24,6 +24,7 @@
#include "freertos/semphr.h"
#include "freertos/xtensa_api.h"
#include "freertos/portmacro.h"
#include "xtensa/core-macros.h"
#include "esp_types.h"
#include "esp_system.h"
#include "esp_task.h"
@ -34,6 +35,8 @@
#include "esp_err.h"
#include "esp_log.h"
#include "esp_pm.h"
#include "esp_ipc.h"
#include "driver/periph_ctrl.h"
#if CONFIG_BT_ENABLED
@ -135,6 +138,7 @@ struct osi_funcs_t {
int32_t (* _task_create)(void *task_func, const char *name, uint32_t stack_depth, void *param, uint32_t prio, void *task_handle, uint32_t core_id);
void (* _task_delete)(void *task_handle);
bool (* _is_in_isr)(void);
int (* _cause_sw_intr_to_core)(int core_id, int intr_no);
void *(* _malloc)(uint32_t size);
void (* _free)(void *p);
int32_t (* _read_efuse_mac)(uint8_t mac[6]);
@ -274,6 +278,26 @@ static bool IRAM_ATTR is_in_isr_wrapper(void)
return (bool)xPortInIsrContext();
}
static void IRAM_ATTR cause_sw_intr(void *arg)
{
/* just convert void * to int, because the width is the same */
uint32_t intr_no = (uint32_t)arg;
XTHAL_SET_INTSET((1<<intr_no));
}
static int IRAM_ATTR cause_sw_intr_to_core_wrapper(int core_id, int intr_no)
{
esp_err_t err = ESP_OK;
if (xPortGetCoreID() == core_id) {
cause_sw_intr((void *)intr_no);
} else {
err = esp_ipc_call(core_id, cause_sw_intr, (void *)intr_no);
}
return err;
}
static int32_t IRAM_ATTR read_mac_wrapper(uint8_t mac[6])
{
return esp_read_mac(mac, ESP_MAC_BT);
@ -315,6 +339,7 @@ static struct osi_funcs_t osi_funcs = {
._task_create = task_create_wrapper,
._task_delete = task_delete_wrapper,
._is_in_isr = is_in_isr_wrapper,
._cause_sw_intr_to_core = cause_sw_intr_to_core_wrapper,
._malloc = malloc,
._free = free,
._read_efuse_mac = read_mac_wrapper,
@ -345,10 +370,10 @@ static uint32_t btdm_config_mask_load(void)
mask |= BTDM_CFG_BT_DATA_RELEASE;
}
#ifdef CONFIG_BT_HCI_UART
#if CONFIG_BTDM_CONTROLLER_HCI_MODE_UART_H4
mask |= BTDM_CFG_HCI_UART;
#endif
#ifdef CONFIG_BTDM_CONTROLLER_RUN_APP_CPU
#if CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE == 1
mask |= BTDM_CFG_CONTROLLER_RUN_APP_CPU;
#endif
return mask;
@ -458,6 +483,8 @@ esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg)
btdm_controller_mem_init();
periph_module_enable(PERIPH_BT_MODULE);
btdm_cfg_mask = btdm_config_mask_load();
ret = btdm_controller_init(btdm_cfg_mask, cfg);
@ -483,6 +510,8 @@ esp_err_t esp_bt_controller_deinit(void)
return ESP_ERR_NO_MEM;
}
periph_module_disable(PERIPH_BT_MODULE);
btdm_controller_status = ESP_BT_CONTROLLER_STATUS_IDLE;
#ifdef CONFIG_PM_ENABLE

@ -1 +1 @@
Subproject commit 0710172bce30dc2478e61b80a8e4f5e80d6f0e05
Subproject commit 8e62573de3dd9565b2209a3e8fe19a9900320b77

View file

@ -49,6 +49,10 @@ typedef enum {
PERIPH_SDIO_SLAVE_MODULE,
PERIPH_CAN_MODULE,
PERIPH_EMAC_MODULE,
PERIPH_RNG_MODULE,
PERIPH_WIFI_MODULE,
PERIPH_BT_MODULE,
PERIPH_WIFI_BT_COMMON_MODULE,
} periph_module_t;
/**

View file

@ -106,6 +106,14 @@ static uint32_t get_clk_en_mask(periph_module_t periph)
return DPORT_CAN_CLK_EN;
case PERIPH_EMAC_MODULE:
return DPORT_WIFI_CLK_EMAC_EN;
case PERIPH_RNG_MODULE:
return DPORT_WIFI_CLK_RNG_EN;
case PERIPH_WIFI_MODULE:
return DPORT_WIFI_CLK_WIFI_EN_M;
case PERIPH_BT_MODULE:
return DPORT_WIFI_CLK_BT_EN_M;
case PERIPH_WIFI_BT_COMMON_MODULE:
return DPORT_WIFI_CLK_WIFI_BT_COMMON_M;
default:
return 0;
}
@ -159,13 +167,17 @@ static uint32_t get_rst_en_mask(periph_module_t periph)
case PERIPH_SPI_DMA_MODULE:
return DPORT_SPI_DMA_RST;
case PERIPH_SDMMC_MODULE:
return DPORT_WIFI_CLK_SDIO_HOST_EN;
return DPORT_SDIO_HOST_RST;
case PERIPH_SDIO_SLAVE_MODULE:
return DPORT_WIFI_CLK_SDIOSLAVE_EN;
return DPORT_SDIO_RST;
case PERIPH_CAN_MODULE:
return DPORT_CAN_RST;
case PERIPH_EMAC_MODULE:
return DPORT_WIFI_CLK_EMAC_EN;
return DPORT_EMAC_RST;
case PERIPH_WIFI_MODULE:
case PERIPH_BT_MODULE:
case PERIPH_WIFI_BT_COMMON_MODULE:
return 0;
default:
return 0;
}
@ -179,6 +191,10 @@ static bool is_wifi_clk_peripheral(periph_module_t periph)
case PERIPH_SDMMC_MODULE:
case PERIPH_SDIO_SLAVE_MODULE:
case PERIPH_EMAC_MODULE:
case PERIPH_RNG_MODULE:
case PERIPH_WIFI_MODULE:
case PERIPH_BT_MODULE:
case PERIPH_WIFI_BT_COMMON_MODULE:
return true;
default:
return false;

View file

@ -1140,12 +1140,14 @@ esp_err_t uart_driver_delete(uart_port_t uart_num)
free(p_uart_obj[uart_num]);
p_uart_obj[uart_num] = NULL;
if(uart_num == UART_NUM_0) {
periph_module_disable(PERIPH_UART0_MODULE);
} else if(uart_num == UART_NUM_1) {
periph_module_disable(PERIPH_UART1_MODULE);
} else if(uart_num == UART_NUM_2) {
periph_module_disable(PERIPH_UART2_MODULE);
if (uart_num != CONFIG_CONSOLE_UART_NUM ) {
if(uart_num == UART_NUM_0) {
periph_module_disable(PERIPH_UART0_MODULE);
} else if(uart_num == UART_NUM_1) {
periph_module_disable(PERIPH_UART1_MODULE);
} else if(uart_num == UART_NUM_2) {
periph_module_disable(PERIPH_UART2_MODULE);
}
}
return ESP_OK;
}

View file

@ -842,7 +842,7 @@ config ESP32_WIFI_STATIC_TX_BUFFER_NUM
int "Max number of WiFi static TX buffers"
depends on ESP32_WIFI_STATIC_TX_BUFFER
range 6 64
default 32
default 16
help
Set the number of WiFi static TX buffers. Each buffer takes approximately 1.6KB of RAM.
The static RX buffers are allocated when esp_wifi_init() is called, they are not released
@ -856,7 +856,7 @@ config ESP32_WIFI_STATIC_TX_BUFFER_NUM
config ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM
int "Max number of WiFi dynamic TX buffers"
depends on ESP32_WIFI_DYNAMIC_TX_BUFFER
range 16 64
range 16 128
default 32
help
Set the number of WiFi dynamic TX buffers. The size of each dynamic TX buffer is not fixed,
@ -1009,4 +1009,4 @@ config PM_TRACE
applications.
endmenu # "Power Management"
endmenu # "Power Management"

View file

@ -29,6 +29,7 @@
#include "soc/rtc_cntl_reg.h"
#include "soc/dport_reg.h"
#include "soc/i2s_reg.h"
#include "driver/periph_ctrl.h"
#include "xtensa/core-macros.h"
/* Number of cycles to wait from the 32k XTAL oscillator to consider it running.
@ -236,4 +237,7 @@ void esp_perip_clk_init(void)
/* Disable WiFi/BT/SDIO clocks. */
DPORT_CLEAR_PERI_REG_MASK(DPORT_WIFI_CLK_EN_REG, wifi_bt_sdio_clk);
/* Enable RNG clock. */
periph_module_enable(PERIPH_RNG_MODULE);
}

View file

@ -48,6 +48,7 @@ extern "C" {
#define ESP_ERR_ESPNOW_NOT_FOUND (ESP_ERR_ESPNOW_BASE + 4) /*!< ESPNOW peer is not found */
#define ESP_ERR_ESPNOW_INTERNAL (ESP_ERR_ESPNOW_BASE + 5) /*!< Internal error */
#define ESP_ERR_ESPNOW_EXIST (ESP_ERR_ESPNOW_BASE + 6) /*!< ESPNOW peer has existed */
#define ESP_ERR_ESPNOW_IF (ESP_ERR_ESPNOW_BASE + 7) /*!< Interface error */
#define ESP_NOW_ETH_ALEN 6 /*!< Length of ESPNOW peer MAC address */
#define ESP_NOW_KEY_LEN 16 /*!< Length of ESPNOW peer local master key */
@ -191,6 +192,7 @@ esp_err_t esp_now_unregister_send_cb(void);
* - ESP_ERR_ESPNOW_INTERNAL : internal error
* - ESP_ERR_ESPNOW_NO_MEM : out of memory
* - ESP_ERR_ESPNOW_NOT_FOUND : peer is not found
* - ESP_ERR_ESPNOW_IF : current WiFi interface doesn't match that of peer
*/
esp_err_t esp_now_send(const uint8_t *peer_addr, const uint8_t *data, size_t len);

View file

@ -91,6 +91,8 @@ extern "C" {
#define ESP_ERR_WIFI_PASSWORD (ESP_ERR_WIFI_BASE + 11) /*!< Password is invalid */
#define ESP_ERR_WIFI_TIMEOUT (ESP_ERR_WIFI_BASE + 12) /*!< Timeout error */
#define ESP_ERR_WIFI_WAKE_FAIL (ESP_ERR_WIFI_BASE + 13) /*!< WiFi is in sleep state(RF closed) and wakeup fail */
#define ESP_ERR_WIFI_WOULD_BLOCK (ESP_ERR_WIFI_BASE + 14) /*!< The caller would block */
#define ESP_ERR_WIFI_NOT_CONNECT (ESP_ERR_WIFI_BASE + 15) /*!< Station still in disconnect status */
/**
* @brief WiFi stack configuration parameters passed to esp_wifi_init call.
@ -392,7 +394,8 @@ esp_err_t esp_wifi_scan_get_ap_records(uint16_t *number, wifi_ap_record_t *ap_re
*
* @return
* - ESP_OK: succeed
* - others: fail
* - ESP_ERR_WIFI_CONN: The station interface don't initialized
* - ESP_ERR_WIFI_NOT_CONNECT: The station is in disconnect status
*/
esp_err_t esp_wifi_sta_get_ap_info(wifi_ap_record_t *ap_info);

@ -1 +1 @@
Subproject commit a0d77be618bc933415eb0ffd7c2a0610ef3336d6
Subproject commit bd07fc21efcf7a91a088cb01e56d24a7ba6bc3a6

View file

@ -35,6 +35,7 @@
#include "phy.h"
#include "phy_init_data.h"
#include "esp_coexist.h"
#include "driver/periph_ctrl.h"
static const char* TAG = "phy_init";
@ -50,8 +51,8 @@ esp_err_t esp_phy_rf_init(const esp_phy_init_data_t* init_data,
_lock_acquire(&s_phy_rf_init_lock);
if (s_phy_rf_init_count == 0) {
// Enable WiFi peripheral clock
DPORT_SET_PERI_REG_MASK(DPORT_WIFI_CLK_EN_REG, DPORT_WIFI_CLK_WIFI_EN | DPORT_WIFI_CLK_RNG_EN);
// Enable WiFi/BT common peripheral clock
periph_module_enable(PERIPH_WIFI_BT_COMMON_MODULE);
ESP_LOGV(TAG, "register_chipv7_phy, init_data=%p, cal_data=%p, mode=%d",
init_data, calibration_data, mode);
phy_set_wifi_mode_only(0);
@ -75,8 +76,8 @@ esp_err_t esp_phy_rf_deinit(void)
if (s_phy_rf_init_count == 1) {
// Disable PHY and RF.
phy_close_rf();
// Disable WiFi peripheral clock. Do not disable clock for hardware RNG
DPORT_CLEAR_PERI_REG_MASK(DPORT_WIFI_CLK_EN_REG, DPORT_WIFI_CLK_WIFI_EN);
// Disable WiFi/BT common peripheral clock. Do not disable clock for hardware RNG
periph_module_disable(PERIPH_WIFI_BT_COMMON_MODULE);
} else {
#if CONFIG_SW_COEXIST_ENABLE
coex_deinit();

View file

@ -198,14 +198,22 @@ static void rtc_wdt_disable()
* Placed into IRAM as flash may need some time to be powered on.
*/
static esp_err_t esp_light_sleep_inner(uint32_t pd_flags,
rtc_cpu_freq_t cpu_freq, uint32_t flash_enable_time_us) IRAM_ATTR __attribute__((noinline));
rtc_cpu_freq_t cpu_freq, uint32_t flash_enable_time_us,
rtc_vddsdio_config_t vddsdio_config) IRAM_ATTR __attribute__((noinline));
static esp_err_t esp_light_sleep_inner(uint32_t pd_flags,
rtc_cpu_freq_t cpu_freq, uint32_t flash_enable_time_us)
rtc_cpu_freq_t cpu_freq, uint32_t flash_enable_time_us,
rtc_vddsdio_config_t vddsdio_config)
{
// Enter sleep
esp_err_t err = esp_sleep_start(pd_flags);
// If VDDSDIO regulator was controlled by RTC registers before sleep,
// restore the configuration.
if (vddsdio_config.force) {
rtc_vddsdio_set_config(vddsdio_config);
}
// Restore CPU frequency
rtc_clk_cpu_freq_set(cpu_freq);
@ -244,6 +252,7 @@ esp_err_t esp_light_sleep_start()
s_config.sleep_duration -= flash_enable_time_us;
}
#endif //CONFIG_SPIRAM_SUPPORT
rtc_vddsdio_config_t vddsdio_config = rtc_vddsdio_get_config();
// Safety net: enable WDT in case exit from light sleep fails
rtc_wdt_enable(1000);
@ -252,7 +261,8 @@ esp_err_t esp_light_sleep_start()
rtc_cpu_freq_t cpu_freq = rtc_clk_cpu_freq_get();
// Enter sleep, then wait for flash to be ready on wakeup
esp_err_t err = esp_light_sleep_inner(pd_flags, cpu_freq, flash_enable_time_us);
esp_err_t err = esp_light_sleep_inner(pd_flags, cpu_freq,
flash_enable_time_us, vddsdio_config);
// At this point, if FRC1 is used for timekeeping, time will be lagging behind.
// This will update the microsecond count based on RTC timer.

View file

@ -22,6 +22,7 @@
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_types.h"
#include "esp_log.h"
#include "spiram_psram.h"
#include "rom/ets_sys.h"
#include "rom/spi_flash.h"
@ -30,6 +31,7 @@
#include "soc/io_mux_reg.h"
#include "soc/dport_reg.h"
#include "soc/gpio_sig_map.h"
#include "soc/efuse_reg.h"
#include "driver/gpio.h"
#include "driver/spi_common.h"
@ -71,6 +73,8 @@
#define PSRAM_SPIWP_IO 10
#define PSRAM_SPIHD_IO 9
#define PSRAM_INTERNAL_IO_28 28
#define PSRAM_INTERNAL_IO_29 29
#define PSRAM_IO_MATRIX_DUMMY_40M 1
#define PSRAM_IO_MATRIX_DUMMY_80M 2
@ -84,7 +88,7 @@
#define SPI_CACHE_DUMMY SPI0_R_FAST_DUMMY_CYCLELEN //dout 7
#endif
static const char* TAG = "psram";
typedef enum {
PSRAM_SPI_1 = 0x1,
PSRAM_SPI_2,
@ -418,7 +422,6 @@ void IRAM_ATTR psram_spi_init(psram_spi_num_t spi_num, psram_cache_mode_t mode)
static void IRAM_ATTR psram_gpio_config(psram_cache_mode_t mode)
{
gpio_matrix_out(FLASH_CLK_IO, SPICLK_OUT_IDX, 0, 0);
gpio_matrix_out(FLASH_CS_IO, SPICS0_OUT_IDX, 0, 0);
gpio_matrix_out(PSRAM_SPIQ_IO, SPIQ_OUT_IDX, 0, 0);
gpio_matrix_in(PSRAM_SPIQ_IO, SPIQ_IN_IDX, 0);
@ -434,16 +437,25 @@ static void IRAM_ATTR psram_gpio_config(psram_cache_mode_t mode)
extra_dummy = PSRAM_IO_MATRIX_DUMMY_40M;
g_rom_spiflash_dummy_len_plus[1] = PSRAM_IO_MATRIX_DUMMY_40M;
SET_PERI_REG_BITS(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN_V, SPI_CACHE_DUMMY + PSRAM_IO_MATRIX_DUMMY_80M, SPI_USR_DUMMY_CYCLELEN_S); //DUMMY
//set drive ability for clock
SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, 3, FUN_DRV_S);
SET_PERI_REG_BITS(GPIO_PIN_MUX_REG[PSRAM_CLK_IO], FUN_DRV, 2, FUN_DRV_S);
break;
case PSRAM_CACHE_F80M_S80M:
extra_dummy = PSRAM_IO_MATRIX_DUMMY_80M;
g_rom_spiflash_dummy_len_plus[1] = PSRAM_IO_MATRIX_DUMMY_80M;
SET_PERI_REG_BITS(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN_V, SPI_CACHE_DUMMY + PSRAM_IO_MATRIX_DUMMY_80M, SPI_USR_DUMMY_CYCLELEN_S); //DUMMY
//set drive ability for clock
SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, 3, FUN_DRV_S);
SET_PERI_REG_BITS(GPIO_PIN_MUX_REG[PSRAM_CLK_IO], FUN_DRV, 3, FUN_DRV_S);
break;
case PSRAM_CACHE_F40M_S40M:
extra_dummy = PSRAM_IO_MATRIX_DUMMY_40M;
g_rom_spiflash_dummy_len_plus[1] = PSRAM_IO_MATRIX_DUMMY_40M;
SET_PERI_REG_BITS(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN_V, SPI_CACHE_DUMMY + PSRAM_IO_MATRIX_DUMMY_40M, SPI_USR_DUMMY_CYCLELEN_S); //DUMMY
//set drive ability for clock
SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, 2, FUN_DRV_S);
SET_PERI_REG_BITS(GPIO_PIN_MUX_REG[PSRAM_CLK_IO], FUN_DRV, 2, FUN_DRV_S);
break;
default:
break;
@ -451,17 +463,31 @@ static void IRAM_ATTR psram_gpio_config(psram_cache_mode_t mode)
SET_PERI_REG_MASK(SPI_USER_REG(0), SPI_USR_DUMMY); // dummy en
//select pin function gpio
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA0_U, 2);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA1_U, 2);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA2_U, 2);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA3_U, 2);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, 2);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CMD_U, 2);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA0_U, PIN_FUNC_GPIO);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA1_U, PIN_FUNC_GPIO);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA2_U, PIN_FUNC_GPIO);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA3_U, PIN_FUNC_GPIO);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CMD_U, PIN_FUNC_GPIO);
//flash clock signal should come from IO MUX.
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, FUNC_SD_CLK_SPICLK);
}
//psram gpio init , different working frequency we have different solutions
esp_err_t IRAM_ATTR psram_enable(psram_cache_mode_t mode, psram_vaddr_mode_t vaddrmode) //psram init
{
uint32_t chip_ver = REG_GET_FIELD(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_VER_PKG);
uint32_t pkg_ver = chip_ver & 0x7;
if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5) {
ESP_EARLY_LOGE(TAG, "ESP32D2WD do not support psram yet");
return ESP_FAIL;
} else if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD2) {
ESP_EARLY_LOGE(TAG, "ESP32PICOD2 do not support psram yet");
return ESP_FAIL;
} else if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4) {
ESP_EARLY_LOGE(TAG, "ESP32PICOD4 do not support psram yet");
return ESP_FAIL;
}
/* note: If the third mode(80Mhz+80Mhz) is enabled, VSPI port will be occupied by the system,
Application code should never touch VSPI hardware in this case. We try to stop applications
from doing this using the drivers by claiming the port for ourselves*/
@ -515,20 +541,20 @@ esp_err_t IRAM_ATTR psram_enable(psram_cache_mode_t mode, psram_vaddr_mode_t vad
We do this by routing it signal to signal 224/225, which are used as a loopback; the extra run through
the GPIO matrix causes the delay. We use GPIO20 (which is not in any package but has pad logic in
silicon) as a temporary pad for this. So the signal path is:
GPIO6(SPI CLK) --> signal224(in then out) --> internal GPIO20 --> signal225(in then out) --> GPIO17(PSRAM CLK)
SPI CLK --> GPIO28 --> signal224(in then out) --> internal GPIO29 --> signal225(in then out) --> GPIO17(PSRAM CLK)
*/
gpio_matrix_in(FLASH_CLK_IO, SIG_IN_FUNC224_IDX, 0);
gpio_matrix_out(20, SIG_IN_FUNC224_IDX, 0, 0);
gpio_matrix_in(20, SIG_IN_FUNC225_IDX, 0);
gpio_matrix_out(PSRAM_INTERNAL_IO_28, SPICLK_OUT_IDX, 0, 0);
gpio_matrix_in(PSRAM_INTERNAL_IO_28, SIG_IN_FUNC224_IDX, 0);
gpio_matrix_out(PSRAM_INTERNAL_IO_29, SIG_IN_FUNC224_IDX, 0, 0);
gpio_matrix_in(PSRAM_INTERNAL_IO_29, SIG_IN_FUNC225_IDX, 0);
gpio_matrix_out(PSRAM_CLK_IO, SIG_IN_FUNC225_IDX, 0, 0);
break;
}
CLEAR_PERI_REG_MASK(SPI_USER_REG(PSRAM_SPI_1), SPI_CS_SETUP_M);
psram_gpio_config(mode);
WRITE_PERI_REG(GPIO_ENABLE_W1TS_REG, BIT(PSRAM_CS_IO)| BIT(PSRAM_CLK_IO));
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[PSRAM_CS_IO], 2);
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[PSRAM_CLK_IO], 2);
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[PSRAM_CS_IO], PIN_FUNC_GPIO);
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[PSRAM_CLK_IO], PIN_FUNC_GPIO);
uint32_t id;
psram_read_id(&id);
if (((id >> PSRAM_MFG_ID_S) & PSRAM_MFG_ID_M) != PSRAM_MFG_ID_V) {

View file

@ -266,15 +266,10 @@ void IRAM_ATTR esp_restart(void)
*/
void IRAM_ATTR esp_restart_noos()
{
const uint32_t core_id = xPortGetCoreID();
const uint32_t other_core_id = core_id == 0 ? 1 : 0;
esp_cpu_stall(other_core_id);
// Disable interrupts
xt_ints_off(0xFFFFFFFF);
// other core is now stalled, can access DPORT registers directly
esp_dport_access_int_pause();
// We need to disable TG0/TG1 watchdogs
// First enable RTC watchdog for 1 second
// Enable RTC watchdog for 1 second
REG_WRITE(RTC_CNTL_WDTWPROTECT_REG, RTC_CNTL_WDT_WKEY_VALUE);
REG_WRITE(RTC_CNTL_WDTCONFIG0_REG,
RTC_CNTL_WDT_FLASHBOOT_MOD_EN_M |
@ -284,6 +279,18 @@ void IRAM_ATTR esp_restart_noos()
(1 << RTC_CNTL_WDT_CPU_RESET_LENGTH_S) );
REG_WRITE(RTC_CNTL_WDTCONFIG1_REG, rtc_clk_slow_freq_get_hz() * 1);
// Reset and stall the other CPU.
// CPU must be reset before stalling, in case it was running a s32c1i
// instruction. This would cause memory pool to be locked by arbiter
// to the stalled CPU, preventing current CPU from accessing this pool.
const uint32_t core_id = xPortGetCoreID();
const uint32_t other_core_id = (core_id == 0) ? 1 : 0;
esp_cpu_reset(other_core_id);
esp_cpu_stall(other_core_id);
// Other core is now stalled, can access DPORT registers directly
esp_dport_access_int_abort();
// Disable TG0/TG1 watchdogs
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
TIMERG0.wdt_config0.en = 0;
@ -292,8 +299,10 @@ void IRAM_ATTR esp_restart_noos()
TIMERG1.wdt_config0.en = 0;
TIMERG1.wdt_wprotect=0;
// Disable all interrupts
xt_ints_off(0xFFFFFFFF);
// Flush any data left in UART FIFOs
uart_tx_wait_idle(0);
uart_tx_wait_idle(1);
uart_tx_wait_idle(2);
// Disable cache
Cache_Read_Disable(0);
@ -310,11 +319,6 @@ void IRAM_ATTR esp_restart_noos()
WRITE_PERI_REG(GPIO_FUNC5_IN_SEL_CFG_REG, 0x30);
#endif
// Flush any data left in UART FIFOs
uart_tx_wait_idle(0);
uart_tx_wait_idle(1);
uart_tx_wait_idle(2);
// Reset wifi/bluetooth/ethernet/sdio (bb/mac)
DPORT_SET_PERI_REG_MASK(DPORT_CORE_RST_EN_REG,
DPORT_BB_RST | DPORT_FE_RST | DPORT_MAC_RST |
@ -337,14 +341,14 @@ void IRAM_ATTR esp_restart_noos()
// Reset CPUs
if (core_id == 0) {
// Running on PRO CPU: APP CPU is stalled. Can reset both CPUs.
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG,
RTC_CNTL_SW_PROCPU_RST_M | RTC_CNTL_SW_APPCPU_RST_M);
esp_cpu_reset(1);
esp_cpu_reset(0);
} else {
// Running on APP CPU: need to reset PRO CPU and unstall it,
// then reset APP CPU
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_PROCPU_RST_M);
esp_cpu_reset(0);
esp_cpu_unstall(0);
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_APPCPU_RST_M);
esp_cpu_reset(1);
}
while(true) {
;
@ -397,8 +401,10 @@ static void get_chip_info_esp32(esp_chip_info_t* out_info)
if ((reg & EFUSE_RD_CHIP_VER_DIS_BT_M) == 0) {
out_info->features |= CHIP_FEATURE_BT | CHIP_FEATURE_BLE;
}
if (((reg & EFUSE_RD_CHIP_VER_PKG_M) >> EFUSE_RD_CHIP_VER_PKG_S) ==
EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5) {
int package = (reg & EFUSE_RD_CHIP_VER_PKG_M) >> EFUSE_RD_CHIP_VER_PKG_S;
if (package == EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5 ||
package == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD2 ||
package == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4) {
out_info->features |= CHIP_FEATURE_EMB_FLASH;
}
}

View file

@ -69,7 +69,7 @@ TEST_CASE("Scheduler disabled can handle a pending context switch on resume", "[
.alarm_en = 1,
.auto_reload = 1,
.counter_dir = TIMER_COUNT_UP,
.divider = 1,
.divider = 2, //Range is 2 to 65536
.intr_type = TIMER_INTR_LEVEL,
.counter_en = TIMER_PAUSE,
};

View file

@ -140,7 +140,7 @@ static void test_resume_task_from_isr(int target_core)
.alarm_en = 1,
.auto_reload = 0,
.counter_dir = TIMER_COUNT_UP,
.divider = 1,
.divider = 2, //Range is 2 to 65536
.intr_type = TIMER_INTR_LEVEL,
.counter_en = TIMER_PAUSE,
};

View file

@ -1353,8 +1353,6 @@ tcp_kill_state(enum tcp_state state)
struct tcp_pcb *pcb, *inactive;
u32_t inactivity;
LWIP_ASSERT("invalid state", (state == CLOSING) || (state == LAST_ACK));
inactivity = 0;
inactive = NULL;
/* Go through the list of active pcbs and get the oldest pcb that is in state

View file

@ -44,6 +44,12 @@ void IRAM_ATTR esp_cpu_unstall(int cpu_id)
}
}
void IRAM_ATTR esp_cpu_reset(int cpu_id)
{
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG,
cpu_id == 0 ? RTC_CNTL_SW_PROCPU_RST_M : RTC_CNTL_SW_APPCPU_RST_M);
}
bool IRAM_ATTR esp_cpu_in_ocd_debug_mode()
{
#if CONFIG_ESP32_DEBUG_OCDAWARE

View file

@ -85,6 +85,13 @@ void esp_cpu_stall(int cpu_id);
*/
void esp_cpu_unstall(int cpu_id);
/**
* @brief Reset CPU using RTC controller
* @param cpu_id ID of the CPU to reset (0 = PRO, 1 = APP)
*/
void esp_cpu_reset(int cpu_id);
/**
* @brief Returns true if a JTAG debugger is attached to CPU
* OCD (on chip debug) port.

View file

@ -1043,16 +1043,19 @@
#define DPORT_WIFI_CLK_EN_V 0xFFFFFFFF
#define DPORT_WIFI_CLK_EN_S 0
/* Mask for all Wifi clock bits - 0, 1, 2, 3, 6, 7, 8, 9, 10, 15 */
#define DPORT_WIFI_CLK_WIFI_EN 0x000007cf
/* Mask for all Wifi clock bits - 1, 2, 10 */
#define DPORT_WIFI_CLK_WIFI_EN 0x00000406
#define DPORT_WIFI_CLK_WIFI_EN_M ((DPORT_WIFI_CLK_WIFI_EN_V)<<(DPORT_WIFI_CLK_WIFI_EN_S))
#define DPORT_WIFI_CLK_WIFI_EN_V 0x1FF
#define DPORT_WIFI_CLK_WIFI_EN_V 0x406
#define DPORT_WIFI_CLK_WIFI_EN_S 0
/* Mask for all Bluetooth clock bits - 11, 16, 17 */
#define DPORT_WIFI_CLK_BT_EN 0x61
#define DPORT_WIFI_CLK_BT_EN_M ((DPORT_WIFI_CLK_BT_EN_V)<<(DPORT_WIFI_CLK_BT_EN_S))
#define DPORT_WIFI_CLK_BT_EN_V 0x61
#define DPORT_WIFI_CLK_BT_EN_S 11
/* Mask for clock bits used by both WIFI and Bluetooth, bit 0, 3, 6, 7, 8, 9 */
#define DPORT_WIFI_CLK_WIFI_BT_COMMON_M 0x000003c9
/* Remaining single bit clock masks */
#define DPORT_WIFI_CLK_SDIOSLAVE_EN BIT(4)
#define DPORT_WIFI_CLK_UNUSED_BIT5 BIT(5)

View file

@ -100,6 +100,8 @@
#define EFUSE_RD_CHIP_VER_PKG_ESP32D0WDQ6 0
#define EFUSE_RD_CHIP_VER_PKG_ESP32D0WDQ5 1
#define EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5 2
#define EFUSE_RD_CHIP_VER_PKG_ESP32PICOD2 4
#define EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4 5
/* EFUSE_RD_SPI_PAD_CONFIG_HD : RO ;bitpos:[8:4] ;default: 5'b0 ; */
/*description: read for SPI_pad_config_hd*/
#define EFUSE_RD_SPI_PAD_CONFIG_HD 0x0000001F

View file

@ -129,8 +129,7 @@
#define GPIO_STRAP_REG (DR_REG_GPIO_BASE + 0x0038)
/* GPIO_STRAPPING : RO ;bitpos:[15:0] ;default: ; */
/*description: GPIO strapping results: {2'd0 boot_sel_dig[7:1] vsdio_boot_sel
boot_sel_chip[5:0]}. Boot_sel_dig[7:1]: {U0RXD SD_CLK SD_CMD SD_DATA0 SD_DATA1 SD_DATA2 SD_DATA3}. vsdio_boot_sel: MTDI. boot_sel_chip[5:0]: {GPIO0 U0TXD GPIO2 GPIO4 MTDO GPIO5}*/
/*description: {10'b0, MTDI, GPIO0, GPIO2, GPIO4, MTDO, GPIO5} */
#define GPIO_STRAPPING 0x0000FFFF
#define GPIO_STRAPPING_M ((GPIO_STRAPPING_V)<<(GPIO_STRAPPING_S))
#define GPIO_STRAPPING_V 0xFFFF

View file

@ -400,6 +400,15 @@ uint64_t rtc_time_slowclk_to_us(uint64_t rtc_cycles, uint32_t period);
*/
uint64_t rtc_time_get();
/**
* @brief Busy loop until next RTC_SLOW_CLK cycle
*
* This function returns not earlier than the next RTC_SLOW_CLK clock cycle.
* In some cases (e.g. when RTC_SLOW_CLK cycle is very close), it may return
* one RTC_SLOW_CLK cycle later.
*/
void rtc_clk_wait_for_slow_cycle();
/**
* @brief sleep configuration for rtc_sleep_init function
*/
@ -554,6 +563,36 @@ typedef struct {
*/
void rtc_init(rtc_config_t cfg);
/**
* Structure describing vddsdio configuration
*/
typedef struct {
uint32_t force : 1; //!< If 1, use configuration from RTC registers; if 0, use EFUSE/bootstrapping pins.
uint32_t enable : 1; //!< Enable VDDSDIO regulator
uint32_t tieh : 1; //!< Select VDDSDIO voltage: 1 — 1.8V, 0 — 3.3V
uint32_t drefh : 2; //!< Tuning parameter for VDDSDIO regulator
uint32_t drefm : 2; //!< Tuning parameter for VDDSDIO regulator
uint32_t drefl : 2; //!< Tuning parameter for VDDSDIO regulator
} rtc_vddsdio_config_t;
/**
* Get current VDDSDIO configuration
* If VDDSDIO configuration is overridden by RTC, get values from RTC
* Otherwise, if VDDSDIO is configured by EFUSE, get values from EFUSE
* Otherwise, use default values and the level of MTDI bootstrapping pin.
* @return currently used VDDSDIO configuration
*/
rtc_vddsdio_config_t rtc_vddsdio_get_config();
/**
* Set new VDDSDIO configuration using RTC registers.
* If config.force == 1, this overrides configuration done using bootstrapping
* pins and EFUSE.
*
* @param config new VDDSDIO configuration
*/
void rtc_vddsdio_set_config(rtc_vddsdio_config_t config);
#ifdef __cplusplus
}

View file

@ -72,9 +72,6 @@ static const char* TAG = "rtc_clk";
* All values are in microseconds.
* TODO: some of these are excessive, and should be reduced.
*/
#define DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_150K 20
#define DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_32K 160
#define DELAY_CPU_FREQ_SWITCH_TO_PLL 20
#define DELAY_PLL_DBIAS_RAISE 3
#define DELAY_PLL_ENABLE_WITH_150K 80
#define DELAY_PLL_ENABLE_WITH_32K 160
@ -397,9 +394,12 @@ void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq)
REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_XTL);
REG_SET_FIELD(APB_CTRL_SYSCLK_CONF_REG, APB_CTRL_PRE_DIV_CNT, 0);
ets_update_cpu_frequency(xtal_freq);
uint32_t delay_xtal_switch = (rtc_clk_slow_freq_get() == RTC_SLOW_FREQ_RTC) ?
DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_150K : DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_32K;
ets_delay_us(delay_xtal_switch);
/* Frequency switch is synchronized to SLOW_CLK cycle. Wait until the switch
* is complete before disabling the PLL.
*/
rtc_clk_wait_for_slow_cycle();
DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 0);
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG,
RTC_CNTL_BB_I2C_FORCE_PD | RTC_CNTL_BBPLL_FORCE_PD |
@ -443,7 +443,7 @@ void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq)
s_pll_freq = 480;
}
REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_PLL);
ets_delay_us(DELAY_CPU_FREQ_SWITCH_TO_PLL);
rtc_clk_wait_for_slow_cycle();
rtc_clk_apb_freq_update(80 * MHZ);
}
s_cur_freq = cpu_freq;
@ -558,6 +558,13 @@ void rtc_clk_xtal_freq_update(rtc_xtal_freq_t xtal_freq)
static rtc_xtal_freq_t rtc_clk_xtal_freq_estimate()
{
/* Enable 8M/256 clock if needed */
const bool clk_8m_enabled = rtc_clk_8m_enabled();
const bool clk_8md256_enabled = rtc_clk_8md256_enabled();
if (!clk_8md256_enabled) {
rtc_clk_8m_enable(true, true);
}
uint64_t cal_val = rtc_clk_cal_ratio(RTC_CAL_8MD256, XTAL_FREQ_EST_CYCLES);
/* cal_val contains period of 8M/256 clock in XTAL clock cycles
* (shifted by RTC_CLK_CAL_FRACT bits).
@ -581,6 +588,8 @@ static rtc_xtal_freq_t rtc_clk_xtal_freq_estimate()
SOC_LOGW(TAG, "Bogus XTAL frequency: %d MHz", freq_mhz);
return RTC_XTAL_FREQ_AUTO;
}
/* Restore 8M and 8md256 clocks to original state */
rtc_clk_8m_enable(clk_8m_enabled, clk_8md256_enabled);
}
void rtc_clk_apb_freq_update(uint32_t apb_freq)
@ -634,7 +643,6 @@ void rtc_clk_init(rtc_clk_config_t cfg)
CLEAR_PERI_REG_MASK(ANA_CONFIG_REG, I2C_APLL_M | I2C_BBPLL_M);
/* Estimate XTAL frequency */
rtc_xtal_freq_t est_xtal_freq = rtc_clk_xtal_freq_estimate();
rtc_xtal_freq_t xtal_freq = cfg.xtal_freq;
if (xtal_freq == RTC_XTAL_FREQ_AUTO) {
if (clk_val_is_valid(READ_PERI_REG(RTC_XTAL_FREQ_REG))) {
@ -642,7 +650,7 @@ void rtc_clk_init(rtc_clk_config_t cfg)
xtal_freq = rtc_clk_xtal_freq_get();
} else {
/* Not set yet, estimate XTAL frequency based on RTC_FAST_CLK */
xtal_freq = est_xtal_freq;
xtal_freq = rtc_clk_xtal_freq_estimate();
if (xtal_freq == RTC_XTAL_FREQ_AUTO) {
SOC_LOGW(TAG, "Can't estimate XTAL frequency, assuming 26MHz");
xtal_freq = RTC_XTAL_FREQ_26M;
@ -653,8 +661,11 @@ void rtc_clk_init(rtc_clk_config_t cfg)
* frequency is different. If autodetection failed, worst case we get a
* bit of garbage output.
*/
SOC_LOGW(TAG, "Possibly invalid CONFIG_ESP32_XTAL_FREQ setting (%dMHz). Detected %d MHz.",
xtal_freq, est_xtal_freq);
rtc_xtal_freq_t est_xtal_freq = rtc_clk_xtal_freq_estimate();
if (est_xtal_freq != xtal_freq) {
SOC_LOGW(TAG, "Possibly invalid CONFIG_ESP32_XTAL_FREQ setting (%dMHz). Detected %d MHz.",
xtal_freq, est_xtal_freq);
}
}
uart_tx_wait_idle(0);
rtc_clk_xtal_freq_update(xtal_freq);

View file

@ -18,6 +18,8 @@
#include "soc/rtc.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/dport_reg.h"
#include "soc/efuse_reg.h"
#include "soc/gpio_reg.h"
void rtc_init(rtc_config_t cfg)
@ -94,3 +96,51 @@ void rtc_init(rtc_config_t cfg)
CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_ISO_REG, RTC_CNTL_DG_PAD_FORCE_NOISO);
}
}
rtc_vddsdio_config_t rtc_vddsdio_get_config()
{
rtc_vddsdio_config_t result;
uint32_t sdio_conf_reg = REG_READ(RTC_CNTL_SDIO_CONF_REG);
result.drefh = (sdio_conf_reg & RTC_CNTL_DREFH_SDIO_M) >> RTC_CNTL_DREFH_SDIO_S;
result.drefm = (sdio_conf_reg & RTC_CNTL_DREFM_SDIO_M) >> RTC_CNTL_DREFM_SDIO_S;
result.drefl = (sdio_conf_reg & RTC_CNTL_DREFL_SDIO_M) >> RTC_CNTL_DREFL_SDIO_S;
if (sdio_conf_reg & RTC_CNTL_SDIO_FORCE) {
// Get configuration from RTC
result.force = 1;
result.enable = (sdio_conf_reg & RTC_CNTL_XPD_SDIO_REG_M) >> RTC_CNTL_XPD_SDIO_REG_S;
result.tieh = (sdio_conf_reg & RTC_CNTL_SDIO_TIEH_M) >> RTC_CNTL_SDIO_TIEH_S;
return result;
}
uint32_t efuse_reg = REG_READ(EFUSE_BLK0_RDATA4_REG);
if (efuse_reg & EFUSE_RD_SDIO_FORCE) {
// Get configuration from EFUSE
result.force = 0;
result.enable = (efuse_reg & EFUSE_RD_XPD_SDIO_REG_M) >> EFUSE_RD_XPD_SDIO_REG_S;
result.tieh = (efuse_reg & EFUSE_RD_SDIO_TIEH_M) >> EFUSE_RD_SDIO_TIEH_S;
// in this case, DREFH/M/L are also set from EFUSE
result.drefh = (efuse_reg & EFUSE_RD_SDIO_DREFH_M) >> EFUSE_RD_SDIO_DREFH_S;
result.drefm = (efuse_reg & EFUSE_RD_SDIO_DREFM_M) >> EFUSE_RD_SDIO_DREFM_S;
result.drefl = (efuse_reg & EFUSE_RD_SDIO_DREFL_M) >> EFUSE_RD_SDIO_DREFL_S;
return result;
}
// Otherwise, VDD_SDIO is controlled by bootstrapping pin
uint32_t strap_reg = REG_READ(GPIO_STRAP_REG);
result.force = 0;
result.tieh = (strap_reg & BIT(5)) ? 0 : 1;
result.enable = result.tieh == 0; // only power on the regulator if VDD=1.8
return result;
}
void rtc_vddsdio_set_config(rtc_vddsdio_config_t config)
{
uint32_t val = 0;
val |= (config.force << RTC_CNTL_SDIO_FORCE_S);
val |= (config.enable << RTC_CNTL_XPD_SDIO_REG_S);
val |= (config.drefh << RTC_CNTL_DREFH_SDIO_S);
val |= (config.drefm << RTC_CNTL_DREFM_SDIO_S);
val |= (config.drefl << RTC_CNTL_DREFL_SDIO_S);
val |= (config.tieh << RTC_CNTL_SDIO_TIEH_S);
val |= RTC_CNTL_SDIO_PD_EN;
REG_WRITE(RTC_CNTL_SDIO_CONF_REG, val);
}

View file

@ -195,14 +195,12 @@ void rtc_sleep_init(rtc_sleep_config_t cfg)
RTC_CNTL_RFRX_PBUS_PU | RTC_CNTL_TXRF_I2C_PU);
} else {
CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_DG_WRAP_PD_EN);
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_FORCE_NOSLEEP);
REG_SET_FIELD(RTC_CNTL_BIAS_CONF_REG, RTC_CNTL_DBG_ATTEN, 0);
}
if (cfg.vddsdio_pd_en) {
SET_PERI_REG_MASK(RTC_CNTL_SDIO_CONF_REG, RTC_CNTL_SDIO_PD_EN);
} else {
CLEAR_PERI_REG_MASK(RTC_CNTL_SDIO_CONF_REG, RTC_CNTL_SDIO_PD_EN);
}
/* enable VDDSDIO control by state machine */
REG_CLR_BIT(RTC_CNTL_SDIO_CONF_REG, RTC_CNTL_SDIO_FORCE);
REG_SET_FIELD(RTC_CNTL_SDIO_CONF_REG, RTC_CNTL_SDIO_PD_EN, cfg.vddsdio_pd_en);
REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DBIAS_SLP, cfg.rtc_dbias_slp);
REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DBIAS_WAK, cfg.rtc_dbias_wak);

View file

@ -135,3 +135,20 @@ uint64_t rtc_time_get()
t |= ((uint64_t) READ_PERI_REG(RTC_CNTL_TIME1_REG)) << 32;
return t;
}
void rtc_clk_wait_for_slow_cycle()
{
REG_CLR_BIT(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_START_CYCLING | TIMG_RTC_CALI_START);
REG_CLR_BIT(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_RDY);
REG_SET_FIELD(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_CLK_SEL, RTC_CAL_RTC_MUX);
/* Request to run calibration for 0 slow clock cycles.
* RDY bit will be set on the nearest slow clock cycle.
*/
REG_SET_FIELD(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_MAX, 0);
REG_SET_BIT(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_START);
ets_delay_us(1); /* RDY needs some time to go low */
while (!GET_PERI_REG_MASK(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_RDY)) {
ets_delay_us(1);
}
}

View file

@ -1,15 +1,17 @@
#include <stdio.h>
#include "unity.h"
#include "rom/ets_sys.h"
#include "rom/uart.h"
#include "soc/rtc.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/rtc_io_reg.h"
#include "soc/sens_reg.h"
#include "soc/io_mux_reg.h"
#include "driver/rtc_io.h"
#include "test_utils.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#define CALIBRATE_ONE(cali_clk) calibrate_one(cali_clk, #cali_clk)
@ -89,3 +91,35 @@ TEST_CASE("Output 8M XTAL clock to GPIO25", "[rtc_clk][ignore]")
SET_PERI_REG_MASK(RTC_IO_RTC_DEBUG_SEL_REG, RTC_IO_DEBUG_12M_NO_GATING);
pull_out_clk(RTC_IO_DEBUG_SEL0_8M);
}
static void test_clock_switching(void (*switch_func)(rtc_cpu_freq_t))
{
uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM);
const int test_duration_sec = 10;
ref_clock_init();
uint64_t t_start = ref_clock_get();
rtc_cpu_freq_t cur_freq = rtc_clk_cpu_freq_get();
int count = 0;
while (ref_clock_get() - t_start < test_duration_sec * 1000000) {
switch_func(RTC_CPU_FREQ_XTAL);
switch_func(cur_freq);
++count;
}
uint64_t t_end = ref_clock_get();
printf("Switch count: %d. Average time to switch PLL -> XTAL -> PLL: %d us\n", count, (int) ((t_end - t_start) / count));
ref_clock_deinit();
}
TEST_CASE("Test switching between PLL and XTAL", "[rtc_clk]")
{
test_clock_switching(rtc_clk_cpu_freq_set);
}
TEST_CASE("Test fast switching between PLL and XTAL", "[rtc_clk]")
{
test_clock_switching(rtc_clk_cpu_freq_set_fast);
}

View file

@ -21,6 +21,31 @@ config SPI_FLASH_ROM_DRIVER_PATCH
This option is needed to write to flash on ESP32-D2WD, and any configuration
where external SPI flash is connected to non-default pins.
choice SPI_FLASH_WRITING_DANGEROUS_REGIONS
bool "Writing to dangerous flash regions"
default SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS
help
SPI flash APIs can optionally abort or return a failure code
if erasing or writing addresses that fall at the beginning
of flash (covering the bootloader and partition table) or that
overlap the app partition that contains the running app.
It is not recommended to ever write to these regions from an IDF app,
and this check prevents logic errors or corrupted firmware memory from
damaging these regions.
Note that this feature *does not* check calls to the esp_rom_xxx SPI flash
ROM functions. These functions should not be called directly from IDF
applications.
config SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS
bool "Aborts"
config SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS
bool "Fails"
config SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED
bool "Allowed"
endchoice
endmenu

View file

@ -31,6 +31,8 @@
#include "esp_spi_flash.h"
#include "esp_log.h"
#include "esp_clk.h"
#include "esp_flash_partitions.h"
#include "esp_ota_ops.h"
#include "cache_utils.h"
/* bytes erased by SPIEraseBlock() ROM function */
@ -83,6 +85,45 @@ const DRAM_ATTR spi_flash_guard_funcs_t g_flash_guard_no_os_ops = {
static const spi_flash_guard_funcs_t *s_flash_guard_ops;
#ifdef CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS
#define UNSAFE_WRITE_ADDRESS abort()
#else
#define UNSAFE_WRITE_ADDRESS return false
#endif
/* CHECK_WRITE_ADDRESS macro to fail writes which land in the
bootloader, partition table, or running application region.
*/
#if CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED
#define CHECK_WRITE_ADDRESS(ADDR, SIZE)
#else /* FAILS or ABORTS */
#define CHECK_WRITE_ADDRESS(ADDR, SIZE) do { \
if (!is_safe_write_address(ADDR, SIZE)) { \
return ESP_ERR_INVALID_ARG; \
} \
} while(0)
#endif // CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED
static __attribute__((unused)) bool is_safe_write_address(size_t addr, size_t size)
{
bool result = true;
if (addr <= ESP_PARTITION_TABLE_OFFSET + ESP_PARTITION_TABLE_MAX_LEN) {
UNSAFE_WRITE_ADDRESS;
}
const esp_partition_t *p = esp_ota_get_running_partition();
if (addr >= p->address && addr < p->address + p->size) {
UNSAFE_WRITE_ADDRESS;
}
if (addr < p->address && addr + size > p->address) {
UNSAFE_WRITE_ADDRESS;
}
return result;
}
void spi_flash_init()
{
spi_flash_init_lock();
@ -146,11 +187,13 @@ static esp_rom_spiflash_result_t IRAM_ATTR spi_flash_unlock()
esp_err_t IRAM_ATTR spi_flash_erase_sector(size_t sec)
{
CHECK_WRITE_ADDRESS(sec * SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE);
return spi_flash_erase_range(sec * SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE);
}
esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size)
{
CHECK_WRITE_ADDRESS(start_addr, size);
if (start_addr % SPI_FLASH_SEC_SIZE != 0) {
return ESP_ERR_INVALID_ARG;
}
@ -187,6 +230,7 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size)
esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size)
{
CHECK_WRITE_ADDRESS(dst, size);
// Out of bound writes are checked in ROM code, but we can give better
// error code here
if (dst + size > g_rom_flashchip.chip_size) {
@ -281,6 +325,7 @@ out:
esp_err_t IRAM_ATTR spi_flash_write_encrypted(size_t dest_addr, const void *src, size_t size)
{
CHECK_WRITE_ADDRESS(dest_addr, size);
const uint8_t *ssrc = (const uint8_t *)src;
if ((dest_addr % 16) != 0) {
return ESP_ERR_INVALID_ARG;

View file

@ -0,0 +1,33 @@
#include <string.h>
#include "unity.h"
#include "esp_spi_flash.h"
#include "esp_ota_ops.h"
#if CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS || CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS
static const char *data = "blah blah blah";
#if CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS
#define TEST_TAGS "[spi_flash]"
#else // ABORTS
#define TEST_TAGS "[spi_flash][ignore]"
#endif
TEST_CASE("can't overwrite bootloader", TEST_TAGS)
{
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, spi_flash_write(0x1000, data, strlen(data)));
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, spi_flash_write(0x0FF8, data, strlen(data)));
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, spi_flash_write(0x1400, data, strlen(data)));
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, spi_flash_erase_range(0x8000, 0x2000));
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, spi_flash_erase_range(0x7000, 0x2000));
}
TEST_CASE("can't overwrite current running app", TEST_TAGS)
{
const esp_partition_t *p = esp_ota_get_running_partition();
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, spi_flash_write(p->address + 1024, data, strlen(data)));
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, spi_flash_erase_range(p->address + 4096, 8192));
}
#endif // FAILS || ABORTS

View file

@ -592,14 +592,7 @@ To override the name of this file, set the ``SDKCONFIG_DEFAULTS`` environment va
Save flash arguments
--------------------
There're some scenarios that we want to flash the target board without IDF. For this case we want to save the built binaries, esptool.py and esptool write_flash arguments. It's simple to write a script to save binaries and esptool.py. For flash arguments, we can add the following code to application project makefile::
print_flash_cmd:
echo $(ESPTOOL_WRITE_FLASH_OPTIONS) $(ESPTOOL_ALL_FLASH_ARGS) | sed -e 's:'$(PWD)/build/'::g'
the original ESPTOOL_ALL_FLASH_ARGS are absolute file name. Usually we want to save relative file name so we can move the bin folder to somewhere else. For this case we can use ``sed`` to convert to relative file name, like what we did in the example above.
When running ``make print_flash_cmd``, it will print the flash arguments::
There're some scenarios that we want to flash the target board without IDF. For this case we want to save the built binaries, esptool.py and esptool write_flash arguments. It's simple to write a script to save binaries and esptool.py. We can use command ``make print_flash_cmd``, it will print the flash arguments::
--flash_mode dio --flash_freq 40m --flash_size detect 0x1000 bootloader/bootloader.bin 0x10000 example_app.bin 0x8000 partition_table_unit_test_app.bin

View file

@ -7,7 +7,7 @@ All versions of ESP32 WROVER KIT boards have JTAG functionality build in. Puttin
Configure Hardware
^^^^^^^^^^^^^^^^^^
1. Enable on-board JTAG functionality by setting JP8 according to :doc:`../../get-started/get-started-wrover-kit`, section :ref:`esp-wrover-kit-setup-options`.
1. Enable on-board JTAG functionality by setting JP8 according to :doc:`../../get-started/get-started-wrover-kit`, section :ref:`get-started-esp-wrover-kit-setup-options`.
2. Verify if ESP32 pins used for JTAG communication are not connected to some other h/w that may disturb JTAG operation:

View file

@ -138,7 +138,9 @@ Application Examples
Configure UART settings and install UART driver to read/write using UART1 interface: :example:`peripherals/uart_echo`.
Demonstration how report report various communication events and how to use patern detection interrupts: :example:`peripherals/uart_events`.
Demonstration of how to report various communication events and how to use patern detection interrupts: :example:`peripherals/uart_events`.
Transmitting and receiveing with the same UART in two separate FreeRTOS tasks: :example:`peripherals/uart_async_rxtxtasks`.
API Reference

View file

@ -7,7 +7,7 @@ This user guide shows how to get started with ESP32-DevKitC development board.
What You Need
-------------
* 1 × ESP32-DevKitC board
* 1 × :ref:`ESP32-DevKitC board <get-started-esp32-devkitc-board-front>`
* 1 × USB A / micro USB B cable
* 1 × PC loaded with Windows, Linux or Mac OS
@ -34,6 +34,8 @@ USB
I/O
Most of the pins on the ESP-WROOM-32 are broken out to the pin headers on the board. Users can program ESP32 to enable multiple functions such as PWM,ADC, DAC, I2C, I2S, SPI, etc.
.. _get-started-esp32-devkitc-board-front:
.. figure:: ../_static/esp32-devkitc-functional-overview.png
:align: center
:alt: ESP32-DevKitC board layout
@ -42,6 +44,20 @@ I/O
ESP32-DevKitC board layout
Power Supply Options
--------------------
There following options are available to provide power supply to the ESP32-PICO-KIT V4:
1. Micro USB port, this is default power supply connection
2. 5V / GND header pins
3. 3V3 / GND header pins
.. warning::
Above options are mutually exclusive, i.e. the power supply may be provided using only one of the above options. Attempt to power the board using more than one connection at a time may damage the board and/or the power supply source.
Start Application Development
------------------------------

View file

@ -7,7 +7,7 @@ This user guide shows how to get started with the ESP32-PICO-KIT V4 mini develop
What You Need
-------------
* 1 × ESP32-PICO-KIT V4 mini development board
* 1 × :ref:`ESP32-PICO-KIT V4 mini development board <get-started-pico-kit-v4-board-front>`
* 1 × USB A / Micro USB B cable
* 1 × PC loaded with Windows, Linux or Mac OS
@ -40,6 +40,8 @@ EN Button
BOOT Button
Holding down the Boot button and pressing the EN button initiates the firmware download mode. Then user can download firmware through the serial port.
.. _get-started-pico-kit-v4-board-front:
.. figure:: ../_static/esp32-pico-kit-v4-layout.jpg
:align: center
:alt: ESP32-PICO-KIT V4 board layout
@ -48,6 +50,20 @@ BOOT Button
ESP32-PICO-KIT V4 board layout
Power Supply Options
--------------------
There following options are available to provide power supply to the ESP32-PICO-KIT V4:
1. Micro USB port, this is default power supply connection
2. 5V / GND header pins
3. 3V3 / GND header pins
.. warning::
Above options are mutually exclusive, i.e. the power supply may be provided using only one of the above options. Attempt to power the board using more than one connection at a time may damage the board and/or the power supply source.
Start Application Development
------------------------------

View file

@ -3,13 +3,13 @@ ESP-WROVER-KIT V3 Getting Started Guide
This user guide shows how to get started with ESP-WROVER-KIT V3 development board including description of its functionality and configuration options. For description of other versions of the ESP-WROVER-KIT check :doc:`../hw-reference/index`.
If you like to start using this board right now, go directly to section :ref:`esp-wrover-kit-start-development`.
If you like to start using this board right now, go directly to section :ref:`get-started-esp-wrover-kit-start-development`.
What You Need
-------------
* 1 × ESP-WROVER-KIT V3 board
* 1 × :ref:`ESP-WROVER-KIT V3 board <get-started-esp-wrover-kit-board-front>`
* 1 x Micro USB 2.0 Cable, Type A to Micro B
* 1 × PC loaded with Windows, Linux or Mac OS
@ -70,7 +70,7 @@ Boot
USB
USB interface. It functions as the power supply for the board and the communication interface between PC and ESP32 module.
Power Select
Power supply selection interface: the ESP-WROVER-KIT can be powered through the USB interface or the 5V Input interface. The user can select the power supply with a jumper. More details can be found in section :ref:`esp-wrover-kit-setup-options`, jumper header JP7.
Power supply selection interface: the ESP-WROVER-KIT can be powered through the USB interface or the 5V Input interface. The user can select the power supply with a jumper. More details can be found in section :ref:`get-started-esp-wrover-kit-setup-options`, jumper header JP7.
Power Key
Power on/off button: toggling to the right powers the board on; toggling to the left powers the board off.
5V Input
@ -82,11 +82,13 @@ Camera
RGB
Red, green and blue (RGB) light emitting diodes (LEDs), which may be controlled by pulse width modulation (PWM).
I/O
All the pins on the ESP32 module are led out to the pin headers on the ESPWROVER-KIT. Users can program ESP32 to enable multiple functions such as PWM, ADC, DAC, I2C, I2S, SPI, etc.
All the pins on the ESP32 module are led out to the pin headers on the ESP-WROVER-KIT. Users can program ESP32 to enable multiple functions such as PWM, ADC, DAC, I2C, I2S, SPI, etc.
Micro SD Card
Micro SD card slot for data storage.
LCD
ESP-WROVER-KIT supports mounting and interfacing a 3.2” SPI (standard 4-wire Serial Peripheral Interface) LCD, as shown on figure :ref:`esp-wrover-kit-board-back`.
ESP-WROVER-KIT supports mounting and interfacing a 3.2” SPI (standard 4-wire Serial Peripheral Interface) LCD, as shown on figure :ref:`get-started-esp-wrover-kit-board-back`.
.. _get-started-esp-wrover-kit-board-front:
.. figure:: ../_static/esp32-wrover-kit-layout-front.jpg
:align: center
@ -95,7 +97,7 @@ LCD
ESP-WROVER-KIT board layout - front
.. _esp-wrover-kit-board-back:
.. _get-started-esp-wrover-kit-board-back:
.. figure:: ../_static/esp32-wrover-kit-layout-back.jpg
:align: center
@ -105,7 +107,7 @@ LCD
ESP-WROVER-KIT board layout - back
.. _esp-wrover-kit-setup-options:
.. _get-started-esp-wrover-kit-setup-options:
Setup Options
^^^^^^^^^^^^^
@ -116,7 +118,7 @@ There are five jumper headers available to set up the board functionality. Typic
| Header | Jumper Setting | Description of Functionality |
+--------+------------------+--------------------------------------------------+
| JP7 | |jp7-ext_5v| | Power ESP-WROVER-KIT board from an external |
| | | power supply |
| | | | power supply |
+--------+------------------+--------------------------------------------------+
| JP7 | |jp7-usb_5v| | Power ESP-WROVER-KIT board from an USB port |
+--------+------------------+--------------------------------------------------+
@ -135,60 +137,116 @@ Allocation of ESP32 Pins
Several pins / terminals of ESP32 module are allocated to the on board hardware. Some of them, like GPIO0 or GPIO2, have multiple functions. If certain hardware is not installed, e.g. nothing is plugged in to the Camera / JP4 header, then selected GPIOs may be used for other purposes.
Main I/O Connector / JP1
""""""""""""""""""""""""
The JP1 connector is shown in two columns in the middle under "I/O" headers. The two columns "Shared With" outside, describe where else on the board certain GPIO is used.
+----------------------+------+------+----------------------+
| Shared With | I/O | I/O | Shared With |
+======================+======+======+======================+
| | 3.3V | GND | |
+----------------------+------+------+----------------------+
| NC/XTAL | IO32 | IO33 | NC/XTAL |
+----------------------+------+------+----------------------+
| JTAG, MicroSD | IO12 | IO13 | JTAG, MicroSD |
+----------------------+------+------+----------------------+
| JTAG, MicroSD | IO14 | IO27 | Camera |
+----------------------+------+------+----------------------+
| Camera | IO26 | IO25 | Camera, LCD |
+----------------------+------+------+----------------------+
| Camera | IO35 | IO34 | Camera |
+----------------------+------+------+----------------------+
| Camera | IO39 | IO36 | Camera |
+----------------------+------+------+----------------------+
| JTAG | EN | IO23 | Camera, LCD |
+----------------------+------+------+----------------------+
| Camera, LCD | IO22 | IO21 | Camera, LCD, MicroSD |
+----------------------+------+------+----------------------+
| Camera, LCD | IO19 | IO18 | Camera, LCD |
+----------------------+------+------+----------------------+
| Camera, LCD | IO5 | IO17 | PSRAM |
+----------------------+------+------+----------------------+
| PSRAM | IO16 | IO4 | LED, Camera, MicroSD |
+----------------------+------+------+----------------------+
| LED, Boot | IO0 | IO2 | LED, Camera, MicroSD |
+----------------------+------+------+----------------------+
| JTAG, MicroSD | IO15 | 5V | |
+----------------------+------+------+----------------------+
Legend:
* NC/XTAL - :ref:`32.768 kHz Oscillator <get-started-esp-wrover-kit-xtal>`
* JTAG - :ref:`JTAG / JP8 <get-started-esp-wrover-jtag-header>`
* Boot - Boot button / SW2
* Camera - :ref:`Camera / JP4 <get-started-esp-wrover-camera-header>`
* LED - :ref:`RGB LED <get-started-esp-wrover-rgb-led-connections>`
* MicroSD - :ref:`MicroSD Card / J4 <get-started-esp-wrover-microsd-card-slot>`
* LCD - :ref:`LCD / U5 <get-started-esp-wrover-lcd-connector>`
* PSRAM - ESP32-WROVER's PSRAM, if ESP32-WROVER is installed
.. _get-started-esp-wrover-kit-xtal:
32.768 kHz Oscillator
"""""""""""""""""""""
+---+---------------+
| | ESP32 Pin |
+===+===============+
| 1 | GPIO32 |
+---+---------------+
| 2 | GPIO33 |
+---+---------------+
+---+-----------+
| | ESP32 Pin |
+===+===========+
| 1 | GPIO32 |
+---+-----------+
| 2 | GPIO33 |
+---+-----------+
.. note::
As GPIO32 and GPIO33 are connected to the oscillator, to maintain signal integrity, they are not connected to JP1 I/O expansion connector. This allocation may be changed from oscillator to JP1 by desoldering 0R resistors from positions R11 / R23 and installing them in positions R12 / R24.
.. _get-started-esp-wrover-spi-flash-header:
SPI Flash / JP13
""""""""""""""""
+---+---------------+
| | ESP32 Pin |
+===+===============+
| 1 | CLK / GPIO6 |
+---+---------------+
| 2 | SD0 / GPIO7 |
+---+---------------+
| 3 | SD1 / GPIO8 |
+---+---------------+
| 4 | SD2 / GPIO9 |
+---+---------------+
| 5 | SD3 / GPIO10 |
+---+---------------+
| 6 | CMD / GPIO11 |
+---+---------------+
+---+--------------+
| | ESP32 Pin |
+===+==============+
| 1 | CLK / GPIO6 |
+---+--------------+
| 2 | SD0 / GPIO7 |
+---+--------------+
| 3 | SD1 / GPIO8 |
+---+--------------+
| 4 | SD2 / GPIO9 |
+---+--------------+
| 5 | SD3 / GPIO10 |
+---+--------------+
| 6 | CMD / GPIO11 |
+---+--------------+
.. _get-started-esp-wrover-jtag-header:
JTAG / JP8
""""""""""
+---+---------------+----------------+
| | ESP32 Pin | JTAG Signal |
+===+===============+================+
| 1 | CHIP_PU | TRST_N |
+---+---------------+----------------+
| 2 | MTDO / GPIO15 | TDO |
+---+---------------+----------------+
| 3 | MTDI / GPIO12 | TDI |
+---+---------------+----------------+
| 4 | MTCK / GPIO13 | TCK |
+---+---------------+----------------+
| 5 | MTMS / GPIO14 | TMS |
+---+---------------+----------------+
+---+---------------+-------------+
| | ESP32 Pin | JTAG Signal |
+===+===============+=============+
| 1 | EN | TRST_N |
+---+---------------+-------------+
| 2 | MTDO / GPIO15 | TDO |
+---+---------------+-------------+
| 3 | MTDI / GPIO12 | TDI |
+---+---------------+-------------+
| 4 | MTCK / GPIO13 | TCK |
+---+---------------+-------------+
| 5 | MTMS / GPIO14 | TMS |
+---+---------------+-------------+
.. _get-started-esp-wrover-camera-header:
Camera / JP4
""""""""""""
@ -227,20 +285,24 @@ Camera / JP4
+----+--------------+----------------------+
.. _get-started-esp-wrover-rgb-led-connections:
RGB LED
"""""""
+---+---------------+----------------+
| | ESP32 Pin | RGB LED |
+===+===============+================+
| 1 | GPIO0 | Red |
+---+---------------+----------------+
| 2 | GPIO2 | Blue |
+---+---------------+----------------+
| 3 | GPIO4 | Green |
+---+---------------+----------------+
+---+-----------+---------+
| | ESP32 Pin | RGB LED |
+===+===========+=========+
| 1 | GPIO0 | Red |
+---+-----------+---------+
| 2 | GPIO2 | Blue |
+---+-----------+---------+
| 3 | GPIO4 | Green |
+---+-----------+---------+
.. _get-started-esp-wrover-microsd-card-slot:
MicroSD Card / J4
"""""""""""""""""
@ -263,30 +325,31 @@ MicroSD Card / J4
+---+---------------+----------------+
.. _get-started-esp-wrover-lcd-connector:
LCD / U5
""""""""
+---+---------------+----------------+
| | ESP32 Pin | LCD Signal |
+===+===============+================+
| 1 | GPIO18 | RESET |
+---+---------------+----------------+
| 2 | GPIO19 | SCL |
+---+---------------+----------------+
| 3 | GPIO21 | D/C |
+---+---------------+----------------+
| 4 | GPIO22 | CS |
+---+---------------+----------------+
| 5 | GPIO23 | SDA |
+---+---------------+----------------+
| 6 | GPIO25 | SDO |
+---+---------------+----------------+
| 7 | GPIO5 | Backlight |
+---+---------------+----------------+
+---+-----------+------------+
| | ESP32 Pin | LCD Signal |
+===+===========+============+
| 1 | GPIO18 | RESET |
+---+-----------+------------+
| 2 | GPIO19 | SCL |
+---+-----------+------------+
| 3 | GPIO21 | D/C |
+---+-----------+------------+
| 4 | GPIO22 | CS |
+---+-----------+------------+
| 5 | GPIO23 | SDA |
+---+-----------+------------+
| 6 | GPIO25 | SDO |
+---+-----------+------------+
| 7 | GPIO5 | Backlight |
+---+-----------+------------+
.. _esp-wrover-kit-start-development:
.. _get-started-esp-wrover-kit-start-development:
Start Application Development
-----------------------------

View file

@ -11,3 +11,4 @@ ESP32 Hardware Reference
Silicon Errata (PDF) <https://espressif.com/sites/default/files/documentation/eco_and_workarounds_for_bugs_in_esp32_en.pdf>
Modules and Boards <modules-and-boards>
Previous Versions of Modules and Boards <modules-and-boards-previous>
Espressif Products Ordering Information (PDF) <http://www.espressif.com/sites/default/files/documentation/espressif_products_ordering_information_en.pdf>

View file

@ -89,7 +89,7 @@ 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 press buttons to reset the board and put it in upload mode.
.. figure:: https://dl.espressif.com/dl/schematics/pictures/esp32-core-board-v2.jpg
.. figure:: https://dl.espressif.com/dl/schematics/pictures/esp32-core-board-v2.png
:align: center
:alt: ESP32 Core Board V2 / ESP32 DevKitC board
:width: 50%

View file

@ -1,6 +1,6 @@
# This is a list of python packages used to generate documentation. This file is used with pip:
# pip install -r requirements.txt
#
sphinx==1.6.3
sphinx==1.6.5
sphinx-rtd-theme
breathe==4.7.1
breathe==4.7.3

View file

@ -8,7 +8,7 @@
#
#
# Sphinx known issue https://github.com/sphinx-doc/sphinx/issues/2683\
# Sphinx known issue https://github.com/sphinx-doc/sphinx/issues/2683
#
_build/inc/esp_a2dp_api.inc:line: WARNING: Invalid definition: Expected identifier in nested name. [error at 21]
union esp_a2d_mcc_t::@1 esp_a2d_mcc_t::cie
@ -16,10 +16,3 @@ _build/inc/esp_a2dp_api.inc:line: WARNING: Invalid definition: Expected identifi
_build/inc/esp_bt_defs.inc:line: WARNING: Invalid definition: Expected identifier in nested name. [error at 21]
union esp_bt_uuid_t::@0 esp_bt_uuid_t::uuid
---------------------^
#
# Sphinx known issue https://github.com/sphinx-doc/sphinx/issues/4041
#
${IDF_PATH}/docs/api-reference/storage/sdmmc.rst:line: WARNING: cpp:typeOrConcept targets a member (sdmmc_host_t::slot).
${IDF_PATH}/docs/api-reference/storage/sdmmc.rst:line: WARNING: cpp:typeOrConcept targets a member (sdmmc_host_t::slot).
${IDF_PATH}/docs/api-reference/storage/sdmmc.rst:line: WARNING: cpp:typeOrConcept targets a member (sdmmc_host_t::slot).

View file

@ -332,7 +332,7 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
esp_ble_gattc_search_service(spp_gattc_if, spp_conn_id, &spp_service_uuid);
break;
case ESP_GATTC_DISCONNECT_EVT:
ESP_LOGI(GATTC_TAG, "disconnect , status = %d", p_data->disconnect.status);
ESP_LOGI(GATTC_TAG, "disconnect");
free_gattc_srv_db();
esp_ble_gap_start_scanning(SCAN_ALL_THE_TIME);
break;
@ -639,4 +639,3 @@ void app_main()
ble_client_appRegister();
spp_uart_init();
}

View file

@ -184,11 +184,11 @@ static void example_event_callback(esp_blufi_cb_event_t event, esp_blufi_cb_para
server_if=param->connect.server_if;
conn_id=param->connect.conn_id;
esp_ble_gap_stop_advertising();
blufi_security_deinit();
blufi_security_init();
break;
case ESP_BLUFI_EVENT_BLE_DISCONNECT:
BLUFI_INFO("BLUFI ble disconnect\n");
blufi_security_deinit();
esp_ble_gap_start_advertising(&example_adv_params);
break;
case ESP_BLUFI_EVENT_SET_WIFI_OPMODE:

View file

@ -90,22 +90,28 @@ void blufi_dh_negotiate_data_handler(uint8_t *data, int len, uint8_t **output_da
blufi_sec->dh_param_len = ((data[1]<<8)|data[2]);
if (blufi_sec->dh_param) {
free(blufi_sec->dh_param);
blufi_sec->dh_param = NULL;
}
blufi_sec->dh_param = (uint8_t *)malloc(blufi_sec->dh_param_len);
if (blufi_sec->dh_param == NULL) {
BLUFI_ERROR("%s, malloc failed\n", __func__);
return;
}
break;
case SEC_TYPE_DH_PARAM_DATA:
case SEC_TYPE_DH_PARAM_DATA:{
if (blufi_sec->dh_param == NULL) {
BLUFI_ERROR("%s, blufi_sec->dh_param == NULL\n", __func__);
return;
}
uint8_t *param = blufi_sec->dh_param;
memcpy(blufi_sec->dh_param, &data[1], blufi_sec->dh_param_len);
ret = mbedtls_dhm_read_params(&blufi_sec->dhm, &blufi_sec->dh_param, &blufi_sec->dh_param[blufi_sec->dh_param_len]);
ret = mbedtls_dhm_read_params(&blufi_sec->dhm, &param, &param[blufi_sec->dh_param_len]);
if (ret) {
BLUFI_ERROR("%s read param failed %d\n", __func__, ret);
return;
}
free(blufi_sec->dh_param);
blufi_sec->dh_param = NULL;
ret = mbedtls_dhm_make_public(&blufi_sec->dhm, (int) mbedtls_mpi_size( &blufi_sec->dhm.P ), blufi_sec->self_public_key, blufi_sec->dhm.len, myrand, NULL);
if (ret) {
BLUFI_ERROR("%s make public failed %d\n", __func__, ret);
@ -126,6 +132,8 @@ void blufi_dh_negotiate_data_handler(uint8_t *data, int len, uint8_t **output_da
*output_data = &blufi_sec->self_public_key[0];
*output_len = blufi_sec->dhm.len;
*need_free = false;
}
break;
case SEC_TYPE_DH_P:
break;
@ -194,6 +202,13 @@ esp_err_t blufi_security_init(void)
void blufi_security_deinit(void)
{
if (blufi_sec == NULL) {
return;
}
if (blufi_sec->dh_param){
free(blufi_sec->dh_param);
blufi_sec->dh_param = NULL;
}
mbedtls_dhm_free(&blufi_sec->dhm);
mbedtls_aes_free(&blufi_sec->aes);

View file

@ -112,8 +112,7 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
}
break;
case ESP_GATTC_CONNECT_EVT:{
//p_data->connect.status always be ESP_GATT_OK
ESP_LOGI(GATTC_TAG, "ESP_GATTC_CONNECT_EVT conn_id %d, if %d, status %d", p_data->connect.conn_id, gattc_if, p_data->connect.status);
ESP_LOGI(GATTC_TAG, "ESP_GATTC_CONNECT_EVT conn_id %d, if %d", p_data->connect.conn_id, gattc_if);
gl_profile_tab[PROFILE_A_APP_ID].conn_id = p_data->connect.conn_id;
memcpy(gl_profile_tab[PROFILE_A_APP_ID].remote_bda, p_data->connect.remote_bda, sizeof(esp_bd_addr_t));
ESP_LOGI(GATTC_TAG, "REMOTE BDA:");
@ -170,7 +169,7 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
}
if (count > 0){
char_elem_result = (esp_gattc_char_elem_t *)malloc(sizeof(char_elem_result) * count);
char_elem_result = (esp_gattc_char_elem_t *)malloc(sizeof(esp_gattc_char_elem_t) * count);
if (!char_elem_result){
ESP_LOGE(GATTC_TAG, "gattc no mem");
}else{
@ -216,7 +215,7 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_attr_count error");
}
if (count > 0){
descr_elem_result = malloc(sizeof(descr_elem_result) * count);
descr_elem_result = malloc(sizeof(esp_gattc_descr_elem_t) * count);
if (!descr_elem_result){
ESP_LOGE(GATTC_TAG, "malloc error, gattc no mem");
}else{
@ -257,7 +256,11 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
break;
}
case ESP_GATTC_NOTIFY_EVT:
ESP_LOGI(GATTC_TAG, "ESP_GATTC_NOTIFY_EVT, receive notify value:");
if (p_data->notify.is_notify){
ESP_LOGI(GATTC_TAG, "ESP_GATTC_NOTIFY_EVT, receive notify value:");
}else{
ESP_LOGI(GATTC_TAG, "ESP_GATTC_NOTIFY_EVT, receive indicate value:");
}
esp_log_buffer_hex(GATTC_TAG, p_data->notify.value, p_data->notify.value_len);
break;
case ESP_GATTC_WRITE_DESCR_EVT:
@ -296,7 +299,7 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
case ESP_GATTC_DISCONNECT_EVT:
connect = false;
get_server = false;
ESP_LOGI(GATTC_TAG, "ESP_GATTC_DISCONNECT_EVT, status = %d", p_data->disconnect.status);
ESP_LOGI(GATTC_TAG, "ESP_GATTC_DISCONNECT_EVT, reason = %d", p_data->disconnect.reason);
break;
default:
break;

View file

@ -191,7 +191,7 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_attr_count error, %d", __LINE__);
}
if (count > 0){
char_elem_result = (esp_gattc_char_elem_t *)malloc(sizeof(char_elem_result) * count);
char_elem_result = (esp_gattc_char_elem_t *)malloc(sizeof(esp_gattc_char_elem_t) * count);
if (!char_elem_result){
ESP_LOGE(GATTC_TAG, "gattc no mem");
}else{
@ -245,7 +245,7 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_attr_count error, %d", __LINE__);
}
if (count > 0){
descr_elem_result = malloc(sizeof(descr_elem_result) * count);
descr_elem_result = malloc(sizeof(esp_gattc_descr_elem_t) * count);
if (!descr_elem_result){
ESP_LOGE(GATTC_TAG, "malloc error, gattc no mem");
}else{
@ -306,7 +306,7 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
ESP_LOGI(GATTC_TAG, "Write char success ");
break;
case ESP_GATTC_DISCONNECT_EVT:
ESP_LOGI(GATTC_TAG, "ESP_GATTC_DISCONNECT_EVT, status = %d", p_data->disconnect.status);
ESP_LOGI(GATTC_TAG, "ESP_GATTC_DISCONNECT_EVT, reason = %d", p_data->disconnect.reason);
connect = false;
get_service = false;
break;

View file

@ -313,7 +313,10 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i
gl_profile_tab[PROFILE_A_APP_ID].service_id.id.uuid.len = ESP_UUID_LEN_16;
gl_profile_tab[PROFILE_A_APP_ID].service_id.id.uuid.uuid.uuid16 = GATTS_SERVICE_UUID_TEST_A;
esp_ble_gap_set_device_name(TEST_DEVICE_NAME);
esp_err_t set_dev_name_ret = esp_ble_gap_set_device_name(TEST_DEVICE_NAME);
if (set_dev_name_ret){
ESP_LOGE(GATTS_TAG, "set device name failed, error code = %x", set_dev_name_ret);
}
#ifdef CONFIG_SET_RAW_ADV_DATA
esp_err_t raw_adv_ret = esp_ble_gap_config_adv_data_raw(raw_adv_data, sizeof(raw_adv_data));
if (raw_adv_ret){
@ -408,7 +411,6 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i
case ESP_GATTS_MTU_EVT:
ESP_LOGI(GATTS_TAG, "ESP_GATTS_MTU_EVT, MTU %d", param->mtu.mtu);
break;
case ESP_GATTS_CONF_EVT:
case ESP_GATTS_UNREG_EVT:
break;
case ESP_GATTS_CREATE_EVT:
@ -475,11 +477,10 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i
conn_params.max_int = 0x20; // max_int = 0x20*1.25ms = 40ms
conn_params.min_int = 0x10; // min_int = 0x10*1.25ms = 20ms
conn_params.timeout = 400; // timeout = 400*10ms = 4000ms
ESP_LOGI(GATTS_TAG, "ESP_GATTS_CONNECT_EVT, conn_id %d, remote %02x:%02x:%02x:%02x:%02x:%02x:, is_conn %d",
ESP_LOGI(GATTS_TAG, "ESP_GATTS_CONNECT_EVT, conn_id %d, remote %02x:%02x:%02x:%02x:%02x:%02x:",
param->connect.conn_id,
param->connect.remote_bda[0], param->connect.remote_bda[1], param->connect.remote_bda[2],
param->connect.remote_bda[3], param->connect.remote_bda[4], param->connect.remote_bda[5],
param->connect.is_connected);
param->connect.remote_bda[3], param->connect.remote_bda[4], param->connect.remote_bda[5]);
gl_profile_tab[PROFILE_A_APP_ID].conn_id = param->connect.conn_id;
//start sent the update connection parameters to the peer device.
esp_ble_gap_update_conn_params(&conn_params);
@ -489,6 +490,12 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i
ESP_LOGI(GATTS_TAG, "ESP_GATTS_DISCONNECT_EVT");
esp_ble_gap_start_advertising(&adv_params);
break;
case ESP_GATTS_CONF_EVT:
ESP_LOGI(GATTS_TAG, "ESP_GATTS_CONF_EVT, status %d", param->conf.status);
if (param->conf.status != ESP_GATT_OK){
esp_log_buffer_hex(GATTS_TAG, param->conf.value, param->conf.len);
}
break;
case ESP_GATTS_OPEN_EVT:
case ESP_GATTS_CANCEL_OPEN_EVT:
case ESP_GATTS_CLOSE_EVT:
@ -575,7 +582,6 @@ static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_i
case ESP_GATTS_MTU_EVT:
ESP_LOGI(GATTS_TAG, "ESP_GATTS_MTU_EVT, MTU %d", param->mtu.mtu);
break;
case ESP_GATTS_CONF_EVT:
case ESP_GATTS_UNREG_EVT:
break;
case ESP_GATTS_CREATE_EVT:
@ -621,13 +627,18 @@ static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_i
case ESP_GATTS_STOP_EVT:
break;
case ESP_GATTS_CONNECT_EVT:
ESP_LOGI(GATTS_TAG, "CONNECT_EVT, conn_id %d, remote %02x:%02x:%02x:%02x:%02x:%02x:, is_conn %d",
ESP_LOGI(GATTS_TAG, "CONNECT_EVT, conn_id %d, remote %02x:%02x:%02x:%02x:%02x:%02x:",
param->connect.conn_id,
param->connect.remote_bda[0], param->connect.remote_bda[1], param->connect.remote_bda[2],
param->connect.remote_bda[3], param->connect.remote_bda[4], param->connect.remote_bda[5],
param->connect.is_connected);
param->connect.remote_bda[3], param->connect.remote_bda[4], param->connect.remote_bda[5]);
gl_profile_tab[PROFILE_B_APP_ID].conn_id = param->connect.conn_id;
break;
case ESP_GATTS_CONF_EVT:
ESP_LOGI(GATTS_TAG, "ESP_GATTS_CONF_EVT status %d", param->conf.status);
if (param->conf.status != ESP_GATT_OK){
esp_log_buffer_hex(GATTS_TAG, param->conf.value, param->conf.len);
}
break;
case ESP_GATTS_DISCONNECT_EVT:
case ESP_GATTS_OPEN_EVT:
case ESP_GATTS_CANCEL_OPEN_EVT:

View file

@ -209,7 +209,7 @@ static void gattc_profile_a_event_handler(esp_gattc_cb_event_t event, esp_gatt_i
ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_attr_count error");
}
if (count > 0) {
char_elem_result_a = (esp_gattc_char_elem_t *)malloc(sizeof(char_elem_result_a) * count);
char_elem_result_a = (esp_gattc_char_elem_t *)malloc(sizeof(esp_gattc_char_elem_t) * count);
if (!char_elem_result_a){
ESP_LOGE(GATTC_TAG, "gattc no mem");
}else {
@ -255,7 +255,7 @@ static void gattc_profile_a_event_handler(esp_gattc_cb_event_t event, esp_gatt_i
ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_attr_count error");
}
if (count > 0){
descr_elem_result_a = malloc(sizeof(descr_elem_result_a) * count);
descr_elem_result_a = (esp_gattc_descr_elem_t *)malloc(sizeof(esp_gattc_descr_elem_t) * count);
if (!descr_elem_result_a){
ESP_LOGE(GATTC_TAG, "malloc error, gattc no mem");
}else{
@ -410,7 +410,7 @@ static void gattc_profile_b_event_handler(esp_gattc_cb_event_t event, esp_gatt_i
}
if (count > 0){
char_elem_result_b = (esp_gattc_char_elem_t *)malloc(sizeof(char_elem_result_b) * count);
char_elem_result_b = (esp_gattc_char_elem_t *)malloc(sizeof(esp_gattc_char_elem_t) * count);
if (!char_elem_result_b){
ESP_LOGE(GATTC_TAG, "gattc no mem");
}else{
@ -457,7 +457,7 @@ static void gattc_profile_b_event_handler(esp_gattc_cb_event_t event, esp_gatt_i
ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_attr_count error");
}
if (count > 0){
descr_elem_result_b = malloc(sizeof(descr_elem_result_b) * count);
descr_elem_result_b = (esp_gattc_descr_elem_t *)malloc(sizeof(esp_gattc_descr_elem_t) * count);
if (!descr_elem_result_b){
ESP_LOGE(GATTC_TAG, "malloc error, gattc no mem");
}else{
@ -609,7 +609,7 @@ static void gattc_profile_c_event_handler(esp_gattc_cb_event_t event, esp_gatt_i
}
if (count > 0){
char_elem_result_c = (esp_gattc_char_elem_t *)malloc(sizeof(char_elem_result_c) * count);
char_elem_result_c = (esp_gattc_char_elem_t *)malloc(sizeof(esp_gattc_char_elem_t) * count);
if (!char_elem_result_c){
ESP_LOGE(GATTC_TAG, "gattc no mem");
}else{
@ -655,7 +655,7 @@ static void gattc_profile_c_event_handler(esp_gattc_cb_event_t event, esp_gatt_i
ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_attr_count error");
}
if (count > 0){
descr_elem_result_c = malloc(sizeof(descr_elem_result_c) * count);
descr_elem_result_c = (esp_gattc_descr_elem_t *)malloc(sizeof(esp_gattc_descr_elem_t) * count);
if (!descr_elem_result_c){
ESP_LOGE(GATTC_TAG, "malloc error, gattc no mem");
}else{

View file

@ -0,0 +1,35 @@
import re
import os
import sys
# this is a test case write with tiny-test-fw.
# to run test cases outside tiny-test-fw,
# we need to set environment variable `TEST_FW_PATH`,
# then get and insert `TEST_FW_PATH` to sys path before import FW module
test_fw_path = os.getenv("TEST_FW_PATH")
if test_fw_path and test_fw_path not in sys.path:
sys.path.insert(0, test_fw_path)
import TinyFW
import IDF
@IDF.idf_example_test(env_tag="Example_WIFI")
def test_examples_protocol_https_request(env, extra_data):
"""
steps: |
1. join AP
2. connect to www.howsmyssl.com:443
3. send http request
"""
dut1 = env.get_dut("https_request", "examples/protocols/https_request")
dut1.start_app()
dut1.expect("Connecting to www.howsmyssl.com:443", timeout=30)
dut1.expect("Performing the SSL/TLS handshake")
dut1.expect("Certificate verified.", timeout=15)
dut1.expect("Cipher suite is TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256", timeout=20)
dut1.expect(re.compile(r"Completed (\d) requests"))
if __name__ == '__main__':
test_examples_protocol_https_request()

View file

@ -8,10 +8,10 @@ config WIFI_SSID
config WIFI_PASSWORD
string "WiFi Password"
default "myssid"
default "mypassword"
help
WiFi password (WPA or WPA2) for the example to use.
Can be left blank if the network has no security set.
endmenu
endmenu

View file

@ -38,6 +38,7 @@ help:
@echo "make app - Build just the app"
@echo "make app-flash - Flash just the app"
@echo "make app-clean - Clean just the app"
@echo "make print_flash_cmd - Print the arguments for esptool when flash"
@echo ""
@echo "See also 'make bootloader', 'make bootloader-flash', 'make bootloader-clean', "
@echo "'make partition_table', etc, etc."
@ -496,6 +497,10 @@ list-components:
$(info COMPONENT_PATHS (paths to all components):)
$(foreach cp,$(COMPONENT_PATHS),$(info $(cp)))
# print flash command, so users can dump this to config files and download somewhere without idf
print_flash_cmd:
echo $(ESPTOOL_WRITE_FLASH_OPTIONS) $(ESPTOOL_ALL_FLASH_ARGS) | sed -e 's:'$(PWD)/build/'::g'
# Check toolchain version using the output of xtensa-esp32-elf-gcc --version command.
# The output normally looks as follows
# xtensa-esp32-elf-gcc (crosstool-NG crosstool-ng-1.22.0-59-ga194053) 4.8.5

View file

@ -118,7 +118,8 @@ build_example () {
(
make MAKEFLAGS= clean &&
make MAKEFLAGS= defconfig &&
make all
make all &&
make print_flash_cmd | tail -n 1 > build/download.config
) &> >(tee -a "${BUILDLOG}") || {
RESULT=$?; FAILED_EXAMPLES+=" ${EXAMPLE_NAME}"
make MAKEFLAGS= V=1 clean defconfig && make V=1 # verbose output for errors

93
tools/tiny-test-fw/App.py Normal file
View file

@ -0,0 +1,93 @@
# Copyright 2015-2017 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.
"""
class for handling Test Apps. Currently it provides the following features:
1. get SDK path
2. get SDK tools
3. parse application info from its path. for example:
* provide download info
* provide partition table info
Test Apps should inherent from BaseApp class and overwrite the methods.
"""
import os
import sys
import time
# timestamp used for calculate log folder name
LOG_FOLDER_TIMESTAMP = time.time()
class BaseApp(object):
"""
Base Class for App.
Defines the mandatory methods that App need to implement.
Also implements some common methods.
:param app_path: the path for app.
"""
def __init__(self, app_path):
pass
@classmethod
def get_sdk_path(cls):
"""
get sdk path.
subclass must overwrite this method.
:return: abs sdk path
"""
pass
@classmethod
def get_tools(cls):
"""
get SDK related tools for applications
subclass must overwrite this method.
:return: tuple, abs path of each tool
"""
pass
@classmethod
def get_log_folder(cls, test_suite_name):
"""
By default log folder is ``${SDK_PATH}/TEST_LOGS/${test_suite_name}_${timestamp}``.
The log folder name is consist once start running, ensure all logs of will be put into the same folder.
:param test_suite_name: the test suite name, by default it's the base file name for main module
:return: the log folder path
"""
if not test_suite_name:
test_suite_name = os.path.splitext(os.path.basename(sys.modules['__main__'].__file__))[0]
sdk_path = cls.get_sdk_path()
return os.path.join(sdk_path, "TEST_LOGS",
test_suite_name +
time.strftime("_%m%d_%H_%M_%S", time.localtime(LOG_FOLDER_TIMESTAMP)))
def process_app_info(self):
"""
parse built app info for DUTTool
subclass must overwrite this method.
:return: required info for specific DUTTool
"""
pass

View file

@ -0,0 +1,179 @@
# Copyright 2015-2017 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.
"""
Command line tool to assign example tests to CI test jobs.
"""
# TODO: Need to handle running examples on different chips
import os
import sys
import re
import argparse
import yaml
test_fw_path = os.getenv("TEST_FW_PATH")
if test_fw_path:
sys.path.insert(0, test_fw_path)
from Utility import CaseConfig, SearchCases, GitlabCIJob
class Group(object):
MAX_EXECUTION_TIME = 30
MAX_CASE = 15
SORT_KEYS = ["env_tag"]
def __init__(self, case):
self.execution_time = 0
self.case_list = [case]
self.filters = dict(zip(self.SORT_KEYS, [case.case_info[x] for x in self.SORT_KEYS]))
def accept_new_case(self):
"""
check if allowed to add any case to this group
:return: True or False
"""
max_time = (sum([x.case_info["execution_time"] for x in self.case_list]) < self.MAX_EXECUTION_TIME)
max_case = (len(self.case_list) < self.MAX_CASE)
return max_time and max_case
def add_case(self, case):
"""
add case to current group
:param case: test case
:return: True if add succeed, else False
"""
added = False
if self.accept_new_case():
for key in self.filters:
if case.case_info[key] != self.filters[key]:
break
else:
self.case_list.append(case)
added = True
return added
def output(self):
"""
output data for job configs
:return: {"Filter": case filter, "CaseConfig": list of case configs for cases in this group}
"""
output_data = {
"Filter": self.filters,
"CaseConfig": [{"name": x.case_info["name"]} for x in self.case_list],
}
return output_data
class AssignTest(object):
"""
Auto assign tests to CI jobs.
:param test_case: path of test case file(s)
:param ci_config_file: path of ``.gitlab-ci.yml``
"""
CI_TEST_JOB_PATTERN = re.compile(r"^example_test_.+")
def __init__(self, test_case, ci_config_file):
self.test_cases = self._search_cases(test_case)
self.jobs = self._parse_gitlab_ci_config(ci_config_file)
def _parse_gitlab_ci_config(self, ci_config_file):
with open(ci_config_file, "r") as f:
ci_config = yaml.load(f)
job_list = list()
for job_name in ci_config:
if self.CI_TEST_JOB_PATTERN.search(job_name) is not None:
job_list.append(GitlabCIJob.Job(ci_config[job_name], job_name))
return job_list
@staticmethod
def _search_cases(test_case, case_filter=None):
"""
:param test_case: path contains test case folder
:param case_filter: filter for test cases
:return: filtered test case list
"""
test_methods = SearchCases.Search.search_test_cases(test_case)
return CaseConfig.filter_test_cases(test_methods, case_filter if case_filter else dict())
def _group_cases(self):
"""
separate all cases into groups according group rules. each group will be executed by one CI job.
:return: test case groups.
"""
groups = []
for case in self.test_cases:
for group in groups:
# add to current group
if group.add_case(case):
break
else:
# create new group
groups.append(Group(case))
return groups
def assign_cases(self):
"""
separate test cases to groups and assign test cases to CI jobs.
:raise AssertError: if failed to assign any case to CI job.
:return: None
"""
failed_to_assign = []
test_groups = self._group_cases()
for group in test_groups:
for job in self.jobs:
if job.match_group(group):
job.assign_group(group)
break
else:
failed_to_assign.append(group)
assert not failed_to_assign
def output_configs(self, output_path):
"""
:param output_path: path to output config files for each CI job
:return: None
"""
if not os.path.exists(output_path):
os.makedirs(output_path)
for job in self.jobs:
job.output_config(output_path)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("test_case",
help="test case folder or file")
parser.add_argument("ci_config_file",
help="gitlab ci config file")
parser.add_argument("output_path",
help="output path of config files")
args = parser.parse_args()
assign_test = AssignTest(args.test_case, args.ci_config_file)
assign_test.assign_cases()
assign_test.output_configs(args.output_path)

550
tools/tiny-test-fw/DUT.py Normal file
View file

@ -0,0 +1,550 @@
# Copyright 2015-2017 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.
"""
DUT provides 3 major groups of features:
* DUT port feature, provide basic open/close/read/write features
* DUT tools, provide extra methods to control the device, like download and start app
* DUT expect method, provide features for users to check DUT outputs
The current design of DUT have 3 classes for one DUT: BaseDUT, DUTPort, DUTTool.
* BaseDUT class:
* defines methods DUT port and DUT tool need to overwrite
* provide the expect methods and some other methods based on DUTPort
* DUTPort class:
* inherent from BaseDUT class
* implements the port features by overwriting port methods defined in BaseDUT
* DUTTool class:
* inherent from one of the DUTPort class
* implements the tools features by overwriting tool methods defined in BaseDUT
* could add some new methods provided by the tool
This module implements the BaseDUT class and one of the port class SerialDUT.
User should implement their DUTTool classes.
If they using different port then need to implement their DUTPort class as well.
"""
import time
import re
import threading
import copy
import sys
import functools
import serial
from serial.tools import list_ports
if sys.version_info[0] == 2:
import Queue as _queue
else:
import queue as _queue
class ExpectTimeout(ValueError):
""" timeout for expect method """
pass
class UnsupportedExpectItem(ValueError):
""" expect item not supported by the expect method """
pass
def _expect_lock(func):
@functools.wraps(func)
def handler(self, *args, **kwargs):
with self.expect_lock:
ret = func(self, *args, **kwargs)
return ret
return handler
class _DataCache(_queue.Queue):
"""
Data cache based on Queue. Allow users to process data cache based on bytes instead of Queue."
"""
def __init__(self, maxsize=0):
_queue.Queue.__init__(self, maxsize=maxsize)
self.data_cache = str()
def get_data(self, timeout=0):
"""
get a copy of data from cache.
:param timeout: timeout for waiting new queue item
:return: copy of data cache
"""
# make sure timeout is non-negative
if timeout < 0:
timeout = 0
try:
data = self.get(timeout=timeout)
if isinstance(data, bytes):
# convert bytes to string
try:
data = data.decode("utf-8", "ignore")
except UnicodeDecodeError:
data = data.decode("iso8859-1",)
self.data_cache += data
except _queue.Empty:
# don't do anything when on update for cache
pass
return copy.deepcopy(self.data_cache)
def flush(self, index=0xFFFFFFFF):
"""
flush data from cache.
:param index: if < 0 then don't do flush, otherwise flush data before index
:return: None
"""
# first add data in queue to cache
self.get_data()
if index > 0:
self.data_cache = self.data_cache[index:]
class _RecvThread(threading.Thread):
def __init__(self, read, data_cache):
super(_RecvThread, self).__init__()
self.exit_event = threading.Event()
self.setDaemon(True)
self.read = read
self.data_cache = data_cache
def run(self):
while not self.exit_event.isSet():
data = self.read(1000)
if data:
self.data_cache.put(data)
def exit(self):
self.exit_event.set()
self.join()
class BaseDUT(object):
"""
:param name: application defined name for port
:param port: comport name, used to create DUT port
:param log_file: log file name
:param app: test app instance
:param kwargs: extra args for DUT to create ports
"""
DEFAULT_EXPECT_TIMEOUT = 5
def __init__(self, name, port, log_file, app, **kwargs):
self.expect_lock = threading.Lock()
self.name = name
self.port = port
self.log_file = log_file
self.app = app
self.data_cache = _DataCache()
self.receive_thread = None
# open and start during init
self.open()
def __str__(self):
return "DUT({}: {})".format(self.name, str(self.port))
# define for methods need to be overwritten by Port
@classmethod
def list_available_ports(cls):
"""
list all available ports.
subclass (port) must overwrite this method.
:return: list of available comports
"""
pass
def _port_open(self):
"""
open the port.
subclass (port) must overwrite this method.
:return: None
"""
pass
def _port_read(self, size=1):
"""
read form port. This method should not blocking for long time, otherwise receive thread can not exit.
subclass (port) must overwrite this method.
:param size: max size to read.
:return: read data.
"""
pass
def _port_write(self, data):
"""
write to port.
subclass (port) must overwrite this method.
:param data: data to write
:return: None
"""
pass
def _port_close(self):
"""
close port.
subclass (port) must overwrite this method.
:return: None
"""
pass
# methods that need to be overwritten by Tool
@classmethod
def confirm_dut(cls, port, app, **kwargs):
"""
confirm if it's a DUT, usually used by auto detecting DUT in by Env config.
subclass (tool) must overwrite this method.
:param port: comport
:param app: app instance
:return: True or False
"""
pass
def start_app(self):
"""
usually after we got DUT, we need to do some extra works to let App start.
For example, we need to reset->download->reset to let IDF application start on DUT.
subclass (tool) must overwrite this method.
:return: None
"""
pass
# methods that features raw port methods
def open(self):
"""
open port and create thread to receive data.
:return: None
"""
self._port_open()
self.receive_thread = _RecvThread(self._port_read, self.data_cache)
self.receive_thread.start()
def close(self):
"""
close receive thread and then close port.
:return: None
"""
if self.receive_thread:
self.receive_thread.exit()
self._port_close()
def write(self, data, eol="\r\n", flush=True):
"""
:param data: data
:param eol: end of line pattern.
:param flush: if need to flush received data cache before write data.
usually we need to flush data before write,
make sure processing outputs generated by wrote.
:return: None
"""
# do flush before write
if flush:
self.data_cache.flush()
# do write if cache
if data:
self._port_write(data + eol if eol else data)
@_expect_lock
def read(self, size=0xFFFFFFFF):
"""
read(size=0xFFFFFFFF)
read raw data. NOT suggested to use this method.
Only use it if expect method doesn't meet your requirement.
:param size: read size. default read all data
:return: read data
"""
data = self.data_cache.get_data(0)[:size]
self.data_cache.flush(size)
return data
# expect related methods
@staticmethod
def _expect_str(data, pattern):
"""
protected method. check if string is matched in data cache.
:param data: data to process
:param pattern: string
:return: pattern if match succeed otherwise None
"""
index = data.find(pattern)
if index != -1:
ret = pattern
index += len(pattern)
else:
ret = None
return ret, index
@staticmethod
def _expect_re(data, pattern):
"""
protected method. check if re pattern is matched in data cache
:param data: data to process
:param pattern: compiled RegEx pattern
:return: match groups if match succeed otherwise None
"""
ret = None
match = pattern.search(data)
if match:
ret = match.groups()
index = match.end()
else:
index = -1
return ret, index
EXPECT_METHOD = [
[type(re.compile("")), "_expect_re"],
[str, "_expect_str"],
]
def _get_expect_method(self, pattern):
"""
protected method. get expect method according to pattern type.
:param pattern: expect pattern, string or compiled RegEx
:return: ``_expect_str`` or ``_expect_re``
"""
for expect_method in self.EXPECT_METHOD:
if isinstance(pattern, expect_method[0]):
method = expect_method[1]
break
else:
raise UnsupportedExpectItem()
return self.__getattribute__(method)
@_expect_lock
def expect(self, pattern, timeout=DEFAULT_EXPECT_TIMEOUT):
"""
expect(pattern, timeout=DEFAULT_EXPECT_TIMEOUT)
expect received data on DUT match the pattern. will raise exception when expect timeout.
:raise ExpectTimeout: failed to find the pattern before timeout
:raise UnsupportedExpectItem: pattern is not string or compiled RegEx
:param pattern: string or compiled RegEx(string pattern)
:param timeout: timeout for expect
:return: string if pattern is string; matched groups if pattern is RegEx
"""
method = self._get_expect_method(pattern)
# non-blocking get data for first time
data = self.data_cache.get_data(0)
start_time = time.time()
while True:
ret, index = method(data, pattern)
if ret is not None or time.time() - start_time > timeout:
self.data_cache.flush(index)
break
# wait for new data from cache
data = self.data_cache.get_data(time.time() + timeout - start_time)
if ret is None:
raise ExpectTimeout(self.name + ": " + str(pattern))
return ret
def _expect_multi(self, expect_all, expect_item_list, timeout):
"""
protected method. internal logical for expect multi.
:param expect_all: True or False, expect all items in the list or any in the list
:param expect_item_list: expect item list
:param timeout: timeout
:return: None
"""
def process_expected_item(item_raw):
# convert item raw data to standard dict
item = {
"pattern": item_raw[0] if isinstance(item_raw, tuple) else item_raw,
"method": self._get_expect_method(item_raw[0] if isinstance(item_raw, tuple)
else item_raw),
"callback": item_raw[1] if isinstance(item_raw, tuple) else None,
"index": -1,
"ret": None,
}
return item
expect_items = [process_expected_item(x) for x in expect_item_list]
# non-blocking get data for first time
data = self.data_cache.get_data(0)
start_time = time.time()
matched_expect_items = list()
while True:
for expect_item in expect_items:
if expect_item not in matched_expect_items:
# exclude those already matched
expect_item["ret"], expect_item["index"] = \
expect_item["method"](data, expect_item["pattern"])
if expect_item["ret"] is not None:
# match succeed for one item
matched_expect_items.append(expect_item)
break
# if expect all, then all items need to be matched,
# else only one item need to matched
if expect_all:
match_succeed = (matched_expect_items == expect_items)
else:
match_succeed = True if matched_expect_items else False
if time.time() - start_time > timeout or match_succeed:
break
else:
data = self.data_cache.get_data(time.time() + timeout - start_time)
if match_succeed:
# do callback and flush matched data cache
slice_index = -1
for expect_item in matched_expect_items:
# trigger callback
if expect_item["callback"]:
expect_item["callback"](expect_item["ret"])
slice_index = max(slice_index, expect_item["index"])
# flush already matched data
self.data_cache.flush(slice_index)
else:
raise ExpectTimeout(self.name + ": " + str(expect_items))
@_expect_lock
def expect_any(self, *expect_items, **timeout):
"""
expect_any(*expect_items, timeout=DEFAULT_TIMEOUT)
expect any of the patterns.
will call callback (if provided) if pattern match succeed and then return.
will pass match result to the callback.
:raise ExpectTimeout: failed to match any one of the expect items before timeout
:raise UnsupportedExpectItem: pattern in expect_item is not string or compiled RegEx
:arg expect_items: one or more expect items.
string, compiled RegEx pattern or (string or RegEx(string pattern), callback)
:keyword timeout: timeout for expect
:return: None
"""
# to be compatible with python2
# in python3 we can write f(self, *expect_items, timeout=DEFAULT_TIMEOUT)
if "timeout" not in timeout:
timeout["timeout"] = self.DEFAULT_EXPECT_TIMEOUT
return self._expect_multi(False, expect_items, **timeout)
@_expect_lock
def expect_all(self, *expect_items, **timeout):
"""
expect_all(*expect_items, timeout=DEFAULT_TIMEOUT)
expect all of the patterns.
will call callback (if provided) if all pattern match succeed and then return.
will pass match result to the callback.
:raise ExpectTimeout: failed to match all of the expect items before timeout
:raise UnsupportedExpectItem: pattern in expect_item is not string or compiled RegEx
:arg expect_items: one or more expect items.
string, compiled RegEx pattern or (string or RegEx(string pattern), callback)
:keyword timeout: timeout for expect
:return: None
"""
# to be compatible with python2
# in python3 we can write f(self, *expect_items, timeout=DEFAULT_TIMEOUT)
if "timeout" not in timeout:
timeout["timeout"] = self.DEFAULT_EXPECT_TIMEOUT
return self._expect_multi(True, expect_items, **timeout)
class SerialDUT(BaseDUT):
""" serial with logging received data feature """
DEFAULT_UART_CONFIG = {
"baudrate": 115200,
"bytesize": serial.EIGHTBITS,
"parity": serial.PARITY_NONE,
"stopbits": serial.STOPBITS_ONE,
"timeout": 0.05,
"xonxoff": False,
"rtscts": False,
}
def __init__(self, name, port, log_file, app, **kwargs):
self.port_inst = None
self.serial_configs = self.DEFAULT_UART_CONFIG.copy()
self.serial_configs.update(kwargs)
super(SerialDUT, self).__init__(name, port, log_file, app, **kwargs)
@staticmethod
def _format_data(data):
"""
format data for logging. do decode and add timestamp.
:param data: raw data from read
:return: formatted data (str)
"""
timestamp = time.time()
timestamp = "{}:{}".format(time.strftime("%m-%d %H:%M:%S", time.localtime(timestamp)),
str(timestamp % 1)[2:5])
try:
formatted_data = "[{}]:\r\n{}\r\n".format(timestamp, data.decode("utf-8", "ignore"))
except UnicodeDecodeError:
# if utf-8 fail, use iso-8859-1 (single char codec with range 0-255)
formatted_data = "[{}]:\r\n{}\r\n".format(timestamp, data.decode("iso8859-1",))
return formatted_data
def _port_open(self):
self.port_inst = serial.Serial(self.port, **self.serial_configs)
def _port_close(self):
self.port_inst.close()
def _port_read(self, size=1):
data = self.port_inst.read(size)
if data:
with open(self.log_file, "a+") as _log_file:
_log_file.write(self._format_data(data))
return data
def _port_write(self, data):
self.port_inst.write(data)
@classmethod
def list_available_ports(cls):
return [x.device for x in list_ports.comports()]

156
tools/tiny-test-fw/Env.py Normal file
View file

@ -0,0 +1,156 @@
# Copyright 2015-2017 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.
""" Test Env, manages DUT, App and EnvConfig, interface for test cases to access these components """
import os
import threading
import functools
import EnvConfig
def _synced(func):
@functools.wraps(func)
def decorator(self, *args, **kwargs):
with self.lock:
ret = func(self, *args, **kwargs)
return ret
decorator.__doc__ = func.__doc__
return decorator
class Env(object):
"""
test env, manages DUTs and env configs.
:keyword app: class for default application
:keyword dut: class for default DUT
:keyword env_tag: test env tag, used to select configs from env config file
:keyword env_config_file: test env config file path
:keyword test_name: test suite name, used when generate log folder name
"""
def __init__(self,
app=None,
dut=None,
env_tag=None,
env_config_file=None,
test_name=None,
**kwargs):
self.app_cls = app
self.default_dut_cls = dut
self.config = EnvConfig.Config(env_config_file, env_tag)
self.log_path = self.app_cls.get_log_folder(test_name)
if not os.path.exists(self.log_path):
os.makedirs(self.log_path)
self.allocated_duts = dict()
self.lock = threading.RLock()
@_synced
def get_dut(self, dut_name, app_path, dut_class=None, app_class=None):
"""
get_dut(dut_name, app_path, dut_class=None, app_class=None)
:param dut_name: user defined name for DUT
:param app_path: application path, app instance will use this path to process application info
:param dut_class: dut class, if not specified will use default dut class of env
:param app_class: app class, if not specified will use default app of env
:return: dut instance
"""
if dut_name in self.allocated_duts:
dut = self.allocated_duts[dut_name]["dut"]
else:
if dut_class is None:
dut_class = self.default_dut_cls
if app_class is None:
app_class = self.app_cls
app_inst = app_class(app_path)
try:
port = self.config.get_variable(dut_name)
except ValueError:
# try to auto detect ports
allocated_ports = [self.allocated_duts[x]["port"] for x in self.allocated_duts]
available_ports = dut_class.list_available_ports()
for port in available_ports:
if port not in allocated_ports:
if dut_class.confirm_dut(port, app_inst):
break
else:
port = None
if port:
try:
dut_config = self.get_variable(dut_name + "_port_config")
except ValueError:
dut_config = dict()
dut = self.default_dut_cls(dut_name, port,
os.path.join(self.log_path, dut_name + ".log"),
app_inst,
**dut_config)
self.allocated_duts[dut_name] = {"port": port, "dut": dut}
else:
raise ValueError("Failed to get DUT")
return dut
@_synced
def close_dut(self, dut_name):
"""
close_dut(dut_name)
close one DUT by name if DUT name is valid (the name used by ``get_dut``). otherwise will do nothing.
:param dut_name: user defined name for DUT
:return: None
"""
try:
dut = self.allocated_duts.pop(dut_name)["dut"]
dut.close()
except KeyError:
pass
@_synced
def get_variable(self, variable_name):
"""
get_variable(variable_name)
get variable from config file. If failed then try to auto-detected it.
:param variable_name: name of the variable
:return: value of variable if successfully found. otherwise None.
"""
return self.config.get_variable(variable_name)
@_synced
def get_pc_nic_info(self, nic_name="pc_nic"):
"""
get_pc_nic_info(nic_name="pc_nic")
try to get nic info (ip address, ipv6 address, mac address)
:param nic_name: pc nic name. allows passing variable name, nic name value or omitted (to get default nic info).
:return: a dict of address ("ipv4", "ipv6", "mac") if successfully found. otherwise None.
"""
# TODO: need to implement auto get nic info method
return self.config.get_variable("nic_info/" + nic_name)
@_synced
def close(self):
"""
close()
close all DUTs of the Env.
:return: None
"""
for dut_name in self.allocated_duts:
dut = self.allocated_duts[dut_name]["dut"]
dut.close()
self.allocated_duts = dict()

View file

@ -0,0 +1,74 @@
# Copyright 2015-2017 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.
"""
The test env could change when we running test from different computers.
Test env config provide ``get_variable`` method to allow user get test environment related variables.
It will first try to get variable from config file.
If failed, then it will try to auto detect (Not supported yet).
Config file format is yaml. it's a set of key-value pair. The following is an example of config file::
Example_WIFI:
ap_ssid: "myssid"
ap_password: "mypassword"
Example_ShieldBox:
attenuator_port: "/dev/ttyUSB2"
ap_ssid: "myssid"
ap_password: "mypassword"
It will first define the env tag for each environment, then add its key-value pairs.
This will prevent test cases from getting configs from other env when there're configs for multiple env in one file.
"""
import yaml
class Config(object):
""" Test Env Config """
def __init__(self, config_file, env_tag):
self.configs = self.load_config_file(config_file, env_tag)
@staticmethod
def load_config_file(config_file, env_name):
"""
load configs from config file.
:param config_file: config file path
:param env_name: env tag name
:return: configs for the test env
"""
try:
with open(config_file) as f:
configs = yaml.load(f)[env_name]
except (OSError, TypeError):
configs = dict()
return configs
def get_variable(self, variable_name):
"""
first try to get from config file. if not found, try to auto detect the variable.
:param variable_name: name of variable
:return: value or None
"""
try:
value = self.configs[variable_name]
except KeyError:
#TODO: to support auto get variable here
value = None
if value is None:
raise ValueError("Failed to get variable")
return value

View file

@ -0,0 +1,6 @@
.external_ap: &external_ap
ap_ssid: "myssid"
ap_password: "mypassword"
Examples_WIFI:
<<: external_ap

View file

@ -0,0 +1,164 @@
# Copyright 2015-2017 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.
""" IDF Test Applications """
import subprocess
import os
import App
class IDFApp(App.BaseApp):
"""
Implements common esp-idf application behavior.
idf applications should inherent from this class and overwrite method get_binary_path.
"""
IDF_DOWNLOAD_CONFIG_FILE = "download.config"
def __init__(self, app_path):
super(IDFApp, self).__init__(app_path)
self.idf_path = self.get_sdk_path()
self.binary_path = self.get_binary_path(app_path)
assert os.path.exists(self.binary_path)
assert self.IDF_DOWNLOAD_CONFIG_FILE in os.listdir(self.binary_path)
self.esptool, self.partition_tool = self.get_tools()
@classmethod
def get_sdk_path(cls):
idf_path = os.getenv("IDF_PATH")
assert idf_path
assert os.path.exists(idf_path)
return idf_path
@classmethod
def get_tools(cls):
idf_path = cls.get_sdk_path()
# get esptool and partition tool for esp-idf
esptool = os.path.join(idf_path, "components",
"esptool_py", "esptool", "esptool.py")
partition_tool = os.path.join(idf_path, "components",
"partition_table", "gen_esp32part.py")
assert os.path.exists(esptool) and os.path.exists(partition_tool)
return esptool, partition_tool
def get_binary_path(self, app_path):
"""
get binary path according to input app_path.
subclass must overwrite this method.
:param app_path: path of application
:return: abs app binary path
"""
pass
def process_arg(self, arg):
"""
process args in download.config. convert to abs path for .bin args. strip spaces and CRLFs.
"""
if ".bin" in arg:
ret = os.path.join(self.binary_path, arg)
else:
ret = arg
return ret.strip("\r\n ")
def process_app_info(self):
"""
get app download config and partition info from a specific app path
:return: download config, partition info
"""
with open(os.path.join(self.binary_path, self.IDF_DOWNLOAD_CONFIG_FILE), "r") as f:
configs = f.read().split(" ")
download_configs = ["--chip", "auto", "--before", "default_reset",
"--after", "hard_reset", "write_flash", "-z"]
download_configs += [self.process_arg(x) for x in configs]
# handle partition table
for partition_file in download_configs:
if "partition" in partition_file:
partition_file = os.path.join(self.binary_path, partition_file)
break
else:
raise ValueError("No partition table found for IDF binary path: {}".format(self.binary_path))
process = subprocess.Popen(["python", self.partition_tool, partition_file],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
raw_data = process.stdout.read()
if isinstance(raw_data, bytes):
raw_data = raw_data.decode()
partition_table = dict()
for line in raw_data.splitlines():
if line[0] != "#":
try:
_name, _type, _subtype, _offset, _size, _flags = line.split(",")
if _size[-1] == "K":
_size = int(_size[:-1]) * 1024
elif _size[-1] == "M":
_size = int(_size[:-1]) * 1024 * 1024
else:
_size = int(_size)
except ValueError:
continue
partition_table[_name] = {
"type": _type,
"subtype": _subtype,
"offset": _offset,
"size": _size,
"flags": _flags
}
return download_configs, partition_table
class Example(IDFApp):
def get_binary_path(self, app_path):
# build folder of example path
path = os.path.join(self.idf_path, app_path, "build")
if not os.path.exists(path):
# search for CI build folders
app = os.path.basename(app_path)
example_path = os.path.join(self.idf_path, "build_examples", "example_builds")
for dirpath, dirnames, files in os.walk(example_path):
if dirnames:
if dirnames[0] == app:
path = os.path.join(example_path, dirpath, dirnames[0], "build")
break
else:
raise OSError("Failed to find example binary")
return path
class UT(IDFApp):
def get_binary_path(self, app_path):
if app_path:
# specified path, join it and the idf path
path = os.path.join(self.idf_path, app_path)
else:
path = os.path.join(self.idf_path, "tools", "unit-test-app", "build")
return path
class SSC(IDFApp):
def get_binary_path(self, app_path):
# TODO: to implement SSC get binary path
return app_path
class AT(IDFApp):
def get_binary_path(self, app_path):
# TODO: to implement AT get binary path
return app_path

View file

@ -0,0 +1,126 @@
# Copyright 2015-2017 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.
""" DUT for IDF applications """
import os
import re
import subprocess
import functools
import DUT
class IDFToolError(OSError):
pass
def _tool_method(func):
""" close port, execute tool method and then reopen port """
@functools.wraps(func)
def handler(self, *args, **kwargs):
self.close()
ret = func(self, *args, **kwargs)
self.open()
return ret
return handler
class IDFDUT(DUT.SerialDUT):
""" IDF DUT, extends serial with ESPTool methods """
CHIP_TYPE_PATTERN = re.compile(r"Detecting chip type[.:\s]+(.+)")
def __init__(self, name, port, log_file, app, **kwargs):
self.download_config, self.partition_table = app.process_app_info()
super(IDFDUT, self).__init__(name, port, log_file, app, **kwargs)
@classmethod
def get_chip(cls, app, port):
"""
get chip id via esptool
:param app: application instance (to get tool)
:param port: comport
:return: chip ID or None
"""
try:
output = subprocess.check_output(["python", app.esptool, "--port", port, "chip_id"])
except subprocess.CalledProcessError:
output = bytes()
if isinstance(output, bytes):
output = output.decode()
chip_type = cls.CHIP_TYPE_PATTERN.search(output)
return chip_type.group(1) if chip_type else None
@classmethod
def confirm_dut(cls, port, app, **kwargs):
return cls.get_chip(app, port) is not None
@_tool_method
def start_app(self):
"""
download and start app.
:return: None
"""
retry_baud_rates = ["921600", "115200"]
error = IDFToolError()
for baud_rate in retry_baud_rates:
try:
subprocess.check_output(["python", self.app.esptool,
"--port", self.port, "--baud", baud_rate]
+ self.download_config)
break
except subprocess.CalledProcessError as error:
continue
else:
raise error
@_tool_method
def reset(self):
"""
reset DUT with esptool
:return: None
"""
subprocess.check_output(["python", self.app.esptool, "--port", self.port, "run"])
@_tool_method
def dump_flush(self, output_file, **kwargs):
"""
dump flush
:param output_file: output file name, if relative path, will use sdk path as base path.
:keyword partition: partition name, dump the partition.
``partition`` is preferred than using ``address`` and ``size``.
:keyword address: dump from address (need to be used with size)
:keyword size: dump size (need to be used with address)
:return: None
"""
if os.path.isabs(output_file) is False:
output_file = os.path.relpath(output_file, self.app.get_log_folder())
if "partition" in kwargs:
partition = self.partition_table[kwargs["partition"]]
_address = partition["offset"]
_size = partition["size"]
elif "address" in kwargs and "size" in kwargs:
_address = kwargs["address"]
_size = kwargs["size"]
else:
raise IDFToolError("You must specify 'partition' or ('address' and 'size') to dump flash")
subprocess.check_output(
["python", self.app.esptool, "--port", self.port, "--baud", "921600",
"--before", "default_reset", "--after", "hard_reset", "read_flash",
_address, _size, output_file]
)

View file

@ -0,0 +1,36 @@
# Copyright 2015-2017 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.
import TinyFW
from IDF.IDFApp import Example, UT
from IDF.IDFDUT import IDFDUT
def idf_example_test(app=Example, dut=IDFDUT, chip="ESP32",
module="examples", execution_time=1,
**kwargs):
"""
decorator for testing idf examples (with default values for some keyword args).
:param app: test application class
:param dut: dut class
:param chip: chip supported, string or tuple
:param module: module, string
:param execution_time: execution time in minutes, int
:param kwargs: other keyword args
:return: test method
"""
# not use partial function as define as function support auto generating document
return TinyFW.test_method(app=app, dut=dut, chip=chip, module=module,
execution_time=execution_time, **kwargs)

View file

@ -0,0 +1,80 @@
# Copyright 2015-2017 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.
"""
Command line interface to run test cases from a given path.
* search and run test cases of a given path
* config file which support to filter test cases and passing data to test case
Use ``python Runner.py test_case_path -c config_file -e env_config_file`` to run test cases.
"""
import os
import sys
import argparse
import threading
import TinyFW
from Utility import SearchCases, CaseConfig
class Runner(threading.Thread):
"""
:param test_case: test case file or folder
:param case_config: case config file, allow to filter test cases and pass data to test case
:param env_config_file: env config file
"""
def __init__(self, test_case, case_config, env_config_file=None):
super(Runner, self).__init__()
self.setDaemon(True)
test_methods = SearchCases.Search.search_test_cases(test_case)
self.test_cases = CaseConfig.Parser.apply_config(test_methods, case_config)
self.test_result = True
if case_config:
test_suite_name = os.path.splitext(os.path.basename(case_config))[0]
else:
test_suite_name = "TestRunner"
TinyFW.set_default_config(env_config_file=env_config_file, test_suite_name=test_suite_name)
def run(self):
for case in self.test_cases:
self.test_result = self.test_result and case.run()
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("test_case",
help="test case folder or file")
parser.add_argument("--case_config", "-c", default=None,
help="case filter/config file")
parser.add_argument("--env_config_file", "-e", default=None,
help="test env config file")
args = parser.parse_args()
runner = Runner(args.test_case, args.case_config, args.env_config_file)
runner.start()
while True:
try:
runner.join(1)
if not runner.isAlive():
break
except KeyboardInterrupt:
print("exit by Ctrl-C")
break
if not runner.test_result:
sys.exit(1)

View file

@ -0,0 +1,53 @@
# Copyright 2015-2017 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.
import yaml
class TestCase(object):
"""
Test Case Object, mainly used with runner.
runner can parse all test cases from a given path, set data and config for test case in prepare stage.
TestCase instance will record these data, provide run method to let runner execute test case.
:param test_method: test function
:param extra_data: data passed to test function
:param overwrite_args: kwargs that overwrite original test case configs
"""
DEFAULT_CASE_DOC = dict()
def __init__(self, test_method, extra_data, **overwrite_args):
self.test_method = test_method
self.extra_data = extra_data
self.overwrite_args = overwrite_args
def run(self):
""" execute the test case """
return self.test_method(self.extra_data, **self.overwrite_args)
def document(self):
"""
generate test case document.
parse the case doc with yaml parser and update to original case attributes.
:return: case document, dict of case attributes and values
"""
doc_string = self.test_method.__doc__
try:
doc = yaml.load(doc_string)
except (AttributeError, OSError, UnicodeDecodeError):
doc = self.DEFAULT_CASE_DOC
doc.update(self.test_method.env_args)
doc.update(self.test_method.accepted_filter)
return doc

View file

@ -0,0 +1,220 @@
# Copyright 2015-2017 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.
""" Interface for test cases. """
import sys
import os
import time
import traceback
import inspect
import functools
import xunitgen
import Env
import DUT
import App
XUNIT_FILE_NAME = "XUNIT_RESULT.xml"
XUNIT_RECEIVER = xunitgen.EventReceiver()
XUNIT_DEFAULT_TEST_SUITE = "test-suite"
_COLOR_CODES = {
"white": '\033[0m',
"red": '\033[31m',
"green": '\033[32m',
"orange": '\033[33m',
"blue": '\033[34m',
"purple": '\033[35m',
"W": '\033[0m',
"R": '\033[31m',
"G": '\033[32m',
"O": '\033[33m',
"B": '\033[34m',
"P": '\033[35m'
}
def console_log(data, color="white"):
"""
log data to console.
(if not flush console log, Gitlab-CI won't update logs during job execution)
:param data: data content
:param color: color
"""
if color not in _COLOR_CODES:
color = "white"
color_codes = _COLOR_CODES[color]
print(color_codes + data)
if color not in ["white", "W"]:
# reset color to white for later logs
print(_COLOR_CODES["white"] + "\r")
sys.stdout.flush()
class DefaultEnvConfig(object):
"""
default test configs. There're 3 places to set configs, priority is (high -> low):
1. overwrite set by caller of test method
2. values set by test_method decorator
3. default env config get from this class
"""
DEFAULT_CONFIG = {
"app": App.BaseApp,
"dut": DUT.BaseDUT,
"env_tag": "default",
"env_config_file": None,
"test_suite_name": None,
}
@classmethod
def set_default_config(cls, **kwargs):
"""
:param kwargs: configs need to be updated
:return: None
"""
cls.DEFAULT_CONFIG.update(kwargs)
@classmethod
def get_default_config(cls):
"""
:return: current default config
"""
return cls.DEFAULT_CONFIG.copy()
set_default_config = DefaultEnvConfig.set_default_config
get_default_config = DefaultEnvConfig.get_default_config
class TestResult(object):
TEST_RESULT = {
"pass": [],
"fail": [],
}
@classmethod
def get_failed_cases(cls):
"""
:return: failed test cases
"""
return cls.TEST_RESULT["fail"]
@classmethod
def get_passed_cases(cls):
"""
:return: passed test cases
"""
return cls.TEST_RESULT["pass"]
@classmethod
def set_result(cls, result, case_name):
"""
:param result: True or False
:param case_name: test case name
:return: None
"""
cls.TEST_RESULT["pass" if result else "fail"].append(case_name)
get_failed_cases = TestResult.get_failed_cases
get_passed_cases = TestResult.get_passed_cases
MANDATORY_INFO = {
"execution_time": 1,
"env_tag": "default",
}
def test_method(**kwargs):
"""
decorator for test case function.
The following keyword arguments are pre-defined.
Any other keyword arguments will be regarded as filter for the test case,
able to access them by ``case_info`` attribute of test method.
:keyword app: class for test app. see :doc:`App <App>` for details
:keyword dut: class for current dut. see :doc:`DUT <DUT>` for details
:keyword env_tag: name for test environment, used to select configs from config file
:keyword env_config_file: test env config file. usually will not set this keyword when define case
:keyword test_suite_name: test suite name, used for generating log folder name and adding xunit format test result.
usually will not set this keyword when define case
"""
def test(test_func):
# get test function file name
frame = inspect.stack()
test_func_file_name = frame[1][1]
case_info = MANDATORY_INFO.copy()
case_info["name"] = test_func.__name__
case_info.update(kwargs)
# create env instance
env_config = DefaultEnvConfig.get_default_config()
for key in kwargs:
if key in env_config:
env_config[key] = kwargs[key]
@functools.wraps(test_func)
def handle_test(extra_data=None, **overwrite):
"""
create env, run test and record test results
:param extra_data: extra data that runner or main passed to test case
:param overwrite: args that runner or main want to overwrite
:return: None
"""
env_config.update(overwrite)
env_inst = Env.Env(**env_config)
# prepare for xunit test results
xunit_file = os.path.join(env_inst.app_cls.get_log_folder(env_config["test_suite_name"]),
XUNIT_FILE_NAME)
XUNIT_RECEIVER.begin_case(test_func.__name__, time.time(), test_func_file_name)
try:
console_log("starting running test: " + test_func.__name__, color="green")
# execute test function
test_func(env_inst, extra_data)
# if finish without exception, test result is True
result = True
except Exception as e:
# handle all the exceptions here
traceback.print_exc()
result = False
# log failure
XUNIT_RECEIVER.failure(str(e), test_func_file_name)
finally:
# do close all DUTs
env_inst.close()
# end case and output result
XUNIT_RECEIVER.end_case(test_func.__name__, time.time())
with open(xunit_file, "ab+") as f:
f.write(xunitgen.toxml(XUNIT_RECEIVER.results(),
XUNIT_DEFAULT_TEST_SUITE))
if result:
console_log("Test Succeed: " + test_func.__name__, color="green")
else:
console_log(("Test Fail: " + test_func.__name__), color="red")
TestResult.set_result(result, test_func.__name__)
return result
handle_test.case_info = case_info
handle_test.test_method = True
return handle_test
return test

View file

@ -0,0 +1,199 @@
# Copyright 2015-2017 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.
"""
Processing case config files.
This is mainly designed for CI, we need to auto create and assign test jobs.
Template Config File::
TestConfig:
app:
path: Users/Test/TinyTestFW/IDF/IDFApp.py
class: Example
dut:
path:
class:
config_file: /somewhere/config_file_for_runner
test_name: CI_test_job_1
Filter:
chip: ESP32
env_tag: default
CaseConfig:
- name: test_examples_protocol_https_request
# optional
extra_data: some extra data passed to case with kwarg extra_data
overwrite: # overwrite test configs
app:
path: Users/Test/TinyTestFW/IDF/IDFApp.py
class: Example
- name: xxx
"""
# TODO: add a function to use suitable import lib for python2 and python3
import imp
import yaml
import TestCase
def _filter_one_case(test_method, case_filter):
""" Apply filter for one case (the filter logic is the same as described in ``filter_test_cases``) """
filter_result = True
for key in case_filter:
if key in test_method.case_info:
# the filter key is both in case and filter
# we need to check if they match
filter_item, accepted_item = case_filter[key], test_method.case_info[key]
if isinstance(filter_item, (tuple, list)) \
and isinstance(accepted_item, (tuple, list)):
# both list/tuple, check if they have common item
filter_result = True if set(filter_item) & set(accepted_item) else False
elif isinstance(filter_item, (tuple, list)):
# filter item list/tuple, check if case accepted value in filter item list/tuple
filter_result = True if accepted_item in filter_item else False
elif isinstance(accepted_item, (tuple, list)):
# accepted item list/tuple, check if case filter value is in accept item list/tuple
filter_result = True if filter_item in accepted_item else False
else:
# both string/int, just do string compare
filter_result = (filter_item == accepted_item)
else:
# key in filter only, which means the case supports all values for this filter key, match succeed
pass
if not filter_result:
# match failed
break
return filter_result
def filter_test_cases(test_methods, case_filter):
"""
filter test case. filter logic:
1. if filter key both in case attribute and filter:
* if both value is string/int, then directly compare
* if one is list/tuple, the other one is string/int, then check if string/int is in list/tuple
* if both are list/tuple, then check if they have common item
2. if only case attribute or filter have the key, filter succeed
for example, the following are match succeed scenarios
(the rule is symmetric, result is same if exchange values for user filter and case attribute):
* user case filter is ``chip: ["esp32", "esp32c"]``, case doesn't have ``chip`` attribute
* user case filter is ``chip: ["esp32", "esp32c"]``, case attribute is ``chip: "esp32"``
* user case filter is ``chip: "esp32"``, case attribute is ``chip: "esp32"``
:param test_methods: a list of test methods functions
:param case_filter: case filter
:return: filtered test methods
"""
filtered_test_methods = []
for test_method in test_methods:
if _filter_one_case(test_method, case_filter):
filtered_test_methods.append(test_method)
return filtered_test_methods
class Parser(object):
DEFAULT_CONFIG = {
"TestConfig": dict(),
"Filter": dict(),
"CaseConfig": [{"extra_data": None}],
}
@classmethod
def parse_config_file(cls, config_file):
"""
parse from config file and then update to default config.
:param config_file: config file path
:return: configs
"""
configs = cls.DEFAULT_CONFIG.copy()
if config_file:
with open(config_file, "r") as f:
configs.update(yaml.load(f))
return configs
@classmethod
def handle_overwrite_args(cls, overwrite):
"""
handle overwrite configs. import module from path and then get the required class.
:param overwrite: overwrite args
:return: dict of (original key: class)
"""
output = dict()
for key in overwrite:
_path = overwrite[key]["path"]
# TODO: add a function to use suitable import lib for python2 and python3
_module = imp.load_source(str(hash(_path)), overwrite[key]["path"])
output[key] = _module.__getattribute__(overwrite[key]["class"])
return output
@classmethod
def apply_config(cls, test_methods, config_file):
"""
apply config for test methods
:param test_methods: a list of test methods functions
:param config_file: case filter file
:return: filtered cases
"""
configs = cls.parse_config_file(config_file)
test_case_list = []
for _config in configs["CaseConfig"]:
_filter = configs["Filter"].copy()
_filter.update(_config)
_overwrite = cls.handle_overwrite_args(_filter.pop("overwrite", dict()))
_extra_data = _filter.pop("extra_data", None)
for test_method in test_methods:
if _filter_one_case(test_method, _filter):
test_case_list.append(TestCase.TestCase(test_method, _extra_data, **_overwrite))
return test_case_list
class Generator(object):
""" Case config file generator """
def __init__(self):
self.default_config = {
"TestConfig": dict(),
"Filter": dict(),
}
def set_default_configs(self, test_config, case_filter):
"""
:param test_config: "TestConfig" value
:param case_filter: "Filter" value
:return: None
"""
self.default_config = {"TestConfig": test_config, "Filter": case_filter}
def generate_config(self, case_configs, output_file):
"""
:param case_configs: "CaseConfig" value
:param output_file: output file path
:return: None
"""
config = self.default_config.copy()
config.update({"CaseConfig": case_configs})
with open(output_file, "w") as f:
yaml.dump(config, f)

View file

@ -0,0 +1,73 @@
# Copyright 2015-2017 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.
import os
import yaml
class Job(dict):
"""
Gitlab CI job
:param job: job data loaded from .gitlab-ci.yml
:param job_name: job name
"""
def __init__(self, job, job_name):
super(Job, self).__init__(job)
self["name"] = job_name
def match_group(self, group):
"""
Match group by tags of job.
All filters values of group should be included in tags.
:param group: case group to match
:return: True or False
"""
match_result = False
for _ in range(1):
if "case group" in self:
# this job is already assigned
break
for value in group.filters.values():
if value not in self["tags"]:
break
else:
continue
break
else:
match_result = True
return match_result
def assign_group(self, group):
"""
assign a case group to a test job.
:param group: the case group to assign
"""
self["case group"] = group
def output_config(self, file_path):
"""
output test config to the given path.
file name will be job_name.yml
:param file_path: output file path
:return: None
"""
file_name = os.path.join(file_path, self["name"] + ".yml")
if "case group" in self:
with open(file_name, "w") as f:
yaml.dump(self["case group"].output(), f)

View file

@ -0,0 +1,112 @@
# Copyright 2015-2017 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.
""" search test cases from a given file or path """
import os
import fnmatch
import types
import copy
# TODO: add a function to use suitable import lib for python2 and python3
import imp
class Search(object):
TEST_CASE_FILE_PATTERN = "*_test.py"
@classmethod
def _search_cases_from_file(cls, file_name):
""" get test cases from test case .py file """
print("Try to get cases from: " + file_name)
test_functions = []
try:
# TODO: add a function to use suitable import lib for python2 and python3
mod = imp.load_source(str(hash(file_name)), file_name)
for func in [mod.__getattribute__(x) for x in dir(mod)
if isinstance(mod.__getattribute__(x), types.FunctionType)]:
try:
# test method decorator will add test_method attribute to test function
if func.test_method:
test_functions.append(func)
except AttributeError:
continue
except ImportError as e:
print("ImportError: \r\n\tFile:" + file_name + "\r\n\tError:" + str(e))
for i, test_function in enumerate(test_functions):
print("\t{}. ".format(i+1) + test_function.case_info["name"])
return test_functions
@classmethod
def _search_test_case_files(cls, test_case, file_pattern):
""" search all test case files recursively of a path """
if not os.path.exists(test_case):
raise OSError("test case path not exist")
if os.path.isdir(test_case):
test_case_files = []
for root, _, file_names in os.walk(test_case):
for filename in fnmatch.filter(file_names, file_pattern):
test_case_files.append(os.path.join(root, filename))
else:
test_case_files = [test_case]
return test_case_files
@classmethod
def replicate_case(cls, case):
"""
Replicate cases according to its filter values.
If one case has specified filter chip=(ESP32, ESP32C),
it will create 2 cases, one for ESP32 and on for ESP32C.
Once the cases are replicated, it's easy to filter those we want to execute.
:param case: the original case
:return: a list of replicated cases
"""
replicate_config = []
for key in case.case_info:
if isinstance(case.case_info[key], (list, tuple)):
replicate_config.append(key)
def _replicate_for_key(case_list, replicate_key, replicate_list):
case_out = []
for _case in case_list:
for value in replicate_list:
new_case = copy.deepcopy(_case)
new_case.case_info[replicate_key] = value
case_out.append(new_case)
return case_out
replicated_cases = [case]
for key in replicate_config:
replicated_cases = _replicate_for_key(replicated_cases, key, case.case_info[key])
return replicated_cases
@classmethod
def search_test_cases(cls, test_case):
"""
search all test cases from a folder or file, and then do case replicate.
:param test_case: test case file(s) path
:return: a list of replicated test methods
"""
test_case_files = cls._search_test_case_files(test_case, cls.TEST_CASE_FILE_PATTERN)
test_cases = []
for test_case_file in test_case_files:
test_cases += cls._search_cases_from_file(test_case_file)
# handle replicate cases
test_case_out = []
for case in test_cases:
test_case_out += cls.replicate_case(case)
return test_case_out

View file

View file

@ -0,0 +1,26 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXAPI = sphinx-apidoc
SPHINXAPISRC = ..
SPHINXBUILD = python -msphinx
SPHINXPROJ = TinyTestFW
SOURCEDIR = .
BUILDDIR = _build
# define the files to be excluded here
EXCLUEDLIST = "$(SPHINXAPISRC)/example.py"
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXAPI) -o $(SOURCEDIR) $(SPHINXAPISRC) $(EXCLUEDLIST)
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

0
tools/tiny-test-fw/docs/_static/.keep vendored Normal file
View file

View file

@ -0,0 +1,159 @@
# -*- coding: utf-8 -*-
#
# TinyTestFW documentation build configuration file, created by
# sphinx-quickstart on Thu Sep 21 20:19:12 2017.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import sys
sys.path.insert(0, os.path.abspath('..'))
# import sphinx_rtd_theme
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['sphinx.ext.autodoc',
'sphinx.ext.viewcode']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'TinyTestFW'
copyright = u'2017, Espressif'
author = u'Espressif'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = u'0.1'
# The full version, including alpha/beta/rc tags.
release = u'0.1'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# -- Options for HTMLHelp output ------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'TinyTestFWdoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'TinyTestFW.tex', u'TinyTestFW Documentation',
u'He Yinling', 'manual'),
]
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'tinytestfw', u'TinyTestFW Documentation',
[author], 1)
]
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'TinyTestFW', u'TinyTestFW Documentation',
author, 'TinyTestFW', 'One line description of project.',
'Miscellaneous'),
]

View file

@ -0,0 +1,139 @@
.. TinyTestFW documentation master file, created by
sphinx-quickstart on Thu Sep 21 20:19:12 2017.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to TinyTestFW's documentation!
======================================
We have a lot of test which depends on interact with DUT via communication port.
Usually we send command to the port and then check response to see if the test succeed.
TinyTestFW is designed for such scenarios.
It supports ESP-IDF applications and is able for other applications by writing new bundles.
Test FW features
----------------
1. Test Environment:
1. DUT: DUT provides methods to interact with DUT
* read/write through port
* expect method which supports expect one or multiple string or RegEx
* tool methods provided by the tool bundle, like ``start_app``, ``reset``
2. App:
* provide some specific features to the test application of DUT, for example:
* SDK path
* SDK tools
* application information like partition table, download configs
3. Environment Configs:
* support get env configs from config file or auto-detect from current PC
* provide ``get_variable`` method to get variables
2. allow to customize components (DUT, App) to support different devices
3. Integrate to CI:
* provide interfaces for Gitlab-CI
* provide ``search case`` and ``runner`` interfaces, able to integrate with other CI
Example
-------
Let's first check a simple simple::
import re
import os
import sys
test_fw_path = os.getenv("TEST_FW_PATH")
if test_fw_path:
sys.path.insert(0, test_fw_path)
import TinyFW
from IDF import IDFApp, IDFDUT
@TinyFW.test_method(app=IDFApp.Example, dut=IDFDUT.IDFDUT, env_tag="Example_WIFI",
chip="ESP32", module="examples", execution_time=1)
def test_examples_protocol_https_request(env, extra_data):
"""
steps: |
1. join AP
2. connect to www.howsmyssl.com:443
3. send http request
"""
dut1 = env.get_dut("https_request", "examples/protocols/https_request")
dut1.start_app()
dut1.expect("Connecting to www.howsmyssl.com:443", timeout=30)
dut1.expect("Performing the SSL/TLS handshake")
dut1.expect("Certificate verified.", timeout=15)
dut1.expect_all(re.compile(r"Cipher suite is TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256"),
"Reading HTTP response",
timeout=20)
dut1.expect(re.compile(r"Completed (\d) requests"))
if __name__ == '__main__':
TinyFW.set_default_config(config_file="EnvConfigTemplate.yml")
test_examples_protocol_https_request()
SOP for adding test cases
-------------------------
1. import test framework:
^^^^^^^^^^^^^^^^^^^^^^^^^
* we assume ``TEST_FW_PATH`` is pre-defined before running the tests
* Then we can import python packages and files from ``TEST_FW_PATH``
2. define test case:
^^^^^^^^^^^^^^^^^^^^
1. define test case ``test_xxx(env, extra_data)``
* env: instance of test env, see :doc:`Test Env <Env>` for details
* extra_data: extra data passed from test case caller
2. add decorator for test case
* add decorator ``TinyFW.test_method`` to test method
* define default case configs and filters in decorator, see :doc:`TinyFW.test_method <TinyFW>`
3. execute test cases:
^^^^^^^^^^^^^^^^^^^^^^
* define in ``main`` section and execute from this file
1. set preset configs(optional). If the config is not define in case decorator, it will use the preset configs.
2. call test case method:
* if you don't pass any arguments, it will use default values
* you can pass ``extra_data`` to test case by adding ``extra_data=some_data`` as kwarg of test case method.
default value for extra_data is None.
* you can overwrite test case config by adding them as kwarg of test case method.
It will overwrite preset configs and case default configs.
Examples::
test_examples_protocol_https_request(extra_data=["data1", "data2"], dut=SomeOtherDUT, env_tag="OtherEnv")
* or, use ``runner`` to execute. see :doc:`runner <Runner>` for details
.. toctree::
:maxdepth: 2
:caption: Contents:
modules
Dependency
==========
Support for both Python2 and Python3 (tested on python 2.7.13 and 3.6.2).
The following 3rd party lib is required:
* pyserial
* pyyaml
* xunitgen
To build document, we need to install ``Sphinx`` and ``sphinx-rtd-theme`` (you may replace this with your own theme).
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

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