Merge branch 'master' into feature/dualcore_spi_flash_api

* master: (130 commits)
  lwip: Define LWIP_ESP8266 in port lwipopts.h not gcc command line
  CI: Build the esp-idf-template with the matching branch name, if it exists
  README: Add Resources section with some links
  Rename README.buildenv to docs/build_system.rst and ReST-ify it
  Eclipse docs: Easier to just replace entire PATH, msys32 has everything we need to build/flash
  test_build_system: Print ESP_IDF_TEMPLATE_GIT for easier debugging
  Name component makefiles component.mk instead of Makefile
  Eclipse doc: Add troubleshooting note about Makefile directories
  eclipse_make.sh: Fix printing of make directory
  Move bin/eclipse_windows_make.sh to tools/windows_eclipse_make.sh
  Eclipse docs: Prepend IDF paths to beginning of PATH
  Set default SPI flash access mode to DIO
  FreeRTOS: temporary solution for memory canaries and memory debug
  tcpip_adapter: fix dhcp client work flow
  event: not post got ip event if static is invalid
  tcpip_adapter: typedef clean up
  event: post got ip event when use static ip
  tcpip_adapter: use dhcp callback to post got ip event
  dhcp: add dhcp callback
  lwip: remove netif_reg_addr_change_cb
  ...
This commit is contained in:
Ivan Grokhotkov 2016-09-09 17:14:16 +08:00
commit be4dfed822
264 changed files with 108540 additions and 3493 deletions

View file

@ -3,6 +3,9 @@ stages:
- test
- deploy
before_script:
- git submodule update --init --recursive
build_template_app:
stage: build
image: espressif/esp32-ci-env
@ -15,6 +18,10 @@ build_template_app:
script:
- git clone https://github.com/espressif/esp-idf-template.git
- cd esp-idf-template
# Try to use the same branch name for esp-idf-template that we're
# using on esp-idf. If it doesn't exist then just stick to the default
# branch
- git checkout ${CI_BUILD_REF_NAME} || echo "Using esp-idf-template default branch..."
- make defconfig
- make all
@ -25,6 +32,14 @@ test_nvs_on_host:
- cd components/nvs_flash/test
- make test
test_build_system:
stage: test
image: espressif/esp32-ci-env
variables:
IDF_PATH: "$CI_PROJECT_DIR"
script:
- ./make/test_build_system.sh
push_master_to_github:
stage: deploy
only:

View file

@ -1,265 +0,0 @@
The build structure of the Espressif IoT Development Framework explained.
An ESP-IDF project can be seen as an almagation of a number of components.
For example, for a webserver that shows the current humidity, we would
have:
- The ESP32 base libraries (libc, rom bindings etc)
- The WiFi drivers
- A TCP/IP stack
- The FreeRTOS operating system
- A webserver
- A driver for an humidity sensor
- Main code tying it all together
ESP-IDF makes these components explicit and configurable. To do that, when a project
is compiled, the build environment will look up all the components in the
ESP-IDF directories, the project directories and optionally custom other component
directories. It then allows the user to configure compile-time options using
a friendly text-based menu system to customize the ESP-IDF as well as other components
to the requirements of the project. After the components are customized, the
build process will compile everything into an output file, which can then be uploaded
into a board in a way that can also be defined by components.
A project in this sense is defined as a directory under which all the files required
to build it live, excluding the ESP-IDF files and the toolchain. A simple project
tree looks like this:
- myProject/ - build/
- components/ - component1/ - Makefile
- Kconfig
- src1.c
- component2/ - Makefile
- Kconfig
- src1.c
- main/ - src1.c
- src2.c
- Makefile
As we can see, a project consists of a components/ subdirectory containing its
components as well as one or more directories containing the project-specific
sources; by default a single directory called 'main' is assumed. The project
directory will also have a Makefile where the projects name as well as optionally
other options are defined. After compilation, the project directory will contain
a 'build'-directory containing all of the objects, libraries and other generated
files as well as the final binary.
Components also have a Makefile containing various definititions influencing
the build process of the component as well as the project it's used in, as
well as a Kconfig file defining the compile-time options that are settable
by means of the menu system.
Project makefile variables that can be set by the programmer:
PROJECT_NAME: Mandatory. Name for the project
BUILD_DIR_BASE: Set the directory where all objects/libraries/binaries end up in.
Defaults to $(PROJECT_PATH)/build
COMPONENT_DIRS: Search path for components. Defaults to the component/ directories
in the ESP-IDF path and the project path.
COMPONENTS: A list of component names. Defaults to all the component found in the
COMPONENT_DIRS directory
EXTRA_COMPONENT_DIRS: Defaults to unset. Use this to add directories to the default
COMPONENT_DIRS.
SRCDIRS: Directories under the project dir containing project-specific sources.
Defaults to 'main'. These are treated as 'lite' components: they do not have
include directories that are passed to the compilation pass of all components and
they do not have a Kconfig option.
Component makefile variables that can be set by the programmer:
COMPONENT_ADD_INCLUDEDIRS: Relative path to include directories to be added to
the entire project
COMPONENT_PRIV_INCLUDEDIRS: Relative path to include directories that are only used
when compiling this specific component
COMPONENT_DEPENDS: Names of any components that need to be compiled before this component.
COMPONENT_ADD_LDFLAGS: Ld flags to add for this project. Defaults to -l$(COMPONENT_NAME).
Add libraries etc in the current directory as $(abspath libwhatever.a)
COMPONENT_EXTRA_INCLUDES: Any extra include paths. These will be prefixed with '-I' and
passed to the compiler; please put absolute paths here.
COMPONENT_SRCDIRS: Relative directories to look in for sources. Defaults to '.', the current
directory (the root of the component) only. Use this to specify any subdirectories. Note
that specifying this overwrites the default action of compiling everything in the
components root dir; to keep this behaviour please also add '.' as a directory in this
list.
COMPONENT_OBJS: Object files to compile. Defaults to the .o variants of all .c and .S files
that are found in COMPONENT_SRCDIRS.
COMPONENT_EXTRA_CLEAN: Files that are generated using rules in the components Makefile
that also need to be cleaned
COMPONENT_BUILDRECIPE: Recipe to build the component. Optional. Defaults to building all
COMPONENT_OBJS and linking them into lib(componentname).a
COMPONENT_CLEANRECIPE: Recipe to clean the component. Optional. Defaults to removing
all built objects and libraries.
COMPONENT_BUILD_DIR: Equals the cwd of the component build, which is the build dir
of the component (where all the .o etc files should be created).
These variables are already set early on in the Makefile and the values in it will
be usable in component or project Makefiles:
CC, LD, AR, OBJCOPY: Xtensa gcc tools
HOSTCC, HOSTLD etc: Host gcc tools
LDFLAGS, CFLAGS: Set to usable values as defined in ESP-IDF Makefile
PROJECT_NAME: Name of the project, as set in project makefile
PROJECT_PATH: Path to the root of the project folder
COMPONENTS: Name of the components to be included
CONFIG_*: Values set by 'make menuconfig' also have corresponding Makefile variables.
For components, there also are these defines:
COMPONENT_PATH: Absolute path to the root of the source tree of the component we're
compiling
COMPONENT_LIBRARY: The full path to the static library the components compilation pass
is supposed to generate
How this works:
The Make process is always invoked from the project directory by the
user; invoking it anywhere else gives an error. This is what happens if
we build a binary:
The Makefile first determines how it was included. It determines it was
included as a project file in this case and will continue to figure out
various paths as well as the components available to it. It will also
collect the ldflags and includes that the components specify they need.
It does this by running a dummy Make on the components with a target that
will output these values.
The Makefile will then create targets to build the lib*.a libraries of
all components and make the elf target depend on this. The targets
invoke Make on the makefiles of the components in a subshell: this way
the components have full freedom to do whatever is necessary to build
the library without influencing other components. By default, the
component includes the utility makefile $(IDF_PATH)/make/component.mk.
This provides default targets and configurations that will work
out-of-the-box for most projects.
For components that have parts that need to be run when building of the
project is done, you can create a file called Makefile.projbuild in the
component root directory. This file will be included in the main
Makefile. For the menu, there's an equivalent: if you want to include
options not in the 'components' submenu, create a Kconfig.projbuild and
it will be included in the main menu of menuconfig. Take good care when
(re)defining stuff here: because it's included with all the other
.projbuild files, it's possible to overwrite variables or re-declare
targets defined in the ESP-IDF makefile/Kconfig and other .projbuild files
WRITING COMPONENT MAKEFILES
A component consists of a directory which doubles as the name for the
component: a component named 'httpd' lives in a directory called 'httpd'
Because components usually live under the project directory (although
they can also reside in an other folder), the path to this may be
something like /home/myuser/projects/myprojects/components/httpd .
One of the things that most components will have is a Makefile,
containing instructions on how to build the component. Because the
build environment tries to set reasonable defaults that will work most
of the time, a component Makefile can be pretty small. At the absolute
minimum, it will just include the ESP-IDF component makefile, which adds
component functionality:
----8<----
include $(IDF_PATH)/make/component.mk
---->8----
This will take all the .c and .S files in the component root and compile
them into object files, finally linking them into a library.
Subdirectories are ignored; if your project has sources in subdirectories
instead of in the root of the component, you can tell that to the build
system by setting COMPONENT_SRCDIRS:
----8<----
COMPONENT_SRCDIRS := src1 src2
include $(IDF_PATH)/make/component.mk
---->8----
This will compile all source files in the src1/ and src2/ subdirectories
instead.
The standard component.mk logic adds all .S and .c files in the source
directories as sources to be compiled unconditionally. It is possible
to circumvent that logic and hardcode the objects to be compiled by
manually setting the COMPONENT_OBJS variable to the name of the
objects that need to be generated:
----8<----
COMPONENT_OBJS := file1.o file2.o thing/filea.o thing/fileb.o anotherthing/main.o
include $(IDF_PATH)/make/component.mk
---->8----
This can also be used in order to conditionally compile some files
dependent on the options selected in the Makefile:
Kconfig:
----8<----
config FOO_ENABLE_BAR
bool "Enable the BAR feature."
help
This enables the BAR feature of the FOO component.
---->8----
Makefile:
----8<----
COMPONENT_OBJS := foo_a.o foo_b.o $(if $(CONFIG_FOO_ENABLE_BAR),foo_bar.o foo_bar_interface.o)
include $(IDF_PATH)/make/component.mk
---->8----
Some components will have a situation where a source file isn't supplied
with the component itself but has to be generated from another file. Say
our component has a header file that consists of the converted binary
data of a BMP file, converted using a hypothetical tool called bmp2h. The
header file is then included in as C source file called graphics_lib.c.
----8<----
COMPONENT_EXTRA_CLEAN := logo.h
graphics_lib.o: logo.h
logo.h: $(COMPONENT_PATH)/logo.bmp
bmp2h -i $^ -o $@
include $(IDF_PATH)/make/component.mk
---->8----
In this example, graphics_lib.o and logo.h will be generated in the
current directory (the build directory) while logo.bmp comes with the
component and resides under the component path. Because logo.h is a
generated file, it needs to be cleaned when make clean is called which
why it is added to the COMPONENT_EXTRA_CLEAN variable.
This will work just fine, but there's one last cosmetic improvement that
can be done. The make system tries to make the make process somewhat
easier on the eyes by hiding the commands (unless you run make with the
V=1 switch) and this does not do that yet. Here's an improved version
that will output in the same style as the rest of the make process:
----8<----
COMPONENT_EXTRA_CLEAN := test_tjpgd_logo.h
graphics_lib.o: logo.h
logo.h: $(COMPONENT_PATH)/logo.bmp
$(vecho) BMP2H $@
$(Q) bmp2h -i $^ -o $@
include $(IDF_PATH)/make/component.mk
---->8----
Obviously, there are cases where all these recipes are insufficient for a
certain component, for example when the component is basically a wrapper
around another third-party component not originally intended to be
compiled under this build system. In that case, it's possible to forego
the build system entirely by setting COMPONENT_OWNBUILDTARGET and
possibly COMPONENT_OWNCLEANTARGET and defining your own build- and clean
target. The build target can do anything as long as it creates
$(COMPONENT_LIBRARY) for the main file to link into the project binary,
and even that is not necessary: if the COMPONENT_ADD_LDFLAGS variable
is set, the component can instruct the linker to do anything else as well.

View file

