Merge branch 'master' into open-libmain

This commit is contained in:
Angus Gratton 2016-06-30 09:09:27 +10:00
commit d8bcb5d702
65 changed files with 3272 additions and 739 deletions

7
.gitmodules vendored
View file

@ -4,3 +4,10 @@
[submodule "extras/mbedtls/mbedtls"] [submodule "extras/mbedtls/mbedtls"]
path = extras/mbedtls/mbedtls path = extras/mbedtls/mbedtls
url = https://github.com/ARMmbed/mbedtls.git url = https://github.com/ARMmbed/mbedtls.git
[submodule "extras/jsmn/jsmn"]
path = extras/jsmn/jsmn
url = https://github.com/zserge/jsmn.git
[submodule "bootloader/rboot"]
path = bootloader/rboot
url = https://github.com/raburton/rboot.git

View file

@ -2,17 +2,15 @@ language: c
sudo: false sudo: false
env: env:
# Target commit for https://github.com/pfalcon/esp-open-sdk/ # Target commit for https://github.com/pfalcon/esp-open-sdk/
OPENSDK_COMMIT=9bd3aba OPENSDK_COMMIT=a48b12f
CROSS_ROOT="${HOME}/toolchain-${OPENSDK_COMMIT}" CROSS_ROOT="${HOME}/toolchain-${OPENSDK_COMMIT}"
CROSS_BINDIR="${CROSS_ROOT}/bin" CROSS_BINDIR="${CROSS_ROOT}/bin"
ESPTOOL2_COMMIT=ec0e2c7 CROSS="ccache xtensa-lx106-elf-"
ESPTOOL2_DIR="${HOME}/esptool2-${ESPTOOL2_COMMIT}" MAKE_CMD="make WARNINGS_AS_ERRORS=1 -C examples/ build-examples"
PATH=${PATH}:${CROSS_BINDIR}:${ESPTOOL2_DIR} PATH=${PATH}:${CROSS_BINDIR}
MAKE_CMD="make WARNINGS_AS_ERRORS=1 -C examples/ build-examples CROSS=\"ccache xtensa-lx106-elf-\""
cache: cache:
directories: directories:
- ${CROSS_ROOT} - ${CROSS_ROOT}
- ${ESPTOOL2_DIR}
addons: addons:
apt: apt:
packages: packages:
@ -34,31 +32,16 @@ addons:
- python-serial - python-serial
- sed - sed
- git - git
- vim-common
before_install: before_install:
# Install a toolchain using esp-open-sdk (parts we need for this are the GNU toolchain and libhal) - travis_wait 30 utils/travis_build/install_toolchain.sh
#
# Adds hack of "{$HAS_TC} || -Buildstep-" to avoid rebuilding toolchain if it's already
# installed from the cache. If this gets any more complex it should be spun out to a standalone shell script.
- export HAS_TC="test -d ${CROSS_BINDIR}"
- unset CC # Travis sets this due to "language: c", but it confuses autotools configure when cross-building
- ${HAS_TC} || git clone --recursive https://github.com/pfalcon/esp-open-sdk.git
- ${HAS_TC} || cd esp-open-sdk
- ${HAS_TC} || git reset --hard ${OPENSDK_COMMIT}
- ${HAS_TC} || git submodule update
- ${HAS_TC} || sed -i "s/2.69/2.68/" lx106-hal/configure.ac # this is a nasty hack as Ubuntu Precise only has autoconf 2.68 not 2.69...
- ${HAS_TC} || sed -r -i 's%TOOLCHAIN ?=.*%TOOLCHAIN=${CROSS_ROOT}%' Makefile
- ${HAS_TC} || make STANDALONE=n
- export HAS_ET2="test -f ${ESPTOOL2_DIR}/esptool2"
- ${HAS_ET2} || git clone https://github.com/raburton/esptool2 ${ESPTOOL2_DIR}
- ${HAS_ET2} || cd ${ESPTOOL2_DIR}
- ${HAS_ET2} || git reset --hard ${ESPTOOL2_COMMIT}
- ${HAS_ET2} || make
script: script:
- cd ${TRAVIS_BUILD_DIR} - cd ${TRAVIS_BUILD_DIR}
# Remove ssid_config requirement for examples # Remove ssid_config requirement for examples
- sed -i "s%#error%//#error%" include/ssid_config.h - sed -i "s%#error%//#error%" include/ssid_config.h
# Try building everything w/ non-verbose output (to avoid 4MB log limit), but if it fails then # Don't verbose-build all examples (too much output), only verbose-build errors
# rerun with V=1 so the error part is output verbosely - ( ${MAKE_CMD} ) || ( ${MAKE_CMD} V=1 )
- ${MAKE_CMD} || ( ${MAKE_CMD} V=1 ) # build bootloader
- make -C bootloader/

View file

@ -73,6 +73,7 @@
#include <malloc.h> #include <malloc.h>
#include <unistd.h> #include <unistd.h>
#include <stdio.h> #include <stdio.h>
#include <xtensa_ops.h>
#include "FreeRTOS.h" #include "FreeRTOS.h"
#include "task.h" #include "task.h"
@ -81,13 +82,14 @@
unsigned cpu_sr; unsigned cpu_sr;
char level1_int_disabled; char level1_int_disabled;
/* Supervisor stack pointer entry. This is the "high water mark" of how far the /* Supervisor stack pointer entry. This is the "high water mark" of
supervisor stack grew down before task started. how far the supervisor stack grew down before task started. Is zero
before the scheduler starts.
After tasks start, task stacks are all allocated from the heap and After the scheduler starts, task stacks are all allocated from the
FreeRTOS checks for stack overflow. heap and FreeRTOS checks for stack overflow.
*/ */
static uint32_t xPortSupervisorStackPointer; void *xPortSupervisorStackPointer;
/* /*
* Stack initialization * Stack initialization
@ -218,9 +220,9 @@ size_t xPortGetFreeHeapSize( void )
struct mallinfo mi = mallinfo(); struct mallinfo mi = mallinfo();
uint32_t brk_val = (uint32_t) sbrk(0); uint32_t brk_val = (uint32_t) sbrk(0);
uint32_t sp = xPortSupervisorStackPointer; intptr_t sp = (intptr_t)xPortSupervisorStackPointer;
if(sp == 0) /* scheduler not started */ if(sp == 0) /* scheduler not started */
__asm__ __volatile__ ("mov %0, a1\n" : "=a"(sp)); SP(sp);
return sp - brk_val + mi.fordblks; return sp - brk_val + mi.fordblks;
} }

59
bootloader/Makefile Normal file
View file

@ -0,0 +1,59 @@
# This is a wrapper around the rboot makefile, which gives us the parameters
# we need to use rboot with esp-open-rtos.
#
# Use 'make bootloader' to build a custom bootloader.
#
# 'make flash' for any esp-open-rtos program will use the compiled
# bootloader if it exists, or a prebuilt bootloader if no custom
# bootloader was compiled.
#
# The wrapper means we don't require esptool2 in the build process, so we can just use
# esptool.py (still need xxd, grep, sed to generate the header - see below.)
BOOTLOADER_DIR:=$(dir $(abspath $(lastword $(MAKEFILE_LIST))))
include ../parameters.mk
all: $(FIRMWARE_DIR)/rboot.bin
rboot/Makefile:
$(error rboot git submodule is not checkedo out. Try running 'git submodule update --init --recursive')
$(FIRMWARE_DIR)/rboot.bin: $(BUILD_DIR)/rboot.elf $(FIRMWARE_DIR)
@echo "FW rboot.bin"
$(Q) $(ESPTOOL) elf2image $(ESPTOOL_ARGS) $< -o $(BUILD_DIR)/
$(Q) mv $(BUILD_DIR)/0x00000.bin $@
# rboot generates this header using the 'esptool2 -header' option. To try and avoid
# esptool2 as a dependency, we try it here using grep, sed, xxd (all fairly common Unix tools)
$(BUILD_DIR)/rboot-hex2a.h: $(BUILD_DIR)/rboot-stage2a.elf $(BUILD_DIR)
@echo "Extracting stub image header..."
$(Q) xtensa-lx106-elf-objcopy $< --only-section .text -Obinary $(BUILD_DIR)/rboot-hex2a.bin
$(Q) xxd -i $(BUILD_DIR)/rboot-hex2a.bin > $@.in
$(Q) sed -i "s/unsigned char .\+\[\]/const uint8 _text_data[]/" $@.in
$(Q) sed -i "s/unsigned int .\+_len/const uint32 _text_len/" $@.in
$(Q) echo "const uint32 entry_addr = $$(xtensa-lx106-elf-objdump -f $< | grep 'start address' | grep -o '0x.\+');" >> $@.in
$(Q) echo "const uint32 _text_addr = 0x$$(xtensa-lx106-elf-objdump -h -j .text $< | grep ".text" | grep -o '401.....' | head -n1);" >> $@.in
$(Q) mv $@.in $@
RBOOT_BUILD_BASE="$(abspath $(BUILD_DIR))"
RBOOT_FW_BASE="$(abspath $(FIRMWARE_DIR))"
MAKE_VARS=RBOOT_EXTRA_INCDIR=$(BOOTLOADER_DIR) RBOOT_BUILD_BASE=$(RBOOT_BUILD_BASE) RBOOT_FW_BASE=$(RBOOT_FW_BASE)
$(BUILD_DIR)/rboot-stage2a.elf: $(BUILD_DIR)
$(Q) $(MAKE) -C rboot $(RBOOT_BUILD_BASE)/rboot-stage2a.elf $(MAKE_VARS)
$(BUILD_DIR)/rboot.elf: $(BUILD_DIR)/rboot-hex2a.h
$(Q) $(MAKE) -C rboot $(RBOOT_BUILD_BASE)/rboot.elf $(MAKE_VARS)
$(BUILD_DIR) $(FIRMWARE_DIR):
mkdir -p $@
flash: $(FIRMWARE_DIR)/rboot.bin
$(Q) $(ESPTOOL) -p $(ESPPORT) -b $(ESPBAUD) write_flash $(ESPTOOL_ARGS) 0x0 $<
clean:
$(Q) rm -rf $(BUILD_DIR)
$(Q) rm -rf $(FIRMWARE_DIR)
.PHONY: all

12
bootloader/README.md Normal file
View file

@ -0,0 +1,12 @@
OTA Bootloader (rboot) source module and support files.
rboot is an open source bootloader by Richard Burton:
https://github.com/raburton/rboot
Can be used to build an esp-open-rtos compatible rboot bootloader. Run 'make bootloader' in this directory to compile a new bootloader.
Compiling a new bootloader is optional, there's a prebuilt one in the "firmware_prebuilt" directory that will be used if no new bootloader was compiled.
It is also possible to use rboot from upstream verbatim, but *ensure that the `RBOOT_BIG_FLASH` option is enabled or images in slots other than 0 won't work correctly.
See the contents of the 'rboot' directory for more information on rboot.

Binary file not shown.

Binary file not shown.

1
bootloader/rboot Submodule

@ -0,0 +1 @@
Subproject commit 30afbaa777e00abf9d7d469fb3345f118c4975c1

23
bootloader/rboot.h Normal file
View file

@ -0,0 +1,23 @@
/* rboot header overrides
This "wrapper" header contains default values for building rboot
on/for esp-open-rtos. It gets included both when building the
bootloader and when building extras/rboot-ota support. It includes
the default bootloader/rboot/rboot.h header via the gcc
include_next mechanism.
*/
#ifndef __RBOOT_H__
// Big flash support is required for esp-open-rtos (we use 8Mbit
// "slots" only.)
#define BOOT_BIG_FLASH
// enable 2 way communication between
// rBoot and the user app via the esp rtc data area
#define BOOT_RTC_ENABLED
// Call 'main' rboot.h to pick up defaults for other parameters
#include_next "rboot.h"
#endif

187
common.mk
View file

