Merge branch 'master' into feature/wps

This commit is contained in:
Xia Xiao Tian 2016-11-14 15:24:12 +08:00
commit c625f0e906
121 changed files with 5797 additions and 1719 deletions

3
.gitmodules vendored
View file

@ -7,3 +7,6 @@
[submodule "components/bt/lib"]
path = components/bt/lib
url = https://github.com/espressif/esp32-bt-lib.git
[submodule "components/micro-ecc/micro-ecc"]
path = components/micro-ecc/micro-ecc
url = https://github.com/kmackay/micro-ecc.git

View file

@ -39,6 +39,7 @@ If this process passes, it will be merged onto the public github repository.
Legal Part
----------
Before a contribution can be accepted, you will need to sign our `Contributor Agreement <http://esp-idf.readthedocs.io/en/latest/contributing.html>`_. You will be prompted for this automatically as part of the Pull Request process.
Before a contribution can be accepted, you will need to sign our :doc:`contributor-agreement`. You will be prompted for this automatically as part of the Pull Request process.

View file

@ -20,12 +20,99 @@ config LOG_BOOTLOADER_LEVEL_VERBOSE
endchoice
config LOG_BOOTLOADER_LEVEL
int
default 0 if LOG_BOOTLOADER_LEVEL_NONE
default 1 if LOG_BOOTLOADER_LEVEL_ERROR
default 2 if LOG_BOOTLOADER_LEVEL_WARN
default 3 if LOG_BOOTLOADER_LEVEL_INFO
default 4 if LOG_BOOTLOADER_LEVEL_DEBUG
default 5 if LOG_BOOTLOADER_LEVEL_VERBOSE
int
default 0 if LOG_BOOTLOADER_LEVEL_NONE
default 1 if LOG_BOOTLOADER_LEVEL_ERROR
default 2 if LOG_BOOTLOADER_LEVEL_WARN
default 3 if LOG_BOOTLOADER_LEVEL_INFO
default 4 if LOG_BOOTLOADER_LEVEL_DEBUG
default 5 if LOG_BOOTLOADER_LEVEL_VERBOSE
endmenu
menu "Secure boot configuration"
choice SECURE_BOOTLOADER
bool "Secure bootloader"
default SECURE_BOOTLOADER_DISABLED
help
Build a bootloader with the secure boot flag enabled.
Secure bootloader can be one-time-flash (chip will only ever
boot that particular bootloader), or a digest key can be used
to allow the secure bootloader to be re-flashed with
modifications. Secure boot also permanently disables JTAG.
See docs/security/secure-boot.rst for details.
config SECURE_BOOTLOADER_DISABLED
bool "Disabled"
config SECURE_BOOTLOADER_ONE_TIME_FLASH
bool "One-time flash"
help
On first boot, the bootloader will generate a key which is not readable externally or by software. A digest is generated from the bootloader image itself. This digest will be verified on each subsequent boot.
Enabling this option means that the bootloader cannot be changed after the first time it is booted.
config SECURE_BOOTLOADER_REFLASHABLE
bool "Reflashable"
help
Generate a reusable secure bootloader key, derived (via SHA-256) from the secure boot signing key.
This allows the secure bootloader to be re-flashed by anyone with access to the secure boot signing key.
This option is less secure than one-time flash, because a leak of the digest key from one device allows reflashing of any device that uses it.
endchoice
config SECURE_BOOT_SIGNING_KEY
string "Secure boot signing key"
depends on SECURE_BOOTLOADER_ENABLED
default secure_boot_signing_key.pem
help
Path to the key file used to sign partition tables and app images for secure boot.
Key file is an ECDSA private key (NIST256p curve) in PEM format.
Path is evaluated relative to the project directory.
You can generate a new signing key by running the following command:
espsecure.py generate_signing_key secure_boot_signing_key.pem
See docs/security/secure-boot.rst for details.
config SECURE_BOOT_DISABLE_JTAG
bool "First boot: Permanently disable JTAG"
depends on SECURE_BOOTLOADER_ENABLED
default Y
help
Bootloader permanently disable JTAG (across entire chip) when enabling secure boot. This happens on first boot of the bootloader.
It is recommended this option remains set for production environments.
config SECURE_BOOT_DISABLE_ROM_BASIC
bool "First boot: Permanently disable ROM BASIC fallback"
depends on SECURE_BOOTLOADER_ENABLED
default Y
help
Bootloader permanently disables ROM BASIC (on UART console) as a fallback if the bootloader image becomes invalid. This happens on first boot.
It is recommended this option remains set in production environments.
config SECURE_BOOT_TEST_MODE
bool "Test mode: don't actually enable secure boot"
depends on SECURE_BOOTLOADER_ENABLED
default N
help
If this option is set, all permanent secure boot changes (via Efuse) are disabled.
This option is for testing purposes only - it effectively completely disables secure boot protection.
config SECURE_BOOTLOADER_ENABLED
bool
default SECURE_BOOTLOADER_ONE_TIME_FLASH || SECURE_BOOTLOADER_REFLASHABLE
endmenu

View file

@ -13,45 +13,110 @@ ifndef IS_BOOTLOADER_BUILD
BOOTLOADER_COMPONENT_PATH := $(COMPONENT_PATH)
BOOTLOADER_BUILD_DIR=$(abspath $(BUILD_DIR_BASE)/bootloader)
BOOTLOADER_BIN=$(BOOTLOADER_BUILD_DIR)/bootloader.bin
BOOTLOADER_SDKCONFIG=$(BOOTLOADER_BUILD_DIR)/sdkconfig
# signing key path is resolved relative to the project directory
SECURE_BOOT_SIGNING_KEY=$(abspath $(call dequote,$(CONFIG_SECURE_BOOT_SIGNING_KEY)))
export SECURE_BOOT_SIGNING_KEY # used by bootloader_support component
# Custom recursive make for bootloader sub-project
BOOTLOADER_MAKE=+$(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src \
V=$(V) SDKCONFIG=$(BOOTLOADER_SDKCONFIG) \
BUILD_DIR_BASE=$(BOOTLOADER_BUILD_DIR) \
V=$(V) BUILD_DIR_BASE=$(BOOTLOADER_BUILD_DIR)
.PHONY: bootloader-clean bootloader-flash bootloader $(BOOTLOADER_BIN)
$(BOOTLOADER_BIN): | $(BOOTLOADER_BUILD_DIR)/sdkconfig
$(Q) $(BOOTLOADER_MAKE) $@
bootloader-clean:
$(Q) $(BOOTLOADER_MAKE) app-clean config-clean
$(Q) rm -f $(BOOTLOADER_SDKCONFIG) $(BOOTLOADER_SDKCONFIG).old
$(BOOTLOADER_BIN): $(SDKCONFIG_MAKEFILE)
$(BOOTLOADER_MAKE) $@
clean: bootloader-clean
bootloader: $(BOOTLOADER_BIN)
@echo "Bootloader built. Default flash command is:"
@echo "$(ESPTOOLPY_WRITE_FLASH) 0x1000 $(BOOTLOADER_BIN)"
ifdef CONFIG_SECURE_BOOTLOADER_DISABLED
# If secure boot disabled, bootloader flashing is integrated
# with 'make flash' and no warnings are printed.
all_binaries: $(BOOTLOADER_BIN)
bootloader: $(BOOTLOADER_BIN)
@echo $(SEPARATOR)
@echo "Bootloader built. Default flash command is:"
@echo "$(ESPTOOLPY_WRITE_FLASH) 0x1000 $^"
ESPTOOL_ALL_FLASH_ARGS += 0x1000 $(BOOTLOADER_BIN)
# bootloader-flash calls flash in the bootloader dummy project
bootloader-flash: $(BOOTLOADER_BIN)
$(BOOTLOADER_MAKE) flash
$(ESPTOOLPY_WRITE_FLASH) 0x1000 $^
# synchronise the project level config to the bootloader's
# config
$(BOOTLOADER_SDKCONFIG): $(PROJECT_PATH)/sdkconfig | $(BOOTLOADER_BUILD_DIR)
$(Q) cp $< $@
else ifdef CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH
#### TEMPORARILY DISABLE THIS OPTION
ifneq ("$(IDF_INSECURE_SECURE_BOOT)","1")
bootloader:
@echo "Secure boot features are not yet mature, so the current secure bootloader will not properly secure the device"
@echo "If you flash this bootloader, you will be left with an non-updateable bootloader that is missing features."
@echo "If you really want to do this, set the environment variable IDF_INSECURE_SECURE_BOOT=1 and rerun make."
exit 1
else
# One time flashing requires user to run esptool.py command themselves,
# and warning is printed about inability to reflash.
bootloader: $(BOOTLOADER_BIN)
@echo $(SEPARATOR)
@echo "Bootloader built. One-time flash command is:"
@echo "$(ESPTOOLPY_WRITE_FLASH) 0x1000 $(BOOTLOADER_BIN)"
@echo $(SEPARATOR)
@echo "* IMPORTANT: After first boot, BOOTLOADER CANNOT BE RE-FLASHED on same device"
endif # IDF_INSECURE_SECURE_BOOT
else ifdef CONFIG_SECURE_BOOTLOADER_REFLASHABLE
# Reflashable secure bootloader
# generates a digest binary (bootloader + digest)
#### TEMPORARILY DISABLE THIS OPTION
ifneq ("$(IDF_INSECURE_SECURE_BOOT)","1")
bootloader:
@echo "Secure boot features are not yet mature, so the current secure bootloader will not properly secure the device."
@echo "If using this feature, expect to reflash the bootloader at least one more time."
@echo "If you really want to do this, set the environment variable IDF_INSECURE_SECURE_BOOT=1 and rerun make."
exit 1
else
BOOTLOADER_DIGEST_BIN := $(BOOTLOADER_BUILD_DIR)/bootloader-reflash-digest.bin
SECURE_BOOTLOADER_KEY := $(BOOTLOADER_BUILD_DIR)/secure-bootloader-key.bin
$(SECURE_BOOTLOADER_KEY): $(SECURE_BOOT_SIGNING_KEY)
$(Q) $(ESPSECUREPY) digest_private_key -k $< $@
bootloader: $(BOOTLOADER_DIGEST_BIN)
@echo $(SEPARATOR)
@echo "Bootloader built and secure digest generated. First time flash command is:"
@echo "$(ESPEFUSEPY) burn_key secure_boot $(SECURE_BOOTLOADER_KEY)"
@echo "$(ESPTOOLPY_WRITE_FLASH) 0x1000 $(BOOTLOADER_BIN)"
@echo $(SEPARATOR)
@echo "To reflash the bootloader after initial flash:"
@echo "$(ESPTOOLPY_WRITE_FLASH) 0x0 $(BOOTLOADER_DIGEST_BIN)"
@echo $(SEPARATOR)
@echo "* After first boot, only re-flashes of this kind (with same key) will be accepted."
@echo "* Not recommended to re-use the same secure boot keyfile on multiple production devices."
$(BOOTLOADER_DIGEST_BIN): $(BOOTLOADER_BIN) $(SECURE_BOOTLOADER_KEY)
@echo "DIGEST $(notdir $@)"
$(Q) $(ESPSECUREPY) digest_secure_bootloader -k $(SECURE_BOOTLOADER_KEY) -o $@ $<
endif # IDF_INSECURE_SECURE_BOOT
else
bootloader:
@echo "Invalid bootloader target: bad sdkconfig?"
@exit 1
endif
all_binaries: $(BOOTLOADER_BIN)
bootloader-clean:
$(BOOTLOADER_MAKE) app-clean
rm -f $(SECURE_BOOTLOADER_KEY) $(BOOTLOADER_DIGEST_BIN)
$(BOOTLOADER_BUILD_DIR):
$(Q) mkdir -p $@
mkdir -p $@
else
CFLAGS += -D BOOTLOADER_BUILD=1 -I $(IDF_PATH)/components/esp32/include
CFLAGS += -D BOOTLOADER_BUILD=1 -I $(IDF_PATH)/components/esp32/include
endif

View file

@ -4,7 +4,10 @@
#
PROJECT_NAME := bootloader
COMPONENTS := esptool_py bootloader log spi_flash
#We cannot include the esp32 component directly but we need its includes.
#This is fixed by adding CFLAGS from Makefile.projbuild
COMPONENTS := esptool_py bootloader bootloader_support log spi_flash micro-ecc
# The bootloader pseudo-component is also included in this build, for its Kconfig.projbuild to be included.
#
@ -12,7 +15,7 @@ COMPONENTS := esptool_py bootloader log spi_flash
IS_BOOTLOADER_BUILD := 1
export IS_BOOTLOADER_BUILD
#We cannot include the esp32 component directly but we need its includes.
#This is fixed by adding CFLAGS from Makefile.projbuild
# include the top-level "project" include directory, for sdkconfig.h
CFLAGS += -I$(BUILD_DIR_BASE)/../include
include $(IDF_PATH)/make/project.mk

View file

@ -25,8 +25,6 @@ extern "C"
#define BOOT_VERSION "V0.1"
#define SPI_SEC_SIZE 0x1000
#define MEM_CACHE(offset) (uint8_t *)(0x3f400000 + (offset))
#define CACHE_READ_32(offset) ((uint32_t *)(0x3f400000 + (offset)))
#define IROM_LOW 0x400D0000
#define IROM_HIGH 0x40400000
#define DROM_LOW 0x3F400000
@ -36,7 +34,6 @@ extern "C"
#define RTC_DATA_LOW 0x50000000
#define RTC_DATA_HIGH 0x50002000
#define PART_TYPE_APP 0x00
#define PART_SUBTYPE_FACTORY 0x00
#define PART_SUBTYPE_OTA_FLAG 0x10
@ -62,12 +59,7 @@ typedef struct {
uint32_t selected_subtype;
} bootloader_state_t;
void boot_cache_redirect( uint32_t pos, size_t size );
uint32_t get_bin_len(uint32_t pos);
bool flash_encrypt(bootloader_state_t *bs);
bool secure_boot(void);
#ifdef __cplusplus
}

View file

