Merge branch 'master' into feature/build_component_project_vars

This commit is contained in:
Angus Gratton 2016-11-14 14:48:10 +11:00
commit c15024e629
85 changed files with 5078 additions and 1137 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

@ -14,32 +14,105 @@ BOOTLOADER_COMPONENT_PATH := $(COMPONENT_PATH)
BOOTLOADER_BUILD_DIR=$(abspath $(BUILD_DIR_BASE)/bootloader)
BOOTLOADER_BIN=$(BOOTLOADER_BUILD_DIR)/bootloader.bin
# 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) BUILD_DIR_BASE=$(BOOTLOADER_BUILD_DIR) \
V=$(V) BUILD_DIR_BASE=$(BOOTLOADER_BUILD_DIR)
.PHONY: bootloader-clean bootloader-flash bootloader $(BOOTLOADER_BIN)
$(BOOTLOADER_BIN): $(SDKCONFIG_MAKEFILE)
$(BOOTLOADER_MAKE) $@
bootloader-clean:
$(BOOTLOADER_MAKE) app-clean
clean: bootloader-clean
ifdef CONFIG_SECURE_BOOTLOADER_DISABLED
# If secure boot disabled, bootloader flashing is integrated
# with 'make flash' and no warnings are printed.
bootloader: $(BOOTLOADER_BIN)
@echo $(SEPARATOR)
@echo "Bootloader built. Default flash command is:"
@echo "$(ESPTOOLPY_WRITE_FLASH) 0x1000 $^"
all_binaries: $(BOOTLOADER_BIN)
ESPTOOL_ALL_FLASH_ARGS += 0x1000 $(BOOTLOADER_BIN)
# bootloader-flash calls flash in the bootloader dummy project
bootloader-flash: $(BOOTLOADER_BIN)
$(ESPTOOLPY_WRITE_FLASH) 0x1000 $^
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):
mkdir -p $@

View file

@ -7,7 +7,7 @@ PROJECT_NAME := bootloader
#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 log spi_flash
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.
#

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

@ -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

@ -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"

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

@ -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

@ -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,23 +11,43 @@ 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)
$(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))..."
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)

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

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

@ -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

@ -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

@ -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