@ -20,156 +20,10 @@
# assume the 'root' directory (ie top of the tree) is the directory common.mk is in # assume the 'root' directory (ie top of the tree) is the directory common.mk is in
ROOT := $(dir $(lastword $(MAKEFILE_LIST))) ROOT := $(dir $(lastword $(MAKEFILE_LIST)))
# include optional local overrides at the root level, then in program directory include $(ROOT)parameters.mk
#
# Create either of these files for local system overrides if possible,
# instead of editing this makefile directly.
-include $(ROOT)local.mk
-include local.mk
# Flash size in megabits
# Valid values are same as for esptool.py - 2,4,8,16,32
FLASH_SIZE ?= 16
# Flash mode, valid values are same as for esptool.py - qio,qout,dio.dout
FLASH_MODE ?= qio
# Flash speed in MHz, valid values are same as for esptool.py - 80, 40, 26, 20
FLASH_SPEED ?= 40
# Output directories to store intermediate compiled files
# relative to the program directory
BUILD_DIR ?= $(PROGRAM_DIR)build/
FIRMWARE_DIR ?= $(PROGRAM_DIR)firmware/
# esptool.py from https://github.com/themadinventor/esptool
ESPTOOL ?= esptool.py
# serial port settings for esptool.py
ESPPORT ?= /dev/ttyUSB0
ESPBAUD ?= 115200
# set this to 0 if you don't need floating point support in printf/scanf
# this will save approx 14.5KB flash space and 448 bytes of statically allocated
# data RAM
#
# NB: Setting the value to 0 requires a recent esptool.py (Feb 2016 / commit ebf02c9)
PRINTF_SCANF_FLOAT_SUPPORT ?= 1
# Set OTA to 1 to build an image that supports rBoot OTA bootloader
#
# Currently only works with 16mbit or more flash sizes, with 8mbit
# images for each "slot"
OTA ?= 0
ifeq ($(OTA),1)
# for OTA, we build a "SDK v1.2 bootloader" compatible image where everything is in
# one file (should work with the v1.2 binary bootloader, and the FOSS rBoot bootloader).
IMGTOOL ?= esptool2
# Tell C preprocessor that we're building for OTA
CPPFLAGS = -DOTA
endif
FLAVOR ?= release # or debug
# Compiler names, etc. assume gdb
CROSS ?= xtensa-lx106-elf-
# Path to the filteroutput.py tool
FILTEROUTPUT ?= $(ROOT)/utils/filteroutput.py
AR = $(CROSS)ar
CC = $(CROSS)gcc
CPP = $(CROSS)cpp
LD = $(CROSS)gcc
NM = $(CROSS)nm
C++ = $(CROSS)g++
SIZE = $(CROSS)size
OBJCOPY = $(CROSS)objcopy
OBJDUMP = $(CROSS)objdump
# Source components to compile and link. Each of these are subdirectories
# of the root, with a 'component.mk' file.
COMPONENTS ?= $(EXTRA_COMPONENTS) FreeRTOS lwip core open_esplibs
# binary esp-iot-rtos SDK libraries to link. These are pre-processed prior to linking.
SDK_LIBS ?= main net80211 phy pp wpa
# open source libraries linked in
LIBS ?= hal gcc c
# set to 0 if you want to use the toolchain libc instead of esp-open-rtos newlib
OWN_LIBC ?= 1
# Note: you will need a recent esp
ENTRY_SYMBOL ?= call_user_start
# Set this to zero if you don't want individual function & data sections
# (some code may be slightly slower, linking will be slighty slower,
# but compiled code size will come down a small amount.)
SPLIT_SECTIONS ?= 1
# Set this to 1 to have all compiler warnings treated as errors (and stop the
# build). This is recommended whenever you are working on code which will be
# submitted back to the main project, as all submitted code will be expected to
# compile without warnings to be accepted.
WARNINGS_AS_ERRORS ?= 0
# Common flags for both C & C++_
C_CXX_FLAGS ?= -Wall -Wl,-EL -nostdlib $(EXTRA_C_CXX_FLAGS)
# Flags for C only
CFLAGS ?= $(C_CXX_FLAGS) -std=gnu99 $(EXTRA_CFLAGS)
# Flags for C++ only
CXXFLAGS ?= $(C_CXX_FLAGS) -fno-exceptions -fno-rtti $(EXTRA_CXXFLAGS)
# these aren't technically preprocesor args, but used by all 3 of C, C++, assembler
CPPFLAGS += -mlongcalls -mtext-section-literals
LDFLAGS = -nostdlib -Wl,--no-check-sections -L$(BUILD_DIR)sdklib -L$(ROOT)lib -u $(ENTRY_SYMBOL) -Wl,-static -Wl,-Map=$(BUILD_DIR)$(PROGRAM).map $(EXTRA_LDFLAGS)
ifeq ($(WARNINGS_AS_ERRORS),1)
C_CXX_FLAGS += -Werror
endif
ifeq ($(SPLIT_SECTIONS),1)
C_CXX_FLAGS += -ffunction-sections -fdata-sections
LDFLAGS += -Wl,-gc-sections
endif
ifeq ($(FLAVOR),debug)
C_CXX_FLAGS += -g -O0
LDFLAGS += -g -O0
else ifeq ($(FLAVOR),sdklike)
# These are flags intended to produce object code as similar as possible to
# the output of the compiler used to build the SDK libs (for comparison of
# disassemblies when coding replacement routines). It is not normally
# intended to be used otherwise.
CFLAGS += -O2 -Os -fno-inline -fno-ipa-cp -fno-toplevel-reorder -fno-caller-saves -fconserve-stack
LDFLAGS += -O2
else
C_CXX_FLAGS += -g -O2
LDFLAGS += -g -O2
endif
GITSHORTREV=\"$(shell cd $(ROOT); git rev-parse --short -q HEAD 2> /dev/null)\"
ifeq ($(GITSHORTREV),\"\")
GITSHORTREV="\"(nogit)\"" # (same length as a short git hash)
endif
CPPFLAGS += -DGITSHORTREV=$(GITSHORTREV)
ifeq ($(OTA),0)
LINKER_SCRIPTS = $(ROOT)ld/nonota.ld
else
LINKER_SCRIPTS = $(ROOT)ld/ota.ld
endif
LINKER_SCRIPTS += $(ROOT)ld/common.ld $(ROOT)ld/rom.ld
####
#### no user configurable options below here
####
ifndef PROGRAM ifndef PROGRAM
$(error "Set the PROGRAM environment variable in your Makefile before including common.mk" $(error "Set the PROGRAM environment variable in your Makefile before including common.mk")
endif endif
# hacky way to get a single space value # hacky way to get a single space value
@ -188,28 +42,7 @@ LIB_ARGS = $(addprefix -l,$(LIBS))
PROGRAM_OUT = $(BUILD_DIR)$(PROGRAM).out PROGRAM_OUT = $(BUILD_DIR)$(PROGRAM).out
LDFLAGS += $(addprefix -T,$(LINKER_SCRIPTS)) LDFLAGS += $(addprefix -T,$(LINKER_SCRIPTS))
ifeq ($(OTA),0)
# for non-OTA, we create two different files for uploading into the flash
# these are the names and options to generate them
FW_ADDR_1 = 0x00000
FW_ADDR_2 = 0x20000
FW_FILE_1 = $(addprefix $(FIRMWARE_DIR),$(FW_ADDR_1).bin)
FW_FILE_2 = $(addprefix $(FIRMWARE_DIR),$(FW_ADDR_2).bin)
else
# for OTA, it's a single monolithic image
FW_FILE = $(addprefix $(FIRMWARE_DIR),$(PROGRAM).bin) FW_FILE = $(addprefix $(FIRMWARE_DIR),$(PROGRAM).bin)
endif
# firmware tool arguments
ESPTOOL_ARGS=-fs $(FLASH_SIZE)m -fm $(FLASH_MODE) -ff $(FLASH_SPEED)m
IMGTOOL_FLASH_SIZE_2=256
IMGTOOL_FLASH_SIZE_4=512
IMGTOOL_FLASH_SIZE_8=1024
IMGTOOL_FLASH_SIZE_16=2048
IMGTOOL_FLASH_SIZE_32=4096
IMGTOOL_FLASH_SIZE=$(value IMGTOOL_FLASH_SIZE_$(FLASH_SIZE))
IMGTOOL_ARGS=-$(IMGTOOL_FLASH_SIZE) -$(FLASH_MODE) -$(FLASH_SPEED)
# Common include directories, shared across all "components" # Common include directories, shared across all "components"
# components will add their include directories to this argument # components will add their include directories to this argument
@ -235,7 +68,7 @@ Q := @
vecho := @echo vecho := @echo
endif endif
.PHONY: all clean debug_print .PHONY: all clean flash erase_flash
all: $(PROGRAM_OUT) $(FW_FILE_1) $(FW_FILE_2) $(FW_FILE) all: $(PROGRAM_OUT) $(FW_FILE_1) $(FW_FILE_2) $(FW_FILE)
@ -373,16 +206,14 @@ $(FW_FILE_1) $(FW_FILE_2): $(PROGRAM_OUT) $(FIRMWARE_DIR)
$(Q) $(ESPTOOL) elf2image $(ESPTOOL_ARGS) $< -o $(FIRMWARE_DIR) $(Q) $(ESPTOOL) elf2image $(ESPTOOL_ARGS) $< -o $(FIRMWARE_DIR)
$(FW_FILE): $(PROGRAM_OUT) $(FIRMWARE_DIR) $(FW_FILE): $(PROGRAM_OUT) $(FIRMWARE_DIR)
$(Q) $(IMGTOOL) $(IMGTOOL_ARGS) -bin -boot2 $(PROGRAM_OUT) $(FW_FILE) .text .data .rodata $(vecho) "FW $@"
$(Q) $(ESPTOOL) elf2image --version=2 $(ESPTOOL_ARGS) $< -o $(FW_FILE)
ifeq ($(OTA),0)
flash: $(FW_FILE_1) $(FW_FILE_2)
$(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) write_flash $(ESPTOOL_ARGS) $(FW_ADDR_2) $(FW_FILE_2) $(FW_ADDR_1) $(FW_FILE_1)
else
flash: $(FW_FILE) flash: $(FW_FILE)
$(vecho) "Flashing OTA image slot 0 (bootloader not updated)" $(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) write_flash $(ESPTOOL_ARGS) 0x0 $(RBOOT_BIN) 0x1000 $(RBOOT_CONF) 0x2000 $(FW_FILE)
$(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) write_flash $(ESPTOOL_ARGS) 0x2000 $(FW_FILE)
endif erase_flash:
$(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) erase_flash
size: $(PROGRAM_OUT) size: $(PROGRAM_OUT)
$(Q) $(CROSS)size --format=sysv $(PROGRAM_OUT) $(Q) $(CROSS)size --format=sysv $(PROGRAM_OUT)

View file

@ -24,6 +24,7 @@
#include "os_version.h" #include "os_version.h"
#include "espressif/esp_common.h" #include "espressif/esp_common.h"
#include "espressif/phy_info.h"
#include "sdk_internal.h" #include "sdk_internal.h"
#include "esplibs/libmain.h" #include "esplibs/libmain.h"
@ -32,8 +33,6 @@
void user_init(void); void user_init(void);
#define BOOT_INFO_SIZE 28 #define BOOT_INFO_SIZE 28
//TODO: phy_info should probably be a struct (no idea about its organization, though)
#define PHY_INFO_SIZE 128
// These are the offsets of these values within the RTCMEM regions. It appears // These are the offsets of these values within the RTCMEM regions. It appears
// that the ROM saves them to RTCMEM before calling us, and we pull them out of // that the ROM saves them to RTCMEM before calling us, and we pull them out of
@ -44,26 +43,6 @@ void user_init(void);
extern uint32_t _bss_start; extern uint32_t _bss_start;
extern uint32_t _bss_end; extern uint32_t _bss_end;
// .Ldata003 -- .irom.text+0x0
static const uint8_t IROM default_phy_info[PHY_INFO_SIZE] = {
0x05, 0x00, 0x04, 0x02, 0x05, 0x05, 0x05, 0x02,
0x05, 0x00, 0x04, 0x05, 0x05, 0x04, 0x05, 0x05,
0x04, 0xfe, 0xfd, 0xff, 0xf0, 0xf0, 0xf0, 0xe0,
0xe0, 0xe0, 0xe1, 0x0a, 0xff, 0xff, 0xf8, 0x00,
0xf8, 0xf8, 0x52, 0x4e, 0x4a, 0x44, 0x40, 0x38,
0x00, 0x00, 0x01, 0x01, 0x02, 0x03, 0x04, 0x05,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xe1, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x93, 0x43, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
// user_init_flag -- .bss+0x0 // user_init_flag -- .bss+0x0
uint8_t sdk_user_init_flag; uint8_t sdk_user_init_flag;
@ -81,7 +60,7 @@ xTaskHandle sdk_xWatchDogTaskHandle;
static void IRAM get_otp_mac_address(uint8_t *buf); static void IRAM get_otp_mac_address(uint8_t *buf);
static void IRAM set_spi0_divisor(uint32_t divisor); static void IRAM set_spi0_divisor(uint32_t divisor);
static void zero_bss(void); static void zero_bss(void);
static void init_networking(uint8_t *phy_info, uint8_t *mac_addr); static void init_networking(sdk_phy_info_t *phy_info, uint8_t *mac_addr);
static void init_g_ic(void); static void init_g_ic(void);
static void user_start_phase2(void); static void user_start_phase2(void);
static void dump_flash_sector(uint32_t start_sector, uint32_t length); static void dump_flash_sector(uint32_t start_sector, uint32_t length);
@ -254,7 +233,7 @@ static void zero_bss(void) {
} }
// .Lfunc006 -- .irom0.text+0x70 // .Lfunc006 -- .irom0.text+0x70
static void init_networking(uint8_t *phy_info, uint8_t *mac_addr) { static void init_networking(sdk_phy_info_t *phy_info, uint8_t *mac_addr) {
if (sdk_register_chipv6_phy(phy_info)) { if (sdk_register_chipv6_phy(phy_info)) {
printf("FATAL: sdk_register_chipv6_phy failed"); printf("FATAL: sdk_register_chipv6_phy failed");
abort(); abort();
@ -361,9 +340,9 @@ extern void (*__init_array_start)(void);
extern void (*__init_array_end)(void); extern void (*__init_array_end)(void);
// .Lfunc009 -- .irom0.text+0x5b4 // .Lfunc009 -- .irom0.text+0x5b4
static void user_start_phase2(void) { static __attribute__((noinline)) void user_start_phase2(void) {
uint8_t *buf; uint8_t *buf;
uint8_t *phy_info; sdk_phy_info_t phy_info, default_phy_info;
sdk_system_rtc_mem_read(0, &sdk_rst_if, sizeof(sdk_rst_if)); sdk_system_rtc_mem_read(0, &sdk_rst_if, sizeof(sdk_rst_if));
if (sdk_rst_if.reason > 3) { if (sdk_rst_if.reason > 3) {
@ -381,8 +360,16 @@ static void user_start_phase2(void) {
sdk_info._unknown4 = 0x00ffffff; sdk_info._unknown4 = 0x00ffffff;
sdk_info._unknown8 = 0x0104a8c0; sdk_info._unknown8 = 0x0104a8c0;
init_g_ic(); init_g_ic();
phy_info = malloc(PHY_INFO_SIZE);
sdk_spi_flash_read(sdk_flashchip.chip_size - sdk_flashchip.sector_size * 4, (uint32_t *)phy_info, PHY_INFO_SIZE); read_saved_phy_info(&phy_info);
get_default_phy_info(&default_phy_info);
if (phy_info.version != default_phy_info.version) {
/* Versions don't match, use default for PHY info
(may be a blank config sector, or a new default version.)
*/
memcpy(&phy_info, &default_phy_info, sizeof(sdk_phy_info_t));
}
// Disable default buffering on stdout // Disable default buffering on stdout
setbuf(stdout, NULL); setbuf(stdout, NULL);
@ -390,13 +377,7 @@ static void user_start_phase2(void) {
uart_flush_txfifo(0); uart_flush_txfifo(0);
uart_flush_txfifo(1); uart_flush_txfifo(1);
if (phy_info[0] != 5) { init_networking(&phy_info, sdk_info.sta_mac_addr);
// Bad version byte. Discard what we read and use default values
// instead.
memcpy(phy_info, default_phy_info, PHY_INFO_SIZE);
}
init_networking(phy_info, sdk_info.sta_mac_addr);
free(phy_info);
// Call gcc constructor functions // Call gcc constructor functions
void (**ctor)(void); void (**ctor)(void);
@ -433,11 +414,11 @@ static void dump_flash_sector(uint32_t start_sector, uint32_t length) {
} }
// .Lfunc011 -- .irom0.text+0x790 // .Lfunc011 -- .irom0.text+0x790
static void dump_flash_config_sectors(uint32_t start_sector) { static __attribute__((noinline)) void dump_flash_config_sectors(uint32_t start_sector) {
printf("system param error\n"); printf("system param error\n");
// Note: original SDK code didn't dump PHY info // Note: original SDK code didn't dump PHY info
printf("phy_info:\n"); printf("phy_info:\n");
dump_flash_sector(start_sector, PHY_INFO_SIZE); dump_flash_sector(start_sector, sizeof(sdk_phy_info_t));
printf("\ng_ic saved 0:\n"); printf("\ng_ic saved 0:\n");
dump_flash_sector(start_sector + 1, sizeof(struct sdk_g_ic_saved_st)); dump_flash_sector(start_sector + 1, sizeof(struct sdk_g_ic_saved_st));
printf("\ng_ic saved 1:\n"); printf("\ng_ic saved 1:\n");

View file

@ -11,6 +11,8 @@
#include <stdio.h> #include <stdio.h>
#include <FreeRTOS.h> #include <FreeRTOS.h>
#include <task.h> #include <task.h>
#include <malloc.h>
#include <unistd.h>
#include "debug_dumps.h" #include "debug_dumps.h"
#include "common_macros.h" #include "common_macros.h"
@ -19,13 +21,19 @@
#include "esp/uart.h" #include "esp/uart.h"
#include "espressif/esp_common.h" #include "espressif/esp_common.h"
#include "sdk_internal.h" #include "sdk_internal.h"
#include "esplibs/libmain.h"
/* Forward declarations */ /* Forward declarations */
static void IRAM fatal_handler_prelude(void); static void IRAM fatal_handler_prelude(void);
/* Inner parts of crash handlers marked noinline to ensure they don't inline into IRAM. */
static void __attribute__((noinline)) __attribute__((noreturn)) fatal_exception_handler_inner(uint32_t *sp, bool registers_saved_on_stack); /* Inner parts of crash handlers */
typedef void __attribute__((noreturn)) (*fatal_exception_handler_fn)(uint32_t *sp, bool registers_saved_on_stack);
static void __attribute__((noreturn)) standard_fatal_exception_handler_inner(uint32_t *sp, bool registers_saved_on_stack);
static void __attribute__((noreturn)) second_fatal_exception_handler_inner(uint32_t *sp, bool registers_saved_on_stack);
static void __attribute__((noinline)) __attribute__((noreturn)) abort_handler_inner(uint32_t *caller, uint32_t *sp); static void __attribute__((noinline)) __attribute__((noreturn)) abort_handler_inner(uint32_t *caller, uint32_t *sp);
static IRAM_DATA fatal_exception_handler_fn fatal_exception_handler_inner = standard_fatal_exception_handler_inner;
/* fatal_exception_handler called from any unhandled user exception /* fatal_exception_handler called from any unhandled user exception
* *
* (similar to a hard fault on other processor architectures) * (similar to a hard fault on other processor architectures)
@ -36,7 +44,8 @@ static void __attribute__((noinline)) __attribute__((noreturn)) abort_handler_in
*/ */
void IRAM __attribute__((noreturn)) fatal_exception_handler(uint32_t *sp, bool registers_saved_on_stack) { void IRAM __attribute__((noreturn)) fatal_exception_handler(uint32_t *sp, bool registers_saved_on_stack) {
fatal_handler_prelude(); fatal_handler_prelude();
fatal_exception_handler_inner(sp, registers_saved_on_stack); fatal_exception_handler_fn inner_fn = fatal_exception_handler_inner;
inner_fn(sp, registers_saved_on_stack);
} }
/* Abort implementation /* Abort implementation
@ -130,6 +139,12 @@ void dump_registers_in_exception_handler(uint32_t *sp) {
printf("SAR %08x\n", saved[0x13]); printf("SAR %08x\n", saved[0x13]);
} }
static void __attribute__((noreturn)) post_crash_reset(void) {
uart_flush_txfifo(0);
uart_flush_txfifo(1);
sdk_system_restart_in_nmi();
while(1) {}
}
/* Prelude ensures exceptions/NMI off and flash is mapped, allowing /* Prelude ensures exceptions/NMI off and flash is mapped, allowing
calls to non-IRAM functions. calls to non-IRAM functions.
@ -148,7 +163,10 @@ static void IRAM fatal_handler_prelude(void) {
/* Main part of fatal exception handler, is run from flash to save /* Main part of fatal exception handler, is run from flash to save
some IRAM. some IRAM.
*/ */
static void fatal_exception_handler_inner(uint32_t *sp, bool registers_saved_on_stack) { static void standard_fatal_exception_handler_inner(uint32_t *sp, bool registers_saved_on_stack) {
/* Replace the fatal exception handler 'inner' function so we
don't end up in a crash loop if this handler crashes. */
fatal_exception_handler_inner = second_fatal_exception_handler_inner;
dump_excinfo(); dump_excinfo();
if (sp) { if (sp) {
if (registers_saved_on_stack) { if (registers_saved_on_stack) {
@ -156,10 +174,52 @@ static void fatal_exception_handler_inner(uint32_t *sp, bool registers_saved_on_
} }
dump_stack(sp); dump_stack(sp);
} }
uart_flush_txfifo(0); dump_heapinfo();
uart_flush_txfifo(1); post_crash_reset();
sdk_system_restart_in_nmi(); }
while(1) {}
/* This is the exception handler that gets called if a crash occurs inside the standard handler,
so we don't end up in a crash loop. It doesn't rely on contents of stack or heap.
*/
static void second_fatal_exception_handler_inner(uint32_t *sp, bool registers_saved_on_stack) {
dump_excinfo();
printf("Second fatal exception occured inside fatal exception handler. Can't continue.\n");
post_crash_reset();
}
void dump_heapinfo(void)
{
extern char _heap_start;
extern uint32_t xPortSupervisorStackPointer;
struct mallinfo mi = mallinfo();
uint32_t brk_val = (uint32_t) sbrk(0);
uint32_t sp = xPortSupervisorStackPointer;
if(sp == 0) {
SP(sp);
}
/* Total free heap is all memory that could be allocated via
malloc (assuming fragmentation doesn't become a problem) */
printf("\nFree Heap: %d\n", sp - brk_val + mi.fordblks);
/* delta between brk & supervisor sp is the contiguous memory
region that is available to be put into heap space via
brk(). */
printf("_heap_start %p brk 0x%08x supervisor sp 0x%08x sp-brk %d bytes\n",
&_heap_start, brk_val, sp, sp-brk_val);
/* arena/fordblks/uordblks determines the amount of free space
inside the heap region already added via brk(). May be
fragmented.
The values in parentheses are the values used internally by
nano-mallocr.c, the field names outside parentheses are the
POSIX compliant field names of the mallinfo structure.
"arena" should be equal to brk-_heap_start ie total size available.
*/
printf("arena (total_size) %d fordblks (free_size) %d uordblocks (used_size) %d\n",
mi.arena, mi.fordblks, mi.uordblks);
} }
/* Main part of abort handler, can be run from flash to save some /* Main part of abort handler, can be run from flash to save some
@ -168,8 +228,6 @@ static void fatal_exception_handler_inner(uint32_t *sp, bool registers_saved_on_
static void abort_handler_inner(uint32_t *caller, uint32_t *sp) { static void abort_handler_inner(uint32_t *caller, uint32_t *sp) {
printf("abort() invoked at %p.\n", caller); printf("abort() invoked at %p.\n", caller);
dump_stack(sp); dump_stack(sp);
uart_flush_txfifo(0); dump_heapinfo();
uart_flush_txfifo(1); post_crash_reset();
sdk_system_restart_in_nmi();
while(1) {}
} }

32
core/esp_phy.c Normal file
View file

@ -0,0 +1,32 @@
/** esp/phy.h
*
* PHY hardware management functions.
*
* Part of esp-open-rtos
* Copyright (C) 2016 ChefSteps, Inc
* BSD Licensed as described in the file LICENSE
*/
#include <esp/phy.h>
#include <esp/gpio.h>
void bt_coexist_configure(bt_coexist_mode_t mode, uint8_t bt_active_pin, uint8_t bt_priority_pin)
{
/* Disable coexistence entirely before changing pin assignments */
GPIO.OUT &= ~(GPIO_OUT_BT_COEXIST_MASK);
uint32_t new_mask = 0;
new_mask = VAL2FIELD_M(GPIO_OUT_BT_ACTIVE_PIN, bt_active_pin) +
VAL2FIELD_M(GPIO_OUT_BT_PRIORITY_PIN, bt_priority_pin);
if(mode == BT_COEXIST_USE_BT_ACTIVE || mode == BT_COEXIST_USE_BT_ACTIVE_PRIORITY) {
gpio_enable(bt_active_pin, GPIO_INPUT);
new_mask |= GPIO_OUT_BT_ACTIVE_ENABLE;
}
if(mode == BT_COEXIST_USE_BT_ACTIVE_PRIORITY) {
gpio_enable(bt_priority_pin, GPIO_INPUT);
new_mask |= GPIO_OUT_BT_PRIORITY_ENABLE;
}
GPIO.OUT |= new_mask;
}

View file

@ -37,15 +37,71 @@
#define VAL2FIELD_M(fieldname, value) (((value) & fieldname##_M) << fieldname##_S) #define VAL2FIELD_M(fieldname, value) (((value) & fieldname##_M) << fieldname##_S)
#define SET_FIELD_M(regbits, fieldname, value) (((regbits) & ~FIELD_MASK(fieldname)) | VAL2FIELD_M(fieldname, value)) #define SET_FIELD_M(regbits, fieldname, value) (((regbits) & ~FIELD_MASK(fieldname)) | VAL2FIELD_M(fieldname, value))
/* Use this macro to store constant values in IROM flash instead /* Use the IRAM macro to place functions into Instruction RAM (IRAM)
of having them loaded into rodata (which resides in DRAM) instead of flash (aka irom).
Unlike the ESP8266 SDK you don't need an attribute like this for (This is the opposite to the Espressif SDK, where functions default
standard functions. They're stored in flash by default. But to being placed in IRAM but the ICACHE_FLASH_ATTR attribute will
variables need them. place them in flash.)
Important to note: IROM flash can only be accessed via 32-bit word Use the IRAM attribute for functions which are called when the
aligned reads. It's up to the user of this attribute to ensure this. flash may not be available (for example during NMI exceptions), or
for functions which are called very frequently and need high
performance.
Usage example:
void IRAM high_performance_function(void)
{
// do important thing here
}
Bear in mind IRAM is limited (32KB), compared to up to 1MB of flash.
*/
#define IRAM __attribute__((section(".iram1.text")))
/* Use the RAM macro to place constant data (rodata) into RAM (data
RAM) instead of the default placement in flash. This is useful for
constant data which needs high performance access.
Usage example:
const RAM uint8_t constants[] = { 1, 2, 3, 7 };
When placing string literals in RAM, they need to be declared with
the type "const char[]" not "const char *"
Usage example:
const RAM char hello_world[] = "Hello World";
*/
#define RAM __attribute__((section(".data")))
/* Use the IRAM_DATA macro to place data into Instruction RAM (IRAM)
instead of the default of flash (for constant data) or data RAM
(for non-constant data).
This may be useful to free up data RAM. However all data read from
any instruction space (either IRAM or Flash) must be 32-bit aligned
word reads. Reading unaligned data stored with IRAM_DATA will be
slower than reading data stored in RAM. You can't perform unaligned
writes to IRAM.
*/
#define IRAM_DATA __attribute__((section(".iram1.data")))
/* Use the IROM macro to store constant values in IROM flash. In
esp-open-rtos this is already the default location for most constant
data (rodata), so you don't need this attribute in 99% of cases.
The exceptions are to mark data in the core & freertos libraries,
where the default for constant data storage is RAM.
(Unlike the Espressif SDK you don't need to use an attribute like
ICACHE_FLASH_ATTR for functions, they go into flash by default.)
Important to note: IROM flash is accessed via 32-bit word aligned
reads. esp-open-rtos does some magic to "fix" unaligned reads, but
performance is reduced.
*/ */
#ifdef __cplusplus #ifdef __cplusplus
#define IROM __attribute__((section(".irom0.literal"))) #define IROM __attribute__((section(".irom0.literal")))
@ -53,30 +109,5 @@
#define IROM __attribute__((section(".irom0.literal"))) const #define IROM __attribute__((section(".irom0.literal"))) const
#endif #endif
/* Use this macro to place functions into Instruction RAM (IRAM)
instead of flash memory (IROM).
This is useful for functions which are called when the flash may
not be available (for example during NMI exceptions), or for
functions which are called very frequently and need high
performance.
Bear in mind IRAM is limited (32KB), compared to up to 1MB of flash.
*/
#define IRAM __attribute__((section(".iram1.text")))
/* Use this macro to place read-only data into Instruction RAM (IRAM)
instead of loaded into rodata which resides in DRAM.
This may be useful to free up data RAM. However all data read from
the instruction space must be 32-bit aligned word reads
(non-aligned reads will use an interrupt routine to "fix" them and
still work, but are very slow..
*/
#ifdef __cplusplus
#define IRAM_DATA __attribute__((section(".iram1.rodata")))
#else
#define IRAM_DATA __attribute__((section(".iram1.rodata"))) const
#endif
#endif #endif

View file

@ -10,9 +10,12 @@
#define _DEBUG_DUMPS_H #define _DEBUG_DUMPS_H
#include <stdint.h> #include <stdint.h>
/* Dump stack memory starting from stack pointer address sp. */ /* Dump stack memory to stdout, starting from stack pointer address sp. */
void dump_stack(uint32_t *sp); void dump_stack(uint32_t *sp);
/* Dump heap statistics to stdout */
void dump_heapinfo(void);
/* Called from exception_vectors.S when a fatal exception occurs. /* Called from exception_vectors.S when a fatal exception occurs.
Probably not useful to be called in other contexts. Probably not useful to be called in other contexts.

View file

@ -81,9 +81,9 @@ static inline void gpio_set_output_on_sleep(const uint8_t gpio_num, bool enabled
static inline void gpio_write(const uint8_t gpio_num, const bool set) static inline void gpio_write(const uint8_t gpio_num, const bool set)
{ {
if (set) if (set)
GPIO.OUT_SET = BIT(gpio_num); GPIO.OUT_SET = BIT(gpio_num) & GPIO_OUT_PIN_MASK;
else else
GPIO.OUT_CLEAR = BIT(gpio_num); GPIO.OUT_CLEAR = BIT(gpio_num) & GPIO_OUT_PIN_MASK;
} }
/* Toggle output of a pin /* Toggle output of a pin
@ -102,9 +102,9 @@ static inline void gpio_toggle(const uint8_t gpio_num)
task's pins, without needing to disable/enable interrupts. task's pins, without needing to disable/enable interrupts.
*/ */
if(GPIO.OUT & BIT(gpio_num)) if(GPIO.OUT & BIT(gpio_num))
GPIO.OUT_CLEAR = BIT(gpio_num); GPIO.OUT_CLEAR = BIT(gpio_num) & GPIO_OUT_PIN_MASK;
else else
GPIO.OUT_SET = BIT(gpio_num); GPIO.OUT_SET = BIT(gpio_num) & GPIO_OUT_PIN_MASK;
} }
/* Read input value of a GPIO pin. /* Read input value of a GPIO pin.

View file

@ -61,6 +61,21 @@ struct GPIO_REGS {
_Static_assert(sizeof(struct GPIO_REGS) == 0x74, "GPIO_REGS is the wrong size"); _Static_assert(sizeof(struct GPIO_REGS) == 0x74, "GPIO_REGS is the wrong size");
/* Details for additional OUT register fields */
/* Bottom 16 bits of GPIO.OUT are for GPIOs 0-15, but upper 16 bits
are used to configure the input signalling pins for Bluetooth
Coexistence config (see esp/phy.h for a wrapper function).
*/
#define GPIO_OUT_PIN_MASK 0x0000FFFF
#define GPIO_OUT_BT_COEXIST_MASK 0x03FF0000
#define GPIO_OUT_BT_ACTIVE_ENABLE BIT(24)
#define GPIO_OUT_BT_PRIORITY_ENABLE BIT(25)
#define GPIO_OUT_BT_ACTIVE_PIN_M 0x0F
#define GPIO_OUT_BT_ACTIVE_PIN_S 16
#define GPIO_OUT_BT_PRIORITY_PIN_M 0x0F
#define GPIO_OUT_BT_PRIORITY_PIN_S 20
/* Details for CONF[i] registers */ /* Details for CONF[i] registers */
/* GPIO.CONF[i] control the pin behavior for the corresponding GPIO in/output. /* GPIO.CONF[i] control the pin behavior for the corresponding GPIO in/output.

58
core/include/esp/phy.h Normal file
View file

@ -0,0 +1,58 @@
/** esp/phy.h
*
* PHY hardware management functions.
*
* These are not enough to configure the ESP8266 PHY by themselves
* (yet), but they provide utilities to modify the configuration set
* up via the SDK.
*
* Functions implemented here deal directly with the hardware, not the
* SDK software layers.
*
* Part of esp-open-rtos
* Copyright (C) 2016 ChefSteps, Inc
* BSD Licensed as described in the file LICENSE
*/
#ifndef _ESP_PHY_H
#define _ESP_PHY_H
#include <esp/types.h>
#include <common_macros.h>
#include <stdint.h>
typedef enum {
BT_COEXIST_NONE,
BT_COEXIST_USE_BT_ACTIVE,
BT_COEXIST_USE_BT_ACTIVE_PRIORITY,
} bt_coexist_mode_t;
/** Override the Bluetooth Coexistence BT_ACTIVE pin setting
taken from the phy_info structure.
This enables other pins to be used for Bluetooth Coexistence
signals (rather than just the two provided for by phy_info). (Note
that not that not all pins are confirmed to work, GPIO 0 is
confirmed not usable as the SDK configures it as the WLAN_ACTIVE
output - even if you change the pin mode the SDK will change it
back.)
To change pins and have coexistence work successfully the BT
coexistence feature must be enabled in the phy_info configuration
(get_default_phy_info()). You can then modify the initial
configuration by calling this function from your own user_init
function.
(Eventually we should be able to support enough PHY registers
to enable coexistence without SDK support at all, but not yet.)
This function will enable bt_active_pin & bt_priority_as GPIO
inputs, according to the mode parameter.
Remember that the default Bluetooth Coexistence pins will be
configured as GPIOs by the SDK already, so you may want to
reconfigure/re-iomux them after calling this function.
*/
void bt_coexist_configure(bt_coexist_mode_t mode, uint8_t bt_active_pin, uint8_t bt_priority_pin);
#endif

View file

@ -4,6 +4,7 @@
#include "espressif/esp_wifi.h" #include "espressif/esp_wifi.h"
#include "espressif/spi_flash.h" #include "espressif/spi_flash.h"
#include "etstimer.h" #include "etstimer.h"
#include "espressif/phy_info.h"
#include "lwip/netif.h" #include "lwip/netif.h"
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -224,7 +225,7 @@ void sdk_phy_enable_agc(void);
void sdk_pm_attach(void); void sdk_pm_attach(void);
void sdk_pp_attach(void); void sdk_pp_attach(void);
void sdk_pp_soft_wdt_init(void); void sdk_pp_soft_wdt_init(void);
int sdk_register_chipv6_phy(uint8_t *); int sdk_register_chipv6_phy(sdk_phy_info_t *);
void sdk_sleep_reset_analog_rtcreg_8266(void); void sdk_sleep_reset_analog_rtcreg_8266(void);
uint32_t sdk_system_get_checksum(uint8_t *, uint32_t); uint32_t sdk_system_get_checksum(uint8_t *, uint32_t);
void sdk_wDevEnableRx(void); void sdk_wDevEnableRx(void);

View file

@ -11,27 +11,12 @@
#ifndef _XTENSA_OPS_H #ifndef _XTENSA_OPS_H
#define _XTENSA_OPS_H #define _XTENSA_OPS_H
// GCC macros for reading, writing, and exchanging Xtensa processor special
// registers:
#define RSR(var, reg) asm volatile ("rsr %0, " #reg : "=r" (var))
#define WSR(var, reg) asm volatile ("wsr %0, " #reg : : "r" (var))
#define XSR(var, reg) asm volatile ("xsr %0, " #reg : "+r" (var))
// GCC macros for performing associated "*sync" opcodes
#define ISYNC() asm volatile ( "isync" )
#define RSYNC() asm volatile ( "rsync" )
#define ESYNC() asm volatile ( "esync" )
#define DSYNC() asm volatile ( "dsync" )
/* Read stack pointer to variable. /* Read stack pointer to variable.
* *
* Note that the compiler will push a stack frame (minimum 16 bytes) * Note that the compiler will push a stack frame (minimum 16 bytes)
* in the prelude of a C function that calls any other functions. * in the prelude of a C function that calls any other functions.
*/ */
#define SP(var) asm volatile ("mov %0, a1" : "=r" (var)); #define SP(var) asm volatile ("mov %0, a1" : "=r" (var))
/* Read the function return address to a variable. /* Read the function return address to a variable.
* *
@ -40,4 +25,17 @@
*/ */
#define RETADDR(var) asm volatile ("mov %0, a0" : "=r" (var)) #define RETADDR(var) asm volatile ("mov %0, a0" : "=r" (var))
/* GCC macros for reading, writing, and exchanging Xtensa processor special
* registers:
*/
#define RSR(var, reg) asm volatile ("rsr %0, " #reg : "=r" (var));
#define WSR(var, reg) asm volatile ("wsr %0, " #reg : : "r" (var));
#define XSR(var, reg) asm volatile ("xsr %0, " #reg : "+r" (var));
// GCC macros for performing associated "*sync" opcodes
#define ISYNC() asm volatile ( "isync" )
#define RSYNC() asm volatile ( "rsync" )
#define ESYNC() asm volatile ( "esync" )
#define DSYNC() asm volatile ( "dsync" )
#endif /* _XTENSA_OPS_H */ #endif /* _XTENSA_OPS_H */

View file

@ -9,9 +9,12 @@
#include <sys/errno.h> #include <sys/errno.h>
#include <espressif/sdk_private.h> #include <espressif/sdk_private.h>
#include <common_macros.h> #include <common_macros.h>
#include <xtensa_ops.h>
#include <esp/uart.h> #include <esp/uart.h>
#include <stdlib.h> #include <stdlib.h>
extern void *xPortSupervisorStackPointer;
IRAM caddr_t _sbrk_r (struct _reent *r, int incr) IRAM caddr_t _sbrk_r (struct _reent *r, int incr)
{ {
extern char _heap_start; /* linker script defined */ extern char _heap_start; /* linker script defined */
@ -21,13 +24,17 @@ IRAM caddr_t _sbrk_r (struct _reent *r, int incr)
if (heap_end == NULL) if (heap_end == NULL)
heap_end = &_heap_start; heap_end = &_heap_start;
prev_heap_end = heap_end; prev_heap_end = heap_end;
/* TODO: Check stack collision
if (heap_end + incr > stack_ptr) intptr_t sp = (intptr_t)xPortSupervisorStackPointer;
if(sp == 0) /* scheduler not started */
SP(sp);
if ((intptr_t)heap_end + incr >= sp)
{ {
_write (1, "_sbrk: Heap collided with stack\n", 32); r->_errno = ENOMEM;
abort(); return (caddr_t)-1;
} }
*/
heap_end += incr; heap_end += incr;
return (caddr_t) prev_heap_end; return (caddr_t) prev_heap_end;

161
core/phy_info.c Normal file
View file

@ -0,0 +1,161 @@
/* Routines to allow custom access to the Internal Espressif
SDK PHY datastructures.
Matches espressif/phy_internal.h
Part of esp-open-rtos. Copyright (C) 2016 Angus Gratton,
BSD Licensed as described in the file LICENSE.
*/
#include <espressif/phy_info.h>
#include <espressif/esp_common.h>
#include <common_macros.h>
#include <string.h>
static const sdk_phy_info_t IROM default_phy_info = {
._reserved00 = { 0x05, 0x00, 0x04, 0x02, 0x05 },
.version = 5,
._reserved06 = { 0x05, 0x02, 0x05, 0x00, 0x04, 0x05, 0x05, 0x04,
0x05, 0x05, 0x04,-0x02,-0x03,-0x01,-0x10,-0x10,
-0x10,-0x20,-0x20, -0x20},
.spur_freq_primary = 225,
.spur_freq_divisor = 10,
.spur_freq_en_h = 0xFF,
.spur_freq_en_l = 0xFF,
._reserved1e = { 0xf8, 0, 0xf8, 0xf8 },
.target_power = { 82, 78, 74, 68, 64, 56 },
.target_power_index_mcs = { 0, 0, 1, 1, 2, 3, 4, 5 },
.crystal_freq = CRYSTAL_FREQ_26M,
.sdio_config = SDIO_CONFIG_AUTO,
.bt_coexist_config = BT_COEXIST_CONFIG_NONE,
.bt_coexist_protocol = BT_COEXIST_PROTOCOL_WIFI_ONLY,
.dual_ant_config = DUAL_ANT_CONFIG_NONE,
._reserved34 = 0x02,
.crystal_sleep = CRYSTAL_SLEEP_OFF,
.spur_freq_2_primary = 225,
.spur_freq_2_divisor = 10,
.spur_freq_2_en_h = 0x00,
.spur_freq_2_en_l = 0x00,
.spur_freq_cfg_msb = 0x00,
.spur_freq_2_cfg_msb = 0x00,
.spur_freq_3_cfg = 0x0000,
.spur_freq_4_cfg = 0x0000,
._reserved4a = { 0x01, 0x93, 0x43, 0x00 },
.low_power_en = false,
.lp_atten_stage01 = LP_ATTEN_STAGE01_23DB,
.lp_atten_bb = 0,
.pwr_ind_11b_en = false,
.pwr_ind_11b_0 = 0,
.pwr_ind_11b_1 = 0,
/* Nominal 3.3V VCC. NOTE: This value is 0 in the
esp-open-rtos SDK default config sector, and may be unused
by that version of the SDK?
*/
.pa_vdd = 33,
/* Note: untested with the esp-open-rtos SDK default config sector, may be unused? */
.freq_correct_mode = FREQ_CORRECT_DISABLE,
.force_freq_offset = 0,
/* Note: is zero with the esp-open-rtos SDK default config sector, may be unused? */
.rf_cal_mode = RF_CAL_MODE_SAVED,
};
void get_default_phy_info(sdk_phy_info_t *info) __attribute__((weak, alias("get_sdk_default_phy_info")));
void get_sdk_default_phy_info(sdk_phy_info_t *info)
{
memcpy(info, &default_phy_info, sizeof(sdk_phy_info_t));
}
void read_saved_phy_info(sdk_phy_info_t *info)
{
sdk_spi_flash_read(sdk_flashchip.chip_size - sdk_flashchip.sector_size * 4, (uint32_t *)info, sizeof(sdk_phy_info_t));
}
void write_saved_phy_info(const sdk_phy_info_t *info)
{
sdk_spi_flash_write(sdk_flashchip.chip_size - sdk_flashchip.sector_size * 4, (uint32_t *)info, sizeof(sdk_phy_info_t));
}
void dump_phy_info(const sdk_phy_info_t *info, bool raw)
{
printf("version=%d\n", info->version);
printf("spur_freq = %.3f (%d/%d)\n",
(float)info->spur_freq_primary / info->spur_freq_divisor,
info->spur_freq_primary,
info->spur_freq_divisor);
printf("spur_freq_en = 0x%02x 0x%02x\n", info->spur_freq_en_h,
info->spur_freq_en_l);
printf("target_power\n");
for(int i = 0; i < 6; i++) {
printf(" %d: %.2fdB (raw 0x%02x)\n", i,
info->target_power[i]/4.0,
info->target_power[i]);
}
printf("target_power_index_mcs:");
for(int i = 0; i < 8; i++) {
printf(" %d%c", info->target_power_index_mcs[i],
i == 7 ? '\n' : ',');
}
printf("crystal_freq: %s (raw %d)\n",
(info->crystal_freq == CRYSTAL_FREQ_40M ? "40MHz" :
(info->crystal_freq == CRYSTAL_FREQ_26M ? "26MHz" :
(info->crystal_freq == CRYSTAL_FREQ_24M ? "24MHz" : "???"))),
info->crystal_freq);
printf("sdio_config: %d\n", info->sdio_config);
printf("bt_coexist config: %d protocol: 0x%02x\n",
info->bt_coexist_config, info->bt_coexist_protocol);
printf("dual_ant_config: %d\n", info->dual_ant_config);
printf("crystal_sleep: %d\n", info->crystal_sleep);
printf("spur_freq_2 = %.3f (%d/%d)\n",
(float)info->spur_freq_2_primary / info->spur_freq_2_divisor,
info->spur_freq_2_primary,
info->spur_freq_2_divisor);
printf("spur_freq_2_en = 0x%02x 0x%02x\n", info->spur_freq_2_en_h,
info->spur_freq_2_en_l);
printf("spur_freq_cfg_msb = 0x%02x\n", info->spur_freq_cfg_msb);
printf("spur_freq_2_)cfg_msb = 0x%02x\n", info->spur_freq_2_cfg_msb);
printf("spur_freq_3_cfg = 0x%04x\n", info->spur_freq_3_cfg);
printf("spur_freq_4_cfg = 0x%04x\n", info->spur_freq_4_cfg);
printf("low_power_en = %d\n", info->low_power_en);
printf("lp_atten_stage01 = 0x%02x\n", info->lp_atten_stage01);
printf("lp_atten_bb = %.2f (raw 0x%02x)\n", info->lp_atten_bb / 4.0,
info->lp_atten_bb);
printf("pa_vdd = %d\n", info->pa_vdd);
printf("freq_correct_mode = 0x%02x\n", info->freq_correct_mode);
printf("force_freq_offset = %d\n", info->force_freq_offset);
printf("rf_cal_mode = 0x%02x\n", info->rf_cal_mode);
if(raw) {
printf("Raw values:");
uint8_t *p = (uint8_t *)info;
for(int i = 0; i < sizeof(sdk_phy_info_t); i ++) {
if(i % 8 == 0) {
printf("\n0x%02x:", i);
}
printf(" %02x", p[i]);
}
printf("\n\n");
}
}

View file

@ -12,8 +12,6 @@
* Copyright (C) 2015 Superhouse Automation Pty Ltd * Copyright (C) 2015 Superhouse Automation Pty Ltd
* BSD Licensed as described in the file LICENSE * BSD Licensed as described in the file LICENSE
*/ */
#ifdef OTA
#define RBOOT_CONFIG_BASE (0x40200000 + 0x1000) #define RBOOT_CONFIG_BASE (0x40200000 + 0x1000)
#define RBOOT_ROMS_OFFS 0x8 /* offset of rboot_config_t.roms array in config */ #define RBOOT_ROMS_OFFS 0x8 /* offset of rboot_config_t.roms array in config */
@ -74,5 +72,3 @@ Cache_Read_Enable:
movi a0, cache_return_save /* restore a0 return address */ movi a0, cache_return_save /* restore a0 return address */
l32i a0, a0, 0 l32i a0, a0, 0
ret.n ret.n
#endif

View file

@ -0,0 +1,4 @@
PROGRAM=dht_sensor
EXTRA_COMPONENTS = extras/dht
include ../../common.mk

View file

@ -0,0 +1,46 @@
/*
*
* This sample code is in the public domain.
*/
#include "espressif/esp_common.h"
#include "esp/uart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "dht.h"
#include "esp8266.h"
/* An example using the ubiquitous DHT** humidity sensors
* to read and print a new temperature and humidity measurement
* from a sensor attached to GPIO pin 4.
*/
int const dht_gpio = 4;
void dhtMeasurementTask(void *pvParameters)
{
int8_t temperature = 0;
int8_t humidity = 0;
// DHT sensors that come mounted on a PCB generally have
// pull-up resistors on the data pin. It is recommended
// to provide an external pull-up resistor otherwise...
gpio_set_pullup(dht_gpio, false, false);
while(1) {
if (dht_fetch_data(dht_gpio, &humidity, &temperature)) {
printf("Humidity: %i%% Temp: %iC\n", humidity, temperature);
} else {
printf("Could not read data from sensor...");
}
// Three second delay...
vTaskDelay(3000 / portTICK_RATE_MS);
}
}
void user_init(void)
{
uart_set_baud(0, 115200);
xTaskCreate(dhtMeasurementTask, (signed char *)"dhtMeasurementTask", 256, NULL, 2, NULL);
}

View file

@ -13,9 +13,9 @@
#define TESTSTRING "O hai there! %d %d %d" #define TESTSTRING "O hai there! %d %d %d"
const char *dramtest = TESTSTRING; const RAM char dramtest[] = TESTSTRING;
const __attribute__((section(".iram1.notrodata"))) char iramtest[] = TESTSTRING; const char *iromtest = TESTSTRING;
const __attribute__((section(".text.notrodata"))) char iromtest[] = TESTSTRING; const IRAM_DATA char iramtest[] = TESTSTRING;
static inline uint32_t get_ccount (void) static inline uint32_t get_ccount (void)
{ {

View file

@ -0,0 +1,4 @@
# Simple makefile for simple example
PROGRAM=json_jsmn_simple
EXTRA_COMPONENTS = extras/jsmn
include ../../common.mk

View file

@ -0,0 +1,112 @@
/**
* Copyright (c) 2010 Serge A. Zaitsev and
* 2016 Drasko DRASKOVIC <drasko.draskovic@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "espressif/esp_common.h"
#include "esp/uart.h"
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "jsmn.h"
/**
* A small example of jsmn parsing when JSON structure is known and number of
* tokens is predictable.
*/
const char *JSON_STRING =
"{\"user\": \"johndoe\", \"admin\": false, \"uid\": 1000,\n "
"\"groups\": [\"users\", \"wheel\", \"audio\", \"video\"]}";
static int jsoneq(const char *json, jsmntok_t *tok, const char *s) {
if (tok->type == JSMN_STRING && (int) strlen(s) == tok->end - tok->start &&
strncmp(json + tok->start, s, tok->end - tok->start) == 0) {
return 0;
}
return -1;
}
void json_test(void *pvParameters)
{
int i;
int r;
jsmn_parser p;
jsmntok_t t[128]; /* We expect no more than 128 tokens */
jsmn_init(&p);
r = jsmn_parse(&p, JSON_STRING, strlen(JSON_STRING), t, sizeof(t)/sizeof(t[0]));
if (r < 0) {
printf("Failed to parse JSON: %d\n", r);
}
/* Assume the top-level element is an object */
if (r < 1 || t[0].type != JSMN_OBJECT) {
printf("Object expected\n");
}
/* Loop over all keys of the root object */
for (i = 1; i < r; i++) {
if (jsoneq(JSON_STRING, &t[i], "user") == 0) {
/* We may use strndup() to fetch string value */
printf("- User: %.*s\n", t[i+1].end-t[i+1].start,
JSON_STRING + t[i+1].start);
i++;
} else if (jsoneq(JSON_STRING, &t[i], "admin") == 0) {
/* We may additionally check if the value is either "true" or "false" */
printf("- Admin: %.*s\n", t[i+1].end-t[i+1].start,
JSON_STRING + t[i+1].start);
i++;
} else if (jsoneq(JSON_STRING, &t[i], "uid") == 0) {
/* We may want to do strtol() here to get numeric value */
printf("- UID: %.*s\n", t[i+1].end-t[i+1].start,
JSON_STRING + t[i+1].start);
i++;
} else if (jsoneq(JSON_STRING, &t[i], "groups") == 0) {
int j;
printf("- Groups:\n");
if (t[i+1].type != JSMN_ARRAY) {
continue; /* We expect groups to be an array of strings */
}
for (j = 0; j < t[i+1].size; j++) {
jsmntok_t *g = &t[i+j+2];
printf(" * %.*s\n", g->end - g->start, JSON_STRING + g->start);
}
i += t[i+1].size + 1;
} else {
printf("Unexpected key: %.*s\n", t[i].end-t[i].start,
JSON_STRING + t[i].start);
}
}
while (1)
;
}
void user_init(void)
{
uart_set_baud(0, 115200);
printf("SDK version:%s\n", sdk_system_get_sdk_version());
xTaskCreate(json_test, (signed char *)"jsont", 1024, NULL, 2, NULL);
}

View file

@ -157,6 +157,7 @@ static void mqtt_task(void *pvParameters)
break; break;
} }
printf("Connection dropped, request restart\n\r"); printf("Connection dropped, request restart\n\r");
DisconnectNetwork(&network);
taskYIELD(); taskYIELD();
} }
} }

View file

@ -1,5 +1,4 @@
PROGRAM=ota_basic PROGRAM=ota_basic
OTA=1 EXTRA_COMPONENTS=extras/rboot-ota extras/mbedtls
EXTRA_COMPONENTS=extras/rboot-ota
include ../../common.mk include ../../common.mk

View file

@ -1,23 +1,121 @@
/* A very simple OTA example /* A very simple OTA example
* *
* Binds a TCP socket, reads an image from it over TFTP and then flashes live. * Tries to run both a TFTP client and a TFTP server simultaneously, either will accept a TTP firmware and update it.
*
* Not a realistic OTA setup, this needs adapting (choose either client or server) before you'd want to use it.
* *
* For more information about esp-open-rtos OTA see https://github.com/SuperHouse/esp-open-rtos/wiki/OTA-Update-Configuration * For more information about esp-open-rtos OTA see https://github.com/SuperHouse/esp-open-rtos/wiki/OTA-Update-Configuration
* *
* NOT SUITABLE TO PUT ON THE INTERNET OR INTO A PRODUCTION ENVIRONMENT!!!! * NOT SUITABLE TO PUT ON THE INTERNET OR INTO A PRODUCTION ENVIRONMENT!!!!
*/ */
#include <string.h>
#include "espressif/esp_common.h" #include "espressif/esp_common.h"
#include "esp/uart.h" #include "esp/uart.h"
#include "FreeRTOS.h" #include "FreeRTOS.h"
#include "task.h" #include "task.h"
#include "esp8266.h" #include "esp8266.h"
#include "ssid_config.h" #include "ssid_config.h"
#include "mbedtls/sha256.h"
#include "ota-tftp.h" #include "ota-tftp.h"
#include "rboot-integration.h"
#include "rboot.h"
#include "rboot-api.h" #include "rboot-api.h"
/* TFTP client will request this image filenames from this server */
#define TFTP_IMAGE_SERVER "192.168.1.23"
#define TFTP_IMAGE_FILENAME1 "firmware1.bin"
#define TFTP_IMAGE_FILENAME2 "firmware2.bin"
/* Output of the command 'sha256sum firmware1.bin' */
static const char *FIRMWARE1_SHA256 = "88199daff8b9e76975f685ec7f95bc1df3c61bd942a33a54a40707d2a41e5488";
/* Example function to TFTP download a firmware file and verify its SHA256 before
booting into it.
*/
static void tftpclient_download_and_verify_file1(int slot, rboot_config *conf)
{
printf("Downloading %s to slot %d...\n", TFTP_IMAGE_FILENAME1, slot);
int res = ota_tftp_download(TFTP_IMAGE_SERVER, TFTP_PORT+1, TFTP_IMAGE_FILENAME1, 1000, slot, NULL);
printf("ota_tftp_download %s result %d\n", TFTP_IMAGE_FILENAME1, res);
if (res != 0) {
return;
}
printf("Looks valid, calculating SHA256...\n");
uint32_t length;
bool valid = rboot_verify_image(conf->roms[slot], &length, NULL);
static mbedtls_sha256_context ctx;
mbedtls_sha256_init(&ctx);
mbedtls_sha256_starts(&ctx, 0);
valid = valid && rboot_digest_image(conf->roms[slot], length, (rboot_digest_update_fn)mbedtls_sha256_update, &ctx);
static uint8_t hash_result[32];
mbedtls_sha256_finish(&ctx, hash_result);
mbedtls_sha256_free(&ctx);
if(!valid)
{
printf("Not valid after all :(\n");
return;
}
printf("Image SHA256 = ");
valid = true;
for(int i = 0; i < sizeof(hash_result); i++) {
char hexbuf[3];
snprintf(hexbuf, 3, "%02x", hash_result[i]);
printf(hexbuf);
if(strncmp(hexbuf, FIRMWARE1_SHA256+i*2, 2))
valid = false;
}
printf("\n");
if(!valid) {
printf("Downloaded image SHA256 didn't match expected '%s'\n", FIRMWARE1_SHA256);
return;
}
printf("SHA256 Matches. Rebooting into slot %d...\n", slot);
rboot_set_current_rom(slot);
sdk_system_restart();
}
/* Much simpler function that just downloads a file via TFTP into an rboot slot.
(
*/
static void tftpclient_download_file2(int slot)
{
printf("Downloading %s to slot %d...\n", TFTP_IMAGE_FILENAME2, slot);
int res = ota_tftp_download(TFTP_IMAGE_SERVER, TFTP_PORT+1, TFTP_IMAGE_FILENAME2, 1000, slot, NULL);
printf("ota_tftp_download %s result %d\n", TFTP_IMAGE_FILENAME2, res);
}
void tftp_client_task(void *pvParameters)
{
printf("TFTP client task starting...\n");
rboot_config conf;
conf = rboot_get_config();
int slot = (conf.current_rom + 1) % conf.count;
printf("Image will be saved in OTA slot %d.\n", slot);
if(slot == conf.current_rom) {
printf("FATAL ERROR: Only one OTA slot is configured!\n");
while(1) {}
}
/* Alternate between trying two different filenames. Probalby want to change this if making a practical
example!
Note: example will reboot into FILENAME1 if it is successfully downloaded, but FILENAME2 is ignored.
*/
while(1) {
tftpclient_download_and_verify_file1(slot, &conf);
vTaskDelay(5000 / portTICK_RATE_MS);
tftpclient_download_file2(slot);
vTaskDelay(5000 / portTICK_RATE_MS);
}
}
void user_init(void) void user_init(void)
{ {
uart_set_baud(0, 115200); uart_set_baud(0, 115200);
@ -38,5 +136,7 @@ void user_init(void)
sdk_wifi_set_opmode(STATION_MODE); sdk_wifi_set_opmode(STATION_MODE);
sdk_wifi_station_set_config(&config); sdk_wifi_station_set_config(&config);
printf("Starting TFTP server...");
ota_tftp_init_server(TFTP_PORT); ota_tftp_init_server(TFTP_PORT);
xTaskCreate(&tftp_client_task, (signed char *)"tftp_client", 2048, NULL, 2, NULL);
} }

6
examples/sntp/Makefile Normal file
View file

@ -0,0 +1,6 @@
# Makefile for the sntp_example program
PROGRAM=sntp_example
EXTRA_COMPONENTS = extras/sntp
include ../../common.mk

View file

@ -0,0 +1,79 @@
/*
* Test code for SNTP on esp-open-rtos.
*
* Jesus Alonso (doragasu)
*/
#include <espressif/esp_common.h>
#include <esp/uart.h>
#include <string.h>
#include <stdio.h>
#include <FreeRTOS.h>
#include <task.h>
#include <lwip/err.h>
#include <lwip/sockets.h>
#include <lwip/sys.h>
#include <lwip/netdb.h>
#include <lwip/dns.h>
#include <ssid_config.h>
/* Add extras/sntp component to makefile for this include to work */
#include <sntp.h>
#include <time.h>
#define SNTP_SERVERS "0.pool.ntp.org", "1.pool.ntp.org", \
"2.pool.ntp.org", "3.pool.ntp.org"
#define vTaskDelayMs(ms) vTaskDelay((ms)/portTICK_RATE_MS)
#define UNUSED_ARG(x) (void)x
void sntp_tsk(void *pvParameters)
{
char *servers[] = {SNTP_SERVERS};
UNUSED_ARG(pvParameters);
/* Wait until we have joined AP and are assigned an IP */
while (sdk_wifi_station_get_connect_status() != STATION_GOT_IP) {
vTaskDelayMs(100);
}
/* Start SNTP */
printf("Starting SNTP... ");
/* SNTP will request an update each 5 minutes */
sntp_set_update_delay(5*60000);
/* Set GMT+1 zone, daylight savings off */
const struct timezone tz = {1*60, 0};
/* SNTP initialization */
sntp_initialize(&tz);
/* Servers must be configured right after initialization */
sntp_set_servers(servers, sizeof(servers) / sizeof(char*));
printf("DONE!\n");
/* Print date and time each 5 seconds */
while(1) {
vTaskDelayMs(5000);
time_t ts = time(NULL);
printf("TIME: %s", ctime(&ts));
}
}
void user_init(void)
{
uart_set_baud(0, 115200);
printf("SDK version:%s\n", sdk_system_get_sdk_version());
struct sdk_station_config config = {
.ssid = WIFI_SSID,
.password = WIFI_PASS,
};
/* required to call wifi_set_opmode before station_set_config */
sdk_wifi_set_opmode(STATION_MODE);
sdk_wifi_station_set_config(&config);
xTaskCreate(sntp_tsk, (signed char *)"SNTP", 1024, NULL, 1, NULL);
}

10
extras/dht/component.mk Normal file
View file

@ -0,0 +1,10 @@
# Component makefile for extras/dht
INC_DIRS += $(ROOT)extras/dht
# args for passing into compile rule generation
extras/dht_INC_DIR = $(ROOT)extras/dht
extras/dht_SRC_DIR = $(ROOT)extras/dht
$(eval $(call component_compile_rules,extras/dht))

144
extras/dht/dht.c Normal file
View file

@ -0,0 +1,144 @@
/*
* Part of esp-open-rtos
* Copyright (C) 2016 Jonathan Hartsuiker (https://github.com/jsuiker)
* BSD Licensed as described in the file LICENSE
*
*/
#include "dht.h"
#include "string.h"
#include "task.h"
#include "esp/gpio.h"
#include <espressif/esp_misc.h> // sdk_os_delay_us
#ifndef DEBUG_DHT
#define DEBUG_DHT 0
#endif
#if DEBUG_DHT
#define debug(fmt, ...) printf("%s" fmt "\n", "dht: ", ## __VA_ARGS__);
#else
#define debug(fmt, ...) /* (do nothing) */
#endif
/*
* Note:
* A suitable pull-up resistor should be connected to the selected GPIO line
*
* __ ______ _______ ___________________________
* \ A / \ C / \ DHT duration_data_low / \
* \_______/ B \______/ D \__________________________/ DHT duration_data_high \__
*
*
* Initializing communications with the DHT requires four 'phases' as follows:
*
* Phase A - MCU pulls signal low for at least 18000 us
* Phase B - MCU allows signal to float back up and waits 20-40us for DHT to pull it low
* Phase C - DHT pulls signal low for ~80us
* Phase D - DHT lets signal float back up for ~80us
*
* After this, the DHT transmits its first bit by holding the signal low for 50us
* and then letting it float back high for a period of time that depends on the data bit.
* duration_data_high is shorter than 50us for a logic '0' and longer than 50us for logic '1'.
*
* There are a total of 40 data bits trasnmitted sequentially. These bits are read into a byte array
* of length 5. The first and third bytes are humidity (%) and temperature (C), respectively. Bytes 2 and 4
* are zero-filled and the fifth is a checksum such that:
*
* byte_5 == (byte_1 + byte_2 + byte_3 + btye_4) & 0xFF
*
*/
/*
* @pin the selected GPIO pin
* @interval how frequently the pin state is checked in microseconds
* @timeout maximum length of time to wait for the expected pin state
* @expected_pin_state high (true) or low (false) pin state
* @counter pointer to external uint8_t for tallying the duration waited for the pin state
*/
bool dht_await_pin_state(uint8_t pin, uint8_t interval, uint8_t timeout, bool expected_pin_state, uint8_t * counter) {
for (*counter = 0; *counter < timeout; *counter+=interval) {
if (gpio_read(pin) == expected_pin_state) return true;
sdk_os_delay_us(interval);
}
return false;
}
/*
*
*
* @pin the selected GPIO pin
* @humidity pointer to external int8_t to store resulting humidity value
* @temperature pointer to external int8_t to store resulting temperature value
*/
bool dht_fetch_data(int8_t pin, int8_t * humidity, int8_t * temperature) {
int8_t data[40] = {0};
int8_t result[5] = {0};
uint8_t i = 0;
uint8_t init_phase_duration = 0;
uint8_t duration_data_low = 0;
uint8_t duration_data_high = 0;
gpio_enable(pin, GPIO_OUT_OPEN_DRAIN);
taskENTER_CRITICAL();
// Phase 'A' pulling signal low to initiate read sequence
gpio_write(pin, 0);
sdk_os_delay_us(20000);
gpio_write(pin, 1);
// Step through Phase 'B' at 2us intervals, 40us max
if (dht_await_pin_state(pin, 2, 40, false, &init_phase_duration)) {
// Step through Phase 'C ' at 2us intervals, 88us max
if (dht_await_pin_state(pin, 2, 88, true, &init_phase_duration)) {
// Step through Phase 'D' at 2us intervals, 88us max
if (dht_await_pin_state(pin, 2, 88, false, &init_phase_duration)) {
// Read in each of the 40 bits of data...
for (i = 0; i < 40; i++) {
if (dht_await_pin_state(pin, 2, 60, true, &duration_data_low)) {
if (dht_await_pin_state(pin, 2, 75, false, &duration_data_high)) {
data[i] = duration_data_high > duration_data_low;
}
}
}
taskEXIT_CRITICAL();
for (i = 0; i < 40; i++) {
// Read each bit into 'result' byte array...
result[i/8] <<= 1;
result[i/8] |= data[i];
}
if (result[4] == ((result[0] + result[1] + result[2] + result[3]) & 0xFF)) {
// Data valid, checksum succeeded...
*humidity = result[0];
*temperature = result[2];
debug("Successfully retrieved sensor data...");
return true;
} else {
debug("Checksum failed, invalid data received from sensor...");
}
} else {
debug("Initialization error, problem in phase 'D'...");
}
} else {
debug("Initialization error, problem in phase 'C'...");
}
} else {
debug("Initialization error, problem in phase 'B'...");
}
taskEXIT_CRITICAL();
return false;
}

17
extras/dht/dht.h Normal file
View file

@ -0,0 +1,17 @@
/*
* Part of esp-open-rtos
* Copyright (C) 2016 Jonathan Hartsuiker (https://github.com/jsuiker)
* BSD Licensed as described in the file LICENSE
*
*/
#ifndef __DTH_H__
#define __DHT_H__
#include "FreeRTOS.h"
bool dht_wait_for_pin_state(uint8_t pin, uint8_t interval, uint8_t timeout, bool expected_pin_sate, uint8_t * counter);
bool dht_fetch_data(int8_t pin, int8_t * humidity, int8_t * temperature);
#endif

10
extras/jsmn/component.mk Normal file
View file

@ -0,0 +1,10 @@
# Component makefile for extras/jsmn
# expected anyone using jsmn json component includes it as 'jsmn/jsmn.h'
INC_DIRS += $(jsmn_ROOT)jsmn
# args for passing into compile rule generation
jsmn_INC_DIR =
jsmn_SRC_DIR = $(jsmn_ROOT)jsmn
$(eval $(call component_compile_rules,jsmn))

1
extras/jsmn/jsmn Submodule

@ -0,0 +1 @@
Subproject commit bbc6755fce14c713f9bb4ba47c688d15efc1394b

@ -1 +1 @@
Subproject commit a7ffc8f7396573bec401e0afcc073137522d5305 Subproject commit 0a0c22e0efcf2f8f71d7e16712f80b8f77326f72

View file

@ -217,18 +217,14 @@ int mbedtls_net_bind( mbedtls_net_context *ctx, const char *bind_ip, const char
} }
#if ( defined(_WIN32) || defined(_WIN32_WCE) ) && !defined(EFIX64) && \ static int socket_errno( const mbedtls_net_context *ctx )
!defined(EFI32)
/*
* Check if the requested operation would be blocking on a non-blocking socket
* and thus 'failed' with a negative return value.
*/
static int net_would_block( const mbedtls_net_context *ctx )
{ {
((void) ctx); int sock_errno = 0;
return( WSAGetLastError() == WSAEWOULDBLOCK ); u32_t optlen = sizeof(sock_errno);
lwip_getsockopt(ctx->fd, SOL_SOCKET, SO_ERROR, &sock_errno, &optlen);
return sock_errno;
} }
#else
/* /*
* Check if the requested operation would be blocking on a non-blocking socket * Check if the requested operation would be blocking on a non-blocking socket
* and thus 'failed' with a negative return value. * and thus 'failed' with a negative return value.
@ -243,7 +239,8 @@ static int net_would_block( const mbedtls_net_context *ctx )
if( ( fcntl( ctx->fd, F_GETFL, 0) & O_NONBLOCK ) != O_NONBLOCK ) if( ( fcntl( ctx->fd, F_GETFL, 0) & O_NONBLOCK ) != O_NONBLOCK )
return( 0 ); return( 0 );
switch( errno )
switch( socket_errno(ctx) )
{ {
#if defined EAGAIN #if defined EAGAIN
case EAGAIN: case EAGAIN:
@ -255,7 +252,6 @@ static int net_would_block( const mbedtls_net_context *ctx )
} }
return( 0 ); return( 0 );
} }
#endif /* ( _WIN32 || _WIN32_WCE ) && !EFIX64 && !EFI32 */
/* /*
* Accept a connection from a remote client * Accept a connection from a remote client
@ -401,9 +397,7 @@ void mbedtls_net_usleep( unsigned long usec )
#endif #endif
} }
/* /* Read at most 'len' characters */
* Read at most 'len' characters
*/
int mbedtls_net_recv( void *ctx, unsigned char *buf, size_t len ) int mbedtls_net_recv( void *ctx, unsigned char *buf, size_t len )
{ {
int ret; int ret;
@ -419,17 +413,13 @@ int mbedtls_net_recv( void *ctx, unsigned char *buf, size_t len )
if( net_would_block( ctx ) != 0 ) if( net_would_block( ctx ) != 0 )
return( MBEDTLS_ERR_SSL_WANT_READ ); return( MBEDTLS_ERR_SSL_WANT_READ );
#if ( defined(_WIN32) || defined(_WIN32_WCE) ) && !defined(EFIX64) && \ int sock_errno = socket_errno(ctx);
!defined(EFI32)
if( WSAGetLastError() == WSAECONNRESET ) if( sock_errno == EPIPE || sock_errno == ECONNRESET )
return( MBEDTLS_ERR_NET_CONN_RESET );
#else
if( errno == EPIPE || errno == ECONNRESET )
return( MBEDTLS_ERR_NET_CONN_RESET ); return( MBEDTLS_ERR_NET_CONN_RESET );
if( errno == EINTR ) if( sock_errno == EINTR )
return( MBEDTLS_ERR_SSL_WANT_READ ); return( MBEDTLS_ERR_SSL_WANT_READ );
#endif
return( MBEDTLS_ERR_NET_RECV_FAILED ); return( MBEDTLS_ERR_NET_RECV_FAILED );
} }
@ -465,14 +455,8 @@ int mbedtls_net_recv_timeout( void *ctx, unsigned char *buf, size_t len,
if( ret < 0 ) if( ret < 0 )
{ {
#if ( defined(_WIN32) || defined(_WIN32_WCE) ) && !defined(EFIX64) && \
!defined(EFI32)
if( WSAGetLastError() == WSAEINTR )
return( MBEDTLS_ERR_SSL_WANT_READ );
#else
if( errno == EINTR ) if( errno == EINTR )
return( MBEDTLS_ERR_SSL_WANT_READ ); return( MBEDTLS_ERR_SSL_WANT_READ );
#endif
return( MBEDTLS_ERR_NET_RECV_FAILED ); return( MBEDTLS_ERR_NET_RECV_FAILED );
} }
@ -499,17 +483,13 @@ int mbedtls_net_send( void *ctx, const unsigned char *buf, size_t len )
if( net_would_block( ctx ) != 0 ) if( net_would_block( ctx ) != 0 )
return( MBEDTLS_ERR_SSL_WANT_WRITE ); return( MBEDTLS_ERR_SSL_WANT_WRITE );
#if ( defined(_WIN32) || defined(_WIN32_WCE) ) && !defined(EFIX64) && \ int sock_errno = socket_errno(ctx);
!defined(EFI32)
if( WSAGetLastError() == WSAECONNRESET ) if( sock_errno == EPIPE || sock_errno == ECONNRESET )
return( MBEDTLS_ERR_NET_CONN_RESET );
#else
if( errno == EPIPE || errno == ECONNRESET )
return( MBEDTLS_ERR_NET_CONN_RESET ); return( MBEDTLS_ERR_NET_CONN_RESET );
if( errno == EINTR ) if( sock_errno == EINTR )
return( MBEDTLS_ERR_SSL_WANT_WRITE ); return( MBEDTLS_ERR_SSL_WANT_WRITE );
#endif
return( MBEDTLS_ERR_NET_SEND_FAILED ); return( MBEDTLS_ERR_NET_SEND_FAILED );
} }

View file

@ -1,8 +1,9 @@
# Component makefile for extras/rboot-ota # Component makefile for extras/rboot-ota
INC_DIRS += $(rboot-ota_ROOT) # global include directories need to find rboot.h for integration, even
# when just including rboot-api.h :(
INC_DIRS += $(rboot-ota_ROOT) $(ROOT)bootloader $(ROOT)bootloader/rboot
# args for passing into compile rule generation
rboot-ota_SRC_DIR = $(rboot-ota_ROOT) rboot-ota_SRC_DIR = $(rboot-ota_ROOT)
$(eval $(call component_compile_rules,rboot-ota)) $(eval $(call component_compile_rules,rboot-ota))

View file

@ -22,12 +22,12 @@
#include <espressif/esp_system.h> #include <espressif/esp_system.h>
#include "ota-tftp.h" #include "ota-tftp.h"
#include "rboot.h"
#include "rboot-api.h" #include "rboot-api.h"
#define TFTP_FIRMWARE_FILE "firmware.bin" #define TFTP_FIRMWARE_FILE "firmware.bin"
#define TFTP_OCTET_MODE "octet" /* non-case-sensitive */ #define TFTP_OCTET_MODE "octet" /* non-case-sensitive */
#define TFTP_OP_RRQ 1
#define TFTP_OP_WRQ 2 #define TFTP_OP_WRQ 2
#define TFTP_OP_DATA 3 #define TFTP_OP_DATA 3
#define TFTP_OP_ACK 4 #define TFTP_OP_ACK 4
@ -43,8 +43,9 @@
static void tftp_task(void *port_p); static void tftp_task(void *port_p);
static char *tftp_get_field(int field, struct netbuf *netbuf); static char *tftp_get_field(int field, struct netbuf *netbuf);
static err_t tftp_receive_data(struct netconn *nc, size_t write_offs, size_t limit_offs, size_t *received_len); static err_t tftp_receive_data(struct netconn *nc, size_t write_offs, size_t limit_offs, size_t *received_len, ip_addr_t *peer_addr, int peer_port, tftp_receive_cb receive_cb);
static err_t tftp_send_ack(struct netconn *nc, int block); static err_t tftp_send_ack(struct netconn *nc, int block);
static err_t tftp_send_rrq(struct netconn *nc, const char *filename);
static void tftp_send_error(struct netconn *nc, int err_code, const char *err_msg); static void tftp_send_error(struct netconn *nc, int err_code, const char *err_msg);
void ota_tftp_init_server(int listen_port) void ota_tftp_init_server(int listen_port)
@ -52,6 +53,62 @@ void ota_tftp_init_server(int listen_port)
xTaskCreate(tftp_task, (signed char *)"tftpOTATask", 512, (void *)listen_port, 2, NULL); xTaskCreate(tftp_task, (signed char *)"tftpOTATask", 512, (void *)listen_port, 2, NULL);
} }
err_t ota_tftp_download(const char *server, int port, const char *filename,
int timeout, int ota_slot, tftp_receive_cb receive_cb)
{
rboot_config rboot_config = rboot_get_config();
/* Validate the OTA slot parameter */
if(rboot_config.current_rom == ota_slot || rboot_config.count <= ota_slot)
{
return ERR_VAL;
}
/* This is all we need to know from the rboot config - where we need
to write data to.
*/
uint32_t flash_offset = rboot_config.roms[ota_slot];
struct netconn *nc = netconn_new (NETCONN_UDP);
err_t err;
if(!nc) {
return ERR_IF;
}
netconn_set_recvtimeout(nc, timeout);
/* try to bind our client port as our local port,
or keep trying the next 10 ports after it */
int local_port = port-1;
do {
err = netconn_bind(nc, IP_ADDR_ANY, ++local_port);
} while(err == ERR_USE && local_port < port + 10);
if(err) {
netconn_delete(nc);
return err;
}
ip_addr_t addr;
err = netconn_gethostbyname(server, &addr);
if(err) {
netconn_delete(nc);
return err;
}
netconn_connect(nc, &addr, port);
err = tftp_send_rrq(nc, filename);
if(err) {
netconn_delete(nc);
return err;
}
size_t received_len;
err = tftp_receive_data(nc, flash_offset, flash_offset+MAX_IMAGE_SIZE,
&received_len, &addr, port, receive_cb);
netconn_delete(nc);
return err;
}
static void tftp_task(void *listen_port) static void tftp_task(void *listen_port)
{ {
struct netconn *nc = netconn_new (NETCONN_UDP); struct netconn *nc = netconn_new (NETCONN_UDP);
@ -100,7 +157,7 @@ static void tftp_task(void *listen_port)
/* check mode */ /* check mode */
char *mode = tftp_get_field(1, netbuf); char *mode = tftp_get_field(1, netbuf);
if(!mode || strcmp("octet", mode)) { if(!mode || strcmp(TFTP_OCTET_MODE, mode)) {
tftp_send_error(nc, TFTP_ERR_ILLEGAL, "Mode must be octet/binary"); tftp_send_error(nc, TFTP_ERR_ILLEGAL, "Mode must be octet/binary");
free(mode); free(mode);
netbuf_delete(netbuf); netbuf_delete(netbuf);
@ -133,7 +190,8 @@ static void tftp_task(void *listen_port)
/* Finished WRQ phase, start TFTP data transfer */ /* Finished WRQ phase, start TFTP data transfer */
size_t received_len; size_t received_len;
int recv_err = tftp_receive_data(nc, conf.roms[slot], conf.roms[slot]+MAX_IMAGE_SIZE, &received_len); netconn_set_recvtimeout(nc, 10000);
int recv_err = tftp_receive_data(nc, conf.roms[slot], conf.roms[slot]+MAX_IMAGE_SIZE, &received_len, NULL, 0, NULL);
netconn_disconnect(nc); netconn_disconnect(nc);
printf("OTA TFTP receive data result %d bytes %d\r\n", recv_err, received_len); printf("OTA TFTP receive data result %d bytes %d\r\n", recv_err, received_len);
@ -182,20 +240,48 @@ static char *tftp_get_field(int field, struct netbuf *netbuf)
return result; return result;
} }
static err_t tftp_receive_data(struct netconn *nc, size_t write_offs, size_t limit_offs, size_t *received_len) #define TFTP_TIMEOUT_RETRANSMITS 10
static err_t tftp_receive_data(struct netconn *nc, size_t write_offs, size_t limit_offs, size_t *received_len, ip_addr_t *peer_addr, int peer_port, tftp_receive_cb receive_cb)
{ {
*received_len = 0; *received_len = 0;
const int DATA_PACKET_SZ = 512 + 4; /*( packet size plus header */ const int DATA_PACKET_SZ = 512 + 4; /*( packet size plus header */
uint32_t start_offs = write_offs; uint32_t start_offs = write_offs;
int block = 1; int block = 1;
struct netbuf *netbuf; struct netbuf *netbuf = 0;
int retries = TFTP_TIMEOUT_RETRANSMITS;
while(1) while(1)
{ {
netconn_set_recvtimeout(nc, 10000); if(peer_addr) {
netconn_disconnect(nc);
}
err_t err = netconn_recv(nc, &netbuf); err_t err = netconn_recv(nc, &netbuf);
if(peer_addr) {
if(netbuf) {
/* For TFTP server, the UDP connection is already established. But for client,
we don't know what port the server is using until we see the first data
packet - so we connect here.
*/
netconn_connect(nc, netbuf_fromaddr(netbuf), netbuf_fromport(netbuf));
peer_addr = 0;
} else {
/* Otherwise, temporarily re-connect so we can send errors */
netconn_connect(nc, peer_addr, peer_port);
}
}
if(err == ERR_TIMEOUT) { if(err == ERR_TIMEOUT) {
if(retries-- > 0 && block > 1) {
/* Retransmit the last ACK, wait for repeat data block.
This doesn't work for the first block, have to time out and start again. */
tftp_send_ack(nc, block-1);
continue;
}
tftp_send_error(nc, TFTP_ERR_ILLEGAL, "Timeout"); tftp_send_error(nc, TFTP_ERR_ILLEGAL, "Timeout");
return ERR_TIMEOUT; return ERR_TIMEOUT;
} }
@ -225,6 +311,9 @@ static err_t tftp_receive_data(struct netconn *nc, size_t write_offs, size_t lim
} }
} }
/* Reset retry count if we got valid data */
retries = TFTP_TIMEOUT_RETRANSMITS;
if(write_offs % SECTOR_SIZE == 0) { if(write_offs % SECTOR_SIZE == 0) {
sdk_spi_flash_erase_sector(write_offs / SECTOR_SIZE); sdk_spi_flash_erase_sector(write_offs / SECTOR_SIZE);
} }
@ -281,7 +370,9 @@ static err_t tftp_receive_data(struct netconn *nc, size_t write_offs, size_t lim
it so the client gets an indication if things were successful. it so the client gets an indication if things were successful.
*/ */
const char *err = "Unknown validation error"; const char *err = "Unknown validation error";
if(!rboot_verify_image(start_offs, *received_len, &err)) { uint32_t image_length;
if(!rboot_verify_image(start_offs, &image_length, &err)
|| image_length != *received_len) {
tftp_send_error(nc, TFTP_ERR_ILLEGAL, err); tftp_send_error(nc, TFTP_ERR_ILLEGAL, err);
return ERR_VAL; return ERR_VAL;
} }
@ -293,6 +384,11 @@ static err_t tftp_receive_data(struct netconn *nc, size_t write_offs, size_t lim
return ack_err; return ack_err;
} }
// Make sure ack was successful before calling callback.
if(receive_cb) {
receive_cb(*received_len);
}
if(len < DATA_PACKET_SZ) { if(len < DATA_PACKET_SZ) {
return ERR_OK; return ERR_OK;
} }
@ -325,3 +421,17 @@ static void tftp_send_error(struct netconn *nc, int err_code, const char *err_ms
netconn_send(nc, err); netconn_send(nc, err);
netbuf_delete(err); netbuf_delete(err);
} }
static err_t tftp_send_rrq(struct netconn *nc, const char *filename)
{
struct netbuf *rrqbuf = netbuf_new();
uint16_t *rrqdata = (uint16_t *)netbuf_alloc(rrqbuf, 4 + strlen(filename) + strlen(TFTP_OCTET_MODE));
rrqdata[0] = htons(TFTP_OP_RRQ);
char *rrq_filename = (char *)&rrqdata[1];
strcpy(rrq_filename, filename);
strcpy(rrq_filename + strlen(filename) + 1, TFTP_OCTET_MODE);
err_t err = netconn_send(nc, rrqbuf);
netbuf_delete(rrqbuf);
return err;
}

View file

@ -1,5 +1,10 @@
#ifndef _OTA_TFTP_H #ifndef _OTA_TFTP_H
#define _OTA_TFTP_H #define _OTA_TFTP_H
#include "lwip/err.h"
typedef void (*tftp_receive_cb)(size_t bytes_received);
/* TFTP Server OTA Support /* TFTP Server OTA Support
* *
* To use, call ota_tftp_init_server() which will start the TFTP server task * To use, call ota_tftp_init_server() which will start the TFTP server task
@ -30,6 +35,21 @@
*/ */
void ota_tftp_init_server(int listen_port); void ota_tftp_init_server(int listen_port);
/* Attempt to make a TFTP client connection and download the specified filename.
'timeout' is in milliseconds, and is timeout for any UDP exchange
_not_ the entire download.
Returns 0 on success, LWIP err.h values for errors.
Does not change the current firmware slot, or reboot.
receive_cb: called repeatedly after each successful packet that
has been written to flash and ACKed. Can pass NULL to omit.
*/
err_t ota_tftp_download(const char *server, int port, const char *filename,
int timeout, int ota_slot, tftp_receive_cb receive_cb);
#define TFTP_PORT 69 #define TFTP_PORT 69
#endif #endif

View file

@ -4,9 +4,11 @@
// richardaburton@gmail.com // richardaburton@gmail.com
// See license.txt for license terms. // See license.txt for license terms.
// OTA code based on SDK sample from Espressif. // OTA code based on SDK sample from Espressif.
//
// esp-open-rtos additions Copyright 2016 Angus Gratton
////////////////////////////////////////////////// //////////////////////////////////////////////////
#include <rboot.h> #include <rboot-api.h>
#include <string.h> #include <string.h>
//#include <c_types.h> //#include <c_types.h>
//#include <spi_flash.h> //#include <spi_flash.h>
@ -209,6 +211,164 @@ bool ICACHE_FLASH_ATTR rboot_get_last_boot_mode(uint8 *mode) {
} }
#endif #endif
/* NOTE: Functions below here were added for esp-open-rtos only */
uint32_t rboot_get_slot_offset(uint8_t slot) {
rboot_config conf;
conf = rboot_get_config();
if (slot >= conf.count) return (uint32_t)-1;
return conf.roms[slot];
}
/* Structures for parsing the rboot OTA image format */
typedef struct __attribute__((packed)) {
uint8_t magic;
uint8_t section_count;
uint8_t val[2]; /* flash size & speed when placed @ offset 0, I thik ignored otherwise */
uint32_t entrypoint;
} image_header_t;
typedef struct __attribute__((packed)) {
uint32_t load_addr;
uint32_t length;
} section_header_t;
#define ROM_MAGIC_OLD 0xe9
#define ROM_MAGIC_NEW 0xea
bool rboot_verify_image(uint32_t initial_offset, uint32_t *image_length, const char **error_message)
{
uint32_t offset = initial_offset;
char *error = NULL;
RBOOT_DEBUG("rboot_verify_image: verifying image at 0x%08x\n", initial_offset);
if(offset % 4) {
error = "Unaligned flash offset";
goto fail;
}
/* sanity limit on how far we can read */
uint32_t end_limit = offset + 0x100000;
image_header_t image_header __attribute__((aligned(4)));
if(sdk_spi_flash_read(offset, &image_header, sizeof(image_header_t))) {
error = "Flash fail";
goto fail;
}
offset += sizeof(image_header_t);
if(image_header.magic != ROM_MAGIC_OLD && image_header.magic != ROM_MAGIC_NEW) {
error = "Missing initial magic";
goto fail;
}
bool is_new_header = (image_header.magic == ROM_MAGIC_NEW); /* a v1.2/rboot header, so expect a v1.1 header after the initial section */
int remaining_sections = image_header.section_count;
uint8_t checksum = CHKSUM_INIT;
while(remaining_sections > 0 && offset < end_limit)
{
/* read section header */
section_header_t header __attribute__((aligned(4)));
if(sdk_spi_flash_read(offset, &header, sizeof(section_header_t))) {
error = "Flash fail";
goto fail;
}
RBOOT_DEBUG("Found section @ 0x%08x (abs 0x%08x) length %d load 0x%08x\n", offset-initial_offset, offset, header.length, header.load_addr);
offset += sizeof(section_header_t);
if(header.length+offset > end_limit) {
break; /* sanity check: will reading section take us off end of expected flashregion? */
}
if(header.length % 4) {
error = "Header length not modulo 4";
goto fail;
}
if(!is_new_header) {
/* Add individual data of the section to the checksum. */
char chunk[16] __attribute__((aligned(4)));
for(int i = 0; i < header.length; i++) {
if(i % sizeof(chunk) == 0)
sdk_spi_flash_read(offset+i, (uint32_t *)chunk, sizeof(chunk));
checksum ^= chunk[i % sizeof(chunk)];
}
}
offset += header.length;
/* pad section to 4 byte align */
offset = (offset+3) & ~3;
remaining_sections--;
if(is_new_header) {
/* pad to a 16 byte offset */
offset = (offset+15) & ~15;
/* expect a v1.1 header here at start of "real" sections */
sdk_spi_flash_read(offset, (uint32_t *)&image_header, sizeof(image_header_t));
offset += sizeof(image_header_t);
if(image_header.magic != ROM_MAGIC_OLD) {
error = "Bad second magic";
goto fail;
}
remaining_sections = image_header.section_count;
is_new_header = false;
}
}
if(remaining_sections > 0) {
error = "Image truncated";
goto fail;
}
/* add a byte for the image checksum (actually comes after the padding) */
offset++;
/* pad the image length to a 16 byte boundary */
offset = (offset+15) & ~15;
uint32_t read_checksum;
sdk_spi_flash_read(offset-1, &read_checksum, 1);
if((uint8_t)read_checksum != checksum) {
error = "Invalid checksum";
goto fail;
}
RBOOT_DEBUG("rboot_verify_image: verified expected 0x%08x bytes.\n", offset - initial_offset);
if(image_length)
*image_length = offset - initial_offset;
return true;
fail:
if(error_message)
*error_message = error;
if(error) {
printf("%s: %s\n", __func__, error);
}
if(image_length)
*image_length = offset - initial_offset;
return false;
}
bool rboot_digest_image(uint32_t offset, uint32_t image_length, rboot_digest_update_fn update_fn, void *update_ctx)
{
uint8_t buf[32] __attribute__((aligned(4)));
for(int i = 0; i < image_length; i += sizeof(buf)) {
if(sdk_spi_flash_read(offset+i, buf, sizeof(buf)))
return false;
uint32_t digest_len = sizeof(buf);
if(i + digest_len > image_length)
digest_len = image_length - i;
update_fn(update_ctx, buf, digest_len);
}
return true;
}
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -12,6 +12,7 @@
* @{ * @{
*/ */
#include <rboot-integration.h>
#include <rboot.h> #include <rboot.h>
#ifdef __cplusplus #ifdef __cplusplus
@ -128,6 +129,49 @@ bool ICACHE_FLASH_ATTR rboot_get_last_boot_rom(uint8 *rom);
bool ICACHE_FLASH_ATTR rboot_get_last_boot_mode(uint8 *mode); bool ICACHE_FLASH_ATTR rboot_get_last_boot_mode(uint8 *mode);
#endif #endif
/* ADDITIONS TO RBOOT-API FOR ESP-OPEN-RTOS FOLLOW */
/* Returns offset of given rboot slot, or (uint32_t)-1 if slot is invalid.
*/
uint32_t rboot_get_slot_offset(uint8_t slot);
/** @description Verify basic image parameters - headers, CRC8 checksum.
@param Offset of image to verify. Can use rboot_get_slot_offset() to find.
@param Optional pointer will return the total valid length of the image.
@param Optional pointer to a static human-readable error message if fails.
@return True for valid, False for invalid.
**/
bool rboot_verify_image(uint32_t offset, uint32_t *image_length, const char **error_message);
/* @description Digest callback prototype, designed to be compatible with
mbedtls digest functions (SHA, MD5, etc.)
See the ota_basic example to see an example of calculating the
SHA256 digest of an OTA image.
*/
typedef void (*rboot_digest_update_fn)(void * ctx, void *data, size_t data_len);
/** @description Calculate a digest over the image at the offset specified
@note This function is actually a generic function that hashes SPI
flash contents, doesn't know anything about rboot image format. Use
rboot_verify_image to ensure a well-formed OTA image.
@param offset - Starting offset of image to hash (should be 4 byte aligned.)
@param image_length - Length of image to hash (should be 4 byte aligned.)
@param update_fn - Function to update digest (see rboot_digest_update_fn for details)
@param update_ctx - Context argument for digest update function.
@return True if digest completes successfully, false if digest function failed part way through
**/
bool rboot_digest_image(uint32_t offset, uint32_t image_length, rboot_digest_update_fn update_fn, void *update_ctx);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -1,14 +0,0 @@
#ifndef _RBOOT_CONFIG_H
/* rboot configuration parameters.
Must match the config in the compiler bootloader rboot.h. Values below are the rboot.h defaults.
Override rboot parameters by editing this file, or (much better
alternative) copy rboot-config.h to your program directory or
program/include/ directory, then edit it to override these values.
*/
#define RBOOT_MAX_ROMS 4
//#define RBOOT_CONFIG_CHECKSUM
#endif

View file

@ -1,132 +0,0 @@
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <espressif/spi_flash.h>
#include <espressif/esp_system.h>
#include <FreeRTOS.h>
#include <task.h>
#include <rboot.h>
#define ROM_MAGIC_OLD 0xe9
#define ROM_MAGIC_NEW 0xea
typedef struct __attribute__((packed)) {
uint8_t magic;
uint8_t section_count;
uint8_t val[2]; /* flash size & speed when placed @ offset 0, I think ignored otherwise */
uint32_t entrypoint;
} image_header_t;
typedef struct __attribute__((packed)) {
uint32_t load_addr;
uint32_t length;
} section_header_t;
// Check that a valid-looking rboot image is found at this offset on the flash, and
// takes up 'expected_length' bytes.
bool rboot_verify_image(uint32_t offset, uint32_t expected_length, const char **error_message)
{
char *error = NULL;
if(offset % 4) {
error = "Unaligned flash offset";
goto fail;
}
uint32_t end_offset = offset + expected_length;
image_header_t image_header;
sdk_spi_flash_read(offset, (uint32_t *)&image_header, sizeof(image_header_t));
offset += sizeof(image_header_t);
if(image_header.magic != ROM_MAGIC_OLD && image_header.magic != ROM_MAGIC_NEW) {
error = "Missing initial magic";
goto fail;
}
bool is_new_header = (image_header.magic == ROM_MAGIC_NEW); /* a v1.2/rboot header, so expect a v1.1 header after the initial section */
int remaining_sections = image_header.section_count;
uint8_t checksum = CHKSUM_INIT;
while(remaining_sections > 0 && offset < end_offset)
{
/* read section header */
section_header_t header;
sdk_spi_flash_read(offset, (uint32_t *)&header, sizeof(section_header_t));
RBOOT_DEBUG("Found section @ 0x%08lx length 0x%08lx load 0x%08lx padded 0x%08lx\r\n", offset, header.length, header.load_addr, header.length);
offset += sizeof(section_header_t);
if(header.length+offset > end_offset) {
break; /* sanity check: will reading section take us off end of expected flashregion? */
}
if(!is_new_header) {
/* Add individual data of the section to the checksum. */
char chunk[16];
for(int i = 0; i < header.length; i++) {
if(i % sizeof(chunk) == 0)
sdk_spi_flash_read(offset+i, (uint32_t *)chunk, sizeof(chunk));
checksum ^= chunk[i % sizeof(chunk)];
}
}
offset += header.length;
/* pad section to 4 byte align */
offset = (offset+3) & ~3;
remaining_sections--;
if(is_new_header) {
/* pad to a 16 byte offset */
offset = (offset+15) & ~15;
/* expect a v1.1 header here at start of "real" sections */
sdk_spi_flash_read(offset, (uint32_t *)&image_header, sizeof(image_header_t));
offset += sizeof(image_header_t);
if(image_header.magic != ROM_MAGIC_OLD) {
error = "Bad second magic";
goto fail;
}
remaining_sections = image_header.section_count;
is_new_header = false;
}
}
if(remaining_sections > 0) {
error = "Image truncated";
goto fail;
}
/* add a byte for the image checksum (actually comes after the padding) */
offset++;
/* pad the image length to a 16 byte boundary */
offset = (offset+15) & ~15;
uint8_t read_checksum;
sdk_spi_flash_read(offset-1, (uint32_t *)&read_checksum, 1); /* not sure if this will work */
if(read_checksum != checksum) {
error = "Invalid checksum";
goto fail;
}
if(offset != end_offset) {
error = "Wrong length";
goto fail;
}
RBOOT_DEBUG("rboot_verify_image: verified expected 0x%08lx bytes.\r\n", expected_length);
return true;
fail:
if(error_message)
*error_message = error;
printf("%s: %s\r\n", __func__, error);
return false;
}

View file

@ -9,6 +9,10 @@
#include <FreeRTOS.h> #include <FreeRTOS.h>
#include <task.h> #include <task.h>
/***************************************************
* Platform configuration definitions *
***************************************************/
#define uint8 uint8_t #define uint8 uint8_t
#define uint16 uint16_t #define uint16 uint16_t
#define uint32 uint32_t #define uint32 uint32_t
@ -28,7 +32,11 @@
#define RBOOT_DEBUG(f_, ...) #define RBOOT_DEBUG(f_, ...)
#endif #endif
// Check that a valid-looking rboot image is found at this offset on the flash, and /* Enable checksumming when writing out the config,
// takes up 'expected_length' bytes. so if the bootloader is built with checksumming then
bool rboot_verify_image(uint32_t offset, uint32_t expected_length, const char **error_message); it will still work.
*/
#define BOOT_CONFIG_CHKSUM
#endif // __RBOOT_INTEGRATION_H__ #endif // __RBOOT_INTEGRATION_H__

View file

@ -1,111 +0,0 @@
#ifndef __RBOOT_H__
#define __RBOOT_H__
//////////////////////////////////////////////////
// rBoot open source boot loader for ESP8266.
// Copyright 2015 Richard A Burton
// richardaburton@gmail.com
// See license.txt for license terms.
//////////////////////////////////////////////////
#ifdef __cplusplus
extern "C" {
#endif
#define RBOOT_INTEGRATION
// uncomment to use only c code
// if you aren't using gcc you may need to do this
//#define BOOT_NO_ASM
// uncomment to have a checksum on the boot config
//#define BOOT_CONFIG_CHKSUM
// uncomment to enable big flash support (>1MB)
#define BOOT_BIG_FLASH
// uncomment to enable 2 way communication between
// rBoot and the user app via the esp rtc data area
#define BOOT_RTC_ENABLED
// uncomment to enable GPIO booting
//#define BOOT_GPIO_ENABLED
// uncomment to include .irom0.text section in the checksum
// roms must be built with esptool2 using -iromchksum option
//#define BOOT_IROM_CHKSUM
// uncomment to add a boot delay, allows you time to connect
// a terminal before rBoot starts to run and output messages
// value is in microseconds
//#define BOOT_DELAY_MICROS 2000000
// increase if required
#define MAX_ROMS 4
#define CHKSUM_INIT 0xef
#define SECTOR_SIZE 0x1000
#define BOOT_CONFIG_SECTOR 1
#define BOOT_CONFIG_MAGIC 0xe1
#define BOOT_CONFIG_VERSION 0x01
#define MODE_STANDARD 0x00
#define MODE_GPIO_ROM 0x01
#define MODE_TEMP_ROM 0x02
#define RBOOT_RTC_MAGIC 0x2334ae68
#define RBOOT_RTC_READ 1
#define RBOOT_RTC_WRITE 0
#define RBOOT_RTC_ADDR 64
#ifdef RBOOT_INTEGRATION
#include <rboot-integration.h>
#endif
/** @brief Structure containing rBoot configuration
* @note ROM addresses must be multiples of 0x1000 (flash sector aligned).
* Without BOOT_BIG_FLASH only the first 8Mbit (1MB) of the chip will
* be memory mapped so ROM slots containing .irom0.text sections must
* remain below 0x100000. Slots beyond this will only be accessible via
* spi read calls, so use these for stored resources, not code. With
* BOOT_BIG_FLASH the flash will be mapped in chunks of 8MBit (1MB), so
* ROMs can be anywhere, but must not straddle two 8MBit (1MB) blocks.
* @ingroup rboot
*/
typedef struct {
uint8 magic; ///< Our magic, identifies rBoot configuration - should be BOOT_CONFIG_MAGIC
uint8 version; ///< Version of configuration structure - should be BOOT_CONFIG_VERSION
uint8 mode; ///< Boot loader mode (MODE_STANDARD | MODE_GPIO_ROM)
uint8 current_rom; ///< Currently selected ROM (will be used for next standard boot)
uint8 gpio_rom; ///< ROM to use for GPIO boot (hardware switch) with mode set to MODE_GPIO_ROM
uint8 count; ///< Quantity of ROMs available to boot
uint8 unused[2]; ///< Padding (not used)
uint32 roms[MAX_ROMS]; ///< Flash addresses of each ROM
#ifdef BOOT_CONFIG_CHKSUM
uint8 chksum; ///< Checksum of this configuration structure (if BOOT_CONFIG_CHKSUM defined)
#endif
} rboot_config;
#ifdef BOOT_RTC_ENABLED
/** @brief Structure containing rBoot status/control data
* @note This structure is used to, optionally, communicate between rBoot and
* the user app. It is stored in the ESP RTC data area.
* @ingroup rboot
*/
typedef struct {
uint32 magic; ///< Magic, identifies rBoot RTC data - should be RBOOT_RTC_MAGIC
uint8 next_mode; ///< The next boot mode, defaults to MODE_STANDARD - can be set to MODE_TEMP_ROM
uint8 last_mode; ///< The last (this) boot mode - can be MODE_STANDARD, MODE_GPIO_ROM or MODE_TEMP_ROM
uint8 last_rom; ///< The last (this) boot rom number
uint8 temp_rom; ///< The next boot rom number when next_mode set to MODE_TEMP_ROM
uint8 chksum; ///< Checksum of this structure this will be updated for you passed to the API
} rboot_rtc_data;
#endif
#ifdef __cplusplus
}
#endif
#endif

8
extras/sntp/component.mk Normal file
View file

@ -0,0 +1,8 @@
# Component makefile for extras/sntp
INC_DIRS += $(sntp_ROOT)
# args for passing into compile rule generation
sntp_SRC_DIR = $(sntp_ROOT)
$(eval $(call component_compile_rules,sntp))

748
extras/sntp/sntp.c Normal file
View file

@ -0,0 +1,748 @@
/**
* @file
* SNTP client module
*
*/
/*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
*
* Author: Simon Goldschmidt (lwIP raw API part)
*
* Modified by Jesus Alonso (doragasu) to allow setting servers dynamically.
*/
#include "lwip/opt.h"
#include "sntp.h"
#include "lwip/timers.h"
#include "lwip/udp.h"
#include "lwip/dns.h"
#include "lwip/ip_addr.h"
#include "lwip/pbuf.h"
#include <string.h>
#include <time.h>
#if LWIP_UDP
/** This is simple "SNTP" client for socket or raw API.
* It is a minimal implementation of SNTPv4 as specified in RFC 4330.
*
* For a list of some public NTP servers, see this link :
* http://support.ntp.org/bin/view/Servers/NTPPoolServers
*
* @todo:
* - set/change servers at runtime
* - complete SNTP_CHECK_RESPONSE checks 3 and 4
* - support broadcast/multicast mode?
*/
/** Decide whether to build SNTP for socket or raw API
* The socket API SNTP client is a very minimal implementation that does not
* fully confor to the SNTPv4 RFC, especially regarding server load and error
* procesing. */
#ifndef SNTP_SOCKET
#define SNTP_SOCKET 0
#endif
#if SNTP_SOCKET
#include "lwip/sockets.h"
#endif
/**
* SNTP_DEBUG: Enable debugging for SNTP.
*/
#ifndef SNTP_DEBUG
#define SNTP_DEBUG LWIP_DBG_OFF
#endif
/** SNTP server port */
#ifndef SNTP_PORT
#define SNTP_PORT 123
#endif
/** Set this to 1 to allow SNTP_SERVER_ADDRESS to be a DNS name */
#ifndef SNTP_SERVER_DNS
#define SNTP_SERVER_DNS 0
#endif
/** Set to the number of servers supported (at least 1) */
#ifndef SNTP_NUM_SERVERS_SUPPORTED
#define SNTP_NUM_SERVERS_SUPPORTED 1
#endif
/** SNTP server address:
* - as IPv4 address in "u32_t" format
* - as a DNS name if SNTP_SERVER_DNS is set to 1
* May contain multiple server names (e.g. "pool.ntp.org","second.time.server")
*/
#ifndef SNTP_SERVER_ADDRESS
#if SNTP_SERVER_DNS
#define SNTP_SERVER_ADDRESS "pool.ntp.org"
#else
#define SNTP_SERVER_ADDRESS "213.161.194.93" /* pool.ntp.org */
#endif
#endif
/** Sanity check:
* Define this to
* - 0 to turn off sanity checks (default; smaller code)
* - >= 1 to check address and port of the response packet to ensure the
* response comes from the server we sent the request to.
* - >= 2 to check returned Originate Timestamp against Transmit Timestamp
* sent to the server (to ensure response to older request).
* - >= 3 @todo: discard reply if any of the LI, Stratum, or Transmit Timestamp
* fields is 0 or the Mode field is not 4 (unicast) or 5 (broadcast).
* - >= 4 @todo: to check that the Root Delay and Root Dispersion fields are each
* greater than or equal to 0 and less than infinity, where infinity is
* currently a cozy number like one second. This check avoids using a
* server whose synchronization source has expired for a very long time.
*/
#ifndef SNTP_CHECK_RESPONSE
#define SNTP_CHECK_RESPONSE 0
#endif
/** According to the RFC, this shall be a random delay
* between 1 and 5 minutes (in milliseconds) to prevent load peaks.
* This can be defined to a random generation function,
* which must return the delay in milliseconds as u32_t.
* Turned off by default.
*/
#ifndef SNTP_STARTUP_DELAY
#define SNTP_STARTUP_DELAY 0
#endif
/** SNTP receive timeout - in milliseconds
* Also used as retry timeout - this shouldn't be too low.
* Default is 3 seconds.
*/
#ifndef SNTP_RECV_TIMEOUT
#define SNTP_RECV_TIMEOUT 3000
#endif
/** SNTP update delay - in milliseconds
* Default is 1 hour.
*/
#ifndef SNTP_UPDATE_DELAY
#define SNTP_UPDATE_DELAY 3600000
#endif
#if (SNTP_UPDATE_DELAY < 15000) && !SNTP_SUPPRESS_DELAY_CHECK
#error "SNTPv4 RFC 4330 enforces a minimum update time of 15 seconds!"
#endif
/** SNTP macro to change system time and/or the update the RTC clock */
#ifndef SNTP_SET_SYSTEM_TIME
#define SNTP_SET_SYSTEM_TIME(sec) ((void)sec)
#endif
/** SNTP macro to change system time including microseconds */
#ifdef SNTP_SET_SYSTEM_TIME_US
#define SNTP_CALC_TIME_US 1
#define SNTP_RECEIVE_TIME_SIZE 2
#else
#define SNTP_SET_SYSTEM_TIME_US(sec, us)
#define SNTP_CALC_TIME_US 0
#define SNTP_RECEIVE_TIME_SIZE 1
#endif
/** SNTP macro to get system time, used with SNTP_CHECK_RESPONSE >= 2
* to send in request and compare in response.
*/
#ifndef SNTP_GET_SYSTEM_TIME
#define SNTP_GET_SYSTEM_TIME(sec, us) do { (sec) = 0; (us) = 0; } while(0)
#endif
/** Default retry timeout (in milliseconds) if the response
* received is invalid.
* This is doubled with each retry until SNTP_RETRY_TIMEOUT_MAX is reached.
*/
#ifndef SNTP_RETRY_TIMEOUT
#define SNTP_RETRY_TIMEOUT SNTP_RECV_TIMEOUT
#endif
/** Maximum retry timeout (in milliseconds). */
#ifndef SNTP_RETRY_TIMEOUT_MAX
#define SNTP_RETRY_TIMEOUT_MAX (SNTP_RETRY_TIMEOUT * 10)
#endif
/** Increase retry timeout with every retry sent
* Default is on to conform to RFC.
*/
#ifndef SNTP_RETRY_TIMEOUT_EXP
#define SNTP_RETRY_TIMEOUT_EXP 1
#endif
/* the various debug levels for this file */
#define SNTP_DEBUG_TRACE (SNTP_DEBUG | LWIP_DBG_TRACE)
#define SNTP_DEBUG_STATE (SNTP_DEBUG | LWIP_DBG_STATE)
#define SNTP_DEBUG_WARN (SNTP_DEBUG | LWIP_DBG_LEVEL_WARNING)
#define SNTP_DEBUG_WARN_STATE (SNTP_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE)
#define SNTP_DEBUG_SERIOUS (SNTP_DEBUG | LWIP_DBG_LEVEL_SERIOUS)
#define SNTP_ERR_KOD 1
/* SNTP protocol defines */
#define SNTP_MSG_LEN 48
#define SNTP_OFFSET_LI_VN_MODE 0
#define SNTP_LI_MASK 0xC0
#define SNTP_LI_NO_WARNING 0x00
#define SNTP_LI_LAST_MINUTE_61_SEC 0x01
#define SNTP_LI_LAST_MINUTE_59_SEC 0x02
#define SNTP_LI_ALARM_CONDITION 0x03 /* (clock not synchronized) */
#define SNTP_VERSION_MASK 0x38
#define SNTP_VERSION (4/* NTP Version 4*/<<3)
#define SNTP_MODE_MASK 0x07
#define SNTP_MODE_CLIENT 0x03
#define SNTP_MODE_SERVER 0x04
#define SNTP_MODE_BROADCAST 0x05
#define SNTP_OFFSET_STRATUM 1
#define SNTP_STRATUM_KOD 0x00
#define SNTP_OFFSET_ORIGINATE_TIME 24
#define SNTP_OFFSET_RECEIVE_TIME 32
#define SNTP_OFFSET_TRANSMIT_TIME 40
/* number of seconds between 1900 and 1970 */
#define DIFF_SEC_1900_1970 (2208988800UL)
/**
* SNTP packet format (without optional fields)
* Timestamps are coded as 64 bits:
* - 32 bits seconds since Jan 01, 1970, 00:00
* - 32 bits seconds fraction (0-padded)
* For future use, if the MSB in the seconds part is set, seconds are based
* on Feb 07, 2036, 06:28:16.
*/
#ifdef PACK_STRUCT_USE_INCLUDES
# include "arch/bpstruct.h"
#endif
PACK_STRUCT_BEGIN
struct sntp_msg {
PACK_STRUCT_FIELD(u8_t li_vn_mode);
PACK_STRUCT_FIELD(u8_t stratum);
PACK_STRUCT_FIELD(u8_t poll);
PACK_STRUCT_FIELD(u8_t precision);
PACK_STRUCT_FIELD(u32_t root_delay);
PACK_STRUCT_FIELD(u32_t root_dispersion);
PACK_STRUCT_FIELD(u32_t reference_identifier);
PACK_STRUCT_FIELD(u32_t reference_timestamp[2]);
PACK_STRUCT_FIELD(u32_t originate_timestamp[2]);
PACK_STRUCT_FIELD(u32_t receive_timestamp[2]);
PACK_STRUCT_FIELD(u32_t transmit_timestamp[2]);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END
#ifdef PACK_STRUCT_USE_INCLUDES
# include "arch/epstruct.h"
#endif
/* function prototypes */
static void sntp_request(void *arg);
/** The UDP pcb used by the SNTP client */
static struct udp_pcb* sntp_pcb;
/** Addresses of servers */
static char* sntp_server_addresses[SNTP_NUM_SERVERS_SUPPORTED];
/** The currently used server (initialized to 0) */
static u8_t sntp_num_servers;
#if (SNTP_NUM_SERVERS_SUPPORTED > 1)
static u8_t sntp_current_server;
#else
#define sntp_current_server 0
#endif /* SNTP_NUM_SERVERS_SUPPORTED */
static u32_t sntp_update_delay = SNTP_UPDATE_DELAY;
#if SNTP_RETRY_TIMEOUT_EXP
#define SNTP_RESET_RETRY_TIMEOUT() sntp_retry_timeout = SNTP_RETRY_TIMEOUT
/** Retry time, initialized with SNTP_RETRY_TIMEOUT and doubled with each retry. */
static u32_t sntp_retry_timeout;
#else /* SNTP_RETRY_TIMEOUT_EXP */
#define SNTP_RESET_RETRY_TIMEOUT()
#define sntp_retry_timeout SNTP_RETRY_TIMEOUT
#endif /* SNTP_RETRY_TIMEOUT_EXP */
#if SNTP_CHECK_RESPONSE >= 1
/** Saves the last server address to compare with response */
static ip_addr_t sntp_last_server_address;
#endif /* SNTP_CHECK_RESPONSE >= 1 */
#if SNTP_CHECK_RESPONSE >= 2
/** Saves the last timestamp sent (which is sent back by the server)
* to compare against in response */
static u32_t sntp_last_timestamp_sent[2];
#endif /* SNTP_CHECK_RESPONSE >= 2 */
/**
* SNTP processing of received timestamp
*/
static void
sntp_process(u32_t *receive_timestamp)
{
/* convert SNTP time (1900-based) to unix GMT time (1970-based)
* @todo: if MSB is 1, SNTP time is 2036-based!
*/
time_t t = (ntohl(receive_timestamp[0]) - DIFF_SEC_1900_1970);
#if SNTP_CALC_TIME_US
u32_t us = ntohl(receive_timestamp[1]) / 4295;
SNTP_SET_SYSTEM_TIME_US(t, us);
/* display local time from GMT time */
LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s, %"U32_F" us", ctime(&t), us));
#else /* SNTP_CALC_TIME_US */
/* change system time and/or the update the RTC clock */
SNTP_SET_SYSTEM_TIME(t);
/* display local time from GMT time */
LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s", ctime(&t)));
#endif /* SNTP_CALC_TIME_US */
}
/**
* Initialize request struct to be sent to server.
*/
static void
sntp_initialize_request(struct sntp_msg *req)
{
memset(req, 0, SNTP_MSG_LEN);
req->li_vn_mode = SNTP_LI_NO_WARNING | SNTP_VERSION | SNTP_MODE_CLIENT;
#if SNTP_CHECK_RESPONSE >= 2
{
u32_t sntp_time_sec, sntp_time_us;
/* fill in transmit timestamp and save it in 'sntp_last_timestamp_sent' */
SNTP_GET_SYSTEM_TIME(sntp_time_sec, sntp_time_us);
sntp_last_timestamp_sent[0] = htonl(sntp_time_sec + DIFF_SEC_1900_1970);
req->transmit_timestamp[0] = sntp_last_timestamp_sent[0];
/* we send/save us instead of fraction to be faster... */
sntp_last_timestamp_sent[1] = htonl(sntp_time_us);
req->transmit_timestamp[1] = sntp_last_timestamp_sent[1];
}
#endif /* SNTP_CHECK_RESPONSE >= 2 */
}
#if SNTP_SOCKET
/**
* Send an SNTP request via sockets.
* This is a very minimal implementation that does not fully conform
* to the SNTPv4 RFC, especially regarding server load and error procesing.
*/
static void
sntp_request(void *arg)
{
int sock;
struct sockaddr_in local;
struct sockaddr_in to;
int tolen;
int size;
int timeout;
struct sntp_msg sntpmsg;
ip_addr_t sntp_server_address;
LWIP_UNUSED_ARG(arg);
/* if we got a valid SNTP server address... */
if (ipaddr_aton(SNTP_SERVER_ADDRESS, &sntp_server_address)) {
/* create new socket */
sock = lwip_socket(AF_INET, SOCK_DGRAM, 0);
if (sock >= 0) {
/* prepare local address */
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = PP_HTONS(INADDR_ANY);
local.sin_addr.s_addr = PP_HTONL(INADDR_ANY);
/* bind to local address */
if (lwip_bind(sock, (struct sockaddr *)&local, sizeof(local)) == 0) {
/* set recv timeout */
timeout = SNTP_RECV_TIMEOUT;
lwip_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
/* prepare SNTP request */
sntp_initialize_request(&sntpmsg);
/* prepare SNTP server address */
memset(&to, 0, sizeof(to));
to.sin_family = AF_INET;
to.sin_port = PP_HTONS(SNTP_PORT);
inet_addr_from_ipaddr(&to.sin_addr, &sntp_server_address);
/* send SNTP request to server */
if (lwip_sendto(sock, &sntpmsg, SNTP_MSG_LEN, 0, (struct sockaddr *)&to, sizeof(to)) >= 0) {
/* receive SNTP server response */
tolen = sizeof(to);
size = lwip_recvfrom(sock, &sntpmsg, SNTP_MSG_LEN, 0, (struct sockaddr *)&to, (socklen_t *)&tolen);
/* if the response size is good */
if (size == SNTP_MSG_LEN) {
/* if this is a SNTP response... */
if (((sntpmsg.li_vn_mode & SNTP_MODE_MASK) == SNTP_MODE_SERVER) ||
((sntpmsg.li_vn_mode & SNTP_MODE_MASK) == SNTP_MODE_BROADCAST)) {
/* do time processing */
sntp_process(sntpmsg.receive_timestamp);
} else {
LWIP_DEBUGF( SNTP_DEBUG_WARN, ("sntp_request: not response frame code\n"));
}
}
} else {
LWIP_DEBUGF( SNTP_DEBUG_WARN, ("sntp_request: not sendto==%i\n", errno));
}
}
/* close the socket */
closesocket(sock);
}
}
}
/**
* SNTP thread
*/
static void
sntp_thread(void *arg)
{
LWIP_UNUSED_ARG(arg);
while(1) {
sntp_request(NULL);
sys_msleep(sntp_update_delay);
}
}
/**
* Initialize this module when using sockets
*/
void
sntp_init(void)
{
sys_thread_new("sntp_thread", sntp_thread, NULL, DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_PRIO);
}
#else /* SNTP_SOCKET */
/**
* Retry: send a new request (and increase retry timeout).
*
* @param arg is unused (only necessary to conform to sys_timeout)
*/
static void
sntp_retry(void* arg)
{
LWIP_UNUSED_ARG(arg);
LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_retry: Next request will be sent in %"U32_F" ms\n",
sntp_retry_timeout));
/* set up a timer to send a retry and increase the retry delay */
sys_timeout(sntp_retry_timeout, sntp_request, NULL);
#if SNTP_RETRY_TIMEOUT_EXP
{
u32_t new_retry_timeout;
/* increase the timeout for next retry */
new_retry_timeout = sntp_retry_timeout << 1;
/* limit to maximum timeout and prevent overflow */
if ((new_retry_timeout <= SNTP_RETRY_TIMEOUT_MAX) &&
(new_retry_timeout > sntp_retry_timeout)) {
sntp_retry_timeout = new_retry_timeout;
}
}
#endif /* SNTP_RETRY_TIMEOUT_EXP */
}
#if (SNTP_NUM_SERVERS_SUPPORTED > 1)
/**
* If Kiss-of-Death is received (or another packet parsing error),
* try the next server or retry the current server and increase the retry
* timeout if only one server is available.
*
* @param arg is unused (only necessary to conform to sys_timeout)
*/
static void
sntp_try_next_server(void* arg)
{
LWIP_UNUSED_ARG(arg);
if (sntp_num_servers > 1) {
/* new server: reset retry timeout */
SNTP_RESET_RETRY_TIMEOUT();
sntp_current_server++;
if (sntp_current_server >= sntp_num_servers) {
sntp_current_server = 0;
}
LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_try_next_server: Sending request to server %"U16_F"\n",
(u16_t)sntp_current_server));
/* instantly send a request to the next server */
sntp_request(NULL);
} else {
sntp_retry(NULL);
}
}
#else /* SNTP_NUM_SERVERS_SUPPORTED > 1 */
/* Always retry on error if only one server is supported */
#define sntp_try_next_server sntp_retry
#endif /* SNTP_NUM_SERVERS_SUPPORTED > 1 */
/** UDP recv callback for the sntp pcb */
static void
sntp_recv(void *arg, struct udp_pcb* pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
{
u8_t mode;
u8_t stratum;
u32_t receive_timestamp[SNTP_RECEIVE_TIME_SIZE];
err_t err;
LWIP_UNUSED_ARG(arg);
LWIP_UNUSED_ARG(pcb);
/* packet received: stop retry timeout */
sys_untimeout(sntp_try_next_server, NULL);
sys_untimeout(sntp_request, NULL);
err = ERR_ARG;
#if SNTP_CHECK_RESPONSE >= 1
/* check server address and port */
if (ip_addr_cmp(addr, &sntp_last_server_address) &&
(port == SNTP_PORT))
#else /* SNTP_CHECK_RESPONSE >= 1 */
LWIP_UNUSED_ARG(addr);
LWIP_UNUSED_ARG(port);
#endif /* SNTP_CHECK_RESPONSE >= 1 */
{
/* process the response */
if (p->tot_len == SNTP_MSG_LEN) {
pbuf_copy_partial(p, &mode, 1, SNTP_OFFSET_LI_VN_MODE);
mode &= SNTP_MODE_MASK;
/* if this is a SNTP response... */
if ((mode == SNTP_MODE_SERVER) ||
(mode == SNTP_MODE_BROADCAST)) {
pbuf_copy_partial(p, &stratum, 1, SNTP_OFFSET_STRATUM);
if (stratum == SNTP_STRATUM_KOD) {
/* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */
err = SNTP_ERR_KOD;
LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Received Kiss-of-Death\n"));
} else {
#if SNTP_CHECK_RESPONSE >= 2
/* check originate_timetamp against sntp_last_timestamp_sent */
u32_t originate_timestamp[2];
pbuf_copy_partial(p, &originate_timestamp, 8, SNTP_OFFSET_ORIGINATE_TIME);
if ((originate_timestamp[0] != sntp_last_timestamp_sent[0]) ||
(originate_timestamp[1] != sntp_last_timestamp_sent[1]))
{
LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid originate timestamp in response\n"));
} else
#endif /* SNTP_CHECK_RESPONSE >= 2 */
/* @todo: add code for SNTP_CHECK_RESPONSE >= 3 and >= 4 here */
{
/* correct answer */
err = ERR_OK;
pbuf_copy_partial(p, &receive_timestamp, SNTP_RECEIVE_TIME_SIZE * 4, SNTP_OFFSET_RECEIVE_TIME);
}
}
} else {
LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid mode in response: %"U16_F"\n", (u16_t)mode));
}
} else {
LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid packet length: %"U16_F"\n", p->tot_len));
}
}
pbuf_free(p);
if (err == ERR_OK) {
/* Correct response, reset retry timeout */
SNTP_RESET_RETRY_TIMEOUT();
sntp_process(receive_timestamp);
/* Set up timeout for next request */
sys_timeout(sntp_update_delay, sntp_request, NULL);
LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Scheduled next time request: %"U32_F" ms\n",
sntp_update_delay));
} else if (err == SNTP_ERR_KOD) {
/* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */
sntp_try_next_server(NULL);
} else {
/* another error, try the same server again */
sntp_retry(NULL);
}
}
/** Actually send an sntp request to a server.
*
* @param server_addr resolved IP address of the SNTP server
*/
static void
sntp_send_request(ip_addr_t *server_addr)
{
struct pbuf* p;
p = pbuf_alloc(PBUF_TRANSPORT, SNTP_MSG_LEN, PBUF_RAM);
if (p != NULL) {
struct sntp_msg *sntpmsg = (struct sntp_msg *)p->payload;
LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_send_request: Sending request to server\n"));
/* initialize request message */
sntp_initialize_request(sntpmsg);
/* send request */
udp_sendto(sntp_pcb, p, server_addr, SNTP_PORT);
/* set up receive timeout: try next server or retry on timeout */
sys_timeout((u32_t)SNTP_RECV_TIMEOUT, sntp_try_next_server, NULL);
#if SNTP_CHECK_RESPONSE >= 1
/* save server address to verify it in sntp_recv */
ip_addr_set(&sntp_last_server_address, server_addr);
#endif /* SNTP_CHECK_RESPONSE >= 1 */
} else {
LWIP_DEBUGF(SNTP_DEBUG_SERIOUS, ("sntp_send_request: Out of memory, trying again in %"U32_F" ms\n",
(u32_t)SNTP_RETRY_TIMEOUT));
/* out of memory: set up a timer to send a retry */
sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_request, NULL);
}
}
#if SNTP_SERVER_DNS
/**
* DNS found callback when using DNS names as server address.
*/
static void
sntp_dns_found(const char* hostname, ip_addr_t *ipaddr, void *arg)
{
LWIP_UNUSED_ARG(hostname);
LWIP_UNUSED_ARG(arg);
if (ipaddr != NULL) {
/* Address resolved, send request */
LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_dns_found: Server address resolved, sending request\n"));
sntp_send_request(ipaddr);
} else {
/* DNS resolving failed -> try another server */
LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("sntp_dns_found: Failed to resolve server address resolved, trying next server\n"));
sntp_try_next_server(NULL);
}
}
#endif /* SNTP_SERVER_DNS */
/**
* Send out an sntp request via raw API.
*
* @param arg is unused (only necessary to conform to sys_timeout)
*/
static void
sntp_request(void *arg)
{
ip_addr_t sntp_server_address;
err_t err;
LWIP_UNUSED_ARG(arg);
/* initialize SNTP server address */
#if SNTP_SERVER_DNS
err = dns_gethostbyname(sntp_server_addresses[sntp_current_server], &sntp_server_address,
sntp_dns_found, NULL);
if (err == ERR_INPROGRESS) {
/* DNS request sent, wait for sntp_dns_found being called */
LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_request: Waiting for server address to be resolved.\n"));
return;
}
#else /* SNTP_SERVER_DNS */
err = ipaddr_aton(sntp_server_addresses[sntp_current_server], &sntp_server_address)
? ERR_OK : ERR_ARG;
#endif /* SNTP_SERVER_DNS */
if (err == ERR_OK) {
sntp_send_request(&sntp_server_address);
} else {
/* address conversion failed, try another server */
LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("sntp_request: Invalid server address, trying next server.\n"));
sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_try_next_server, NULL);
}
}
/**
* Initialize this module when using raw API.
* Send out request instantly or after SNTP_STARTUP_DELAY.
*/
void
sntp_init(void)
{
char *def_addr[] = {SNTP_SERVER_ADDRESS};
sntp_num_servers = 0;
sntp_set_servers(def_addr, sizeof(def_addr) / sizeof(char*));
SNTP_RESET_RETRY_TIMEOUT();
sntp_pcb = udp_new();
LWIP_ASSERT("Failed to allocate udp pcb for sntp client", sntp_pcb != NULL);
if (sntp_pcb != NULL) {
udp_recv(sntp_pcb, sntp_recv, NULL);
#if SNTP_STARTUP_DELAY
sys_timeout((u32_t)SNTP_STARTUP_DELAY, sntp_request, NULL);
#else
sntp_request(NULL);
#endif
}
}
#endif /* SNTP_SOCKET */
/* Additions to allow dynamically settings servers and update delay */
/**
* Set the NTP servers
*/
int sntp_set_servers(char *server_url[], int num_servers)
{
int i;
/* Check number of servers requested */
if (SNTP_NUM_SERVERS_SUPPORTED < num_servers) return -1;
/* Free previously allocated buffers */
for (i = sntp_num_servers - 1; i >= 0; i--) {
free(sntp_server_addresses[i]);
sntp_server_addresses[i] = NULL;
}
/* Allocate memory and copy servers */
for (i = 0; i < num_servers; i++) {
sntp_server_addresses[i] = malloc(strlen(server_url[i]));
if (sntp_server_addresses[i]) {
strcpy(sntp_server_addresses[i], server_url[i]);
} else {
sntp_num_servers = i;
return -2;
}
}
sntp_num_servers = num_servers;
return 0;
}
void sntp_set_update_delay(uint32_t ms)
{
sntp_update_delay = ms > 15000?ms:15000;
}
#endif /* LWIP_UDP */

76
extras/sntp/sntp.h Normal file
View file

@ -0,0 +1,76 @@
/************************************************************************//**
* RTC time keeping and synchronization using SNTP for esp-open-rtos.
* Uses SNTP lwIP contribution.
*
* 2016, Jesus Alonso (doragasu)
****************************************************************************/
#ifndef _SNTP_H_
#define _SNTP_H_
#include <stdint.h>
#include <sys/time.h>
#include <time.h>
/*
* Function used by lwIP sntp module to update the date/time,
* with microseconds resolution.
*/
#define SNTP_SET_SYSTEM_TIME_US(sec, us) sntp_update_rtc(sec, us)
/*
* For the lwIP implementation of SNTP to allow using names for NTP servers.
*/
#define SNTP_SERVER_DNS 1
/*
* Number of supported NTP servers
*/
#define SNTP_NUM_SERVERS_SUPPORTED 4
/*
* Initialize the module, and start requesting SNTP updates. This function
* must be called only once.
* WARNING: tz->tz_dsttime doesn't have the same meaning as the standard
* implementation. If it is set to 1, a dst hour will be applied. If set
* to zero, time will not be modified.
*/
void sntp_initialize(const struct timezone *tz);
/*
* Sets time zone. Allowed values are in the range [-11, 13].
* NOTE: Settings do not take effect until SNTP time is updated. It is
* recommended to set these parameters only during initialization.
* WARNING: tz->tz_dsttime doesn't have the same meaning as the standard
* implementation. If it is set to 1, a dst hour will be applied. If set
* to zero, time will not be modified.
*/
void sntp_set_timezone(const struct timezone *tz);
/*
* Set SNTP servers. Up to SNTP_NUM_SERVERS_SUPPORTED can be set.
* Returns 0 if OK, less than 0 if error.
* NOTE: This function must NOT be called before sntp_initialize().
*/
int sntp_set_servers(char *server_url[], int num_servers);
/*
* Sets the update delay in ms. If requested value is less than 15s,
* a 15s update interval will be set.
*/
void sntp_set_update_delay(uint32_t ms);
/*
* Returns the time read from RTC counter, in seconds from Epoch. If
* us is not null, it will be filled with the microseconds.
*/
time_t sntp_get_rtc_time(int32_t *us);
/*
* Update RTC timer. This function is called by the SNTP module each time
* an SNTP update is received.
*/
void sntp_update_rtc(time_t t, uint32_t us);
#endif /* _SNTP_H_ */

112
extras/sntp/sntp_fun.c Normal file
View file

@ -0,0 +1,112 @@
/*
* Auxiliary functions to handle date/time along with lwIP sntp implementation.
*
* Jesus Alonso (doragasu)
*/
#include <sys/reent.h>
#include <sys/types.h>
#include <sys/errno.h>
#include <stdio.h>
#include <espressif/esp_common.h>
#include <esp/timer.h>
#include <esp/rtc_regs.h>
#include "sntp.h"
#define TIMER_COUNT RTC.COUNTER
// daylight settings
// Base calculated with value obtained from NTP server (64 bits)
#define sntp_base (*((uint64_t*)RTC.SCRATCH))
// Timer value when base was obtained
#define tim_ref (RTC.SCRATCH[2])
// Calibration value
#define cal (RTC.SCRATCH[3])
// Timezone related data.
static struct timezone stz;
// Implemented in sntp.c
void sntp_init(void);
// Sets time zone.
// NOTE: Settings do not take effect until SNTP time is updated.
void sntp_set_timezone(const struct timezone *tz) {
if (tz) {
stz = *tz;
} else {
stz.tz_minuteswest = 0;
stz.tz_dsttime = 0;
}
}
// Initialization
void sntp_initialize(const struct timezone *tz) {
if (tz) {
stz = *tz;
} else {
stz.tz_minuteswest = 0;
stz.tz_dsttime = 0;
}
sntp_base = 0;
// To avoid div by 0 exceptions if requesting time before SNTP config
cal = 1;
tim_ref = TIMER_COUNT;
sntp_init();
}
// Check if a timer wrap has occurred. Compensate sntp_base reference
// if affirmative.
// TODO: think about multitasking and race conditions
static inline void sntp_check_timer_wrap(uint32_t current_value) {
if (current_value < tim_ref) {
// Timer wrap has occurred, compensate by subtracting 2^32 to ref.
sntp_base -= 1LLU<<32;
// DEBUG
printf("\nTIMER WRAPPED!\n");
}
}
// Return secs. If us is not a null pointer, fill it with usecs
inline time_t sntp_get_rtc_time(int32_t *us) {
time_t secs;
uint32_t tim;
uint64_t base;
tim = TIMER_COUNT;
// Check for timer wrap
sntp_check_timer_wrap(tim);
base = sntp_base + tim - tim_ref;
secs = base * cal / (1000000U<<12);
if (us) {
*us = base * cal % (1000000U<<12);
}
return secs;
}
// Syscall implementation. doesn't seem to use tzp.
int _gettimeofday_r(struct _reent *r, struct timeval *tp, void *tzp) {
(void)r;
// Syscall defined by xtensa newlib defines tzp as void*
// So it looks like it is not used. Also check tp is not NULL
if (tzp || !tp) return EINVAL;
tp->tv_sec = sntp_get_rtc_time((int32_t*)&tp->tv_usec);
return 0;
}
// Update RTC timer. Called by SNTP module each time it receives an update.
void sntp_update_rtc(time_t t, uint32_t us) {
// Apply daylight and timezone correction
t += (stz.tz_minuteswest + stz.tz_dsttime * 60) * 60;
// DEBUG: Compute and print drift
int64_t sntp_current = sntp_base + TIMER_COUNT - tim_ref;
int64_t sntp_correct = (((uint64_t)us + (uint64_t)t * 1000000U)<<12) / cal;
printf("\nRTC Adjust: drift = %ld ticks, cal = %d\n", (time_t)(sntp_correct - sntp_current), cal);
tim_ref = TIMER_COUNT;
cal = sdk_system_rtc_clock_cali_proc();
sntp_base = (((uint64_t)us + (uint64_t)t * 1000000U)<<12) / cal;
}

View file

@ -66,7 +66,7 @@
//}} //}}
//DPORT{{ //DPORT{{
#define HOST_INF_SEL (0x28) #define HOST_INF_SEL (PERIPHS_DPORT_BASEADDR + 0x28)
#define DPORT_LINK_DEVICE_SEL 0x000000FF #define DPORT_LINK_DEVICE_SEL 0x000000FF
#define DPORT_LINK_DEVICE_SEL_S 8 #define DPORT_LINK_DEVICE_SEL_S 8
#define DPORT_PERI_IO_SWAP 0x000000FF #define DPORT_PERI_IO_SWAP 0x000000FF

View file

@ -0,0 +1,482 @@
/** Internal Espressif SDK "PHY info" data structure
The data structure (sdk_phy_info_t) is used to configure the
ESP8266 PHY layer via the SDK. The fields here are not written
directly to hardware, the SDK code (mostly in libphy) parses this
structure and configures the hardware.
The structure loaded at reset time from a flash configuration
sector (see read_saved_phy_info()) (Espressif's SDK sources this
from a file "esp_init_data_default.bin"). If no valid structure is
found in the flash config sector then the SDK loads default values
(see get_default_phy_info()). It is possible to implement a custom
get_default_phy_info() to change the PHY default settings (see the
'version' field below).
@note It is possible that the SDK will quietly write a new
configuration sector to flash itself following internal
calibration, etc. However this does not seem to happen, you need to
flash it explicitly if you want it stored there.
@note Most of what is below is unconfirmed, except where a @note
says that it has been confirmed to work as expected. Please
consider submitting notes if you find behaviour here that works or
doesn't work as expected.
Information on the meaning/offset of fields comes from Espressif's
flash download tool, which uses an Excel spreadsheet (in the
init_data directory of the ZIP file) to configure and a Python
script to convert an esp_init_data_custom.bin file to flash:
http://bbs.espressif.com/viewtopic.php?f=5&t=433
Many fields remain undocumented (& disassembly of libphy suggests
that some documented fields supported undocumented values.)
A few additional notes about the phy_info fields can be found
in the ESP Arduino ESP8266 phy_init_data structure (however most of
that content is verbatim from Espressif's spreadsheet):
https://github.com/esp8266/Arduino/blob/master/cores/esp8266/core_esp8266_phy.c#L29
Part of esp-open-rtos. Copyright (C) 2016 Angus Gratton,
BSD Licensed as described in the file LICENSE.
*/
#ifndef _ESPRESSIF_PHY_INFO_H
#define _ESPRESSIF_PHY_INFO_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
/* CRYSTAL_FREQ_xx values as used by sdk_phy_info_t.crystal_freq */
#define CRYSTAL_FREQ_40M 0
#define CRYSTAL_FREQ_26M 1
#define CRYSTAL_FREQ_24M 2
/* SDIO_CONFIG_xx values as used by sdk_phy_info_t.sdio_config */
#define SDIO_CONFIG_AUTO 0 /* Uses pin strapping to determine */
#define SDIO_CONFIG_SDIOV1_1 /* Data output on negative edge */
#define SDIO_CONFIG_SDIOV2_0 /* data output on positive edge */
/* BT_COEXIST_CONFIG_xx values as used by sdk_phy_info_t.bt_coexist */
/* No bluetooth */
#define BT_COEXIST_CONFIG_NONE 0
/* Coexistence configuration A:
GPIO 0 - WLAN_ACTIVE
GPIO 14 - BT_ACTIVE
GPIO 13 - BT_PRIORITY
GPIO 3 - ANT_SEL_BT
*/
#define BT_COEXIST_CONFIG_A 1
/* No coexistence, but Bluetooth enabled?
Unsure how this works?
*/
#define BT_COEXIST_CONFIG_PRESENT 2
/* Coexistence configuration B:
GPIO 0 - WLAN_ACTIVE
GPIO 14 - BT_PRIORITY
GPIO 13 - BT_ACTIVE
GPIO 3 - ANT_SEL_BT
*/
#define BT_COEXIST_CONFIG_B 3
/* BT_COEXIST_PROTOCOL_xx values for coexistence protocol,
field sdk_phy_info_t.bt_coexist_protocol
*/
#define BT_COEXIST_PROTOCOL_WIFI_ONLY 0
#define BT_COEXIST_PROTOCOL_BT_ONLY 1
/* Coexistence is enabled, Bluetooth has its own antenna */
#define BT_COEXIST_PROTOCOL_FLAG_SEPARATE_ANT 2
/* Coexistence is enabled, Bluetooth shares WiFi antenna */
#define BT_COEXIST_PROTOCOL_FLAG_SHARE_ANT 4
/* Coexistence is enabled, use only BT_ACTIVE signal */
#define BT_COEXIST_PROTOCOL_FLAG_BT_ACTIVE_ONLY 0
/* Coexistence is enabled, use both BT_ACTIVE and BT_PRIORITY signals */
#define BT_COEXIST_PROTOCOL_FLAG_BT_ACTIVE_PRIORITY 1
/* DUAL_ANT_CONFIG_xx values for dual antenna config,
field sdk_phy_info_t.dual_ant_config
(Not really clear how this feature works, if at all.)
*/
#define DUAL_ANT_CONFIG_NONE 0
/* antenna diversity for WiFi, use GPIO0 + U0RXD (?) */
#define DUAL_ANT_CONFIG_DUAL 1
/* TX/RX switch for external PA & LNA: GPIO 0 high, GPIO 3 low during TX */
#define DUAL_ANT_CONFIG_TX_GPIO0_HIGH_GPIO3_LOW
/* TX/RX switch for external PA & LNA: GPIO 0 low, GPIO 3 high during TX */
#define DUAL_ANT_CONFIG_TX_GPIO0_LOW_GPIO3_HIGH
/* CRYSTAL_SLEEP_xx values used for sdk_phy_info_t.crystal_sleep
*/
#define CRYSTAL_SLEEP_OFF 0
#define CRYSTAL_SLEEP_ON 1
#define CRYSTAL_SLEEP_GPIO16 2
#define CRYSTAL_SLEEP_GPIO2 3
/* RF Stage 0 & 1 attenuation constants. Use for sdk_phy_info_t.lp_atten_stage01
@note These values have been tested and are confirmed to work as
expected by measuring RSSI w/ rt73 USB adapter in monitor mode
(some values also checked on spectrum analyzer) - provided
low_power_en is set then the signal is attenuated as per this
setting.
(It may look like LP_ATTEN_STAGE01_11_5DB is out of order, but
according to monitor mode captures this is the correct ordering of
these constants.)
Setting the numeric values in between these constants appears to
also attenuate the signal, but not necessarily by the amount you'd
expect.
*/
#define LP_ATTEN_STAGE01_0DB 0x0f /* 0dB */
#define LP_ATTEN_STAGE01_2_5DB 0x0e /* -2.5dB */
#define LP_ATTEN_STAGE01_6DB 0x0d /* -6dB */
#define LP_ATTEN_STAGE01_8_5DB 0x09 /* -8.5dB */
#define LP_ATTEN_STAGE01_11_5DB 0x0c /* -11.5dB */
#define LP_ATTEN_STAGE01_14DB 0x08 /* -14dB */
#define LP_ATTEN_STAGE01_17_5DB 0x04 /* -17.5dB */
#define LP_ATTEN_STAGE01_23DB 0x00 /* -23dB */
/* Constant for sdk_phy_info_t.pa_vdd */
#define PA_VDD_MEASURE_VCC 0xFF
/* Bitmask flags for sdk_phy_info_t.freq_correct_mode */
/* Set this flag to disable frequency offset correction */
#define FREQ_CORRECT_DISABLE 0
/* Set this flag to enable frequency offset correction */
#define FREQ_CORRECT_ENABLE BIT(0)
/* Set = Baseband PLL frequency is 160MHz (can only apply +ve offset)
* Unset = Baseband PLL frequency is 168MHz (can apply +ve/-ve offset */
#define FREQ_CORRECT_BB_160M BIT(1)
/* Set = use force_freq_offset field to correct, Unset = automatically
measure & correct offset
*/
#define FREQ_CORRECT_FORCE BIT(2)
/* RF_CAL_MODE_xx fields used for sdk_phy_info_t.rf_cal_mode
*/
/* Use saved RF CAL data from flash, only. RF init takes 2ms. */
#define RF_CAL_MODE_SAVED 0
/* Calibrate TX power control only, use saved RF CAL data for others.
RF init takes 20ms. */
#define RF_CAL_MODE_TXPOWER_ONLY 1
/* Unclear if/how this mode is different to 2? */
#define RF_CAL_MODE_SAVED_2 2
/* Run full RF CAL routine. RF init takes approx 200ms. */
#define RF_CAL_MODE_FULL 3
/* Data structure that maps to the phy_info configuration block */
typedef struct __attribute__((packed)) {
uint8_t _reserved00[0x05]; /* 0x00 - 0x04 */
/* This "version" field was set to 5 in the SDK phy_info,
and the original SDK startup code checks it is 5 and then loads
default PHY configuration otherwise.
esp-open-rtos will load phy_info from get_default_phy_info() if
the value stored in flash has a different value to the value
returned in get_default_phy_info(). This means you can
increment the version return by get_default_phy_info() (to any
value but 0xFF), and know that the new defaults will replace
any older stored values.
@notes It's not clear whether this is actually a version field
(the other 24 bytes here have equally arbitrary numbers in
them.) Changing the "version" to other values does not seem to
effect WiFi performance at all, neither does zeroing out the
first 5 reserved bytes in _reserved00. However zeroing bytes in
the _reserved06 region will break WiFi entirely.
*/
uint8_t version; /* 0x05 */
int8_t _reserved06[0x14]; /* 0x06 - 0x19 */
/* spur_freq = spur_freq_primary / spur_freq_divisor */
uint8_t spur_freq_primary; /* 0x1a */
uint8_t spur_freq_divisor; /* 0x1b */
/* Bitmask to enable spur_freq for each channel
Appears to be a big endian short word?
*/
uint8_t spur_freq_en_h; /* 0x1c */
uint8_t spur_freq_en_l; /* 0x1d */
uint8_t _reserved1e[4]; /* 0x1e - 0x21 */
/* Each value is a target power level.
Units are 1/4 dBm ie value 64 = 16dBm.
SDK defaults to using these transmit powers:
20.5dBm, 19.5dBm, 18.5dBm, 17dBm, 16dBm, 14dBm
@note Adjusting these values is confirmed to reduce
transmit power accordingly.
*/
uint8_t target_power[6]; /* 0x22 - 0x27 */
/* Maps 8 MCS (modulation & coding schemes) types for 802.11b, g &
* n to a target_power level index (0-5), set above.
This mapping of MCS slot to MCS type is derived from the
spreadsheet and also a table sent by Espressif, but is untested
and may be SDK version dependendent (especially any 802.11n
rates). However the general relationship is confirmed to hold
(higher MCS index = higher bit rate).
MCS 0: 1Mbps/2Mbps/5.5Mbps/11Mbps (802.11b) / 6Mbps/9Mbps (802.11g)
default target_power 0 (default 20.5dBm)
(see also pwr_ind_11b_en)
MCS 1: 12Mbps (802.11g)
default target_power 0 (default 20.5dBm)
MCS 2: 18Mbps (802.11g)
default target_power 1 (19.5dBm)
MCS 3: 24Mbps (802.11g)
default target_power 1 (19.5dBm)
MCS 4: 36Mbps (802.11g)
default target_power 2 (18.5dBm)
MCS 5: 48Mbps (802.11g)
default target_power 3 (17dBm)
MCS 6: 54Mbps (802.11g)
default target_power 4 (16dBm)
MCS 7: 65Mbps (802.11n) - unclear if ever used?
default target_power 5 (14dBm)
*/
uint8_t target_power_index_mcs[8]; /* 0x28 - 0x2f */
/* One of CRYSTAL_FREQ_40M / CRYSTAL_FREQ_26M / CRYSTAL_FREQ_24M
The crystal configured here is the input to the PLL setting
calculations which are used to derive the CPU & APB peripheral
clock frequency, and probably the WiFi PLLs (unconfirmed.)
*/
uint8_t crystal_freq; /* 0x30 */
uint8_t _unused31; /* 0x31: Possibly high byte of crystal freq? */
/* One of SDIO_CONFIG_AUTO, SDIO_CONFIG_SDIOV1_1, SDIO_CONFIG_SDIOV2_0 */
uint8_t sdio_config; /* 0x32 */
/* BT coexistence pin configuration.
One of BT_COEXIST_CONFIG_NONE, BT_COEXIST_CONFIG_A,
BT_COEXIST_CONFIG_PRESENT, BT_COEXIST_CONFIG_B
*/
uint8_t bt_coexist_config; /* 0x33 */
/* BT coexistence pin protocol.
If no coexistence:
Either BT_COEXIST_PROTOCOL_WIFI_ONLY, or
BT_COEXIST_PROTOCOL_BT_ONLY.
If coexistence:
Combine one of
BT_COEXIST_PROTOCOL_FLAG_SEPARATE_ANT or
BT_COEXIST_PROTOCOL_FLAG_SHARE_ANT
with one of
BT_COEXIST_PROTOCOL_FLAG_BT_ACTIVE_ONLY or
BT_COEXIST_PROTOCOL_FLAG_BT_ACTIVE_BT_PRIORITY
*/
uint8_t bt_coexist_protocol; /* 0x34 */
/* Dual antenna configuration
One of DUAL_ANT_CONFIG_NONE, DUAL_ANT_CONFIG_DUAL,
DUAL_ANT_CONFIG_TX_GPIO0_HIGH_GPIO3_LOW,
DUAL_ANT_CONFIG_TX_GPIO0_LOW_GPIO3_HIGH
*/
uint8_t dual_ant_config; /* 0x35 */
uint8_t _reserved34; /* 0x36 */
/* For sharing crystal clock with other devices:
one of CRYSTAL_SLEEP_OFF, CRYSTAL_SLEEP_ON,
CRYSTAL_SLEEP_GPIO16, CRYSTAL_SLEEP_GPIO2
*/
uint8_t crystal_sleep; /* 0x37 */
uint8_t _unused38[8];
/* spur_freq_2 = spur_freq_2_primary / spur_freq_2_divisor */
uint8_t spur_freq_2_primary; /* 0x40 */
uint8_t spur_freq_2_divisor; /* 0x41 */
/* Bitmask to enable spur_freq_2 for each channel?
Appears to be a big endian short word?
*/
uint8_t spur_freq_2_en_h; /* 0x42 */
uint8_t spur_freq_2_en_l; /* 0x43 */
/* Not really clear what these do */
uint8_t spur_freq_cfg_msb; /* 0x44 */
uint8_t spur_freq_2_cfg_msb; /* 0x45 */
uint16_t spur_freq_3_cfg; /* 0x46 - 0x47 */
uint16_t spur_freq_4_cfg; /* 0x48 - 0x49 */
uint8_t _reserved4a[4]; /* 0x4a - 0x4d */
uint8_t _unused78[15]; /* 0x4e - 0x5c */
/* Flag to enable low power mode */
uint8_t low_power_en; /* 0x5d */
/* Low Power attenuation of RF gain stages 0 & 1
Attenuates transmit power if/when low_power_en is set.
Use one of the constants LP_ATTEN_STAGE01_0DB,
LP_ATTEN_STAGE01_2_5DB, LP_ATTEN_STAGE01_6DB,
LP_ATTEN_STAGE01_8_5DB, LP_ATTEN_STAGE01_11_5DB,
LP_ATTEN_STAGE01_14DB, LP_ATTEN_STAGE01_17_5DB,
LP_ATTEN_STAGE01_23DB.
*/
uint8_t lp_atten_stage01; /* 0x5e */
/* Low Power(?) attenuation of baseband gain
Units are minus 1/4 dB, ie value 4 == -1dB.
Maximum value is 24 (0x18) == -6dB
*/
uint8_t lp_atten_bb; /* 0x5f */
/* I believe this means, when pwr_ind_11b_en == 0 then the 802.11g
MCS 0 level from target_power_index_mcs are used to
determine 802.11b transmit power level.
However, when pwr_ind_11b_en == 1 then the index values in
pwr_ind_11b_0 & pwr_ind_11b_1 are used for 802.11b instead.
This is all unconfirmed, if you can confirm then please update
this comment.
*/
uint8_t pwr_ind_11b_en; /* 0x60 */
/* 802.11b low data rate power index (0~5).
Sets the power level index for operation at 1 & 2Mbps
*/
uint8_t pwr_ind_11b_0; /* 0x61 */
/* 802.11b high data rate power index (0~5)
Sets the power level index for operation at 5.5 & 11Mbps
*/
uint8_t pwr_ind_11b_1; /* 0x62 */
uint8_t _unused63[8]; /* 0x63 - 0x6a */
/* Set the voltage of PA_VDD, which appears to be an internal analog
reference voltage(?)
This field is called vdd33_const in the Arduino phy fields,
and relates to usage of the TOUT pin (ADC pin).
Set to PA_VDD_MEASURE_VCC (0xFF) and leave TOUT (ADC) pin
floating in order to use the ADC to measure the 3.3V input
voltage.
Set to value in the range 18-36 (1.8V to 3.6V) to set a
reference voltage(?) when using TOUT pin as an ADC input. I
think this is the reference voltage used to scale the 0-1V
which is allowed on the pin, in order to get an accurate
reading. So it should be set to a value that matches system
VCC... I think!
*/
uint8_t pa_vdd; /* 0x6b */
/* Disable RF calibration cycle for this many times */
uint8_t disable_rfcal_count; /* 0x6c */
uint8_t _unused6d[3];
/* Flags for frequency correction
A bitmask combination of any of: FREQ_CORRECT_DISABLE,
FREQ_CORRECT_ENABLE, FREQ_CORRECT_BB_160M, FREQ_CORRECT_FORCE
*/
uint8_t freq_correct_mode; /* 0x70 */
/* Force frequency offset adjustment (instead of auto measuring)
units are 1 = 8kHz, full range +/- 1016kHz.
Only used if FREQ_CORRECT_ENABLE and FREQ_CORRECT_FORCE are
set in freq_correct_mode.
Unclear whether setting FREQ_CORRECT_BB_160M (which allows only positive offsets) changes the usable range.
*/
int8_t force_freq_offset; /* 0x71 */
/* Use stored data in flash for RF calibration.
This field was previously called rf_cal_use_flash.
Acceptable values one of RF_CAL_MODE_SAVED, RF_CAL_MODE_TXPOWER_ONLY, RF_CAL_MODE_SAVED_2, RF_CAL_MODE_FULL.
*/
uint8_t rf_cal_mode; /* 0x72 */
uint8_t _unused73[13];
} sdk_phy_info_t;
/* Some sanity check static assertions. These can probably be
removed after this structure has been better tested.
*/
_Static_assert(sizeof(sdk_phy_info_t) == 128, "sdk_phy_info_t is wrong size!");
_Static_assert(offsetof(sdk_phy_info_t, version) == 5, "version at wrong offset");
_Static_assert(offsetof(sdk_phy_info_t, target_power) == 34, "target_power_qdb at wrong offset");
_Static_assert(offsetof(sdk_phy_info_t, bt_coexist_protocol) == 52, "bt_coexist_protocol at wrong offset");
_Static_assert(offsetof(sdk_phy_info_t, spur_freq_2_primary) == 64, "spur_freq_2_primary at wrong offset");
_Static_assert(offsetof(sdk_phy_info_t, lp_atten_stage01) == 94, "lp_atten_stage01 at wrong offset");
_Static_assert(offsetof(sdk_phy_info_t, pa_vdd) == 107, "pa_vdd aka vdd33_const at wrong offset");
_Static_assert(offsetof(sdk_phy_info_t, rf_cal_mode) == 114, "rf_cal_use_flash at wrong offset!");
/* Read the default PHY info into the supplied structure.
This function is weak-aliased to get_sdk_default_phy_info() so you
can replace it with your own if you want to vary the default values
- suggested way to do this is to call get_sdk_default_phy_info()
and then only update the fields you care about.
The default PHY info is used at startup whenever the version field
in the default sdk_phy_info_t does not match the version field
stored in flash. So you can increment the version field to force a
reset to defaults, regardless of what values are in flash.
*/
void get_default_phy_info(sdk_phy_info_t *info);
/* Read the "SDK default" PHY info as used by the Espressif SDK */
void get_sdk_default_phy_info(sdk_phy_info_t *info);
/* Read the PHY info currently stored in the SPI flash SDK configuration sector.
This PHY info is updated by the SDK following RF calibration, etc.
Note that the saved data may be corrupt - read the 'version' field to verify.
*/
void read_saved_phy_info(sdk_phy_info_t *info);
/* Update the saved PHY info in the SPI flash. A reset is necessary to use these values.
Note that the SDK may clobber these values, so it's recommended you reset ASAP after updating them.
*/
void write_saved_phy_info(const sdk_phy_info_t *info);
/* Dump known fields in the phy info structure to stdout,
if 'raw' flag is set then the raw hex values are also dumped.
*/
void dump_phy_info(const sdk_phy_info_t *info, bool raw);
#endif

View file

@ -1,6 +1,61 @@
/*
* Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#ifndef _SYS_QUEUE_H_ #ifndef _SYS_QUEUE_H_
#define _SYS_QUEUE_H_ #define _SYS_QUEUE_H_
/*
* Note: This header file comes to us from the Espressif
* SDK. Espressif adapted it from BSD's queue.h. The BSD copyright
* notice and the following extract from the explanatory statement
* were re-added by esp-open-rtos.
*
* FreeBSD version of this header:
* https://svnweb.freebsd.org/base/head/sys/sys/queue.h?view=markup
*
* ****
*
* A singly-linked list is headed by a single forward pointer. The elements
* are singly linked for minimum space and pointer manipulation overhead at
* the expense of O(n) removal for arbitrary elements. New elements can be
* added to the list after an existing element or at the head of the list.
* Elements being removed from the head of the list should use the explicit
* macro for this purpose for optimum efficiency. A singly-linked list may
* only be traversed in the forward direction. Singly-linked lists are ideal
* for applications with large datasets and few or no removals or for
* implementing a LIFO queue.
*
* For details on the use of these macros, see the queue(3) manual page.
*
*/
#define QMD_SAVELINK(name, link) #define QMD_SAVELINK(name, link)
#define TRASHIT(x) #define TRASHIT(x)

View file

@ -33,7 +33,7 @@ sdk_SpiFlashOpResult sdk_spi_flash_erase_sector(uint16_t sec);
/* Write data to flash. /* Write data to flash.
des_addr is byte offset to write to. Should be 4-byte aligned. des_addr is byte offset to write to. Should be 4-byte aligned.
src is pointer to a buffer to read bytes from. src is pointer to a buffer to read bytes from. Should be 4-byte aligned.
size is length of buffer in bytes. Should be a multiple of 4. size is length of buffer in bytes. Should be a multiple of 4.
*/ */
sdk_SpiFlashOpResult sdk_spi_flash_write(uint32_t des_addr, uint32_t *src, uint32_t size); sdk_SpiFlashOpResult sdk_spi_flash_write(uint32_t des_addr, uint32_t *src, uint32_t size);
@ -41,7 +41,7 @@ sdk_SpiFlashOpResult sdk_spi_flash_write(uint32_t des_addr, uint32_t *src, uint3
/* Read data from flash. /* Read data from flash.
src_addr is byte offset to read from. Should be 4-byte aligned. src_addr is byte offset to read from. Should be 4-byte aligned.
des is pointer to a buffer to read bytes into. des is pointer to a buffer to read bytes into. Should be 4-byte aligned.
size is number of bytes to read. Should be a multiple of 4. size is number of bytes to read. Should be a multiple of 4.
*/ */
sdk_SpiFlashOpResult sdk_spi_flash_read(uint32_t src_addr, uint32_t *des, uint32_t size); sdk_SpiFlashOpResult sdk_spi_flash_read(uint32_t src_addr, uint32_t *des, uint32_t size);

View file

@ -1,15 +0,0 @@
/* Memory layout for esp-open-rtos when not using OTA
(ie ROM bootloader only, no second stage rboot)
*/
MEMORY
{
dport0_0_seg : org = 0x3FF00000, len = 0x10
dram0_0_seg : org = 0x3FFE8000, len = 0x14000
iram1_0_seg : org = 0x40100000, len = 0x08000
/* irom0 section, mapped from SPI flash
- Origin is offset by 0x20000 to leave space for bootloader RAM sections.
- Length is max 8Mbit of mappable flash, minus start offset
*/
irom0_0_seg : org = 0x40220000, len = (1M - 0x20000)
}

View file

@ -1,15 +0,0 @@
/* Memory layout for esp-open-rtos when using OTA second stage bootloader */
MEMORY
{
dport0_0_seg : org = 0x3FF00000, len = 0x10
dram0_0_seg : org = 0x3FFE8000, len = 0x14000
iram1_0_seg : org = 0x40100000, len = 0x08000
/* irom0 section, mapped from SPI flash
- Origin is offset by 0x2010 to create spacer for second stage bootloader image,
header.
- Length is max 8Mbit of mappable flash, minus start offset
*/
irom0_0_seg : org = 0x40202010, len = (1M - 0x2010)
}

View file

@ -1,7 +1,17 @@
/* /* Memory layout for esp-open-rtos when using OTA second stage bootloader */
* Common (OTA and non-OTA) parts for the esp-open-rtos Linker Script MEMORY
* {
dport0_0_seg : org = 0x3FF00000, len = 0x10
dram0_0_seg : org = 0x3FFE8000, len = 0x14000
iram1_0_seg : org = 0x40100000, len = 0x08000
/* irom0 section, mapped from SPI flash
- Origin is offset by 0x2010 to create spacer for second stage bootloader image,
header.
- Length is max 8Mbit of mappable flash, minus start offset
*/ */
irom0_0_seg : org = 0x40202010, len = (1M - 0x2010)
}
/* FreeRTOS memory management functions /* FreeRTOS memory management functions
@ -132,43 +142,6 @@ SECTIONS
_etext = .; _etext = .;
} >iram1_0_seg :iram1_0_phdr } >iram1_0_seg :iram1_0_phdr
.irom0.text : ALIGN(4)
{
_irom0_text_start = ABSOLUTE(.);
/* esp-open-rtos compiled code goes into IROM by default
(except for libgcc which is matched above.)
*/
*(.literal .text .literal.* .text.*)
/* Anything explicitly marked as "irom" or "irom0" should go here */
*(.irom.* .irom.*.* .irom0.*)
_irom0_text_end = ABSOLUTE(.);
/* Temporary .rodata hacks start here, eventually all rodata will
be in irom by default */
/* mbedtls rodata */
*mbedtls.a:*.o(.rodata.* .rodata)
/* actual certificate in example (TEMPORARY HACK) */
*:cert.o(.rodata.* .rodata)
/* C++ constructor and destructor tables, properly ordered: */
__init_array_start = ABSOLUTE(.);
KEEP (*crtbegin.o(.ctors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
__init_array_end = ABSOLUTE(.);
KEEP (*crtbegin.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
/* C++ exception handlers table: */
__XT_EXCEPTION_DESCS__ = ABSOLUTE(.);
*(.xt_except_desc)
*(.gnu.linkonce.h.*)
__XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.);
*(.xt_except_desc_end)
} >irom0_0_seg :irom0_0_phdr
.data : ALIGN(4) .data : ALIGN(4)
{ {
_data_start = ABSOLUTE(.); _data_start = ABSOLUTE(.);
@ -186,27 +159,35 @@ SECTIONS
_data_end = ABSOLUTE(.); _data_end = ABSOLUTE(.);
} >dram0_0_seg :dram0_0_phdr } >dram0_0_seg :dram0_0_phdr
/* rodata in DRAM
cherry-picked compilation units that need rodata
to be in DRAM - anything that may be run while
SPI flash is unmapped (ie IRAM functions that are
called from interrupt context or spi flash management
functions) need their compilation units listed here.
If you have constant data that is performance-critical,
list the compilation unit(s) here as well.
*/
.rodata : ALIGN(4) .rodata : ALIGN(4)
{ {
_rodata_start = ABSOLUTE(.); _rodata_start = ABSOLUTE(.);
*(.rodata)
*(.rodata.*) /* Store all of core, libc, freertos .rodata in RAM by default
*(.gnu.linkonce.r.*) (some parts are necessary, some parts for performance reasons.)
*(.rodata1) */
__XT_EXCEPTION_TABLE__ = ABSOLUTE(.); *core.a:*(.rodata.* .rodata) *libc.a:*.o(.rodata.* .rodata)
*(.xt_except_table) *freertos.a:*(.rodata.* .rodata)
*(.gcc_except_table)
*(.gnu.linkonce.e.*) /* spi flash management rodata needs to be accessed
*(.gnu.version_r) while flash is unmapped. */
*(.eh_frame) *libmain.a:spi_flash.o(.rodata.* .rodata)
. = (. + 3) & ~ 3;
*(.dynamic) /* libpp wdev.o has the NMI handler (sdk_wDev_ProcessFiq)
*(.gnu.version_d) which runs at all times, flash mapped or not. */
. = ALIGN(4); /* this table MUST be 4-byte aligned */ *libpp.a:wdev.o(.rodata.* .rodata)
_bss_table_start = ABSOLUTE(.);
LONG(_bss_start)
LONG(_bss_end)
_bss_table_end = ABSOLUTE(.);
_rodata_end = ABSOLUTE(.); _rodata_end = ABSOLUTE(.);
} > dram0_0_seg :dram0_0_phdr } > dram0_0_seg :dram0_0_phdr
@ -232,7 +213,72 @@ SECTIONS
_heap_start = ABSOLUTE(.); _heap_start = ABSOLUTE(.);
/* _stack_sentry = ALIGN(0x8); */ /* _stack_sentry = ALIGN(0x8); */
} >dram0_0_seg :dram0_0_bss_phdr } >dram0_0_seg :dram0_0_bss_phdr
/* __stack = 0x3ffc8000; */ /* __stack = 0x3ffc8000; <-- this value seems a bit odd, stack on sdk_user_start is ~0x3ffffce9 */
/* All data that goes to flash (IROM) ends up in this section */
.irom0.text : ALIGN(4)
{
/*****************************
* Actual irom0 text section *
*****************************/
_irom0_text_start = ABSOLUTE(.);
/* esp-open-rtos compiled code goes into IROM by default
(except for libgcc which is matched above.)
We also link .rodata here in the hope that data is stored near
its code on the flash (in practice this doesn't quite happen. :/)
*/
*(.literal .text .literal.* .text.* .rodata .rodata.*)
/* Anything explicitly marked as "irom" or "irom0" should go here */
*(.irom.* .irom.*.* .irom0.*)
_irom0_text_end = ABSOLUTE(.);
/**************************************************************
C++ constructor and destructor tables, properly ordered:
**************************************************************/
__init_array_start = ABSOLUTE(.);
KEEP (*crtbegin.o(.ctors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
__init_array_end = ABSOLUTE(.);
KEEP (*crtbegin.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
/***********************************
C++ exception handlers table: *
**********************************/
__XT_EXCEPTION_DESCS__ = ABSOLUTE(.);
*(.xt_except_desc)
*(.gnu.linkonce.h.*)
__XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.);
*(.xt_except_desc_end)
/***********************************
Additional .rodata special sections
stored in flash
************************************/
. = ALIGN(4);
*(.gnu.linkonce.r.*)
__XT_EXCEPTION_TABLE__ = ABSOLUTE(.);
*(.xt_except_table)
*(.gcc_except_table)
*(.gnu.linkonce.e.*)
*(.gnu.version_r)
*(.eh_frame)
. = ALIGN(4);
*(.dynamic)
*(.gnu.version_d)
. = ALIGN(4); /* this table MUST be 4-byte aligned */
_bss_table_start = ABSOLUTE(.);
LONG(_bss_start)
LONG(_bss_end)
_bss_table_end = ABSOLUTE(.);
} > irom0_0_seg :irom0_0_phdr
.lit4 : ALIGN(4) .lit4 : ALIGN(4)
{ {

View file

@ -50,6 +50,8 @@
#include <sys/time.h> #include <sys/time.h>
#include <sys/errno.h> #include <sys/errno.h>
#define ERRNO
#define BYTE_ORDER LITTLE_ENDIAN #define BYTE_ORDER LITTLE_ENDIAN
/** @todo fix some warnings: don't use #pragma if compiling with cygwin gcc */ /** @todo fix some warnings: don't use #pragma if compiling with cygwin gcc */

View file

@ -487,7 +487,7 @@ void sys_init(void)
u32_t sys_now(void) u32_t sys_now(void)
{ {
return xTaskGetTickCount(); return xTaskGetTickCount() * portTICK_RATE_MS;
} }
/*---------------------------------------------------------------------------* /*---------------------------------------------------------------------------*

141
parameters.mk Normal file
View file

@ -0,0 +1,141 @@
# Parameters for the esp-open-rtos make process
#
# You can edit this file to change parameters, but a better option is
# to create a local.mk file and add overrides there. The local.mk file
# can be in the root directory, or the program directory alongside the
# Makefile, or both.
#
-include $(ROOT)local.mk
-include local.mk
# Flash size in megabits
# Valid values are same as for esptool.py - 2,4,8,16,32
FLASH_SIZE ?= 16
# Flash mode, valid values are same as for esptool.py - qio,qout,dio.dout
FLASH_MODE ?= qio
# Flash speed in MHz, valid values are same as for esptool.py - 80, 40, 26, 20
FLASH_SPEED ?= 40
# Output directories to store intermediate compiled files
# relative to the program directory
BUILD_DIR ?= $(PROGRAM_DIR)build/
FIRMWARE_DIR ?= $(PROGRAM_DIR)firmware/
# esptool.py from https://github.com/themadinventor/esptool
ESPTOOL ?= esptool.py
# serial port settings for esptool.py
ESPPORT ?= /dev/ttyUSB0
ESPBAUD ?= 115200
# firmware tool arguments
ESPTOOL_ARGS=-fs $(FLASH_SIZE)m -fm $(FLASH_MODE) -ff $(FLASH_SPEED)m
# set this to 0 if you don't need floating point support in printf/scanf
# this will save approx 14.5KB flash space and 448 bytes of statically allocated
# data RAM
#
# NB: Setting the value to 0 requires a recent esptool.py (Feb 2016 / commit ebf02c9)
PRINTF_SCANF_FLOAT_SUPPORT ?= 1
FLAVOR ?= release # or debug
# Compiler names, etc. assume gdb
CROSS ?= xtensa-lx106-elf-
# Path to the filteroutput.py tool
FILTEROUTPUT ?= $(ROOT)/utils/filteroutput.py
AR = $(CROSS)ar
CC = $(CROSS)gcc
CPP = $(CROSS)cpp
LD = $(CROSS)gcc
NM = $(CROSS)nm
C++ = $(CROSS)g++
SIZE = $(CROSS)size
OBJCOPY = $(CROSS)objcopy
OBJDUMP = $(CROSS)objdump
# Source components to compile and link. Each of these are subdirectories
# of the root, with a 'component.mk' file.
COMPONENTS ?= $(EXTRA_COMPONENTS) FreeRTOS lwip core open_esplibs
# binary esp-iot-rtos SDK libraries to link. These are pre-processed prior to linking.
SDK_LIBS ?= main net80211 phy pp wpa
# open source libraries linked in
LIBS ?= hal gcc c
# set to 0 if you want to use the toolchain libc instead of esp-open-rtos newlib
OWN_LIBC ?= 1
# Note: you will need a recent esp
ENTRY_SYMBOL ?= call_user_start
# Set this to zero if you don't want individual function & data sections
# (some code may be slightly slower, linking will be slighty slower,
# but compiled code size will come down a small amount.)
SPLIT_SECTIONS ?= 1
# Set this to 1 to have all compiler warnings treated as errors (and stop the
# build). This is recommended whenever you are working on code which will be
# submitted back to the main project, as all submitted code will be expected to
# compile without warnings to be accepted.
WARNINGS_AS_ERRORS ?= 0
# Common flags for both C & C++_
C_CXX_FLAGS ?= -Wall -Wl,-EL -nostdlib $(EXTRA_C_CXX_FLAGS)
# Flags for C only
CFLAGS ?= $(C_CXX_FLAGS) -std=gnu99 $(EXTRA_CFLAGS)
# Flags for C++ only
CXXFLAGS ?= $(C_CXX_FLAGS) -fno-exceptions -fno-rtti $(EXTRA_CXXFLAGS)
# these aren't all technically preprocesor args, but used by all 3 of C, C++, assembler
CPPFLAGS += -mlongcalls -mtext-section-literals
LDFLAGS = -nostdlib -Wl,--no-check-sections -L$(BUILD_DIR)sdklib -L$(ROOT)lib -u $(ENTRY_SYMBOL) -Wl,-static -Wl,-Map=$(BUILD_DIR)$(PROGRAM).map $(EXTRA_LDFLAGS)
ifeq ($(WARNINGS_AS_ERRORS),1)
C_CXX_FLAGS += -Werror
endif
ifeq ($(SPLIT_SECTIONS),1)
C_CXX_FLAGS += -ffunction-sections -fdata-sections
LDFLAGS += -Wl,-gc-sections
endif
ifeq ($(FLAVOR),debug)
C_CXX_FLAGS += -g -O0
LDFLAGS += -g -O0
else ifeq ($(FLAVOR),sdklike)
# These are flags intended to produce object code as similar as possible to
# the output of the compiler used to build the SDK libs (for comparison of
# disassemblies when coding replacement routines). It is not normally
# intended to be used otherwise.
CFLAGS += -O2 -Os -fno-inline -fno-ipa-cp -fno-toplevel-reorder
LDFLAGS += -O2
else
C_CXX_FLAGS += -g -O2
LDFLAGS += -g -O2
endif
GITSHORTREV=\"$(shell cd $(ROOT); git rev-parse --short -q HEAD 2> /dev/null)\"
ifeq ($(GITSHORTREV),\"\")
GITSHORTREV="\"(nogit)\"" # (same length as a short git hash)
endif
CPPFLAGS += -DGITSHORTREV=$(GITSHORTREV)
LINKER_SCRIPTS += $(ROOT)ld/program.ld $(ROOT)ld/rom.ld
# rboot firmware binary paths for flashing
RBOOT_BIN = $(ROOT)bootloader/firmware/rboot.bin
RBOOT_PREBUILT_BIN = $(ROOT)bootloader/firmware_prebuilt/rboot.bin
RBOOT_CONF = $(ROOT)bootloader/firmware_prebuilt/blank_config.bin
# if a custom bootloader hasn't been compiled, use the
# prebuilt binary from the source tree
ifeq (,$(wildcard $(RBOOT_BIN)))
RBOOT_BIN=$(RBOOT_PREBUILT_BIN)
endif

View file

@ -50,7 +50,7 @@ def main():
default=74880) default=74880)
parser.add_argument( parser.add_argument(
'--reset-on-connect', '-r', '--reset-on-connect', '-r',
help='Reset ESP8266 (via DTR) on serial connect', help='Reset ESP8266 (via DTR) on serial connect. (Linux resets even if not set, except when using NodeMCU-style auto-reset circuit.)',
action='store_true') action='store_true')
args = parser.parse_args() args = parser.parse_args()

View file

@ -0,0 +1,35 @@
#!/bin/bash
set -euv
# Called by Travis to install a toolchain using esp-open-sdk (parts we
# ne for esp-open-rtos are the GNU toolchain, libhal, and esptool.py.)
if test -d ${CROSS_BINDIR}; then
echo "Using cached toolchain in ${CROSS_BINDIR}"
exit 0
fi
# Travis sets this due to "language: c", but it confuses autotools configure when cross-building
unset CC
git clone --recursive https://github.com/pfalcon/esp-open-sdk.git
cd esp-open-sdk
git reset --hard ${OPENSDK_COMMIT}
git submodule update --init
# this is a nasty hack as Ubuntu Precise only has autoconf 2.68 not 2.69...
sed -i "s/2.69/2.68/" lx106-hal/configure.ac
# build the toolchain relative to the CROSS_ROOT directory
sed -r -i 's%TOOLCHAIN ?=.*%TOOLCHAIN=${CROSS_ROOT}%' Makefile
# will dump log on failure
echo "Building toolchain without live progress, as progress spinner fills up log..."
if !( make toolchain esptool libhal STANDALONE=n 2>&1 > make.log ); then
cat make.log
echo "Exiting due to failed toolchain build"
exit 3
fi
echo "Toolchain build completed in ${CROSS_ROOT}."