@ -33,6 +33,9 @@
#include "soc/timer_group_reg.h"
#include "sdkconfig.h"
#include "esp_image_format.h"
#include "esp_secure_boot.h"
#include "bootloader_flash.h"
#include "bootloader_config.h"
@ -49,7 +52,7 @@ flash cache is down and the app CPU is in reset. We do have a stack, so we can d
extern void Cache_Flush(int);
void bootloader_main();
void unpack_load_app(const esp_partition_pos_t *app_node);
static void unpack_load_app(const esp_partition_pos_t *app_node);
void print_flash_info(const esp_image_header_t* pfhdr);
void set_cache_and_start_app(uint32_t drom_addr,
uint32_t drom_load_addr,
@ -94,53 +97,6 @@ void IRAM_ATTR call_start_cpu0()
bootloader_main();
}
/**
* @function : get_bin_len
* @description: get bin's length
*
* @inputs: pos bin locate address in flash
* @return: uint32 length of bin,if bin MAGIC error return 0
*/
uint32_t get_bin_len(uint32_t pos)
{
uint32_t len = 8 + 16;
uint8_t i;
ESP_LOGD(TAG, "pos %d %x",pos,*(uint8_t *)pos);
if(0xE9 != *(uint8_t *)pos) {
return 0;
}
for (i = 0; i < *(uint8_t *)(pos + 1); i++) {
len += *(uint32_t *)(pos + len + 4) + 8;
}
if (len % 16 != 0) {
len = (len / 16 + 1) * 16;
} else {
len += 16;
}
ESP_LOGD(TAG, "bin length = %d", len);
return len;
}
/**
* @function : boot_cache_redirect
* @description: Configure several pages in flash map so that `size` bytes
* starting at `pos` are mapped to 0x3f400000.
* This sets up mapping only for PRO CPU.
*
* @inputs: pos address in flash
* size size of the area to map, in bytes
*/
void boot_cache_redirect( uint32_t pos, size_t size )
{
uint32_t pos_aligned = pos & 0xffff0000;
uint32_t count = (size + 0xffff) / 0x10000;
Cache_Read_Disable( 0 );
Cache_Flush( 0 );
ESP_LOGD(TAG, "mmu set paddr=%08x count=%d", pos_aligned, count );
cache_flash_mmu_set( 0, 0, 0x3f400000, pos_aligned, 64, count );
Cache_Read_Enable( 0 );
}
/**
* @function : load_partition_table
@ -148,85 +104,103 @@ void boot_cache_redirect( uint32_t pos, size_t size )
* OTA info sector, factory app sector, and test app sector.
*
* @inputs: bs bootloader state structure used to save the data
* addr address of partition table in flash
* @return: return true, if the partition table is loaded (and MD5 checksum is valid)
*
*/
bool load_partition_table(bootloader_state_t* bs, uint32_t addr)
bool load_partition_table(bootloader_state_t* bs)
{
esp_partition_info_t partition;
uint32_t end = addr + 0x1000;
int index = 0;
const esp_partition_info_t *partitions;
const int ESP_PARTITION_TABLE_DATA_LEN = 0xC00; /* length of actual data (signature is appended to this) */
const int MAX_PARTITIONS = ESP_PARTITION_TABLE_DATA_LEN / sizeof(esp_partition_info_t);
char *partition_usage;
ESP_LOGI(TAG, "Partition Table:");
ESP_LOGI(TAG, "## Label Usage Type ST Offset Length");
while (addr < end) {
ESP_LOGD(TAG, "load partition table entry from %x(%08x)", addr, MEM_CACHE(addr));
memcpy(&partition, MEM_CACHE(addr), sizeof(partition));
ESP_LOGD(TAG, "type=%x subtype=%x", partition.type, partition.subtype);
#ifdef CONFIG_SECURE_BOOTLOADER_ENABLED
if(esp_secure_boot_enabled()) {
ESP_LOGI(TAG, "Verifying partition table signature...");
esp_err_t err = esp_secure_boot_verify_signature(ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to verify partition table signature.");
return false;
}
ESP_LOGD(TAG, "Partition table signature verified");
}
#endif
partitions = bootloader_mmap(ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN);
if (!partitions) {
ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN);
return false;
}
ESP_LOGD(TAG, "mapped partition table 0x%x at 0x%x", ESP_PARTITION_TABLE_ADDR, (intptr_t)partitions);
for(int i = 0; i < MAX_PARTITIONS; i++) {
const esp_partition_info_t *partition = &partitions[i];
ESP_LOGD(TAG, "load partition table entry 0x%x", (intptr_t)partition);
ESP_LOGD(TAG, "type=%x subtype=%x", partition->type, partition->subtype);
partition_usage = "unknown";
if (partition.magic == ESP_PARTITION_MAGIC) { /* valid partition definition */
switch(partition.type) {
case PART_TYPE_APP: /* app partition */
switch(partition.subtype) {
case PART_SUBTYPE_FACTORY: /* factory binary */
bs->factory = partition.pos;
partition_usage = "factory app";
break;
case PART_SUBTYPE_TEST: /* test binary */
bs->test = partition.pos;
partition_usage = "test app";
break;
default:
/* OTA binary */
if ((partition.subtype & ~PART_SUBTYPE_OTA_MASK) == PART_SUBTYPE_OTA_FLAG) {
bs->ota[partition.subtype & PART_SUBTYPE_OTA_MASK] = partition.pos;
++bs->app_count;
partition_usage = "OTA app";
}
else {
partition_usage = "Unknown app";
}
break;
if (partition->magic != ESP_PARTITION_MAGIC) {
/* invalid partition definition indicates end-of-table */
break;
}
/* valid partition table */
switch(partition->type) {
case PART_TYPE_APP: /* app partition */
switch(partition->subtype) {
case PART_SUBTYPE_FACTORY: /* factory binary */
bs->factory = partition->pos;
partition_usage = "factory app";
break;
case PART_SUBTYPE_TEST: /* test binary */
bs->test = partition->pos;
partition_usage = "test app";
break;
default:
/* OTA binary */
if ((partition->subtype & ~PART_SUBTYPE_OTA_MASK) == PART_SUBTYPE_OTA_FLAG) {
bs->ota[partition->subtype & PART_SUBTYPE_OTA_MASK] = partition->pos;
++bs->app_count;
partition_usage = "OTA app";
}
break; /* PART_TYPE_APP */
case PART_TYPE_DATA: /* data partition */
switch(partition.subtype) {
case PART_SUBTYPE_DATA_OTA: /* ota data */
bs->ota_info = partition.pos;
partition_usage = "OTA data";
break;
case PART_SUBTYPE_DATA_RF:
partition_usage = "RF data";
break;
case PART_SUBTYPE_DATA_WIFI:
partition_usage = "WiFi data";
break;
default:
partition_usage = "Unknown data";
break;
else {
partition_usage = "Unknown app";
}
break; /* PARTITION_USAGE_DATA */
default: /* other partition type */
break;
}
}
/* invalid partition magic number */
else {
break; /* todo: validate md5 */
break; /* PART_TYPE_APP */
case PART_TYPE_DATA: /* data partition */
switch(partition->subtype) {
case PART_SUBTYPE_DATA_OTA: /* ota data */
bs->ota_info = partition->pos;
partition_usage = "OTA data";
break;
case PART_SUBTYPE_DATA_RF:
partition_usage = "RF data";
break;
case PART_SUBTYPE_DATA_WIFI:
partition_usage = "WiFi data";
break;
default:
partition_usage = "Unknown data";
break;
}
break; /* PARTITION_USAGE_DATA */
default: /* other partition type */
break;
}
/* print partition type info */
ESP_LOGI(TAG, "%2d %-16s %-16s %02x %02x %08x %08x", index, partition.label, partition_usage,
partition.type, partition.subtype,
partition.pos.offset, partition.pos.size);
index++;
addr += sizeof(partition);
ESP_LOGI(TAG, "%2d %-16s %-16s %02x %02x %08x %08x", i, partition->label, partition_usage,
partition->type, partition->subtype,
partition->pos.offset, partition->pos.size);
}
bootloader_munmap(partitions);
ESP_LOGI(TAG,"End of partition table");
return true;
}
@ -254,8 +228,10 @@ void bootloader_main()
esp_image_header_t fhdr;
bootloader_state_t bs;
SpiFlashOpResult spiRet1,spiRet2;
SpiFlashOpResult spiRet1,spiRet2;
esp_ota_select_entry_t sa,sb;
const esp_ota_select_entry_t *ota_select_map;
memset(&bs, 0, sizeof(bs));
ESP_LOGI(TAG, "compile time " __TIME__ );
@ -263,16 +239,17 @@ void bootloader_main()
REG_CLR_BIT( RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_FLASHBOOT_MOD_EN );
REG_CLR_BIT( TIMG_WDTCONFIG0_REG(0), TIMG_WDT_FLASHBOOT_MOD_EN );
SPIUnlock();
/*register first sector in drom0 page 0 */
boot_cache_redirect( 0, 0x5000 );
memcpy((unsigned int *) &fhdr, MEM_CACHE(0x1000), sizeof(esp_image_header_t) );
if(esp_image_load_header(0x1000, &fhdr) != ESP_OK) {
ESP_LOGE(TAG, "failed to load bootloader header!");
return;
}
print_flash_info(&fhdr);
update_flash_config(&fhdr);
if (!load_partition_table(&bs, ESP_PARTITION_TABLE_ADDR)) {
if (!load_partition_table(&bs)) {
ESP_LOGE(TAG, "load partition table error!");
return;
}
@ -281,9 +258,19 @@ void bootloader_main()
if (bs.ota_info.offset != 0) { // check if partition table has OTA info partition
//ESP_LOGE("OTA info sector handling is not implemented");
boot_cache_redirect(bs.ota_info.offset, bs.ota_info.size );
memcpy(&sa,MEM_CACHE(bs.ota_info.offset & 0x0000ffff),sizeof(sa));
memcpy(&sb,MEM_CACHE((bs.ota_info.offset + 0x1000)&0x0000ffff) ,sizeof(sb));
if (bs.ota_info.size < 2 * sizeof(esp_ota_select_entry_t)) {
ESP_LOGE(TAG, "ERROR: ota_info partition size %d is too small (minimum %d bytes)", bs.ota_info.size, sizeof(esp_ota_select_entry_t));
return;
}
ota_select_map = bootloader_mmap(bs.ota_info.offset, bs.ota_info.size);
if (!ota_select_map) {
ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", bs.ota_info.offset, bs.ota_info.size);
return;
}
sa = ota_select_map[0];
sb = ota_select_map[1];
bootloader_munmap(ota_select_map);
if(sa.ota_seq == 0xFFFFFFFF && sb.ota_seq == 0xFFFFFFFF) {
// init status flash
load_part_pos = bs.ota[0];
@ -329,35 +316,61 @@ void bootloader_main()
}
ESP_LOGI(TAG, "Loading app partition at offset %08x", load_part_pos);
if(fhdr.secure_boot_flag == 0x01) {
/* protect the 2nd_boot */
if(false == secure_boot()){
ESP_LOGE(TAG, "secure boot failed");
return;
}
#ifdef CONFIG_SECURE_BOOTLOADER_ENABLED
/* Generate secure digest from this bootloader to protect future
modifications */
esp_err_t err = esp_secure_boot_permanently_enable();
if (err != ESP_OK) {
ESP_LOGE(TAG, "Bootloader digest generation failed (%d). SECURE BOOT IS NOT ENABLED.", err);
/* Allow booting to continue, as the failure is probably
due to user-configured EFUSEs for testing...
*/
}
#endif
if(fhdr.encrypt_flag == 0x01) {
/* encrypt flash */
/* encrypt flash */
if (false == flash_encrypt(&bs)) {
ESP_LOGE(TAG, "flash encrypt failed");
return;
}
}
// copy sections to RAM, set up caches, and start application
// copy loaded segments to RAM, set up caches for mapped segments, and start application
unpack_load_app(&load_part_pos);
}
void unpack_load_app(const esp_partition_pos_t* partition)
static void unpack_load_app(const esp_partition_pos_t* partition)
{
boot_cache_redirect(partition->offset, partition->size);
uint32_t pos = 0;
esp_err_t err;
esp_image_header_t image_header;
memcpy(&image_header, MEM_CACHE(pos), sizeof(image_header));
pos += sizeof(image_header);
uint32_t image_length;
/* TODO: verify the app image as part of OTA boot decision, so can have fallbacks */
err = esp_image_basic_verify(partition->offset, &image_length);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to verify app image @ 0x%x (%d)", partition->offset, err);
return;
}
#ifdef CONFIG_SECURE_BOOTLOADER_ENABLED
if (esp_secure_boot_enabled()) {
ESP_LOGI(TAG, "Verifying app signature @ 0x%x (length 0x%x)", partition->offset, image_length);
err = esp_secure_boot_verify_signature(partition->offset, image_length);
if (err != ESP_OK) {
ESP_LOGE(TAG, "App image @ 0x%x failed signature verification (%d)", partition->offset, err);
return;
}
ESP_LOGD(TAG, "App signature is valid");
}
#endif
if (esp_image_load_header(partition->offset, &image_header) != ESP_OK) {
ESP_LOGE(TAG, "Failed to load app image header @ 0x%x", partition->offset);
return;
}
uint32_t drom_addr = 0;
uint32_t drom_load_addr = 0;
@ -366,24 +379,27 @@ void unpack_load_app(const esp_partition_pos_t* partition)
uint32_t irom_load_addr = 0;
uint32_t irom_size = 0;
/* Reload the RTC memory sections whenever a non-deepsleep reset
/* Reload the RTC memory segments whenever a non-deepsleep reset
is occurring */
bool load_rtc_memory = rtc_get_reset_reason(0) != DEEPSLEEP_RESET;
ESP_LOGD(TAG, "bin_header: %u %u %u %u %08x", image_header.magic,
image_header.blocks,
image_header.segment_count,
image_header.spi_mode,
image_header.spi_size,
(unsigned)image_header.entry_addr);
for (uint32_t section_index = 0;
section_index < image_header.blocks;
++section_index) {
esp_image_section_header_t section_header = {0};
memcpy(&section_header, MEM_CACHE(pos), sizeof(section_header));
pos += sizeof(section_header);
for (int segment = 0; segment < image_header.segment_count; segment++) {
esp_image_segment_header_t segment_header;
uint32_t data_offs;
if(esp_image_load_segment_header(segment, partition->offset,
&image_header, &segment_header,
&data_offs) != ESP_OK) {
ESP_LOGE(TAG, "failed to load segment header #%d", segment);
return;
}
const uint32_t address = section_header.load_addr;
const uint32_t address = segment_header.load_addr;
bool load = true;
bool map = false;
if (address == 0x00000000) { // padding, ignore block
@ -395,47 +411,50 @@ void unpack_load_app(const esp_partition_pos_t* partition)
}
if (address >= DROM_LOW && address < DROM_HIGH) {
ESP_LOGD(TAG, "found drom section, map from %08x to %08x", pos,
section_header.load_addr);
drom_addr = partition->offset + pos - sizeof(section_header);
drom_load_addr = section_header.load_addr;
drom_size = section_header.data_len + sizeof(section_header);
ESP_LOGD(TAG, "found drom segment, map from %08x to %08x", data_offs,
segment_header.load_addr);
drom_addr = data_offs;
drom_load_addr = segment_header.load_addr;
drom_size = segment_header.data_len + sizeof(segment_header);
load = false;
map = true;
}
if (address >= IROM_LOW && address < IROM_HIGH) {
ESP_LOGD(TAG, "found irom section, map from %08x to %08x", pos,
section_header.load_addr);
irom_addr = partition->offset + pos - sizeof(section_header);
irom_load_addr = section_header.load_addr;
irom_size = section_header.data_len + sizeof(section_header);
ESP_LOGD(TAG, "found irom segment, map from %08x to %08x", data_offs,
segment_header.load_addr);
irom_addr = data_offs;
irom_load_addr = segment_header.load_addr;
irom_size = segment_header.data_len + sizeof(segment_header);
load = false;
map = true;
}
if (!load_rtc_memory && address >= RTC_IRAM_LOW && address < RTC_IRAM_HIGH) {
ESP_LOGD(TAG, "Skipping RTC code section at %08x\n", pos);
ESP_LOGD(TAG, "Skipping RTC code segment at %08x\n", data_offs);
load = false;
}
if (!load_rtc_memory && address >= RTC_DATA_LOW && address < RTC_DATA_HIGH) {
ESP_LOGD(TAG, "Skipping RTC data section at %08x\n", pos);
ESP_LOGD(TAG, "Skipping RTC data segment at %08x\n", data_offs);
load = false;
}
ESP_LOGI(TAG, "section %d: paddr=0x%08x vaddr=0x%08x size=0x%05x (%6d) %s", section_index, pos,
section_header.load_addr, section_header.data_len, section_header.data_len, (load)?"load":(map)?"map":"");
ESP_LOGI(TAG, "segment %d: paddr=0x%08x vaddr=0x%08x size=0x%05x (%6d) %s", segment, data_offs - sizeof(esp_image_segment_header_t),
segment_header.load_addr, segment_header.data_len, segment_header.data_len, (load)?"load":(map)?"map":"");
if (!load) {
pos += section_header.data_len;
continue;
if (load) {
const void *data = bootloader_mmap(data_offs, segment_header.data_len);
if(!data) {
ESP_LOGE(TAG, "bootloader_mmap(0x%xc, 0x%x) failed",
data_offs, segment_header.data_len);
return;
}
memcpy((void *)segment_header.load_addr, data, segment_header.data_len);
bootloader_munmap(data);
}
memcpy((void*) section_header.load_addr, MEM_CACHE(pos), section_header.data_len);
pos += section_header.data_len;
}
set_cache_and_start_app(drom_addr,
drom_load_addr,
drom_size,
@ -520,7 +539,7 @@ void print_flash_info(const esp_image_header_t* phdr)
#if (BOOT_LOG_LEVEL >= BOOT_LOG_LEVEL_NOTICE)
ESP_LOGD(TAG, "magic %02x", phdr->magic );
ESP_LOGD(TAG, "blocks %02x", phdr->blocks );
ESP_LOGD(TAG, "segments %02x", phdr->segment_count );
ESP_LOGD(TAG, "spi_mode %02x", phdr->spi_mode );
ESP_LOGD(TAG, "spi_speed %02x", phdr->spi_speed );
ESP_LOGD(TAG, "spi_size %02x", phdr->spi_size );

View file

@ -1,12 +1,9 @@
#
# Main Makefile. This is basically the same as a component makefile.
# Main bootloader Makefile.
#
# This Makefile should, at the very least, just include $(IDF_PATH)/make/component_common.mk. By default,
# this will take the sources in the src/ directory, compile them and link them into
# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
# please read the esp-idf build system document if you need to do this.
# This is basically the same as a component makefile, but in the case of the bootloader
# we pull in bootloader-specific linker arguments.
#
COMPONENT_ADD_LDFLAGS := -L $(abspath .) -lmain -T esp32.bootloader.ld -T $(IDF_PATH)/components/esp32/ld/esp32.rom.ld
COMPONENT_ADD_LDFLAGS := -L $(COMPONENT_PATH) -lmain -T esp32.bootloader.ld -T $(IDF_PATH)/components/esp32/ld/esp32.rom.ld
include $(IDF_PATH)/make/component_common.mk

View file

@ -17,6 +17,7 @@
#include "esp_types.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "esp_err.h"
#include "rom/cache.h"
#include "rom/ets_sys.h"
@ -30,6 +31,7 @@
#include "sdkconfig.h"
#include "bootloader_config.h"
#include "esp_image_format.h"
static const char* TAG = "flash_encrypt";
@ -90,103 +92,97 @@ bool flash_encrypt_write(uint32_t pos, uint32_t len)
Cache_Read_Enable(0);
return true;
}
/**
* @function : flash_encrypt
* @description: encrypt 2nd boot ,partition table ,factory bin <EFBFBD><EFBFBD>test bin (if use)<EFBFBD><EFBFBD>ota bin
* <EFBFBD><EFBFBD>OTA info sector.
*
* @inputs: bs bootloader state structure used to save the data
*
*
* @return: return true, if the encrypt flash success
*
*
*/
bool flash_encrypt(bootloader_state_t *bs)
{
uint32_t bin_len = 0;
uint32_t flash_crypt_cnt = REG_GET_FIELD(EFUSE_BLK0_RDATA0_REG, EFUSE_FLASH_CRYPT_CNT);
uint8_t count = bitcount(flash_crypt_cnt);
int i = 0;
ESP_LOGD(TAG, "flash encrypt cnt %x, bitcount %d", flash_crypt_cnt, count);
esp_err_t err;
uint32_t image_len = 0;
uint32_t flash_crypt_cnt = REG_GET_FIELD(EFUSE_BLK0_RDATA0_REG, EFUSE_FLASH_CRYPT_CNT);
uint8_t count = bitcount(flash_crypt_cnt);
ESP_LOGD(TAG, "flash encrypt cnt %x, bitcount %d", flash_crypt_cnt, count);
if ((count % 2) == 0) {
boot_cache_redirect( 0, 64*1024);
/* encrypt iv and abstruct */
if (false == flash_encrypt_write(0, SPI_SEC_SIZE)) {
ESP_LOGE(TAG, "encrypt iv and abstract error");
return false;
}
if ((count % 2) == 0) {
/* encrypt iv and abstract */
if (false == flash_encrypt_write(0, SPI_SEC_SIZE)) {
ESP_LOGE(TAG, "encrypt iv and abstract error");
return false;
}
/* encrypt bootloader image */
err = esp_image_basic_verify(0x1000, &image_len);
if(err == ESP_OK && image_len != 0) {
if (false == flash_encrypt_write(0x1000, image_len)) {
ESP_LOGE(TAG, "encrypt 2nd boot error");
return false;
}
} else {
ESP_LOGE(TAG, "2nd boot len error");
return false;
}
/* encrypt write boot bin*/
bin_len = get_bin_len((uint32_t)MEM_CACHE(0x1000));
if(bin_len != 0) {
if (false == flash_encrypt_write(0x1000, bin_len)) {
ESP_LOGE(TAG, "encrypt 2nd boot error");
return false;
}
} else {
ESP_LOGE(TAG, "2nd boot len error");
return false;
}
/* encrypt partition table */
if (false == flash_encrypt_write(ESP_PARTITION_TABLE_ADDR, SPI_SEC_SIZE)) {
ESP_LOGE(TAG, "encrypt partition table error");
return false;
}
if (false == flash_encrypt_write(ESP_PARTITION_TABLE_ADDR, SPI_SEC_SIZE)) {
ESP_LOGE(TAG, "encrypt partition table error");
return false;
}
/* encrypt write factory bin */
if(bs->factory.offset != 0x00) {
ESP_LOGD(TAG, "have factory bin");
boot_cache_redirect(bs->factory.offset, bs->factory.size);
bin_len = get_bin_len((uint32_t)MEM_CACHE(bs->factory.offset&0xffff));
if(bin_len != 0) {
if (false == flash_encrypt_write(bs->factory.offset, bin_len)) {
ESP_LOGE(TAG, "encrypt factory bin error");
return false;
}
}
}
if(bs->factory.offset != 0 && bs->factory.size != 0) {
ESP_LOGD(TAG, "have factory bin");
if (false == flash_encrypt_write(bs->factory.offset, bs->factory.size)) {
ESP_LOGE(TAG, "encrypt factory bin error");
return false;
}
}
/* encrypt write test bin */
if(bs->test.offset != 0x00) {
ESP_LOGD(TAG, "have test bin");
boot_cache_redirect(bs->test.offset, bs->test.size);
bin_len = get_bin_len((uint32_t)MEM_CACHE(bs->test.offset&0xffff));
if(bin_len != 0) {
if (false == flash_encrypt_write(bs->test.offset, bin_len)) {
ESP_LOGE(TAG, "encrypt test bin error");
return false;
}
}
}
if(bs->test.offset != 0 && bs->test.size != 0) {
ESP_LOGD(TAG, "have test bin");
if (false == flash_encrypt_write(bs->test.offset, bs->test.size)) {
ESP_LOGE(TAG, "encrypt test bin error");
return false;
}
}
/* encrypt write ota bin */
for (i = 0;i<16;i++) {
if(bs->ota[i].offset != 0x00) {
ESP_LOGD(TAG, "have ota[%d] bin",i);
boot_cache_redirect(bs->ota[i].offset, bs->ota[i].size);
bin_len = get_bin_len((uint32_t)MEM_CACHE(bs->ota[i].offset&0xffff));
if(bin_len != 0) {
if (false == flash_encrypt_write(bs->ota[i].offset, bin_len)) {
ESP_LOGE(TAG, "encrypt ota bin error");
return false;
}
}
}
}
for (int i = 0; i < 16; i++) {
if(bs->ota[i].offset != 0 && bs->ota[i].size != 0) {
ESP_LOGD(TAG, "have ota[%d] bin",i);
if (false == flash_encrypt_write(bs->ota[i].offset, bs->ota[i].size)) {
ESP_LOGE(TAG, "encrypt ota bin error");
return false;
}
}
}
/* encrypt write ota info bin */
if (false == flash_encrypt_write(bs->ota_info.offset, 2*SPI_SEC_SIZE)) {
ESP_LOGE(TAG, "encrypt ota info error");
return false;
}
REG_SET_FIELD(EFUSE_BLK0_WDATA0_REG, EFUSE_FLASH_CRYPT_CNT, 0x04);
REG_WRITE(EFUSE_CONF_REG, 0x5A5A); /* efuse_pgm_op_ena, force no rd/wr disable */
REG_WRITE(EFUSE_CMD_REG, 0x02); /* efuse_pgm_cmd */
while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_pagm_cmd=0 */
ESP_LOGW(TAG, "burn flash_crypt_cnt");
REG_WRITE(EFUSE_CONF_REG, 0x5AA5); /* efuse_read_op_ena, release force */
REG_WRITE(EFUSE_CMD_REG, 0x01); /* efuse_read_cmd */
while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_read_cmd=0 */
return true;
} else {
ESP_LOGI(TAG, "flash already encrypted.");
return true;
}
if (false == flash_encrypt_write(bs->ota_info.offset, 2*SPI_SEC_SIZE)) {
ESP_LOGE(TAG, "encrypt ota info error");
return false;
}
REG_SET_FIELD(EFUSE_BLK0_WDATA0_REG, EFUSE_FLASH_CRYPT_CNT, 0x04);
REG_WRITE(EFUSE_CONF_REG, 0x5A5A); /* efuse_pgm_op_ena, force no rd/wr disable */
REG_WRITE(EFUSE_CMD_REG, 0x02); /* efuse_pgm_cmd */
while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_pagm_cmd=0 */
ESP_LOGW(TAG, "burn flash_crypt_cnt");
REG_WRITE(EFUSE_CONF_REG, 0x5AA5); /* efuse_read_op_ena, release force */
REG_WRITE(EFUSE_CMD_REG, 0x01); /* efuse_read_cmd */
while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_read_cmd=0 */
return true;
} else {
ESP_LOGI(TAG, "flash already encrypted.");
return true;
}
}

View file

@ -1,127 +0,0 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string.h>
#include "esp_attr.h"
#include "esp_types.h"
#include "esp_log.h"
#include "rom/cache.h"
#include "rom/ets_sys.h"
#include "rom/spi_flash.h"
#include "rom/secure_boot.h"
#include "soc/dport_reg.h"
#include "soc/io_mux_reg.h"
#include "soc/efuse_reg.h"
#include "soc/rtc_cntl_reg.h"
#include "sdkconfig.h"
#include "bootloader_config.h"
static const char* TAG = "secure_boot";
/**
* @function : secure_boot_generate
* @description: generate boot abstract & iv
*
* @inputs: bool
*/
bool secure_boot_generate(uint32_t bin_len){
SpiFlashOpResult spiRet;
uint16_t i;
uint32_t buf[32];
if (bin_len % 128 != 0) {
bin_len = (bin_len / 128 + 1) * 128;
}
ets_secure_boot_start();
ets_secure_boot_rd_iv(buf);
ets_secure_boot_hash(NULL);
Cache_Read_Disable(0);
/* iv stored in sec 0 */
spiRet = SPIEraseSector(0);
if (spiRet != SPI_FLASH_RESULT_OK)
{
ESP_LOGE(TAG, SPI_ERROR_LOG);
return false;
}
/* write iv to flash, 0x0000, 128 bytes (1024 bits) */
spiRet = SPIWrite(0, buf, 128);
if (spiRet != SPI_FLASH_RESULT_OK)
{
ESP_LOGE(TAG, SPI_ERROR_LOG);
return false;
}
ESP_LOGD(TAG, "write iv to flash.");
Cache_Read_Enable(0);
/* read 4K code image from flash, for test */
for (i = 0; i < bin_len; i+=128) {
ets_secure_boot_hash((uint32_t *)(0x3f400000 + 0x1000 + i));
}
ets_secure_boot_obtain();
ets_secure_boot_rd_abstract(buf);
ets_secure_boot_finish();
Cache_Read_Disable(0);
/* write abstract to flash, 0x0080, 64 bytes (512 bits) */
spiRet = SPIWrite(0x80, buf, 64);
if (spiRet != SPI_FLASH_RESULT_OK) {
ESP_LOGE(TAG, SPI_ERROR_LOG);
return false;
}
ESP_LOGD(TAG, "write abstract to flash.");
Cache_Read_Enable(0);
return true;
}
/**
* @function : secure_boot
* @description: protect boot code in flash
*
* @inputs: bool
*/
bool secure_boot(void){
uint32_t bin_len = 0;
if (REG_READ(EFUSE_BLK0_RDATA6_REG) & EFUSE_RD_ABS_DONE_0)
{
ESP_LOGD(TAG, "already secure boot !");
return true;
} else {
boot_cache_redirect( 0, 64*1024);
bin_len = get_bin_len((uint32_t)MEM_CACHE(0x1000));
if (bin_len == 0) {
ESP_LOGE(TAG, "boot len is error");
return false;
}
if (false == secure_boot_generate(bin_len)){
ESP_LOGE(TAG, "secure boot generate failed");
return false;
}
}
REG_SET_BIT(EFUSE_BLK0_WDATA6_REG, EFUSE_RD_ABS_DONE_0);
REG_WRITE(EFUSE_CONF_REG, 0x5A5A); /* efuse_pgm_op_ena, force no rd/wr disable */
REG_WRITE(EFUSE_CMD_REG, 0x02); /* efuse_pgm_cmd */
while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_pagm_cmd=0 */
ESP_LOGW(TAG, "burn abstract_done_0");
REG_WRITE(EFUSE_CONF_REG, 0x5AA5); /* efuse_read_op_ena, release force */
REG_WRITE(EFUSE_CMD_REG, 0x01); /* efuse_read_cmd */
while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_read_cmd=0 */
ESP_LOGI(TAG, "read EFUSE_BLK0_RDATA6 %x", REG_READ(EFUSE_BLK0_RDATA6_REG));
return true;
}

View file

@ -0,0 +1,9 @@
Bootloader Support Component
============================
Overview
--------
"Bootloader support" contains APIs which are used by the bootloader but are also needed for the main app.
Code in this component needs to be aware of being executed in a bootloader environment (no RTOS available, BOOTLOADER_BUILD macro set) or in an esp-idf app environment (RTOS running, need locking support.)

View file

@ -0,0 +1,35 @@
COMPONENT_ADD_INCLUDEDIRS := include
COMPONENT_PRIV_INCLUDEDIRS := include_priv
ifdef IS_BOOTLOADER_BUILD
# share "private" headers with the bootloader component
# eventual goal: all functionality that needs this lives in bootloader_support
COMPONENT_ADD_INCLUDEDIRS += include_priv
endif
COMPONENT_SRCDIRS := src
#
# Secure boot signing key support
#
ifdef CONFIG_SECURE_BOOTLOADER_ENABLED
# this path is created relative to the component build directory
SECURE_BOOT_VERIFICATION_KEY := $(abspath signature_verification_key.bin)
$(SECURE_BOOT_SIGNING_KEY):
@echo "Need to generate secure boot signing key."
@echo "One way is to run this command:"
@echo "$(ESPSECUREPY) generate_signing_key $@"
@echo "Keep key file safe after generating."
@echo "(See secure boot documentation for risks & alternatives.)"
@exit 1
$(SECURE_BOOT_VERIFICATION_KEY): $(SECURE_BOOT_SIGNING_KEY)
$(ESPSECUREPY) extract_public_key --keyfile $< $@
COMPONENT_EXTRA_CLEAN += $(SECURE_BOOT_VERIFICATION_KEY)
COMPONENT_EMBED_FILES := $(SECURE_BOOT_VERIFICATION_KEY)
endif

View file

@ -0,0 +1,132 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __ESP32_IMAGE_FORMAT_H
#define __ESP32_IMAGE_FORMAT_H
#include <stdbool.h>
#include <esp_err.h>
#define ESP_ERR_IMAGE_BASE 0x2000
#define ESP_ERR_IMAGE_FLASH_FAIL (ESP_ERR_IMAGE_BASE + 1)
#define ESP_ERR_IMAGE_INVALID (ESP_ERR_IMAGE_BASE + 2)
/* Support for app/bootloader image parsing
Can be compiled as part of app or bootloader code.
*/
/* SPI flash mode, used in esp_image_header_t */
typedef enum {
ESP_IMAGE_SPI_MODE_QIO,
ESP_IMAGE_SPI_MODE_QOUT,
ESP_IMAGE_SPI_MODE_DIO,
ESP_IMAGE_SPI_MODE_DOUT,
ESP_IMAGE_SPI_MODE_FAST_READ,
ESP_IMAGE_SPI_MODE_SLOW_READ
} esp_image_spi_mode_t;
/* SPI flash clock frequency */
enum {
ESP_IMAGE_SPI_SPEED_40M,
ESP_IMAGE_SPI_SPEED_26M,
ESP_IMAGE_SPI_SPEED_20M,
ESP_IMAGE_SPI_SPEED_80M = 0xF
} esp_image_spi_freq_t;
/* Supported SPI flash sizes */
typedef enum {
ESP_IMAGE_FLASH_SIZE_1MB = 0,
ESP_IMAGE_FLASH_SIZE_2MB,
ESP_IMAGE_FLASH_SIZE_4MB,
ESP_IMAGE_FLASH_SIZE_8MB,
ESP_IMAGE_FLASH_SIZE_16MB,
ESP_IMAGE_FLASH_SIZE_MAX
} esp_image_flash_size_t;
#define ESP_IMAGE_HEADER_MAGIC 0xE9
/* Main header of binary image */
typedef struct {
uint8_t magic;
uint8_t segment_count;
uint8_t spi_mode; /* flash read mode (esp_image_spi_mode_t as uint8_t) */
uint8_t spi_speed: 4; /* flash frequency (esp_image_spi_freq_t as uint8_t) */
uint8_t spi_size: 4; /* flash chip size (esp_image_flash_size_t as uint8_t) */
uint32_t entry_addr;
uint8_t encrypt_flag; /* encrypt flag */
uint8_t extra_header[15]; /* ESP32 additional header, unused by second bootloader */
} esp_image_header_t;
/* Header of binary image segment */
typedef struct {
uint32_t load_addr;
uint32_t data_len;
} esp_image_segment_header_t;
/**
* @brief Read an ESP image header from flash.
*
* @param src_addr Address in flash to load image header. Must be 4 byte aligned.
* @param[out] image_header Pointer to an esp_image_header_t struture to be filled with data. If the function fails, contents are undefined.
*
* @return ESP_OK if image header was loaded, ESP_ERR_IMAGE_FLASH_FAIL
* if a SPI flash error occurs, ESP_ERR_IMAGE_INVALID if the image header
* appears invalid.
*/
esp_err_t esp_image_load_header(uint32_t src_addr, esp_image_header_t *image_header);
/**
* @brief Read the segment header and data offset of a segment in the image.
*
* @param index Index of the segment to load information for.
* @param src_addr Base address in flash of the image.
* @param[in] image_header Pointer to the flash image header, already loaded by @ref esp_image_load_header().
* @param[out] segment_header Pointer to a segment header structure to be filled with data. If the function fails, contents are undefined.
* @param[out] segment_data_offset Pointer to the data offset of the segment.
*
* @return ESP_OK if segment_header & segment_data_offset were loaded successfully, ESP_ERR_IMAGE_FLASH_FAIL if a SPI flash error occurs, ESP_ERR_IMAGE_INVALID if the image header appears invalid, ESP_ERR_INVALID_ARG if the index is invalid.
*/
esp_err_t esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const esp_image_header_t *image_header, esp_image_segment_header_t *segment_header, uint32_t *segment_data_offset);
/**
* @brief Return length of an image in flash. Non-cryptographically validates image integrity in the process.
*
* If the image has a secure boot signature appended, the signature is not checked and this length is not included in the result.
*
* Image validation checks:
* - Magic byte
* - No single segment longer than 16MB
* - Total image no longer than 16MB
* - 8 bit image checksum is valid
*
* @param src_addr Offset of the start of the image in flash. Must be 4 byte aligned.
* @param[out] length Length of the image, set to a value if the image is valid. Can be null.
*
* @return ESP_OK if image is valid, ESP_FAIL or ESP_ERR_IMAGE_INVALID on errors.
*
*/
esp_err_t esp_image_basic_verify(uint32_t src_addr, uint32_t *length);
typedef struct {
uint32_t drom_addr;
uint32_t drom_load_addr;
uint32_t drom_size;
uint32_t irom_addr;
uint32_t irom_load_addr;
uint32_t irom_size;
} esp_image_flash_mapping_t;
#endif

View file

@ -0,0 +1,75 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __ESP32_SECUREBOOT_H
#define __ESP32_SECUREBOOT_H
#include <stdbool.h>
#include <esp_err.h>
#include "soc/efuse_reg.h"
/* Support functions for secure boot features.
Can be compiled as part of app or bootloader code.
*/
/** @brief Is secure boot currently enabled in hardware?
*
* Secure boot is enabled if the ABS_DONE_0 efuse is blown. This means
* that the ROM bootloader code will only boot a verified secure
* bootloader digest from now on.
*
* @return true if secure boot is enabled.
*/
static inline bool esp_secure_boot_enabled(void) {
return REG_READ(EFUSE_BLK0_RDATA6_REG) & EFUSE_RD_ABS_DONE_0;
}
/** @brief Enable secure boot if it is not already enabled.
*
* @important If this function succeeds, secure boot is permanently
* enabled on the chip via efuse.
*
* @important This function is intended to be called from bootloader code only.
*
* If secure boot is not yet enabled for bootloader, this will
* generate the secure boot digest and enable secure boot by blowing
* the EFUSE_RD_ABS_DONE_0 efuse.
*
* This function does not verify secure boot of the bootloader (the
* ROM bootloader does this.)
*
* Will fail if efuses have been part-burned in a way that indicates
* secure boot should not or could not be correctly enabled.
*
*
* @return ESP_ERR_INVALID_STATE if efuse state doesn't allow
* secure boot to be enabled cleanly. ESP_OK if secure boot
* is enabled on this chip from now on.
*/
esp_err_t esp_secure_boot_permanently_enable(void);
/** @brief Verify the secure boot signature (determinstic ECDSA w/ SHA256) appended to some binary data in flash.
*
* Public key is compiled into the calling program. See docs/security/secure-boot.rst for details.
*
* @param src_addr Starting offset of the data in flash.
* @param length Length of data in bytes. Signature is appended -after- length bytes.
*
* @return ESP_OK if signature is valid, ESP_ERR_INVALID_STATE if
* signature fails, ESP_FAIL for other failures (ie can't read flash).
*/
esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length);
#endif

View file

@ -0,0 +1,69 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __BOOTLOADER_FLASH_H
#define __BOOTLOADER_FLASH_H
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include <esp_err.h>
/* Provide a Flash API for bootloader_support code,
that can be used from bootloader or app code.
This header is available to source code in the bootloader &
bootloader_support components only.
*/
/**
* @brief Map a region of flash to data memory
*
* @important In bootloader code, only one region can be bootloader_mmaped at once. The previous region must be bootloader_munmapped before another region is mapped.
*
* @important In app code, these functions are not thread safe.
*
* Call bootloader_munmap once for each successful call to bootloader_mmap.
*
* In esp-idf app, this function maps directly to spi_flash_mmap.
*
* @param offset - Starting flash offset to map to memory.
* @param length - Length of data to map.
*
* @return Pointer to mapped data memory (at src_addr), or NULL
* if an allocation error occured.
*/
const void *bootloader_mmap(uint32_t src_addr, uint32_t size);
/**
* @brief Unmap a previously mapped region of flash
*
* Call bootloader_munmap once for each successful call to bootloader_mmap.
*/
void bootloader_munmap(const void *mapping);
/**
* @brief Read data from Flash.
*
* @note Both src and dest have to be 4-byte aligned.
*
* @param src source address of the data in Flash.
* @param dest pointer to the destination buffer
* @param size length of data
*
* @return esp_err_t
*/
esp_err_t bootloader_flash_read(size_t src_addr, void *dest, size_t size);
#endif

View file

@ -0,0 +1,122 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stddef.h>
#include <bootloader_flash.h>
#include <esp_log.h>
#include <esp_spi_flash.h> /* including in bootloader for error values */
#ifndef BOOTLOADER_BUILD
/* Normal app version maps to esp_spi_flash.h operations...
*/
static const char *TAG = "bootloader_mmap";
static spi_flash_mmap_memory_t map;
const void *bootloader_mmap(uint32_t src_addr, uint32_t size)
{
if (map) {
ESP_LOGE(TAG, "tried to bootloader_mmap twice");
return NULL; /* existing mapping in use... */
}
const void *result = NULL;
esp_err_t err = spi_flash_mmap(src_addr, size, SPI_FLASH_MMAP_DATA, &result, &map);
if (err != ESP_OK) {
result = NULL;
}
return result;
}
void bootloader_munmap(const void *mapping)
{
if(mapping && map) {
spi_flash_munmap(map);
}
map = 0;
}
esp_err_t bootloader_flash_read(size_t src, void *dest, size_t size)
{
return spi_flash_read(src, dest, size);
}
#else
/* Bootloader version, uses ROM functions only */
#include <rom/spi_flash.h>
#include <rom/cache.h>
static const char *TAG = "bootloader_flash";
static bool mapped;
const void *bootloader_mmap(uint32_t src_addr, uint32_t size)
{
if (mapped) {
ESP_LOGE(TAG, "tried to bootloader_mmap twice");
return NULL; /* can't map twice */
}
uint32_t src_addr_aligned = src_addr & 0xffff0000;
uint32_t count = (size + (src_addr - src_addr_aligned) + 0xffff) / 0x10000;
Cache_Read_Disable(0);
Cache_Flush(0);
ESP_LOGD(TAG, "mmu set paddr=%08x count=%d", src_addr_aligned, count );
cache_flash_mmu_set( 0, 0, 0x3f400000, src_addr_aligned, 64, count );
Cache_Read_Enable( 0 );
mapped = true;
return (void *)(0x3f400000 + (src_addr - src_addr_aligned));
}
void bootloader_munmap(const void *mapping)
{
if (mapped) {
/* Full MMU reset */
Cache_Read_Disable(0);
Cache_Flush(0);
mmu_init(0);
mapped = false;
}
}
esp_err_t bootloader_flash_read(size_t src_addr, void *dest, size_t size)
{
if(src_addr & 3) {
ESP_LOGE(TAG, "bootloader_flash_read src_addr 0x%x not 4-byte aligned", src_addr);
return ESP_FAIL;
}
if((intptr_t)dest & 3) {
ESP_LOGE(TAG, "bootloader_flash_read dest 0x%x not 4-byte aligned", (intptr_t)dest);
return ESP_FAIL;
}
Cache_Read_Disable(0);
Cache_Flush(0);
SpiFlashOpResult r = SPIRead(src_addr, dest, size);
Cache_Read_Enable(0);
switch(r) {
case SPI_FLASH_RESULT_OK:
return ESP_OK;
case SPI_FLASH_RESULT_ERR:
return ESP_ERR_FLASH_OP_FAIL;
case SPI_FLASH_RESULT_TIMEOUT:
return ESP_ERR_FLASH_OP_TIMEOUT;
default:
return ESP_FAIL;
}
}
#endif

View file

@ -0,0 +1,161 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string.h>
#include <esp_image_format.h>
#include <esp_log.h>
#include <bootloader_flash.h>
const static char *TAG = "esp_image";
#define SIXTEEN_MB 0x1000000
#define ESP_ROM_CHECKSUM_INITIAL 0xEF
esp_err_t esp_image_load_header(uint32_t src_addr, esp_image_header_t *image_header)
{
esp_err_t err;
ESP_LOGD(TAG, "reading image header @ 0x%x", src_addr);
err = bootloader_flash_read(src_addr, image_header, sizeof(esp_image_header_t));
if (err == ESP_OK) {
if (image_header->magic != ESP_IMAGE_HEADER_MAGIC) {
ESP_LOGE(TAG, "image at 0x%x has invalid magic byte", src_addr);
err = ESP_ERR_IMAGE_INVALID;
}
if (image_header->spi_mode > ESP_IMAGE_SPI_MODE_SLOW_READ) {
ESP_LOGW(TAG, "image at 0x%x has invalid SPI mode %d", src_addr, image_header->spi_mode);
}
if (image_header->spi_speed > ESP_IMAGE_SPI_SPEED_80M) {
ESP_LOGW(TAG, "image at 0x%x has invalid SPI speed %d", src_addr, image_header->spi_speed);
}
if (image_header->spi_size > ESP_IMAGE_FLASH_SIZE_MAX) {
ESP_LOGW(TAG, "image at 0x%x has invalid SPI size %d", src_addr, image_header->spi_size);
}
}
if (err != ESP_OK) {
bzero(image_header, sizeof(esp_image_header_t));
}
return err;
}
esp_err_t esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const esp_image_header_t *image_header, esp_image_segment_header_t *segment_header, uint32_t *segment_data_offset)
{
esp_err_t err = ESP_OK;
uint32_t next_addr = src_addr + sizeof(esp_image_header_t);
if(index >= image_header->segment_count) {
ESP_LOGE(TAG, "index %d higher than segment count %d", index, image_header->segment_count);
return ESP_ERR_INVALID_ARG;
}
for(int i = 0; i <= index && err == ESP_OK; i++) {
ESP_LOGV(TAG, "loading segment header %d at offset 0x%x", i, next_addr);
err = bootloader_flash_read(next_addr, segment_header, sizeof(esp_image_segment_header_t));
if (err == ESP_OK) {
if ((segment_header->data_len & 3) != 0
|| segment_header->data_len >= SIXTEEN_MB) {
ESP_LOGE(TAG, "invalid segment length 0x%x", segment_header->data_len);
err = ESP_ERR_IMAGE_INVALID;
}
next_addr += sizeof(esp_image_segment_header_t);
ESP_LOGV(TAG, "segment data length 0x%x data starts 0x%x", segment_header->data_len, next_addr);
*segment_data_offset = next_addr;
next_addr += segment_header->data_len;
}
}
if (err != ESP_OK) {
*segment_data_offset = 0;
bzero(segment_header, sizeof(esp_image_segment_header_t));
}
return err;
}
esp_err_t esp_image_basic_verify(uint32_t src_addr, uint32_t *p_length)
{
esp_err_t err;
uint8_t buf[16];
uint8_t checksum = ESP_ROM_CHECKSUM_INITIAL;
esp_image_header_t image_header;
esp_image_segment_header_t segment_header = { 0 };
uint32_t segment_data_offs = 0;
const uint8_t *segment_data;
uint32_t end_addr;
uint32_t length;
if (p_length != NULL) {
*p_length = 0;
}
err = esp_image_load_header(src_addr, &image_header);
if (err != ESP_OK) {
return err;
}
ESP_LOGD(TAG, "reading %d image segments", image_header.segment_count);
/* Checksum each segment's data */
for (int i = 0; i < image_header.segment_count; i++) {
err = esp_image_load_segment_header(i, src_addr, &image_header,
&segment_header, &segment_data_offs);
if (err != ESP_OK) {
return err;
}
segment_data = bootloader_mmap(segment_data_offs, segment_header.data_len);
if (segment_data == NULL) {
ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", segment_data_offs, segment_header.data_len);
return ESP_FAIL;
}
for(int i = 0; i < segment_header.data_len; i++) {
checksum ^= segment_data[i];
}
bootloader_munmap(segment_data);
}
/* End of image, verify checksum */
end_addr = segment_data_offs + segment_header.data_len;
if (end_addr < src_addr) {
ESP_LOGE(TAG, "image offset has wrapped");
return ESP_ERR_IMAGE_INVALID;
}
length = end_addr - src_addr;
if (length >= SIXTEEN_MB) {
ESP_LOGE(TAG, "invalid total length 0x%x", length);
return ESP_ERR_IMAGE_INVALID;
}
/* image padded to next full 16 byte block, with checksum byte at very end */
ESP_LOGV(TAG, "unpadded image length 0x%x", length);
length += 16; /* always pad by at least 1 byte */
length = length - (length % 16);
ESP_LOGV(TAG, "padded image length 0x%x", length);
ESP_LOGD(TAG, "reading checksum block at 0x%x", src_addr + length - 16);
bootloader_flash_read(src_addr + length - 16, buf, 16);
if (checksum != buf[15]) {
ESP_LOGE(TAG, "checksum failed. Calculated 0x%x read 0x%x",
checksum, buf[15]);
return ESP_ERR_IMAGE_INVALID;
}
if (p_length != NULL) {
*p_length = length;
}
return ESP_OK;
}

View file

@ -0,0 +1,222 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string.h>
#include "esp_attr.h"
#include "esp_types.h"
#include "esp_log.h"
#include "rom/cache.h"
#include "rom/ets_sys.h"
#include "rom/spi_flash.h"
#include "rom/secure_boot.h"
#include "soc/dport_reg.h"
#include "soc/io_mux_reg.h"
#include "soc/efuse_reg.h"
#include "soc/rtc_cntl_reg.h"
#include "sdkconfig.h"
#include "bootloader_flash.h"
#include "esp_image_format.h"
#include "esp_secure_boot.h"
static const char* TAG = "secure_boot";
#define HASH_BLOCK_SIZE 128
#define IV_LEN HASH_BLOCK_SIZE
#define DIGEST_LEN 64
/**
* @function : secure_boot_generate
* @description: generate boot digest (aka "abstract") & iv
*
* @inputs: image_len - length of image to calculate digest for
*/
static bool secure_boot_generate(uint32_t image_len){
SpiFlashOpResult spiRet;
/* buffer is uint32_t not uint8_t to meet ROM SPI API signature */
uint32_t buf[IV_LEN / sizeof(uint32_t)];
const void *image;
/* hardware secure boot engine only takes full blocks, so round up the
image length. The additional data should all be 0xFF.
*/
if (image_len % HASH_BLOCK_SIZE != 0) {
image_len = (image_len / HASH_BLOCK_SIZE + 1) * HASH_BLOCK_SIZE;
}
ets_secure_boot_start();
ets_secure_boot_rd_iv(buf);
ets_secure_boot_hash(NULL);
Cache_Read_Disable(0);
/* iv stored in sec 0 */
spiRet = SPIEraseSector(0);
if (spiRet != SPI_FLASH_RESULT_OK)
{
ESP_LOGE(TAG, "SPI erase failed %d", spiRet);
return false;
}
Cache_Read_Enable(0);
/* write iv to flash, 0x0000, 128 bytes (1024 bits) */
ESP_LOGD(TAG, "write iv to flash.");
spiRet = SPIWrite(0, buf, IV_LEN);
if (spiRet != SPI_FLASH_RESULT_OK)
{
ESP_LOGE(TAG, "SPI write failed %d", spiRet);
return false;
}
bzero(buf, sizeof(buf));
/* generate digest from image contents */
image = bootloader_mmap(0x1000, image_len);
if (!image) {
ESP_LOGE(TAG, "bootloader_mmap(0x1000, 0x%x) failed", image_len);
return false;
}
for (int i = 0; i < image_len; i+= HASH_BLOCK_SIZE) {
ets_secure_boot_hash(image + i/sizeof(void *));
}
bootloader_munmap(image);
ets_secure_boot_obtain();
ets_secure_boot_rd_abstract(buf);
ets_secure_boot_finish();
ESP_LOGD(TAG, "write digest to flash.");
spiRet = SPIWrite(0x80, buf, DIGEST_LEN);
if (spiRet != SPI_FLASH_RESULT_OK) {
ESP_LOGE(TAG, "SPI write failed %d", spiRet);
return false;
}
ESP_LOGD(TAG, "write digest to flash.");
Cache_Read_Enable(0);
return true;
}
/* Burn values written to the efuse write registers */
static inline void burn_efuses()
{
#ifdef CONFIG_SECURE_BOOT_TEST_MODE
ESP_LOGE(TAG, "SECURE BOOT TEST MODE. Not really burning any efuses!");
#else
REG_WRITE(EFUSE_CONF_REG, 0x5A5A); /* efuse_pgm_op_ena, force no rd/wr disable */
REG_WRITE(EFUSE_CMD_REG, 0x02); /* efuse_pgm_cmd */
while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_pagm_cmd=0 */
REG_WRITE(EFUSE_CONF_REG, 0x5AA5); /* efuse_read_op_ena, release force */
REG_WRITE(EFUSE_CMD_REG, 0x01); /* efuse_read_cmd */
while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_read_cmd=0 */
#endif
}
esp_err_t esp_secure_boot_permanently_enable(void) {
esp_err_t err;
uint32_t image_len = 0;
if (esp_secure_boot_enabled())
{
ESP_LOGI(TAG, "bootloader secure boot is already enabled, continuing..");
return ESP_OK;
}
err = esp_image_basic_verify(0x1000, &image_len);
if (err != ESP_OK) {
ESP_LOGE(TAG, "bootloader image appears invalid! error %d", err);
return err;
}
uint32_t dis_reg = REG_READ(EFUSE_BLK0_RDATA0_REG);
bool efuse_key_read_protected = dis_reg & EFUSE_RD_DIS_BLK2;
bool efuse_key_write_protected = dis_reg & EFUSE_WR_DIS_BLK2;
if (efuse_key_read_protected == false
&& efuse_key_write_protected == false
&& REG_READ(EFUSE_BLK2_RDATA0_REG) == 0
&& REG_READ(EFUSE_BLK2_RDATA1_REG) == 0
&& REG_READ(EFUSE_BLK2_RDATA2_REG) == 0
&& REG_READ(EFUSE_BLK2_RDATA3_REG) == 0
&& REG_READ(EFUSE_BLK2_RDATA4_REG) == 0
&& REG_READ(EFUSE_BLK2_RDATA5_REG) == 0
&& REG_READ(EFUSE_BLK2_RDATA6_REG) == 0
&& REG_READ(EFUSE_BLK2_RDATA7_REG) == 0) {
ESP_LOGI(TAG, "Generating new secure boot key...");
/* reuse the secure boot IV generation function to generate
the key, as this generator uses the hardware RNG. */
uint32_t buf[32];
ets_secure_boot_start();
ets_secure_boot_rd_iv(buf);
ets_secure_boot_finish();
for (int i = 0; i < 8; i++) {
ESP_LOGV(TAG, "EFUSE_BLK2_WDATA%d_REG = 0x%08x", i, buf[i]);
REG_WRITE(EFUSE_BLK2_WDATA0_REG + 4*i, buf[i]);
}
bzero(buf, sizeof(buf));
burn_efuses();
ESP_LOGI(TAG, "Read & write protecting new key...");
REG_WRITE(EFUSE_BLK0_WDATA0_REG, EFUSE_WR_DIS_BLK2 | EFUSE_RD_DIS_BLK2);
burn_efuses();
efuse_key_read_protected = true;
efuse_key_write_protected = true;
} else {
ESP_LOGW(TAG, "Using pre-loaded secure boot key in EFUSE block 2");
}
ESP_LOGI(TAG, "Generating secure boot digest...");
if (false == secure_boot_generate(image_len)){
ESP_LOGE(TAG, "secure boot generation failed");
return ESP_FAIL;
}
ESP_LOGI(TAG, "Digest generation complete.");
if (!efuse_key_read_protected) {
ESP_LOGE(TAG, "Pre-loaded key is not read protected. Refusing to blow secure boot efuse.");
return ESP_ERR_INVALID_STATE;
}
if (!efuse_key_write_protected) {
ESP_LOGE(TAG, "Pre-loaded key is not write protected. Refusing to blow secure boot efuse.");
return ESP_ERR_INVALID_STATE;
}
ESP_LOGI(TAG, "blowing secure boot efuse...");
ESP_LOGD(TAG, "before updating, EFUSE_BLK0_RDATA6 %x", REG_READ(EFUSE_BLK0_RDATA6_REG));
uint32_t new_wdata6 = EFUSE_RD_ABS_DONE_0;
#ifdef CONFIG_SECURE_BOOT_DISABLE_JTAG
ESP_LOGI(TAG, "disabling JTAG...");
new_wdata6 |= EFUSE_RD_DISABLE_JTAG;
#endif
#ifdef CONFIG_SECURE_BOOT_DISABLE_UART_BOOTLOADER
ESP_LOGI(TAG, "disabling UART bootloader...");
new_wdata6 |= EFUSE_RD_CONSOLE_DEBUG_DISABLE_S;
#endif
REG_WRITE(EFUSE_BLK0_WDATA6_REG, new_wdata6);
burn_efuses();
uint32_t after = REG_READ(EFUSE_BLK0_RDATA6_REG);
ESP_LOGD(TAG, "after updating, EFUSE_BLK0_RDATA6 %x", after);
if (after & EFUSE_RD_ABS_DONE_0) {
ESP_LOGI(TAG, "secure boot is now enabled for bootloader image");
return ESP_OK;
} else {
#ifdef CONFIG_SECURE_BOOT_TEST_MODE
ESP_LOGE(TAG, "secure boot not enabled due to test mode");
#else
ESP_LOGE(TAG, "secure boot not enabled for bootloader image, EFUSE_RD_ABS_DONE_0 is probably write protected!");
#endif
return ESP_ERR_INVALID_STATE;
}
}

View file

@ -0,0 +1,107 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "sdkconfig.h"
#include "bootloader_flash.h"
#include "esp_log.h"
#include "esp_image_format.h"
#include "esp_secure_boot.h"
#include "uECC.h"
#ifdef BOOTLOADER_BUILD
#include "rom/sha.h"
typedef SHA_CTX sha_context;
#else
#include "hwcrypto/sha.h"
typedef esp_sha_context sha_context;
#endif
typedef struct {
uint32_t version;
uint8_t signature[64];
} signature_block_t;
static const char* TAG = "secure_boot";
extern const uint8_t signature_verification_key_start[] asm("_binary_signature_verification_key_bin_start");
extern const uint8_t signature_verification_key_end[] asm("_binary_signature_verification_key_bin_end");
#define SIGNATURE_VERIFICATION_KEYLEN 64
esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
{
sha_context sha;
uint8_t digest[32];
ptrdiff_t keylen;
const uint8_t *data, *digest_data;
uint32_t digest_len;
const signature_block_t *sigblock;
bool is_valid;
ESP_LOGD(TAG, "verifying signature src_addr 0x%x length 0x%x", src_addr, length);
data = bootloader_mmap(src_addr, length + sizeof(signature_block_t));
if(data == NULL) {
ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", src_addr, length+sizeof(signature_block_t));
return ESP_FAIL;
}
sigblock = (const signature_block_t *)(data + length);
if (sigblock->version != 0) {
ESP_LOGE(TAG, "src 0x%x has invalid signature version field 0x%08x", src_addr, sigblock->version);
goto unmap_and_fail;
}
#ifdef BOOTLOADER_BUILD
/* Use ROM SHA functions directly */
ets_sha_enable();
ets_sha_init(&sha);
digest_len = length * 8;
digest_data = data;
while (digest_len > 0) {
uint32_t chunk_len = (digest_len > 64) ? 64 : digest_len;
ets_sha_update(&sha, SHA2_256, digest_data, chunk_len);
digest_len -= chunk_len;
digest_data += chunk_len / 8;
}
ets_sha_finish(&sha, SHA2_256, digest);
ets_sha_disable();
#else
/* Use thread-safe esp-idf SHA layer */
esp_sha256_init(&sha);
esp_sha256_start(&sha, false);
esp_sha256_update(&sha, data, length);
esp_sha256_finish(&sha, digest);
esp_sha256_free(&sha);
#endif
keylen = signature_verification_key_end - signature_verification_key_start;
if(keylen != SIGNATURE_VERIFICATION_KEYLEN) {
ESP_LOGE(TAG, "Embedded public verification key has wrong length %d", keylen);
goto unmap_and_fail;
}
is_valid = uECC_verify(signature_verification_key_start,
digest, sizeof(digest), sigblock->signature,
uECC_secp256r1());
bootloader_munmap(data);
return is_valid ? ESP_OK : ESP_ERR_IMAGE_INVALID;
unmap_and_fail:
bootloader_munmap(data);
return ESP_FAIL;
}

View file

@ -2,20 +2,16 @@
# Component Makefile
#
#COMPONENT_ADD_INCLUDEDIRS :=
COMPONENT_ADD_INCLUDEDIRS := include
CFLAGS += -Wno-error=unused-label -Wno-error=return-type -Wno-error=missing-braces -Wno-error=pointer-sign -Wno-error=parentheses
LIBS := btdm_app
COMPONENT_ADD_LDFLAGS := -lbt -L$(abspath lib) \
COMPONENT_ADD_LDFLAGS := -lbt -L $(COMPONENT_PATH)/lib \
$(addprefix -l,$(LIBS)) \
$(LINKER_SCRIPTS)
include $(IDF_PATH)/make/component_common.mk
ALL_LIB_FILES := $(patsubst %,$(COMPONENT_PATH)/lib/lib%.a,$(LIBS))
$(COMPONENT_LIBRARY): $(ALL_LIB_FILES)

View file

@ -1,14 +1,8 @@
#
# Component Makefile
#
# This Makefile should, at the very least, just include $(SDK_PATH)/make/component.mk. By default,
# this will take the sources in this directory, compile them and link them into
# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
# please read the SDK documents if you need to do this.
#
COMPONENT_ADD_INCLUDEDIRS := include
COMPONENT_PRIV_INCLUDEDIRS := include/driver
include $(IDF_PATH)/make/component_common.mk

View file

@ -18,34 +18,13 @@
#include "freertos/xtensa_api.h"
#include "driver/gpio.h"
#include "soc/soc.h"
#include "esp_log.h"
//TODO: move debug options to menuconfig
#define GPIO_DBG_ENABLE (0)
#define GPIO_WARNING_ENABLE (0)
#define GPIO_ERROR_ENABLE (0)
#define GPIO_INFO_ENABLE (0)
//DBG INFOR
#if GPIO_INFO_ENABLE
#define GPIO_INFO ets_printf
#else
#define GPIO_INFO(...)
#endif
#if GPIO_WARNING_ENABLE
#define GPIO_WARNING(format,...) do{\
ets_printf("[waring][%s#%u]",__FUNCTION__,__LINE__);\
ets_printf(format,##__VA_ARGS__);\
}while(0)
#else
#define GPIO_WARNING(...)
#endif
#if GPIO_ERROR_ENABLE
#define GPIO_ERROR(format,...) do{\
ets_printf("[error][%s#%u]",__FUNCTION__,__LINE__);\
ets_printf(format,##__VA_ARGS__);\
}while(0)
#else
#define GPIO_ERROR(...)
#endif
static const char* GPIO_TAG = "GPIO";
#define GPIO_CHECK(a, str, ret_val) if (!(a)) { \
ESP_LOGE(GPIO_TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
return (ret_val); \
}
const uint32_t GPIO_PIN_MUX_REG[GPIO_PIN_COUNT] = {
GPIO_PIN_REG_0,
@ -90,33 +69,17 @@ const uint32_t GPIO_PIN_MUX_REG[GPIO_PIN_COUNT] = {
GPIO_PIN_REG_39
};
static int is_valid_gpio(int gpio_num)
{
if(gpio_num >= GPIO_PIN_COUNT || GPIO_PIN_MUX_REG[gpio_num] == 0) {
GPIO_ERROR("GPIO io_num=%d does not exist\n",gpio_num);
return 0;
}
return 1;
}
esp_err_t gpio_set_intr_type(gpio_num_t gpio_num, gpio_int_type_t intr_type)
{
if(!is_valid_gpio(gpio_num)) {
return ESP_ERR_INVALID_ARG;
}
if(intr_type >= GPIO_INTR_MAX) {
GPIO_ERROR("Unknown GPIO intr:%u\n",intr_type);
return ESP_ERR_INVALID_ARG;
}
GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG);
GPIO_CHECK(intr_type < GPIO_INTR_MAX, "GPIO interrupt type error", ESP_ERR_INVALID_ARG);
GPIO.pin[gpio_num].int_type = intr_type;
return ESP_OK;
}
esp_err_t gpio_intr_enable(gpio_num_t gpio_num)
{
if(!is_valid_gpio(gpio_num)) {
return ESP_ERR_INVALID_ARG;
}
GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG);
if(xPortGetCoreID() == 0) {
GPIO.pin[gpio_num].int_ena = GPIO_PRO_CPU_INTR_ENA; //enable pro cpu intr
} else {
@ -127,18 +90,14 @@ esp_err_t gpio_intr_enable(gpio_num_t gpio_num)
esp_err_t gpio_intr_disable(gpio_num_t gpio_num)
{
if(!is_valid_gpio(gpio_num)) {
return ESP_ERR_INVALID_ARG;
}
GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG);
GPIO.pin[gpio_num].int_ena = 0; //disable GPIO intr
return ESP_OK;
}
static esp_err_t gpio_output_disable(gpio_num_t gpio_num)
{
if(!is_valid_gpio(gpio_num)) {
return ESP_ERR_INVALID_ARG;
}
GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG);
if(gpio_num < 32) {
GPIO.enable_w1tc = (0x1 << gpio_num);
} else {
@ -149,13 +108,7 @@ static esp_err_t gpio_output_disable(gpio_num_t gpio_num)
static esp_err_t gpio_output_enable(gpio_num_t gpio_num)
{
if(gpio_num >= 34) {
GPIO_ERROR("io_num=%d can only be input\n",gpio_num);
return ESP_ERR_INVALID_ARG;
}
if(!is_valid_gpio(gpio_num)) {
return ESP_ERR_INVALID_ARG;
}
GPIO_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "GPIO output gpio_num error", ESP_ERR_INVALID_ARG);
if(gpio_num < 32) {
GPIO.enable_w1ts = (0x1 << gpio_num);
} else {
@ -166,9 +119,7 @@ static esp_err_t gpio_output_enable(gpio_num_t gpio_num)
esp_err_t gpio_set_level(gpio_num_t gpio_num, uint32_t level)
{
if(!GPIO_IS_VALID_GPIO(gpio_num)) {
return ESP_ERR_INVALID_ARG;
}
GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG);
if(level) {
if(gpio_num < 32) {
GPIO.out_w1ts = (1 << gpio_num);
@ -196,9 +147,8 @@ int gpio_get_level(gpio_num_t gpio_num)
esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull)
{
if(!is_valid_gpio(gpio_num)) {
return ESP_ERR_INVALID_ARG;
}
GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG);
GPIO_CHECK(pull <= GPIO_FLOATING, "GPIO pull mode error", ESP_ERR_INVALID_ARG);
esp_err_t ret = ESP_OK;
switch(pull) {
case GPIO_PULLUP_ONLY:
@ -218,7 +168,7 @@ esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull)
PIN_PULLDWN_DIS(GPIO_PIN_MUX_REG[gpio_num]);
break;
default:
GPIO_ERROR("Unknown pull up/down mode,gpio_num=%u,pull=%u\n",gpio_num,pull);
ESP_LOGE(GPIO_TAG, "Unknown pull up/down mode,gpio_num=%u,pull=%u",gpio_num,pull);
ret = ESP_ERR_INVALID_ARG;
break;
}
@ -227,11 +177,9 @@ esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull)
esp_err_t gpio_set_direction(gpio_num_t gpio_num, gpio_mode_t mode)
{
if(!is_valid_gpio(gpio_num)) {
return ESP_ERR_INVALID_ARG;
}
GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG);
if(gpio_num >= 34 && (mode & (GPIO_MODE_DEF_OUTPUT))) {
GPIO_ERROR("io_num=%d can only be input\n",gpio_num);
ESP_LOGE(GPIO_TAG, "io_num=%d can only be input",gpio_num);
return ESP_ERR_INVALID_ARG;
}
esp_err_t ret = ESP_OK;
@ -266,54 +214,56 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig)
uint64_t gpio_pin_mask = (pGPIOConfig->pin_bit_mask);
uint32_t io_reg = 0;
uint32_t io_num = 0;
uint64_t bit_valid = 0;
uint8_t input_en = 0;
uint8_t output_en = 0;
uint8_t od_en = 0;
uint8_t pu_en = 0;
uint8_t pd_en = 0;
if(pGPIOConfig->pin_bit_mask == 0 || pGPIOConfig->pin_bit_mask >= (((uint64_t) 1) << GPIO_PIN_COUNT)) {
GPIO_ERROR("GPIO_PIN mask error \n");
ESP_LOGE(GPIO_TAG, "GPIO_PIN mask error ");
return ESP_ERR_INVALID_ARG;
}
if((pGPIOConfig->mode) & (GPIO_MODE_DEF_OUTPUT)) {
//GPIO 34/35/36/37/38/39 can only be used as input mode;
if((gpio_pin_mask & ( GPIO_SEL_34 | GPIO_SEL_35 | GPIO_SEL_36 | GPIO_SEL_37 | GPIO_SEL_38 | GPIO_SEL_39))) {
GPIO_ERROR("GPIO34-39 can only be used as input mode\n");
ESP_LOGE(GPIO_TAG, "GPIO34-39 can only be used as input mode");
return ESP_ERR_INVALID_ARG;
}
}
do {
io_reg = GPIO_PIN_MUX_REG[io_num];
if(((gpio_pin_mask >> io_num) & BIT(0)) && io_reg) {
GPIO_INFO("Gpio%02d |Mode:",io_num);
if((pGPIOConfig->mode) & GPIO_MODE_DEF_INPUT) {
GPIO_INFO("INPUT ");
input_en = 1;
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[io_num]);
} else {
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[io_num]);
}
if((pGPIOConfig->mode) & GPIO_MODE_DEF_OD) {
GPIO_INFO("OD ");
od_en = 1;
GPIO.pin[io_num].pad_driver = 1; /*0x01 Open-drain */
} else {
GPIO.pin[io_num].pad_driver = 0; /*0x00 Normal gpio output */
}
if((pGPIOConfig->mode) & GPIO_MODE_DEF_OUTPUT) {
GPIO_INFO("OUTPUT ");
output_en = 1;
gpio_output_enable(io_num);
} else {
gpio_output_disable(io_num);
}
GPIO_INFO("|");
if(pGPIOConfig->pull_up_en) {
GPIO_INFO("PU ");
pu_en = 1;
PIN_PULLUP_EN(io_reg);
} else {
PIN_PULLUP_DIS(io_reg);
}
if(pGPIOConfig->pull_down_en) {
GPIO_INFO("PD ");
pd_en = 1;
PIN_PULLDWN_EN(io_reg);
} else {
PIN_PULLDWN_DIS(io_reg);
}
GPIO_INFO("Intr:%d |\n",pGPIOConfig->intr_type);
ESP_LOGI(GPIO_TAG, "GPIO[%d]| InputEn: %d| OutputEn: %d| OpenDrain: %d| Pullup: %d| Pulldown: %d| Intr:%d ", io_num, input_en, output_en, od_en, pu_en, pd_en, pGPIOConfig->intr_type);
gpio_set_intr_type(io_num, pGPIOConfig->intr_type);
if(pGPIOConfig->intr_type) {
gpio_intr_enable(io_num);
@ -321,8 +271,6 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig)
gpio_intr_disable(io_num);
}
PIN_FUNC_SELECT(io_reg, PIN_FUNC_GPIO); /*function number 2 is GPIO_FUNC for each pin */
} else if(bit_valid && (io_reg == 0)) {
GPIO_WARNING("io_num=%d does not exist\n",io_num);
}
io_num++;
} while(io_num < GPIO_PIN_COUNT);
@ -331,9 +279,7 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig)
esp_err_t gpio_isr_register(uint32_t gpio_intr_num, void (*fn)(void*), void * arg)
{
if(fn == NULL) {
return ESP_ERR_INVALID_ARG;
}
GPIO_CHECK(fn, "GPIO ISR null", ESP_ERR_INVALID_ARG);
ESP_INTR_DISABLE(gpio_intr_num);
intr_matrix_set(xPortGetCoreID(), ETS_GPIO_INTR_SOURCE, gpio_intr_num);
xt_set_interrupt_handler(gpio_intr_num, fn, arg);
@ -344,15 +290,13 @@ esp_err_t gpio_isr_register(uint32_t gpio_intr_num, void (*fn)(void*), void * ar
/*only level interrupt can be used for wake-up function*/
esp_err_t gpio_wakeup_enable(gpio_num_t gpio_num, gpio_int_type_t intr_type)
{
if(!is_valid_gpio(gpio_num)) {
return ESP_ERR_INVALID_ARG;
}
GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG);
esp_err_t ret = ESP_OK;
if((intr_type == GPIO_INTR_LOW_LEVEL) || (intr_type == GPIO_INTR_HIGH_LEVEL)) {
GPIO.pin[gpio_num].int_type = intr_type;
GPIO.pin[gpio_num].wakeup_enable = 0x1;
} else {
GPIO_ERROR("GPIO wakeup only support Level mode,but edge mode set. gpio_num:%u\n",gpio_num);
ESP_LOGE(GPIO_TAG, "GPIO wakeup only support Level mode,but edge mode set. gpio_num:%u",gpio_num);
ret = ESP_ERR_INVALID_ARG;
}
return ret;
@ -360,9 +304,7 @@ esp_err_t gpio_wakeup_enable(gpio_num_t gpio_num, gpio_int_type_t intr_type)
esp_err_t gpio_wakeup_disable(gpio_num_t gpio_num)
{
if(!is_valid_gpio(gpio_num)) {
return ESP_ERR_INVALID_ARG;
}
GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG);
GPIO.pin[gpio_num].wakeup_enable = 0;
return ESP_OK;
}

View file

@ -20,6 +20,7 @@
#include "soc/gpio_struct.h"
#include "soc/rtc_io_reg.h"
#include "soc/io_mux_reg.h"
#include "soc/gpio_sig_map.h"
#include "rom/gpio.h"
#include "esp_attr.h"
@ -313,7 +314,7 @@ esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull);
*
* @param gpio_num GPIO number.
*
* @param intr_type GPIO wake-up type. Only GPIO_INTR_LOLEVEL\GPIO_INTR_HILEVEL can be used
* @param intr_type GPIO wake-up type. Only GPIO_INTR_LOW_LEVEL\GPIO_INTR_HIGH_LEVEL can be used.
*
* @return
* - ESP_OK Success

View file

@ -0,0 +1,763 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _DRIVER_UART_H_
#define _DRIVER_UART_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "soc/uart_reg.h"
#include "soc/uart_struct.h"
#include "esp_err.h"
#include "driver/periph_ctrl.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/xtensa_api.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/ringbuf.h"
#include <esp_types.h>
#define UART_FIFO_LEN (128) /*< Length of the hardware FIFO buffers */
#define UART_INTR_MASK 0x1ff
#define UART_LINE_INV_MASK (0x3f << 19)
#define UART_BITRATE_MAX 5000000
#define UART_PIN_NO_CHANGE (-1)
#define UART_INVERSE_DISABLE (0x0) /*!< Disable UART signal inverse*/
#define UART_INVERSE_RXD (UART_RXD_INV_M) /*!< UART RXD input inverse*/
#define UART_INVERSE_CTS (UART_CTS_INV_M) /*!< UART CTS input inverse*/
#define UART_INVERSE_TXD (UART_TXD_INV_M) /*!< UART TXD output inverse*/
#define UART_INVERSE_RTS (UART_RTS_INV_M) /*!< UART RTS output inverse*/
typedef enum {
UART_DATA_5_BITS = 0x0, /*!< word length: 5bits*/
UART_DATA_6_BITS = 0x1, /*!< word length: 6bits*/
UART_DATA_7_BITS = 0x2, /*!< word length: 7bits*/
UART_DATA_8_BITS = 0x3, /*!< word length: 8bits*/
UART_DATA_BITS_MAX = 0X4,
} uart_word_length_t;
typedef enum {
UART_STOP_BITS_1 = 0x1, /*!< stop bit: 1bit*/
UART_STOP_BITS_1_5 = 0x2, /*!< stop bit: 1.5bits*/
UART_STOP_BITS_2 = 0x3, /*!< stop bit: 2bits*/
UART_STOP_BITS_MAX = 0x4,
} uart_stop_bits_t;
typedef enum {
UART_NUM_0 = 0x0, /*!< UART base address 0x3ff40000*/
UART_NUM_1 = 0x1, /*!< UART base address 0x3ff50000*/
UART_NUM_2 = 0x2, /*!< UART base address 0x3ff6E000*/
UART_NUM_MAX,
} uart_port_t;
typedef enum {
UART_PARITY_DISABLE = 0x0, /*!< Disable UART parity*/
UART_PARITY_EVEN = 0x10, /*!< Enable UART even parity*/
UART_PARITY_ODD = 0x11 /*!< Enable UART odd parity*/
} uart_parity_t;
typedef enum {
UART_HW_FLOWCTRL_DISABLE = 0x0, /*!< disable hardware flow control*/
UART_HW_FLOWCTRL_RTS = 0x1, /*!< enable RX hardware flow control (rts)*/
UART_HW_FLOWCTRL_CTS = 0x2, /*!< enable TX hardware flow control (cts)*/
UART_HW_FLOWCTRL_CTS_RTS = 0x3, /*!< enable hardware flow control*/
UART_HW_FLOWCTRL_MAX = 0x4,
} uart_hw_flowcontrol_t;
typedef struct {
int baud_rate; /*!< UART baudrate*/
uart_word_length_t data_bits; /*!< UART byte size*/
uart_parity_t parity; /*!< UART parity mode*/
uart_stop_bits_t stop_bits; /*!< UART stop bits*/
uart_hw_flowcontrol_t flow_ctrl; /*!< UART HW flow control mode(cts/rts)*/
uint8_t rx_flow_ctrl_thresh ; /*!< UART HW RTS threshold*/
} uart_config_t;
typedef struct {
uint32_t intr_enable_mask; /*!< UART interrupt enable mask, choose from UART_XXXX_INT_ENA_M under UART_INT_ENA_REG(i), connect with bit-or operator*/
uint8_t rx_timeout_thresh; /*!< UART timeout interrupt threshold(unit: time of sending one byte)*/
uint8_t txfifo_empty_intr_thresh; /*!< UART TX empty interrupt threshold.*/
uint8_t rxfifo_full_thresh; /*!< UART RX full interrupt threshold.*/
} uart_intr_config_t;
typedef enum {
UART_DATA, /*!< UART data event*/
UART_BREAK, /*!< UART break event*/
UART_BUFFER_FULL, /*!< UART RX buffer full event*/
UART_FIFO_OVF, /*!< UART FIFO overflow event*/
UART_FRAME_ERR, /*!< UART RX frame error event*/
UART_PARITY_ERR, /*!< UART RX parity event*/
UART_DATA_BREAK, /*!< UART TX data and break event*/
UART_EVENT_MAX, /*!< UART event max index*/
} uart_event_type_t;
typedef struct {
uart_event_type_t type; /*!< UART event type */
size_t size; /*!< UART data size for UART_DATA event*/
} uart_event_t;
/**
* @brief Set UART data bits.
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param data_bit UART data bits
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit);
/**
* @brief Get UART data bits.
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @return
* - ESP_FAIL Parameter error
* - ESP_OK Success, result will be put in (*data_bit)
*/
esp_err_t uart_get_word_length(uart_port_t uart_num, uart_word_length_t* data_bit);
/**
* @brief Set UART stop bits.
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param bit_num UART stop bits
*
* @return
* - ESP_OK Success
* - ESP_FAIL Fail
*/
esp_err_t uart_set_stop_bits(uart_port_t uart_no, uart_stop_bits_t bit_num);
/**
* @brief Set UART stop bits.
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @return
* - ESP_FAIL Parameter error
* - ESP_OK Success, result will be put in (*stop_bit)
*/
esp_err_t uart_get_stop_bits(uart_port_t uart_num, uart_stop_bits_t* stop_bit);
/**
* @brief Set UART parity.
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param parity_mode the enum of uart parity configuration
*
* @return
* - ESP_FAIL Parameter error
* - ESP_OK Success
*/
esp_err_t uart_set_parity(uart_port_t uart_no, uart_parity_t parity_mode);
/**
* @brief Get UART parity mode.
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @return
* - ESP_FAIL Parameter error
* - ESP_OK Success, result will be put in (*parity_mode)
*
*/
esp_err_t uart_get_parity(uart_port_t uart_num, uart_parity_t* parity_mode);
/**
* @brief Set UART baud rate.
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param baud_rate UART baud-rate.
*
* @return
* - ESP_FAIL Parameter error
* - ESP_OK Success
*/
esp_err_t uart_set_baudrate(uart_port_t uart_no, uint32_t baud_rate);
/**
* @brief Get UART bit-rate.
*
* @param uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @return
* - ESP_FAIL Parameter error
* - ESP_OK Success, result will be put in (*baudrate)
*
*/
esp_err_t uart_get_baudrate(uart_port_t uart_num, uint32_t* baudrate);
/**
* @brief Set UART line inverse mode
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param inverse_mask Choose the wires that need to be inversed.
*
* (inverse_mask should be chosen from UART_INVERSE_RXD/UART_INVERSE_TXD/UART_INVERSE_RTS/UART_INVERSE_CTS, combine with OR-OPERATION)
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_set_line_inverse(uart_port_t uart_no, uint32_t inverse_mask);
/**
* @brief Set hardware flow control.
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param flow_ctrl Hardware flow control mode
*
* @param rx_thresh Threshold of Hardware RX flow control(0 ~ UART_FIFO_LEN)
*
* Only when UART_HW_FLOWCTRL_RTS is set , will the rx_thresh value be set.
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_no, uart_hw_flowcontrol_t flow_ctrl, uint8_t rx_thresh);
/**
* @brief Get hardware flow control mode
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @return
* - ESP_FAIL Parameter error
* - ESP_OK Success, result will be put in (*flow_ctrl)
*/
esp_err_t uart_get_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t* flow_ctrl);
/**
* @brief Clear UART interrupt status
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param clr_mask Bit mask of the status that to be cleared.
*
* (enable_mask should be chosen from the fields of register UART_INT_CLR_REG)
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_clear_intr_status(uart_port_t uart_num, uint32_t clr_mask);
/**
* @brief Set UART interrupt enable
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param enable_mask Bit mask of the enable bits.
*
* (enable_mask should be chosen from the fields of register UART_INT_ENA_REG)
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_enable_intr_mask(uart_port_t uart_num, uint32_t enable_mask);
/**
* @brief Clear UART interrupt enable bits
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param disable_mask Bit mask of the disable bits.
*
* (disable_mask should be chosen from the fields of register UART_INT_ENA_REG)
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_disable_intr_mask(uart_port_t uart_num, uint32_t disable_mask);
/**
* @brief Enable UART RX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT)
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_enable_rx_intr(uart_port_t uart_num);
/**
* @brief Disable UART RX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT)
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_disable_rx_intr(uart_port_t uart_num);
/**
* @brief Disable UART TX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT)
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_disable_tx_intr(uart_port_t uart_num);
/**
* @brief Enable UART TX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT)
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param enable 1: enable; 0: disable
*
* @param thresh Threshold of TX interrupt, 0 ~ UART_FIFO_LEN
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh);
/**
* @brief register UART interrupt handler(ISR).
* @note
* UART ISR handler will be attached to the same CPU core that this function is running on.
* Users should know that which CPU is running and then pick a INUM that is not used by system.
* We can find the information of INUM and interrupt level in soc.h.
*
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details
*
* @param fn Interrupt handler function.
* @attention
* The ISR handler function MUST be defined with attribution of "IRAM_ATTR" for now.
* @param arg parameter for handler function
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (*fn)(void*), void * arg);
/**
* @brief Set UART pin number
*
* @note
* Internal signal can be output to multiple GPIO pads
* Only one GPIO pad can connect with input signal
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param tx_io_num UART TX pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin.
*
* @param rx_io_num UART RX pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin.
*
* @param rts_io_num UART RTS pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin.
*
* @param cts_io_num UART CTS pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin.
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int rts_io_num, int cts_io_num);
/**
* @brief UART set RTS level (before inverse)
* UART rx hardware flow control should not be set.
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param level 1: RTS output low(active); 0: RTS output high(block)
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_set_rts(uart_port_t uart_num, int level);
/**
* @brief UART set DTR level (before inverse)
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param level 1: DTR output low; 0: DTR output high
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_set_dtr(uart_port_t uart_num, int level);
/**
* @brief UART parameter configure
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param uart_config UART parameter settings
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_config);
/**
* @brief UART interrupt configure
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param intr_conf UART interrupt settings
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_conf);
/**
* @brief Install UART driver.
*
* UART ISR handler will be attached to the same CPU core that this function is running on.
* Users should know that which CPU is running and then pick a INUM that is not used by system.
* We can find the information of INUM and interrupt level in soc.h.
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param rx_buffer_size UART RX ring buffer size
*
* @param tx_buffer_size UART TX ring buffer size.
*
* If set to zero, driver will not use TX buffer, TX function will block task until all data have been sent out..
*
* @param queue_size UART event queue size/depth.
*
* @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details
*
* @param uart_queue UART event queue handle, if set NULL, driver will not use an event queue.
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, int uart_intr_num, void* uart_queue);
/**
* @brief Uninstall UART driver.
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_driver_delete(uart_port_t uart_num);
/**
* @brief Wait UART TX FIFO empty
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param ticks_to_wait Timeout, count in RTOS ticks
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
* - ESP_ERR_TIMEOUT Timeout
*/
esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait);
/**
* @brief Send data to the UART port from a given buffer and length,
* This function will not wait for the space in TX FIFO, just fill the TX FIFO and return when the FIFO is full.
* @note
* This function should only be used when UART TX buffer is not enabled.
*
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param buffer data buffer address
*
* @param len data length to send
*
* @return
* - (-1) Parameter error
* - OTHERS(>=0) The number of data that pushed to the TX FIFO
*/
int uart_tx_chars(uart_port_t uart_no, const char* buffer, uint32_t len);
/**
* @brief Send data to the UART port from a given buffer and length,
*
* If parameter tx_buffer_size is set to zero:
* This function will not return until all the data have been sent out, or at least pushed into TX FIFO.
*
* Otherwise, if tx_buffer_size > 0, this function will return after copying all the data to tx ringbuffer,
* then, UART ISR will move data from ring buffer to TX FIFO gradually.
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param src data buffer address
*
* @param size data length to send
*
* @return
* - (-1) Parameter error
* - OTHERS(>=0) The number of data that pushed to the TX FIFO
*/
int uart_write_bytes(uart_port_t uart_num, const char* src, size_t size);
/**
* @brief Send data to the UART port from a given buffer and length,
*
* If parameter tx_buffer_size is set to zero:
* This function will not return until all the data and the break signal have been sent out.
* After all data send out, send a break signal.
*
* Otherwise, if tx_buffer_size > 0, this function will return after copying all the data to tx ringbuffer,
* then, UART ISR will move data from ring buffer to TX FIFO gradually.
* After all data send out, send a break signal.
*
*
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param src data buffer address
*
* @param size data length to send
*
* @param brk_len break signal length (unit: one bit's time@current_baudrate)
*
* @return
* - (-1) Parameter error
* - OTHERS(>=0) The number of data that pushed to the TX FIFO
*/
int uart_write_bytes_with_break(uart_port_t uart_num, const char* src, size_t size, int brk_len);
/**
* @brief UART read bytes from UART buffer
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param buf pointer to the buffer.
*
* @param length data length
*
* @param ticks_to_wait sTimeout, count in RTOS ticks
*
*
* @return
* - (-1) Error
* - Others return a char data from uart fifo.
*/
int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickType_t ticks_to_wait);
/**
* @brief UART ring buffer flush
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_flush(uart_port_t uart_num);
/***************************EXAMPLE**********************************
*
*
* ----------------EXAMPLE OF UART SETTING ---------------------
* @code{c}
* //1. Setup UART
* #include "freertos/queue.h"
* #define UART_INTR_NUM 17 //choose one interrupt number from soc.h
* //a. Set UART parameter
* int uart_num = 0; //uart port number
* uart_config_t uart_config = {
* .baud_rate = UART_BITRATE_115200, //baudrate
* .data_bits = UART_DATA_8_BITS, //data bit mode
* .parity = UART_PARITY_DISABLE, //parity mode
* .stop_bits = UART_STOP_BITS_1, //stop bit mode
* .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, //hardware flow control(cts/rts)
* .rx_flow_ctrl_thresh = 120, //flow control threshold
* };
* uart_param_config(uart_num, &uart_config);
* //b1. Setup UART driver(with UART queue)
* QueueHandle_t uart_queue;
* //parameters here are just an example, tx buffer size is 2048
* uart_driver_install(uart_num, 1024 * 2, 1024 * 2, 10, UART_INTR_NUM, &uart_queue);
* //b2. Setup UART driver(without UART queue)
* //parameters here are just an example, tx buffer size is 0
* uart_driver_install(uart_num, 1024 * 2, 0, 10, UART_INTR_NUM, NULL);
*@endcode
*-----------------------------------------------------------------------------*
* @code{c}
* //2. Set UART pin
* //set UART pin, not needed if use default pins.
* uart_set_pin(uart_num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, 15, 13);
* @endcode
*-----------------------------------------------------------------------------*
* @code{c}
* //3. Read data from UART.
* uint8_t data[128];
* int length = 0;
* length = uart_read_bytes(uart_num, data, sizeof(data), 100);
* @endcode
*-----------------------------------------------------------------------------*
* @code{c}
* //4. Write data to UART.
* char* test_str = "This is a test string.\n"
* uart_write_bytes(uart_num, (const char*)test_str, strlen(test_str));
* @endcode
*-----------------------------------------------------------------------------*
* @code{c}
* //5. Write data to UART, end with a break signal.
* uart_write_bytes_with_break(0, "test break\n",strlen("test break\n"), 100);
* @endcode
*-----------------------------------------------------------------------------*
* @code{c}
* //6. an example of echo test with hardware flow control on UART1
* void uart_loop_back_test()
* {
* int uart_num = 1;
* uart_config_t uart_config = {
* .baud_rate = 115200,
* .data_bits = UART_DATA_8_BITS,
* .parity = UART_PARITY_DISABLE,
* .stop_bits = UART_STOP_BITS_1,
* .flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS,
* .rx_flow_ctrl_thresh = 122,
* };
* //Configure UART1 parameters
* uart_param_config(uart_num, &uart_config);
* //Set UART1 pins(TX: IO16, RX: IO17, RTS: IO18, CTS: IO19)
* uart_set_pin(uart_num, 16, 17, 18, 19);
* //Install UART driver( We don't need an event queue here)
* uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, 17, NULL, RINGBUF_TYPE_BYTEBUF);
* uint8_t data[1000];
* while(1) {
* //Read data from UART
* int len = uart_read_bytes(uart_num, data, sizeof(data), 10);
* //Write data back to UART
* uart_write_bytes(uart_num, (const char*)data, len);
* }
* }
* @endcode
*-----------------------------------------------------------------------------*
* @code{c}
* //7. An example of using UART event queue on UART0.
* #include "freertos/queue.h"
* //A queue to handle UART event.
* QueueHandle_t uart0_queue;
* static const char *TAG = "uart_example";
* void uart_task(void *pvParameters)
* {
* int uart_num = (int)pvParameters;
* uart_event_t event;
* uint8_t dtmp[1000];
* for(;;) {
* //Waiting for UART event.
* if(xQueueReceive(uart0_queue, (void * )&event, (portTickType)portMAX_DELAY)) {
* ESP_LOGI(TAG, "uart[%d] event:", uart_num);
* switch(event.type) {
* memset(dtmp, 0, sizeof(dtmp));
* //Event of UART receving data
* case UART_DATA:
* ESP_LOGI(TAG,"data, len: %d", event.size);
* int len = uart_read_bytes(uart_num, dtmp, event.size, 10);
* ESP_LOGI(TAG, "uart read: %d", len);
uart_write_bytes(uart_num, (const char*)dtmp, len);
* break;
* //Event of HW FIFO overflow detected
* case UART_FIFO_OVF:
* ESP_LOGI(TAG, "hw fifo overflow\n");
* break;
* //Event of UART ring buffer full
* case UART_BUFFER_FULL:
* ESP_LOGI(TAG, "ring buffer full\n");
* break;
* //Event of UART RX break detected
* case UART_BREAK:
* ESP_LOGI(TAG, "uart rx break\n");
* break;
* //Event of UART parity check error
* case UART_PARITY_ERR:
* ESP_LOGI(TAG, "uart parity error\n");
* break;
* //Event of UART frame error
* case UART_FRAME_ERR:
* ESP_LOGI(TAG, "uart frame error\n");
* break;
* //Others
* default:
* ESP_LOGI(TAG, "uart event type: %d\n", event.type);
* break;
* }
* }
* }
* vTaskDelete(NULL);
* }
*
* void uart_queue_test()
* {
* int uart_num = 0;
* uart_config_t uart_config = {
* .baud_rate = 115200,
* .data_bits = UART_DATA_8_BITS,
* .parity = UART_PARITY_DISABLE,
* .stop_bits = UART_STOP_BITS_1,
* .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
* .rx_flow_ctrl_thresh = 122,
* };
* //Set UART parameters
* uart_param_config(uart_num, &uart_config);
* //Set UART pins,(-1: default pin, no change.)
* uart_set_pin(uart_num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, 15, 13);
* //Set UART log level
* esp_log_level_set(TAG, ESP_LOG_INFO);
* //Install UART driver, and get the queue.
* uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, 17, &uart0_queue, RINGBUF_TYPE_BYTEBUF);
* //Create a task to handler UART event from ISR
* xTaskCreate(uart_task, "uTask", 2048*8, (void*)uart_num, 10, NULL);
* }
* @endcode
*
***************************END OF EXAMPLE**********************************/
#ifdef __cplusplus
}
#endif
#endif /*_DRIVER_UART_H_*/