@ -49,3 +49,11 @@ The simplest way to use the partition table is to `make menuconfig` and choose o
In both cases the factory app is flashed at offset 0x10000. If you `make partition_table` then it will print a summary of the partition table.
For more details about partition tables and how to create custom variations, view the `docs/partition_tables.rst` file.
# Resources
* The [docs directory of the esp-idf repository](https://github.com/espressif/esp-idf/tree/master/docs) contains esp-idf documentation.
* The [esp32.com forum](http://esp32.com/) is a place to ask questions and find community resources.
* [Check the Issues section on github](https://github.com/espressif/esp-idf/issues) if you find a bug or have a feature request. Please check existing Issues before opening a new one.

View file

@ -13,7 +13,7 @@ BOOTLOADER_COMPONENT_PATH := $(COMPONENT_PATH)
BOOTLOADER_BUILD_DIR=$(BUILD_DIR_BASE)/bootloader
BOOTLOADER_BIN=$(BOOTLOADER_BUILD_DIR)/bootloader.bin
.PHONY: bootloader-clean bootloader-flash bootloader
.PHONY: bootloader-clean bootloader-flash bootloader $(BOOTLOADER_BIN)
$(BOOTLOADER_BIN): $(COMPONENT_PATH)/src/sdkconfig
$(Q) PROJECT_PATH= \

View file

@ -92,23 +92,28 @@ void IRAM_ATTR call_start_cpu0()
//Clear bss
memset(&_bss_start, 0, (&_bss_end - &_bss_start) * sizeof(_bss_start));
/* completely reset MMU for both CPUs
(in case serial bootloader was running) */
Cache_Read_Disable(0);
Cache_Read_Disable(1);
Cache_Flush(0);
Cache_Flush(1);
mmu_init(0);
mmu_init(1);
/* (above steps probably unnecessary for most serial bootloader
usage, all that's absolutely needed is that we unmask DROM0
cache on the following two lines - normal ROM boot exits with
DROM0 cache unmasked, but serial bootloader exits with it
masked. However can't hurt to be thorough and reset
everything.)
*/
REG_CLR_BIT(PRO_CACHE_CTRL1_REG, DPORT_PRO_CACHE_MASK_DROM0);
REG_CLR_BIT(APP_CACHE_CTRL1_REG, DPORT_APP_CACHE_MASK_DROM0);
/* completely reset MMU for both CPUs
(in case serial bootloader was running) */
Cache_Read_Disable(0);
Cache_Read_Disable(1);
Cache_Flush(0);
Cache_Flush(1);
mmu_init(0);
REG_SET_BIT(APP_CACHE_CTRL1_REG, DPORT_APP_CACHE_MMU_IA_CLR);
mmu_init(1);
REG_CLR_BIT(APP_CACHE_CTRL1_REG, DPORT_APP_CACHE_MMU_IA_CLR);
/* (above steps probably unnecessary for most serial bootloader
usage, all that's absolutely needed is that we unmask DROM0
cache on the following two lines - normal ROM boot exits with
DROM0 cache unmasked, but serial bootloader exits with it
masked. However can't hurt to be thorough and reset
everything.)
The lines which manipulate DPORT_APP_CACHE_MMU_IA_CLR bit are
necessary to work around a hardware bug.
*/
REG_CLR_BIT(PRO_CACHE_CTRL1_REG, DPORT_PRO_CACHE_MASK_DROM0);
REG_CLR_BIT(APP_CACHE_CTRL1_REG, DPORT_APP_CACHE_MASK_DROM0);
bootloader_main();
}

View file

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

View file

@ -6,6 +6,9 @@ config WIFI_ENABLED
help
This compiles in the low-level WiFi stack.
Temporarily, this option requires that FreeRTOS runs in single core mode.
depends on FREERTOS_UNICORE
config WIFI_AUTO_STARTUP
bool "Start WiFi with system startup"
default "y"

View file

@ -1,23 +1,39 @@
#
# Component Makefile
#
# This Makefile should, at the very least, just include $(IDF_PATH)/make/component.mk. By default,
# This Makefile should, at the very least, just include $(IDF_PATH)/make/component_common.mk. By default,
# this will take the sources in this directory, compile them and link them into
# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
# please read the esp-idf build system document if you need to do this.
#
-include $(PROJECT_PATH)/build/include/config/auto.conf
LIBS := crypto core net80211 phy rtc pp wpa wps
ifeq ($(CONFIG_MEMMAP_BT),y)
ifeq ($(CONFIG_MEMMAP_TRACEMEM),y)
LINKER_SCRIPTS = -T esp32.bt.trace.ld
else
LINKER_SCRIPTS = -T esp32.bt.ld
endif
else
ifeq ($(CONFIG_MEMMAP_TRACEMEM),y)
LINKER_SCRIPTS = -T esp32.trace.ld
else
LINKER_SCRIPTS = -T esp32.ld
endif
endif
LINKER_SCRIPTS += -T esp32.common.ld -T esp32.rom.ld
COMPONENT_ADD_LDFLAGS := -lesp32 \
$(abspath libhal.a) \
-L$(abspath lib) \
$(addprefix -l,$(LIBS)) \
-L $(abspath ld) \
-T eagle.fpga32.v7.ld \
-T eagle.fpga32.rom.addr.v7.ld
$(LINKER_SCRIPTS)
include $(IDF_PATH)/make/component.mk
include $(IDF_PATH)/make/component_common.mk
ALL_LIB_FILES := $(patsubst %,$(COMPONENT_PATH)/lib/lib%.a,$(LIBS))
@ -30,8 +46,11 @@ $(ALL_LIB_FILES):
@echo "Warning: Missing libraries in components/esp32/lib/ submodule. Going to try running 'git submodule update --init' in esp-idf root directory..."
cd ${IDF_PATH} && git submodule update --init
# adding $(ALL_LIB_FILES) as a build dependency here is a hack to make
# sure they get evaluated. Once TW6630 lands and we have library file
# dependencies available at the project level, we can probably lose
# this.
build: $(ALL_LIB_FILES)
# this is a hack to make sure the app is re-linked if the binary
# libraries change or are updated. If they change, the main esp32
# library will be rebuild by AR andthis will trigger a re-linking of
# the entire app.
#
# It would be better for components to be able to expose any of these
# non-standard dependencies via get_variable, but this will do for now.
$(COMPONENT_LIBRARY): $(ALL_LIB_FILES)

View file

@ -64,6 +64,8 @@ void uartAttach();
void ets_set_appcpu_boot_addr(uint32_t ent);
int ets_getAppEntry();
static bool app_cpu_started = false;
void IRAM_ATTR call_user_start_cpu0() {
//Kill wdt
REG_CLR_BIT(0x3ff4808c, BIT(10)); //RTCCNTL+8C RTC_WDTCONFIG0 RTC_
@ -114,53 +116,34 @@ void IRAM_ATTR call_user_start_cpu0() {
heap_alloc_caps_init();
ets_printf("Pro cpu up.\n");
#ifndef CONFIG_FREERTOS_UNICORE
ets_printf("Running app cpu, entry point is %p\n", call_user_start_cpu1);
ets_delay_us(60000);
ets_printf("Starting app cpu, entry point is %p\n", call_user_start_cpu1);
SET_PERI_REG_MASK(APPCPU_CTRL_REG_B, DPORT_APPCPU_CLKGATE_EN);
CLEAR_PERI_REG_MASK(APPCPU_CTRL_REG_C, DPORT_APPCPU_RUNSTALL);
SET_PERI_REG_MASK(APPCPU_CTRL_REG_A, DPORT_APPCPU_RESETTING);
CLEAR_PERI_REG_MASK(APPCPU_CTRL_REG_A, DPORT_APPCPU_RESETTING);
for (int i=0; i<20; i++) ets_delay_us(40000);
ets_set_appcpu_boot_addr((uint32_t)call_user_start_cpu1);
ets_delay_us(10000);
// while (ets_getAppEntry()==(int)call_user_start_cpu1) ;
//Because of Reasons (tm), the pro cpu cannot use the SPI flash while the app cpu is booting.
// while(((READ_PERI_REG(RTC_STORE7))&BIT(31)) == 0) ; // check APP boot complete flag
ets_delay_us(50000);
ets_delay_us(50000);
ets_printf("\n\nBack to pro cpu.\n");
while (!app_cpu_started) {
ets_delay_us(100);
}
#else
CLEAR_PERI_REG_MASK(APPCPU_CTRL_REG_B, DPORT_APPCPU_CLKGATE_EN);
#endif
ets_printf("Pro cpu start user code\n");
user_start_cpu0();
}
extern int xPortGetCoreID();
extern int _init_start;
/*
We arrive here because the pro CPU pulled us from reset. IRAM is in place, cache is still disabled, we can execute C code.
*/
void IRAM_ATTR call_user_start_cpu1() {
//We need to do this ASAP because otherwise the structure to catch the SYSCALL instruction, which
//we abuse to do ROM calls, won't work.
asm volatile (\
"wsr %0, vecbase\n" \
::"r"(&_init_start));
//Enable SPI flash
// PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA3_U, FUNC_SD_DATA3_SPIWP); // swap PIN SDDATA3 from uart1 to spi, because cache need spi
ets_printf("App cpu up\n");
//Make page 0 access raise an exception
//Also some other unused pages so we can catch weirdness
//ToDo: this but nicer.
@ -192,16 +175,15 @@ void IRAM_ATTR call_user_start_cpu1() {
"isync\n" \
:::"a4","a5");
ets_printf("App cpu up.\n");
app_cpu_started = 1;
user_start_cpu1();
}
extern volatile int port_xSchedulerRunning;
extern int xPortStartScheduler();
void user_start_cpu1(void) {
ets_printf("App cpu is running!\n");
//Wait for the freertos initialization is finished on CPU0
while (port_xSchedulerRunning == 0) ;
ets_printf("Core0 started initializing FreeRTOS. Jumping to scheduler.\n");
@ -221,20 +203,13 @@ static void do_global_ctors(void) {
void user_start_cpu0(void) {
esp_err_t ret;
ets_setup_syscalls();
do_global_ctors();
#if 1 //workaround
for (uint8_t i = 5; i < 8; i++) {
ets_printf("erase sector %d\n", i);
spi_flash_erase_sector(i);
}
#endif
#if CONFIG_WIFI_ENABLED
ets_printf("nvs_flash_init\n");
ret = nvs_flash_init(5, 3);
if (ESP_OK != ret) {
esp_err_t ret = nvs_flash_init(5, 3);
if (ret != ESP_OK) {
ets_printf("nvs_flash_init fail, ret=%d\n", ret);
}
@ -242,8 +217,6 @@ void user_start_cpu0(void) {
esp_event_init(NULL);
// TODO: consider ethernet interface
#if CONFIG_WIFI_ENABLED
tcpip_adapter_init();
#endif

View file

@ -13,6 +13,7 @@
// limitations under the License.
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "esp_err.h"
#include "esp_wifi.h"
@ -28,26 +29,10 @@
#define ESP32_WORKAROUND 1
#if CONFIG_WIFI_ENABLED
#ifdef ESP32_WORKAROUND
extern SemaphoreHandle_t stdio_mutex_tx;
#define os_printf(fmt, ...) do {\
if (!stdio_mutex_tx) {\
stdio_mutex_tx = xSemaphoreCreateMutex();\
}\
\
xSemaphoreTake(stdio_mutex_tx, portMAX_DELAY);\
ets_printf(fmt, ##__VA_ARGS__);\
xSemaphoreGive(stdio_mutex_tx);\
} while (0)
#endif
static xQueueHandle g_event_handler = NULL;
static system_event_cb_t g_event_handler_cb;
#define WIFI_DEBUG os_printf
#define WIFI_DEBUG(...)
#define WIFI_API_CALL_CHECK(info, api_call, ret) \
do{\
esp_err_t __err = (api_call);\
@ -70,6 +55,7 @@ static esp_err_t system_event_sta_start_handle_default(system_event_t *event);
static esp_err_t system_event_sta_stop_handle_default(system_event_t *event);
static esp_err_t system_event_sta_connected_handle_default(system_event_t *event);
static esp_err_t system_event_sta_disconnected_handle_default(system_event_t *event);
static esp_err_t system_event_sta_gotip_default(system_event_t *event);
static system_event_handle_t g_system_event_handle_table[] = {
{SYSTEM_EVENT_WIFI_READY, NULL},
@ -79,6 +65,7 @@ static system_event_handle_t g_system_event_handle_table[] = {
{SYSTEM_EVENT_STA_CONNECTED, system_event_sta_connected_handle_default},
{SYSTEM_EVENT_STA_DISCONNECTED, system_event_sta_disconnected_handle_default},
{SYSTEM_EVENT_STA_AUTHMODE_CHANGE, NULL},
{SYSTEM_EVENT_STA_GOTIP, system_event_sta_gotip_default},
{SYSTEM_EVENT_AP_START, system_event_ap_start_handle_default},
{SYSTEM_EVENT_AP_STOP, system_event_ap_stop_handle_default},
{SYSTEM_EVENT_AP_STACONNECTED, NULL},
@ -87,9 +74,22 @@ static system_event_handle_t g_system_event_handle_table[] = {
{SYSTEM_EVENT_MAX, NULL},
};
static esp_err_t system_event_sta_gotip_default(system_event_t *event)
{
extern esp_err_t esp_wifi_set_sta_ip(void);
WIFI_API_CALL_CHECK("esp_wifi_set_sta_ip", esp_wifi_set_sta_ip(), ESP_OK);
printf("ip: " IPSTR ", mask: " IPSTR ", gw: " IPSTR "\n",
IP2STR(&event->event_info.got_ip.ip_info.ip),
IP2STR(&event->event_info.got_ip.ip_info.netmask),
IP2STR(&event->event_info.got_ip.ip_info.gw));
return ESP_OK;
}
esp_err_t system_event_ap_start_handle_default(system_event_t *event)
{
struct ip_info ap_ip;
tcpip_adapter_ip_info_t ap_ip;
uint8_t ap_mac[6];
WIFI_API_CALL_CHECK("esp_wifi_reg_rxcb", esp_wifi_reg_rxcb(WIFI_IF_AP, (wifi_rxcb_t)tcpip_adapter_ap_input), ESP_OK);
@ -112,7 +112,7 @@ esp_err_t system_event_ap_stop_handle_default(system_event_t *event)
esp_err_t system_event_sta_start_handle_default(system_event_t *event)
{
struct ip_info sta_ip;
tcpip_adapter_ip_info_t sta_ip;
uint8_t sta_mac[6];
WIFI_API_CALL_CHECK("esp_wifi_mac_get", esp_wifi_get_mac(WIFI_IF_STA, sta_mac), ESP_OK);
@ -131,10 +131,33 @@ esp_err_t system_event_sta_stop_handle_default(system_event_t *event)
esp_err_t system_event_sta_connected_handle_default(system_event_t *event)
{
tcpip_adapter_dhcp_status_t status;
WIFI_API_CALL_CHECK("esp_wifi_reg_rxcb", esp_wifi_reg_rxcb(WIFI_IF_STA, (wifi_rxcb_t)tcpip_adapter_sta_input), ESP_OK);
tcpip_adapter_up(TCPIP_ADAPTER_IF_STA);
tcpip_adapter_dhcpc_start(TCPIP_ADAPTER_IF_STA);
tcpip_adapter_dhcpc_get_status(TCPIP_ADAPTER_IF_STA, &status);
if (status == TCPIP_ADAPTER_DHCP_INIT) {
tcpip_adapter_dhcpc_start(TCPIP_ADAPTER_IF_STA);
} else if (status == TCPIP_ADAPTER_DHCP_STOPPED) {
tcpip_adapter_ip_info_t sta_ip;
tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &sta_ip);
if (!(ip4_addr_isany_val(sta_ip.ip) || ip4_addr_isany_val(sta_ip.netmask) || ip4_addr_isany_val(sta_ip.gw))) {
system_event_t evt;
//notify event
evt.event_id = SYSTEM_EVENT_STA_GOTIP;
memcpy(&evt.event_info.got_ip.ip_info, &sta_ip, sizeof(tcpip_adapter_ip_info_t));
esp_event_send(&evt);
} else {
WIFI_DEBUG("invalid static ip\n");
}
}
return ESP_OK;
}
@ -157,78 +180,109 @@ static esp_err_t esp_wifi_post_event_to_user(system_event_t *event)
static esp_err_t esp_system_event_debug(system_event_t *event)
{
system_event_sta_scan_done_t *scan_done;
system_event_sta_connected_t *connected;
system_event_sta_disconnected_t *disconnected;
system_event_sta_authmode_change_t *auth_change;
system_event_ap_staconnected_t *staconnected;
system_event_ap_stadisconnected_t *stadisconnected;
system_event_ap_probe_req_rx_t *ap_probereqrecved;
if (event == NULL) {
os_printf("Error: event is null!\n");
printf("Error: event is null!\n");
return ESP_FAIL;
}
os_printf("received event: ");
WIFI_DEBUG("received event: ");
switch (event->event_id) {
case SYSTEM_EVENT_WIFI_READY:
os_printf("SYSTEM_EVENT_WIFI_READY\n");
{
WIFI_DEBUG("SYSTEM_EVENT_WIFI_READY\n");
break;
}
case SYSTEM_EVENT_SCAN_DONE:
{
system_event_sta_scan_done_t *scan_done;
scan_done = &event->event_info.scan_done;
os_printf("SYSTEM_EVENT_SCAN_DONE\nstatus:%d, number:%d\n", \
scan_done->status, scan_done->number);
WIFI_DEBUG("SYSTEM_EVENT_SCAN_DONE\nstatus:%d, number:%d\n", scan_done->status, scan_done->number);
break;
}
case SYSTEM_EVENT_STA_START:
os_printf("SYSTEM_EVENT_STA_START\n");
{
WIFI_DEBUG("SYSTEM_EVENT_STA_START\n");
break;
}
case SYSTEM_EVENT_STA_STOP:
os_printf("SYSTEM_EVENT_STA_STOP\n");
{
WIFI_DEBUG("SYSTEM_EVENT_STA_STOP\n");
break;
}
case SYSTEM_EVENT_STA_CONNECTED:
{
system_event_sta_connected_t *connected;
connected = &event->event_info.connected;
os_printf("SYSTEM_EVENT_STA_CONNECTED\nssid:%s, ssid_len:%d, bssid:%02x:%02x:%02x:%02x:%02x:%02x, channel:%d\n", \
WIFI_DEBUG("SYSTEM_EVENT_STA_CONNECTED\nssid:%s, ssid_len:%d, bssid:%02x:%02x:%02x:%02x:%02x:%02x, channel:%d, authmode:%d\n", \
connected->ssid, connected->ssid_len, connected->bssid[0], connected->bssid[0], connected->bssid[1], \
connected->bssid[3], connected->bssid[4], connected->bssid[5], connected->channel);
connected->bssid[3], connected->bssid[4], connected->bssid[5], connected->channel, connected->authmode);
break;
}
case SYSTEM_EVENT_STA_DISCONNECTED:
{
system_event_sta_disconnected_t *disconnected;
disconnected = &event->event_info.disconnected;
os_printf("SYSTEM_EVENT_STA_DISCONNECTED\nssid:%s, ssid_len:%d, bssid:%02x:%02x:%02x:%02x:%02x:%02x, reason:%d\n", \
WIFI_DEBUG("SYSTEM_EVENT_STA_DISCONNECTED\nssid:%s, ssid_len:%d, bssid:%02x:%02x:%02x:%02x:%02x:%02x, reason:%d\n", \
disconnected->ssid, disconnected->ssid_len, disconnected->bssid[0], disconnected->bssid[0], disconnected->bssid[1], \
disconnected->bssid[3], disconnected->bssid[4], disconnected->bssid[5], disconnected->reason);
break;
}
case SYSTEM_EVENT_STA_AUTHMODE_CHANGE:
{
system_event_sta_authmode_change_t *auth_change;
auth_change = &event->event_info.auth_change;
os_printf("SYSTEM_EVENT_STA_AUTHMODE_CHNAGE\nold_mode:%d, new_mode:%d\n", auth_change->old_mode, auth_change->new_mode);
WIFI_DEBUG("SYSTEM_EVENT_STA_AUTHMODE_CHNAGE\nold_mode:%d, new_mode:%d\n", auth_change->old_mode, auth_change->new_mode);
break;
}
case SYSTEM_EVENT_STA_GOTIP:
{
system_event_sta_got_ip_t *got_ip;
got_ip = &event->event_info.got_ip;
WIFI_DEBUG("SYSTEM_EVENT_STA_GOTIP\n");
break;
}
case SYSTEM_EVENT_AP_START:
os_printf("SYSTEM_EVENT_AP_START\n");
{
WIFI_DEBUG("SYSTEM_EVENT_AP_START\n");
break;
}
case SYSTEM_EVENT_AP_STOP:
os_printf("SYSTEM_EVENT_AP_STOP\n");
{
WIFI_DEBUG("SYSTEM_EVENT_AP_STOP\n");
break;
}
case SYSTEM_EVENT_AP_STACONNECTED:
{
system_event_ap_staconnected_t *staconnected;
staconnected = &event->event_info.sta_connected;
os_printf("SYSTEM_EVENT_AP_STACONNECTED\nmac:%02x:%02x:%02x:%02x:%02x:%02x, aid:%d\n", \
WIFI_DEBUG("SYSTEM_EVENT_AP_STACONNECTED\nmac:%02x:%02x:%02x:%02x:%02x:%02x, aid:%d\n", \
staconnected->mac[0], staconnected->mac[0], staconnected->mac[1], \
staconnected->mac[3], staconnected->mac[4], staconnected->mac[5], staconnected->aid);
break;
}
case SYSTEM_EVENT_AP_STADISCONNECTED:
{
system_event_ap_stadisconnected_t *stadisconnected;
stadisconnected = &event->event_info.sta_disconnected;
os_printf("SYSTEM_EVENT_AP_STADISCONNECTED\nmac:%02x:%02x:%02x:%02x:%02x:%02x, aid:%d\n", \
WIFI_DEBUG("SYSTEM_EVENT_AP_STADISCONNECTED\nmac:%02x:%02x:%02x:%02x:%02x:%02x, aid:%d\n", \
stadisconnected->mac[0], stadisconnected->mac[0], stadisconnected->mac[1], \
stadisconnected->mac[3], stadisconnected->mac[4], stadisconnected->mac[5], stadisconnected->aid);
break;
}
case SYSTEM_EVENT_AP_PROBEREQRECVED:
{
system_event_ap_probe_req_rx_t *ap_probereqrecved;
ap_probereqrecved = &event->event_info.ap_probereqrecved;
os_printf("SYSTEM_EVENT_AP_PROBEREQRECVED\nrssi:%d, mac:%02x:%02x:%02x:%02x:%02x:%02x\n", \
WIFI_DEBUG("SYSTEM_EVENT_AP_PROBEREQRECVED\nrssi:%d, mac:%02x:%02x:%02x:%02x:%02x:%02x\n", \
ap_probereqrecved->rssi, ap_probereqrecved->mac[0], ap_probereqrecved->mac[0], ap_probereqrecved->mac[1], \
ap_probereqrecved->mac[3], ap_probereqrecved->mac[4], ap_probereqrecved->mac[5]);
break;
}
default:
os_printf("Error: no such kind of event!\n");
{
printf("Error: no such kind of event!\n");
break;
}
}
return ESP_OK;
@ -237,19 +291,19 @@ static esp_err_t esp_system_event_debug(system_event_t *event)
static esp_err_t esp_system_event_handler(system_event_t *event)
{
if (event == NULL) {
os_printf("Error: event is null!\n");
printf("Error: event is null!\n");
return ESP_FAIL;
}
esp_system_event_debug(event);
if ((event->event_id < SYSTEM_EVENT_MAX) && (event->event_id == g_system_event_handle_table[event->event_id].event_id)){
if (g_system_event_handle_table[event->event_id].event_handle){
os_printf("enter default callback\n");
WIFI_DEBUG("enter default callback\n");
g_system_event_handle_table[event->event_id].event_handle(event);
os_printf("exit default callback\n");
WIFI_DEBUG("exit default callback\n");
}
} else {
os_printf("mismatch or invalid event, id=%d\n", event->event_id);
printf("mismatch or invalid event, id=%d\n", event->event_id);
}
return esp_wifi_post_event_to_user(event);
@ -264,7 +318,7 @@ static void esp_system_event_task(void *pvParameters)
if (xQueueReceive(g_event_handler, &evt, portMAX_DELAY) == pdPASS) {
ret = esp_system_event_handler(&evt);
if (ret == ESP_FAIL)
os_printf("esp wifi post event to user fail!\n");
printf("esp wifi post event to user fail!\n");
}
}
}
@ -282,8 +336,8 @@ esp_err_t esp_event_send(system_event_t *event)
ret = xQueueSendToBack((xQueueHandle)g_event_handler, event, 0);
if (pdPASS != ret){
if (event) ets_printf("e=%d f\n", event->event_id);
else ets_printf("e null\n");
if (event) printf("e=%d f\n", event->event_id);
else printf("e null\n");
return ESP_FAIL;
}

View file

@ -77,9 +77,9 @@ This array is *NOT* const because it gets modified depending on what pools are/a
*/
static HeapRegionTagged_t regions[]={
{ (uint8_t *)0x3F800000, 0x20000, 15, 0}, //SPI SRAM, if available
// { (uint8_t *)0x3FFAE000, 0x2000, 0, 0}, //pool 16 <- can be used for BT <- THIS POOL DOESN'T WORK for some reason! Hw seems fine. ToDo: Figure out.
{ (uint8_t *)0x3FFAE000, 0x2000, 0, 0}, //pool 16 <- used for rom code
{ (uint8_t *)0x3FFB0000, 0x8000, 0, 0}, //pool 15 <- can be used for BT
{ (uint8_t *)0x3FFB8000, 0x8000, 0, 0}, //pool 14
{ (uint8_t *)0x3FFB8000, 0x8000, 0, 0}, //pool 14 <- can be used for BT
{ (uint8_t *)0x3FFC0000, 0x2000, 0, 0}, //pool 10-13, mmu page 0
{ (uint8_t *)0x3FFC2000, 0x2000, 0, 0}, //pool 10-13, mmu page 1
{ (uint8_t *)0x3FFC4000, 0x2000, 0, 0}, //pool 10-13, mmu page 2
@ -158,7 +158,6 @@ static void disable_mem_region(void *from, void *to) {
ToDo: These are very dependent on the linker script, and the logic involving this works only
because we're not using the SPI flash yet! If we enable that, this will break. ToDo: Rewrite by then.
*/
extern int _init_start, _text_end;
extern int _bss_start, _heap_start;
/*
@ -170,12 +169,23 @@ Same with loading of apps. Same with using SPI RAM.
void heap_alloc_caps_init() {
int i;
//Disable the bits of memory where this code is loaded.
disable_mem_region(&_init_start, &_text_end);
disable_mem_region(&_bss_start, &_heap_start);
disable_mem_region((void*)0x3ffae000, (void*)0x3ffb0000); //knock out ROM data region
disable_mem_region((void*)0x3ffe0000, (void*)0x3ffe8000); //knock out ROM data region
disable_mem_region((void*)0x40070000, (void*)0x40078000); //CPU0 cache region
disable_mem_region((void*)0x40078000, (void*)0x40080000); //CPU1 cache region
disable_mem_region((void*)0x40080000, (void*)0x400a0000); //pool 2-5
// TODO: this region should be checked, since we don't need to knock out all region finally
disable_mem_region((void*)0x3ffe0000, (void*)0x3ffe8000); //knock out ROM data region
#if CONFIG_MEMMAP_BT
disable_mem_region((void*)0x3ffb0000, (void*)0x3ffc0000); //knock out BT data region
#endif
#if CONFIG_MEMMAP_TRACEMEM
disable_mem_region((void*)0x3fff8000, (void*)0x40000000); //knock out trace mem region
#endif
#if 0
enable_spi_sram();
#else

View file

@ -20,16 +20,10 @@
//and all variables in shared RAM. This can be redirected to IRAM if
//needed using these macros.
//Forces data and flash into IRAM instead of flash / shared RAM
// Forces code into IRAM instead of flash
#define IRAM_ATTR __attribute__((section(".iram1")))
// Forces data into DRAM instead of flash
#define DRAM_ATTR __attribute__((section(".dram1")))
//Forces data and flash into the IRAM section of a specific core.
//Normally, you shouldn't have to use this: the linker will take care of
//only linking in the functions for that specific core.
#define IRAM_C0_ATTR __attribute__((section(".iram1pro")))
#define DRAM_C0_ATTR __attribute__((section(".iram1pro")))
#define IRAM_C1_ATTR __attribute__((section(".dram1app")))
#define DRAM_C1_ATTR __attribute__((section(".dram1app")))
#endif /* __ESP_ATTR_H__ */

View file

@ -19,6 +19,9 @@
#include <stdbool.h>
#include "esp_err.h"
#include "esp_wifi.h"
#include "tcpip_adapter.h"
#ifdef __cplusplus
extern "C" {
@ -32,6 +35,7 @@ typedef enum {
SYSTEM_EVENT_STA_CONNECTED, /**< ESP32 station connected to AP */
SYSTEM_EVENT_STA_DISCONNECTED, /**< ESP32 station disconnected to AP */
SYSTEM_EVENT_STA_AUTHMODE_CHANGE, /**< the auth mode of AP connected by ESP32 station changed */
SYSTEM_EVENT_STA_GOTIP, /**< ESP32 station received IP address */
SYSTEM_EVENT_AP_START, /**< ESP32 softap start */
SYSTEM_EVENT_AP_STOP, /**< ESP32 softap start */
SYSTEM_EVENT_AP_STACONNECTED, /**< a station connected to ESP32 soft-AP */
@ -43,6 +47,7 @@ typedef enum {
typedef struct {
uint32_t status; /**< status of scanning APs*/
uint8_t number;
uint8_t scan_id;
} system_event_sta_scan_done_t;
typedef struct {
@ -50,6 +55,7 @@ typedef struct {
uint8_t ssid_len; /**< SSID length of connected AP */
uint8_t bssid[6]; /**< BSSID of connected AP*/
uint8_t channel; /**< channel of connected AP*/
wifi_auth_mode_t authmode;
} system_event_sta_connected_t;
typedef struct {
@ -60,10 +66,14 @@ typedef struct {
} system_event_sta_disconnected_t;
typedef struct {
uint8_t old_mode; /**< the old auth mode of AP */
uint8_t new_mode; /**< the new auth mode of AP */
wifi_auth_mode_t old_mode; /**< the old auth mode of AP */
wifi_auth_mode_t new_mode; /**< the new auth mode of AP */
} system_event_sta_authmode_change_t;
typedef struct {
tcpip_adapter_ip_info_t ip_info;
} system_event_sta_got_ip_t;
typedef struct {
uint8_t mac[6]; /**< MAC address of the station connected to ESP32 soft-AP */
uint8_t aid; /**< the aid that ESP32 soft-AP gives to the station connected to */
@ -84,6 +94,7 @@ typedef union {
system_event_sta_disconnected_t disconnected; /**< ESP32 station disconnected to AP */
system_event_sta_scan_done_t scan_done; /**< ESP32 station scan (APs) done */
system_event_sta_authmode_change_t auth_change; /**< the auth mode of AP ESP32 station connected to changed */
system_event_sta_got_ip_t got_ip;
system_event_ap_staconnected_t sta_connected; /**< a station connected to ESP32 soft-AP */
system_event_ap_stadisconnected_t sta_disconnected; /**< a station disconnected to ESP32 soft-AP */
system_event_ap_probe_req_rx_t ap_probereqrecved; /**< ESP32 softAP receive probe request packet */

View file

@ -0,0 +1,209 @@
// Copyright 2010-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __ESP_INTR_H__
#define __ESP_INTR_H__
#include "rom/ets_sys.h"
#include "freertos/xtensa_api.h"
#ifdef __cplusplus
extern "C" {
#endif
#define ESP_CCOMPARE_INTR_ATTACH(func, arg) \
xt_set_interrupt_handler(ETS_CCOMPARE_INUM, (func), (void *)(arg))
#define ESP_EPWM_INTR_ATTACH(func, arg) \
xt_set_interrupt_handler(ETS_EPWM_INUM, (func), (void *)(arg))
#define ESP_MPWM_INTR_ATTACH(func, arg) \
xt_set_interrupt_handler(ETS_MPWM_INUM, (func), (void *)(arg))
#define ESP_SPI1_INTR_ATTACH(func, arg) \
xt_set_interrupt_handler(ETS_SPI1_INUM, (func), (void *)(arg))
#define ESP_SPI2_INTR_ATTACH(func, arg) \
xt_set_interrupt_handler(ETS_SPI2_INUM, (func), (void *)(arg))
#define ESP_SPI3_INTR_ATTACH(func, arg) \
xt_set_interrupt_handler(ETS_SPI3_INUM, (func), (void *)(arg))
#define ESP_I2S0_INTR_ATTACH(func, arg) \
xt_set_interrupt_handler(ETS_I2S0_INUM, (func), (void *)(arg))
#define ESP_PCNT_INTR_ATTACH(func, arg) \
xt_set_interrupt_handler(ETS_PCNT_INUM, (func), (void *)(arg))
#define ESP_LEDC_INTR_ATTACH(func, arg) \
xt_set_interrupt_handler(ETS_LEDC_INUM, (func), (void *)(arg))
#define ESP_WMAC_INTR_ATTACH(func, arg) \
xt_set_interrupt_handler(ETS_WMAC_INUM, (func), (void *)(arg))
#define ESP_FRC_TIMER1_INTR_ATTACH(func, arg) \
xt_set_interrupt_handler(ETS_FRC_TIMER1_INUM, (func), (void *)(arg))
#define ESP_FRC_TIMER2_INTR_ATTACH(func, arg) \
xt_set_interrupt_handler(ETS_FRC_TIMER2_INUM, (func), (void *)(arg))
#define ESP_GPIO_INTR_ATTACH(func, arg) \
xt_set_interrupt_handler(ETS_GPIO_INUM, (func), (void *)(arg))
#define ESP_UART_INTR_ATTACH(func, arg) \
xt_set_interrupt_handler(ETS_UART_INUM, (func), (void *)(arg))
#define ESP_WDT_INTR_ATTACH(func, arg) \
xt_set_interrupt_handler(ETS_WDT_INUM, (func), (void *)(arg))
#define ESP_RTC_INTR_ATTACH(func, arg) \
xt_set_interrupt_handler(ETS_RTC_INUM, (func), (void *)(arg))
#define ESP_SLC_INTR_ATTACH(func, arg) \
xt_set_interrupt_handler(ETS_SLC_INUM, (func), (void *)(arg))
#define ESP_RMT_CTRL_INTRL(func,arg)\
xt_set_interrupt_handler(ETS_RMT_CTRL_INUM, (func), (void *)(arg))
#define ESP_INTR_ENABLE(inum) \
xt_ints_on((1<<inum))
#define ESP_INTR_DISABLE(inum) \
xt_ints_off((1<<inum))
#define ESP_CCOMPARE_INTR_ENBALE() \
ESP_INTR_ENABLE(ETS_CCOMPARE_INUM)
#define ESP_CCOMPARE_INTR_DISBALE() \
ESP_INTR_DISABLE(ETS_CCOMPARE_INUM)
#define ESP_SPI1_INTR_ENABLE() \
ESP_INTR_ENABLE(ETS_SPI1_INUM)
#define ESP_SPI1_INTR_DISABLE() \
ESP_INTR_DISABLE(ETS_SPI1_INUM)
#define ESP_SPI2_INTR_ENABLE() \
ESP_INTR_ENABLE(ETS_SPI2_INUM)
#define ESP_PWM_INTR_ENABLE() \
ESP_INTR_ENABLE(ETS_PWM_INUM)
#define ESP_PWM_INTR_DISABLE() \
ESP_INTR_DISABLE(ETS_PWM_INUM)
#define ESP_SPI2_INTR_DISABLE() \
ESP_INTR_DISABLE(ETS_SPI2_INUM)
#define ESP_SPI3_INTR_ENABLE() \
ESP_INTR_ENABLE(ETS_SPI3_INUM)
#define ESP_SPI3_INTR_DISABLE() \
ESP_INTR_DISABLE(ETS_SPI3_INUM)
#define ESP_I2S0_INTR_ENABLE() \
ESP_INTR_ENABLE(ETS_I2S0_INUM)
#define ESP_I2S0_INTR_DISABLE() \
ESP_INTR_DISABLE(ETS_I2S0_INUM)
#define ESP_I2S1_INTR_ENABLE() \
ESP_INTR_ENABLE(ETS_I2S1_INUM)
#define ESP_I2S1_INTR_DISABLE() \
ESP_INTR_DISABLE(ETS_I2S1_INUM)
#define ESP_MPWM_INTR_ENABLE() \
ESP_INTR_ENABLE(ETS_MPWM_INUM)
#define ESP_EPWM_INTR_ENABLE() \
ESP_INTR_ENABLE(ETS_EPWM_INUM)
#define ESP_MPWM_INTR_DISABLE() \
ESP_INTR_DISABLE(ETS_MPWM_INUM)
#define ESP_EPWM_INTR_DISABLE() \
ESP_INTR_DISABLE(ETS_EPWM_INUM)
#define ESP_BB_INTR_ENABLE() \
ESP_INTR_ENABLE(ETS_BB_INUM)
#define ESP_BB_INTR_DISABLE() \
ESP_INTR_DISABLE(ETS_BB_INUM)
#define ESP_UART_INTR_ENABLE() \
ESP_INTR_ENABLE(ETS_UART_INUM)
#define ESP_UART_INTR_DISABLE() \
ESP_INTR_DISABLE(ETS_UART_INUM)
#define ESP_LEDC_INTR_ENABLE() \
ESP_INTR_ENABLE(ETS_LEDC_INUM)
#define ESP_LEDC_INTR_DISABLE() \
ESP_INTR_DISABLE(ETS_LEDC_INUM)
#define ESP_GPIO_INTR_ENABLE() \
ESP_INTR_ENABLE(ETS_GPIO_INUM)
#define ESP_GPIO_INTR_DISABLE() \
ESP_INTR_DISABLE(ETS_GPIO_INUM)
#define ESP_WDT_INTR_ENABLE() \
ESP_INTR_ENABLE(ETS_WDT_INUM)
#define ESP_WDT_INTR_DISABLE() \
ESP_INTR_DISABLE(ETS_WDT_INUM)
#define ESP_FRC1_INTR_ENABLE() \
ESP_INTR_ENABLE(ETS_FRC_TIMER1_INUM)
#define ESP_FRC1_INTR_DISABLE() \
ESP_INTR_DISABLE(ETS_FRC_TIMER1_INUM)
#define ESP_FRC2_INTR_ENABLE() \
ESP_INTR_ENABLE(ETS_FRC_TIMER2_INUM)
#define ESP_FRC2_INTR_DISABLE() \
ESP_INTR_DISABLE(ETS_FRC_TIMER2_INUM)
#define ESP_RTC_INTR_ENABLE() \
ESP_INTR_ENABLE(ETS_RTC_INUM)
#define ESP_RTC_INTR_DISABLE() \
ESP_INTR_DISABLE(ETS_RTC_INUM)
#define ESP_SLC_INTR_ENABLE() \
ESP_INTR_ENABLE(ETS_SLC_INUM)
#define ESP_SLC_INTR_DISABLE() \
ESP_INTR_DISABLE(ETS_SLC_INUM)
#define ESP_PCNT_INTR_ENABLE() \
ESP_INTR_ENABLE(ETS_PCNT_INUM)
#define ESP_PCNT_INTR_DISABLE() \
ESP_INTR_DISABLE(ETS_PCNT_INUM)
#define ESP_RMT_CTRL_ENABLE() \
ESP_INTR_ENABLE(ETS_RMT_CTRL_INUM)
#define ESP_RMT_CTRL_DIABLE() \
ESP_INTR_DISABLE(ETS_RMT_CTRL_INUM)
#ifdef __cplusplus
}
#endif
#endif /* __ESP_INTR_H__ */

View file

@ -17,6 +17,8 @@
#include <stdint.h>
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
@ -84,15 +86,6 @@ void system_deep_sleep(uint64_t time_in_us);
*/
uint32_t system_get_time(void);
/**
* @brief Print the system memory distribution, including data/rodata/bss/heap.
*
* @param null
*
* @return null
*/
void system_print_meminfo(void);
/**
* @brief Get the size of available heap.
*
@ -102,22 +95,6 @@ void system_print_meminfo(void);
*/
uint32_t system_get_free_heap_size(void);
/**
* @brief Get the chip ID.
*
* Example:
* <pre>
* uint8 chip_id[6];
* system_get_chip_id(chip_id);
* </pre>
*
* @param uint8 *chip_id : the chip ID
*
* @return true : succeed
* @return false : fail
*/
bool system_get_chip_id(uint8_t *chip_id);
/**
* @brief Get RTC time, unit: RTC clock cycle.
*
@ -171,98 +148,6 @@ bool system_rtc_mem_read(uint16_t src, void *dst, uint16_t n);
*/
bool system_rtc_mem_write(uint16_t dst, const void *src, uint16_t n);
typedef enum {
ADC1_PAD_GPIO36 = 0,
ADC1_PAD_GPIO37,
ADC1_PAD_GPIO38,
ADC1_PAD_GPIO39,
ADC1_PAD_GPIO32,
ADC1_PAD_GPIO33,
ADC1_PAD_GPIO34,
ADC1_PAD_GPIO35
} adc1_read_pad_t;
typedef enum {
ADC1_ATTEN_0DB = 0,
ADC1_ATTEN_3DB,
ADC1_ATTEN_6DB,
ADC1_ATTEN_12DB
} adc1_read_atten_t;
/**
* @brief Read ADC1.
*
* @param adc1_read_pad pad : the corresponding GPIO
* @param adc1_read_atten atten : value of attenuation
*
* @return range of the return value is [0, 4096].
* - If atten == 0, the range of voltage can be measured is [0, 1] V.
* - If atten == 1, the range of voltage can be measured is [0, 1.4] V.
* - If atten == 2, the range of voltage can be measured is [0, 2] V.
* - If atten == 3, the range of voltage can be measured is [0, 4] V.
*/
uint16_t system_adc1_read(adc1_read_pad_t pad, adc1_read_atten_t atten);
/**
* @brief Measure the power voltage of VDD3P3 pin 3 and 4, unit : 1/1024 V.
*
* @attention system_get_vdd33 depends on RF, please do not use it if RF is disabled.
*
* @param null
*
* @return Power voltage of VDD33, unit : 1/1024 V
*/
uint16_t system_get_vdd33(void);
/**
* @brief Write data into flash with protection.
*
* Flash read/write has to be 4-bytes aligned.
*
* Protection of flash read/write :
* use 3 sectors (4KBytes per sector) to save 4KB data with protect,
* sector 0 and sector 1 are data sectors, back up each other,
* save data alternately, sector 2 is flag sector, point out which sector
* is keeping the latest data, sector 0 or sector 1.
*
* @param uint16 start_sec : start sector (sector 0) of the 3 sectors which are
* used for flash read/write protection.
* - For example, in IOT_Demo we can use the 3 sectors (3 * 4KB) starting from flash
* 0x3D000 for flash read/write protection, so the parameter start_sec should be 0x3D
* @param void *param : pointer of the data to be written
* @param uint16 len : data length, should be less than a sector, which is 4 * 1024
*
* @return true : succeed
* @return false : fail
*/
bool system_param_save_with_protect(uint16_t start_sec, void *param, uint16_t len);
/**
* @brief Read the data saved into flash with the read/write protection.
*
* Flash read/write has to be 4-bytes aligned.
*
* Read/write protection of flash:
* use 3 sectors (4KB per sector) to save 4KB data with protect, sector
* 0 and sector 1 are data sectors, back up each other, save data alternately,
* sector 2 is flag sector, point out which sector is keeping the latest data,
* sector 0 or sector 1.
*
* @param uint16 start_sec : start sector (sector 0) of the 3 sectors used for
* flash read/write protection. It cannot be sector 1 or sector 2.
* - For example, in IOT_Demo, the 3 sectors (3 * 4KB) starting from flash 0x3D000
* can be used for flash read/write protection.
* The parameter start_sec is 0x3D, and it cannot be 0x3E or 0x3F.
* @param uint16 offset : offset of data saved in sector
* @param void *param : data pointer
* @param uint16 len : data length, offset + len =< 4 * 1024
*
* @return true : succeed
* @return false : fail
*/
bool system_param_load(uint16_t start_sec, uint16_t offset, void *param, uint16_t len);
/** \defgroup System_boot_APIs Boot APIs
* @brief boot APIs
*/
@ -288,63 +173,15 @@ bool system_param_load(uint16_t start_sec, uint16_t offset, void *param, uint16_
* @{
*/
typedef enum {
DEFAULT_MAC = 0, /**< Default hardware MAC provided by Espressif Systems */
USER_MAC, /**< User-define hardware MAC */
} mac_group_t;
typedef enum {
WIFI_MAC = 0, /**< Hardware MAC address of ESP32 WiFi */
BT_MAC, /**< Hardware MAC address of ESP32 bluetooth */
} mac_type_t;
/**
* @brief Set user-define hardware MAC address.
*
* @attention Hardware MAC address can only be set ONCE for each ESP32 chip.
*
* @param mac_type type : type of hardware MAC address.
* @param uint8 *mac : user-define hardware MAC address, length: 6 bytes.
*
* @return 0 : succeed to set.
* @return 1 : the hardware MAC has been set once, users can not set it any more.
* @return 2 : fail to set.
* @return 3 : invalid parameter.
*/
int system_efuse_program_user_mac(mac_type_t type, uint8_t *mac);
/**
* @brief Read hardware MAC address.
*
* @param mac_group group : default MAC or user-defined MAC.
* @param mac_type type : type of hardware MAC address.
* @param uint8 *mac : the hardware MAC address, length: 6 bytes.
* @param uint8 mac[6] : the hardware MAC address, length: 6 bytes.
*
* @return true : succeed
* @return false : fail
* @return esp_err_t
*/
bool system_efuse_read_mac(mac_group_t group, mac_type_t type, uint8_t *mac);
esp_err_t system_efuse_read_mac(uint8_t mac[6]);
/**
* @brief Set hardware MAC group, default MAC or user-defined MAC.
*
* @attention This API needs system_restart to take effect.
*
* @param mac_group group : default MAC or user-defined MAC.
*
* @return true : succeed
* @return false : fail
*/
bool system_efuse_set_mac_group(mac_group_t group);
/**
* @brief Get hardware MAC group, default MAC or user-defined MAC.
*
* @param null
*
* @return mac_group, the hardware MAC group.
*/
mac_group_t system_efuse_get_mac_group(void);
void system_init(void);

View file

@ -19,6 +19,7 @@
#include <stdbool.h>
#include "esp_err.h"
#include "rom/queue.h"
#ifdef __cplusplus
extern "C" {
@ -121,6 +122,10 @@ esp_err_t esp_wifi_connect(void);
esp_err_t esp_wifi_disconnect(void);
esp_err_t esp_wifi_clear_fast_connect(void);
esp_err_t esp_wifi_kick_station(uint16_t aid);
typedef struct {
char *ssid; /**< SSID of AP */
uint8_t *bssid; /**< MAC address of AP */
@ -128,7 +133,7 @@ typedef struct {
bool show_hidden; /**< enable to scan AP whose SSID is hidden */
} wifi_scan_config_t;
esp_err_t esp_wifi_scan_start(wifi_scan_config_t *conf);
esp_err_t esp_wifi_scan_start(wifi_scan_config_t *conf, bool block);
esp_err_t esp_wifi_scan_stop(void);
@ -139,7 +144,7 @@ typedef struct {
uint8_t ssid[32]; /**< SSID of AP */
uint8_t primary; /**< channel of AP */
wifi_second_chan_t second; /**< second channel of AP */
char rssi; /**< single strength of AP */
signed char rssi; /**< signal strength of AP */
wifi_auth_mode_t authmode; /**< authmode of AP */
}wifi_ap_list_t;
@ -187,7 +192,7 @@ esp_err_t esp_wifi_get_mac(wifi_interface_t ifx, uint8_t mac[6]);
typedef void (* wifi_promiscuous_cb_t)(void *buf, uint16_t len);
wifi_promiscuous_cb_t wifi_set_promiscuous_rx_cb(wifi_promiscuous_cb_t cb);
esp_err_t esp_wifi_set_promiscuous_rx_cb(wifi_promiscuous_cb_t cb);
esp_err_t esp_wifi_set_promiscuous(uint8_t enable);
@ -220,10 +225,30 @@ esp_err_t esp_wifi_set_config(wifi_interface_t ifx, wifi_config_t *conf);
esp_err_t esp_wifi_get_config(wifi_interface_t ifx, wifi_config_t *conf);
struct station_info {
STAILQ_ENTRY(station_info) next;
uint8_t bssid[6];
};
esp_err_t esp_wifi_get_station_list(struct station_info **station);
esp_err_t esp_wifi_free_station_list(void);
typedef enum {
WIFI_STORAGE_RAM,
WIFI_STORAGE_FLASH,
} wifi_storage_t;
esp_err_t esp_wifi_set_storage(wifi_storage_t storage);
typedef esp_err_t (*wifi_rxcb_t)(void *buffer, uint16_t len, void* eb);
esp_err_t esp_wifi_reg_rxcb(wifi_interface_t ifx, wifi_rxcb_t fn);
esp_err_t esp_wifi_set_auto_connect(bool en);
esp_err_t esp_wifi_get_auto_connect(bool *en);
#ifdef __cplusplus
}
#endif

View file

@ -31,7 +31,7 @@ void ets_aes_enable(void);
void ets_aes_disable(void);
void ets_set_endian(bool key_word_swap, bool key_byte_swap,
void ets_aes_set_endian(bool key_word_swap, bool key_byte_swap,
bool in_word_swap, bool in_byte_swap,
bool out_word_swap, bool out_byte_swap);

View file

@ -232,10 +232,10 @@ void intr_matrix_set(int cpu_no, uint32_t model_num, uint32_t intr_num);
#define ETS_INTR_ENABLE(inum) \
ets_isr_unmask((1<<inum))
xt_ints_on((1<<inum))
#define ETS_INTR_DISABLE(inum) \
ets_isr_mask((1<<inum))
xt_ints_off((1<<inum))
#define ETS_CCOMPARE_INTR_ENBALE() \
ETS_INTR_ENABLE(ETS_CCOMPARE_INUM)

View file

@ -15,14 +15,16 @@
#ifndef _ROM_MD5_HASH_H_
#define _ROM_MD5_HASH_H_
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
struct MD5Context {
uint32 buf[4];
uint32 bits[2];
uint8 in[64];
uint32_t buf[4];
uint32_t bits[2];
uint8_t in[64];
};
void MD5Init(struct MD5Context *context);

View file

@ -0,0 +1,36 @@
// Copyright 2010-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _SOC_CPU_H
#define _SOC_CPU_H
#include "xtensa/corebits.h"
/* C macros for xtensa special register read/write/exchange */
#define RSR(reg, curval) asm volatile ("rsr %0, " #reg : "=r" (curval));
#define WSR(reg, newval) asm volatile ("wsr %0, " #reg : : "r" (newval));
#define XSR(reg, swapval) asm volatile ("xsr %0, " #reg : "+r" (swapval));
/* Return true if the CPU is in an interrupt context
(PS.UM == 0)
*/
static inline bool cpu_in_interrupt_context(void)
{
uint32_t ps;
RSR(PS, ps);
return (ps & PS_UM) == 0;
}
#endif

View file

@ -89,7 +89,7 @@
#define APB_CLK_FREQ_ROM 13*1000000
#define CPU_CLK_FREQ_ROM APB_CLK_FREQ_ROM
#define CPU_CLK_FREQ APB_CLK_FREQ
#define APB_CLK_FREQ 40*1000000 //unit: Hz
#define APB_CLK_FREQ 80*1000000 //unit: Hz
#define UART_CLK_FREQ APB_CLK_FREQ
//#define WDT_CLK_FREQ APB_CLK_FREQ
#define TIMER_CLK_FREQ (80000000>>4) //80MHz divided by 16

View file

@ -0,0 +1,14 @@
/* THESE ARE THE VIRTUAL RUNTIME ADDRESSES */
/* The load addresses are defined later using the AT statements. */
MEMORY
{
/* All these values assume the flash cache is on, and have the blocks this uses subtracted from the length
of the various regions. The 'data access port' dram/drom regions map to the same iram/irom regions but
are connected to the data port of the CPU and eg allow bytewise access. */
iram0_0_seg (RX) : org = 0x40080000, len = 0x20000 /* IRAM for PRO cpu. Not sure if happy with this, this is MMU area... */
iram0_2_seg (RX) : org = 0x400D0018, len = 0x330000 /* Even though the segment name is iram, it is actually mapped to flash */
dram0_0_seg (RW) : org = 0x3FFC0000, len = 0x40000 /* Shared RAM, minus rom bss/data/stack.*/
drom0_0_seg (R) : org = 0x3F400010, len = 0x800000
}
_heap_end = 0x40000000;

View file

@ -0,0 +1,14 @@
/* THESE ARE THE VIRTUAL RUNTIME ADDRESSES */
/* The load addresses are defined later using the AT statements. */
MEMORY
{
/* All these values assume the flash cache is on, and have the blocks this uses subtracted from the length
of the various regions. The 'data access port' dram/drom regions map to the same iram/irom regions but
are connected to the data port of the CPU and eg allow bytewise access. */
iram0_0_seg (RX) : org = 0x40080000, len = 0x20000 /* IRAM for PRO cpu. Not sure if happy with this, this is MMU area... */
iram0_2_seg (RX) : org = 0x400D0018, len = 0x330000 /* Even though the segment name is iram, it is actually mapped to flash */
dram0_0_seg (RW) : org = 0x3FFC0000, len = 0x38000 /* Shared RAM, minus rom bss/data/stack.*/
drom0_0_seg (R) : org = 0x3F400010, len = 0x800000
}
_heap_end = 0x3FFF8000;

View file

@ -1,18 +1,3 @@
/* THESE ARE THE VIRTUAL RUNTIME ADDRESSES */
/* The load addresses are defined later using the AT statements. */
MEMORY
{
/* All these values assume the flash cache is on, and have the blocks this uses subtracted from the length
of the various regions. The 'data access port' dram/drom regions map to the same iram/irom regions but
are connected to the data port of the CPU and eg allow bytewise access. */
iram0_0_seg (RX) : org = 0x40080000, len = 0x18000 /* IRAM for PRO cpu. Not sure if happy with this, this is MMU area... */
iram0_2_seg (RX) : org = 0x400D0018, len = 0x330000 /* Even though the segment name is iram, it is actually mapped to flash */
dram0_0_seg (RW) : org = 0x3FFC0000, len = 0x20000 /* Shared RAM, minus rom bss/data/stack.*/
drom0_0_seg (R) : org = 0x3F400010, len = 0x800000
}
_heap_end = 0x3fffe000;
/* Default entry point: */
ENTRY(call_user_start_cpu0);
@ -106,6 +91,7 @@ SECTIONS
KEEP(*(.sdata2.*))
KEEP(*(.gnu.linkonce.s2.*))
KEEP(*(.jcr))
*(.dram1 .dram1.*)
_data_end = ABSOLUTE(.);
. = ALIGN(4);
_heap_start = ABSOLUTE(.);

View file

@ -0,0 +1,14 @@
/* THESE ARE THE VIRTUAL RUNTIME ADDRESSES */
/* The load addresses are defined later using the AT statements. */
MEMORY
{
/* All these values assume the flash cache is on, and have the blocks this uses subtracted from the length
of the various regions. The 'data access port' dram/drom regions map to the same iram/irom regions but
are connected to the data port of the CPU and eg allow bytewise access. */
iram0_0_seg (RX) : org = 0x40080000, len = 0x20000 /* IRAM for PRO cpu. Not sure if happy with this, this is MMU area... */
iram0_2_seg (RX) : org = 0x400D0018, len = 0x330000 /* Even though the segment name is iram, it is actually mapped to flash */
dram0_0_seg (RW) : org = 0x3FFB0000, len = 0x50000 /* Shared RAM, minus rom bss/data/stack.*/
drom0_0_seg (R) : org = 0x3F400010, len = 0x800000
}
_heap_end = 0x40000000;

View file

@ -0,0 +1,14 @@
/* THESE ARE THE VIRTUAL RUNTIME ADDRESSES */
/* The load addresses are defined later using the AT statements. */
MEMORY
{
/* All these values assume the flash cache is on, and have the blocks this uses subtracted from the length
of the various regions. The 'data access port' dram/drom regions map to the same iram/irom regions but
are connected to the data port of the CPU and eg allow bytewise access. */
iram0_0_seg (RX) : org = 0x40080000, len = 0x20000 /* IRAM for PRO cpu. Not sure if happy with this, this is MMU area... */
iram0_2_seg (RX) : org = 0x400D0018, len = 0x330000 /* Even though the segment name is iram, it is actually mapped to flash */
dram0_0_seg (RW) : org = 0x3FFB0000, len = 0x48000 /* Shared RAM, minus rom bss/data/stack.*/
drom0_0_seg (R) : org = 0x3F400010, len = 0x800000
}
_heap_end = 0x3FFF8000;

@ -1 +1 @@
Subproject commit 40dc7af7f3d8da6745476e66cbd65be9b8988f6c
Subproject commit b95a359c344cd052f45e3c38515e4613dd29f298

View file

@ -19,13 +19,15 @@
#include <errno.h>
#include <sys/reent.h>
#include <stdlib.h>
#include "esp_attr.h"
#include "rom/libc_stubs.h"
#include "rom/uart.h"
#include "soc/cpu.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/portmacro.h"
#include "freertos/task.h"
int uart_tx_one_char(uint8_t c);
void abort() {
do
{
@ -136,6 +138,21 @@ int _open_r(struct _reent *r, const char * path, int flags, int mode) {
ssize_t _write_r(struct _reent *r, int fd, const void * data, size_t size) {
const char* p = (const char*) data;
if (fd == STDOUT_FILENO) {
static _lock_t stdout_lock; /* lazily initialised */
/* Even though newlib does stream locking on stdout, we need
a dedicated stdout UART lock...
This is because each task has its own _reent structure with
unique FILEs for stdin/stdout/stderr, so these are
per-thread (lazily initialised by __sinit the first time a
stdio function is used, see findfp.c:235.
It seems like overkill to allocate a FILE-per-task and lock
a thread-local stream, but I see no easy way to fix this
(pre-__sinit_, tasks have "fake" FILEs ie __sf_fake_stdout
which aren't fully valid.)
*/
_lock_acquire_recursive(&stdout_lock);
while(size--) {
#if CONFIG_NEWLIB_STDOUT_ADDCR
if (*p=='\n') {
@ -145,6 +162,7 @@ ssize_t _write_r(struct _reent *r, int fd, const void * data, size_t size) {
uart_tx_one_char(*p);
++p;
}
_lock_release_recursive(&stdout_lock);
}
return size;
}
@ -158,37 +176,188 @@ ssize_t _read_r(struct _reent *r, int fd, void * dst, size_t size) {
return 0;
}
// TODO: implement locks via FreeRTOS mutexes
void _lock_init(_lock_t *lock) {
/* Notes on our newlib lock implementation:
*
* - Use FreeRTOS mutex semaphores as locks.
* - lock_t is int, but we store an xSemaphoreHandle there.
* - Locks are no-ops until the FreeRTOS scheduler is running.
* - Due to this, locks need to be lazily initialised the first time
* they are acquired. Initialisation/deinitialisation of locks is
* protected by lock_init_spinlock.
* - Race conditions around lazy initialisation (via lock_acquire) are
* protected against.
* - Anyone calling lock_close is reponsible for ensuring noone else
* is holding the lock at this time.
* - Race conditions between lock_close & lock_init (for the same lock)
* are the responsibility of the caller.
*/
static portMUX_TYPE lock_init_spinlock = portMUX_INITIALIZER_UNLOCKED;
/* Initialise the given lock by allocating a new mutex semaphore
as the _lock_t value.
*/
static void IRAM_ATTR lock_init_generic(_lock_t *lock, uint8_t mutex_type) {
portENTER_CRITICAL(&lock_init_spinlock);
if (xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED) {
/* nothing to do until the scheduler is running */
*lock = 0; /* ensure lock is zeroed out, in case it's an automatic variable */
portEXIT_CRITICAL(&lock_init_spinlock);
return;
}
if (*lock) {
/* Lock already initialised (either we didn't check earlier,
or it got initialised while we were waiting for the
spinlock.) */
}
else
{
/* Create a new semaphore
this is a bit of an API violation, as we're calling the
private function xQueueCreateMutex(x) directly instead of
the xSemaphoreCreateMutex / xSemaphoreCreateRecursiveMutex
wrapper functions...
The better alternative would be to pass pointers to one of
the two xSemaphoreCreate___Mutex functions, but as FreeRTOS
implements these as macros instead of inline functions
(*party like it's 1998!*) it's not possible to do this
without writing wrappers. Doing it this way seems much less
spaghetti-like.
*/
xSemaphoreHandle new_sem = xQueueCreateMutex(mutex_type);
if (!new_sem) {
abort(); /* No more semaphores available or OOM */
}
*lock = (_lock_t)new_sem;
}
portEXIT_CRITICAL(&lock_init_spinlock);
}
void _lock_init_recursive(_lock_t *lock) {
void IRAM_ATTR _lock_init(_lock_t *lock) {
lock_init_generic(lock, queueQUEUE_TYPE_MUTEX);
}
void _lock_close(_lock_t *lock) {
void IRAM_ATTR _lock_init_recursive(_lock_t *lock) {
lock_init_generic(lock, queueQUEUE_TYPE_RECURSIVE_MUTEX);
}
void _lock_close_recursive(_lock_t *lock) {
/* Free the mutex semaphore pointed to by *lock, and zero it out.
Note that FreeRTOS doesn't account for deleting mutexes while they
are held, and neither do we... so take care not to delete newlib
locks while they may be held by other tasks!
*/
void IRAM_ATTR _lock_close(_lock_t *lock) {
portENTER_CRITICAL(&lock_init_spinlock);
if (*lock) {
xSemaphoreHandle h = (xSemaphoreHandle)(*lock);
#if (INCLUDE_xSemaphoreGetMutexHolder == 1)
configASSERT(xSemaphoreGetMutexHolder(h) == NULL); /* mutex should not be held */
#endif
vSemaphoreDelete(h);
*lock = 0;
}
portEXIT_CRITICAL(&lock_init_spinlock);
}
void _lock_acquire(_lock_t *lock) {
/* Acquire the mutex semaphore for lock. wait up to delay ticks.
mutex_type is queueQUEUE_TYPE_RECURSIVE_MUTEX or queueQUEUE_TYPE_MUTEX
*/
static int IRAM_ATTR lock_acquire_generic(_lock_t *lock, uint32_t delay, uint8_t mutex_type) {
xSemaphoreHandle h = (xSemaphoreHandle)(*lock);
if (!h) {
if (xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED) {
return 0; /* locking is a no-op before scheduler is up, so this "succeeds" */
}
/* lazy initialise lock - might have had a static initializer in newlib (that we don't use),
or _lock_init might have been called before the scheduler was running... */
lock_init_generic(lock, mutex_type);
h = (xSemaphoreHandle)(*lock);
configASSERT(h != NULL);
}
BaseType_t success;
if (cpu_in_interrupt_context()) {
/* In ISR Context */
if (mutex_type == queueQUEUE_TYPE_RECURSIVE_MUTEX) {
abort(); /* recursive mutexes make no sense in ISR context */
}
BaseType_t higher_task_woken = false;
success = xSemaphoreTakeFromISR(h, &higher_task_woken);
if (!success && delay > 0) {
abort(); /* Tried to block on mutex from ISR, couldn't... rewrite your program to avoid libc interactions in ISRs! */
}
if (higher_task_woken) {
portYIELD_FROM_ISR();
}
}
else {
/* In task context */
if (mutex_type == queueQUEUE_TYPE_RECURSIVE_MUTEX) {
success = xSemaphoreTakeRecursive(h, delay);
} else {
success = xSemaphoreTake(h, delay);
}
}
return (success == pdTRUE) ? 0 : -1;
}
void _lock_acquire_recursive(_lock_t *lock) {
void IRAM_ATTR _lock_acquire(_lock_t *lock) {
lock_acquire_generic(lock, portMAX_DELAY, queueQUEUE_TYPE_MUTEX);
}
int _lock_try_acquire(_lock_t *lock) {
return 0;
void IRAM_ATTR _lock_acquire_recursive(_lock_t *lock) {
lock_acquire_generic(lock, portMAX_DELAY, queueQUEUE_TYPE_RECURSIVE_MUTEX);
}
int _lock_try_acquire_recursive(_lock_t *lock) {
return 0;
int IRAM_ATTR _lock_try_acquire(_lock_t *lock) {
return lock_acquire_generic(lock, 0, queueQUEUE_TYPE_MUTEX);
}
void _lock_release(_lock_t *lock) {
int IRAM_ATTR _lock_try_acquire_recursive(_lock_t *lock) {
return lock_acquire_generic(lock, 0, queueQUEUE_TYPE_RECURSIVE_MUTEX);
}
void _lock_release_recursive(_lock_t *lock) {
/* Release the mutex semaphore for lock.
mutex_type is queueQUEUE_TYPE_RECURSIVE_MUTEX or queueQUEUE_TYPE_MUTEX
*/
static void IRAM_ATTR lock_release_generic(_lock_t *lock, uint8_t mutex_type) {
xSemaphoreHandle h = (xSemaphoreHandle)(*lock);
if (h == NULL) {
/* This is probably because the scheduler isn't running yet,
or the scheduler just started running and some code was
"holding" a not-yet-initialised lock... */
return;
}
if (cpu_in_interrupt_context()) {
if (mutex_type == queueQUEUE_TYPE_RECURSIVE_MUTEX) {
abort(); /* indicates logic bug, it shouldn't be possible to lock recursively in ISR */
}
BaseType_t higher_task_woken = false;
xSemaphoreGiveFromISR(h, &higher_task_woken);
if (higher_task_woken) {
portYIELD_FROM_ISR();
}
} else {
if (mutex_type == queueQUEUE_TYPE_RECURSIVE_MUTEX) {
xSemaphoreGiveRecursive(h);
} else {
xSemaphoreGive(h);
}
}
}
void IRAM_ATTR _lock_release(_lock_t *lock) {
lock_release_generic(lock, queueQUEUE_TYPE_MUTEX);
}
void IRAM_ATTR _lock_release_recursive(_lock_t *lock) {
lock_release_generic(lock, queueQUEUE_TYPE_RECURSIVE_MUTEX);
}
static struct _reent s_reent;
@ -239,7 +408,7 @@ static struct syscall_stub_table s_stub_table = {
._lock_init = &_lock_init,
._lock_init_recursive = &_lock_init_recursive,
._lock_close = &_lock_close,
._lock_close_recursive = &_lock_close_recursive,
._lock_close_recursive = &_lock_close,
._lock_acquire = &_lock_acquire,
._lock_acquire_recursive = &_lock_acquire_recursive,
._lock_try_acquire = &_lock_try_acquire,

View file

@ -13,6 +13,7 @@
// limitations under the License.
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include "esp_err.h"
#include "esp_wifi.h"
@ -23,30 +24,11 @@
#include "freertos/queue.h"
#include "freertos/semphr.h"
//#include "tcpip_adapter.h"
#define ESP32_WORKAROUND 1
#if CONFIG_WIFI_ENABLED
#ifdef ESP32_WORKAROUND
SemaphoreHandle_t stdio_mutex_tx = NULL;
#define os_printf(fmt, ...) do {\
if (!stdio_mutex_tx) {\
stdio_mutex_tx = xSemaphoreCreateMutex();\
}\
\
xSemaphoreTake(stdio_mutex_tx, portMAX_DELAY);\
ets_printf(fmt, ##__VA_ARGS__);\
xSemaphoreGive(stdio_mutex_tx);\
} while (0)
#endif
static wifi_startup_cb_t startup_cb;
#define WIFI_DEBUG os_printf
#define WIFI_DEBUG(...)
#define WIFI_API_CALL_CHECK(info, api_call, ret) \
do{\
esp_err_t __err = (api_call);\
@ -56,6 +38,8 @@ do{\
}\
} while(0)
static void esp_wifi_task(void *pvParameters)
{
esp_err_t err;
@ -65,29 +49,37 @@ static void esp_wifi_task(void *pvParameters)
do {
err = esp_wifi_init(&cfg);
if (err != ESP_OK) {
WIFI_DEBUG("esp_wifi_init fail, ret=%d\n", err);
break;
}
if (startup_cb) {
err = (*startup_cb)();
if (err != ESP_OK) {
WIFI_DEBUG("startup_cb fail, ret=%d\n", err);
break;
}
}
err = esp_wifi_start();
if (err != ESP_OK) { // TODO: if already started, it's also OK
if (err != ESP_OK) {
WIFI_DEBUG("esp_wifi_start fail, ret=%d\n", err);
break;
}
#if CONFIG_WIFI_AUTO_CONNECT
wifi_mode_t mode;
bool auto_connect;
err = esp_wifi_get_mode(&mode);
if (err != ESP_OK){
WIFI_DEBUG("esp_wifi_get_mode fail, ret=%d\n", err);
}
esp_wifi_get_mode(&mode);
if (mode == WIFI_MODE_STA || mode == WIFI_MODE_APSTA) {
err = esp_wifi_get_auto_connect(&auto_connect);
if ((mode == WIFI_MODE_STA || mode == WIFI_MODE_APSTA) && auto_connect) {
err = esp_wifi_connect();
if (err != ESP_OK) {
WIFI_DEBUG("esp_wifi_connect fail, ret=%d\n", err);
break;
}
}
@ -95,6 +87,7 @@ static void esp_wifi_task(void *pvParameters)
} while (0);
if (err != ESP_OK) {
WIFI_DEBUG("wifi startup fail, deinit\n");
esp_wifi_deinit();
}

View file

@ -46,4 +46,52 @@ config ESPTOOLPY_COMPRESSED
decompress it on the fly before flashing it. For most payloads, this should result in a
speed increase.
choice ESPTOOLPY_FLASHMODE
prompt "Flash SPI mode"
default ESPTOOLPY_FLASHMODE_DIO
help
Mode the flash chip is flashed in, as well as the default mode for the
binary to run in.
config ESPTOOLPY_FLASHMODE_QIO
bool "QIO"
config ESPTOOLPY_FLASHMODE_QOUT
bool "QOUT"
config ESPTOOLPY_FLASHMODE_DIO
bool "DIO"
config ESPTOOLPY_FLASHMODE_DOUT
bool "DOUT"
endchoice
config ESPTOOLPY_FLASHMODE
string
default "qio" if ESPTOOLPY_FLASHMODE_QIO
default "qout" if ESPTOOLPY_FLASHMODE_QOUT
default "dio" if ESPTOOLPY_FLASHMODE_DIO
default "dout" if ESPTOOLPY_FLASHMODE_DOUT
choice ESPTOOLPY_FLASHFREQ
prompt "Flash SPI speed"
default ESPTOOLPY_FLASHFREQ_40M
help
The SPI flash frequency to be used.
config ESPTOOLPY_FLASHFREQ_80M
bool "80 MHz"
config ESPTOOLPY_FLASHFREQ_40M
bool "40 MHz"
config ESPTOOLPY_FLASHFREQ_26M
bool "26 MHz"
config ESPTOOLPY_FLASHFREQ_20M
bool "20 MHz"
endchoice
config ESPTOOLPY_FLASHFREQ
string
default "80m" if ESPTOOLPY_FLASHFREQ_80M
default "40m" if ESPTOOLPY_FLASHFREQ_40M
default "26m" if ESPTOOLPY_FLASHFREQ_26M
default "20m" if ESPTOOLPY_FLASHFREQ_20M
endmenu

View file

@ -2,6 +2,8 @@
# components have their own flash targets that can use these variables.
ESPPORT ?= $(CONFIG_ESPTOOLPY_PORT)
ESPBAUD ?= $(CONFIG_ESPTOOLPY_BAUD)
ESPFLASHMODE ?= $(CONFIG_ESPTOOLPY_FLASHMODE)
ESPFLASHFREQ ?= $(CONFIG_ESPTOOLPY_FLASHFREQ)
PYTHON ?= $(call dequote,$(CONFIG_PYTHON))
@ -12,12 +14,12 @@ PYTHON ?= $(call dequote,$(CONFIG_PYTHON))
ESPTOOLPY := $(PYTHON) $(IDF_PATH)/bin/esptool.py --chip esp32
ESPTOOLPY_SERIAL := $(ESPTOOLPY) --port $(ESPPORT) --baud $(ESPBAUD)
ESPTOOLPY_WRITE_FLASH=$(ESPTOOLPY_SERIAL) write_flash $(if $(CONFIG_ESPTOOLPY_COMPRESSED),-z)
ESPTOOLPY_WRITE_FLASH=$(ESPTOOLPY_SERIAL) write_flash $(if $(CONFIG_ESPTOOLPY_COMPRESSED),-z) --flash_mode $(ESPFLASHMODE) --flash_freq $(ESPFLASHFREQ)
ESPTOOL_ALL_FLASH_ARGS += $(CONFIG_APP_OFFSET) $(APP_BIN)
$(APP_BIN): $(APP_ELF)
$(Q) $(ESPTOOLPY) elf2image -o $@ $<
$(Q) $(ESPTOOLPY) elf2image --flash_mode $(ESPFLASHMODE) --flash_freq $(ESPFLASHFREQ) -o $@ $<
flash: all_binaries
@echo "Flashing project app to $(CONFIG_APP_OFFSET)..."

21
components/expat/COPYING Normal file
View file

@ -0,0 +1,21 @@
Copyright (c) 1998-2000 Thai Open Source Software Center Ltd and Clark Cooper
Copyright (c) 2001-2016 Expat maintainers
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.

15
components/expat/Makefile Normal file
View file

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

139
components/expat/README Normal file
View file

@ -0,0 +1,139 @@
Expat, Release 2.2.0
This is Expat, a C library for parsing XML, written by James Clark.
Expat is a stream-oriented XML parser. This means that you register
handlers with the parser before starting the parse. These handlers
are called when the parser discovers the associated structures in the
document being parsed. A start tag is an example of the kind of
structures for which you may register handlers.
Windows users should use the expat_win32bin package, which includes
both precompiled libraries and executables, and source code for
developers.
Expat is free software. You may copy, distribute, and modify it under
the terms of the License contained in the file COPYING distributed
with this package. This license is the same as the MIT/X Consortium
license.
Versions of Expat that have an odd minor version (the middle number in
the release above), are development releases and should be considered
as beta software. Releases with even minor version numbers are
intended to be production grade software.
If you are building Expat from a check-out from the CVS repository,
you need to run a script that generates the configure script using the
GNU autoconf and libtool tools. To do this, you need to have
autoconf 2.58 or newer. Run the script like this:
./buildconf.sh
Once this has been done, follow the same instructions as for building
from a source distribution.
To build Expat from a source distribution, you first run the
configuration shell script in the top level distribution directory:
./configure
There are many options which you may provide to configure (which you
can discover by running configure with the --help option). But the
one of most interest is the one that sets the installation directory.
By default, the configure script will set things up to install
libexpat into /usr/local/lib, expat.h into /usr/local/include, and
xmlwf into /usr/local/bin. If, for example, you'd prefer to install
into /home/me/mystuff/lib, /home/me/mystuff/include, and
/home/me/mystuff/bin, you can tell configure about that with:
./configure --prefix=/home/me/mystuff
Another interesting option is to enable 64-bit integer support for
line and column numbers and the over-all byte index:
./configure CPPFLAGS=-DXML_LARGE_SIZE
However, such a modification would be a breaking change to the ABI
and is therefore not recommended for general use - e.g. as part of
a Linux distribution - but rather for builds with special requirements.
After running the configure script, the "make" command will build
things and "make install" will install things into their proper
location. Have a look at the "Makefile" to learn about additional
"make" options. Note that you need to have write permission into
the directories into which things will be installed.
If you are interested in building Expat to provide document
information in UTF-16 encoding rather than the default UTF-8, follow
these instructions (after having run "make distclean"):
1. For UTF-16 output as unsigned short (and version/error
strings as char), run:
./configure CPPFLAGS=-DXML_UNICODE
For UTF-16 output as wchar_t (incl. version/error strings),
run:
./configure CFLAGS="-g -O2 -fshort-wchar" \
CPPFLAGS=-DXML_UNICODE_WCHAR_T
2. Edit the MakeFile, changing:
LIBRARY = libexpat.la
to:
LIBRARY = libexpatw.la
(Note the additional "w" in the library name.)
3. Run "make buildlib" (which builds the library only).
Or, to save step 2, run "make buildlib LIBRARY=libexpatw.la".
4. Run "make installlib" (which installs the library only).
Or, if step 2 was omitted, run "make installlib LIBRARY=libexpatw.la".
Using DESTDIR or INSTALL_ROOT is enabled, with INSTALL_ROOT being the default
value for DESTDIR, and the rest of the make file using only DESTDIR.
It works as follows:
$ make install DESTDIR=/path/to/image
overrides the in-makefile set DESTDIR, while both
$ INSTALL_ROOT=/path/to/image make install
$ make install INSTALL_ROOT=/path/to/image
use DESTDIR=$(INSTALL_ROOT), even if DESTDIR eventually is defined in the
environment, because variable-setting priority is
1) commandline
2) in-makefile
3) environment
Note: This only applies to the Expat library itself, building UTF-16 versions
of xmlwf and the tests is currently not supported.
Note for Solaris users: The "ar" command is usually located in
"/usr/ccs/bin", which is not in the default PATH. You will need to
add this to your path for the "make" command, and probably also switch
to GNU make (the "make" found in /usr/ccs/bin does not seem to work
properly -- apparently it does not understand .PHONY directives). If
you're using ksh or bash, use this command to build:
PATH=/usr/ccs/bin:$PATH make
When using Expat with a project using autoconf for configuration, you
can use the probing macro in conftools/expat.m4 to determine how to
include Expat. See the comments at the top of that file for more
information.
A reference manual is available in the file doc/reference.html in this
distribution.
The homepage for this project is http://www.libexpat.org/. There
are links there to connect you to the bug reports page. If you need
to report a bug when you don't have access to a browser, you may also
send a bug report by email to expat-bugs@mail.libexpat.org.
Discussion related to the direction of future expat development takes
place on expat-discuss@mail.libexpat.org. Archives of this list and
other Expat-related lists may be found at:
http://mail.libexpat.org/mailman/listinfo/

View file

@ -0,0 +1,92 @@
/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
See the file COPYING for copying permission.
*/
#define ASCII_A 0x41
#define ASCII_B 0x42
#define ASCII_C 0x43
#define ASCII_D 0x44
#define ASCII_E 0x45
#define ASCII_F 0x46
#define ASCII_G 0x47
#define ASCII_H 0x48
#define ASCII_I 0x49
#define ASCII_J 0x4A
#define ASCII_K 0x4B
#define ASCII_L 0x4C
#define ASCII_M 0x4D
#define ASCII_N 0x4E
#define ASCII_O 0x4F
#define ASCII_P 0x50
#define ASCII_Q 0x51
#define ASCII_R 0x52
#define ASCII_S 0x53
#define ASCII_T 0x54
#define ASCII_U 0x55
#define ASCII_V 0x56
#define ASCII_W 0x57
#define ASCII_X 0x58
#define ASCII_Y 0x59
#define ASCII_Z 0x5A
#define ASCII_a 0x61
#define ASCII_b 0x62
#define ASCII_c 0x63
#define ASCII_d 0x64
#define ASCII_e 0x65
#define ASCII_f 0x66
#define ASCII_g 0x67
#define ASCII_h 0x68
#define ASCII_i 0x69
#define ASCII_j 0x6A
#define ASCII_k 0x6B
#define ASCII_l 0x6C
#define ASCII_m 0x6D
#define ASCII_n 0x6E
#define ASCII_o 0x6F
#define ASCII_p 0x70
#define ASCII_q 0x71
#define ASCII_r 0x72
#define ASCII_s 0x73
#define ASCII_t 0x74
#define ASCII_u 0x75
#define ASCII_v 0x76
#define ASCII_w 0x77
#define ASCII_x 0x78
#define ASCII_y 0x79
#define ASCII_z 0x7A
#define ASCII_0 0x30
#define ASCII_1 0x31
#define ASCII_2 0x32
#define ASCII_3 0x33
#define ASCII_4 0x34
#define ASCII_5 0x35
#define ASCII_6 0x36
#define ASCII_7 0x37
#define ASCII_8 0x38
#define ASCII_9 0x39
#define ASCII_TAB 0x09
#define ASCII_SPACE 0x20
#define ASCII_EXCL 0x21
#define ASCII_QUOT 0x22
#define ASCII_AMP 0x26
#define ASCII_APOS 0x27
#define ASCII_MINUS 0x2D
#define ASCII_PERIOD 0x2E
#define ASCII_COLON 0x3A
#define ASCII_SEMI 0x3B
#define ASCII_LT 0x3C
#define ASCII_EQUALS 0x3D
#define ASCII_GT 0x3E
#define ASCII_LSQB 0x5B
#define ASCII_RSQB 0x5D
#define ASCII_UNDERSCORE 0x5F
#define ASCII_LPAREN 0x28
#define ASCII_RPAREN 0x29
#define ASCII_FF 0x0C
#define ASCII_SLASH 0x2F
#define ASCII_HASH 0x23
#define ASCII_PIPE 0x7C
#define ASCII_COMMA 0x2C

View file

@ -0,0 +1,36 @@
/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
See the file COPYING for copying permission.
*/
/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML,
/* 0x0C */ BT_NONXML, BT_CR, BT_NONXML, BT_NONXML,
/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM,
/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS,
/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS,
/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL,
/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI,
/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST,
/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB,
/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT,
/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER,

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,103 @@
/* expat_config.h. Generated from expat_config.h.in by configure. */
/* expat_config.h.in. Generated from configure.ac by autoheader. */
/* 1234 = LIL_ENDIAN, 4321 = BIGENDIAN */
#define BYTEORDER 1234
/* Define to 1 if you have the `bcopy' function. */
#define HAVE_BCOPY 1
/* Define to 1 if you have the <dlfcn.h> header file. */
#define HAVE_DLFCN_H 1
/* Define to 1 if you have the <fcntl.h> header file. */
#define HAVE_FCNTL_H 1
/* Define to 1 if you have the `getpagesize' function. */
#define HAVE_GETPAGESIZE 1
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* Define to 1 if you have the `memmove' function. */
#define HAVE_MEMMOVE 1
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* Define to 1 if you have a working `mmap' system call. */
#define HAVE_MMAP 1
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define to 1 if you have the <sys/param.h> header file. */
#define HAVE_SYS_PARAM_H 1
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* Define to the sub-directory in which libtool stores uninstalled libraries.
*/
#define LT_OBJDIR ".libs/"
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "expat-bugs@libexpat.org"
/* Define to the full name of this package. */
#define PACKAGE_NAME "expat"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "expat 2.2.0"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "expat"
/* Define to the home page for this package. */
#define PACKAGE_URL ""
/* Define to the version of this package. */
#define PACKAGE_VERSION "2.2.0"
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* whether byteorder is bigendian */
/* #undef WORDS_BIGENDIAN */
/* Define to specify how much context to retain around the current parse
point. */
#define XML_CONTEXT_BYTES 1024
/* Define to make parameter entity parsing functionality available. */
#define XML_DTD 1
/* Define to make XML Namespaces functionality available. */
#define XML_NS 1
/* Define to __FUNCTION__ or "" if `__func__' does not conform to ANSI C. */
/* #undef __func__ */
/* Define to empty if `const' does not conform to ANSI C. */
/* #undef const */
/* Define to `long int' if <sys/types.h> does not define. */
/* #undef off_t */
/* Define to `unsigned int' if <sys/types.h> does not define. */
/* #undef size_t */

View file

@ -0,0 +1,129 @@
/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
See the file COPYING for copying permission.
*/
#ifndef Expat_External_INCLUDED
#define Expat_External_INCLUDED 1
/* External API definitions */
#if defined(_MSC_EXTENSIONS) && !defined(__BEOS__) && !defined(__CYGWIN__)
#define XML_USE_MSC_EXTENSIONS 1
#endif
/* Expat tries very hard to make the API boundary very specifically
defined. There are two macros defined to control this boundary;
each of these can be defined before including this header to
achieve some different behavior, but doing so it not recommended or
tested frequently.
XMLCALL - The calling convention to use for all calls across the
"library boundary." This will default to cdecl, and
try really hard to tell the compiler that's what we
want.
XMLIMPORT - Whatever magic is needed to note that a function is
to be imported from a dynamically loaded library
(.dll, .so, or .sl, depending on your platform).
The XMLCALL macro was added in Expat 1.95.7. The only one which is
expected to be directly useful in client code is XMLCALL.
Note that on at least some Unix versions, the Expat library must be
compiled with the cdecl calling convention as the default since
system headers may assume the cdecl convention.
*/
#ifndef XMLCALL
#if defined(_MSC_VER)
#define XMLCALL __cdecl
#elif defined(__GNUC__) && defined(__i386) && !defined(__INTEL_COMPILER)
#define XMLCALL __attribute__((cdecl))
#else
/* For any platform which uses this definition and supports more than
one calling convention, we need to extend this definition to
declare the convention used on that platform, if it's possible to
do so.
If this is the case for your platform, please file a bug report
with information on how to identify your platform via the C
pre-processor and how to specify the same calling convention as the
platform's malloc() implementation.
*/
#define XMLCALL
#endif
#endif /* not defined XMLCALL */
#if !defined(XML_STATIC) && !defined(XMLIMPORT)
#ifndef XML_BUILDING_EXPAT
/* using Expat from an application */
#ifdef XML_USE_MSC_EXTENSIONS
#define XMLIMPORT __declspec(dllimport)
#endif
#endif
#endif /* not defined XML_STATIC */
#if !defined(XMLIMPORT) && defined(__GNUC__) && (__GNUC__ >= 4)
#define XMLIMPORT __attribute__ ((visibility ("default")))
#endif
/* If we didn't define it above, define it away: */
#ifndef XMLIMPORT
#define XMLIMPORT
#endif
#if defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96))
#define XML_ATTR_MALLOC __attribute__((__malloc__))
#else
#define XML_ATTR_MALLOC
#endif
#if defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
#define XML_ATTR_ALLOC_SIZE(x) __attribute__((__alloc_size__(x)))
#else
#define XML_ATTR_ALLOC_SIZE(x)
#endif
#define XMLPARSEAPI(type) XMLIMPORT type XMLCALL
#ifdef __cplusplus
extern "C" {
#endif
#ifdef XML_UNICODE_WCHAR_T
#define XML_UNICODE
#endif
#ifdef XML_UNICODE /* Information is UTF-16 encoded. */
#ifdef XML_UNICODE_WCHAR_T
typedef wchar_t XML_Char;
typedef wchar_t XML_LChar;
#else
typedef unsigned short XML_Char;
typedef char XML_LChar;
#endif /* XML_UNICODE_WCHAR_T */
#else /* Information is UTF-8 encoded. */
typedef char XML_Char;
typedef char XML_LChar;
#endif /* XML_UNICODE */
#ifdef XML_LARGE_SIZE /* Use large integers for file/stream positions. */
#if defined(XML_USE_MSC_EXTENSIONS) && _MSC_VER < 1400
typedef __int64 XML_Index;
typedef unsigned __int64 XML_Size;
#else
typedef long long XML_Index;
typedef unsigned long long XML_Size;
#endif
#else
typedef long XML_Index;
typedef unsigned long XML_Size;
#endif /* XML_LARGE_SIZE */
#ifdef __cplusplus
}
#endif
#endif /* not Expat_External_INCLUDED */

View file

@ -0,0 +1,37 @@
/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
See the file COPYING for copying permission.
*/
/* Like asciitab.h, except that 0xD has code BT_S rather than BT_CR */
/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML,
/* 0x0C */ BT_NONXML, BT_S, BT_NONXML, BT_NONXML,
/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM,
/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS,
/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS,
/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL,
/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI,
/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST,
/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB,
/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT,
/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER,

View file

@ -0,0 +1,95 @@
/* internal.h
Internal definitions used by Expat. This is not needed to compile
client code.
The following calling convention macros are defined for frequently
called functions:
FASTCALL - Used for those internal functions that have a simple
body and a low number of arguments and local variables.
PTRCALL - Used for functions called though function pointers.
PTRFASTCALL - Like PTRCALL, but for low number of arguments.
inline - Used for selected internal functions for which inlining
may improve performance on some platforms.
Note: Use of these macros is based on judgement, not hard rules,
and therefore subject to change.
*/
#if defined(__GNUC__) && defined(__i386__) && !defined(__MINGW32__)
/* We'll use this version by default only where we know it helps.
regparm() generates warnings on Solaris boxes. See SF bug #692878.
Instability reported with egcs on a RedHat Linux 7.3.
Let's comment out:
#define FASTCALL __attribute__((stdcall, regparm(3)))
and let's try this:
*/
#define FASTCALL __attribute__((regparm(3)))
#define PTRFASTCALL __attribute__((regparm(3)))
#endif
/* Using __fastcall seems to have an unexpected negative effect under
MS VC++, especially for function pointers, so we won't use it for
now on that platform. It may be reconsidered for a future release
if it can be made more effective.
Likely reason: __fastcall on Windows is like stdcall, therefore
the compiler cannot perform stack optimizations for call clusters.
*/
/* Make sure all of these are defined if they aren't already. */
#ifndef FASTCALL
#define FASTCALL
#endif
#ifndef PTRCALL
#define PTRCALL
#endif
#ifndef PTRFASTCALL
#define PTRFASTCALL
#endif
#ifndef XML_MIN_SIZE
#if !defined(__cplusplus) && !defined(inline)
#ifdef __GNUC__
#define inline __inline
#endif /* __GNUC__ */
#endif
#endif /* XML_MIN_SIZE */
#ifdef __cplusplus
#define inline inline
#else
#ifndef inline
#define inline
#endif
#endif
#ifndef UNUSED_P
# ifdef __GNUC__
# define UNUSED_P(p) UNUSED_ ## p __attribute__((__unused__))
# else
# define UNUSED_P(p) UNUSED_ ## p
# endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
void
align_limit_to_full_utf8_characters(const char * from, const char ** fromLimRef);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,36 @@
/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
See the file COPYING for copying permission.
*/
/* 0x80 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0x84 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0x88 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0x8C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0x90 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0x94 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0x98 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0x9C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0xA0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0xA4 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0xA8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER,
/* 0xAC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0xB0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0xB4 */ BT_OTHER, BT_NMSTRT, BT_OTHER, BT_NAME,
/* 0xB8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER,
/* 0xBC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0xC0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xC4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xC8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xCC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xD0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xD4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
/* 0xD8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xDC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xE0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xE4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xE8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xEC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xF0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xF4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
/* 0xF8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xFC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,

View file

@ -0,0 +1,150 @@
static const unsigned namingBitmap[] = {
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0x00000000, 0x04000000, 0x87FFFFFE, 0x07FFFFFE,
0x00000000, 0x00000000, 0xFF7FFFFF, 0xFF7FFFFF,
0xFFFFFFFF, 0x7FF3FFFF, 0xFFFFFDFE, 0x7FFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE00F, 0xFC31FFFF,
0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF,
0xFFFFFFFF, 0xF80001FF, 0x00000003, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0xFFFFD740, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD,
0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF,
0xFFFF0003, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF,
0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE,
0x0000007F, 0x00000000, 0xFFFF0000, 0x000707FF,
0x00000000, 0x07FFFFFE, 0x000007FE, 0xFFFE0000,
0xFFFFFFFF, 0x7CFFFFFF, 0x002F7FFF, 0x00000060,
0xFFFFFFE0, 0x23FFFFFF, 0xFF000000, 0x00000003,
0xFFF99FE0, 0x03C5FDFF, 0xB0000000, 0x00030003,
0xFFF987E0, 0x036DFDFF, 0x5E000000, 0x001C0000,
0xFFFBAFE0, 0x23EDFDFF, 0x00000000, 0x00000001,
0xFFF99FE0, 0x23CDFDFF, 0xB0000000, 0x00000003,
0xD63DC7E0, 0x03BFC718, 0x00000000, 0x00000000,
0xFFFDDFE0, 0x03EFFDFF, 0x00000000, 0x00000003,
0xFFFDDFE0, 0x03EFFDFF, 0x40000000, 0x00000003,
0xFFFDDFE0, 0x03FFFDFF, 0x00000000, 0x00000003,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0xFFFFFFFE, 0x000D7FFF, 0x0000003F, 0x00000000,
0xFEF02596, 0x200D6CAE, 0x0000001F, 0x00000000,
0x00000000, 0x00000000, 0xFFFFFEFF, 0x000003FF,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0xFFFFFFFF, 0xFFFF003F, 0x007FFFFF,
0x0007DAED, 0x50000000, 0x82315001, 0x002C62AB,
0x40000000, 0xF580C900, 0x00000007, 0x02010800,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0x0FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x03FFFFFF,
0x3F3FFFFF, 0xFFFFFFFF, 0xAAFF3F3F, 0x3FFFFFFF,
0xFFFFFFFF, 0x5FDFFFFF, 0x0FCF1FDC, 0x1FDC1FFF,
0x00000000, 0x00004C40, 0x00000000, 0x00000000,
0x00000007, 0x00000000, 0x00000000, 0x00000000,
0x00000080, 0x000003FE, 0xFFFFFFFE, 0xFFFFFFFF,
0x001FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x07FFFFFF,
0xFFFFFFE0, 0x00001FFF, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0x0000003F, 0x00000000, 0x00000000,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0x0000000F, 0x00000000, 0x00000000,
0x00000000, 0x07FF6000, 0x87FFFFFE, 0x07FFFFFE,
0x00000000, 0x00800000, 0xFF7FFFFF, 0xFF7FFFFF,
0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF,
0xFFFFFFFF, 0xF80001FF, 0x00030003, 0x00000000,
0xFFFFFFFF, 0xFFFFFFFF, 0x0000003F, 0x00000003,
0xFFFFD7C0, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD,
0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF,
0xFFFF007B, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF,
0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE,
0xFFFE007F, 0xBBFFFFFB, 0xFFFF0016, 0x000707FF,
0x00000000, 0x07FFFFFE, 0x0007FFFF, 0xFFFF03FF,
0xFFFFFFFF, 0x7CFFFFFF, 0xFFEF7FFF, 0x03FF3DFF,
0xFFFFFFEE, 0xF3FFFFFF, 0xFF1E3FFF, 0x0000FFCF,
0xFFF99FEE, 0xD3C5FDFF, 0xB080399F, 0x0003FFCF,
0xFFF987E4, 0xD36DFDFF, 0x5E003987, 0x001FFFC0,
0xFFFBAFEE, 0xF3EDFDFF, 0x00003BBF, 0x0000FFC1,
0xFFF99FEE, 0xF3CDFDFF, 0xB0C0398F, 0x0000FFC3,
0xD63DC7EC, 0xC3BFC718, 0x00803DC7, 0x0000FF80,
0xFFFDDFEE, 0xC3EFFDFF, 0x00603DDF, 0x0000FFC3,
0xFFFDDFEC, 0xC3EFFDFF, 0x40603DDF, 0x0000FFC3,
0xFFFDDFEC, 0xC3FFFDFF, 0x00803DCF, 0x0000FFC3,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0xFFFFFFFE, 0x07FF7FFF, 0x03FF7FFF, 0x00000000,
0xFEF02596, 0x3BFF6CAE, 0x03FF3F5F, 0x00000000,
0x03000000, 0xC2A003FF, 0xFFFFFEFF, 0xFFFE03FF,
0xFEBF0FDF, 0x02FE3FFF, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x1FFF0000, 0x00000002,
0x000000A0, 0x003EFFFE, 0xFFFFFFFE, 0xFFFFFFFF,
0x661FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x77FFFFFF,
};
static const unsigned char nmstrtPages[] = {
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00,
0x00, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13,
0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x15, 0x16, 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, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18,
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,
};
static const unsigned char namePages[] = {
0x19, 0x03, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x00,
0x00, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13,
0x26, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x27, 0x16, 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, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18,
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,
};

View file

@ -0,0 +1,37 @@
/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
See the file COPYING for copying permission.
*/
/* 0x80 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0x84 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0x88 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0x8C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0x90 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0x94 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0x98 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0x9C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0xA0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0xA4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0xA8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0xAC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0xB0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0xB4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0xB8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0xBC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0xC0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
/* 0xC4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
/* 0xC8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
/* 0xCC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
/* 0xD0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
/* 0xD4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
/* 0xD8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
/* 0xDC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
/* 0xE0 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
/* 0xE4 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
/* 0xE8 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
/* 0xEC */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
/* 0xF0 */ BT_LEAD4, BT_LEAD4, BT_LEAD4, BT_LEAD4,
/* 0xF4 */ BT_LEAD4, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0xF8 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0xFC */ BT_NONXML, BT_NONXML, BT_MALFORM, BT_MALFORM,

View file

@ -0,0 +1,114 @@
/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
See the file COPYING for copying permission.
*/
#ifndef XmlRole_INCLUDED
#define XmlRole_INCLUDED 1
#ifdef __VMS
/* 0 1 2 3 0 1 2 3
1234567890123456789012345678901 1234567890123456789012345678901 */
#define XmlPrologStateInitExternalEntity XmlPrologStateInitExternalEnt
#endif
#include "xmltok.h"
#ifdef __cplusplus
extern "C" {
#endif
enum {
XML_ROLE_ERROR = -1,
XML_ROLE_NONE = 0,
XML_ROLE_XML_DECL,
XML_ROLE_INSTANCE_START,
XML_ROLE_DOCTYPE_NONE,
XML_ROLE_DOCTYPE_NAME,
XML_ROLE_DOCTYPE_SYSTEM_ID,
XML_ROLE_DOCTYPE_PUBLIC_ID,
XML_ROLE_DOCTYPE_INTERNAL_SUBSET,
XML_ROLE_DOCTYPE_CLOSE,
XML_ROLE_GENERAL_ENTITY_NAME,
XML_ROLE_PARAM_ENTITY_NAME,
XML_ROLE_ENTITY_NONE,
XML_ROLE_ENTITY_VALUE,
XML_ROLE_ENTITY_SYSTEM_ID,
XML_ROLE_ENTITY_PUBLIC_ID,
XML_ROLE_ENTITY_COMPLETE,
XML_ROLE_ENTITY_NOTATION_NAME,
XML_ROLE_NOTATION_NONE,
XML_ROLE_NOTATION_NAME,
XML_ROLE_NOTATION_SYSTEM_ID,
XML_ROLE_NOTATION_NO_SYSTEM_ID,
XML_ROLE_NOTATION_PUBLIC_ID,
XML_ROLE_ATTRIBUTE_NAME,
XML_ROLE_ATTRIBUTE_TYPE_CDATA,
XML_ROLE_ATTRIBUTE_TYPE_ID,
XML_ROLE_ATTRIBUTE_TYPE_IDREF,
XML_ROLE_ATTRIBUTE_TYPE_IDREFS,
XML_ROLE_ATTRIBUTE_TYPE_ENTITY,
XML_ROLE_ATTRIBUTE_TYPE_ENTITIES,
XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN,
XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS,
XML_ROLE_ATTRIBUTE_ENUM_VALUE,
XML_ROLE_ATTRIBUTE_NOTATION_VALUE,
XML_ROLE_ATTLIST_NONE,
XML_ROLE_ATTLIST_ELEMENT_NAME,
XML_ROLE_IMPLIED_ATTRIBUTE_VALUE,
XML_ROLE_REQUIRED_ATTRIBUTE_VALUE,
XML_ROLE_DEFAULT_ATTRIBUTE_VALUE,
XML_ROLE_FIXED_ATTRIBUTE_VALUE,
XML_ROLE_ELEMENT_NONE,
XML_ROLE_ELEMENT_NAME,
XML_ROLE_CONTENT_ANY,
XML_ROLE_CONTENT_EMPTY,
XML_ROLE_CONTENT_PCDATA,
XML_ROLE_GROUP_OPEN,
XML_ROLE_GROUP_CLOSE,
XML_ROLE_GROUP_CLOSE_REP,
XML_ROLE_GROUP_CLOSE_OPT,
XML_ROLE_GROUP_CLOSE_PLUS,
XML_ROLE_GROUP_CHOICE,
XML_ROLE_GROUP_SEQUENCE,
XML_ROLE_CONTENT_ELEMENT,
XML_ROLE_CONTENT_ELEMENT_REP,
XML_ROLE_CONTENT_ELEMENT_OPT,
XML_ROLE_CONTENT_ELEMENT_PLUS,
XML_ROLE_PI,
XML_ROLE_COMMENT,
#ifdef XML_DTD
XML_ROLE_TEXT_DECL,
XML_ROLE_IGNORE_SECT,
XML_ROLE_INNER_PARAM_ENTITY_REF,
#endif /* XML_DTD */
XML_ROLE_PARAM_ENTITY_REF
};
typedef struct prolog_state {
int (PTRCALL *handler) (struct prolog_state *state,
int tok,
const char *ptr,
const char *end,
const ENCODING *enc);
unsigned level;
int role_none;
#ifdef XML_DTD
unsigned includeLevel;
int documentEntity;
int inEntityValue;
#endif /* XML_DTD */
} PROLOG_STATE;
void XmlPrologStateInit(PROLOG_STATE *);
#ifdef XML_DTD
void XmlPrologStateInitExternalEntity(PROLOG_STATE *);
#endif /* XML_DTD */
#define XmlTokenRole(state, tok, ptr, end, enc) \
(((state)->handler)(state, tok, ptr, end, enc))
#ifdef __cplusplus
}
#endif
#endif /* not XmlRole_INCLUDED */

View file

@ -0,0 +1,322 @@
/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
See the file COPYING for copying permission.
*/
#ifndef XmlTok_INCLUDED
#define XmlTok_INCLUDED 1
#ifdef __cplusplus
extern "C" {
#endif
/* The following token may be returned by XmlContentTok */
#define XML_TOK_TRAILING_RSQB -5 /* ] or ]] at the end of the scan; might be
start of illegal ]]> sequence */
/* The following tokens may be returned by both XmlPrologTok and
XmlContentTok.
*/
#define XML_TOK_NONE -4 /* The string to be scanned is empty */
#define XML_TOK_TRAILING_CR -3 /* A CR at the end of the scan;
might be part of CRLF sequence */
#define XML_TOK_PARTIAL_CHAR -2 /* only part of a multibyte sequence */
#define XML_TOK_PARTIAL -1 /* only part of a token */
#define XML_TOK_INVALID 0
/* The following tokens are returned by XmlContentTok; some are also
returned by XmlAttributeValueTok, XmlEntityTok, XmlCdataSectionTok.
*/
#define XML_TOK_START_TAG_WITH_ATTS 1
#define XML_TOK_START_TAG_NO_ATTS 2
#define XML_TOK_EMPTY_ELEMENT_WITH_ATTS 3 /* empty element tag <e/> */
#define XML_TOK_EMPTY_ELEMENT_NO_ATTS 4
#define XML_TOK_END_TAG 5
#define XML_TOK_DATA_CHARS 6
#define XML_TOK_DATA_NEWLINE 7
#define XML_TOK_CDATA_SECT_OPEN 8
#define XML_TOK_ENTITY_REF 9
#define XML_TOK_CHAR_REF 10 /* numeric character reference */
/* The following tokens may be returned by both XmlPrologTok and
XmlContentTok.
*/
#define XML_TOK_PI 11 /* processing instruction */
#define XML_TOK_XML_DECL 12 /* XML decl or text decl */
#define XML_TOK_COMMENT 13
#define XML_TOK_BOM 14 /* Byte order mark */
/* The following tokens are returned only by XmlPrologTok */
#define XML_TOK_PROLOG_S 15
#define XML_TOK_DECL_OPEN 16 /* <!foo */
#define XML_TOK_DECL_CLOSE 17 /* > */
#define XML_TOK_NAME 18
#define XML_TOK_NMTOKEN 19
#define XML_TOK_POUND_NAME 20 /* #name */
#define XML_TOK_OR 21 /* | */
#define XML_TOK_PERCENT 22
#define XML_TOK_OPEN_PAREN 23
#define XML_TOK_CLOSE_PAREN 24
#define XML_TOK_OPEN_BRACKET 25
#define XML_TOK_CLOSE_BRACKET 26
#define XML_TOK_LITERAL 27
#define XML_TOK_PARAM_ENTITY_REF 28
#define XML_TOK_INSTANCE_START 29
/* The following occur only in element type declarations */
#define XML_TOK_NAME_QUESTION 30 /* name? */
#define XML_TOK_NAME_ASTERISK 31 /* name* */
#define XML_TOK_NAME_PLUS 32 /* name+ */
#define XML_TOK_COND_SECT_OPEN 33 /* <![ */
#define XML_TOK_COND_SECT_CLOSE 34 /* ]]> */
#define XML_TOK_CLOSE_PAREN_QUESTION 35 /* )? */
#define XML_TOK_CLOSE_PAREN_ASTERISK 36 /* )* */
#define XML_TOK_CLOSE_PAREN_PLUS 37 /* )+ */
#define XML_TOK_COMMA 38
/* The following token is returned only by XmlAttributeValueTok */
#define XML_TOK_ATTRIBUTE_VALUE_S 39
/* The following token is returned only by XmlCdataSectionTok */
#define XML_TOK_CDATA_SECT_CLOSE 40
/* With namespace processing this is returned by XmlPrologTok for a
name with a colon.
*/
#define XML_TOK_PREFIXED_NAME 41
#ifdef XML_DTD
#define XML_TOK_IGNORE_SECT 42
#endif /* XML_DTD */
#ifdef XML_DTD
#define XML_N_STATES 4
#else /* not XML_DTD */
#define XML_N_STATES 3
#endif /* not XML_DTD */
#define XML_PROLOG_STATE 0
#define XML_CONTENT_STATE 1
#define XML_CDATA_SECTION_STATE 2
#ifdef XML_DTD
#define XML_IGNORE_SECTION_STATE 3
#endif /* XML_DTD */
#define XML_N_LITERAL_TYPES 2
#define XML_ATTRIBUTE_VALUE_LITERAL 0
#define XML_ENTITY_VALUE_LITERAL 1
/* The size of the buffer passed to XmlUtf8Encode must be at least this. */
#define XML_UTF8_ENCODE_MAX 4
/* The size of the buffer passed to XmlUtf16Encode must be at least this. */
#define XML_UTF16_ENCODE_MAX 2
typedef struct position {
/* first line and first column are 0 not 1 */
XML_Size lineNumber;
XML_Size columnNumber;
} POSITION;
typedef struct {
const char *name;
const char *valuePtr;
const char *valueEnd;
char normalized;
} ATTRIBUTE;
struct encoding;
typedef struct encoding ENCODING;
typedef int (PTRCALL *SCANNER)(const ENCODING *,
const char *,
const char *,
const char **);
enum XML_Convert_Result {
XML_CONVERT_COMPLETED = 0,
XML_CONVERT_INPUT_INCOMPLETE = 1,
XML_CONVERT_OUTPUT_EXHAUSTED = 2 /* and therefore potentially input remaining as well */
};
struct encoding {
SCANNER scanners[XML_N_STATES];
SCANNER literalScanners[XML_N_LITERAL_TYPES];
int (PTRCALL *sameName)(const ENCODING *,
const char *,
const char *);
int (PTRCALL *nameMatchesAscii)(const ENCODING *,
const char *,
const char *,
const char *);
int (PTRFASTCALL *nameLength)(const ENCODING *, const char *);
const char *(PTRFASTCALL *skipS)(const ENCODING *, const char *);
int (PTRCALL *getAtts)(const ENCODING *enc,
const char *ptr,
int attsMax,
ATTRIBUTE *atts);
int (PTRFASTCALL *charRefNumber)(const ENCODING *enc, const char *ptr);
int (PTRCALL *predefinedEntityName)(const ENCODING *,
const char *,
const char *);
void (PTRCALL *updatePosition)(const ENCODING *,
const char *ptr,
const char *end,
POSITION *);
int (PTRCALL *isPublicId)(const ENCODING *enc,
const char *ptr,
const char *end,
const char **badPtr);
enum XML_Convert_Result (PTRCALL *utf8Convert)(const ENCODING *enc,
const char **fromP,
const char *fromLim,
char **toP,
const char *toLim);
enum XML_Convert_Result (PTRCALL *utf16Convert)(const ENCODING *enc,
const char **fromP,
const char *fromLim,
unsigned short **toP,
const unsigned short *toLim);
int minBytesPerChar;
char isUtf8;
char isUtf16;
};
/* Scan the string starting at ptr until the end of the next complete
token, but do not scan past eptr. Return an integer giving the
type of token.
Return XML_TOK_NONE when ptr == eptr; nextTokPtr will not be set.
Return XML_TOK_PARTIAL when the string does not contain a complete
token; nextTokPtr will not be set.
Return XML_TOK_INVALID when the string does not start a valid
token; nextTokPtr will be set to point to the character which made
the token invalid.
Otherwise the string starts with a valid token; nextTokPtr will be
set to point to the character following the end of that token.
Each data character counts as a single token, but adjacent data
characters may be returned together. Similarly for characters in
the prolog outside literals, comments and processing instructions.
*/
#define XmlTok(enc, state, ptr, end, nextTokPtr) \
(((enc)->scanners[state])(enc, ptr, end, nextTokPtr))
#define XmlPrologTok(enc, ptr, end, nextTokPtr) \
XmlTok(enc, XML_PROLOG_STATE, ptr, end, nextTokPtr)
#define XmlContentTok(enc, ptr, end, nextTokPtr) \
XmlTok(enc, XML_CONTENT_STATE, ptr, end, nextTokPtr)
#define XmlCdataSectionTok(enc, ptr, end, nextTokPtr) \
XmlTok(enc, XML_CDATA_SECTION_STATE, ptr, end, nextTokPtr)
#ifdef XML_DTD
#define XmlIgnoreSectionTok(enc, ptr, end, nextTokPtr) \
XmlTok(enc, XML_IGNORE_SECTION_STATE, ptr, end, nextTokPtr)
#endif /* XML_DTD */
/* This is used for performing a 2nd-level tokenization on the content
of a literal that has already been returned by XmlTok.
*/
#define XmlLiteralTok(enc, literalType, ptr, end, nextTokPtr) \
(((enc)->literalScanners[literalType])(enc, ptr, end, nextTokPtr))
#define XmlAttributeValueTok(enc, ptr, end, nextTokPtr) \
XmlLiteralTok(enc, XML_ATTRIBUTE_VALUE_LITERAL, ptr, end, nextTokPtr)
#define XmlEntityValueTok(enc, ptr, end, nextTokPtr) \
XmlLiteralTok(enc, XML_ENTITY_VALUE_LITERAL, ptr, end, nextTokPtr)
#define XmlSameName(enc, ptr1, ptr2) (((enc)->sameName)(enc, ptr1, ptr2))
#define XmlNameMatchesAscii(enc, ptr1, end1, ptr2) \
(((enc)->nameMatchesAscii)(enc, ptr1, end1, ptr2))
#define XmlNameLength(enc, ptr) \
(((enc)->nameLength)(enc, ptr))
#define XmlSkipS(enc, ptr) \
(((enc)->skipS)(enc, ptr))
#define XmlGetAttributes(enc, ptr, attsMax, atts) \
(((enc)->getAtts)(enc, ptr, attsMax, atts))
#define XmlCharRefNumber(enc, ptr) \
(((enc)->charRefNumber)(enc, ptr))
#define XmlPredefinedEntityName(enc, ptr, end) \
(((enc)->predefinedEntityName)(enc, ptr, end))
#define XmlUpdatePosition(enc, ptr, end, pos) \
(((enc)->updatePosition)(enc, ptr, end, pos))
#define XmlIsPublicId(enc, ptr, end, badPtr) \
(((enc)->isPublicId)(enc, ptr, end, badPtr))
#define XmlUtf8Convert(enc, fromP, fromLim, toP, toLim) \
(((enc)->utf8Convert)(enc, fromP, fromLim, toP, toLim))
#define XmlUtf16Convert(enc, fromP, fromLim, toP, toLim) \
(((enc)->utf16Convert)(enc, fromP, fromLim, toP, toLim))
typedef struct {
ENCODING initEnc;
const ENCODING **encPtr;
} INIT_ENCODING;
int XmlParseXmlDecl(int isGeneralTextEntity,
const ENCODING *enc,
const char *ptr,
const char *end,
const char **badPtr,
const char **versionPtr,
const char **versionEndPtr,
const char **encodingNamePtr,
const ENCODING **namedEncodingPtr,
int *standalonePtr);
int XmlInitEncoding(INIT_ENCODING *, const ENCODING **, const char *name);
const ENCODING *XmlGetUtf8InternalEncoding(void);
const ENCODING *XmlGetUtf16InternalEncoding(void);
int FASTCALL XmlUtf8Encode(int charNumber, char *buf);
int FASTCALL XmlUtf16Encode(int charNumber, unsigned short *buf);
int XmlSizeOfUnknownEncoding(void);
typedef int (XMLCALL *CONVERTER) (void *userData, const char *p);
ENCODING *
XmlInitUnknownEncoding(void *mem,
int *table,
CONVERTER convert,
void *userData);
int XmlParseXmlDeclNS(int isGeneralTextEntity,
const ENCODING *enc,
const char *ptr,
const char *end,
const char **badPtr,
const char **versionPtr,
const char **versionEndPtr,
const char **encodingNamePtr,
const ENCODING **namedEncodingPtr,
int *standalonePtr);
int XmlInitEncodingNS(INIT_ENCODING *, const ENCODING **, const char *name);
const ENCODING *XmlGetUtf8InternalEncodingNS(void);
const ENCODING *XmlGetUtf16InternalEncodingNS(void);
ENCODING *
XmlInitUnknownEncodingNS(void *mem,
int *table,
CONVERTER convert,
void *userData);
#ifdef __cplusplus
}
#endif
#endif /* not XmlTok_INCLUDED */

View file

@ -0,0 +1,46 @@
/*
Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
See the file COPYING for copying permission.
*/
enum {
BT_NONXML,
BT_MALFORM,
BT_LT,
BT_AMP,
BT_RSQB,
BT_LEAD2,
BT_LEAD3,
BT_LEAD4,
BT_TRAIL,
BT_CR,
BT_LF,
BT_GT,
BT_QUOT,
BT_APOS,
BT_EQUALS,
BT_QUEST,
BT_EXCL,
BT_SOL,
BT_SEMI,
BT_NUM,
BT_LSQB,
BT_S,
BT_NMSTRT,
BT_COLON,
BT_HEX,
BT_DIGIT,
BT_NAME,
BT_MINUS,
BT_OTHER, /* known not to be a name or name start character */
BT_NONASCII, /* might be a name or name start character */
BT_PERCNT,
BT_LPAR,
BT_RPAR,
BT_AST,
BT_PLUS,
BT_COMMA,
BT_VERBAR
};
#include <stddef.h>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,115 @@
/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
See the file COPYING for copying permission.
*/
/* This file is included! */
#ifdef XML_TOK_NS_C
const ENCODING *
NS(XmlGetUtf8InternalEncoding)(void)
{
return &ns(internal_utf8_encoding).enc;
}
const ENCODING *
NS(XmlGetUtf16InternalEncoding)(void)
{
#if BYTEORDER == 1234
return &ns(internal_little2_encoding).enc;
#elif BYTEORDER == 4321
return &ns(internal_big2_encoding).enc;
#else
const short n = 1;
return (*(const char *)&n
? &ns(internal_little2_encoding).enc
: &ns(internal_big2_encoding).enc);
#endif
}
static const ENCODING * const NS(encodings)[] = {
&ns(latin1_encoding).enc,
&ns(ascii_encoding).enc,
&ns(utf8_encoding).enc,
&ns(big2_encoding).enc,
&ns(big2_encoding).enc,
&ns(little2_encoding).enc,
&ns(utf8_encoding).enc /* NO_ENC */
};
static int PTRCALL
NS(initScanProlog)(const ENCODING *enc, const char *ptr, const char *end,
const char **nextTokPtr)
{
return initScan(NS(encodings), (const INIT_ENCODING *)enc,
XML_PROLOG_STATE, ptr, end, nextTokPtr);
}
static int PTRCALL
NS(initScanContent)(const ENCODING *enc, const char *ptr, const char *end,
const char **nextTokPtr)
{
return initScan(NS(encodings), (const INIT_ENCODING *)enc,
XML_CONTENT_STATE, ptr, end, nextTokPtr);
}
int
NS(XmlInitEncoding)(INIT_ENCODING *p, const ENCODING **encPtr,
const char *name)
{
int i = getEncodingIndex(name);
if (i == UNKNOWN_ENC)
return 0;
SET_INIT_ENC_INDEX(p, i);
p->initEnc.scanners[XML_PROLOG_STATE] = NS(initScanProlog);
p->initEnc.scanners[XML_CONTENT_STATE] = NS(initScanContent);
p->initEnc.updatePosition = initUpdatePosition;
p->encPtr = encPtr;
*encPtr = &(p->initEnc);
return 1;
}
static const ENCODING *
NS(findEncoding)(const ENCODING *enc, const char *ptr, const char *end)
{
#define ENCODING_MAX 128
char buf[ENCODING_MAX];
char *p = buf;
int i;
XmlUtf8Convert(enc, &ptr, end, &p, p + ENCODING_MAX - 1);
if (ptr != end)
return 0;
*p = 0;
if (streqci(buf, KW_UTF_16) && enc->minBytesPerChar == 2)
return enc;
i = getEncodingIndex(buf);
if (i == UNKNOWN_ENC)
return 0;
return NS(encodings)[i];
}
int
NS(XmlParseXmlDecl)(int isGeneralTextEntity,
const ENCODING *enc,
const char *ptr,
const char *end,
const char **badPtr,
const char **versionPtr,
const char **versionEndPtr,
const char **encodingName,
const ENCODING **encoding,
int *standalone)
{
return doParseXmlDecl(NS(findEncoding),
isGeneralTextEntity,
enc,
ptr,
end,
badPtr,
versionPtr,
versionEndPtr,
encodingName,
encoding,
standalone);
}
#endif /* XML_TOK_NS_C */

View file

@ -0,0 +1,127 @@
/* Copyright (c) 1998-2003 Thai Open Source Software Center Ltd
See the file COPYING for copying permission.
chardata.c
*/
#ifdef HAVE_EXPAT_CONFIG_H
#include <expat_config.h>
#endif
#include "minicheck.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "chardata.h"
static int
xmlstrlen(const XML_Char *s)
{
int len = 0;
assert(s != NULL);
while (s[len] != 0)
++len;
return len;
}
void
CharData_Init(CharData *storage)
{
assert(storage != NULL);
storage->count = -1;
}
void
CharData_AppendString(CharData *storage, const char *s)
{
int maxchars = sizeof(storage->data) / sizeof(storage->data[0]);
int len;
assert(s != NULL);
len = strlen(s);
if (storage->count < 0)
storage->count = 0;
if ((len + storage->count) > maxchars) {
len = (maxchars - storage->count);
}
if (len + storage->count < (int)sizeof(storage->data)) {
memcpy(storage->data + storage->count, s, len);
storage->count += len;
}
}
void
CharData_AppendXMLChars(CharData *storage, const XML_Char *s, int len)
{
int maxchars;
assert(storage != NULL);
assert(s != NULL);
maxchars = sizeof(storage->data) / sizeof(storage->data[0]);
if (storage->count < 0)
storage->count = 0;
if (len < 0)
len = xmlstrlen(s);
if ((len + storage->count) > maxchars) {
len = (maxchars - storage->count);
}
if (len + storage->count < (int)sizeof(storage->data)) {
memcpy(storage->data + storage->count, s,
len * sizeof(storage->data[0]));
storage->count += len;
}
}
int
CharData_CheckString(CharData *storage, const char *expected)
{
char buffer[1280];
int len;
int count;
assert(storage != NULL);
assert(expected != NULL);
count = (storage->count < 0) ? 0 : storage->count;
len = strlen(expected);
if (len != count) {
if (sizeof(XML_Char) == 1)
sprintf(buffer, "wrong number of data characters:"
" got %d, expected %d:\n%s", count, len, storage->data);
else
sprintf(buffer,
"wrong number of data characters: got %d, expected %d",
count, len);
fail(buffer);
return 0;
}
if (memcmp(expected, storage->data, len) != 0) {
fail("got bad data bytes");
return 0;
}
return 1;
}
int
CharData_CheckXMLChars(CharData *storage, const XML_Char *expected)
{
char buffer[1024];
int len = xmlstrlen(expected);
int count;
assert(storage != NULL);
count = (storage->count < 0) ? 0 : storage->count;
if (len != count) {
sprintf(buffer, "wrong number of data characters: got %d, expected %d",
count, len);
fail(buffer);
return 0;
}
if (memcmp(expected, storage->data, len * sizeof(storage->data[0])) != 0) {
fail("got bad data bytes");
return 0;
}
return 1;
}

View file

@ -0,0 +1,66 @@
/* This is simple demonstration of how to use expat. This program
reads an XML document from standard input and writes a line with
the name of each element to standard output indenting child
elements by one tab stop more than their parent element.
It must be used with Expat compiled for UTF-8 output.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "expat.h"
#define XML_FMT_INT_MOD "l"
static void XMLCALL
startElement(void *userData, const char *name, const char **atts)
{
int i;
int *depthPtr = (int *)userData;
(void)atts;
for (i = 0; i < *depthPtr; i++)
putchar('\t');
puts(name);
*depthPtr += 1;
}
static void XMLCALL
endElement(void *userData, const char *name)
{
int *depthPtr = (int *)userData;
(void)name;
*depthPtr -= 1;
}
int xml_main(int argc, const char *argv)
{
char *buf = NULL;
int depth = 0;
if (argv == NULL){
printf("no element parse\n");
return 1;
}
XML_Parser parser = XML_ParserCreate(NULL);
if (parser){
XML_SetUserData(parser, &depth);
XML_SetElementHandler(parser, startElement, endElement);
buf = (char*)malloc(argc + 1);
bzero(buf, argc + 1);
memcpy(buf, argv, argc);
if (XML_Parse(parser, buf, argc, 0) == XML_STATUS_ERROR) {
printf( "%s at line %" XML_FMT_INT_MOD "u\n",
XML_ErrorString(XML_GetErrorCode(parser)),
XML_GetCurrentLineNumber(parser));
}else{
printf("XML_Parse Sucessful\n ");
}
}
XML_ParserFree(parser);
return 0;
}

View file

@ -0,0 +1,40 @@
/* chardata.h
Interface to some helper routines used to accumulate and check text
and attribute content.
*/
#ifdef __cplusplus
extern "C" {
#endif
#ifndef XML_CHARDATA_H
#define XML_CHARDATA_H 1
#ifndef XML_VERSION
#include "expat.h" /* need XML_Char */
#endif
typedef struct {
int count; /* # of chars, < 0 if not set */
XML_Char data[1024];
} CharData;
void CharData_Init(CharData *storage);
void CharData_AppendString(CharData *storage, const char *s);
void CharData_AppendXMLChars(CharData *storage, const XML_Char *s, int len);
int CharData_CheckString(CharData *storage, const char *s);
int CharData_CheckXMLChars(CharData *storage, const XML_Char *s);
#endif /* XML_CHARDATA_H */
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,95 @@
/* Miniature re-implementation of the "check" library.
*
* This is intended to support just enough of check to run the Expat
* tests. This interface is based entirely on the portion of the
* check library being used.
*
* This is *source* compatible, but not necessary *link* compatible.
*/
#ifdef __cplusplus
extern "C" {
#endif
#define CK_NOFORK 0
#define CK_FORK 1
#define CK_SILENT 0
#define CK_NORMAL 1
#define CK_VERBOSE 2
/* Workaround for Microsoft's compiler and Tru64 Unix systems where the
C compiler has a working __func__, but the C++ compiler only has a
working __FUNCTION__. This could be fixed in configure.in, but it's
not worth it right now. */
#if defined (_MSC_VER) || (defined(__osf__) && defined(__cplusplus))
#define __func__ __FUNCTION__
#endif
/* ISO C90 does not support '__func__' predefined identifier */
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ < 199901)
# define __func__ "(unknown)"
#endif
#define START_TEST(testname) static void testname(void) { \
_check_set_test_info(__func__, __FILE__, __LINE__); \
{
#define END_TEST
#define fail(msg) _fail_unless(0, __FILE__, __LINE__, msg)
typedef void (*tcase_setup_function)(void);
typedef void (*tcase_teardown_function)(void);
typedef void (*tcase_test_function)(void);
typedef struct SRunner SRunner;
typedef struct Suite Suite;
typedef struct TCase TCase;
struct SRunner {
Suite *suite;
int nchecks;
int nfailures;
};
struct Suite {
const char *name;
TCase *tests;
};
struct TCase {
const char *name;
tcase_setup_function setup;
tcase_teardown_function teardown;
tcase_test_function *tests;
int ntests;
int allocated;
TCase *next_tcase;
};
/* Internal helper. */
void _check_set_test_info(char const *function,
char const *filename, int lineno);
/*
* Prototypes for the actual implementation.
*/
void _fail_unless(int condition, const char *file, int line, const char *msg);
Suite *suite_create(const char *name);
TCase *tcase_create(const char *name);
void suite_add_tcase(Suite *suite, TCase *tc);
void tcase_add_checked_fixture(TCase *,
tcase_setup_function,
tcase_teardown_function);
void tcase_add_test(TCase *tc, tcase_test_function test);
SRunner *srunner_create(Suite *suite);
void srunner_run_all(SRunner *runner, int verbosity);
int srunner_ntests_failed(SRunner *runner);
void srunner_free(SRunner *runner);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,183 @@
/* Miniature re-implementation of the "check" library.
*
* This is intended to support just enough of check to run the Expat
* tests. This interface is based entirely on the portion of the
* check library being used.
*/
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <assert.h>
#include "internal.h" /* for UNUSED_P only */
#include "minicheck.h"
Suite *
suite_create(const char *name)
{
Suite *suite = (Suite *) calloc(1, sizeof(Suite));
if (suite != NULL) {
suite->name = name;
}
return suite;
}
TCase *
tcase_create(const char *name)
{
TCase *tc = (TCase *) calloc(1, sizeof(TCase));
if (tc != NULL) {
tc->name = name;
}
return tc;
}
void
suite_add_tcase(Suite *suite, TCase *tc)
{
assert(suite != NULL);
assert(tc != NULL);
assert(tc->next_tcase == NULL);
tc->next_tcase = suite->tests;
suite->tests = tc;
}
void
tcase_add_checked_fixture(TCase *tc,
tcase_setup_function setup,
tcase_teardown_function teardown)
{
assert(tc != NULL);
tc->setup = setup;
tc->teardown = teardown;
}
void
tcase_add_test(TCase *tc, tcase_test_function test)
{
assert(tc != NULL);
if (tc->allocated == tc->ntests) {
int nalloc = tc->allocated + 100;
size_t new_size = sizeof(tcase_test_function) * nalloc;
tcase_test_function *new_tests = realloc(tc->tests, new_size);
assert(new_tests != NULL);
if (new_tests != tc->tests) {
free(tc->tests);
tc->tests = new_tests;
}
tc->allocated = nalloc;
}
tc->tests[tc->ntests] = test;
tc->ntests++;
}
SRunner *
srunner_create(Suite *suite)
{
SRunner *runner = calloc(1, sizeof(SRunner));
if (runner != NULL) {
runner->suite = suite;
}
return runner;
}
static jmp_buf env;
static char const *_check_current_function = NULL;
static int _check_current_lineno = -1;
static char const *_check_current_filename = NULL;
void
_check_set_test_info(char const *function, char const *filename, int lineno)
{
_check_current_function = function;
_check_current_lineno = lineno;
_check_current_filename = filename;
}
static void
add_failure(SRunner *runner, int verbosity)
{
runner->nfailures++;
if (verbosity >= CK_VERBOSE) {
printf("%s:%d: %s\n", _check_current_filename,
_check_current_lineno, _check_current_function);
}
}
void
srunner_run_all(SRunner *runner, int verbosity)
{
Suite *suite;
TCase *tc;
assert(runner != NULL);
suite = runner->suite;
tc = suite->tests;
while (tc != NULL) {
int i;
for (i = 0; i < tc->ntests; ++i) {
runner->nchecks++;
if (tc->setup != NULL) {
/* setup */
if (setjmp(env)) {
add_failure(runner, verbosity);
continue;
}
tc->setup();
}
/* test */
if (setjmp(env)) {
add_failure(runner, verbosity);
continue;
}
(tc->tests[i])();
/* teardown */
if (tc->teardown != NULL) {
if (setjmp(env)) {
add_failure(runner, verbosity);
continue;
}
tc->teardown();
}
}
tc = tc->next_tcase;
}
if (verbosity) {
int passed = runner->nchecks - runner->nfailures;
double percentage = ((double) passed) / runner->nchecks;
int display = (int) (percentage * 100);
printf("%d%%: Checks: %d, Failed: %d\n",
display, runner->nchecks, runner->nfailures);
}
}
void
_fail_unless(int UNUSED_P(condition), const char *UNUSED_P(file), int UNUSED_P(line), const char *msg)
{
/* Always print the error message so it isn't lost. In this case,
we have a failure, so there's no reason to be quiet about what
it is.
*/
if (msg != NULL)
printf("%s", msg);
longjmp(env, 1);
}
int
srunner_ntests_failed(SRunner *runner)
{
assert(runner != NULL);
return runner->nfailures;
}
void
srunner_free(SRunner *runner)
{
free(runner->suite);
free(runner);
}

View file

@ -0,0 +1,23 @@
/*
* Since at least FreeRTOS V7.5.3 uxTopUsedPriority is no longer
* present in the kernel, so it has to be supplied by other means for
* OpenOCD's threads awareness.
*
* Add this file to your project, and, if you're using --gc-sections,
* ``--undefined=uxTopUsedPriority'' (or
* ``-Wl,--undefined=uxTopUsedPriority'' when using gcc for final
* linking) to your LDFLAGS; same with all the other symbols you need.
*/
#include "FreeRTOS.h"
#include "sdkconfig.h"
#ifdef __GNUC__
#define USED __attribute__((used))
#else
#define USED
#endif
#ifdef CONFIG_FREERTOS_DEBUG_OCDAWARE
const int USED uxTopUsedPriority = configMAX_PRIORITIES - 1;
#endif

View file

@ -75,13 +75,16 @@ endchoice
config FREERTOS_THREAD_LOCAL_STORAGE_POINTERS
int "Amount of thread local storage pointers"
range 0 256
default 0
range 0 256 if !WIFI_ENABLED
range 1 256 if WIFI_ENABLED
default 1
help
FreeRTOS has the ability to store per-thread pointers in the task
control block. This controls the amount of pointers available;
0 turns off this functionality.
If using the WiFi stack, this value must be at least 1.
#This still needs to be implemented.
choice FREERTOS_PANIC
prompt "Panic handler behaviour"
@ -121,5 +124,71 @@ config FREERTOS_DEBUG_OCDAWARE
The FreeRTOS panic and unhandled exception handers can detect a JTAG OCD debugger and
instead of panicking, have the debugger stop on the offending instruction.
choice FREERTOS_ASSERT
prompt "FreeRTOS assertions"
default FREERTOS_ASSERT_FAIL_ABORT
help
Failed FreeRTOS configASSERT() assertions can be configured to
behave in different ways.
config FREERTOS_ASSERT_FAIL_ABORT
bool "abort() on failed assertions"
help
If a FreeRTOS configASSERT() fails, FreeRTOS will abort() and
halt execution. The panic handler can be configured to handle
the outcome of an abort() in different ways.
config FREERTOS_ASSERT_FAIL_PRINT_CONTINUE
bool "Print and continue failed assertions"
help
If a FreeRTOS assertion fails, print it out and continue.
config FREERTOS_ASSERT_DISABLE
bool "Disable FreeRTOS assertions"
help
FreeRTOS configASSERT() will not be compiled into the binary.
endchoice
config FREERTOS_BREAK_ON_SCHEDULER_START_JTAG
bool "Stop program on scheduler start when JTAG/OCD is detected"
depends on FREERTOS_DEBUG_OCDAWARE
default y
help
If JTAG/OCD is connected, stop execution when the scheduler is started and the first
task is executed.
menuconfig ENABLE_MEMORY_DEBUG
bool "Enable heap memory debug"
default n
help
Enable this option to show malloc heap block and memory crash detect
menuconfig FREERTOS_DEBUG_INTERNALS
bool "Debug FreeRTOS internals"
default n
help
Enable this option to show the menu with internal FreeRTOS debugging features.
This option does not change any code by itself, it just shows/hides some options.
if FREERTOS_DEBUG_INTERNALS
config FREERTOS_PORTMUX_DEBUG
bool "Debug portMUX portENTER_CRITICAL/portEXIT_CRITICAL"
depends on FREERTOS_DEBUG_INTERNALS
default n
help
If enabled, debug information (including integrity checks) will be printed
to UART for the port-specific MUX implementation.
config FREERTOS_PORTMUX_DEBUG_RECURSIVE
bool "Debug portMUX Recursion"
depends on FREERTOS_PORTMUX_DEBUG
default n
help
If enabled, additional debug information will be printed for recursive
portMUX usage.
endif # FREERTOS_DEBUG_INTERNALS
endmenu

View file

@ -1,8 +0,0 @@
#
# Component Makefile
#
COMPONENT_ADD_INCLUDEDIRS := include
COMPONENT_PRIV_INCLUDEDIRS := include/freertos
include $(IDF_PATH)/make/component.mk

View file

@ -0,0 +1,9 @@
#
# Component Makefile
#
COMPONENT_ADD_LDFLAGS = -l$(COMPONENT_NAME) -Wl,--undefined=uxTopUsedPriority
COMPONENT_ADD_INCLUDEDIRS := include
COMPONENT_PRIV_INCLUDEDIRS := include/freertos
include $(IDF_PATH)/make/component_common.mk

View file

@ -132,6 +132,7 @@ task.h is included from an application file. */
#include "FreeRTOS.h"
#include "task.h"
#include "heap_regions_debug.h"
#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE
@ -171,7 +172,7 @@ static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert );
/* The size of the structure placed at the beginning of each allocated memory
block must by correctly byte aligned. */
static const uint32_t uxHeapStructSize = ( ( sizeof ( BlockLink_t ) + ( portBYTE_ALIGNMENT - 1 ) ) & ~portBYTE_ALIGNMENT_MASK );
static const uint32_t uxHeapStructSize = ( ( sizeof ( BlockLink_t ) + BLOCK_HEAD_LEN + BLOCK_TAIL_LEN + ( portBYTE_ALIGNMENT - 1 ) ) & ~portBYTE_ALIGNMENT_MASK );
/* Create a couple of list links to mark the start and end of the list. */
static BlockLink_t xStart, *pxEnd = NULL;
@ -238,6 +239,13 @@ void *pvReturn = NULL;
while( ( ( pxBlock->xTag != tag ) || ( pxBlock->xBlockSize < xWantedSize ) ) && ( pxBlock->pxNextFreeBlock != NULL ) )
{
// ets_printf("Block %x -> %x\n", (uint32_t)pxBlock, (uint32_t)pxBlock->pxNextFreeBlock);
#if (configENABLE_MEMORY_DEBUG == 1)
{
mem_check_block(pxBlock);
}
#endif
pxPreviousBlock = pxBlock;
pxBlock = pxBlock->pxNextFreeBlock;
}
@ -248,7 +256,7 @@ void *pvReturn = NULL;
{
/* Return the memory space pointed to - jumping over the
BlockLink_t structure at its start. */
pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + uxHeapStructSize );
pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + uxHeapStructSize - BLOCK_TAIL_LEN - BLOCK_HEAD_LEN);
/* This block is being returned for use so must be taken out
of the list of free blocks. */
@ -256,13 +264,14 @@ void *pvReturn = NULL;
/* If the block is larger than required it can be split into
two. */
if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE )
{
/* This block is to be split into two. Create a new
block following the number of bytes requested. The void
cast is used to prevent byte alignment warnings from the
compiler. */
pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );
pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize);
/* Calculate the sizes of two blocks split from the
single block. */
@ -270,6 +279,13 @@ void *pvReturn = NULL;
pxNewBlockLink->xTag = tag;
pxBlock->xBlockSize = xWantedSize;
#if (configENABLE_MEMORY_DEBUG == 1)
{
mem_init_dog(pxNewBlockLink);
}
#endif
/* Insert the new block into the list of free blocks. */
prvInsertBlockIntoFreeList( ( pxNewBlockLink ) );
}
@ -293,6 +309,13 @@ void *pvReturn = NULL;
by the application and has no "next" block. */
pxBlock->xBlockSize |= xBlockAllocatedBit;
pxBlock->pxNextFreeBlock = NULL;
#if (configENABLE_MEMORY_DEBUG == 1)
{
mem_init_dog(pxBlock);
mem_malloc_block(pxBlock);
}
#endif
}
else
{
@ -340,11 +363,20 @@ BlockLink_t *pxLink;
{
/* The memory being freed will have an BlockLink_t structure immediately
before it. */
puc -= uxHeapStructSize;
puc -= (uxHeapStructSize - BLOCK_TAIL_LEN - BLOCK_HEAD_LEN) ;
/* This casting is to keep the compiler from issuing warnings. */
pxLink = ( void * ) puc;
#if (configENABLE_MEMORY_DEBUG == 1)
{
taskENTER_CRITICAL(&xMallocMutex);
mem_check_block(pxLink);
mem_free_block(pxLink);
taskEXIT_CRITICAL(&xMallocMutex);
}
#endif
/* Check the block is actually allocated. */
configASSERT( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 );
configASSERT( pxLink->pxNextFreeBlock == NULL );
@ -497,7 +529,7 @@ const HeapRegionTagged_t *pxHeapRegion;
{
/* xStart is used to hold a pointer to the first item in the list of
free blocks. The void cast is used to prevent compiler warnings. */
xStart.pxNextFreeBlock = ( BlockLink_t * ) pucAlignedHeap;
xStart.pxNextFreeBlock = ( BlockLink_t * ) (pucAlignedHeap + BLOCK_HEAD_LEN);
xStart.xBlockSize = ( size_t ) 0;
}
else
@ -519,7 +551,7 @@ const HeapRegionTagged_t *pxHeapRegion;
ulAddress = ( ( uint32_t ) pucAlignedHeap ) + xTotalRegionSize;
ulAddress -= uxHeapStructSize;
ulAddress &= ~portBYTE_ALIGNMENT_MASK;
pxEnd = ( BlockLink_t * ) ulAddress;
pxEnd = ( BlockLink_t * ) (ulAddress + BLOCK_HEAD_LEN);
pxEnd->xBlockSize = 0;
pxEnd->pxNextFreeBlock = NULL;
pxEnd->xTag = -1;
@ -527,8 +559,8 @@ const HeapRegionTagged_t *pxHeapRegion;
/* To start with there is a single free block in this region that is
sized to take up the entire heap region minus the space taken by the
free block structure. */
pxFirstFreeBlockInRegion = ( BlockLink_t * ) pucAlignedHeap;
pxFirstFreeBlockInRegion->xBlockSize = ulAddress - ( uint32_t ) pxFirstFreeBlockInRegion;
pxFirstFreeBlockInRegion = ( BlockLink_t * ) (pucAlignedHeap + BLOCK_HEAD_LEN);
pxFirstFreeBlockInRegion->xBlockSize = ulAddress - ( uint32_t ) pxFirstFreeBlockInRegion + BLOCK_HEAD_LEN;
pxFirstFreeBlockInRegion->pxNextFreeBlock = pxEnd;
pxFirstFreeBlockInRegion->xTag=pxHeapRegion->xTag;
@ -545,6 +577,13 @@ const HeapRegionTagged_t *pxHeapRegion;
xDefinedRegions++;
xRegIdx++;
pxHeapRegion = &( pxHeapRegions[ xRegIdx ] );
#if (configENABLE_MEMORY_DEBUG == 1)
{
mem_init_dog(pxFirstFreeBlockInRegion);
mem_init_dog(pxEnd);
}
#endif
}
xMinimumEverFreeBytesRemaining = xTotalHeapSize;
@ -555,5 +594,12 @@ const HeapRegionTagged_t *pxHeapRegion;
/* Work out the position of the top bit in a size_t variable. */
xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 );
#if (configENABLE_MEMORY_DEBUG == 1)
{
mem_debug_init(uxHeapStructSize, &xStart, pxEnd, &xMallocMutex, xBlockAllocatedBit);
mem_check_all(0);
}
#endif
}

View file

@ -0,0 +1,193 @@
#include "heap_regions_debug.h"
#include "FreeRTOS.h"
#include "task.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#if (configENABLE_MEMORY_DEBUG == 1)
static os_block_t g_malloc_list, *g_free_list=NULL, *g_end;
static size_t g_heap_struct_size;
static mem_dbg_ctl_t g_mem_dbg;
char g_mem_print = 0;
static portMUX_TYPE *g_malloc_mutex = NULL;
static unsigned int g_alloc_bit;
#define MEM_DEBUG(...)
void mem_debug_init(size_t size, void *start, void *end, portMUX_TYPE *mutex, unsigned int alloc_bit)
{
MEM_DEBUG("size=%d start=%p end=%p mutex=%p alloc_bit=0x%x\n", size, start, end, mutex, alloc_bit);
memset(&g_mem_dbg, 0, sizeof(g_mem_dbg));
memset(&g_malloc_list, 0, sizeof(g_malloc_list));
g_malloc_mutex = mutex;
g_heap_struct_size = size;
g_free_list = start;
g_end = end;
g_alloc_bit = alloc_bit;
}
void mem_debug_push(char type, void *addr)
{
os_block_t *b = (os_block_t*)addr;
debug_block_t *debug_b = DEBUG_BLOCK(b);
MEM_DEBUG("push type=%d addr=%p\n", type, addr);
if (g_mem_print){
if (type == DEBUG_TYPE_MALLOC){
ets_printf("task=%s t=%s s=%u a=%p\n", debug_b->head.task?debug_b->head.task:"", type==DEBUG_TYPE_MALLOC?"m":"f", b->size&(~g_alloc_bit), addr);
} else {
ets_printf("task=%s t=%s s=%u a=%p\n", debug_b->head.task?debug_b->head.task:"", type==DEBUG_TYPE_MALLOC?"m":"f", b->size&(~g_alloc_bit), addr);
}
} else {
mem_dbg_info_t *info = &g_mem_dbg.info[g_mem_dbg.cnt%DEBUG_MAX_INFO_NUM];
info->addr = addr;
info->type = type;
info->time = g_mem_dbg.cnt;
g_mem_dbg.cnt++;
}
}
void mem_debug_malloc_show(void)
{
os_block_t *b = g_malloc_list.next;
debug_block_t *d;
taskENTER_CRITICAL(g_malloc_mutex);
while (b){
d = DEBUG_BLOCK(b);
d->head.task[3] = '\0';
ets_printf("t=%s s=%u a=%p\n", d->head.task?d->head.task:"", b->size&(~g_alloc_bit), b);
b = b->next;
}
taskEXIT_CRITICAL(g_malloc_mutex);
}
void mem_debug_show(void)
{
uint32_t i;
if (!g_mem_print) return;
for (i=0; i<DEBUG_MAX_INFO_NUM; i++){
ets_printf("%u %s %p\n", g_mem_dbg.info[i].time, g_mem_dbg.info[i].type == DEBUG_TYPE_FREE?"f":"m", g_mem_dbg.info[i].addr);
}
}
void mem_check_block(void* data)
{
debug_block_t *b = DEBUG_BLOCK(data);
MEM_DEBUG("check block data=%p\n", data);
if (data && (HEAD_DOG(b) == DEBUG_DOG_VALUE)){
if (TAIL_DOG(b) != DEBUG_DOG_VALUE){
ets_printf("f task=%s a=%p h=%08x t=%08x\n", b->head.task?b->head.task:"", b, HEAD_DOG(b), TAIL_DOG(b));
DOG_ASSERT();
}
} else {
ets_printf("f task=%s a=%p h=%08x\n", b->head.task?b->head.task:"", b, HEAD_DOG(b));\
DOG_ASSERT();
}
}
void mem_init_dog(void *data)
{
debug_block_t *b = DEBUG_BLOCK(data);
xTaskHandle task;
MEM_DEBUG("init dog, data=%p debug_block=%p block_size=%x\n", data, b, b->os_block.size);
if (!data) return;
#if (INCLUDE_pcTaskGetTaskName == 1)
task = xTaskGetCurrentTaskHandle();
if (task){
strncpy(b->head.task, pcTaskGetTaskName(task), 3);
b->head.task[3] = '\0';
}
#else
b->head.task = '\0';
#endif
HEAD_DOG(b) = DEBUG_DOG_VALUE;
TAIL_DOG(b) = DEBUG_DOG_VALUE;
}
void mem_check_all(void* pv)
{
os_block_t *b;
if (pv){
char *puc = (char*)(pv);
os_block_t *b;
puc -= (g_heap_struct_size - BLOCK_TAIL_LEN - BLOCK_HEAD_LEN);
b = (os_block_t*)puc;
mem_check_block(b);
}
taskENTER_CRITICAL(g_malloc_mutex);
b = g_free_list->next;
while(b && b != g_end){
mem_check_block(b);
ets_printf("check b=%p size=%d ok\n", b, b->size);
b = b->next;
}
taskEXIT_CRITICAL(g_malloc_mutex);
}
void mem_malloc_show(void)
{
os_block_t *b = g_malloc_list.next;
debug_block_t *debug_b;
while (b){
debug_b = DEBUG_BLOCK(b);
ets_printf("%s %p %p %u\n", debug_b->head.task, debug_b, b, b->size&(~g_alloc_bit));
b = b->next;
}
}
void mem_malloc_block(void *data)
{
os_block_t *b = (os_block_t*)data;
MEM_DEBUG("mem malloc block data=%p, size=%u\n", data, b->size&(~g_alloc_bit));
mem_debug_push(DEBUG_TYPE_MALLOC, data);
if (b){
b->next = g_malloc_list.next;
g_malloc_list.next = b;
}
}
void mem_free_block(void *data)
{
os_block_t *del = (os_block_t*)data;
os_block_t *b = g_malloc_list.next;
os_block_t *pre = &g_malloc_list;
debug_block_t *debug_b;
MEM_DEBUG("mem free block data=%p, size=%d\n", data, del->size&(~g_alloc_bit));
mem_debug_push(DEBUG_TYPE_FREE, data);
if (!del) {
return;
}
while (b){
if ( (del == b) ){
pre->next = b->next;
b->next = NULL;
return;
}
pre = b;
b = b->next;
}
debug_b = DEBUG_BLOCK(del);
ets_printf("%s %p %p %u already free\n", debug_b->head.task, debug_b, del, del->size&(~g_alloc_bit));
mem_malloc_show();
abort();
}
#endif

View file

@ -188,8 +188,12 @@ extern "C" {
#endif
#ifndef INCLUDE_pcTaskGetTaskName
#if ( configENABLE_MEMORY_DEBUG == 1)
#define INCLUDE_pcTaskGetTaskName 1
#else
#define INCLUDE_pcTaskGetTaskName 0
#endif
#endif
#ifndef configUSE_APPLICATION_TASK_TAG
#define configUSE_APPLICATION_TASK_TAG 0
@ -783,6 +787,10 @@ V8 if desired. */
#define xList List_t
#endif /* configENABLE_BACKWARD_COMPATIBILITY */
#ifndef configESP32_PER_TASK_DATA
#define configESP32_PER_TASK_DATA 1
#endif
#ifdef __cplusplus
}
#endif

View file

@ -106,12 +106,25 @@
#include "xtensa_config.h"
#if 1
/* configASSERT behaviour */
#ifndef __ASSEMBLER__
#include "rom/ets_sys.h"
#define configASSERT(a) if (!(a)) ets_printf("%s:%d (%s)- assert failed!\n", __FILE__, __LINE__, __FUNCTION__)
#endif
#if defined(CONFIG_FREERTOS_ASSERT_DISABLE)
#define configASSERT(a) /* assertions disabled */
#elif defined(CONFIG_FREERTOS_ASSERT_FAIL_PRINT_CONTINUE)
#define configASSERT(a) if (!(a)) { \
ets_printf("%s:%d (%s)- assert failed!\n", __FILE__, __LINE__, \
__FUNCTION__); \
}
#else /* CONFIG_FREERTOS_ASSERT_FAIL_ABORT */
#define configASSERT(a) if (!(a)) { \
ets_printf("%s:%d (%s)- assert failed!\n", __FILE__, __LINE__, \
__FUNCTION__); \
abort(); \
}
#endif
#endif /* def __ASSEMBLER__ */
/*-----------------------------------------------------------
@ -209,6 +222,12 @@
#define INCLUDE_vTaskDelay 1
#define INCLUDE_uxTaskGetStackHighWaterMark 1
#ifndef configENABLE_MEMORY_DEBUG
#define configENABLE_MEMORY_DEBUG 0
#endif
#define INCLUDE_xSemaphoreGetMutexHolder 1
/* The priority at which the tick interrupt runs. This should probably be
kept at 1. */
#define configKERNEL_INTERRUPT_PRIORITY 1

View file

@ -0,0 +1,78 @@
#ifndef _HEAP_REGION_DEBUG_H
#define _HEAP_REGION_DEBUG_H
#include "FreeRTOS.h"
#if (configENABLE_MEMORY_DEBUG == 1)
#define DEBUG_DOG_VALUE 0x1a2b3c4d
#define DEBUG_MAX_INFO_NUM 20
#define DEBUG_TYPE_MALLOC 1
#define DEBUG_TYPE_FREE 2
typedef struct {
unsigned int dog;
char task[4];
unsigned int pc;
}block_head_t;
typedef struct {
unsigned int dog;
}block_tail_t;
/* Please keep this definition same as BlockLink_t */
typedef struct _os_block_t {
struct _os_block_t *next;
size_t size;
unsigned int xtag;
}os_block_t;
typedef struct {
block_head_t head;
os_block_t os_block;
}debug_block_t;
typedef struct _mem_dbg_info{
void *addr;
char *task;
uint32_t pc;
uint32_t time;
uint8_t type;
}mem_dbg_info_t;
typedef struct _mem_dbg_ctl{
mem_dbg_info_t info[DEBUG_MAX_INFO_NUM];
uint32_t cnt;
}mem_dbg_ctl_t;
#define BLOCK_HEAD_LEN sizeof(block_head_t)
#define BLOCK_TAIL_LEN sizeof(block_tail_t)
#define OS_BLOCK(_b) ((os_block_t*)((debug_block_t*)((char*)(_b) + BLOCK_HEAD_LEN)))
#define DEBUG_BLOCK(_b) ((debug_block_t*)((char*)(_b) - BLOCK_HEAD_LEN))
#define HEAD_DOG(_b) ((_b)->head.dog)
#define TAIL_DOG(_b) (*(unsigned int*)((char*)(_b) + (((_b)->os_block.size & (~g_alloc_bit) ) - BLOCK_TAIL_LEN)))
#define DOG_ASSERT()\
{\
mem_debug_show();\
abort();\
}
extern void mem_check_block(void * data);
extern void mem_init_dog(void *data);
extern void mem_debug_init(size_t size, void *start, void *end, portMUX_TYPE *mutex, unsigned int alloc_bit);
extern void mem_malloc_block(void *data);
extern void mem_free_block(void *data);
extern void mem_check_all(void* pv);
#else
#define mem_check_block(...)
#define mem_init_dog(...)
#define BLOCK_HEAD_LEN 0
#define BLOCK_TAIL_LEN 0
#endif
#endif

View file

@ -0,0 +1,7 @@
#ifndef PANIC_H
#define PANIC_H
void setBreakpointIfJtag(void *fn);
#endif

View file

@ -118,12 +118,14 @@ typedef unsigned portBASE_TYPE UBaseType_t;
// portbenchmark
#include "portbenchmark.h"
#include "sdkconfig.h"
#define portFIRST_TASK_HOOK CONFIG_FREERTOS_BREAK_ON_SCHEDULER_START_JTAG
#define portMUX_DEBUG
typedef struct {
volatile uint32_t mux;
#ifdef portMUX_DEBUG
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
const char *lastLockedFn;
int lastLockedLine;
#endif
@ -148,7 +150,7 @@ typedef struct {
#define portMUX_VAL_SHIFT 0
//Keep this in sync with the portMUX_TYPE struct definition please.
#ifdef portMUX_DEBUG
#ifndef CONFIG_FREERTOS_PORTMUX_DEBUG
#define portMUX_INITIALIZER_UNLOCKED { \
.mux = portMUX_MAGIC_VAL|portMUX_FREE_VAL \
}
@ -167,13 +169,6 @@ typedef struct {
#define portCRITICAL_NESTING_IN_TCB 1
/*
Enable this to enable mux debugging. With this on, the mux code will warn you for deadlocks
and double releases etc.
*/
#define portMUX_DEBUG
/*
Modifications to portENTER_CRITICAL:
@ -195,7 +190,7 @@ This all assumes that interrupts are either entirely disabled or enabled. Interr
will break this scheme.
*/
void vPortCPUInitializeMutex(portMUX_TYPE *mux);
#ifdef portMUX_DEBUG
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
void vPortCPUAcquireMutex(portMUX_TYPE *mux, const char *function, int line);
portBASE_TYPE vPortCPUReleaseMutex(portMUX_TYPE *mux, const char *function, int line);
void vTaskEnterCritical( portMUX_TYPE *mux, const char *function, int line );

View file

@ -75,6 +75,8 @@
#error "include FreeRTOS.h must appear in source files before include task.h"
#endif
#include <limits.h>
#include "list.h"
#include "portmacro.h"
@ -91,7 +93,7 @@ extern "C" {
#define tskKERNEL_VERSION_MINOR 2
#define tskKERNEL_VERSION_BUILD 0
#define tskNO_AFFINITY portNUM_PROCESSORS
#define tskNO_AFFINITY INT_MAX
/**
* task. h

View file

@ -35,7 +35,7 @@ overflow handler also is in here.
*/
#if !CONFIG_FREERTOS_PANIC_SILENT_REBOOT
//os_printf may be broken, so we fix our own printing fns...
//printf may be broken, so we fix our own printing fns...
inline static void panicPutchar(char c) {
while (((READ_PERI_REG(UART_STATUS_REG(0))>>UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT)>=126) ;
WRITE_PERI_REG(UART_FIFO_REG(0), c);
@ -142,6 +142,16 @@ void panicHandler(XtExcFrame *frame) {
commonErrorHandler(frame);
}
static void setFirstBreakpoint(uint32_t pc) {
asm(
"wsr.ibreaka0 %0\n" \
"rsr.ibreakenable a3\n" \
"movi a4,1\n" \
"or a4, a4, a3\n" \
"wsr.ibreakenable a4\n" \
::"r"(pc):"a3","a4");
}
void xt_unhandled_exception(XtExcFrame *frame) {
int *regs=(int*)frame;
int x;
@ -158,13 +168,7 @@ void xt_unhandled_exception(XtExcFrame *frame) {
panicPutStr(". Setting bp and returning..\r\n");
//Stick a hardware breakpoint on the address the handler returns to. This way, the OCD debugger
//will kick in exactly at the context the error happened.
asm(
"wsr.ibreaka0 %0\n" \
"rsr.ibreakenable a3\n" \
"movi a4,1\n" \
"or a4, a4, a3\n" \
"wsr.ibreakenable a4\n" \
::"r"(regs[1]):"a3","a4");
setFirstBreakpoint(regs[1]);
return;
}
panicPutStr(". Exception was unhandled.\r\n");
@ -209,3 +213,9 @@ void commonErrorHandler(XtExcFrame *frame) {
while(1);
#endif
}
void setBreakpointIfJtag(void *fn) {
if (!inOCDMode()) return;
setFirstBreakpoint((uint32_t)fn);
}

View file

@ -101,6 +101,8 @@
#include "FreeRTOS.h"
#include "task.h"
#include "panic.h"
/* Defined in portasm.h */
extern void _frxt_tick_timer_init(void);
@ -249,7 +251,7 @@ void vPortStoreTaskMPUSettings( xMPU_SETTINGS *xMPUSettings, const struct xMEMOR
/*
* Wrapper for the Xtensa compare-and-set instruction. This subroutine will atomically compare
* *addr to compare, and if it's the same, will set *addr to set. It will return the old value
* *mux to compare, and if it's the same, will set *mux to set. It will return the old value
* of *addr.
*
* Note: the NOPs are needed on the ESP31 processor but superfluous on the ESP32.
@ -274,7 +276,7 @@ uint32_t uxPortCompareSet(volatile uint32_t *mux, uint32_t compare, uint32_t set
* For kernel use: Initialize a per-CPU mux. Mux will be initialized unlocked.
*/
void vPortCPUInitializeMutex(portMUX_TYPE *mux) {
#ifdef portMUX_DEBUG
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
ets_printf("Initializing mux %p\n", mux);
mux->lastLockedFn="(never locked)";
mux->lastLockedLine=-1;
@ -286,7 +288,7 @@ void vPortCPUInitializeMutex(portMUX_TYPE *mux) {
/*
* For kernel use: Acquire a per-CPU mux. Spinlocks, so don't hold on to these muxes for too long.
*/
#ifdef portMUX_DEBUG
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
void vPortCPUAcquireMutex(portMUX_TYPE *mux, const char *fnName, int line) {
#else
void vPortCPUAcquireMutex(portMUX_TYPE *mux) {
@ -294,7 +296,7 @@ void vPortCPUAcquireMutex(portMUX_TYPE *mux) {
uint32_t res;
uint32_t recCnt;
unsigned int irqStatus;
#ifdef portMUX_DEBUG
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
uint32_t cnt=(1<<16);
if ( (mux->mux & portMUX_MAGIC_MASK) != portMUX_MAGIC_VAL ) {
ets_printf("ERROR: vPortCPUAcquireMutex: mux %p is uninitialized (0x%X)! Called from %s line %d.\n", mux, mux->mux, fnName, line);
@ -311,21 +313,21 @@ void vPortCPUAcquireMutex(portMUX_TYPE *mux) {
//Mux was already locked by us. Just bump the recurse count by one.
recCnt=(res&portMUX_CNT_MASK)>>portMUX_CNT_SHIFT;
recCnt++;
#ifdef portMUX_DEBUG_RECURSIVE
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG_RECURSIVE
ets_printf("Recursive lock: recCnt=%d last non-recursive lock %s line %d, curr %s line %d\n", recCnt, mux->lastLockedFn, mux->lastLockedLine, fnName, line);
#endif
mux->mux=portMUX_MAGIC_VAL|(recCnt<<portMUX_CNT_SHIFT)|(xPortGetCoreID()<<portMUX_VAL_SHIFT);
break;
}
cnt--;
#ifdef portMUX_DEBUG
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
cnt--;
if (cnt==0) {
ets_printf("Timeout on mux! last non-recursive lock %s line %d, curr %s line %d\n", mux->lastLockedFn, mux->lastLockedLine, fnName, line);
ets_printf("Mux value %X\n", mux->mux);
}
#endif
} while (res!=portMUX_FREE_VAL);
#ifdef portMUX_DEBUG
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
if (res==portMUX_FREE_VAL) { //initial lock
mux->lastLockedFn=fnName;
mux->lastLockedLine=line;
@ -338,7 +340,7 @@ void vPortCPUAcquireMutex(portMUX_TYPE *mux) {
* For kernel use: Release a per-CPU mux. Returns true if everything is OK, false if mux
* was already unlocked or is locked by a different core.
*/
#ifdef portMUX_DEBUG
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
portBASE_TYPE vPortCPUReleaseMutex(portMUX_TYPE *mux, const char *fnName, int line) {
#else
portBASE_TYPE vPortCPUReleaseMutex(portMUX_TYPE *mux) {
@ -349,7 +351,7 @@ portBASE_TYPE vPortCPUReleaseMutex(portMUX_TYPE *mux) {
portBASE_TYPE ret=pdTRUE;
// ets_printf("Unlock %p\n", mux);
irqStatus=portENTER_CRITICAL_NESTED();
#ifdef portMUX_DEBUG
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
const char *lastLockedFn=mux->lastLockedFn;
int lastLockedLine=mux->lastLockedLine;
mux->lastLockedFn=fnName;
@ -360,13 +362,13 @@ portBASE_TYPE vPortCPUReleaseMutex(portMUX_TYPE *mux) {
res=uxPortCompareSet(&mux->mux, (xPortGetCoreID()<<portMUX_VAL_SHIFT)|portMUX_MAGIC_VAL, portMUX_FREE_VAL);
if ( res == portMUX_FREE_VAL ) {
#ifdef portMUX_DEBUG
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
ets_printf("ERROR: vPortCPUReleaseMutex: mux %p was already unlocked!\n", mux);
ets_printf("Last non-recursive unlock %s line %d, curr unlock %s line %d\n", lastLockedFn, lastLockedLine, fnName, line);
#endif
ret=pdFALSE;
} else if ( ((res&portMUX_VAL_MASK)>>portMUX_VAL_SHIFT) != xPortGetCoreID() ) {
#ifdef portMUX_DEBUG
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
ets_printf("ERROR: vPortCPUReleaseMutex: mux %p wasn't locked by this core (%d) but by core %d (ret=%x, mux=%x).\n", mux, xPortGetCoreID(), ((res&portMUX_VAL_MASK)>>portMUX_VAL_SHIFT), res, mux->mux);
ets_printf("Last non-recursive lock %s line %d\n", lastLockedFn, lastLockedLine);
ets_printf("Called by %s line %d\n", fnName, line);
@ -376,7 +378,7 @@ portBASE_TYPE vPortCPUReleaseMutex(portMUX_TYPE *mux) {
//We locked this, but the reccount isn't zero. Decrease refcount and continue.
recCnt=(res&portMUX_CNT_MASK)>>portMUX_CNT_SHIFT;
recCnt--;
#ifdef portMUX_DEBUG_RECURSIVE
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG_RECURSIVE
ets_printf("Recursive unlock: recCnt=%d last locked %s line %d, curr %s line %d\n", recCnt, lastLockedFn, lastLockedLine, fnName, line);
#endif
mux->mux=portMUX_MAGIC_VAL|(recCnt<<portMUX_CNT_SHIFT)|(xPortGetCoreID()<<portMUX_VAL_SHIFT);
@ -385,6 +387,10 @@ portBASE_TYPE vPortCPUReleaseMutex(portMUX_TYPE *mux) {
return ret;
}
#if CONFIG_FREERTOS_BREAK_ON_SCHEDULER_START_JTAG
void vPortFirstTaskHook(TaskFunction_t function) {
setBreakpointIfJtag(function);
}
#endif

View file

@ -483,14 +483,15 @@ int8_t *pcAllocatedBuffer;
void* xQueueGetMutexHolder( QueueHandle_t xSemaphore )
{
void *pxReturn;
Queue_t * const pxQueue = ( Queue_t * ) xSemaphore;
void *pxReturn;
/* This function is called by xSemaphoreGetMutexHolder(), and should not
be called directly. Note: This is a good way of determining if the
calling task is the mutex holder, but not a good way of determining the
identity of the mutex holder, as the holder may change between the
following critical section exiting and the function returning. */
taskENTER_CRITICAL();
taskENTER_CRITICAL(&pxQueue->mux);
{
if( ( ( Queue_t * ) xSemaphore )->uxQueueType == queueQUEUE_IS_MUTEX )
{
@ -501,7 +502,7 @@ int8_t *pcAllocatedBuffer;
pxReturn = NULL;
}
}
taskEXIT_CRITICAL();
taskEXIT_CRITICAL(&pxQueue->mux);
return pxReturn;
} /*lint !e818 xSemaphore cannot be a pointer to const because it is a typedef. */

View file

@ -84,6 +84,7 @@ task.h is included from an application file. */
#include "timers.h"
#include "StackMacros.h"
#include "portmacro.h"
#include "semphr.h"
/* Lint e961 and e750 are suppressed as a MISRA exception justified because the
MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined for the
@ -445,6 +446,11 @@ to its original value when it is released. */
extern void vApplicationTickHook( void );
#endif
#if portFIRST_TASK_HOOK
extern void vPortFirstTaskHook(TaskFunction_t taskfn);
#endif
/* File private functions. --------------------------------*/
/*
@ -707,6 +713,12 @@ BaseType_t i;
/* Schedule if nothing is scheduled yet, or overwrite a task of lower prio. */
if ( pxCurrentTCB[i] == NULL || pxCurrentTCB[i]->uxPriority <= uxPriority )
{
#if portFIRST_TASK_HOOK
if ( i == 0) {
vPortFirstTaskHook(pxTaskCode);
}
#endif /* configFIRST_TASK_HOOK */
pxCurrentTCB[i] = pxNewTCB;
break;
}
@ -3743,14 +3755,14 @@ scheduler will re-enable the interrupts instead. */
#if ( portCRITICAL_NESTING_IN_TCB == 1 )
#ifdef portMUX_DEBUG
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
void vTaskEnterCritical( portMUX_TYPE *mux, const char *function, int line )
#else
void vTaskEnterCritical( portMUX_TYPE *mux )
#endif
{
portDISABLE_INTERRUPTS();
#ifdef portMUX_DEBUG
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
vPortCPUAcquireMutex( mux, function, line );
#else
vPortCPUAcquireMutex( mux );
@ -3783,13 +3795,13 @@ scheduler will re-enable the interrupts instead. */
#if ( portCRITICAL_NESTING_IN_TCB == 1 )
#ifdef portMUX_DEBUG
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
void vTaskExitCritical( portMUX_TYPE *mux, const char *function, int line )
#else
void vTaskExitCritical( portMUX_TYPE *mux )
#endif
{
#ifdef portMUX_DEBUG
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
vPortCPUReleaseMutex( mux, function, line );
#else
vPortCPUReleaseMutex( mux );
@ -4568,9 +4580,6 @@ TickType_t uxReturn;
#endif /* configUSE_TASK_NOTIFICATIONS */
/*-----------------------------------------------------------*/
#ifdef FREERTOS_MODULE_TEST
#include "tasks_test_access_functions.h"
#endif

20
components/json/LICENSE Normal file
View file

@ -0,0 +1,20 @@
Copyright (c) 2009 Dave Gamble
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.

13
components/json/Makefile Normal file
View file

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

247
components/json/README Normal file
View file

@ -0,0 +1,247 @@
/*
Copyright (c) 2009 Dave Gamble
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.
*/
Welcome to cJSON.
cJSON aims to be the dumbest possible parser that you can get your job done with.
It's a single file of C, and a single header file.
JSON is described best here: http://www.json.org/
It's like XML, but fat-free. You use it to move data around, store things, or just
generally represent your program's state.
First up, how do I build?
Add cJSON.c to your project, and put cJSON.h somewhere in the header search path.
For example, to build the test app:
gcc cJSON.c test.c -o test -lm
./test
As a library, cJSON exists to take away as much legwork as it can, but not get in your way.
As a point of pragmatism (i.e. ignoring the truth), I'm going to say that you can use it
in one of two modes: Auto and Manual. Let's have a quick run-through.
I lifted some JSON from this page: http://www.json.org/fatfree.html
That page inspired me to write cJSON, which is a parser that tries to share the same
philosophy as JSON itself. Simple, dumb, out of the way.
Some JSON:
{
"name": "Jack (\"Bee\") Nimble",
"format": {
"type": "rect",
"width": 1920,
"height": 1080,
"interlace": false,
"frame rate": 24
}
}
Assume that you got this from a file, a webserver, or magic JSON elves, whatever,
you have a char * to it. Everything is a cJSON struct.
Get it parsed:
cJSON *root = cJSON_Parse(my_json_string);
This is an object. We're in C. We don't have objects. But we do have structs.
What's the framerate?
cJSON *format = cJSON_GetObjectItem(root,"format");
int framerate = cJSON_GetObjectItem(format,"frame rate")->valueint;
Want to change the framerate?
cJSON_GetObjectItem(format,"frame rate")->valueint=25;
Back to disk?
char *rendered=cJSON_Print(root);
Finished? Delete the root (this takes care of everything else).
cJSON_Delete(root);
That's AUTO mode. If you're going to use Auto mode, you really ought to check pointers
before you dereference them. If you want to see how you'd build this struct in code?
cJSON *root,*fmt;
root=cJSON_CreateObject();
cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble"));
cJSON_AddItemToObject(root, "format", fmt=cJSON_CreateObject());
cJSON_AddStringToObject(fmt,"type", "rect");
cJSON_AddNumberToObject(fmt,"width", 1920);
cJSON_AddNumberToObject(fmt,"height", 1080);
cJSON_AddFalseToObject (fmt,"interlace");
cJSON_AddNumberToObject(fmt,"frame rate", 24);
Hopefully we can agree that's not a lot of code? There's no overhead, no unnecessary setup.
Look at test.c for a bunch of nice examples, mostly all ripped off the json.org site, and
a few from elsewhere.
What about manual mode? First up you need some detail.
Let's cover how the cJSON objects represent the JSON data.
cJSON doesn't distinguish arrays from objects in handling; just type.
Each cJSON has, potentially, a child, siblings, value, a name.
The root object has: Object Type and a Child
The Child has name "name", with value "Jack ("Bee") Nimble", and a sibling:
Sibling has type Object, name "format", and a child.
That child has type String, name "type", value "rect", and a sibling:
Sibling has type Number, name "width", value 1920, and a sibling:
Sibling has type Number, name "height", value 1080, and a sibling:
Sibling has type False, name "interlace", and a sibling:
Sibling has type Number, name "frame rate", value 24
Here's the structure:
typedef struct cJSON {
struct cJSON *next,*prev;
struct cJSON *child;
int type;
char *valuestring;
int valueint;
double valuedouble;
char *string;
} cJSON;
By default all values are 0 unless set by virtue of being meaningful.
next/prev is a doubly linked list of siblings. next takes you to your sibling,
prev takes you back from your sibling to you.
Only objects and arrays have a "child", and it's the head of the doubly linked list.
A "child" entry will have prev==0, but next potentially points on. The last sibling has next=0.
The type expresses Null/True/False/Number/String/Array/Object, all of which are #defined in
cJSON.h
A Number has valueint and valuedouble. If you're expecting an int, read valueint, if not read
valuedouble.
Any entry which is in the linked list which is the child of an object will have a "string"
which is the "name" of the entry. When I said "name" in the above example, that's "string".
"string" is the JSON name for the 'variable name' if you will.
Now you can trivially walk the lists, recursively, and parse as you please.
You can invoke cJSON_Parse to get cJSON to parse for you, and then you can take
the root object, and traverse the structure (which is, formally, an N-tree),
and tokenise as you please. If you wanted to build a callback style parser, this is how
you'd do it (just an example, since these things are very specific):
void parse_and_callback(cJSON *item,const char *prefix)
{
while (item)
{
char *newprefix=malloc(strlen(prefix)+strlen(item->name)+2);
sprintf(newprefix,"%s/%s",prefix,item->name);
int dorecurse=callback(newprefix, item->type, item);
if (item->child && dorecurse) parse_and_callback(item->child,newprefix);
item=item->next;
free(newprefix);
}
}
The prefix process will build you a separated list, to simplify your callback handling.
The 'dorecurse' flag would let the callback decide to handle sub-arrays on it's own, or
let you invoke it per-item. For the item above, your callback might look like this:
int callback(const char *name,int type,cJSON *item)
{
if (!strcmp(name,"name")) { /* populate name */ }
else if (!strcmp(name,"format/type") { /* handle "rect" */ }
else if (!strcmp(name,"format/width") { /* 800 */ }
else if (!strcmp(name,"format/height") { /* 600 */ }
else if (!strcmp(name,"format/interlace") { /* false */ }
else if (!strcmp(name,"format/frame rate") { /* 24 */ }
return 1;
}
Alternatively, you might like to parse iteratively.
You'd use:
void parse_object(cJSON *item)
{
int i; for (i=0;i<cJSON_GetArraySize(item);i++)
{
cJSON *subitem=cJSON_GetArrayItem(item,i);
// handle subitem.
}
}
Or, for PROPER manual mode:
void parse_object(cJSON *item)
{
cJSON *subitem=item->child;
while (subitem)
{
// handle subitem
if (subitem->child) parse_object(subitem->child);
subitem=subitem->next;
}
}
Of course, this should look familiar, since this is just a stripped-down version
of the callback-parser.
This should cover most uses you'll find for parsing. The rest should be possible
to infer.. and if in doubt, read the source! There's not a lot of it! ;)
In terms of constructing JSON data, the example code above is the right way to do it.
You can, of course, hand your sub-objects to other functions to populate.
Also, if you find a use for it, you can manually build the objects.
For instance, suppose you wanted to build an array of objects?
cJSON *objects[24];
cJSON *Create_array_of_anything(cJSON **items,int num)
{
int i;cJSON *prev, *root=cJSON_CreateArray();
for (i=0;i<24;i++)
{
if (!i) root->child=objects[i];
else prev->next=objects[i], objects[i]->prev=prev;
prev=objects[i];
}
return root;
}
and simply: Create_array_of_anything(objects,24);
cJSON doesn't make any assumptions about what order you create things in.
You can attach the objects, as above, and later add children to each
of those objects.
As soon as you call cJSON_Print, it renders the structure to text.
The test.c code shows how to handle a bunch of typical cases. If you uncomment
the code, it'll load, parse and print a bunch of test files, also from json.org,
which are more complex than I'd care to try and stash into a const char array[].
Enjoy cJSON!
- Dave Gamble, Aug 2009

View file

@ -0,0 +1,149 @@
/*
Copyright (c) 2009 Dave Gamble
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.
*/
#ifndef cJSON__h
#define cJSON__h
#ifdef __cplusplus
extern "C"
{
#endif
/* cJSON Types: */
#define cJSON_False 0
#define cJSON_True 1
#define cJSON_NULL 2
#define cJSON_Number 3
#define cJSON_String 4
#define cJSON_Array 5
#define cJSON_Object 6
#define cJSON_IsReference 256
#define cJSON_StringIsConst 512
/* The cJSON structure: */
typedef struct cJSON {
struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
int type; /* The type of the item, as above. */
char *valuestring; /* The item's string, if type==cJSON_String */
int valueint; /* The item's number, if type==cJSON_Number */
double valuedouble; /* The item's number, if type==cJSON_Number */
char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
} cJSON;
typedef struct cJSON_Hooks {
void *(*malloc_fn)(size_t sz);
void (*free_fn)(void *ptr);
} cJSON_Hooks;
/* Supply malloc, realloc and free functions to cJSON */
extern void cJSON_InitHooks(cJSON_Hooks* hooks);
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
extern cJSON *cJSON_Parse(const char *value);
/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
extern char *cJSON_Print(cJSON *item);
/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
extern char *cJSON_PrintUnformatted(cJSON *item);
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
extern char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt);
/* Delete a cJSON entity and all subentities. */
extern void cJSON_Delete(cJSON *c);
/* Returns the number of items in an array (or object). */
extern int cJSON_GetArraySize(cJSON *array);
/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
/* Get item "string" from object. Case insensitive. */
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
extern const char *cJSON_GetErrorPtr(void);
/* These calls create a cJSON item of the appropriate type. */
extern cJSON *cJSON_CreateNull(void);
extern cJSON *cJSON_CreateTrue(void);
extern cJSON *cJSON_CreateFalse(void);
extern cJSON *cJSON_CreateBool(int b);
extern cJSON *cJSON_CreateNumber(double num);
extern cJSON *cJSON_CreateString(const char *string);
extern cJSON *cJSON_CreateArray(void);
extern cJSON *cJSON_CreateObject(void);
/* These utilities create an Array of count items. */
extern cJSON *cJSON_CreateIntArray(const int *numbers,int count);
extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count);
extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count);
extern cJSON *cJSON_CreateStringArray(const char **strings,int count);
/* Append item to the specified array/object. */
extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
extern void cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item); /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object */
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);
/* Remove/Detatch items from Arrays/Objects. */
extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which);
extern void cJSON_DeleteItemFromArray(cJSON *array,int which);
extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string);
extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string);
/* Update array items. */
extern void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem); /* Shifts pre-existing items to the right. */
extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem);
extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
/* Duplicate a cJSON item */
extern cJSON *cJSON_Duplicate(cJSON *item,int recurse);
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
need to be released. With recurse!=0, it will duplicate any children connected to the item.
The item->next and ->prev pointers are always zero on return from Duplicate. */
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated);
extern void cJSON_Minify(char *json);
/* Macros for creating things quickly. */
#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull())
#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
#define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
#define cJSON_SetNumberValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,750 @@
/*
Copyright (c) 2009 Dave Gamble
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.
*/
/* cJSON */
/* JSON parser in C. */
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <float.h>
#include <limits.h>
#include <ctype.h>
#include "cJSON.h"
static const char *ep;
const char *cJSON_GetErrorPtr(void) {return ep;}
static int cJSON_strcasecmp(const char *s1,const char *s2)
{
if (!s1) return (s1==s2)?0:1;if (!s2) return 1;
for(; tolower(*(const unsigned char *)s1) == tolower(*(const unsigned char *)s2); ++s1, ++s2) if(*s1 == 0) return 0;
return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
}
static void *(*cJSON_malloc)(size_t sz) = malloc;
static void (*cJSON_free)(void *ptr) = free;
static char* cJSON_strdup(const char* str)
{
size_t len;
char* copy;
len = strlen(str) + 1;
if (!(copy = (char*)cJSON_malloc(len))) return 0;
memcpy(copy,str,len);
return copy;
}
void cJSON_InitHooks(cJSON_Hooks* hooks)
{
if (!hooks) { /* Reset hooks */
cJSON_malloc = malloc;
cJSON_free = free;
return;
}
cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
cJSON_free = (hooks->free_fn)?hooks->free_fn:free;
}
/* Internal constructor. */
static cJSON *cJSON_New_Item(void)
{
cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
if (node) memset(node,0,sizeof(cJSON));
return node;
}
/* Delete a cJSON structure. */
void cJSON_Delete(cJSON *c)
{
cJSON *next;
while (c)
{
next=c->next;
if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child);
if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring);
if (!(c->type&cJSON_StringIsConst) && c->string) cJSON_free(c->string);
cJSON_free(c);
c=next;
}
}
/* Parse the input text to generate a number, and populate the result into item. */
static const char *parse_number(cJSON *item,const char *num)
{
double n=0,sign=1,scale=0;int subscale=0,signsubscale=1;
if (*num=='-') sign=-1,num++; /* Has sign? */
if (*num=='0') num++; /* is zero */
if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */
if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */
if (*num=='e' || *num=='E') /* Exponent? */
{ num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */
while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */
}
n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */
item->valuedouble=n;
item->valueint=(int)n;
item->type=cJSON_Number;
return num;
}
static int pow2gt (int x) { --x; x|=x>>1; x|=x>>2; x|=x>>4; x|=x>>8; x|=x>>16; return x+1; }
typedef struct {char *buffer; int length; int offset; } printbuffer;
static char* ensure(printbuffer *p,int needed)
{
char *newbuffer;int newsize;
if (!p || !p->buffer) return 0;
needed+=p->offset;
if (needed<=p->length) return p->buffer+p->offset;
newsize=pow2gt(needed);
newbuffer=(char*)cJSON_malloc(newsize);
if (!newbuffer) {cJSON_free(p->buffer);p->length=0,p->buffer=0;return 0;}
if (newbuffer) memcpy(newbuffer,p->buffer,p->length);
cJSON_free(p->buffer);
p->length=newsize;
p->buffer=newbuffer;
return newbuffer+p->offset;
}
static int update(printbuffer *p)
{
char *str;
if (!p || !p->buffer) return 0;
str=p->buffer+p->offset;
return p->offset+strlen(str);
}
/* Render the number nicely from the given item into a string. */
static char *print_number(cJSON *item,printbuffer *p)
{
char *str=0;
double d=item->valuedouble;
if (d==0)
{
if (p) str=ensure(p,2);
else str=(char*)cJSON_malloc(2); /* special case for 0. */
if (str) strcpy(str,"0");
}
else if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN)
{
if (p) str=ensure(p,21);
else str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */
if (str) sprintf(str,"%d",item->valueint);
}
else
{
if (p) str=ensure(p,64);
else str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */
if (str)
{
if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)sprintf(str,"%.0f",d);
else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d);
else sprintf(str,"%f",d);
}
}
return str;
}
static unsigned parse_hex4(const char *str)
{
unsigned h=0;
if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
h=h<<4;str++;
if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
h=h<<4;str++;
if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
h=h<<4;str++;
if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
return h;
}
/* Parse the input text into an unescaped cstring, and populate item. */
static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
static const char *parse_string(cJSON *item,const char *str)
{
const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2;
if (*str!='\"') {ep=str;return 0;} /* not a string! */
while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */
out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */
if (!out) return 0;
ptr=str+1;ptr2=out;
while (*ptr!='\"' && *ptr)
{
if (*ptr!='\\') *ptr2++=*ptr++;
else
{
ptr++;
switch (*ptr)
{
case 'b': *ptr2++='\b'; break;
case 'f': *ptr2++='\f'; break;
case 'n': *ptr2++='\n'; break;
case 'r': *ptr2++='\r'; break;
case 't': *ptr2++='\t'; break;
case 'u': /* transcode utf16 to utf8. */
uc=parse_hex4(ptr+1);ptr+=4; /* get the unicode char. */
if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; /* check for invalid. */
if (uc>=0xD800 && uc<=0xDBFF) /* UTF16 surrogate pairs. */
{
if (ptr[1]!='\\' || ptr[2]!='u') break; /* missing second-half of surrogate. */
uc2=parse_hex4(ptr+3);ptr+=6;
if (uc2<0xDC00 || uc2>0xDFFF) break; /* invalid second-half of surrogate. */
uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF));
}
len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len;
switch (len) {
case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
case 1: *--ptr2 =(uc | firstByteMark[len]);
}
ptr2+=len;
break;
default: *ptr2++=*ptr; break;
}
ptr++;
}
}
*ptr2=0;
if (*ptr=='\"') ptr++;
item->valuestring=out;
item->type=cJSON_String;
return ptr;
}
/* Render the cstring provided to an escaped version that can be printed. */
static char *print_string_ptr(const char *str,printbuffer *p)
{
const char *ptr;char *ptr2,*out;int len=0,flag=0;unsigned char token;
for (ptr=str;*ptr;ptr++) flag|=((*ptr>0 && *ptr<32)||(*ptr=='\"')||(*ptr=='\\'))?1:0;
if (!flag)
{
len=ptr-str;
if (p) out=ensure(p,len+3);
else out=(char*)cJSON_malloc(len+3);
if (!out) return 0;
ptr2=out;*ptr2++='\"';
strcpy(ptr2,str);
ptr2[len]='\"';
ptr2[len+1]=0;
return out;
}
if (!str)
{
if (p) out=ensure(p,3);
else out=(char*)cJSON_malloc(3);
if (!out) return 0;
strcpy(out,"\"\"");
return out;
}
ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;}
if (p) out=ensure(p,len+3);
else out=(char*)cJSON_malloc(len+3);
if (!out) return 0;
ptr2=out;ptr=str;
*ptr2++='\"';
while (*ptr)
{
if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++;
else
{
*ptr2++='\\';
switch (token=*ptr++)
{
case '\\': *ptr2++='\\'; break;
case '\"': *ptr2++='\"'; break;
case '\b': *ptr2++='b'; break;
case '\f': *ptr2++='f'; break;
case '\n': *ptr2++='n'; break;
case '\r': *ptr2++='r'; break;
case '\t': *ptr2++='t'; break;
default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */
}
}
}
*ptr2++='\"';*ptr2++=0;
return out;
}
/* Invote print_string_ptr (which is useful) on an item. */
static char *print_string(cJSON *item,printbuffer *p) {return print_string_ptr(item->valuestring,p);}
/* Predeclare these prototypes. */
static const char *parse_value(cJSON *item,const char *value);
static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p);
static const char *parse_array(cJSON *item,const char *value);
static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p);
static const char *parse_object(cJSON *item,const char *value);
static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p);
/* Utility to jump whitespace and cr/lf */
static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;}
/* Parse an object - create a new root, and populate. */
cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated)
{
const char *end=0;
cJSON *c=cJSON_New_Item();
ep=0;
if (!c) return 0; /* memory fail */
end=parse_value(c,skip(value));
if (!end) {cJSON_Delete(c);return 0;} /* parse failure. ep is set. */
/* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}}
if (return_parse_end) *return_parse_end=end;
return c;
}
/* Default options for cJSON_Parse */
cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);}
/* Render a cJSON item/entity/structure to text. */
char *cJSON_Print(cJSON *item) {return print_value(item,0,1,0);}
char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0,0);}
char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt)
{
printbuffer p;
p.buffer=(char*)cJSON_malloc(prebuffer);
p.length=prebuffer;
p.offset=0;
return print_value(item,0,fmt,&p);
return p.buffer;
}
/* Parser core - when encountering text, process appropriately. */
static const char *parse_value(cJSON *item,const char *value)
{
if (!value) return 0; /* Fail on null. */
if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; }
if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; }
if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; }
if (*value=='\"') { return parse_string(item,value); }
if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); }
if (*value=='[') { return parse_array(item,value); }
if (*value=='{') { return parse_object(item,value); }
ep=value;return 0; /* failure. */
}
/* Render a value to text. */
static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p)
{
char *out=0;
if (!item) return 0;
if (p)
{
switch ((item->type)&255)
{
case cJSON_NULL: {out=ensure(p,5); if (out) strcpy(out,"null"); break;}
case cJSON_False: {out=ensure(p,6); if (out) strcpy(out,"false"); break;}
case cJSON_True: {out=ensure(p,5); if (out) strcpy(out,"true"); break;}
case cJSON_Number: out=print_number(item,p);break;
case cJSON_String: out=print_string(item,p);break;
case cJSON_Array: out=print_array(item,depth,fmt,p);break;
case cJSON_Object: out=print_object(item,depth,fmt,p);break;
}
}
else
{
switch ((item->type)&255)
{
case cJSON_NULL: out=cJSON_strdup("null"); break;
case cJSON_False: out=cJSON_strdup("false");break;
case cJSON_True: out=cJSON_strdup("true"); break;
case cJSON_Number: out=print_number(item,0);break;
case cJSON_String: out=print_string(item,0);break;
case cJSON_Array: out=print_array(item,depth,fmt,0);break;
case cJSON_Object: out=print_object(item,depth,fmt,0);break;
}
}
return out;
}
/* Build an array from input text. */
static const char *parse_array(cJSON *item,const char *value)
{
cJSON *child;
if (*value!='[') {ep=value;return 0;} /* not an array! */
item->type=cJSON_Array;
value=skip(value+1);
if (*value==']') return value+1; /* empty array. */
item->child=child=cJSON_New_Item();
if (!item->child) return 0; /* memory fail */
value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */
if (!value) return 0;
while (*value==',')
{
cJSON *new_item;
if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
child->next=new_item;new_item->prev=child;child=new_item;
value=skip(parse_value(child,skip(value+1)));
if (!value) return 0; /* memory fail */
}
if (*value==']') return value+1; /* end of array */
ep=value;return 0; /* malformed. */
}
/* Render an array to text */
static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p)
{
char **entries;
char *out=0,*ptr,*ret;int len=5;
cJSON *child=item->child;
int numentries=0,i=0,fail=0;
size_t tmplen=0;
/* How many entries in the array? */
while (child) numentries++,child=child->next;
/* Explicitly handle numentries==0 */
if (!numentries)
{
if (p) out=ensure(p,3);
else out=(char*)cJSON_malloc(3);
if (out) strcpy(out,"[]");
return out;
}
if (p)
{
/* Compose the output array. */
i=p->offset;
ptr=ensure(p,1);if (!ptr) return 0; *ptr='['; p->offset++;
child=item->child;
while (child && !fail)
{
print_value(child,depth+1,fmt,p);
p->offset=update(p);
if (child->next) {len=fmt?2:1;ptr=ensure(p,len+1);if (!ptr) return 0;*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;p->offset+=len;}
child=child->next;
}
ptr=ensure(p,2);if (!ptr) return 0; *ptr++=']';*ptr=0;
out=(p->buffer)+i;
}
else
{
/* Allocate an array to hold the values for each */
entries=(char**)cJSON_malloc(numentries*sizeof(char*));
if (!entries) return 0;
memset(entries,0,numentries*sizeof(char*));
/* Retrieve all the results: */
child=item->child;
while (child && !fail)
{
ret=print_value(child,depth+1,fmt,0);
entries[i++]=ret;
if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1;
child=child->next;
}
/* If we didn't fail, try to malloc the output string */
if (!fail) out=(char*)cJSON_malloc(len);
/* If that fails, we fail. */
if (!out) fail=1;
/* Handle failure. */
if (fail)
{
for (i=0;i<numentries;i++) if (entries[i]) cJSON_free(entries[i]);
cJSON_free(entries);
return 0;
}
/* Compose the output array. */
*out='[';
ptr=out+1;*ptr=0;
for (i=0;i<numentries;i++)
{
tmplen=strlen(entries[i]);memcpy(ptr,entries[i],tmplen);ptr+=tmplen;
if (i!=numentries-1) {*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;}
cJSON_free(entries[i]);
}
cJSON_free(entries);
*ptr++=']';*ptr++=0;
}
return out;
}
/* Build an object from the text. */
static const char *parse_object(cJSON *item,const char *value)
{
cJSON *child;
if (*value!='{') {ep=value;return 0;} /* not an object! */
item->type=cJSON_Object;
value=skip(value+1);
if (*value=='}') return value+1; /* empty array. */
item->child=child=cJSON_New_Item();
if (!item->child) return 0;
value=skip(parse_string(child,skip(value)));
if (!value) return 0;
child->string=child->valuestring;child->valuestring=0;
if (*value!=':') {ep=value;return 0;} /* fail! */
value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
if (!value) return 0;
while (*value==',')
{
cJSON *new_item;
if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
child->next=new_item;new_item->prev=child;child=new_item;
value=skip(parse_string(child,skip(value+1)));
if (!value) return 0;
child->string=child->valuestring;child->valuestring=0;
if (*value!=':') {ep=value;return 0;} /* fail! */
value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
if (!value) return 0;
}
if (*value=='}') return value+1; /* end of array */
ep=value;return 0; /* malformed. */
}
/* Render an object to text. */
static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p)
{
char **entries=0,**names=0;
char *out=0,*ptr,*ret,*str;int len=7,i=0,j;
cJSON *child=item->child;
int numentries=0,fail=0;
size_t tmplen=0;
/* Count the number of entries. */
while (child) numentries++,child=child->next;
/* Explicitly handle empty object case */
if (!numentries)
{
if (p) out=ensure(p,fmt?depth+4:3);
else out=(char*)cJSON_malloc(fmt?depth+4:3);
if (!out) return 0;
ptr=out;*ptr++='{';
if (fmt) {*ptr++='\n';for (i=0;i<depth-1;i++) *ptr++='\t';}
*ptr++='}';*ptr++=0;
return out;
}
if (p)
{
/* Compose the output: */
i=p->offset;
len=fmt?2:1; ptr=ensure(p,len+1); if (!ptr) return 0;
*ptr++='{'; if (fmt) *ptr++='\n'; *ptr=0; p->offset+=len;
child=item->child;depth++;
while (child)
{
if (fmt)
{
ptr=ensure(p,depth); if (!ptr) return 0;
for (j=0;j<depth;j++) *ptr++='\t';
p->offset+=depth;
}
print_string_ptr(child->string,p);
p->offset=update(p);
len=fmt?2:1;
ptr=ensure(p,len); if (!ptr) return 0;
*ptr++=':';if (fmt) *ptr++='\t';
p->offset+=len;
print_value(child,depth,fmt,p);
p->offset=update(p);
len=(fmt?1:0)+(child->next?1:0);
ptr=ensure(p,len+1); if (!ptr) return 0;
if (child->next) *ptr++=',';
if (fmt) *ptr++='\n';*ptr=0;
p->offset+=len;
child=child->next;
}
ptr=ensure(p,fmt?(depth+1):2); if (!ptr) return 0;
if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t';
*ptr++='}';*ptr=0;
out=(p->buffer)+i;
}
else
{
/* Allocate space for the names and the objects */
entries=(char**)cJSON_malloc(numentries*sizeof(char*));
if (!entries) return 0;
names=(char**)cJSON_malloc(numentries*sizeof(char*));
if (!names) {cJSON_free(entries);return 0;}
memset(entries,0,sizeof(char*)*numentries);
memset(names,0,sizeof(char*)*numentries);
/* Collect all the results into our arrays: */
child=item->child;depth++;if (fmt) len+=depth;
while (child)
{
names[i]=str=print_string_ptr(child->string,0);
entries[i++]=ret=print_value(child,depth,fmt,0);
if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1;
child=child->next;
}
/* Try to allocate the output string */
if (!fail) out=(char*)cJSON_malloc(len);
if (!out) fail=1;
/* Handle failure */
if (fail)
{
for (i=0;i<numentries;i++) {if (names[i]) cJSON_free(names[i]);if (entries[i]) cJSON_free(entries[i]);}
cJSON_free(names);cJSON_free(entries);
return 0;
}
/* Compose the output: */
*out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0;
for (i=0;i<numentries;i++)
{
if (fmt) for (j=0;j<depth;j++) *ptr++='\t';
tmplen=strlen(names[i]);memcpy(ptr,names[i],tmplen);ptr+=tmplen;
*ptr++=':';if (fmt) *ptr++='\t';
strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
if (i!=numentries-1) *ptr++=',';
if (fmt) *ptr++='\n';*ptr=0;
cJSON_free(names[i]);cJSON_free(entries[i]);
}
cJSON_free(names);cJSON_free(entries);
if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t';
*ptr++='}';*ptr++=0;
}
return out;
}
/* Get Array size/item / object item. */
int cJSON_GetArraySize(cJSON *array) {cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;}
cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;}
cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;}
/* Utility for array list handling. */
static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;}
/* Utility for handling references. */
static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;}
/* Add item to array/object. */
void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}}
void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);}
void cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (!(item->type&cJSON_StringIsConst) && item->string) cJSON_free(item->string);item->string=(char*)string;item->type|=cJSON_StringIsConst;cJSON_AddItemToArray(object,item);}
void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));}
void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));}
cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0;
if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;}
void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));}
cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;}
void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));}
/* Replace array/object items with new ones. */
void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) {cJSON_AddItemToArray(array,newitem);return;}
newitem->next=c;newitem->prev=c->prev;c->prev=newitem;if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;}
void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return;
newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem;
if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);}
void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}}
/* Create basic types: */
cJSON *cJSON_CreateNull(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;}
cJSON *cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;}
cJSON *cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;}
cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;}
cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;}
cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;}
cJSON *cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;}
cJSON *cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;}
/* Create Arrays: */
cJSON *cJSON_CreateIntArray(const int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateFloatArray(const float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateDoubleArray(const double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateString(strings[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
/* Duplication */
cJSON *cJSON_Duplicate(cJSON *item,int recurse)
{
cJSON *newitem,*cptr,*nptr=0,*newchild;
/* Bail on bad ptr */
if (!item) return 0;
/* Create new item */
newitem=cJSON_New_Item();
if (!newitem) return 0;
/* Copy over all vars */
newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble;
if (item->valuestring) {newitem->valuestring=cJSON_strdup(item->valuestring); if (!newitem->valuestring) {cJSON_Delete(newitem);return 0;}}
if (item->string) {newitem->string=cJSON_strdup(item->string); if (!newitem->string) {cJSON_Delete(newitem);return 0;}}
/* If non-recursive, then we're done! */
if (!recurse) return newitem;
/* Walk the ->next chain for the child. */
cptr=item->child;
while (cptr)
{
newchild=cJSON_Duplicate(cptr,1); /* Duplicate (with recurse) each item in the ->next chain */
if (!newchild) {cJSON_Delete(newitem);return 0;}
if (nptr) {nptr->next=newchild,newchild->prev=nptr;nptr=newchild;} /* If newitem->child already set, then crosswire ->prev and ->next and move on */
else {newitem->child=newchild;nptr=newchild;} /* Set newitem->child and move to it */
cptr=cptr->next;
}
return newitem;
}
void cJSON_Minify(char *json)
{
char *into=json;
while (*json)
{
if (*json==' ') json++;
else if (*json=='\t') json++; /* Whitespace characters. */
else if (*json=='\r') json++;
else if (*json=='\n') json++;
else if (*json=='/' && json[1]=='/') while (*json && *json!='\n') json++; /* double-slash comments, to end of line. */
else if (*json=='/' && json[1]=='*') {while (*json && !(*json=='*' && json[1]=='/')) json++;json+=2;} /* multiline comments. */
else if (*json=='\"'){*into++=*json++;while (*json && *json!='\"'){if (*json=='\\') *into++=*json++;*into++=*json++;}*into++=*json++;} /* string literals, which are \" sensitive. */
else *into++=*json++; /* All other characters. */
}
*into=0; /* and null-terminate. */
}

View file

@ -0,0 +1,395 @@
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "cJSON_Utils.h"
static int cJSONUtils_strcasecmp(const char *s1,const char *s2)
{
if (!s1) return (s1==s2)?0:1;if (!s2) return 1;
for(; tolower(*(const unsigned char *)s1) == tolower(*(const unsigned char *)s2); ++s1, ++s2) if(*s1 == 0) return 0;
return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
}
/* JSON Pointer implementation: */
static int cJSONUtils_Pstrcasecmp(const char *a,const char *e)
{
if (!a || !e) return (a==e)?0:1;
for (;*a && *e && *e!='/';a++,e++) {
if (*e=='~') {if (!(e[1]=='0' && *a=='~') && !(e[1]=='1' && *a=='/')) return 1; else e++;}
else if (tolower(*(const unsigned char *)a)!=tolower(*(const unsigned char *)e)) return 1;
}
if ((*e!=0 && *e!='/') != (*a!=0)) return 1;
return 0;
}
static int cJSONUtils_PointerEncodedstrlen(const char *s) {int l=0;for (;*s;s++,l++) if (*s=='~' || *s=='/') l++;return l;}
static void cJSONUtils_PointerEncodedstrcpy(char *d,const char *s)
{
for (;*s;s++)
{
if (*s=='/') {*d++='~';*d++='1';}
else if (*s=='~') {*d++='~';*d++='0';}
else *d++=*s;
}
*d=0;
}
char *cJSONUtils_FindPointerFromObjectTo(cJSON *object,cJSON *target)
{
int type=object->type,c=0;cJSON *obj=0;
if (object==target) return strdup("");
for (obj=object->child;obj;obj=obj->next,c++)
{
char *found=cJSONUtils_FindPointerFromObjectTo(obj,target);
if (found)
{
if (type==cJSON_Array)
{
char *ret=(char*)malloc(strlen(found)+23);
sprintf(ret,"/%d%s",c,found);
free(found);
return ret;
}
else if (type==cJSON_Object)
{
char *ret=(char*)malloc(strlen(found)+cJSONUtils_PointerEncodedstrlen(obj->string)+2);
*ret='/';cJSONUtils_PointerEncodedstrcpy(ret+1,obj->string);
strcat(ret,found);
free(found);
return ret;
}
free(found);
return 0;
}
}
return 0;
}
cJSON *cJSONUtils_GetPointer(cJSON *object,const char *pointer)
{
while (*pointer++=='/' && object)
{
if (object->type==cJSON_Array)
{
int which=0; while (*pointer>='0' && *pointer<='9') which=(10*which) + *pointer++ - '0';
if (*pointer && *pointer!='/') return 0;
object=cJSON_GetArrayItem(object,which);
}
else if (object->type==cJSON_Object)
{
object=object->child; while (object && cJSONUtils_Pstrcasecmp(object->string,pointer)) object=object->next; /* GetObjectItem. */
while (*pointer && *pointer!='/') pointer++;
}
else return 0;
}
return object;
}
/* JSON Patch implementation. */
static void cJSONUtils_InplaceDecodePointerString(char *string)
{
char *s2=string;
for (;*string;s2++,string++) *s2=(*string!='~')?(*string):((*(++string)=='0')?'~':'/');
*s2=0;
}
static cJSON *cJSONUtils_PatchDetach(cJSON *object,const char *path)
{
char *parentptr=0,*childptr=0;cJSON *parent=0,*ret=0;
parentptr=strdup(path); childptr=strrchr(parentptr,'/'); if (childptr) *childptr++=0;
parent=cJSONUtils_GetPointer(object,parentptr);
cJSONUtils_InplaceDecodePointerString(childptr);
if (!parent) ret=0; /* Couldn't find object to remove child from. */
else if (parent->type==cJSON_Array) ret=cJSON_DetachItemFromArray(parent,atoi(childptr));
else if (parent->type==cJSON_Object) ret=cJSON_DetachItemFromObject(parent,childptr);
free(parentptr);
return ret;
}
static int cJSONUtils_Compare(cJSON *a,cJSON *b)
{
if (a->type!=b->type) return -1; /* mismatched type. */
switch (a->type)
{
case cJSON_Number: return (a->valueint!=b->valueint || a->valuedouble!=b->valuedouble)?-2:0; /* numeric mismatch. */
case cJSON_String: return (strcmp(a->valuestring,b->valuestring)!=0)?-3:0; /* string mismatch. */
case cJSON_Array: for (a=a->child,b=b->child;a && b;a=a->next,b=b->next) {int err=cJSONUtils_Compare(a,b);if (err) return err;}
return (a || b)?-4:0; /* array size mismatch. */
case cJSON_Object:
cJSONUtils_SortObject(a);
cJSONUtils_SortObject(b);
a=a->child,b=b->child;
while (a && b)
{
int err;
if (cJSONUtils_strcasecmp(a->string,b->string)) return -6; /* missing member */
err=cJSONUtils_Compare(a,b);if (err) return err;
a=a->next,b=b->next;
}
return (a || b)?-5:0; /* object length mismatch */
default: break;
}
return 0;
}
static int cJSONUtils_ApplyPatch(cJSON *object,cJSON *patch)
{
cJSON *op=0,*path=0,*value=0,*parent=0;int opcode=0;char *parentptr=0,*childptr=0;
op=cJSON_GetObjectItem(patch,"op");
path=cJSON_GetObjectItem(patch,"path");
if (!op || !path) return 2; /* malformed patch. */
if (!strcmp(op->valuestring,"add")) opcode=0;
else if (!strcmp(op->valuestring,"remove")) opcode=1;
else if (!strcmp(op->valuestring,"replace"))opcode=2;
else if (!strcmp(op->valuestring,"move")) opcode=3;
else if (!strcmp(op->valuestring,"copy")) opcode=4;
else if (!strcmp(op->valuestring,"test")) return cJSONUtils_Compare(cJSONUtils_GetPointer(object,path->valuestring),cJSON_GetObjectItem(patch,"value"));
else return 3; /* unknown opcode. */
if (opcode==1 || opcode==2) /* Remove/Replace */
{
cJSON_Delete(cJSONUtils_PatchDetach(object,path->valuestring)); /* Get rid of old. */
if (opcode==1) return 0; /* For Remove, this is job done. */
}
if (opcode==3 || opcode==4) /* Copy/Move uses "from". */
{
cJSON *from=cJSON_GetObjectItem(patch,"from"); if (!from) return 4; /* missing "from" for copy/move. */
if (opcode==3) value=cJSONUtils_PatchDetach(object,from->valuestring);
if (opcode==4) value=cJSONUtils_GetPointer(object,from->valuestring);
if (!value) return 5; /* missing "from" for copy/move. */
if (opcode==4) value=cJSON_Duplicate(value,1);
if (!value) return 6; /* out of memory for copy/move. */
}
else /* Add/Replace uses "value". */
{
value=cJSON_GetObjectItem(patch,"value");
if (!value) return 7; /* missing "value" for add/replace. */
value=cJSON_Duplicate(value,1);
if (!value) return 8; /* out of memory for add/replace. */
}
/* Now, just add "value" to "path". */
parentptr=strdup(path->valuestring); childptr=strrchr(parentptr,'/'); if (childptr) *childptr++=0;
parent=cJSONUtils_GetPointer(object,parentptr);
cJSONUtils_InplaceDecodePointerString(childptr);
/* add, remove, replace, move, copy, test. */
if (!parent) {free(parentptr); cJSON_Delete(value); return 9;} /* Couldn't find object to add to. */
else if (parent->type==cJSON_Array)
{
if (!strcmp(childptr,"-")) cJSON_AddItemToArray(parent,value);
else cJSON_InsertItemInArray(parent,atoi(childptr),value);
}
else if (parent->type==cJSON_Object)
{
cJSON_DeleteItemFromObject(parent,childptr);
cJSON_AddItemToObject(parent,childptr,value);
}
else
{
cJSON_Delete(value);
}
free(parentptr);
return 0;
}
int cJSONUtils_ApplyPatches(cJSON *object,cJSON *patches)
{
int err;
if (patches->type!=cJSON_Array) return 1; /* malformed patches. */
if (patches) patches=patches->child;
while (patches)
{
if ((err=cJSONUtils_ApplyPatch(object,patches))) return err;
patches=patches->next;
}
return 0;
}
static void cJSONUtils_GeneratePatch(cJSON *patches,const char *op,const char *path,const char *suffix,cJSON *val)
{
cJSON *patch=cJSON_CreateObject();
cJSON_AddItemToObject(patch,"op",cJSON_CreateString(op));
if (suffix)
{
char *newpath=(char*)malloc(strlen(path)+cJSONUtils_PointerEncodedstrlen(suffix)+2);
cJSONUtils_PointerEncodedstrcpy(newpath+sprintf(newpath,"%s/",path),suffix);
cJSON_AddItemToObject(patch,"path",cJSON_CreateString(newpath));
free(newpath);
}
else cJSON_AddItemToObject(patch,"path",cJSON_CreateString(path));
if (val) cJSON_AddItemToObject(patch,"value",cJSON_Duplicate(val,1));
cJSON_AddItemToArray(patches,patch);
}
void cJSONUtils_AddPatchToArray(cJSON *array,const char *op,const char *path,cJSON *val) {cJSONUtils_GeneratePatch(array,op,path,0,val);}
static void cJSONUtils_CompareToPatch(cJSON *patches,const char *path,cJSON *from,cJSON *to)
{
if (from->type!=to->type) {cJSONUtils_GeneratePatch(patches,"replace",path,0,to); return; }
switch (from->type)
{
case cJSON_Number:
if (from->valueint!=to->valueint || from->valuedouble!=to->valuedouble)
cJSONUtils_GeneratePatch(patches,"replace",path,0,to);
return;
case cJSON_String:
if (strcmp(from->valuestring,to->valuestring)!=0)
cJSONUtils_GeneratePatch(patches,"replace",path,0,to);
return;
case cJSON_Array:
{
int c;char *newpath=(char*)malloc(strlen(path)+23); /* Allow space for 64bit int. */
for (c=0,from=from->child,to=to->child;from && to;from=from->next,to=to->next,c++){
sprintf(newpath,"%s/%d",path,c); cJSONUtils_CompareToPatch(patches,newpath,from,to);
}
for (;from;from=from->next,c++) {sprintf(newpath,"%d",c); cJSONUtils_GeneratePatch(patches,"remove",path,newpath,0); }
for (;to;to=to->next,c++) cJSONUtils_GeneratePatch(patches,"add",path,"-",to);
free(newpath);
return;
}
case cJSON_Object:
{
cJSON *a,*b;
cJSONUtils_SortObject(from);
cJSONUtils_SortObject(to);
a=from->child,b=to->child;
while (a || b)
{
int diff=(!a)?1:(!b)?-1:cJSONUtils_strcasecmp(a->string,b->string);
if (!diff)
{
char *newpath=(char*)malloc(strlen(path)+cJSONUtils_PointerEncodedstrlen(a->string)+2);
cJSONUtils_PointerEncodedstrcpy(newpath+sprintf(newpath,"%s/",path),a->string);
cJSONUtils_CompareToPatch(patches,newpath,a,b);
free(newpath);
a=a->next;
b=b->next;
}
else if (diff<0) {cJSONUtils_GeneratePatch(patches,"remove",path,a->string,0); a=a->next;}
else {cJSONUtils_GeneratePatch(patches,"add",path,b->string,b); b=b->next;}
}
return;
}
default: break;
}
}
cJSON* cJSONUtils_GeneratePatches(cJSON *from,cJSON *to)
{
cJSON *patches=cJSON_CreateArray();
cJSONUtils_CompareToPatch(patches,"",from,to);
return patches;
}
static cJSON *cJSONUtils_SortList(cJSON *list)
{
cJSON *first=list,*second=list,*ptr=list;
if (!list || !list->next) return list; /* One entry is sorted already. */
while (ptr && ptr->next && cJSONUtils_strcasecmp(ptr->string,ptr->next->string)<0) ptr=ptr->next; /* Test for list sorted. */
if (!ptr || !ptr->next) return list; /* Leave sorted lists unmodified. */
ptr=list;
while (ptr) {second=second->next;ptr=ptr->next;if (ptr) ptr=ptr->next;} /* Walk two pointers to find the middle. */
if (second && second->prev) second->prev->next=0; /* Split the lists */
first=cJSONUtils_SortList(first); /* Recursively sort the sub-lists. */
second=cJSONUtils_SortList(second);
list=ptr=0;
while (first && second) /* Merge the sub-lists */
{
if (cJSONUtils_strcasecmp(first->string,second->string)<0)
{
if (!list) list=ptr=first;
else {ptr->next=first;first->prev=ptr;ptr=first;}
first=first->next;
}
else
{
if (!list) list=ptr=second;
else {ptr->next=second;second->prev=ptr;ptr=second;}
second=second->next;
}
}
if (first) { if (!list) return first; ptr->next=first; first->prev=ptr; } /* Append any tails. */
if (second) { if (!list) return second; ptr->next=second; second->prev=ptr; }
return list;
}
void cJSONUtils_SortObject(cJSON *object) {object->child=cJSONUtils_SortList(object->child);}
cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch)
{
if (!patch || patch->type != cJSON_Object) {cJSON_Delete(target);return cJSON_Duplicate(patch,1);}
if (!target || target->type != cJSON_Object) {cJSON_Delete(target);target=cJSON_CreateObject();}
patch=patch->child;
while (patch)
{
if (patch->type == cJSON_NULL) cJSON_DeleteItemFromObject(target,patch->string);
else
{
cJSON *replaceme=cJSON_DetachItemFromObject(target,patch->string);
cJSON_AddItemToObject(target,patch->string,cJSONUtils_MergePatch(replaceme,patch));
}
patch=patch->next;
}
return target;
}
cJSON *cJSONUtils_GenerateMergePatch(cJSON *from,cJSON *to)
{
cJSON *patch=0;
if (!to) return cJSON_CreateNull();
if (to->type!=cJSON_Object || !from || from->type!=cJSON_Object) return cJSON_Duplicate(to,1);
cJSONUtils_SortObject(from);
cJSONUtils_SortObject(to);
from=from->child;to=to->child;
patch=cJSON_CreateObject();
while (from || to)
{
int compare=from?(to?strcmp(from->string,to->string):-1):1;
if (compare<0)
{
cJSON_AddItemToObject(patch,from->string,cJSON_CreateNull());
from=from->next;
}
else if (compare>0)
{
cJSON_AddItemToObject(patch,to->string,cJSON_Duplicate(to,1));
to=to->next;
}
else
{
if (cJSONUtils_Compare(from,to)) cJSON_AddItemToObject(patch,to->string,cJSONUtils_GenerateMergePatch(from,to));
from=from->next;to=to->next;
}
}
if (!patch->child) {cJSON_Delete(patch);return 0;}
return patch;
}

View file

@ -0,0 +1,30 @@
#include "cJSON.h"
/* Implement RFC6901 (https://tools.ietf.org/html/rfc6901) JSON Pointer spec. */
cJSON *cJSONUtils_GetPointer(cJSON *object,const char *pointer);
/* Implement RFC6902 (https://tools.ietf.org/html/rfc6902) JSON Patch spec. */
cJSON* cJSONUtils_GeneratePatches(cJSON *from,cJSON *to);
void cJSONUtils_AddPatchToArray(cJSON *array,const char *op,const char *path,cJSON *val); /* Utility for generating patch array entries. */
int cJSONUtils_ApplyPatches(cJSON *object,cJSON *patches); /* Returns 0 for success. */
/*
// Note that ApplyPatches is NOT atomic on failure. To implement an atomic ApplyPatches, use:
//int cJSONUtils_AtomicApplyPatches(cJSON **object, cJSON *patches)
//{
// cJSON *modme=cJSON_Duplicate(*object,1);
// int error=cJSONUtils_ApplyPatches(modme,patches);
// if (!error) {cJSON_Delete(*object);*object=modme;}
// else cJSON_Delete(modme);
// return error;
//}
// Code not added to library since this strategy is a LOT slower.
*/
/* Implement RFC7386 (https://tools.ietf.org/html/rfc7396) JSON Merge Patch spec. */
cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch); /* target will be modified by patch. return value is new ptr for target. */
cJSON *cJSONUtils_GenerateMergePatch(cJSON *from,cJSON *to); /* generates a patch to move from -> to */
char *cJSONUtils_FindPointerFromObjectTo(cJSON *object,cJSON *target); /* Given a root object and a target object, construct a pointer from one to the other. */
void cJSONUtils_SortObject(cJSON *object); /* Sorts the members of the object into alphabetical order. */

View file

@ -1,11 +0,0 @@
#
# Component Makefile
#
COMPONENT_ADD_INCLUDEDIRS := include/lwip include/lwip/port
COMPONENT_SRCDIRS := api apps core/ipv4 core/ipv6 core netif port/freertos port/netif port
EXTRA_CFLAGS := -Wno-error=address -Waddress -DLWIP_ESP8266
include $(IDF_PATH)/make/component.mk

View file

@ -405,7 +405,7 @@ do{\
/** The global array of available sockets */
static struct lwip_sock sockets[NUM_SOCKETS];
#ifdef LWIP_THREAD_SAFE
#if LWIP_THREAD_SAFE
static bool sockets_init_flag = false;
#endif
/** The global list of tasks waiting for select */

View file

@ -62,7 +62,7 @@ static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__;
/* global variables */
#ifdef PERF
uint32 g_rx_post_mbox_fail_cnt = 0;
uint32_t g_rx_post_mbox_fail_cnt = 0;
#endif
static tcpip_init_done_fn tcpip_init_done;
static void *tcpip_init_done_arg;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,716 @@
/**
* @file
* SNTP client module
*
* This is simple "SNTP" client for the lwIP 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?
*/
/*
* Copyright (c) 2007-2009 Frédéric Bernon, Simon Goldschmidt
* 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.
* 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: Frédéric Bernon, Simon Goldschmidt
*/
#include "apps/sntp/sntp.h"
#include "lwip/opt.h"
#include "lwip/timers.h"
#include "lwip/udp.h"
#include "lwip/dns.h"
#include "lwip/ip_addr.h"
#include "lwip/pbuf.h"
#include "lwip/dhcp.h"
#include <string.h>
#include <time.h>
#if LWIP_UDP
/* Handle support for more than one server via SNTP_MAX_SERVERS */
#if SNTP_MAX_SERVERS > 1
#define SNTP_SUPPORT_MULTIPLE_SERVERS 1
#else /* NTP_MAX_SERVERS > 1 */
#define SNTP_SUPPORT_MULTIPLE_SERVERS 0
#endif /* NTP_MAX_SERVERS > 1 */
#if (SNTP_UPDATE_DELAY < 15000) && !defined(SNTP_SUPPRESS_DELAY_CHECK)
#error "SNTPv4 RFC 4330 enforces a minimum update time of 15 seconds (define SNTP_SUPPRESS_DELAY_CHECK to disable this error)!"
#endif
/* Configure behaviour depending on microsecond or second precision */
#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
/* 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 (MSB=1)*/
#define DIFF_SEC_1900_1970 (2208988800UL)
/* number of seconds between 1970 and Feb 7, 2036 (6:28:16 UTC) (MSB=0) */
#define DIFF_SEC_1970_2036 (2085978496UL)
/**
* 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_FLD_8(u8_t li_vn_mode);
PACK_STRUCT_FLD_8(u8_t stratum);
PACK_STRUCT_FLD_8(u8_t poll);
PACK_STRUCT_FLD_8(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 operating mode */
static u8_t sntp_opmode;
/** The UDP pcb used by the SNTP client */
static struct udp_pcb* sntp_pcb;
/** Names/Addresses of servers */
struct sntp_server {
#if SNTP_SERVER_DNS
char* name;
#endif /* SNTP_SERVER_DNS */
ip_addr_t addr;
};
static struct sntp_server sntp_servers[SNTP_MAX_SERVERS];
#if SNTP_GET_SERVERS_FROM_DHCP
static u8_t sntp_set_servers_from_dhcp;
#endif /* SNTP_GET_SERVERS_FROM_DHCP */
#if SNTP_SUPPORT_MULTIPLE_SERVERS
/** The currently used server (initialized to 0) */
static u8_t sntp_current_server;
#else /* SNTP_SUPPORT_MULTIPLE_SERVERS */
#define sntp_current_server 0
#endif /* SNTP_SUPPORT_MULTIPLE_SERVERS */
#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)
* if MSB is 0, SNTP time is 2036-based!
*/
u32_t rx_secs = ntohl(receive_timestamp[0]);
int is_1900_based = ((rx_secs & 0x80000000) != 0);
u32_t t = is_1900_based ? (rx_secs - DIFF_SEC_1900_1970) : (rx_secs + DIFF_SEC_1970_2036);
time_t tim = t;
#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(&tim), 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(&tim)));
#endif /* SNTP_CALC_TIME_US */
LWIP_UNUSED_ARG(tim);
}
/**
* 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 */
}
/**
* 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_SUPPORT_MULTIPLE_SERVERS
/**
* 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.
* (implicitly, SNTP_MAX_SERVERS > 1)
*
* @param arg is unused (only necessary to conform to sys_timeout)
*/
static void
sntp_try_next_server(void* arg)
{
u8_t old_server, i;
LWIP_UNUSED_ARG(arg);
old_server = sntp_current_server;
for (i = 0; i < SNTP_MAX_SERVERS - 1; i++) {
sntp_current_server++;
if (sntp_current_server >= SNTP_MAX_SERVERS) {
sntp_current_server = 0;
}
if (!ip_addr_isany(&sntp_servers[sntp_current_server].addr)
#if SNTP_SERVER_DNS
|| (sntp_servers[sntp_current_server].name != NULL)
#endif
) {
LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_try_next_server: Sending request to server %"U16_F"\n",
(u16_t)sntp_current_server));
/* new server: reset retry timeout */
SNTP_RESET_RETRY_TIMEOUT();
/* instantly send a request to the next server */
sntp_request(NULL);
return;
}
}
/* no other valid server found */
sntp_current_server = old_server;
sntp_retry(NULL);
}
#else /* SNTP_SUPPORT_MULTIPLE_SERVERS */
/* Always retry on error if only one server is supported */
#define sntp_try_next_server sntp_retry
#endif /* SNTP_SUPPORT_MULTIPLE_SERVERS */
/** UDP recv callback for the sntp pcb */
static void
sntp_recv(void *arg, struct udp_pcb* pcb, struct pbuf *p, const 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 (((sntp_opmode != SNTP_OPMODE_POLL) || 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 (((sntp_opmode == SNTP_OPMODE_POLL) && (mode == SNTP_MODE_SERVER)) ||
((sntp_opmode == SNTP_OPMODE_LISTENONLY) && (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_TRANSMIT_TIME);
}
}
} else {
LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid mode in response: %"U16_F"\n", (u16_t)mode));
/* wait for correct response */
err = ERR_TIMEOUT;
}
} else {
LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid packet length: %"U16_F"\n", p->tot_len));
}
}
#if SNTP_CHECK_RESPONSE >= 1
else {
/* packet from wrong remote address or port, wait for correct response */
err = ERR_TIMEOUT;
}
#endif /* SNTP_CHECK_RESPONSE >= 1 */
pbuf_free(p);
if (err == ERR_OK) {
sntp_process(receive_timestamp);
/* Set up timeout for next request (only if poll response was received)*/
if (sntp_opmode == SNTP_OPMODE_POLL) {
/* Correct response, reset retry timeout */
SNTP_RESET_RETRY_TIMEOUT();
sys_timeout((u32_t)SNTP_UPDATE_DELAY, sntp_request, NULL);
LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Scheduled next time request: %"U32_F" ms\n",
(u32_t)SNTP_UPDATE_DELAY));
}
} else if (err != ERR_TIMEOUT) {
/* Errors are only processed in case of an explicit poll response */
if (sntp_opmode == SNTP_OPMODE_POLL) {
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(const 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);
/* free the pbuf after sending it */
pbuf_free(p);
/* 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, const 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.
*
* @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
if (sntp_servers[sntp_current_server].name) {
/* always resolve the name and rely on dns-internal caching & timeout */
ip_addr_set_zero(&sntp_servers[sntp_current_server].addr);
err = dns_gethostbyname(sntp_servers[sntp_current_server].name, &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 if (err == ERR_OK) {
sntp_servers[sntp_current_server].addr = sntp_server_address;
}
} else
#endif /* SNTP_SERVER_DNS */
{
sntp_server_address = sntp_servers[sntp_current_server].addr;
err = (ip_addr_isany_val(sntp_server_address)) ? ERR_ARG : ERR_OK;
}
if (err == ERR_OK) {
LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_request: current server address is %s\n",
ipaddr_ntoa(&sntp_server_address)));
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.
* Send out request instantly or after SNTP_STARTUP_DELAY(_FUNC).
*/
void
sntp_init(void)
{
#ifdef SNTP_SERVER_ADDRESS
#if SNTP_SERVER_DNS
sntp_setservername(0, SNTP_SERVER_ADDRESS);
#else
#error SNTP_SERVER_ADDRESS string not supported SNTP_SERVER_DNS==0
#endif
#endif /* SNTP_SERVER_ADDRESS */
if (sntp_pcb == NULL) {
sntp_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
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_opmode == SNTP_OPMODE_POLL) {
SNTP_RESET_RETRY_TIMEOUT();
#if SNTP_STARTUP_DELAY
sys_timeout((u32_t)SNTP_STARTUP_DELAY_FUNC, sntp_request, NULL);
#else
sntp_request(NULL);
#endif
} else if (sntp_opmode == SNTP_OPMODE_LISTENONLY) {
ip_set_option(sntp_pcb, SOF_BROADCAST);
udp_bind(sntp_pcb, IP_ANY_TYPE, SNTP_PORT);
}
}
}
}
/**
* Stop this module.
*/
void
sntp_stop(void)
{
if (sntp_pcb != NULL) {
sys_untimeout(sntp_request, NULL);
udp_remove(sntp_pcb);
sntp_pcb = NULL;
}
}
/**
* Get enabled state.
*/
u8_t sntp_enabled(void)
{
return (sntp_pcb != NULL)? 1 : 0;
}
/**
* Sets the operating mode.
* @param operating_mode one of the available operating modes
*/
void
sntp_setoperatingmode(u8_t operating_mode)
{
LWIP_ASSERT("Invalid operating mode", operating_mode <= SNTP_OPMODE_LISTENONLY);
LWIP_ASSERT("Operating mode must not be set while SNTP client is running", sntp_pcb == NULL);
sntp_opmode = operating_mode;
}
/**
* Gets the operating mode.
*/
u8_t
sntp_getoperatingmode(void)
{
return sntp_opmode;
}
#if SNTP_GET_SERVERS_FROM_DHCP
/**
* Config SNTP server handling by IP address, name, or DHCP; clear table
* @param set_servers_from_dhcp enable or disable getting server addresses from dhcp
*/
void
sntp_servermode_dhcp(int set_servers_from_dhcp)
{
u8_t new_mode = set_servers_from_dhcp ? 1 : 0;
if (sntp_set_servers_from_dhcp != new_mode) {
sntp_set_servers_from_dhcp = new_mode;
}
}
#endif /* SNTP_GET_SERVERS_FROM_DHCP */
/**
* Initialize one of the NTP servers by IP address
*
* @param numdns the index of the NTP server to set must be < SNTP_MAX_SERVERS
* @param dnsserver IP address of the NTP server to set
*/
void
sntp_setserver(u8_t idx, const ip_addr_t *server)
{
if (idx < SNTP_MAX_SERVERS) {
if (server != NULL) {
sntp_servers[idx].addr = (*server);
} else {
ip_addr_set_zero(&sntp_servers[idx].addr);
}
#if SNTP_SERVER_DNS
sntp_servers[idx].name = NULL;
#endif
}
}
#if LWIP_DHCP && SNTP_GET_SERVERS_FROM_DHCP
/**
* Initialize one of the NTP servers by IP address, required by DHCP
*
* @param numdns the index of the NTP server to set must be < SNTP_MAX_SERVERS
* @param dnsserver IP address of the NTP server to set
*/
void
dhcp_set_ntp_servers(u8_t num, const ip4_addr_t *server)
{
LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp: %s %u.%u.%u.%u as NTP server #%u via DHCP\n",
(sntp_set_servers_from_dhcp ? "Got" : "Rejected"),
ip4_addr1(server), ip4_addr2(server), ip4_addr3(server), ip4_addr4(server), num));
if (sntp_set_servers_from_dhcp && num) {
u8_t i;
for (i = 0; (i < num) && (i < SNTP_MAX_SERVERS); i++) {
ip_addr_t addr;
ip_addr_copy_from_ip4(addr, server[i]);
sntp_setserver(i, &addr);
}
for (i = num; i < SNTP_MAX_SERVERS; i++) {
sntp_setserver(i, NULL);
}
}
}
#endif /* LWIP_DHCP && SNTP_GET_SERVERS_FROM_DHCP */
/**
* Obtain one of the currently configured by IP address (or DHCP) NTP servers
*
* @param numdns the index of the NTP server
* @return IP address of the indexed NTP server or "ip_addr_any" if the NTP
* server has not been configured by address (or at all).
*/
ip_addr_t
sntp_getserver(u8_t idx)
{
if (idx < SNTP_MAX_SERVERS) {
return sntp_servers[idx].addr;
}
return *IP_ADDR_ANY;
}
#if SNTP_SERVER_DNS
/**
* Initialize one of the NTP servers by name
*
* @param numdns the index of the NTP server to set must be < SNTP_MAX_SERVERS
* @param dnsserver DNS name of the NTP server to set, to be resolved at contact time
*/
void
sntp_setservername(u8_t idx, char *server)
{
if (idx < SNTP_MAX_SERVERS) {
sntp_servers[idx].name = server;
}
}
/**
* Obtain one of the currently configured by name NTP servers.
*
* @param numdns the index of the NTP server
* @return IP address of the indexed NTP server or NULL if the NTP
* server has not been configured by name (or at all)
*/
char *
sntp_getservername(u8_t idx)
{
if (idx < SNTP_MAX_SERVERS) {
return sntp_servers[idx].name;
}
return NULL;
}
#endif /* SNTP_SERVER_DNS */
#endif /* LWIP_UDP */