@ -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,14 +20,24 @@ 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)..."
$(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:"

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

@ -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

@ -392,8 +392,13 @@ file called graphics_lib.c::
In this example, graphics_lib.o and logo.h will be generated in the
component build directory, whereas logo.bmp resides in the component
source directory.
current directory (the build directory) while logo.bmp comes with the
component and resides under the component path. 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.
Cosmetic Improvements
^^^^^^^^^^^^^^^^^^^^^
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
@ -407,6 +412,28 @@ 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.
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.
Fully Overriding The Component Makefile
---------------------------------------

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,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";

View file

@ -3,3 +3,8 @@
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
# embed files from the "certs" directory as binary data symbols
# in the app
COMPONENT_EMBED_TXTFILES := server_root_cert.pem

View file

@ -74,8 +74,18 @@ static const char *REQUEST = "GET " WEB_URL " HTTP/1.1\n"
"User-Agent: esp-idf/1.0 esp32\n"
"\n";
/* Root cert for howsmyssl.com, found in cert.c */
extern const char *server_root_cert;
/* Root cert for howsmyssl.com, taken from server_root_cert.pem
The PEM file was extracted from the output of this command:
openssl s_client -showcerts -connect www.howsmyssl.com:443 </dev/null
The CA root cert is the last cert given in the chain of certs.
To embed it in the app binary, the PEM file is named
in the component.mk COMPONENT_EMBED_TXTFILES variable.
*/
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");
#ifdef MBEDTLS_DEBUG_C
@ -191,7 +201,9 @@ static void https_get_task(void *pvParameters)
ESP_LOGI(TAG, "Loading the CA root certificate...");
ret = mbedtls_x509_crt_parse(&cacert, (uint8_t*)server_root_cert, strlen(server_root_cert)+1);
ret = mbedtls_x509_crt_parse(&cacert, server_root_cert_pem_start,
server_root_cert_pem_end-server_root_cert_pem_start);
if(ret < 0)
{
ESP_LOGE(TAG, "mbedtls_x509_crt_parse returned -0x%x\n\n", -ret);

View file

@ -0,0 +1,27 @@
-----BEGIN CERTIFICATE-----
MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8
SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA
a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T
AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF
AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/
wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG
PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
-----END CERTIFICATE-----

View file

@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := nvs-rw-value
include $(IDF_PATH)/make/project.mk

View file

@ -0,0 +1,13 @@
# Non-Volatile Storage (NVS) Read and Write Example
Demonstrates how to read and write a single integer value using NVS.
The value holds the number of ESP32 module restarts. Since it is written to NVS, the value is preserved between restarts.
Example also shows how to check if read / write operation was successful, or certain value is not initialized in NVS. Diagnostic is provided in plain text to help track program flow and capture any issues on the way.
Check another example *08_nvs_rw_blob*, that shows how to read and write variable length binary data (blob).
Detailed functional description of NVS and API is provided in [documentation](http://esp-idf.readthedocs.io/en/latest/api/nvs_flash.html).
See the README.md file in the upper level 'examples' directory for more information about examples.

View file

@ -0,0 +1,2 @@
include $(IDF_PATH)/make/component_common.mk

View file

@ -0,0 +1,80 @@
/* Non-Volatile Storage (NVS) Read and Write a Value - Example
For other examples please check:
https://github.com/espressif/esp-idf/tree/master/examples
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "nvs.h"
void app_main()
{
nvs_flash_init();
nvs_handle my_handle;
esp_err_t err;
printf("\n");
// Open
printf("Opening Non-Volatile Storage (NVS) ... ");
err = nvs_open("storage", NVS_READWRITE, &my_handle);
if (err != ESP_OK) {
printf("Error (%d) opening NVS!\n", err);
} else {
printf("Done\n");
// Read
printf("Reading restart counter from NVS ... ");
int32_t restart_counter = 0; // value will default to 0, if not set yet in NVS
err = nvs_get_i32(my_handle, "restart_conter", &restart_counter);
switch (err) {
case ESP_OK:
printf("Done\n");
printf("Restart counter = %d\n", restart_counter);
break;
case ESP_ERR_NVS_NOT_FOUND:
printf("The value is not initialized yet!\n");
break;
default :
printf("Error (%d) reading!\n", err);
}
// Write
printf("Updating restart counter in NVS ... ");
restart_counter++;
err = nvs_set_i32(my_handle, "restart_conter", restart_counter);
printf((err != ESP_OK) ? "Failed!\n" : "Done\n");
// Commit written value.
// After setting any values, nvs_commit() must be called to ensure changes are written
// to flash storage. Implementations may write to storage at other times,
// but this is not guaranteed.
printf("Committing updates in NVS ... ");
err = nvs_commit(my_handle);
printf((err != ESP_OK) ? "Failed!\n" : "Done\n");
// Close
nvs_close(my_handle);
}
printf("\n");
// Restart module
for (int i = 10; i >= 0; i--) {
printf("Restarting in %d seconds...\n", i);
vTaskDelay(1000 / portTICK_RATE_MS);
}
printf("Restarting now.\n");
fflush(stdout);
system_restart();
}

View file

@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := nvs-rw-blob
include $(IDF_PATH)/make/project.mk

View file

@ -0,0 +1,14 @@
# Non-Volatile Storage (NVS) Read and Write Example
Demonstrates how to read and write a single integer value and a blob (binary large object) using NVS to preserve them between ESP32 module restarts.
* value - tracks number of ESP32 module soft and hard restarts.
* blob - contains a table with module run times. The table is read from NVS to dynamically allocated RAM. New run time is added to the table on each manually triggered soft restart and written back to NVS. Triggering is done by pulling down GPIO0.
Example also shows how to implement diagnostics if read / write operation was successful.
If not done already, consider checking simpler example *07_nvs_rw_value*, that has been used as a starting point for preparing this one.
Detailed functional description of NVS and API is provided in [documentation](http://esp-idf.readthedocs.io/en/latest/api/nvs_flash.html).
See the README.md file in the upper level 'examples' directory for more information about examples.

View file

@ -0,0 +1,2 @@
include $(IDF_PATH)/make/component_common.mk

View file

@ -0,0 +1,178 @@
/* Non-Volatile Storage (NVS) Read and Write a Blob - Example
For other examples please check:
https://github.com/espressif/esp-idf/tree/master/examples
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "nvs.h"
#include "driver/gpio.h"
#define STORAGE_NAMESPACE "storage"
/* Save the number of module restarts in NVS
by first reading and then incrementing
the number that has been saved previously.
Return an error if anything goes wrong
during this process.
*/
esp_err_t save_restart_counter(void)
{
nvs_handle my_handle;
esp_err_t err;
// Open
err = nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &my_handle);
if (err != ESP_OK) return err;
// Read
int32_t restart_counter = 0; // value will default to 0, if not set yet in NVS
err = nvs_get_i32(my_handle, "restart_conter", &restart_counter);
if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err;
// Write
restart_counter++;
err = nvs_set_i32(my_handle, "restart_conter", restart_counter);
if (err != ESP_OK) return err;
// Commit written value.
// After setting any values, nvs_commit() must be called to ensure changes are written
// to flash storage. Implementations may write to storage at other times,
// but this is not guaranteed.
err = nvs_commit(my_handle);
if (err != ESP_OK) return err;
// Close
nvs_close(my_handle);
return ESP_OK;
}
/* Save new run time value in NVS
by first reading a table of previously saved values
and then adding the new value at the end of the table.
Return an error if anything goes wrong
during this process.
*/
esp_err_t save_run_time(void)
{
nvs_handle my_handle;
esp_err_t err;
// Open
err = nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &my_handle);
if (err != ESP_OK) return err;
// Read the size of memory space required for blob
size_t required_size = 0; // value will default to 0, if not set yet in NVS
err = nvs_get_blob(my_handle, "run_time", NULL, &required_size);
if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err;
// Read previously saved blob if available
uint32_t* run_time = malloc(required_size + sizeof(uint32_t));
if (required_size > 0) {
err = nvs_get_blob(my_handle, "run_time", run_time, &required_size);
if (err != ESP_OK) return err;
}
// Write value including previously saved blob if available
required_size += sizeof(uint32_t);
run_time[required_size / sizeof(uint32_t) - 1] = xTaskGetTickCount() * portTICK_PERIOD_MS;
err = nvs_set_blob(my_handle, "run_time", run_time, required_size);
if (err != ESP_OK) return err;
free(run_time);
// Commit
err = nvs_commit(my_handle);
if (err != ESP_OK) return err;
// Close
nvs_close(my_handle);
return ESP_OK;
}
/* Read from NVS and print restart counter
and the table with run times.
Return an error if anything goes wrong
during this process.
*/
esp_err_t print_what_saved(void)
{
nvs_handle my_handle;
esp_err_t err;
// Open
err = nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &my_handle);
if (err != ESP_OK) return err;
// Read restart counter
int32_t restart_counter = 0; // value will default to 0, if not set yet in NVS
err = nvs_get_i32(my_handle, "restart_conter", &restart_counter);
if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err;
printf("Restart counter = %d\n", restart_counter);
// Read run time blob
size_t required_size = 0; // value will default to 0, if not set yet in NVS
// obtain required memory space to store blob being read from NVS
err = nvs_get_blob(my_handle, "run_time", NULL, &required_size);
if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err;
printf("Run time:\n");
if (required_size == 0) {
printf("Nothing saved yet!\n");
} else {
uint32_t* run_time = malloc(required_size);
err = nvs_get_blob(my_handle, "run_time", run_time, &required_size);
if (err != ESP_OK) return err;
for (int i = 0; i < required_size / sizeof(uint32_t); i++) {
printf("%d: %d\n", i + 1, run_time[i]);
}
free(run_time);
}
// Close
nvs_close(my_handle);
return ESP_OK;
}
void app_main()
{
nvs_flash_init();
esp_err_t err;
err = print_what_saved();
if (err != ESP_OK) printf("Error (%d) reading data from NVS!\n", err);
err = save_restart_counter();
if (err != ESP_OK) printf("Error (%d) saving restart counter to NVS!\n", err);
gpio_pad_select_gpio(GPIO_NUM_0);
gpio_set_direction(GPIO_NUM_0, GPIO_MODE_DEF_INPUT);
/* Read the status of GPIO0. If GPIO0 is LOW for longer than 1000 ms,
then save module's run time and restart it
*/
while (1) {
if (gpio_get_level(GPIO_NUM_0) == 0) {
vTaskDelay(1000 / portTICK_RATE_MS);
if(gpio_get_level(GPIO_NUM_0) == 0) {
err = save_run_time();
if (err != ESP_OK) printf("Error (%d) saving run time blob to NVS!\n", err);
printf("Restarting...\n");
fflush(stdout);
system_restart();
}
}
vTaskDelay(200 / portTICK_RATE_MS);
}
}

