mirror of
https://github.com/nodemcu/nodemcu-firmware.git
synced 2025-01-16 20:52:57 +08:00
Removed all currently-unused code & docs.
Heading towards having only ESP32-aware/capable code in this branch.
This commit is contained in:
parent
ddeb26c458
commit
fe602d2d7e
4
.gitmodules
vendored
4
.gitmodules
vendored
@ -1,9 +1,7 @@
|
||||
[submodule "esp8266-rtos-sdk"]
|
||||
path = sdk/esp8266-rtos-sdk
|
||||
url = https://github.com/espressif/ESP8266_RTOS_SDK.git
|
||||
[submodule "toolchains"]
|
||||
path = tools/toolchains
|
||||
url = https://github.com/jmattsson/nodemcu-prebuilt-toolchains.git
|
||||
[submodule "sdk/esp32-esp-idf"]
|
||||
path = sdk/esp32-esp-idf
|
||||
url = https://github.com/espressif/esp-idf.git
|
||||
ignore = dirty
|
||||
|
108
README.md
108
README.md
@ -1,108 +0,0 @@
|
||||
# **NodeMCU 1.5.1** #
|
||||
|
||||
[![Join the chat at https://gitter.im/nodemcu/nodemcu-firmware](https://img.shields.io/gitter/room/badges/shields.svg)](https://gitter.im/nodemcu/nodemcu-firmware?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[![Build Status](https://travis-ci.org/nodemcu/nodemcu-firmware.svg)](https://travis-ci.org/nodemcu/nodemcu-firmware)
|
||||
[![Documentation Status](https://readthedocs.com/projects/nodemcu/badge/?version=dev)](http://nodemcu.readthedocs.io/)
|
||||
[![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://github.com/nodemcu/nodemcu-firmware/blob/master/LICENSE)
|
||||
|
||||
### A Lua based firmware for ESP8266 WiFi SOC
|
||||
|
||||
NodeMCU is an [eLua](http://www.eluaproject.net/) based firmware for the [ESP8266 WiFi SOC from Espressif](http://espressif.com/en/products/esp8266/). The firmware is based on the [Espressif NON-OS SDK 1.5.1](http://bbs.espressif.com/viewtopic.php?f=46&p=5315) and uses a file system based on [spiffs](https://github.com/pellepl/spiffs). The code repository consists of 98.1% C-code that glues the thin Lua veneer to the SDK.
|
||||
|
||||
The NodeMCU *firmware* is a companion project to the popular [NodeMCU dev kits](https://github.com/nodemcu/nodemcu-devkit-v1.0), ready-made open source development boards with ESP8266-12E chips.
|
||||
|
||||
# Summary
|
||||
|
||||
- Easy to program wireless node and/or access point
|
||||
- Based on Lua 5.1.4 (without *debug, os* modules)
|
||||
- Asynchronous event-driven programming model
|
||||
- 40+ built-in modules
|
||||
- Firmware available with or without floating point support (integer-only uses less memory)
|
||||
- Up-to-date documentation at [https://nodemcu.readthedocs.io](https://nodemcu.readthedocs.io)
|
||||
|
||||
# Programming Model
|
||||
|
||||
The NodeMCU programming model is similar to that of [Node.js](https://en.wikipedia.org/wiki/Node.js), only in Lua. It is asynchronous and event-driven. Many functions, therefore, have parameters for callback functions. To give you an idea what a NodeMCU program looks like study the short snippets below. For more extensive examples have a look at the [`/lua_examples`](lua_examples) folder in the repository on GitHub.
|
||||
|
||||
```lua
|
||||
-- a simple HTTP server
|
||||
srv = net.createServer(net.TCP)
|
||||
srv:listen(80, function(conn)
|
||||
conn:on("receive", function(conn, payload)
|
||||
print(payload)
|
||||
conn:send("<h1> Hello, NodeMCU.</h1>")
|
||||
end)
|
||||
conn:on("sent", function(conn) conn:close() end)
|
||||
end)
|
||||
```
|
||||
```lua
|
||||
-- connect to WiFi access point
|
||||
wifi.setmode(wifi.STATION)
|
||||
wifi.sta.config("SSID", "password")
|
||||
```
|
||||
|
||||
# Documentation
|
||||
|
||||
The entire [NodeMCU documentation](https://nodemcu.readthedocs.io) is maintained right in this repository at [/docs](docs). The fact that the API documentation is mainted in the same repository as the code that *provides* the API ensures consistency between the two. With every commit the documentation is rebuilt by Read the Docs and thus transformed from terse Markdown into a nicely browsable HTML site at [https://nodemcu.readthedocs.io](https://nodemcu.readthedocs.io).
|
||||
|
||||
- How to [build the firmware](https://nodemcu.readthedocs.io/en/dev/en/build/)
|
||||
- How to [flash the firmware](https://nodemcu.readthedocs.io/en/dev/en/flash/)
|
||||
- How to [upload code and NodeMCU IDEs](https://nodemcu.readthedocs.io/en/dev/en/upload/)
|
||||
- API documentation for every module
|
||||
|
||||
# Support
|
||||
|
||||
See [https://nodemcu.readthedocs.io/en/dev/en/support/](https://nodemcu.readthedocs.io/en/dev/en/support/).
|
||||
|
||||
# License
|
||||
|
||||
[MIT](https://github.com/nodemcu/nodemcu-firmware/blob/master/LICENSE) © [zeroday](https://github.com/NodeMCU)/[nodemcu.com](http://nodemcu.com/index_en.html)
|
||||
|
||||
# Build Options
|
||||
|
||||
The following sections explain some of the options you have if you want to [build your own NodeMCU firmware](http://nodemcu.readthedocs.io/en/dev/en/build/).
|
||||
|
||||
### Select Modules
|
||||
|
||||
Disable modules you won't be using to reduce firmware size and free up some RAM. The ESP8266 is quite limited in available RAM and running out of memory can cause a system panic. The default configuration is designed to run on all ESP modules including the 512 KB modules like ESP-01 and only includes general purpose interface modules which require at most two GPIO pins.
|
||||
|
||||
Edit `app/include/user_modules.h` and comment-out the `#define` statement for modules you don't need. Example:
|
||||
|
||||
```c
|
||||
...
|
||||
#define LUA_USE_MODULES_MQTT
|
||||
// #define LUA_USE_MODULES_COAP
|
||||
// #define LUA_USE_MODULES_U8G
|
||||
...
|
||||
```
|
||||
|
||||
### Tag Your Build
|
||||
|
||||
Identify your firmware builds by editing `app/include/user_version.h`
|
||||
|
||||
```c
|
||||
#define NODE_VERSION "NodeMCU 1.5.1+myname"
|
||||
#ifndef BUILD_DATE
|
||||
#define BUILD_DATE "YYYYMMDD"
|
||||
#endif
|
||||
```
|
||||
|
||||
### Set UART Bit Rate
|
||||
|
||||
The initial baud rate at boot time is 115200bps. You can change this by
|
||||
editing `BIT_RATE_DEFAULT` in `app/include/user_config.h`:
|
||||
|
||||
```c
|
||||
#define BIT_RATE_DEFAULT BIT_RATE_115200
|
||||
```
|
||||
|
||||
Note that, by default, the firmware runs an auto-baudrate detection algorithm so that typing a few characters at boot time will cause
|
||||
the firmware to lock onto that baud rate (between 1200 and 230400).
|
||||
|
||||
### Debugging
|
||||
|
||||
To enable runtime debug messages to serial console edit `app/include/user_config.h`
|
||||
|
||||
```c
|
||||
#define DEVELOP_VERSION
|
||||
```
|
3
app/.gitignore
vendored
3
app/.gitignore
vendored
@ -1,3 +0,0 @@
|
||||
*.output*
|
||||
mapfile*
|
||||
!.gitignore
|
181
app/Makefile
181
app/Makefile
@ -1,181 +0,0 @@
|
||||
#############################################################
|
||||
# Required variables for each makefile
|
||||
# Discard this section from all parent makefiles
|
||||
# Expected variables (with automatic defaults):
|
||||
# CSRCS (all "C" files in the dir)
|
||||
# SUBDIRS (all subdirs with a Makefile)
|
||||
# GEN_LIBS - list of libs to be generated ()
|
||||
# GEN_IMAGES - list of object file images to be generated ()
|
||||
# GEN_BINS - list of binaries to be generated ()
|
||||
# COMPONENTS_xxx - a list of libs/objs in the form
|
||||
# subdir/lib to be extracted and rolled up into
|
||||
# a generated lib/image xxx.a ()
|
||||
#
|
||||
#FLAVOR = release
|
||||
FLAVOR = debug
|
||||
|
||||
#EXTRA_CCFLAGS += -u
|
||||
|
||||
ifndef PDIR # {
|
||||
GEN_IMAGES= eagle.app.v6.out
|
||||
GEN_BINS= eagle.app.v6.bin
|
||||
SPECIAL_MKTARGETS=$(APP_MKTARGETS)
|
||||
SUBDIRS= \
|
||||
user \
|
||||
driver \
|
||||
json \
|
||||
platform \
|
||||
lua \
|
||||
coap \
|
||||
mqtt \
|
||||
task \
|
||||
u8glib \
|
||||
ucglib \
|
||||
smart \
|
||||
modules \
|
||||
spiffs \
|
||||
cjson \
|
||||
crypto \
|
||||
dhtlib \
|
||||
tsl2561 \
|
||||
net \
|
||||
http
|
||||
|
||||
endif # } PDIR
|
||||
|
||||
APPDIR = .
|
||||
LDDIR = ../ld
|
||||
|
||||
CCFLAGS += -Os
|
||||
|
||||
TARGET_LDFLAGS = \
|
||||
-nostdlib \
|
||||
-Wl,-EL \
|
||||
--longcalls \
|
||||
--text-section-literals
|
||||
|
||||
ifeq ($(FLAVOR),debug)
|
||||
TARGET_LDFLAGS += -g -Os
|
||||
endif
|
||||
|
||||
ifeq ($(FLAVOR),release)
|
||||
TARGET_LDFLAGS += -Os
|
||||
endif
|
||||
|
||||
COMPONENTS_eagle.app.v6 = \
|
||||
user/libuser.a \
|
||||
driver/libdriver.a \
|
||||
json/libjson.a \
|
||||
platform/libplatform.a \
|
||||
task/libtask.a \
|
||||
lua/liblua.a \
|
||||
coap/coap.a \
|
||||
mqtt/mqtt.a \
|
||||
u8glib/u8glib.a \
|
||||
ucglib/ucglib.a \
|
||||
smart/smart.a \
|
||||
spiffs/spiffs.a \
|
||||
cjson/libcjson.a \
|
||||
crypto/libcrypto.a \
|
||||
dhtlib/libdhtlib.a \
|
||||
tsl2561/tsl2561lib.a \
|
||||
http/libhttp.a \
|
||||
net/libnodemcu_net.a \
|
||||
modules/libmodules.a \
|
||||
|
||||
# Inspect the modules library and work out which modules need to be linked.
|
||||
# For each enabled module, a symbol name of the form XYZ_module_selected is
|
||||
# returned. At link time those names are declared undefined, so those (and
|
||||
# only those) modules are pulled in.
|
||||
SELECTED_MODULE_SYMS=$(filter %_module_selected %module_selected1,$(shell $(NM) modules/.output/$(TARGET)/$(FLAVOR)/lib/libmodules.a))
|
||||
|
||||
USED_SDK_LIBS= \
|
||||
crypto \
|
||||
freertos \
|
||||
gcc \
|
||||
lwip \
|
||||
main \
|
||||
net80211 \
|
||||
phy \
|
||||
pp \
|
||||
smartconfig \
|
||||
ssl \
|
||||
wpa \
|
||||
wps \
|
||||
$(TARGET_SDK_LIBS) \
|
||||
|
||||
LINKFLAGS_eagle.app.v6 = \
|
||||
-Wl,--gc-sections \
|
||||
-Wl,-Map=mapfile.$(TARGET) \
|
||||
-nostdlib \
|
||||
-T$(LD_FILE) \
|
||||
-Wl,@$(LDDIR)/defsym.rom \
|
||||
-T$(LDDIR)/extrasyms.rom \
|
||||
-Wl,--no-check-sections \
|
||||
-Wl,-static \
|
||||
$(TARGET_LDFLAGS) \
|
||||
$(addprefix -u , $(SELECTED_MODULE_SYMS)) \
|
||||
-Wl,--start-group \
|
||||
$(DEP_LIBS_eagle.app.v6) \
|
||||
-Wl,--end-group \
|
||||
-Wl,--no-gc-sections \
|
||||
-Wl,--start-group \
|
||||
$(addprefix -l,$(USED_SDK_LIBS)) \
|
||||
-lhal \
|
||||
-Wl,--end-group \
|
||||
|
||||
DEPENDS_eagle.app.v6 = \
|
||||
$(LD_FILE) \
|
||||
Makefile
|
||||
|
||||
#############################################################
|
||||
# Configuration i.e. compile options etc.
|
||||
# Target specific stuff (defines etc.) goes in here!
|
||||
# Generally values applying to a tree are captured in the
|
||||
# makefile at its root level - these are then overridden
|
||||
# for a subtree within the makefile rooted therein
|
||||
#
|
||||
|
||||
#UNIVERSAL_TARGET_DEFINES = \
|
||||
|
||||
# Other potential configuration flags include:
|
||||
# -DTXRX_TXBUF_DEBUG
|
||||
# -DTXRX_RXBUF_DEBUG
|
||||
# -DWLAN_CONFIG_CCX
|
||||
CONFIGURATION_DEFINES = -D__ets__ \
|
||||
-DICACHE_FLASH \
|
||||
-DLUA_OPTIMIZE_MEMORY=2 \
|
||||
-DMIN_OPT_LEVEL=2 \
|
||||
-DLWIP_OPEN_SRC \
|
||||
-DPBUF_RSV_FOR_WLAN \
|
||||
-DEBUF_LWIP \
|
||||
|
||||
DEFINES += \
|
||||
$(UNIVERSAL_TARGET_DEFINES) \
|
||||
$(CONFIGURATION_DEFINES)
|
||||
|
||||
DDEFINES += \
|
||||
$(UNIVERSAL_TARGET_DEFINES) \
|
||||
$(CONFIGURATION_DEFINES)
|
||||
|
||||
|
||||
#############################################################
|
||||
# Recursion Magic - Don't touch this!!
|
||||
#
|
||||
# Each subtree potentially has an include directory
|
||||
# corresponding to the common APIs applicable to modules
|
||||
# rooted at that subtree. Accordingly, the INCLUDE PATH
|
||||
# of a module can only contain the include directories up
|
||||
# its parent path, and not its siblings
|
||||
#
|
||||
# Required for each makefile to inherit from the parent
|
||||
#
|
||||
|
||||
INCLUDES := $(INCLUDES) -I $(PDIR)include
|
||||
INCLUDES += -I ./
|
||||
PDIR := ../$(PDIR)
|
||||
sinclude $(PDIR)Makefile
|
||||
|
||||
|
||||
.PHONY: FORCE
|
||||
FORCE:
|
@ -1,76 +0,0 @@
|
||||
# If Lua is installed in a non-standard location, please set the LUA_DIR
|
||||
# environment variable to point to prefix for the install. Eg:
|
||||
# Unix: export LUA_DIR=/home/user/pkg
|
||||
# Windows: set LUA_DIR=c:\lua51
|
||||
|
||||
project(lua-cjson C)
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
|
||||
option(USE_INTERNAL_FPCONV "Use internal strtod() / g_fmt() code for performance")
|
||||
option(MULTIPLE_THREADS "Support multi-threaded apps with internal fpconv - recommended" ON)
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING
|
||||
"Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel."
|
||||
FORCE)
|
||||
endif()
|
||||
|
||||
find_package(Lua51 REQUIRED)
|
||||
include_directories(${LUA_INCLUDE_DIR})
|
||||
|
||||
if(NOT USE_INTERNAL_FPCONV)
|
||||
# Use libc number conversion routines (strtod(), sprintf())
|
||||
set(FPCONV_SOURCES fpconv.c)
|
||||
else()
|
||||
# Use internal number conversion routines
|
||||
add_definitions(-DUSE_INTERNAL_FPCONV)
|
||||
set(FPCONV_SOURCES g_fmt.c dtoa.c)
|
||||
|
||||
include(TestBigEndian)
|
||||
TEST_BIG_ENDIAN(IEEE_BIG_ENDIAN)
|
||||
if(IEEE_BIG_ENDIAN)
|
||||
add_definitions(-DIEEE_BIG_ENDIAN)
|
||||
endif()
|
||||
|
||||
if(MULTIPLE_THREADS)
|
||||
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
|
||||
find_package(Threads REQUIRED)
|
||||
if(NOT CMAKE_USE_PTHREADS_INIT)
|
||||
message(FATAL_ERROR
|
||||
"Pthreads not found - required by MULTIPLE_THREADS option")
|
||||
endif()
|
||||
add_definitions(-DMULTIPLE_THREADS)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Handle platforms missing isinf() macro (Eg, some Solaris systems).
|
||||
include(CheckSymbolExists)
|
||||
CHECK_SYMBOL_EXISTS(isinf math.h HAVE_ISINF)
|
||||
if(NOT HAVE_ISINF)
|
||||
add_definitions(-DUSE_INTERNAL_ISINF)
|
||||
endif()
|
||||
|
||||
set(_MODULE_LINK "${CMAKE_THREAD_LIBS_INIT}")
|
||||
get_filename_component(_lua_lib_dir ${LUA_LIBRARY} PATH)
|
||||
|
||||
if(APPLE)
|
||||
set(CMAKE_SHARED_MODULE_CREATE_C_FLAGS
|
||||
"${CMAKE_SHARED_MODULE_CREATE_C_FLAGS} -undefined dynamic_lookup")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
# Win32 modules need to be linked to the Lua library.
|
||||
set(_MODULE_LINK ${LUA_LIBRARY} ${_MODULE_LINK})
|
||||
set(_lua_module_dir "${_lua_lib_dir}")
|
||||
# Windows sprintf()/strtod() handle NaN/inf differently. Not supported.
|
||||
add_definitions(-DDISABLE_INVALID_NUMBERS)
|
||||
else()
|
||||
set(_lua_module_dir "${_lua_lib_dir}/lua/5.1")
|
||||
endif()
|
||||
|
||||
add_library(cjson MODULE lua_cjson.c strbuf.c ${FPCONV_SOURCES})
|
||||
set_target_properties(cjson PROPERTIES PREFIX "")
|
||||
target_link_libraries(cjson ${_MODULE_LINK})
|
||||
install(TARGETS cjson DESTINATION "${_lua_module_dir}")
|
||||
|
||||
# vi:ai et sw=4 ts=4:
|
@ -1,21 +0,0 @@
|
||||
Copyright (c) 2010-2012 Mark Pulford <mark@kyne.com.au>
|
||||
2015 Zeroday Hong <zeroday@nodemcu.com> nodemcu.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@ -1,47 +0,0 @@
|
||||
|
||||
#############################################################
|
||||
# Required variables for each makefile
|
||||
# Discard this section from all parent makefiles
|
||||
# Expected variables (with automatic defaults):
|
||||
# CSRCS (all "C" files in the dir)
|
||||
# SUBDIRS (all subdirs with a Makefile)
|
||||
# GEN_LIBS - list of libs to be generated ()
|
||||
# GEN_IMAGES - list of images to be generated ()
|
||||
# COMPONENTS_xxx - a list of libs/objs in the form
|
||||
# subdir/lib to be extracted and rolled up into
|
||||
# a generated lib/image xxx.a ()
|
||||
#
|
||||
ifndef PDIR
|
||||
|
||||
GEN_LIBS = libcjson.a
|
||||
|
||||
endif
|
||||
|
||||
|
||||
#############################################################
|
||||
# Configuration i.e. compile options etc.
|
||||
# Target specific stuff (defines etc.) goes in here!
|
||||
# Generally values applying to a tree are captured in the
|
||||
# makefile at its root level - these are then overridden
|
||||
# for a subtree within the makefile rooted therein
|
||||
#
|
||||
#DEFINES +=
|
||||
|
||||
#############################################################
|
||||
# Recursion Magic - Don't touch this!!
|
||||
#
|
||||
# Each subtree potentially has an include directory
|
||||
# corresponding to the common APIs applicable to modules
|
||||
# rooted at that subtree. Accordingly, the INCLUDE PATH
|
||||
# of a module can only contain the include directories up
|
||||
# its parent path, and not its siblings
|
||||
#
|
||||
# Required for each makefile to inherit from the parent
|
||||
#
|
||||
|
||||
INCLUDES := $(INCLUDES) -I $(PDIR)include
|
||||
INCLUDES += -I ./
|
||||
INCLUDES += -I ../libc
|
||||
PDIR := ../$(PDIR)
|
||||
sinclude $(PDIR)Makefile
|
||||
|
@ -1,9 +0,0 @@
|
||||
The following people have helped with bug reports, testing and/or
|
||||
suggestions:
|
||||
|
||||
- Louis-Philippe Perron (@loopole)
|
||||
- Ondřej Jirman
|
||||
- Steve Donovan <steve.j.donovan@gmail.com>
|
||||
- Zhang "agentzh" Yichun <agentzh@gmail.com>
|
||||
|
||||
Thanks!
|
@ -1,28 +0,0 @@
|
||||
#include "cjson_mem.h"
|
||||
#include "../lua/lauxlib.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
static lua_State *gL;
|
||||
static const char errfmt[] = "cjson %salloc: out of mem (%d bytes)";
|
||||
|
||||
void cjson_mem_setlua (lua_State *L)
|
||||
{
|
||||
gL = L;
|
||||
}
|
||||
|
||||
void *cjson_mem_malloc (uint32_t sz)
|
||||
{
|
||||
void *p = (void*)malloc (sz);
|
||||
if (!p && gL)
|
||||
luaL_error (gL, errfmt, "m", sz);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
void *cjson_mem_realloc (void *o, uint32_t sz)
|
||||
{
|
||||
void *p = (void*)realloc (o, sz);
|
||||
if (!p && gL)
|
||||
luaL_error (gL, errfmt, "re", sz);
|
||||
return p;
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
#ifndef _CJSON_MEM_H_
|
||||
#define _CJSON_MEM_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "../lua/lua.h"
|
||||
|
||||
void cjson_mem_setlua (lua_State *L);
|
||||
|
||||
void *cjson_mem_malloc (uint32_t sz);
|
||||
void *cjson_mem_realloc (void *p, uint32_t sz);
|
||||
|
||||
#endif
|
@ -1,50 +0,0 @@
|
||||
parser:
|
||||
- call parse_value
|
||||
- next_token
|
||||
? <EOF> nop.
|
||||
|
||||
parse_value:
|
||||
- next_token
|
||||
? <OBJ_BEGIN> call parse_object.
|
||||
? <ARR_BEGIN> call parse_array.
|
||||
? <STRING> push. return.
|
||||
? <BOOLEAN> push. return.
|
||||
? <NULL> push. return.
|
||||
? <NUMBER> push. return.
|
||||
|
||||
parse_object:
|
||||
- push table
|
||||
- next_token
|
||||
? <STRING> push.
|
||||
- next_token
|
||||
? <COLON> nop.
|
||||
- call parse_value
|
||||
- set table
|
||||
- next_token
|
||||
? <OBJ_END> return.
|
||||
? <COMMA> loop parse_object.
|
||||
|
||||
parse_array:
|
||||
- push table
|
||||
- call parse_value
|
||||
- table append
|
||||
- next_token
|
||||
? <COMMA> loop parse_array.
|
||||
? ] return.
|
||||
|
||||
next_token:
|
||||
- check next character
|
||||
? { return <OBJ_BEGIN>
|
||||
? } return <OBJ_END>
|
||||
? [ return <ARR_BEGIN>
|
||||
? ] return <ARR_END>
|
||||
? , return <COMMA>
|
||||
? : return <COLON>
|
||||
? [-0-9] gobble number. return <NUMBER>
|
||||
? " gobble string. return <STRING>
|
||||
? [ \t\n] eat whitespace.
|
||||
? n Check "null". return <NULL> or <UNKNOWN>
|
||||
? t Check "true". return <BOOLEAN> or <UNKNOWN>
|
||||
? f Check "false". return <BOOLEAN> or <UNKNOWN>
|
||||
? . return <UNKNOWN>
|
||||
? \0 return <END>
|
4359
app/cjson/dtoa.c
4359
app/cjson/dtoa.c
File diff suppressed because it is too large
Load Diff
@ -1,74 +0,0 @@
|
||||
#ifndef _DTOA_CONFIG_H
|
||||
#define _DTOA_CONFIG_H
|
||||
#if 0
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* Ensure dtoa.c does not USE_LOCALE. Lua CJSON must not use locale
|
||||
* aware conversion routines. */
|
||||
#undef USE_LOCALE
|
||||
|
||||
/* dtoa.c should not touch errno, Lua CJSON does not use it, and it
|
||||
* may not be threadsafe */
|
||||
#define NO_ERRNO
|
||||
|
||||
#define Long int32_t
|
||||
#define ULong uint32_t
|
||||
#define Llong int64_t
|
||||
#define ULLong uint64_t
|
||||
|
||||
#ifdef IEEE_BIG_ENDIAN
|
||||
#define IEEE_MC68k
|
||||
#else
|
||||
#define IEEE_8087
|
||||
#endif
|
||||
|
||||
#define MALLOC(n) xmalloc(n)
|
||||
|
||||
static void *xmalloc(size_t size)
|
||||
{
|
||||
void *p;
|
||||
|
||||
p = malloc(size);
|
||||
if (!p) {
|
||||
fprintf(stderr, "Out of memory");
|
||||
abort();
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
#ifdef MULTIPLE_THREADS
|
||||
|
||||
/* Enable locking to support multi-threaded applications */
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
static pthread_mutex_t private_dtoa_lock[2] = {
|
||||
PTHREAD_MUTEX_INITIALIZER,
|
||||
PTHREAD_MUTEX_INITIALIZER
|
||||
};
|
||||
|
||||
#define ACQUIRE_DTOA_LOCK(n) do { \
|
||||
int r = pthread_mutex_lock(&private_dtoa_lock[n]); \
|
||||
if (r) { \
|
||||
fprintf(stderr, "pthread_mutex_lock failed with %d\n", r); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define FREE_DTOA_LOCK(n) do { \
|
||||
int r = pthread_mutex_unlock(&private_dtoa_lock[n]); \
|
||||
if (r) { \
|
||||
fprintf(stderr, "pthread_mutex_unlock failed with %d\n", r);\
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif /* MULTIPLE_THREADS */
|
||||
#endif
|
||||
#endif /* _DTOA_CONFIG_H */
|
||||
|
||||
/* vi:ai et sw=4 ts=4:
|
||||
*/
|
@ -1,209 +0,0 @@
|
||||
/* fpconv - Floating point conversion routines
|
||||
*
|
||||
* Copyright (c) 2011-2012 Mark Pulford <mark@kyne.com.au>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* JSON uses a '.' decimal separator. strtod() / sprintf() under C libraries
|
||||
* with locale support will break when the decimal separator is a comma.
|
||||
*
|
||||
* fpconv_* will around these issues with a translation buffer if required.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
// #include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "fpconv.h"
|
||||
|
||||
#if 0
|
||||
/* Lua CJSON assumes the locale is the same for all threads within a
|
||||
* process and doesn't change after initialisation.
|
||||
*
|
||||
* This avoids the need for per thread storage or expensive checks
|
||||
* for call. */
|
||||
static char locale_decimal_point = '.';
|
||||
|
||||
/* In theory multibyte decimal_points are possible, but
|
||||
* Lua CJSON only supports UTF-8 and known locales only have
|
||||
* single byte decimal points ([.,]).
|
||||
*
|
||||
* localconv() may not be thread safe (=>crash), and nl_langinfo() is
|
||||
* not supported on some platforms. Use sprintf() instead - if the
|
||||
* locale does change, at least Lua CJSON won't crash. */
|
||||
static void fpconv_update_locale()
|
||||
{
|
||||
char buf[8];
|
||||
|
||||
sprintf(buf, "%g", 0.5);
|
||||
|
||||
/* Failing this test might imply the platform has a buggy dtoa
|
||||
* implementation or wide characters */
|
||||
if (buf[0] != '0' || buf[2] != '5' || buf[3] != 0) {
|
||||
NODE_ERR("Error: wide characters found or printf() bug.");
|
||||
return;
|
||||
}
|
||||
|
||||
locale_decimal_point = buf[1];
|
||||
}
|
||||
|
||||
/* Check for a valid number character: [-+0-9a-yA-Y.]
|
||||
* Eg: -0.6e+5, infinity, 0xF0.F0pF0
|
||||
*
|
||||
* Used to find the probable end of a number. It doesn't matter if
|
||||
* invalid characters are counted - strtod() will find the valid
|
||||
* number if it exists. The risk is that slightly more memory might
|
||||
* be allocated before a parse error occurs. */
|
||||
static inline int valid_number_character(char ch)
|
||||
{
|
||||
char lower_ch;
|
||||
|
||||
if ('0' <= ch && ch <= '9')
|
||||
return 1;
|
||||
if (ch == '-' || ch == '+' || ch == '.')
|
||||
return 1;
|
||||
|
||||
/* Hex digits, exponent (e), base (p), "infinity",.. */
|
||||
lower_ch = ch | 0x20;
|
||||
if ('a' <= lower_ch && lower_ch <= 'y')
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Calculate the size of the buffer required for a strtod locale
|
||||
* conversion. */
|
||||
static int strtod_buffer_size(const char *s)
|
||||
{
|
||||
const char *p = s;
|
||||
|
||||
while (valid_number_character(*p))
|
||||
p++;
|
||||
|
||||
return p - s;
|
||||
}
|
||||
|
||||
/* Similar to strtod(), but must be passed the current locale's decimal point
|
||||
* character. Guaranteed to be called at the start of any valid number in a string */
|
||||
double fpconv_strtod(const char *nptr, char **endptr)
|
||||
{
|
||||
char localbuf[FPCONV_G_FMT_BUFSIZE];
|
||||
char *buf, *endbuf, *dp;
|
||||
int buflen;
|
||||
double value;
|
||||
|
||||
/* System strtod() is fine when decimal point is '.' */
|
||||
if (locale_decimal_point == '.')
|
||||
return c_strtod(nptr, endptr);
|
||||
|
||||
buflen = strtod_buffer_size(nptr);
|
||||
if (!buflen) {
|
||||
/* No valid characters found, standard strtod() return */
|
||||
*endptr = (char *)nptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Duplicate number into buffer */
|
||||
if (buflen >= FPCONV_G_FMT_BUFSIZE) {
|
||||
/* Handle unusually large numbers */
|
||||
buf = malloc(buflen + 1);
|
||||
if (!buf) {
|
||||
NODE_ERR("not enough memory\n");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
/* This is the common case.. */
|
||||
buf = localbuf;
|
||||
}
|
||||
memcpy(buf, nptr, buflen);
|
||||
buf[buflen] = 0;
|
||||
|
||||
/* Update decimal point character if found */
|
||||
dp = strchr(buf, '.');
|
||||
if (dp)
|
||||
*dp = locale_decimal_point;
|
||||
|
||||
value = c_strtod(buf, &endbuf);
|
||||
*endptr = (char *)&nptr[endbuf - buf];
|
||||
if (buflen >= FPCONV_G_FMT_BUFSIZE)
|
||||
free(buf);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/* "fmt" must point to a buffer of at least 6 characters */
|
||||
static void set_number_format(char *fmt, int precision)
|
||||
{
|
||||
int d1, d2, i;
|
||||
|
||||
if(!(1 <= precision && precision <= 14)) return;
|
||||
|
||||
/* Create printf format (%.14g) from precision */
|
||||
d1 = precision / 10;
|
||||
d2 = precision % 10;
|
||||
fmt[0] = '%';
|
||||
fmt[1] = '.';
|
||||
i = 2;
|
||||
if (d1) {
|
||||
fmt[i++] = '0' + d1;
|
||||
}
|
||||
fmt[i++] = '0' + d2;
|
||||
fmt[i++] = 'g';
|
||||
fmt[i] = 0;
|
||||
}
|
||||
|
||||
/* Assumes there is always at least 32 characters available in the target buffer */
|
||||
int fpconv_g_fmt(char *str, double num, int precision)
|
||||
{
|
||||
char buf[FPCONV_G_FMT_BUFSIZE];
|
||||
char fmt[6];
|
||||
int len;
|
||||
char *b;
|
||||
|
||||
set_number_format(fmt, precision);
|
||||
|
||||
/* Pass through when decimal point character is dot. */
|
||||
if (locale_decimal_point == '.'){
|
||||
sprintf(str, fmt, num);
|
||||
return strlen(str);
|
||||
}
|
||||
|
||||
/* snprintf() to a buffer then translate for other decimal point characters */
|
||||
sprintf(buf, fmt, num);
|
||||
len = strlen(buf);
|
||||
|
||||
/* Copy into target location. Translate decimal point if required */
|
||||
b = buf;
|
||||
do {
|
||||
*str++ = (*b == locale_decimal_point ? '.' : *b);
|
||||
} while(*b++);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void fpconv_init()
|
||||
{
|
||||
fpconv_update_locale();
|
||||
}
|
||||
#endif
|
||||
/* vi:ai et sw=4 ts=4:
|
||||
*/
|
@ -1,24 +0,0 @@
|
||||
/* Lua CJSON floating point conversion routines */
|
||||
|
||||
/* Buffer required to store the largest string representation of a double.
|
||||
*
|
||||
* Longest double printed with %.14g is 21 characters long:
|
||||
* -1.7976931348623e+308 */
|
||||
# define FPCONV_G_FMT_BUFSIZE 32
|
||||
|
||||
#if 0
|
||||
#ifdef USE_INTERNAL_FPCONV
|
||||
static inline void fpconv_init()
|
||||
{
|
||||
/* Do nothing - not required */
|
||||
}
|
||||
#else
|
||||
extern inline void fpconv_init();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
extern int fpconv_g_fmt(char*, double, int);
|
||||
extern double fpconv_strtod(const char*, char**);
|
||||
|
||||
/* vi:ai et sw=4 ts=4:
|
||||
*/
|
@ -1,112 +0,0 @@
|
||||
/****************************************************************
|
||||
*
|
||||
* The author of this software is David M. Gay.
|
||||
*
|
||||
* Copyright (c) 1991, 1996 by Lucent Technologies.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose without fee is hereby granted, provided that this entire notice
|
||||
* is included in all copies of any software which is or includes a copy
|
||||
* or modification of this software and in all copies of the supporting
|
||||
* documentation for such software.
|
||||
*
|
||||
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
|
||||
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY
|
||||
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
|
||||
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
|
||||
*
|
||||
***************************************************************/
|
||||
|
||||
/* g_fmt(buf,x) stores the closest decimal approximation to x in buf;
|
||||
* it suffices to declare buf
|
||||
* char buf[32];
|
||||
*/
|
||||
#if 0
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
extern char *dtoa(double, int, int, int *, int *, char **);
|
||||
extern int g_fmt(char *, double, int);
|
||||
extern void freedtoa(char*);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
fpconv_g_fmt(char *b, double x, int precision)
|
||||
{
|
||||
register int i, k;
|
||||
register char *s;
|
||||
int decpt, j, sign;
|
||||
char *b0, *s0, *se;
|
||||
|
||||
b0 = b;
|
||||
#ifdef IGNORE_ZERO_SIGN
|
||||
if (!x) {
|
||||
*b++ = '0';
|
||||
*b = 0;
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
s = s0 = dtoa(x, 2, precision, &decpt, &sign, &se);
|
||||
if (sign)
|
||||
*b++ = '-';
|
||||
if (decpt == 9999) /* Infinity or Nan */ {
|
||||
while((*b++ = *s++));
|
||||
/* "b" is used to calculate the return length. Decrement to exclude the
|
||||
* Null terminator from the length */
|
||||
b--;
|
||||
goto done0;
|
||||
}
|
||||
if (decpt <= -4 || decpt > precision) {
|
||||
*b++ = *s++;
|
||||
if (*s) {
|
||||
*b++ = '.';
|
||||
while((*b = *s++))
|
||||
b++;
|
||||
}
|
||||
*b++ = 'e';
|
||||
/* sprintf(b, "%+.2d", decpt - 1); */
|
||||
if (--decpt < 0) {
|
||||
*b++ = '-';
|
||||
decpt = -decpt;
|
||||
}
|
||||
else
|
||||
*b++ = '+';
|
||||
for(j = 2, k = 10; 10*k <= decpt; j++, k *= 10);
|
||||
for(;;) {
|
||||
i = decpt / k;
|
||||
*b++ = i + '0';
|
||||
if (--j <= 0)
|
||||
break;
|
||||
decpt -= i*k;
|
||||
decpt *= 10;
|
||||
}
|
||||
*b = 0;
|
||||
}
|
||||
else if (decpt <= 0) {
|
||||
*b++ = '0';
|
||||
*b++ = '.';
|
||||
for(; decpt < 0; decpt++)
|
||||
*b++ = '0';
|
||||
while((*b++ = *s++));
|
||||
b--;
|
||||
}
|
||||
else {
|
||||
while((*b = *s++)) {
|
||||
b++;
|
||||
if (--decpt == 0 && *s)
|
||||
*b++ = '.';
|
||||
}
|
||||
for(; decpt > 0; decpt--)
|
||||
*b++ = '0';
|
||||
*b = 0;
|
||||
}
|
||||
done0:
|
||||
freedtoa(s0);
|
||||
#ifdef IGNORE_ZERO_SIGN
|
||||
done:
|
||||
#endif
|
||||
return b - b0;
|
||||
}
|
||||
#endif
|
@ -1,271 +0,0 @@
|
||||
local json = require "cjson"
|
||||
|
||||
-- Various common routines used by the Lua CJSON package
|
||||
--
|
||||
-- Mark Pulford <mark@kyne.com.au>
|
||||
|
||||
-- Determine with a Lua table can be treated as an array.
|
||||
-- Explicitly returns "not an array" for very sparse arrays.
|
||||
-- Returns:
|
||||
-- -1 Not an array
|
||||
-- 0 Empty table
|
||||
-- >0 Highest index in the array
|
||||
local function is_array(table)
|
||||
local max = 0
|
||||
local count = 0
|
||||
for k, v in pairs(table) do
|
||||
if type(k) == "number" then
|
||||
if k > max then max = k end
|
||||
count = count + 1
|
||||
else
|
||||
return -1
|
||||
end
|
||||
end
|
||||
if max > count * 2 then
|
||||
return -1
|
||||
end
|
||||
|
||||
return max
|
||||
end
|
||||
|
||||
local serialise_value
|
||||
|
||||
local function serialise_table(value, indent, depth)
|
||||
local spacing, spacing2, indent2
|
||||
if indent then
|
||||
spacing = "\n" .. indent
|
||||
spacing2 = spacing .. " "
|
||||
indent2 = indent .. " "
|
||||
else
|
||||
spacing, spacing2, indent2 = " ", " ", false
|
||||
end
|
||||
depth = depth + 1
|
||||
if depth > 50 then
|
||||
return "Cannot serialise any further: too many nested tables"
|
||||
end
|
||||
|
||||
local max = is_array(value)
|
||||
|
||||
local comma = false
|
||||
local fragment = { "{" .. spacing2 }
|
||||
if max > 0 then
|
||||
-- Serialise array
|
||||
for i = 1, max do
|
||||
if comma then
|
||||
table.insert(fragment, "," .. spacing2)
|
||||
end
|
||||
table.insert(fragment, serialise_value(value[i], indent2, depth))
|
||||
comma = true
|
||||
end
|
||||
elseif max < 0 then
|
||||
-- Serialise table
|
||||
for k, v in pairs(value) do
|
||||
if comma then
|
||||
table.insert(fragment, "," .. spacing2)
|
||||
end
|
||||
table.insert(fragment,
|
||||
("[%s] = %s"):format(serialise_value(k, indent2, depth),
|
||||
serialise_value(v, indent2, depth)))
|
||||
comma = true
|
||||
end
|
||||
end
|
||||
table.insert(fragment, spacing .. "}")
|
||||
|
||||
return table.concat(fragment)
|
||||
end
|
||||
|
||||
function serialise_value(value, indent, depth)
|
||||
if indent == nil then indent = "" end
|
||||
if depth == nil then depth = 0 end
|
||||
|
||||
if value == json.null then
|
||||
return "json.null"
|
||||
elseif type(value) == "string" then
|
||||
return ("%q"):format(value)
|
||||
elseif type(value) == "nil" or type(value) == "number" or
|
||||
type(value) == "boolean" then
|
||||
return tostring(value)
|
||||
elseif type(value) == "table" then
|
||||
return serialise_table(value, indent, depth)
|
||||
else
|
||||
return "\"<" .. type(value) .. ">\""
|
||||
end
|
||||
end
|
||||
|
||||
local function file_load(filename)
|
||||
local file
|
||||
if filename == nil then
|
||||
file = io.stdin
|
||||
else
|
||||
local err
|
||||
file, err = io.open(filename, "rb")
|
||||
if file == nil then
|
||||
error(("Unable to read '%s': %s"):format(filename, err))
|
||||
end
|
||||
end
|
||||
local data = file:read("*a")
|
||||
|
||||
if filename ~= nil then
|
||||
file:close()
|
||||
end
|
||||
|
||||
if data == nil then
|
||||
error("Failed to read " .. filename)
|
||||
end
|
||||
|
||||
return data
|
||||
end
|
||||
|
||||
local function file_save(filename, data)
|
||||
local file
|
||||
if filename == nil then
|
||||
file = io.stdout
|
||||
else
|
||||
local err
|
||||
file, err = io.open(filename, "wb")
|
||||
if file == nil then
|
||||
error(("Unable to write '%s': %s"):format(filename, err))
|
||||
end
|
||||
end
|
||||
file:write(data)
|
||||
if filename ~= nil then
|
||||
file:close()
|
||||
end
|
||||
end
|
||||
|
||||
local function compare_values(val1, val2)
|
||||
local type1 = type(val1)
|
||||
local type2 = type(val2)
|
||||
if type1 ~= type2 then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Check for NaN
|
||||
if type1 == "number" and val1 ~= val1 and val2 ~= val2 then
|
||||
return true
|
||||
end
|
||||
|
||||
if type1 ~= "table" then
|
||||
return val1 == val2
|
||||
end
|
||||
|
||||
-- check_keys stores all the keys that must be checked in val2
|
||||
local check_keys = {}
|
||||
for k, _ in pairs(val1) do
|
||||
check_keys[k] = true
|
||||
end
|
||||
|
||||
for k, v in pairs(val2) do
|
||||
if not check_keys[k] then
|
||||
return false
|
||||
end
|
||||
|
||||
if not compare_values(val1[k], val2[k]) then
|
||||
return false
|
||||
end
|
||||
|
||||
check_keys[k] = nil
|
||||
end
|
||||
for k, _ in pairs(check_keys) do
|
||||
-- Not the same if any keys from val1 were not found in val2
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local test_count_pass = 0
|
||||
local test_count_total = 0
|
||||
|
||||
local function run_test_summary()
|
||||
return test_count_pass, test_count_total
|
||||
end
|
||||
|
||||
local function run_test(testname, func, input, should_work, output)
|
||||
local function status_line(name, status, value)
|
||||
local statusmap = { [true] = ":success", [false] = ":error" }
|
||||
if status ~= nil then
|
||||
name = name .. statusmap[status]
|
||||
end
|
||||
print(("[%s] %s"):format(name, serialise_value(value, false)))
|
||||
end
|
||||
|
||||
local result = { pcall(func, unpack(input)) }
|
||||
local success = table.remove(result, 1)
|
||||
|
||||
local correct = false
|
||||
if success == should_work and compare_values(result, output) then
|
||||
correct = true
|
||||
test_count_pass = test_count_pass + 1
|
||||
end
|
||||
test_count_total = test_count_total + 1
|
||||
|
||||
local teststatus = { [true] = "PASS", [false] = "FAIL" }
|
||||
print(("==> Test [%d] %s: %s"):format(test_count_total, testname,
|
||||
teststatus[correct]))
|
||||
|
||||
status_line("Input", nil, input)
|
||||
if not correct then
|
||||
status_line("Expected", should_work, output)
|
||||
end
|
||||
status_line("Received", success, result)
|
||||
print()
|
||||
|
||||
return correct, result
|
||||
end
|
||||
|
||||
local function run_test_group(tests)
|
||||
local function run_helper(name, func, input)
|
||||
if type(name) == "string" and #name > 0 then
|
||||
print("==> " .. name)
|
||||
end
|
||||
-- Not a protected call, these functions should never generate errors.
|
||||
func(unpack(input or {}))
|
||||
print()
|
||||
end
|
||||
|
||||
for _, v in ipairs(tests) do
|
||||
-- Run the helper if "should_work" is missing
|
||||
if v[4] == nil then
|
||||
run_helper(unpack(v))
|
||||
else
|
||||
run_test(unpack(v))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Run a Lua script in a separate environment
|
||||
local function run_script(script, env)
|
||||
local env = env or {}
|
||||
local func
|
||||
|
||||
-- Use setfenv() if it exists, otherwise assume Lua 5.2 load() exists
|
||||
if _G.setfenv then
|
||||
func = loadstring(script)
|
||||
if func then
|
||||
setfenv(func, env)
|
||||
end
|
||||
else
|
||||
func = load(script, nil, nil, env)
|
||||
end
|
||||
|
||||
if func == nil then
|
||||
error("Invalid syntax.")
|
||||
end
|
||||
func()
|
||||
|
||||
return env
|
||||
end
|
||||
|
||||
-- Export functions
|
||||
return {
|
||||
serialise_value = serialise_value,
|
||||
file_load = file_load,
|
||||
file_save = file_save,
|
||||
compare_values = compare_values,
|
||||
run_test_summary = run_test_summary,
|
||||
run_test = run_test,
|
||||
run_test_group = run_test_group,
|
||||
run_script = run_script
|
||||
}
|
||||
|
||||
-- vi:ai et sw=4 ts=4:
|
@ -1,14 +0,0 @@
|
||||
#!/usr/bin/env lua
|
||||
|
||||
-- usage: json2lua.lua [json_file]
|
||||
--
|
||||
-- Eg:
|
||||
-- echo '[ "testing" ]' | ./json2lua.lua
|
||||
-- ./json2lua.lua test.json
|
||||
|
||||
local json = require "cjson"
|
||||
local util = require "cjson.util"
|
||||
|
||||
local json_text = util.file_load(arg[1])
|
||||
local t = json.decode(json_text)
|
||||
print(util.serialise_value(t))
|
@ -1,20 +0,0 @@
|
||||
#!/usr/bin/env lua
|
||||
|
||||
-- usage: lua2json.lua [lua_file]
|
||||
--
|
||||
-- Eg:
|
||||
-- echo '{ "testing" }' | ./lua2json.lua
|
||||
-- ./lua2json.lua test.lua
|
||||
|
||||
local json = require "cjson"
|
||||
local util = require "cjson.util"
|
||||
|
||||
local env = {
|
||||
json = { null = json.null },
|
||||
null = json.null
|
||||
}
|
||||
|
||||
local t = util.run_script("data = " .. util.file_load(arg[1]), env)
|
||||
print(json.encode(t.data))
|
||||
|
||||
-- vi:ai et sw=4 ts=4:
|
@ -1,168 +0,0 @@
|
||||
= Lua CJSON 2.1devel Manual =
|
||||
Mark Pulford <mark@kyne.com.au>
|
||||
:revdate: 1st March 2012
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
The Lua CJSON module provides JSON support for Lua.
|
||||
|
||||
*Features*::
|
||||
- Fast, standards compliant encoding/parsing routines
|
||||
- Full support for JSON with UTF-8, including decoding surrogate pairs
|
||||
- Optional run-time support for common exceptions to the JSON
|
||||
specification (infinity, NaN,..)
|
||||
- No dependencies on other libraries
|
||||
|
||||
*Caveats*::
|
||||
- UTF-16 and UTF-32 are not supported
|
||||
|
||||
Lua CJSON is covered by the MIT license. Review the file +LICENSE+ for
|
||||
details.
|
||||
|
||||
API (Functions)
|
||||
---------------
|
||||
|
||||
Synopsis
|
||||
~~~~~~~~
|
||||
|
||||
[source,lua]
|
||||
------------
|
||||
|
||||
-- Translate Lua value to/from JSON
|
||||
text = cjson.encode(value)
|
||||
value = cjson.decode(text)
|
||||
|
||||
|
||||
Module Instantiation
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
decode
|
||||
~~~~~~
|
||||
|
||||
[source,lua]
|
||||
------------
|
||||
value = cjson.decode(json_text)
|
||||
------------
|
||||
|
||||
+cjson.decode+ will deserialise any UTF-8 JSON string into a Lua value
|
||||
or table.
|
||||
|
||||
UTF-16 and UTF-32 JSON strings are not supported.
|
||||
|
||||
+cjson.decode+ requires that any NULL (ASCII 0) and double quote (ASCII
|
||||
34) characters are escaped within strings. All escape codes will be
|
||||
decoded and other bytes will be passed transparently. UTF-8 characters
|
||||
are not validated during decoding and should be checked elsewhere if
|
||||
required.
|
||||
|
||||
JSON +null+ will be converted to a NULL +lightuserdata+ value. This can
|
||||
be compared with +cjson.null+ for convenience.
|
||||
|
||||
By default, numbers incompatible with the JSON specification (infinity,
|
||||
NaN, hexadecimal) can be decoded. This default can be changed with
|
||||
<<decode_invalid_numbers,+cjson.decode_invalid_numbers+>>.
|
||||
|
||||
.Example: Decoding
|
||||
[source,lua]
|
||||
json_text = '[ true, { "foo": "bar" } ]'
|
||||
value = cjson.decode(json_text)
|
||||
-- Returns: { true, { foo = "bar" } }
|
||||
|
||||
[CAUTION]
|
||||
Care must be taken after decoding JSON objects with numeric keys. Each
|
||||
numeric key will be stored as a Lua +string+. Any subsequent code
|
||||
assuming type +number+ may break.
|
||||
|
||||
|
||||
[[encode]]
|
||||
encode
|
||||
~~~~~~
|
||||
|
||||
[source,lua]
|
||||
------------
|
||||
json_text = cjson.encode(value)
|
||||
------------
|
||||
|
||||
+cjson.encode+ will serialise a Lua value into a string containing the
|
||||
JSON representation.
|
||||
|
||||
+cjson.encode+ supports the following types:
|
||||
|
||||
- +boolean+
|
||||
- +lightuserdata+ (NULL value only)
|
||||
- +nil+
|
||||
- +number+
|
||||
- +string+
|
||||
- +table+
|
||||
|
||||
The remaining Lua types will generate an error:
|
||||
|
||||
- +function+
|
||||
- +lightuserdata+ (non-NULL values)
|
||||
- +thread+
|
||||
- +userdata+
|
||||
|
||||
By default, numbers are encoded with 14 significant digits. Refer to
|
||||
<<encode_number_precision,+cjson.encode_number_precision+>> for details.
|
||||
|
||||
Lua CJSON will escape the following characters within each UTF-8 string:
|
||||
|
||||
- Control characters (ASCII 0 - 31)
|
||||
- Double quote (ASCII 34)
|
||||
- Forward slash (ASCII 47)
|
||||
- Blackslash (ASCII 92)
|
||||
- Delete (ASCII 127)
|
||||
|
||||
All other bytes are passed transparently.
|
||||
|
||||
[CAUTION]
|
||||
=========
|
||||
Lua CJSON will successfully encode/decode binary strings, but this is
|
||||
technically not supported by JSON and may not be compatible with other
|
||||
JSON libraries. To ensure the output is valid JSON, applications should
|
||||
ensure all Lua strings passed to +cjson.encode+ are UTF-8.
|
||||
|
||||
Base64 is commonly used to encode binary data as the most efficient
|
||||
encoding under UTF-8 can only reduce the encoded size by a further
|
||||
~8%. Lua Base64 routines can be found in the
|
||||
http://w3.impa.br/%7Ediego/software/luasocket/[LuaSocket] and
|
||||
http://www.tecgraf.puc-rio.br/%7Elhf/ftp/lua/#lbase64[lbase64] packages.
|
||||
=========
|
||||
|
||||
Lua CJSON uses a heuristic to determine whether to encode a Lua table as
|
||||
a JSON array or an object. A Lua table with only positive integer keys
|
||||
of type +number+ will be encoded as a JSON array. All other tables will
|
||||
be encoded as a JSON object.
|
||||
|
||||
Lua CJSON does not use metamethods when serialising tables.
|
||||
|
||||
- +rawget+ is used to iterate over Lua arrays
|
||||
- +next+ is used to iterate over Lua objects
|
||||
|
||||
Lua arrays with missing entries (_sparse arrays_) may optionally be
|
||||
encoded in several different ways. Refer to
|
||||
<<encode_sparse_array,+cjson.encode_sparse_array+>> for details.
|
||||
|
||||
JSON object keys are always strings. Hence +cjson.encode+ only supports
|
||||
table keys which are type +number+ or +string+. All other types will
|
||||
generate an error.
|
||||
|
||||
[NOTE]
|
||||
Standards compliant JSON must be encapsulated in either an object (+{}+)
|
||||
or an array (+[]+). If strictly standards compliant JSON is desired, a
|
||||
table must be passed to +cjson.encode+.
|
||||
|
||||
By default, encoding the following Lua values will generate errors:
|
||||
|
||||
- Numbers incompatible with the JSON specification (infinity, NaN)
|
||||
- Tables nested more than 1000 levels deep
|
||||
- Excessively sparse Lua arrays
|
||||
|
||||
.Example: Encoding
|
||||
[source,lua]
|
||||
value = { true, { foo = "bar" } }
|
||||
json_text = cjson.encode(value)
|
||||
-- Returns: '[true,{"foo":"bar"}]'
|
||||
|
||||
// vi:ft=asciidoc tw=72:
|
@ -1,563 +0,0 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Network Working Group D. Crockford
|
||||
Request for Comments: 4627 JSON.org
|
||||
Category: Informational July 2006
|
||||
|
||||
|
||||
The application/json Media Type for JavaScript Object Notation (JSON)
|
||||
|
||||
Status of This Memo
|
||||
|
||||
This memo provides information for the Internet community. It does
|
||||
not specify an Internet standard of any kind. Distribution of this
|
||||
memo is unlimited.
|
||||
|
||||
Copyright Notice
|
||||
|
||||
Copyright (C) The Internet Society (2006).
|
||||
|
||||
Abstract
|
||||
|
||||
JavaScript Object Notation (JSON) is a lightweight, text-based,
|
||||
language-independent data interchange format. It was derived from
|
||||
the ECMAScript Programming Language Standard. JSON defines a small
|
||||
set of formatting rules for the portable representation of structured
|
||||
data.
|
||||
|
||||
1. Introduction
|
||||
|
||||
JavaScript Object Notation (JSON) is a text format for the
|
||||
serialization of structured data. It is derived from the object
|
||||
literals of JavaScript, as defined in the ECMAScript Programming
|
||||
Language Standard, Third Edition [ECMA].
|
||||
|
||||
JSON can represent four primitive types (strings, numbers, booleans,
|
||||
and null) and two structured types (objects and arrays).
|
||||
|
||||
A string is a sequence of zero or more Unicode characters [UNICODE].
|
||||
|
||||
An object is an unordered collection of zero or more name/value
|
||||
pairs, where a name is a string and a value is a string, number,
|
||||
boolean, null, object, or array.
|
||||
|
||||
An array is an ordered sequence of zero or more values.
|
||||
|
||||
The terms "object" and "array" come from the conventions of
|
||||
JavaScript.
|
||||
|
||||
JSON's design goals were for it to be minimal, portable, textual, and
|
||||
a subset of JavaScript.
|
||||
|
||||
|
||||
|
||||
Crockford Informational [Page 1]
|
||||
|
||||
RFC 4627 JSON July 2006
|
||||
|
||||
|
||||
1.1. Conventions Used in This Document
|
||||
|
||||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
|
||||
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
|
||||
document are to be interpreted as described in [RFC2119].
|
||||
|
||||
The grammatical rules in this document are to be interpreted as
|
||||
described in [RFC4234].
|
||||
|
||||
2. JSON Grammar
|
||||
|
||||
A JSON text is a sequence of tokens. The set of tokens includes six
|
||||
structural characters, strings, numbers, and three literal names.
|
||||
|
||||
A JSON text is a serialized object or array.
|
||||
|
||||
JSON-text = object / array
|
||||
|
||||
These are the six structural characters:
|
||||
|
||||
begin-array = ws %x5B ws ; [ left square bracket
|
||||
|
||||
begin-object = ws %x7B ws ; { left curly bracket
|
||||
|
||||
end-array = ws %x5D ws ; ] right square bracket
|
||||
|
||||
end-object = ws %x7D ws ; } right curly bracket
|
||||
|
||||
name-separator = ws %x3A ws ; : colon
|
||||
|
||||
value-separator = ws %x2C ws ; , comma
|
||||
|
||||
Insignificant whitespace is allowed before or after any of the six
|
||||
structural characters.
|
||||
|
||||
ws = *(
|
||||
%x20 / ; Space
|
||||
%x09 / ; Horizontal tab
|
||||
%x0A / ; Line feed or New line
|
||||
%x0D ; Carriage return
|
||||
)
|
||||
|
||||
2.1. Values
|
||||
|
||||
A JSON value MUST be an object, array, number, or string, or one of
|
||||
the following three literal names:
|
||||
|
||||
false null true
|
||||
|
||||
|
||||
|
||||
Crockford Informational [Page 2]
|
||||
|
||||
RFC 4627 JSON July 2006
|
||||
|
||||
|
||||
The literal names MUST be lowercase. No other literal names are
|
||||
allowed.
|
||||
|
||||
value = false / null / true / object / array / number / string
|
||||
|
||||
false = %x66.61.6c.73.65 ; false
|
||||
|
||||
null = %x6e.75.6c.6c ; null
|
||||
|
||||
true = %x74.72.75.65 ; true
|
||||
|
||||
2.2. Objects
|
||||
|
||||
An object structure is represented as a pair of curly brackets
|
||||
surrounding zero or more name/value pairs (or members). A name is a
|
||||
string. A single colon comes after each name, separating the name
|
||||
from the value. A single comma separates a value from a following
|
||||
name. The names within an object SHOULD be unique.
|
||||
|
||||
object = begin-object [ member *( value-separator member ) ]
|
||||
end-object
|
||||
|
||||
member = string name-separator value
|
||||
|
||||
2.3. Arrays
|
||||
|
||||
An array structure is represented as square brackets surrounding zero
|
||||
or more values (or elements). Elements are separated by commas.
|
||||
|
||||
array = begin-array [ value *( value-separator value ) ] end-array
|
||||
|
||||
2.4. Numbers
|
||||
|
||||
The representation of numbers is similar to that used in most
|
||||
programming languages. A number contains an integer component that
|
||||
may be prefixed with an optional minus sign, which may be followed by
|
||||
a fraction part and/or an exponent part.
|
||||
|
||||
Octal and hex forms are not allowed. Leading zeros are not allowed.
|
||||
|
||||
A fraction part is a decimal point followed by one or more digits.
|
||||
|
||||
An exponent part begins with the letter E in upper or lowercase,
|
||||
which may be followed by a plus or minus sign. The E and optional
|
||||
sign are followed by one or more digits.
|
||||
|
||||
Numeric values that cannot be represented as sequences of digits
|
||||
(such as Infinity and NaN) are not permitted.
|
||||
|
||||
|
||||
|
||||
Crockford Informational [Page 3]
|
||||
|
||||
RFC 4627 JSON July 2006
|
||||
|
||||
|
||||
number = [ minus ] int [ frac ] [ exp ]
|
||||
|
||||
decimal-point = %x2E ; .
|
||||
|
||||
digit1-9 = %x31-39 ; 1-9
|
||||
|
||||
e = %x65 / %x45 ; e E
|
||||
|
||||
exp = e [ minus / plus ] 1*DIGIT
|
||||
|
||||
frac = decimal-point 1*DIGIT
|
||||
|
||||
int = zero / ( digit1-9 *DIGIT )
|
||||
|
||||
minus = %x2D ; -
|
||||
|
||||
plus = %x2B ; +
|
||||
|
||||
zero = %x30 ; 0
|
||||
|
||||
2.5. Strings
|
||||
|
||||
The representation of strings is similar to conventions used in the C
|
||||
family of programming languages. A string begins and ends with
|
||||
quotation marks. All Unicode characters may be placed within the
|
||||
quotation marks except for the characters that must be escaped:
|
||||
quotation mark, reverse solidus, and the control characters (U+0000
|
||||
through U+001F).
|
||||
|
||||
Any character may be escaped. If the character is in the Basic
|
||||
Multilingual Plane (U+0000 through U+FFFF), then it may be
|
||||
represented as a six-character sequence: a reverse solidus, followed
|
||||
by the lowercase letter u, followed by four hexadecimal digits that
|
||||
encode the character's code point. The hexadecimal letters A though
|
||||
F can be upper or lowercase. So, for example, a string containing
|
||||
only a single reverse solidus character may be represented as
|
||||
"\u005C".
|
||||
|
||||
Alternatively, there are two-character sequence escape
|
||||
representations of some popular characters. So, for example, a
|
||||
string containing only a single reverse solidus character may be
|
||||
represented more compactly as "\\".
|
||||
|
||||
To escape an extended character that is not in the Basic Multilingual
|
||||
Plane, the character is represented as a twelve-character sequence,
|
||||
encoding the UTF-16 surrogate pair. So, for example, a string
|
||||
containing only the G clef character (U+1D11E) may be represented as
|
||||
"\uD834\uDD1E".
|
||||
|
||||
|
||||
|
||||
Crockford Informational [Page 4]
|
||||
|
||||
RFC 4627 JSON July 2006
|
||||
|
||||
|
||||
string = quotation-mark *char quotation-mark
|
||||
|
||||
char = unescaped /
|
||||
escape (
|
||||
%x22 / ; " quotation mark U+0022
|
||||
%x5C / ; \ reverse solidus U+005C
|
||||
%x2F / ; / solidus U+002F
|
||||
%x62 / ; b backspace U+0008
|
||||
%x66 / ; f form feed U+000C
|
||||
%x6E / ; n line feed U+000A
|
||||
%x72 / ; r carriage return U+000D
|
||||
%x74 / ; t tab U+0009
|
||||
%x75 4HEXDIG ) ; uXXXX U+XXXX
|
||||
|
||||
escape = %x5C ; \
|
||||
|
||||
quotation-mark = %x22 ; "
|
||||
|
||||
unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
|
||||
|
||||
3. Encoding
|
||||
|
||||
JSON text SHALL be encoded in Unicode. The default encoding is
|
||||
UTF-8.
|
||||
|
||||
Since the first two characters of a JSON text will always be ASCII
|
||||
characters [RFC0020], it is possible to determine whether an octet
|
||||
stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking
|
||||
at the pattern of nulls in the first four octets.
|
||||
|
||||
00 00 00 xx UTF-32BE
|
||||
00 xx 00 xx UTF-16BE
|
||||
xx 00 00 00 UTF-32LE
|
||||
xx 00 xx 00 UTF-16LE
|
||||
xx xx xx xx UTF-8
|
||||
|
||||
4. Parsers
|
||||
|
||||
A JSON parser transforms a JSON text into another representation. A
|
||||
JSON parser MUST accept all texts that conform to the JSON grammar.
|
||||
A JSON parser MAY accept non-JSON forms or extensions.
|
||||
|
||||
An implementation may set limits on the size of texts that it
|
||||
accepts. An implementation may set limits on the maximum depth of
|
||||
nesting. An implementation may set limits on the range of numbers.
|
||||
An implementation may set limits on the length and character contents
|
||||
of strings.
|
||||
|
||||
|
||||
|
||||
|
||||
Crockford Informational [Page 5]
|
||||
|
||||
RFC 4627 JSON July 2006
|
||||
|
||||
|
||||
5. Generators
|
||||
|
||||
A JSON generator produces JSON text. The resulting text MUST
|
||||
strictly conform to the JSON grammar.
|
||||
|
||||
6. IANA Considerations
|
||||
|
||||
The MIME media type for JSON text is application/json.
|
||||
|
||||
Type name: application
|
||||
|
||||
Subtype name: json
|
||||
|
||||
Required parameters: n/a
|
||||
|
||||
Optional parameters: n/a
|
||||
|
||||
Encoding considerations: 8bit if UTF-8; binary if UTF-16 or UTF-32
|
||||
|
||||
JSON may be represented using UTF-8, UTF-16, or UTF-32. When JSON
|
||||
is written in UTF-8, JSON is 8bit compatible. When JSON is
|
||||
written in UTF-16 or UTF-32, the binary content-transfer-encoding
|
||||
must be used.
|
||||
|
||||
Security considerations:
|
||||
|
||||
Generally there are security issues with scripting languages. JSON
|
||||
is a subset of JavaScript, but it is a safe subset that excludes
|
||||
assignment and invocation.
|
||||
|
||||
A JSON text can be safely passed into JavaScript's eval() function
|
||||
(which compiles and executes a string) if all the characters not
|
||||
enclosed in strings are in the set of characters that form JSON
|
||||
tokens. This can be quickly determined in JavaScript with two
|
||||
regular expressions and calls to the test and replace methods.
|
||||
|
||||
var my_JSON_object = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
|
||||
text.replace(/"(\\.|[^"\\])*"/g, ''))) &&
|
||||
eval('(' + text + ')');
|
||||
|
||||
Interoperability considerations: n/a
|
||||
|
||||
Published specification: RFC 4627
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Crockford Informational [Page 6]
|
||||
|
||||
RFC 4627 JSON July 2006
|
||||
|
||||
|
||||
Applications that use this media type:
|
||||
|
||||
JSON has been used to exchange data between applications written
|
||||
in all of these programming languages: ActionScript, C, C#,
|
||||
ColdFusion, Common Lisp, E, Erlang, Java, JavaScript, Lua,
|
||||
Objective CAML, Perl, PHP, Python, Rebol, Ruby, and Scheme.
|
||||
|
||||
Additional information:
|
||||
|
||||
Magic number(s): n/a
|
||||
File extension(s): .json
|
||||
Macintosh file type code(s): TEXT
|
||||
|
||||
Person & email address to contact for further information:
|
||||
Douglas Crockford
|
||||
douglas@crockford.com
|
||||
|
||||
Intended usage: COMMON
|
||||
|
||||
Restrictions on usage: none
|
||||
|
||||
Author:
|
||||
Douglas Crockford
|
||||
douglas@crockford.com
|
||||
|
||||
Change controller:
|
||||
Douglas Crockford
|
||||
douglas@crockford.com
|
||||
|
||||
7. Security Considerations
|
||||
|
||||
See Security Considerations in Section 6.
|
||||
|
||||
8. Examples
|
||||
|
||||
This is a JSON object:
|
||||
|
||||
{
|
||||
"Image": {
|
||||
"Width": 800,
|
||||
"Height": 600,
|
||||
"Title": "View from 15th Floor",
|
||||
"Thumbnail": {
|
||||
"Url": "http://www.example.com/image/481989943",
|
||||
"Height": 125,
|
||||
"Width": "100"
|
||||
},
|
||||
"IDs": [116, 943, 234, 38793]
|
||||
|
||||
|
||||
|
||||
Crockford Informational [Page 7]
|
||||
|
||||
RFC 4627 JSON July 2006
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Its Image member is an object whose Thumbnail member is an object
|
||||
and whose IDs member is an array of numbers.
|
||||
|
||||
This is a JSON array containing two objects:
|
||||
|
||||
[
|
||||
{
|
||||
"precision": "zip",
|
||||
"Latitude": 37.7668,
|
||||
"Longitude": -122.3959,
|
||||
"Address": "",
|
||||
"City": "SAN FRANCISCO",
|
||||
"State": "CA",
|
||||
"Zip": "94107",
|
||||
"Country": "US"
|
||||
},
|
||||
{
|
||||
"precision": "zip",
|
||||
"Latitude": 37.371991,
|
||||
"Longitude": -122.026020,
|
||||
"Address": "",
|
||||
"City": "SUNNYVALE",
|
||||
"State": "CA",
|
||||
"Zip": "94085",
|
||||
"Country": "US"
|
||||
}
|
||||
]
|
||||
|
||||
9. References
|
||||
|
||||
9.1. Normative References
|
||||
|
||||
[ECMA] European Computer Manufacturers Association, "ECMAScript
|
||||
Language Specification 3rd Edition", December 1999,
|
||||
<http://www.ecma-international.org/publications/files/
|
||||
ecma-st/ECMA-262.pdf>.
|
||||
|
||||
[RFC0020] Cerf, V., "ASCII format for network interchange", RFC 20,
|
||||
October 1969.
|
||||
|
||||
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
|
||||
Requirement Levels", BCP 14, RFC 2119, March 1997.
|
||||
|
||||
[RFC4234] Crocker, D. and P. Overell, "Augmented BNF for Syntax
|
||||
Specifications: ABNF", RFC 4234, October 2005.
|
||||
|
||||
|
||||
|
||||
Crockford Informational [Page 8]
|
||||
|
||||
RFC 4627 JSON July 2006
|
||||
|
||||
|
||||
[UNICODE] The Unicode Consortium, "The Unicode Standard Version 4.0",
|
||||
2003, <http://www.unicode.org/versions/Unicode4.1.0/>.
|
||||
|
||||
Author's Address
|
||||
|
||||
Douglas Crockford
|
||||
JSON.org
|
||||
EMail: douglas@crockford.com
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Crockford Informational [Page 9]
|
||||
|
||||
RFC 4627 JSON July 2006
|
||||
|
||||
|
||||
Full Copyright Statement
|
||||
|
||||
Copyright (C) The Internet Society (2006).
|
||||
|
||||
This document is subject to the rights, licenses and restrictions
|
||||
contained in BCP 78, and except as set forth therein, the authors
|
||||
retain all their rights.
|
||||
|
||||
This document and the information contained herein are provided on an
|
||||
"AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
|
||||
OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
|
||||
ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
|
||||
INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
Intellectual Property
|
||||
|
||||
The IETF takes no position regarding the validity or scope of any
|
||||
Intellectual Property Rights or other rights that might be claimed to
|
||||
pertain to the implementation or use of the technology described in
|
||||
this document or the extent to which any license under such rights
|
||||
might or might not be available; nor does it represent that it has
|
||||
made any independent effort to identify any such rights. Information
|
||||
on the procedures with respect to rights in RFC documents can be
|
||||
found in BCP 78 and BCP 79.
|
||||
|
||||
Copies of IPR disclosures made to the IETF Secretariat and any
|
||||
assurances of licenses to be made available, or the result of an
|
||||
attempt made to obtain a general license or permission for the use of
|
||||
such proprietary rights by implementers or users of this
|
||||
specification can be obtained from the IETF on-line IPR repository at
|
||||
http://www.ietf.org/ipr.
|
||||
|
||||
The IETF invites any interested party to bring to its attention any
|
||||
copyrights, patents or patent applications, or other proprietary
|
||||
rights that may cover technology that may be required to implement
|
||||
this standard. Please address the information to the IETF at
|
||||
ietf-ipr@ietf.org.
|
||||
|
||||
Acknowledgement
|
||||
|
||||
Funding for the RFC Editor function is provided by the IETF
|
||||
Administrative Support Activity (IASA).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Crockford Informational [Page 10]
|
||||
|
@ -1,253 +0,0 @@
|
||||
/* strbuf - String buffer routines
|
||||
*
|
||||
* Copyright (c) 2010-2012 Mark Pulford <mark@kyne.com.au>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "strbuf.h"
|
||||
#include "cjson_mem.h"
|
||||
|
||||
int strbuf_init(strbuf_t *s, int len)
|
||||
{
|
||||
int size;
|
||||
|
||||
if (len <= 0)
|
||||
size = STRBUF_DEFAULT_SIZE;
|
||||
else
|
||||
size = len + 1; /* \0 terminator */
|
||||
|
||||
s->buf = NULL;
|
||||
s->size = size;
|
||||
s->length = 0;
|
||||
s->increment = STRBUF_DEFAULT_INCREMENT;
|
||||
s->dynamic = 0;
|
||||
s->reallocs = 0;
|
||||
s->debug = 0;
|
||||
|
||||
s->buf = (char *)cjson_mem_malloc(size);
|
||||
if (!s->buf){
|
||||
NODE_ERR("not enough memory\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
strbuf_ensure_null(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
strbuf_t *strbuf_new(int len)
|
||||
{
|
||||
strbuf_t *s;
|
||||
|
||||
s = (strbuf_t *)cjson_mem_malloc(sizeof(strbuf_t));
|
||||
if (!s){
|
||||
NODE_ERR("not enough memory\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strbuf_init(s, len);
|
||||
|
||||
/* Dynamic strbuf allocation / deallocation */
|
||||
s->dynamic = 1;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int strbuf_set_increment(strbuf_t *s, int increment)
|
||||
{
|
||||
/* Increment > 0: Linear buffer growth rate
|
||||
* Increment < -1: Exponential buffer growth rate */
|
||||
if (increment == 0 || increment == -1){
|
||||
NODE_ERR("BUG: Invalid string increment");
|
||||
return -1;
|
||||
}
|
||||
|
||||
s->increment = increment;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void debug_stats(strbuf_t *s)
|
||||
{
|
||||
if (s->debug) {
|
||||
NODE_ERR("strbuf(%lx) reallocs: %d, length: %d, size: %d\n",
|
||||
(long)s, s->reallocs, s->length, s->size);
|
||||
}
|
||||
}
|
||||
|
||||
/* If strbuf_t has not been dynamically allocated, strbuf_free() can
|
||||
* be called any number of times strbuf_init() */
|
||||
void strbuf_free(strbuf_t *s)
|
||||
{
|
||||
debug_stats(s);
|
||||
|
||||
if (s->buf) {
|
||||
free(s->buf);
|
||||
s->buf = NULL;
|
||||
}
|
||||
if (s->dynamic)
|
||||
free(s);
|
||||
}
|
||||
|
||||
char *strbuf_free_to_string(strbuf_t *s, int *len)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
debug_stats(s);
|
||||
|
||||
strbuf_ensure_null(s);
|
||||
|
||||
buf = s->buf;
|
||||
if (len)
|
||||
*len = s->length;
|
||||
|
||||
if (s->dynamic)
|
||||
free(s);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int calculate_new_size(strbuf_t *s, int len)
|
||||
{
|
||||
int reqsize, newsize;
|
||||
|
||||
if (len <= 0){
|
||||
NODE_ERR("BUG: Invalid strbuf length requested");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Ensure there is room for optional NULL termination */
|
||||
reqsize = len + 1;
|
||||
|
||||
/* If the user has requested to shrink the buffer, do it exactly */
|
||||
if (s->size > reqsize)
|
||||
return reqsize;
|
||||
|
||||
newsize = s->size;
|
||||
if (s->increment < 0) {
|
||||
/* Exponential sizing */
|
||||
while (newsize < reqsize)
|
||||
newsize *= -s->increment;
|
||||
} else {
|
||||
/* Linear sizing */
|
||||
newsize = (((reqsize -1) / s->increment) + 1) * s->increment;
|
||||
}
|
||||
|
||||
return newsize;
|
||||
}
|
||||
|
||||
|
||||
/* Ensure strbuf can handle a string length bytes long (ignoring NULL
|
||||
* optional termination). */
|
||||
int strbuf_resize(strbuf_t *s, int len)
|
||||
{
|
||||
int newsize;
|
||||
|
||||
newsize = calculate_new_size(s, len);
|
||||
|
||||
if (s->debug > 1) {
|
||||
NODE_ERR("strbuf(%lx) resize: %d => %d\n",
|
||||
(long)s, s->size, newsize);
|
||||
}
|
||||
|
||||
s->buf = (char *)cjson_mem_realloc(s->buf, newsize);
|
||||
if (!s->buf){
|
||||
NODE_ERR("not enough memory");
|
||||
return -1;
|
||||
}
|
||||
s->size = newsize;
|
||||
s->reallocs++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void strbuf_append_string(strbuf_t *s, const char *str)
|
||||
{
|
||||
int space, i;
|
||||
|
||||
space = strbuf_empty_length(s);
|
||||
|
||||
for (i = 0; str[i]; i++) {
|
||||
if (space < 1) {
|
||||
strbuf_resize(s, s->length + 1);
|
||||
space = strbuf_empty_length(s);
|
||||
}
|
||||
|
||||
s->buf[s->length] = str[i];
|
||||
s->length++;
|
||||
space--;
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
/* strbuf_append_fmt() should only be used when an upper bound
|
||||
* is known for the output string. */
|
||||
void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
int fmt_len;
|
||||
|
||||
strbuf_ensure_empty_length(s, len);
|
||||
|
||||
va_start(arg, fmt);
|
||||
fmt_len = vsnprintf(s->buf + s->length, len, fmt, arg);
|
||||
va_end(arg);
|
||||
|
||||
if (fmt_len < 0)
|
||||
die("BUG: Unable to convert number"); /* This should never happen.. */
|
||||
|
||||
s->length += fmt_len;
|
||||
}
|
||||
|
||||
/* strbuf_append_fmt_retry() can be used when the there is no known
|
||||
* upper bound for the output string. */
|
||||
void strbuf_append_fmt_retry(strbuf_t *s, const char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
int fmt_len, try;
|
||||
int empty_len;
|
||||
|
||||
/* If the first attempt to append fails, resize the buffer appropriately
|
||||
* and try again */
|
||||
for (try = 0; ; try++) {
|
||||
va_start(arg, fmt);
|
||||
/* Append the new formatted string */
|
||||
/* fmt_len is the length of the string required, excluding the
|
||||
* trailing NULL */
|
||||
empty_len = strbuf_empty_length(s);
|
||||
/* Add 1 since there is also space to store the terminating NULL. */
|
||||
fmt_len = vsnprintf(s->buf + s->length, empty_len + 1, fmt, arg);
|
||||
va_end(arg);
|
||||
|
||||
if (fmt_len <= empty_len)
|
||||
break; /* SUCCESS */
|
||||
if (try > 0)
|
||||
die("BUG: length of formatted string changed");
|
||||
|
||||
strbuf_resize(s, s->length + fmt_len);
|
||||
}
|
||||
|
||||
s->length += fmt_len;
|
||||
}
|
||||
#endif
|
||||
/* vi:ai et sw=4 ts=4:
|
||||
*/
|
@ -1,155 +0,0 @@
|
||||
/* strbuf - String buffer routines
|
||||
*
|
||||
* Copyright (c) 2010-2012 Mark Pulford <mark@kyne.com.au>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include "user_config.h"
|
||||
|
||||
/* Size: Total bytes allocated to *buf
|
||||
* Length: String length, excluding optional NULL terminator.
|
||||
* Increment: Allocation increments when resizing the string buffer.
|
||||
* Dynamic: True if created via strbuf_new()
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
char *buf;
|
||||
int size;
|
||||
int length;
|
||||
int increment;
|
||||
int dynamic;
|
||||
int reallocs;
|
||||
int debug;
|
||||
} strbuf_t;
|
||||
|
||||
#ifndef STRBUF_DEFAULT_SIZE
|
||||
#define STRBUF_DEFAULT_SIZE 1023
|
||||
#endif
|
||||
#ifndef STRBUF_DEFAULT_INCREMENT
|
||||
#define STRBUF_DEFAULT_INCREMENT -2
|
||||
#endif
|
||||
|
||||
/* Initialise */
|
||||
extern strbuf_t *strbuf_new(int len);
|
||||
extern int strbuf_init(strbuf_t *s, int len);
|
||||
extern int strbuf_set_increment(strbuf_t *s, int increment);
|
||||
|
||||
/* Release */
|
||||
extern void strbuf_free(strbuf_t *s);
|
||||
extern char *strbuf_free_to_string(strbuf_t *s, int *len);
|
||||
|
||||
/* Management */
|
||||
extern int strbuf_resize(strbuf_t *s, int len);
|
||||
static int strbuf_empty_length(strbuf_t *s);
|
||||
static int strbuf_length(strbuf_t *s);
|
||||
static char *strbuf_string(strbuf_t *s, int *len);
|
||||
static void strbuf_ensure_empty_length(strbuf_t *s, int len);
|
||||
static char *strbuf_empty_ptr(strbuf_t *s);
|
||||
static void strbuf_extend_length(strbuf_t *s, int len);
|
||||
|
||||
/* Update */
|
||||
extern void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...);
|
||||
extern void strbuf_append_fmt_retry(strbuf_t *s, const char *format, ...);
|
||||
static void strbuf_append_mem(strbuf_t *s, const char *c, int len);
|
||||
extern void strbuf_append_string(strbuf_t *s, const char *str);
|
||||
static void strbuf_append_char(strbuf_t *s, const char c);
|
||||
static void strbuf_ensure_null(strbuf_t *s);
|
||||
|
||||
/* Reset string for before use */
|
||||
static inline void strbuf_reset(strbuf_t *s)
|
||||
{
|
||||
s->length = 0;
|
||||
}
|
||||
|
||||
static inline int strbuf_allocated(strbuf_t *s)
|
||||
{
|
||||
return s->buf != NULL;
|
||||
}
|
||||
|
||||
/* Return bytes remaining in the string buffer
|
||||
* Ensure there is space for a NULL terminator. */
|
||||
static inline int strbuf_empty_length(strbuf_t *s)
|
||||
{
|
||||
return s->size - s->length - 1;
|
||||
}
|
||||
|
||||
static inline void strbuf_ensure_empty_length(strbuf_t *s, int len)
|
||||
{
|
||||
if (len > strbuf_empty_length(s))
|
||||
strbuf_resize(s, s->length + len);
|
||||
}
|
||||
|
||||
static inline char *strbuf_empty_ptr(strbuf_t *s)
|
||||
{
|
||||
return s->buf + s->length;
|
||||
}
|
||||
|
||||
static inline void strbuf_extend_length(strbuf_t *s, int len)
|
||||
{
|
||||
s->length += len;
|
||||
}
|
||||
|
||||
static inline int strbuf_length(strbuf_t *s)
|
||||
{
|
||||
return s->length;
|
||||
}
|
||||
|
||||
static inline void strbuf_append_char(strbuf_t *s, const char c)
|
||||
{
|
||||
strbuf_ensure_empty_length(s, 1);
|
||||
s->buf[s->length++] = c;
|
||||
}
|
||||
|
||||
static inline void strbuf_append_char_unsafe(strbuf_t *s, const char c)
|
||||
{
|
||||
s->buf[s->length++] = c;
|
||||
}
|
||||
|
||||
static inline void strbuf_append_mem(strbuf_t *s, const char *c, int len)
|
||||
{
|
||||
strbuf_ensure_empty_length(s, len);
|
||||
memcpy(s->buf + s->length, c, len);
|
||||
s->length += len;
|
||||
}
|
||||
|
||||
static inline void strbuf_append_mem_unsafe(strbuf_t *s, const char *c, int len)
|
||||
{
|
||||
memcpy(s->buf + s->length, c, len);
|
||||
s->length += len;
|
||||
}
|
||||
|
||||
static inline void strbuf_ensure_null(strbuf_t *s)
|
||||
{
|
||||
s->buf[s->length] = 0;
|
||||
}
|
||||
|
||||
static inline char *strbuf_string(strbuf_t *s, int *len)
|
||||
{
|
||||
if (len)
|
||||
*len = s->length;
|
||||
|
||||
return s->buf;
|
||||
}
|
||||
|
||||
/* vi:ai et sw=4 ts=4:
|
||||
*/
|
@ -1,4 +0,0 @@
|
||||
These JSON examples were taken from the JSON website
|
||||
(http://json.org/example.html) and RFC 4627.
|
||||
|
||||
Used with permission.
|
@ -1,131 +0,0 @@
|
||||
#!/usr/bin/env lua
|
||||
|
||||
-- This benchmark script measures wall clock time and should be
|
||||
-- run on an unloaded system.
|
||||
--
|
||||
-- Your Mileage May Vary.
|
||||
--
|
||||
-- Mark Pulford <mark@kyne.com.au>
|
||||
|
||||
local json_module = os.getenv("JSON_MODULE") or "cjson"
|
||||
|
||||
require "socket"
|
||||
local json = require(json_module)
|
||||
local util = require "cjson.util"
|
||||
|
||||
local function find_func(mod, funcnames)
|
||||
for _, v in ipairs(funcnames) do
|
||||
if mod[v] then
|
||||
return mod[v]
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
local json_encode = find_func(json, { "encode", "Encode", "to_string", "stringify", "json" })
|
||||
local json_decode = find_func(json, { "decode", "Decode", "to_value", "parse" })
|
||||
|
||||
local function average(t)
|
||||
local total = 0
|
||||
for _, v in ipairs(t) do
|
||||
total = total + v
|
||||
end
|
||||
return total / #t
|
||||
end
|
||||
|
||||
function benchmark(tests, seconds, rep)
|
||||
local function bench(func, iter)
|
||||
-- Use socket.gettime() to measure microsecond resolution
|
||||
-- wall clock time.
|
||||
local t = socket.gettime()
|
||||
for i = 1, iter do
|
||||
func(i)
|
||||
end
|
||||
t = socket.gettime() - t
|
||||
|
||||
-- Don't trust any results when the run lasted for less than a
|
||||
-- millisecond - return nil.
|
||||
if t < 0.001 then
|
||||
return nil
|
||||
end
|
||||
|
||||
return (iter / t)
|
||||
end
|
||||
|
||||
-- Roughly calculate the number of interations required
|
||||
-- to obtain a particular time period.
|
||||
local function calc_iter(func, seconds)
|
||||
local iter = 1
|
||||
local rate
|
||||
-- Warm up the bench function first.
|
||||
func()
|
||||
while not rate do
|
||||
rate = bench(func, iter)
|
||||
iter = iter * 10
|
||||
end
|
||||
return math.ceil(seconds * rate)
|
||||
end
|
||||
|
||||
local test_results = {}
|
||||
for name, func in pairs(tests) do
|
||||
-- k(number), v(string)
|
||||
-- k(string), v(function)
|
||||
-- k(number), v(function)
|
||||
if type(func) == "string" then
|
||||
name = func
|
||||
func = _G[name]
|
||||
end
|
||||
|
||||
local iter = calc_iter(func, seconds)
|
||||
|
||||
local result = {}
|
||||
for i = 1, rep do
|
||||
result[i] = bench(func, iter)
|
||||
end
|
||||
|
||||
-- Remove the slowest half (round down) of the result set
|
||||
table.sort(result)
|
||||
for i = 1, math.floor(#result / 2) do
|
||||
table.remove(result, 1)
|
||||
end
|
||||
|
||||
test_results[name] = average(result)
|
||||
end
|
||||
|
||||
return test_results
|
||||
end
|
||||
|
||||
function bench_file(filename)
|
||||
local data_json = util.file_load(filename)
|
||||
local data_obj = json_decode(data_json)
|
||||
|
||||
local function test_encode()
|
||||
json_encode(data_obj)
|
||||
end
|
||||
local function test_decode()
|
||||
json_decode(data_json)
|
||||
end
|
||||
|
||||
local tests = {}
|
||||
if json_encode then tests.encode = test_encode end
|
||||
if json_decode then tests.decode = test_decode end
|
||||
|
||||
return benchmark(tests, 0.1, 5)
|
||||
end
|
||||
|
||||
-- Optionally load any custom configuration required for this module
|
||||
local success, data = pcall(util.file_load, ("bench-%s.lua"):format(json_module))
|
||||
if success then
|
||||
util.run_script(data, _G)
|
||||
configure(json)
|
||||
end
|
||||
|
||||
for i = 1, #arg do
|
||||
local results = bench_file(arg[i])
|
||||
for k, v in pairs(results) do
|
||||
print(("%s\t%s\t%d"):format(arg[i], k, v))
|
||||
end
|
||||
end
|
||||
|
||||
-- vi:ai et sw=4 ts=4:
|
@ -1,22 +0,0 @@
|
||||
{
|
||||
"glossary": {
|
||||
"title": "example glossary",
|
||||
"GlossDiv": {
|
||||
"title": "S",
|
||||
"GlossList": {
|
||||
"GlossEntry": {
|
||||
"ID": "SGML",
|
||||
"SortAs": "SGML",
|
||||
"GlossTerm": "Standard Generalized Mark up Language",
|
||||
"Acronym": "SGML",
|
||||
"Abbrev": "ISO 8879:1986",
|
||||
"GlossDef": {
|
||||
"para": "A meta-markup language, used to create markup languages such as DocBook.",
|
||||
"GlossSeeAlso": ["GML", "XML"]
|
||||
},
|
||||
"GlossSee": "markup"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
{"menu": {
|
||||
"id": "file",
|
||||
"value": "File",
|
||||
"popup": {
|
||||
"menuitem": [
|
||||
{"value": "New", "onclick": "CreateNewDoc()"},
|
||||
{"value": "Open", "onclick": "OpenDoc()"},
|
||||
{"value": "Close", "onclick": "CloseDoc()"}
|
||||
]
|
||||
}
|
||||
}}
|
@ -1,26 +0,0 @@
|
||||
{"widget": {
|
||||
"debug": "on",
|
||||
"window": {
|
||||
"title": "Sample Konfabulator Widget",
|
||||
"name": "main_window",
|
||||
"width": 500,
|
||||
"height": 500
|
||||
},
|
||||
"image": {
|
||||
"src": "Images/Sun.png",
|
||||
"name": "sun1",
|
||||
"hOffset": 250,
|
||||
"vOffset": 250,
|
||||
"alignment": "center"
|
||||
},
|
||||
"text": {
|
||||
"data": "Click Here",
|
||||
"size": 36,
|
||||
"style": "bold",
|
||||
"name": "text1",
|
||||
"hOffset": 250,
|
||||
"vOffset": 100,
|
||||
"alignment": "center",
|
||||
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
|
||||
}
|
||||
}}
|
@ -1,88 +0,0 @@
|
||||
{"web-app": {
|
||||
"servlet": [
|
||||
{
|
||||
"servlet-name": "cofaxCDS",
|
||||
"servlet-class": "org.cofax.cds.CDSServlet",
|
||||
"init-param": {
|
||||
"configGlossary:installationAt": "Philadelphia, PA",
|
||||
"configGlossary:adminEmail": "ksm@pobox.com",
|
||||
"configGlossary:poweredBy": "Cofax",
|
||||
"configGlossary:poweredByIcon": "/images/cofax.gif",
|
||||
"configGlossary:staticPath": "/content/static",
|
||||
"templateProcessorClass": "org.cofax.WysiwygTemplate",
|
||||
"templateLoaderClass": "org.cofax.FilesTemplateLoader",
|
||||
"templatePath": "templates",
|
||||
"templateOverridePath": "",
|
||||
"defaultListTemplate": "listTemplate.htm",
|
||||
"defaultFileTemplate": "articleTemplate.htm",
|
||||
"useJSP": false,
|
||||
"jspListTemplate": "listTemplate.jsp",
|
||||
"jspFileTemplate": "articleTemplate.jsp",
|
||||
"cachePackageTagsTrack": 200,
|
||||
"cachePackageTagsStore": 200,
|
||||
"cachePackageTagsRefresh": 60,
|
||||
"cacheTemplatesTrack": 100,
|
||||
"cacheTemplatesStore": 50,
|
||||
"cacheTemplatesRefresh": 15,
|
||||
"cachePagesTrack": 200,
|
||||
"cachePagesStore": 100,
|
||||
"cachePagesRefresh": 10,
|
||||
"cachePagesDirtyRead": 10,
|
||||
"searchEngineListTemplate": "forSearchEnginesList.htm",
|
||||
"searchEngineFileTemplate": "forSearchEngines.htm",
|
||||
"searchEngineRobotsDb": "WEB-INF/robots.db",
|
||||
"useDataStore": true,
|
||||
"dataStoreClass": "org.cofax.SqlDataStore",
|
||||
"redirectionClass": "org.cofax.SqlRedirection",
|
||||
"dataStoreName": "cofax",
|
||||
"dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver",
|
||||
"dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon",
|
||||
"dataStoreUser": "sa",
|
||||
"dataStorePassword": "dataStoreTestQuery",
|
||||
"dataStoreTestQuery": "SET NOCOUNT ON;select test='test';",
|
||||
"dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log",
|
||||
"dataStoreInitConns": 10,
|
||||
"dataStoreMaxConns": 100,
|
||||
"dataStoreConnUsageLimit": 100,
|
||||
"dataStoreLogLevel": "debug",
|
||||
"maxUrlLength": 500}},
|
||||
{
|
||||
"servlet-name": "cofaxEmail",
|
||||
"servlet-class": "org.cofax.cds.EmailServlet",
|
||||
"init-param": {
|
||||
"mailHost": "mail1",
|
||||
"mailHostOverride": "mail2"}},
|
||||
{
|
||||
"servlet-name": "cofaxAdmin",
|
||||
"servlet-class": "org.cofax.cds.AdminServlet"},
|
||||
|
||||
{
|
||||
"servlet-name": "fileServlet",
|
||||
"servlet-class": "org.cofax.cds.FileServlet"},
|
||||
{
|
||||
"servlet-name": "cofaxTools",
|
||||
"servlet-class": "org.cofax.cms.CofaxToolsServlet",
|
||||
"init-param": {
|
||||
"templatePath": "toolstemplates/",
|
||||
"log": 1,
|
||||
"logLocation": "/usr/local/tomcat/logs/CofaxTools.log",
|
||||
"logMaxSize": "",
|
||||
"dataLog": 1,
|
||||
"dataLogLocation": "/usr/local/tomcat/logs/dataLog.log",
|
||||
"dataLogMaxSize": "",
|
||||
"removePageCache": "/content/admin/remove?cache=pages&id=",
|
||||
"removeTemplateCache": "/content/admin/remove?cache=templates&id=",
|
||||
"fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder",
|
||||
"lookInContext": 1,
|
||||
"adminGroupID": 4,
|
||||
"betaServer": true}}],
|
||||
"servlet-mapping": {
|
||||
"cofaxCDS": "/",
|
||||
"cofaxEmail": "/cofaxutil/aemail/*",
|
||||
"cofaxAdmin": "/admin/*",
|
||||
"fileServlet": "/static/*",
|
||||
"cofaxTools": "/tools/*"},
|
||||
|
||||
"taglib": {
|
||||
"taglib-uri": "cofax.tld",
|
||||
"taglib-location": "/WEB-INF/tlds/cofax.tld"}}}
|
@ -1,27 +0,0 @@
|
||||
{"menu": {
|
||||
"header": "SVG Viewer",
|
||||
"items": [
|
||||
{"id": "Open"},
|
||||
{"id": "OpenNew", "label": "Open New"},
|
||||
null,
|
||||
{"id": "ZoomIn", "label": "Zoom In"},
|
||||
{"id": "ZoomOut", "label": "Zoom Out"},
|
||||
{"id": "OriginalView", "label": "Original View"},
|
||||
null,
|
||||
{"id": "Quality"},
|
||||
{"id": "Pause"},
|
||||
{"id": "Mute"},
|
||||
null,
|
||||
{"id": "Find", "label": "Find..."},
|
||||
{"id": "FindAgain", "label": "Find Again"},
|
||||
{"id": "Copy"},
|
||||
{"id": "CopyAgain", "label": "Copy Again"},
|
||||
{"id": "CopySVG", "label": "Copy SVG"},
|
||||
{"id": "ViewSVG", "label": "View SVG"},
|
||||
{"id": "ViewSource", "label": "View Source"},
|
||||
{"id": "SaveAs", "label": "Save As"},
|
||||
null,
|
||||
{"id": "Help"},
|
||||
{"id": "About", "label": "About Adobe CVG Viewer..."}
|
||||
]
|
||||
}}
|
@ -1,23 +0,0 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
# Create test comparison data using a different UTF-8 implementation.
|
||||
|
||||
# The generated utf8.dat file must have the following MD5 sum:
|
||||
# cff03b039d850f370a7362f3313e5268
|
||||
|
||||
use strict;
|
||||
|
||||
# 0xD800 - 0xDFFF are used to encode supplementary codepoints
|
||||
# 0x10000 - 0x10FFFF are supplementary codepoints
|
||||
my (@codepoints) = (0 .. 0xD7FF, 0xE000 .. 0x10FFFF);
|
||||
|
||||
my $utf8 = pack("U*", @codepoints);
|
||||
defined($utf8) or die "Unable create UTF-8 string\n";
|
||||
|
||||
open(FH, ">:utf8", "utf8.dat")
|
||||
or die "Unable to open utf8.dat: $!\n";
|
||||
print FH $utf8
|
||||
or die "Unable to write utf8.dat\n";
|
||||
close(FH);
|
||||
|
||||
# vi:ai et sw=4 ts=4:
|
@ -1,7 +0,0 @@
|
||||
[ 0.110001,
|
||||
0.12345678910111,
|
||||
0.412454033640,
|
||||
2.6651441426902,
|
||||
2.718281828459,
|
||||
3.1415926535898,
|
||||
2.1406926327793 ]
|
@ -1 +0,0 @@
|
||||
"\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#$%&'()*+,-.\/0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u007f<37><66><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
|
@ -1,13 +0,0 @@
|
||||
{
|
||||
"Image": {
|
||||
"Width": 800,
|
||||
"Height": 600,
|
||||
"Title": "View from 15th Floor",
|
||||
"Thumbnail": {
|
||||
"Url": "http://www.example.com/image/481989943",
|
||||
"Height": 125,
|
||||
"Width": "100"
|
||||
},
|
||||
"IDs": [116, 943, 234, 38793]
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
[
|
||||
{
|
||||
"precision": "zip",
|
||||
"Latitude": 37.7668,
|
||||
"Longitude": -122.3959,
|
||||
"Address": "",
|
||||
"City": "SAN FRANCISCO",
|
||||
"State": "CA",
|
||||
"Zip": "94107",
|
||||
"Country": "US"
|
||||
},
|
||||
{
|
||||
"precision": "zip",
|
||||
"Latitude": 37.371991,
|
||||
"Longitude": -122.026020,
|
||||
"Address": "",
|
||||
"City": "SUNNYVALE",
|
||||
"State": "CA",
|
||||
"Zip": "94085",
|
||||
"Country": "US"
|
||||
}
|
||||
]
|
@ -1,425 +0,0 @@
|
||||
#!/usr/bin/env lua
|
||||
|
||||
-- Lua CJSON tests
|
||||
--
|
||||
-- Mark Pulford <mark@kyne.com.au>
|
||||
--
|
||||
-- Note: The output of this script is easier to read with "less -S"
|
||||
|
||||
local json = require "cjson"
|
||||
local json_safe = require "cjson.safe"
|
||||
local util = require "cjson.util"
|
||||
|
||||
local function gen_raw_octets()
|
||||
local chars = {}
|
||||
for i = 0, 255 do chars[i + 1] = string.char(i) end
|
||||
return table.concat(chars)
|
||||
end
|
||||
|
||||
-- Generate every UTF-16 codepoint, including supplementary codes
|
||||
local function gen_utf16_escaped()
|
||||
-- Create raw table escapes
|
||||
local utf16_escaped = {}
|
||||
local count = 0
|
||||
|
||||
local function append_escape(code)
|
||||
local esc = ('\\u%04X'):format(code)
|
||||
table.insert(utf16_escaped, esc)
|
||||
end
|
||||
|
||||
table.insert(utf16_escaped, '"')
|
||||
for i = 0, 0xD7FF do
|
||||
append_escape(i)
|
||||
end
|
||||
-- Skip 0xD800 - 0xDFFF since they are used to encode supplementary
|
||||
-- codepoints
|
||||
for i = 0xE000, 0xFFFF do
|
||||
append_escape(i)
|
||||
end
|
||||
-- Append surrogate pair for each supplementary codepoint
|
||||
for high = 0xD800, 0xDBFF do
|
||||
for low = 0xDC00, 0xDFFF do
|
||||
append_escape(high)
|
||||
append_escape(low)
|
||||
end
|
||||
end
|
||||
table.insert(utf16_escaped, '"')
|
||||
|
||||
return table.concat(utf16_escaped)
|
||||
end
|
||||
|
||||
function load_testdata()
|
||||
local data = {}
|
||||
|
||||
-- Data for 8bit raw <-> escaped octets tests
|
||||
data.octets_raw = gen_raw_octets()
|
||||
data.octets_escaped = util.file_load("octets-escaped.dat")
|
||||
|
||||
-- Data for \uXXXX -> UTF-8 test
|
||||
data.utf16_escaped = gen_utf16_escaped()
|
||||
|
||||
-- Load matching data for utf16_escaped
|
||||
local utf8_loaded
|
||||
utf8_loaded, data.utf8_raw = pcall(util.file_load, "utf8.dat")
|
||||
if not utf8_loaded then
|
||||
data.utf8_raw = "Failed to load utf8.dat - please run genutf8.pl"
|
||||
end
|
||||
|
||||
data.table_cycle = {}
|
||||
data.table_cycle[1] = data.table_cycle
|
||||
|
||||
local big = {}
|
||||
for i = 1, 1100 do
|
||||
big = { { 10, false, true, json.null }, "string", a = big }
|
||||
end
|
||||
data.deeply_nested_data = big
|
||||
|
||||
return data
|
||||
end
|
||||
|
||||
function test_decode_cycle(filename)
|
||||
local obj1 = json.decode(util.file_load(filename))
|
||||
local obj2 = json.decode(json.encode(obj1))
|
||||
return util.compare_values(obj1, obj2)
|
||||
end
|
||||
|
||||
-- Set up data used in tests
|
||||
local Inf = math.huge;
|
||||
local NaN = math.huge * 0;
|
||||
|
||||
local testdata = load_testdata()
|
||||
|
||||
local cjson_tests = {
|
||||
-- Test API variables
|
||||
{ "Check module name, version",
|
||||
function () return json._NAME, json._VERSION end, { },
|
||||
true, { "cjson", "2.1devel" } },
|
||||
|
||||
-- Test decoding simple types
|
||||
{ "Decode string",
|
||||
json.decode, { '"test string"' }, true, { "test string" } },
|
||||
{ "Decode numbers",
|
||||
json.decode, { '[ 0.0, -5e3, -1, 0.3e-3, 1023.2, 0e10 ]' },
|
||||
true, { { 0.0, -5000, -1, 0.0003, 1023.2, 0 } } },
|
||||
{ "Decode null",
|
||||
json.decode, { 'null' }, true, { json.null } },
|
||||
{ "Decode true",
|
||||
json.decode, { 'true' }, true, { true } },
|
||||
{ "Decode false",
|
||||
json.decode, { 'false' }, true, { false } },
|
||||
{ "Decode object with numeric keys",
|
||||
json.decode, { '{ "1": "one", "3": "three" }' },
|
||||
true, { { ["1"] = "one", ["3"] = "three" } } },
|
||||
{ "Decode object with string keys",
|
||||
json.decode, { '{ "a": "a", "b": "b" }' },
|
||||
true, { { a = "a", b = "b" } } },
|
||||
{ "Decode array",
|
||||
json.decode, { '[ "one", null, "three" ]' },
|
||||
true, { { "one", json.null, "three" } } },
|
||||
|
||||
-- Test decoding errors
|
||||
{ "Decode UTF-16BE [throw error]",
|
||||
json.decode, { '\0"\0"' },
|
||||
false, { "JSON parser does not support UTF-16 or UTF-32" } },
|
||||
{ "Decode UTF-16LE [throw error]",
|
||||
json.decode, { '"\0"\0' },
|
||||
false, { "JSON parser does not support UTF-16 or UTF-32" } },
|
||||
{ "Decode UTF-32BE [throw error]",
|
||||
json.decode, { '\0\0\0"' },
|
||||
false, { "JSON parser does not support UTF-16 or UTF-32" } },
|
||||
{ "Decode UTF-32LE [throw error]",
|
||||
json.decode, { '"\0\0\0' },
|
||||
false, { "JSON parser does not support UTF-16 or UTF-32" } },
|
||||
{ "Decode partial JSON [throw error]",
|
||||
json.decode, { '{ "unexpected eof": ' },
|
||||
false, { "Expected value but found T_END at character 21" } },
|
||||
{ "Decode with extra comma [throw error]",
|
||||
json.decode, { '{ "extra data": true }, false' },
|
||||
false, { "Expected the end but found T_COMMA at character 23" } },
|
||||
{ "Decode invalid escape code [throw error]",
|
||||
json.decode, { [[ { "bad escape \q code" } ]] },
|
||||
false, { "Expected object key string but found invalid escape code at character 16" } },
|
||||
{ "Decode invalid unicode escape [throw error]",
|
||||
json.decode, { [[ { "bad unicode \u0f6 escape" } ]] },
|
||||
false, { "Expected object key string but found invalid unicode escape code at character 17" } },
|
||||
{ "Decode invalid keyword [throw error]",
|
||||
json.decode, { ' [ "bad barewood", test ] ' },
|
||||
false, { "Expected value but found invalid token at character 20" } },
|
||||
{ "Decode invalid number #1 [throw error]",
|
||||
json.decode, { '[ -+12 ]' },
|
||||
false, { "Expected value but found invalid number at character 3" } },
|
||||
{ "Decode invalid number #2 [throw error]",
|
||||
json.decode, { '-v' },
|
||||
false, { "Expected value but found invalid number at character 1" } },
|
||||
{ "Decode invalid number exponent [throw error]",
|
||||
json.decode, { '[ 0.4eg10 ]' },
|
||||
false, { "Expected comma or array end but found invalid token at character 6" } },
|
||||
|
||||
-- Test decoding nested arrays / objects
|
||||
{ "Set decode_max_depth(5)",
|
||||
json.decode_max_depth, { 5 }, true, { 5 } },
|
||||
{ "Decode array at nested limit",
|
||||
json.decode, { '[[[[[ "nested" ]]]]]' },
|
||||
true, { {{{{{ "nested" }}}}} } },
|
||||
{ "Decode array over nested limit [throw error]",
|
||||
json.decode, { '[[[[[[ "nested" ]]]]]]' },
|
||||
false, { "Found too many nested data structures (6) at character 6" } },
|
||||
{ "Decode object at nested limit",
|
||||
json.decode, { '{"a":{"b":{"c":{"d":{"e":"nested"}}}}}' },
|
||||
true, { {a={b={c={d={e="nested"}}}}} } },
|
||||
{ "Decode object over nested limit [throw error]",
|
||||
json.decode, { '{"a":{"b":{"c":{"d":{"e":{"f":"nested"}}}}}}' },
|
||||
false, { "Found too many nested data structures (6) at character 26" } },
|
||||
{ "Set decode_max_depth(1000)",
|
||||
json.decode_max_depth, { 1000 }, true, { 1000 } },
|
||||
{ "Decode deeply nested array [throw error]",
|
||||
json.decode, { string.rep("[", 1100) .. '1100' .. string.rep("]", 1100)},
|
||||
false, { "Found too many nested data structures (1001) at character 1001" } },
|
||||
|
||||
-- Test encoding nested tables
|
||||
{ "Set encode_max_depth(5)",
|
||||
json.encode_max_depth, { 5 }, true, { 5 } },
|
||||
{ "Encode nested table as array at nested limit",
|
||||
json.encode, { {{{{{"nested"}}}}} }, true, { '[[[[["nested"]]]]]' } },
|
||||
{ "Encode nested table as array after nested limit [throw error]",
|
||||
json.encode, { { {{{{{"nested"}}}}} } },
|
||||
false, { "Cannot serialise, excessive nesting (6)" } },
|
||||
{ "Encode nested table as object at nested limit",
|
||||
json.encode, { {a={b={c={d={e="nested"}}}}} },
|
||||
true, { '{"a":{"b":{"c":{"d":{"e":"nested"}}}}}' } },
|
||||
{ "Encode nested table as object over nested limit [throw error]",
|
||||
json.encode, { {a={b={c={d={e={f="nested"}}}}}} },
|
||||
false, { "Cannot serialise, excessive nesting (6)" } },
|
||||
{ "Encode table with cycle [throw error]",
|
||||
json.encode, { testdata.table_cycle },
|
||||
false, { "Cannot serialise, excessive nesting (6)" } },
|
||||
{ "Set encode_max_depth(1000)",
|
||||
json.encode_max_depth, { 1000 }, true, { 1000 } },
|
||||
{ "Encode deeply nested data [throw error]",
|
||||
json.encode, { testdata.deeply_nested_data },
|
||||
false, { "Cannot serialise, excessive nesting (1001)" } },
|
||||
|
||||
-- Test encoding simple types
|
||||
{ "Encode null",
|
||||
json.encode, { json.null }, true, { 'null' } },
|
||||
{ "Encode true",
|
||||
json.encode, { true }, true, { 'true' } },
|
||||
{ "Encode false",
|
||||
json.encode, { false }, true, { 'false' } },
|
||||
{ "Encode empty object",
|
||||
json.encode, { { } }, true, { '{}' } },
|
||||
{ "Encode integer",
|
||||
json.encode, { 10 }, true, { '10' } },
|
||||
{ "Encode string",
|
||||
json.encode, { "hello" }, true, { '"hello"' } },
|
||||
{ "Encode Lua function [throw error]",
|
||||
json.encode, { function () end },
|
||||
false, { "Cannot serialise function: type not supported" } },
|
||||
|
||||
-- Test decoding invalid numbers
|
||||
{ "Set decode_invalid_numbers(true)",
|
||||
json.decode_invalid_numbers, { true }, true, { true } },
|
||||
{ "Decode hexadecimal",
|
||||
json.decode, { '0x6.ffp1' }, true, { 13.9921875 } },
|
||||
{ "Decode numbers with leading zero",
|
||||
json.decode, { '[ 0123, 00.33 ]' }, true, { { 123, 0.33 } } },
|
||||
{ "Decode +-Inf",
|
||||
json.decode, { '[ +Inf, Inf, -Inf ]' }, true, { { Inf, Inf, -Inf } } },
|
||||
{ "Decode +-Infinity",
|
||||
json.decode, { '[ +Infinity, Infinity, -Infinity ]' },
|
||||
true, { { Inf, Inf, -Inf } } },
|
||||
{ "Decode +-NaN",
|
||||
json.decode, { '[ +NaN, NaN, -NaN ]' }, true, { { NaN, NaN, NaN } } },
|
||||
{ "Decode Infrared (not infinity) [throw error]",
|
||||
json.decode, { 'Infrared' },
|
||||
false, { "Expected the end but found invalid token at character 4" } },
|
||||
{ "Decode Noodle (not NaN) [throw error]",
|
||||
json.decode, { 'Noodle' },
|
||||
false, { "Expected value but found invalid token at character 1" } },
|
||||
{ "Set decode_invalid_numbers(false)",
|
||||
json.decode_invalid_numbers, { false }, true, { false } },
|
||||
{ "Decode hexadecimal [throw error]",
|
||||
json.decode, { '0x6' },
|
||||
false, { "Expected value but found invalid number at character 1" } },
|
||||
{ "Decode numbers with leading zero [throw error]",
|
||||
json.decode, { '[ 0123, 00.33 ]' },
|
||||
false, { "Expected value but found invalid number at character 3" } },
|
||||
{ "Decode +-Inf [throw error]",
|
||||
json.decode, { '[ +Inf, Inf, -Inf ]' },
|
||||
false, { "Expected value but found invalid token at character 3" } },
|
||||
{ "Decode +-Infinity [throw error]",
|
||||
json.decode, { '[ +Infinity, Infinity, -Infinity ]' },
|
||||
false, { "Expected value but found invalid token at character 3" } },
|
||||
{ "Decode +-NaN [throw error]",
|
||||
json.decode, { '[ +NaN, NaN, -NaN ]' },
|
||||
false, { "Expected value but found invalid token at character 3" } },
|
||||
{ 'Set decode_invalid_numbers("on")',
|
||||
json.decode_invalid_numbers, { "on" }, true, { true } },
|
||||
|
||||
-- Test encoding invalid numbers
|
||||
{ "Set encode_invalid_numbers(false)",
|
||||
json.encode_invalid_numbers, { false }, true, { false } },
|
||||
{ "Encode NaN [throw error]",
|
||||
json.encode, { NaN },
|
||||
false, { "Cannot serialise number: must not be NaN or Infinity" } },
|
||||
{ "Encode Infinity [throw error]",
|
||||
json.encode, { Inf },
|
||||
false, { "Cannot serialise number: must not be NaN or Infinity" } },
|
||||
{ "Set encode_invalid_numbers(\"null\")",
|
||||
json.encode_invalid_numbers, { "null" }, true, { "null" } },
|
||||
{ "Encode NaN as null",
|
||||
json.encode, { NaN }, true, { "null" } },
|
||||
{ "Encode Infinity as null",
|
||||
json.encode, { Inf }, true, { "null" } },
|
||||
{ "Set encode_invalid_numbers(true)",
|
||||
json.encode_invalid_numbers, { true }, true, { true } },
|
||||
{ "Encode NaN",
|
||||
json.encode, { NaN }, true, { "NaN" } },
|
||||
{ "Encode +Infinity",
|
||||
json.encode, { Inf }, true, { "Infinity" } },
|
||||
{ "Encode -Infinity",
|
||||
json.encode, { -Inf }, true, { "-Infinity" } },
|
||||
{ 'Set encode_invalid_numbers("off")',
|
||||
json.encode_invalid_numbers, { "off" }, true, { false } },
|
||||
|
||||
-- Test encoding tables
|
||||
{ "Set encode_sparse_array(true, 2, 3)",
|
||||
json.encode_sparse_array, { true, 2, 3 }, true, { true, 2, 3 } },
|
||||
{ "Encode sparse table as array #1",
|
||||
json.encode, { { [3] = "sparse test" } },
|
||||
true, { '[null,null,"sparse test"]' } },
|
||||
{ "Encode sparse table as array #2",
|
||||
json.encode, { { [1] = "one", [4] = "sparse test" } },
|
||||
true, { '["one",null,null,"sparse test"]' } },
|
||||
{ "Encode sparse array as object",
|
||||
json.encode, { { [1] = "one", [5] = "sparse test" } },
|
||||
true, { '{"1":"one","5":"sparse test"}' } },
|
||||
{ "Encode table with numeric string key as object",
|
||||
json.encode, { { ["2"] = "numeric string key test" } },
|
||||
true, { '{"2":"numeric string key test"}' } },
|
||||
{ "Set encode_sparse_array(false)",
|
||||
json.encode_sparse_array, { false }, true, { false, 2, 3 } },
|
||||
{ "Encode table with incompatible key [throw error]",
|
||||
json.encode, { { [false] = "wrong" } },
|
||||
false, { "Cannot serialise boolean: table key must be a number or string" } },
|
||||
|
||||
-- Test escaping
|
||||
{ "Encode all octets (8-bit clean)",
|
||||
json.encode, { testdata.octets_raw }, true, { testdata.octets_escaped } },
|
||||
{ "Decode all escaped octets",
|
||||
json.decode, { testdata.octets_escaped }, true, { testdata.octets_raw } },
|
||||
{ "Decode single UTF-16 escape",
|
||||
json.decode, { [["\uF800"]] }, true, { "\239\160\128" } },
|
||||
{ "Decode all UTF-16 escapes (including surrogate combinations)",
|
||||
json.decode, { testdata.utf16_escaped }, true, { testdata.utf8_raw } },
|
||||
{ "Decode swapped surrogate pair [throw error]",
|
||||
json.decode, { [["\uDC00\uD800"]] },
|
||||
false, { "Expected value but found invalid unicode escape code at character 2" } },
|
||||
{ "Decode duplicate high surrogate [throw error]",
|
||||
json.decode, { [["\uDB00\uDB00"]] },
|
||||
false, { "Expected value but found invalid unicode escape code at character 2" } },
|
||||
{ "Decode duplicate low surrogate [throw error]",
|
||||
json.decode, { [["\uDB00\uDB00"]] },
|
||||
false, { "Expected value but found invalid unicode escape code at character 2" } },
|
||||
{ "Decode missing low surrogate [throw error]",
|
||||
json.decode, { [["\uDB00"]] },
|
||||
false, { "Expected value but found invalid unicode escape code at character 2" } },
|
||||
{ "Decode invalid low surrogate [throw error]",
|
||||
json.decode, { [["\uDB00\uD"]] },
|
||||
false, { "Expected value but found invalid unicode escape code at character 2" } },
|
||||
|
||||
-- Test locale support
|
||||
--
|
||||
-- The standard Lua interpreter is ANSI C online doesn't support locales
|
||||
-- by default. Force a known problematic locale to test strtod()/sprintf().
|
||||
{ "Set locale to cs_CZ (comma separator)", function ()
|
||||
os.setlocale("cs_CZ")
|
||||
json.new()
|
||||
end },
|
||||
{ "Encode number under comma locale",
|
||||
json.encode, { 1.5 }, true, { '1.5' } },
|
||||
{ "Decode number in array under comma locale",
|
||||
json.decode, { '[ 10, "test" ]' }, true, { { 10, "test" } } },
|
||||
{ "Revert locale to POSIX", function ()
|
||||
os.setlocale("C")
|
||||
json.new()
|
||||
end },
|
||||
|
||||
-- Test encode_keep_buffer() and enable_number_precision()
|
||||
{ "Set encode_keep_buffer(false)",
|
||||
json.encode_keep_buffer, { false }, true, { false } },
|
||||
{ "Set encode_number_precision(3)",
|
||||
json.encode_number_precision, { 3 }, true, { 3 } },
|
||||
{ "Encode number with precision 3",
|
||||
json.encode, { 1/3 }, true, { "0.333" } },
|
||||
{ "Set encode_number_precision(14)",
|
||||
json.encode_number_precision, { 14 }, true, { 14 } },
|
||||
{ "Set encode_keep_buffer(true)",
|
||||
json.encode_keep_buffer, { true }, true, { true } },
|
||||
|
||||
-- Test config API errors
|
||||
-- Function is listed as '?' due to pcall
|
||||
{ "Set encode_number_precision(0) [throw error]",
|
||||
json.encode_number_precision, { 0 },
|
||||
false, { "bad argument #1 to '?' (expected integer between 1 and 14)" } },
|
||||
{ "Set encode_number_precision(\"five\") [throw error]",
|
||||
json.encode_number_precision, { "five" },
|
||||
false, { "bad argument #1 to '?' (number expected, got string)" } },
|
||||
{ "Set encode_keep_buffer(nil, true) [throw error]",
|
||||
json.encode_keep_buffer, { nil, true },
|
||||
false, { "bad argument #2 to '?' (found too many arguments)" } },
|
||||
{ "Set encode_max_depth(\"wrong\") [throw error]",
|
||||
json.encode_max_depth, { "wrong" },
|
||||
false, { "bad argument #1 to '?' (number expected, got string)" } },
|
||||
{ "Set decode_max_depth(0) [throw error]",
|
||||
json.decode_max_depth, { "0" },
|
||||
false, { "bad argument #1 to '?' (expected integer between 1 and 2147483647)" } },
|
||||
{ "Set encode_invalid_numbers(-2) [throw error]",
|
||||
json.encode_invalid_numbers, { -2 },
|
||||
false, { "bad argument #1 to '?' (invalid option '-2')" } },
|
||||
{ "Set decode_invalid_numbers(true, false) [throw error]",
|
||||
json.decode_invalid_numbers, { true, false },
|
||||
false, { "bad argument #2 to '?' (found too many arguments)" } },
|
||||
{ "Set encode_sparse_array(\"not quite on\") [throw error]",
|
||||
json.encode_sparse_array, { "not quite on" },
|
||||
false, { "bad argument #1 to '?' (invalid option 'not quite on')" } },
|
||||
|
||||
{ "Reset Lua CJSON configuration", function () json = json.new() end },
|
||||
-- Wrap in a function to ensure the table returned by json.new() is used
|
||||
{ "Check encode_sparse_array()",
|
||||
function (...) return json.encode_sparse_array(...) end, { },
|
||||
true, { false, 2, 10 } },
|
||||
|
||||
{ "Encode (safe) simple value",
|
||||
json_safe.encode, { true },
|
||||
true, { "true" } },
|
||||
{ "Encode (safe) argument validation [throw error]",
|
||||
json_safe.encode, { "arg1", "arg2" },
|
||||
false, { "bad argument #1 to '?' (expected 1 argument)" } },
|
||||
{ "Decode (safe) error generation",
|
||||
json_safe.decode, { "Oops" },
|
||||
true, { nil, "Expected value but found invalid token at character 1" } },
|
||||
{ "Decode (safe) error generation after new()",
|
||||
function(...) return json_safe.new().decode(...) end, { "Oops" },
|
||||
true, { nil, "Expected value but found invalid token at character 1" } },
|
||||
}
|
||||
|
||||
print(("==> Testing Lua CJSON version %s\n"):format(json._VERSION))
|
||||
|
||||
util.run_test_group(cjson_tests)
|
||||
|
||||
for _, filename in ipairs(arg) do
|
||||
util.run_test("Decode cycle " .. filename, test_decode_cycle, { filename },
|
||||
true, { true })
|
||||
end
|
||||
|
||||
local pass, total = util.run_test_summary()
|
||||
|
||||
if pass == total then
|
||||
print("==> Summary: all tests succeeded")
|
||||
else
|
||||
print(("==> Summary: %d/%d tests failed"):format(total - pass, total))
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
-- vi:ai et sw=4 ts=4:
|
@ -1 +0,0 @@
|
||||
{ "array": [ 10, true, null ] }
|
@ -1,20 +0,0 @@
|
||||
Copyright (c) 2013 Toby Jaffey <toby@1248.io>
|
||||
2015 Zeroday Hong <zeroday@nodemcu.com> nodemcu.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
@ -1,45 +0,0 @@
|
||||
|
||||
#############################################################
|
||||
# Required variables for each makefile
|
||||
# Discard this section from all parent makefiles
|
||||
# Expected variables (with automatic defaults):
|
||||
# CSRCS (all "C" files in the dir)
|
||||
# SUBDIRS (all subdirs with a Makefile)
|
||||
# GEN_LIBS - list of libs to be generated ()
|
||||
# GEN_IMAGES - list of images to be generated ()
|
||||
# COMPONENTS_xxx - a list of libs/objs in the form
|
||||
# subdir/lib to be extracted and rolled up into
|
||||
# a generated lib/image xxx.a ()
|
||||
#
|
||||
ifndef PDIR
|
||||
GEN_LIBS = coap.a
|
||||
endif
|
||||
|
||||
#############################################################
|
||||
# Configuration i.e. compile options etc.
|
||||
# Target specific stuff (defines etc.) goes in here!
|
||||
# Generally values applying to a tree are captured in the
|
||||
# makefile at its root level - these are then overridden
|
||||
# for a subtree within the makefile rooted therein
|
||||
#
|
||||
#DEFINES +=
|
||||
|
||||
#############################################################
|
||||
# Recursion Magic - Don't touch this!!
|
||||
#
|
||||
# Each subtree potentially has an include directory
|
||||
# corresponding to the common APIs applicable to modules
|
||||
# rooted at that subtree. Accordingly, the INCLUDE PATH
|
||||
# of a module can only contain the include directories up
|
||||
# its parent path, and not its siblings
|
||||
#
|
||||
# Required for each makefile to inherit from the parent
|
||||
#
|
||||
|
||||
INCLUDES := $(INCLUDES) -I $(PDIR)include
|
||||
INCLUDES += -I ./
|
||||
INCLUDES += -I ../libc
|
||||
INCLUDES += -I ../lua
|
||||
PDIR := ../$(PDIR)
|
||||
sinclude $(PDIR)Makefile
|
||||
|
556
app/coap/coap.c
556
app/coap/coap.c
@ -1,556 +0,0 @@
|
||||
#include "user_config.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "coap.h"
|
||||
#include "uri.h"
|
||||
|
||||
extern void endpoint_setup(void);
|
||||
extern const coap_endpoint_t endpoints[];
|
||||
|
||||
#ifdef COAP_DEBUG
|
||||
void coap_dumpHeader(coap_header_t *hdr)
|
||||
{
|
||||
printf("Header:\n");
|
||||
printf(" ver 0x%02X\n", hdr->ver);
|
||||
printf(" t 0x%02X\n", hdr->ver);
|
||||
printf(" tkl 0x%02X\n", hdr->tkl);
|
||||
printf(" code 0x%02X\n", hdr->code);
|
||||
printf(" id 0x%02X%02X\n", hdr->id[0], hdr->id[1]);
|
||||
}
|
||||
|
||||
void coap_dump(const uint8_t *buf, size_t buflen, bool bare)
|
||||
{
|
||||
if (bare)
|
||||
{
|
||||
while(buflen--)
|
||||
printf("%02X%s", *buf++, (buflen > 0) ? " " : "");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Dump: ");
|
||||
while(buflen--)
|
||||
printf("%02X%s", *buf++, (buflen > 0) ? " " : "");
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int coap_parseHeader(coap_header_t *hdr, const uint8_t *buf, size_t buflen)
|
||||
{
|
||||
if (buflen < 4)
|
||||
return COAP_ERR_HEADER_TOO_SHORT;
|
||||
hdr->ver = (buf[0] & 0xC0) >> 6;
|
||||
if (hdr->ver != 1)
|
||||
return COAP_ERR_VERSION_NOT_1;
|
||||
hdr->t = (buf[0] & 0x30) >> 4;
|
||||
hdr->tkl = buf[0] & 0x0F;
|
||||
hdr->code = buf[1];
|
||||
hdr->id[0] = buf[2];
|
||||
hdr->id[1] = buf[3];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int coap_buildHeader(const coap_header_t *hdr, uint8_t *buf, size_t buflen)
|
||||
{
|
||||
// build header
|
||||
if (buflen < 4)
|
||||
return COAP_ERR_BUFFER_TOO_SMALL;
|
||||
|
||||
buf[0] = (hdr->ver & 0x03) << 6;
|
||||
buf[0] |= (hdr->t & 0x03) << 4;
|
||||
buf[0] |= (hdr->tkl & 0x0F);
|
||||
buf[1] = hdr->code;
|
||||
buf[2] = hdr->id[0];
|
||||
buf[3] = hdr->id[1];
|
||||
return 4;
|
||||
}
|
||||
|
||||
int coap_parseToken(coap_buffer_t *tokbuf, const coap_header_t *hdr, const uint8_t *buf, size_t buflen)
|
||||
{
|
||||
if (hdr->tkl == 0)
|
||||
{
|
||||
tokbuf->p = NULL;
|
||||
tokbuf->len = 0;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
if (hdr->tkl <= 8)
|
||||
{
|
||||
if (4U + hdr->tkl > buflen)
|
||||
return COAP_ERR_TOKEN_TOO_SHORT; // tok bigger than packet
|
||||
tokbuf->p = buf+4; // past header
|
||||
tokbuf->len = hdr->tkl;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// invalid size
|
||||
return COAP_ERR_TOKEN_TOO_SHORT;
|
||||
}
|
||||
}
|
||||
|
||||
int coap_buildToken(const coap_buffer_t *tokbuf, const coap_header_t *hdr, uint8_t *buf, size_t buflen)
|
||||
{
|
||||
// inject token
|
||||
uint8_t *p;
|
||||
if (buflen < (4U + hdr->tkl))
|
||||
return COAP_ERR_BUFFER_TOO_SMALL;
|
||||
p = buf + 4;
|
||||
if ((hdr->tkl > 0) && (hdr->tkl != tokbuf->len))
|
||||
return COAP_ERR_UNSUPPORTED;
|
||||
|
||||
if (hdr->tkl > 0)
|
||||
memcpy(p, tokbuf->p, hdr->tkl);
|
||||
|
||||
// http://tools.ietf.org/html/rfc7252#section-3.1
|
||||
// inject options
|
||||
return hdr->tkl;
|
||||
}
|
||||
|
||||
// advances p
|
||||
int coap_parseOption(coap_option_t *option, uint16_t *running_delta, const uint8_t **buf, size_t buflen)
|
||||
{
|
||||
const uint8_t *p = *buf;
|
||||
uint8_t headlen = 1;
|
||||
uint16_t len, delta;
|
||||
|
||||
if (buflen < headlen) // too small
|
||||
return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
|
||||
|
||||
delta = (p[0] & 0xF0) >> 4;
|
||||
len = p[0] & 0x0F;
|
||||
|
||||
// These are untested and may be buggy
|
||||
if (delta == 13)
|
||||
{
|
||||
headlen++;
|
||||
if (buflen < headlen)
|
||||
return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
|
||||
delta = p[1] + 13;
|
||||
p++;
|
||||
}
|
||||
else
|
||||
if (delta == 14)
|
||||
{
|
||||
headlen += 2;
|
||||
if (buflen < headlen)
|
||||
return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
|
||||
delta = ((p[1] << 8) | p[2]) + 269;
|
||||
p+=2;
|
||||
}
|
||||
else
|
||||
if (delta == 15)
|
||||
return COAP_ERR_OPTION_DELTA_INVALID;
|
||||
|
||||
if (len == 13)
|
||||
{
|
||||
headlen++;
|
||||
if (buflen < headlen)
|
||||
return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
|
||||
len = p[1] + 13;
|
||||
p++;
|
||||
}
|
||||
else
|
||||
if (len == 14)
|
||||
{
|
||||
headlen += 2;
|
||||
if (buflen < headlen)
|
||||
return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
|
||||
len = ((p[1] << 8) | p[2]) + 269;
|
||||
p+=2;
|
||||
}
|
||||
else
|
||||
if (len == 15)
|
||||
return COAP_ERR_OPTION_LEN_INVALID;
|
||||
|
||||
if ((p + 1 + len) > (*buf + buflen))
|
||||
return COAP_ERR_OPTION_TOO_BIG;
|
||||
|
||||
//printf("option num=%d\n", delta + *running_delta);
|
||||
option->num = delta + *running_delta;
|
||||
option->buf.p = p+1;
|
||||
option->buf.len = len;
|
||||
//coap_dump(p+1, len, false);
|
||||
|
||||
// advance buf
|
||||
*buf = p + 1 + len;
|
||||
*running_delta += delta;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// http://tools.ietf.org/html/rfc7252#section-3.1
|
||||
int coap_parseOptionsAndPayload(coap_option_t *options, uint8_t *numOptions, coap_buffer_t *payload, const coap_header_t *hdr, const uint8_t *buf, size_t buflen)
|
||||
{
|
||||
size_t optionIndex = 0;
|
||||
uint16_t delta = 0;
|
||||
const uint8_t *p = buf + 4 + hdr->tkl;
|
||||
const uint8_t *end = buf + buflen;
|
||||
int rc;
|
||||
if (p > end)
|
||||
return COAP_ERR_OPTION_OVERRUNS_PACKET; // out of bounds
|
||||
|
||||
//coap_dump(p, end - p);
|
||||
|
||||
// 0xFF is payload marker
|
||||
while((optionIndex < *numOptions) && (p < end) && (*p != 0xFF))
|
||||
{
|
||||
if (0 != (rc = coap_parseOption(&options[optionIndex], &delta, &p, end-p)))
|
||||
return rc;
|
||||
optionIndex++;
|
||||
}
|
||||
*numOptions = optionIndex;
|
||||
|
||||
if (p+1 < end && *p == 0xFF) // payload marker
|
||||
{
|
||||
payload->p = p+1;
|
||||
payload->len = end-(p+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
payload->p = NULL;
|
||||
payload->len = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int coap_buildOptionHeader(uint32_t optDelta, size_t length, uint8_t *buf, size_t buflen)
|
||||
{
|
||||
int n = 0;
|
||||
uint8_t *p = buf;
|
||||
uint8_t len, delta = 0;
|
||||
|
||||
if (buflen < 5)
|
||||
return COAP_ERR_BUFFER_TOO_SMALL;
|
||||
|
||||
coap_option_nibble(optDelta, &delta);
|
||||
coap_option_nibble(length, &len);
|
||||
|
||||
*p++ = (0xFF & (delta << 4 | len));
|
||||
n++;
|
||||
if (delta == 13)
|
||||
{
|
||||
*p++ = (optDelta - 13);
|
||||
n++;
|
||||
}
|
||||
else
|
||||
if (delta == 14)
|
||||
{
|
||||
*p++ = ((optDelta-269) >> 8);
|
||||
*p++ = (0xFF & (optDelta-269));
|
||||
n+=2;
|
||||
}
|
||||
if (len == 13)
|
||||
{
|
||||
*p++ = (length - 13);
|
||||
n++;
|
||||
}
|
||||
else
|
||||
if (len == 14)
|
||||
{
|
||||
*p++ = (length >> 8);
|
||||
*p++ = (0xFF & (length-269));
|
||||
n+=2;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
#ifdef COAP_DEBUG
|
||||
void coap_dumpOptions(coap_option_t *opts, size_t numopt)
|
||||
{
|
||||
size_t i;
|
||||
printf(" Options:\n");
|
||||
for (i=0;i<numopt;i++)
|
||||
{
|
||||
printf(" 0x%02X [ ", opts[i].num);
|
||||
coap_dump(opts[i].buf.p, opts[i].buf.len, true);
|
||||
printf(" ]\n");
|
||||
}
|
||||
}
|
||||
|
||||
void coap_dumpPacket(coap_packet_t *pkt)
|
||||
{
|
||||
coap_dumpHeader(&pkt->hdr);
|
||||
coap_dumpOptions(pkt->opts, pkt->numopts);
|
||||
printf("Payload: ");
|
||||
coap_dump(pkt->payload.p, pkt->payload.len, true);
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
int coap_parse(coap_packet_t *pkt, const uint8_t *buf, size_t buflen)
|
||||
{
|
||||
int rc;
|
||||
|
||||
// coap_dump(buf, buflen, false);
|
||||
|
||||
if (0 != (rc = coap_parseHeader(&pkt->hdr, buf, buflen)))
|
||||
return rc;
|
||||
// coap_dumpHeader(&hdr);
|
||||
if (0 != (rc = coap_parseToken(&pkt->tok, &pkt->hdr, buf, buflen)))
|
||||
return rc;
|
||||
pkt->numopts = MAXOPT;
|
||||
if (0 != (rc = coap_parseOptionsAndPayload(pkt->opts, &(pkt->numopts), &(pkt->payload), &pkt->hdr, buf, buflen)))
|
||||
return rc;
|
||||
// coap_dumpOptions(opts, numopt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// options are always stored consecutively, so can return a block with same option num
|
||||
const coap_option_t *coap_findOptions(const coap_packet_t *pkt, uint8_t num, uint8_t *count)
|
||||
{
|
||||
// FIXME, options is always sorted, can find faster than this
|
||||
size_t i;
|
||||
const coap_option_t *first = NULL;
|
||||
*count = 0;
|
||||
for (i=0;i<pkt->numopts;i++)
|
||||
{
|
||||
if (pkt->opts[i].num == num)
|
||||
{
|
||||
if (NULL == first)
|
||||
first = &pkt->opts[i];
|
||||
(*count)++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (NULL != first)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return first;
|
||||
}
|
||||
|
||||
int coap_buffer_to_string(char *strbuf, size_t strbuflen, const coap_buffer_t *buf)
|
||||
{
|
||||
if (buf->len+1 > strbuflen)
|
||||
return COAP_ERR_BUFFER_TOO_SMALL;
|
||||
memcpy(strbuf, buf->p, buf->len);
|
||||
strbuf[buf->len] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int coap_build(uint8_t *buf, size_t *buflen, const coap_packet_t *pkt)
|
||||
{
|
||||
size_t opts_len = 0, hdr_len = 0, tok_len = 0;
|
||||
size_t i;
|
||||
uint8_t *p = buf;
|
||||
size_t left = *buflen;
|
||||
uint16_t running_delta = 0;
|
||||
|
||||
hdr_len = coap_buildHeader(&(pkt->hdr), buf, *buflen);
|
||||
p += hdr_len;
|
||||
left -= hdr_len;
|
||||
|
||||
tok_len = coap_buildToken(&(pkt->tok), &(pkt->hdr), buf, *buflen);
|
||||
p += tok_len;
|
||||
left -= tok_len;
|
||||
|
||||
for (i=0;i<pkt->numopts;i++)
|
||||
{
|
||||
uint8_t len, delta = 0;
|
||||
uint16_t optDelta = 0;
|
||||
int rc = 0;
|
||||
|
||||
if (((size_t)(p-buf)) > *buflen)
|
||||
return COAP_ERR_BUFFER_TOO_SMALL;
|
||||
optDelta = pkt->opts[i].num - running_delta;
|
||||
|
||||
rc = coap_buildOptionHeader(optDelta, pkt->opts[i].buf.len, p, left);
|
||||
p += rc;
|
||||
left -= rc;
|
||||
|
||||
memcpy(p, pkt->opts[i].buf.p, pkt->opts[i].buf.len);
|
||||
p += pkt->opts[i].buf.len;
|
||||
left -= pkt->opts[i].buf.len;
|
||||
running_delta = pkt->opts[i].num;
|
||||
}
|
||||
|
||||
opts_len = (p - buf) - 4; // number of bytes used by options
|
||||
|
||||
if (pkt->payload.len > 0)
|
||||
{
|
||||
if (*buflen < 4 + 1 + pkt->payload.len + opts_len)
|
||||
return COAP_ERR_BUFFER_TOO_SMALL;
|
||||
buf[4 + opts_len] = 0xFF; // payload marker
|
||||
memcpy(buf+5 + opts_len, pkt->payload.p, pkt->payload.len);
|
||||
*buflen = opts_len + 5 + pkt->payload.len;
|
||||
}
|
||||
else
|
||||
*buflen = opts_len + 4;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void coap_option_nibble(uint32_t value, uint8_t *nibble)
|
||||
{
|
||||
if (value<13)
|
||||
{
|
||||
*nibble = (0xFF & value);
|
||||
}
|
||||
else
|
||||
if (value<=0xFF+13)
|
||||
{
|
||||
*nibble = 13;
|
||||
} else if (value<=0xFFFF+269)
|
||||
{
|
||||
*nibble = 14;
|
||||
}
|
||||
}
|
||||
|
||||
int coap_make_response(coap_rw_buffer_t *scratch, coap_packet_t *pkt, const uint8_t *content, size_t content_len, uint8_t msgid_hi, uint8_t msgid_lo, const coap_buffer_t* tok, coap_responsecode_t rspcode, coap_content_type_t content_type)
|
||||
{
|
||||
pkt->hdr.ver = 0x01;
|
||||
pkt->hdr.t = COAP_TYPE_ACK;
|
||||
pkt->hdr.tkl = 0;
|
||||
pkt->hdr.code = rspcode;
|
||||
pkt->hdr.id[0] = msgid_hi;
|
||||
pkt->hdr.id[1] = msgid_lo;
|
||||
pkt->numopts = 1;
|
||||
|
||||
// need token in response
|
||||
if (tok) {
|
||||
pkt->hdr.tkl = tok->len;
|
||||
pkt->tok = *tok;
|
||||
}
|
||||
|
||||
// safe because 1 < MAXOPT
|
||||
pkt->opts[0].num = COAP_OPTION_CONTENT_FORMAT;
|
||||
pkt->opts[0].buf.p = scratch->p;
|
||||
if (scratch->len < 2)
|
||||
return COAP_ERR_BUFFER_TOO_SMALL;
|
||||
scratch->p[0] = ((uint16_t)content_type & 0xFF00) >> 8;
|
||||
scratch->p[1] = ((uint16_t)content_type & 0x00FF);
|
||||
pkt->opts[0].buf.len = 2;
|
||||
pkt->payload.p = content;
|
||||
pkt->payload.len = content_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
unsigned int coap_encode_var_bytes(unsigned char *buf, unsigned int val) {
|
||||
unsigned int n, i;
|
||||
|
||||
for (n = 0, i = val; i && n < sizeof(val); ++n)
|
||||
i >>= 8;
|
||||
|
||||
i = n;
|
||||
while (i--) {
|
||||
buf[i] = val & 0xff;
|
||||
val >>= 8;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static uint8_t _token_data[4] = {'n','o','d','e'};
|
||||
coap_buffer_t the_token = { _token_data, 4 };
|
||||
static unsigned short message_id;
|
||||
|
||||
int coap_make_request(coap_rw_buffer_t *scratch, coap_packet_t *pkt, coap_msgtype_t t, coap_method_t m, coap_uri_t *uri, const uint8_t *payload, size_t payload_len)
|
||||
{
|
||||
int res;
|
||||
pkt->hdr.ver = 0x01;
|
||||
pkt->hdr.t = t;
|
||||
pkt->hdr.tkl = 0;
|
||||
pkt->hdr.code = m;
|
||||
pkt->hdr.id[0] = (message_id >> 8) & 0xFF; //msgid_hi;
|
||||
pkt->hdr.id[1] = message_id & 0xFF; //msgid_lo;
|
||||
message_id++;
|
||||
NODE_DBG("message_id: %d.\n", message_id);
|
||||
pkt->numopts = 0;
|
||||
|
||||
if (the_token.len) {
|
||||
pkt->hdr.tkl = the_token.len;
|
||||
pkt->tok = the_token;
|
||||
}
|
||||
|
||||
if (scratch->len < 2) // TBD...
|
||||
return COAP_ERR_BUFFER_TOO_SMALL;
|
||||
|
||||
uint8_t *saved = scratch->p;
|
||||
|
||||
/* split arg into Uri-* options */
|
||||
// const char *addr = uri->host.s;
|
||||
// if(uri->host.length && (strlen(addr) != uri->host.length || memcmp(addr, uri->host.s, uri->host.length) != 0)){
|
||||
if(uri->host.length){
|
||||
/* add Uri-Host */
|
||||
// addr is destination address
|
||||
pkt->opts[pkt->numopts].num = COAP_OPTION_URI_HOST;
|
||||
pkt->opts[pkt->numopts].buf.p = uri->host.s;
|
||||
pkt->opts[pkt->numopts].buf.len = uri->host.length;
|
||||
pkt->numopts++;
|
||||
}
|
||||
|
||||
if (uri->port != COAP_DEFAULT_PORT) {
|
||||
pkt->opts[pkt->numopts].num = COAP_OPTION_URI_PORT;
|
||||
res = coap_encode_var_bytes(scratch->p, uri->port);
|
||||
pkt->opts[pkt->numopts].buf.len = res;
|
||||
pkt->opts[pkt->numopts].buf.p = scratch->p;
|
||||
scratch->p += res;
|
||||
scratch->len -= res;
|
||||
pkt->numopts++;
|
||||
}
|
||||
|
||||
if (uri->path.length) {
|
||||
res = coap_split_path(scratch, pkt, uri->path.s, uri->path.length);
|
||||
}
|
||||
|
||||
if (uri->query.length) {
|
||||
res = coap_split_query(scratch, pkt, uri->query.s, uri->query.length);
|
||||
}
|
||||
|
||||
pkt->payload.p = payload;
|
||||
pkt->payload.len = payload_len;
|
||||
scratch->p = saved; // save back the pointer.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// FIXME, if this looked in the table at the path before the method then
|
||||
// it could more easily return 405 errors
|
||||
int coap_handle_req(coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt)
|
||||
{
|
||||
const coap_option_t *opt;
|
||||
int i;
|
||||
uint8_t count;
|
||||
const coap_endpoint_t *ep = endpoints;
|
||||
|
||||
while(NULL != ep->handler)
|
||||
{
|
||||
if (ep->method != inpkt->hdr.code)
|
||||
goto next;
|
||||
if (NULL != (opt = coap_findOptions(inpkt, COAP_OPTION_URI_PATH, &count)))
|
||||
{
|
||||
// if (count != ep->path->count)
|
||||
if ((count != ep->path->count ) && (count != ep->path->count + 1)) // +1 for /f/[function], /v/[variable]
|
||||
goto next;
|
||||
for (i=0;i<ep->path->count;i++)
|
||||
{
|
||||
if (opt[i].buf.len != strlen(ep->path->elems[i]))
|
||||
goto next;
|
||||
if (0 != memcmp(ep->path->elems[i], opt[i].buf.p, opt[i].buf.len))
|
||||
goto next;
|
||||
}
|
||||
// pre-path match!
|
||||
if (count==ep->path->count+1 && ep->user_entry == NULL)
|
||||
goto next;
|
||||
return ep->handler(ep, scratch, inpkt, outpkt, inpkt->hdr.id[0], inpkt->hdr.id[1]);
|
||||
}
|
||||
next:
|
||||
ep++;
|
||||
}
|
||||
|
||||
coap_make_response(scratch, outpkt, NULL, 0, inpkt->hdr.id[0], inpkt->hdr.id[1], &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void coap_setup(void)
|
||||
{
|
||||
message_id = (unsigned short)rand(); // calculate only once
|
||||
}
|
||||
|
||||
inline int
|
||||
check_token(coap_packet_t *pkt) {
|
||||
return pkt->tok.len == the_token.len && memcmp(pkt->tok.p, the_token.p, the_token.len) == 0;
|
||||
}
|
205
app/coap/coap.h
205
app/coap/coap.h
@ -1,205 +0,0 @@
|
||||
#ifndef COAP_H
|
||||
#define COAP_H 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "lualib.h"
|
||||
#include "lauxlib.h"
|
||||
|
||||
#define MAXOPT 16
|
||||
#define MAX_MESSAGE_SIZE 1152
|
||||
#define MAX_PAYLOAD_SIZE 1024
|
||||
#define MAX_REQUEST_SIZE 576
|
||||
#define MAX_REQ_SCRATCH_SIZE 60
|
||||
|
||||
#define COAP_RESPONSE_CLASS(C) (((C) >> 5) & 0xFF)
|
||||
|
||||
//http://tools.ietf.org/html/rfc7252#section-3
|
||||
typedef struct
|
||||
{
|
||||
uint8_t ver; /* CoAP version number */
|
||||
uint8_t t; /* CoAP Message Type */
|
||||
uint8_t tkl; /* Token length: indicates length of the Token field */
|
||||
uint8_t code; /* CoAP status code. Can be request (0.xx), success reponse (2.xx),
|
||||
* client error response (4.xx), or rever error response (5.xx)
|
||||
* For possible values, see http://tools.ietf.org/html/rfc7252#section-12.1 */
|
||||
uint8_t id[2];
|
||||
} coap_header_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const uint8_t *p;
|
||||
size_t len;
|
||||
} coap_buffer_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t *p;
|
||||
size_t len;
|
||||
} coap_rw_buffer_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t num; /* Option number. See http://tools.ietf.org/html/rfc7252#section-5.10 */
|
||||
coap_buffer_t buf; /* Option value */
|
||||
} coap_option_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
coap_header_t hdr; /* Header of the packet */
|
||||
coap_buffer_t tok; /* Token value, size as specified by hdr.tkl */
|
||||
uint8_t numopts; /* Number of options */
|
||||
coap_option_t opts[MAXOPT]; /* Options of the packet. For possible entries see
|
||||
* http://tools.ietf.org/html/rfc7252#section-5.10 */
|
||||
coap_buffer_t payload; /* Payload carried by the packet */
|
||||
coap_rw_buffer_t content; // content->p = malloc(...) , and free it when done.
|
||||
} coap_packet_t;
|
||||
|
||||
/////////////////////////////////////////
|
||||
|
||||
//http://tools.ietf.org/html/rfc7252#section-12.2
|
||||
typedef enum
|
||||
{
|
||||
COAP_OPTION_IF_MATCH = 1,
|
||||
COAP_OPTION_URI_HOST = 3,
|
||||
COAP_OPTION_ETAG = 4,
|
||||
COAP_OPTION_IF_NONE_MATCH = 5,
|
||||
COAP_OPTION_OBSERVE = 6,
|
||||
COAP_OPTION_URI_PORT = 7,
|
||||
COAP_OPTION_LOCATION_PATH = 8,
|
||||
COAP_OPTION_URI_PATH = 11,
|
||||
COAP_OPTION_CONTENT_FORMAT = 12,
|
||||
COAP_OPTION_MAX_AGE = 14,
|
||||
COAP_OPTION_URI_QUERY = 15,
|
||||
COAP_OPTION_ACCEPT = 17,
|
||||
COAP_OPTION_LOCATION_QUERY = 20,
|
||||
COAP_OPTION_PROXY_URI = 35,
|
||||
COAP_OPTION_PROXY_SCHEME = 39
|
||||
} coap_option_num_t;
|
||||
|
||||
//http://tools.ietf.org/html/rfc7252#section-12.1.1
|
||||
typedef enum
|
||||
{
|
||||
COAP_METHOD_GET = 1,
|
||||
COAP_METHOD_POST = 2,
|
||||
COAP_METHOD_PUT = 3,
|
||||
COAP_METHOD_DELETE = 4
|
||||
} coap_method_t;
|
||||
|
||||
//http://tools.ietf.org/html/rfc7252#section-12.1.1
|
||||
typedef enum
|
||||
{
|
||||
COAP_TYPE_CON = 0,
|
||||
COAP_TYPE_NONCON = 1,
|
||||
COAP_TYPE_ACK = 2,
|
||||
COAP_TYPE_RESET = 3
|
||||
} coap_msgtype_t;
|
||||
|
||||
//http://tools.ietf.org/html/rfc7252#section-5.2
|
||||
//http://tools.ietf.org/html/rfc7252#section-12.1.2
|
||||
#define MAKE_RSPCODE(clas, det) ((clas << 5) | (det))
|
||||
typedef enum
|
||||
{
|
||||
COAP_RSPCODE_CONTENT = MAKE_RSPCODE(2, 5),
|
||||
COAP_RSPCODE_NOT_FOUND = MAKE_RSPCODE(4, 4),
|
||||
COAP_RSPCODE_BAD_REQUEST = MAKE_RSPCODE(4, 0),
|
||||
COAP_RSPCODE_CHANGED = MAKE_RSPCODE(2, 4)
|
||||
} coap_responsecode_t;
|
||||
|
||||
//http://tools.ietf.org/html/rfc7252#section-12.3
|
||||
typedef enum
|
||||
{
|
||||
COAP_CONTENTTYPE_NONE = -1, // bodge to allow us not to send option block
|
||||
COAP_CONTENTTYPE_TEXT_PLAIN = 0,
|
||||
COAP_CONTENTTYPE_APPLICATION_LINKFORMAT = 40,
|
||||
COAP_CONTENTTYPE_APPLICATION_XML = 41,
|
||||
COAP_CONTENTTYPE_APPLICATION_OCTET_STREAM = 42,
|
||||
COAP_CONTENTTYPE_APPLICATION_EXI = 47,
|
||||
COAP_CONTENTTYPE_APPLICATION_JSON = 50,
|
||||
} coap_content_type_t;
|
||||
|
||||
///////////////////////
|
||||
|
||||
typedef enum
|
||||
{
|
||||
COAP_ERR_NONE = 0,
|
||||
COAP_ERR_HEADER_TOO_SHORT = 1,
|
||||
COAP_ERR_VERSION_NOT_1 = 2,
|
||||
COAP_ERR_TOKEN_TOO_SHORT = 3,
|
||||
COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER = 4,
|
||||
COAP_ERR_OPTION_TOO_SHORT = 5,
|
||||
COAP_ERR_OPTION_OVERRUNS_PACKET = 6,
|
||||
COAP_ERR_OPTION_TOO_BIG = 7,
|
||||
COAP_ERR_OPTION_LEN_INVALID = 8,
|
||||
COAP_ERR_BUFFER_TOO_SMALL = 9,
|
||||
COAP_ERR_UNSUPPORTED = 10,
|
||||
COAP_ERR_OPTION_DELTA_INVALID = 11,
|
||||
} coap_error_t;
|
||||
|
||||
///////////////////////
|
||||
typedef struct coap_endpoint_t coap_endpoint_t;
|
||||
|
||||
typedef int (*coap_endpoint_func)(const coap_endpoint_t *ep, coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt, uint8_t id_hi, uint8_t id_lo);
|
||||
#define MAX_SEGMENTS 3 // 2 = /foo/bar, 3 = /foo/bar/baz
|
||||
#define MAX_SEGMENTS_SIZE 16
|
||||
typedef struct
|
||||
{
|
||||
int count;
|
||||
const char *elems[MAX_SEGMENTS];
|
||||
} coap_endpoint_path_t;
|
||||
|
||||
typedef struct coap_luser_entry coap_luser_entry;
|
||||
|
||||
struct coap_luser_entry{
|
||||
lua_State *L;
|
||||
// int ref;
|
||||
// char name[MAX_SEGMENTS_SIZE+1]; // +1 for string '\0'
|
||||
const char *name;
|
||||
coap_luser_entry *next;
|
||||
int content_type;
|
||||
};
|
||||
|
||||
struct coap_endpoint_t{
|
||||
coap_method_t method; /* (i.e. POST, PUT or GET) */
|
||||
coap_endpoint_func handler; /* callback function which handles this
|
||||
* type of endpoint (and calls
|
||||
* coap_make_response() at some point) */
|
||||
const coap_endpoint_path_t *path; /* path towards a resource (i.e. foo/bar/) */
|
||||
const char *core_attr; /* the 'ct' attribute, as defined in RFC7252, section 7.2.1.:
|
||||
* "The Content-Format code "ct" attribute
|
||||
* provides a hint about the
|
||||
* Content-Formats this resource returns."
|
||||
* (Section 12.3. lists possible ct values.) */
|
||||
coap_luser_entry *user_entry;
|
||||
};
|
||||
|
||||
|
||||
///////////////////////
|
||||
void coap_dumpPacket(coap_packet_t *pkt);
|
||||
int coap_parse(coap_packet_t *pkt, const uint8_t *buf, size_t buflen);
|
||||
int coap_buffer_to_string(char *strbuf, size_t strbuflen, const coap_buffer_t *buf);
|
||||
const coap_option_t *coap_findOptions(const coap_packet_t *pkt, uint8_t num, uint8_t *count);
|
||||
int coap_build(uint8_t *buf, size_t *buflen, const coap_packet_t *pkt);
|
||||
void coap_dump(const uint8_t *buf, size_t buflen, bool bare);
|
||||
int coap_make_response(coap_rw_buffer_t *scratch, coap_packet_t *pkt, const uint8_t *content, size_t content_len, uint8_t msgid_hi, uint8_t msgid_lo, const coap_buffer_t* tok, coap_responsecode_t rspcode, coap_content_type_t content_type);
|
||||
int coap_handle_req(coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt);
|
||||
void coap_option_nibble(uint32_t value, uint8_t *nibble);
|
||||
void coap_setup(void);
|
||||
void endpoint_setup(void);
|
||||
|
||||
int coap_buildOptionHeader(uint32_t optDelta, size_t length, uint8_t *buf, size_t buflen);
|
||||
int check_token(coap_packet_t *pkt);
|
||||
|
||||
#include "uri.h"
|
||||
int coap_make_request(coap_rw_buffer_t *scratch, coap_packet_t *pkt, coap_msgtype_t t, coap_method_t m, coap_uri_t *uri, const uint8_t *payload, size_t payload_len);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,70 +0,0 @@
|
||||
#include "user_config.h"
|
||||
#include "c_types.h"
|
||||
|
||||
#include "coap.h"
|
||||
#include "coap_timer.h"
|
||||
#include "hash.h"
|
||||
#include "node.h"
|
||||
|
||||
extern coap_queue_t *gQueue;
|
||||
|
||||
void coap_client_response_handler(char *data, unsigned short len, unsigned short size, const uint32_t ip, const uint32_t port)
|
||||
{
|
||||
NODE_DBG("coap_client_response_handler is called.\n");
|
||||
coap_packet_t pkt;
|
||||
pkt.content.p = NULL;
|
||||
pkt.content.len = 0;
|
||||
int rc;
|
||||
|
||||
if (0 != (rc = coap_parse(&pkt, data, len))){
|
||||
NODE_DBG("Bad packet rc=%d\n", rc);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef COAP_DEBUG
|
||||
coap_dumpPacket(&pkt);
|
||||
#endif
|
||||
/* check if this is a response to our original request */
|
||||
if (!check_token(&pkt)) {
|
||||
/* drop if this was just some message, or send RST in case of notification */
|
||||
if (pkt.hdr.t == COAP_TYPE_CON || pkt.hdr.t == COAP_TYPE_NONCON){
|
||||
// coap_send_rst(pkt); // send RST response
|
||||
// or, just ignore it.
|
||||
}
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (pkt.hdr.t == COAP_TYPE_RESET) {
|
||||
NODE_DBG("got RST\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
coap_tid_t id = COAP_INVALID_TID;
|
||||
coap_transaction_id(ip, port, &pkt, &id);
|
||||
/* transaction done, remove the node from queue */
|
||||
// stop timer
|
||||
coap_timer_stop();
|
||||
// remove the node
|
||||
coap_remove_node(&gQueue, id);
|
||||
// calculate time elapsed
|
||||
coap_timer_update(&gQueue);
|
||||
coap_timer_start(&gQueue);
|
||||
|
||||
if (COAP_RESPONSE_CLASS(pkt.hdr.code) == 2)
|
||||
{
|
||||
/* There is no block option set, just read the data and we are done. */
|
||||
NODE_DBG("%d.%02d\t", (pkt.hdr.code >> 5), pkt.hdr.code & 0x1F);
|
||||
NODE_DBG((char *)(pkt.payload.p));
|
||||
}
|
||||
else if (COAP_RESPONSE_CLASS(pkt.hdr.code) >= 4)
|
||||
{
|
||||
NODE_DBG("%d.%02d\t", (pkt.hdr.code >> 5), pkt.hdr.code & 0x1F);
|
||||
NODE_DBG((char *)(pkt.payload.p));
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
if(!gQueue){ // if there is no node pending in the queue, disconnect from host.
|
||||
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
#ifndef _COAP_CLIENT_H
|
||||
#define _COAP_CLIENT_H 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void coap_client_response_handler(char *data, unsigned short len, unsigned short size, const uint32_t ip, const uint32_t port);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,76 +0,0 @@
|
||||
// No espconn on ESP32
|
||||
#ifdef __ESP8266__
|
||||
|
||||
#include <string.h>
|
||||
#include "coap_io.h"
|
||||
#include "node.h"
|
||||
#include "espconn.h"
|
||||
#include "coap_timer.h"
|
||||
|
||||
extern coap_queue_t *gQueue;
|
||||
|
||||
/* releases space allocated by PDU if free_pdu is set */
|
||||
coap_tid_t coap_send(struct espconn *pesp_conn, coap_pdu_t *pdu) {
|
||||
coap_tid_t id = COAP_INVALID_TID;
|
||||
uint32_t ip = 0, port = 0;
|
||||
if ( !pesp_conn || !pdu )
|
||||
return id;
|
||||
|
||||
espconn_sent(pesp_conn, (unsigned char *)(pdu->msg.p), pdu->msg.len);
|
||||
|
||||
if(pesp_conn->type == ESPCONN_TCP){
|
||||
memcpy(&ip, pesp_conn->proto.tcp->remote_ip, sizeof(ip));
|
||||
port = pesp_conn->proto.tcp->remote_port;
|
||||
}else{
|
||||
memcpy(&ip, pesp_conn->proto.udp->remote_ip, sizeof(ip));
|
||||
port = pesp_conn->proto.udp->remote_port;
|
||||
}
|
||||
coap_transaction_id(ip, port, pdu->pkt, &id);
|
||||
return id;
|
||||
}
|
||||
|
||||
coap_tid_t coap_send_confirmed(struct espconn *pesp_conn, coap_pdu_t *pdu) {
|
||||
coap_queue_t *node;
|
||||
coap_tick_t diff;
|
||||
uint32_t r;
|
||||
|
||||
node = coap_new_node();
|
||||
if (!node) {
|
||||
NODE_DBG("coap_send_confirmed: insufficient memory\n");
|
||||
return COAP_INVALID_TID;
|
||||
}
|
||||
|
||||
node->retransmit_cnt = 0;
|
||||
node->id = coap_send(pesp_conn, pdu);
|
||||
if (COAP_INVALID_TID == node->id) {
|
||||
NODE_DBG("coap_send_confirmed: error sending pdu\n");
|
||||
coap_free_node(node);
|
||||
return COAP_INVALID_TID;
|
||||
}
|
||||
r = rand();
|
||||
|
||||
/* add randomized RESPONSE_TIMEOUT to determine retransmission timeout */
|
||||
node->timeout = COAP_DEFAULT_RESPONSE_TIMEOUT * COAP_TICKS_PER_SECOND +
|
||||
(COAP_DEFAULT_RESPONSE_TIMEOUT >> 1) *
|
||||
((COAP_TICKS_PER_SECOND * (r & 0xFF)) >> 8);
|
||||
|
||||
node->pconn = pesp_conn;
|
||||
node->pdu = pdu;
|
||||
|
||||
/* Set timer for pdu retransmission. If this is the first element in
|
||||
* the retransmission queue, the base time is set to the current
|
||||
* time and the retransmission time is node->timeout. If there is
|
||||
* already an entry in the sendqueue, we must check if this node is
|
||||
* to be retransmitted earlier. Therefore, node->timeout is first
|
||||
* normalized to the timeout and then inserted into the queue with
|
||||
* an adjusted relative time.
|
||||
*/
|
||||
coap_timer_stop();
|
||||
coap_timer_update(&gQueue);
|
||||
node->t = node->timeout;
|
||||
coap_insert_node(&gQueue, node);
|
||||
coap_timer_start(&gQueue);
|
||||
return node->id;
|
||||
}
|
||||
|
||||
#endif
|
@ -1,22 +0,0 @@
|
||||
#ifndef _COAP_IO_H
|
||||
#define _COAP_IO_H 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "c_types.h"
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "espconn.h"
|
||||
#include "pdu.h"
|
||||
#include "hash.h"
|
||||
|
||||
coap_tid_t coap_send(struct espconn *pesp_conn, coap_pdu_t *pdu);
|
||||
|
||||
coap_tid_t coap_send_confirmed(struct espconn *pesp_conn, coap_pdu_t *pdu);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,60 +0,0 @@
|
||||
#include "user_config.h"
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "coap.h"
|
||||
|
||||
size_t coap_server_respond(char *req, unsigned short reqlen, char *rsp, unsigned short rsplen)
|
||||
{
|
||||
NODE_DBG("coap_server_respond is called.\n");
|
||||
size_t rlen = rsplen;
|
||||
coap_packet_t pkt;
|
||||
pkt.content.p = NULL;
|
||||
pkt.content.len = 0;
|
||||
uint8_t scratch_raw[4];
|
||||
coap_rw_buffer_t scratch_buf = {scratch_raw, sizeof(scratch_raw)};
|
||||
int rc;
|
||||
|
||||
#ifdef COAP_DEBUG
|
||||
NODE_DBG("Received: ");
|
||||
coap_dump(req, reqlen, true);
|
||||
NODE_DBG("\n");
|
||||
#endif
|
||||
|
||||
if (0 != (rc = coap_parse(&pkt, req, reqlen))){
|
||||
NODE_DBG("Bad packet rc=%d\n", rc);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
coap_packet_t rsppkt;
|
||||
rsppkt.content.p = NULL;
|
||||
rsppkt.content.len = 0;
|
||||
#ifdef COAP_DEBUG
|
||||
coap_dumpPacket(&pkt);
|
||||
#endif
|
||||
coap_handle_req(&scratch_buf, &pkt, &rsppkt);
|
||||
if (0 != (rc = coap_build(rsp, &rlen, &rsppkt))){
|
||||
NODE_DBG("coap_build failed rc=%d\n", rc);
|
||||
// return 0;
|
||||
rlen = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef COAP_DEBUG
|
||||
NODE_DBG("Responding: ");
|
||||
coap_dump(rsp, rlen, true);
|
||||
NODE_DBG("\n");
|
||||
#endif
|
||||
#ifdef COAP_DEBUG
|
||||
coap_dumpPacket(&rsppkt);
|
||||
#endif
|
||||
}
|
||||
if(rsppkt.content.p){
|
||||
free(rsppkt.content.p);
|
||||
rsppkt.content.p = NULL;
|
||||
rsppkt.content.len = 0;
|
||||
}
|
||||
return rlen;
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
#ifndef _COAP_SERVER_H
|
||||
#define _COAP_SERVER_H 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
size_t coap_server_respond(char *req, unsigned short reqlen, char *rsp, unsigned short rsplen);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,83 +0,0 @@
|
||||
// No espconn on ESP32
|
||||
#ifdef __ESP8266__
|
||||
#include "node.h"
|
||||
#include "coap_timer.h"
|
||||
#include "coap_io.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_system.h"
|
||||
|
||||
static os_timer_t coap_timer;
|
||||
static coap_tick_t basetime = 0;
|
||||
|
||||
void coap_timer_elapsed(coap_tick_t *diff){
|
||||
coap_tick_t now = system_get_time() / 1000; // coap_tick_t is in ms. also sys_timer
|
||||
if(now>=basetime){
|
||||
*diff = now-basetime;
|
||||
} else {
|
||||
*diff = now + SYS_TIME_MAX -basetime;
|
||||
}
|
||||
basetime = now;
|
||||
}
|
||||
|
||||
void coap_timer_tick(void *arg){
|
||||
if( !arg )
|
||||
return;
|
||||
coap_queue_t **queue = (coap_queue_t **)arg;
|
||||
if( !(*queue) )
|
||||
return;
|
||||
|
||||
coap_queue_t *node = coap_pop_next( queue );
|
||||
/* re-initialize timeout when maximum number of retransmissions are not reached yet */
|
||||
if (node->retransmit_cnt < COAP_DEFAULT_MAX_RETRANSMIT) {
|
||||
node->retransmit_cnt++;
|
||||
node->t = node->timeout << node->retransmit_cnt;
|
||||
|
||||
NODE_DBG("** retransmission #%d of transaction %d\n",
|
||||
node->retransmit_cnt, (((uint16_t)(node->pdu->pkt->hdr.id[0]))<<8)+node->pdu->pkt->hdr.id[1]);
|
||||
node->id = coap_send(node->pconn, node->pdu);
|
||||
if (COAP_INVALID_TID == node->id) {
|
||||
NODE_DBG("retransmission: error sending pdu\n");
|
||||
coap_delete_node(node);
|
||||
} else {
|
||||
coap_insert_node(queue, node);
|
||||
}
|
||||
} else {
|
||||
/* And finally delete the node */
|
||||
coap_delete_node( node );
|
||||
}
|
||||
|
||||
coap_timer_start(queue);
|
||||
}
|
||||
|
||||
void coap_timer_setup(coap_queue_t ** queue, coap_tick_t t){
|
||||
os_timer_disarm(&coap_timer);
|
||||
os_timer_setfn(&coap_timer, (os_timer_func_t *)coap_timer_tick, queue);
|
||||
os_timer_arm(&coap_timer, t, 0); // no repeat
|
||||
}
|
||||
|
||||
void coap_timer_stop(void){
|
||||
os_timer_disarm(&coap_timer);
|
||||
}
|
||||
|
||||
void coap_timer_update(coap_queue_t ** queue){
|
||||
if (!queue)
|
||||
return;
|
||||
coap_tick_t diff = 0;
|
||||
coap_queue_t *first = *queue;
|
||||
coap_timer_elapsed(&diff); // update: basetime = now, diff = now - oldbase, means time elapsed
|
||||
if (first) {
|
||||
// diff ms time is elapsed, re-calculate the first node->t
|
||||
if (first->t >= diff){
|
||||
first->t -= diff;
|
||||
} else {
|
||||
first->t = 0; // when timer enabled, time out almost immediately
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void coap_timer_start(coap_queue_t ** queue){
|
||||
if(*queue){ // if there is node in the queue, set timeout to its ->t.
|
||||
coap_timer_setup(queue, (*queue)->t);
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,31 +0,0 @@
|
||||
#ifndef _COAP_TIMER_H
|
||||
#define _COAP_TIMER_H 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "node.h"
|
||||
|
||||
#define SYS_TIME_MAX (0xFFFFFFFF / 1000)
|
||||
|
||||
#define COAP_DEFAULT_RESPONSE_TIMEOUT 2 /* response timeout in seconds */
|
||||
#define COAP_DEFAULT_MAX_RETRANSMIT 4 /* max number of retransmissions */
|
||||
#define COAP_TICKS_PER_SECOND 1000 // ms
|
||||
#define DEFAULT_MAX_TRANSMIT_WAIT 90
|
||||
|
||||
void coap_timer_elapsed(coap_tick_t *diff);
|
||||
|
||||
void coap_timer_setup(coap_queue_t ** queue, coap_tick_t t);
|
||||
|
||||
void coap_timer_stop(void);
|
||||
|
||||
void coap_timer_update(coap_queue_t ** queue);
|
||||
|
||||
void coap_timer_start(coap_queue_t ** queue);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,301 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "esp_libc.h"
|
||||
#include "coap.h"
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
#include "os_type.h"
|
||||
#include "user_interface.h"
|
||||
#include "user_config.h"
|
||||
|
||||
void build_well_known_rsp(char *rsp, uint16_t rsplen);
|
||||
|
||||
void endpoint_setup(void)
|
||||
{
|
||||
coap_setup();
|
||||
}
|
||||
|
||||
static const coap_endpoint_path_t path_well_known_core = {2, {".well-known", "core"}};
|
||||
static int handle_get_well_known_core(const coap_endpoint_t *ep, coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt, uint8_t id_hi, uint8_t id_lo)
|
||||
{
|
||||
outpkt->content.p = (uint8_t *)zalloc(MAX_PAYLOAD_SIZE); // this should be free-ed when outpkt is built in coap_server_respond()
|
||||
if(outpkt->content.p == NULL){
|
||||
NODE_DBG("not enough memory\n");
|
||||
return COAP_ERR_BUFFER_TOO_SMALL;
|
||||
}
|
||||
outpkt->content.len = MAX_PAYLOAD_SIZE;
|
||||
build_well_known_rsp(outpkt->content.p, outpkt->content.len);
|
||||
return coap_make_response(scratch, outpkt, (const uint8_t *)outpkt->content.p, strlen(outpkt->content.p), id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_APPLICATION_LINKFORMAT);
|
||||
}
|
||||
|
||||
static const coap_endpoint_path_t path_variable = {2, {"v1", "v"}};
|
||||
static int handle_get_variable(const coap_endpoint_t *ep, coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt, uint8_t id_hi, uint8_t id_lo)
|
||||
{
|
||||
const coap_option_t *opt;
|
||||
uint8_t count;
|
||||
int n;
|
||||
if (NULL != (opt = coap_findOptions(inpkt, COAP_OPTION_URI_PATH, &count)))
|
||||
{
|
||||
if ((count != ep->path->count ) && (count != ep->path->count + 1)) // +1 for /f/[function], /v/[variable]
|
||||
{
|
||||
NODE_DBG("should never happen.\n");
|
||||
goto end;
|
||||
}
|
||||
if (count == ep->path->count + 1)
|
||||
{
|
||||
coap_luser_entry *h = ep->user_entry->next; // ->next: skip the first entry(head)
|
||||
while(NULL != h){
|
||||
if (opt[count-1].buf.len != strlen(h->name))
|
||||
{
|
||||
h = h->next;
|
||||
continue;
|
||||
}
|
||||
if (0 == memcmp(h->name, opt[count-1].buf.p, opt[count-1].buf.len))
|
||||
{
|
||||
NODE_DBG("/v1/v/");
|
||||
NODE_DBG((char *)h->name);
|
||||
NODE_DBG(" match.\n");
|
||||
if(h->L == NULL)
|
||||
return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE);
|
||||
if(strlen(h->name))
|
||||
{
|
||||
n = lua_gettop(h->L);
|
||||
lua_getglobal(h->L, h->name);
|
||||
if (!lua_isnumber(h->L, -1) && !lua_isstring(h->L, -1)) {
|
||||
NODE_DBG ("should be a number or string.\n");
|
||||
lua_settop(h->L, n);
|
||||
return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE);
|
||||
} else {
|
||||
const char *res = lua_tostring(h->L,-1);
|
||||
lua_settop(h->L, n);
|
||||
return coap_make_response(scratch, outpkt, (const uint8_t *)res, strlen(res), id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, h->content_type);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
h = h->next;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
NODE_DBG("/v1/v match.\n");
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
NODE_DBG("none match.\n");
|
||||
end:
|
||||
return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_TEXT_PLAIN);
|
||||
}
|
||||
|
||||
static const coap_endpoint_path_t path_function = {2, {"v1", "f"}};
|
||||
static int handle_post_function(const coap_endpoint_t *ep, coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt, uint8_t id_hi, uint8_t id_lo)
|
||||
{
|
||||
const coap_option_t *opt;
|
||||
uint8_t count;
|
||||
int n;
|
||||
if (NULL != (opt = coap_findOptions(inpkt, COAP_OPTION_URI_PATH, &count)))
|
||||
{
|
||||
if ((count != ep->path->count ) && (count != ep->path->count + 1)) // +1 for /f/[function], /v/[variable]
|
||||
{
|
||||
NODE_DBG("should never happen.\n");
|
||||
goto end;
|
||||
}
|
||||
if (count == ep->path->count + 1)
|
||||
{
|
||||
coap_luser_entry *h = ep->user_entry->next; // ->next: skip the first entry(head)
|
||||
while(NULL != h){
|
||||
if (opt[count-1].buf.len != strlen(h->name))
|
||||
{
|
||||
h = h->next;
|
||||
continue;
|
||||
}
|
||||
if (0 == memcmp(h->name, opt[count-1].buf.p, opt[count-1].buf.len))
|
||||
{
|
||||
NODE_DBG("/v1/f/");
|
||||
NODE_DBG((char *)h->name);
|
||||
NODE_DBG(" match.\n");
|
||||
|
||||
if(h->L == NULL)
|
||||
return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE);
|
||||
|
||||
if(strlen(h->name))
|
||||
{
|
||||
n = lua_gettop(h->L);
|
||||
lua_getglobal(h->L, h->name);
|
||||
if (lua_type(h->L, -1) != LUA_TFUNCTION) {
|
||||
NODE_DBG ("should be a function\n");
|
||||
lua_settop(h->L, n);
|
||||
return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE);
|
||||
} else {
|
||||
lua_pushlstring(h->L, inpkt->payload.p, inpkt->payload.len); // make sure payload.p is filled with '\0' after payload.len, or use lua_pushlstring
|
||||
lua_call(h->L, 1, 1);
|
||||
if (!lua_isnil(h->L, -1)){ /* get return? */
|
||||
if( lua_isstring(h->L, -1) ) // deal with the return string
|
||||
{
|
||||
size_t len = 0;
|
||||
const char *ret = luaL_checklstring( h->L, -1, &len );
|
||||
if(len > MAX_PAYLOAD_SIZE){
|
||||
lua_settop(h->L, n);
|
||||
luaL_error( h->L, "return string:<MAX_PAYLOAD_SIZE" );
|
||||
return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE);
|
||||
}
|
||||
NODE_DBG((char *)ret);
|
||||
NODE_DBG("\n");
|
||||
lua_settop(h->L, n);
|
||||
return coap_make_response(scratch, outpkt, ret, len, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_TEXT_PLAIN);
|
||||
}
|
||||
} else {
|
||||
lua_settop(h->L, n);
|
||||
return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_TEXT_PLAIN);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
h = h->next;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
NODE_DBG("/v1/f match.\n");
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
NODE_DBG("none match.\n");
|
||||
end:
|
||||
return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE);
|
||||
}
|
||||
|
||||
extern lua_Load gLoad;
|
||||
static const coap_endpoint_path_t path_command = {2, {"v1", "c"}};
|
||||
static int handle_post_command(const coap_endpoint_t *ep, coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt, uint8_t id_hi, uint8_t id_lo)
|
||||
{
|
||||
if (inpkt->payload.len == 0)
|
||||
return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_BAD_REQUEST, COAP_CONTENTTYPE_TEXT_PLAIN);
|
||||
if (inpkt->payload.len > 0)
|
||||
{
|
||||
lua_Load *load = &gLoad;
|
||||
if(load->line_position == 0){
|
||||
coap_buffer_to_string(load->line, load->len,&inpkt->payload);
|
||||
load->line_position = strlen(load->line)+1;
|
||||
// load->line[load->line_position-1] = '\n';
|
||||
// load->line[load->line_position] = 0;
|
||||
// load->line_position++;
|
||||
load->done = 1;
|
||||
NODE_DBG("Get command:\n");
|
||||
NODE_DBG(load->line); // buggy here
|
||||
NODE_DBG("\nResult(if any):\n");
|
||||
#if 0 // FIXME
|
||||
system_os_post (LUA_TASK_PRIO, LUA_PROCESS_LINE_SIG, 0);
|
||||
#endif
|
||||
}
|
||||
return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_TEXT_PLAIN);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t id = 0;
|
||||
static const coap_endpoint_path_t path_id = {2, {"v1", "id"}};
|
||||
static int handle_get_id(const coap_endpoint_t *ep, coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt, uint8_t id_hi, uint8_t id_lo)
|
||||
{
|
||||
#if defined(__ESP8266__)
|
||||
id = system_get_chip_id();
|
||||
#elif defined(__ESP32__)
|
||||
uint8_t tmp;
|
||||
system_get_chip_id (&tmp); // TODO: deal with failure
|
||||
id = tmp;
|
||||
#endif
|
||||
return coap_make_response(scratch, outpkt, (const uint8_t *)(&id), sizeof(uint32_t), id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_TEXT_PLAIN);
|
||||
}
|
||||
|
||||
coap_luser_entry var_head = {NULL,NULL,NULL,0};
|
||||
coap_luser_entry *variable_entry = &var_head;
|
||||
|
||||
coap_luser_entry func_head = {NULL,NULL,NULL,0};
|
||||
coap_luser_entry *function_entry = &func_head;
|
||||
|
||||
const coap_endpoint_t endpoints[] =
|
||||
{
|
||||
{COAP_METHOD_GET, handle_get_well_known_core, &path_well_known_core, "ct=40", NULL},
|
||||
{COAP_METHOD_GET, handle_get_variable, &path_variable, "ct=0", &var_head},
|
||||
{COAP_METHOD_POST, handle_post_function, &path_function, NULL, &func_head},
|
||||
{COAP_METHOD_POST, handle_post_command, &path_command, NULL, NULL},
|
||||
{COAP_METHOD_GET, handle_get_id, &path_id, "ct=0", NULL},
|
||||
{(coap_method_t)0, NULL, NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
void build_well_known_rsp(char *rsp, uint16_t rsplen)
|
||||
{
|
||||
const coap_endpoint_t *ep = endpoints;
|
||||
int i;
|
||||
uint16_t len = rsplen;
|
||||
|
||||
memset(rsp, 0, len);
|
||||
|
||||
len--; // Null-terminated string
|
||||
|
||||
while(NULL != ep->handler)
|
||||
{
|
||||
if (NULL == ep->core_attr) {
|
||||
ep++;
|
||||
continue;
|
||||
}
|
||||
if (NULL == ep->user_entry){
|
||||
if (0 < strlen(rsp)) {
|
||||
strncat(rsp, ",", len);
|
||||
len--;
|
||||
}
|
||||
|
||||
strncat(rsp, "<", len);
|
||||
len--;
|
||||
|
||||
for (i = 0; i < ep->path->count; i++) {
|
||||
strncat(rsp, "/", len);
|
||||
len--;
|
||||
|
||||
strncat(rsp, ep->path->elems[i], len);
|
||||
len -= strlen(ep->path->elems[i]);
|
||||
}
|
||||
|
||||
strncat(rsp, ">;", len);
|
||||
len -= 2;
|
||||
|
||||
strncat(rsp, ep->core_attr, len);
|
||||
len -= strlen(ep->core_attr);
|
||||
} else {
|
||||
coap_luser_entry *h = ep->user_entry->next; // ->next: skip the first entry(head)
|
||||
while(NULL != h){
|
||||
if (0 < strlen(rsp)) {
|
||||
strncat(rsp, ",", len);
|
||||
len--;
|
||||
}
|
||||
|
||||
strncat(rsp, "<", len);
|
||||
len--;
|
||||
|
||||
for (i = 0; i < ep->path->count; i++) {
|
||||
strncat(rsp, "/", len);
|
||||
len--;
|
||||
|
||||
strncat(rsp, ep->path->elems[i], len);
|
||||
len -= strlen(ep->path->elems[i]);
|
||||
}
|
||||
|
||||
strncat(rsp, "/", len);
|
||||
len--;
|
||||
|
||||
strncat(rsp, h->name, len);
|
||||
len -= strlen(h->name);
|
||||
|
||||
strncat(rsp, ">;", len);
|
||||
len -= 2;
|
||||
|
||||
strncat(rsp, ep->core_attr, len);
|
||||
len -= strlen(ep->core_attr);
|
||||
|
||||
h = h->next;
|
||||
}
|
||||
}
|
||||
ep++;
|
||||
}
|
||||
}
|
||||
|
@ -1,30 +0,0 @@
|
||||
#include "hash.h"
|
||||
#include <string.h>
|
||||
/* Caution: When changing this, update COAP_DEFAULT_WKC_HASHKEY
|
||||
* accordingly (see int coap_hash_path());
|
||||
*/
|
||||
void coap_hash(const unsigned char *s, unsigned int len, coap_key_t h) {
|
||||
size_t j;
|
||||
|
||||
while (len--) {
|
||||
j = sizeof(coap_key_t)-1;
|
||||
|
||||
while (j) {
|
||||
h[j] = ((h[j] << 7) | (h[j-1] >> 1)) + h[j];
|
||||
--j;
|
||||
}
|
||||
|
||||
h[0] = (h[0] << 7) + h[0] + *s++;
|
||||
}
|
||||
}
|
||||
|
||||
void coap_transaction_id(const uint32_t ip, const uint32_t port, const coap_packet_t *pkt, coap_tid_t *id) {
|
||||
coap_key_t h;
|
||||
memset(h, 0, sizeof(coap_key_t));
|
||||
|
||||
/* Compare the transport address. */
|
||||
coap_hash((const unsigned char *)&(port), sizeof(port), h);
|
||||
coap_hash((const unsigned char *)&(ip), sizeof(ip), h);
|
||||
coap_hash((const unsigned char *)(pkt->hdr.id), sizeof(pkt->hdr.id), h);
|
||||
*id = ((h[0] << 8) | h[1]) ^ ((h[2] << 8) | h[3]);
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
#ifndef _HASH_H
|
||||
#define _HASH_H 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "coap.h"
|
||||
|
||||
typedef unsigned char coap_key_t[4];
|
||||
|
||||
/* CoAP transaction id */
|
||||
/*typedef unsigned short coap_tid_t; */
|
||||
typedef int coap_tid_t;
|
||||
#define COAP_INVALID_TID -1
|
||||
|
||||
void coap_transaction_id(const uint32_t ip, const uint32_t port, const coap_packet_t *pkt, coap_tid_t *id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
142
app/coap/node.c
142
app/coap/node.c
@ -1,142 +0,0 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "esp_libc.h"
|
||||
#include "node.h"
|
||||
|
||||
static inline coap_queue_t *
|
||||
coap_malloc_node(void) {
|
||||
return (coap_queue_t *)zalloc(sizeof(coap_queue_t));
|
||||
}
|
||||
|
||||
void coap_free_node(coap_queue_t *node) {
|
||||
free(node);
|
||||
}
|
||||
|
||||
int coap_insert_node(coap_queue_t **queue, coap_queue_t *node) {
|
||||
coap_queue_t *p, *q;
|
||||
if ( !queue || !node )
|
||||
return 0;
|
||||
|
||||
/* set queue head if empty */
|
||||
if ( !*queue ) {
|
||||
*queue = node;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* replace queue head if PDU's time is less than head's time */
|
||||
q = *queue;
|
||||
if (node->t < q->t) {
|
||||
node->next = q;
|
||||
*queue = node;
|
||||
q->t -= node->t; /* make q->t relative to node->t */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* search for right place to insert */
|
||||
do {
|
||||
node->t -= q->t; /* make node-> relative to q->t */
|
||||
p = q;
|
||||
q = q->next;
|
||||
} while (q && q->t <= node->t);
|
||||
|
||||
/* insert new item */
|
||||
if (q) {
|
||||
q->t -= node->t; /* make q->t relative to node->t */
|
||||
}
|
||||
node->next = q;
|
||||
p->next = node;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int coap_delete_node(coap_queue_t *node) {
|
||||
if ( !node )
|
||||
return 0;
|
||||
|
||||
coap_delete_pdu(node->pdu);
|
||||
coap_free_node(node);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void coap_delete_all(coap_queue_t *queue) {
|
||||
if ( !queue )
|
||||
return;
|
||||
|
||||
coap_delete_all( queue->next );
|
||||
coap_delete_node( queue );
|
||||
}
|
||||
|
||||
coap_queue_t * coap_new_node(void) {
|
||||
coap_queue_t *node;
|
||||
node = coap_malloc_node();
|
||||
|
||||
if ( ! node ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(node, 0, sizeof(*node));
|
||||
return node;
|
||||
}
|
||||
|
||||
coap_queue_t * coap_peek_next( coap_queue_t *queue ) {
|
||||
if ( !queue )
|
||||
return NULL;
|
||||
|
||||
return queue;
|
||||
}
|
||||
|
||||
coap_queue_t * coap_pop_next( coap_queue_t **queue ) { // this function is called inside timeout callback only.
|
||||
coap_queue_t *next;
|
||||
|
||||
if ( !(*queue) )
|
||||
return NULL;
|
||||
|
||||
next = *queue;
|
||||
*queue = (*queue)->next;
|
||||
// if (queue) {
|
||||
// queue->t += next->t;
|
||||
// }
|
||||
next->next = NULL;
|
||||
return next;
|
||||
}
|
||||
|
||||
int coap_remove_node( coap_queue_t **queue, const coap_tid_t id){
|
||||
coap_queue_t *p, *q, *node;
|
||||
if ( !queue )
|
||||
return 0;
|
||||
if ( !*queue ) // if empty
|
||||
return 0;
|
||||
|
||||
q = *queue;
|
||||
if (q->id == id) {
|
||||
node = q;
|
||||
*queue = q->next;
|
||||
node->next = NULL;
|
||||
if(*queue){
|
||||
(*queue)->t += node->t;
|
||||
}
|
||||
coap_delete_node(node);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* search for right node to remove */
|
||||
while (q && q->id != id) {
|
||||
p = q;
|
||||
q = q->next;
|
||||
}
|
||||
|
||||
/* find the node */
|
||||
if (q) {
|
||||
node = q; /* save the node */
|
||||
p->next = q->next; /* remove the node */
|
||||
q = q->next;
|
||||
node->next = NULL;
|
||||
if (q) // add node->t to the node after.
|
||||
{
|
||||
q->t += node->t;
|
||||
}
|
||||
coap_delete_node(node);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
#ifndef _NODE_H
|
||||
#define _NODE_H 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "hash.h"
|
||||
#include "pdu.h"
|
||||
|
||||
struct coap_queue_t;
|
||||
typedef uint32_t coap_tick_t;
|
||||
|
||||
/*
|
||||
1. queue(first)->t store when to send PDU for the next time, it's a base(absolute) time
|
||||
2. queue->next->t store the delta between time and base-time. queue->next->t = timeout + now - basetime
|
||||
3. node->next->t store the delta between time and previous->t. node->next->t = timeout + now - node->t - basetime
|
||||
4. time to fire: 10, 15, 18, 25
|
||||
node->t: 10, 5, 3, 7
|
||||
*/
|
||||
|
||||
typedef struct coap_queue_t {
|
||||
struct coap_queue_t *next;
|
||||
|
||||
coap_tick_t t; /**< when to send PDU for the next time */
|
||||
unsigned char retransmit_cnt; /**< retransmission counter, will be removed when zero */
|
||||
unsigned int timeout; /**< the randomized timeout value */
|
||||
|
||||
coap_tid_t id; /**< unique transaction id */
|
||||
|
||||
// coap_packet_t *pkt;
|
||||
coap_pdu_t *pdu; /**< the CoAP PDU to send */
|
||||
struct espconn *pconn;
|
||||
} coap_queue_t;
|
||||
|
||||
void coap_free_node(coap_queue_t *node);
|
||||
|
||||
/** Adds node to given queue, ordered by node->t. */
|
||||
int coap_insert_node(coap_queue_t **queue, coap_queue_t *node);
|
||||
|
||||
/** Destroys specified node. */
|
||||
int coap_delete_node(coap_queue_t *node);
|
||||
|
||||
/** Removes all items from given queue and frees the allocated storage. */
|
||||
void coap_delete_all(coap_queue_t *queue);
|
||||
|
||||
/** Creates a new node suitable for adding to the CoAP sendqueue. */
|
||||
coap_queue_t *coap_new_node(void);
|
||||
|
||||
coap_queue_t *coap_pop_next( coap_queue_t **queue );
|
||||
|
||||
int coap_remove_node( coap_queue_t **queue, const coap_tid_t id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,66 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include "pdu.h"
|
||||
#include "esp_libc.h"
|
||||
|
||||
coap_pdu_t * coap_new_pdu(void) {
|
||||
coap_pdu_t *pdu = NULL;
|
||||
pdu = (coap_pdu_t *)zalloc(sizeof(coap_pdu_t));
|
||||
if(!pdu){
|
||||
NODE_DBG("coap_new_pdu malloc error.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pdu->scratch.p = (uint8_t *)zalloc(MAX_REQ_SCRATCH_SIZE);
|
||||
if(!pdu->scratch.p){
|
||||
NODE_DBG("coap_new_pdu malloc error.\n");
|
||||
free(pdu);
|
||||
return NULL;
|
||||
}
|
||||
pdu->scratch.len = MAX_REQ_SCRATCH_SIZE;
|
||||
|
||||
pdu->pkt = (coap_packet_t *)zalloc(sizeof(coap_packet_t));
|
||||
if(!pdu->pkt){
|
||||
NODE_DBG("coap_new_pdu malloc error.\n");
|
||||
free(pdu->scratch.p);
|
||||
free(pdu);
|
||||
return NULL;
|
||||
}
|
||||
pdu->pkt->content.p = NULL;
|
||||
pdu->pkt->content.len = 0;
|
||||
|
||||
pdu->msg.p = (uint8_t *)zalloc(MAX_REQUEST_SIZE+1); // +1 for string '\0'
|
||||
if(!pdu->msg.p){
|
||||
NODE_DBG("coap_new_pdu malloc error.\n");
|
||||
free(pdu->pkt);
|
||||
free(pdu->scratch.p);
|
||||
free(pdu);
|
||||
return NULL;
|
||||
}
|
||||
pdu->msg.len = MAX_REQUEST_SIZE;
|
||||
return pdu;
|
||||
}
|
||||
|
||||
void coap_delete_pdu(coap_pdu_t *pdu){
|
||||
if(!pdu)
|
||||
return;
|
||||
|
||||
if(pdu->scratch.p){
|
||||
free(pdu->scratch.p);
|
||||
pdu->scratch.p = NULL;
|
||||
pdu->scratch.len = 0;
|
||||
}
|
||||
|
||||
if(pdu->pkt){
|
||||
free(pdu->pkt);
|
||||
pdu->pkt = NULL;
|
||||
}
|
||||
|
||||
if(pdu->msg.p){
|
||||
free(pdu->msg.p);
|
||||
pdu->msg.p = NULL;
|
||||
pdu->msg.len = 0;
|
||||
}
|
||||
|
||||
free(pdu);
|
||||
pdu = NULL;
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
#ifndef _PDU_H
|
||||
#define _PDU_H 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "coap.h"
|
||||
|
||||
/** Header structure for CoAP PDUs */
|
||||
typedef struct {
|
||||
coap_rw_buffer_t scratch;
|
||||
coap_packet_t *pkt;
|
||||
coap_rw_buffer_t msg; /**< the CoAP msg to send */
|
||||
} coap_pdu_t;
|
||||
|
||||
coap_pdu_t *coap_new_pdu(void);
|
||||
|
||||
void coap_delete_pdu(coap_pdu_t *pdu);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,28 +0,0 @@
|
||||
/* str.c -- strings to be used in the CoAP library
|
||||
*
|
||||
* Copyright (C) 2010,2011 Olaf Bergmann <bergmann@tzi.org>
|
||||
*
|
||||
* This file is part of the CoAP library libcoap. Please see
|
||||
* README for terms of use.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "str.h"
|
||||
|
||||
str * coap_new_string(size_t size) {
|
||||
str *s = (str *)malloc(sizeof(str) + size + 1);
|
||||
if ( !s ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(s, 0, sizeof(str));
|
||||
s->s = ((unsigned char *)s) + sizeof(str);
|
||||
return s;
|
||||
}
|
||||
|
||||
void coap_delete_string(str *s) {
|
||||
free(s);
|
||||
}
|
||||
|
@ -1,30 +0,0 @@
|
||||
/* str.h -- strings to be used in the CoAP library
|
||||
*
|
||||
* Copyright (C) 2010,2011 Olaf Bergmann <bergmann@tzi.org>
|
||||
*
|
||||
* This file is part of the CoAP library libcoap. Please see
|
||||
* README for terms of use.
|
||||
*/
|
||||
|
||||
#ifndef _COAP_STR_H_
|
||||
#define _COAP_STR_H_
|
||||
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
size_t length; /* length of string */
|
||||
unsigned char *s; /* string data */
|
||||
} str;
|
||||
|
||||
#define COAP_SET_STR(st,l,v) { (st)->length = (l), (st)->s = (v); }
|
||||
|
||||
/**
|
||||
* Returns a new string object with at least size bytes storage
|
||||
* allocated. The string must be released using coap_delete_string();
|
||||
*/
|
||||
str *coap_new_string(size_t size);
|
||||
|
||||
/** Deletes the given string and releases any memory allocated. */
|
||||
void coap_delete_string(str *);
|
||||
|
||||
#endif /* _COAP_STR_H_ */
|
468
app/coap/uri.c
468
app/coap/uri.c
@ -1,468 +0,0 @@
|
||||
/* uri.c -- helper functions for URI treatment
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "coap.h"
|
||||
#include "uri.h"
|
||||
|
||||
#ifndef assert
|
||||
// #warning "assertions are disabled"
|
||||
# define assert(x) do { \
|
||||
if(!x) NODE_ERR("uri.c assert!\n"); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* A length-safe version of strchr(). This function returns a pointer
|
||||
* to the first occurrence of @p c in @p s, or @c NULL if not found.
|
||||
*
|
||||
* @param s The string to search for @p c.
|
||||
* @param len The length of @p s.
|
||||
* @param c The character to search.
|
||||
*
|
||||
* @return A pointer to the first occurence of @p c, or @c NULL
|
||||
* if not found.
|
||||
*/
|
||||
static inline unsigned char *
|
||||
strnchr(unsigned char *s, size_t len, unsigned char c) {
|
||||
while (len && *s++ != c)
|
||||
--len;
|
||||
|
||||
return len ? s : NULL;
|
||||
}
|
||||
|
||||
int coap_split_uri(unsigned char *str_var, size_t len, coap_uri_t *uri) {
|
||||
unsigned char *p, *q;
|
||||
int secure = 0, res = 0;
|
||||
|
||||
if (!str_var || !uri)
|
||||
return -1;
|
||||
|
||||
memset(uri, 0, sizeof(coap_uri_t));
|
||||
uri->port = COAP_DEFAULT_PORT;
|
||||
|
||||
/* search for scheme */
|
||||
p = str_var;
|
||||
if (*p == '/') {
|
||||
q = p;
|
||||
goto path;
|
||||
}
|
||||
|
||||
q = (unsigned char *)COAP_DEFAULT_SCHEME;
|
||||
while (len && *q && tolower(*p) == *q) {
|
||||
++p; ++q; --len;
|
||||
}
|
||||
|
||||
/* If q does not point to the string end marker '\0', the schema
|
||||
* identifier is wrong. */
|
||||
if (*q) {
|
||||
res = -1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* There might be an additional 's', indicating the secure version: */
|
||||
if (len && (secure = tolower(*p) == 's')) {
|
||||
++p; --len;
|
||||
}
|
||||
|
||||
q = (unsigned char *)"://";
|
||||
while (len && *q && tolower(*p) == *q) {
|
||||
++p; ++q; --len;
|
||||
}
|
||||
|
||||
if (*q) {
|
||||
res = -2;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* p points to beginning of Uri-Host */
|
||||
q = p;
|
||||
if (len && *p == '[') { /* IPv6 address reference */
|
||||
++p;
|
||||
|
||||
while (len && *q != ']') {
|
||||
++q; --len;
|
||||
}
|
||||
|
||||
if (!len || *q != ']' || p == q) {
|
||||
res = -3;
|
||||
goto error;
|
||||
}
|
||||
|
||||
COAP_SET_STR(&uri->host, q - p, p);
|
||||
++q; --len;
|
||||
} else { /* IPv4 address or FQDN */
|
||||
while (len && *q != ':' && *q != '/' && *q != '?') {
|
||||
*q = tolower(*q);
|
||||
++q;
|
||||
--len;
|
||||
}
|
||||
|
||||
if (p == q) {
|
||||
res = -3;
|
||||
goto error;
|
||||
}
|
||||
|
||||
COAP_SET_STR(&uri->host, q - p, p);
|
||||
}
|
||||
|
||||
/* check for Uri-Port */
|
||||
if (len && *q == ':') {
|
||||
p = ++q;
|
||||
--len;
|
||||
|
||||
while (len && isdigit(*q)) {
|
||||
++q;
|
||||
--len;
|
||||
}
|
||||
|
||||
if (p < q) { /* explicit port number given */
|
||||
int uri_port = 0;
|
||||
|
||||
while (p < q)
|
||||
uri_port = uri_port * 10 + (*p++ - '0');
|
||||
|
||||
uri->port = uri_port;
|
||||
}
|
||||
}
|
||||
|
||||
path: /* at this point, p must point to an absolute path */
|
||||
|
||||
if (!len)
|
||||
goto end;
|
||||
|
||||
if (*q == '/') {
|
||||
p = ++q;
|
||||
--len;
|
||||
|
||||
while (len && *q != '?') {
|
||||
++q;
|
||||
--len;
|
||||
}
|
||||
|
||||
if (p < q) {
|
||||
COAP_SET_STR(&uri->path, q - p, p);
|
||||
p = q;
|
||||
}
|
||||
}
|
||||
|
||||
/* Uri_Query */
|
||||
if (len && *p == '?') {
|
||||
++p;
|
||||
--len;
|
||||
COAP_SET_STR(&uri->query, len, p);
|
||||
len = 0;
|
||||
}
|
||||
|
||||
end:
|
||||
return len ? -1 : 0;
|
||||
|
||||
error:
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates decimal value from hexadecimal ASCII character given in
|
||||
* @p c. The caller must ensure that @p c actually represents a valid
|
||||
* heaxdecimal character, e.g. with isxdigit(3).
|
||||
*
|
||||
* @hideinitializer
|
||||
*/
|
||||
#define hexchar_to_dec(c) ((c) & 0x40 ? ((c) & 0x0F) + 9 : ((c) & 0x0F))
|
||||
|
||||
/**
|
||||
* Decodes percent-encoded characters while copying the string @p seg
|
||||
* of size @p length to @p buf. The caller of this function must
|
||||
* ensure that the percent-encodings are correct (i.e. the character
|
||||
* '%' is always followed by two hex digits. and that @p buf provides
|
||||
* sufficient space to hold the result. This function is supposed to
|
||||
* be called by make_decoded_option() only.
|
||||
*
|
||||
* @param seg The segment to decode and copy.
|
||||
* @param length Length of @p seg.
|
||||
* @param buf The result buffer.
|
||||
*/
|
||||
void decode_segment(const unsigned char *seg, size_t length, unsigned char *buf) {
|
||||
|
||||
while (length--) {
|
||||
|
||||
if (*seg == '%') {
|
||||
*buf = (hexchar_to_dec(seg[1]) << 4) + hexchar_to_dec(seg[2]);
|
||||
|
||||
seg += 2; length -= 2;
|
||||
} else {
|
||||
*buf = *seg;
|
||||
}
|
||||
|
||||
++buf; ++seg;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs through the given path (or query) segment and checks if
|
||||
* percent-encodings are correct. This function returns @c -1 on error
|
||||
* or the length of @p s when decoded.
|
||||
*/
|
||||
int check_segment(const unsigned char *s, size_t length) {
|
||||
|
||||
size_t n = 0;
|
||||
|
||||
while (length) {
|
||||
if (*s == '%') {
|
||||
if (length < 2 || !(isxdigit(s[1]) && isxdigit(s[2])))
|
||||
return -1;
|
||||
|
||||
s += 2;
|
||||
length -= 2;
|
||||
}
|
||||
|
||||
++s; ++n; --length;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a coap option from given string @p s to @p buf. @p s should
|
||||
* point to a (percent-encoded) path or query segment of a coap_uri_t
|
||||
* object. The created option will have type @c 0, and the length
|
||||
* parameter will be set according to the size of the decoded string.
|
||||
* On success, this function returns the option's size, or a value
|
||||
* less than zero on error. This function must be called from
|
||||
* coap_split_path_impl() only.
|
||||
*
|
||||
* @param s The string to decode.
|
||||
* @param length The size of the percent-encoded string @p s.
|
||||
* @param buf The buffer to store the new coap option.
|
||||
* @param buflen The maximum size of @p buf.
|
||||
*
|
||||
* @return The option's size, or @c -1 on error.
|
||||
*
|
||||
* @bug This function does not split segments that are bigger than 270
|
||||
* bytes.
|
||||
*/
|
||||
int make_decoded_option(const unsigned char *s, size_t length,
|
||||
unsigned char *buf, size_t buflen) {
|
||||
int res;
|
||||
size_t written;
|
||||
|
||||
if (!buflen) {
|
||||
NODE_DBG("make_decoded_option(): buflen is 0!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = check_segment(s, length);
|
||||
if (res < 0)
|
||||
return -1;
|
||||
|
||||
/* write option header using delta 0 and length res */
|
||||
// written = coap_opt_setheader(buf, buflen, 0, res);
|
||||
written = coap_buildOptionHeader(0, res, buf, buflen);
|
||||
|
||||
assert(written <= buflen);
|
||||
|
||||
if (!written) /* encoding error */
|
||||
return -1;
|
||||
|
||||
buf += written; /* advance past option type/length */
|
||||
buflen -= written;
|
||||
|
||||
if (buflen < (size_t)res) {
|
||||
NODE_DBG("buffer too small for option\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
decode_segment(s, length, buf);
|
||||
|
||||
return written + res;
|
||||
}
|
||||
|
||||
|
||||
#ifndef min
|
||||
#define min(a,b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
typedef void (*segment_handler_t)(unsigned char *, size_t, void *);
|
||||
|
||||
/**
|
||||
* Splits the given string into segments. You should call one of the
|
||||
* macros coap_split_path() or coap_split_query() instead.
|
||||
*
|
||||
* @param parse_iter The iterator used for tokenizing.
|
||||
* @param h A handler that is called with every token.
|
||||
* @param data Opaque data that is passed to @p h when called.
|
||||
*
|
||||
* @return The number of characters that have been parsed from @p s.
|
||||
*/
|
||||
size_t coap_split_path_impl(coap_parse_iterator_t *parse_iter,
|
||||
segment_handler_t h, void *data) {
|
||||
unsigned char *seg;
|
||||
size_t length;
|
||||
|
||||
assert(parse_iter);
|
||||
assert(h);
|
||||
|
||||
length = parse_iter->n;
|
||||
|
||||
while ( (seg = coap_parse_next(parse_iter)) ) {
|
||||
|
||||
/* any valid path segment is handled here: */
|
||||
h(seg, parse_iter->segment_length, data);
|
||||
}
|
||||
|
||||
return length - (parse_iter->n - parse_iter->segment_length);
|
||||
}
|
||||
|
||||
struct pkt_scr {
|
||||
coap_packet_t *pkt;
|
||||
coap_rw_buffer_t *scratch;
|
||||
int n;
|
||||
};
|
||||
|
||||
void write_option(unsigned char *s, size_t len, void *data) {
|
||||
struct pkt_scr *state = (struct pkt_scr *)data;
|
||||
int res;
|
||||
assert(state);
|
||||
|
||||
/* skip empty segments and those that consist of only one or two dots */
|
||||
if (memcmp(s, "..", min(len,2)) == 0)
|
||||
return;
|
||||
|
||||
res = check_segment(s, len);
|
||||
if (res < 0){
|
||||
NODE_DBG("not a valid segment\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (state->scratch->len < (size_t)res) {
|
||||
NODE_DBG("buffer too small for option\n");
|
||||
return;
|
||||
}
|
||||
|
||||
decode_segment(s, len, state->scratch->p);
|
||||
|
||||
if (res > 0) {
|
||||
state->pkt->opts[state->pkt->numopts].buf.p = state->scratch->p;
|
||||
state->pkt->opts[state->pkt->numopts].buf.len = res;
|
||||
state->scratch->p += res;
|
||||
state->scratch->len -= res;
|
||||
state->pkt->numopts++;
|
||||
state->n++;
|
||||
}
|
||||
}
|
||||
|
||||
int coap_split_path(coap_rw_buffer_t *scratch, coap_packet_t *pkt, const unsigned char *s, size_t length) {
|
||||
struct pkt_scr tmp = { pkt, scratch, 0 };
|
||||
coap_parse_iterator_t pi;
|
||||
|
||||
coap_parse_iterator_init((unsigned char *)s, length,
|
||||
'/', (unsigned char *)"?#", 2, &pi);
|
||||
coap_split_path_impl(&pi, write_option, &tmp);
|
||||
|
||||
int i;
|
||||
for(i=0;i<tmp.n;i++){
|
||||
pkt->opts[pkt->numopts - i - 1].num = COAP_OPTION_URI_PATH;
|
||||
}
|
||||
|
||||
return tmp.n;
|
||||
}
|
||||
|
||||
int coap_split_query(coap_rw_buffer_t *scratch, coap_packet_t *pkt, const unsigned char *s, size_t length) {
|
||||
struct pkt_scr tmp = { pkt, scratch, 0 };
|
||||
coap_parse_iterator_t pi;
|
||||
|
||||
coap_parse_iterator_init((unsigned char *)s, length,
|
||||
'&', (unsigned char *)"#", 1, &pi);
|
||||
|
||||
coap_split_path_impl(&pi, write_option, &tmp);
|
||||
|
||||
int i;
|
||||
for(i=0;i<tmp.n;i++){
|
||||
pkt->opts[pkt->numopts - i - 1].num = COAP_OPTION_URI_QUERY;
|
||||
}
|
||||
|
||||
return tmp.n;
|
||||
}
|
||||
|
||||
#define URI_DATA(uriobj) ((unsigned char *)(uriobj) + sizeof(coap_uri_t))
|
||||
|
||||
coap_uri_t * coap_new_uri(const unsigned char *uri, unsigned int length) {
|
||||
unsigned char *result;
|
||||
|
||||
result = (unsigned char *)malloc(length + 1 + sizeof(coap_uri_t));
|
||||
|
||||
if (!result)
|
||||
return NULL;
|
||||
|
||||
memcpy(URI_DATA(result), uri, length);
|
||||
URI_DATA(result)[length] = '\0'; /* make it zero-terminated */
|
||||
|
||||
if (coap_split_uri(URI_DATA(result), length, (coap_uri_t *)result) < 0) {
|
||||
free(result);
|
||||
return NULL;
|
||||
}
|
||||
return (coap_uri_t *)result;
|
||||
}
|
||||
|
||||
/* iterator functions */
|
||||
|
||||
coap_parse_iterator_t * coap_parse_iterator_init(unsigned char *s, size_t n,
|
||||
unsigned char separator,
|
||||
unsigned char *delim, size_t dlen,
|
||||
coap_parse_iterator_t *pi) {
|
||||
assert(pi);
|
||||
assert(separator);
|
||||
|
||||
pi->separator = separator;
|
||||
pi->delim = delim;
|
||||
pi->dlen = dlen;
|
||||
pi->pos = s;
|
||||
pi->n = n;
|
||||
pi->segment_length = 0;
|
||||
|
||||
return pi;
|
||||
}
|
||||
|
||||
unsigned char * coap_parse_next(coap_parse_iterator_t *pi) {
|
||||
unsigned char *p;
|
||||
|
||||
if (!pi)
|
||||
return NULL;
|
||||
|
||||
/* proceed to the next segment */
|
||||
pi->n -= pi->segment_length;
|
||||
pi->pos += pi->segment_length;
|
||||
pi->segment_length = 0;
|
||||
|
||||
/* last segment? */
|
||||
if (!pi->n || strnchr(pi->delim, pi->dlen, *pi->pos)) {
|
||||
pi->pos = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* skip following separator (the first segment might not have one) */
|
||||
if (*pi->pos == pi->separator) {
|
||||
++pi->pos;
|
||||
--pi->n;
|
||||
}
|
||||
|
||||
p = pi->pos;
|
||||
|
||||
while (pi->segment_length < pi->n && *p != pi->separator &&
|
||||
!strnchr(pi->delim, pi->dlen, *p)) {
|
||||
++p;
|
||||
++pi->segment_length;
|
||||
}
|
||||
|
||||
if (!pi->n) {
|
||||
pi->pos = NULL;
|
||||
pi->segment_length = 0;
|
||||
}
|
||||
|
||||
return pi->pos;
|
||||
}
|
166
app/coap/uri.h
166
app/coap/uri.h
@ -1,166 +0,0 @@
|
||||
/* uri.h -- helper functions for URI treatment
|
||||
*
|
||||
* Copyright (C) 2010,2011 Olaf Bergmann <bergmann@tzi.org>
|
||||
*
|
||||
* This file is part of the CoAP library libcoap. Please see
|
||||
* README for terms of use.
|
||||
*/
|
||||
|
||||
#ifndef _COAP_URI_H_
|
||||
#define _COAP_URI_H_
|
||||
|
||||
#define COAP_DEFAULT_SCHEME "coap" /* the default scheme for CoAP URIs */
|
||||
#define COAP_DEFAULT_PORT 5683
|
||||
|
||||
#include "str.h"
|
||||
|
||||
/** Representation of parsed URI. Components may be filled from a
|
||||
* string with coap_split_uri() and can be used as input for
|
||||
* option-creation functions. */
|
||||
typedef struct {
|
||||
str host; /**< host part of the URI */
|
||||
unsigned short port; /**< The port in host byte order */
|
||||
str path; /**< Beginning of the first path segment.
|
||||
Use coap_split_path() to create Uri-Path options */
|
||||
str query; /**< The query part if present */
|
||||
} coap_uri_t;
|
||||
|
||||
/**
|
||||
* Creates a new coap_uri_t object from the specified URI. Returns the new
|
||||
* object or NULL on error. The memory allocated by the new coap_uri_t
|
||||
* must be released using coap_free().
|
||||
* @param uri The URI path to copy.
|
||||
* @para length The length of uri.
|
||||
*
|
||||
* @return New URI object or NULL on error.
|
||||
*/
|
||||
coap_uri_t *coap_new_uri(const unsigned char *uri, unsigned int length);
|
||||
|
||||
/**
|
||||
* @defgroup uri_parse URI Parsing Functions
|
||||
*
|
||||
* CoAP PDUs contain normalized URIs with their path and query split into
|
||||
* multiple segments. The functions in this module help splitting strings.
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Iterator to for tokenizing a URI path or query. This structure must
|
||||
* be initialized with coap_parse_iterator_init(). Call
|
||||
* coap_parse_next() to walk through the tokens.
|
||||
*
|
||||
* @code
|
||||
* unsigned char *token;
|
||||
* coap_parse_iterator_t pi;
|
||||
* coap_parse_iterator_init(uri.path.s, uri.path.length, '/', "?#", 2, &pi);
|
||||
*
|
||||
* while ((token = coap_parse_next(&pi))) {
|
||||
* ... do something with token ...
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
typedef struct {
|
||||
size_t n; /**< number of remaining characters in buffer */
|
||||
unsigned char separator; /**< segment separators */
|
||||
unsigned char *delim; /**< delimiters where to split the string */
|
||||
size_t dlen; /**< length of separator */
|
||||
unsigned char *pos; /**< current position in buffer */
|
||||
size_t segment_length; /**< length of current segment */
|
||||
} coap_parse_iterator_t;
|
||||
|
||||
/**
|
||||
* Initializes the given iterator @p pi.
|
||||
*
|
||||
* @param s The string to tokenize.
|
||||
* @param n The length of @p s.
|
||||
* @param separator The separator character that delimits tokens.
|
||||
* @param delim A set of characters that delimit @s.
|
||||
* @param dlen The length of @p delim.
|
||||
* @param pi The iterator object to initialize.
|
||||
*
|
||||
* @return The initialized iterator object @p pi.
|
||||
*/
|
||||
coap_parse_iterator_t *
|
||||
coap_parse_iterator_init(unsigned char *s, size_t n,
|
||||
unsigned char separator,
|
||||
unsigned char *delim, size_t dlen,
|
||||
coap_parse_iterator_t *pi);
|
||||
|
||||
/**
|
||||
* Updates the iterator @p pi to point to the next token. This
|
||||
* function returns a pointer to that token or @c NULL if no more
|
||||
* tokens exist. The contents of @p pi will be updated. In particular,
|
||||
* @c pi->segment_length specifies the length of the current token, @c
|
||||
* pi->pos points to its beginning.
|
||||
*
|
||||
* @param pi The iterator to update.
|
||||
*
|
||||
* @return The next token or @c NULL if no more tokens exist.
|
||||
*/
|
||||
unsigned char *coap_parse_next(coap_parse_iterator_t *pi);
|
||||
|
||||
/**
|
||||
* Parses a given string into URI components. The identified syntactic
|
||||
* components are stored in the result parameter @p uri. Optional URI
|
||||
* components that are not specified will be set to { 0, 0 }, except
|
||||
* for the port which is set to @c COAP_DEFAULT_PORT. This function
|
||||
* returns @p 0 if parsing succeeded, a value less than zero
|
||||
* otherwise.
|
||||
*
|
||||
* @param str_var The string to split up.
|
||||
* @param len The actual length of @p str_var
|
||||
* @param uri The coap_uri_t object to store the result.
|
||||
* @return @c 0 on success, or < 0 on error.
|
||||
*
|
||||
* @note The host name part will be converted to lower case by this
|
||||
* function.
|
||||
*/
|
||||
int
|
||||
coap_split_uri(unsigned char *str_var, size_t len, coap_uri_t *uri);
|
||||
|
||||
/**
|
||||
* Splits the given URI path into segments. Each segment is preceded
|
||||
* by an option pseudo-header with delta-value 0 and the actual length
|
||||
* of the respective segment after percent-decoding.
|
||||
*
|
||||
* @param s The path string to split.
|
||||
* @param length The actual length of @p s.
|
||||
* @param buf Result buffer for parsed segments.
|
||||
* @param buflen Maximum length of @p buf. Will be set to the actual number
|
||||
* of bytes written into buf on success.
|
||||
*
|
||||
* @return The number of segments created or @c -1 on error.
|
||||
*/
|
||||
#if 0
|
||||
int coap_split_path(const unsigned char *s, size_t length,
|
||||
unsigned char *buf, size_t *buflen);
|
||||
#else
|
||||
int
|
||||
coap_split_path(coap_rw_buffer_t *scratch, coap_packet_t *pkt,
|
||||
const unsigned char *s, size_t length);
|
||||
#endif
|
||||
/**
|
||||
* Splits the given URI query into segments. Each segment is preceded
|
||||
* by an option pseudo-header with delta-value 0 and the actual length
|
||||
* of the respective query term.
|
||||
*
|
||||
* @param s The query string to split.
|
||||
* @param length The actual length of @p s.
|
||||
* @param buf Result buffer for parsed segments.
|
||||
* @param buflen Maximum length of @p buf. Will be set to the actual number
|
||||
* of bytes written into buf on success.
|
||||
*
|
||||
* @return The number of segments created or @c -1 on error.
|
||||
*
|
||||
* @bug This function does not reserve additional space for delta > 12.
|
||||
*/
|
||||
#if 0
|
||||
int coap_split_query(const unsigned char *s, size_t length,
|
||||
unsigned char *buf, size_t *buflen);
|
||||
#else
|
||||
int coap_split_query(coap_rw_buffer_t *scratch, coap_packet_t *pkt,
|
||||
const unsigned char *s, size_t length);
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
#endif /* _COAP_URI_H_ */
|
@ -1,46 +0,0 @@
|
||||
|
||||
#############################################################
|
||||
# Required variables for each makefile
|
||||
# Discard this section from all parent makefiles
|
||||
# Expected variables (with automatic defaults):
|
||||
# CSRCS (all "C" files in the dir)
|
||||
# SUBDIRS (all subdirs with a Makefile)
|
||||
# GEN_LIBS - list of libs to be generated ()
|
||||
# GEN_IMAGES - list of images to be generated ()
|
||||
# COMPONENTS_xxx - a list of libs/objs in the form
|
||||
# subdir/lib to be extracted and rolled up into
|
||||
# a generated lib/image xxx.a ()
|
||||
#
|
||||
ifndef PDIR
|
||||
GEN_LIBS = libcrypto.a
|
||||
endif
|
||||
|
||||
STD_CFLAGS=-std=gnu11 -Wimplicit
|
||||
|
||||
#############################################################
|
||||
# Configuration i.e. compile options etc.
|
||||
# Target specific stuff (defines etc.) goes in here!
|
||||
# Generally values applying to a tree are captured in the
|
||||
# makefile at its root level - these are then overridden
|
||||
# for a subtree within the makefile rooted therein
|
||||
#
|
||||
#DEFINES +=
|
||||
|
||||
#############################################################
|
||||
# Recursion Magic - Don't touch this!!
|
||||
#
|
||||
# Each subtree potentially has an include directory
|
||||
# corresponding to the common APIs applicable to modules
|
||||
# rooted at that subtree. Accordingly, the INCLUDE PATH
|
||||
# of a module can only contain the include directories up
|
||||
# its parent path, and not its siblings
|
||||
#
|
||||
# Required for each makefile to inherit from the parent
|
||||
#
|
||||
|
||||
INCLUDES := $(INCLUDES) -I $(PDIR)include
|
||||
INCLUDES += -I ./
|
||||
INCLUDES += -I ../libc
|
||||
PDIR := ../$(PDIR)
|
||||
sinclude $(PDIR)Makefile
|
||||
|
@ -1,206 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015, DiUS Computing Pty Ltd (jmattsson@dius.com.au)
|
||||
* 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. Neither the name of the copyright holder nor the names of contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``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 OR CONTRIBUTOR(S) 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.
|
||||
*
|
||||
*/
|
||||
#include "digests.h"
|
||||
#include "user_config.h"
|
||||
#include "rom.h"
|
||||
#include "osapi.h"
|
||||
#include "mem.h"
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef MD2_ENABLE
|
||||
#include "ssl/ssl_crypto.h"
|
||||
#endif
|
||||
|
||||
#ifdef SHA2_ENABLE
|
||||
#include "sha2.h"
|
||||
#endif
|
||||
|
||||
typedef char ensure_int_and_size_t_same[(sizeof(int)==sizeof(size_t)) ? 0 : -1];
|
||||
|
||||
/* None of the functions match the prototype fully due to the void *, and in
|
||||
some cases also the int vs size_t len, so wrap declarations in a macro. */
|
||||
#define MECH(pfx, u, ds, bs) \
|
||||
{ #pfx, \
|
||||
(create_ctx_fn)pfx ## u ## Init, \
|
||||
(update_ctx_fn)pfx ## u ## Update, \
|
||||
(finalize_ctx_fn)pfx ## u ## Final, \
|
||||
sizeof(pfx ## _CTX), \
|
||||
ds, \
|
||||
bs }
|
||||
|
||||
static const digest_mech_info_t hash_mechs[] ICACHE_RODATA_ATTR =
|
||||
{
|
||||
#ifdef MD2_ENABLE
|
||||
MECH(MD2, _ , MD2_SIZE, 16),
|
||||
#endif
|
||||
MECH(MD5, , MD5_DIGEST_LENGTH, 64)
|
||||
,MECH(SHA1, , SHA1_DIGEST_LENGTH, 64)
|
||||
#ifdef SHA2_ENABLE
|
||||
,MECH(SHA256, _ , SHA256_DIGEST_LENGTH, SHA256_BLOCK_LENGTH)
|
||||
,MECH(SHA384, _ , SHA384_DIGEST_LENGTH, SHA384_BLOCK_LENGTH)
|
||||
,MECH(SHA512, _ , SHA512_DIGEST_LENGTH, SHA512_BLOCK_LENGTH)
|
||||
#endif
|
||||
};
|
||||
|
||||
#undef MECH
|
||||
|
||||
const digest_mech_info_t *ICACHE_FLASH_ATTR crypto_digest_mech (const char *mech)
|
||||
{
|
||||
if (!mech)
|
||||
return 0;
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < (sizeof (hash_mechs) / sizeof (digest_mech_info_t)); ++i)
|
||||
{
|
||||
const digest_mech_info_t *mi = hash_mechs + i;
|
||||
if (strcasecmp (mech, mi->name) == 0)
|
||||
return mi;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char crypto_hexbytes[] = "0123456789abcdef";
|
||||
|
||||
// note: supports in-place encoding
|
||||
void ICACHE_FLASH_ATTR crypto_encode_asciihex (const char *bin, size_t binlen, char *outbuf)
|
||||
{
|
||||
size_t aidx = binlen * 2 -1;
|
||||
int i;
|
||||
for (i = binlen -1; i >= 0; --i)
|
||||
{
|
||||
outbuf[aidx--] = crypto_hexbytes[bin[i] & 0xf];
|
||||
outbuf[aidx--] = crypto_hexbytes[bin[i] >> 4];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int ICACHE_FLASH_ATTR crypto_hash (const digest_mech_info_t *mi,
|
||||
const char *data, size_t data_len,
|
||||
uint8_t *digest)
|
||||
{
|
||||
if (!mi)
|
||||
return EINVAL;
|
||||
|
||||
void *ctx = (void *)malloc (mi->ctx_size);
|
||||
if (!ctx)
|
||||
return ENOMEM;
|
||||
|
||||
mi->create (ctx);
|
||||
mi->update (ctx, data, data_len);
|
||||
mi->finalize (digest, ctx);
|
||||
|
||||
free (ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int ICACHE_FLASH_ATTR crypto_fhash (const digest_mech_info_t *mi,
|
||||
read_fn read, int readarg,
|
||||
uint8_t *digest)
|
||||
{
|
||||
if (!mi)
|
||||
return EINVAL;
|
||||
|
||||
// Initialise
|
||||
void *ctx = (void *)malloc (mi->ctx_size);
|
||||
if (!ctx)
|
||||
return ENOMEM;
|
||||
mi->create (ctx);
|
||||
|
||||
// Hash bytes from file in blocks
|
||||
uint8_t* buffer = (uint8_t*)malloc (mi->block_size);
|
||||
if (!buffer)
|
||||
return ENOMEM;
|
||||
|
||||
int read_len = 0;
|
||||
do {
|
||||
read_len = read(readarg, buffer, mi->block_size);
|
||||
mi->update (ctx, buffer, read_len);
|
||||
} while (read_len == mi->block_size);
|
||||
|
||||
// Finish up
|
||||
mi->finalize (digest, ctx);
|
||||
|
||||
free (buffer);
|
||||
free (ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int ICACHE_FLASH_ATTR crypto_hmac (const digest_mech_info_t *mi,
|
||||
const char *data, size_t data_len,
|
||||
const char *key, size_t key_len,
|
||||
uint8_t *digest)
|
||||
{
|
||||
if (!mi)
|
||||
return EINVAL;
|
||||
|
||||
void *ctx = (void *)malloc (mi->ctx_size);
|
||||
if (!ctx)
|
||||
return ENOMEM;
|
||||
|
||||
// If key too long, it needs to be hashed before use
|
||||
if (key_len > mi->block_size)
|
||||
{
|
||||
mi->create (ctx);
|
||||
mi->update (ctx, key, key_len);
|
||||
mi->finalize (digest, ctx);
|
||||
key = digest;
|
||||
key_len = mi->digest_size;
|
||||
}
|
||||
|
||||
const size_t bs = mi->block_size;
|
||||
uint8_t k_ipad[bs];
|
||||
uint8_t k_opad[bs];
|
||||
|
||||
os_memset (k_ipad, 0x36, bs);
|
||||
os_memset (k_opad, 0x5c, bs);
|
||||
size_t i;
|
||||
for (i = 0; i < key_len; ++i)
|
||||
{
|
||||
k_ipad[i] ^= key[i];
|
||||
k_opad[i] ^= key[i];
|
||||
}
|
||||
|
||||
mi->create (ctx);
|
||||
mi->update (ctx, k_ipad, bs);
|
||||
mi->update (ctx, data, data_len);
|
||||
mi->finalize (digest, ctx);
|
||||
|
||||
mi->create (ctx);
|
||||
mi->update (ctx, k_opad, bs);
|
||||
mi->update (ctx, digest, mi->digest_size);
|
||||
mi->finalize (digest, ctx);
|
||||
|
||||
free (ctx);
|
||||
return 0;
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
#ifndef _CRYPTO_DIGESTS_H_
|
||||
#define _CRYPTO_DIGESTS_H_
|
||||
|
||||
#include <c_types.h>
|
||||
|
||||
typedef void (*create_ctx_fn)(void *ctx);
|
||||
typedef void (*update_ctx_fn)(void *ctx, const uint8_t *msg, int len);
|
||||
typedef void (*finalize_ctx_fn)(uint8_t *digest, void *ctx);
|
||||
typedef size_t ( *read_fn )(int fd, void *ptr, size_t len);
|
||||
|
||||
/**
|
||||
* Description of a message digest mechanism.
|
||||
*
|
||||
* Typical usage (if not using the crypto_xxxx() functions below):
|
||||
* digest_mech_info_t *mi = crypto_digest_mech (chosen_algorithm);
|
||||
* void *ctx = malloc (mi->ctx_size);
|
||||
* mi->create (ctx);
|
||||
* mi->update (ctx, data, len);
|
||||
* ...
|
||||
* uint8_t *digest = malloc (mi->digest_size);
|
||||
* mi->finalize (digest, ctx);
|
||||
* ...
|
||||
* free (ctx);
|
||||
* free (digest);
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/* Note: All entries are 32bit to enable placement using ICACHE_RODATA_ATTR.*/
|
||||
const char * name;
|
||||
create_ctx_fn create;
|
||||
update_ctx_fn update;
|
||||
finalize_ctx_fn finalize;
|
||||
uint32_t ctx_size;
|
||||
uint32_t digest_size;
|
||||
uint32_t block_size;
|
||||
} digest_mech_info_t;
|
||||
|
||||
|
||||
/**
|
||||
* Looks up the mech data for a specified digest algorithm.
|
||||
* @param mech The name of the algorithm, e.g. "MD5", "SHA256"
|
||||
* @returns The mech data, or null if the mech is unknown.
|
||||
*/
|
||||
const digest_mech_info_t *crypto_digest_mech (const char *mech);
|
||||
|
||||
/**
|
||||
* Wrapper function for performing a one-in-all hashing operation.
|
||||
* @param mi A mech from @c crypto_digest_mech(). A null pointer @c mi
|
||||
* is harmless, but will of course result in an error return.
|
||||
* @param data The data to create a digest for.
|
||||
* @param data_len Number of bytes at @c data to digest.
|
||||
* @param digest Output buffer, must be at least @c mi->digest_size in size.
|
||||
* @return 0 on success, non-zero on error.
|
||||
*/
|
||||
int crypto_hash (const digest_mech_info_t *mi, const char *data, size_t data_len, uint8_t *digest);
|
||||
|
||||
/**
|
||||
* Wrapper function for performing a one-in-all hashing operation of a file.
|
||||
* @param mi A mech from @c crypto_digest_mech(). A null pointer @c mi
|
||||
* is harmless, but will of course result in an error return.
|
||||
* @param read Pointer to the read function (e.g. fs_read)
|
||||
* @param readarg Argument to pass to the read function (e.g. file descriptor)
|
||||
* @param digest Output buffer, must be at least @c mi->digest_size in size.
|
||||
* @return 0 on success, non-zero on error.
|
||||
*/
|
||||
int crypto_fhash (const digest_mech_info_t *mi, read_fn read, int readarg, uint8_t *digest);
|
||||
|
||||
/**
|
||||
* Generate a HMAC signature.
|
||||
* @param mi A mech from @c crypto_digest_mech(). A null pointer @c mi
|
||||
* is harmless, but will of course result in an error return.
|
||||
* @param data The data to generate a signature for.
|
||||
* @param data_len Number of bytes at @c data to process.
|
||||
* @param key The key to use.
|
||||
* @param key_len Number of bytes the @c key comprises.
|
||||
* @param digest Output buffer, must be at least @c mi->digest_size in size.
|
||||
* @return 0 on success, non-zero on error.
|
||||
*/
|
||||
int crypto_hmac (const digest_mech_info_t *mi, const char *data, size_t data_len, const char *key, size_t key_len, uint8_t *digest);
|
||||
|
||||
/**
|
||||
* Perform ASCII Hex encoding. Does not null-terminate the buffer.
|
||||
*
|
||||
* @param bin The buffer to ascii-hex encode.
|
||||
* @param bin_len Number of bytes in @c bin to encode.
|
||||
* @param outbuf Output buffer, must be at least @c bin_len*2 bytes in size.
|
||||
* Note that in-place encoding is supported, and as such
|
||||
* bin==outbuf is safe, provided the buffer is large enough.
|
||||
*/
|
||||
void crypto_encode_asciihex (const char *bin, size_t bin_len, char *outbuf);
|
||||
|
||||
|
||||
/** Text string "0123456789abcdef" */
|
||||
const char crypto_hexbytes[17];
|
||||
|
||||
#endif
|
@ -1,131 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Dius Computing Pty Ltd. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - 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.
|
||||
* - Neither the name of the copyright holders nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @author Johny Mattsson <jmattsson@dius.com.au>
|
||||
*/
|
||||
|
||||
#include "mech.h"
|
||||
#include "sdk-aes.h"
|
||||
#include <string.h>
|
||||
|
||||
/* ----- AES ---------------------------------------------------------- */
|
||||
|
||||
static const struct aes_funcs
|
||||
{
|
||||
void *(*init) (const char *key, size_t keylen);
|
||||
void (*crypt) (void *ctx, const char *in, char *out);
|
||||
void (*deinit) (void *ctx);
|
||||
} aes_funcs[] =
|
||||
{
|
||||
{ aes_encrypt_init, aes_encrypt, aes_encrypt_deinit },
|
||||
{ aes_decrypt_init, aes_decrypt, aes_decrypt_deinit }
|
||||
};
|
||||
|
||||
static bool do_aes (crypto_op_t *co, bool with_cbc)
|
||||
{
|
||||
const struct aes_funcs *funcs = &aes_funcs[co->op];
|
||||
|
||||
void *ctx = funcs->init (co->key, co->keylen);
|
||||
if (!ctx)
|
||||
return false;
|
||||
|
||||
char iv[AES_BLOCKSIZE] = { 0 };
|
||||
if (with_cbc && co->ivlen)
|
||||
memcpy (iv, co->iv, co->ivlen < AES_BLOCKSIZE ? co->ivlen : AES_BLOCKSIZE);
|
||||
|
||||
const char *src = co->data;
|
||||
char *dst = co->out;
|
||||
|
||||
size_t left = co->datalen;
|
||||
while (left)
|
||||
{
|
||||
char block[AES_BLOCKSIZE] = { 0 };
|
||||
size_t n = left > AES_BLOCKSIZE ? AES_BLOCKSIZE : left;
|
||||
memcpy (block, src, n);
|
||||
|
||||
if (with_cbc && co->op == OP_ENCRYPT)
|
||||
{
|
||||
const char *xor = (src == co->data) ? iv : dst - AES_BLOCKSIZE;
|
||||
int i;
|
||||
for (i = 0; i < AES_BLOCKSIZE; ++i)
|
||||
block[i] ^= xor[i];
|
||||
}
|
||||
|
||||
funcs->crypt (ctx, block, dst);
|
||||
|
||||
if (with_cbc && co->op == OP_DECRYPT)
|
||||
{
|
||||
const char *xor = (src == co->data) ? iv : src - AES_BLOCKSIZE;
|
||||
int i;
|
||||
for (i = 0; i < AES_BLOCKSIZE; ++i)
|
||||
dst[i] ^= xor[i];
|
||||
}
|
||||
|
||||
left -= n;
|
||||
src += n;
|
||||
dst += n;
|
||||
}
|
||||
|
||||
funcs->deinit (ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool do_aes_ecb (crypto_op_t *co)
|
||||
{
|
||||
return do_aes (co, false);
|
||||
}
|
||||
|
||||
static bool do_aes_cbc (crypto_op_t *co)
|
||||
{
|
||||
return do_aes (co, true);
|
||||
}
|
||||
|
||||
|
||||
/* ----- mechs -------------------------------------------------------- */
|
||||
|
||||
static const crypto_mech_t mechs[] =
|
||||
{
|
||||
{ "AES-ECB", do_aes_ecb, AES_BLOCKSIZE },
|
||||
{ "AES-CBC", do_aes_cbc, AES_BLOCKSIZE }
|
||||
};
|
||||
|
||||
|
||||
const crypto_mech_t *crypto_encryption_mech (const char *name)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < sizeof (mechs) / sizeof (mechs[0]); ++i)
|
||||
{
|
||||
const crypto_mech_t *mech = mechs + i;
|
||||
if (strcasecmp (name, mech->name) == 0)
|
||||
return mech;
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
#ifndef _MECH_H_
|
||||
#define _MECH_H_
|
||||
|
||||
#include "c_types.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char *key;
|
||||
size_t keylen;
|
||||
const char *iv;
|
||||
size_t ivlen;
|
||||
const char *data;
|
||||
size_t datalen;
|
||||
char *out;
|
||||
size_t outlen;
|
||||
enum { OP_ENCRYPT, OP_DECRYPT } op;
|
||||
} crypto_op_t;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char *name;
|
||||
bool (*run) (crypto_op_t *op);
|
||||
uint16_t block_size;
|
||||
} crypto_mech_t;
|
||||
|
||||
|
||||
const crypto_mech_t *crypto_encryption_mech (const char *name);
|
||||
|
||||
#endif
|
@ -1,14 +0,0 @@
|
||||
#ifndef _SDK_AES_H_
|
||||
#define _SDK_AES_H_
|
||||
|
||||
#define AES_BLOCKSIZE 16
|
||||
|
||||
void *aes_encrypt_init (const char *key, size_t len);
|
||||
void aes_encrypt (void *ctx, const char *plain, char *crypt);
|
||||
void aes_encrypt_deinit (void *ctx);
|
||||
|
||||
void *aes_decrypt_init (const char *key, size_t len);
|
||||
void aes_decrypt (void *ctx, const char *crypt, char *plain);
|
||||
void aes_decrypt_deinit (void *ctx);
|
||||
|
||||
#endif
|
@ -1,913 +0,0 @@
|
||||
/*
|
||||
* FILE: sha2.c
|
||||
* AUTHOR: Aaron D. Gifford - http://www.aarongifford.com/
|
||||
*
|
||||
* Copyright (c) 2000-2001, Aaron D. Gifford
|
||||
* Copyright (c) 2015, DiUS Computing Pty Ltd (jmattsson@dius.com.au)
|
||||
* 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. Neither the name of the copyright holder nor the names of contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``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 OR CONTRIBUTOR(S) 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "user_config.h"
|
||||
|
||||
#ifdef SHA2_ENABLE
|
||||
#include "sha2.h"
|
||||
#include <string.h> /* memcpy()/memset() or bcopy()/bzero() */
|
||||
#define assert(x) do {} while (0)
|
||||
|
||||
/*
|
||||
* ASSERT NOTE:
|
||||
* Some sanity checking code is included using assert(). On my FreeBSD
|
||||
* system, this additional code can be removed by compiling with NDEBUG
|
||||
* defined. Check your own systems manpage on assert() to see how to
|
||||
* compile WITHOUT the sanity checking code on your system.
|
||||
*
|
||||
* UNROLLED TRANSFORM LOOP NOTE:
|
||||
* You can define SHA2_UNROLL_TRANSFORM to use the unrolled transform
|
||||
* loop version for the hash transform rounds (defined using macros
|
||||
* later in this file). Either define on the command line, for example:
|
||||
*
|
||||
* cc -DSHA2_UNROLL_TRANSFORM -o sha2 sha2.c sha2prog.c
|
||||
*
|
||||
* or define below:
|
||||
*
|
||||
* #define SHA2_UNROLL_TRANSFORM
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
typedef uint8_t sha2_byte; /* Exactly 1 byte */
|
||||
typedef uint32_t sha2_word32; /* Exactly 4 bytes */
|
||||
typedef uint64_t sha2_word64; /* Exactly 8 bytes */
|
||||
|
||||
|
||||
/*** SHA-256/384/512 Various Length Definitions ***********************/
|
||||
#define SHA256_SHORT_BLOCK_LENGTH (SHA256_BLOCK_LENGTH - 8)
|
||||
#define SHA384_SHORT_BLOCK_LENGTH (SHA384_BLOCK_LENGTH - 16)
|
||||
#define SHA512_SHORT_BLOCK_LENGTH (SHA512_BLOCK_LENGTH - 16)
|
||||
|
||||
|
||||
/*** ENDIAN REVERSAL MACROS *******************************************/
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
#define REVERSE32(w,x) { \
|
||||
sha2_word32 tmp = (w); \
|
||||
tmp = (tmp >> 16) | (tmp << 16); \
|
||||
(x) = ((tmp & 0xff00ff00UL) >> 8) | ((tmp & 0x00ff00ffUL) << 8); \
|
||||
}
|
||||
#define REVERSE64(w,x) { \
|
||||
sha2_word64 tmp = (w); \
|
||||
tmp = (tmp >> 32) | (tmp << 32); \
|
||||
tmp = ((tmp & 0xff00ff00ff00ff00ULL) >> 8) | \
|
||||
((tmp & 0x00ff00ff00ff00ffULL) << 8); \
|
||||
(x) = ((tmp & 0xffff0000ffff0000ULL) >> 16) | \
|
||||
((tmp & 0x0000ffff0000ffffULL) << 16); \
|
||||
}
|
||||
#endif /* __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ */
|
||||
|
||||
/*
|
||||
* Macro for incrementally adding the unsigned 64-bit integer n to the
|
||||
* unsigned 128-bit integer (represented using a two-element array of
|
||||
* 64-bit words):
|
||||
*/
|
||||
#define ADDINC128(w,n) { \
|
||||
(w)[0] += (sha2_word64)(n); \
|
||||
if ((w)[0] < (n)) { \
|
||||
(w)[1]++; \
|
||||
} \
|
||||
}
|
||||
|
||||
/*
|
||||
* Macros for copying blocks of memory and for zeroing out ranges
|
||||
* of memory. Using these macros makes it easy to switch from
|
||||
* using memset()/memcpy() and using bzero()/bcopy().
|
||||
*
|
||||
* Please define either SHA2_USE_MEMSET_MEMCPY or define
|
||||
* SHA2_USE_BZERO_BCOPY depending on which function set you
|
||||
* choose to use:
|
||||
*/
|
||||
#if !defined(SHA2_USE_MEMSET_MEMCPY) && !defined(SHA2_USE_BZERO_BCOPY)
|
||||
/* Default to memset()/memcpy() if no option is specified */
|
||||
#define SHA2_USE_MEMSET_MEMCPY 1
|
||||
#endif
|
||||
#if defined(SHA2_USE_MEMSET_MEMCPY) && defined(SHA2_USE_BZERO_BCOPY)
|
||||
/* Abort with an error if BOTH options are defined */
|
||||
#error Define either SHA2_USE_MEMSET_MEMCPY or SHA2_USE_BZERO_BCOPY, not both!
|
||||
#endif
|
||||
|
||||
#ifdef SHA2_USE_MEMSET_MEMCPY
|
||||
#define MEMSET_BZERO(p,l) memset((p), 0, (l))
|
||||
#define MEMCPY_BCOPY(d,s,l) memcpy((d), (s), (l))
|
||||
#endif
|
||||
#ifdef SHA2_USE_BZERO_BCOPY
|
||||
#define MEMSET_BZERO(p,l) bzero((p), (l))
|
||||
#define MEMCPY_BCOPY(d,s,l) bcopy((s), (d), (l))
|
||||
#endif
|
||||
|
||||
|
||||
/*** THE SIX LOGICAL FUNCTIONS ****************************************/
|
||||
/*
|
||||
* Bit shifting and rotation (used by the six SHA-XYZ logical functions:
|
||||
*
|
||||
* NOTE: The naming of R and S appears backwards here (R is a SHIFT and
|
||||
* S is a ROTATION) because the SHA-256/384/512 description document
|
||||
* (see http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf) uses this
|
||||
* same "backwards" definition.
|
||||
*/
|
||||
/* Shift-right (used in SHA-256, SHA-384, and SHA-512): */
|
||||
#define R(b,x) ((x) >> (b))
|
||||
/* 32-bit Rotate-right (used in SHA-256): */
|
||||
#define S32(b,x) (((x) >> (b)) | ((x) << (32 - (b))))
|
||||
/* 64-bit Rotate-right (used in SHA-384 and SHA-512): */
|
||||
#define S64(b,x) (((x) >> (b)) | ((x) << (64 - (b))))
|
||||
|
||||
/* Two of six logical functions used in SHA-256, SHA-384, and SHA-512: */
|
||||
#define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z)))
|
||||
#define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
|
||||
|
||||
/* Four of six logical functions used in SHA-256: */
|
||||
#define Sigma0_256(x) (S32(2, (x)) ^ S32(13, (x)) ^ S32(22, (x)))
|
||||
#define Sigma1_256(x) (S32(6, (x)) ^ S32(11, (x)) ^ S32(25, (x)))
|
||||
#define sigma0_256(x) (S32(7, (x)) ^ S32(18, (x)) ^ R(3 , (x)))
|
||||
#define sigma1_256(x) (S32(17, (x)) ^ S32(19, (x)) ^ R(10, (x)))
|
||||
|
||||
/* Four of six logical functions used in SHA-384 and SHA-512: */
|
||||
#define Sigma0_512(x) (S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x)))
|
||||
#define Sigma1_512(x) (S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x)))
|
||||
#define sigma0_512(x) (S64( 1, (x)) ^ S64( 8, (x)) ^ R( 7, (x)))
|
||||
#define sigma1_512(x) (S64(19, (x)) ^ S64(61, (x)) ^ R( 6, (x)))
|
||||
|
||||
/*** INTERNAL FUNCTION PROTOTYPES *************************************/
|
||||
/* NOTE: These should not be accessed directly from outside this
|
||||
* library -- they are intended for private internal visibility/use
|
||||
* only.
|
||||
*/
|
||||
void SHA512_Last(SHA512_CTX*);
|
||||
void SHA256_Transform(SHA256_CTX*, const sha2_word32*);
|
||||
void SHA512_Transform(SHA512_CTX*, const sha2_word64*);
|
||||
|
||||
|
||||
/*** SHA-XYZ INITIAL HASH VALUES AND CONSTANTS ************************/
|
||||
/* Hash constant words K for SHA-256: */
|
||||
const static sha2_word32 K256[64] ICACHE_RODATA_ATTR = {
|
||||
0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL,
|
||||
0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL,
|
||||
0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL,
|
||||
0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL,
|
||||
0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
|
||||
0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL,
|
||||
0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL,
|
||||
0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL,
|
||||
0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL,
|
||||
0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
|
||||
0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL,
|
||||
0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL,
|
||||
0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL,
|
||||
0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL,
|
||||
0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
|
||||
0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
|
||||
};
|
||||
|
||||
/* Initial hash value H for SHA-256: */
|
||||
const static sha2_word32 sha256_initial_hash_value[8] ICACHE_RODATA_ATTR = {
|
||||
0x6a09e667UL,
|
||||
0xbb67ae85UL,
|
||||
0x3c6ef372UL,
|
||||
0xa54ff53aUL,
|
||||
0x510e527fUL,
|
||||
0x9b05688cUL,
|
||||
0x1f83d9abUL,
|
||||
0x5be0cd19UL
|
||||
};
|
||||
|
||||
/* Hash constant words K for SHA-384 and SHA-512: */
|
||||
const static sha2_word64 K512[80] ICACHE_RODATA_ATTR = {
|
||||
0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL,
|
||||
0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
|
||||
0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
|
||||
0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
|
||||
0xd807aa98a3030242ULL, 0x12835b0145706fbeULL,
|
||||
0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
|
||||
0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL,
|
||||
0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
|
||||
0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
|
||||
0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
|
||||
0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL,
|
||||
0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
|
||||
0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL,
|
||||
0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,
|
||||
0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
|
||||
0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
|
||||
0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL,
|
||||
0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
|
||||
0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL,
|
||||
0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
|
||||
0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
|
||||
0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,
|
||||
0xd192e819d6ef5218ULL, 0xd69906245565a910ULL,
|
||||
0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
|
||||
0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL,
|
||||
0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,
|
||||
0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
|
||||
0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
|
||||
0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL,
|
||||
0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
|
||||
0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL,
|
||||
0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
|
||||
0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,
|
||||
0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,
|
||||
0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL,
|
||||
0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
|
||||
0x28db77f523047d84ULL, 0x32caab7b40c72493ULL,
|
||||
0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,
|
||||
0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
|
||||
0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
|
||||
};
|
||||
|
||||
/* Initial hash value H for SHA-384 */
|
||||
const static sha2_word64 sha384_initial_hash_value[8] ICACHE_RODATA_ATTR = {
|
||||
0xcbbb9d5dc1059ed8ULL,
|
||||
0x629a292a367cd507ULL,
|
||||
0x9159015a3070dd17ULL,
|
||||
0x152fecd8f70e5939ULL,
|
||||
0x67332667ffc00b31ULL,
|
||||
0x8eb44a8768581511ULL,
|
||||
0xdb0c2e0d64f98fa7ULL,
|
||||
0x47b5481dbefa4fa4ULL
|
||||
};
|
||||
|
||||
/* Initial hash value H for SHA-512 */
|
||||
const static sha2_word64 sha512_initial_hash_value[8] ICACHE_RODATA_ATTR = {
|
||||
0x6a09e667f3bcc908ULL,
|
||||
0xbb67ae8584caa73bULL,
|
||||
0x3c6ef372fe94f82bULL,
|
||||
0xa54ff53a5f1d36f1ULL,
|
||||
0x510e527fade682d1ULL,
|
||||
0x9b05688c2b3e6c1fULL,
|
||||
0x1f83d9abfb41bd6bULL,
|
||||
0x5be0cd19137e2179ULL
|
||||
};
|
||||
|
||||
|
||||
/*** SHA-256: *********************************************************/
|
||||
void ICACHE_FLASH_ATTR SHA256_Init(SHA256_CTX* context) {
|
||||
if (context == (SHA256_CTX*)0) {
|
||||
return;
|
||||
}
|
||||
MEMCPY_BCOPY(context->state, sha256_initial_hash_value, SHA256_DIGEST_LENGTH);
|
||||
MEMSET_BZERO(context->buffer, SHA256_BLOCK_LENGTH);
|
||||
context->bitcount = 0;
|
||||
}
|
||||
|
||||
#ifdef SHA2_UNROLL_TRANSFORM
|
||||
|
||||
/* Unrolled SHA-256 round macros: */
|
||||
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN_
|
||||
|
||||
#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) \
|
||||
REVERSE32(*data++, W256[j]); \
|
||||
T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \
|
||||
K256[j] + W256[j]; \
|
||||
(d) += T1; \
|
||||
(h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \
|
||||
j++
|
||||
|
||||
|
||||
#else /* __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN_ */
|
||||
|
||||
#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) \
|
||||
T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \
|
||||
K256[j] + (W256[j] = *data++); \
|
||||
(d) += T1; \
|
||||
(h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \
|
||||
j++
|
||||
|
||||
#endif /* __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN_ */
|
||||
|
||||
#define ROUND256(a,b,c,d,e,f,g,h) \
|
||||
s0 = W256[(j+1)&0x0f]; \
|
||||
s0 = sigma0_256(s0); \
|
||||
s1 = W256[(j+14)&0x0f]; \
|
||||
s1 = sigma1_256(s1); \
|
||||
T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + K256[j] + \
|
||||
(W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); \
|
||||
(d) += T1; \
|
||||
(h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \
|
||||
j++
|
||||
|
||||
void ICACHE_FLASH_ATTR SHA256_Transform(SHA256_CTX* context, const sha2_word32* data) {
|
||||
sha2_word32 a, b, c, d, e, f, g, h, s0, s1;
|
||||
sha2_word32 T1, *W256;
|
||||
int j;
|
||||
|
||||
W256 = (sha2_word32*)context->buffer;
|
||||
|
||||
/* Initialize registers with the prev. intermediate value */
|
||||
a = context->state[0];
|
||||
b = context->state[1];
|
||||
c = context->state[2];
|
||||
d = context->state[3];
|
||||
e = context->state[4];
|
||||
f = context->state[5];
|
||||
g = context->state[6];
|
||||
h = context->state[7];
|
||||
|
||||
j = 0;
|
||||
do {
|
||||
/* Rounds 0 to 15 (unrolled): */
|
||||
ROUND256_0_TO_15(a,b,c,d,e,f,g,h);
|
||||
ROUND256_0_TO_15(h,a,b,c,d,e,f,g);
|
||||
ROUND256_0_TO_15(g,h,a,b,c,d,e,f);
|
||||
ROUND256_0_TO_15(f,g,h,a,b,c,d,e);
|
||||
ROUND256_0_TO_15(e,f,g,h,a,b,c,d);
|
||||
ROUND256_0_TO_15(d,e,f,g,h,a,b,c);
|
||||
ROUND256_0_TO_15(c,d,e,f,g,h,a,b);
|
||||
ROUND256_0_TO_15(b,c,d,e,f,g,h,a);
|
||||
} while (j < 16);
|
||||
|
||||
/* Now for the remaining rounds to 64: */
|
||||
do {
|
||||
ROUND256(a,b,c,d,e,f,g,h);
|
||||
ROUND256(h,a,b,c,d,e,f,g);
|
||||
ROUND256(g,h,a,b,c,d,e,f);
|
||||
ROUND256(f,g,h,a,b,c,d,e);
|
||||
ROUND256(e,f,g,h,a,b,c,d);
|
||||
ROUND256(d,e,f,g,h,a,b,c);
|
||||
ROUND256(c,d,e,f,g,h,a,b);
|
||||
ROUND256(b,c,d,e,f,g,h,a);
|
||||
} while (j < 64);
|
||||
|
||||
/* Compute the current intermediate hash value */
|
||||
context->state[0] += a;
|
||||
context->state[1] += b;
|
||||
context->state[2] += c;
|
||||
context->state[3] += d;
|
||||
context->state[4] += e;
|
||||
context->state[5] += f;
|
||||
context->state[6] += g;
|
||||
context->state[7] += h;
|
||||
|
||||
/* Clean up */
|
||||
a = b = c = d = e = f = g = h = T1 = 0;
|
||||
}
|
||||
|
||||
#else /* SHA2_UNROLL_TRANSFORM */
|
||||
|
||||
void ICACHE_FLASH_ATTR SHA256_Transform(SHA256_CTX* context, const sha2_word32* data) {
|
||||
sha2_word32 a, b, c, d, e, f, g, h, s0, s1;
|
||||
sha2_word32 T1, T2, *W256;
|
||||
int j;
|
||||
|
||||
W256 = (sha2_word32*)context->buffer;
|
||||
|
||||
/* Initialize registers with the prev. intermediate value */
|
||||
a = context->state[0];
|
||||
b = context->state[1];
|
||||
c = context->state[2];
|
||||
d = context->state[3];
|
||||
e = context->state[4];
|
||||
f = context->state[5];
|
||||
g = context->state[6];
|
||||
h = context->state[7];
|
||||
|
||||
j = 0;
|
||||
do {
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
/* Copy data while converting to host byte order */
|
||||
REVERSE32(*data++,W256[j]);
|
||||
/* Apply the SHA-256 compression function to update a..h */
|
||||
T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + W256[j];
|
||||
#else /* __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN_ */
|
||||
/* Apply the SHA-256 compression function to update a..h with copy */
|
||||
T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + (W256[j] = *data++);
|
||||
#endif /* __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN_ */
|
||||
T2 = Sigma0_256(a) + Maj(a, b, c);
|
||||
h = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = d + T1;
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = T1 + T2;
|
||||
|
||||
j++;
|
||||
} while (j < 16);
|
||||
|
||||
do {
|
||||
/* Part of the message block expansion: */
|
||||
s0 = W256[(j+1)&0x0f];
|
||||
s0 = sigma0_256(s0);
|
||||
s1 = W256[(j+14)&0x0f];
|
||||
s1 = sigma1_256(s1);
|
||||
|
||||
/* Apply the SHA-256 compression function to update a..h */
|
||||
T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] +
|
||||
(W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0);
|
||||
T2 = Sigma0_256(a) + Maj(a, b, c);
|
||||
h = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = d + T1;
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = T1 + T2;
|
||||
|
||||
j++;
|
||||
} while (j < 64);
|
||||
|
||||
/* Compute the current intermediate hash value */
|
||||
context->state[0] += a;
|
||||
context->state[1] += b;
|
||||
context->state[2] += c;
|
||||
context->state[3] += d;
|
||||
context->state[4] += e;
|
||||
context->state[5] += f;
|
||||
context->state[6] += g;
|
||||
context->state[7] += h;
|
||||
|
||||
/* Clean up */
|
||||
a = b = c = d = e = f = g = h = T1 = T2 = 0;
|
||||
}
|
||||
|
||||
#endif /* SHA2_UNROLL_TRANSFORM */
|
||||
|
||||
void ICACHE_FLASH_ATTR SHA256_Update(SHA256_CTX* context, const sha2_byte *data, size_t len) {
|
||||
unsigned int freespace, usedspace;
|
||||
|
||||
if (len == 0) {
|
||||
/* Calling with no data is valid - we do nothing */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Sanity check: */
|
||||
assert(context != (SHA256_CTX*)0 && data != (sha2_byte*)0);
|
||||
|
||||
usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH;
|
||||
if (usedspace > 0) {
|
||||
/* Calculate how much free space is available in the buffer */
|
||||
freespace = SHA256_BLOCK_LENGTH - usedspace;
|
||||
|
||||
if (len >= freespace) {
|
||||
/* Fill the buffer completely and process it */
|
||||
MEMCPY_BCOPY(&context->buffer[usedspace], data, freespace);
|
||||
context->bitcount += freespace << 3;
|
||||
len -= freespace;
|
||||
data += freespace;
|
||||
SHA256_Transform(context, (sha2_word32*)context->buffer);
|
||||
} else {
|
||||
/* The buffer is not yet full */
|
||||
MEMCPY_BCOPY(&context->buffer[usedspace], data, len);
|
||||
context->bitcount += len << 3;
|
||||
/* Clean up: */
|
||||
usedspace = freespace = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
while (len >= SHA256_BLOCK_LENGTH) {
|
||||
/* Process as many complete blocks as we can */
|
||||
SHA256_Transform(context, (sha2_word32*)data);
|
||||
context->bitcount += SHA256_BLOCK_LENGTH << 3;
|
||||
len -= SHA256_BLOCK_LENGTH;
|
||||
data += SHA256_BLOCK_LENGTH;
|
||||
}
|
||||
if (len > 0) {
|
||||
/* There's left-overs, so save 'em */
|
||||
MEMCPY_BCOPY(context->buffer, data, len);
|
||||
context->bitcount += len << 3;
|
||||
}
|
||||
/* Clean up: */
|
||||
usedspace = freespace = 0;
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR SHA256_Final(sha2_byte digest[], SHA256_CTX* context) {
|
||||
sha2_word32 *d = (sha2_word32*)digest;
|
||||
unsigned int usedspace;
|
||||
|
||||
/* Sanity check: */
|
||||
assert(context != (SHA256_CTX*)0);
|
||||
|
||||
/* If no digest buffer is passed, we don't bother doing this: */
|
||||
if (digest != (sha2_byte*)0) {
|
||||
usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH;
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
/* Convert FROM host byte order */
|
||||
REVERSE64(context->bitcount,context->bitcount);
|
||||
#endif
|
||||
if (usedspace > 0) {
|
||||
/* Begin padding with a 1 bit: */
|
||||
context->buffer[usedspace++] = 0x80;
|
||||
|
||||
if (usedspace <= SHA256_SHORT_BLOCK_LENGTH) {
|
||||
/* Set-up for the last transform: */
|
||||
MEMSET_BZERO(&context->buffer[usedspace], SHA256_SHORT_BLOCK_LENGTH - usedspace);
|
||||
} else {
|
||||
if (usedspace < SHA256_BLOCK_LENGTH) {
|
||||
MEMSET_BZERO(&context->buffer[usedspace], SHA256_BLOCK_LENGTH - usedspace);
|
||||
}
|
||||
/* Do second-to-last transform: */
|
||||
SHA256_Transform(context, (sha2_word32*)context->buffer);
|
||||
|
||||
/* And set-up for the last transform: */
|
||||
MEMSET_BZERO(context->buffer, SHA256_SHORT_BLOCK_LENGTH);
|
||||
}
|
||||
} else {
|
||||
/* Set-up for the last transform: */
|
||||
MEMSET_BZERO(context->buffer, SHA256_SHORT_BLOCK_LENGTH);
|
||||
|
||||
/* Begin padding with a 1 bit: */
|
||||
*context->buffer = 0x80;
|
||||
}
|
||||
/* Set the bit count: */
|
||||
*(sha2_word64*)&context->buffer[SHA256_SHORT_BLOCK_LENGTH] = context->bitcount;
|
||||
|
||||
/* Final transform: */
|
||||
SHA256_Transform(context, (sha2_word32*)context->buffer);
|
||||
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
{
|
||||
/* Convert TO host byte order */
|
||||
int j;
|
||||
for (j = 0; j < 8; j++) {
|
||||
REVERSE32(context->state[j],context->state[j]);
|
||||
*d++ = context->state[j];
|
||||
}
|
||||
}
|
||||
#else
|
||||
MEMCPY_BCOPY(d, context->state, SHA256_DIGEST_LENGTH);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Clean up state data: */
|
||||
MEMSET_BZERO(context, sizeof(SHA256_CTX));
|
||||
usedspace = 0;
|
||||
}
|
||||
|
||||
|
||||
/*** SHA-512: *********************************************************/
|
||||
void ICACHE_FLASH_ATTR SHA512_Init(SHA512_CTX* context) {
|
||||
if (context == (SHA512_CTX*)0) {
|
||||
return;
|
||||
}
|
||||
MEMCPY_BCOPY(context->state, sha512_initial_hash_value, SHA512_DIGEST_LENGTH);
|
||||
MEMSET_BZERO(context->buffer, SHA512_BLOCK_LENGTH);
|
||||
context->bitcount[0] = context->bitcount[1] = 0;
|
||||
}
|
||||
|
||||
#ifdef SHA2_UNROLL_TRANSFORM
|
||||
|
||||
/* Unrolled SHA-512 round macros: */
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN_
|
||||
|
||||
#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) \
|
||||
REVERSE64(*data++, W512[j]); \
|
||||
T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \
|
||||
K512[j] + W512[j]; \
|
||||
(d) += T1, \
|
||||
(h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)), \
|
||||
j++
|
||||
|
||||
|
||||
#else /* __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN_ */
|
||||
|
||||
#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) \
|
||||
T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \
|
||||
K512[j] + (W512[j] = *data++); \
|
||||
(d) += T1; \
|
||||
(h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \
|
||||
j++
|
||||
|
||||
#endif /* __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN_ */
|
||||
|
||||
#define ROUND512(a,b,c,d,e,f,g,h) \
|
||||
s0 = W512[(j+1)&0x0f]; \
|
||||
s0 = sigma0_512(s0); \
|
||||
s1 = W512[(j+14)&0x0f]; \
|
||||
s1 = sigma1_512(s1); \
|
||||
T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + K512[j] + \
|
||||
(W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); \
|
||||
(d) += T1; \
|
||||
(h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \
|
||||
j++
|
||||
|
||||
void ICACHE_FLASH_ATTR SHA512_Transform(SHA512_CTX* context, const sha2_word64* data) {
|
||||
sha2_word64 a, b, c, d, e, f, g, h, s0, s1;
|
||||
sha2_word64 T1, *W512 = (sha2_word64*)context->buffer;
|
||||
int j;
|
||||
|
||||
/* Initialize registers with the prev. intermediate value */
|
||||
a = context->state[0];
|
||||
b = context->state[1];
|
||||
c = context->state[2];
|
||||
d = context->state[3];
|
||||
e = context->state[4];
|
||||
f = context->state[5];
|
||||
g = context->state[6];
|
||||
h = context->state[7];
|
||||
|
||||
j = 0;
|
||||
do {
|
||||
ROUND512_0_TO_15(a,b,c,d,e,f,g,h);
|
||||
ROUND512_0_TO_15(h,a,b,c,d,e,f,g);
|
||||
ROUND512_0_TO_15(g,h,a,b,c,d,e,f);
|
||||
ROUND512_0_TO_15(f,g,h,a,b,c,d,e);
|
||||
ROUND512_0_TO_15(e,f,g,h,a,b,c,d);
|
||||
ROUND512_0_TO_15(d,e,f,g,h,a,b,c);
|
||||
ROUND512_0_TO_15(c,d,e,f,g,h,a,b);
|
||||
ROUND512_0_TO_15(b,c,d,e,f,g,h,a);
|
||||
} while (j < 16);
|
||||
|
||||
/* Now for the remaining rounds up to 79: */
|
||||
do {
|
||||
ROUND512(a,b,c,d,e,f,g,h);
|
||||
ROUND512(h,a,b,c,d,e,f,g);
|
||||
ROUND512(g,h,a,b,c,d,e,f);
|
||||
ROUND512(f,g,h,a,b,c,d,e);
|
||||
ROUND512(e,f,g,h,a,b,c,d);
|
||||
ROUND512(d,e,f,g,h,a,b,c);
|
||||
ROUND512(c,d,e,f,g,h,a,b);
|
||||
ROUND512(b,c,d,e,f,g,h,a);
|
||||
} while (j < 80);
|
||||
|
||||
/* Compute the current intermediate hash value */
|
||||
context->state[0] += a;
|
||||
context->state[1] += b;
|
||||
context->state[2] += c;
|
||||
context->state[3] += d;
|
||||
context->state[4] += e;
|
||||
context->state[5] += f;
|
||||
context->state[6] += g;
|
||||
context->state[7] += h;
|
||||
|
||||
/* Clean up */
|
||||
a = b = c = d = e = f = g = h = T1 = 0;
|
||||
}
|
||||
|
||||
#else /* SHA2_UNROLL_TRANSFORM */
|
||||
|
||||
void ICACHE_FLASH_ATTR SHA512_Transform(SHA512_CTX* context, const sha2_word64* data) {
|
||||
sha2_word64 a, b, c, d, e, f, g, h, s0, s1;
|
||||
sha2_word64 T1, T2, *W512 = (sha2_word64*)context->buffer;
|
||||
int j;
|
||||
|
||||
/* Initialize registers with the prev. intermediate value */
|
||||
a = context->state[0];
|
||||
b = context->state[1];
|
||||
c = context->state[2];
|
||||
d = context->state[3];
|
||||
e = context->state[4];
|
||||
f = context->state[5];
|
||||
g = context->state[6];
|
||||
h = context->state[7];
|
||||
|
||||
j = 0;
|
||||
do {
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
/* Convert TO host byte order */
|
||||
REVERSE64(*data++, W512[j]);
|
||||
/* Apply the SHA-512 compression function to update a..h */
|
||||
T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + W512[j];
|
||||
#else /* __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN_ */
|
||||
/* Apply the SHA-512 compression function to update a..h with copy */
|
||||
T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + (W512[j] = *data++);
|
||||
#endif /* __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN_ */
|
||||
T2 = Sigma0_512(a) + Maj(a, b, c);
|
||||
h = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = d + T1;
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = T1 + T2;
|
||||
|
||||
j++;
|
||||
} while (j < 16);
|
||||
|
||||
do {
|
||||
/* Part of the message block expansion: */
|
||||
s0 = W512[(j+1)&0x0f];
|
||||
s0 = sigma0_512(s0);
|
||||
s1 = W512[(j+14)&0x0f];
|
||||
s1 = sigma1_512(s1);
|
||||
|
||||
/* Apply the SHA-512 compression function to update a..h */
|
||||
T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] +
|
||||
(W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0);
|
||||
T2 = Sigma0_512(a) + Maj(a, b, c);
|
||||
h = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = d + T1;
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = T1 + T2;
|
||||
|
||||
j++;
|
||||
} while (j < 80);
|
||||
|
||||
/* Compute the current intermediate hash value */
|
||||
context->state[0] += a;
|
||||
context->state[1] += b;
|
||||
context->state[2] += c;
|
||||
context->state[3] += d;
|
||||
context->state[4] += e;
|
||||
context->state[5] += f;
|
||||
context->state[6] += g;
|
||||
context->state[7] += h;
|
||||
|
||||
/* Clean up */
|
||||
a = b = c = d = e = f = g = h = T1 = T2 = 0;
|
||||
}
|
||||
|
||||
#endif /* SHA2_UNROLL_TRANSFORM */
|
||||
|
||||
void ICACHE_FLASH_ATTR SHA512_Update(SHA512_CTX* context, const sha2_byte *data, size_t len) {
|
||||
unsigned int freespace, usedspace;
|
||||
|
||||
if (len == 0) {
|
||||
/* Calling with no data is valid - we do nothing */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Sanity check: */
|
||||
assert(context != (SHA512_CTX*)0 && data != (sha2_byte*)0);
|
||||
|
||||
usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;
|
||||
if (usedspace > 0) {
|
||||
/* Calculate how much free space is available in the buffer */
|
||||
freespace = SHA512_BLOCK_LENGTH - usedspace;
|
||||
|
||||
if (len >= freespace) {
|
||||
/* Fill the buffer completely and process it */
|
||||
MEMCPY_BCOPY(&context->buffer[usedspace], data, freespace);
|
||||
ADDINC128(context->bitcount, freespace << 3);
|
||||
len -= freespace;
|
||||
data += freespace;
|
||||
SHA512_Transform(context, (sha2_word64*)context->buffer);
|
||||
} else {
|
||||
/* The buffer is not yet full */
|
||||
MEMCPY_BCOPY(&context->buffer[usedspace], data, len);
|
||||
ADDINC128(context->bitcount, len << 3);
|
||||
/* Clean up: */
|
||||
usedspace = freespace = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
while (len >= SHA512_BLOCK_LENGTH) {
|
||||
/* Process as many complete blocks as we can */
|
||||
SHA512_Transform(context, (sha2_word64*)data);
|
||||
ADDINC128(context->bitcount, SHA512_BLOCK_LENGTH << 3);
|
||||
len -= SHA512_BLOCK_LENGTH;
|
||||
data += SHA512_BLOCK_LENGTH;
|
||||
}
|
||||
if (len > 0) {
|
||||
/* There's left-overs, so save 'em */
|
||||
MEMCPY_BCOPY(context->buffer, data, len);
|
||||
ADDINC128(context->bitcount, len << 3);
|
||||
}
|
||||
/* Clean up: */
|
||||
usedspace = freespace = 0;
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR SHA512_Last(SHA512_CTX* context) {
|
||||
unsigned int usedspace;
|
||||
|
||||
usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
/* Convert FROM host byte order */
|
||||
REVERSE64(context->bitcount[0],context->bitcount[0]);
|
||||
REVERSE64(context->bitcount[1],context->bitcount[1]);
|
||||
#endif
|
||||
if (usedspace > 0) {
|
||||
/* Begin padding with a 1 bit: */
|
||||
context->buffer[usedspace++] = 0x80;
|
||||
|
||||
if (usedspace <= SHA512_SHORT_BLOCK_LENGTH) {
|
||||
/* Set-up for the last transform: */
|
||||
MEMSET_BZERO(&context->buffer[usedspace], SHA512_SHORT_BLOCK_LENGTH - usedspace);
|
||||
} else {
|
||||
if (usedspace < SHA512_BLOCK_LENGTH) {
|
||||
MEMSET_BZERO(&context->buffer[usedspace], SHA512_BLOCK_LENGTH - usedspace);
|
||||
}
|
||||
/* Do second-to-last transform: */
|
||||
SHA512_Transform(context, (sha2_word64*)context->buffer);
|
||||
|
||||
/* And set-up for the last transform: */
|
||||
MEMSET_BZERO(context->buffer, SHA512_BLOCK_LENGTH - 2);
|
||||
}
|
||||
} else {
|
||||
/* Prepare for final transform: */
|
||||
MEMSET_BZERO(context->buffer, SHA512_SHORT_BLOCK_LENGTH);
|
||||
|
||||
/* Begin padding with a 1 bit: */
|
||||
*context->buffer = 0x80;
|
||||
}
|
||||
/* Store the length of input data (in bits): */
|
||||
*(sha2_word64*)&context->buffer[SHA512_SHORT_BLOCK_LENGTH] = context->bitcount[1];
|
||||
*(sha2_word64*)&context->buffer[SHA512_SHORT_BLOCK_LENGTH+8] = context->bitcount[0];
|
||||
|
||||
/* Final transform: */
|
||||
SHA512_Transform(context, (sha2_word64*)context->buffer);
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR SHA512_Final(sha2_byte digest[], SHA512_CTX* context) {
|
||||
sha2_word64 *d = (sha2_word64*)digest;
|
||||
|
||||
/* Sanity check: */
|
||||
assert(context != (SHA512_CTX*)0);
|
||||
|
||||
/* If no digest buffer is passed, we don't bother doing this: */
|
||||
if (digest != (sha2_byte*)0) {
|
||||
SHA512_Last(context);
|
||||
|
||||
/* Save the hash data for output: */
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
{
|
||||
/* Convert TO host byte order */
|
||||
int j;
|
||||
for (j = 0; j < 8; j++) {
|
||||
REVERSE64(context->state[j],context->state[j]);
|
||||
*d++ = context->state[j];
|
||||
}
|
||||
}
|
||||
#else
|
||||
MEMCPY_BCOPY(d, context->state, SHA512_DIGEST_LENGTH);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Zero out state data */
|
||||
MEMSET_BZERO(context, sizeof(SHA512_CTX));
|
||||
}
|
||||
|
||||
|
||||
/*** SHA-384: *********************************************************/
|
||||
void ICACHE_FLASH_ATTR SHA384_Init(SHA384_CTX* context) {
|
||||
if (context == (SHA384_CTX*)0) {
|
||||
return;
|
||||
}
|
||||
MEMCPY_BCOPY(context->state, sha384_initial_hash_value, SHA512_DIGEST_LENGTH);
|
||||
MEMSET_BZERO(context->buffer, SHA384_BLOCK_LENGTH);
|
||||
context->bitcount[0] = context->bitcount[1] = 0;
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR SHA384_Update(SHA384_CTX* context, const sha2_byte* data, size_t len) {
|
||||
SHA512_Update((SHA512_CTX*)context, data, len);
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR SHA384_Final(sha2_byte digest[], SHA384_CTX* context) {
|
||||
sha2_word64 *d = (sha2_word64*)digest;
|
||||
|
||||
/* Sanity check: */
|
||||
assert(context != (SHA384_CTX*)0);
|
||||
|
||||
/* If no digest buffer is passed, we don't bother doing this: */
|
||||
if (digest != (sha2_byte*)0) {
|
||||
SHA512_Last((SHA512_CTX*)context);
|
||||
|
||||
/* Save the hash data for output: */
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
{
|
||||
/* Convert TO host byte order */
|
||||
int j;
|
||||
for (j = 0; j < 6; j++) {
|
||||
REVERSE64(context->state[j],context->state[j]);
|
||||
*d++ = context->state[j];
|
||||
}
|
||||
}
|
||||
#else
|
||||
MEMCPY_BCOPY(d, context->state, SHA384_DIGEST_LENGTH);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Zero out state data */
|
||||
MEMSET_BZERO(context, sizeof(SHA384_CTX));
|
||||
}
|
||||
|
||||
#endif // SHA2_ENABLE
|
@ -1,47 +0,0 @@
|
||||
#ifndef __SHA2_H__
|
||||
#define __SHA2_H__
|
||||
|
||||
#include <c_types.h>
|
||||
|
||||
/**************************************************************************
|
||||
* SHA256/384/512 declarations
|
||||
**************************************************************************/
|
||||
|
||||
#define SHA256_BLOCK_LENGTH 64
|
||||
#define SHA256_DIGEST_LENGTH 32
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t state[8];
|
||||
uint64_t bitcount;
|
||||
uint8_t buffer[SHA256_BLOCK_LENGTH];
|
||||
} SHA256_CTX;
|
||||
|
||||
|
||||
void SHA256_Init(SHA256_CTX *);
|
||||
void SHA256_Update(SHA256_CTX *, const uint8_t *msg, size_t len);
|
||||
void SHA256_Final(uint8_t[SHA256_DIGEST_LENGTH], SHA256_CTX*);
|
||||
|
||||
#define SHA384_BLOCK_LENGTH 128
|
||||
#define SHA384_DIGEST_LENGTH 48
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint64_t state[8];
|
||||
uint64_t bitcount[2];
|
||||
uint8_t buffer[SHA384_BLOCK_LENGTH];
|
||||
} SHA384_CTX;
|
||||
|
||||
void SHA384_Init(SHA384_CTX*);
|
||||
void SHA384_Update(SHA384_CTX*, const uint8_t *msg, size_t len);
|
||||
void SHA384_Final(uint8_t[SHA384_DIGEST_LENGTH], SHA384_CTX*);
|
||||
|
||||
#define SHA512_BLOCK_LENGTH 128
|
||||
#define SHA512_DIGEST_LENGTH 64
|
||||
typedef SHA384_CTX SHA512_CTX;
|
||||
|
||||
void SHA512_Init(SHA512_CTX*);
|
||||
void SHA512_Update(SHA512_CTX*, const uint8_t *msg, size_t len);
|
||||
void SHA512_Final(uint8_t[SHA512_DIGEST_LENGTH], SHA512_CTX*);
|
||||
|
||||
#endif
|
@ -1,49 +0,0 @@
|
||||
|
||||
#############################################################
|
||||
# Required variables for each makefile
|
||||
# Discard this section from all parent makefiles
|
||||
# Expected variables (with automatic defaults):
|
||||
# CSRCS (all "C" files in the dir)
|
||||
# SUBDIRS (all subdirs with a Makefile)
|
||||
# GEN_LIBS - list of libs to be generated ()
|
||||
# GEN_IMAGES - list of images to be generated ()
|
||||
# COMPONENTS_xxx - a list of libs/objs in the form
|
||||
# subdir/lib to be extracted and rolled up into
|
||||
# a generated lib/image xxx.a ()
|
||||
#
|
||||
ifndef PDIR
|
||||
GEN_LIBS = libdhtlib.a
|
||||
endif
|
||||
|
||||
|
||||
#############################################################
|
||||
# Configuration i.e. compile options etc.
|
||||
# Target specific stuff (defines etc.) goes in here!
|
||||
# Generally values applying to a tree are captured in the
|
||||
# makefile at its root level - these are then overridden
|
||||
# for a subtree within the makefile rooted therein
|
||||
#
|
||||
#DEFINES +=
|
||||
|
||||
#############################################################
|
||||
# Recursion Magic - Don't touch this!!
|
||||
#
|
||||
# Each subtree potentially has an include directory
|
||||
# corresponding to the common APIs applicable to modules
|
||||
# rooted at that subtree. Accordingly, the INCLUDE PATH
|
||||
# of a module can only contain the include directories up
|
||||
# its parent path, and not its siblings
|
||||
#
|
||||
# Required for each makefile to inherit from the parent
|
||||
#
|
||||
|
||||
INCLUDES := $(INCLUDES) -I $(PDIR)include
|
||||
INCLUDES += -I ./
|
||||
INCLUDES += -I ./include
|
||||
INCLUDES += -I ../include
|
||||
INCLUDES += -I ../../include
|
||||
INCLUDES += -I ../libc
|
||||
INCLUDES += -I ../platform
|
||||
PDIR := ../$(PDIR)
|
||||
sinclude $(PDIR)Makefile
|
||||
|
319
app/dhtlib/dht.c
319
app/dhtlib/dht.c
@ -1,319 +0,0 @@
|
||||
//
|
||||
// FILE: dht.cpp
|
||||
// AUTHOR: Rob Tillaart
|
||||
// VERSION: 0.1.14
|
||||
// PURPOSE: DHT Temperature & Humidity Sensor library for Arduino
|
||||
// URL: http://arduino.cc/playground/Main/DHTLib
|
||||
//
|
||||
// HISTORY:
|
||||
// 0.1.14 replace digital read with faster (~3x) code => more robust low MHz machines.
|
||||
// 0.1.13 fix negative dht_temperature
|
||||
// 0.1.12 support DHT33 and DHT44 initial version
|
||||
// 0.1.11 renamed DHTLIB_TIMEOUT
|
||||
// 0.1.10 optimized faster WAKEUP + TIMEOUT
|
||||
// 0.1.09 optimize size: timeout check + use of mask
|
||||
// 0.1.08 added formula for timeout based upon clockspeed
|
||||
// 0.1.07 added support for DHT21
|
||||
// 0.1.06 minimize footprint (2012-12-27)
|
||||
// 0.1.05 fixed negative dht_temperature bug (thanks to Roseman)
|
||||
// 0.1.04 improved readability of code using DHTLIB_OK in code
|
||||
// 0.1.03 added error values for temp and dht_humidity when read failed
|
||||
// 0.1.02 added error codes
|
||||
// 0.1.01 added support for Arduino 1.0, fixed typos (31/12/2011)
|
||||
// 0.1.00 by Rob Tillaart (01/04/2011)
|
||||
//
|
||||
// inspired by DHT11 library
|
||||
//
|
||||
// Released to the public domain
|
||||
//
|
||||
|
||||
#include "user_interface.h"
|
||||
#include "platform.h"
|
||||
#include <stdio.h>
|
||||
#include "dht.h"
|
||||
|
||||
#ifndef LOW
|
||||
#define LOW 0
|
||||
#endif /* ifndef LOW */
|
||||
|
||||
#ifndef HIGH
|
||||
#define HIGH 1
|
||||
#endif /* ifndef HIGH */
|
||||
|
||||
#define COMBINE_HIGH_AND_LOW_BYTE(byte_high, byte_low) (((byte_high) << 8) | (byte_low))
|
||||
|
||||
static double dht_humidity;
|
||||
static double dht_temperature;
|
||||
|
||||
static uint8_t dht_bytes[5]; // buffer to receive data
|
||||
static int dht_readSensor(uint8_t pin, uint8_t wakeupDelay);
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
//
|
||||
// PUBLIC
|
||||
//
|
||||
|
||||
// return values:
|
||||
// Humidity
|
||||
double dht_getHumidity(void)
|
||||
{
|
||||
return dht_humidity;
|
||||
}
|
||||
|
||||
// return values:
|
||||
// Temperature
|
||||
double dht_getTemperature(void)
|
||||
{
|
||||
return dht_temperature;
|
||||
}
|
||||
|
||||
// return values:
|
||||
// DHTLIB_OK
|
||||
// DHTLIB_ERROR_CHECKSUM
|
||||
// DHTLIB_ERROR_TIMEOUT
|
||||
int dht_read_universal(uint8_t pin)
|
||||
{
|
||||
// READ VALUES
|
||||
int rv = dht_readSensor(pin, DHTLIB_DHT_UNI_WAKEUP);
|
||||
if (rv != DHTLIB_OK)
|
||||
{
|
||||
dht_humidity = DHTLIB_INVALID_VALUE; // invalid value, or is NaN prefered?
|
||||
dht_temperature = DHTLIB_INVALID_VALUE; // invalid value
|
||||
return rv; // propagate error value
|
||||
}
|
||||
|
||||
#if defined(DHT_DEBUG_BYTES)
|
||||
int i;
|
||||
for (i = 0; i < 5; i++)
|
||||
{
|
||||
DHT_DEBUG("%02X\n", dht_bytes[i]);
|
||||
}
|
||||
#endif // defined(DHT_DEBUG_BYTES)
|
||||
|
||||
// Assume it is DHT11
|
||||
// If it is DHT11, both bit[1] and bit[3] is 0
|
||||
if ((dht_bytes[1] == 0) && (dht_bytes[3] == 0))
|
||||
{
|
||||
// It may DHT11
|
||||
// CONVERT AND STORE
|
||||
DHT_DEBUG("DHT11 method\n");
|
||||
dht_humidity = dht_bytes[0]; // dht_bytes[1] == 0;
|
||||
dht_temperature = dht_bytes[2]; // dht_bytes[3] == 0;
|
||||
|
||||
// TEST CHECKSUM
|
||||
// dht_bytes[1] && dht_bytes[3] both 0
|
||||
uint8_t sum = dht_bytes[0] + dht_bytes[2];
|
||||
if (dht_bytes[4] != sum)
|
||||
{
|
||||
// It may not DHT11
|
||||
dht_humidity = DHTLIB_INVALID_VALUE; // invalid value, or is NaN prefered?
|
||||
dht_temperature = DHTLIB_INVALID_VALUE; // invalid value
|
||||
// Do nothing
|
||||
}
|
||||
else
|
||||
{
|
||||
return DHTLIB_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// Assume it is not DHT11
|
||||
// CONVERT AND STORE
|
||||
DHT_DEBUG("DHTxx method\n");
|
||||
dht_humidity = (double)COMBINE_HIGH_AND_LOW_BYTE(dht_bytes[0], dht_bytes[1]) * 0.1;
|
||||
dht_temperature = (double)COMBINE_HIGH_AND_LOW_BYTE(dht_bytes[2] & 0x7F, dht_bytes[3]) * 0.1;
|
||||
if (dht_bytes[2] & 0x80) // negative dht_temperature
|
||||
{
|
||||
dht_temperature = -dht_temperature;
|
||||
}
|
||||
|
||||
// TEST CHECKSUM
|
||||
uint8_t sum = dht_bytes[0] + dht_bytes[1] + dht_bytes[2] + dht_bytes[3];
|
||||
if (dht_bytes[4] != sum)
|
||||
{
|
||||
return DHTLIB_ERROR_CHECKSUM;
|
||||
}
|
||||
return DHTLIB_OK;
|
||||
}
|
||||
|
||||
// return values:
|
||||
// DHTLIB_OK
|
||||
// DHTLIB_ERROR_CHECKSUM
|
||||
// DHTLIB_ERROR_TIMEOUT
|
||||
int dht_read11(uint8_t pin)
|
||||
{
|
||||
// READ VALUES
|
||||
int rv = dht_readSensor(pin, DHTLIB_DHT11_WAKEUP);
|
||||
if (rv != DHTLIB_OK)
|
||||
{
|
||||
dht_humidity = DHTLIB_INVALID_VALUE; // invalid value, or is NaN prefered?
|
||||
dht_temperature = DHTLIB_INVALID_VALUE; // invalid value
|
||||
return rv;
|
||||
}
|
||||
|
||||
// CONVERT AND STORE
|
||||
dht_humidity = dht_bytes[0]; // dht_bytes[1] == 0;
|
||||
dht_temperature = dht_bytes[2]; // dht_bytes[3] == 0;
|
||||
|
||||
// TEST CHECKSUM
|
||||
// dht_bytes[1] && dht_bytes[3] both 0
|
||||
uint8_t sum = dht_bytes[0] + dht_bytes[2];
|
||||
if (dht_bytes[4] != sum) return DHTLIB_ERROR_CHECKSUM;
|
||||
|
||||
return DHTLIB_OK;
|
||||
}
|
||||
|
||||
|
||||
// return values:
|
||||
// DHTLIB_OK
|
||||
// DHTLIB_ERROR_CHECKSUM
|
||||
// DHTLIB_ERROR_TIMEOUT
|
||||
int dht_read(uint8_t pin)
|
||||
{
|
||||
// READ VALUES
|
||||
int rv = dht_readSensor(pin, DHTLIB_DHT_WAKEUP);
|
||||
if (rv != DHTLIB_OK)
|
||||
{
|
||||
dht_humidity = DHTLIB_INVALID_VALUE; // invalid value, or is NaN prefered?
|
||||
dht_temperature = DHTLIB_INVALID_VALUE; // invalid value
|
||||
return rv; // propagate error value
|
||||
}
|
||||
|
||||
// CONVERT AND STORE
|
||||
dht_humidity = (double)COMBINE_HIGH_AND_LOW_BYTE(dht_bytes[0], dht_bytes[1]) * 0.1;
|
||||
dht_temperature = (double)COMBINE_HIGH_AND_LOW_BYTE(dht_bytes[2] & 0x7F, dht_bytes[3]) * 0.1;
|
||||
if (dht_bytes[2] & 0x80) // negative dht_temperature
|
||||
{
|
||||
dht_temperature = -dht_temperature;
|
||||
}
|
||||
|
||||
// TEST CHECKSUM
|
||||
uint8_t sum = dht_bytes[0] + dht_bytes[1] + dht_bytes[2] + dht_bytes[3];
|
||||
if (dht_bytes[4] != sum)
|
||||
{
|
||||
return DHTLIB_ERROR_CHECKSUM;
|
||||
}
|
||||
return DHTLIB_OK;
|
||||
}
|
||||
|
||||
// return values:
|
||||
// DHTLIB_OK
|
||||
// DHTLIB_ERROR_CHECKSUM
|
||||
// DHTLIB_ERROR_TIMEOUT
|
||||
int dht_read21(uint8_t pin) __attribute__((alias("dht_read")));
|
||||
|
||||
// return values:
|
||||
// DHTLIB_OK
|
||||
// DHTLIB_ERROR_CHECKSUM
|
||||
// DHTLIB_ERROR_TIMEOUT
|
||||
int dht_read22(uint8_t pin) __attribute__((alias("dht_read")));
|
||||
|
||||
// return values:
|
||||
// DHTLIB_OK
|
||||
// DHTLIB_ERROR_CHECKSUM
|
||||
// DHTLIB_ERROR_TIMEOUT
|
||||
int dht_read33(uint8_t pin) __attribute__((alias("dht_read")));
|
||||
|
||||
// return values:
|
||||
// DHTLIB_OK
|
||||
// DHTLIB_ERROR_CHECKSUM
|
||||
// DHTLIB_ERROR_TIMEOUT
|
||||
int dht_read44(uint8_t pin) __attribute__((alias("dht_read")));
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
//
|
||||
// PRIVATE
|
||||
//
|
||||
|
||||
// return values:
|
||||
// DHTLIB_OK
|
||||
// DHTLIB_ERROR_TIMEOUT
|
||||
int dht_readSensor(uint8_t pin, uint8_t wakeupDelay)
|
||||
{
|
||||
// INIT BUFFERVAR TO RECEIVE DATA
|
||||
uint8_t mask = 128;
|
||||
uint8_t idx = 0;
|
||||
uint8_t i = 0;
|
||||
|
||||
// replace digitalRead() with Direct Port Reads.
|
||||
// reduces footprint ~100 bytes => portability issue?
|
||||
// direct port read is about 3x faster
|
||||
// uint8_t bit = digitalPinToBitMask(pin);
|
||||
// uint8_t port = digitalPinToPort(pin);
|
||||
// volatile uint8_t *PIR = portInputRegister(port);
|
||||
|
||||
// EMPTY BUFFER
|
||||
for (i = 0; i < 5; i++) dht_bytes[i] = 0;
|
||||
|
||||
// REQUEST SAMPLE
|
||||
// pinMode(pin, OUTPUT);
|
||||
platform_gpio_mode(pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_PULLUP);
|
||||
DIRECT_MODE_OUTPUT(pin);
|
||||
// digitalWrite(pin, LOW); // T-be
|
||||
DIRECT_WRITE_LOW(pin);
|
||||
// delay(wakeupDelay);
|
||||
for (i = 0; i < wakeupDelay; i++) os_delay_us(1000);
|
||||
// Disable interrupts
|
||||
ets_intr_lock();
|
||||
// digitalWrite(pin, HIGH); // T-go
|
||||
DIRECT_WRITE_HIGH(pin);
|
||||
os_delay_us(40);
|
||||
// pinMode(pin, INPUT);
|
||||
DIRECT_MODE_INPUT(pin);
|
||||
|
||||
// GET ACKNOWLEDGE or TIMEOUT
|
||||
uint16_t loopCntLOW = DHTLIB_TIMEOUT;
|
||||
while (DIRECT_READ(pin) == LOW ) // T-rel
|
||||
{
|
||||
os_delay_us(1);
|
||||
if (--loopCntLOW == 0) return DHTLIB_ERROR_TIMEOUT;
|
||||
}
|
||||
|
||||
uint16_t loopCntHIGH = DHTLIB_TIMEOUT;
|
||||
while (DIRECT_READ(pin) != LOW ) // T-reh
|
||||
{
|
||||
os_delay_us(1);
|
||||
if (--loopCntHIGH == 0) return DHTLIB_ERROR_TIMEOUT;
|
||||
}
|
||||
|
||||
// READ THE OUTPUT - 40 BITS => 5 BYTES
|
||||
for (i = 40; i != 0; i--)
|
||||
{
|
||||
loopCntLOW = DHTLIB_TIMEOUT;
|
||||
while (DIRECT_READ(pin) == LOW )
|
||||
{
|
||||
os_delay_us(1);
|
||||
if (--loopCntLOW == 0) return DHTLIB_ERROR_TIMEOUT;
|
||||
}
|
||||
|
||||
uint32_t t = system_get_time();
|
||||
|
||||
loopCntHIGH = DHTLIB_TIMEOUT;
|
||||
while (DIRECT_READ(pin) != LOW )
|
||||
{
|
||||
os_delay_us(1);
|
||||
if (--loopCntHIGH == 0) return DHTLIB_ERROR_TIMEOUT;
|
||||
}
|
||||
|
||||
if ((system_get_time() - t) > 40)
|
||||
{
|
||||
dht_bytes[idx] |= mask;
|
||||
}
|
||||
mask >>= 1;
|
||||
if (mask == 0) // next byte?
|
||||
{
|
||||
mask = 128;
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
// Enable interrupts
|
||||
ets_intr_unlock();
|
||||
// pinMode(pin, OUTPUT);
|
||||
DIRECT_MODE_OUTPUT(pin);
|
||||
// digitalWrite(pin, HIGH);
|
||||
DIRECT_WRITE_HIGH(pin);
|
||||
|
||||
return DHTLIB_OK;
|
||||
}
|
||||
//
|
||||
// END OF FILE
|
||||
//
|
@ -1,70 +0,0 @@
|
||||
//
|
||||
// FILE: dht.h
|
||||
// AUTHOR: Rob Tillaart
|
||||
// VERSION: 0.1.14
|
||||
// PURPOSE: DHT Temperature & Humidity Sensor library for Arduino
|
||||
// URL: http://arduino.cc/playground/Main/DHTLib
|
||||
//
|
||||
// HISTORY:
|
||||
// see dht.cpp file
|
||||
//
|
||||
|
||||
#ifndef dht_h
|
||||
#define dht_h
|
||||
|
||||
// #if ARDUINO < 100
|
||||
// #include <WProgram.h>
|
||||
// #else
|
||||
// #include <Arduino.h>
|
||||
// #endif
|
||||
#include "c_types.h"
|
||||
|
||||
#define DHT_LIB_VERSION "0.1.14"
|
||||
|
||||
#define DHTLIB_OK 0
|
||||
#define DHTLIB_ERROR_CHECKSUM -1
|
||||
#define DHTLIB_ERROR_TIMEOUT -2
|
||||
#define DHTLIB_INVALID_VALUE -999
|
||||
|
||||
#define DHTLIB_DHT11_WAKEUP 18
|
||||
#define DHTLIB_DHT_WAKEUP 1
|
||||
#define DHTLIB_DHT_UNI_WAKEUP 18
|
||||
|
||||
#define DHT_DEBUG
|
||||
|
||||
// max timeout is 100 usec.
|
||||
// For a 16 Mhz proc 100 usec is 1600 clock cycles
|
||||
// loops using DHTLIB_TIMEOUT use at least 4 clock cycli
|
||||
// so 100 us takes max 400 loops
|
||||
// so by dividing F_CPU by 40000 we "fail" as fast as possible
|
||||
// ESP8266 uses delay_us get 1us time
|
||||
#define DHTLIB_TIMEOUT (100)
|
||||
|
||||
// Platform specific I/O definitions
|
||||
|
||||
#define DIRECT_READ(pin) (0x1 & GPIO_INPUT_GET(GPIO_ID_PIN(pin_num[pin])))
|
||||
#define DIRECT_MODE_INPUT(pin) GPIO_DIS_OUTPUT(pin_num[pin])
|
||||
#define DIRECT_MODE_OUTPUT(pin)
|
||||
#define DIRECT_WRITE_LOW(pin) (GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[pin]), 0))
|
||||
#define DIRECT_WRITE_HIGH(pin) (GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[pin]), 1))
|
||||
|
||||
// return values:
|
||||
// DHTLIB_OK
|
||||
// DHTLIB_ERROR_CHECKSUM
|
||||
// DHTLIB_ERROR_TIMEOUT
|
||||
int dht_read_universal(uint8_t pin);
|
||||
int dht_read11(uint8_t pin);
|
||||
int dht_read(uint8_t pin);
|
||||
|
||||
int dht_read21(uint8_t pin);
|
||||
int dht_read22(uint8_t pin);
|
||||
int dht_read33(uint8_t pin);
|
||||
int dht_read44(uint8_t pin);
|
||||
|
||||
double dht_getHumidity(void);
|
||||
double dht_getTemperature(void);
|
||||
|
||||
#endif
|
||||
//
|
||||
// END OF FILE
|
||||
//
|
@ -1,46 +0,0 @@
|
||||
|
||||
#############################################################
|
||||
# Required variables for each makefile
|
||||
# Discard this section from all parent makefiles
|
||||
# Expected variables (with automatic defaults):
|
||||
# CSRCS (all "C" files in the dir)
|
||||
# SUBDIRS (all subdirs with a Makefile)
|
||||
# GEN_LIBS - list of libs to be generated ()
|
||||
# GEN_IMAGES - list of images to be generated ()
|
||||
# COMPONENTS_xxx - a list of libs/objs in the form
|
||||
# subdir/lib to be extracted and rolled up into
|
||||
# a generated lib/image xxx.a ()
|
||||
#
|
||||
ifndef PDIR
|
||||
GEN_LIBS = libdriver.a
|
||||
endif
|
||||
|
||||
STD_CFLAGS=-std=gnu11 -Wimplicit
|
||||
|
||||
#############################################################
|
||||
# Configuration i.e. compile options etc.
|
||||
# Target specific stuff (defines etc.) goes in here!
|
||||
# Generally values applying to a tree are captured in the
|
||||
# makefile at its root level - these are then overridden
|
||||
# for a subtree within the makefile rooted therein
|
||||
#
|
||||
#DEFINES +=
|
||||
|
||||
#############################################################
|
||||
# Recursion Magic - Don't touch this!!
|
||||
#
|
||||
# Each subtree potentially has an include directory
|
||||
# corresponding to the common APIs applicable to modules
|
||||
# rooted at that subtree. Accordingly, the INCLUDE PATH
|
||||
# of a module can only contain the include directories up
|
||||
# its parent path, and not its siblings
|
||||
#
|
||||
# Required for each makefile to inherit from the parent
|
||||
#
|
||||
|
||||
INCLUDES := $(INCLUDES) -I $(PDIR)include -I ../include/driver
|
||||
INCLUDES += -I ./
|
||||
INCLUDES += -I ../platform
|
||||
PDIR := ../$(PDIR)
|
||||
sinclude $(PDIR)Makefile
|
||||
|
@ -1,47 +0,0 @@
|
||||
#ifdef __ESP8266__
|
||||
|
||||
#include "ets_sys.h"
|
||||
#include "osapi.h"
|
||||
#include "eagle_soc.h"
|
||||
#include "driver/gpio16.h"
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
gpio16_output_conf(void)
|
||||
{
|
||||
WRITE_PERI_REG(PAD_XPD_DCDC_CONF,
|
||||
(READ_PERI_REG(PAD_XPD_DCDC_CONF) & 0xffffffbc) | (uint32)0x1); // mux configuration for XPD_DCDC to output rtc_gpio0
|
||||
|
||||
WRITE_PERI_REG(RTC_GPIO_CONF,
|
||||
(READ_PERI_REG(RTC_GPIO_CONF) & (uint32)0xfffffffe) | (uint32)0x0); //mux configuration for out enable
|
||||
|
||||
WRITE_PERI_REG(RTC_GPIO_ENABLE,
|
||||
(READ_PERI_REG(RTC_GPIO_ENABLE) & (uint32)0xfffffffe) | (uint32)0x1); //out enable
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
gpio16_output_set(uint8 value)
|
||||
{
|
||||
WRITE_PERI_REG(RTC_GPIO_OUT,
|
||||
(READ_PERI_REG(RTC_GPIO_OUT) & (uint32)0xfffffffe) | (uint32)(value & 1));
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
gpio16_input_conf(void)
|
||||
{
|
||||
WRITE_PERI_REG(PAD_XPD_DCDC_CONF,
|
||||
(READ_PERI_REG(PAD_XPD_DCDC_CONF) & 0xffffffbc) | (uint32)0x1); // mux configuration for XPD_DCDC and rtc_gpio0 connection
|
||||
|
||||
WRITE_PERI_REG(RTC_GPIO_CONF,
|
||||
(READ_PERI_REG(RTC_GPIO_CONF) & (uint32)0xfffffffe) | (uint32)0x0); //mux configuration for out enable
|
||||
|
||||
WRITE_PERI_REG(RTC_GPIO_ENABLE,
|
||||
READ_PERI_REG(RTC_GPIO_ENABLE) & (uint32)0xfffffffe); //out disable
|
||||
}
|
||||
|
||||
uint8 ICACHE_FLASH_ATTR
|
||||
gpio16_input_get(void)
|
||||
{
|
||||
return (uint8)(READ_PERI_REG(RTC_GPIO_IN_DATA) & 1);
|
||||
}
|
||||
|
||||
#endif
|
@ -1,334 +0,0 @@
|
||||
/******************************************************************************
|
||||
* Copyright 2013-2014 Espressif Systems (Wuxi)
|
||||
*
|
||||
* FileName: i2c_master.c
|
||||
*
|
||||
* Description: i2c master API
|
||||
*
|
||||
* Modification history:
|
||||
* 2014/3/12, v1.0 create this file.
|
||||
*******************************************************************************/
|
||||
#include "ets_sys.h"
|
||||
#include "osapi.h"
|
||||
#include "esp_misc.h"
|
||||
#include "platform.h"
|
||||
#include "gpio.h"
|
||||
|
||||
#include "driver/i2c_master.h"
|
||||
|
||||
#include "pin_map.h"
|
||||
|
||||
LOCAL uint8 m_nLastSDA;
|
||||
LOCAL uint8 m_nLastSCL;
|
||||
|
||||
LOCAL uint8 pinSDA = 2;
|
||||
LOCAL uint8 pinSCL = 15;
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : i2c_master_setDC
|
||||
* Description : Internal used function -
|
||||
* set i2c SDA and SCL bit value for half clk cycle
|
||||
* Parameters : uint8 SDA
|
||||
* uint8 SCL
|
||||
* Returns : NONE
|
||||
*******************************************************************************/
|
||||
LOCAL void ICACHE_FLASH_ATTR
|
||||
i2c_master_setDC(uint8 SDA, uint8 SCL)
|
||||
{
|
||||
SDA &= 0x01;
|
||||
SCL &= 0x01;
|
||||
m_nLastSDA = SDA;
|
||||
m_nLastSCL = SCL;
|
||||
|
||||
if ((0 == SDA) && (0 == SCL)) {
|
||||
I2C_MASTER_SDA_LOW_SCL_LOW();
|
||||
} else if ((0 == SDA) && (1 == SCL)) {
|
||||
I2C_MASTER_SDA_LOW_SCL_HIGH();
|
||||
} else if ((1 == SDA) && (0 == SCL)) {
|
||||
I2C_MASTER_SDA_HIGH_SCL_LOW();
|
||||
} else {
|
||||
I2C_MASTER_SDA_HIGH_SCL_HIGH();
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : i2c_master_getDC
|
||||
* Description : Internal used function -
|
||||
* get i2c SDA bit value
|
||||
* Parameters : NONE
|
||||
* Returns : uint8 - SDA bit value
|
||||
*******************************************************************************/
|
||||
LOCAL uint8 ICACHE_FLASH_ATTR
|
||||
i2c_master_getDC(void)
|
||||
{
|
||||
uint8 sda_out;
|
||||
sda_out = GPIO_INPUT_GET(GPIO_ID_PIN(I2C_MASTER_SDA_GPIO));
|
||||
return sda_out;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : i2c_master_init
|
||||
* Description : initilize I2C bus to enable i2c operations
|
||||
* Parameters : NONE
|
||||
* Returns : NONE
|
||||
*******************************************************************************/
|
||||
void ICACHE_FLASH_ATTR
|
||||
i2c_master_init(void)
|
||||
{
|
||||
uint8 i;
|
||||
|
||||
i2c_master_setDC(1, 0);
|
||||
i2c_master_wait(5);
|
||||
|
||||
// when SCL = 0, toggle SDA to clear up
|
||||
i2c_master_setDC(0, 0) ;
|
||||
i2c_master_wait(5);
|
||||
i2c_master_setDC(1, 0) ;
|
||||
i2c_master_wait(5);
|
||||
|
||||
// set data_cnt to max value
|
||||
for (i = 0; i < 28; i++) {
|
||||
i2c_master_setDC(1, 0);
|
||||
i2c_master_wait(5); // sda 1, scl 0
|
||||
i2c_master_setDC(1, 1);
|
||||
i2c_master_wait(5); // sda 1, scl 1
|
||||
}
|
||||
|
||||
// reset all
|
||||
i2c_master_stop();
|
||||
return;
|
||||
}
|
||||
|
||||
uint8 i2c_master_get_pinSDA(){
|
||||
return pinSDA;
|
||||
}
|
||||
|
||||
uint8 i2c_master_get_pinSCL(){
|
||||
return pinSCL;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : i2c_master_gpio_init
|
||||
* Description : config SDA and SCL gpio to open-drain output mode,
|
||||
* mux and gpio num defined in i2c_master.h
|
||||
* Parameters : NONE
|
||||
* Returns : NONE
|
||||
*******************************************************************************/
|
||||
void ICACHE_FLASH_ATTR
|
||||
i2c_master_gpio_init(uint8 sda, uint8 scl)
|
||||
{
|
||||
pinSDA = pin_num[sda];
|
||||
pinSCL = pin_num[scl];
|
||||
|
||||
ETS_GPIO_INTR_DISABLE() ;
|
||||
// ETS_INTR_LOCK();
|
||||
|
||||
PIN_FUNC_SELECT(I2C_MASTER_SDA_MUX, I2C_MASTER_SDA_FUNC);
|
||||
PIN_FUNC_SELECT(I2C_MASTER_SCL_MUX, I2C_MASTER_SCL_FUNC);
|
||||
|
||||
GPIO_REG_WRITE(GPIO_PIN_ADDR(GPIO_ID_PIN(I2C_MASTER_SDA_GPIO)), GPIO_REG_READ(GPIO_PIN_ADDR(GPIO_ID_PIN(I2C_MASTER_SDA_GPIO))) | GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_ENABLE)); //open drain;
|
||||
GPIO_REG_WRITE(GPIO_ENABLE_ADDRESS, GPIO_REG_READ(GPIO_ENABLE_ADDRESS) | (1 << I2C_MASTER_SDA_GPIO));
|
||||
GPIO_REG_WRITE(GPIO_PIN_ADDR(GPIO_ID_PIN(I2C_MASTER_SCL_GPIO)), GPIO_REG_READ(GPIO_PIN_ADDR(GPIO_ID_PIN(I2C_MASTER_SCL_GPIO))) | GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_ENABLE)); //open drain;
|
||||
GPIO_REG_WRITE(GPIO_ENABLE_ADDRESS, GPIO_REG_READ(GPIO_ENABLE_ADDRESS) | (1 << I2C_MASTER_SCL_GPIO));
|
||||
|
||||
I2C_MASTER_SDA_HIGH_SCL_HIGH();
|
||||
|
||||
ETS_GPIO_INTR_ENABLE() ;
|
||||
// ETS_INTR_UNLOCK();
|
||||
|
||||
i2c_master_init();
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : i2c_master_start
|
||||
* Description : set i2c to send state
|
||||
* Parameters : NONE
|
||||
* Returns : NONE
|
||||
*******************************************************************************/
|
||||
void ICACHE_FLASH_ATTR
|
||||
i2c_master_start(void)
|
||||
{
|
||||
i2c_master_setDC(1, m_nLastSCL);
|
||||
i2c_master_wait(5);
|
||||
i2c_master_setDC(1, 1);
|
||||
i2c_master_wait(5); // sda 1, scl 1
|
||||
i2c_master_setDC(0, 1);
|
||||
i2c_master_wait(5); // sda 0, scl 1
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : i2c_master_stop
|
||||
* Description : set i2c to stop sending state
|
||||
* Parameters : NONE
|
||||
* Returns : NONE
|
||||
*******************************************************************************/
|
||||
void ICACHE_FLASH_ATTR
|
||||
i2c_master_stop(void)
|
||||
{
|
||||
i2c_master_wait(5);
|
||||
|
||||
i2c_master_setDC(0, m_nLastSCL);
|
||||
i2c_master_wait(5); // sda 0
|
||||
i2c_master_setDC(0, 1);
|
||||
i2c_master_wait(5); // sda 0, scl 1
|
||||
i2c_master_setDC(1, 1);
|
||||
i2c_master_wait(5); // sda 1, scl 1
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : i2c_master_setAck
|
||||
* Description : set ack to i2c bus as level value
|
||||
* Parameters : uint8 level - 0 or 1
|
||||
* Returns : NONE
|
||||
*******************************************************************************/
|
||||
void ICACHE_FLASH_ATTR
|
||||
i2c_master_setAck(uint8 level)
|
||||
{
|
||||
i2c_master_setDC(m_nLastSDA, 0);
|
||||
i2c_master_wait(5);
|
||||
i2c_master_setDC(level, 0);
|
||||
i2c_master_wait(5); // sda level, scl 0
|
||||
i2c_master_setDC(level, 1);
|
||||
i2c_master_wait(8); // sda level, scl 1
|
||||
i2c_master_setDC(level, 0);
|
||||
i2c_master_wait(5); // sda level, scl 0
|
||||
i2c_master_setDC(1, 0);
|
||||
i2c_master_wait(5);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : i2c_master_getAck
|
||||
* Description : confirm if peer send ack
|
||||
* Parameters : NONE
|
||||
* Returns : uint8 - ack value, 0 or 1
|
||||
*******************************************************************************/
|
||||
uint8 ICACHE_FLASH_ATTR
|
||||
i2c_master_getAck(void)
|
||||
{
|
||||
uint8 retVal;
|
||||
i2c_master_setDC(m_nLastSDA, 0);
|
||||
i2c_master_wait(5);
|
||||
i2c_master_setDC(1, 0);
|
||||
i2c_master_wait(5);
|
||||
i2c_master_setDC(1, 1);
|
||||
i2c_master_wait(5);
|
||||
|
||||
retVal = i2c_master_getDC();
|
||||
i2c_master_wait(5);
|
||||
i2c_master_setDC(1, 0);
|
||||
i2c_master_wait(5);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : i2c_master_checkAck
|
||||
* Description : get dev response
|
||||
* Parameters : NONE
|
||||
* Returns : true : get ack ; false : get nack
|
||||
*******************************************************************************/
|
||||
bool ICACHE_FLASH_ATTR
|
||||
i2c_master_checkAck(void)
|
||||
{
|
||||
if(i2c_master_getAck()){
|
||||
return FALSE;
|
||||
}else{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : i2c_master_send_ack
|
||||
* Description : response ack
|
||||
* Parameters : NONE
|
||||
* Returns : NONE
|
||||
*******************************************************************************/
|
||||
void ICACHE_FLASH_ATTR
|
||||
i2c_master_send_ack(void)
|
||||
{
|
||||
i2c_master_setAck(0x0);
|
||||
}
|
||||
/******************************************************************************
|
||||
* FunctionName : i2c_master_send_nack
|
||||
* Description : response nack
|
||||
* Parameters : NONE
|
||||
* Returns : NONE
|
||||
*******************************************************************************/
|
||||
void ICACHE_FLASH_ATTR
|
||||
i2c_master_send_nack(void)
|
||||
{
|
||||
i2c_master_setAck(0x1);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : i2c_master_readByte
|
||||
* Description : read Byte from i2c bus
|
||||
* Parameters : NONE
|
||||
* Returns : uint8 - readed value
|
||||
*******************************************************************************/
|
||||
uint8 ICACHE_FLASH_ATTR
|
||||
i2c_master_readByte(void)
|
||||
{
|
||||
uint8 retVal = 0;
|
||||
uint8 k, i;
|
||||
|
||||
i2c_master_wait(5);
|
||||
i2c_master_setDC(m_nLastSDA, 0);
|
||||
i2c_master_wait(5); // sda 1, scl 0
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
i2c_master_wait(5);
|
||||
i2c_master_setDC(1, 0);
|
||||
i2c_master_wait(5); // sda 1, scl 0
|
||||
i2c_master_setDC(1, 1);
|
||||
i2c_master_wait(5); // sda 1, scl 1
|
||||
|
||||
k = i2c_master_getDC();
|
||||
i2c_master_wait(5);
|
||||
|
||||
if (i == 7) {
|
||||
i2c_master_wait(3); ////
|
||||
}
|
||||
|
||||
k <<= (7 - i);
|
||||
retVal |= k;
|
||||
}
|
||||
|
||||
i2c_master_setDC(1, 0);
|
||||
i2c_master_wait(5); // sda 1, scl 0
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : i2c_master_writeByte
|
||||
* Description : write wrdata value(one byte) into i2c
|
||||
* Parameters : uint8 wrdata - write value
|
||||
* Returns : NONE
|
||||
*******************************************************************************/
|
||||
void ICACHE_FLASH_ATTR
|
||||
i2c_master_writeByte(uint8 wrdata)
|
||||
{
|
||||
uint8 dat;
|
||||
sint8 i;
|
||||
|
||||
i2c_master_wait(5);
|
||||
|
||||
i2c_master_setDC(m_nLastSDA, 0);
|
||||
i2c_master_wait(5);
|
||||
|
||||
for (i = 7; i >= 0; i--) {
|
||||
dat = wrdata >> i;
|
||||
i2c_master_setDC(dat, 0);
|
||||
i2c_master_wait(5);
|
||||
i2c_master_setDC(dat, 1);
|
||||
i2c_master_wait(5);
|
||||
|
||||
if (i == 0) {
|
||||
i2c_master_wait(3); ////
|
||||
}
|
||||
|
||||
i2c_master_setDC(dat, 0);
|
||||
i2c_master_wait(5);
|
||||
}
|
||||
}
|
167
app/driver/key.c
167
app/driver/key.c
@ -1,167 +0,0 @@
|
||||
/******************************************************************************
|
||||
* Copyright 2013-2014 Espressif Systems (Wuxi)
|
||||
*
|
||||
* FileName: key.c
|
||||
*
|
||||
* Description: key driver, now can use different gpio and install different function
|
||||
*
|
||||
* Modification history:
|
||||
* 2014/5/1, v1.0 create this file.
|
||||
*******************************************************************************/
|
||||
#ifdef __ESP8266__
|
||||
|
||||
#include "ets_sys.h"
|
||||
#include "os_type.h"
|
||||
#include "osapi.h"
|
||||
#include "mem.h"
|
||||
#include "platform.h"
|
||||
#include "gpio.h"
|
||||
#include "user_interface.h"
|
||||
|
||||
#include "driver/key.h"
|
||||
|
||||
LOCAL void ICACHE_RAM_ATTR key_intr_handler(void *arg);
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : key_init_single
|
||||
* Description : init single key's gpio and register function
|
||||
* Parameters : uint8 gpio_id - which gpio to use
|
||||
* uint32 gpio_name - gpio mux name
|
||||
* uint32 gpio_func - gpio function
|
||||
* key_function long_press - long press function, needed to install
|
||||
* key_function short_press - short press function, needed to install
|
||||
* Returns : single_key_param - single key parameter, needed by key init
|
||||
*******************************************************************************/
|
||||
struct single_key_param *ICACHE_FLASH_ATTR
|
||||
key_init_single(uint8 gpio_id, uint32 gpio_name, uint8 gpio_func, key_function long_press, key_function short_press)
|
||||
{
|
||||
struct single_key_param *single_key = (struct single_key_param *)zalloc(sizeof(struct single_key_param));
|
||||
|
||||
single_key->gpio_id = gpio_id;
|
||||
single_key->gpio_name = gpio_name;
|
||||
single_key->gpio_func = gpio_func;
|
||||
single_key->long_press = long_press;
|
||||
single_key->short_press = short_press;
|
||||
|
||||
return single_key;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : key_init
|
||||
* Description : init keys
|
||||
* Parameters : key_param *keys - keys parameter, which inited by key_init_single
|
||||
* Returns : none
|
||||
*******************************************************************************/
|
||||
void ICACHE_FLASH_ATTR
|
||||
key_init(struct keys_param *keys)
|
||||
{
|
||||
uint8 i;
|
||||
|
||||
ETS_GPIO_INTR_ATTACH(key_intr_handler, keys);
|
||||
|
||||
ETS_GPIO_INTR_DISABLE();
|
||||
|
||||
for (i = 0; i < keys->key_num; i++) {
|
||||
keys->single_key[i]->key_level = 1;
|
||||
|
||||
PIN_FUNC_SELECT(keys->single_key[i]->gpio_name, keys->single_key[i]->gpio_func);
|
||||
|
||||
gpio_output_set(0, 0, 0, GPIO_ID_PIN(keys->single_key[i]->gpio_id));
|
||||
|
||||
gpio_register_set(GPIO_PIN_ADDR(keys->single_key[i]->gpio_id), GPIO_PIN_INT_TYPE_SET(GPIO_PIN_INTR_DISABLE)
|
||||
| GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_DISABLE)
|
||||
| GPIO_PIN_SOURCE_SET(GPIO_AS_PIN_SOURCE));
|
||||
|
||||
//clear gpio14 status
|
||||
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(keys->single_key[i]->gpio_id));
|
||||
|
||||
//enable interrupt
|
||||
gpio_pin_intr_state_set(GPIO_ID_PIN(keys->single_key[i]->gpio_id), GPIO_PIN_INTR_NEGEDGE);
|
||||
}
|
||||
|
||||
ETS_GPIO_INTR_ENABLE();
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : key_5s_cb
|
||||
* Description : long press 5s timer callback
|
||||
* Parameters : single_key_param *single_key - single key parameter
|
||||
* Returns : none
|
||||
*******************************************************************************/
|
||||
LOCAL void ICACHE_FLASH_ATTR
|
||||
key_5s_cb(struct single_key_param *single_key)
|
||||
{
|
||||
os_timer_disarm(&single_key->key_5s);
|
||||
|
||||
// low, then restart
|
||||
if (0 == GPIO_INPUT_GET(GPIO_ID_PIN(single_key->gpio_id))) {
|
||||
if (single_key->long_press) {
|
||||
single_key->long_press();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : key_50ms_cb
|
||||
* Description : 50ms timer callback to check it's a real key push
|
||||
* Parameters : single_key_param *single_key - single key parameter
|
||||
* Returns : none
|
||||
*******************************************************************************/
|
||||
LOCAL void ICACHE_FLASH_ATTR
|
||||
key_50ms_cb(struct single_key_param *single_key)
|
||||
{
|
||||
os_timer_disarm(&single_key->key_50ms);
|
||||
|
||||
// high, then key is up
|
||||
if (1 == GPIO_INPUT_GET(GPIO_ID_PIN(single_key->gpio_id))) {
|
||||
os_timer_disarm(&single_key->key_5s);
|
||||
single_key->key_level = 1;
|
||||
gpio_pin_intr_state_set(GPIO_ID_PIN(single_key->gpio_id), GPIO_PIN_INTR_NEGEDGE);
|
||||
|
||||
if (single_key->short_press) {
|
||||
single_key->short_press();
|
||||
}
|
||||
} else {
|
||||
gpio_pin_intr_state_set(GPIO_ID_PIN(single_key->gpio_id), GPIO_PIN_INTR_POSEDGE);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : key_intr_handler
|
||||
* Description : key interrupt handler
|
||||
* Parameters : key_param *keys - keys parameter, which inited by key_init_single
|
||||
* Returns : none
|
||||
*******************************************************************************/
|
||||
LOCAL void
|
||||
key_intr_handler(void *arg)
|
||||
{
|
||||
struct keys_param *keys = arg;
|
||||
uint8 i;
|
||||
uint32 gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);
|
||||
|
||||
for (i = 0; i < keys->key_num; i++) {
|
||||
if (gpio_status & BIT(keys->single_key[i]->gpio_id)) {
|
||||
//disable interrupt
|
||||
gpio_pin_intr_state_set(GPIO_ID_PIN(keys->single_key[i]->gpio_id), GPIO_PIN_INTR_DISABLE);
|
||||
|
||||
//clear interrupt status
|
||||
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status & BIT(keys->single_key[i]->gpio_id));
|
||||
|
||||
if (keys->single_key[i]->key_level == 1) {
|
||||
// 5s, restart & enter softap mode
|
||||
os_timer_disarm(&keys->single_key[i]->key_5s);
|
||||
os_timer_setfn(&keys->single_key[i]->key_5s, (os_timer_func_t *)key_5s_cb, keys->single_key[i]);
|
||||
os_timer_arm(&keys->single_key[i]->key_5s, 5000, 0);
|
||||
keys->single_key[i]->key_level = 0;
|
||||
gpio_pin_intr_state_set(GPIO_ID_PIN(keys->single_key[i]->gpio_id), GPIO_PIN_INTR_POSEDGE);
|
||||
} else {
|
||||
// 50ms, check if this is a real key up
|
||||
os_timer_disarm(&keys->single_key[i]->key_50ms);
|
||||
os_timer_setfn(&keys->single_key[i]->key_50ms, (os_timer_func_t *)key_50ms_cb, keys->single_key[i]);
|
||||
os_timer_arm(&keys->single_key[i]->key_50ms, 50, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,557 +0,0 @@
|
||||
/*
|
||||
Adaptation of Paul Stoffregen's One wire library to the NodeMcu
|
||||
|
||||
The latest version of this library may be found at:
|
||||
http://www.pjrc.com/teensy/td_libs_OneWire.html
|
||||
|
||||
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.
|
||||
|
||||
Much of the code was inspired by Derek Yerger's code, though I don't
|
||||
think much of that remains. In any event that was..
|
||||
(copyleft) 2006 by Derek Yerger - Free to distribute freely.
|
||||
|
||||
The CRC code was excerpted and inspired by the Dallas Semiconductor
|
||||
sample code bearing this copyright.
|
||||
//---------------------------------------------------------------------------
|
||||
// Copyright (C) 2000 Dallas Semiconductor Corporation, All Rights Reserved.
|
||||
//
|
||||
// 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 DALLAS SEMICONDUCTOR 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.
|
||||
//
|
||||
// Except as contained in this notice, the name of Dallas Semiconductor
|
||||
// shall not be used except as stated in the Dallas Semiconductor
|
||||
// Branding Policy.
|
||||
//--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "driver/onewire.h"
|
||||
#include "platform.h"
|
||||
#include "osapi.h"
|
||||
#include "esp_misc.h"
|
||||
|
||||
#define noInterrupts ets_intr_lock
|
||||
#define interrupts ets_intr_unlock
|
||||
#define delayMicroseconds os_delay_us
|
||||
|
||||
// 1 for keeping the parasitic power on H
|
||||
#define owDefaultPower 1
|
||||
|
||||
#if ONEWIRE_SEARCH
|
||||
// global search state
|
||||
static unsigned char ROM_NO[NUM_OW][8];
|
||||
static uint8_t LastDiscrepancy[NUM_OW];
|
||||
static uint8_t LastFamilyDiscrepancy[NUM_OW];
|
||||
static uint8_t LastDeviceFlag[NUM_OW];
|
||||
#endif
|
||||
|
||||
void onewire_init(uint8_t pin)
|
||||
{
|
||||
// pinMode(pin, INPUT);
|
||||
platform_gpio_mode(pin, PLATFORM_GPIO_INPUT, PLATFORM_GPIO_PULLUP);
|
||||
#if ONEWIRE_SEARCH
|
||||
onewire_reset_search(pin);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Perform the onewire reset function. We will wait up to 250uS for
|
||||
// the bus to come high, if it doesn't then it is broken or shorted
|
||||
// and we return a 0;
|
||||
//
|
||||
// Returns 1 if a device asserted a presence pulse, 0 otherwise.
|
||||
//
|
||||
uint8_t onewire_reset(uint8_t pin)
|
||||
{
|
||||
uint8_t r;
|
||||
uint8_t retries = 125;
|
||||
|
||||
noInterrupts();
|
||||
DIRECT_MODE_INPUT(pin);
|
||||
interrupts();
|
||||
// wait until the wire is high... just in case
|
||||
do {
|
||||
if (--retries == 0) return 0;
|
||||
delayMicroseconds(2);
|
||||
} while ( !DIRECT_READ(pin));
|
||||
|
||||
noInterrupts();
|
||||
DIRECT_WRITE_LOW(pin);
|
||||
DIRECT_MODE_OUTPUT(pin); // drive output low
|
||||
interrupts();
|
||||
delayMicroseconds(480);
|
||||
noInterrupts();
|
||||
DIRECT_MODE_INPUT(pin); // allow it to float
|
||||
delayMicroseconds(70);
|
||||
r = !DIRECT_READ(pin);
|
||||
interrupts();
|
||||
delayMicroseconds(410);
|
||||
return r;
|
||||
}
|
||||
|
||||
//
|
||||
// Write a bit. Port and bit is used to cut lookup time and provide
|
||||
// more certain timing.
|
||||
//
|
||||
static void onewire_write_bit(uint8_t pin, uint8_t v)
|
||||
{
|
||||
if (v & 1) {
|
||||
noInterrupts();
|
||||
DIRECT_WRITE_LOW(pin);
|
||||
DIRECT_MODE_OUTPUT(pin); // drive output low
|
||||
delayMicroseconds(10);
|
||||
DIRECT_WRITE_HIGH(pin); // drive output high
|
||||
interrupts();
|
||||
delayMicroseconds(55);
|
||||
} else {
|
||||
noInterrupts();
|
||||
DIRECT_WRITE_LOW(pin);
|
||||
DIRECT_MODE_OUTPUT(pin); // drive output low
|
||||
delayMicroseconds(65);
|
||||
DIRECT_WRITE_HIGH(pin); // drive output high
|
||||
interrupts();
|
||||
delayMicroseconds(5);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Read a bit. Port and bit is used to cut lookup time and provide
|
||||
// more certain timing.
|
||||
//
|
||||
static uint8_t onewire_read_bit(uint8_t pin)
|
||||
{
|
||||
uint8_t r;
|
||||
|
||||
noInterrupts();
|
||||
DIRECT_MODE_OUTPUT(pin);
|
||||
DIRECT_WRITE_LOW(pin);
|
||||
delayMicroseconds(3);
|
||||
DIRECT_MODE_INPUT(pin); // let pin float, pull up will raise
|
||||
delayMicroseconds(10);
|
||||
r = DIRECT_READ(pin);
|
||||
interrupts();
|
||||
delayMicroseconds(53);
|
||||
return r;
|
||||
}
|
||||
|
||||
//
|
||||
// Write a byte. The writing code uses the active drivers to raise the
|
||||
// pin high, if you need power after the write (e.g. DS18S20 in
|
||||
// parasite power mode) then set 'power' to 1, otherwise the pin will
|
||||
// go tri-state at the end of the write to avoid heating in a short or
|
||||
// other mishap.
|
||||
//
|
||||
void onewire_write(uint8_t pin, uint8_t v, uint8_t power /* = 0 */) {
|
||||
uint8_t bitMask;
|
||||
|
||||
for (bitMask = 0x01; bitMask; bitMask <<= 1) {
|
||||
onewire_write_bit(pin, (bitMask & v)?1:0);
|
||||
}
|
||||
if ( !power) {
|
||||
noInterrupts();
|
||||
DIRECT_MODE_INPUT(pin);
|
||||
DIRECT_WRITE_LOW(pin);
|
||||
interrupts();
|
||||
}
|
||||
}
|
||||
|
||||
void onewire_write_bytes(uint8_t pin, const uint8_t *buf, uint16_t count, bool power /* = 0 */) {
|
||||
uint16_t i;
|
||||
for (i = 0 ; i < count ; i++)
|
||||
onewire_write(pin, buf[i], owDefaultPower);
|
||||
if (!power) {
|
||||
noInterrupts();
|
||||
DIRECT_MODE_INPUT(pin);
|
||||
DIRECT_WRITE_LOW(pin);
|
||||
interrupts();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Read a byte
|
||||
//
|
||||
uint8_t onewire_read(uint8_t pin) {
|
||||
uint8_t bitMask;
|
||||
uint8_t r = 0;
|
||||
|
||||
for (bitMask = 0x01; bitMask; bitMask <<= 1) {
|
||||
if (onewire_read_bit(pin)) r |= bitMask;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
void onewire_read_bytes(uint8_t pin, uint8_t *buf, uint16_t count) {
|
||||
uint16_t i;
|
||||
for (i = 0 ; i < count ; i++)
|
||||
buf[i] = onewire_read(pin);
|
||||
}
|
||||
|
||||
//
|
||||
// Do a ROM select
|
||||
//
|
||||
void onewire_select(uint8_t pin, const uint8_t rom[8])
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
onewire_write(pin, 0x55, owDefaultPower); // Choose ROM
|
||||
|
||||
for (i = 0; i < 8; i++) onewire_write(pin, rom[i], owDefaultPower);
|
||||
}
|
||||
|
||||
//
|
||||
// Do a ROM skip
|
||||
//
|
||||
void onewire_skip(uint8_t pin)
|
||||
{
|
||||
onewire_write(pin, 0xCC, owDefaultPower); // Skip ROM
|
||||
}
|
||||
|
||||
void onewire_depower(uint8_t pin)
|
||||
{
|
||||
noInterrupts();
|
||||
DIRECT_MODE_INPUT(pin);
|
||||
interrupts();
|
||||
}
|
||||
|
||||
#if ONEWIRE_SEARCH
|
||||
|
||||
//
|
||||
// You need to use this function to start a search again from the beginning.
|
||||
// You do not need to do it for the first search, though you could.
|
||||
//
|
||||
void onewire_reset_search(uint8_t pin)
|
||||
{
|
||||
// reset the search state
|
||||
LastDiscrepancy[pin] = 0;
|
||||
LastDeviceFlag[pin] = FALSE;
|
||||
LastFamilyDiscrepancy[pin] = 0;
|
||||
int i;
|
||||
for(i = 7; ; i--) {
|
||||
ROM_NO[pin][i] = 0;
|
||||
if ( i == 0) break;
|
||||
}
|
||||
}
|
||||
|
||||
// Setup the search to find the device type 'family_code' on the next call
|
||||
// to search(*newAddr) if it is present.
|
||||
//
|
||||
void onewire_target_search(uint8_t pin, uint8_t family_code)
|
||||
{
|
||||
// set the search state to find SearchFamily type devices
|
||||
ROM_NO[pin][0] = family_code;
|
||||
uint8_t i;
|
||||
for (i = 1; i < 8; i++)
|
||||
ROM_NO[pin][i] = 0;
|
||||
LastDiscrepancy[pin] = 64;
|
||||
LastFamilyDiscrepancy[pin] = 0;
|
||||
LastDeviceFlag[pin] = FALSE;
|
||||
}
|
||||
|
||||
//
|
||||
// Perform a search. If this function returns a '1' then it has
|
||||
// enumerated the next device and you may retrieve the ROM from the
|
||||
// OneWire::address variable. If there are no devices, no further
|
||||
// devices, or something horrible happens in the middle of the
|
||||
// enumeration then a 0 is returned. If a new device is found then
|
||||
// its address is copied to newAddr. Use OneWire::reset_search() to
|
||||
// start over.
|
||||
//
|
||||
// --- Replaced by the one from the Dallas Semiconductor web site ---
|
||||
//--------------------------------------------------------------------------
|
||||
// Perform the 1-Wire Search Algorithm on the 1-Wire bus using the existing
|
||||
// search state.
|
||||
// Return TRUE : device found, ROM number in ROM_NO buffer
|
||||
// FALSE : device not found, end of search
|
||||
//
|
||||
uint8_t onewire_search(uint8_t pin, uint8_t *newAddr)
|
||||
{
|
||||
uint8_t id_bit_number;
|
||||
uint8_t last_zero, rom_byte_number, search_result;
|
||||
uint8_t id_bit, cmp_id_bit;
|
||||
|
||||
unsigned char rom_byte_mask, search_direction;
|
||||
|
||||
// initialize for search
|
||||
id_bit_number = 1;
|
||||
last_zero = 0;
|
||||
rom_byte_number = 0;
|
||||
rom_byte_mask = 1;
|
||||
search_result = 0;
|
||||
|
||||
// if the last call was not the last one
|
||||
if (!LastDeviceFlag[pin])
|
||||
{
|
||||
// 1-Wire reset
|
||||
if (!onewire_reset(pin))
|
||||
{
|
||||
// reset the search
|
||||
LastDiscrepancy[pin] = 0;
|
||||
LastDeviceFlag[pin] = FALSE;
|
||||
LastFamilyDiscrepancy[pin] = 0;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// issue the search command
|
||||
onewire_write(pin, 0xF0, owDefaultPower);
|
||||
|
||||
// loop to do the search
|
||||
do
|
||||
{
|
||||
// read a bit and its complement
|
||||
id_bit = onewire_read_bit(pin);
|
||||
cmp_id_bit = onewire_read_bit(pin);
|
||||
|
||||
// check for no devices on 1-wire
|
||||
if ((id_bit == 1) && (cmp_id_bit == 1))
|
||||
break;
|
||||
else
|
||||
{
|
||||
// all devices coupled have 0 or 1
|
||||
if (id_bit != cmp_id_bit)
|
||||
search_direction = id_bit; // bit write value for search
|
||||
else
|
||||
{
|
||||
// if this discrepancy if before the Last Discrepancy
|
||||
// on a previous next then pick the same as last time
|
||||
if (id_bit_number < LastDiscrepancy[pin])
|
||||
search_direction = ((ROM_NO[pin][rom_byte_number] & rom_byte_mask) > 0);
|
||||
else
|
||||
// if equal to last pick 1, if not then pick 0
|
||||
search_direction = (id_bit_number == LastDiscrepancy[pin]);
|
||||
|
||||
// if 0 was picked then record its position in LastZero
|
||||
if (search_direction == 0)
|
||||
{
|
||||
last_zero = id_bit_number;
|
||||
|
||||
// check for Last discrepancy in family
|
||||
if (last_zero < 9)
|
||||
LastFamilyDiscrepancy[pin] = last_zero;
|
||||
}
|
||||
}
|
||||
|
||||
// set or clear the bit in the ROM byte rom_byte_number
|
||||
// with mask rom_byte_mask
|
||||
if (search_direction == 1)
|
||||
ROM_NO[pin][rom_byte_number] |= rom_byte_mask;
|
||||
else
|
||||
ROM_NO[pin][rom_byte_number] &= ~rom_byte_mask;
|
||||
|
||||
// serial number search direction write bit
|
||||
onewire_write_bit(pin, search_direction);
|
||||
|
||||
// increment the byte counter id_bit_number
|
||||
// and shift the mask rom_byte_mask
|
||||
id_bit_number++;
|
||||
rom_byte_mask <<= 1;
|
||||
|
||||
// if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask
|
||||
if (rom_byte_mask == 0)
|
||||
{
|
||||
rom_byte_number++;
|
||||
rom_byte_mask = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
while(rom_byte_number < 8); // loop until through all ROM bytes 0-7
|
||||
|
||||
// if the search was successful then
|
||||
if (!(id_bit_number < 65))
|
||||
{
|
||||
// search successful so set LastDiscrepancy,LastDeviceFlag,search_result
|
||||
LastDiscrepancy[pin] = last_zero;
|
||||
|
||||
// check for last device
|
||||
if (LastDiscrepancy[pin] == 0)
|
||||
LastDeviceFlag[pin] = TRUE;
|
||||
|
||||
search_result = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// if no device found then reset counters so next 'search' will be like a first
|
||||
if (!search_result || !ROM_NO[pin][0])
|
||||
{
|
||||
LastDiscrepancy[pin] = 0;
|
||||
LastDeviceFlag[pin] = FALSE;
|
||||
LastFamilyDiscrepancy[pin] = 0;
|
||||
search_result = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (rom_byte_number = 0; rom_byte_number < 8; rom_byte_number++)
|
||||
{
|
||||
newAddr[rom_byte_number] = ROM_NO[pin][rom_byte_number];
|
||||
}
|
||||
}
|
||||
return search_result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if ONEWIRE_CRC
|
||||
// The 1-Wire CRC scheme is described in Maxim Application Note 27:
|
||||
// "Understanding and Using Cyclic Redundancy Checks with Maxim iButton Products"
|
||||
//
|
||||
|
||||
#if ONEWIRE_CRC8_TABLE
|
||||
// This table comes from Dallas sample code where it is freely reusable,
|
||||
// though Copyright (C) 2000 Dallas Semiconductor Corporation
|
||||
static const uint8_t dscrc_table[] = {
|
||||
0, 94,188,226, 97, 63,221,131,194,156,126, 32,163,253, 31, 65,
|
||||
157,195, 33,127,252,162, 64, 30, 95, 1,227,189, 62, 96,130,220,
|
||||
35,125,159,193, 66, 28,254,160,225,191, 93, 3,128,222, 60, 98,
|
||||
190,224, 2, 92,223,129, 99, 61,124, 34,192,158, 29, 67,161,255,
|
||||
70, 24,250,164, 39,121,155,197,132,218, 56,102,229,187, 89, 7,
|
||||
219,133,103, 57,186,228, 6, 88, 25, 71,165,251,120, 38,196,154,
|
||||
101, 59,217,135, 4, 90,184,230,167,249, 27, 69,198,152,122, 36,
|
||||
248,166, 68, 26,153,199, 37,123, 58,100,134,216, 91, 5,231,185,
|
||||
140,210, 48,110,237,179, 81, 15, 78, 16,242,172, 47,113,147,205,
|
||||
17, 79,173,243,112, 46,204,146,211,141,111, 49,178,236, 14, 80,
|
||||
175,241, 19, 77,206,144,114, 44,109, 51,209,143, 12, 82,176,238,
|
||||
50,108,142,208, 83, 13,239,177,240,174, 76, 18,145,207, 45,115,
|
||||
202,148,118, 40,171,245, 23, 73, 8, 86,180,234,105, 55,213,139,
|
||||
87, 9,235,181, 54,104,138,212,149,203, 41,119,244,170, 72, 22,
|
||||
233,183, 85, 11,136,214, 52,106, 43,117,151,201, 74, 20,246,168,
|
||||
116, 42,200,150, 21, 75,169,247,182,232, 10, 84,215,137,107, 53};
|
||||
|
||||
#ifndef pgm_read_byte
|
||||
#define pgm_read_byte(addr) (*(const uint8_t *)(addr))
|
||||
#endif
|
||||
|
||||
//
|
||||
// Compute a Dallas Semiconductor 8 bit CRC. These show up in the ROM
|
||||
// and the registers. (note: this might better be done without to
|
||||
// table, it would probably be smaller and certainly fast enough
|
||||
// compared to all those delayMicrosecond() calls. But I got
|
||||
// confused, so I use this table from the examples.)
|
||||
//
|
||||
uint8_t onewire_crc8(const uint8_t *addr, uint8_t len)
|
||||
{
|
||||
uint8_t crc = 0;
|
||||
|
||||
while (len--) {
|
||||
crc = pgm_read_byte(dscrc_table + (crc ^ *addr++));
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
#else
|
||||
//
|
||||
// Compute a Dallas Semiconductor 8 bit CRC directly.
|
||||
// this is much slower, but much smaller, than the lookup table.
|
||||
//
|
||||
uint8_t onewire_crc8(const uint8_t *addr, uint8_t len)
|
||||
{
|
||||
uint8_t crc = 0;
|
||||
|
||||
while (len--) {
|
||||
uint8_t inbyte = *addr++;
|
||||
uint8_t i;
|
||||
for (i = 8; i; i--) {
|
||||
uint8_t mix = (crc ^ inbyte) & 0x01;
|
||||
crc >>= 1;
|
||||
if (mix) crc ^= 0x8C;
|
||||
inbyte >>= 1;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ONEWIRE_CRC16
|
||||
// Compute the 1-Wire CRC16 and compare it against the received CRC.
|
||||
// Example usage (reading a DS2408):
|
||||
// // Put everything in a buffer so we can compute the CRC easily.
|
||||
// uint8_t buf[13];
|
||||
// buf[0] = 0xF0; // Read PIO Registers
|
||||
// buf[1] = 0x88; // LSB address
|
||||
// buf[2] = 0x00; // MSB address
|
||||
// WriteBytes(net, buf, 3); // Write 3 cmd bytes
|
||||
// ReadBytes(net, buf+3, 10); // Read 6 data bytes, 2 0xFF, 2 CRC16
|
||||
// if (!CheckCRC16(buf, 11, &buf[11])) {
|
||||
// // Handle error.
|
||||
// }
|
||||
//
|
||||
// @param input - Array of bytes to checksum.
|
||||
// @param len - How many bytes to use.
|
||||
// @param inverted_crc - The two CRC16 bytes in the received data.
|
||||
// This should just point into the received data,
|
||||
// *not* at a 16-bit integer.
|
||||
// @param crc - The crc starting value (optional)
|
||||
// @return True, iff the CRC matches.
|
||||
bool onewire_check_crc16(const uint8_t* input, uint16_t len, const uint8_t* inverted_crc, uint16_t crc)
|
||||
{
|
||||
crc = ~onewire_crc16(input, len, crc);
|
||||
return (crc & 0xFF) == inverted_crc[0] && (crc >> 8) == inverted_crc[1];
|
||||
}
|
||||
|
||||
// Compute a Dallas Semiconductor 16 bit CRC. This is required to check
|
||||
// the integrity of data received from many 1-Wire devices. Note that the
|
||||
// CRC computed here is *not* what you'll get from the 1-Wire network,
|
||||
// for two reasons:
|
||||
// 1) The CRC is transmitted bitwise inverted.
|
||||
// 2) Depending on the endian-ness of your processor, the binary
|
||||
// representation of the two-byte return value may have a different
|
||||
// byte order than the two bytes you get from 1-Wire.
|
||||
// @param input - Array of bytes to checksum.
|
||||
// @param len - How many bytes to use.
|
||||
// @param crc - The crc starting value (optional)
|
||||
// @return The CRC16, as defined by Dallas Semiconductor.
|
||||
uint16_t onewire_crc16(const uint8_t* input, uint16_t len, uint16_t crc)
|
||||
{
|
||||
static const uint8_t oddparity[16] =
|
||||
{ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 };
|
||||
|
||||
uint16_t i;
|
||||
for (i = 0 ; i < len ; i++) {
|
||||
// Even though we're just copying a byte from the input,
|
||||
// we'll be doing 16-bit computation with it.
|
||||
uint16_t cdata = input[i];
|
||||
cdata = (cdata ^ crc) & 0xff;
|
||||
crc >>= 8;
|
||||
|
||||
if (oddparity[cdata & 0x0F] ^ oddparity[cdata >> 4])
|
||||
crc ^= 0xC001;
|
||||
|
||||
cdata <<= 6;
|
||||
crc ^= cdata;
|
||||
cdata <<= 1;
|
||||
crc ^= cdata;
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
471
app/driver/pwm.c
471
app/driver/pwm.c
@ -1,471 +0,0 @@
|
||||
/******************************************************************************
|
||||
* Copyright 2013-2014 Espressif Systems (Wuxi)
|
||||
*
|
||||
* FileName: pwm.c
|
||||
*
|
||||
* Description: pwm driver
|
||||
*
|
||||
* Modification history:
|
||||
* 2014/5/1, v1.0 create this file.
|
||||
*******************************************************************************/
|
||||
|
||||
// ESP32 has own pwm driver in libdriver.a
|
||||
#ifdef __ESP8266__
|
||||
#include "platform.h"
|
||||
|
||||
#include "ets_sys.h"
|
||||
#include "os_type.h"
|
||||
#include "osapi.h"
|
||||
#include "gpio.h"
|
||||
#include "hw_timer.h"
|
||||
#include "esp_misc.h"
|
||||
|
||||
#include "user_interface.h"
|
||||
#include "driver/pwm.h"
|
||||
|
||||
// #define PWM_DBG os_printf
|
||||
#define PWM_DBG
|
||||
|
||||
// Enabling the next line will cause the interrupt handler to toggle
|
||||
// this output pin during processing so that the timing is obvious
|
||||
//
|
||||
// #define PWM_DBG_PIN 13 // GPIO7
|
||||
|
||||
#ifdef PWM_DBG_PIN
|
||||
#define PWM_DBG_PIN_HIGH() GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, 1 << PWM_DBG_PIN)
|
||||
#define PWM_DBG_PIN_LOW() GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, 1 << PWM_DBG_PIN)
|
||||
#else
|
||||
#define PWM_DBG_PIN_HIGH()
|
||||
#define PWM_DBG_PIN_LOW()
|
||||
#endif
|
||||
|
||||
LOCAL struct pwm_single_param pwm_single_toggle[2][PWM_CHANNEL + 1];
|
||||
LOCAL struct pwm_single_param *pwm_single;
|
||||
|
||||
LOCAL struct pwm_param pwm;
|
||||
|
||||
// LOCAL uint8 pwm_out_io_num[PWM_CHANNEL] = {PWM_0_OUT_IO_NUM, PWM_1_OUT_IO_NUM, PWM_2_OUT_IO_NUM};
|
||||
LOCAL int8 pwm_out_io_num[PWM_CHANNEL] = {-1, -1, -1, -1, -1, -1};
|
||||
|
||||
LOCAL uint8 pwm_channel_toggle[2];
|
||||
LOCAL uint8 *pwm_channel;
|
||||
|
||||
// Toggle flips between 1 and 0 when we make updates so that the interrupt code
|
||||
// cn switch cleanly between the two states. The cinterrupt handler uses either
|
||||
// the pwm_single_toggle[0] or pwm_single_toggle[1]
|
||||
// pwm_toggle indicates which state should be used on the *next* timer interrupt
|
||||
// freq boundary.
|
||||
LOCAL uint8 pwm_toggle = 1;
|
||||
LOCAL volatile uint8 pwm_current_toggle = 1;
|
||||
LOCAL uint8 pwm_timer_down = 1;
|
||||
|
||||
LOCAL uint8 pwm_current_channel = 0;
|
||||
|
||||
LOCAL uint16 pwm_gpio = 0;
|
||||
|
||||
LOCAL uint8 pwm_channel_num = 0;
|
||||
|
||||
LOCAL void ICACHE_RAM_ATTR pwm_tim1_intr_handler(uint32_t p);
|
||||
#define TIMER_OWNER ((uint32_t) 'P')
|
||||
|
||||
LOCAL void ICACHE_FLASH_ATTR
|
||||
pwm_insert_sort(struct pwm_single_param pwm[], uint8 n)
|
||||
{
|
||||
uint8 i;
|
||||
|
||||
for (i = 1; i < n; i++) {
|
||||
if (pwm[i].h_time < pwm[i - 1].h_time) {
|
||||
int8 j = i - 1;
|
||||
struct pwm_single_param tmp;
|
||||
|
||||
os_memcpy(&tmp, &pwm[i], sizeof(struct pwm_single_param));
|
||||
|
||||
while (tmp.h_time < pwm[j].h_time) {
|
||||
os_memcpy(&pwm[j + 1], &pwm[j], sizeof(struct pwm_single_param));
|
||||
j--;
|
||||
if (j < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
os_memcpy(&pwm[j + 1], &tmp, sizeof(struct pwm_single_param));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns FALSE if we cannot start
|
||||
bool ICACHE_FLASH_ATTR
|
||||
pwm_start(void)
|
||||
{
|
||||
uint8 i, j;
|
||||
PWM_DBG("--Function pwm_start() is called\n");
|
||||
PWM_DBG("pwm_gpio:%x,pwm_channel_num:%d\n",pwm_gpio,pwm_channel_num);
|
||||
PWM_DBG("pwm_out_io_num[0]:%d,[1]:%d,[2]:%d\n",pwm_out_io_num[0],pwm_out_io_num[1],pwm_out_io_num[2]);
|
||||
PWM_DBG("pwm.period:%d,pwm.duty[0]:%d,[1]:%d,[2]:%d\n",pwm.period,pwm.duty[0],pwm.duty[1],pwm.duty[2]);
|
||||
|
||||
// First we need to make sure that the interrupt handler is running
|
||||
// out of the same set of params as we expect
|
||||
while (!pwm_timer_down && pwm_toggle != pwm_current_toggle) {
|
||||
os_delay_us(100);
|
||||
}
|
||||
if (pwm_timer_down) {
|
||||
pwm_toggle = pwm_current_toggle;
|
||||
}
|
||||
|
||||
uint8_t new_toggle = pwm_toggle ^ 0x01;
|
||||
|
||||
struct pwm_single_param *local_single = pwm_single_toggle[new_toggle];
|
||||
uint8 *local_channel = &pwm_channel_toggle[new_toggle];
|
||||
|
||||
// step 1: init PWM_CHANNEL+1 channels param
|
||||
for (i = 0; i < pwm_channel_num; i++) {
|
||||
uint32 us = pwm.period * pwm.duty[i] / PWM_DEPTH;
|
||||
local_single[i].h_time = US_TO_RTC_TIMER_TICKS(us);
|
||||
PWM_DBG("i:%d us:%d ht:%d\n",i,us,local_single[i].h_time);
|
||||
local_single[i].gpio_set = 0;
|
||||
local_single[i].gpio_clear = 1 << pin_num[pwm_out_io_num[i]];
|
||||
}
|
||||
|
||||
local_single[pwm_channel_num].h_time = US_TO_RTC_TIMER_TICKS(pwm.period);
|
||||
local_single[pwm_channel_num].gpio_set = pwm_gpio;
|
||||
local_single[pwm_channel_num].gpio_clear = 0;
|
||||
PWM_DBG("i:%d period:%d ht:%d\n",pwm_channel_num,pwm.period,local_single[pwm_channel_num].h_time);
|
||||
// step 2: sort, small to big
|
||||
pwm_insert_sort(local_single, pwm_channel_num + 1);
|
||||
|
||||
*local_channel = pwm_channel_num + 1;
|
||||
PWM_DBG("1channel:%d,single[0]:%d,[1]:%d,[2]:%d,[3]:%d\n",*local_channel,local_single[0].h_time,local_single[1].h_time,local_single[2].h_time,local_single[3].h_time);
|
||||
// step 3: combine same duty channels (or nearly the same duty). If there is
|
||||
// under 2 us between pwm outputs, then treat them as the same.
|
||||
for (i = pwm_channel_num; i > 0; i--) {
|
||||
if (local_single[i].h_time <= local_single[i - 1].h_time + US_TO_RTC_TIMER_TICKS(2)) {
|
||||
local_single[i - 1].gpio_set |= local_single[i].gpio_set;
|
||||
local_single[i - 1].gpio_clear |= local_single[i].gpio_clear;
|
||||
|
||||
for (j = i + 1; j < *local_channel; j++) {
|
||||
os_memcpy(&local_single[j - 1], &local_single[j], sizeof(struct pwm_single_param));
|
||||
}
|
||||
|
||||
(*local_channel)--;
|
||||
}
|
||||
}
|
||||
PWM_DBG("2channel:%d,single[0]:%d,[1]:%d,[2]:%d,[3]:%d\n",*local_channel,local_single[0].h_time,local_single[1].h_time,local_single[2].h_time,local_single[3].h_time);
|
||||
// step 4: cacl delt time
|
||||
for (i = *local_channel - 1; i > 0; i--) {
|
||||
local_single[i].h_time -= local_single[i - 1].h_time;
|
||||
}
|
||||
|
||||
// step 5: last channel needs to clean
|
||||
local_single[*local_channel-1].gpio_clear = 0;
|
||||
|
||||
// step 6: if first channel duty is 0, remove it
|
||||
if (local_single[0].h_time == 0) {
|
||||
local_single[*local_channel - 1].gpio_set &= ~local_single[0].gpio_clear;
|
||||
local_single[*local_channel - 1].gpio_clear |= local_single[0].gpio_clear;
|
||||
|
||||
for (i = 1; i < *local_channel; i++) {
|
||||
os_memcpy(&local_single[i - 1], &local_single[i], sizeof(struct pwm_single_param));
|
||||
}
|
||||
|
||||
(*local_channel)--;
|
||||
}
|
||||
|
||||
// Make the new ones active
|
||||
pwm_toggle = new_toggle;
|
||||
|
||||
// if timer is down, need to set gpio and start timer
|
||||
if (pwm_timer_down == 1) {
|
||||
pwm_channel = local_channel;
|
||||
pwm_single = local_single;
|
||||
pwm_current_toggle = pwm_toggle;
|
||||
// start
|
||||
gpio_output_set(local_single[0].gpio_set, local_single[0].gpio_clear, pwm_gpio, 0);
|
||||
|
||||
// yeah, if all channels' duty is 0 or 255, don't need to start timer, otherwise start...
|
||||
if (*local_channel != 1) {
|
||||
PWM_DBG("Need to setup timer\n");
|
||||
if (!platform_hw_timer_init(TIMER_OWNER, NMI_SOURCE, FALSE)) {
|
||||
return FALSE;
|
||||
}
|
||||
pwm_timer_down = 0;
|
||||
platform_hw_timer_set_func(TIMER_OWNER, pwm_tim1_intr_handler, 0);
|
||||
platform_hw_timer_arm_ticks(TIMER_OWNER, local_single[0].h_time);
|
||||
} else {
|
||||
PWM_DBG("Timer left idle\n");
|
||||
platform_hw_timer_close(TIMER_OWNER);
|
||||
}
|
||||
} else {
|
||||
// ensure that all outputs are outputs
|
||||
gpio_output_set(0, 0, pwm_gpio, 0);
|
||||
}
|
||||
|
||||
#ifdef PWM_DBG_PIN
|
||||
// Enable as output
|
||||
gpio_output_set(0, 0, 1 << PWM_DBG_PIN, 0);
|
||||
#endif
|
||||
|
||||
PWM_DBG("3channel:%d,single[0]:%d,[1]:%d,[2]:%d,[3]:%d\n",*local_channel,local_single[0].h_time,local_single[1].h_time,local_single[2].h_time,local_single[3].h_time);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : pwm_set_duty
|
||||
* Description : set each channel's duty params
|
||||
* Parameters : uint8 duty : 0 ~ PWM_DEPTH
|
||||
* uint8 channel : channel index
|
||||
* Returns : NONE
|
||||
*******************************************************************************/
|
||||
void ICACHE_FLASH_ATTR
|
||||
pwm_set_duty(uint16 duty, uint8 channel)
|
||||
{
|
||||
uint8 i;
|
||||
for(i=0;i<pwm_channel_num;i++){
|
||||
if(pwm_out_io_num[i] == channel){
|
||||
channel = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i==pwm_channel_num) // non found
|
||||
return;
|
||||
|
||||
if (duty < 1) {
|
||||
pwm.duty[channel] = 0;
|
||||
} else if (duty >= PWM_DEPTH) {
|
||||
pwm.duty[channel] = PWM_DEPTH;
|
||||
} else {
|
||||
pwm.duty[channel] = duty;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : pwm_set_freq
|
||||
* Description : set pwm frequency
|
||||
* Parameters : uint16 freq : 100hz typically
|
||||
* Returns : NONE
|
||||
*******************************************************************************/
|
||||
void ICACHE_FLASH_ATTR
|
||||
pwm_set_freq(uint16 freq, uint8 channel)
|
||||
{
|
||||
if (freq > PWM_FREQ_MAX) {
|
||||
pwm.freq = PWM_FREQ_MAX;
|
||||
} else if (freq < 1) {
|
||||
pwm.freq = 1;
|
||||
} else {
|
||||
pwm.freq = freq;
|
||||
}
|
||||
|
||||
pwm.period = PWM_1S / pwm.freq;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : pwm_set_freq_duty
|
||||
* Description : set pwm frequency and each channel's duty
|
||||
* Parameters : uint16 freq : 100hz typically
|
||||
* uint16 *duty : each channel's duty
|
||||
* Returns : NONE
|
||||
*******************************************************************************/
|
||||
LOCAL void ICACHE_FLASH_ATTR
|
||||
pwm_set_freq_duty(uint16 freq, uint16 *duty)
|
||||
{
|
||||
uint8 i;
|
||||
|
||||
pwm_set_freq(freq, 0);
|
||||
|
||||
for (i = 0; i < PWM_CHANNEL; i++) {
|
||||
// pwm_set_duty(duty[i], i);
|
||||
if(pwm_out_io_num[i] != -1)
|
||||
pwm_set_duty(duty[i], pwm_out_io_num[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : pwm_get_duty
|
||||
* Description : get duty of each channel
|
||||
* Parameters : uint8 channel : channel index
|
||||
* Returns : NONE
|
||||
*******************************************************************************/
|
||||
uint16 ICACHE_FLASH_ATTR
|
||||
pwm_get_duty(uint8 channel)
|
||||
{
|
||||
uint8 i;
|
||||
for(i=0;i<pwm_channel_num;i++){
|
||||
if(pwm_out_io_num[i] == channel){
|
||||
channel = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i==pwm_channel_num) // non found
|
||||
return 0;
|
||||
|
||||
return pwm.duty[channel];
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : pwm_get_freq
|
||||
* Description : get pwm frequency
|
||||
* Parameters : NONE
|
||||
* Returns : uint16 : pwm frequency
|
||||
*******************************************************************************/
|
||||
uint16 ICACHE_FLASH_ATTR
|
||||
pwm_get_freq(uint8 channel)
|
||||
{
|
||||
return pwm.freq;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : pwm_period_timer
|
||||
* Description : pwm period timer function, output high level,
|
||||
* start each channel's high level timer
|
||||
* Parameters : NONE
|
||||
* Returns : NONE
|
||||
*******************************************************************************/
|
||||
LOCAL void ICACHE_RAM_ATTR
|
||||
pwm_tim1_intr_handler(uint32_t p)
|
||||
{
|
||||
(void)p;
|
||||
|
||||
PWM_DBG_PIN_HIGH();
|
||||
|
||||
int offset = 0;
|
||||
|
||||
while (1) {
|
||||
if (pwm_current_channel >= (*pwm_channel - 1)) {
|
||||
pwm_single = pwm_single_toggle[pwm_toggle];
|
||||
pwm_channel = &pwm_channel_toggle[pwm_toggle];
|
||||
pwm_current_toggle = pwm_toggle;
|
||||
|
||||
gpio_output_set(pwm_single[*pwm_channel - 1].gpio_set,
|
||||
pwm_single[*pwm_channel - 1].gpio_clear,
|
||||
0,
|
||||
0);
|
||||
|
||||
pwm_current_channel = 0;
|
||||
|
||||
if (*pwm_channel == 1) {
|
||||
pwm_timer_down = 1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
gpio_output_set(pwm_single[pwm_current_channel].gpio_set,
|
||||
pwm_single[pwm_current_channel].gpio_clear,
|
||||
0, 0);
|
||||
|
||||
pwm_current_channel++;
|
||||
}
|
||||
|
||||
int next_time = pwm_single[pwm_current_channel].h_time;
|
||||
// Delay now holds the time (in ticks) since when the last timer expiry was
|
||||
PWM_DBG_PIN_LOW();
|
||||
int delay = platform_hw_timer_get_delay_ticks(TIMER_OWNER) + 4 - offset;
|
||||
|
||||
offset += next_time;
|
||||
|
||||
next_time = next_time - delay;
|
||||
|
||||
if (next_time > US_TO_RTC_TIMER_TICKS(4)) {
|
||||
PWM_DBG_PIN_HIGH();
|
||||
platform_hw_timer_arm_ticks(TIMER_OWNER, next_time);
|
||||
break;
|
||||
}
|
||||
PWM_DBG_PIN_HIGH();
|
||||
}
|
||||
|
||||
PWM_DBG_PIN_LOW();
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : pwm_init
|
||||
* Description : pwm gpio, params and timer initialization
|
||||
* Parameters : uint16 freq : pwm freq param
|
||||
* uint16 *duty : each channel's duty
|
||||
* Returns : void
|
||||
*******************************************************************************/
|
||||
void ICACHE_FLASH_ATTR
|
||||
pwm_init(uint16 freq, uint16 *duty)
|
||||
{
|
||||
uint8 i;
|
||||
|
||||
// PIN_FUNC_SELECT(PWM_0_OUT_IO_MUX, PWM_0_OUT_IO_FUNC);
|
||||
// PIN_FUNC_SELECT(PWM_1_OUT_IO_MUX, PWM_1_OUT_IO_FUNC);
|
||||
// PIN_FUNC_SELECT(PWM_2_OUT_IO_MUX, PWM_2_OUT_IO_FUNC);
|
||||
// GPIO_OUTPUT_SET(GPIO_ID_PIN(PWM_0_OUT_IO_NUM), 0);
|
||||
// GPIO_OUTPUT_SET(GPIO_ID_PIN(PWM_1_OUT_IO_NUM), 0);
|
||||
// GPIO_OUTPUT_SET(GPIO_ID_PIN(PWM_2_OUT_IO_NUM), 0);
|
||||
|
||||
for (i = 0; i < PWM_CHANNEL; i++) {
|
||||
// pwm_gpio |= (1 << pwm_out_io_num[i]);
|
||||
pwm_gpio = 0;
|
||||
pwm.duty[i] = 0;
|
||||
}
|
||||
|
||||
pwm_set_freq(500, 0);
|
||||
// pwm_set_freq_duty(freq, duty);
|
||||
|
||||
pwm_start();
|
||||
|
||||
PWM_DBG("pwm_init returning\n");
|
||||
}
|
||||
|
||||
bool ICACHE_FLASH_ATTR
|
||||
pwm_add(uint8 channel){
|
||||
PWM_DBG("--Function pwm_add() is called. channel:%d\n", channel);
|
||||
PWM_DBG("pwm_gpio:%x,pwm_channel_num:%d\n",pwm_gpio,pwm_channel_num);
|
||||
PWM_DBG("pwm_out_io_num[0]:%d,[1]:%d,[2]:%d\n",pwm_out_io_num[0],pwm_out_io_num[1],pwm_out_io_num[2]);
|
||||
PWM_DBG("pwm.duty[0]:%d,[1]:%d,[2]:%d\n",pwm.duty[0],pwm.duty[1],pwm.duty[2]);
|
||||
uint8 i;
|
||||
for(i=0;i<PWM_CHANNEL;i++){
|
||||
if(pwm_out_io_num[i]==channel) // already exist
|
||||
return true;
|
||||
if(pwm_out_io_num[i] == -1){ // empty exist
|
||||
pwm_out_io_num[i] = channel;
|
||||
pwm.duty[i] = 0;
|
||||
pwm_gpio |= (1 << pin_num[channel]);
|
||||
PIN_FUNC_SELECT(pin_mux[channel], pin_func[channel]);
|
||||
GPIO_REG_WRITE(GPIO_PIN_ADDR(GPIO_ID_PIN(pin_num[channel])), GPIO_REG_READ(GPIO_PIN_ADDR(GPIO_ID_PIN(pin_num[channel]))) & (~ GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_ENABLE))); //disable open drain;
|
||||
pwm_channel_num++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ICACHE_FLASH_ATTR
|
||||
pwm_delete(uint8 channel){
|
||||
PWM_DBG("--Function pwm_delete() is called. channel:%d\n", channel);
|
||||
PWM_DBG("pwm_gpio:%x,pwm_channel_num:%d\n",pwm_gpio,pwm_channel_num);
|
||||
PWM_DBG("pwm_out_io_num[0]:%d,[1]:%d,[2]:%d\n",pwm_out_io_num[0],pwm_out_io_num[1],pwm_out_io_num[2]);
|
||||
PWM_DBG("pwm.duty[0]:%d,[1]:%d,[2]:%d\n",pwm.duty[0],pwm.duty[1],pwm.duty[2]);
|
||||
uint8 i,j;
|
||||
for(i=0;i<pwm_channel_num;i++){
|
||||
if(pwm_out_io_num[i]==channel){ // exist
|
||||
pwm_out_io_num[i] = -1;
|
||||
pwm_gpio &= ~(1 << pin_num[channel]); //clear the bit
|
||||
for(j=i;j<pwm_channel_num-1;j++){
|
||||
pwm_out_io_num[j] = pwm_out_io_num[j+1];
|
||||
pwm.duty[j] = pwm.duty[j+1];
|
||||
}
|
||||
pwm_out_io_num[pwm_channel_num-1] = -1;
|
||||
pwm.duty[pwm_channel_num-1] = 0;
|
||||
pwm_channel_num--;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// non found
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ICACHE_FLASH_ATTR
|
||||
pwm_exist(uint8 channel){
|
||||
PWM_DBG("--Function pwm_exist() is called. channel:%d\n", channel);
|
||||
PWM_DBG("pwm_gpio:%x,pwm_channel_num:%d\n",pwm_gpio,pwm_channel_num);
|
||||
PWM_DBG("pwm_out_io_num[0]:%d,[1]:%d,[2]:%d\n",pwm_out_io_num[0],pwm_out_io_num[1],pwm_out_io_num[2]);
|
||||
PWM_DBG("pwm.duty[0]:%d,[1]:%d,[2]:%d\n",pwm.duty[0],pwm.duty[1],pwm.duty[2]);
|
||||
uint8 i;
|
||||
for(i=0;i<PWM_CHANNEL;i++){
|
||||
if(pwm_out_io_num[i]==channel) // exist
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
@ -1,323 +0,0 @@
|
||||
/*
|
||||
* Driver for interfacing to cheap rotary switches that
|
||||
* have a quadrature output with an optional press button
|
||||
*
|
||||
* This sets up the relevant gpio as interrupt and then keeps track of
|
||||
* the position of the switch in software. Changes are enqueued to task
|
||||
* level and a task message posted when required. If the queue fills up
|
||||
* then moves are ignored, but the last press/release will be included.
|
||||
*
|
||||
* Philip Gladstone, N1DQ
|
||||
*/
|
||||
#ifdef __ESP8266__
|
||||
|
||||
#include "platform.h"
|
||||
#include "c_types.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "driver/rotary.h"
|
||||
#include "user_interface.h"
|
||||
#include "esp_system.h"
|
||||
#include "task/task.h"
|
||||
#include "ets_sys.h"
|
||||
|
||||
//
|
||||
// Queue is empty if read == write.
|
||||
// However, we always want to keep the previous value
|
||||
// so writing is only allowed if write - read < QUEUE_SIZE - 1
|
||||
|
||||
#define QUEUE_SIZE 8
|
||||
|
||||
#define GET_LAST_STATUS(d) (d->queue[(d->write_offset-1) & (QUEUE_SIZE - 1)])
|
||||
#define GET_PREV_STATUS(d) (d->queue[(d->write_offset-2) & (QUEUE_SIZE - 1)])
|
||||
#define HAS_QUEUED_DATA(d) (d->read_offset < d->write_offset)
|
||||
#define HAS_QUEUE_SPACE(d) (d->read_offset + QUEUE_SIZE - 1 > d->write_offset)
|
||||
|
||||
#define REPLACE_STATUS(d, x) (d->queue[(d->write_offset-1) & (QUEUE_SIZE - 1)] = (rotary_event_t) { (x), system_get_time() })
|
||||
#define QUEUE_STATUS(d, x) (d->queue[(d->write_offset++) & (QUEUE_SIZE - 1)] = (rotary_event_t) { (x), system_get_time() })
|
||||
|
||||
#define GET_READ_STATUS(d) (d->queue[d->read_offset & (QUEUE_SIZE - 1)])
|
||||
#define ADVANCE_IF_POSSIBLE(d) if (d->read_offset < d->write_offset) { d->read_offset++; }
|
||||
|
||||
#define STATUS_IS_PRESSED(x) ((x & 0x80000000) != 0)
|
||||
|
||||
typedef struct {
|
||||
int8_t phase_a_pin;
|
||||
int8_t phase_b_pin;
|
||||
int8_t press_pin;
|
||||
uint32_t read_offset; // Accessed by task
|
||||
uint32_t write_offset; // Accessed by ISR
|
||||
uint32_t pin_mask;
|
||||
uint32_t phase_a;
|
||||
uint32_t phase_b;
|
||||
uint32_t press;
|
||||
uint32_t last_press_change_time;
|
||||
int tasknumber;
|
||||
rotary_event_t queue[QUEUE_SIZE];
|
||||
} DATA;
|
||||
|
||||
static DATA *data[ROTARY_CHANNEL_COUNT];
|
||||
|
||||
static uint8_t task_queued;
|
||||
|
||||
static void set_gpio_bits(void);
|
||||
|
||||
static void rotary_clear_pin(int pin)
|
||||
{
|
||||
if (pin >= 0) {
|
||||
gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[pin]), GPIO_PIN_INTR_DISABLE);
|
||||
platform_gpio_mode(pin, PLATFORM_GPIO_INPUT, PLATFORM_GPIO_PULLUP);
|
||||
}
|
||||
}
|
||||
|
||||
// Just takes the channel number. Cleans up the resources used.
|
||||
int rotary_close(uint32_t channel)
|
||||
{
|
||||
if (channel >= sizeof(data) / sizeof(data[0])) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
DATA *d = data[channel];
|
||||
|
||||
if (!d) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
data[channel] = NULL;
|
||||
|
||||
rotary_clear_pin(d->phase_a_pin);
|
||||
rotary_clear_pin(d->phase_b_pin);
|
||||
rotary_clear_pin(d->press_pin);
|
||||
|
||||
free(d);
|
||||
|
||||
set_gpio_bits();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t ICACHE_RAM_ATTR rotary_interrupt(uint32_t ret_gpio_status)
|
||||
{
|
||||
// This function really is running at interrupt level with everything
|
||||
// else masked off. It should take as little time as necessary.
|
||||
//
|
||||
//
|
||||
|
||||
// This gets the set of pins which have changed status
|
||||
uint32 gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < sizeof(data) / sizeof(data[0]); i++) {
|
||||
DATA *d = data[i];
|
||||
if (!d || (gpio_status & d->pin_mask) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status & d->pin_mask);
|
||||
|
||||
uint32_t bits = GPIO_REG_READ(GPIO_IN_ADDRESS);
|
||||
|
||||
uint32_t last_status = GET_LAST_STATUS(d).pos;
|
||||
|
||||
uint32_t now = system_get_time();
|
||||
|
||||
uint32_t new_status;
|
||||
|
||||
new_status = last_status & 0x80000000;
|
||||
|
||||
// This is the debounce logic for the press switch. We ignore changes
|
||||
// for 10ms after a change.
|
||||
if (now - d->last_press_change_time > 10 * 1000) {
|
||||
new_status = (bits & d->press) ? 0 : 0x80000000;
|
||||
if (STATUS_IS_PRESSED(new_status ^ last_status)) {
|
||||
d->last_press_change_time = now;
|
||||
}
|
||||
}
|
||||
|
||||
// A B
|
||||
// 1 1 => 0
|
||||
// 1 0 => 1
|
||||
// 0 0 => 2
|
||||
// 0 1 => 3
|
||||
|
||||
int micropos = 2;
|
||||
if (bits & d->phase_b) {
|
||||
micropos = 3;
|
||||
}
|
||||
if (bits & d->phase_a) {
|
||||
micropos ^= 3;
|
||||
}
|
||||
|
||||
int32_t rotary_pos = last_status;
|
||||
|
||||
switch ((micropos - last_status) & 3) {
|
||||
case 0:
|
||||
// No change, nothing to do
|
||||
break;
|
||||
case 1:
|
||||
// Incremented by 1
|
||||
rotary_pos++;
|
||||
break;
|
||||
case 3:
|
||||
// Decremented by 1
|
||||
rotary_pos--;
|
||||
break;
|
||||
default:
|
||||
// We missed an interrupt
|
||||
// We will ignore... but mark it.
|
||||
rotary_pos += 1000000;
|
||||
break;
|
||||
}
|
||||
|
||||
new_status |= rotary_pos & 0x7fffffff;
|
||||
|
||||
if (last_status != new_status) {
|
||||
// Either we overwrite the status or we add a new one
|
||||
if (!HAS_QUEUED_DATA(d)
|
||||
|| STATUS_IS_PRESSED(last_status ^ new_status)
|
||||
|| STATUS_IS_PRESSED(last_status ^ GET_PREV_STATUS(d).pos)) {
|
||||
if (HAS_QUEUE_SPACE(d)) {
|
||||
QUEUE_STATUS(d, new_status);
|
||||
if (!task_queued) {
|
||||
if (task_post_medium(d->tasknumber, (task_param_t) &task_queued)) {
|
||||
task_queued = 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
REPLACE_STATUS(d, new_status);
|
||||
}
|
||||
} else {
|
||||
REPLACE_STATUS(d, new_status);
|
||||
}
|
||||
}
|
||||
ret_gpio_status &= ~(d->pin_mask);
|
||||
}
|
||||
|
||||
return ret_gpio_status;
|
||||
}
|
||||
|
||||
// The pin numbers are actual platform GPIO numbers
|
||||
int rotary_setup(uint32_t channel, int phase_a, int phase_b, int press, task_handle_t tasknumber )
|
||||
{
|
||||
if (channel >= sizeof(data) / sizeof(data[0])) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (data[channel]) {
|
||||
if (rotary_close(channel)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
DATA *d = (DATA *) zalloc(sizeof(DATA));
|
||||
if (!d) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
data[channel] = d;
|
||||
int i;
|
||||
|
||||
d->tasknumber = tasknumber;
|
||||
|
||||
d->phase_a = 1 << pin_num[phase_a];
|
||||
platform_gpio_mode(phase_a, PLATFORM_GPIO_INT, PLATFORM_GPIO_PULLUP);
|
||||
gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[phase_a]), GPIO_PIN_INTR_ANYEDGE);
|
||||
d->phase_a_pin = phase_a;
|
||||
|
||||
d->phase_b = 1 << pin_num[phase_b];
|
||||
platform_gpio_mode(phase_b, PLATFORM_GPIO_INT, PLATFORM_GPIO_PULLUP);
|
||||
gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[phase_b]), GPIO_PIN_INTR_ANYEDGE);
|
||||
d->phase_b_pin = phase_b;
|
||||
|
||||
if (press >= 0) {
|
||||
d->press = 1 << pin_num[press];
|
||||
platform_gpio_mode(press, PLATFORM_GPIO_INT, PLATFORM_GPIO_PULLUP);
|
||||
gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[press]), GPIO_PIN_INTR_ANYEDGE);
|
||||
}
|
||||
d->press_pin = press;
|
||||
|
||||
d->pin_mask = d->phase_a | d->phase_b | d->press;
|
||||
|
||||
set_gpio_bits();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void set_gpio_bits()
|
||||
{
|
||||
uint32_t bits = 0;
|
||||
for (int i = 0; i < ROTARY_CHANNEL_COUNT; i++) {
|
||||
DATA *d = data[i];
|
||||
|
||||
if (d) {
|
||||
bits = bits | d->pin_mask;
|
||||
}
|
||||
}
|
||||
|
||||
platform_gpio_register_intr_hook(bits, rotary_interrupt);
|
||||
}
|
||||
|
||||
bool rotary_has_queued_event(uint32_t channel)
|
||||
{
|
||||
if (channel >= sizeof(data) / sizeof(data[0])) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DATA *d = data[channel];
|
||||
|
||||
if (!d) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return HAS_QUEUED_DATA(d);
|
||||
}
|
||||
|
||||
// Get the oldest event in the queue and remove it (if possible)
|
||||
bool rotary_getevent(uint32_t channel, rotary_event_t *resultp)
|
||||
{
|
||||
rotary_event_t result = { 0 };
|
||||
|
||||
if (channel >= sizeof(data) / sizeof(data[0])) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DATA *d = data[channel];
|
||||
|
||||
if (!d) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ETS_GPIO_INTR_DISABLE();
|
||||
|
||||
bool status = FALSE;
|
||||
|
||||
if (HAS_QUEUED_DATA(d)) {
|
||||
result = GET_READ_STATUS(d);
|
||||
d->read_offset++;
|
||||
status = TRUE;
|
||||
} else {
|
||||
result = GET_LAST_STATUS(d);
|
||||
}
|
||||
|
||||
ETS_GPIO_INTR_ENABLE();
|
||||
|
||||
*resultp = result;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int rotary_getpos(uint32_t channel)
|
||||
{
|
||||
if (channel >= sizeof(data) / sizeof(data[0])) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
DATA *d = data[channel];
|
||||
|
||||
if (!d) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return GET_LAST_STATUS(d).pos;
|
||||
}
|
||||
#endif
|
@ -1,38 +0,0 @@
|
||||
/* Sigma-delta only on the ESP8266 */
|
||||
#ifdef __ESP8266__
|
||||
|
||||
#include "driver/sigma_delta.h"
|
||||
#include "esp8266/gpio_register.h"
|
||||
|
||||
|
||||
void sigma_delta_setup( void )
|
||||
{
|
||||
GPIO_REG_WRITE(GPIO_SIGMA_DELTA,
|
||||
GPIO_SIGMA_DELTA_SET(GPIO_SIGMA_DELTA_ENABLE) |
|
||||
GPIO_SIGMA_DELTA_TARGET_SET(0x00) |
|
||||
GPIO_SIGMA_DELTA_PRESCALE_SET(0x00));
|
||||
}
|
||||
|
||||
void sigma_delta_stop( void )
|
||||
{
|
||||
GPIO_REG_WRITE(GPIO_SIGMA_DELTA,
|
||||
GPIO_SIGMA_DELTA_SET(GPIO_SIGMA_DELTA_DISABLE) |
|
||||
GPIO_SIGMA_DELTA_TARGET_SET(0x00) |
|
||||
GPIO_SIGMA_DELTA_PRESCALE_SET(0x00) );
|
||||
}
|
||||
|
||||
void sigma_delta_set_prescale_target( sint16 prescale, sint16 target )
|
||||
{
|
||||
uint32_t prescale_mask, target_mask;
|
||||
|
||||
prescale_mask = prescale >= 0 ? GPIO_SIGMA_DELTA_PRESCALE_MASK : 0x00;
|
||||
target_mask = target >= 0 ? GPIO_SIGMA_DELTA_TARGET_MASK : 0x00;
|
||||
|
||||
// set prescale and target with one register access to avoid glitches
|
||||
GPIO_REG_WRITE(GPIO_SIGMA_DELTA,
|
||||
(GPIO_REG_READ(GPIO_SIGMA_DELTA) & ~(prescale_mask | target_mask)) |
|
||||
(GPIO_SIGMA_DELTA_PRESCALE_SET(prescale) & prescale_mask) |
|
||||
(GPIO_SIGMA_DELTA_TARGET_SET(target) & target_mask));
|
||||
}
|
||||
|
||||
#endif
|
681
app/driver/spi.c
681
app/driver/spi.c
@ -1,681 +0,0 @@
|
||||
#ifdef __ESP8266__
|
||||
#include "driver/spi.h"
|
||||
#include "platform.h"
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : spi_lcd_mode_init
|
||||
* Description : SPI master initial function for driving LCD TM035PDZV36
|
||||
* Parameters : uint8 spi_no - SPI module number, Only "SPI" and "HSPI" are valid
|
||||
*******************************************************************************/
|
||||
void spi_lcd_mode_init(uint8 spi_no)
|
||||
{
|
||||
uint32 regvalue;
|
||||
if(spi_no>1) return; //handle invalid input number
|
||||
//bit9 of PERIPHS_IO_MUX should be cleared when HSPI clock doesn't equal CPU clock
|
||||
//bit8 of PERIPHS_IO_MUX should be cleared when SPI clock doesn't equal CPU clock
|
||||
if(spi_no==SPI){
|
||||
WRITE_PERI_REG(PERIPHS_IO_MUX, 0x005); //clear bit9,and bit8
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, 1);//configure io to spi mode
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CMD_U, 1);//configure io to spi mode
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA0_U, 1);//configure io to spi mode
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA1_U, 1);//configure io to spi mode
|
||||
}else if(spi_no==HSPI){
|
||||
WRITE_PERI_REG(PERIPHS_IO_MUX, 0x105); //clear bit9
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, 2);//configure io to spi mode
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 2);//configure io to spi mode
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, 2);//configure io to spi mode
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 2);//configure io to spi mode
|
||||
}
|
||||
|
||||
SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_CS_SETUP|SPI_CS_HOLD|SPI_USR_COMMAND);
|
||||
CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_FLASH_MODE);
|
||||
// SPI clock=CPU clock/8
|
||||
WRITE_PERI_REG(SPI_CLOCK(spi_no),
|
||||
((1&SPI_CLKDIV_PRE)<<SPI_CLKDIV_PRE_S)|
|
||||
((3&SPI_CLKCNT_N)<<SPI_CLKCNT_N_S)|
|
||||
((1&SPI_CLKCNT_H)<<SPI_CLKCNT_H_S)|
|
||||
((3&SPI_CLKCNT_L)<<SPI_CLKCNT_L_S)); //clear bit 31,set SPI clock div
|
||||
|
||||
}
|
||||
/******************************************************************************
|
||||
* FunctionName : spi_lcd_9bit_write
|
||||
* Description : SPI 9bits transmission function for driving LCD TM035PDZV36
|
||||
* Parameters : uint8 spi_no - SPI module number, Only "SPI" and "HSPI" are valid
|
||||
* uint8 high_bit - first high bit of the data, 0 is for "0",the other value 1-255 is for "1"
|
||||
* uint8 low_8bit- the rest 8bits of the data.
|
||||
*******************************************************************************/
|
||||
void spi_lcd_9bit_write(uint8 spi_no,uint8 high_bit,uint8 low_8bit)
|
||||
{
|
||||
uint32 regvalue;
|
||||
uint8 bytetemp;
|
||||
if(spi_no>1) return; //handle invalid input number
|
||||
|
||||
if(high_bit) bytetemp=(low_8bit>>1)|0x80;
|
||||
else bytetemp=(low_8bit>>1)&0x7f;
|
||||
|
||||
regvalue= ((8&SPI_USR_COMMAND_BITLEN)<<SPI_USR_COMMAND_BITLEN_S)|((uint32)bytetemp); //configure transmission variable,9bit transmission length and first 8 command bit
|
||||
if(low_8bit&0x01) regvalue|=BIT15; //write the 9th bit
|
||||
while(READ_PERI_REG(SPI_CMD(spi_no))&SPI_USR); //waiting for spi module available
|
||||
WRITE_PERI_REG(SPI_USER2(spi_no), regvalue); //write command and command length into spi reg
|
||||
SET_PERI_REG_MASK(SPI_CMD(spi_no), SPI_USR); //transmission start
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : spi_master_init
|
||||
* Description : SPI master initial function for common byte units transmission
|
||||
* Parameters : uint8 spi_no - SPI module number, Only "SPI" and "HSPI" are valid
|
||||
*******************************************************************************/
|
||||
void spi_master_init(uint8 spi_no, unsigned cpol, unsigned cpha, uint32_t clock_div)
|
||||
{
|
||||
uint32 regvalue;
|
||||
|
||||
if(spi_no>1) return; //handle invalid input number
|
||||
|
||||
SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_CS_SETUP|SPI_CS_HOLD|SPI_RD_BYTE_ORDER|SPI_WR_BYTE_ORDER|SPI_DOUTDIN);
|
||||
|
||||
// set clock polarity (Reference: http://bbs.espressif.com/viewtopic.php?f=49&t=1570)
|
||||
// phase is dependent on polarity. See Issue #1161
|
||||
if (cpol == 1) {
|
||||
SET_PERI_REG_MASK(SPI_PIN(spi_no), SPI_IDLE_EDGE);
|
||||
} else {
|
||||
CLEAR_PERI_REG_MASK(SPI_PIN(spi_no), SPI_IDLE_EDGE);
|
||||
}
|
||||
|
||||
//set clock phase
|
||||
if (cpha == cpol) {
|
||||
// Mode 3: MOSI is set on falling edge of clock
|
||||
// Mode 0: MOSI is set on falling edge of clock
|
||||
CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_CK_OUT_EDGE);
|
||||
SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_CK_I_EDGE);
|
||||
} else {
|
||||
// Mode 2: MOSI is set on rising edge of clock
|
||||
// Mode 1: MOSI is set on rising edge of clock
|
||||
SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_CK_OUT_EDGE);
|
||||
CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_CK_I_EDGE);
|
||||
}
|
||||
|
||||
CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_FLASH_MODE|SPI_USR_MISO|SPI_USR_ADDR|SPI_USR_COMMAND|SPI_USR_DUMMY);
|
||||
|
||||
//clear Dual or Quad lines transmission mode
|
||||
CLEAR_PERI_REG_MASK(SPI_CTRL(spi_no), SPI_QIO_MODE|SPI_DIO_MODE|SPI_DOUT_MODE|SPI_QOUT_MODE);
|
||||
|
||||
// SPI clock = CPU clock / clock_div
|
||||
// the divider needs to be a multiple of 2 to get a proper waveform shape
|
||||
if ((clock_div & 0x01) != 0) {
|
||||
// bump the divider to the next N*2
|
||||
clock_div += 0x02;
|
||||
}
|
||||
clock_div >>= 1;
|
||||
// clip to maximum possible CLKDIV_PRE
|
||||
clock_div = clock_div > SPI_CLKDIV_PRE ? SPI_CLKDIV_PRE : clock_div - 1;
|
||||
|
||||
WRITE_PERI_REG(SPI_CLOCK(spi_no),
|
||||
((clock_div&SPI_CLKDIV_PRE)<<SPI_CLKDIV_PRE_S)|
|
||||
((1&SPI_CLKCNT_N)<<SPI_CLKCNT_N_S)|
|
||||
((0&SPI_CLKCNT_H)<<SPI_CLKCNT_H_S)|
|
||||
((1&SPI_CLKCNT_L)<<SPI_CLKCNT_L_S)); //clear bit 31,set SPI clock div
|
||||
|
||||
if(spi_no==SPI){
|
||||
WRITE_PERI_REG(PERIPHS_IO_MUX, 0x005);
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, 1);//configure io to spi mode
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CMD_U, 1);//configure io to spi mode
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA0_U, 1);//configure io to spi mode
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA1_U, 1);//configure io to spi mode
|
||||
}
|
||||
else if(spi_no==HSPI){
|
||||
WRITE_PERI_REG(PERIPHS_IO_MUX, 0x105);
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, 2);//configure io to spi mode
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 2);//configure io to spi mode
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, 2);//configure io to spi mode
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 2);//configure io to spi mode
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : spi_mast_set_mosi
|
||||
* Description : Enter provided data into MOSI buffer.
|
||||
* The data is regarded as a sequence of bits with length 'bitlen'.
|
||||
* It will be written left-aligned starting from position 'offset'.
|
||||
* Parameters : uint8 spi_no - SPI module number, Only "SPI" and "HSPI" are valid
|
||||
* uint16 offset - offset into MOSI buffer (number of bits)
|
||||
* uint8 bitlen - valid number of bits in data
|
||||
* uint32 data - data to be written into buffer
|
||||
*******************************************************************************/
|
||||
void spi_mast_set_mosi(uint8 spi_no, uint16 offset, uint8 bitlen, uint32 data)
|
||||
{
|
||||
uint8 wn, wn_offset, wn_bitlen;
|
||||
uint32 wn_data;
|
||||
|
||||
if (spi_no > 1)
|
||||
return; // handle invalid input number
|
||||
if (bitlen > 32)
|
||||
return; // handle invalid input number
|
||||
|
||||
while(READ_PERI_REG(SPI_CMD(spi_no)) & SPI_USR);
|
||||
|
||||
// determine which SPI_Wn register is addressed
|
||||
wn = offset >> 5;
|
||||
if (wn > 15)
|
||||
return; // out of range
|
||||
wn_offset = offset & 0x1f;
|
||||
if (32 - wn_offset < bitlen)
|
||||
{
|
||||
// splitting required
|
||||
wn_bitlen = 32 - wn_offset;
|
||||
wn_data = data >> (bitlen - wn_bitlen);
|
||||
}
|
||||
else
|
||||
{
|
||||
wn_bitlen = bitlen;
|
||||
wn_data = data;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
// write payload data to SPI_Wn
|
||||
SET_PERI_REG_BITS(REG_SPI_BASE(spi_no) +0x40 + wn*4, BIT(wn_bitlen) - 1, wn_data, 32 - (wn_offset + wn_bitlen));
|
||||
|
||||
// prepare writing of dangling data part
|
||||
wn += 1;
|
||||
wn_offset = 0;
|
||||
if (wn <= 15)
|
||||
bitlen -= wn_bitlen;
|
||||
else
|
||||
bitlen = 0; // force abort
|
||||
wn_bitlen = bitlen;
|
||||
wn_data = data;
|
||||
} while (bitlen > 0);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : spi_mast_get_miso
|
||||
* Description : Retrieve data from MISO buffer.
|
||||
* The data is regarded as a sequence of bits with length 'bitlen'.
|
||||
* It will be read starting left-aligned from position 'offset'.
|
||||
* Parameters : uint8 spi_no - SPI module number, Only "SPI" and "HSPI" are valid
|
||||
* uint16 offset - offset into MISO buffer (number of bits)
|
||||
* uint8 bitlen - requested number of bits in data
|
||||
*******************************************************************************/
|
||||
uint32 spi_mast_get_miso(uint8 spi_no, uint16 offset, uint8 bitlen)
|
||||
{
|
||||
uint8 wn, wn_offset, wn_bitlen;
|
||||
uint32 wn_data = 0;
|
||||
|
||||
if (spi_no > 1)
|
||||
return 0; // handle invalid input number
|
||||
|
||||
while(READ_PERI_REG(SPI_CMD(spi_no)) & SPI_USR);
|
||||
|
||||
// determine which SPI_Wn register is addressed
|
||||
wn = offset >> 5;
|
||||
if (wn > 15)
|
||||
return 0; // out of range
|
||||
wn_offset = offset & 0x1f;
|
||||
|
||||
if (bitlen > (32 - wn_offset))
|
||||
{
|
||||
// splitting required
|
||||
wn_bitlen = 32 - wn_offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
wn_bitlen = bitlen;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
wn_data |= (READ_PERI_REG(REG_SPI_BASE(spi_no) +0x40 + wn*4) >> (32 - (wn_offset + wn_bitlen))) & (BIT(wn_bitlen) - 1);
|
||||
|
||||
// prepare reading of dangling data part
|
||||
wn_data <<= bitlen - wn_bitlen;
|
||||
wn += 1;
|
||||
wn_offset = 0;
|
||||
if (wn <= 15)
|
||||
bitlen -= wn_bitlen;
|
||||
else
|
||||
bitlen = 0; // force abort
|
||||
wn_bitlen = bitlen;
|
||||
} while (bitlen > 0);
|
||||
|
||||
return wn_data;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : spi_mast_transaction
|
||||
* Description : Start a transaction and wait for completion.
|
||||
* Parameters : uint8 spi_no - SPI module number, Only "SPI" and "HSPI" are valid
|
||||
* uint8 cmd_bitlen - Valid number of bits in cmd_data.
|
||||
* uint16 cmd_data - Command data.
|
||||
* uint8 addr_bitlen - Valid number of bits in addr_data.
|
||||
* uint32 addr_data - Address data.
|
||||
* uint16 mosi_bitlen - Valid number of bits in MOSI buffer.
|
||||
* uint8 dummy_bitlen - Number of dummy cycles.
|
||||
* sint16 miso_bitlen - number of bits to be captured in MISO buffer.
|
||||
* negative value activates full-duplex mode.
|
||||
*******************************************************************************/
|
||||
void spi_mast_transaction(uint8 spi_no, uint8 cmd_bitlen, uint16 cmd_data, uint8 addr_bitlen, uint32 addr_data,
|
||||
uint16 mosi_bitlen, uint8 dummy_bitlen, sint16 miso_bitlen)
|
||||
{
|
||||
if (spi_no > 1)
|
||||
return; // handle invalid input number
|
||||
|
||||
while(READ_PERI_REG(SPI_CMD(spi_no)) & SPI_USR);
|
||||
|
||||
// default disable COMMAND, ADDR, MOSI, DUMMY, MISO, and DOUTDIN (aka full-duplex)
|
||||
CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_COMMAND|SPI_USR_ADDR|SPI_USR_MOSI|SPI_USR_DUMMY|SPI_USR_MISO|SPI_DOUTDIN);
|
||||
// default set bit lengths
|
||||
WRITE_PERI_REG(SPI_USER1(spi_no),
|
||||
((addr_bitlen - 1) & SPI_USR_ADDR_BITLEN) << SPI_USR_ADDR_BITLEN_S |
|
||||
((mosi_bitlen - 1) & SPI_USR_MOSI_BITLEN) << SPI_USR_MOSI_BITLEN_S |
|
||||
((dummy_bitlen - 1) & SPI_USR_DUMMY_CYCLELEN) << SPI_USR_DUMMY_CYCLELEN_S |
|
||||
((miso_bitlen - 1) & SPI_USR_MISO_BITLEN) << SPI_USR_MISO_BITLEN_S);
|
||||
|
||||
// handle the transaction components
|
||||
if (cmd_bitlen > 0)
|
||||
{
|
||||
uint16 cmd = cmd_data << (16 - cmd_bitlen); // align to MSB
|
||||
cmd = (cmd >> 8) | (cmd << 8); // swap byte order
|
||||
WRITE_PERI_REG(SPI_USER2(spi_no),
|
||||
((cmd_bitlen - 1 & SPI_USR_COMMAND_BITLEN) << SPI_USR_COMMAND_BITLEN_S) |
|
||||
(cmd & SPI_USR_COMMAND_VALUE));
|
||||
SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_COMMAND);
|
||||
}
|
||||
if (addr_bitlen > 0)
|
||||
{
|
||||
WRITE_PERI_REG(SPI_ADDR(spi_no), addr_data << (32 - addr_bitlen));
|
||||
SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_ADDR);
|
||||
}
|
||||
if (mosi_bitlen > 0)
|
||||
{
|
||||
SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MOSI);
|
||||
}
|
||||
if (dummy_bitlen > 0)
|
||||
{
|
||||
SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_DUMMY);
|
||||
}
|
||||
if (miso_bitlen > 0)
|
||||
{
|
||||
SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MISO);
|
||||
}
|
||||
else if (miso_bitlen < 0)
|
||||
{
|
||||
SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_DOUTDIN);
|
||||
}
|
||||
|
||||
// start transaction
|
||||
SET_PERI_REG_MASK(SPI_CMD(spi_no), SPI_USR);
|
||||
|
||||
while(READ_PERI_REG(SPI_CMD(spi_no)) & SPI_USR);
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : spi_byte_write_espslave
|
||||
* Description : SPI master 1 byte transmission function for esp8266 slave,
|
||||
* transmit 1byte data to esp8266 slave buffer needs 16bit transmission ,
|
||||
* first byte is command 0x04 to write slave buffer, second byte is data
|
||||
* Parameters : uint8 spi_no - SPI module number, Only "SPI" and "HSPI" are valid
|
||||
* uint8 data- transmitted data
|
||||
*******************************************************************************/
|
||||
void spi_byte_write_espslave(uint8 spi_no,uint8 data)
|
||||
{
|
||||
uint32 regvalue;
|
||||
|
||||
if(spi_no>1) return; //handle invalid input number
|
||||
|
||||
while(READ_PERI_REG(SPI_CMD(spi_no))&SPI_USR);
|
||||
SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MOSI);
|
||||
CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MISO|SPI_USR_ADDR|SPI_USR_DUMMY);
|
||||
|
||||
//SPI_FLASH_USER2 bit28-31 is cmd length,cmd bit length is value(0-15)+1,
|
||||
// bit15-0 is cmd value.
|
||||
//0x70000000 is for 8bits cmd, 0x04 is eps8266 slave write cmd value
|
||||
WRITE_PERI_REG(SPI_USER2(spi_no),
|
||||
((7&SPI_USR_COMMAND_BITLEN)<<SPI_USR_COMMAND_BITLEN_S)|4);
|
||||
WRITE_PERI_REG(SPI_W0(spi_no), (uint32)(data));
|
||||
SET_PERI_REG_MASK(SPI_CMD(spi_no), SPI_USR);
|
||||
}
|
||||
/******************************************************************************
|
||||
* FunctionName : spi_byte_read_espslave
|
||||
* Description : SPI master 1 byte read function for esp8266 slave,
|
||||
* read 1byte data from esp8266 slave buffer needs 16bit transmission ,
|
||||
* first byte is command 0x06 to read slave buffer, second byte is recieved data
|
||||
* Parameters : uint8 spi_no - SPI module number, Only "SPI" and "HSPI" are valid
|
||||
* uint8* data- recieved data address
|
||||
*******************************************************************************/
|
||||
void spi_byte_read_espslave(uint8 spi_no,uint8 *data)
|
||||
{
|
||||
uint32 regvalue;
|
||||
|
||||
if(spi_no>1) return; //handle invalid input number
|
||||
|
||||
while(READ_PERI_REG(SPI_CMD(spi_no))&SPI_USR);
|
||||
|
||||
SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MISO);
|
||||
CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MOSI|SPI_USR_ADDR|SPI_USR_DUMMY);
|
||||
//SPI_FLASH_USER2 bit28-31 is cmd length,cmd bit length is value(0-15)+1,
|
||||
// bit15-0 is cmd value.
|
||||
//0x70000000 is for 8bits cmd, 0x06 is eps8266 slave read cmd value
|
||||
WRITE_PERI_REG(SPI_USER2(spi_no),
|
||||
((7&SPI_USR_COMMAND_BITLEN)<<SPI_USR_COMMAND_BITLEN_S)|6);
|
||||
SET_PERI_REG_MASK(SPI_CMD(spi_no), SPI_USR);
|
||||
|
||||
while(READ_PERI_REG(SPI_CMD(spi_no))&SPI_USR);
|
||||
*data=(uint8)(READ_PERI_REG(SPI_W0(spi_no))&0xff);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : spi_slave_init
|
||||
* Description : SPI slave mode initial funtion, including mode setting,
|
||||
* IO setting, transmission interrupt opening, interrupt function registration
|
||||
* Parameters : uint8 spi_no - SPI module number, Only "SPI" and "HSPI" are valid
|
||||
*******************************************************************************/
|
||||
void spi_slave_init(uint8 spi_no)
|
||||
{
|
||||
uint32 regvalue;
|
||||
if(spi_no>1)
|
||||
return; //handle invalid input number
|
||||
|
||||
//clear bit9,bit8 of reg PERIPHS_IO_MUX
|
||||
//bit9 should be cleared when HSPI clock doesn't equal CPU clock
|
||||
//bit8 should be cleared when SPI clock doesn't equal CPU clock
|
||||
////WRITE_PERI_REG(PERIPHS_IO_MUX, 0x105); //clear bit9//TEST
|
||||
if(spi_no==SPI){
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, 1);//configure io to spi mode
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CMD_U, 1);//configure io to spi mode
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA0_U, 1);//configure io to spi mode
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA1_U, 1);//configure io to spi mode
|
||||
}else if(spi_no==HSPI){
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, 2);//configure io to spi mode
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 2);//configure io to spi mode
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, 2);//configure io to spi mode
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 2);//configure io to spi mode
|
||||
}
|
||||
|
||||
//regvalue=READ_PERI_REG(SPI_FLASH_SLAVE(spi_no));
|
||||
//slave mode,slave use buffers which are register "SPI_FLASH_C0~C15", enable trans done isr
|
||||
//set bit 30 bit 29 bit9,bit9 is trans done isr mask
|
||||
SET_PERI_REG_MASK( SPI_SLAVE(spi_no),
|
||||
SPI_SLAVE_MODE|SPI_SLV_WR_RD_BUF_EN|
|
||||
SPI_SLV_WR_BUF_DONE_EN|SPI_SLV_RD_BUF_DONE_EN|
|
||||
SPI_SLV_WR_STA_DONE_EN|SPI_SLV_RD_STA_DONE_EN|
|
||||
SPI_TRANS_DONE_EN);
|
||||
//disable general trans intr
|
||||
//CLEAR_PERI_REG_MASK(SPI_SLAVE(spi_no),SPI_TRANS_DONE_EN);
|
||||
|
||||
CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_FLASH_MODE);//disable flash operation mode
|
||||
SET_PERI_REG_MASK(SPI_USER(spi_no),SPI_USR_MISO_HIGHPART);//SLAVE SEND DATA BUFFER IN C8-C15
|
||||
|
||||
|
||||
//////**************RUN WHEN SLAVE RECIEVE*******************///////
|
||||
//tow lines below is to configure spi timing.
|
||||
SET_PERI_REG_MASK(SPI_CTRL2(spi_no),(0x2&SPI_MOSI_DELAY_NUM)<<SPI_MOSI_DELAY_NUM_S) ;//delay num
|
||||
os_printf("SPI_CTRL2 is %08x\n",READ_PERI_REG(SPI_CTRL2(spi_no)));
|
||||
WRITE_PERI_REG(SPI_CLOCK(spi_no), 0);
|
||||
|
||||
|
||||
|
||||
/////***************************************************//////
|
||||
|
||||
//set 8 bit slave command length, because slave must have at least one bit addr,
|
||||
//8 bit slave+8bit addr, so master device first 2 bytes can be regarded as a command
|
||||
//and the following bytes are datas,
|
||||
//32 bytes input wil be stored in SPI_FLASH_C0-C7
|
||||
//32 bytes output data should be set to SPI_FLASH_C8-C15
|
||||
WRITE_PERI_REG(SPI_USER2(spi_no), (0x7&SPI_USR_COMMAND_BITLEN)<<SPI_USR_COMMAND_BITLEN_S); //0x70000000
|
||||
|
||||
//set 8 bit slave recieve buffer length, the buffer is SPI_FLASH_C0-C7
|
||||
//set 8 bit slave status register, which is the low 8 bit of register "SPI_FLASH_STATUS"
|
||||
SET_PERI_REG_MASK(SPI_SLAVE1(spi_no), ((0xff&SPI_SLV_BUF_BITLEN)<< SPI_SLV_BUF_BITLEN_S)|
|
||||
((0x7&SPI_SLV_STATUS_BITLEN)<<SPI_SLV_STATUS_BITLEN_S)|
|
||||
((0x7&SPI_SLV_WR_ADDR_BITLEN)<<SPI_SLV_WR_ADDR_BITLEN_S)|
|
||||
((0x7&SPI_SLV_RD_ADDR_BITLEN)<<SPI_SLV_RD_ADDR_BITLEN_S));
|
||||
|
||||
SET_PERI_REG_MASK(SPI_PIN(spi_no),BIT19);//BIT19
|
||||
|
||||
//maybe enable slave transmission liston
|
||||
SET_PERI_REG_MASK(SPI_CMD(spi_no),SPI_USR);
|
||||
//register level2 isr function, which contains spi, hspi and i2s events
|
||||
ETS_SPI_INTR_ATTACH(spi_slave_isr_handler,NULL);
|
||||
//enable level2 isr, which contains spi, hspi and i2s events
|
||||
ETS_SPI_INTR_ENABLE();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* =============================================================================================
|
||||
* code below is for spi slave r/w testcase with 2 r/w state lines connected to the spi master mcu
|
||||
* replace with your own process functions
|
||||
* find "add system_os_post here" in spi_slave_isr_handler.
|
||||
* =============================================================================================
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef SPI_SLAVE_DEBUG
|
||||
/******************************************************************************
|
||||
* FunctionName : hspi_master_readwrite_repeat
|
||||
* Description : SPI master test function for reading and writing esp8266 slave buffer,
|
||||
the function uses HSPI module
|
||||
*******************************************************************************/
|
||||
os_timer_t timer2;
|
||||
|
||||
void hspi_master_readwrite_repeat(void)
|
||||
{
|
||||
static uint8 data=0;
|
||||
uint8 temp;
|
||||
|
||||
os_timer_disarm(&timer2);
|
||||
spi_byte_read_espslave(HSPI,&temp);
|
||||
|
||||
temp++;
|
||||
spi_byte_write_espslave(HSPI,temp);
|
||||
os_timer_setfn(&timer2, (os_timer_func_t *)hspi_master_readwrite_repeat, NULL);
|
||||
os_timer_arm(&timer2, 500, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : spi_slave_isr_handler
|
||||
* Description : SPI interrupt function, SPI HSPI and I2S interrupt can trig this function
|
||||
some basic operation like clear isr flag has been done,
|
||||
and it is availible for adding user coder in the funtion
|
||||
* Parameters : void *para- function parameter address, which has been registered in function spi_slave_init
|
||||
*******************************************************************************/
|
||||
#include "gpio.h"
|
||||
#include "user_interface.h"
|
||||
#include "mem.h"
|
||||
static uint8 spi_data[32] = {0};
|
||||
static uint8 idx = 0;
|
||||
static uint8 spi_flg = 0;
|
||||
#define SPI_MISO
|
||||
#define SPI_QUEUE_LEN 8
|
||||
#define MOSI 0
|
||||
#define MISO 1
|
||||
#define STATUS_R_IN_WR 2
|
||||
#define STATUS_W 3
|
||||
#define TR_DONE_ALONE 4
|
||||
#define WR_RD 5
|
||||
#define DATA_ERROR 6
|
||||
#define STATUS_R_IN_RD 7
|
||||
//init the two intr line of slave
|
||||
//gpio0: wr_ready ,and
|
||||
//gpio2: rd_ready , controlled by slave
|
||||
void ICACHE_FLASH_ATTR
|
||||
gpio_init()
|
||||
{
|
||||
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0);
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);
|
||||
//PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO4_U, FUNC_GPIO4);
|
||||
GPIO_OUTPUT_SET(0, 1);
|
||||
GPIO_OUTPUT_SET(2, 0);
|
||||
//GPIO_OUTPUT_SET(4, 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void spi_slave_isr_handler(void *para)
|
||||
{
|
||||
uint32 regvalue,calvalue;
|
||||
static uint8 state =0;
|
||||
uint32 recv_data,send_data;
|
||||
|
||||
if(READ_PERI_REG(0x3ff00020)&BIT4){
|
||||
//following 3 lines is to clear isr signal
|
||||
CLEAR_PERI_REG_MASK(SPI_SLAVE(SPI), 0x3ff);
|
||||
}else if(READ_PERI_REG(0x3ff00020)&BIT7){ //bit7 is for hspi isr,
|
||||
regvalue=READ_PERI_REG(SPI_SLAVE(HSPI));
|
||||
CLEAR_PERI_REG_MASK(SPI_SLAVE(HSPI),
|
||||
SPI_TRANS_DONE_EN|
|
||||
SPI_SLV_WR_STA_DONE_EN|
|
||||
SPI_SLV_RD_STA_DONE_EN|
|
||||
SPI_SLV_WR_BUF_DONE_EN|
|
||||
SPI_SLV_RD_BUF_DONE_EN);
|
||||
SET_PERI_REG_MASK(SPI_SLAVE(HSPI), SPI_SYNC_RESET);
|
||||
CLEAR_PERI_REG_MASK(SPI_SLAVE(HSPI),
|
||||
SPI_TRANS_DONE|
|
||||
SPI_SLV_WR_STA_DONE|
|
||||
SPI_SLV_RD_STA_DONE|
|
||||
SPI_SLV_WR_BUF_DONE|
|
||||
SPI_SLV_RD_BUF_DONE);
|
||||
SET_PERI_REG_MASK(SPI_SLAVE(HSPI),
|
||||
SPI_TRANS_DONE_EN|
|
||||
SPI_SLV_WR_STA_DONE_EN|
|
||||
SPI_SLV_RD_STA_DONE_EN|
|
||||
SPI_SLV_WR_BUF_DONE_EN|
|
||||
SPI_SLV_RD_BUF_DONE_EN);
|
||||
|
||||
if(regvalue&SPI_SLV_WR_BUF_DONE){
|
||||
GPIO_OUTPUT_SET(0, 0);
|
||||
idx=0;
|
||||
while(idx<8){
|
||||
recv_data=READ_PERI_REG(SPI_W0(HSPI)+(idx<<2));
|
||||
spi_data[idx<<2] = recv_data&0xff;
|
||||
spi_data[(idx<<2)+1] = (recv_data>>8)&0xff;
|
||||
spi_data[(idx<<2)+2] = (recv_data>>16)&0xff;
|
||||
spi_data[(idx<<2)+3] = (recv_data>>24)&0xff;
|
||||
idx++;
|
||||
}
|
||||
//add system_os_post here
|
||||
GPIO_OUTPUT_SET(0, 1);
|
||||
}
|
||||
if(regvalue&SPI_SLV_RD_BUF_DONE){
|
||||
//it is necessary to call GPIO_OUTPUT_SET(2, 1), when new data is preped in SPI_W8-15 and needs to be sended.
|
||||
GPIO_OUTPUT_SET(2, 0);
|
||||
//add system_os_post here
|
||||
//system_os_post(USER_TASK_PRIO_1,WR_RD,regvalue);
|
||||
|
||||
}
|
||||
|
||||
}else if(READ_PERI_REG(0x3ff00020)&BIT9){ //bit7 is for i2s isr,
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef SPI_SLAVE_DEBUG
|
||||
os_event_t * spiQueue;
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
set_miso_data()
|
||||
{
|
||||
if(GPIO_INPUT_GET(2)==0){
|
||||
WRITE_PERI_REG(SPI_W8(HSPI),0x05040302);
|
||||
WRITE_PERI_REG(SPI_W9(HSPI),0x09080706);
|
||||
WRITE_PERI_REG(SPI_W10(HSPI),0x0d0c0b0a);
|
||||
WRITE_PERI_REG(SPI_W11(HSPI),0x11100f0e);
|
||||
|
||||
WRITE_PERI_REG(SPI_W12(HSPI),0x15141312);
|
||||
WRITE_PERI_REG(SPI_W13(HSPI),0x19181716);
|
||||
WRITE_PERI_REG(SPI_W14(HSPI),0x1d1c1b1a);
|
||||
WRITE_PERI_REG(SPI_W15(HSPI),0x21201f1e);
|
||||
GPIO_OUTPUT_SET(2, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
disp_spi_data()
|
||||
{
|
||||
uint8 i = 0;
|
||||
for(i=0;i<32;i++){
|
||||
os_printf("data %d : 0x%02x\n\r",i,spi_data[i]);
|
||||
}
|
||||
//os_printf("d31:0x%02x\n\r",spi_data[31]);
|
||||
}
|
||||
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
spi_task(os_event_t *e)
|
||||
{
|
||||
uint8 data;
|
||||
switch(e->sig){
|
||||
case MOSI:
|
||||
disp_spi_data();
|
||||
break;
|
||||
case STATUS_R_IN_WR :
|
||||
os_printf("SR ERR in WRPR,Reg:%08x \n",e->par);
|
||||
break;
|
||||
case STATUS_W:
|
||||
os_printf("SW ERR,Reg:%08x\n",e->par);
|
||||
break;
|
||||
case TR_DONE_ALONE:
|
||||
os_printf("TD ALO ERR,Reg:%08x\n",e->par);
|
||||
break;
|
||||
case WR_RD:
|
||||
os_printf("WR&RD ERR,Reg:%08x\n",e->par);
|
||||
break;
|
||||
case DATA_ERROR:
|
||||
os_printf("Data ERR,Reg:%08x\n",e->par);
|
||||
break;
|
||||
case STATUS_R_IN_RD :
|
||||
os_printf("SR ERR in RDPR,Reg:%08x\n",e->par);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
spi_task_init(void)
|
||||
{
|
||||
spiQueue = (os_event_t*)malloc(sizeof(os_event_t)*SPI_QUEUE_LEN);
|
||||
system_os_task(spi_task,USER_TASK_PRIO_1,spiQueue,SPI_QUEUE_LEN);
|
||||
}
|
||||
|
||||
os_timer_t spi_timer_test;
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
spi_test_init()
|
||||
{
|
||||
os_printf("spi init\n\r");
|
||||
spi_slave_init(HSPI);
|
||||
os_printf("gpio init\n\r");
|
||||
gpio_init();
|
||||
os_printf("spi task init \n\r");
|
||||
spi_task_init();
|
||||
#ifdef SPI_MISO
|
||||
os_printf("spi miso init\n\r");
|
||||
set_miso_data();
|
||||
#endif
|
||||
|
||||
//os_timer_disarm(&spi_timer_test);
|
||||
//os_timer_setfn(&spi_timer_test, (os_timer_func_t *)set_miso_data, NULL);//wjl
|
||||
//os_timer_arm(&spi_timer_test,50,1);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,378 +0,0 @@
|
||||
/*
|
||||
* ESPRSSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2015 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
* 2016 DiUS Computing Pty Ltd <jmattsson@dius.com.au>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS ESP8266/ESP32 only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
|
||||
#if defined(__ESP32__)
|
||||
# include "freertos/xtensa_api.h"
|
||||
# define ETS_UART_INTR_ENABLE() xt_ints_on(1 << ETS_UART_INUM)
|
||||
# define ETS_UART_INTR_DISABLE() xt_ints_off(1 << ETS_UART_INUM)
|
||||
#endif
|
||||
|
||||
#if defined(__ESP8266__)
|
||||
# include "rom.h"
|
||||
# include "ioswap.h"
|
||||
# define FUNC_U0RXD 0
|
||||
# define FUNC_U0CTS 4
|
||||
# define os_printf_isr(...) do {} while (0)
|
||||
# define ETS_UART_INTR_ENABLE() _xt_isr_unmask(1 << ETS_UART_INUM)
|
||||
# define ETS_UART_INTR_DISABLE() _xt_isr_mask(1 << ETS_UART_INUM)
|
||||
#endif
|
||||
|
||||
#include "esp_common.h"
|
||||
#include "uart.h"
|
||||
#include "gpio.h"
|
||||
#include "task/task.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#define UART_INTR_MASK 0x1ff
|
||||
#define UART_LINE_INV_MASK (0x3f << 19)
|
||||
|
||||
|
||||
static xQueueHandle uartQ[2];
|
||||
static task_handle_t input_task = 0;
|
||||
|
||||
|
||||
void uart_tx_one_char(uint32_t uart, uint8_t TxChar)
|
||||
{
|
||||
while (true) {
|
||||
uint32 fifo_cnt = READ_PERI_REG(UART_STATUS(uart)) & (UART_TXFIFO_CNT << UART_TXFIFO_CNT_S);
|
||||
if ((fifo_cnt >> UART_TXFIFO_CNT_S & UART_TXFIFO_CNT) < 126) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
WRITE_PERI_REG(UART_FIFO(uart) , TxChar);
|
||||
}
|
||||
|
||||
|
||||
static void uart1_write_char(char c)
|
||||
{
|
||||
if (c == '\n') {
|
||||
uart_tx_one_char(UART1, '\r');
|
||||
uart_tx_one_char(UART1, '\n');
|
||||
} else if (c == '\r') {
|
||||
} else {
|
||||
uart_tx_one_char(UART1, c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void uart0_write_char(char c)
|
||||
{
|
||||
if (c == '\n') {
|
||||
uart_tx_one_char(UART0, '\r');
|
||||
uart_tx_one_char(UART0, '\n');
|
||||
} else if (c == '\r') {
|
||||
} else {
|
||||
uart_tx_one_char(UART0, c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=================================================================
|
||||
|
||||
void UART_SetWordLength(UART_Port uart_no, UART_WordLength len)
|
||||
{
|
||||
SET_PERI_REG_BITS(UART_CONF0(uart_no), UART_BIT_NUM, len, UART_BIT_NUM_S);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UART_SetStopBits(UART_Port uart_no, UART_StopBits bit_num)
|
||||
{
|
||||
SET_PERI_REG_BITS(UART_CONF0(uart_no), UART_STOP_BIT_NUM, bit_num, UART_STOP_BIT_NUM_S);
|
||||
}
|
||||
|
||||
|
||||
void UART_SetLineInverse(UART_Port uart_no, UART_LineLevelInverse inverse_mask)
|
||||
{
|
||||
CLEAR_PERI_REG_MASK(UART_CONF0(uart_no), UART_LINE_INV_MASK);
|
||||
SET_PERI_REG_MASK(UART_CONF0(uart_no), inverse_mask);
|
||||
}
|
||||
|
||||
|
||||
void UART_SetParity(UART_Port uart_no, UART_ParityMode Parity_mode)
|
||||
{
|
||||
CLEAR_PERI_REG_MASK(UART_CONF0(uart_no), UART_PARITY | UART_PARITY_EN);
|
||||
|
||||
if (Parity_mode == USART_Parity_None) {
|
||||
} else {
|
||||
SET_PERI_REG_MASK(UART_CONF0(uart_no), Parity_mode | UART_PARITY_EN);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void UART_SetBaudrate(UART_Port uart_no, uint32 baud_rate)
|
||||
{
|
||||
uart_div_modify(uart_no, UART_CLK_FREQ / baud_rate);
|
||||
}
|
||||
|
||||
|
||||
//only when USART_HardwareFlowControl_RTS is set , will the rx_thresh value be set.
|
||||
void UART_SetFlowCtrl(UART_Port uart_no, UART_HwFlowCtrl flow_ctrl, uint8 rx_thresh)
|
||||
{
|
||||
if (flow_ctrl & USART_HardwareFlowControl_RTS) {
|
||||
#if defined(__ESP8266__)
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, FUNC_U0RTS);
|
||||
#elif defined(__ESP32__)
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, FUNC_MTDO_U0RTS);
|
||||
#endif
|
||||
SET_PERI_REG_BITS(UART_CONF1(uart_no), UART_RX_FLOW_THRHD, rx_thresh, UART_RX_FLOW_THRHD_S);
|
||||
SET_PERI_REG_MASK(UART_CONF1(uart_no), UART_RX_FLOW_EN);
|
||||
} else {
|
||||
CLEAR_PERI_REG_MASK(UART_CONF1(uart_no), UART_RX_FLOW_EN);
|
||||
}
|
||||
|
||||
if (flow_ctrl & USART_HardwareFlowControl_CTS) {
|
||||
#if defined(__ESP8266__)
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_UART0_CTS);
|
||||
#elif defined(__ESP32__)
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_MTCK_U0CTS);
|
||||
#endif
|
||||
SET_PERI_REG_MASK(UART_CONF0(uart_no), UART_TX_FLOW_EN);
|
||||
} else {
|
||||
CLEAR_PERI_REG_MASK(UART_CONF0(uart_no), UART_TX_FLOW_EN);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void UART_WaitTxFifoEmpty(UART_Port uart_no) //do not use if tx flow control enabled
|
||||
{
|
||||
while (READ_PERI_REG(UART_STATUS(uart_no)) & (UART_TXFIFO_CNT << UART_TXFIFO_CNT_S));
|
||||
}
|
||||
|
||||
|
||||
void UART_ResetFifo(UART_Port uart_no)
|
||||
{
|
||||
SET_PERI_REG_MASK(UART_CONF0(uart_no), UART_RXFIFO_RST | UART_TXFIFO_RST);
|
||||
CLEAR_PERI_REG_MASK(UART_CONF0(uart_no), UART_RXFIFO_RST | UART_TXFIFO_RST);
|
||||
}
|
||||
|
||||
|
||||
void UART_ClearIntrStatus(UART_Port uart_no, uint32 clr_mask)
|
||||
{
|
||||
WRITE_PERI_REG(UART_INT_CLR(uart_no), clr_mask);
|
||||
}
|
||||
|
||||
|
||||
void UART_SetIntrEna(UART_Port uart_no, uint32 ena_mask)
|
||||
{
|
||||
SET_PERI_REG_MASK(UART_INT_ENA(uart_no), ena_mask);
|
||||
}
|
||||
|
||||
|
||||
void UART_intr_handler_register(void *fn, void *arg)
|
||||
{
|
||||
#if defined(__ESP8266__)
|
||||
_xt_isr_attach(ETS_UART_INUM, fn, arg);
|
||||
#elif defined(__ESP32__)
|
||||
xt_set_interrupt_handler(ETS_UART_INUM, fn, arg);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void UART_SetPrintPort(UART_Port uart_no)
|
||||
{
|
||||
if (uart_no == 1) {
|
||||
os_install_putc1(uart1_write_char);
|
||||
} else {
|
||||
os_install_putc1(uart0_write_char);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void UART_ParamConfig(UART_Port uart_no, const UART_ConfigTypeDef *pUARTConfig)
|
||||
{
|
||||
if (uart_no == UART1) {
|
||||
#if defined(__ESP8266__)
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_U1TXD_BK);
|
||||
#elif defined(__ESP32__)
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA3_U, FUNC_SD_DATA3_U1TXD);
|
||||
#endif
|
||||
} else {
|
||||
PIN_PULLUP_DIS(PERIPHS_IO_MUX_U0TXD_U);
|
||||
#if defined(__ESP8266__)
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD);
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD);
|
||||
#elif defined(__ESP32__)
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_U0RXD);
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_U0TXD);
|
||||
#endif
|
||||
}
|
||||
|
||||
UART_SetFlowCtrl(uart_no, pUARTConfig->flow_ctrl, pUARTConfig->UART_RxFlowThresh);
|
||||
UART_SetBaudrate(uart_no, pUARTConfig->baud_rate);
|
||||
|
||||
WRITE_PERI_REG(UART_CONF0(uart_no),
|
||||
((pUARTConfig->parity == USART_Parity_None) ? 0x0 : (UART_PARITY_EN | pUARTConfig->parity))
|
||||
| (pUARTConfig->stop_bits << UART_STOP_BIT_NUM_S)
|
||||
| (pUARTConfig->data_bits << UART_BIT_NUM_S)
|
||||
| ((pUARTConfig->flow_ctrl & USART_HardwareFlowControl_CTS) ? UART_TX_FLOW_EN : 0x0)
|
||||
| pUARTConfig->UART_InverseMask
|
||||
#if defined(__ESP32__)
|
||||
| UART_TICK_REF_ALWAYS_ON
|
||||
#endif
|
||||
);
|
||||
|
||||
UART_ResetFifo(uart_no);
|
||||
}
|
||||
|
||||
|
||||
void UART_IntrConfig(UART_Port uart_no, const UART_IntrConfTypeDef *pUARTIntrConf)
|
||||
{
|
||||
uint32 reg_val = 0;
|
||||
UART_ClearIntrStatus(uart_no, UART_INTR_MASK);
|
||||
reg_val = READ_PERI_REG(UART_CONF1(uart_no));
|
||||
|
||||
reg_val |= ((pUARTIntrConf->UART_IntrEnMask & UART_RXFIFO_TOUT_INT_ENA) ?
|
||||
((((pUARTIntrConf->UART_RX_TimeOutIntrThresh)&UART_RX_TOUT_THRHD) << UART_RX_TOUT_THRHD_S) | UART_RX_TOUT_EN) : 0);
|
||||
|
||||
reg_val |= ((pUARTIntrConf->UART_IntrEnMask & UART_RXFIFO_FULL_INT_ENA) ?
|
||||
(((pUARTIntrConf->UART_RX_FifoFullIntrThresh)&UART_RXFIFO_FULL_THRHD) << UART_RXFIFO_FULL_THRHD_S) : 0);
|
||||
|
||||
reg_val |= ((pUARTIntrConf->UART_IntrEnMask & UART_TXFIFO_EMPTY_INT_ENA) ?
|
||||
(((pUARTIntrConf->UART_TX_FifoEmptyIntrThresh)&UART_TXFIFO_EMPTY_THRHD) << UART_TXFIFO_EMPTY_THRHD_S) : 0);
|
||||
|
||||
WRITE_PERI_REG(UART_CONF1(uart_no), reg_val);
|
||||
CLEAR_PERI_REG_MASK(UART_INT_ENA(uart_no), UART_INTR_MASK);
|
||||
SET_PERI_REG_MASK(UART_INT_ENA(uart_no), pUARTIntrConf->UART_IntrEnMask);
|
||||
}
|
||||
|
||||
|
||||
static void uart0_rx_intr_handler(void *para)
|
||||
{
|
||||
/* uart0 and uart1 intr combine togther, when interrupt occur, see reg 0x3ff20020, bit2, bit0 represents
|
||||
* uart1 and uart0 respectively
|
||||
*/
|
||||
uint8 uart_no = UART0; // TODO: support UART1 as well
|
||||
uint8 fifo_len = 0;
|
||||
uint32 uart_intr_status = READ_PERI_REG(UART_INT_ST(uart_no)) ;
|
||||
|
||||
while (uart_intr_status != 0x0) {
|
||||
if (UART_FRM_ERR_INT_ST == (uart_intr_status & UART_FRM_ERR_INT_ST)) {
|
||||
//os_printf_isr("FRM_ERR\r\n");
|
||||
WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_FRM_ERR_INT_CLR);
|
||||
} else if (UART_RXFIFO_FULL_INT_ST == (uart_intr_status & UART_RXFIFO_FULL_INT_ST)) {
|
||||
//os_printf_isr("full\r\n");
|
||||
fifo_len = (READ_PERI_REG(UART_STATUS(uart_no)) >> UART_RXFIFO_CNT_S)&UART_RXFIFO_CNT;
|
||||
|
||||
for (int i = 0; i < fifo_len; ++i)
|
||||
{
|
||||
char c = READ_PERI_REG(UART_FIFO(uart_no)) & 0xff;
|
||||
if (uartQ[uart_no])
|
||||
xQueueSendToBackFromISR (uartQ[uart_no], &c, NULL);
|
||||
}
|
||||
|
||||
WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_RXFIFO_FULL_INT_CLR);
|
||||
//CLEAR_PERI_REG_MASK(UART_INT_ENA(uart_no), UART_RXFIFO_FULL_INT_ENA|UART_RXFIFO_TOUT_INT_ENA);
|
||||
} else if (UART_RXFIFO_TOUT_INT_ST == (uart_intr_status & UART_RXFIFO_TOUT_INT_ST)) {
|
||||
//os_printf_isr("timeout\r\n");
|
||||
fifo_len = (READ_PERI_REG(UART_STATUS(uart_no)) >> UART_RXFIFO_CNT_S)&UART_RXFIFO_CNT;
|
||||
|
||||
for (int i = 0; i < fifo_len; ++i)
|
||||
{
|
||||
char c = READ_PERI_REG(UART_FIFO(uart_no)) & 0xff;
|
||||
if (uartQ[uart_no])
|
||||
xQueueSendToBackFromISR (uartQ[uart_no], &c, NULL);
|
||||
}
|
||||
|
||||
WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_RXFIFO_TOUT_INT_CLR);
|
||||
} else if (UART_TXFIFO_EMPTY_INT_ST == (uart_intr_status & UART_TXFIFO_EMPTY_INT_ST)) {
|
||||
//os_printf_isr("empty\n\r");
|
||||
WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_TXFIFO_EMPTY_INT_CLR);
|
||||
CLEAR_PERI_REG_MASK(UART_INT_ENA(uart_no), UART_TXFIFO_EMPTY_INT_ENA);
|
||||
} else if (UART_RXFIFO_OVF_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_RXFIFO_OVF_INT_ST)) {
|
||||
WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_RXFIFO_OVF_INT_CLR);
|
||||
//os_printf_isr("RX OVF!!\r\n");
|
||||
} else {
|
||||
//skip
|
||||
}
|
||||
|
||||
uart_intr_status = READ_PERI_REG(UART_INT_ST(uart_no)) ;
|
||||
}
|
||||
|
||||
if (fifo_len && input_task)
|
||||
task_post_low (input_task, false);
|
||||
}
|
||||
|
||||
|
||||
void uart_init_uart0_console (const UART_ConfigTypeDef *config, task_handle_t tsk)
|
||||
{
|
||||
input_task = tsk;
|
||||
uartQ[0] = xQueueCreate (0x100, sizeof (char));
|
||||
|
||||
UART_WaitTxFifoEmpty(UART0);
|
||||
|
||||
UART_ParamConfig(UART0, config);
|
||||
|
||||
UART_IntrConfTypeDef uart_intr;
|
||||
uart_intr.UART_IntrEnMask =
|
||||
UART_RXFIFO_TOUT_INT_ENA |
|
||||
UART_FRM_ERR_INT_ENA |
|
||||
UART_RXFIFO_FULL_INT_ENA |
|
||||
UART_TXFIFO_EMPTY_INT_ENA;
|
||||
uart_intr.UART_RX_FifoFullIntrThresh = 10;
|
||||
uart_intr.UART_RX_TimeOutIntrThresh = 2;
|
||||
uart_intr.UART_TX_FifoEmptyIntrThresh = 20;
|
||||
UART_IntrConfig(UART0, &uart_intr);
|
||||
|
||||
UART_SetPrintPort(UART0);
|
||||
UART_intr_handler_register(uart0_rx_intr_handler, NULL);
|
||||
ETS_UART_INTR_ENABLE();
|
||||
}
|
||||
|
||||
|
||||
bool uart0_getc (char *c)
|
||||
{
|
||||
return (uartQ[UART0] && (xQueueReceive (uartQ[UART0], c, 0) == pdTRUE));
|
||||
}
|
||||
|
||||
|
||||
void uart0_alt (bool on)
|
||||
{
|
||||
#if defined(__ESP8266__)
|
||||
if (on)
|
||||
{
|
||||
PIN_PULLUP_DIS(PERIPHS_IO_MUX_MTDO_U);
|
||||
PIN_PULLUP_EN(PERIPHS_IO_MUX_MTCK_U);
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_U0CTS);
|
||||
// now make RTS/CTS behave as TX/RX
|
||||
IOSWAP |= (1 << IOSWAPU0);
|
||||
}
|
||||
else
|
||||
{
|
||||
PIN_PULLUP_DIS(PERIPHS_IO_MUX_U0TXD_U);
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD);
|
||||
PIN_PULLUP_EN(PERIPHS_IO_MUX_U0RXD_U);
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD);
|
||||
// now make RX/TX behave as TX/RX
|
||||
IOSWAP &= ~(1 << IOSWAPU0);
|
||||
}
|
||||
#else
|
||||
printf("Alternate UART0 pins not supported on this chip\n");
|
||||
#endif
|
||||
}
|
||||
|
@ -1,48 +0,0 @@
|
||||
|
||||
#############################################################
|
||||
# Required variables for each makefile
|
||||
# Discard this section from all parent makefiles
|
||||
# Expected variables (with automatic defaults):
|
||||
# CSRCS (all "C" files in the dir)
|
||||
# SUBDIRS (all subdirs with a Makefile)
|
||||
# GEN_LIBS - list of libs to be generated ()
|
||||
# GEN_IMAGES - list of images to be generated ()
|
||||
# COMPONENTS_xxx - a list of libs/objs in the form
|
||||
# subdir/lib to be extracted and rolled up into
|
||||
# a generated lib/image xxx.a ()
|
||||
#
|
||||
ifndef PDIR
|
||||
GEN_LIBS = libhttp.a
|
||||
endif
|
||||
|
||||
STD_CFLAGS=-std=gnu11 -Wimplicit
|
||||
|
||||
#############################################################
|
||||
# Configuration i.e. compile options etc.
|
||||
# Target specific stuff (defines etc.) goes in here!
|
||||
# Generally values applying to a tree are captured in the
|
||||
# makefile at its root level - these are then overridden
|
||||
# for a subtree within the makefile rooted therein
|
||||
#
|
||||
#DEFINES +=
|
||||
|
||||
#############################################################
|
||||
# Recursion Magic - Don't touch this!!
|
||||
#
|
||||
# Each subtree potentially has an include directory
|
||||
# corresponding to the common APIs applicable to modules
|
||||
# rooted at that subtree. Accordingly, the INCLUDE PATH
|
||||
# of a module can only contain the include directories up
|
||||
# its parent path, and not its siblings
|
||||
#
|
||||
# Required for each makefile to inherit from the parent
|
||||
#
|
||||
|
||||
INCLUDES := $(INCLUDES) -I $(PDIR)include
|
||||
INCLUDES += -I ./
|
||||
INCLUDES += -I ./include
|
||||
INCLUDES += -I ../include
|
||||
INCLUDES += -I ../../include
|
||||
PDIR := ../$(PDIR)
|
||||
sinclude $(PDIR)Makefile
|
||||
|
@ -1,559 +0,0 @@
|
||||
/*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* Martin d'Allens <martin.dallens@gmail.com> wrote this file. As long as you retain
|
||||
* this notice you can do whatever you want with this stuff. If we meet some day,
|
||||
* and you think this stuff is worth it, you can buy me a beer in return.
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
* FIXME: sprintf->snprintf everywhere.
|
||||
* FIXME: support null characters in responses.
|
||||
*/
|
||||
|
||||
// No espconn on ESP32
|
||||
#ifdef __ESP8266__
|
||||
|
||||
#include "osapi.h"
|
||||
#include "user_interface.h"
|
||||
#include "espconn.h"
|
||||
#include "mem.h"
|
||||
#include "limits.h"
|
||||
#include "httpclient.h"
|
||||
#include "stdlib.h"
|
||||
|
||||
/* Internal state. */
|
||||
typedef struct request_args_t {
|
||||
char * hostname;
|
||||
int port;
|
||||
bool secure;
|
||||
char * method;
|
||||
char * path;
|
||||
char * headers;
|
||||
char * post_data;
|
||||
char * buffer;
|
||||
int buffer_size;
|
||||
int timeout;
|
||||
os_timer_t timeout_timer;
|
||||
http_callback_t callback_handle;
|
||||
} request_args_t;
|
||||
|
||||
static char * ICACHE_FLASH_ATTR esp_strdup( const char * str )
|
||||
{
|
||||
if ( str == NULL )
|
||||
{
|
||||
return(NULL);
|
||||
}
|
||||
char * new_str = (char *) malloc( os_strlen( str ) + 1 ); /* 1 for null character */
|
||||
if ( new_str == NULL )
|
||||
{
|
||||
HTTPCLIENT_DEBUG( "esp_strdup: malloc error" );
|
||||
return(NULL);
|
||||
}
|
||||
os_strcpy( new_str, str );
|
||||
return(new_str);
|
||||
}
|
||||
|
||||
|
||||
static int ICACHE_FLASH_ATTR
|
||||
esp_isupper( char c )
|
||||
{
|
||||
return(c >= 'A' && c <= 'Z');
|
||||
}
|
||||
|
||||
|
||||
static int ICACHE_FLASH_ATTR
|
||||
esp_isalpha( char c )
|
||||
{
|
||||
return( (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') );
|
||||
}
|
||||
|
||||
|
||||
static int ICACHE_FLASH_ATTR
|
||||
esp_isspace( char c )
|
||||
{
|
||||
return(c == ' ' || c == '\t' || c == '\n' || c == '\12');
|
||||
}
|
||||
|
||||
|
||||
static int ICACHE_FLASH_ATTR
|
||||
esp_isdigit( char c )
|
||||
{
|
||||
return(c >= '0' && c <= '9');
|
||||
}
|
||||
|
||||
|
||||
static int ICACHE_FLASH_ATTR http_chunked_decode( const char * chunked, char * decode )
|
||||
{
|
||||
int i = 0, j = 0;
|
||||
int decode_size = 0;
|
||||
char *str = (char *) chunked;
|
||||
do
|
||||
{
|
||||
char * endstr;
|
||||
/* [chunk-size] */
|
||||
i = strtoul( str + j, NULL, 16 );
|
||||
HTTPCLIENT_DEBUG( "Chunk Size:%d\r\n", i );
|
||||
if ( i <= 0 )
|
||||
break;
|
||||
/* [chunk-size-end-ptr] */
|
||||
endstr = (char *) os_strstr( str + j, "\r\n" );
|
||||
/* [chunk-ext] */
|
||||
j += endstr - (str + j);
|
||||
/* [CRLF] */
|
||||
j += 2;
|
||||
/* [chunk-data] */
|
||||
decode_size += i;
|
||||
os_memcpy( (char *) &decode[decode_size - i], (char *) str + j, i );
|
||||
j += i;
|
||||
/* [CRLF] */
|
||||
j += 2;
|
||||
}
|
||||
while ( true );
|
||||
|
||||
/*
|
||||
*
|
||||
* footer CRLF
|
||||
*
|
||||
*/
|
||||
|
||||
return(j);
|
||||
}
|
||||
|
||||
|
||||
static void ICACHE_FLASH_ATTR http_receive_callback( void * arg, char * buf, unsigned short len )
|
||||
{
|
||||
struct espconn * conn = (struct espconn *) arg;
|
||||
request_args_t * req = (request_args_t *) conn->reverse;
|
||||
|
||||
if ( req->buffer == NULL )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Let's do the equivalent of a realloc(). */
|
||||
const int new_size = req->buffer_size + len;
|
||||
char * new_buffer;
|
||||
if ( new_size > BUFFER_SIZE_MAX || NULL == (new_buffer = (char *) malloc( new_size ) ) )
|
||||
{
|
||||
HTTPCLIENT_DEBUG( "Response too long (%d)\n", new_size );
|
||||
req->buffer[0] = '\0'; /* Discard the buffer to avoid using an incomplete response. */
|
||||
#if 0
|
||||
if ( req->secure )
|
||||
espconn_secure_disconnect( conn );
|
||||
else
|
||||
#endif
|
||||
espconn_disconnect( conn );
|
||||
return; /* The disconnect callback will be called. */
|
||||
}
|
||||
|
||||
os_memcpy( new_buffer, req->buffer, req->buffer_size );
|
||||
os_memcpy( new_buffer + req->buffer_size - 1 /*overwrite the null character*/, buf, len ); /* Append new data. */
|
||||
new_buffer[new_size - 1] = '\0'; /* Make sure there is an end of string. */
|
||||
|
||||
free( req->buffer );
|
||||
req->buffer = new_buffer;
|
||||
req->buffer_size = new_size;
|
||||
}
|
||||
|
||||
|
||||
static void ICACHE_FLASH_ATTR http_send_callback( void * arg )
|
||||
{
|
||||
struct espconn * conn = (struct espconn *) arg;
|
||||
request_args_t * req = (request_args_t *) conn->reverse;
|
||||
|
||||
if ( req->post_data == NULL )
|
||||
{
|
||||
HTTPCLIENT_DEBUG( "All sent\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The headers were sent, now send the contents. */
|
||||
HTTPCLIENT_DEBUG( "Sending request body\n" );
|
||||
#if 0
|
||||
if ( req->secure )
|
||||
espconn_secure_send( conn, (uint8_t *) req->post_data, strlen( req->post_data ) );
|
||||
else
|
||||
#endif
|
||||
espconn_send( conn, (uint8_t *) req->post_data, strlen( req->post_data ) );
|
||||
free( req->post_data );
|
||||
req->post_data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void ICACHE_FLASH_ATTR http_connect_callback( void * arg )
|
||||
{
|
||||
HTTPCLIENT_DEBUG( "Connected\n" );
|
||||
struct espconn * conn = (struct espconn *) arg;
|
||||
request_args_t * req = (request_args_t *) conn->reverse;
|
||||
|
||||
espconn_regist_recvcb( conn, http_receive_callback );
|
||||
espconn_regist_sentcb( conn, http_send_callback );
|
||||
|
||||
char post_headers[32] = "";
|
||||
|
||||
if ( req->post_data != NULL ) /* If there is data then add Content-Length header. */
|
||||
{
|
||||
os_sprintf( post_headers, "Content-Length: %d\r\n", strlen( req->post_data ) );
|
||||
}
|
||||
|
||||
if(req->headers == NULL) /* Avoid NULL pointer, it may cause exception */
|
||||
{
|
||||
req->headers = (char *)malloc(sizeof(char));
|
||||
req->headers[0] = '\0';
|
||||
}
|
||||
|
||||
char buf[69 + strlen( req->method ) + strlen( req->path ) + strlen( req->hostname ) +
|
||||
strlen( req->headers ) + strlen( post_headers )];
|
||||
int len = os_sprintf( buf,
|
||||
"%s %s HTTP/1.1\r\n"
|
||||
"Host: %s:%d\r\n"
|
||||
"Connection: close\r\n"
|
||||
"User-Agent: ESP8266\r\n"
|
||||
"%s"
|
||||
"%s"
|
||||
"\r\n",
|
||||
req->method, req->path, req->hostname, req->port, req->headers, post_headers );
|
||||
|
||||
#if 0
|
||||
if ( req->secure )
|
||||
espconn_secure_send( conn, (uint8_t *) buf, len );
|
||||
else
|
||||
#endif
|
||||
espconn_send( conn, (uint8_t *) buf, len );
|
||||
if(req->headers != NULL)
|
||||
free( req->headers );
|
||||
req->headers = NULL;
|
||||
HTTPCLIENT_DEBUG( "Sending request header\n" );
|
||||
}
|
||||
|
||||
|
||||
static void ICACHE_FLASH_ATTR http_disconnect_callback( void * arg )
|
||||
{
|
||||
HTTPCLIENT_DEBUG( "Disconnected\n" );
|
||||
struct espconn *conn = (struct espconn *) arg;
|
||||
|
||||
if ( conn == NULL )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( conn->proto.tcp != NULL )
|
||||
{
|
||||
free( conn->proto.tcp );
|
||||
}
|
||||
if ( conn->reverse != NULL )
|
||||
{
|
||||
request_args_t * req = (request_args_t *) conn->reverse;
|
||||
int http_status = -1;
|
||||
char * body = "";
|
||||
|
||||
// Turn off timeout timer
|
||||
os_timer_disarm( &(req->timeout_timer) );
|
||||
|
||||
if ( req->buffer == NULL )
|
||||
{
|
||||
HTTPCLIENT_DEBUG( "Buffer probably shouldn't be NULL\n" );
|
||||
}
|
||||
else if ( req->buffer[0] != '\0' )
|
||||
{
|
||||
/* FIXME: make sure this is not a partial response, using the Content-Length header. */
|
||||
const char * version_1_0 = "HTTP/1.0 ";
|
||||
const char * version_1_1 = "HTTP/1.1 ";
|
||||
if (( os_strncmp( req->buffer, version_1_0, strlen( version_1_0 ) ) != 0 ) &&
|
||||
( os_strncmp( req->buffer, version_1_1, strlen( version_1_1 ) ) != 0 ))
|
||||
{
|
||||
HTTPCLIENT_DEBUG( "Invalid version in %s\n", req->buffer );
|
||||
}
|
||||
else
|
||||
{
|
||||
http_status = atoi( req->buffer + strlen( version_1_0 ) );
|
||||
body = (char *) os_strstr(req->buffer, "\r\n\r\n");
|
||||
|
||||
if (NULL == body) {
|
||||
/* Find missing body */
|
||||
HTTPCLIENT_DEBUG("Body shouldn't be NULL\n");
|
||||
/* To avoid NULL body */
|
||||
body = "";
|
||||
} else {
|
||||
/* Skip CR & LF */
|
||||
body = body + 4;
|
||||
}
|
||||
|
||||
if ( os_strstr( req->buffer, "Transfer-Encoding: chunked" ) )
|
||||
{
|
||||
int body_size = req->buffer_size - (body - req->buffer);
|
||||
char chunked_decode_buffer[body_size];
|
||||
os_memset( chunked_decode_buffer, 0, body_size );
|
||||
/* Chuncked data */
|
||||
http_chunked_decode( body, chunked_decode_buffer );
|
||||
os_memcpy( body, chunked_decode_buffer, body_size );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( req->callback_handle != NULL ) /* Callback is optional. */
|
||||
{
|
||||
req->callback_handle( body, http_status, req->buffer );
|
||||
}
|
||||
if (req->buffer) {
|
||||
free( req->buffer );
|
||||
}
|
||||
free( req->hostname );
|
||||
free( req->method );
|
||||
free( req->path );
|
||||
free( req );
|
||||
}
|
||||
/* Fix memory leak. */
|
||||
espconn_delete( conn );
|
||||
free( conn );
|
||||
}
|
||||
|
||||
|
||||
static void ICACHE_FLASH_ATTR http_error_callback( void *arg, sint8 errType )
|
||||
{
|
||||
HTTPCLIENT_DEBUG( "Disconnected with error\n" );
|
||||
http_disconnect_callback( arg );
|
||||
}
|
||||
|
||||
|
||||
static void ICACHE_FLASH_ATTR http_timeout_callback( void *arg )
|
||||
{
|
||||
HTTPCLIENT_DEBUG( "Connection timeout\n" );
|
||||
struct espconn * conn = (struct espconn *) arg;
|
||||
if ( conn == NULL )
|
||||
{
|
||||
return;
|
||||
}
|
||||
if ( conn->reverse == NULL )
|
||||
{
|
||||
return;
|
||||
}
|
||||
request_args_t * req = (request_args_t *) conn->reverse;
|
||||
/* Call disconnect */
|
||||
#if 0
|
||||
if ( req->secure )
|
||||
espconn_secure_disconnect( conn );
|
||||
else
|
||||
#endif
|
||||
espconn_disconnect( conn );
|
||||
}
|
||||
|
||||
|
||||
static void ICACHE_FLASH_ATTR http_dns_callback( const char * hostname, ip_addr_t * addr, void * arg )
|
||||
{
|
||||
request_args_t * req = (request_args_t *) arg;
|
||||
|
||||
if ( addr == NULL )
|
||||
{
|
||||
HTTPCLIENT_DEBUG( "DNS failed for %s\n", hostname );
|
||||
if ( req->callback_handle != NULL )
|
||||
{
|
||||
req->callback_handle( "", -1, "" );
|
||||
}
|
||||
free( req );
|
||||
}
|
||||
else
|
||||
{
|
||||
HTTPCLIENT_DEBUG( "DNS found %s " IPSTR "\n", hostname, IP2STR( addr ) );
|
||||
|
||||
struct espconn * conn = (struct espconn *) zalloc( sizeof(struct espconn) );
|
||||
conn->type = ESPCONN_TCP;
|
||||
conn->state = ESPCONN_NONE;
|
||||
conn->proto.tcp = (esp_tcp *) zalloc( sizeof(esp_tcp) );
|
||||
conn->proto.tcp->local_port = espconn_port();
|
||||
conn->proto.tcp->remote_port = req->port;
|
||||
conn->reverse = req;
|
||||
|
||||
os_memcpy( conn->proto.tcp->remote_ip, addr, 4 );
|
||||
|
||||
espconn_regist_connectcb( conn, http_connect_callback );
|
||||
espconn_regist_disconcb( conn, http_disconnect_callback );
|
||||
espconn_regist_reconcb( conn, http_error_callback );
|
||||
|
||||
/* Set connection timeout timer */
|
||||
os_timer_disarm( &(req->timeout_timer) );
|
||||
os_timer_setfn( &(req->timeout_timer), (os_timer_func_t *) http_timeout_callback, conn );
|
||||
os_timer_arm( &(req->timeout_timer), req->timeout, false );
|
||||
|
||||
#if 0
|
||||
if ( req->secure )
|
||||
{
|
||||
espconn_secure_set_size( ESPCONN_CLIENT, 5120 ); /* set SSL buffer size */
|
||||
espconn_secure_connect( conn );
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
espconn_connect( conn );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ICACHE_FLASH_ATTR http_raw_request( const char * hostname, int port, bool secure, const char * method, const char * path, const char * headers, const char * post_data, http_callback_t callback_handle )
|
||||
{
|
||||
HTTPCLIENT_DEBUG( "DNS request\n" );
|
||||
|
||||
request_args_t * req = (request_args_t *) zalloc( sizeof(request_args_t) );
|
||||
req->hostname = esp_strdup( hostname );
|
||||
req->port = port;
|
||||
req->secure = secure;
|
||||
req->method = esp_strdup( method );
|
||||
req->path = esp_strdup( path );
|
||||
req->headers = esp_strdup( headers );
|
||||
req->post_data = esp_strdup( post_data );
|
||||
req->buffer_size = 1;
|
||||
req->buffer = (char *) malloc( 1 );
|
||||
req->buffer[0] = '\0'; /* Empty string. */
|
||||
req->callback_handle = callback_handle;
|
||||
req->timeout = HTTP_REQUEST_TIMEOUT_MS;
|
||||
|
||||
ip_addr_t addr;
|
||||
err_t error = espconn_gethostbyname( (struct espconn *) req, /* It seems we don't need a real espconn pointer here. */
|
||||
hostname, &addr, http_dns_callback );
|
||||
|
||||
if ( error == ESPCONN_INPROGRESS )
|
||||
{
|
||||
HTTPCLIENT_DEBUG( "DNS pending\n" );
|
||||
}
|
||||
else if ( error == ESPCONN_OK )
|
||||
{
|
||||
/* Already in the local names table (or hostname was an IP address), execute the callback ourselves. */
|
||||
http_dns_callback( hostname, &addr, req );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( error == ESPCONN_ARG )
|
||||
{
|
||||
HTTPCLIENT_DEBUG( "DNS arg error %s\n", hostname );
|
||||
}else {
|
||||
HTTPCLIENT_DEBUG( "DNS error code %d\n", error );
|
||||
}
|
||||
http_dns_callback( hostname, NULL, req ); /* Handle all DNS errors the same way. */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Parse an URL of the form http://host:port/path
|
||||
* <host> can be a hostname or an IP address
|
||||
* <port> is optional
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR http_request( const char * url, const char * method, const char * headers, const char * post_data, http_callback_t callback_handle )
|
||||
{
|
||||
/*
|
||||
* FIXME: handle HTTP auth with http://user:pass@host/
|
||||
* FIXME: get rid of the #anchor part if present.
|
||||
*/
|
||||
|
||||
char hostname[128] = "";
|
||||
int port = 80;
|
||||
bool secure = false;
|
||||
|
||||
bool is_http = os_strncmp( url, "http://", strlen( "http://" ) ) == 0;
|
||||
bool is_https = os_strncmp( url, "https://", strlen( "https://" ) ) == 0;
|
||||
|
||||
if ( is_http )
|
||||
url += strlen( "http://" ); /* Get rid of the protocol. */
|
||||
else if ( is_https )
|
||||
{
|
||||
port = 443;
|
||||
secure = true;
|
||||
url += strlen( "https://" ); /* Get rid of the protocol. */
|
||||
}
|
||||
else
|
||||
{
|
||||
HTTPCLIENT_DEBUG( "URL is not HTTP or HTTPS %s\n", url );
|
||||
return;
|
||||
}
|
||||
|
||||
char * path = os_strchr( url, '/' );
|
||||
if ( path == NULL )
|
||||
{
|
||||
path = os_strchr( url, '\0' ); /* Pointer to end of string. */
|
||||
}
|
||||
|
||||
char * colon = os_strchr( url, ':' );
|
||||
if ( colon > path )
|
||||
{
|
||||
colon = NULL; /* Limit the search to characters before the path. */
|
||||
}
|
||||
|
||||
if (path - url >= sizeof(hostname)) {
|
||||
HTTPCLIENT_DEBUG( "hostname is too long %s\n", url );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( colon == NULL ) /* The port is not present. */
|
||||
{
|
||||
os_memcpy( hostname, url, path - url );
|
||||
hostname[path - url] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
port = atoi( colon + 1 );
|
||||
if ( port == 0 )
|
||||
{
|
||||
HTTPCLIENT_DEBUG( "Port error %s\n", url );
|
||||
return;
|
||||
}
|
||||
|
||||
os_memcpy( hostname, url, colon - url );
|
||||
hostname[colon - url] = '\0';
|
||||
}
|
||||
|
||||
|
||||
if ( path[0] == '\0' ) /* Empty path is not allowed. */
|
||||
{
|
||||
path = "/";
|
||||
}
|
||||
|
||||
HTTPCLIENT_DEBUG( "hostname=%s\n", hostname );
|
||||
HTTPCLIENT_DEBUG( "port=%d\n", port );
|
||||
HTTPCLIENT_DEBUG( "method=%s\n", method );
|
||||
HTTPCLIENT_DEBUG( "path=%s\n", path );
|
||||
http_raw_request( hostname, port, secure, method, path, headers, post_data, callback_handle );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Parse an URL of the form http://host:port/path
|
||||
* <host> can be a hostname or an IP address
|
||||
* <port> is optional
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR http_post( const char * url, const char * headers, const char * post_data, http_callback_t callback_handle )
|
||||
{
|
||||
http_request( url, "POST", headers, post_data, callback_handle );
|
||||
}
|
||||
|
||||
|
||||
void ICACHE_FLASH_ATTR http_get( const char * url, const char * headers, http_callback_t callback_handle )
|
||||
{
|
||||
http_request( url, "GET", headers, NULL, callback_handle );
|
||||
}
|
||||
|
||||
|
||||
void ICACHE_FLASH_ATTR http_delete( const char * url, const char * headers, const char * post_data, http_callback_t callback_handle )
|
||||
{
|
||||
http_request( url, "DELETE", headers, post_data, callback_handle );
|
||||
}
|
||||
|
||||
|
||||
void ICACHE_FLASH_ATTR http_put( const char * url, const char * headers, const char * post_data, http_callback_t callback_handle )
|
||||
{
|
||||
http_request( url, "PUT", headers, post_data, callback_handle );
|
||||
}
|
||||
|
||||
|
||||
void ICACHE_FLASH_ATTR http_callback_example( char * response, int http_status, char * full_response )
|
||||
{
|
||||
os_printf( "http_status=%d\n", http_status );
|
||||
if ( http_status != HTTP_STATUS_GENERIC_ERROR )
|
||||
{
|
||||
os_printf( "strlen(full_response)=%d\n", strlen( full_response ) );
|
||||
os_printf( "response=%s<EOF>\n", response );
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,97 +0,0 @@
|
||||
/*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* Martin d'Allens <martin.dallens@gmail.com> wrote this file. As long as you retain
|
||||
* this notice you can do whatever you want with this stuff. If we meet some day,
|
||||
* and you think this stuff is worth it, you can buy me a beer in return.
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef __HTTPCLIENT_H__
|
||||
#define __HTTPCLIENT_H__
|
||||
|
||||
#if defined(GLOBAL_DEBUG_ON)
|
||||
#define HTTPCLIENT_DEBUG_ON
|
||||
#endif
|
||||
#if defined(HTTPCLIENT_DEBUG_ON)
|
||||
#define HTTPCLIENT_DEBUG(format, ...) os_printf(format, ##__VA_ARGS__)
|
||||
#else
|
||||
#define HTTPCLIENT_DEBUG(format, ...)
|
||||
#endif
|
||||
|
||||
#if defined(USES_SDK_BEFORE_V140)
|
||||
#define espconn_send espconn_sent
|
||||
#define espconn_secure_send espconn_secure_sent
|
||||
#endif
|
||||
|
||||
/*
|
||||
* In case of TCP or DNS error the callback is called with this status.
|
||||
*/
|
||||
#define HTTP_STATUS_GENERIC_ERROR (-1)
|
||||
|
||||
/*
|
||||
* Size of http responses that will cause an error.
|
||||
*/
|
||||
#define BUFFER_SIZE_MAX (0x2000)
|
||||
|
||||
/*
|
||||
* Timeout of http request.
|
||||
*/
|
||||
#define HTTP_REQUEST_TIMEOUT_MS (10000)
|
||||
|
||||
/*
|
||||
* "full_response" is a string containing all response headers and the response body.
|
||||
* "response_body and "http_status" are extracted from "full_response" for convenience.
|
||||
*
|
||||
* A successful request corresponds to an HTTP status code of 200 (OK).
|
||||
* More info at http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
|
||||
*/
|
||||
typedef void (* http_callback_t)(char * response_body, int http_status, char * full_response);
|
||||
|
||||
/*
|
||||
* Call this function to skip URL parsing if the arguments are already in separate variables.
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR http_raw_request(const char * hostname, int port, bool secure, const char * method, const char * path, const char * headers, const char * post_data, http_callback_t callback_handle);
|
||||
|
||||
/*
|
||||
* Request data from URL use custom method.
|
||||
* The data should be encoded as any format.
|
||||
* Try:
|
||||
* http_request("http://httpbin.org/post", "OPTIONS", "Content-type: text/plain", "Hello world", http_callback_example);
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR http_request(const char * url, const char * method, const char * headers, const char * post_data, http_callback_t callback_handle);
|
||||
|
||||
/*
|
||||
* Post data to a web form.
|
||||
* The data should be encoded as any format.
|
||||
* Try:
|
||||
* http_post("http://httpbin.org/post", "Content-type: application/json", "{\"hello\": \"world\"}", http_callback_example);
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR http_post(const char * url, const char * headers, const char * post_data, http_callback_t callback_handle);
|
||||
|
||||
/*
|
||||
* Download a web page from its URL.
|
||||
* Try:
|
||||
* http_get("http://wtfismyip.com/text", NULL, http_callback_example);
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR http_get(const char * url, const char * headers, http_callback_t callback_handle);
|
||||
/*
|
||||
* Delete a web page from its URL.
|
||||
* Try:
|
||||
* http_delete("http://wtfismyip.com/text", NULL, http_callback_example);
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR http_delete(const char * url, const char * headers, const char * post_data, http_callback_t callback_handle);
|
||||
/*
|
||||
* Update data to a web form.
|
||||
* The data should be encoded as any format.
|
||||
* Try:
|
||||
* http_put("http://httpbin.org/post", "Content-type: application/json", "{\"hello\": \"world\"}", http_callback_example);
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR http_put(const char * url, const char * headers, const char * post_data, http_callback_t callback_handle);
|
||||
|
||||
/*
|
||||
* Output on the UART.
|
||||
*/
|
||||
void http_callback_example(char * response, int http_status, char * full_response);
|
||||
|
||||
#endif // __HTTPCLIENT_H__
|
@ -1,9 +0,0 @@
|
||||
#ifndef __GPIO16_H__
|
||||
#define __GPIO16_H__
|
||||
|
||||
void gpio16_output_conf(void);
|
||||
void gpio16_output_set(uint8 value);
|
||||
void gpio16_input_conf(void);
|
||||
uint8 gpio16_input_get(void);
|
||||
|
||||
#endif
|
@ -1,74 +0,0 @@
|
||||
#ifndef __I2C_MASTER_H__
|
||||
#define __I2C_MASTER_H__
|
||||
|
||||
#define I2C_MASTER_SDA_MUX (pin_mux[sda])
|
||||
#define I2C_MASTER_SCL_MUX (pin_mux[scl])
|
||||
#define I2C_MASTER_SDA_GPIO (pinSDA)
|
||||
#define I2C_MASTER_SCL_GPIO (pinSCL)
|
||||
#define I2C_MASTER_SDA_FUNC (pin_func[sda])
|
||||
#define I2C_MASTER_SCL_FUNC (pin_func[scl])
|
||||
|
||||
// #define I2C_MASTER_SDA_MUX PERIPHS_IO_MUX_GPIO2_U
|
||||
// #define I2C_MASTER_SCL_MUX PERIPHS_IO_MUX_MTDO_U
|
||||
// #define I2C_MASTER_SDA_GPIO 2
|
||||
// #define I2C_MASTER_SCL_GPIO 15
|
||||
// #define I2C_MASTER_SDA_FUNC FUNC_GPIO2
|
||||
// #define I2C_MASTER_SCL_FUNC FUNC_GPIO15
|
||||
|
||||
// #define I2C_MASTER_SDA_MUX PERIPHS_IO_MUX_GPIO2_U
|
||||
// #define I2C_MASTER_SCL_MUX PERIPHS_IO_MUX_MTMS_U
|
||||
// #define I2C_MASTER_SDA_GPIO 2
|
||||
// #define I2C_MASTER_SCL_GPIO 14
|
||||
// #define I2C_MASTER_SDA_FUNC FUNC_GPIO2
|
||||
// #define I2C_MASTER_SCL_FUNC FUNC_GPIO14
|
||||
|
||||
//#define I2C_MASTER_SDA_MUX PERIPHS_IO_MUX_GPIO2_U
|
||||
//#define I2C_MASTER_SCL_MUX PERIPHS_IO_MUX_GPIO0_U
|
||||
//#define I2C_MASTER_SDA_GPIO 2
|
||||
//#define I2C_MASTER_SCL_GPIO 0
|
||||
//#define I2C_MASTER_SDA_FUNC FUNC_GPIO2
|
||||
//#define I2C_MASTER_SCL_FUNC FUNC_GPIO0
|
||||
|
||||
#if 0
|
||||
#define I2C_MASTER_GPIO_SET(pin) \
|
||||
gpio_output_set(1<<pin,0,1<<pin,0)
|
||||
|
||||
#define I2C_MASTER_GPIO_CLR(pin) \
|
||||
gpio_output_set(0,1<<pin,1<<pin,0)
|
||||
|
||||
#define I2C_MASTER_GPIO_OUT(pin,val) \
|
||||
if(val) I2C_MASTER_GPIO_SET(pin);\
|
||||
else I2C_MASTER_GPIO_CLR(pin)
|
||||
#endif
|
||||
|
||||
#define I2C_MASTER_SDA_HIGH_SCL_HIGH() \
|
||||
gpio_output_set(1<<I2C_MASTER_SDA_GPIO | 1<<I2C_MASTER_SCL_GPIO, 0, 1<<I2C_MASTER_SDA_GPIO | 1<<I2C_MASTER_SCL_GPIO, 0)
|
||||
|
||||
#define I2C_MASTER_SDA_HIGH_SCL_LOW() \
|
||||
gpio_output_set(1<<I2C_MASTER_SDA_GPIO, 1<<I2C_MASTER_SCL_GPIO, 1<<I2C_MASTER_SDA_GPIO | 1<<I2C_MASTER_SCL_GPIO, 0)
|
||||
|
||||
#define I2C_MASTER_SDA_LOW_SCL_HIGH() \
|
||||
gpio_output_set(1<<I2C_MASTER_SCL_GPIO, 1<<I2C_MASTER_SDA_GPIO, 1<<I2C_MASTER_SDA_GPIO | 1<<I2C_MASTER_SCL_GPIO, 0)
|
||||
|
||||
#define I2C_MASTER_SDA_LOW_SCL_LOW() \
|
||||
gpio_output_set(0, 1<<I2C_MASTER_SDA_GPIO | 1<<I2C_MASTER_SCL_GPIO, 1<<I2C_MASTER_SDA_GPIO | 1<<I2C_MASTER_SCL_GPIO, 0)
|
||||
|
||||
void i2c_master_gpio_init(uint8 sda, uint8 scl);
|
||||
void i2c_master_init(void);
|
||||
|
||||
#define i2c_master_wait os_delay_us
|
||||
void i2c_master_stop(void);
|
||||
void i2c_master_start(void);
|
||||
void i2c_master_setAck(uint8 level);
|
||||
uint8 i2c_master_getAck(void);
|
||||
uint8 i2c_master_readByte(void);
|
||||
void i2c_master_writeByte(uint8 wrdata);
|
||||
|
||||
bool i2c_master_checkAck(void);
|
||||
void i2c_master_send_ack(void);
|
||||
void i2c_master_send_nack(void);
|
||||
|
||||
uint8 i2c_master_get_pinSDA();
|
||||
uint8 i2c_master_get_pinSCL();
|
||||
|
||||
#endif
|
@ -1,27 +0,0 @@
|
||||
#ifndef __KEY_H__
|
||||
#define __KEY_H__
|
||||
|
||||
#include "gpio.h"
|
||||
|
||||
typedef void (* key_function)(void);
|
||||
|
||||
struct single_key_param {
|
||||
uint8 key_level;
|
||||
uint8 gpio_id;
|
||||
uint8 gpio_func;
|
||||
uint32 gpio_name;
|
||||
os_timer_t key_5s;
|
||||
os_timer_t key_50ms;
|
||||
key_function short_press;
|
||||
key_function long_press;
|
||||
};
|
||||
|
||||
struct keys_param {
|
||||
uint8 key_num;
|
||||
struct single_key_param **single_key;
|
||||
};
|
||||
|
||||
struct single_key_param *key_init_single(uint8 gpio_id, uint32 gpio_name, uint8 gpio_func, key_function long_press, key_function short_press);
|
||||
void key_init(struct keys_param *key);
|
||||
|
||||
#endif
|
@ -1,150 +0,0 @@
|
||||
#ifndef __ONEWIRE_H__
|
||||
#define __ONEWIRE_H__
|
||||
|
||||
#include "c_types.h"
|
||||
|
||||
// You can exclude certain features from OneWire. In theory, this
|
||||
// might save some space. In practice, the compiler automatically
|
||||
// removes unused code (technically, the linker, using -fdata-sections
|
||||
// and -ffunction-sections when compiling, and Wl,--gc-sections
|
||||
// when linking), so most of these will not result in any code size
|
||||
// reduction. Well, unless you try to use the missing features
|
||||
// and redesign your program to not need them! ONEWIRE_CRC8_TABLE
|
||||
// is the exception, because it selects a fast but large algorithm
|
||||
// or a small but slow algorithm.
|
||||
|
||||
// you can exclude onewire_search by defining that to 0
|
||||
#ifndef ONEWIRE_SEARCH
|
||||
#define ONEWIRE_SEARCH 1
|
||||
#endif
|
||||
|
||||
// You can exclude CRC checks altogether by defining this to 0
|
||||
#ifndef ONEWIRE_CRC
|
||||
#define ONEWIRE_CRC 1
|
||||
#endif
|
||||
|
||||
// Select the table-lookup method of computing the 8-bit CRC
|
||||
// by setting this to 1. The lookup table enlarges code size by
|
||||
// about 250 bytes. It does NOT consume RAM (but did in very
|
||||
// old versions of OneWire). If you disable this, a slower
|
||||
// but very compact algorithm is used.
|
||||
#ifndef ONEWIRE_CRC8_TABLE
|
||||
#define ONEWIRE_CRC8_TABLE 0
|
||||
#endif
|
||||
|
||||
// You can allow 16-bit CRC checks by defining this to 1
|
||||
// (Note that ONEWIRE_CRC must also be 1.)
|
||||
#ifndef ONEWIRE_CRC16
|
||||
#define ONEWIRE_CRC16 1
|
||||
#endif
|
||||
|
||||
// Platform specific I/O definitions
|
||||
|
||||
#define DIRECT_READ(pin) (0x1 & GPIO_INPUT_GET(GPIO_ID_PIN(pin_num[pin])))
|
||||
#define DIRECT_MODE_INPUT(pin) GPIO_DIS_OUTPUT(pin_num[pin])
|
||||
#define DIRECT_MODE_OUTPUT(pin)
|
||||
#define DIRECT_WRITE_LOW(pin) (GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[pin]), 0))
|
||||
#define DIRECT_WRITE_HIGH(pin) (GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[pin]), 1))
|
||||
|
||||
void onewire_init(uint8_t pin);
|
||||
|
||||
// Perform a 1-Wire reset cycle. Returns 1 if a device responds
|
||||
// with a presence pulse. Returns 0 if there is no device or the
|
||||
// bus is shorted or otherwise held low for more than 250uS
|
||||
uint8_t onewire_reset(uint8_t pin);
|
||||
|
||||
// Issue a 1-Wire rom select command, you do the reset first.
|
||||
void onewire_select(uint8_t pin, const uint8_t rom[8]);
|
||||
|
||||
// Issue a 1-Wire rom skip command, to address all on bus.
|
||||
void onewire_skip(uint8_t pin);
|
||||
|
||||
// Write a byte. If 'power' is one then the wire is held high at
|
||||
// the end for parasitically powered devices. You are responsible
|
||||
// for eventually depowering it by calling depower() or doing
|
||||
// another read or write.
|
||||
void onewire_write(uint8_t pin, uint8_t v, uint8_t power);
|
||||
|
||||
void onewire_write_bytes(uint8_t pin, const uint8_t *buf, uint16_t count, bool power);
|
||||
|
||||
// Read a byte.
|
||||
uint8_t onewire_read(uint8_t pin);
|
||||
|
||||
void onewire_read_bytes(uint8_t pin, uint8_t *buf, uint16_t count);
|
||||
|
||||
// Write a bit. The bus is always left powered at the end, see
|
||||
// note in write() about that.
|
||||
// void onewire_write_bit(uint8_t pin, uint8_t v);
|
||||
|
||||
// Read a bit.
|
||||
// uint8_t onewire_read_bit(uint8_t pin);
|
||||
|
||||
// Stop forcing power onto the bus. You only need to do this if
|
||||
// you used the 'power' flag to write() or used a write_bit() call
|
||||
// and aren't about to do another read or write. You would rather
|
||||
// not leave this powered if you don't have to, just in case
|
||||
// someone shorts your bus.
|
||||
void onewire_depower(uint8_t pin);
|
||||
|
||||
#if ONEWIRE_SEARCH
|
||||
// Clear the search state so that if will start from the beginning again.
|
||||
void onewire_reset_search(uint8_t pin);
|
||||
|
||||
// Setup the search to find the device type 'family_code' on the next call
|
||||
// to search(*newAddr) if it is present.
|
||||
void onewire_target_search(uint8_t pin, uint8_t family_code);
|
||||
|
||||
// Look for the next device. Returns 1 if a new address has been
|
||||
// returned. A zero might mean that the bus is shorted, there are
|
||||
// no devices, or you have already retrieved all of them. It
|
||||
// might be a good idea to check the CRC to make sure you didn't
|
||||
// get garbage. The order is deterministic. You will always get
|
||||
// the same devices in the same order.
|
||||
uint8_t onewire_search(uint8_t pin, uint8_t *newAddr);
|
||||
#endif
|
||||
|
||||
#if ONEWIRE_CRC
|
||||
// Compute a Dallas Semiconductor 8 bit CRC, these are used in the
|
||||
// ROM and scratchpad registers.
|
||||
uint8_t onewire_crc8(const uint8_t *addr, uint8_t len);
|
||||
|
||||
#if ONEWIRE_CRC16
|
||||
// Compute the 1-Wire CRC16 and compare it against the received CRC.
|
||||
// Example usage (reading a DS2408):
|
||||
// // Put everything in a buffer so we can compute the CRC easily.
|
||||
// uint8_t buf[13];
|
||||
// buf[0] = 0xF0; // Read PIO Registers
|
||||
// buf[1] = 0x88; // LSB address
|
||||
// buf[2] = 0x00; // MSB address
|
||||
// WriteBytes(net, buf, 3); // Write 3 cmd bytes
|
||||
// ReadBytes(net, buf+3, 10); // Read 6 data bytes, 2 0xFF, 2 CRC16
|
||||
// if (!CheckCRC16(buf, 11, &buf[11])) {
|
||||
// // Handle error.
|
||||
// }
|
||||
//
|
||||
// @param input - Array of bytes to checksum.
|
||||
// @param len - How many bytes to use.
|
||||
// @param inverted_crc - The two CRC16 bytes in the received data.
|
||||
// This should just point into the received data,
|
||||
// *not* at a 16-bit integer.
|
||||
// @param crc - The crc starting value (optional)
|
||||
// @return True, iff the CRC matches.
|
||||
bool onewire_check_crc16(const uint8_t* input, uint16_t len, const uint8_t* inverted_crc, uint16_t crc);
|
||||
|
||||
// Compute a Dallas Semiconductor 16 bit CRC. This is required to check
|
||||
// the integrity of data received from many 1-Wire devices. Note that the
|
||||
// CRC computed here is *not* what you'll get from the 1-Wire network,
|
||||
// for two reasons:
|
||||
// 1) The CRC is transmitted bitwise inverted.
|
||||
// 2) Depending on the endian-ness of your processor, the binary
|
||||
// representation of the two-byte return value may have a different
|
||||
// byte order than the two bytes you get from 1-Wire.
|
||||
// @param input - Array of bytes to checksum.
|
||||
// @param len - How many bytes to use.
|
||||
// @param crc - The crc starting value (optional)
|
||||
// @return The CRC16, as defined by Dallas Semiconductor.
|
||||
uint16_t onewire_crc16(const uint8_t* input, uint16_t len, uint16_t crc);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,46 +0,0 @@
|
||||
#ifndef __PWM_H__
|
||||
#define __PWM_H__
|
||||
|
||||
#define PWM_CHANNEL 6
|
||||
|
||||
struct pwm_single_param {
|
||||
uint16 gpio_set;
|
||||
uint16 gpio_clear;
|
||||
uint32 h_time;
|
||||
};
|
||||
|
||||
struct pwm_param {
|
||||
uint32 period;
|
||||
uint16 freq;
|
||||
uint16 duty[PWM_CHANNEL];
|
||||
};
|
||||
|
||||
#define PWM_DEPTH 1023
|
||||
#define PWM_FREQ_MAX 1000
|
||||
|
||||
#define PWM_1S 1000000
|
||||
|
||||
// #define PWM_0_OUT_IO_MUX PERIPHS_IO_MUX_MTMS_U
|
||||
// #define PWM_0_OUT_IO_NUM 14
|
||||
// #define PWM_0_OUT_IO_FUNC FUNC_GPIO14
|
||||
|
||||
// #define PWM_1_OUT_IO_MUX PERIPHS_IO_MUX_MTDI_U
|
||||
// #define PWM_1_OUT_IO_NUM 12
|
||||
// #define PWM_1_OUT_IO_FUNC FUNC_GPIO12
|
||||
|
||||
// #define PWM_2_OUT_IO_MUX PERIPHS_IO_MUX_MTCK_U
|
||||
// #define PWM_2_OUT_IO_NUM 13
|
||||
// #define PWM_2_OUT_IO_FUNC FUNC_GPIO13
|
||||
|
||||
void pwm_init(uint16 freq, uint16 *duty);
|
||||
bool pwm_start(void);
|
||||
|
||||
void pwm_set_duty(uint16 duty, uint8 channel);
|
||||
uint16 pwm_get_duty(uint8 channel);
|
||||
void pwm_set_freq(uint16 freq, uint8 channel);
|
||||
uint16 pwm_get_freq(uint8 channel);
|
||||
bool pwm_add(uint8 channel);
|
||||
bool pwm_delete(uint8 channel);
|
||||
bool pwm_exist(uint8 channel);
|
||||
#endif
|
||||
|
@ -1,8 +0,0 @@
|
||||
#ifndef READLINE_APP_H
|
||||
#define READLINE_APP_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
bool uart_getc(char *c);
|
||||
|
||||
#endif /* READLINE_APP_H */
|
@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Definitions to access the Rotary driver
|
||||
*/
|
||||
#ifndef __ROTARY_H__
|
||||
#define __ROTARY_H__
|
||||
|
||||
#include "c_types.h"
|
||||
|
||||
#define ROTARY_CHANNEL_COUNT 3
|
||||
|
||||
typedef struct {
|
||||
uint32_t pos;
|
||||
uint32_t time_us;
|
||||
} rotary_event_t;
|
||||
|
||||
int rotary_setup(uint32_t channel, int phaseA, int phaseB, int press, task_handle_t tasknumber);
|
||||
|
||||
bool rotary_getevent(uint32_t channel, rotary_event_t *result);
|
||||
|
||||
bool rotary_has_queued_event(uint32_t channel);
|
||||
|
||||
int rotary_getpos(uint32_t channel);
|
||||
|
||||
int rotary_close(uint32_t channel);
|
||||
|
||||
#endif
|
@ -1,11 +0,0 @@
|
||||
#ifndef SIGMA_DELTA_APP_H
|
||||
#define SIGMA_DELTA_APP_H
|
||||
|
||||
#include "eagle_soc.h"
|
||||
#include "c_types.h"
|
||||
|
||||
void sigma_delta_setup( void );
|
||||
void sigma_delta_stop( void );
|
||||
void sigma_delta_set_prescale_target( sint16 prescale, sint16 target );
|
||||
|
||||
#endif
|
@ -1,56 +0,0 @@
|
||||
#ifndef SPI_APP_H
|
||||
#define SPI_APP_H
|
||||
|
||||
#ifdef __ESP8266__
|
||||
# include "spi_register.h"
|
||||
#endif
|
||||
#include "eagle_soc.h"
|
||||
#include "rom.h"
|
||||
#include "osapi.h"
|
||||
#include "uart.h"
|
||||
#include "os_type.h"
|
||||
|
||||
/*SPI number define*/
|
||||
#define SPI 0
|
||||
#define HSPI 1
|
||||
|
||||
|
||||
|
||||
//lcd drive function
|
||||
void spi_lcd_mode_init(uint8 spi_no);
|
||||
void spi_lcd_9bit_write(uint8 spi_no,uint8 high_bit,uint8 low_8bit);
|
||||
|
||||
//spi master init funtion
|
||||
void spi_master_init(uint8 spi_no, unsigned cpol, unsigned cpha, uint32_t clock_div);
|
||||
// fill MOSI buffer
|
||||
void spi_mast_set_mosi(uint8 spi_no, uint16 offset, uint8 bitlen, uint32 data);
|
||||
// retrieve data from MISO buffer
|
||||
uint32 spi_mast_get_miso(uint8 spi_no, uint16 offset, uint8 bitlen);
|
||||
// initiate SPI transaction
|
||||
void spi_mast_transaction(uint8 spi_no, uint8 cmd_bitlen, uint16 cmd_data, uint8 addr_bitlen, uint32 addr_data,
|
||||
uint16 mosi_bitlen, uint8 dummy_bitlen, sint16 miso_bitlen);
|
||||
|
||||
//transmit data to esp8266 slave buffer,which needs 16bit transmission ,
|
||||
//first byte is master command 0x04, second byte is master data
|
||||
void spi_byte_write_espslave(uint8 spi_no,uint8 data);
|
||||
//read data from esp8266 slave buffer,which needs 16bit transmission ,
|
||||
//first byte is master command 0x06, second byte is to read slave data
|
||||
void spi_byte_read_espslave(uint8 spi_no,uint8 *data);
|
||||
|
||||
//esp8266 slave mode initial
|
||||
void spi_slave_init(uint8 spi_no);
|
||||
//esp8266 slave isr handle funtion,tiggered when any transmission is finished.
|
||||
//the function is registered in spi_slave_init.
|
||||
void spi_slave_isr_handler(void *para);
|
||||
|
||||
|
||||
//hspi test function, used to test esp8266 spi slave
|
||||
void hspi_master_readwrite_repeat(void);
|
||||
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
spi_test_init(void);
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -1,189 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2010 - 2011 Espressif System
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SPI_REGISTER_H_INCLUDED
|
||||
#define SPI_REGISTER_H_INCLUDED
|
||||
|
||||
#define REG_SPI_BASE(i) (0x60000200-i*0x100)
|
||||
#define SPI_CMD(i) (REG_SPI_BASE(i) + 0x0)
|
||||
#define SPI_USR (BIT(18))
|
||||
|
||||
#define SPI_ADDR(i) (REG_SPI_BASE(i) + 0x4)
|
||||
|
||||
#define SPI_CTRL(i) (REG_SPI_BASE(i) + 0x8)
|
||||
#define SPI_WR_BIT_ORDER (BIT(26))
|
||||
#define SPI_RD_BIT_ORDER (BIT(25))
|
||||
#define SPI_QIO_MODE (BIT(24))
|
||||
#define SPI_DIO_MODE (BIT(23))
|
||||
#define SPI_QOUT_MODE (BIT(20))
|
||||
#define SPI_DOUT_MODE (BIT(14))
|
||||
#define SPI_FASTRD_MODE (BIT(13))
|
||||
|
||||
|
||||
|
||||
#define SPI_RD_STATUS(i) (REG_SPI_BASE(i) + 0x10)
|
||||
|
||||
#define SPI_CTRL2(i) (REG_SPI_BASE(i) + 0x14)
|
||||
|
||||
#define SPI_CS_DELAY_NUM 0x0000000F
|
||||
#define SPI_CS_DELAY_NUM_S 28
|
||||
#define SPI_CS_DELAY_MODE 0x00000003
|
||||
#define SPI_CS_DELAY_MODE_S 26
|
||||
#define SPI_MOSI_DELAY_NUM 0x00000007
|
||||
#define SPI_MOSI_DELAY_NUM_S 23
|
||||
#define SPI_MOSI_DELAY_MODE 0x00000003
|
||||
#define SPI_MOSI_DELAY_MODE_S 21
|
||||
#define SPI_MISO_DELAY_NUM 0x00000007
|
||||
#define SPI_MISO_DELAY_NUM_S 18
|
||||
#define SPI_MISO_DELAY_MODE 0x00000003
|
||||
#define SPI_MISO_DELAY_MODE_S 16
|
||||
#define SPI_CK_OUT_HIGH_MODE 0x0000000F
|
||||
#define SPI_CK_OUT_HIGH_MODE_S 12
|
||||
#define SPI_CK_OUT_LOW_MODE 0x0000000F
|
||||
#define SPI_CK_OUT_LOW_MODE_S 8
|
||||
|
||||
#define SPI_CLOCK(i) (REG_SPI_BASE(i) + 0x18)
|
||||
#define SPI_CLK_EQU_SYSCLK (BIT(31))
|
||||
#define SPI_CLKDIV_PRE 0x00001FFF
|
||||
#define SPI_CLKDIV_PRE_S 18
|
||||
#define SPI_CLKCNT_N 0x0000003F
|
||||
#define SPI_CLKCNT_N_S 12
|
||||
#define SPI_CLKCNT_H 0x0000003F
|
||||
#define SPI_CLKCNT_H_S 6
|
||||
#define SPI_CLKCNT_L 0x0000003F
|
||||
#define SPI_CLKCNT_L_S 0
|
||||
|
||||
#define SPI_USER(i) (REG_SPI_BASE(i) + 0x1C)
|
||||
#define SPI_USR_COMMAND (BIT(31))
|
||||
#define SPI_USR_ADDR (BIT(30))
|
||||
#define SPI_USR_DUMMY (BIT(29))
|
||||
#define SPI_USR_MISO (BIT(28))
|
||||
#define SPI_USR_MOSI (BIT(27))
|
||||
|
||||
#define SPI_USR_MOSI_HIGHPART (BIT(25))
|
||||
#define SPI_USR_MISO_HIGHPART (BIT(24))
|
||||
|
||||
|
||||
#define SPI_SIO (BIT(16))
|
||||
#define SPI_FWRITE_QIO (BIT(15))
|
||||
#define SPI_FWRITE_DIO (BIT(14))
|
||||
#define SPI_FWRITE_QUAD (BIT(13))
|
||||
#define SPI_FWRITE_DUAL (BIT(12))
|
||||
#define SPI_WR_BYTE_ORDER (BIT(11))
|
||||
#define SPI_RD_BYTE_ORDER (BIT(10))
|
||||
#define SPI_CK_OUT_EDGE (BIT(7))
|
||||
#define SPI_CK_I_EDGE (BIT(6))
|
||||
#define SPI_CS_SETUP (BIT(5))
|
||||
#define SPI_CS_HOLD (BIT(4))
|
||||
#define SPI_FLASH_MODE (BIT(2))
|
||||
#define SPI_DOUTDIN (BIT(0))
|
||||
|
||||
#define SPI_USER1(i) (REG_SPI_BASE(i) + 0x20)
|
||||
#define SPI_USR_ADDR_BITLEN 0x0000003F
|
||||
#define SPI_USR_ADDR_BITLEN_S 26
|
||||
#define SPI_USR_MOSI_BITLEN 0x000001FF
|
||||
#define SPI_USR_MOSI_BITLEN_S 17
|
||||
#define SPI_USR_MISO_BITLEN 0x000001FF
|
||||
#define SPI_USR_MISO_BITLEN_S 8
|
||||
|
||||
#define SPI_USR_DUMMY_CYCLELEN 0x000000FF
|
||||
#define SPI_USR_DUMMY_CYCLELEN_S 0
|
||||
|
||||
#define SPI_USER2(i) (REG_SPI_BASE(i) + 0x24)
|
||||
#define SPI_USR_COMMAND_BITLEN 0x0000000F
|
||||
#define SPI_USR_COMMAND_BITLEN_S 28
|
||||
#define SPI_USR_COMMAND_VALUE 0x0000FFFF
|
||||
#define SPI_USR_COMMAND_VALUE_S 0
|
||||
|
||||
#define SPI_WR_STATUS(i) (REG_SPI_BASE(i) + 0x28)
|
||||
#define SPI_PIN(i) (REG_SPI_BASE(i) + 0x2C)
|
||||
#define SPI_CS2_DIS (BIT(2))
|
||||
#define SPI_CS1_DIS (BIT(1))
|
||||
#define SPI_CS0_DIS (BIT(0))
|
||||
#define SPI_IDLE_EDGE (BIT(29))
|
||||
|
||||
#define SPI_SLAVE(i) (REG_SPI_BASE(i) + 0x30)
|
||||
#define SPI_SYNC_RESET (BIT(31))
|
||||
#define SPI_SLAVE_MODE (BIT(30))
|
||||
#define SPI_SLV_WR_RD_BUF_EN (BIT(29))
|
||||
#define SPI_SLV_WR_RD_STA_EN (BIT(28))
|
||||
#define SPI_SLV_CMD_DEFINE (BIT(27))
|
||||
#define SPI_TRANS_CNT 0x0000000F
|
||||
#define SPI_TRANS_CNT_S 23
|
||||
#define SPI_TRANS_DONE_EN (BIT(9))
|
||||
#define SPI_SLV_WR_STA_DONE_EN (BIT(8))
|
||||
#define SPI_SLV_RD_STA_DONE_EN (BIT(7))
|
||||
#define SPI_SLV_WR_BUF_DONE_EN (BIT(6))
|
||||
#define SPI_SLV_RD_BUF_DONE_EN (BIT(5))
|
||||
|
||||
|
||||
|
||||
#define SLV_SPI_INT_EN 0x0000001f
|
||||
#define SLV_SPI_INT_EN_S 5
|
||||
|
||||
#define SPI_TRANS_DONE (BIT(4))
|
||||
#define SPI_SLV_WR_STA_DONE (BIT(3))
|
||||
#define SPI_SLV_RD_STA_DONE (BIT(2))
|
||||
#define SPI_SLV_WR_BUF_DONE (BIT(1))
|
||||
#define SPI_SLV_RD_BUF_DONE (BIT(0))
|
||||
|
||||
#define SPI_SLAVE1(i) (REG_SPI_BASE(i) + 0x34)
|
||||
#define SPI_SLV_STATUS_BITLEN 0x0000001F
|
||||
#define SPI_SLV_STATUS_BITLEN_S 27
|
||||
#define SPI_SLV_BUF_BITLEN 0x000001FF
|
||||
#define SPI_SLV_BUF_BITLEN_S 16
|
||||
#define SPI_SLV_RD_ADDR_BITLEN 0x0000003F
|
||||
#define SPI_SLV_RD_ADDR_BITLEN_S 10
|
||||
#define SPI_SLV_WR_ADDR_BITLEN 0x0000003F
|
||||
#define SPI_SLV_WR_ADDR_BITLEN_S 4
|
||||
|
||||
#define SPI_SLV_WRSTA_DUMMY_EN (BIT(3))
|
||||
#define SPI_SLV_RDSTA_DUMMY_EN (BIT(2))
|
||||
#define SPI_SLV_WRBUF_DUMMY_EN (BIT(1))
|
||||
#define SPI_SLV_RDBUF_DUMMY_EN (BIT(0))
|
||||
|
||||
|
||||
|
||||
#define SPI_SLAVE2(i) (REG_SPI_BASE(i) + 0x38)
|
||||
#define SPI_SLV_WRBUF_DUMMY_CYCLELEN 0X000000FF
|
||||
#define SPI_SLV_WRBUF_DUMMY_CYCLELEN_S 24
|
||||
#define SPI_SLV_RDBUF_DUMMY_CYCLELEN 0X000000FF
|
||||
#define SPI_SLV_RDBUF_DUMMY_CYCLELEN_S 16
|
||||
#define SPI_SLV_WRSTR_DUMMY_CYCLELEN 0X000000FF
|
||||
#define SPI_SLV_WRSTR_DUMMY_CYCLELEN_S 8
|
||||
#define SPI_SLV_RDSTR_DUMMY_CYCLELEN 0x000000FF
|
||||
#define SPI_SLV_RDSTR_DUMMY_CYCLELEN_S 0
|
||||
|
||||
#define SPI_SLAVE3(i) (REG_SPI_BASE(i) + 0x3C)
|
||||
#define SPI_SLV_WRSTA_CMD_VALUE 0x000000FF
|
||||
#define SPI_SLV_WRSTA_CMD_VALUE_S 24
|
||||
#define SPI_SLV_RDSTA_CMD_VALUE 0x000000FF
|
||||
#define SPI_SLV_RDSTA_CMD_VALUE_S 16
|
||||
#define SPI_SLV_WRBUF_CMD_VALUE 0x000000FF
|
||||
#define SPI_SLV_WRBUF_CMD_VALUE_S 8
|
||||
#define SPI_SLV_RDBUF_CMD_VALUE 0x000000FF
|
||||
#define SPI_SLV_RDBUF_CMD_VALUE_S 0
|
||||
|
||||
#define SPI_W0(i) (REG_SPI_BASE(i) +0x40)
|
||||
#define SPI_W1(i) (REG_SPI_BASE(i) +0x44)
|
||||
#define SPI_W2(i) (REG_SPI_BASE(i) +0x48)
|
||||
#define SPI_W3(i) (REG_SPI_BASE(i) +0x4C)
|
||||
#define SPI_W4(i) (REG_SPI_BASE(i) +0x50)
|
||||
#define SPI_W5(i) (REG_SPI_BASE(i) +0x54)
|
||||
#define SPI_W6(i) (REG_SPI_BASE(i) +0x58)
|
||||
#define SPI_W7(i) (REG_SPI_BASE(i) +0x5C)
|
||||
#define SPI_W8(i) (REG_SPI_BASE(i) +0x60)
|
||||
#define SPI_W9(i) (REG_SPI_BASE(i) +0x64)
|
||||
#define SPI_W10(i) (REG_SPI_BASE(i) +0x68)
|
||||
#define SPI_W11(i) (REG_SPI_BASE(i) +0x6C)
|
||||
#define SPI_W12(i) (REG_SPI_BASE(i) +0x70)
|
||||
#define SPI_W13(i) (REG_SPI_BASE(i) +0x74)
|
||||
#define SPI_W14(i) (REG_SPI_BASE(i) +0x78)
|
||||
#define SPI_W15(i) (REG_SPI_BASE(i) +0x7C)
|
||||
|
||||
#define SPI_EXT3(i) (REG_SPI_BASE(i) + 0xFC)
|
||||
#define SPI_INT_HOLD_ENA 0x00000003
|
||||
#define SPI_INT_HOLD_ENA_S 0
|
||||
#endif // SPI_REGISTER_H_INCLUDED
|
@ -1,332 +0,0 @@
|
||||
/*
|
||||
* ESPRSSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2015 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS ESP32 only, in which case,
|
||||
* it is 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 __UART_H__
|
||||
#define __UART_H__
|
||||
|
||||
#include "c_types.h" /* for BIT(n) definition */
|
||||
#include "uart_register.h"
|
||||
#include "task/task.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
UART_WordLength_5b = 0x0,
|
||||
UART_WordLength_6b = 0x1,
|
||||
UART_WordLength_7b = 0x2,
|
||||
UART_WordLength_8b = 0x3
|
||||
} UART_WordLength;
|
||||
|
||||
typedef enum {
|
||||
USART_StopBits_1 = 0x1,
|
||||
USART_StopBits_1_5 = 0x2,
|
||||
USART_StopBits_2 = 0x3,
|
||||
} UART_StopBits;
|
||||
|
||||
typedef enum {
|
||||
UART0 = 0x0,
|
||||
UART1 = 0x1,
|
||||
} UART_Port;
|
||||
|
||||
typedef enum {
|
||||
USART_Parity_None = 0x2,
|
||||
USART_Parity_Even = 0x0,
|
||||
USART_Parity_Odd = 0x1
|
||||
} UART_ParityMode;
|
||||
|
||||
typedef enum {
|
||||
BIT_RATE_300 = 300,
|
||||
BIT_RATE_600 = 600,
|
||||
BIT_RATE_1200 = 1200,
|
||||
BIT_RATE_2400 = 2400,
|
||||
BIT_RATE_4800 = 4800,
|
||||
BIT_RATE_9600 = 9600,
|
||||
BIT_RATE_19200 = 19200,
|
||||
BIT_RATE_38400 = 38400,
|
||||
BIT_RATE_57600 = 57600,
|
||||
BIT_RATE_74880 = 74880,
|
||||
BIT_RATE_115200 = 115200,
|
||||
BIT_RATE_230400 = 230400,
|
||||
BIT_RATE_460800 = 460800,
|
||||
BIT_RATE_921600 = 921600,
|
||||
BIT_RATE_1843200 = 1843200,
|
||||
BIT_RATE_3686400 = 3686400,
|
||||
} UART_BautRate; //you can add any rate you need in this range
|
||||
|
||||
typedef enum {
|
||||
USART_HardwareFlowControl_None = 0x0,
|
||||
USART_HardwareFlowControl_RTS = 0x1,
|
||||
USART_HardwareFlowControl_CTS = 0x2,
|
||||
USART_HardwareFlowControl_CTS_RTS = 0x3
|
||||
} UART_HwFlowCtrl;
|
||||
|
||||
typedef enum {
|
||||
UART_None_Inverse = 0x0,
|
||||
UART_Rxd_Inverse = UART_RXD_INV,
|
||||
UART_CTS_Inverse = UART_CTS_INV,
|
||||
UART_Txd_Inverse = UART_TXD_INV,
|
||||
UART_RTS_Inverse = UART_RTS_INV,
|
||||
} UART_LineLevelInverse;
|
||||
|
||||
typedef struct {
|
||||
UART_BautRate baud_rate;
|
||||
UART_WordLength data_bits;
|
||||
UART_ParityMode parity; // chip size in byte
|
||||
UART_StopBits stop_bits;
|
||||
UART_HwFlowCtrl flow_ctrl;
|
||||
uint8 UART_RxFlowThresh ;
|
||||
uint32 UART_InverseMask;
|
||||
} UART_ConfigTypeDef;
|
||||
|
||||
typedef struct {
|
||||
uint32 UART_IntrEnMask;
|
||||
uint8 UART_RX_TimeOutIntrThresh;
|
||||
uint8 UART_TX_FifoEmptyIntrThresh;
|
||||
uint8 UART_RX_FifoFullIntrThresh;
|
||||
} UART_IntrConfTypeDef;
|
||||
|
||||
//=======================================
|
||||
|
||||
/** \defgroup Driver_APIs Driver APIs
|
||||
* @brief Driver APIs
|
||||
*/
|
||||
|
||||
/** @addtogroup Driver_APIs
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** \defgroup UART_Driver_APIs UART Driver APIs
|
||||
* @brief UART driver APIs
|
||||
*/
|
||||
|
||||
/** @addtogroup UART_Driver_APIs
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set UART baud rate.
|
||||
*
|
||||
* Example : uart_div_modify(uart_no, UART_CLK_FREQ / (UartDev.baut_rate));
|
||||
*
|
||||
* @param UART_Port uart_no : UART0 or UART1
|
||||
* @param uint16 div : frequency divider
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
void uart_div_modify(UART_Port uart_no, uint16 div);
|
||||
|
||||
/**
|
||||
* @brief Wait uart tx fifo empty, do not use it if tx flow control enabled.
|
||||
*
|
||||
* @param UART_Port uart_no : UART0 or UART1
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
void UART_WaitTxFifoEmpty(UART_Port uart_no); //do not use if tx flow control enabled
|
||||
|
||||
/**
|
||||
* @brief Clear uart tx fifo and rx fifo.
|
||||
*
|
||||
* @param UART_Port uart_no : UART0 or UART1
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
void UART_ResetFifo(UART_Port uart_no);
|
||||
|
||||
/**
|
||||
* @brief Clear uart interrupt flags.
|
||||
*
|
||||
* @param UART_Port uart_no : UART0 or UART1
|
||||
* @param uint32 clr_mask : To clear the interrupt bits
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
void UART_ClearIntrStatus(UART_Port uart_no, uint32 clr_mask);
|
||||
|
||||
/**
|
||||
* @brief Enable uart interrupts .
|
||||
*
|
||||
* @param UART_Port uart_no : UART0 or UART1
|
||||
* @param uint32 ena_mask : To enable the interrupt bits
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
void UART_SetIntrEna(UART_Port uart_no, uint32 ena_mask);
|
||||
|
||||
/**
|
||||
* @brief Register an application-specific interrupt handler for Uarts interrupts.
|
||||
*
|
||||
* @param void *fn : interrupt handler for Uart interrupts.
|
||||
* @param void *arg : interrupt handler's arg.
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
void UART_intr_handler_register(void *fn, void *arg);
|
||||
|
||||
/**
|
||||
* @brief Config from which serial output printf function.
|
||||
*
|
||||
* @param UART_Port uart_no : UART0 or UART1
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
void UART_SetPrintPort(UART_Port uart_no);
|
||||
|
||||
/**
|
||||
* @brief Config Common parameters of serial ports.
|
||||
*
|
||||
* @param UART_Port uart_no : UART0 or UART1
|
||||
* @param UART_ConfigTypeDef *pUARTConfig : parameters structure
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
void UART_ParamConfig(UART_Port uart_no, const UART_ConfigTypeDef *pUARTConfig);
|
||||
|
||||
/**
|
||||
* @brief Config types of uarts.
|
||||
*
|
||||
* @param UART_Port uart_no : UART0 or UART1
|
||||
* @param UART_IntrConfTypeDef *pUARTIntrConf : parameters structure
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
void UART_IntrConfig(UART_Port uart_no, const UART_IntrConfTypeDef *pUARTIntrConf);
|
||||
|
||||
/**
|
||||
* @brief Config the length of the uart communication data bits.
|
||||
*
|
||||
* @param UART_Port uart_no : UART0 or UART1
|
||||
* @param UART_WordLength len : the length of the uart communication data bits
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
void UART_SetWordLength(UART_Port uart_no, UART_WordLength len);
|
||||
|
||||
/**
|
||||
* @brief Config the length of the uart communication stop bits.
|
||||
*
|
||||
* @param UART_Port uart_no : UART0 or UART1
|
||||
* @param UART_StopBits bit_num : the length uart communication stop bits
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
void UART_SetStopBits(UART_Port uart_no, UART_StopBits bit_num);
|
||||
|
||||
/**
|
||||
* @brief Configure whether to open the parity.
|
||||
*
|
||||
* @param UART_Port uart_no : UART0 or UART1
|
||||
* @param UART_ParityMode Parity_mode : the enum of uart parity configuration
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
void UART_SetParity(UART_Port uart_no, UART_ParityMode Parity_mode) ;
|
||||
|
||||
/**
|
||||
* @brief Configure the Baud rate.
|
||||
*
|
||||
* @param UART_Port uart_no : UART0 or UART1
|
||||
* @param uint32 baud_rate : the Baud rate
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
void UART_SetBaudrate(UART_Port uart_no, uint32 baud_rate);
|
||||
|
||||
/**
|
||||
* @brief Configure Hardware flow control.
|
||||
*
|
||||
* @param UART_Port uart_no : UART0 or UART1
|
||||
* @param UART_HwFlowCtrl flow_ctrl : Hardware flow control mode
|
||||
* @param uint8 rx_thresh : threshold of Hardware flow control
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
void UART_SetFlowCtrl(UART_Port uart_no, UART_HwFlowCtrl flow_ctrl, uint8 rx_thresh);
|
||||
|
||||
/**
|
||||
* @brief Configure trigging signal of uarts.
|
||||
*
|
||||
* @param UART_Port uart_no : UART0 or UART1
|
||||
* @param UART_LineLevelInverse inverse_mask : Choose need to flip the IO
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
void UART_SetLineInverse(UART_Port uart_no, UART_LineLevelInverse inverse_mask) ;
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Set up uart0 for NodeMCU console use.
|
||||
* @param config The UART params to apply.
|
||||
* @param tsk NodeMCU task to be notified when there is input pending.
|
||||
*/
|
||||
void uart_init_uart0_console (const UART_ConfigTypeDef *config, task_handle_t tsk);
|
||||
|
||||
|
||||
/**
|
||||
* Generic UART send interface.
|
||||
* @param uart_no Which UART to send on (UART0 or UART1).
|
||||
* @param c The character to send.
|
||||
*/
|
||||
void uart_tx_one_char (uint32_t uart_no, uint8_t c);
|
||||
|
||||
|
||||
/**
|
||||
* Switch (or unswitch) UART0 to the alternate pins.
|
||||
* Currently only ESP8266.
|
||||
* @param on True to use alternate RX/TX pins, false to use default pins.
|
||||
*/
|
||||
void uart0_alt (bool on);
|
||||
|
||||
|
||||
/**
|
||||
* Attempts to pull a character off the UART0 receive queue.
|
||||
* @param c Where to stash the received character, if any.
|
||||
* @returns True if a character was stored in @c, false if no char available.
|
||||
*/
|
||||
bool uart0_getc (char *c);
|
||||
|
||||
/**
|
||||
* Convenience/consistency wrapper for UART0 output.
|
||||
* @param c The character to output.
|
||||
*/
|
||||
static inline void uart0_putc (char c) { uart_tx_one_char (UART0, c); }
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,74 +0,0 @@
|
||||
#ifndef __MODULE_H__
|
||||
#define __MODULE_H__
|
||||
|
||||
#include "user_modules.h"
|
||||
#include "lrodefs.h"
|
||||
|
||||
/* Registering a module within NodeMCU is really easy these days!
|
||||
*
|
||||
* Most of the work is done by a combination of pre-processor, compiler
|
||||
* and linker "magic". Gone are the days of needing to update 4+ separate
|
||||
* files just to register a module!
|
||||
*
|
||||
* You will need:
|
||||
* - to include this header
|
||||
* - a name for the module
|
||||
* - a LUA_REG_TYPE module map
|
||||
* - optionally, an init function
|
||||
*
|
||||
* Then simply put a line like this at the bottom of your module file:
|
||||
*
|
||||
* NODEMCU_MODULE(MYNAME, "myname", myname_map, luaopen_myname);
|
||||
*
|
||||
* or perhaps
|
||||
*
|
||||
* NODEMCU_MODULE(MYNAME, "myname", myname_map, NULL);
|
||||
*
|
||||
* if you don't need an init function.
|
||||
*
|
||||
* When you've done this, the module can be enabled in user_modules.h with:
|
||||
*
|
||||
* #define LUA_USE_MODULES_MYNAME
|
||||
*
|
||||
* and within NodeMCU you access it with myname.foo(), assuming you have
|
||||
* a foo function in your module.
|
||||
*/
|
||||
|
||||
#define MODULE_EXPAND_(x) x
|
||||
#define MODULE_PASTE_(x,y) x##y
|
||||
#define MODULE_EXPAND_PASTE_(x,y) MODULE_PASTE_(x,y)
|
||||
|
||||
#define LOCK_IN_SECTION(s) __attribute__((used,unused,section(s)))
|
||||
|
||||
/* For the ROM table, we name the variable according to ( | denotes concat):
|
||||
* cfgname | _module_selected | LUA_USE_MODULES_##cfgname
|
||||
* where the LUA_USE_MODULES_XYZ macro is first expanded to yield either
|
||||
* an empty string (or 1) if the module has been enabled, or the literal
|
||||
* LUA_USE_MOUDLE_XYZ in the case it hasn't. Thus, the name of the variable
|
||||
* ends up looking either like XYZ_module_enabled, or if not enabled,
|
||||
* XYZ_module_enabledLUA_USE_MODULES_XYZ. This forms the basis for
|
||||
* letting the build system detect automatically (via nm) which modules need
|
||||
* to be linked in.
|
||||
*/
|
||||
#define NODEMCU_MODULE(cfgname, luaname, map, initfunc) \
|
||||
const LOCK_IN_SECTION(".lua_libs") \
|
||||
luaL_Reg MODULE_PASTE_(lua_lib_,cfgname) = { luaname, initfunc }; \
|
||||
const LOCK_IN_SECTION(".lua_rotable") \
|
||||
luaR_table MODULE_EXPAND_PASTE_(cfgname,MODULE_EXPAND_PASTE_(_module_selected,MODULE_PASTE_(LUA_USE_MODULES_,cfgname))) \
|
||||
= { luaname, map }
|
||||
|
||||
|
||||
/* System module registration support, not using LUA_USE_MODULES_XYZ. */
|
||||
#define BUILTIN_LIB_INIT(name, luaname, initfunc) \
|
||||
const LOCK_IN_SECTION(".lua_libs") \
|
||||
luaL_Reg MODULE_PASTE_(lua_lib_,name) = { luaname, initfunc }
|
||||
|
||||
#define BUILTIN_LIB(name, luaname, map) \
|
||||
const LOCK_IN_SECTION(".lua_rotable") \
|
||||
luaR_table MODULE_PASTE_(lua_rotable_,name) = { luaname, map }
|
||||
|
||||
#if !defined(LUA_CROSS_COMPILER) && !(MIN_OPT_LEVEL==2 && LUA_OPTIMIZE_MEMORY==2)
|
||||
# error "NodeMCU modules must be built with LTR enabled (MIN_OPT_LEVEL=2 and LUA_OPTIMIZE_MEMORY=2)"
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,254 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2001-2003 Swedish Institute of Computer Science.
|
||||
* Copyright (c) 2003-2004 Leon Woestenberg <leon.woestenberg@axon.tv>
|
||||
* Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands.
|
||||
* 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: Adam Dunkels <adam@sics.se>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __NETIF_ETHARP_H__
|
||||
#define __NETIF_ETHARP_H__
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#if LWIP_ARP || LWIP_ETHERNET /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/ip.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef ETHARP_HWADDR_LEN
|
||||
#define ETHARP_HWADDR_LEN 6
|
||||
#endif
|
||||
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/bpstruct.h"
|
||||
#endif
|
||||
PACK_STRUCT_BEGIN
|
||||
struct eth_addr {
|
||||
PACK_STRUCT_FIELD(u8_t addr[ETHARP_HWADDR_LEN]);
|
||||
} PACK_STRUCT_STRUCT;
|
||||
PACK_STRUCT_END
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/epstruct.h"
|
||||
#endif
|
||||
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/bpstruct.h"
|
||||
#endif
|
||||
PACK_STRUCT_BEGIN
|
||||
/** Ethernet header */
|
||||
struct eth_hdr {
|
||||
#if ETH_PAD_SIZE
|
||||
PACK_STRUCT_FIELD(u8_t padding[ETH_PAD_SIZE]);
|
||||
#endif
|
||||
PACK_STRUCT_FIELD(struct eth_addr dest);
|
||||
PACK_STRUCT_FIELD(struct eth_addr src);
|
||||
PACK_STRUCT_FIELD(u16_t type);
|
||||
} PACK_STRUCT_STRUCT;
|
||||
PACK_STRUCT_END
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/epstruct.h"
|
||||
#endif
|
||||
|
||||
#define SIZEOF_ETH_HDR (14 + ETH_PAD_SIZE)
|
||||
|
||||
#if ETHARP_SUPPORT_VLAN
|
||||
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/bpstruct.h"
|
||||
#endif
|
||||
PACK_STRUCT_BEGIN
|
||||
/** VLAN header inserted between ethernet header and payload
|
||||
* if 'type' in ethernet header is ETHTYPE_VLAN.
|
||||
* See IEEE802.Q */
|
||||
struct eth_vlan_hdr {
|
||||
PACK_STRUCT_FIELD(u16_t tpid);
|
||||
PACK_STRUCT_FIELD(u16_t prio_vid);
|
||||
} PACK_STRUCT_STRUCT;
|
||||
PACK_STRUCT_END
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/epstruct.h"
|
||||
#endif
|
||||
|
||||
#define SIZEOF_VLAN_HDR 4
|
||||
#define VLAN_ID(vlan_hdr) (htons((vlan_hdr)->prio_vid) & 0xFFF)
|
||||
|
||||
#endif /* ETHARP_SUPPORT_VLAN */
|
||||
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/bpstruct.h"
|
||||
#endif
|
||||
PACK_STRUCT_BEGIN
|
||||
/** the ARP message, see RFC 826 ("Packet format") */
|
||||
struct etharp_hdr {
|
||||
PACK_STRUCT_FIELD(u16_t hwtype);
|
||||
PACK_STRUCT_FIELD(u16_t proto);
|
||||
PACK_STRUCT_FIELD(u8_t hwlen);
|
||||
PACK_STRUCT_FIELD(u8_t protolen);
|
||||
PACK_STRUCT_FIELD(u16_t opcode);
|
||||
PACK_STRUCT_FIELD(struct eth_addr shwaddr);
|
||||
PACK_STRUCT_FIELD(struct ip_addr2 sipaddr);
|
||||
PACK_STRUCT_FIELD(struct eth_addr dhwaddr);
|
||||
PACK_STRUCT_FIELD(struct ip_addr2 dipaddr);
|
||||
} PACK_STRUCT_STRUCT;
|
||||
PACK_STRUCT_END
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/epstruct.h"
|
||||
#endif
|
||||
|
||||
#define SIZEOF_ETHARP_HDR 28
|
||||
#define SIZEOF_ETHARP_MINSIZE 46
|
||||
#define SIZEOF_ETHARP_PACKET (SIZEOF_ETH_HDR + SIZEOF_ETHARP_HDR)
|
||||
#define SIZEOF_ETHARP_WITHPAD (SIZEOF_ETH_HDR + SIZEOF_ETHARP_MINSIZE)
|
||||
|
||||
/** 5 seconds period */
|
||||
#define ARP_TMR_INTERVAL 5000
|
||||
|
||||
#define ETHTYPE_ARP 0x0806
|
||||
#define ETHTYPE_IP 0x0800
|
||||
#define ETHTYPE_VLAN 0x8100
|
||||
#define ETHTYPE_PPPOEDISC 0x8863 /* PPP Over Ethernet Discovery Stage */
|
||||
#define ETHTYPE_PPPOE 0x8864 /* PPP Over Ethernet Session Stage */
|
||||
#define ETHTYPE_PAE 0x888e
|
||||
|
||||
/** MEMCPY-like macro to copy to/from struct eth_addr's that are local variables
|
||||
* or known to be 32-bit aligned within the protocol header. */
|
||||
#ifndef ETHADDR32_COPY
|
||||
#define ETHADDR32_COPY(src, dst) SMEMCPY(src, dst, ETHARP_HWADDR_LEN)
|
||||
#endif
|
||||
|
||||
/** MEMCPY-like macro to copy to/from struct eth_addr's that are no local
|
||||
* variables and known to be 16-bit aligned within the protocol header. */
|
||||
#ifndef ETHADDR16_COPY
|
||||
#define ETHADDR16_COPY(src, dst) SMEMCPY(src, dst, ETHARP_HWADDR_LEN)
|
||||
#endif
|
||||
|
||||
#if LWIP_ARP /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
/** ARP message types (opcodes) */
|
||||
#define ARP_REQUEST 1
|
||||
#define ARP_REPLY 2
|
||||
|
||||
/** Define this to 1 and define LWIP_ARP_FILTER_NETIF_FN(pbuf, netif, type)
|
||||
* to a filter function that returns the correct netif when using multiple
|
||||
* netifs on one hardware interface where the netif's low-level receive
|
||||
* routine cannot decide for the correct netif (e.g. when mapping multiple
|
||||
* IP addresses to one hardware interface).
|
||||
*/
|
||||
#ifndef LWIP_ARP_FILTER_NETIF
|
||||
#define LWIP_ARP_FILTER_NETIF 0
|
||||
#endif
|
||||
|
||||
#if ARP_QUEUEING
|
||||
/** struct for queueing outgoing packets for unknown address
|
||||
* defined here to be accessed by memp.h
|
||||
*/
|
||||
struct etharp_q_entry {
|
||||
struct etharp_q_entry *next;
|
||||
struct pbuf *p;
|
||||
};
|
||||
#endif /* ARP_QUEUEING */
|
||||
|
||||
#define etharp_init() /* Compatibility define, not init needed. */
|
||||
void etharp_tmr(void)ICACHE_FLASH_ATTR;
|
||||
s8_t etharp_find_addr(struct netif *netif, ip_addr_t *ipaddr,
|
||||
struct eth_addr **eth_ret, ip_addr_t **ip_ret)ICACHE_FLASH_ATTR;
|
||||
err_t etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr)ICACHE_FLASH_ATTR;
|
||||
err_t etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q)ICACHE_FLASH_ATTR;
|
||||
err_t etharp_request(struct netif *netif, ip_addr_t *ipaddr)ICACHE_FLASH_ATTR;
|
||||
/** For Ethernet network interfaces, we might want to send "gratuitous ARP";
|
||||
* this is an ARP packet sent by a node in order to spontaneously cause other
|
||||
* nodes to update an entry in their ARP cache.
|
||||
* From RFC 3220 "IP Mobility Support for IPv4" section 4.6. */
|
||||
#define etharp_gratuitous(netif) etharp_request((netif), &(netif)->ip_addr)
|
||||
void etharp_cleanup_netif(struct netif *netif);
|
||||
|
||||
#if ETHARP_SUPPORT_STATIC_ENTRIES
|
||||
err_t etharp_add_static_entry(ip_addr_t *ipaddr, struct eth_addr *ethaddr)ICACHE_FLASH_ATTR;
|
||||
err_t etharp_remove_static_entry(ip_addr_t *ipaddr)ICACHE_FLASH_ATTR;
|
||||
#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
|
||||
|
||||
#if LWIP_AUTOIP
|
||||
err_t etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr,
|
||||
const struct eth_addr *ethdst_addr,
|
||||
const struct eth_addr *hwsrc_addr, const ip_addr_t *ipsrc_addr,
|
||||
const struct eth_addr *hwdst_addr, const ip_addr_t *ipdst_addr,
|
||||
const u16_t opcode)ICACHE_FLASH_ATTR;
|
||||
#endif /* LWIP_AUTOIP */
|
||||
|
||||
#endif /* LWIP_ARP */
|
||||
|
||||
err_t ethernet_input(struct pbuf *p, struct netif *netif)ICACHE_FLASH_ATTR;
|
||||
|
||||
#define eth_addr_cmp(addr1, addr2) (memcmp((addr1)->addr, (addr2)->addr, ETHARP_HWADDR_LEN) == 0)
|
||||
|
||||
extern const struct eth_addr ethbroadcast, ethzero;
|
||||
|
||||
#endif /* LWIP_ARP || LWIP_ETHERNET */
|
||||
|
||||
#if 0
|
||||
/** Ethernet header */
|
||||
#ifndef ETHARP_HWADDR_LEN
|
||||
#define ETHARP_HWADDR_LEN 6
|
||||
#endif
|
||||
|
||||
|
||||
struct eth_addr {
|
||||
PACK_STRUCT_FIELD(u8_t addr[ETHARP_HWADDR_LEN]);
|
||||
} PACK_STRUCT_STRUCT;
|
||||
|
||||
|
||||
struct eth_hdr {
|
||||
#if ETH_PAD_SIZE
|
||||
PACK_STRUCT_FIELD(u8_t padding[ETH_PAD_SIZE]);
|
||||
#endif
|
||||
PACK_STRUCT_FIELD(struct eth_addr dest);
|
||||
PACK_STRUCT_FIELD(struct eth_addr src);
|
||||
PACK_STRUCT_FIELD(u16_t type);
|
||||
} PACK_STRUCT_STRUCT;
|
||||
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/epstruct.h"
|
||||
#endif
|
||||
|
||||
#define SIZEOF_ETH_HDR (14 + ETH_PAD_SIZE)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __NETIF_ARP_H__ */
|
@ -1,173 +0,0 @@
|
||||
/* $NetBSD: if_llc.h,v 1.12 1999/11/19 20:41:19 thorpej Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1988, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)if_llc.h 8.1 (Berkeley) 6/10/93
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _NET_IF_LLC_H_
|
||||
#define _NET_IF_LLC_H_
|
||||
|
||||
/*
|
||||
* IEEE 802.2 Link Level Control headers, for use in conjunction with
|
||||
* 802.{3,4,5} media access control methods.
|
||||
*
|
||||
* Headers here do not use bit fields due to shortcommings in many
|
||||
* compilers.
|
||||
*/
|
||||
|
||||
struct llc {
|
||||
uint8_t llc_dsap;
|
||||
uint8_t llc_ssap;
|
||||
union {
|
||||
struct {
|
||||
uint8_t control;
|
||||
uint8_t format_id;
|
||||
uint8_t class;
|
||||
uint8_t window_x2;
|
||||
} __packed type_u;
|
||||
struct {
|
||||
uint8_t num_snd_x2;
|
||||
uint8_t num_rcv_x2;
|
||||
} __packed type_i;
|
||||
struct {
|
||||
uint8_t control;
|
||||
uint8_t num_rcv_x2;
|
||||
} __packed type_s;
|
||||
struct {
|
||||
uint8_t control;
|
||||
/*
|
||||
* We cannot put the following fields in a structure because
|
||||
* the structure rounding might cause padding.
|
||||
*/
|
||||
uint8_t frmr_rej_pdu0;
|
||||
uint8_t frmr_rej_pdu1;
|
||||
uint8_t frmr_control;
|
||||
uint8_t frmr_control_ext;
|
||||
uint8_t frmr_cause;
|
||||
} __packed type_frmr;
|
||||
struct {
|
||||
uint8_t control;
|
||||
uint8_t org_code[3];
|
||||
uint16_t ether_type;
|
||||
} __packed type_snap;
|
||||
struct {
|
||||
uint8_t control;
|
||||
uint8_t control_ext;
|
||||
} __packed type_raw;
|
||||
} __packed llc_un;
|
||||
} __packed;
|
||||
|
||||
struct frmrinfo {
|
||||
uint8_t frmr_rej_pdu0;
|
||||
uint8_t frmr_rej_pdu1;
|
||||
uint8_t frmr_control;
|
||||
uint8_t frmr_control_ext;
|
||||
uint8_t frmr_cause;
|
||||
} __packed;
|
||||
|
||||
#define llc_control llc_un.type_u.control
|
||||
#define llc_control_ext llc_un.type_raw.control_ext
|
||||
#define llc_fid llc_un.type_u.format_id
|
||||
#define llc_class llc_un.type_u.class
|
||||
#define llc_window llc_un.type_u.window_x2
|
||||
#define llc_frmrinfo llc_un.type_frmr.frmr_rej_pdu0
|
||||
#define llc_frmr_pdu0 llc_un.type_frmr.frmr_rej_pdu0
|
||||
#define llc_frmr_pdu1 llc_un.type_frmr.frmr_rej_pdu1
|
||||
#define llc_frmr_control llc_un.type_frmr.frmr_control
|
||||
#define llc_frmr_control_ext llc_un.type_frmr.frmr_control_ext
|
||||
#define llc_frmr_cause llc_un.type_frmr.frmr_cause
|
||||
#define llc_snap llc_un.type_snap
|
||||
|
||||
/*
|
||||
* Don't use sizeof(struct llc_un) for LLC header sizes
|
||||
*/
|
||||
#define LLC_ISFRAMELEN 4
|
||||
#define LLC_UFRAMELEN 3
|
||||
#define LLC_FRMRLEN 7
|
||||
#define LLC_SNAPFRAMELEN 8
|
||||
|
||||
#ifdef CTASSERT
|
||||
CTASSERT(sizeof (struct llc) == LLC_SNAPFRAMELEN);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Unnumbered LLC format commands
|
||||
*/
|
||||
#define LLC_UI 0x3
|
||||
#define LLC_UI_P 0x13
|
||||
#define LLC_DISC 0x43
|
||||
#define LLC_DISC_P 0x53
|
||||
#define LLC_UA 0x63
|
||||
#define LLC_UA_P 0x73
|
||||
#define LLC_TEST 0xe3
|
||||
#define LLC_TEST_P 0xf3
|
||||
#define LLC_FRMR 0x87
|
||||
#define LLC_FRMR_P 0x97
|
||||
#define LLC_DM 0x0f
|
||||
#define LLC_DM_P 0x1f
|
||||
#define LLC_XID 0xaf
|
||||
#define LLC_XID_P 0xbf
|
||||
#define LLC_SABME 0x6f
|
||||
#define LLC_SABME_P 0x7f
|
||||
|
||||
/*
|
||||
* Supervisory LLC commands
|
||||
*/
|
||||
#define LLC_RR 0x01
|
||||
#define LLC_RNR 0x05
|
||||
#define LLC_REJ 0x09
|
||||
|
||||
/*
|
||||
* Info format - dummy only
|
||||
*/
|
||||
#define LLC_INFO 0x00
|
||||
|
||||
/*
|
||||
* ISO PDTR 10178 contains among others
|
||||
*/
|
||||
#define LLC_8021D_LSAP 0x42
|
||||
#define LLC_X25_LSAP 0x7e
|
||||
#define LLC_SNAP_LSAP 0xaa
|
||||
#define LLC_ISO_LSAP 0xfe
|
||||
|
||||
#define RFC1042_LEN 6
|
||||
#define RFC1042 {0xAA, 0xAA, 0x03, 0x00, 0x00, 0x00}
|
||||
#define ETHERNET_TUNNEL {0xAA, 0xAA, 0x03, 0x00, 0x00, 0xF8}
|
||||
|
||||
/*
|
||||
* copied from sys/net/ethernet.h
|
||||
*/
|
||||
#define ETHERTYPE_AARP 0x80F3 /* AppleTalk AARP */
|
||||
#define ETHERTYPE_IPX 0x8137 /* Novell (old) NetWare IPX (ECONFIG E option) */
|
||||
|
||||
|
||||
|
||||
#endif /* _NET_IF_LLC_H_ */
|
@ -1,190 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* ppp_oe.h - PPP Over Ethernet implementation for lwIP.
|
||||
*
|
||||
* Copyright (c) 2006 by Marc Boucher, Services Informatiques (MBSI) inc.
|
||||
*
|
||||
* The authors hereby grant permission to use, copy, modify, distribute,
|
||||
* and license this software and its documentation for any purpose, provided
|
||||
* that existing copyright notices are retained in all copies and that this
|
||||
* notice and the following disclaimer are included verbatim in any
|
||||
* distributions. No written agreement, license, or royalty fee is required
|
||||
* for any of the authorized uses.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************
|
||||
* REVISION HISTORY
|
||||
*
|
||||
* 06-01-01 Marc Boucher <marc@mbsi.ca>
|
||||
* Ported to lwIP.
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/* based on NetBSD: if_pppoe.c,v 1.64 2006/01/31 23:50:15 martin Exp */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2002 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by Martin Husemann <martin@NetBSD.org>.
|
||||
*
|
||||
* 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. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the NetBSD
|
||||
* Foundation, Inc. and its contributors.
|
||||
* 4. Neither the name of The NetBSD Foundation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef PPP_OE_H
|
||||
#define PPP_OE_H
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#if PPPOE_SUPPORT > 0
|
||||
|
||||
#include "netif/etharp.h"
|
||||
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/bpstruct.h"
|
||||
#endif
|
||||
PACK_STRUCT_BEGIN
|
||||
struct pppoehdr {
|
||||
PACK_STRUCT_FIELD(u8_t vertype);
|
||||
PACK_STRUCT_FIELD(u8_t code);
|
||||
PACK_STRUCT_FIELD(u16_t session);
|
||||
PACK_STRUCT_FIELD(u16_t plen);
|
||||
} PACK_STRUCT_STRUCT;
|
||||
PACK_STRUCT_END
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/epstruct.h"
|
||||
#endif
|
||||
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/bpstruct.h"
|
||||
#endif
|
||||
PACK_STRUCT_BEGIN
|
||||
struct pppoetag {
|
||||
PACK_STRUCT_FIELD(u16_t tag);
|
||||
PACK_STRUCT_FIELD(u16_t len);
|
||||
} PACK_STRUCT_STRUCT;
|
||||
PACK_STRUCT_END
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/epstruct.h"
|
||||
#endif
|
||||
|
||||
|
||||
#define PPPOE_STATE_INITIAL 0
|
||||
#define PPPOE_STATE_PADI_SENT 1
|
||||
#define PPPOE_STATE_PADR_SENT 2
|
||||
#define PPPOE_STATE_SESSION 3
|
||||
#define PPPOE_STATE_CLOSING 4
|
||||
/* passive */
|
||||
#define PPPOE_STATE_PADO_SENT 1
|
||||
|
||||
#define PPPOE_HEADERLEN sizeof(struct pppoehdr)
|
||||
#define PPPOE_VERTYPE 0x11 /* VER=1, TYPE = 1 */
|
||||
|
||||
#define PPPOE_TAG_EOL 0x0000 /* end of list */
|
||||
#define PPPOE_TAG_SNAME 0x0101 /* service name */
|
||||
#define PPPOE_TAG_ACNAME 0x0102 /* access concentrator name */
|
||||
#define PPPOE_TAG_HUNIQUE 0x0103 /* host unique */
|
||||
#define PPPOE_TAG_ACCOOKIE 0x0104 /* AC cookie */
|
||||
#define PPPOE_TAG_VENDOR 0x0105 /* vendor specific */
|
||||
#define PPPOE_TAG_RELAYSID 0x0110 /* relay session id */
|
||||
#define PPPOE_TAG_SNAME_ERR 0x0201 /* service name error */
|
||||
#define PPPOE_TAG_ACSYS_ERR 0x0202 /* AC system error */
|
||||
#define PPPOE_TAG_GENERIC_ERR 0x0203 /* gerneric error */
|
||||
|
||||
#define PPPOE_CODE_PADI 0x09 /* Active Discovery Initiation */
|
||||
#define PPPOE_CODE_PADO 0x07 /* Active Discovery Offer */
|
||||
#define PPPOE_CODE_PADR 0x19 /* Active Discovery Request */
|
||||
#define PPPOE_CODE_PADS 0x65 /* Active Discovery Session confirmation */
|
||||
#define PPPOE_CODE_PADT 0xA7 /* Active Discovery Terminate */
|
||||
|
||||
#ifndef ETHERMTU
|
||||
#define ETHERMTU 1500
|
||||
#endif
|
||||
|
||||
/* two byte PPP protocol discriminator, then IP data */
|
||||
#define PPPOE_MAXMTU (ETHERMTU-PPPOE_HEADERLEN-2)
|
||||
|
||||
#ifndef PPPOE_MAX_AC_COOKIE_LEN
|
||||
#define PPPOE_MAX_AC_COOKIE_LEN 64
|
||||
#endif
|
||||
|
||||
struct pppoe_softc {
|
||||
struct pppoe_softc *next;
|
||||
struct netif *sc_ethif; /* ethernet interface we are using */
|
||||
int sc_pd; /* ppp unit number */
|
||||
void (*sc_linkStatusCB)(int pd, int up);
|
||||
|
||||
int sc_state; /* discovery phase or session connected */
|
||||
struct eth_addr sc_dest; /* hardware address of concentrator */
|
||||
u16_t sc_session; /* PPPoE session id */
|
||||
|
||||
#ifdef PPPOE_TODO
|
||||
char *sc_service_name; /* if != NULL: requested name of service */
|
||||
char *sc_concentrator_name; /* if != NULL: requested concentrator id */
|
||||
#endif /* PPPOE_TODO */
|
||||
u8_t sc_ac_cookie[PPPOE_MAX_AC_COOKIE_LEN]; /* content of AC cookie we must echo back */
|
||||
size_t sc_ac_cookie_len; /* length of cookie data */
|
||||
#ifdef PPPOE_SERVER
|
||||
u8_t *sc_hunique; /* content of host unique we must echo back */
|
||||
size_t sc_hunique_len; /* length of host unique */
|
||||
#endif
|
||||
int sc_padi_retried; /* number of PADI retries already done */
|
||||
int sc_padr_retried; /* number of PADR retries already done */
|
||||
};
|
||||
|
||||
|
||||
#define pppoe_init() /* compatibility define, no initialization needed */
|
||||
|
||||
err_t pppoe_create(struct netif *ethif, int pd, void (*linkStatusCB)(int pd, int up), struct pppoe_softc **scptr);
|
||||
err_t pppoe_destroy(struct netif *ifp);
|
||||
|
||||
int pppoe_connect(struct pppoe_softc *sc);
|
||||
void pppoe_disconnect(struct pppoe_softc *sc);
|
||||
|
||||
void pppoe_disc_input(struct netif *netif, struct pbuf *p);
|
||||
void pppoe_data_input(struct netif *netif, struct pbuf *p);
|
||||
|
||||
err_t pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb);
|
||||
|
||||
/** used in ppp.c */
|
||||
#define PPPOE_HDRLEN (sizeof(struct eth_hdr) + PPPOE_HEADERLEN)
|
||||
|
||||
#endif /* PPPOE_SUPPORT */
|
||||
|
||||
#endif /* PPP_OE_H */
|
@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011 Espressif System
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _WLAN_LWIP_IF_H_
|
||||
#define _WLAN_LWIP_IF_H_
|
||||
|
||||
#define LWIP_IF0_PRIO 28
|
||||
#define LWIP_IF1_PRIO 29
|
||||
|
||||
enum {
|
||||
SIG_LWIP_RX = 0,
|
||||
};
|
||||
|
||||
struct netif * eagle_lwip_if_alloc(struct ieee80211_conn *conn, const uint8 *macaddr, struct ip_info *info);
|
||||
struct netif * eagle_lwip_getif(uint8 index);
|
||||
|
||||
#ifndef IOT_SIP_MODE
|
||||
sint8 ieee80211_output_pbuf(struct netif *ifp, struct pbuf* pb);
|
||||
#else
|
||||
sint8 ieee80211_output_pbuf(struct ieee80211_conn *conn, esf_buf *eb);
|
||||
#endif
|
||||
|
||||
#endif /* _WLAN_LWIP_IF_H_ */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user