View file

@ -0,0 +1,11 @@
#
# Component Makefile
#
COMPONENT_ADD_INCLUDEDIRS := include/lwip include/lwip/port
COMPONENT_SRCDIRS := api apps/sntp apps core/ipv4 core/ipv6 core netif port/freertos port/netif port
EXTRA_CFLAGS := -Wno-error=address -Waddress
include $(IDF_PATH)/make/component_common.mk

View file

@ -459,7 +459,7 @@ dhcp_fine_tmr(void)
#if 0
if (DHCP_MAXRTX != 0) {
if (netif->dhcp->tries >= DHCP_MAXRTX){
//os_printf("DHCP timeout\n");
//printf("DHCP timeout\n");
if (netif->dhcp_event != NULL)
netif->dhcp_event();
break;
@ -710,6 +710,24 @@ void dhcp_cleanup(struct netif *netif)
}
}
/* Espressif add start. */
/** Set callback for dhcp, reversed parameter for future use.
*
* @param netif the netif from which to remove the struct dhcp
* @param cb callback for chcp
*/
void dhcp_set_cb(struct netif *netif, void (*cb)(void))
{
LWIP_ASSERT("netif != NULL", netif != NULL);
if (netif->dhcp != NULL) {
netif->dhcp->cb = cb;
}
}
/* Espressif add end. */
/**
* Start DHCP negotiation for a network interface.
*
@ -1117,32 +1135,19 @@ dhcp_bind(struct netif *netif)
}
#endif /* LWIP_DHCP_AUTOIP_COOP */
/* Espressif add start. */
/* back up old ip/netmask/gw */
ip4_addr_t ip, mask, gw;
ip4_addr_set(&ip, ip_2_ip4(&netif->ip_addr));
ip4_addr_set(&mask, ip_2_ip4(&netif->netmask));
ip4_addr_set(&gw, ip_2_ip4(&netif->gw));
/* Espressif add end. */
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): IP: 0x%08"X32_F" SN: 0x%08"X32_F" GW: 0x%08"X32_F"\n",
ip4_addr_get_u32(&dhcp->offered_ip_addr), ip4_addr_get_u32(&sn_mask), ip4_addr_get_u32(&gw_addr)));
netif_set_addr(netif, &dhcp->offered_ip_addr, &sn_mask, &gw_addr);
/* interface is used by routing now that an address is set */
#ifdef LWIP_ESP8266
/* use old ip/mask/gw to check whether ip/mask/gw changed */
// extern void system_station_got_ip_set(ip4_addr_t *ip, ip4_addr_t *mask, ip4_addr_t *gw);
// system_station_got_ip_set(&ip, &mask, &gw);
#endif
/* use old ip/mask/gw to check whether ip/mask/gw changed */
// extern void system_station_got_ip_set(ip4_addr_t *ip, ip4_addr_t *mask, ip4_addr_t *gw);
// system_station_got_ip_set(&ip, &mask, &gw);
/* netif is now bound to DHCP leased address */
dhcp_set_state(dhcp, DHCP_STATE_BOUND);
/* Espressif add start. */
if (dhcp->cb != NULL) {
dhcp->cb();
}
/* Espressif add end. */
}
/**
@ -1539,7 +1544,7 @@ again:
break;
case(DHCP_OPTION_DNS_SERVER):
/* special case: there might be more than one server */
LWIP_ERROR("len % 4 == 0", len % 4 == 0, return ERR_VAL;);
//LWIP_ERROR("len % 4 == 0", len % 4 == 0, return ERR_VAL;);
/* limit number of DNS servers */
decode_len = LWIP_MIN(len, 4 * DNS_MAX_SERVERS);
LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;);
@ -1595,7 +1600,7 @@ decode_next:
pbuf_copy_partial(q, &value, copy_len, val_offset);
if (decode_len > 4) {
/* decode more than one u32_t */
LWIP_ERROR("decode_len % 4 == 0", decode_len % 4 == 0, return ERR_VAL;);
//LWIP_ERROR("decode_len % 4 == 0", decode_len % 4 == 0, return ERR_VAL;);
dhcp_got_option(dhcp, decode_idx);
dhcp_set_option_value(dhcp, decode_idx, htonl(value));
decode_len -= 4;

View file

@ -315,14 +315,6 @@ netif_add(struct netif *netif,
return netif;
}
typedef int (*netif_addr_change_cb_t)(struct netif *netif);
static netif_addr_change_cb_t g_netif_addr_change_cb = NULL;
void netif_reg_addr_change_cb(void *cb)
{
g_netif_addr_change_cb = (netif_addr_change_cb_t)cb;
}
#if LWIP_IPV4
/**
* Change IP address configuration for a network interface (including netmask
@ -341,9 +333,6 @@ netif_set_addr(struct netif *netif, const ip4_addr_t *ipaddr, const ip4_addr_t *
netif_set_gw(netif, gw);
/* set ipaddr last to ensure netmask/gw have been set when status callback is called */
netif_set_ipaddr(netif, ipaddr);
if (g_netif_addr_change_cb){
g_netif_addr_change_cb(netif);
}
}
#endif /* LWIP_IPV4*/