View file

@ -18,86 +18,19 @@
#include "freertos/xtensa_api.h"
#include "soc/gpio_sig_map.h"
#include "driver/ledc.h"
#include "esp_log.h"
//TODO: to use APIs in esp_log.h.
#define LEDC_DBG_WARING_ENABLE (0)
#define LEDC_DBG_ERROR_ENABLE (0)
#define LEDC_INFO_ENABLE (0)
#define LEDC_DBG_ENABLE (0)
//DBG INFOR
#if LEDC_DBG_ENABLE
#define LEDC_DBG(format,...) do{\
ets_printf("[dbg][%s#%u]",__FUNCTION__,__LINE__);\
ets_printf(format,##__VA_ARGS__);\
}while(0)
#else
#define LEDC_DBG(...)
#endif
#if LEDC_INFO_ENABLE
#define LEDC_INFO(format,...) do{\
ets_printf("[info][%s#%u]",__FUNCTION__,__LINE__);\
ets_printf(format,##__VA_ARGS__);\
}while(0)
#else
#define LEDC_INFO(...)
#endif
#if LEDC_DBG_WARING_ENABLE
#define LEDC_WARING(format,...) do{\
ets_printf("[waring][%s#%u]",__FUNCTION__,__LINE__);\
ets_printf(format,##__VA_ARGS__);\
}while(0)
#else
#define LEDC_WARING(...)
#endif
#if LEDC_DBG_ERROR_ENABLE
#define LEDC_ERROR(format,...) do{\
ets_printf("[error][%s#%u]",__FUNCTION__,__LINE__);\
ets_printf(format,##__VA_ARGS__);\
}while(0)
#else
#define LEDC_ERROR(...)
#endif
static const char* LEDC_TAG = "LEDC";
static portMUX_TYPE ledc_spinlock = portMUX_INITIALIZER_UNLOCKED;
static bool ledc_is_valid_channel(uint32_t channel)
{
if(channel > LEDC_CHANNEL_7) {
LEDC_ERROR("LEDC CHANNEL ERR: %d\n",channel);
return false;
}
return true;
}
static bool ledc_is_valid_mode(uint32_t mode)
{
if(mode >= LEDC_SPEED_MODE_MAX) {
LEDC_ERROR("LEDC MODE ERR: %d\n",mode);
return false;
}
return true;
}
static bool ledc_is_valid_timer(int timer)
{
if(timer > LEDC_TIMER_3) {
LEDC_ERROR("LEDC TIMER ERR: %d\n", timer);
return false;
}
return true;
}
#define LEDC_CHECK(a, str, ret_val) if (!(a)) { \
ESP_LOGE(LEDC_TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
return (ret_val); \
}
esp_err_t ledc_timer_set(ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_t div_num, uint32_t bit_num, ledc_clk_src_t clk_src)
{
if(!ledc_is_valid_mode(speed_mode)) {
return ESP_ERR_INVALID_ARG;
}
if(!ledc_is_valid_timer(timer_sel)) {
return ESP_ERR_INVALID_ARG;
}
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG);
LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG);
portENTER_CRITICAL(&ledc_spinlock);
LEDC.timer_group[speed_mode].timer[timer_sel].conf.div_num = div_num;
LEDC.timer_group[speed_mode].timer[timer_sel].conf.tick_sel = clk_src;
@ -125,12 +58,8 @@ static esp_err_t ledc_duty_config(ledc_mode_t speed_mode, uint32_t channel_num,
esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint32_t timer_idx)
{
if(!ledc_is_valid_mode(speed_mode)) {
return ESP_ERR_INVALID_ARG;
}
if(!ledc_is_valid_timer(timer_idx)) {
return ESP_ERR_INVALID_ARG;
}
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG);
LEDC_CHECK(timer_idx <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG);
portENTER_CRITICAL(&ledc_spinlock);
LEDC.channel_group[speed_mode].channel[channel].conf0.timer_sel = timer_idx;
portEXIT_CRITICAL(&ledc_spinlock);
@ -139,12 +68,8 @@ esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint
esp_err_t ledc_timer_rst(ledc_mode_t speed_mode, uint32_t timer_sel)
{
if(!ledc_is_valid_mode(speed_mode)) {
return ESP_ERR_INVALID_ARG;
}
if(!ledc_is_valid_timer(timer_sel)) {
return ESP_ERR_INVALID_ARG;
}
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG);
LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG);
portENTER_CRITICAL(&ledc_spinlock);
LEDC.timer_group[speed_mode].timer[timer_sel].conf.rst = 1;
LEDC.timer_group[speed_mode].timer[timer_sel].conf.rst = 0;
@ -154,12 +79,8 @@ esp_err_t ledc_timer_rst(ledc_mode_t speed_mode, uint32_t timer_sel)
esp_err_t ledc_timer_pause(ledc_mode_t speed_mode, uint32_t timer_sel)
{
if(!ledc_is_valid_mode(speed_mode)) {
return ESP_ERR_INVALID_ARG;
}
if(!ledc_is_valid_timer(timer_sel)) {
return ESP_ERR_INVALID_ARG;
}
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG);
LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG);
portENTER_CRITICAL(&ledc_spinlock);
LEDC.timer_group[speed_mode].timer[timer_sel].conf.pause = 1;
portEXIT_CRITICAL(&ledc_spinlock);
@ -168,12 +89,8 @@ esp_err_t ledc_timer_pause(ledc_mode_t speed_mode, uint32_t timer_sel)
esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, uint32_t timer_sel)
{
if(!ledc_is_valid_mode(speed_mode)) {
return ESP_ERR_INVALID_ARG;
}
if(!ledc_is_valid_timer(timer_sel)) {
return ESP_ERR_INVALID_ARG;
}
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG);
LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG);
portENTER_CRITICAL(&ledc_spinlock);
LEDC.timer_group[speed_mode].timer[timer_sel].conf.pause = 0;
portEXIT_CRITICAL(&ledc_spinlock);
@ -182,9 +99,7 @@ esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, uint32_t timer_sel)
static esp_err_t ledc_enable_intr_type(ledc_mode_t speed_mode, uint32_t channel, ledc_intr_type_t type)
{
if(!ledc_is_valid_mode(speed_mode)) {
return ESP_ERR_INVALID_ARG;
}
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG);
uint32_t value;
uint32_t intr_type = type;
portENTER_CRITICAL(&ledc_spinlock);
@ -200,9 +115,7 @@ static esp_err_t ledc_enable_intr_type(ledc_mode_t speed_mode, uint32_t channel,
esp_err_t ledc_isr_register(uint32_t ledc_intr_num, void (*fn)(void*), void * arg)
{
if(fn == NULL) {
return ESP_ERR_INVALID_ARG;
}
LEDC_CHECK(fn, "ledc isr null", ESP_ERR_INVALID_ARG);
portENTER_CRITICAL(&ledc_spinlock);
ESP_INTR_DISABLE(ledc_intr_num);
intr_matrix_set(xPortGetCoreID(), ETS_LEDC_INTR_SOURCE, ledc_intr_num);
@ -218,16 +131,13 @@ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf)
int bit_num = timer_conf->bit_num;
int timer_num = timer_conf->timer_num;
int speed_mode = timer_conf->speed_mode;
if(!ledc_is_valid_mode(speed_mode)) {
return ESP_ERR_INVALID_ARG;
}
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG);
if(freq_hz == 0 || bit_num == 0 || bit_num > LEDC_TIMER_15_BIT) {
LEDC_ERROR("freq_hz=%u bit_num=%u\n", freq_hz, bit_num);
ESP_LOGE(LEDC_TAG, "freq_hz=%u bit_num=%u", freq_hz, bit_num);
return ESP_ERR_INVALID_ARG;
}
if(timer_num > LEDC_TIMER_3) {
LEDC_ERROR("Time Select %u\n", timer_num);
ESP_LOGE(LEDC_TAG, "Time Select %u", timer_num);
return ESP_ERR_INVALID_ARG;
}
esp_err_t ret = ESP_OK;
@ -239,7 +149,7 @@ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf)
/*Selet the reference tick*/
div_param = ((uint64_t) LEDC_REF_CLK_HZ << 8) / freq_hz / precision;
if(div_param <= 256 || div_param > LEDC_DIV_NUM_HSTIMER0_V) {
LEDC_ERROR("div param err,div_param=%u\n", div_param);
ESP_LOGE(LEDC_TAG, "div param err,div_param=%u", (uint32_t)div_param);
ret = ESP_FAIL;
}
timer_clk_src = LEDC_REF_TICK;
@ -254,6 +164,21 @@ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf)
return ret;
}
esp_err_t ledc_set_pin(int gpio_num, ledc_mode_t speed_mode, ledc_channel_t ledc_channel)
{
LEDC_CHECK(ledc_channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG);
LEDC_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "ledc GPIO output number error", ESP_ERR_INVALID_ARG);
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG);
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpio_num], PIN_FUNC_GPIO);
gpio_set_direction(gpio_num, GPIO_MODE_OUTPUT);
if(speed_mode == LEDC_HIGH_SPEED_MODE) {
gpio_matrix_out(gpio_num, LEDC_HS_SIG_OUT0_IDX + ledc_channel, 0, 0);
} else {
}
return ESP_OK;
}
esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf)
{
uint32_t speed_mode = ledc_conf->speed_mode;
@ -262,21 +187,10 @@ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf)
uint32_t timer_select = ledc_conf->timer_sel;
uint32_t intr_type = ledc_conf->intr_type;
uint32_t duty = ledc_conf->duty;
if(!ledc_is_valid_channel(ledc_channel)) {
return ESP_ERR_INVALID_ARG;
}
if(!ledc_is_valid_mode(speed_mode)) {
return ESP_ERR_INVALID_ARG;
}
if(!GPIO_IS_VALID_OUTPUT_GPIO(gpio_num)) {
LEDC_ERROR("GPIO number error: IO%d\n ", gpio_num);
return ESP_ERR_INVALID_ARG;
}
if(timer_select > LEDC_TIMER_3) {
LEDC_ERROR("Time Select %u\n", timer_select);
return ESP_ERR_INVALID_ARG;
}
LEDC_CHECK(ledc_channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG);
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG);
LEDC_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "ledc GPIO output number error", ESP_ERR_INVALID_ARG);
LEDC_CHECK(timer_select <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG);
esp_err_t ret = ESP_OK;
/*set channel parameters*/
/* channel parameters decide how the waveform looks like in one period*/
@ -288,7 +202,7 @@ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf)
ledc_bind_channel_timer(speed_mode, ledc_channel, timer_select);
/*set interrupt type*/
ledc_enable_intr_type(speed_mode, ledc_channel, intr_type);
LEDC_INFO("LEDC_PWM CHANNEL %1u|GPIO %02u|Duty %04u|Time %01u\n",
ESP_LOGI(LEDC_TAG, "LEDC_PWM CHANNEL %1u|GPIO %02u|Duty %04u|Time %01u",
ledc_channel, gpio_num, duty, timer_select
);
/*set LEDC signal in gpio matrix*/
@ -300,12 +214,8 @@ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf)
esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel)
{
if(!ledc_is_valid_mode(speed_mode)) {
return ESP_ERR_INVALID_ARG;
}
if(!ledc_is_valid_channel(channel)) {
return ESP_ERR_INVALID_ARG;
}
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG);
LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG);
portENTER_CRITICAL(&ledc_spinlock);
LEDC.channel_group[speed_mode].channel[channel].conf0.sig_out_en = 1;
LEDC.channel_group[speed_mode].channel[channel].conf1.duty_start = 1;
@ -315,12 +225,8 @@ esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel)
esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idle_level)
{
if(!ledc_is_valid_mode(speed_mode)) {
return ESP_ERR_INVALID_ARG;
}
if(!ledc_is_valid_channel(channel)) {
return ESP_ERR_INVALID_ARG;
}
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG);
LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG);
portENTER_CRITICAL(&ledc_spinlock);
LEDC.channel_group[speed_mode].channel[channel].conf0.idle_lv = idle_level & 0x1;
LEDC.channel_group[speed_mode].channel[channel].conf0.sig_out_en = 0;
@ -331,18 +237,11 @@ esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idl
esp_err_t ledc_set_fade(ledc_mode_t speed_mode, uint32_t channel, uint32_t duty, ledc_duty_direction_t fade_direction,
uint32_t step_num, uint32_t duty_cyle_num, uint32_t duty_scale)
{
if(!ledc_is_valid_mode(speed_mode)) {
return ESP_ERR_INVALID_ARG;
}
if(!ledc_is_valid_channel(channel)) {
return ESP_ERR_INVALID_ARG;
}
if(fade_direction > LEDC_DUTY_DIR_INCREASE) {
LEDC_ERROR("Duty direction err\n");
return ESP_ERR_INVALID_ARG;
}
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG);
LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG);
LEDC_CHECK(fade_direction <= LEDC_DUTY_DIR_INCREASE, "ledc fade direction error", ESP_ERR_INVALID_ARG);
if(step_num > LEDC_DUTY_NUM_HSCH0_V || duty_cyle_num > LEDC_DUTY_CYCLE_HSCH0_V || duty_scale > LEDC_DUTY_SCALE_HSCH0_V) {
LEDC_ERROR("step_num=%u duty_cyle_num=%u duty_scale=%u\n", step_num, duty_cyle_num, duty_scale);
ESP_LOGE(LEDC_TAG, "step_num=%u duty_cyle_num=%u duty_scale=%u", step_num, duty_cyle_num, duty_scale);
return ESP_ERR_INVALID_ARG;
}
ledc_duty_config(speed_mode,
@ -359,12 +258,8 @@ esp_err_t ledc_set_fade(ledc_mode_t speed_mode, uint32_t channel, uint32_t duty,
esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty)
{
if(!ledc_is_valid_mode(speed_mode)) {
return ESP_ERR_INVALID_ARG;
}
if(!ledc_is_valid_channel(channel)) {
return ESP_ERR_INVALID_ARG;
}
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG);
LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG);
ledc_duty_config(speed_mode,
channel, //uint32_t chan_num,
0, //uint32_t hpoint_val,
@ -379,18 +274,14 @@ esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t
int ledc_get_duty(ledc_mode_t speed_mode, ledc_channel_t channel)
{
if(!ledc_is_valid_mode(speed_mode)) {
return -1;
}
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", (-1));
uint32_t duty = (LEDC.channel_group[speed_mode].channel[channel].duty_rd.duty_read >> 4);
return duty;
}
esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t freq_hz)
{
if(!ledc_is_valid_mode(speed_mode)) {
return ESP_ERR_INVALID_ARG;
}
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG);
portENTER_CRITICAL(&ledc_spinlock);
esp_err_t ret = ESP_OK;
uint32_t div_num = 0;
@ -403,7 +294,7 @@ esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t
div_num = ((uint64_t) LEDC_REF_CLK_HZ << 8) / freq_hz / precision;
}
if(div_num <= 256 || div_num > LEDC_DIV_NUM_HSTIMER0) {
LEDC_ERROR("div param err,div_param=%u\n", div_num);
ESP_LOGE(LEDC_TAG, "div param err,div_param=%u", div_num);
ret = ESP_FAIL;
}
LEDC.timer_group[speed_mode].timer[timer_num].conf.div_num = div_num;
@ -413,9 +304,7 @@ esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t
uint32_t ledc_get_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num)
{
if(!ledc_is_valid_mode(speed_mode)) {
return 0;
}
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", (0));
portENTER_CRITICAL(&ledc_spinlock);
uint32_t freq = 0;
uint32_t timer_source_clk = LEDC.timer_group[speed_mode].timer[timer_num].conf.tick_sel;

1008
components/driver/uart.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,12 +1,6 @@
#
# Component Makefile
#
# This Makefile should, at the very least, just include $(IDF_PATH)/make/component_common.mk. By default,
# this will take the sources in this directory, compile them and link them into
# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
# please read the esp-idf build system document if you need to do this.
#
-include include/config/auto.conf
COMPONENT_SRCDIRS := . hwcrypto
@ -15,14 +9,12 @@ LIBS := core net80211 phy rtc pp wpa smartconfig coexist wps
LINKER_SCRIPTS += -T esp32_out.ld -T esp32.common.ld -T esp32.rom.ld -T esp32.peripherals.ld
COMPONENT_ADD_LDFLAGS := -lesp32 \
$(abspath libhal.a) \
-L$(abspath lib) \
$(COMPONENT_PATH)/libhal.a \
-L$(COMPONENT_PATH)/lib \
$(addprefix -l,$(LIBS)) \
-L $(abspath ld) \
-L $(COMPONENT_PATH)/ld \
$(LINKER_SCRIPTS)
include $(IDF_PATH)/make/component_common.mk
ALL_LIB_FILES := $(patsubst %,$(COMPONENT_PATH)/lib/lib%.a,$(LIBS))
# automatically trigger a git submodule update
@ -44,8 +36,6 @@ $(COMPONENT_LIBRARY): $(ALL_LIB_FILES)
# saves us from having to add the target to a Makefile.projbuild
$(COMPONENT_LIBRARY): esp32_out.ld
# .. is BUILD_DIR_BASE here, as component makefiles
# are evaluated with CWD=component build dir
esp32_out.ld: $(COMPONENT_PATH)/ld/esp32.ld ../include/sdkconfig.h
$(CC) -I ../include -C -P -x c -E $< -o $@

View file

@ -177,9 +177,9 @@ void start_cpu0_default(void)
esp_vfs_dev_uart_register();
esp_reent_init(_GLOBAL_REENT);
const char* default_uart_dev = "/dev/uart/0";
_GLOBAL_REENT->_stdin = fopen(default_uart_dev, "r");
_GLOBAL_REENT->_stdout = fopen(default_uart_dev, "w");
_GLOBAL_REENT->_stderr = fopen(default_uart_dev, "w");
_GLOBAL_REENT->_stdin = fopen(default_uart_dev, "r");
do_global_ctors();
#if !CONFIG_FREERTOS_UNICORE
esp_crosscore_int_init();

View file

@ -45,14 +45,9 @@ the ISR will cause it to switch _away_ from it. portYIELD_FROM_ISR will probably
*/
static void esp_crosscore_isr(void *arg) {
uint32_t myReasonVal;
#if 0
//A pointer to the correct reason array item is passed to this ISR.
volatile uint32_t *myReason=arg;
#else
//The previous line does not work yet, the interrupt code needs work to understand two separate interrupt and argument
//tables... this is a valid but slightly less optimal replacement.
volatile uint32_t *myReason=&reason[xPortGetCoreID()];
#endif
//Clear the interrupt first.
if (xPortGetCoreID()==0) {
WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, 0);

View file

@ -22,6 +22,7 @@
#include "esp_event.h"
#include "esp_event_loop.h"
#include "esp_task.h"
#include "rom/ets_sys.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
@ -200,16 +201,14 @@ static esp_err_t esp_system_event_debug(system_event_t *event)
}
case SYSTEM_EVENT_STA_CONNECTED: {
system_event_sta_connected_t *connected = &event->event_info.connected;
ESP_LOGD(TAG, "SYSTEM_EVENT_STA_CONNECTED, ssid:%s, ssid_len:%d, bssid:%02x:%02x:%02x:%02x:%02x:%02x, channel:%d, authmode:%d", \
connected->ssid, connected->ssid_len, connected->bssid[0], connected->bssid[0], connected->bssid[1], \
connected->bssid[3], connected->bssid[4], connected->bssid[5], connected->channel, connected->authmode);
ESP_LOGD(TAG, "SYSTEM_EVENT_STA_CONNECTED, ssid:%s, ssid_len:%d, bssid:" MACSTR ", channel:%d, authmode:%d", \
connected->ssid, connected->ssid_len, MAC2STR(connected->bssid), connected->channel, connected->authmode);
break;
}
case SYSTEM_EVENT_STA_DISCONNECTED: {
system_event_sta_disconnected_t *disconnected = &event->event_info.disconnected;
ESP_LOGD(TAG, "SYSTEM_EVENT_STA_DISCONNECTED, ssid:%s, ssid_len:%d, bssid:%02x:%02x:%02x:%02x:%02x:%02x, reason:%d", \
disconnected->ssid, disconnected->ssid_len, disconnected->bssid[0], disconnected->bssid[0], disconnected->bssid[1], \
disconnected->bssid[3], disconnected->bssid[4], disconnected->bssid[5], disconnected->reason);
ESP_LOGD(TAG, "SYSTEM_EVENT_STA_DISCONNECTED, ssid:%s, ssid_len:%d, bssid:" MACSTR ", reason:%d", \
disconnected->ssid, disconnected->ssid_len, MAC2STR(disconnected->bssid), disconnected->reason);
break;
}
case SYSTEM_EVENT_STA_AUTHMODE_CHANGE: {
@ -251,23 +250,21 @@ static esp_err_t esp_system_event_debug(system_event_t *event)
}
case SYSTEM_EVENT_AP_STACONNECTED: {
system_event_ap_staconnected_t *staconnected = &event->event_info.sta_connected;
ESP_LOGD(TAG, "SYSTEM_EVENT_AP_STACONNECTED, mac:%02x:%02x:%02x:%02x:%02x:%02x, aid:%d", \
staconnected->mac[0], staconnected->mac[0], staconnected->mac[1], \
staconnected->mac[3], staconnected->mac[4], staconnected->mac[5], staconnected->aid);
ESP_LOGD(TAG, "SYSTEM_EVENT_AP_STACONNECTED, mac:" MACSTR ", aid:%d", \
MAC2STR(staconnected->mac), staconnected->aid);
break;
}
case SYSTEM_EVENT_AP_STADISCONNECTED: {
system_event_ap_stadisconnected_t *stadisconnected = &event->event_info.sta_disconnected;
ESP_LOGD(TAG, "SYSTEM_EVENT_AP_STADISCONNECTED, mac:%02x:%02x:%02x:%02x:%02x:%02x, aid:%d", \
stadisconnected->mac[0], stadisconnected->mac[0], stadisconnected->mac[1], \
stadisconnected->mac[3], stadisconnected->mac[4], stadisconnected->mac[5], stadisconnected->aid);
ESP_LOGD(TAG, "SYSTEM_EVENT_AP_STADISCONNECTED, mac:" MACSTR ", aid:%d", \
MAC2STR(stadisconnected->mac), stadisconnected->aid);
break;
}
case SYSTEM_EVENT_AP_PROBEREQRECVED: {
system_event_ap_probe_req_rx_t *ap_probereqrecved = &event->event_info.ap_probereqrecved;
ESP_LOGD(TAG, "SYSTEM_EVENT_AP_PROBEREQRECVED, rssi:%d, mac:%02x:%02x:%02x:%02x:%02x:%02x", \
ap_probereqrecved->rssi, ap_probereqrecved->mac[0], ap_probereqrecved->mac[0], ap_probereqrecved->mac[1], \
ap_probereqrecved->mac[3], ap_probereqrecved->mac[4], ap_probereqrecved->mac[5]);
ESP_LOGD(TAG, "SYSTEM_EVENT_AP_PROBEREQRECVED, rssi:%d, mac:" MACSTR, \
ap_probereqrecved->rssi, \
MAC2STR(ap_probereqrecved->mac));
break;
}
default: {

View file

@ -34,6 +34,8 @@ typedef int32_t esp_err_t;
#define ESP_ERR_INVALID_SIZE 0x104
#define ESP_ERR_NOT_FOUND 0x105
#define ESP_ERR_NOT_SUPPORTED 0x106
#define ESP_ERR_TIMEOUT 0x107
#define ESP_ERR_WIFI_BASE 0x3000 /*!< Starting number of WiFi error codes */

View file

@ -21,57 +21,9 @@ extern "C"
{
#endif
#define ESP_PARTITION_TABLE_ADDR 0x4000
#define ESP_PARTITION_TABLE_ADDR 0x8000
#define ESP_PARTITION_MAGIC 0x50AA
/* SPI flash mode, used in esp_image_header_t */
typedef enum {
ESP_IMAGE_SPI_MODE_QIO,
ESP_IMAGE_SPI_MODE_QOUT,
ESP_IMAGE_SPI_MODE_DIO,
ESP_IMAGE_SPI_MODE_DOUT,
ESP_IMAGE_SPI_MODE_FAST_READ,
ESP_IMAGE_SPI_MODE_SLOW_READ
} esp_image_spi_mode_t;
/* SPI flash clock frequency */
enum {
ESP_IMAGE_SPI_SPEED_40M,
ESP_IMAGE_SPI_SPEED_26M,
ESP_IMAGE_SPI_SPEED_20M,
ESP_IMAGE_SPI_SPEED_80M = 0xF
} esp_image_spi_freq_t;
/* Supported SPI flash sizes */
typedef enum {
ESP_IMAGE_FLASH_SIZE_1MB = 0,
ESP_IMAGE_FLASH_SIZE_2MB,
ESP_IMAGE_FLASH_SIZE_4MB,
ESP_IMAGE_FLASH_SIZE_8MB,
ESP_IMAGE_FLASH_SIZE_16MB,
ESP_IMAGE_FLASH_SIZE_MAX
} esp_image_flash_size_t;
/* Main header of binary image */
typedef struct {
uint8_t magic;
uint8_t blocks;
uint8_t spi_mode; /* flash read mode (esp_image_spi_mode_t as uint8_t) */
uint8_t spi_speed: 4; /* flash frequency (esp_image_spi_freq_t as uint8_t) */
uint8_t spi_size: 4; /* flash chip size (esp_image_flash_size_t as uint8_t) */
uint32_t entry_addr;
uint8_t encrypt_flag; /* encrypt flag */
uint8_t secure_boot_flag; /* secure boot flag */
uint8_t extra_header[14]; /* ESP32 additional header, unused by second bootloader */
} esp_image_header_t;
/* Header of binary image segment */
typedef struct {
uint32_t load_addr;
uint32_t data_len;
} esp_image_section_header_t;
/* OTA selection structure (two copies in the OTA data partition.)
Size of 32 bytes is friendly to flash encryption */
typedef struct {

View file

@ -25,7 +25,7 @@ void ets_secure_boot_start(void);
void ets_secure_boot_finish(void);
void ets_secure_boot_hash(uint32_t *buf);
void ets_secure_boot_hash(const uint32_t *buf);
void ets_secure_boot_obtain(void);

View file

@ -29,6 +29,16 @@
#define EFUSE_RD_EFUSE_RD_DIS_M ((EFUSE_RD_EFUSE_RD_DIS_V)<<(EFUSE_RD_EFUSE_RD_DIS_S))
#define EFUSE_RD_EFUSE_RD_DIS_V 0xF
#define EFUSE_RD_EFUSE_RD_DIS_S 16
/* Read disable bits for efuse blocks 1-3 */
#define EFUSE_RD_DIS_BLK1 (1<<16)
#define EFUSE_RD_DIS_BLK2 (1<<17)
#define EFUSE_RD_DIS_BLK3 (1<<18)
/* Read disable FLASH_CRYPT_CONFIG, CODING_SCHEME & KEY_STATUS
in efuse block 0
*/
#define EFUSE_RD_DIS_BLK0_PARTIAL (1<<19)
/* EFUSE_RD_EFUSE_WR_DIS : RO ;bitpos:[15:0] ;default: 16'b0 ; */
/*description: read for efuse_wr_disable*/
#define EFUSE_RD_EFUSE_WR_DIS 0x0000FFFF
@ -36,6 +46,22 @@
#define EFUSE_RD_EFUSE_WR_DIS_V 0xFFFF
#define EFUSE_RD_EFUSE_WR_DIS_S 0
/* Write disable bits */
#define EFUSE_WR_DIS_RD_DIS (1<<0) /*< disable writing read disable reg */
#define EFUSE_WR_DIS_WR_DIS (1<<1) /*< disable writing write disable reg */
#define EFUSE_WR_DIS_FLASH_CRYPT_CNT (1<<2)
#define EFUSE_WR_DIS_MAC_SPI_CONFIG_HD (1<<3) /*< disable writing MAC & SPI config hd efuses */
#define EFUSE_WR_DIS_XPD_SDIO (1<<5) /*< disable writing SDIO config efuses */
#define EFUSE_WR_DIS_SPI_PAD_CONFIG (1<<6) /*< disable writing SPI_PAD_CONFIG efuses */
#define EFUSE_WR_DIS_BLK1 (1<<7) /*< disable writing BLK1 efuses */
#define EFUSE_WR_DIS_BLK2 (1<<8) /*< disable writing BLK2 efuses */
#define EFUSE_WR_DIS_BLK3 (1<<9) /*< disable writing BLK3 efuses */
#define EFUSE_WR_DIS_FLASH_CRYPT_CODING_SCHEME (1<<10) /*< disable writing FLASH_CRYPT_CONFIG and CODING_SCHEME efuses */
#define EFUSE_WR_DIS_ABS_DONE_0 (1<<12) /*< disable writing ABS_DONE_0 efuse */
#define EFUSE_WR_DIS_ABS_DONE_1 (1<<13) /*< disable writing ABS_DONE_1 efuse */
#define EFUSE_WR_DIS_JTAG_DISABLE (1<<14) /*< disable writing JTAG_DISABLE efuse */
#define EFUSE_WR_DIS_CONSOLE_DL_DISABLE (1<<15) /*< disable writing CONSOLE_DEBUG_DISABLE, DISABLE_DL_ENCRYPT, DISABLE_DL_DECRYPT and DISABLE_DL_CACHE efuses */
#define EFUSE_BLK0_RDATA1_REG (DR_REG_EFUSE_BASE + 0x004)
/* EFUSE_RD_WIFI_MAC_CRC_LOW : RO ;bitpos:[31:0] ;default: 32'b0 ; */
/*description: read for low 32bit WIFI_MAC_Address*/

View file

@ -18,8 +18,10 @@
#include "soc.h"
#define REG_UART_BASE( i ) (DR_REG_UART_BASE + (i) * 0x10000 + ( i > 1 ? 0xe000 : 0 ) )
#define REG_UART_AHB_BASE(i) (0x60000000 + (i) * 0x10000 + ( i > 1 ? 0xe000 : 0 ) )
#define UART_FIFO_AHB_REG(i) (REG_UART_AHB_BASE(i) + 0x0)
#define UART_FIFO_REG(i) (REG_UART_BASE(i) + 0x0)
/* UART_RXFIFO_RD_BYTE : RO ;bitpos:[7:0] ;default: 8'b0 ; */
/*description: This register stores one byte data read by rx fifo.*/
#define UART_RXFIFO_RD_BYTE 0x000000FF

@ -1 +1 @@
Subproject commit 76f91098061b0052fe1bb67e85001014f39b84a0
Subproject commit 84af0ed366e1ba38984f7df517a77f8ec4fa27ed

View file

@ -11,27 +11,47 @@ PYTHON ?= $(call dequote,$(CONFIG_PYTHON))
# two commands that can be used from other components
# to invoke esptool.py (with or without serial port args)
#
# NB: esptool.py lives in the sdk/bin directory not the component directory
ESPTOOLPY_SRC := $(COMPONENT_PATH)/esptool/esptool.py
ESPTOOLPY := $(PYTHON) $(ESPTOOLPY_SRC) --chip esp32
ESPTOOLPY_SERIAL := $(ESPTOOLPY) --port $(ESPPORT) --baud $(ESPBAUD)
# Supporting esptool command line tools
ESPEFUSEPY := $(PYTHON) $(COMPONENT_PATH)/esptool/espefuse.py
ESPSECUREPY := $(PYTHON) $(COMPONENT_PATH)/esptool/espsecure.py
export ESPSECUREPY # is used in bootloader_support component
ESPTOOL_FLASH_OPTIONS := --flash_mode $(ESPFLASHMODE) --flash_freq $(ESPFLASHFREQ) --flash_size $(ESPFLASHSIZE)
# the no-stub argument is temporary until esptool.py fully supports compressed uploads
ESPTOOL_ELF2IMAGE_OPTIONS :=
ESPTOOLPY_WRITE_FLASH=$(ESPTOOLPY_SERIAL) write_flash $(if $(CONFIG_ESPTOOLPY_COMPRESSED),-z) $(ESPTOOL_FLASH_OPTIONS)
ESPTOOL_ALL_FLASH_ARGS += $(CONFIG_APP_OFFSET) $(APP_BIN)
$(APP_BIN): $(APP_ELF) $(ESPTOOLPY_SRC)
$(Q) $(ESPTOOLPY) elf2image $(ESPTOOL_FLASH_OPTIONS) -o $@ $<
ifdef CONFIG_SECURE_BOOTLOADER_ENABLED
ifndef IS_BOOTLOADER_BUILD
# for secure boot, add a signing step to get from unsiged app to signed app
APP_BIN_UNSIGNED := $(APP_BIN:.bin=-unsigned.bin)
$(APP_BIN): $(APP_BIN_UNSIGNED)
$(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) -o $@ $^ # signed in-place
endif
endif
# non-secure boot (or bootloader), both these files are the same
APP_BIN_UNSIGNED ?= $(APP_BIN)
$(APP_BIN_UNSIGNED): $(APP_ELF) $(ESPTOOLPY_SRC)
$(ESPTOOLPY) elf2image $(ESPTOOL_FLASH_OPTIONS) $(ESPTOOL_ELF2IMAGE_OPTIONS) -o $@ $<
flash: all_binaries $(ESPTOOLPY_SRC)
@echo "Flashing binaries to serial port $(ESPPORT) (app at offset $(CONFIG_APP_OFFSET))..."
$(Q) $(ESPTOOLPY_WRITE_FLASH) $(ESPTOOL_ALL_FLASH_ARGS)
ifdef CONFIG_SECURE_BOOTLOADER_ENABLED
@echo "(Secure boot enabled, so bootloader not flashed automatically. See 'make bootloader' output)"
endif
$(ESPTOOLPY_WRITE_FLASH) $(ESPTOOL_ALL_FLASH_ARGS)
app-flash: $(APP_BIN) $(ESPTOOLPY_SRC)
@echo "Flashing app to serial port $(ESPPORT), offset $(CONFIG_APP_OFFSET)..."
$(Q) $(ESPTOOLPY_WRITE_FLASH) $(CONFIG_APP_OFFSET) $(APP_BIN)
$(ESPTOOLPY_WRITE_FLASH) $(CONFIG_APP_OFFSET) $(APP_BIN)
$(eval $(call SubmoduleCheck,$(ESPTOOLPY_SRC),$(COMPONENT_PATH)/esptool))

@ -1 +1 @@
Subproject commit 5c6962e894e0a118c9a4b5760876433493449260
Subproject commit b1e00025fa6cbc63062b205259ee70d91bfe4989

View file

@ -1,15 +1,9 @@
#
# Component Makefile
#
# This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default,
# this will take the sources in this directory, compile them and link them into
# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
# please read the SDK documents if you need to do this.
#
COMPONENT_ADD_INCLUDEDIRS := port/include include/expat
COMPONENT_SRCDIRS := library port
CFLAGS += -Wno-unused-function -DHAVE_EXPAT_CONFIG_H
include $(IDF_PATH)/make/component_common.mk

View file

@ -6,4 +6,3 @@ COMPONENT_ADD_LDFLAGS = -l$(COMPONENT_NAME) -Wl,--undefined=uxTopUsedPriority
COMPONENT_ADD_INCLUDEDIRS := include
COMPONENT_PRIV_INCLUDEDIRS := include/freertos
include $(IDF_PATH)/make/component_common.mk

View file

@ -265,12 +265,6 @@
#define INCLUDE_eTaskGetState 1
#define configUSE_QUEUE_SETS 1
#if (!defined XT_INTEXC_HOOKS)
#define configXT_INTEXC_HOOKS 1 /* Exception hooks used by certain tests */
#if configUSE_TRACE_FACILITY_2
#define configASSERT_2 1 /* Specific to Xtensa port */
#endif
#endif
#define configXT_BOARD 1 /* Board mode */
#define configXT_SIMULATOR 0

View file

@ -18,22 +18,34 @@ to this bit of memory will block.
The requirement for items to be contiguous is slightly problematic when the only way to place
the next item would involve a wraparound from the end to the beginning of the ringbuffer. This can
be solved in two ways:
- allow_split_items = pdTRUE: The insertion code will split the item in two items; one which fits
be solved (or not) in a few ways:
- type = RINGBUF_TYPE_ALLOWSPLIT: The insertion code will split the item in two items; one which fits
in the space left at the end of the ringbuffer, one that contains the remaining data which is placed
in the beginning. Two xRingbufferReceive calls will be needed to retrieve the data.
- allow_split_items = pdFALSE: The insertion code will leave the room at the end of the ringbuffer
- type = RINGBUF_TYPE_NOSPLIT: The insertion code will leave the room at the end of the ringbuffer
unused and instead will put the entire item at the start of the ringbuffer, as soon as there is
enough free space.
- type = RINGBUF_TYPE_BYTEBUF: This is your conventional byte-based ringbuffer. It does have no
overhead, but it has no item contiguousness either: a read will just give you the entire written
buffer space, or the space up to the end of the buffer, and writes can be broken up in any way
possible. Note that this type cannot do a 2nd read before returning the memory of the 1st.
The maximum size of an item will be affected by this decision. When split items are allowed, it's
acceptable to push items of (buffer_size)-16 bytes into the buffer. When it's not allowed, the
maximum size is (buffer_size/2)-8 bytes.
maximum size is (buffer_size/2)-8 bytes. The bytebuf can fill the entire buffer with data, it has
no overhead.
*/
//An opaque handle for a ringbuff object.
typedef void * RingbufHandle_t;
//The various types of buffer
typedef enum {
RINGBUF_TYPE_NOSPLIT = 0,
RINGBUF_TYPE_ALLOWSPLIT,
RINGBUF_TYPE_BYTEBUF
} ringbuf_type_t;
/**
* @brief Create a ring buffer
@ -45,7 +57,7 @@ typedef void * RingbufHandle_t;
*
* @return A RingbufHandle_t handle to the created ringbuffer, or NULL in case of error.
*/
RingbufHandle_t xRingbufferCreate(size_t buf_length, BaseType_t allow_split_items);
RingbufHandle_t xRingbufferCreate(size_t buf_length, ringbuf_type_t type);
/**
@ -120,6 +132,34 @@ void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t
void *xRingbufferReceiveFromISR(RingbufHandle_t ringbuf, size_t *item_size);
/**
* @brief Retrieve bytes from a ByteBuf type of ring buffer, specifying the maximum amount of bytes
* to return
*
* @param ringbuf - Ring buffer to retrieve the item from
* @param item_size - Pointer to a variable to which the size of the retrieved item will be written.
* @param xTicksToWait - Ticks to wait for items in the ringbuffer.
*
* @return Pointer to the retrieved item on success; *item_size filled with the length of the
* item. NULL on timeout, *item_size is untouched in that case.
*/
void *xRingbufferReceiveUpTo(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait, size_t wanted_size);
/**
* @brief Retrieve bytes from a ByteBuf type of ring buffer, specifying the maximum amount of bytes
* to return. Call this from an ISR.
*
* @param ringbuf - Ring buffer to retrieve the item from
* @param item_size - Pointer to a variable to which the size of the retrieved item will be written.
*
* @return Pointer to the retrieved item on success; *item_size filled with the length of the
* item. NULL when the ringbuffer is empty, *item_size is untouched in that case.
*/
void *xRingbufferReceiveUpToFromISR(RingbufHandle_t ringbuf, size_t *item_size, size_t wanted_size);
/**
* @brief Return a previously-retrieved item to the ringbuffer
*

View file

@ -42,7 +42,8 @@ typedef void (*xt_exc_handler)(XtExcFrame *);
/*
-------------------------------------------------------------------------------
Call this function to set a handler for the specified exception.
Call this function to set a handler for the specified exception. The handler
will be installed on the core that calls this function.
n - Exception number (type)
f - Handler function address, NULL to uninstall handler.
@ -61,7 +62,8 @@ extern xt_exc_handler xt_set_exception_handler(int n, xt_exc_handler f);
/*
-------------------------------------------------------------------------------
Call this function to set a handler for the specified interrupt.
Call this function to set a handler for the specified interrupt. The handler
will be installed on the core that calls this function.
n - Interrupt number.
f - Handler function address, NULL to uninstall handler.
@ -73,7 +75,8 @@ extern xt_handler xt_set_interrupt_handler(int n, xt_handler f, void * arg);
/*
-------------------------------------------------------------------------------
Call this function to enable the specified interrupts.
Call this function to enable the specified interrupts on the core that runs
this code.
mask - Bit mask of interrupts to be enabled.
-------------------------------------------------------------------------------
@ -83,7 +86,8 @@ extern void xt_ints_on(unsigned int mask);
/*
-------------------------------------------------------------------------------
Call this function to disable the specified interrupts.
Call this function to disable the specified interrupts on the core that runs
this code.
mask - Bit mask of interrupts to be disabled.
-------------------------------------------------------------------------------

View file

@ -19,11 +19,6 @@ it would on a single-core system: the other core still will keep on
executing all it's own. Use a mux, queue or semaphore to protect your
structures instead.
- While each core has individual interrupts, the handlers are shared. This
means that when you set a handler for an interrupt, it will get triggered if
the interrupt is triggered on both CPU0 as well as on CPU1. This is something
we may change in future FreeRTOS-esp32 releases.
- This FreeRTOS version has the task local storage backported from the 8.2.x
versions. It, however, has an addition: you can also set a callback when you
set the pointer. This callback will be called by the idle task, with the

View file

@ -18,6 +18,7 @@
#include "freertos/queue.h"
#include "freertos/xtensa_api.h"
#include "freertos/ringbuf.h"
#include "esp_attr.h"
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
@ -25,6 +26,7 @@
typedef enum {
flag_allowsplit = 1,
flag_bytebuf = 2,
} rbflag_t;
typedef enum {
@ -33,8 +35,10 @@ typedef enum {
} itemflag_t;
typedef struct ringbuf_t ringbuf_t;
//The ringbuffer structure
typedef struct {
struct ringbuf_t {
SemaphoreHandle_t free_space_sem; //Binary semaphore, wakes up writing threads when there's more free space
SemaphoreHandle_t items_buffered_sem; //Binary semaphore, indicates there are new packets in the circular buffer. See remark.
size_t size; //Size of the data storage
@ -44,7 +48,12 @@ typedef struct {
uint8_t *data; //Data storage
portMUX_TYPE mux; //Spinlock for actual data/ptr/struct modification
rbflag_t flags;
} ringbuf_t;
size_t maxItemSize;
//The following keep function pointers to hold different implementations for ringbuffer management.
BaseType_t (*copyItemToRingbufImpl)(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size);
uint8_t *(*getItemFromRingbufImpl)(ringbuf_t *rb, size_t *length, int wanted_length);
void (*returnItemToRingbufImpl)(ringbuf_t *rb, void *item);
};
@ -73,14 +82,16 @@ static int ringbufferFreeMem(ringbuf_t *rb)
return free_size-1;
}
//Copies a single item to the ring buffer. Assumes there is space in the ringbuffer and
//Copies a single item to the ring buffer; refuses to split items. Assumes there is space in the ringbuffer and
//the ringbuffer is locked. Increases write_ptr to the next item. Returns pdTRUE on
//success, pdFALSE if it can't make the item fit and the calling routine needs to retry
//later or fail.
//This function by itself is not threadsafe, always call from within a muxed section.
static BaseType_t copyItemToRingbuf(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size)
static BaseType_t copyItemToRingbufNoSplit(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size)
{
size_t rbuffer_size=(buffer_size+3)&~3; //Payload length, rounded to next 32-bit value
size_t rbuffer_size;
rbuffer_size=(buffer_size+3)&~3; //Payload length, rounded to next 32-bit value
configASSERT(((int)rb->write_ptr&3)==0); //write_ptr needs to be 32-bit aligned
configASSERT(rb->write_ptr-(rb->data+rb->size) >= sizeof(buf_entry_hdr_t)); //need to have at least the size
//of a header to the end of the ringbuff
@ -88,65 +99,28 @@ static BaseType_t copyItemToRingbuf(ringbuf_t *rb, uint8_t *buffer, size_t buffe
//See if we have enough contiguous space to write the buffer.
if (rem_len < rbuffer_size + sizeof(buf_entry_hdr_t)) {
//The buffer can't be contiguously written to the ringbuffer, but needs special handling. Do
//that depending on how the ringbuffer is configured.
//The code here is also expected to check if the buffer, mangled in whatever way is implemented,
//will still fit, and return pdFALSE if that is not the case.
if (rb->flags & flag_allowsplit) {
//Buffer plus header is not going to fit in the room from wr_pos to the end of the
//ringbuffer... we need to split the write in two.
//First, see if this will fit at all.
if (ringbufferFreeMem(rb) < (sizeof(buf_entry_hdr_t)*2)+rbuffer_size) {
//Will not fit.
return pdFALSE;
}
//Because the code at the end of the function makes sure we always have
//room for a header, this should never assert.
configASSERT(rem_len>=sizeof(buf_entry_hdr_t));
//Okay, it should fit. Write everything.
//First, place bit of buffer that does fit. Write header first...
buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr;
hdr->flags=0;
hdr->len=rem_len-sizeof(buf_entry_hdr_t);
rb->write_ptr+=sizeof(buf_entry_hdr_t);
rem_len-=sizeof(buf_entry_hdr_t);
if (rem_len!=0) {
//..then write the data bit that fits.
memcpy(rb->write_ptr, buffer, rem_len);
//Update vars so the code later on will write the rest of the data.
buffer+=rem_len;
rbuffer_size-=rem_len;
buffer_size-=rem_len;
} else {
//Huh, only the header fit. Mark as dummy so the receive function doesn't receive
//an useless zero-byte packet.
hdr->flags|=iflag_dummydata;
}
rb->write_ptr=rb->data;
} else {
//Buffer plus header is not going to fit in the room from wr_pos to the end of the
//ringbuffer... but we're not allowed to split the buffer. We need to fill the
//rest of the ringbuffer with a dummy item so we can place the data at the _start_ of
//the ringbuffer..
//First, find out if we actually have enough space at the start of the ringbuffer to
//make this work (Again, we need 4 bytes extra because otherwise read_ptr==free_ptr)
if (rb->free_ptr-rb->data < rbuffer_size+sizeof(buf_entry_hdr_t)+4) {
//Will not fit.
return pdFALSE;
}
//If the read buffer hasn't wrapped around yet, there's no way this will work either.
if (rb->free_ptr > rb->write_ptr) {
//No luck.
return pdFALSE;
}
//Okay, it will fit. Mark the rest of the ringbuffer space with a dummy packet.
buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr;
hdr->flags=iflag_dummydata;
//Reset the write pointer to the start of the ringbuffer so the code later on can
//happily write the data.
rb->write_ptr=rb->data;
//Buffer plus header is not going to fit in the room from wr_pos to the end of the
//ringbuffer... but we're not allowed to split the buffer. We need to fill the
//rest of the ringbuffer with a dummy item so we can place the data at the _start_ of
//the ringbuffer..
//First, find out if we actually have enough space at the start of the ringbuffer to
//make this work (Again, we need 4 bytes extra because otherwise read_ptr==free_ptr)
if (rb->free_ptr-rb->data < rbuffer_size+sizeof(buf_entry_hdr_t)+4) {
//Will not fit.
return pdFALSE;
}
//If the read buffer hasn't wrapped around yet, there's no way this will work either.
if (rb->free_ptr > rb->write_ptr) {
//No luck.
return pdFALSE;
}
//Okay, it will fit. Mark the rest of the ringbuffer space with a dummy packet.
buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr;
hdr->flags=iflag_dummydata;
//Reset the write pointer to the start of the ringbuffer so the code later on can
//happily write the data.
rb->write_ptr=rb->data;
} else {
//No special handling needed. Checking if it's gonna fit probably still is a good idea.
if (ringbufferFreeMem(rb) < sizeof(buf_entry_hdr_t)+rbuffer_size) {
@ -174,9 +148,117 @@ static BaseType_t copyItemToRingbuf(ringbuf_t *rb, uint8_t *buffer, size_t buffe
return pdTRUE;
}
//Copies a single item to the ring buffer; allows split items. Assumes there is space in the ringbuffer and
//the ringbuffer is locked. Increases write_ptr to the next item. Returns pdTRUE on
//success, pdFALSE if it can't make the item fit and the calling routine needs to retry
//later or fail.
//This function by itself is not threadsafe, always call from within a muxed section.
static BaseType_t copyItemToRingbufAllowSplit(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size)
{
size_t rbuffer_size;
rbuffer_size=(buffer_size+3)&~3; //Payload length, rounded to next 32-bit value
configASSERT(((int)rb->write_ptr&3)==0); //write_ptr needs to be 32-bit aligned
configASSERT(rb->write_ptr-(rb->data+rb->size) >= sizeof(buf_entry_hdr_t)); //need to have at least the size
//of a header to the end of the ringbuff
size_t rem_len=(rb->data + rb->size) - rb->write_ptr; //length remaining until end of ringbuffer
//See if we have enough contiguous space to write the buffer.
if (rem_len < rbuffer_size + sizeof(buf_entry_hdr_t)) {
//The buffer can't be contiguously written to the ringbuffer, but needs special handling. Do
//that depending on how the ringbuffer is configured.
//The code here is also expected to check if the buffer, mangled in whatever way is implemented,
//will still fit, and return pdFALSE if that is not the case.
//Buffer plus header is not going to fit in the room from wr_pos to the end of the
//ringbuffer... we need to split the write in two.
//First, see if this will fit at all.
if (ringbufferFreeMem(rb) < (sizeof(buf_entry_hdr_t)*2)+rbuffer_size) {
//Will not fit.
return pdFALSE;
}
//Because the code at the end of the function makes sure we always have
//room for a header, this should never assert.
configASSERT(rem_len>=sizeof(buf_entry_hdr_t));
//Okay, it should fit. Write everything.
//First, place bit of buffer that does fit. Write header first...
buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr;
hdr->flags=0;
hdr->len=rem_len-sizeof(buf_entry_hdr_t);
rb->write_ptr+=sizeof(buf_entry_hdr_t);
rem_len-=sizeof(buf_entry_hdr_t);
if (rem_len!=0) {
//..then write the data bit that fits.
memcpy(rb->write_ptr, buffer, rem_len);
//Update vars so the code later on will write the rest of the data.
buffer+=rem_len;
rbuffer_size-=rem_len;
buffer_size-=rem_len;
} else {
//Huh, only the header fit. Mark as dummy so the receive function doesn't receive
//an useless zero-byte packet.
hdr->flags|=iflag_dummydata;
}
rb->write_ptr=rb->data;
} else {
//No special handling needed. Checking if it's gonna fit probably still is a good idea.
if (ringbufferFreeMem(rb) < sizeof(buf_entry_hdr_t)+rbuffer_size) {
//Buffer is not going to fit, period.
return pdFALSE;
}
}
//If we are here, the buffer is guaranteed to fit in the space starting at the write pointer.
buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr;
hdr->len=buffer_size;
hdr->flags=0;
rb->write_ptr+=sizeof(buf_entry_hdr_t);
memcpy(rb->write_ptr, buffer, buffer_size);
rb->write_ptr+=rbuffer_size;
//The buffer will wrap around if we don't have room for a header anymore.
if ((rb->data+rb->size)-rb->write_ptr < sizeof(buf_entry_hdr_t)) {
//'Forward' the write buffer until we are at the start of the ringbuffer.
//The read pointer will always be at the start of a full header, which cannot
//exist at the point of the current write pointer, so there's no chance of overtaking
//that.
rb->write_ptr=rb->data;
}
return pdTRUE;
}
//Copies a bunch of daya to the ring bytebuffer. Assumes there is space in the ringbuffer and
//the ringbuffer is locked. Increases write_ptr to the next item. Returns pdTRUE on
//success, pdFALSE if it can't make the item fit and the calling routine needs to retry
//later or fail.
//This function by itself is not threadsafe, always call from within a muxed section.
static BaseType_t copyItemToRingbufByteBuf(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size)
{
size_t rem_len=(rb->data + rb->size) - rb->write_ptr; //length remaining until end of ringbuffer
//See if we have enough contiguous space to write the buffer.
if (rem_len < buffer_size) {
//...Nope. Write the data bit that fits.
memcpy(rb->write_ptr, buffer, rem_len);
//Update vars so the code later on will write the rest of the data.
buffer+=rem_len;
buffer_size-=rem_len;
rb->write_ptr=rb->data;
}
//If we are here, the buffer is guaranteed to fit in the space starting at the write pointer.
memcpy(rb->write_ptr, buffer, buffer_size);
rb->write_ptr+=buffer_size;
//The buffer will wrap around if we're at the end.
if ((rb->data+rb->size)==rb->write_ptr) {
rb->write_ptr=rb->data;
}
return pdTRUE;
}
//Retrieves a pointer to the data of the next item, or NULL if this is not possible.
//This function by itself is not threadsafe, always call from within a muxed section.
static uint8_t *getItemFromRingbuf(ringbuf_t *rb, size_t *length)
//Because we always return one item, this function ignores the wanted_length variable.
static uint8_t *getItemFromRingbufDefault(ringbuf_t *rb, size_t *length, int wanted_length)
{
uint8_t *ret;
configASSERT(((int)rb->read_ptr&3)==0);
@ -210,10 +292,48 @@ static uint8_t *getItemFromRingbuf(ringbuf_t *rb, size_t *length)
return ret;
}
//Retrieves a pointer to the data in the buffer, or NULL if this is not possible.
//This function by itself is not threadsafe, always call from within a muxed section.
//This function honours the wanted_length and will never return more data than this.
static uint8_t *getItemFromRingbufByteBuf(ringbuf_t *rb, size_t *length, int wanted_length)
{
uint8_t *ret;
if (rb->read_ptr != rb->free_ptr) {
//This type of ringbuff does not support multiple outstanding buffers.
return NULL;
}
if (rb->read_ptr == rb->write_ptr) {
//No data available.
return NULL;
}
ret=rb->read_ptr;
if (rb->read_ptr > rb->write_ptr) {
//Available data wraps around. Give data until the end of the buffer.
*length=rb->size-(rb->read_ptr - rb->data);
if (wanted_length != 0 && *length > wanted_length) {
*length=wanted_length;
rb->read_ptr+=wanted_length;
} else {
rb->read_ptr=rb->data;
}
} else {
//Return data up to write pointer.
*length=rb->write_ptr -rb->read_ptr;
if (wanted_length != 0 && *length > wanted_length) {
*length=wanted_length;
rb->read_ptr+=wanted_length;
} else {
rb->read_ptr=rb->write_ptr;
}
}
return ret;
}
//Returns an item to the ringbuffer. Will mark the item as free, and will see if the free pointer
//can be increase.
//This function by itself is not threadsafe, always call from within a muxed section.
static void returnItemToRingbuf(ringbuf_t *rb, void *item) {
static void returnItemToRingbufDefault(ringbuf_t *rb, void *item) {
uint8_t *data=(uint8_t*)item;
configASSERT(((int)rb->free_ptr&3)==0);
configASSERT(data >= rb->data);
@ -243,12 +363,26 @@ static void returnItemToRingbuf(ringbuf_t *rb, void *item) {
if ((rb->data+rb->size)-rb->free_ptr < sizeof(buf_entry_hdr_t)) {
rb->free_ptr=rb->data;
}
//The free_ptr can not exceed read_ptr, otherwise write_ptr might overwrite read_ptr.
//Read_ptr can not set to rb->data with free_ptr, otherwise write_ptr might wrap around to rb->data.
if(rb->free_ptr == rb->read_ptr) break;
//Next header
hdr=(buf_entry_hdr_t *)rb->free_ptr;
}
}
//Returns an item to the ringbuffer. Will mark the item as free, and will see if the free pointer
//can be increase.
//This function by itself is not threadsafe, always call from within a muxed section.
static void returnItemToRingbufBytebuf(ringbuf_t *rb, void *item) {
uint8_t *data=(uint8_t*)item;
configASSERT(data >= rb->data);
configASSERT(data < rb->data+rb->size);
//Free the read memory.
rb->free_ptr=rb->read_ptr;
}
void xRingbufferPrintInfo(RingbufHandle_t ringbuf)
{
ringbuf_t *rb=(ringbuf_t *)ringbuf;
@ -259,7 +393,7 @@ void xRingbufferPrintInfo(RingbufHandle_t ringbuf)
RingbufHandle_t xRingbufferCreate(size_t buf_length, BaseType_t allow_split_items)
RingbufHandle_t xRingbufferCreate(size_t buf_length, ringbuf_type_t type)
{
ringbuf_t *rb = malloc(sizeof(ringbuf_t));
if (rb==NULL) goto err;
@ -273,9 +407,35 @@ RingbufHandle_t xRingbufferCreate(size_t buf_length, BaseType_t allow_split_item
rb->free_space_sem = xSemaphoreCreateBinary();
rb->items_buffered_sem = xSemaphoreCreateBinary();
rb->flags=0;
if (allow_split_items) rb->flags|=flag_allowsplit;
if (type==RINGBUF_TYPE_ALLOWSPLIT) {
rb->flags|=flag_allowsplit;
rb->copyItemToRingbufImpl=copyItemToRingbufAllowSplit;
rb->getItemFromRingbufImpl=getItemFromRingbufDefault;
rb->returnItemToRingbufImpl=returnItemToRingbufDefault;
//Calculate max item size. Worst case, we need to split an item into two, which means two headers of overhead.
rb->maxItemSize=rb->size-(sizeof(buf_entry_hdr_t)*2)-4;
} else if (type==RINGBUF_TYPE_BYTEBUF) {
rb->flags|=flag_bytebuf;
rb->copyItemToRingbufImpl=copyItemToRingbufByteBuf;
rb->getItemFromRingbufImpl=getItemFromRingbufByteBuf;
rb->returnItemToRingbufImpl=returnItemToRingbufBytebuf;
//Calculate max item size. We have no headers and can split anywhere -> size is total size minus one.
rb->maxItemSize=rb->size-1;
} else if (type==RINGBUF_TYPE_NOSPLIT) {
rb->copyItemToRingbufImpl=copyItemToRingbufNoSplit;
rb->getItemFromRingbufImpl=getItemFromRingbufDefault;
rb->returnItemToRingbufImpl=returnItemToRingbufDefault;
//Calculate max item size. Worst case, we have the write ptr in such a position that we are lacking four bytes of free
//memory to put an item into the rest of the memory. If this happens, we have to dummy-fill
//(item_data-4) bytes of buffer, then we only have (size-(item_data-4) bytes left to fill
//with the real item. (item size being header+data)
rb->maxItemSize=(rb->size/2)-sizeof(buf_entry_hdr_t)-4;
} else {
configASSERT(0);
}
if (rb->free_space_sem == NULL || rb->items_buffered_sem == NULL) goto err;
vPortCPUInitializeMutex(&rb->mux);
return (RingbufHandle_t)rb;
err:
@ -303,18 +463,7 @@ size_t xRingbufferGetMaxItemSize(RingbufHandle_t ringbuf)
{
ringbuf_t *rb=(ringbuf_t *)ringbuf;
configASSERT(rb);
//In both cases, we return 4 bytes less than what we actually can have. If the ringbuffer is
//indeed entirely filled, read_ptr==free_ptr, which throws off the free space calculation.
if (rb->flags & flag_allowsplit) {
//Worst case, we need to split an item into two, which means two headers of overhead.
return rb->size-(sizeof(buf_entry_hdr_t)*2)-4;
} else {
//Worst case, we have the write ptr in such a position that we are lacking four bytes of free
//memory to put an item into the rest of the memory. If this happens, we have to dummy-fill
//(item_data-4) bytes of buffer, then we only have (size-(item_data-4) bytes left to fill
//with the real item. (item size being header+data)
return (rb->size/2)-sizeof(buf_entry_hdr_t)-4;
}
return rb->maxItemSize;
}
BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t dataSize, TickType_t ticks_to_wait)
@ -352,7 +501,7 @@ BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t dataSize,
portENTER_CRITICAL(&rb->mux);
//Another thread may have been able to sneak its write first. Check again now we locked the ringbuff, and retry
//everything if this is the case. Otherwise, we can write and are done.
done=copyItemToRingbuf(rb, data, dataSize);
done=rb->copyItemToRingbufImpl(rb, data, dataSize);
portEXIT_CRITICAL(&rb->mux);
}
xSemaphoreGive(rb->items_buffered_sem);
@ -371,8 +520,7 @@ BaseType_t xRingbufferSendFromISR(RingbufHandle_t ringbuf, void *data, size_t da
//Does not fit in the remaining space in the ringbuffer.
write_succeeded=pdFALSE;
} else {
copyItemToRingbuf(rb, data, dataSize);
write_succeeded=pdTRUE;
write_succeeded = rb->copyItemToRingbufImpl(rb, data, dataSize);
}
portEXIT_CRITICAL_ISR(&rb->mux);
if (write_succeeded) {
@ -382,7 +530,7 @@ BaseType_t xRingbufferSendFromISR(RingbufHandle_t ringbuf, void *data, size_t da
}
void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait)
static void *xRingbufferReceiveGeneric(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait, size_t wanted_size)
{
ringbuf_t *rb=(ringbuf_t *)ringbuf;
uint8_t *itemData;
@ -399,7 +547,7 @@ void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t
}
//Okay, we seem to have data in the buffer. Grab the mux and copy it out if it's still there.
portENTER_CRITICAL(&rb->mux);
itemData=getItemFromRingbuf(rb, item_size);
itemData=rb->getItemFromRingbufImpl(rb, item_size, wanted_size);
portEXIT_CRITICAL(&rb->mux);
if (itemData) {
//We managed to get an item.
@ -409,6 +557,11 @@ void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t
return (void*)itemData;
}
void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait)
{
return xRingbufferReceiveGeneric(ringbuf, item_size, ticks_to_wait, 0);
}
void *xRingbufferReceiveFromISR(RingbufHandle_t ringbuf, size_t *item_size)
{
@ -416,7 +569,28 @@ void *xRingbufferReceiveFromISR(RingbufHandle_t ringbuf, size_t *item_size)
uint8_t *itemData;
configASSERT(rb);
portENTER_CRITICAL_ISR(&rb->mux);
itemData=getItemFromRingbuf(rb, item_size);
itemData=rb->getItemFromRingbufImpl(rb, item_size, 0);
portEXIT_CRITICAL_ISR(&rb->mux);
return (void*)itemData;
}
void *xRingbufferReceiveUpTo(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait, size_t wanted_size) {
ringbuf_t *rb=(ringbuf_t *)ringbuf;
if (wanted_size == 0) return NULL;
configASSERT(rb);
configASSERT(rb->flags & flag_bytebuf);
return xRingbufferReceiveGeneric(ringbuf, item_size, ticks_to_wait, wanted_size);
}
void *xRingbufferReceiveUpToFromISR(RingbufHandle_t ringbuf, size_t *item_size, size_t wanted_size)
{
ringbuf_t *rb=(ringbuf_t *)ringbuf;
uint8_t *itemData;
if (wanted_size == 0) return NULL;
configASSERT(rb);
configASSERT(rb->flags & flag_bytebuf);
portENTER_CRITICAL_ISR(&rb->mux);
itemData=rb->getItemFromRingbufImpl(rb, item_size, 0);
portEXIT_CRITICAL_ISR(&rb->mux);
return (void*)itemData;
}
@ -426,7 +600,7 @@ void vRingbufferReturnItem(RingbufHandle_t ringbuf, void *item)
{
ringbuf_t *rb=(ringbuf_t *)ringbuf;
portENTER_CRITICAL_ISR(&rb->mux);
returnItemToRingbuf(rb, item);
rb->returnItemToRingbufImpl(rb, item);
portEXIT_CRITICAL_ISR(&rb->mux);
xSemaphoreGive(rb->free_space_sem);
}
@ -436,7 +610,7 @@ void vRingbufferReturnItemFromISR(RingbufHandle_t ringbuf, void *item, BaseType_
{
ringbuf_t *rb=(ringbuf_t *)ringbuf;
portENTER_CRITICAL_ISR(&rb->mux);
returnItemToRingbuf(rb, item);
rb->returnItemToRingbufImpl(rb, item);
portEXIT_CRITICAL_ISR(&rb->mux);
xSemaphoreGiveFromISR(rb->free_space_sem, higher_prio_task_awoken);
}

View file

@ -30,7 +30,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <xtensa/config/core.h>
#include "freertos/FreeRTOS.h"
#include "freertos/xtensa_api.h"
#include "freertos/portable.h"
#include "rom/ets_sys.h"
@ -39,7 +41,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
/* Handler table is in xtensa_intr_asm.S */
// Todo: Make multicore - JD
extern xt_exc_handler _xt_exception_table[XCHAL_EXCCAUSE_NUM];
extern xt_exc_handler _xt_exception_table[XCHAL_EXCCAUSE_NUM*portNUM_PROCESSORS];
/*
@ -66,6 +68,8 @@ xt_exc_handler xt_set_exception_handler(int n, xt_exc_handler f)
if( n < 0 || n >= XCHAL_EXCCAUSE_NUM )
return 0; /* invalid exception number */
/* Convert exception number to _xt_exception_table name */
n = n * portNUM_PROCESSORS + xPortGetCoreID();
old = _xt_exception_table[n];
if (f) {
@ -89,7 +93,7 @@ typedef struct xt_handler_table_entry {
void * arg;
} xt_handler_table_entry;
extern xt_handler_table_entry _xt_interrupt_table[XCHAL_NUM_INTERRUPTS];
extern xt_handler_table_entry _xt_interrupt_table[XCHAL_NUM_INTERRUPTS*portNUM_PROCESSORS];
/*
@ -118,6 +122,9 @@ xt_handler xt_set_interrupt_handler(int n, xt_handler f, void * arg)
if( Xthal_intlevel[n] > XCHAL_EXCM_LEVEL )
return 0; /* priority level too high to safely handle in C */
/* Convert exception number to _xt_exception_table name */
n = n * portNUM_PROCESSORS + xPortGetCoreID();
entry = _xt_interrupt_table + n;
old = entry->handler;

View file

@ -30,6 +30,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <xtensa/config/core.h>
#include "xtensa_context.h"
#include "FreeRTOSConfig.h"
#if XCHAL_HAVE_INTERRUPTS
@ -59,6 +60,15 @@ _xt_vpri_mask: .word 0xFFFFFFFF /* Virtual priority mask */
Table of C-callable interrupt handlers for each interrupt. Note that not all
slots can be filled, because interrupts at level > EXCM_LEVEL will not be
dispatched to a C handler by default.
Stored as:
int 0 cpu 0
int 0 cpu 1
...
int 0 cpu n
int 1 cpu 0
int 1 cpu 1
etc
-------------------------------------------------------------------------------
*/
@ -69,7 +79,7 @@ _xt_vpri_mask: .word 0xFFFFFFFF /* Virtual priority mask */
_xt_interrupt_table:
.set i, 0
.rept XCHAL_NUM_INTERRUPTS
.rept XCHAL_NUM_INTERRUPTS*portNUM_PROCESSORS
.word xt_unhandled_interrupt /* handler address */
.word i /* handler arg (default: intnum) */
.set i, i+1
@ -85,6 +95,15 @@ _xt_interrupt_table:
Table of C-callable exception handlers for each exception. Note that not all
slots will be active, because some exceptions (e.g. coprocessor exceptions)
are always handled by the OS and cannot be hooked by user handlers.
Stored as:
exc 0 cpu 0
exc 0 cpu 1
...
exc 0 cpu n
exc 1 cpu 0
exc 1 cpu 1
etc
-------------------------------------------------------------------------------
*/
@ -93,7 +112,7 @@ _xt_interrupt_table:
.align 4
_xt_exception_table:
.rept XCHAL_EXCCAUSE_NUM
.rept XCHAL_EXCCAUSE_NUM * portNUM_PROCESSORS
.word xt_unhandled_exception /* handler address */
.endr

View file

@ -113,6 +113,27 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define XIE_ARG 4
#define XIE_SIZE 8
/*
Macro get_percpu_entry_for - convert a per-core ID into a multicore entry.
Basically does reg=reg*portNUM_PROCESSORS+current_core_id
Multiple versions here to optimize for specific portNUM_PROCESSORS values.
*/
.macro get_percpu_entry_for reg scratch
#if (portNUM_PROCESSORS == 1)
/* No need to do anything */
#elif (portNUM_PROCESSORS == 2)
/* Optimized 2-core code. */
getcoreid \scratch
addx2 \reg,\reg,\scratch
#else
/* Generalized n-core code. Untested! */
movi \scratch,portNUM_PROCESSORS
mull \scratch,\reg,\scratch
getcoreid \reg
add \reg,\scratch,\reg
#endif
.endm
/*
--------------------------------------------------------------------------------
Macro extract_msb - return the input with only the highest bit set.
@ -229,6 +250,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
find_ms_setbit a3, a4, a3, 0 /* a3 = interrupt number */
get_percpu_entry_for a3, a12
movi a4, _xt_interrupt_table
addx8 a3, a3, a4 /* a3 = address of interrupt table entry */
l32i a4, a3, XIE_HANDLER /* a4 = handler address */
@ -395,6 +417,9 @@ panic_print_hex_ok:
with index 0 containing the entry for user exceptions.
Initialized with all 0s, meaning no handler is installed at each level.
See comment in xtensa_rtos.h for more details.
*WARNING* This array is for all CPUs, that is, installing a hook for
one CPU will install it for all others as well!
--------------------------------------------------------------------------------
*/
@ -688,6 +713,7 @@ _xt_user_exc:
rsr a2, EXCCAUSE /* recover exc cause */
movi a3, _xt_exception_table
get_percpu_entry_for a3, a4
addx4 a4, a2, a3 /* a4 = address of exception table entry */
l32i a4, a4, 0 /* a4 = handler address */
#ifdef __XTENSA_CALL0_ABI__

View file

@ -1,13 +1,7 @@
#
# Component Makefile
#
# This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default,
# this will take the sources in this directory, compile them and link them into
# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
# please read the SDK documents if you need to do this.
#
COMPONENT_ADD_INCLUDEDIRS := include port/include
COMPONENT_SRCDIRS := library port
include $(IDF_PATH)/make/component_common.mk

View file

@ -1,3 +1,5 @@
COMPONENT_ADD_INCLUDEDIRS := include
#
# Component Makefile
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
include $(IDF_PATH)/make/component_common.mk

View file

@ -19,6 +19,10 @@
#include <stdarg.h>
#include "sdkconfig.h"
#ifdef BOOTLOADER_BUILD
#include <rom/ets_sys.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif

View file

@ -284,7 +284,15 @@ static inline void heap_swap(int i, int j)
}
#endif //BOOTLOADER_BUILD
IRAM_ATTR uint32_t esp_log_early_timestamp()
#ifndef BOOTLOADER_BUILD
#define ATTR IRAM_ATTR
#else
#define ATTR
#endif // BOOTLOADER_BUILD
uint32_t ATTR esp_log_early_timestamp()
{
return xthal_get_ccount() / (CPU_CLK_FREQ_ROM / 1000);
}
@ -305,9 +313,6 @@ uint32_t IRAM_ATTR esp_log_timestamp()
#else
uint32_t IRAM_ATTR esp_log_timestamp()
{
return esp_log_early_timestamp();
}
uint32_t esp_log_timestamp() __attribute__((alias("esp_log_early_timestamp")));
#endif //BOOTLOADER_BUILD

View file

@ -8,4 +8,3 @@ COMPONENT_SRCDIRS := api apps/sntp apps core/ipv4 core/ipv6 core netif port/free
CFLAGS += -Wno-address -Wno-unused-variable -Wno-unused-but-set-variable
include $(IDF_PATH)/make/component_common.mk

View file

@ -35,6 +35,7 @@
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <sys/fcntl.h>
#include "esp_task.h"
#include "sdkconfig.h"

View file

@ -6,4 +6,3 @@ COMPONENT_ADD_INCLUDEDIRS := port/include include
COMPONENT_SRCDIRS := library port
include $(IDF_PATH)/make/component_common.mk

View file

@ -0,0 +1,6 @@
# only compile the micro-ecc/uECC.c source file
# (SRCDIRS is needed so build system can find the source file)
COMPONENT_SRCDIRS := micro-ecc
COMPONENT_OBJS := micro-ecc/uECC.o
COMPONENT_ADD_INCLUDEDIRS := micro-ecc

@ -0,0 +1 @@
Subproject commit 14222e062d77f45321676e813d9525f32a88e8fa

View file

@ -1,5 +1,4 @@
COMPONENT_ADD_LDFLAGS := $(abspath lib/libc.a) $(abspath lib/libm.a) -lnewlib
COMPONENT_ADD_LDFLAGS := $(COMPONENT_PATH)/lib/libc.a $(COMPONENT_PATH)/lib/libm.a -lnewlib
COMPONENT_ADD_INCLUDEDIRS := include platform_include
include $(IDF_PATH)/make/component_common.mk

View file

@ -55,14 +55,16 @@ static uint64_t get_rtc_time_us()
#endif // WITH_RTC
// time from Epoch to the first boot time
// s_boot_time: time from Epoch to the first boot time
#ifdef WITH_RTC
static RTC_DATA_ATTR struct timeval s_boot_time;
#else
#elif defined(WITH_FRC1)
static struct timeval s_boot_time;
#endif
static _lock_t s_boot_time_lock;
#if defined(WITH_RTC) || defined(WITH_FRC1)
static _lock_t s_boot_time_lock;
#endif
#ifdef WITH_FRC1
#define FRC1_PRESCALER 16
@ -112,7 +114,7 @@ void esp_setup_time_syscalls()
clock_t IRAM_ATTR _times_r(struct _reent *r, struct tms *ptms)
{
clock_t t = xTaskGetTickCount() * (portTICK_PERIOD_MS * CLK_TCK / 1000);
ptms->tms_cstime = t;
ptms->tms_cstime = 0;
ptms->tms_cutime = 0;
ptms->tms_stime = t;
ptms->tms_utime = 0;
@ -121,6 +123,7 @@ clock_t IRAM_ATTR _times_r(struct _reent *r, struct tms *ptms)
return (clock_t) tv.tv_sec;
}
#if defined( WITH_FRC1 ) || defined( WITH_RTC )
static uint64_t get_time_since_boot()
{
uint64_t microseconds = 0;
@ -140,6 +143,7 @@ static uint64_t get_time_since_boot()
#endif
return microseconds;
}
#endif // defined( WITH_FRC1 ) || defined( WITH_RTC )
int IRAM_ATTR _gettimeofday_r(struct _reent *r, struct timeval *tv, void *tz)
{
@ -176,7 +180,7 @@ int settimeofday(const struct timeval *tv, const struct timezone *tz)
}
return 0;
#else
__errno_r(r) = ENOSYS;
errno = ENOSYS;
return -1;
#endif
}

View file

@ -5,5 +5,3 @@
COMPONENT_ADD_INCLUDEDIRS := port/include include
COMPONENT_SRCDIRS := library port
include $(IDF_PATH)/make/component_common.mk

View file

@ -6,4 +6,3 @@ COMPONENT_ADD_INCLUDEDIRS := include
COMPONENT_SRCDIRS := src
include $(IDF_PATH)/make/component_common.mk

View file

@ -656,19 +656,20 @@ esp_err_t Page::findItem(uint8_t nsIndex, ItemType datatype, const char* key, si
if (mState == PageState::CORRUPT || mState == PageState::INVALID || mState == PageState::UNINITIALIZED) {
return ESP_ERR_NVS_NOT_FOUND;
}
if (itemIndex >= ENTRY_COUNT) {
size_t findBeginIndex = itemIndex;
if (findBeginIndex >= ENTRY_COUNT) {
return ESP_ERR_NVS_NOT_FOUND;
}
CachedFindInfo findInfo(nsIndex, datatype, key);
if (mFindInfo == findInfo) {
itemIndex = mFindInfo.itemIndex();
findBeginIndex = mFindInfo.itemIndex();
}
size_t start = mFirstUsedEntry;
if (itemIndex > mFirstUsedEntry && itemIndex < ENTRY_COUNT) {
start = itemIndex;
if (findBeginIndex > mFirstUsedEntry && findBeginIndex < ENTRY_COUNT) {
start = findBeginIndex;
}
size_t end = mNextFreeEntry;

View file

@ -71,8 +71,8 @@ esp_err_t Storage::init(uint32_t baseSector, uint32_t sectorCount)
esp_err_t Storage::findItem(uint8_t nsIndex, ItemType datatype, const char* key, Page* &page, Item& item)
{
size_t itemIndex = 0;
for (auto it = std::begin(mPageManager); it != std::end(mPageManager); ++it) {
size_t itemIndex = 0;
auto err = it->findItem(nsIndex, datatype, key, itemIndex, item);
if (err == ESP_OK) {
page = it;

View file

@ -300,6 +300,27 @@ TEST_CASE("storage doesn't add duplicates within multiple pages", "[nvs]")
CHECK(page.findItem(1, itemTypeOf<int>(), "bar") == ESP_OK);
}
TEST_CASE("storage can find items on second page if first is not fully written and has cached search data", "[nvs]")
{
SpiFlashEmulator emu(3);
Storage storage;
CHECK(storage.init(0, 3) == ESP_OK);
int bar = 0;
uint8_t bigdata[100 * 32] = {0};
// write one big chunk of data
ESP_ERROR_CHECK(storage.writeItem(0, ItemType::BLOB, "first", bigdata, sizeof(bigdata)));
// write second one; it will not fit into the first page
ESP_ERROR_CHECK(storage.writeItem(0, ItemType::BLOB, "second", bigdata, sizeof(bigdata)));
size_t size;
ESP_ERROR_CHECK(storage.getItemDataSize(0, ItemType::BLOB, "first", size));
CHECK(size == sizeof(bigdata));
ESP_ERROR_CHECK(storage.getItemDataSize(0, ItemType::BLOB, "second", size));
CHECK(size == sizeof(bigdata));
}
TEST_CASE("can write and read variable length data lots of times", "[nvs]")
{
SpiFlashEmulator emu(8);
@ -1055,6 +1076,39 @@ TEST_CASE("crc error in variable length item is handled", "[nvs]")
}
TEST_CASE("read/write failure (TW8406)", "[nvs]")
{
SpiFlashEmulator emu(3);
nvs_flash_init_custom(0, 3);
for (int attempts = 0; attempts < 3; ++attempts) {
int i = 0;
nvs_handle light_handle = 0;
char key[15] = {0};
char data[76] = {12, 13, 14, 15, 16};
uint8_t number = 20;
size_t data_len = sizeof(data);
ESP_ERROR_CHECK(nvs_open("LIGHT", NVS_READWRITE, &light_handle));
ESP_ERROR_CHECK(nvs_set_u8(light_handle, "RecordNum", number));
for (i = 0; i < number; ++i) {
sprintf(key, "light%d", i);
ESP_ERROR_CHECK(nvs_set_blob(light_handle, key, data, sizeof(data)));
}
nvs_commit(light_handle);
uint8_t get_number = 0;
ESP_ERROR_CHECK(nvs_get_u8(light_handle, "RecordNum", &get_number));
REQUIRE(number == get_number);
for (i = 0; i < number; ++i) {
char data[76] = {0};
sprintf(key, "light%d", i);
ESP_ERROR_CHECK(nvs_get_blob(light_handle, key, data, &data_len));
}
nvs_close(light_handle);
}
}
TEST_CASE("dump all performance data", "[nvs]")
{
std::cout << "====================" << std::endl << "Dumping benchmarks" << std::endl;

View file

@ -7,4 +7,3 @@ COMPONENT_PRIV_INCLUDEDIRS := include/internal include/platform include/openssl
COMPONENT_SRCDIRS := library platform
include $(IDF_PATH)/make/component_common.mk

View file

@ -11,6 +11,8 @@
# NB: gen_esp32part.py lives in the sdk/bin/ dir not component dir
GEN_ESP32PART := $(PYTHON) $(COMPONENT_PATH)/gen_esp32part.py -q
PARTITION_TABLE_OFFSET := 0x8000
# Path to partition CSV file is relative to project path for custom
# partition CSV files, but relative to component dir otherwise.$
PARTITION_TABLE_ROOT := $(call dequote,$(if $(CONFIG_PARTITION_TABLE_CUSTOM),$(PROJECT_PATH),$(COMPONENT_PATH)))
@ -18,28 +20,38 @@ PARTITION_TABLE_CSV_PATH := $(call dequote,$(abspath $(PARTITION_TABLE_ROOT)/$(s
PARTITION_TABLE_BIN := $(BUILD_DIR_BASE)/$(notdir $(PARTITION_TABLE_CSV_PATH:.csv=.bin))
$(PARTITION_TABLE_BIN): $(PARTITION_TABLE_CSV_PATH)
ifdef CONFIG_SECURE_BOOTLOADER_ENABLED
PARTITION_TABLE_BIN_UNSIGNED := $(PARTITION_TABLE_BIN:.bin=-unsigned.bin)
# add an extra signing step for secure partition table
$(PARTITION_TABLE_BIN): $(PARTITION_TABLE_BIN_UNSIGNED)
$(Q) $(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) -o $@ $<
else
# secure bootloader disabled, both files are the same
PARTITION_TABLE_BIN_UNSIGNED := $(PARTITION_TABLE_BIN)
endif
$(PARTITION_TABLE_BIN_UNSIGNED): $(PARTITION_TABLE_CSV_PATH) $(SDKCONFIG_MAKEFILE)
@echo "Building partitions from $(PARTITION_TABLE_CSV_PATH)..."
$(Q) $(GEN_ESP32PART) $< $@
$(GEN_ESP32PART) $< $@
all_binaries: $(PARTITION_TABLE_BIN)
PARTITION_TABLE_FLASH_CMD = $(ESPTOOLPY_SERIAL) write_flash 0x4000 $(PARTITION_TABLE_BIN)
ESPTOOL_ALL_FLASH_ARGS += 0x4000 $(PARTITION_TABLE_BIN)
PARTITION_TABLE_FLASH_CMD = $(ESPTOOLPY_SERIAL) write_flash $(PARTITION_TABLE_OFFSET) $(PARTITION_TABLE_BIN)
ESPTOOL_ALL_FLASH_ARGS += $(PARTITION_TABLE_OFFSET) $(PARTITION_TABLE_BIN)
partition_table: $(PARTITION_TABLE_BIN)
@echo "Partition table binary generated. Contents:"
@echo $(SEPARATOR)
$(Q) $(GEN_ESP32PART) $<
$(GEN_ESP32PART) $<
@echo $(SEPARATOR)
@echo "Partition flashing command:"
@echo "$(PARTITION_TABLE_FLASH_CMD)"
partition_table-flash: $(PARTITION_TABLE_BIN)
@echo "Flashing partition table..."
$(Q) $(PARTITION_TABLE_FLASH_CMD)
$(PARTITION_TABLE_FLASH_CMD)
partition_table-clean:
$(Q) rm -f $(PARTITION_TABLE_BIN)
rm -f $(PARTITION_TABLE_BIN)
clean: partition_table-clean

View file

@ -9,6 +9,8 @@ import struct
import argparse
import sys
MAX_PARTITION_LENGTH = 0xC00 # 3K for partition data (96 entries) leaves 1K in a 4K sector for signature
__version__ = '1.0'
quiet = False
@ -84,15 +86,22 @@ class PartitionTable(list):
@classmethod
def from_binary(cls, b):
if len(b) % 32 != 0:
raise InputError("Partition table length must be a multiple of 32 bytes. Got %d bytes." % len(b))
result = cls()
for o in range(0,len(b),32):
result.append(PartitionDefinition.from_binary(b[o:o+32]))
data = b[o:o+32]
if len(data) != 32:
raise InputError("Ran out of partition table data before reaching end marker")
if data == '\xFF'*32:
break # end of partition table
result.append(PartitionDefinition.from_binary(data))
return result
def to_binary(self):
return "".join(e.to_binary() for e in self)
result = "".join(e.to_binary() for e in self)
if len(result )>= MAX_PARTITION_LENGTH:
raise InputError("Binary partition table length (%d) longer than max" % len(result))
result += "\xFF" * (MAX_PARTITION_LENGTH - len(result)) # pad the sector, for signing
return result
def to_csv(self, simple_formatting=False):
rows = [ "# Espressif ESP32 Partition Table",

View file

@ -37,6 +37,10 @@ LONGER_BINARY_TABLE += "\xAA\x50\x10\x00" + \
"second" + ("\0"*10) + \
"\x00\x00\x00\x00"
def _strip_trailing_ffs(binary_table):
while binary_table.endswith("\xFF"):
binary_table = binary_table[0:len(binary_table)-1]
return binary_table
class CSVParserTests(unittest.TestCase):
@ -156,7 +160,7 @@ class BinaryOutputTests(unittest.TestCase):
first, 0x30, 0xEE, 0x100400, 0x300000
"""
t = PartitionTable.from_csv(csv)
tb = t.to_binary()
tb = _strip_trailing_ffs(t.to_binary())
self.assertEqual(len(tb), 32)
self.assertEqual('\xAA\x50', tb[0:2]) # magic
self.assertEqual('\x30\xee', tb[2:4]) # type, subtype
@ -170,7 +174,7 @@ first, 0x30, 0xEE, 0x100400, 0x300000
second,0x31, 0xEF, , 0x100000
"""
t = PartitionTable.from_csv(csv)
tb = t.to_binary()
tb = _strip_trailing_ffs(t.to_binary())
self.assertEqual(len(tb), 64)
self.assertEqual('\xAA\x50', tb[0:2])
self.assertEqual('\xAA\x50', tb[32:34])
@ -215,7 +219,7 @@ class BinaryParserTests(unittest.TestCase):
self.assertEqual(t[2].type, 0x10)
self.assertEqual(t[2].name, "second")
round_trip = t.to_binary()
round_trip = _strip_trailing_ffs(t.to_binary())
self.assertEqual(round_trip, LONGER_BINARY_TABLE)
def test_bad_magic(self):
@ -267,7 +271,7 @@ class CSVOutputTests(unittest.TestCase):
self.assertEqual(row[0], "factory")
self.assertEqual(row[1], "app")
self.assertEqual(row[2], "2")
self.assertEqual(row[3], "64K")
self.assertEqual(row[3], "0x10000")
self.assertEqual(row[4], "1M")
# round trip back to a PartitionTable and check is identical
@ -291,7 +295,7 @@ class CommandLineTests(unittest.TestCase):
# reopen the CSV and check the generated binary is identical
with open(csvpath, 'r') as f:
from_csv = PartitionTable.from_csv(f.read())
self.assertEqual(from_csv.to_binary(), LONGER_BINARY_TABLE)
self.assertEqual(_strip_trailing_ffs(from_csv.to_binary()), LONGER_BINARY_TABLE)
# run gen_esp32part.py to conver the CSV to binary again
subprocess.check_call([sys.executable, "../gen_esp32part.py",
@ -299,6 +303,7 @@ class CommandLineTests(unittest.TestCase):
# assert that file reads back as identical
with open(binpath, 'rb') as f:
binary_readback = f.read()
binary_readback = _strip_trailing_ffs(binary_readback)
self.assertEqual(binary_readback, LONGER_BINARY_TABLE)
finally:

View file

@ -38,8 +38,8 @@ static uint32_t s_flash_op_cache_state[2];
#ifndef CONFIG_FREERTOS_UNICORE
static SemaphoreHandle_t s_flash_op_mutex;
static bool s_flash_op_can_start = false;
static bool s_flash_op_complete = false;
static volatile bool s_flash_op_can_start = false;
static volatile bool s_flash_op_complete = false;
void spi_flash_init_lock()
{

View file

@ -5,4 +5,3 @@ ifdef IS_BOOTLOADER_BUILD
COMPONENT_OBJS := spi_flash_rom_patch.o
endif
include $(IDF_PATH)/make/component_common.mk

View file

@ -19,9 +19,15 @@ static const uint32_t STATUS_QIE_BIT = (1 << 9); /* Quad Enable */
#define SPI_IDX 1
#define OTH_IDX 0
#ifndef BOOTLOADER_BUILD
#define ATTR IRAM_ATTR
#else
#define ATTR
#endif // BOOTLOADER_BUILD
extern SpiFlashChip SPI_flashchip_data;
static void IRAM_ATTR Wait_SPI_Idle(void)
static void ATTR Wait_SPI_Idle(void)
{
/* Wait for SPI state machine to be idle */
while((REG_READ(SPI_EXT2_REG(SPI_IDX)) & SPI_ST)) {
@ -42,7 +48,7 @@ static void IRAM_ATTR Wait_SPI_Idle(void)
about interrupts, CPU coordination, flash mapping. However some of
the functions in esp_spi_flash.c call it.
*/
SpiFlashOpResult IRAM_ATTR SPIUnlock(void)
SpiFlashOpResult ATTR SPIUnlock(void)
{
uint32_t status;

View file

@ -1,5 +1,5 @@
#
# Component Makefile
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
include $(IDF_PATH)/make/component_common.mk

View file

@ -1 +1,5 @@
include $(IDF_PATH)/make/component_common.mk
#
# Component Makefile
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -151,6 +151,10 @@ int esp_vfs_open(struct _reent *r, const char * path, int flags, int mode)
const char* path_within_vfs = translate_path(vfs, path);
int ret;
CHECK_AND_CALL(ret, r, vfs, open, path_within_vfs, flags, mode);
if (ret < 0) {
return ret;
}
assert(ret >= vfs->vfs.fd_offset);
return ret - vfs->vfs.fd_offset + (vfs->offset << VFS_INDEX_S);
}

View file

@ -2,5 +2,3 @@ COMPONENT_ADD_INCLUDEDIRS := include port/include
COMPONENT_SRCDIRS := src/crypto
CFLAGS += -DEMBEDDED_SUPP -D__ets__ -Wno-strict-aliasing
include $(IDF_PATH)/make/component_common.mk

View file

@ -668,6 +668,7 @@ mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y)
} else {
#endif
#ifdef BN_S_MP_EXPTMOD_C
(void) dr;
/* otherwise use the generic Barrett reduction technique */
return s_mp_exptmod (G, X, P, Y, 0);
#else

View file

@ -1,5 +1,4 @@
#
# Component Makefile
#
include $(IDF_PATH)/make/component_common.mk
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -1,6 +1,6 @@
PROJECT_NAME = "ESP32 Programming Guide"
INPUT = ../components/esp32/include/esp_wifi.h ../components/driver/include/driver ../components/esp32/include/rom/gpio.h ../components/bt/include ../components/nvs_flash/include ../components/log/include ../components/vfs/include
INPUT = ../components/esp32/include/esp_wifi.h ../components/driver/include/driver ../components/bt/include ../components/nvs_flash/include ../components/log/include ../components/vfs/include
WARN_NO_PARAMDOC = YES

View file

@ -1,20 +1,27 @@
Bluetooth API
=============
Bluetooth
=========
Overview
--------
`Instructions <http://esp-idf.readthedocs.io/en/latest/api/template.html>`_
`Instructions`_
Application Example
-------------------
`Instructions <http://esp-idf.readthedocs.io/en/latest/api/template.html>`_
`Instructions`_
Reference
---------
API Reference
-------------
`Instructions <http://esp-idf.readthedocs.io/en/latest/api/template.html>`_
`Instructions`_
.. _Instructions: template.html
Header Files
^^^^^^^^^^^^
* `bt/include/bt.h <https://github.com/espressif/esp-idf/blob/master/components/bt/include/bt.h>`_
Type Definitions
^^^^^^^^^^^^^^^^

View file

@ -1,20 +1,27 @@
Wi-Fi API
=========
Wi-Fi
=====
Overview
--------
`Instructions <http://esp-idf.readthedocs.io/en/latest/api/template.html>`_
`Instructions`_
Application Example
-------------------
`Instructions <http://esp-idf.readthedocs.io/en/latest/api/template.html>`_
`Instructions`_
Reference
---------
API Reference
-------------
`Instructions <http://esp-idf.readthedocs.io/en/latest/api/template.html>`_
`Instructions`_
.. _Instructions: template.html
Header Files
^^^^^^^^^^^^
* `esp32/include/esp_wifi.h <https://github.com/espressif/esp-idf/blob/master/components/esp32/include/esp_wifi.h>`_
Macros
------
@ -22,14 +29,12 @@ Macros
.. doxygendefine:: WIFI_INIT_CONFIG_DEFAULT
Typedefs
--------
Type Definitions
----------------
.. doxygentypedef:: wifi_promiscuous_cb_t
.. doxygentypedef:: wifi_rxcb_t
.. doxygentypedef:: esp_vendor_ie_cb_t
Functions
---------
@ -42,11 +47,12 @@ Functions
.. doxygenfunction:: esp_wifi_connect
.. doxygenfunction:: esp_wifi_disconnect
.. doxygenfunction:: esp_wifi_clear_fast_connect
.. doxygenfunction:: esp_wifi_kick_station
.. doxygenfunction:: esp_wifi_deauth_sta
.. doxygenfunction:: esp_wifi_scan_start
.. doxygenfunction:: esp_wifi_scan_stop
.. doxygenfunction:: esp_wifi_get_ap_num
.. doxygenfunction:: esp_wifi_get_ap_list
.. doxygenfunction:: esp_wifi_scan_get_ap_num
.. doxygenfunction:: esp_wifi_scan_get_ap_records
.. doxygenfunction:: esp_wifi_sta_get_ap_info
.. doxygenfunction:: esp_wifi_set_ps
.. doxygenfunction:: esp_wifi_get_ps
.. doxygenfunction:: esp_wifi_set_protocol
@ -64,11 +70,11 @@ Functions
.. doxygenfunction:: esp_wifi_get_promiscuous
.. doxygenfunction:: esp_wifi_set_config
.. doxygenfunction:: esp_wifi_get_config
.. doxygenfunction:: esp_wifi_get_station_list
.. doxygenfunction:: esp_wifi_free_station_list
.. doxygenfunction:: esp_wifi_ap_get_sta_list
.. doxygenfunction:: esp_wifi_set_storage
.. doxygenfunction:: esp_wifi_reg_rxcb
.. doxygenfunction:: esp_wifi_set_auto_connect
.. doxygenfunction:: esp_wifi_get_auto_connect
.. doxygenfunction:: esp_wifi_set_vendor_ie
.. doxygenfunction:: esp_wifi_set_vendor_ie_cb

View file

@ -1,26 +1,125 @@
GPIO API
========
GPIO
====
Overview
--------
`Instructions <http://esp-idf.readthedocs.io/en/latest/api/template.html>`_
`Instructions`_
Application Example
-------------------
`Instructions <http://esp-idf.readthedocs.io/en/latest/api/template.html>`_
`Instructions`_
Reference
---------
API Reference
-------------
`Instructions <http://esp-idf.readthedocs.io/en/latest/api/template.html>`_
`Instructions`_
.. _Instructions: template.html
Header Files
^^^^^^^^^^^^
* `driver/include/driver/driver/gpio.h <https://github.com/espressif/esp-idf/blob/master/components/driver/include/driver/gpio.h>`_
Macros
^^^^^^
.. doxygendefine:: GPIO_SEL_0
.. doxygendefine:: GPIO_SEL_1
.. doxygendefine:: GPIO_SEL_2
.. doxygendefine:: GPIO_SEL_3
.. doxygendefine:: GPIO_SEL_4
.. doxygendefine:: GPIO_SEL_5
.. doxygendefine:: GPIO_SEL_6
.. doxygendefine:: GPIO_SEL_7
.. doxygendefine:: GPIO_SEL_8
.. doxygendefine:: GPIO_SEL_9
.. doxygendefine:: GPIO_SEL_10
.. doxygendefine:: GPIO_SEL_11
.. doxygendefine:: GPIO_SEL_12
.. doxygendefine:: GPIO_SEL_13
.. doxygendefine:: GPIO_SEL_14
.. doxygendefine:: GPIO_SEL_15
.. doxygendefine:: GPIO_SEL_16
.. doxygendefine:: GPIO_SEL_17
.. doxygendefine:: GPIO_SEL_18
.. doxygendefine:: GPIO_SEL_19
.. doxygendefine:: GPIO_SEL_21
.. doxygendefine:: GPIO_SEL_22
.. doxygendefine:: GPIO_SEL_23
.. doxygendefine:: GPIO_SEL_25
.. doxygendefine:: GPIO_SEL_26
.. doxygendefine:: GPIO_SEL_27
.. doxygendefine:: GPIO_SEL_32
.. doxygendefine:: GPIO_SEL_33
.. doxygendefine:: GPIO_SEL_34
.. doxygendefine:: GPIO_SEL_35
.. doxygendefine:: GPIO_SEL_36
.. doxygendefine:: GPIO_SEL_37
.. doxygendefine:: GPIO_SEL_38
.. doxygendefine:: GPIO_SEL_39
.. doxygendefine:: GPIO_PIN_REG_0
.. doxygendefine:: GPIO_PIN_REG_1
.. doxygendefine:: GPIO_PIN_REG_2
.. doxygendefine:: GPIO_PIN_REG_3
.. doxygendefine:: GPIO_PIN_REG_4
.. doxygendefine:: GPIO_PIN_REG_5
.. doxygendefine:: GPIO_PIN_REG_6
.. doxygendefine:: GPIO_PIN_REG_7
.. doxygendefine:: GPIO_PIN_REG_8
.. doxygendefine:: GPIO_PIN_REG_9
.. doxygendefine:: GPIO_PIN_REG_10
.. doxygendefine:: GPIO_PIN_REG_11
.. doxygendefine:: GPIO_PIN_REG_12
.. doxygendefine:: GPIO_PIN_REG_13
.. doxygendefine:: GPIO_PIN_REG_14
.. doxygendefine:: GPIO_PIN_REG_15
.. doxygendefine:: GPIO_PIN_REG_16
.. doxygendefine:: GPIO_PIN_REG_17
.. doxygendefine:: GPIO_PIN_REG_18
.. doxygendefine:: GPIO_PIN_REG_19
.. doxygendefine:: GPIO_PIN_REG_20
.. doxygendefine:: GPIO_PIN_REG_21
.. doxygendefine:: GPIO_PIN_REG_22
.. doxygendefine:: GPIO_PIN_REG_23
.. doxygendefine:: GPIO_PIN_REG_25
.. doxygendefine:: GPIO_PIN_REG_26
.. doxygendefine:: GPIO_PIN_REG_27
.. doxygendefine:: GPIO_PIN_REG_32
.. doxygendefine:: GPIO_PIN_REG_33
.. doxygendefine:: GPIO_PIN_REG_34
.. doxygendefine:: GPIO_PIN_REG_35
.. doxygendefine:: GPIO_PIN_REG_36
.. doxygendefine:: GPIO_PIN_REG_37
.. doxygendefine:: GPIO_PIN_REG_38
.. doxygendefine:: GPIO_PIN_REG_39
.. doxygendefine:: GPIO_APP_CPU_INTR_ENA
.. doxygendefine:: GPIO_APP_CPU_NMI_INTR_ENA
.. doxygendefine:: GPIO_PRO_CPU_INTR_ENA
.. doxygendefine:: GPIO_PRO_CPU_NMI_INTR_ENA
.. doxygendefine:: GPIO_SDIO_EXT_INTR_ENA
.. doxygendefine:: GPIO_MODE_DEF_INPUT
.. doxygendefine:: GPIO_MODE_DEF_OUTPUT
.. doxygendefine:: GPIO_MODE_DEF_OD
.. doxygendefine:: GPIO_PIN_COUNT
.. doxygendefine:: GPIO_IS_VALID_GPIO
.. doxygendefine:: GPIO_IS_VALID_OUTPUT_GPIO
Type Definitions
^^^^^^^^^^^^^^^^
.. doxygentypedef:: gpio_event_callback
Enumerations
^^^^^^^^^^^^
.. doxygenenum:: gpio_num_t
.. doxygenenum:: gpio_int_type_t
.. doxygenenum:: gpio_mode_t
.. doxygenenum:: gpio_pullup_t
.. doxygenenum:: gpio_pulldown_t
.. doxygenenum:: gpio_pull_mode_t
Functions
@ -37,54 +136,3 @@ Functions
.. doxygenfunction:: gpio_wakeup_enable
.. doxygenfunction:: gpio_wakeup_disable
.. doxygenfunction:: gpio_isr_register
*Example code:* Configuration of GPIO as an output
.. code-block:: c
gpio_config_t io_conf;
io_conf.intr_type = GPIO_INTR_DISABLE; //disable interrupt
io_conf.mode = GPIO_MODE_OUTPUT; //set as output mode
io_conf.pin_bit_mask = GPIO_SEL_18 | GPIO_SEL_19; //bit mask of the pins that you want to set,e.g.GPIO18/19
io_conf.pull_down_en = 0; //disable pull-down mode
io_conf.pull_up_en = 0; //disable pull-up mode
gpio_config(&io_conf); //configure GPIO with the given settings
*Example code:* Configuration of GPIO as an input
.. code-block:: c
gpio_config_t io_conf;
io_conf.intr_type = GPIO_INTR_POSEDGE; //set posedge interrupt
io_conf.mode = GPIO_MODE_INPUT; //set as input
io_conf.pin_bit_mask = GPIO_SEL_4 | GPIO_SEL_5; //bit mask of the pins that you want to set, e.g.,GPIO4/5
io_conf.pull_down_en = 0; //disable pull-down mode
io_conf.pull_up_en = 1; //enable pull-up mode
gpio_config(&io_conf); //configure GPIO with the given settings
ROM GPIO functions
^^^^^^^^^^^^^^^^^^
.. doxygenfunction:: gpio_init
.. doxygenfunction:: gpio_output_set
.. doxygenfunction:: gpio_output_set_high
.. doxygenfunction:: gpio_input_get
.. doxygenfunction:: gpio_input_get_high
.. doxygenfunction:: gpio_intr_handler_register
.. doxygenfunction:: gpio_intr_pending
.. doxygenfunction:: gpio_intr_pending_high
.. doxygenfunction:: gpio_intr_ack
.. doxygenfunction:: gpio_intr_ack_high
.. doxygenfunction:: gpio_pin_wakeup_enable
.. doxygenfunction:: gpio_pin_wakeup_disable
.. doxygenfunction:: gpio_matrix_in
.. doxygenfunction:: gpio_matrix_out
.. doxygenfunction:: gpio_pad_select_gpio
.. doxygenfunction:: gpio_pad_set_drv
.. doxygenfunction:: gpio_pad_pullup
.. doxygenfunction:: gpio_pad_pulldown
.. doxygenfunction:: gpio_pad_unhold
.. doxygenfunction:: gpio_pad_hold

66
docs/api/ledc.rst Normal file
View file

@ -0,0 +1,66 @@
LED Control
===========
Overview
--------
`Instructions`_
Application Example
-------------------
`Instructions`_
API Reference
-------------
`Instructions`_
.. _Instructions: template.html
Header Files
^^^^^^^^^^^^
* `driver/include/driver/ledc.h <https://github.com/espressif/esp-idf/blob/master/components/driver/include/driver/ledc.h>`_
Data Structures
^^^^^^^^^^^^^^^
.. doxygenstruct:: ledc_channel_config_t
.. doxygenstruct:: ledc_timer_config_t
Macros
^^^^^^
.. doxygendefine:: LEDC_APB_CLK_HZ
.. doxygendefine:: LEDC_REF_CLK_HZ
Enumerations
^^^^^^^^^^^^
.. doxygenenum:: ledc_mode_t
.. doxygenenum:: ledc_intr_type_t
.. doxygenenum:: ledc_duty_direction_t
.. doxygenenum:: ledc_clk_src_t
.. doxygenenum:: ledc_timer_t
.. doxygenenum:: ledc_channel_t
.. doxygenenum:: ledc_timer_bit_t
Functions
^^^^^^^^^
.. doxygenfunction:: ledc_channel_config
.. doxygenfunction:: ledc_timer_config
.. doxygenfunction:: ledc_update_duty
.. doxygenfunction:: ledc_stop
.. doxygenfunction:: ledc_set_freq
.. doxygenfunction:: ledc_get_freq
.. doxygenfunction:: ledc_set_duty
.. doxygenfunction:: ledc_get_duty
.. doxygenfunction:: ledc_set_fade
.. doxygenfunction:: ledc_isr_register
.. doxygenfunction:: ledc_timer_set
.. doxygenfunction:: ledc_timer_rst
.. doxygenfunction:: ledc_timer_pause
.. doxygenfunction:: ledc_timer_resume
.. doxygenfunction:: ledc_bind_channel_timer

View file

@ -1,8 +1,49 @@
.. include:: ../../components/log/README.rst
Application Example
-------------------
`Instructions`_
API Reference
-------------
`Instructions`_
.. _Instructions: template.html
Header Files
^^^^^^^^^^^^
* `log/include/esp_log.h <https://github.com/espressif/esp-idf/blob/master/components/log/include/esp_log.h>`_
Macros
^^^^^^
.. doxygendefine:: LOG_COLOR_E
.. doxygendefine:: LOG_COLOR_W
.. doxygendefine:: LOG_COLOR_I
.. doxygendefine:: LOG_COLOR_D
.. doxygendefine:: LOG_COLOR_V
.. doxygendefine:: LOG_RESET_COLOR
.. doxygendefine:: LOG_FORMAT
.. doxygendefine:: LOG_LOCAL_LEVEL
.. doxygendefine:: ESP_EARLY_LOGE
.. doxygendefine:: ESP_EARLY_LOGW
.. doxygendefine:: ESP_EARLY_LOGI
.. doxygendefine:: ESP_EARLY_LOGD
.. doxygendefine:: ESP_EARLY_LOGV
.. doxygendefine:: ESP_LOGE
.. doxygendefine:: ESP_LOGW
.. doxygendefine:: ESP_LOGI
.. doxygendefine:: ESP_LOGD
.. doxygendefine:: ESP_LOGV
Type Definitions
^^^^^^^^^^^^^^^^
.. doxygentypedef:: vprintf_like_t
Enumerations
^^^^^^^^^^^^
@ -17,3 +58,11 @@ Functions
.. doxygenfunction:: esp_log_write

View file

@ -1,54 +1,21 @@
.. include:: ../../components/nvs_flash/README.rst
Application Example
-------------------
`Instructions <http://esp-idf.readthedocs.io/en/latest/api/template.html>`_
API Reference
-------------
Enumerations
Header Files
^^^^^^^^^^^^
.. doxygenenum:: nvs_open_mode
* `nvs_flash/include/nvs_flash.h <https://github.com/espressif/esp-idf/blob/master/components/nvs_flash/include/nvs_flash.h>`_
* `nvs_flash/include/nvs.h <https://github.com/espressif/esp-idf/blob/master/components/nvs_flash/include/nvs.h>`_
Functions
^^^^^^^^^
.. doxygenfunction:: nvs_flash_init
.. doxygenfunction:: nvs_flash_init_custom
.. doxygenfunction:: nvs_open
*Note: the following nvs_set_X function are "the same" except the data type accepted*
.. doxygenfunction:: nvs_set_i8
.. doxygenfunction:: nvs_set_u8
.. doxygenfunction:: nvs_set_i16
.. doxygenfunction:: nvs_set_u16
.. doxygenfunction:: nvs_set_i32
.. doxygenfunction:: nvs_set_u32
.. doxygenfunction:: nvs_set_i64
.. doxygenfunction:: nvs_set_u64
.. doxygenfunction:: nvs_set_str
.. doxygenfunction:: nvs_set_blob
*Note: the following nvs_get_X functions are "the same" except the data type returned*
.. doxygenfunction:: nvs_get_i8
.. doxygenfunction:: nvs_get_u8
.. doxygenfunction:: nvs_get_i16
.. doxygenfunction:: nvs_get_u16
.. doxygenfunction:: nvs_get_i32
.. doxygenfunction:: nvs_get_u32
.. doxygenfunction:: nvs_get_i64
.. doxygenfunction:: nvs_get_u64
.. doxygenfunction:: nvs_get_str
.. doxygenfunction:: nvs_get_blob
.. doxygenfunction:: nvs_erase_key
.. doxygenfunction:: nvs_erase_all
.. doxygenfunction:: nvs_commit
.. doxygenfunction:: nvs_close
Error codes
^^^^^^^^^^^
Macros
^^^^^^
.. doxygendefine:: ESP_ERR_NVS_BASE
.. doxygendefine:: ESP_ERR_NVS_NOT_INITIALIZED
@ -64,5 +31,43 @@ Error codes
.. doxygendefine:: ESP_ERR_NVS_INVALID_STATE
.. doxygendefine:: ESP_ERR_NVS_INVALID_LENGTH
Type Definitions
^^^^^^^^^^^^^^^^
.. doxygentypedef:: nvs_handle
Enumerations
^^^^^^^^^^^^
.. doxygenenum:: nvs_open_mode
Functions
^^^^^^^^^
.. doxygenfunction:: nvs_open
.. doxygenfunction:: nvs_set_i8
.. doxygenfunction:: nvs_set_u8
.. doxygenfunction:: nvs_set_i16
.. doxygenfunction:: nvs_set_u16
.. doxygenfunction:: nvs_set_i32
.. doxygenfunction:: nvs_set_u32
.. doxygenfunction:: nvs_set_i64
.. doxygenfunction:: nvs_set_u64
.. doxygenfunction:: nvs_set_str
.. doxygenfunction:: nvs_set_blob
.. doxygenfunction:: nvs_get_i8
.. doxygenfunction:: nvs_get_u8
.. doxygenfunction:: nvs_get_i16
.. doxygenfunction:: nvs_get_u16
.. doxygenfunction:: nvs_get_i32
.. doxygenfunction:: nvs_get_u32
.. doxygenfunction:: nvs_get_i64
.. doxygenfunction:: nvs_get_u64
.. doxygenfunction:: nvs_get_str
.. doxygenfunction:: nvs_get_blob
.. doxygenfunction:: nvs_erase_key
.. doxygenfunction:: nvs_erase_all
.. doxygenfunction:: nvs_commit
.. doxygenfunction:: nvs_close
.. doxygenfunction:: nvs_flash_init
.. doxygenfunction:: nvs_flash_init_custom

View file

@ -1,70 +1,110 @@
Template API
=============
Template
========
.. note::
*INSTRUCTIONS*
1. Use this file as a template to document API.
2. Change the file name to the name of the header file that represents documented API.
3. Include respective files with descriptions from the API folder using ``..include::``
* README.rst
* example.rst
4. Optionally provide description right in this file.
5. Once done, remove all instructions like this one and any superfluous headers.
Overview
--------
INSTRUCTIONS: Provide overview where and how this API may be used. For large number of functions, break down description into groups.
.. note::
Use the folowing heading levels:
*INSTRUCTIONS*
* # with overline, for parts
* \* with overline, for chapters
* =, for sections
* -, for subsections
* ^, for subsubsections
* ", for paragraphs
1. Provide overview where and how this API may be used.
2. Where applicable include code snippets to illustrate functionality of particular functions.
3. To distinguish between sections, use the following `heading levels <http://www.sphinx-doc.org/en/stable/rest.html#sections>`_:
* ``#`` with overline, for parts
* ``*`` with overline, for chapters
* ``=``, for sections
* ``-``, for subsections
* ``^``, for subsubsections
* ``"``, for paragraphs
Application Example
-------------------
INSTRUCTIONS: Provide one or more pratical examples to demonstrate functionality of this API.
.. note::
*INSTRUCTIONS*
Reference
---------
1. Provide one or more practical examples to demonstrate functionality of this API.
2. Break down the code into parts and describe functionality of each part.
3. Provide screenshots if applicable.
INSTRUCTIONS: Provide list of API memebers divided into sections. Use coresponding **.. doxygen** directices, so member documentation is auto updated.
API Reference
-------------
* Data Structures **.. doxygenstruct**
* Macros **.. doxygendefine**
* Type Definitions **.. doxygentypedef**
* Enumerations **.. doxygenenum**
* Functions **.. doxygenfunction**
* Variables **.. doxygenvariable**
.. note::
Include code snippotes to ilustrate functionality of particular functions where applicable. Skip section hearder if empty.
*INSTRUCTIONS*
1. Specify the names of header files used to generate this reference. Each name should be linked to the source on `espressif/esp-idf <https://github.com/espressif/esp-idf>`_ repository.
2. Provide list of API members divided into sections.
3. Use corresponding ``.. doxygen..`` directives, so member documentation is auto updated.
* Data Structures -``.. doxygenstruct::`` together with ``:members:``
* Macros - ``.. doxygendefine::``
* Type Definitions - ``.. doxygentypedef::``
* Enumerations - ``.. doxygenenum::``
* Functions - ``.. doxygenfunction::``
See `Breathe documentation <https://breathe.readthedocs.io/en/latest/directives.html>`_ for additional information.
4. Once done remove superfluous headers.
5. When changes are committed and documentation is build, check how this section rendered. :doc:`Correct annotations <../documenting-code>` in respective header files, if required.
Header Files
^^^^^^^^^^^^
* `path/header-file.h`
Data Structures
^^^^^^^^^^^^^^^
.. Data Structures .. doxygenstruct
::
.. doxygenstruct:: name_of_structure
:members:
Macros
^^^^^^
.. Macros .. doxygendefine
::
.. doxygendefine:: name_of_macro
Type Definitions
^^^^^^^^^^^^^^^^
.. Type Definitions .. doxygentypedef
::
.. doxygentypedef:: name_of_type
Enumerations
^^^^^^^^^^^^
.. Enumerations .. doxygenenum
::
.. doxygenenum:: name_of_enumeration
Functions
^^^^^^^^^
.. Functions .. doxygenfunction
::
Variables
^^^^^^^^^
.. Variables .. doxygenvariable
.. doxygenfunction:: name_of_function

98
docs/api/uart.rst Normal file
View file

@ -0,0 +1,98 @@
UART
====
Overview
--------
`Instructions`_
Application Example
-------------------
`Instructions`_
API Reference
-------------
`Instructions`_
.. _Instructions: template.html
Header Files
^^^^^^^^^^^^
* `driver/include/driver/uart.h <https://github.com/espressif/esp-idf/blob/master/components/driver/include/driver/uart.h>`_
Data Structures
^^^^^^^^^^^^^^^
.. doxygenstruct:: uart_config_t
:members:
.. doxygenstruct:: uart_intr_config_t
:members:
.. doxygenstruct:: uart_event_t
:members:
Macros
^^^^^^
.. doxygendefine:: UART_FIFO_LEN
.. doxygendefine:: UART_INTR_MASK
.. doxygendefine:: UART_LINE_INV_MASK
.. doxygendefine:: UART_BITRATE_MAX
.. doxygendefine:: UART_PIN_NO_CHANGE
.. doxygendefine:: UART_INVERSE_DISABLE
.. doxygendefine:: UART_INVERSE_RXD
.. doxygendefine:: UART_INVERSE_CTS
.. doxygendefine:: UART_INVERSE_TXD
.. doxygendefine:: UART_INVERSE_RTS
Enumerations
^^^^^^^^^^^^
.. doxygenenum:: uart_word_length_t
.. doxygenenum:: uart_stop_bits_t
.. doxygenenum:: uart_port_t
.. doxygenenum:: uart_parity_t
.. doxygenenum:: uart_hw_flowcontrol_t
.. doxygenenum:: uart_event_type_t
Functions
^^^^^^^^^
.. doxygenfunction:: uart_set_word_length
.. doxygenfunction:: uart_get_word_length
.. doxygenfunction:: uart_set_stop_bits
.. doxygenfunction:: uart_get_stop_bits
.. doxygenfunction:: uart_set_parity
.. doxygenfunction:: uart_get_parity
.. doxygenfunction:: uart_set_baudrate
.. doxygenfunction:: uart_get_baudrate
.. doxygenfunction:: uart_set_line_inverse
.. doxygenfunction:: uart_set_hw_flow_ctrl
.. doxygenfunction:: uart_get_hw_flow_ctrl
.. doxygenfunction:: uart_clear_intr_status
.. doxygenfunction:: uart_enable_intr_mask
.. doxygenfunction:: uart_disable_intr_mask
.. doxygenfunction:: uart_enable_rx_intr
.. doxygenfunction:: uart_disable_rx_intr
.. doxygenfunction:: uart_disable_tx_intr
.. doxygenfunction:: uart_enable_tx_intr
.. doxygenfunction:: uart_isr_register
.. doxygenfunction:: uart_set_pin
.. doxygenfunction:: uart_set_rts
.. doxygenfunction:: uart_set_dtr
.. doxygenfunction:: uart_param_config
.. doxygenfunction:: uart_intr_config
.. doxygenfunction:: uart_driver_install
.. doxygenfunction:: uart_driver_delete
.. doxygenfunction:: uart_wait_tx_done
.. doxygenfunction:: uart_tx_chars
.. doxygenfunction:: uart_write_bytes
.. doxygenfunction:: uart_write_bytes_with_break
.. doxygenfunction:: uart_read_bytes
.. doxygenfunction:: uart_flush

View file

@ -1,15 +1,25 @@
.. include:: ../../components/vfs/README.rst
Application Example
-------------------
`Instructions <http://esp-idf.readthedocs.io/en/latest/api/template.html>`_
API Reference
-------------
Defines
^^^^^^^
Header Files
^^^^^^^^^^^^
.. doxygendefine:: ESP_VFS_PATH_MAX
* `vfs/include/esp_vfs.h <https://github.com/espressif/esp-idf/blob/master/components/vfs/include/esp_vfs.h>`_
* `vfs/include/esp_vfs_dev.h <https://github.com/espressif/esp-idf/blob/master/components/vfs/include/esp_vfs_dev.h>`_
Macros
^^^^^^
.. doxygendefine:: ESP_VFS_PATH_MAX
.. doxygendefine:: ESP_VFS_FLAG_DEFAULT
.. doxygendefine:: ESP_VFS_FLAG_CONTEXT_PTR
.. doxygendefine:: ESP_VFS_FLAG_CONTEXT_PTR
Structures
^^^^^^^^^^
@ -19,9 +29,15 @@ Structures
Functions
^^^^^^^^^
.. doxygenfunction:: esp_vfs_dev_uart_register
.. doxygenfunction:: esp_vfs_register
.. doxygenfunction:: esp_vfs_write
.. doxygenfunction:: esp_vfs_lseek
.. doxygenfunction:: esp_vfs_read
.. doxygenfunction:: esp_vfs_open
.. doxygenfunction:: esp_vfs_close
.. doxygenfunction:: esp_vfs_fstat
.. doxygenfunction:: esp_vfs_stat
.. doxygenfunction:: esp_vfs_link
.. doxygenfunction:: esp_vfs_unlink
.. doxygenfunction:: esp_vfs_rename
.. doxygenfunction:: esp_vfs_dev_uart_register

View file

@ -8,262 +8,380 @@ Read this document if you want to know how to organise a new ESP-IDF project.
We recommend using the esp-idf-template_ project as a starting point for your project.
Using the Build System
======================
The esp-idf README file contains a description of how to use the build system to build your project.
Overview
========
An ESP-IDF project can be seen as an almagation of a number of components.
For example, for a webserver that shows the current humidity, we would
have:
An ESP-IDF project can be seen as an amalgamation of a number of components.
For example, for a webserver that shows the current humidity, there could be:
- The ESP32 base libraries (libc, rom bindings etc)
- The WiFi drivers
- A TCP/IP stack
- The FreeRTOS operating system
- A webserver
- A driver for an humidity sensor
- A driver for the humidity sensor
- Main code tying it all together
ESP-IDF makes these components explicit and configurable. To do that, when a project
is compiled, the build environment will look up all the components in the
ESP-IDF directories, the project directories and optionally custom other component
directories. It then allows the user to configure compile-time options using
a friendly text-based menu system to customize the ESP-IDF as well as other components
to the requirements of the project. After the components are customized, the
build process will compile everything into an output file, which can then be uploaded
into a board in a way that can also be defined by components.
ESP-IDF makes these components explicit and configurable. To do that,
when a project is compiled, the build environment will look up all the
components in the ESP-IDF directories, the project directories and
(optionally) in additional custom component directories. It then
allows the user to configure the ESP-IDF project using a a text-based
menu system to customize each component. After the components in the
project are configured, the build process will compile the project.
A project in this sense is defined as a directory under which all the files required
to build it live, excluding the ESP-IDF files and the toolchain. A simple project
tree might look like this::
Concepts
--------
- myProject/ - build/
- A "project" is a directory that contains all the files and configuration to build a single "app" (executable), as well as additional supporting output such as a partition table, data/filesystem partitions, and a bootloader.
- "Project configuration" is held in a single file called sdkconfig in the root directory of the project. This configuration file is modified via ``make menuconfig`` to customise the configuration of the project. A single project contains exactly one project configuration.
- An "app" is an executable which is built by esp-idf. A single project will usually build two apps - a "project app" (the main executable, ie your custom firmware) and a "bootloader app" (the initial bootloader program which launches the project app).
- "components" are modular pieces of standalone code which are compiled into static libraries (.a files) and linked into an app. Some are provided by esp-idf itself, others may be sourced from other places.
Some things are not part of the project:
- "ESP-IDF" is not part of the project. Instead it is standalone, and linked to the project via the ``IDF_PATH`` environment variable which holds the path of the ``esp-idf`` directory. This allows the IDF framework to be decoupled from your project.
- The toolchain for compilation is not part of the project. The toolchain should be installed in the system command line PATH, or the path to the toolchain can be set as part of the compiler prefix in the project configuration.
Example Project
---------------
An example project directory tree might look like this::
- myProject/
- Makefile
- sdkconfig
- components/ - component1/ - component.mk
- Kconfig
- src1.c
- component2/ - component.mk
- Kconfig
- src1.c
- include/
- component2.h
- main/ - src1.c
- src2.c
- Makefile
- component.mk
- build/
As we can see, a project consists of a components/ subdirectory containing its
components as well as one or more directories containing the project-specific
sources; by default a single directory called 'main' is assumed. The project
directory will also have a Makefile where the projects name as well as optionally
other options are defined. After compilation, the project directory will contain
a 'build'-directory containing all of the objects, libraries and other generated
files as well as the final binary.
This example "myProject" contains the following elements:
Components also have a custom makefile - ``component.mk``. This contains various definititions
influencing the build process of the component as well as the project it's used
in. Components may also include a Kconfig file defining the compile-time options that are
settable by means of the menu system.
- A top-level project Makefile. This Makefile set the ``PROJECT_NAME`` variable and (optionally) defines
other project-wide make variables. It includes the core ``$(IDF_PATH)/make/project.mk`` makefile which
implements the rest of the ESP-IDF build system.
Project Makefile variables that can be set by the programmer::
- "sdkconfig" project configuration file. This file is created/updated when "make menuconfig" runs, and holds configuration for all of the components in the project (including esp-idf itself). The "sdkconfig" file may or may not be added to the source control system of the project.
PROJECT_NAME: Mandatory. Name for the project
BUILD_DIR_BASE: Set the directory where all objects/libraries/binaries end up in.
Defaults to $(PROJECT_PATH)/build
COMPONENT_DIRS: Search path for components. Defaults to the component/ directories
in the ESP-IDF path and the project path.
COMPONENTS: A list of component names. Defaults to all the component found in the
COMPONENT_DIRS directory
EXTRA_COMPONENT_DIRS: Defaults to unset. Use this to add directories to the default
COMPONENT_DIRS.
SRCDIRS: Directories under the project dir containing project-specific sources.
Defaults to 'main'. These are treated as 'lite' components: they do not have
include directories that are passed to the compilation pass of all components and
they do not have a Kconfig option.
- Optional "components" directory contains components that are part of the project. A project does not have to contain custom components of this kind, but it can be useful for structuring reusable code or including third party components that aren't part of ESP-IDF.
Component-specific component.mk variables that can be set by the programmer::
- "main" directory is a special "pseudo-component" that contains source code for the project itself. "main" is a default name, the Makefile variable ``SRCDIRS`` defaults to this but can be set to look for pseudo-components in other directories.
COMPONENT_ADD_INCLUDEDIRS: Relative path to include directories to be added to
the entire project. If an include directory is only needed to compile this
specific component, don't add it here.
COMPONENT_PRIV_INCLUDEDIRS: Relative path to include directories that are only used
when compiling this specific component.
COMPONENT_DEPENDS: Names of any components that need to be compiled before this component.
COMPONENT_ADD_LDFLAGS: LD flags to add for the entire project. Defaults to -l$(COMPONENT_NAME).
Add libraries etc in the current directory as $(abspath libwhatever.a)
COMPONENT_EXTRA_INCLUDES: Any extra include paths used when compiling the component's
source files. These will be prefixed with '-I' and passed to the compiler.
Similar to COMPONENT_PRIV_INCLUDEDIRS, but these paths are passed as-is instead of
expanded relative to the component directory.
COMPONENT_SRCDIRS: Relative directories to look in for sources. Defaults to '.', the current
directory (the root of the component) only. Use this to specify any subdirectories. Note
that specifying this overwrites the default action of compiling everything in the
components root dir; to keep this behaviour please also add '.' as a directory in this
list.
COMPONENT_OBJS: Object files to compile. Defaults to the .o variants of all .c and .S files
that are found in COMPONENT_SRCDIRS.
COMPONENT_EXTRA_CLEAN: Files that are generated using rules in the components Makefile
that also need to be cleaned
COMPONENT_BUILDRECIPE: Recipe to build the component. Optional. Defaults to building all
COMPONENT_OBJS and linking them into lib(componentname).a
COMPONENT_CLEANRECIPE: Recipe to clean the component. Optional. Defaults to removing
all built objects and libraries.
COMPONENT_BUILD_DIR: Equals the cwd of the component build, which is the build dir
of the component (where all the .o etc files should be created).
- "build" directory is where build output is created. After the make process is run, this directory will contain interim object files and libraries as well as final binary output files. This directory is usually not added to source control or distributed with the project source code.
These variables are already set early on in the Makefile and the values in it will
be usable in component or project Makefiles::
Component directories contain a component makefile - ``component.mk``. This may contain variable definitions
to control the build process of the component, and its integration into the overall project. See `Component Makefiles` for more details.
CC, LD, AR, OBJCOPY: Xtensa gcc tools
HOSTCC, HOSTLD etc: Host gcc tools
LDFLAGS, CFLAGS: Set to usable values as defined in ESP-IDF Makefile
PROJECT_NAME: Name of the project, as set in project makefile
PROJECT_PATH: Path to the root of the project folder
COMPONENTS: Name of the components to be included
CONFIG_*: All values set by 'make menuconfig' have corresponding Makefile variables.
Each component may also include a ``Kconfig`` file defining the `component configuration` options that can be set via the project configuration. Some components may also include ``Kconfig.projbuild`` and ``Makefile.projbuild`` files, which are special files for `overriding parts of the project`.
Inside your component's component.mk makefile, you can override or add to these variables
as necessary. The changes are isolated from other components (see Makefile.projbuild below
if you want to share these changes with all other components.)
Project Makefiles
-----------------
For components, there also are these defines::
Each project has a single Makefile that contains build settings for the entire project. By default, the project Makefile can be quite minimal.
COMPONENT_PATH: Absolute path to the root of the source tree of the component we're
compiling
COMPONENT_LIBRARY: The full path to the static library the components compilation pass
is supposed to generate
Minimal Example Makefile
^^^^^^^^^^^^^^^^^^^^^^^^
Make Process
------------
::
PROJECT_NAME := myProject
include $(IDF_PATH)/make/project.mk
The Make process is always invoked from the project directory by the
user; invoking it anywhere else gives an error. This is what happens if
we build a binary:
The Makefile first determines how it was included. It figures out
various paths as well as the components available to it. It will also
collect the ldflags and includes that the components specify they need.
It does this by running a dummy make on the components with a "get_variable"
target that will output these values.
Mandatory Project Variables
^^^^^^^^^^^^^^^^^^^^^^^^^^^
The Makefile will then create targets to build the lib*.a libraries of
all components and make the elf target depend on this. The main Makefile
invokes Make on the componen.mk of each components inside a sub-mke: this way
the components have full freedom to do whatever is necessary to build
the library without influencing other components. By default, the
component.mk includes the utility makefile $(IDF_PATH)/make/component_common.mk.
This provides default targets and configurations that will work
out-of-the-box for most projects.
- ``PROJECT_NAME``: Name of the project. Binary output files will use this name - ie myProject.bin, myProject.elf.
KConfig
-------
Optional Project Variables
^^^^^^^^^^^^^^^^^^^^^^^^^^
Each component can also have a Kconfig file, alongside the component.mk, that contains
details to add to "menuconfig" for this component.
These variables all have default values that can be overridden for custom behaviour. Look in ``make/project.mk`` for all of the implementation details.
- ``PROJECT_PATH``: Top-level project directory. Defaults to the directory containing the Makefile. Many other project variables are based on this variable. The project path cannot contain spaces.
- ``BUILD_DIR_BASE``: The build directory for all objects/libraries/binaries. Defaults to ``$(PROJECT_PATH)/build``.
- ``COMPONENT_DIRS``: Directories to search for components. Defaults to `$(IDF_PATH)/components`, `$(PROJECT_PATH)/components` and ``EXTRA_COMPONENT_DIRS``. Override this variable if you don't want to search for components in the esp-idf & project ``components`` directories.
- ``EXTRA_COMPONENT_DIRS``: Optional list of additional directories to search for components. Components themselves are in sub-directories of these directories, this is a top-level directory containing the component directories.
- ``COMPONENTS``: A list of component names to build into the project. Defaults to all components found in the COMPONENT_DIRS directories.
- ``SRCDIRS``: Directories under the main project directory which contain project-specific "pseudo-components". Defaults to 'main'. The difference between specifying a directory here and specifying it under ``EXTRA_COMPONENT_DIRS`` is that a directory in ``SRCDIRS`` is a component itself (contains a file "component.mk"), whereas a directory in ``EXTRA_COMPONENT_DIRS`` contains component directories which contain a file "component.mk". See the `Example Project` for a concrete case of this.
Component Makefiles
-------------------
Each project contains one or more components, which can either be part of esp-idf or added from other component directories.
A component is any sub-directory that contains a `component.mk` file.[#f1]_.
Minimal Component Makefile
^^^^^^^^^^^^^^^^^^^^^^^^^^
The minimal ``component.mk`` file is an empty file(!). If the file is empty, the default component behaviour is set:
- All source files in the same directory as the makefile (*.c, *.cpp, *.S) will be compiled into the component library
- A sub-directory "include" will be added to the global include search path for all other components.
- The component library will be linked into the project app.
See `example component makefiles` for more complete component makefile examples.
Note that there is a different between an empty ``component.mk`` file (which invokes default component build behaviour) and no ``component.mk`` file (which means no default component build behaviour will occur.) It is possible for a component to have no `component.mk` file, if it only contains other files which influence the project configuration or build process.
.. component variables:
Preset Component Variables
^^^^^^^^^^^^^^^^^^^^^^^^^^
The following component-specific variables are available for use inside ``component.mk``, but should not be modified:
- ``COMPONENT_PATH``: The component directory. Evaluates to the absolute path of the directory containing ``component.mk``. The component path cannot contain spaces.
- ``COMPONENT_NAME``: Name of the component. Defaults to the name of the component directory.
- ``COMPONENT_BUILD_DIR``: The component build directory. Evaluates to the absolute path of a directory inside `$(BUILD_DIR_BASE)` where this component's source files are to be built. This is also the Current Working Directory any time the component is being built, so relative paths in make targets, etc. will be relative to this directory.
- ``COMPONENT_LIBRARY``: Name of the static library file (relative to the component build directory) that will be built for this component. Defaults to ``$(COMPONENT_NAME).a``.
The following variables are set at the project level, but exported for use in the component build:
- ``PROJECT_NAME``: Name of the project, as set in project Makefile
- ``PROJECT_PATH``: Absolute path of the project directory containing the project Makefile.
- ``COMPONENTS``: Name of all components that are included in this build.
- ``CONFIG_*``: Each value in the project configuration has a corresponding variable available in make. All names begin with ``CONFIG_``.
- ``CC``, ``LD``, ``AR``, ``OBJCOPY``: Full paths to each tool from the gcc xtensa cross-toolchain.
- ``HOSTCC``, ``HOSTLD``, ``HOSTAR``: Full names of each tool from the host native toolchain.
If you modify any of these variables inside ``component.mk`` then this will not prevent other components from building but it may make your component hard to build and/or debug.
Optional Project-Wide Component Variables
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The following variables can be set inside ``component.mk`` to control build settings across the entire project:
- ``COMPONENT_ADD_INCLUDEDIRS``: Paths, relative to the component
directory, which will be added to the include search path for
all components in the project. Defaults to ``include`` if not overridden. If an include directory is only needed to compile
this specific component, add it to ``COMPONENT_PRIV_INCLUDEDIRS`` instead.
- ``COMPONENT_ADD_LDFLAGS``: Add linker arguments to the LDFLAGS for
the app executable. Defaults to ``-l$(COMPONENT_NAME)``. If
adding pre-compiled libraries to this directory, add them as
absolute paths - ie $(COMPONENT_PATH)/libwhatever.a
- ``COMPONENT_DEPENDS``: Optional list of component names that should
be compiled before this component. This is not necessary for
link-time dependencies, because all component include directories
are available at all times. It is necessary if one component
generates an include file which you then want to include in another
component. Most components do not need to set this variable.
Optional Component-Specific Variables
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The following variables can be set inside ``component.mk`` to control the build of that component:
- ``COMPONENT_PRIV_INCLUDEDIRS``: Directory paths, must be relative to
the component directory, which will be added to the include search
path for this component's source files only.
- ``COMPONENT_EXTRA_INCLUDES``: Any extra include paths used when
compiling the component's source files. These will be prefixed with
'-I' and passed as-is to the compiler. Similar to the
``COMPONENT_PRIV_INCLUDEDIRS`` variable, except these paths are not
expanded relative to the component directory.
- ``COMPONENT_SRCDIRS``: Directory paths, must be relative to the
component directory, which will be searched for source files (*.cpp,
*.c, *.S). Defaults to '.', ie the component directory
itself. Override this to specify a different list of directories
which contain source files.
- ``COMPONENT_OBJS``: Object files to compile. Default value is a .o
file for each source file that is found in ``COMPONENT_SRCDIRS``.
Overriding this list allows you to exclude source files in
``COMPONENT_SRCDIRS`` that would otherwise be compiled. See
`Specifying source files`
- ``COMPONENT_EXTRA_CLEAN``: Paths, relative to the component build
directory, of any files that are generated using custom make rules
in the component.mk file and which need to be removed as part of
``make clean``. See `Source Code Generation` for an example.
- ``COMPONENT_OWNBUILDTARGET`` & `COMPONENT_OWNCLEANTARGET`: These
targets allow you to fully override the default build behaviour for
the component. See `Fully Overriding The Component Makefile` for
more details.
- ``CFLAGS``: Flags passed to the C compiler. A default set of
``CFLAGS`` is defined based on project settings. Component-specific
additions can be made via ``CFLAGS +=``. It is also possible
(although not recommended) to override this variable completely for
a component.
- ``CPPFLAGS``: Flags passed to the C preprocessor (used for .c, .cpp
and .S files). A default set of ``CPPFLAGS`` is defined based on
project settings. Component-specific additions can be made via
``CPPFLAGS +=``. It is also possible (although not recommended) to
override this variable completely for a component.
- ``CXXFLAGS``: Flags passed to the C++ compiler. A default set of
``CXXFLAGS`` is defined based on project
settings. Component-specific additions can be made via ``CXXFLAGS
+=``. It is also possible (although not recommended) to override
this variable completely for a component.
Component Configuration
-----------------------
Each component can also have a Kconfig file, alongside ``component.mk``. This contains contains
configuration settings to add to the "make menuconfig" for this component.
These settings are found under the "Component Settings" menu when menuconfig is run.
To create a component KConfig file, it is easiest to start with one of the KConfig files distributed with esp-idf.
For an example, see `Adding conditional configuration`.
Build Process Internals
-----------------------
Top Level: Project Makefile
^^^^^^^^^^^^^^^^^^^^^^^^^^^
- "make" is always run from the project directory and the project makefile, typically named Makefile.
- The project makefile sets ``PROJECT_NAME`` and optionally customises other `optional project variables`
- The project makefile includes ``$(IDF_PATH)/make/project.mk`` which contains the project-level Make logic.
- ``project.mk`` fills in default project-level make variables and includes make variables from the project configuration. If the generated makefile containing project configuration is out of date, then it is regenerated (via targets in ``project_config.mk``) and then the make process restarts from the top.
- ``project.mk`` builds a list of components to build, based on the default component directories or a custom list of components set in `optional project variables`.
- Each component can set some `optional project-wide component variables`. These are included via generated makefiles named ``component_project_vars.mk`` - there is one per component. These generated makefiles are included into ``project.mk``. If any are missing or out of date, they are regenerated (via a recursive make call to the component makefile) and then the make process restarts from the top.
- `Makefile.projbuild` files from components are included into the make process, to add extra targets or configuration.
- By default, the project makefile also generates top-level build & clean targets for each component and sets up `app` and `clean` targets to invoke all of these sub-targets.
- In order to compile each component, a recursive make is performed for the component makefile.
To better understand the project make process, have a read through the ``project.mk`` file itself.
Second Level: Component Makefiles
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Each call to a component makefile goes via the ``$(IDF_PATH)/make/component_wrapper.mk`` wrapper makefile.
- The ``component_wrapper.mk`` is called with the current directory set to the component build directory, and the ``COMPONENT_MAKEFILE`` variable is set to the absolute path to ``component.mk``.
- ``component_wrapper.mk`` sets default values for all `component variables`, then includes the `component.mk` file which can override or modify these.
- If ``COMPONENT_OWNBUILDTARGET`` and ``COMPONENT_OWNCLEANTARGET`` are not defined, default build and clean targets are created for the component's source files and the prerequisite ``COMPONENT_LIBRARY`` static library file.
- The ``component_project_vars.mk`` file has its own target in ``component_wrapper.mk``, which is evaluated from ``project.mk`` if this file needs to be rebuilt due to changes in the component makefile or the project configuration.
To better understand the component make process, have a read through the ``component_wrapper.mk`` file and some of the ``component.mk`` files included with esp-idf.
Debugging The Make Process
--------------------------
Some tips for debugging the esp-idf build system:
- Appending ``V=1`` to the make arguments (or setting it as an environment variable) will cause make to echo all commands executed, and also each directory as it is entered for a sub-make.
- Running ``make -w`` will cause make to echo each directory as it is entered for a sub-make - same as ``V=1`` but without also echoing all commands.
- Running ``make --trace`` (possibly in addition to one of the above arguments) will print out every target as it is built, and the dependency which caused it to be built.
- Running ``make -p`` prints a (very verbose) summary of every generated target in each makefile.
For more debugging tips and general make information, see the `GNU Make Manual`.
Overriding Parts of the Project
-------------------------------
Makefile.projbuild
------------------
^^^^^^^^^^^^^^^^^^
For components that have parts that need to be evaluated in the top-level
project context, you can create a file called Makefile.projbuild in the
component root directory. These files is included into the project's
top-level Makefile.
For components that have build requirements that must be evaluated in the top-level
project make pass, you can create a file called ``Makefile.projbuild`` in the
component directory. This makefile is included when ``project.mk`` is evaluated.
For example, if your component needs to add to CFLAGS for the entire
project (not just for its own source files) then you can set
``CFLAGS +=`` in Makefile.projbuild. Note that this isn't necessary for
adding include directories to the project, you can set
``COMPONENT_ADD_INCLUDEDIRS`` (see above) in the component.mk.
``CFLAGS +=`` in Makefile.projbuild.
``Makefile.projbuild`` files are used heavily inside esp-idf, for defining project-wide build features such as ``esptool.py`` command line arguments and the ``bootloader`` "special app".
Note that ``Makefile.projbuild`` isn't necessary for the most common component uses - such as adding include directories to the project, or LDFLAGS to the final linking step. These values can be customised via the ``component.mk`` file itself. See `Optional Project-Wide Component Variables` for details.
Take care when setting variables or targets in this file. As the values are included into the top-level project makefile pass, they can influence or break functionality across all components!
KConfig.projbuild
-----------------
^^^^^^^^^^^^^^^^^
There's an equivalent to Makefile.projbuild for KConfig: if you want to include
options at the top-level, not inside the 'components' submenu then create a Kconfig.projbuild and
it will be included in the main menu of menuconfig.
This is an equivalent to `Makefile.projbuild` for `component configuration` KConfig files. If you want to include
configuration options at the top-level of menuconfig, rather than inside the "Component Configuration" sub-menu, then these can be defined in the KConfig.projbuild file alongside the ``component.mk`` file.
Take good care when (re)defining stuff here: because it's included with all the other
.projbuild files, it's possible to overwrite variables or re-declare targets defined in
the ESP-IDF makefile/Kconfig and other .projbuild files. It's generally better to just
create a KConfig file, if you can.
Take care when adding configuration values in this file, as they will be included across the entire project configuration. Where possible, it's generally better to create a KConfig file for `component configuration`.
Writing Component Makefiles
Example Component Makefiles
---------------------------
A component consists of a directory which doubles as the name for the
component: a component named 'httpd' lives in a directory called 'httpd'
Because components usually live under the project directory (although
they can also reside in an other folder), the path to this may be
something like /home/myuser/projects/myprojects/components/httpd .
Because the build environment tries to set reasonable defaults that will work most
of the time, component.mk can be very small or even empty (see `Minimal Component Makefile`). However, overriding `component variables` is usually required for some functionality.
Components can have any name (unique to the project) but the name
cannot contain spaces (esp-idf does not support spaces in paths).
One of the things that most components will have is a component.mk makefile,
containing instructions on how to build the component. Because the
build environment tries to set reasonable defaults that will work most
of the time, component.mk can be very small.
Simplest component.mk
=====================
At the minimum, component.mk will just include the ESP-IDF component "common" makefile,
which adds common component functionality::
include $(IDF_PATH)/make/component_common.mk
This will take all the .c and .S files in the component root and compile
them into object files, finally linking them into a library.
Here are some more advanced examples of ``component.mk`` makefiles:
Adding source directories
=========================
^^^^^^^^^^^^^^^^^^^^^^^^^
By default, subdirectories are ignored. If your project has sources in subdirectories
By default, sub-directories are ignored. If your project has sources in sub-directories
instead of in the root of the component then you can tell that to the build
system by setting COMPONENT_SRCDIRS::
system by setting ``COMPONENT_SRCDIRS``::
COMPONENT_SRCDIRS := src1 src2
include $(IDF_PATH)/make/component_common.mk
This will compile all source files in the src1/ and src2/ subdirectories
This will compile all source files in the src1/ and src2/ sub-directories
instead.
Specifying source files
=======================
^^^^^^^^^^^^^^^^^^^^^^^
The standard component.mk logic adds all .S and .c files in the source
directories as sources to be compiled unconditionally. It is possible
to circumvent that logic and hardcode the objects to be compiled by
manually setting the COMPONENT_OBJS variable to the name of the
to circumvent that logic and hard-code the objects to be compiled by
manually setting the ``COMPONENT_OBJS`` variable to the name of the
objects that need to be generated::
COMPONENT_OBJS := file1.o file2.o thing/filea.o thing/fileb.o anotherthing/main.o
include $(IDF_PATH)/make/component_common.mk
COMPONENT_SRCDIRS := . thing anotherthing
Note that ``COMPONENT_SRCDIRS`` must be set as well.
Adding conditional configuration
================================
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The configuration system can be used to conditionally compile some files
dependending on the options selected in ``make menuconfig``:
depending on the options selected in ``make menuconfig``:
Kconfig::
``Kconfig``::
config FOO_ENABLE_BAR
bool "Enable the BAR feature."
help
This enables the BAR feature of the FOO component.
bool "Enable the BAR feature."
help
This enables the BAR feature of the FOO component.
Makefile::
COMPONENT_OBJS := foo_a.o foo_b.o $(if $(CONFIG_FOO_ENABLE_BAR),foo_bar.o foo_bar_interface.o)
include $(IDF_PATH)/make/component_common.mk
``component.mk``::
COMPONENT_OBJS := foo_a.o foo_b.o
ifdef CONFIG_FOO_BAR
COMPONENT_OBJS += foo_bar.o foo_bar_interface.o
endif
See the `GNU Make Manual` for conditional syntax that can be used use in makefiles.
Source Code Generation
======================
^^^^^^^^^^^^^^^^^^^^^^
Some components will have a situation where a source file isn't supplied
with the component itself but has to be generated from another file. Say
our component has a header file that consists of the converted binary
data of a BMP file, converted using a hypothetical tool called bmp2h. The
header file is then included in as C source file called graphics_lib.c::
Some components will have a situation where a source file isn't
supplied with the component itself but has to be generated from
another file. Say our component has a header file that consists of the
converted binary data of a BMP file, converted using a hypothetical
tool called bmp2h. The header file is then included in as C source
file called graphics_lib.c::
COMPONENT_EXTRA_CLEAN := logo.h
@ -272,7 +390,6 @@ header file is then included in as C source file called graphics_lib.c::
logo.h: $(COMPONENT_PATH)/logo.bmp
bmp2h -i $^ -o $@
include $(IDF_PATH)/make/component_common.mk
In this example, graphics_lib.o and logo.h will be generated in the
current directory (the build directory) while logo.bmp comes with the
@ -281,24 +398,42 @@ generated file, it needs to be cleaned when make clean is called which
why it is added to the COMPONENT_EXTRA_CLEAN variable.
Cosmetic Improvements
=====================
^^^^^^^^^^^^^^^^^^^^^
The above example will work just fine, but there's one last cosmetic
improvement that can be done. The make system tries to make the make
process somewhat easier on the eyes by hiding the commands (unless you
run make with the V=1 switch) and this does not do that yet. Here's an
improved version that will output in the same style as the rest of the
make process::
Because logo.h is a generated file, it needs to be cleaned when make
clean is called which why it is added to the COMPONENT_EXTRA_CLEAN
variable.
COMPONENT_EXTRA_CLEAN := test_tjpgd_logo.h
Adding logo.h to the ``graphics_lib.o`` dependencies causes it to be
generated before ``graphics_lib.c`` is compiled.
graphics_lib.o: logo.h
If a a source file in another component included ``logo.h``, then this
component's name would have to be added to the other component's
``COMPONENT_DEPENDS`` list to ensure that the components were built
in-order.
logo.h: $(COMPONENT_PATH)/logo.bmp
$(summary) BMP2H $@
$(Q) bmp2h -i $^ -o $@
Embedding Binary Data
^^^^^^^^^^^^^^^^^^^^^
Sometimes you have a file with some binary or text data that you'd like to make available to your component - but you don't want to reformat the file as C source.
You can set a variable COMPONENT_EMBED_FILES in component.mk, giving the names of the files to embed in this way::
COMPONENT_EMBED_FILES := server_root_cert.der
Or if the file is a string, you can use the variable COMPONENT_EMBED_TXTFILES. This will embed the contents of the text file as a null-terminated string::
COMPONENT_EMBED_TXTFILES := server_root_cert.pem
The file's contents will be added to the .rodata section in flash, and are available via symbol names as follows::
extern const uint8_t server_root_cert_pem_start[] asm("_binary_server_root_cert_pem_start");
extern const uint8_t server_root_cert_pem_end[] asm("_binary_server_root_cert_pem_end");
The names are generated from the full name of the file, as given in COMPONENT_EMBED_FILES. Characters /, ., etc. are replaced with underscores. The _binary prefix in the symbol name is added by objcopy and is the same for both text and binary files.
For an example of using this technique, see examples/04_https_request - the certificate file contents are loaded from the text .pem file at compile time.
include $(IDF_PATH)/make/component_common.mk
Fully Overriding The Component Makefile
---------------------------------------
@ -307,12 +442,15 @@ Obviously, there are cases where all these recipes are insufficient for a
certain component, for example when the component is basically a wrapper
around another third-party component not originally intended to be
compiled under this build system. In that case, it's possible to forego
the build system entirely by setting COMPONENT_OWNBUILDTARGET and
possibly COMPONENT_OWNCLEANTARGET and defining your own build- and clean
the esp-idf build system entirely by setting COMPONENT_OWNBUILDTARGET and
possibly COMPONENT_OWNCLEANTARGET and defining your own targets named ``build`` and ``clean`` in ``component.mk``
target. The build target can do anything as long as it creates
$(COMPONENT_LIBRARY) for the main file to link into the project binary,
and even that is not necessary: if the COMPONENT_ADD_LDFLAGS variable
is set, the component can instruct the linker to do anything else as well.
$(COMPONENT_LIBRARY) for the project make process to link into the app binary.
(Actually, even this is not strictly necessary - if the COMPONENT_ADD_LDFLAGS variable
is set then the component can instruct the linker to link other binaries instead.)
.. _esp-idf-template: https://github.com/espressif/esp-idf-template
.. _GNU Make Manual: https://www.gnu.org/software/make/manual/make.html
.. _[_f1]: Actually, some components in esp-idf are "pure configuration" components that don't have a component.mk file, only a Makefile.projbuild and/or Kconfig.projbuild file. However, these components are unusual and most components have a component.mk file.

View file

@ -22,9 +22,6 @@ import os
# -- Run DoxyGen to prepare XML for Sphinx---------------------------------
# ref. https://github.com/rtfd/readthedocs.org/issues/388
#
# added by krzychb, 24-Oct-2016
#
from subprocess import call, Popen, PIPE
import shlex
@ -75,13 +72,23 @@ copyright = u'2016, Espressif'
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# This is supposed to be "the short X.Y version", but it's the only version
# visible when you open index.html.
# Display full version to make things less confusing.
# If needed, nearest tag is returned by 'git describe --abbrev=0'.
version = run_cmd_get_output('git describe')
# The full version, including alpha/beta/rc tags.
release = run_cmd_get_output('git describe')
# Different setup depending if script is running on ReadTheDocs or elsewhere
on_rtd = os.environ.get('READTHEDOCS') == 'True'
if on_rtd:
# The short X.Y version.
# Apparently ReadTheDocs is getting confused by other version / release
# ReadTheDocs is building specific or the latest release from GitHub.
version = '1.0'
release = '1.0'
else:
# This is supposed to be "the short X.Y version", but it's the only version
# visible when you open index.html.
# Display full version to make things less confusing.
# If needed, nearest tag is returned by 'git describe --abbrev=0'.
version = run_cmd_get_output('git describe')
# The full version, including alpha/beta/rc tags.
release = run_cmd_get_output('git describe')
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@ -288,8 +295,6 @@ texinfo_documents = [
# -- Use sphinx_rtd_theme for local builds --------------------------------
# ref. https://github.com/snide/sphinx_rtd_theme#using-this-theme-locally-then-building-on-read-the-docs
#
# added by krzychb, 24-Oct-2016
#
# on_rtd is whether we are on readthedocs.org
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'

View file

@ -13,7 +13,7 @@ Framework (esp-idf) ("We" or "Us").
The purpose of this contributor agreement ("Agreement") is to clarify
and document the rights granted by contributors to Us. To make this
document effective, please follow the instructions at
https://github.com/espressif/esp-idf/blob/master/CONTRIBUTING.md.
https://github.com/espressif/esp-idf/blob/master/CONTRIBUTING.rst.
1. DEFINITIONS
~~~~~~~~~~~~~~

View file

@ -1,12 +1,12 @@
Deep Sleep Wake Stubs
---------------------
=====================
ESP32 supports running a "deep sleep wake stub" when coming out of deep sleep. This function runs immediately as soon as the chip wakes up - before any normal initialisation, bootloader, or ESP-IDF code has run. After the wake stub runs, the SoC can go back to sleep or continue to start ESP-IDF normally.
Deep sleep wake stub code is loaded into "RTC Fast Memory" and any data which it uses must also be loaded into RTC memory. RTC memory regions hold their contents during deep sleep.
Rules for Wake Stubs
====================
--------------------
Wake stub code must be carefully written:
@ -23,9 +23,9 @@ Wake stub code must be carefully written:
* Wake stub code is a part of the main esp-idf app. During normal running of esp-idf, functions can call the wake stub functions or access RTC memory. It is as if these were regular parts of the app.
Implementing A Stub
===================
-------------------
The wake stub in esp-idf is called ``esp_wake_deep_sleep()``. This function runs whenever the SoC wakes from deep sleep. There is a default version of this function provided in esp-idf, but the default function is weak-linked so if your app contains a function named ``esp_wake_deep_sleep()` then this will override the default.
The wake stub in esp-idf is called ``esp_wake_deep_sleep()``. This function runs whenever the SoC wakes from deep sleep. There is a default version of this function provided in esp-idf, but the default function is weak-linked so if your app contains a function named ``esp_wake_deep_sleep()`` then this will override the default.
If supplying a custom wake stub, the first thing it does should be to call ``esp_default_wake_deep_sleep()``.
@ -36,7 +36,7 @@ If you want to swap between different deep sleep stubs at runtime, it is also po
All of these functions are declared in the ``esp_deepsleep.h`` header under components/esp32.
Loading Code Into RTC Memory
============================
----------------------------
Wake stub code must be resident in RTC Fast Memory. This can be done in one of two ways.
@ -53,7 +53,7 @@ The first way is simpler for very short and simple code, or for source files whe
Loading Data Into RTC Memory
============================
----------------------------
Data used by stub code must be resident in RTC Slow Memory. This memory is also used by the ULP.

View file

@ -1,10 +1,18 @@
Documenting Code
================
The purpose of this description is to provide quick summary on documentation style used in `espressif/esp-idf`_ repository and how to add new documentation.
Introduction
------------
When documenting code for this repository, please follow `Doxygen style <http://www.stack.nl/~dimitri/doxygen/manual/docblocks.html#specialblock>`_. You are doing it by inserting special commands, for instance ``@param``, into standard comments blocks like for example ``/* @param ratio this is oxygen to air ratio */``.
When documenting code for this repository, please follow `Doxygen style <http://www.stack.nl/~dimitri/doxygen/manual/docblocks.html#specialblock>`_. You are doing it by inserting special commands, for instance ``@param``, into standard comments blocks, for example:
::
/**
* @param ratio this is oxygen to air ratio
*/
Doxygen is phrasing the code, extracting the commands together with subsequent text, and building documentation out of it.
@ -14,20 +22,19 @@ Typical comment block, that contains documentation of a function, looks like bel
:align: center
:alt: Sample inline code documentation
Doxygen supports couple of formatting styles. It also gives you great flexibility on level of details to include in documentation. To get the taste of available features please check data reach and very well organized `Doxygen Manual <http://www.stack.nl/~dimitri/doxygen/manual/index.html>`_.
Doxygen supports couple of formatting styles. It also gives you great flexibility on level of details to include in documentation. To get familiar with available features, please check data reach and very well organized `Doxygen Manual <http://www.stack.nl/~dimitri/doxygen/manual/index.html>`_.
Why we need it?
---------------
The purpose of this description is to provide quick summary on documentation style used in `espressif/esp-idf <https://github.com/espressif/esp-idf>`_ repository.
The ultimate goal is to ensure that all the code is consistently documented, so we can use tools like `Sphinx <http://www.sphinx-doc.org/>`_ and `Breathe <https://breathe.readthedocs.io/>`_ to aid preparation and automatic updates of API documentation when the code changes.
The ultimate goal is to ensure that all the code is consistently documented, so we can use tools like `Sphinx <http://www.sphinx-doc.org/>`_ and `Breathe <https://breathe.readthedocs.io/>`_ to aid preparation and automatic updates of API documentation when the code changes. The above piece of code renders in Sphinx like below:
With these tools the above piece of code renders like below:
.. image:: _static/doc-code-documentation-rendered.png
:align: center
:alt: Sample inline code after rendering
Go for it!
----------
@ -57,7 +64,7 @@ When writing code for this repository, please follow guidelines below.
6. To provide well formatted lists, break the line after command (like ``@return`` in example below).
::
.. code-block:: c
...
*
@ -70,53 +77,76 @@ When writing code for this repository, please follow guidelines below.
*
...
7. Overview of functionality of documented header file, or group of files that make a library, should be placed in separate ``README.rst`` file.
7. Overview of functionality of documented header file, or group of files that make a library, should be placed in the same directory in a separate ``README.rst`` file. If directory contains header files for different APIs, then the file name should be ``apiname-readme.rst``.
Go one extra mile
-----------------
There are couple of tips how you can make your documentation even better and more useful to the reader.
There is couple of tips, how you can make your documentation even better and more useful to the reader.
Add code snippets to illustrate implementation. To do so, enclose the snippet using ``@code{c}`` and ``@endcode`` commands.
1. Add code snippets to illustrate implementation. To do so, enclose snippet using ``@code{c}`` and ``@endcode`` commands.
::
.. code-block:: c
...
*
* @code{c}
* // Example of using nvs_get_i32:
* int32_t max_buffer_size = 4096; // default value
* esp_err_t err = nvs_get_i32(my_handle, "max_buffer_size", &max_buffer_size);
* assert(err == ESP_OK || err == ESP_ERR_NVS_NOT_FOUND);
* // if ESP_ERR_NVS_NOT_FOUND was returned, max_buffer_size will still
* // have its default value.
* @endcode
*
...
...
*
* @code{c}
* // Example of using nvs_get_i32:
* int32_t max_buffer_size = 4096; // default value
* esp_err_t err = nvs_get_i32(my_handle, "max_buffer_size", &max_buffer_size);
* assert(err == ESP_OK || err == ESP_ERR_NVS_NOT_FOUND);
* // if ESP_ERR_NVS_NOT_FOUND was returned, max_buffer_size will still
* // have its default value.
* @endcode
*
...
To highlight some information use command ``@attention`` or ``@note``. Example below also shows how to use a numbered list.
The code snippet should be enclosed in a comment block of the function that it illustrates.
::
2. To highlight some important information use command ``@attention`` or ``@note``.
...
*
* @attention
* 1. This API only impact WIFI_MODE_STA or WIFI_MODE_APSTA mode
* 2. If the ESP32 is connected to an AP, call esp_wifi_disconnect to disconnect.
*
...
.. code-block:: c
...
*
* @attention
* 1. This API only impact WIFI_MODE_STA or WIFI_MODE_APSTA mode
* 2. If the ESP32 is connected to an AP, call esp_wifi_disconnect to disconnect.
*
...
Use markdown to make your documentation even more readable. With markdown you can add headers, links, tables and more.
Above example also shows how to use a numbered list.
::
3. Use markdown to make your documentation even more readable. You will add headers, links, tables and more.
...
*
* [ESP32 Technical Reference](http://espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf)
*
...
.. code-block:: c
...
*
* [ESP32 Technical Reference](http://espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf)
*
...
.. note::
Code snippets, notes, links, etc. will not make it to the documentation, if not enclosed in a comment block associated with one of documented objects.
5. Prepare one or more complete code examples together with description. Place them in a separate file ``example.rst`` in the same directory as the API header files. If directory contains header files for different APIs, then the file name should be ``apiname-example.rst``.
Put it all together
-------------------
Once all the above steps are complete, follow instruction in :doc:`api/template` and create a single file, that will merge all individual pieces of prepared documentation. Finally add a link to this file to respective ``.. toctree::`` in ``index.rst`` file located in ``/docs`` folder.
OK, but I am new to Sphinx!
---------------------------
1. No worries. All the software you need is well documented. It is also open source and free. Start by checking `Sphinx <http://www.sphinx-doc.org/>`_ documentation. If you are not clear how to write using rst markup language, see `reStructuredText Primer <http://www.sphinx-doc.org/en/stable/rest.html>`_.
2. Check the source files of this documentation to understand what is behind of what you see now on the screen. Sources are maintained on GitHub in `espressif/esp-idf`_ repository in `/docs <https://github.com/espressif/esp-idf/tree/master/docs>`_ folder. You can go directly to the source file of this page by scrolling up and clicking the link in the top right corner. When on GitHub, see what's really inside, open source files by clicking ``Raw`` button.
3. You will likely want to see how documentation builds and looks like before posting it on the GitHub. There are two options to do so:
* Install `Sphinx <http://www.sphinx-doc.org/>`_, `Breathe <https://breathe.readthedocs.io/>`_ and `Doxygen <http://www.stack.nl/~dimitri/doxygen/>`_ to build it locally. You would need a Linux machine for that.
* Set up an account on `Read the Docs <https://readthedocs.org/>`_ and build documentation in the cloud. Read the Docs provides document building and hosting for free and their service works really quick and great.
Wrap up
-------
@ -124,3 +154,6 @@ Wrap up
We love good code that is doing cool things.
We love it even better, if it is well documented, so we can quickly make it run and also do the cool things.
Go ahead, contribute your code and documentation!
.. _espressif/esp-idf: https://github.com/espressif/esp-idf/

View file

@ -5,7 +5,6 @@ ESP32 Programming Guide
Until ESP-IDF release 1.0, this documentation is a draft. It is incomplete and may have mistakes. Please mind your step!
Contents:
.. toctree::
@ -16,8 +15,6 @@ Contents:
Linux <linux-setup>
Mac OS <macos-setup>
.. Configure - TBA
.. Connect - TBA
.. toctree::
@ -34,6 +31,60 @@ Contents:
partition-tables
build_system
openocd
Secure Boot <security/secure-boot>
.. API Reference
..
Table of Contents Outline
..
1. System - TBA
1.1. Fundamentals of multiprocessor programming with FreeRTOS - TBA
1.2. Application startup flow - TBA
1.3. Flash encryption and secure boot: how they work and APIs - TBA
1.4. Lower Power Coprocessor - TBA
1.5. Watchdogs
1.6. ...
2. Memeory - TBA
2.1. Memory layout of the application (IRAM/IROM, limitations of each) - TBA
2.2. Flash layout and partitions - TBA
2.3. Flash access APIs - TBA
2.4. Partition APIs - TBA
2.5. OTA mechanism (app partitions, OTA partition) and APIs - TBA
2.6. ...
3. Wi-Fi
4. Bluetooth
4.1. BT Classic - TBA
4.2. BLE
5. Ethernet - TBA
6. Interfaces
6.1. GPIO
6.2. ADC - TBA
6.3. DAC - TBA
6.4. UART - TBA
6.5. I2C - TBA
6.6. I2S - TBA
6.7. SPI - TBA
6.8. CAN - TBA
6.9. SD Controller - TBA
6.10. Infrared - TBA
6.11. Pulse Counter - TBA
6.12. PWM - TBA
6.13. LED PWM
6.14. ...
7. Sensors - TBA
7.1. Hall Sensor - TBA
7.2. Temperature Sensor - TBA
7.3. Touch Sensor - TBA
8. Protocols - TBA
9. Components
9.1. Logging <api/log>
9.2 Non-Volatile Storage <api/nvs_flash>
9.3 Virtual Filesystem <api/vfs>
9.3. Http sever - TBA
10. Applications - TBA
..
API Dcoumentation Teamplate
..
.. toctree::
:caption: API Reference
@ -41,10 +92,16 @@ Contents:
Wi-Fi <api/esp_wifi>
Bluetooth <api/bt>
GPIO <api/gpio>
api/gpio
api/uart
api/ledc
Logging <api/log>
Non-volatile storage <api/nvs>
Virtual filesystem <api/vfs>
Non-Volatile Storage <api/nvs_flash>
Virtual Filesystem <api/vfs>
deep-sleep-stub
Template <api/template>
.. toctree::
@ -59,6 +116,7 @@ Contents:
:maxdepth: 1
contributing
Style Guide <style-guide>
documenting-code
contributor-agreement
@ -76,3 +134,4 @@ Indices
* :ref:`genindex`
* :ref:`search`

View file

@ -6,6 +6,8 @@ Overview
A single ESP32's flash can contain multiple apps, as well as many different kinds of data (calibration data, filesystems, parameter storage, etc). For this reason a partition table is flashed to offset 0x4000 in the flash.
Partition table length is 0xC00 bytes (maximum 95 partition table entries). If the partition table is signed due to `secure boot`, the signature is appended after the table data.
Each entry in the partition table has a name (label), type (app, data, or something else), subtype and the offset in flash where the partition is loaded.
The simplest way to use the partition table is to `make menuconfig` and choose one of the simple predefined partition tables:
@ -130,3 +132,6 @@ Flashing the partition table
* ``make flash``: Will flash everything including the partition table.
A manual flashing command is also printed as part of ``make partition_table``.
.. _secure boot: security/secure-boot.rst

View file

@ -0,0 +1,179 @@
Secure Boot
===========
Secure Boot is a feature for ensuring only your code can run on the chip. Data loaded from flash is verified on each reset.
Secure Boot is separate from the Encrypted Flash feature, and you can use secure boot without encrypting the flash contents. However we recommend using both features together for a secure environment.
**IMPORTANT: As Encrypted Flash feature and related security features are not yet released, Secure Boot should not be considered sufficient for a secure device and we strongly recommend not enabling the one-time secure bootloader feature until it is mature.**
Background
----------
- Most data is stored in flash. Flash access does not need to be protected from physical access in order for secure boot to function, because critical data is stored (non-software-accessible) in Efuses internal to the chip.
- Efuses are used to store the secure bootloader key (in efuse block 2), and also a single Efuse bit (ABS_DONE_0) is burned (written to 1) to permanently enable secure boot on the chip. For more details about efuse, see the (forthcoming) chapter in the Technical Reference Manual.
- To understand the secure boot process, first familiarise yourself with the standard `esp-idf boot process`.
- Both stages of the boot process (initial software bootloader load, and subsequent partition & app loading) are verified by the secure boot process, in a "chain of trust" relationship.
Secure Boot Process Overview
----------------------------
This is a high level overview of the secure boot process. Step by step instructions are supplied under `How To Enable Secure Boot`. Further in-depth details are supplied under `Technical Details`:
1. The options to enable secure boot are provided in the ``make menuconfig`` hierarchy, under "Secure Boot Configuration".
2. Secure Boot Configuration includes "Secure boot signing key", which is a file path. This file is a ECDSA public/private key pair in a PEM format file.
2. The software bootloader image is built by esp-idf with secure boot support enabled and the public key (signature verification) portion of the secure boot signing key compiled in. This software bootloader image is flashed at offset 0x1000.
3. On first boot, the software bootloader follows the following process to enable secure boot:
- Hardware secure boot support generates a device secure bootloader key (generated via hardware RNG, then stored read/write protected in efuse), and a secure digest. The digest is derived from the key, an IV, and the bootloader image contents.
- The secure digest is flashed at offset 0x0 in the flash.
- Depending on Secure Boot Configuration, efuses are burned to disable JTAG and the ROM BASIC interpreter (it is strongly recommended these options are turned on.)
- Bootloader permanently enables secure boot by burning the ABS_DONE_0 efuse. The software bootloader then becomes protected (the chip will only boot a bootloader image if the digest matches.)
4. On subsequent boots the ROM bootloader sees that the secure boot efuse is burned, reads the saved digest at 0x0 and uses hardware secure boot support to compare it with a newly calculated digest. If the digest does not match then booting will not continue. The digest and comparison are performed entirely by hardware, and the calculated digest is not readable by software. For technical details see `Hardware Secure Boot Support`.
5. When running in secure boot mode, the software bootloader uses the secure boot signing key (the public key of which is embedded in the bootloader itself, and therefore validated as part of the bootloader) to verify the signature appended to all subsequent partition tables and app images before they are booted.
Keys
----
The following keys are used by the secure boot process:
- "secure bootloader key" is a 256-bit AES key that is stored in Efuse block 2. The bootloader can generate this key itself from the internal hardware random number generator, the user does not need to supply it (it is optionally possible to supply this key, see `Re-Flashable Software Bootloader`). The Efuse holding this key is read & write protected (preventing software access) before secure boot is enabled.
- "secure boot signing key" is a standard ECDSA public/private key pair (see `Image Signing Algorithm`) in PEM format.
- The public key from this key pair (for signature verificaton but not signature creation) is compiled into the software bootloader and used to verify the second stage of booting (partition table, app image) before booting continues. The public key can be freely distributed, it does not need to be kept secret.
- The private key from this key pair *must be securely kept private*, as anyone who has this key can authenticate to any bootloader that is configured with secure boot and the matching public key.
How To Enable Secure Boot
-------------------------
1. Run ``make menuconfig``, navigate to "Secure Boot Configuration" and select the option "One-time Flash". (To understand the alternative "Reflashable" choice, see `Re-Flashable Software Bootloader`.)
2. Select a name for the secure boot signing key. This option will appear after secure boot is enabled. The file can be anywhere on your system. A relative path will be evaluated from the project directory. The file does not need to exist yet.
3. Set other menuconfig options (as desired). Pay particular attention to the "Bootloader Config" options, as you can only flash the bootloader once. Then exit menuconfig and save your configuration
4. The first time you run ``make``, if the signing key is not found then an error message will be printed with a command to generate a signing key via ``espsecure.py generate_signing_key``.
**IMPORTANT** A signing key generated this way will use the best random number source available to the OS and its Python installation (`/dev/urandom` on OSX/Linux and `CryptGenRandom()` on Windows). If this random number source is weak, then the private key will be weak.
**IMPORTANT** For production environments, we recommend generating the keypair using openssl or another industry standard encryption program. See `Generating Secure Boot Signing Key` for more details.
5. Run ``make bootloader`` to build a secure boot enabled bootloader. The output of `make` will include a prompt for a flashing command, using `esptool.py write_flash`.
6. When you're ready to flash the bootloader, run the specified command (you have to enter it yourself, this step is not performed by make) and then wait for flashing to complete. **Remember this is a one time flash, you can't change the bootloader after this!**.
7. Run `make flash` to build and flash the partition table and the just-built app image. The app image will be signed using the signing key you generated in step 4.
*NOTE*: `make flash` doesn't flash the bootloader if secure boot is enabled.
8. Reset the ESP32 and it will boot the software bootloader you flashed. The software bootloader will enable secure boot on the chip, and then it verifies the app image signature and boots the app. You should watch the serial console output from the ESP32 to verify that secure boot is enabled and no errors have occured due to the build configuration.
*NOTE* Secure boot won't be enabled until after a valid partition table and app image have been flashed. This is to prevent accidents before the system is fully configured.
9. On subsequent boots, the secure boot hardware will verify the software bootloader has not changed (using the secure bootloader key) and then the software bootloader will verify the signed partition table and app image (using the public key portion of the secure boot signing key).
Re-Flashable Software Bootloader
--------------------------------
Configuration "Secure Boot: One-Time Flash" is the recommended configuration for production devices. In this mode, each device gets a unique key that is never stored outside the device.
However, an alternative mode "Secure Boot: Reflashable" is also available. This mode allows you to supply a 256-bit key file that is used for the secure bootloader key. As you have the key file, you can generate new bootloader images and secure boot digests for them.
In the esp-idf build process, this 256-bit key file is derived from the app signing key generated during the generate_signing_key step above. The private key's SHA-256 digest is used as the 256-bit secure bootloader key. This is a convenience so you only need to generate/protect a single private key.
*NOTE*: Although it's possible, we strongly recommend not generating one secure boot key and flashing it to every device in a production environment. The "One-Time Flash" option is recommended for production environments.
To enable a reflashable bootloader:
1. In the ``make menuconfig`` step, select "Bootloader Config" -> "Secure Boot" -> "Reflashable".
2. Follow the steps shown above to choose a signing key file, and generate the key file.
3. Run ``make bootloader``. A 256-bit key file will be created, derived from the private key that is used for signing. Two sets of flashing steps will be printed - the first set of steps includes an ``espefuse.py burn_key`` command which is used to write the bootloader key to efuse. (Flashing this key is a one-time-only process.) The second set of steps can be used to reflash the bootloader with a pre-calculated digest (generated during the build process).
4. Resume from `Step 6<Secure Boot Process Overview>` of the one-time process, to flash the bootloader and enable secure boot. Watch the console log output closely to ensure there were no errors in the secure boot configuration.
Generating Secure Boot Signing Key
----------------------------------
The build system will prompt you with a command to generate a new signing key via ``espsecure.py generate_signing_key``. This uses the python-ecdsa library, which in turn uses Python's os.urandom() as a random number source.
The strength of the signing key is proportional to (a) the random number source of the system, and (b) the correctness of the algorithm used. For production devices, we recommend generating signing keys from a system with a quality entropy source, and using the best available EC key generation utilities.
For example, to generate a signing key using the openssl command line:
```
openssl ecparam -name prime256v1 -genkey -noout -out my_secure_boot_signing_key.pem
```
Remember that the strength of the secure boot system depends on keeping the signing key private.
Secure Boot Best Practices
--------------------------
* Generate the signing key on a system with a quality source of entropy.
* Keep the signing key private at all times. A leak of this key will compromise the secure boot system.
* Do not allow any third party to observe any aspects of the key generation or signing process using espsecure.py. Both processes are vulnerable to timing or other side-channel attacks.
* Enable all secure boot options in the Secure Boot Configuration. These include flash encryption, disabling of JTAG, disabling BASIC ROM interpeter, and disabling the UART bootloader encrypted flash access.
Technical Details
-----------------
The following sections contain low-level descriptions of various technical functions:
Hardware Secure Boot Support
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The Secure Boot support hardware can perform three basic operations:
1. Generate a random sequence of bytes from a hardware random number generator.
2. Generate a digest from data (usually the bootloader image from flash) using a key stored in Efuse block 2. The key in Efuse can (& should) be read/write protected, which prevents software access. For full details of this algorithm see `Secure Bootloader Digest Algorithm`. The digest can only be read back by software if Efuse ABS_DONE_0 is *not* burned (ie still 0).
3. Generate a digest from data (usually the bootloader image from flash) using the same algorithm as step 2 and compare it to a pre-calculated digest supplied in a buffer (usually read from flash offset 0x0). The hardware returns a true/false comparison without making the digest available to software. This function is available even when Efuse ABS_DONE_0 is burned.
Secure Bootloader Digest Algorithm
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Starting with an "image" of binary data as input, this algorithm generates a digest as output. The digest is sometimes referred to as an "abstract" in hardware documentation.
For a Python version of this algorithm, see the `espsecure.py` tool in the components/esptool_py directory.
Items marked with (^) are to fulfill hardware restrictions, as opposed to cryptographic restrictions.
1. Prefix the image with a 128 byte randomly generated IV.
2. If the image length is not modulo 128, pad the image to a 128 byte boundary with 0xFF. (^)
3. For each 16 byte plaintext block of the input image:
- Reverse the byte order of the plaintext input block (^)
- Apply AES256 in ECB mode to the plaintext block.
- Reverse the byte order of the ciphertext output block. (^)
- Append to the overall ciphertext output.
4. Byte-swap each 4 byte word of the ciphertext (^)
5. Calculate SHA-512 of the ciphertext.
Output digest is 192 bytes of data: The 128 byte IV, followed by the 64 byte SHA-512 digest.
Image Signing Algorithm
~~~~~~~~~~~~~~~~~~~~~~~
Deterministic ECDSA as specified by `RFC6979`.
- Curve is NIST256p (openssl calls this curve "prime256v1", it is also sometimes called secp256r1).
- Hash function is SHA256.
- Key format used for storage is PEM.
- In the bootloader, the public key (for signature verification) is flashed as 64 raw bytes.
- Image signature is 68 bytes - a 4 byte version word (currently zero), followed by a 64 bytes of signature data. These 68 bytes are appended to an app image or partition table data.
.. _esp-idf boot process: ../boot-process.rst
.. _RFC6979: https://tools.ietf.org/html/rfc6979

View file

@ -172,7 +172,7 @@ To re-format a file, run::
Documenting code
----------------
Please see the guide here: `Documenting Code <documenting-code.rst>`_.
Please see the guide here: :doc:`documenting-code`.
Structure and naming
--------------------

View file

@ -1,10 +1,5 @@
#
# Main Makefile. This is basically the same as a component makefile.
#
# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default,
# this will take the sources in the src/ directory, compile them and link them into
# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
# please read the ESP-IDF documents if you need to do this.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
include $(IDF_PATH)/make/component_common.mk

View file

@ -1,10 +1,4 @@
#
# Main Makefile. This is basically the same as a component makefile.
#
# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default,
# this will take the sources in the src/ directory, compile them and link them into
# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
# please read the ESP-IDF documents if you need to do this.
#
include $(IDF_PATH)/make/component_common.mk
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -1,10 +1,4 @@
#
# Main Makefile. This is basically the same as a component makefile.
#
# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default,
# this will take the sources in the src/ directory, compile them and link them into
# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
# please read the ESP-IDF documents if you need to do this.
#
include $(IDF_PATH)/make/component_common.mk
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -1,44 +0,0 @@
/* This is the CA certificate for the CA trust chain of
www.howsmyssl.com in PEM format, as dumped via:
openssl s_client -showcerts -connect www.howsmyssl.com:443 </dev/null
The CA cert is the last cert in the chain output by the server.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
/*
1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
i:/O=Digital Signature Trust Co./CN=DST Root CA X3
*/
const char *server_root_cert = "-----BEGIN CERTIFICATE-----\r\n"
"MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/\r\n"
"MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\r\n"
"DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow\r\n"
"SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT\r\n"
"GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC\r\n"
"AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF\r\n"
"q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8\r\n"
"SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0\r\n"
"Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA\r\n"
"a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj\r\n"
"/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T\r\n"
"AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG\r\n"
"CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv\r\n"
"bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k\r\n"
"c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw\r\n"
"VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC\r\n"
"ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz\r\n"
"MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu\r\n"
"Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF\r\n"
"AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo\r\n"
"uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/\r\n"
"wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu\r\n"
"X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG\r\n"
"PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6\r\n"
"KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\r\n"
"-----END CERTIFICATE-----\r\n";

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