View file

@ -26,7 +26,7 @@ If you're looking for a more bare-bones project to start from, try [esp-idf-temp
If you have a new example you think we'd like, please consider sending it to us as a Pull Request.
Please read the esp-idf CONTRIBUTING.md file which lays out general contribution rules.
Please read the esp-idf CONTRIBUTING.rst file which lays out general contribution rules.
In addition, here are some tips for creating good examples:

View file

@ -56,8 +56,26 @@ endef
# convenience variable for printing an 80 asterisk wide separator line
SEPARATOR:="*******************************************************************************"
# macro to remove quotes from an argument, ie $(call dequote (CONFIG_BLAH))
# macro to remove quotes from an argument, ie $(call dequote,$(CONFIG_BLAH))
define dequote
$(subst ",,$(1))
endef
# " comment kept here to keep syntax highlighting happy
# macro to keep an absolute path as-is, but resolve a relative path
# against a particular parent directory
#
# $(1) path to resolve
# $(2) directory to resolve non-absolute path against
#
# Path and directory don't have to exist (definition of a "relative
# path" is one that doesn't start with /)
#
# $(2) can contain a trailing forward slash or not, result will not
# double any path slashes.
#
# example $(call resolvepath,$(CONFIG_PATH),$(CONFIG_DIR))
define resolvepath
$(if $(filter /%,$(1)),$(1),$(subst //,/,$(2)/$(1)))
endef

View file

@ -41,6 +41,9 @@ COMPONENT_LIBRARY = lib$(COMPONENT_NAME).a
# Source dirs a component has. Default to root directory of component.
COMPONENT_SRCDIRS = .
#Names of binary files to embed as symbols in the component library
COMPONENT_EMBED_FILES ?=
# By default, include only the include/ dir.
COMPONENT_ADD_INCLUDEDIRS = include
COMPONENT_ADD_LDFLAGS = -l$(COMPONENT_NAME)
@ -127,7 +130,7 @@ build: $(COMPONENT_LIBRARY)
# Build the archive. We remove the archive first, otherwise ar will get confused if we update
# an archive when multiple filenames have the same name (src1/test.o and src2/test.o)
$(COMPONENT_LIBRARY): $(COMPONENT_OBJS)
$(COMPONENT_LIBRARY): $(COMPONENT_OBJS) $(COMPONENT_EMBED_OBJS)
$(summary) AR $@
rm -f $@
$(AR) cru $@ $(COMPONENT_OBJS)
@ -135,7 +138,7 @@ endif
# If COMPONENT_OWNCLEANTARGET is not set, define a phony clean target
ifndef COMPONENT_OWNCLEANTARGET
CLEAN_FILES = $(COMPONENT_LIBRARY) $(COMPONENT_OBJS) $(COMPONENT_OBJS:.o=.d) $(COMPONENT_EXTRA_CLEAN) component_project_vars.mk
CLEAN_FILES = $(COMPONENT_LIBRARY) $(COMPONENT_OBJS) $(COMPONENT_OBJS:.o=.d) $(COMPONENT_EMBED_OBJS) $(COMPONENT_EXTRA_CLEAN) component_project_vars.mk
.PHONY: clean
clean:
$(summary) RM $(CLEAN_FILES)
@ -167,3 +170,34 @@ endef
# Generate all the compile target patterns
$(foreach srcdir,$(COMPONENT_SRCDIRS), $(eval $(call GenerateCompileTargets,$(srcdir))))
## Support for embedding binary files into the ELF as symbols
OBJCOPY_EMBED_ARGS := --input binary --output elf32-xtensa-le --binary-architecture xtensa --rename-section .data=.rodata.embedded
# Generate pattern for embedding text or binary files into the app
# $(1) is name of file (as relative path inside component)
# $(2) is txt or bin depending on file contents
#
# txt files are null-terminated before being embedded (otherwise
# identical behaviour.)
#
# Files are temporarily copied to the build directory before objcopy,
# because objcopy generates the symbol name from the full command line
# path to the input file.
define GenerateEmbedTarget
$(1).$(2).o: $(call resolvepath,$(1),$(COMPONENT_PATH)) | $$(dir $(1))
$$(summary) EMBED $$@
$$(Q) $(if $(filter-out $$(notdir $$(abspath $$<)),$$(abspath $$(notdir $$<))), cp $$< $$(notdir $$<) ) # copy input file to build dir, unless already in build dir
$$(Q) $(if $(subst bin,,$(2)),echo -ne '\0' >> $$(notdir $$<) ) # trailing NUL byte on text output
$$(Q) $$(OBJCOPY) $(OBJCOPY_EMBED_ARGS) $$(notdir $$<) $$@
$$(Q) rm $$(notdir $$<)
endef
# generate targets to embed binary & text files
$(foreach binfile,$(COMPONENT_EMBED_FILES), $(eval $(call GenerateEmbedTarget,$(binfile),bin)))
$(foreach txtfile,$(COMPONENT_EMBED_TXTFILES), $(eval $(call GenerateEmbedTarget,$(txtfile),txt)))
# generate targets to create binary embed directories
$(foreach bindir,$(sort $(dir $(COMPONENT_EMBED_FILES))), $(eval $(call GenerateBuildDirTarget,$(bindir))))

View file

@ -11,13 +11,13 @@
#
.PHONY: build-components menuconfig defconfig all build clean all_binaries
all: all_binaries # other components will add dependencies to 'all_binaries'
@echo "To flash all build output, run 'make flash' or:"
@echo $(ESPTOOLPY_WRITE_FLASH) $(ESPTOOL_ALL_FLASH_ARGS)
# (the reason all_binaries is used instead of 'all' is so that the flash target
# can build everything without triggering the per-component "to flash..."
# output targets.)
all: all_binaries
# see below for recipe of 'all' target
#
# # other components will add dependencies to 'all_binaries'. The
# reason all_binaries is used instead of 'all' is so that the flash
# target can build everything without triggering the per-component "to
# flash..." output targets.)
help:
@echo "Welcome to Espressif IDF build system. Some useful make targets:"
@ -123,6 +123,15 @@ export COMPONENT_INCLUDES
# Set variables common to both project & component
include $(IDF_PATH)/make/common.mk
all:
ifdef CONFIG_SECURE_BOOTLOADER_ENABLED
@echo "(Secure boot enabled, so bootloader not flashed automatically. See 'make bootloader' output)"
@echo "To flash app & partition table, run 'make flash' or:"
else
@echo "To flash all build output, run 'make flash' or:"
endif
@echo $(ESPTOOLPY_WRITE_FLASH) $(ESPTOOL_ALL_FLASH_ARGS)
# Set default LDFLAGS
LDFLAGS ?= -nostdlib \
@ -130,6 +139,7 @@ LDFLAGS ?= -nostdlib \
-L$(IDF_PATH)/ld \
$(addprefix -L$(BUILD_DIR_BASE)/,$(COMPONENTS) $(SRCDIRS)) \
-u call_user_start_cpu0 \
$(EXTRA_LDFLAGS) \
-Wl,--gc-sections \
-Wl,-static \
-Wl,--start-group \
@ -313,6 +323,9 @@ app-clean: $(addsuffix -clean,$(notdir $(COMPONENT_PATHS_BUILDABLE)))
$(summary) RM $(APP_ELF)
rm -f $(APP_ELF) $(APP_BIN) $(APP_MAP)
clean: app-clean
# NB: this ordering is deliberate (app-clean before config-clean),
# so config remains valid during all component clean targets
config-clean: app-clean
clean: config-clean

View file

@ -63,7 +63,6 @@ $(AUTO_CONF_REGEN_TARGET) $(BUILD_DIR_BASE)/include/sdkconfig.h: $(SDKCONFIG) $(
# sometimes you can get an infinite make loop on Windows where sdkconfig always gets regenerated newer
# than the target(!)
clean: config-clean
.PHONY: config-clean
config-clean:
$(summary RM CONFIG)