View file

@ -17,17 +17,6 @@
#include "lwip/ip_addr.h"
//#include "esp_common.h"
#define USE_DNS
/* Here for now until needed in other places in lwIP */
#ifndef isprint
#define in_range(c, lo, up) ((u8_t)c >= lo && (u8_t)c <= up)
#define isprint(c) in_range(c, 0x20, 0x7f)
#define isdigit(c) in_range(c, '0', '9')
#define isxdigit(c) (isdigit(c) || in_range(c, 'a', 'f') || in_range(c, 'A', 'F'))
#define islower(c) in_range(c, 'a', 'z')
#define isspace(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v')
#endif
#ifdef LWIP_ESP8266
typedef struct dhcps_state{
s16_t state;
@ -47,220 +36,6 @@ typedef struct dhcps_msg {
u8_t options[312];
}dhcps_msg;
struct dhcps_lease {
bool enable;
ip4_addr_t start_ip;
ip4_addr_t end_ip;
};
enum dhcps_offer_option{
OFFER_START = 0x00,
OFFER_ROUTER = 0x01,
OFFER_END
};
struct dhcps_pool{
ip4_addr_t ip;
u8_t mac[6];
u32_t lease_timer;
};
typedef struct _list_node{
void *pnode;
struct _list_node *pnext;
}list_node;
extern u32_t dhcps_lease_time;
#define DHCPS_LEASE_TIMER dhcps_lease_time //0x05A0
#define dhcps_router_enabled(offer) ((offer & OFFER_ROUTER) != 0)
void dhcps_start(struct netif *netif);
void dhcps_stop(struct netif *netif);
#else
#include "lwip/opt.h"
#include "lwip/netif.h"
#include "lwip/udp.h"
/** DHCP DEBUG INFO **/
#define DHCPS_DEBUG 1
#if DHCPS_DEBUG
#define log_info(message, ...) do { \
os_printf((message), ##__VA_ARGS__); \
os_printf("\n"); \
} while(0);
#else
#define log_info(message, ...)
#endif
#if (!defined(unlikely))
#define unlikely(Expression) !!(Expression)
#endif
#define REQUIRE_ASSERT(Expression) do{if (!(Expression)) log_info("%d\n", __LINE__);}while(0)
#define REQUIRE_ACTION(Expression,Label,Action) \
do{\
if (unlikely(!(Expression))) \
{\
log_info("%d\n", __LINE__);\
{Action;}\
goto Label;\
}\
}while(0)
#define REQUIRE_NOERROR(Expression,Label) \
do{\
int LocalError;\
LocalError = (int)Expression;\
if (unlikely(LocalError != 0)) \
{\
log_info("%d 0x%x\n", __LINE__, LocalError);\
goto Label;\
}\
}while(0)
#define REQUIRE_NOERROR_ACTION(Expression,Label,Action) \
do{\
int LocalError;\
LocalError = (int)Expression;\
if (unlikely(LocalError != 0)) \
{\
log_info("%d\n", __LINE__);\
{Action;}\
goto Label;\
}\
}while(0)
#define DHCP_OK 0
#define DHCP_FAIL -1
#define DHCP_SERVER_OPENDNS 0xd043dede /* OpenDNS DNS server 208.67.222.222 */
/* DHCP message */
/*
* Code ID of DHCP and BOOTP options
* as defined in RFC 2132
*/
typedef enum
{
DHCP_NONE = 0,
DHCPS_DISCOVER = 1,
DHCPS_OFFER = 2,
DHCPS_REQUEST = 3,
DHCPS_DECLINE = 4,
DHCPS_ACK = 5,
DHCPS_NAK = 6,
DHCPS_RELEASE = 7,
DHCPS_INFORM = 8,
DHCP_FORCE_RENEW = 9,
DHCP_LEASE_QUERY = 10,
DHCP_LEASE_UNASSIGNED = 11,
DHCP_LEASE_UNKNOWN = 12,
DHCP_LEASE_ACTIVE = 13,
} dhcp_msg_type;
typedef enum
{
BOOTPS = 67,
BOOTPC = 68
} ports;
typedef enum
{
BOOTREQUEST = 1,
BOOTREPLY = 2,
} op_types;
typedef enum
{
ETHERNET = 0x01,
ETHERNET_LAN = 0x06,
ETHEFDDI = 0x08,
ETHEIEEE1394 = 0x18
} hardware_types;
typedef enum
{
DHCPS_CHADDR_LEN = 16U,
DHCPS_SNAME_LEN = 64U,
DHCPS_FILE_LEN = 128U,
DHCP_HEADER_SIZE = 236U // without size of options
} head_size;
typedef struct dhcps
{
/** transaction identifier of last sent request */
u32_t xid;
/** incoming msg */
struct dhcps_msg *msg_in;
/** track PCB allocation state */
u8_t pcb_allocated;
/** current DHCP state machine state */
u8_t state;
/** retries of current request */
u8_t tries;
#if LWIP_DHCP_AUTOIP_COOP
u8_t autoip_coop_state;
#endif
u8_t subnet_mask_given;
struct pbuf *p_out; /* pbuf of outcoming msg */
struct dhcp_msg *msg_out; /* outgoing msg */
u16_t options_out_len; /* outgoing msg options length */
u16_t request_timeout; /* #ticks with period DHCP_FINE_TIMER_SECS for request timeout */
u16_t t1_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for renewal time */
u16_t t2_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for rebind time */
u16_t t1_renew_time; /* #ticks with period DHCP_COARSE_TIMER_SECS until next renew try */
u16_t t2_rebind_time; /* #ticks with period DHCP_COARSE_TIMER_SECS until next rebind try */
u16_t lease_used; /* #ticks with period DHCP_COARSE_TIMER_SECS since last received DHCP ack */
u16_t t0_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for lease time */
ip_addr_t server_ip_addr; /* dhcp server address that offered this lease (ip_addr_t because passed to UDP) */
ip4_addr_t offered_ip_addr;
ip4_addr_t offered_sn_mask;
ip4_addr_t offered_gw_addr;
u32_t offered_t0_lease; /* lease period (in seconds) */
u32_t offered_t1_renew; /* recommended renew time (usually 50% of lease period) */
u32_t offered_t2_rebind; /* recommended rebind time (usually 87.5 of lease period) */
#if LWIP_DHCP_BOOTP_FILE
ip_addr_t offered_si_addr;
char boot_file_name[DHCP_FILE_LEN];
#endif /* LWIP_DHCP_BOOTPFILE */
} dhcps;
typedef struct dhcp_message
{
PACK_STRUCT_FLD_8(u8_t op);
PACK_STRUCT_FLD_8(u8_t htype);
PACK_STRUCT_FLD_8(u8_t hlen);
PACK_STRUCT_FLD_8(u8_t hops);
PACK_STRUCT_FIELD(u32_t xid);
PACK_STRUCT_FIELD(u16_t secs);
PACK_STRUCT_FIELD(u16_t flags);
PACK_STRUCT_FLD_S(ip4_addr_p_t ciaddr);
PACK_STRUCT_FLD_S(ip4_addr_p_t yiaddr);
PACK_STRUCT_FLD_S(ip4_addr_p_t siaddr);
PACK_STRUCT_FLD_S(ip4_addr_p_t giaddr);
PACK_STRUCT_FLD_8(u8_t chaddr[DHCPS_CHADDR_LEN]);
PACK_STRUCT_FLD_8(u8_t sname[DHCPS_SNAME_LEN]);
PACK_STRUCT_FLD_8(u8_t file[DHCPS_FILE_LEN]);
// PACK_STRUCT_FIELD(u32_t cookie);
#define DHCPS_MIN_OPTIONS_LEN 312U
/** make sure user does not configure this too small */
#if ((defined(DHCPS_OPTIONS_LEN)) && (DHCPS_OPTIONS_LEN < DHCPS_MIN_OPTIONS_LEN))
#undef DHCPS_OPTIONS_LEN
#endif
/** allow this to be configured in lwipopts.h, but not too small */
#if (!defined(DHCPS_OPTIONS_LEN))
/** set this to be sufficient for your options in outgoing DHCP msgs */
#define DHCPS_OPTIONS_LEN DHCPS_MIN_OPTIONS_LEN
#endif
PACK_STRUCT_FLD_8(u8_t options[DHCPS_OPTIONS_LEN]);
} dhcp_message;
/** DHCP OPTIONS CODE **/
typedef enum
{
@ -378,117 +153,48 @@ typedef enum
CLASSLESS_ROUTE = 121,
} dhcp_msg_option;
typedef struct dhcp_option
{
uint8_t id; // option id
uint8_t len; // option length
uint8_t data[256]; // option data
/* Defined in esp_misc.h */
typedef struct {
bool enable;
ip4_addr_t start_ip;
ip4_addr_t end_ip;
} dhcps_lease_t;
STAILQ_ENTRY(dhcp_option) pointers; // pointers, see queue(3)
} dhcp_option;
typedef STAILQ_HEAD(dhcp_option_list_, dhcp_option) DHCP_OPTION_LIST;
typedef struct dhcp_option_list_ dhcp_option_list;
/*
* Header to manage the database of address bindings.
*/
// static association or dynamic
enum
{
DYNAMIC = 0,
STATIC = 1,
STATIC_OR_DYNAMIC = 2
enum dhcps_offer_option{
OFFER_START = 0x00,
OFFER_ROUTER = 0x01,
OFFER_END
};
// binding status
enum
{
EMPTY = 0,
ASSOCIATED,
PENDINGD,
EXPIRED,
RELEASED
#define DHCPS_MAX_LEASE 0x64
#define DHCPS_LEASE_TIME_DEF (120)
struct dhcps_pool{
ip4_addr_t ip;
u8_t mac[6];
u32_t lease_timer;
};
/*
* IP address used to delimitate an address pool.
*/
typedef struct pool_indexes
{
uint32_t first; // first address of the pool
uint32_t last; // last address of the pool
uint32_t current; // current available address
}pool_indexes;
typedef struct _list_node{
void *pnode;
struct _list_node *pnext;
}list_node;
/*
* The bindings are organized as a double linked list
* using the standard queue(3) library
*/
typedef struct address_binding
{
uint32_t address; // address
uint8_t cident_len; // client identifier len
uint8_t cident[256]; // client identifier
typedef u32_t dhcps_time_t;
typedef u8_t dhcps_offer_t;
time_t binding_time; // time of binding
time_t lease_time; // duration of lease
typedef struct {
dhcps_offer_t dhcps_offer;
dhcps_time_t dhcps_time;
dhcps_lease_t dhcps_poll;
} dhcps_options_t;
int status; // binding status
int is_static; // check if it is a static binding
#define dhcps_router_enabled(offer) ((offer & OFFER_ROUTER) != 0)
STAILQ_ENTRY(address_binding) pointers; // list pointers, see queue(3)
}address_binding;
typedef STAILQ_HEAD(binding_list_, address_binding) BINDING_LIST_HEAD;
typedef struct binding_list_ binding_list;
/*
* Global association pool.
*
* The (static or dynamic) associations tables of the DHCP server,
* are maintained in this global structure.
*
* Note: all the IP addresses are in host order,
* to allow an easy manipulation.
*/
typedef struct address_pool
{
uint32_t server_id; // this server id (IP address)
uint32_t netmask; // network mask
uint32_t gateway; // network gateway
char device[16]; // network device to use
pool_indexes indexes; // used to delimitate a pool of available addresses
time_t lease_time; // default lease time
time_t pending_time; // duration of a binding in the pending state
struct udp_pcb *socket; //
bool flags;
dhcp_option_list options; // options for this pool, see queue
binding_list bindings; // associated addresses, see queue(3)
}address_pool;
/*
* Internal representation of a DHCP message,
* with options parsed into a list...
*/
typedef struct dhcps_msg
{
dhcp_message hdr;
dhcp_option_list opts;
} dhcps_msg;
bool dhcps_option_set(u8_t opt_id, void* optarg);
void dhcps_start(struct netif *netif, struct dhcps_lease *lease_pool);
void dhcps_start(struct netif *netif, ip4_addr_t ip);
void dhcps_stop(struct netif *netif);
bool dhcps_lease_set(struct dhcps_lease *please);
bool dhcps_lease_get(struct dhcps_lease *please);
void *dhcps_option_info(u8_t op_id, u32_t opt_len);
bool dhcp_search_ip_on_mac(u8_t *mac, ip4_addr_t *ip);
#endif
#endif

View file

@ -0,0 +1,71 @@
/*
* Copyright (c) 2007-2009 Frédéric Bernon, Simon Goldschmidt
* 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.
* 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: Frédéric Bernon, Simon Goldschmidt
*
*/
#ifndef LWIP_HDR_APPS_SNTP_H
#define LWIP_HDR_APPS_SNTP_H
#include "apps/sntp/sntp_opts.h"
#include "lwip/ip_addr.h"
#ifdef __cplusplus
extern "C" {
#endif
/* SNTP operating modes: default is to poll using unicast.
The mode has to be set before calling sntp_init(). */
#define SNTP_OPMODE_POLL 0
#define SNTP_OPMODE_LISTENONLY 1
void sntp_setoperatingmode(u8_t operating_mode);
u8_t sntp_getoperatingmode(void);
void sntp_init(void);
void sntp_stop(void);
u8_t sntp_enabled(void);
void sntp_setserver(u8_t idx, const ip_addr_t *addr);
ip_addr_t sntp_getserver(u8_t idx);
#if SNTP_SERVER_DNS
void sntp_setservername(u8_t idx, char *server);
char *sntp_getservername(u8_t idx);
#endif /* SNTP_SERVER_DNS */
#if SNTP_GET_SERVERS_FROM_DHCP
void sntp_servermode_dhcp(int set_servers_from_dhcp);
#else /* SNTP_GET_SERVERS_FROM_DHCP */
#define sntp_servermode_dhcp(x)
#endif /* SNTP_GET_SERVERS_FROM_DHCP */
#ifdef __cplusplus
}
#endif
#endif /* LWIP_HDR_APPS_SNTP_H */

View file

@ -0,0 +1,159 @@
/*
* Copyright (c) 2007-2009 Frédéric Bernon, Simon Goldschmidt
* 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.
* 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: Frédéric Bernon, Simon Goldschmidt
*
*/
#ifndef LWIP_HDR_APPS_SNTP_OPTS_H
#define LWIP_HDR_APPS_SNTP_OPTS_H
#include "lwip/opt.h"
/** SNTP macro to change system time in seconds
* Define SNTP_SET_SYSTEM_TIME_US(sec, us) to set the time in microseconds instead of this one
* if you need the additional precision.
*/
#ifndef SNTP_SET_SYSTEM_TIME
#define SNTP_SET_SYSTEM_TIME(sec) LWIP_UNUSED_ARG(sec)
#endif
/** The maximum number of SNTP servers that can be set */
#ifndef SNTP_MAX_SERVERS
#define SNTP_MAX_SERVERS LWIP_DHCP_MAX_NTP_SERVERS
#endif
/** Set this to 1 to implement the callback function called by dhcp when
* NTP servers are received. */
#ifndef SNTP_GET_SERVERS_FROM_DHCP
#define SNTP_GET_SERVERS_FROM_DHCP LWIP_DHCP_GET_NTP_SRV
#endif
/* Set this to 1 to support DNS names (or IP address strings) to set sntp servers */
#ifndef SNTP_SERVER_DNS
#define SNTP_SERVER_DNS 1
#endif
/** One server address/name can be defined as default if SNTP_SERVER_DNS == 1:
* #define SNTP_SERVER_ADDRESS "pool.ntp.org"
*/
/**
* 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 config of SNTP server(s) by DNS name */
#ifndef SNTP_SERVER_DNS
#define SNTP_SERVER_DNS 0
#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
/** If you want the startup delay to be a function, define this
* to a function (including the brackets) and define SNTP_STARTUP_DELAY to 1.
*/
#ifndef SNTP_STARTUP_DELAY_FUNC
#define SNTP_STARTUP_DELAY_FUNC SNTP_STARTUP_DELAY
#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. Must not be beolw 15 seconds by specification (i.e. 15000)
*/
#ifndef SNTP_UPDATE_DELAY
#define SNTP_UPDATE_DELAY 3600000
#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
#endif /* LWIP_HDR_APPS_SNTP_OPTS_H */

View file

@ -94,6 +94,10 @@ struct dhcp
ip_addr_t offered_si_addr;
char boot_file_name[DHCP_FILE_LEN];
#endif /* LWIP_DHCP_BOOTPFILE */
/* Espressif add start. */
void (*cb)(void); /* callback for dhcp, add a parameter to show dhcp status if needed */
/* Espressif add end. */
};
/* MUST be compiled with "pack structs" or equivalent! */
@ -140,6 +144,10 @@ void dhcp_set_struct(struct netif *netif, struct dhcp *dhcp);
/** Remove a struct dhcp previously set to the netif using dhcp_set_struct() */
#define dhcp_remove_struct(netif) do { (netif)->dhcp = NULL; } while(0)
void dhcp_cleanup(struct netif *netif);
/* Espressif add start. */
/** set callback for DHCP */
void dhcp_set_cb(struct netif *netif, void (*cb)(void));
/* Espressif add end. */
/** start DHCP configuration */
err_t dhcp_start(struct netif *netif);
/** enforce early lease renewal (not needed normally)*/

View file

@ -188,9 +188,9 @@ struct dns_api_msg {
#if LWIP_NETCONN_SEM_PER_THREAD
#ifdef LWIP_ESP8266
#define LWIP_NETCONN_THREAD_SEM_GET() sys_thread_sem()
#define LWIP_NETCONN_THREAD_SEM_ALLOC()
#define LWIP_NETCONN_THREAD_SEM_FREE()
#define LWIP_NETCONN_THREAD_SEM_GET() sys_thread_sem_get()
#define LWIP_NETCONN_THREAD_SEM_ALLOC() sys_thread_sem_init()
#define LWIP_NETCONN_THREAD_SEM_FREE() sys_thread_sem_deinit()
#endif
#endif

View file

@ -505,7 +505,7 @@ int lwip_fcntl(int s, int cmd, int val);
#if LWIP_COMPAT_SOCKETS
#if LWIP_COMPAT_SOCKETS != 2
#ifdef LWIP_THREAD_SAFE
#if LWIP_THREAD_SAFE
int lwip_accept_r(int s, struct sockaddr *addr, socklen_t *addrlen);
int lwip_bind_r(int s, const struct sockaddr *name, socklen_t namelen);

View file

@ -35,6 +35,7 @@
#define __ARCH_CC_H__
#include <stdint.h>
#include <errno.h>
#include "arch/sys_arch.h"
@ -72,6 +73,4 @@ typedef int sys_prot_t;
#define LWIP_NOASSERT
//#define LWIP_ERROR
#define LWIP_PROVIDE_ERRNO
#endif /* __ARCH_CC_H__ */

View file

@ -64,9 +64,10 @@ typedef struct sys_mbox_s {
void sys_arch_assert(const char *file, int line);
uint32_t system_get_time(void);
sys_sem_t* sys_thread_sem(void);
void sys_delay_ms(uint32_t ms);
sys_sem_t* sys_thread_sem_init(void);
void sys_thread_sem_deinit(void);
sys_sem_t* sys_thread_sem_get(void);
#endif /* __SYS_ARCH_H__ */

View file

@ -34,6 +34,8 @@
#include <stdlib.h>
/* Enable all Espressif-only options */
#define LWIP_ESP8266
/*
-----------------------------------------------
@ -221,8 +223,8 @@ extern unsigned long os_random(void);
* TCP_WND: The size of a TCP window. This must be at least
* (2 * TCP_MSS) for things to work well
*/
#define PERF 1
#ifdef PERF
extern unsigned char misc_prof_get_tcpw(void);
extern unsigned char misc_prof_get_tcp_snd_buf(void);
#define TCP_WND (misc_prof_get_tcpw()*TCP_MSS)
@ -506,15 +508,15 @@ extern unsigned char misc_prof_get_tcp_snd_buf(void);
* DHCP_DEBUG: Enable debugging in dhcp.c.
*/
#define DHCP_DEBUG LWIP_DBG_OFF
//#define LWIP_DEBUG 1
//#define TCP_DEBUG LWIP_DBG_ON
#define LWIP_DEBUG 0
#define TCP_DEBUG LWIP_DBG_OFF
#define THREAD_SAFE_DEBUG LWIP_DBG_OFF
#define LWIP_THREAD_SAFE 1
#define CHECKSUM_CHECK_UDP 0
#define CHECKSUM_CHECK_IP 0
#define HEAP_HIGHWAT 6*1024
#define HEAP_HIGHWAT 20*1024
#define LWIP_NETCONN_FULLDUPLEX 1
#define LWIP_NETCONN_SEM_PER_THREAD 1

View file

@ -213,7 +213,7 @@ sys_mbox_new(sys_mbox_t *mbox, int size)
(*mbox)->alive = true;
LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("new *mbox ok\n"));
LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("new *mbox ok mbox=%p os_mbox=%p mbox_lock=%p\n", *mbox, (*mbox)->os_mbox, (*mbox)->lock));
return ERR_OK;
}
@ -234,6 +234,7 @@ sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
if (xQueueSend((*mbox)->os_mbox, &msg, (portTickType)0) == pdPASS) {
xReturn = ERR_OK;
} else {
LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("trypost mbox=%p fail\n", (*mbox)->os_mbox));
xReturn = ERR_MEM;
}
@ -291,9 +292,11 @@ sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
ulReturn = SYS_ARCH_TIMEOUT;
}
} else { // block forever for a message.
while (1){
LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_arch_mbox_fetch: fetch mbox=%p os_mbox=%p lock=%p\n", mbox, (*mbox)->os_mbox, (*mbox)->lock));
if (pdTRUE == xQueueReceive((*mbox)->os_mbox, &(*msg), portMAX_DELAY)){
LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_arch_mbox_fetch:mbox rx msg=%p\n", (*msg)));
break;
}
@ -348,13 +351,15 @@ sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
void
sys_mbox_free(sys_mbox_t *mbox)
{
uint8_t count = 0;
#define MAX_POLL_CNT 100
#define PER_POLL_DELAY 20
uint16_t count = 0;
bool post_null = true;
LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_mbox_free: set alive false\n"));
(*mbox)->alive = false;
while ( count++ < 10 ){
while ( count++ < MAX_POLL_CNT ){ //ESP32_WORKAROUND
LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_mbox_free:try lock=%d\n", count));
if (!sys_mutex_trylock( &(*mbox)->lock )){
LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_mbox_free:get lock ok %d\n", count));
@ -372,10 +377,10 @@ sys_mbox_free(sys_mbox_t *mbox)
}
}
if (count == 10){
if (count == (MAX_POLL_CNT-1)){
printf("WARNING: mbox %p had a consumer who never unblocked. Leaking!\n", (*mbox)->os_mbox);
}
sys_delay_ms(20);
sys_delay_ms(PER_POLL_DELAY);
}
LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_mbox_free:free mbox\n"));
@ -428,6 +433,7 @@ sys_now(void)
return xTaskGetTickCount();
}
static portMUX_TYPE g_lwip_mux = portMUX_INITIALIZER_UNLOCKED;
/*
This optional function does a "fast" critical region protection and returns
the previous protection level. This function is only called during very short
@ -444,7 +450,7 @@ sys_now(void)
sys_prot_t
sys_arch_protect(void)
{
// vTaskEnterCritical();
portENTER_CRITICAL(&g_lwip_mux);
return (sys_prot_t) 1;
}
@ -459,7 +465,7 @@ void
sys_arch_unprotect(sys_prot_t pval)
{
(void) pval;
// vTaskExitCritical();
portEXIT_CRITICAL(&g_lwip_mux);
}
/*-----------------------------------------------------------------------------------*/
@ -475,15 +481,65 @@ sys_arch_assert(const char *file, int line)
while(1);
}
/* This is a super hacky thread-local-storage repository
FreeRTOS 8.2.3 & up have thread local storage in the
OS, which is how we should do this. Once we upgrade FreeRTOS,
we can drop this hacky store and use the FreeRTOS TLS API.
*/
sys_sem_t* sys_thread_sem(void)
#define SYS_TLS_INDEX CONFIG_LWIP_THREAD_LOCAL_STORAGE_INDEX
/*
* get per thread semphore
*/
sys_sem_t* sys_thread_sem_get(void)
{
extern void* xTaskGetLwipSem(void);
return (sys_sem_t*)(xTaskGetLwipSem());
sys_sem_t *sem = (sys_sem_t*)pvTaskGetThreadLocalStoragePointer(xTaskGetCurrentTaskHandle(), SYS_TLS_INDEX);
if (!sem){
sem = sys_thread_sem_init();
}
LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sem_get s=%p\n", sem));
return sem;
}
static void sys_thread_tls_free(int index, void* data)
{
sys_sem_t *sem = (sys_sem_t*)(data);
if (sem && *sem){
LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sem del, i=%d sem=%p\n", index, *sem));
vSemaphoreDelete(*sem);
}
if (sem){
LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sem pointer del, i=%d sem_p=%p\n", index, sem));
free(sem);
}
}
sys_sem_t* sys_thread_sem_init(void)
{
sys_sem_t *sem = (sys_sem_t*)malloc(sizeof(sys_sem_t*));
if (!sem){
printf("sem f1\n");
return 0;
}
*sem = xSemaphoreCreateBinary();
if (!(*sem)){
free(sem);
printf("sem f2\n");
return 0;
}
LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sem init sem_p=%p sem=%p cb=%p\n", sem, *sem, sys_thread_tls_free));
vTaskSetThreadLocalStoragePointerAndDelCallback(xTaskGetCurrentTaskHandle(), SYS_TLS_INDEX, sem, (TlsDeleteCallbackFunction_t)sys_thread_tls_free);
return sem;
}
void sys_thread_sem_deinit(void)
{
sys_sem_t *sem = (sys_sem_t*)pvTaskGetThreadLocalStoragePointer(xTaskGetCurrentTaskHandle(), SYS_TLS_INDEX);
sys_thread_tls_free(SYS_TLS_INDEX, (void*)sem);
vTaskSetThreadLocalStoragePointerAndDelCallback(xTaskGetCurrentTaskHandle(), SYS_TLS_INDEX, 0, 0);
return;
}
void sys_delay_ms(uint32_t ms)

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