Merge branch 'master' into open-libmain
This commit is contained in:
commit
d8bcb5d702
65 changed files with 3272 additions and 739 deletions
7
.gitmodules
vendored
7
.gitmodules
vendored
|
@ -4,3 +4,10 @@
|
|||
[submodule "extras/mbedtls/mbedtls"]
|
||||
path = extras/mbedtls/mbedtls
|
||||
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
|
||||
|
||||
|
|
37
.travis.yml
37
.travis.yml
|
@ -2,17 +2,15 @@ language: c
|
|||
sudo: false
|
||||
env:
|
||||
# Target commit for https://github.com/pfalcon/esp-open-sdk/
|
||||
OPENSDK_COMMIT=9bd3aba
|
||||
OPENSDK_COMMIT=a48b12f
|
||||
CROSS_ROOT="${HOME}/toolchain-${OPENSDK_COMMIT}"
|
||||
CROSS_BINDIR="${CROSS_ROOT}/bin"
|
||||
ESPTOOL2_COMMIT=ec0e2c7
|
||||
ESPTOOL2_DIR="${HOME}/esptool2-${ESPTOOL2_COMMIT}"
|
||||
PATH=${PATH}:${CROSS_BINDIR}:${ESPTOOL2_DIR}
|
||||
MAKE_CMD="make WARNINGS_AS_ERRORS=1 -C examples/ build-examples CROSS=\"ccache xtensa-lx106-elf-\""
|
||||
CROSS="ccache xtensa-lx106-elf-"
|
||||
MAKE_CMD="make WARNINGS_AS_ERRORS=1 -C examples/ build-examples"
|
||||
PATH=${PATH}:${CROSS_BINDIR}
|
||||
cache:
|
||||
directories:
|
||||
- ${CROSS_ROOT}
|
||||
- ${ESPTOOL2_DIR}
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
|
@ -34,31 +32,16 @@ addons:
|
|||
- python-serial
|
||||
- sed
|
||||
- git
|
||||
- vim-common
|
||||
|
||||
before_install:
|
||||
# Install a toolchain using esp-open-sdk (parts we need for this are the GNU toolchain and libhal)
|
||||
#
|
||||
# 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
|
||||
- travis_wait 30 utils/travis_build/install_toolchain.sh
|
||||
|
||||
script:
|
||||
- cd ${TRAVIS_BUILD_DIR}
|
||||
# Remove ssid_config requirement for examples
|
||||
- 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
|
||||
# rerun with V=1 so the error part is output verbosely
|
||||
- ${MAKE_CMD} || ( ${MAKE_CMD} V=1 )
|
||||
# Don't verbose-build all examples (too much output), only verbose-build errors
|
||||
- ( ${MAKE_CMD} ) || ( ${MAKE_CMD} V=1 )
|
||||
# build bootloader
|
||||
- make -C bootloader/
|
||||
|
|
|
@ -73,6 +73,7 @@
|
|||
#include <malloc.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <xtensa_ops.h>
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
|
@ -81,13 +82,14 @@
|
|||
unsigned cpu_sr;
|
||||
char level1_int_disabled;
|
||||
|
||||
/* Supervisor stack pointer entry. This is the "high water mark" of how far the
|
||||
supervisor stack grew down before task started.
|
||||
/* Supervisor stack pointer entry. This is the "high water mark" of
|
||||
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
|
||||
FreeRTOS checks for stack overflow.
|
||||
After the scheduler starts, task stacks are all allocated from the
|
||||
heap and FreeRTOS checks for stack overflow.
|
||||
*/
|
||||
static uint32_t xPortSupervisorStackPointer;
|
||||
void *xPortSupervisorStackPointer;
|
||||
|
||||
/*
|
||||
* Stack initialization
|
||||
|
@ -218,9 +220,9 @@ size_t xPortGetFreeHeapSize( void )
|
|||
struct mallinfo mi = mallinfo();
|
||||
uint32_t brk_val = (uint32_t) sbrk(0);
|
||||
|
||||
uint32_t sp = xPortSupervisorStackPointer;
|
||||
intptr_t sp = (intptr_t)xPortSupervisorStackPointer;
|
||||
if(sp == 0) /* scheduler not started */
|
||||
__asm__ __volatile__ ("mov %0, a1\n" : "=a"(sp));
|
||||
SP(sp);
|
||||
return sp - brk_val + mi.fordblks;
|
||||
}
|
||||
|
||||
|
|
59
bootloader/Makefile
Normal file
59
bootloader/Makefile
Normal 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
12
bootloader/README.md
Normal 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.
|
BIN
bootloader/firmware_prebuilt/blank_config.bin
Normal file
BIN
bootloader/firmware_prebuilt/blank_config.bin
Normal file
Binary file not shown.
BIN
bootloader/firmware_prebuilt/rboot.bin
Normal file
BIN
bootloader/firmware_prebuilt/rboot.bin
Normal file
Binary file not shown.
1
bootloader/rboot
Submodule
1
bootloader/rboot
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 30afbaa777e00abf9d7d469fb3345f118c4975c1
|
23
bootloader/rboot.h
Normal file
23
bootloader/rboot.h
Normal 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
187
common.mk
|
@ -20,156 +20,10 @@
|
|||
# assume the 'root' directory (ie top of the tree) is the directory common.mk is in
|
||||
ROOT := $(dir $(lastword $(MAKEFILE_LIST)))
|
||||
|
||||
# include optional local overrides at the root level, then in program directory
|
||||
#
|
||||
# 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
|
||||
####
|
||||
include $(ROOT)parameters.mk
|
||||
|
||||
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
|
||||
|
||||
# hacky way to get a single space value
|
||||
|
@ -188,28 +42,7 @@ LIB_ARGS = $(addprefix -l,$(LIBS))
|
|||
PROGRAM_OUT = $(BUILD_DIR)$(PROGRAM).out
|
||||
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)
|
||||
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"
|
||||
# components will add their include directories to this argument
|
||||
|
@ -235,7 +68,7 @@ Q := @
|
|||
vecho := @echo
|
||||
endif
|
||||
|
||||
.PHONY: all clean debug_print
|
||||
.PHONY: all clean flash erase_flash
|
||||
|
||||
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)
|
||||
|
||||
$(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)
|
||||
$(vecho) "Flashing OTA image slot 0 (bootloader not updated)"
|
||||
$(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) write_flash $(ESPTOOL_ARGS) 0x2000 $(FW_FILE)
|
||||
endif
|
||||
$(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) write_flash $(ESPTOOL_ARGS) 0x0 $(RBOOT_BIN) 0x1000 $(RBOOT_CONF) 0x2000 $(FW_FILE)
|
||||
|
||||
erase_flash:
|
||||
$(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) erase_flash
|
||||
|
||||
size: $(PROGRAM_OUT)
|
||||
$(Q) $(CROSS)size --format=sysv $(PROGRAM_OUT)
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "os_version.h"
|
||||
|
||||
#include "espressif/esp_common.h"
|
||||
#include "espressif/phy_info.h"
|
||||
#include "sdk_internal.h"
|
||||
#include "esplibs/libmain.h"
|
||||
|
||||
|
@ -32,8 +33,6 @@
|
|||
void user_init(void);
|
||||
|
||||
#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
|
||||
// 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_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
|
||||
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 set_spi0_divisor(uint32_t divisor);
|
||||
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 user_start_phase2(void);
|
||||
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
|
||||
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)) {
|
||||
printf("FATAL: sdk_register_chipv6_phy failed");
|
||||
abort();
|
||||
|
@ -361,9 +340,9 @@ extern void (*__init_array_start)(void);
|
|||
extern void (*__init_array_end)(void);
|
||||
|
||||
// .Lfunc009 -- .irom0.text+0x5b4
|
||||
static void user_start_phase2(void) {
|
||||
static __attribute__((noinline)) void user_start_phase2(void) {
|
||||
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));
|
||||
if (sdk_rst_if.reason > 3) {
|
||||
|
@ -381,8 +360,16 @@ static void user_start_phase2(void) {
|
|||
sdk_info._unknown4 = 0x00ffffff;
|
||||
sdk_info._unknown8 = 0x0104a8c0;
|
||||
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
|
||||
setbuf(stdout, NULL);
|
||||
|
@ -390,13 +377,7 @@ static void user_start_phase2(void) {
|
|||
uart_flush_txfifo(0);
|
||||
uart_flush_txfifo(1);
|
||||
|
||||
if (phy_info[0] != 5) {
|
||||
// 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);
|
||||
init_networking(&phy_info, sdk_info.sta_mac_addr);
|
||||
|
||||
// Call gcc constructor functions
|
||||
void (**ctor)(void);
|
||||
|
@ -433,11 +414,11 @@ static void dump_flash_sector(uint32_t start_sector, uint32_t length) {
|
|||
}
|
||||
|
||||
// .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");
|
||||
// Note: original SDK code didn't dump PHY info
|
||||
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");
|
||||
dump_flash_sector(start_sector + 1, sizeof(struct sdk_g_ic_saved_st));
|
||||
printf("\ng_ic saved 1:\n");
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#include <stdio.h>
|
||||
#include <FreeRTOS.h>
|
||||
#include <task.h>
|
||||
#include <malloc.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "debug_dumps.h"
|
||||
#include "common_macros.h"
|
||||
|
@ -19,13 +21,19 @@
|
|||
#include "esp/uart.h"
|
||||
#include "espressif/esp_common.h"
|
||||
#include "sdk_internal.h"
|
||||
#include "esplibs/libmain.h"
|
||||
|
||||
/* Forward declarations */
|
||||
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 IRAM_DATA fatal_exception_handler_fn fatal_exception_handler_inner = standard_fatal_exception_handler_inner;
|
||||
|
||||
/* fatal_exception_handler called from any unhandled user exception
|
||||
*
|
||||
* (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) {
|
||||
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
|
||||
|
@ -130,6 +139,12 @@ void dump_registers_in_exception_handler(uint32_t *sp) {
|
|||
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
|
||||
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
|
||||
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();
|
||||
if (sp) {
|
||||
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);
|
||||
}
|
||||
uart_flush_txfifo(0);
|
||||
uart_flush_txfifo(1);
|
||||
sdk_system_restart_in_nmi();
|
||||
while(1) {}
|
||||
dump_heapinfo();
|
||||
post_crash_reset();
|
||||
}
|
||||
|
||||
/* 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
|
||||
|
@ -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) {
|
||||
printf("abort() invoked at %p.\n", caller);
|
||||
dump_stack(sp);
|
||||
uart_flush_txfifo(0);
|
||||
uart_flush_txfifo(1);
|
||||
sdk_system_restart_in_nmi();
|
||||
while(1) {}
|
||||
dump_heapinfo();
|
||||
post_crash_reset();
|
||||
}
|
||||
|
|
32
core/esp_phy.c
Normal file
32
core/esp_phy.c
Normal 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;
|
||||
}
|
||||
|
||||
|
|
@ -37,15 +37,71 @@
|
|||
#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))
|
||||
|
||||
/* Use this macro to store constant values in IROM flash instead
|
||||
of having them loaded into rodata (which resides in DRAM)
|
||||
/* Use the IRAM macro to place functions into Instruction RAM (IRAM)
|
||||
instead of flash (aka irom).
|
||||
|
||||
Unlike the ESP8266 SDK you don't need an attribute like this for
|
||||
standard functions. They're stored in flash by default. But
|
||||
variables need them.
|
||||
(This is the opposite to the Espressif SDK, where functions default
|
||||
to being placed in IRAM but the ICACHE_FLASH_ATTR attribute will
|
||||
place them in flash.)
|
||||
|
||||
Important to note: IROM flash can only be accessed via 32-bit word
|
||||
aligned reads. It's up to the user of this attribute to ensure this.
|
||||
Use the IRAM attribute 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.
|
||||
|
||||
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
|
||||
#define IROM __attribute__((section(".irom0.literal")))
|
||||
|
@ -53,30 +109,5 @@
|
|||
#define IROM __attribute__((section(".irom0.literal"))) const
|
||||
#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
|
||||
|
|
|
@ -10,9 +10,12 @@
|
|||
#define _DEBUG_DUMPS_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);
|
||||
|
||||
/* Dump heap statistics to stdout */
|
||||
void dump_heapinfo(void);
|
||||
|
||||
/* Called from exception_vectors.S when a fatal exception occurs.
|
||||
|
||||
Probably not useful to be called in other contexts.
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
if (set)
|
||||
GPIO.OUT_SET = BIT(gpio_num);
|
||||
GPIO.OUT_SET = BIT(gpio_num) & GPIO_OUT_PIN_MASK;
|
||||
else
|
||||
GPIO.OUT_CLEAR = BIT(gpio_num);
|
||||
GPIO.OUT_CLEAR = BIT(gpio_num) & GPIO_OUT_PIN_MASK;
|
||||
}
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
if(GPIO.OUT & BIT(gpio_num))
|
||||
GPIO.OUT_CLEAR = BIT(gpio_num);
|
||||
GPIO.OUT_CLEAR = BIT(gpio_num) & GPIO_OUT_PIN_MASK;
|
||||
else
|
||||
GPIO.OUT_SET = BIT(gpio_num);
|
||||
GPIO.OUT_SET = BIT(gpio_num) & GPIO_OUT_PIN_MASK;
|
||||
}
|
||||
|
||||
/* Read input value of a GPIO pin.
|
||||
|
|
|
@ -61,6 +61,21 @@ struct GPIO_REGS {
|
|||
|
||||
_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 */
|
||||
|
||||
/* GPIO.CONF[i] control the pin behavior for the corresponding GPIO in/output.
|
||||
|
|
58
core/include/esp/phy.h
Normal file
58
core/include/esp/phy.h
Normal 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
|
|
@ -4,6 +4,7 @@
|
|||
#include "espressif/esp_wifi.h"
|
||||
#include "espressif/spi_flash.h"
|
||||
#include "etstimer.h"
|
||||
#include "espressif/phy_info.h"
|
||||
#include "lwip/netif.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -224,7 +225,7 @@ void sdk_phy_enable_agc(void);
|
|||
void sdk_pm_attach(void);
|
||||
void sdk_pp_attach(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);
|
||||
uint32_t sdk_system_get_checksum(uint8_t *, uint32_t);
|
||||
void sdk_wDevEnableRx(void);
|
||||
|
|
|
@ -11,27 +11,12 @@
|
|||
#ifndef _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.
|
||||
*
|
||||
* Note that the compiler will push a stack frame (minimum 16 bytes)
|
||||
* 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.
|
||||
*
|
||||
|
@ -40,4 +25,17 @@
|
|||
*/
|
||||
#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 */
|
||||
|
|
|
@ -9,9 +9,12 @@
|
|||
#include <sys/errno.h>
|
||||
#include <espressif/sdk_private.h>
|
||||
#include <common_macros.h>
|
||||
#include <xtensa_ops.h>
|
||||
#include <esp/uart.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
extern void *xPortSupervisorStackPointer;
|
||||
|
||||
IRAM caddr_t _sbrk_r (struct _reent *r, int incr)
|
||||
{
|
||||
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)
|
||||
heap_end = &_heap_start;
|
||||
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);
|
||||
abort();
|
||||
r->_errno = ENOMEM;
|
||||
return (caddr_t)-1;
|
||||
}
|
||||
*/
|
||||
|
||||
heap_end += incr;
|
||||
|
||||
return (caddr_t) prev_heap_end;
|
||||
|
|
161
core/phy_info.c
Normal file
161
core/phy_info.c
Normal 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");
|
||||
}
|
||||
}
|
|
@ -12,8 +12,6 @@
|
|||
* Copyright (C) 2015 Superhouse Automation Pty Ltd
|
||||
* BSD Licensed as described in the file LICENSE
|
||||
*/
|
||||
#ifdef OTA
|
||||
|
||||
#define RBOOT_CONFIG_BASE (0x40200000 + 0x1000)
|
||||
#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 */
|
||||
l32i a0, a0, 0
|
||||
ret.n
|
||||
|
||||
#endif
|
4
examples/dht_sensor/Makefile
Normal file
4
examples/dht_sensor/Makefile
Normal file
|
@ -0,0 +1,4 @@
|
|||
PROGRAM=dht_sensor
|
||||
EXTRA_COMPONENTS = extras/dht
|
||||
include ../../common.mk
|
||||
|
46
examples/dht_sensor/dht_sensor.c
Normal file
46
examples/dht_sensor/dht_sensor.c
Normal 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);
|
||||
}
|
||||
|
|
@ -13,9 +13,9 @@
|
|||
|
||||
#define TESTSTRING "O hai there! %d %d %d"
|
||||
|
||||
const char *dramtest = TESTSTRING;
|
||||
const __attribute__((section(".iram1.notrodata"))) char iramtest[] = TESTSTRING;
|
||||
const __attribute__((section(".text.notrodata"))) char iromtest[] = TESTSTRING;
|
||||
const RAM char dramtest[] = TESTSTRING;
|
||||
const char *iromtest = TESTSTRING;
|
||||
const IRAM_DATA char iramtest[] = TESTSTRING;
|
||||
|
||||
static inline uint32_t get_ccount (void)
|
||||
{
|
||||
|
|
4
examples/json_jsmn_simple/Makefile
Normal file
4
examples/json_jsmn_simple/Makefile
Normal file
|
@ -0,0 +1,4 @@
|
|||
# Simple makefile for simple example
|
||||
PROGRAM=json_jsmn_simple
|
||||
EXTRA_COMPONENTS = extras/jsmn
|
||||
include ../../common.mk
|
112
examples/json_jsmn_simple/json_jsmn_simple.c
Normal file
112
examples/json_jsmn_simple/json_jsmn_simple.c
Normal 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);
|
||||
}
|
|
@ -157,6 +157,7 @@ static void mqtt_task(void *pvParameters)
|
|||
break;
|
||||
}
|
||||
printf("Connection dropped, request restart\n\r");
|
||||
DisconnectNetwork(&network);
|
||||
taskYIELD();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
PROGRAM=ota_basic
|
||||
OTA=1
|
||||
EXTRA_COMPONENTS=extras/rboot-ota
|
||||
EXTRA_COMPONENTS=extras/rboot-ota extras/mbedtls
|
||||
include ../../common.mk
|
||||
|
||||
|
|
|
@ -1,23 +1,121 @@
|
|||
/* 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
|
||||
*
|
||||
* NOT SUITABLE TO PUT ON THE INTERNET OR INTO A PRODUCTION ENVIRONMENT!!!!
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "espressif/esp_common.h"
|
||||
#include "esp/uart.h"
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include "esp8266.h"
|
||||
#include "ssid_config.h"
|
||||
#include "mbedtls/sha256.h"
|
||||
|
||||
#include "ota-tftp.h"
|
||||
#include "rboot-integration.h"
|
||||
#include "rboot.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)
|
||||
{
|
||||
uart_set_baud(0, 115200);
|
||||
|
@ -38,5 +136,7 @@ void user_init(void)
|
|||
sdk_wifi_set_opmode(STATION_MODE);
|
||||
sdk_wifi_station_set_config(&config);
|
||||
|
||||
printf("Starting TFTP server...");
|
||||
ota_tftp_init_server(TFTP_PORT);
|
||||
xTaskCreate(&tftp_client_task, (signed char *)"tftp_client", 2048, NULL, 2, NULL);
|
||||
}
|
||||
|
|
6
examples/sntp/Makefile
Normal file
6
examples/sntp/Makefile
Normal file
|
@ -0,0 +1,6 @@
|
|||
# Makefile for the sntp_example program
|
||||
|
||||
PROGRAM=sntp_example
|
||||
EXTRA_COMPONENTS = extras/sntp
|
||||
|
||||
include ../../common.mk
|
79
examples/sntp/sntp_example.c
Normal file
79
examples/sntp/sntp_example.c
Normal 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
10
extras/dht/component.mk
Normal 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
144
extras/dht/dht.c
Normal 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
17
extras/dht/dht.h
Normal 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
10
extras/jsmn/component.mk
Normal 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
1
extras/jsmn/jsmn
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit bbc6755fce14c713f9bb4ba47c688d15efc1394b
|
|
@ -1 +1 @@
|
|||
Subproject commit a7ffc8f7396573bec401e0afcc073137522d5305
|
||||
Subproject commit 0a0c22e0efcf2f8f71d7e16712f80b8f77326f72
|
|
@ -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) && \
|
||||
!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 )
|
||||
static int socket_errno( const mbedtls_net_context *ctx )
|
||||
{
|
||||
((void) ctx);
|
||||
return( WSAGetLastError() == WSAEWOULDBLOCK );
|
||||
int sock_errno = 0;
|
||||
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
|
||||
* 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 )
|
||||
return( 0 );
|
||||
|
||||
switch( errno )
|
||||
|
||||
switch( socket_errno(ctx) )
|
||||
{
|
||||
#if defined EAGAIN
|
||||
case EAGAIN:
|
||||
|
@ -255,7 +252,6 @@ static int net_would_block( const mbedtls_net_context *ctx )
|
|||
}
|
||||
return( 0 );
|
||||
}
|
||||
#endif /* ( _WIN32 || _WIN32_WCE ) && !EFIX64 && !EFI32 */
|
||||
|
||||
/*
|
||||
* Accept a connection from a remote client
|
||||
|
@ -401,9 +397,7 @@ void mbedtls_net_usleep( unsigned long usec )
|
|||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Read at most 'len' characters
|
||||
*/
|
||||
/* Read at most 'len' characters */
|
||||
int mbedtls_net_recv( void *ctx, unsigned char *buf, size_t len )
|
||||
{
|
||||
int ret;
|
||||
|
@ -419,17 +413,13 @@ int mbedtls_net_recv( void *ctx, unsigned char *buf, size_t len )
|
|||
if( net_would_block( ctx ) != 0 )
|
||||
return( MBEDTLS_ERR_SSL_WANT_READ );
|
||||
|
||||
#if ( defined(_WIN32) || defined(_WIN32_WCE) ) && !defined(EFIX64) && \
|
||||
!defined(EFI32)
|
||||
if( WSAGetLastError() == WSAECONNRESET )
|
||||
return( MBEDTLS_ERR_NET_CONN_RESET );
|
||||
#else
|
||||
if( errno == EPIPE || errno == ECONNRESET )
|
||||
int sock_errno = socket_errno(ctx);
|
||||
|
||||
if( sock_errno == EPIPE || sock_errno == ECONNRESET )
|
||||
return( MBEDTLS_ERR_NET_CONN_RESET );
|
||||
|
||||
if( errno == EINTR )
|
||||
if( sock_errno == EINTR )
|
||||
return( MBEDTLS_ERR_SSL_WANT_READ );
|
||||
#endif
|
||||
|
||||
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 ( defined(_WIN32) || defined(_WIN32_WCE) ) && !defined(EFIX64) && \
|
||||
!defined(EFI32)
|
||||
if( WSAGetLastError() == WSAEINTR )
|
||||
return( MBEDTLS_ERR_SSL_WANT_READ );
|
||||
#else
|
||||
if( errno == EINTR )
|
||||
return( MBEDTLS_ERR_SSL_WANT_READ );
|
||||
#endif
|
||||
|
||||
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 )
|
||||
return( MBEDTLS_ERR_SSL_WANT_WRITE );
|
||||
|
||||
#if ( defined(_WIN32) || defined(_WIN32_WCE) ) && !defined(EFIX64) && \
|
||||
!defined(EFI32)
|
||||
if( WSAGetLastError() == WSAECONNRESET )
|
||||
return( MBEDTLS_ERR_NET_CONN_RESET );
|
||||
#else
|
||||
if( errno == EPIPE || errno == ECONNRESET )
|
||||
int sock_errno = socket_errno(ctx);
|
||||
|
||||
if( sock_errno == EPIPE || sock_errno == ECONNRESET )
|
||||
return( MBEDTLS_ERR_NET_CONN_RESET );
|
||||
|
||||
if( errno == EINTR )
|
||||
if( sock_errno == EINTR )
|
||||
return( MBEDTLS_ERR_SSL_WANT_WRITE );
|
||||
#endif
|
||||
|
||||
return( MBEDTLS_ERR_NET_SEND_FAILED );
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
# 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)
|
||||
|
||||
$(eval $(call component_compile_rules,rboot-ota))
|
||||
|
|
|
@ -22,12 +22,12 @@
|
|||
#include <espressif/esp_system.h>
|
||||
|
||||
#include "ota-tftp.h"
|
||||
#include "rboot.h"
|
||||
#include "rboot-api.h"
|
||||
|
||||
#define TFTP_FIRMWARE_FILE "firmware.bin"
|
||||
#define TFTP_OCTET_MODE "octet" /* non-case-sensitive */
|
||||
|
||||
#define TFTP_OP_RRQ 1
|
||||
#define TFTP_OP_WRQ 2
|
||||
#define TFTP_OP_DATA 3
|
||||
#define TFTP_OP_ACK 4
|
||||
|
@ -43,8 +43,9 @@
|
|||
|
||||
static void tftp_task(void *port_p);
|
||||
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_rrq(struct netconn *nc, const char *filename);
|
||||
static void tftp_send_error(struct netconn *nc, int err_code, const char *err_msg);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct netconn *nc = netconn_new (NETCONN_UDP);
|
||||
|
@ -100,7 +157,7 @@ static void tftp_task(void *listen_port)
|
|||
|
||||
/* check mode */
|
||||
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");
|
||||
free(mode);
|
||||
netbuf_delete(netbuf);
|
||||
|
@ -133,7 +190,8 @@ static void tftp_task(void *listen_port)
|
|||
|
||||
/* Finished WRQ phase, start TFTP data transfer */
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
const int DATA_PACKET_SZ = 512 + 4; /*( packet size plus header */
|
||||
uint32_t start_offs = write_offs;
|
||||
int block = 1;
|
||||
|
||||
struct netbuf *netbuf;
|
||||
struct netbuf *netbuf = 0;
|
||||
int retries = TFTP_TIMEOUT_RETRANSMITS;
|
||||
|
||||
while(1)
|
||||
{
|
||||
netconn_set_recvtimeout(nc, 10000);
|
||||
if(peer_addr) {
|
||||
netconn_disconnect(nc);
|
||||
}
|
||||
|
||||
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(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");
|
||||
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) {
|
||||
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.
|
||||
*/
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
// Make sure ack was successful before calling callback.
|
||||
if(receive_cb) {
|
||||
receive_cb(*received_len);
|
||||
}
|
||||
|
||||
if(len < DATA_PACKET_SZ) {
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
#ifndef _OTA_TFTP_H
|
||||
#define _OTA_TFTP_H
|
||||
|
||||
#include "lwip/err.h"
|
||||
|
||||
typedef void (*tftp_receive_cb)(size_t bytes_received);
|
||||
|
||||
/* TFTP Server OTA Support
|
||||
*
|
||||
* 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);
|
||||
|
||||
/* 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
|
||||
|
||||
#endif
|
||||
|
|
|
@ -4,9 +4,11 @@
|
|||
// richardaburton@gmail.com
|
||||
// See license.txt for license terms.
|
||||
// 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 <c_types.h>
|
||||
//#include <spi_flash.h>
|
||||
|
@ -209,6 +211,164 @@ bool ICACHE_FLASH_ATTR rboot_get_last_boot_mode(uint8 *mode) {
|
|||
}
|
||||
#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
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
* @{
|
||||
*/
|
||||
|
||||
#include <rboot-integration.h>
|
||||
#include <rboot.h>
|
||||
|
||||
#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);
|
||||
#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
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -9,6 +9,10 @@
|
|||
#include <FreeRTOS.h>
|
||||
#include <task.h>
|
||||
|
||||
/***************************************************
|
||||
* Platform configuration definitions *
|
||||
***************************************************/
|
||||
|
||||
#define uint8 uint8_t
|
||||
#define uint16 uint16_t
|
||||
#define uint32 uint32_t
|
||||
|
@ -28,7 +32,11 @@
|
|||
#define RBOOT_DEBUG(f_, ...)
|
||||
#endif
|
||||
|
||||
// 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);
|
||||
/* Enable checksumming when writing out the config,
|
||||
so if the bootloader is built with checksumming then
|
||||
it will still work.
|
||||
*/
|
||||
#define BOOT_CONFIG_CHKSUM
|
||||
|
||||
|
||||
#endif // __RBOOT_INTEGRATION_H__
|
||||
|
|
|
@ -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
8
extras/sntp/component.mk
Normal 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
748
extras/sntp/sntp.c
Normal 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
76
extras/sntp/sntp.h
Normal 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
112
extras/sntp/sntp_fun.c
Normal 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;
|
||||
}
|
||||
|
|
@ -66,7 +66,7 @@
|
|||
//}}
|
||||
|
||||
//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_S 8
|
||||
#define DPORT_PERI_IO_SWAP 0x000000FF
|
||||
|
|
482
include/espressif/phy_info.h
Normal file
482
include/espressif/phy_info.h
Normal 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
|
|
@ -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_
|
||||
#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 TRASHIT(x)
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ sdk_SpiFlashOpResult sdk_spi_flash_erase_sector(uint16_t sec);
|
|||
/* Write data to flash.
|
||||
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
|
||||
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.
|
||||
*/
|
||||
sdk_SpiFlashOpResult sdk_spi_flash_read(uint32_t src_addr, uint32_t *des, uint32_t size);
|
||||
|
|
15
ld/nonota.ld
15
ld/nonota.ld
|
@ -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)
|
||||
}
|
15
ld/ota.ld
15
ld/ota.ld
|
@ -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)
|
||||
}
|
|
@ -1,7 +1,17 @@
|
|||
/*
|
||||
* Common (OTA and non-OTA) parts for the esp-open-rtos Linker Script
|
||||
*
|
||||
*/
|
||||
/* 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)
|
||||
}
|
||||
|
||||
/* FreeRTOS memory management functions
|
||||
|
||||
|
@ -132,43 +142,6 @@ SECTIONS
|
|||
_etext = .;
|
||||
} >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_start = ABSOLUTE(.);
|
||||
|
@ -186,29 +159,37 @@ SECTIONS
|
|||
_data_end = ABSOLUTE(.);
|
||||
} >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_start = ABSOLUTE(.);
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
*(.gnu.linkonce.r.*)
|
||||
*(.rodata1)
|
||||
__XT_EXCEPTION_TABLE__ = ABSOLUTE(.);
|
||||
*(.xt_except_table)
|
||||
*(.gcc_except_table)
|
||||
*(.gnu.linkonce.e.*)
|
||||
*(.gnu.version_r)
|
||||
*(.eh_frame)
|
||||
. = (. + 3) & ~ 3;
|
||||
*(.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(.);
|
||||
|
||||
/* Store all of core, libc, freertos .rodata in RAM by default
|
||||
(some parts are necessary, some parts for performance reasons.)
|
||||
*/
|
||||
*core.a:*(.rodata.* .rodata) *libc.a:*.o(.rodata.* .rodata)
|
||||
*freertos.a:*(.rodata.* .rodata)
|
||||
|
||||
/* spi flash management rodata needs to be accessed
|
||||
while flash is unmapped. */
|
||||
*libmain.a:spi_flash.o(.rodata.* .rodata)
|
||||
|
||||
/* libpp wdev.o has the NMI handler (sdk_wDev_ProcessFiq)
|
||||
which runs at all times, flash mapped or not. */
|
||||
*libpp.a:wdev.o(.rodata.* .rodata)
|
||||
|
||||
_rodata_end = ABSOLUTE(.);
|
||||
} >dram0_0_seg :dram0_0_phdr
|
||||
} > dram0_0_seg :dram0_0_phdr
|
||||
|
||||
.bss ALIGN(8) (NOLOAD) : ALIGN(4)
|
||||
{
|
||||
|
@ -232,7 +213,72 @@ SECTIONS
|
|||
_heap_start = ABSOLUTE(.);
|
||||
/* _stack_sentry = ALIGN(0x8); */
|
||||
} >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)
|
||||
{
|
|
@ -50,6 +50,8 @@
|
|||
#include <sys/time.h>
|
||||
#include <sys/errno.h>
|
||||
|
||||
#define ERRNO
|
||||
|
||||
#define BYTE_ORDER LITTLE_ENDIAN
|
||||
|
||||
/** @todo fix some warnings: don't use #pragma if compiling with cygwin gcc */
|
||||
|
|
|
@ -487,7 +487,7 @@ void sys_init(void)
|
|||
|
||||
u32_t sys_now(void)
|
||||
{
|
||||
return xTaskGetTickCount();
|
||||
return xTaskGetTickCount() * portTICK_RATE_MS;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*
|
||||
|
|
141
parameters.mk
Normal file
141
parameters.mk
Normal 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
|
|
@ -50,7 +50,7 @@ def main():
|
|||
default=74880)
|
||||
parser.add_argument(
|
||||
'--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')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
|
35
utils/travis_build/install_toolchain.sh
Executable file
35
utils/travis_build/install_toolchain.sh
Executable 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}."
|
Loading…
Reference in a new issue