Merge branch 'osd'

Conflicts:
	apps/arduino/arduino-process.c
	apps/arduino/arduino-process.h
	apps/json-resource/generic_resource.c
	apps/json-resource/generic_resource.h
	apps/time/Makefile.time
	apps/time/resource_gmtime.c
	apps/time/resource_timestamp.c
	apps/time/time.c
	apps/time/time_resource.h
	core/dev/leds.c
	core/lib/petsciiconv.c
	core/net/ip/resolv.c
	core/net/ip/slipdev.c
	core/net/ip/tcpip.c
	core/net/ipv4/uip.c
	core/net/ipv6/uip-ds6.c
	core/net/mac/contikimac/contikimac.c
	core/net/mac/frame802154.h
	core/net/mac/framer-802154.c
	core/net/mac/nullrdc.c
	core/net/rpl/rpl-dag.c
	core/net/rpl/rpl-ext-header.c
	core/net/rpl/rpl-icmp6.c
	core/net/rpl/rpl-mrhof.c
	core/net/rpl/rpl-of0.c
	core/net/rpl/rpl-timers.c
	core/net/rpl/rpl.c
	cpu/avr/Makefile.avr
	cpu/avr/dev/lanc111.c
	cpu/avr/radio/rf230bb/halbb.c
	dev/arduino/arduino-compat.h
	examples/osd/.gitignore
	examples/osd/arduino-dooralert/Makefile
	examples/osd/arduino-dooralert/flash.sh
	examples/osd/arduino-dooralert/run.sh
	examples/osd/arduino-dooralert/sketch.pde
	examples/osd/arduino-merkurboard/Makefile
	examples/osd/arduino-merkurboard/README.md
	examples/osd/arduino-merkurboard/flash.sh
	examples/osd/arduino-merkurboard/project-conf.h
	examples/osd/arduino-merkurboard/run.sh
	examples/osd/arduino-plantobserving/Makefile
	examples/osd/arduino-plantobserving/flash.sh
	examples/osd/arduino-plantobserving/project-conf.h
	examples/osd/arduino-plantobserving/run.sh
	examples/osd/arduino-plantobserving/sketch.pde
	examples/osd/arduino-roomalert/Makefile
	examples/osd/arduino-roomalert/flash.sh
	examples/osd/arduino-roomalert/run.sh
	examples/osd/arduino-roomalert/sketch.pde
	examples/osd/arduino-sketch/Makefile
	examples/osd/arduino-sketch/flash.sh
	examples/osd/arduino-sketch/led_pwm.h
	examples/osd/arduino-sketch/resource_led_pwm.c
	examples/osd/arduino-sketch/run.sh
	examples/osd/arduino-sketch/sketch.pde
	examples/osd/arduino-wateralert/Makefile
	examples/osd/arduino-wateralert/flash.sh
	examples/osd/arduino-wateralert/run.sh
	examples/osd/arduino-wateralert/sketch.pde
	examples/osd/climate/Makefile
	examples/osd/climate/er-example-server.c
	examples/osd/climate/flash.sh
	examples/osd/climate/project-conf.h
	examples/osd/climate/run.sh
	examples/osd/climate/server-only.csc
	examples/osd/climate2/Makefile
	examples/osd/climate2/er-example-server.c
	examples/osd/climate2/flash.sh
	examples/osd/climate2/project-conf.h
	examples/osd/climate2/run.sh
	examples/osd/climate2/server-only.csc
	examples/osd/dual-rgbw-actor/Makefile
	examples/osd/dual-rgbw-actor/flash.sh
	examples/osd/dual-rgbw-actor/run.sh
	examples/osd/dual-rgbw-actor/server-client.csc
	examples/osd/dual-rgbw-actor/server-only.csc
	examples/osd/embedd-vm-merkurboard/Makefile
	examples/osd/embedd-vm-merkurboard/embedd-vm-server.c
	examples/osd/embedd-vm-merkurboard/flash.sh
	examples/osd/embedd-vm-merkurboard/run.sh
	examples/osd/embedd-vm-merkurboard/server-only.csc
	examples/osd/er-rest-example-merkurboard/Makefile
	examples/osd/er-rest-example-merkurboard/README.md
	examples/osd/er-rest-example-merkurboard/er-example-client.c
	examples/osd/er-rest-example-merkurboard/er-example-server.c
	examples/osd/er-rest-example-merkurboard/er-plugtest-server.c
	examples/osd/er-rest-example-merkurboard/flash.sh
	examples/osd/er-rest-example-merkurboard/flashclient.sh
	examples/osd/er-rest-example-merkurboard/project-conf.h
	examples/osd/er-rest-example-merkurboard/run.sh
	examples/osd/er-rest-example-merkurboard/runclient.sh
	examples/osd/er-rest-example-merkurboard/server-client.csc
	examples/osd/light-actor/Makefile
	examples/osd/light-actor/er-example-server.c
	examples/osd/light-actor/flash.sh
	examples/osd/light-actor/pcintkey.c
	examples/osd/light-actor/project-conf.h
	examples/osd/light-actor/run.sh
	examples/osd/light-actor/server-only.csc
	examples/osd/light-shutter-control/Makefile
	examples/osd/light-shutter-control/flash.sh
	examples/osd/light-shutter-control/pcintkey.c
	examples/osd/light-shutter-control/run.sh
	examples/osd/light-shutter-control/server-only.csc
	examples/osd/merkurboard/Makefile
	examples/osd/merkurboard/README.md
	examples/osd/merkurboard/er-example-client.c
	examples/osd/merkurboard/er-example-server.c
	examples/osd/merkurboard/er-plugtest-server.c
	examples/osd/merkurboard/flash.sh
	examples/osd/merkurboard/flashclient.sh
	examples/osd/merkurboard/project-conf.h
	examples/osd/merkurboard/run.sh
	examples/osd/merkurboard/runclient.sh
	examples/osd/native-border-router/Makefile
	examples/osd/native-border-router/border-router-cmds.c
	examples/osd/native-border-router/border-router-cmds.h
	examples/osd/native-border-router/border-router-rdc.c
	examples/osd/native-border-router/border-router.c
	examples/osd/native-border-router/border-router.h
	examples/osd/native-border-router/project-conf.h
	examples/osd/native-border-router/slip-config.c
	examples/osd/native-border-router/slip-dev.c
	examples/osd/native-border-router/tun-bridge.c
	examples/osd/pingtheplug/Makefile
	examples/osd/pingtheplug/er-example-server.c
	examples/osd/pingtheplug/flash.sh
	examples/osd/pingtheplug/pcintkey.c
	examples/osd/pingtheplug/run.sh
	examples/osd/pingtheplug/server-only.csc
	examples/osd/pir-sensor/Makefile
	examples/osd/pir-sensor/flash.sh
	examples/osd/pir-sensor/run.sh
	examples/osd/pir-sensor/server-client.csc
	examples/osd/pir-sensor/server-only.csc
	examples/osd/powerbox/Makefile
	examples/osd/powerbox/er-example-server.c
	examples/osd/powerbox/flash.sh
	examples/osd/powerbox/run.sh
	examples/osd/powerbox/server-only.csc
	examples/osd/pwm-example/Makefile
	examples/osd/pwm-example/er-example-server.c
	examples/osd/pwm-example/flash.sh
	examples/osd/pwm-example/led_pwm.h
	examples/osd/pwm-example/resource_led_pwm.c
	examples/osd/pwm-example/run.sh
	examples/osd/rpl-border-router/Makefile
	examples/osd/rpl-border-router/border-router.c
	examples/osd/rpl-border-router/flash.sh
	examples/osd/rpl-border-router/project-conf.h
	examples/osd/rpl-border-router/run.sh
	examples/osd/rpl-border-router/slip-bridge.c
	examples/osd/runall.sh
	examples/osd/servo-sensor/Makefile
	examples/osd/servo-sensor/er-example-server.c
	examples/osd/servo-sensor/flash.sh
	examples/osd/servo-sensor/project-conf.h
	examples/osd/servo-sensor/run.sh
	examples/osd/servo-sensor/server-client.csc
	examples/osd/servo-sensor/server-only.csc
	examples/osd/slip-radio/Makefile
	examples/osd/slip-radio/flash.sh
	examples/osd/slip-radio/no-framer.c
	examples/osd/slip-radio/project-conf.h
	examples/osd/slip-radio/run.sh
	examples/osd/slip-radio/slip-net.c
	examples/osd/slip-radio/slip-radio-cc2420.c
	examples/osd/slip-radio/slip-radio-sky-sensors.c
	examples/osd/slip-radio/slip-radio.c
	examples/osd/slip-radio/slip-radio.h
	examples/osd/wallclock-time/Makefile
	examples/osd/wallclock-time/flash.sh
	examples/osd/wallclock-time/run.sh
	examples/osd/wirelessplug/Makefile
	examples/osd/wirelessplug/flash.sh
	examples/osd/wirelessplug/run.sh
	examples/osd/wirelessplug/server-client.csc
	examples/osd/wirelessplug/server-only.csc
	platform/avr-atmega128rfa1/apps/raven-lcd-interface/raven-lcd.c
	platform/avr-raven/apps/raven-lcd-interface/raven-lcd.c
	tools/tunslip6.c
This commit is contained in:
Ralf Schlatterbeck 2016-08-12 22:04:56 +02:00
commit 28cb276c70
3042 changed files with 322924 additions and 112623 deletions

14
.gitattributes vendored Normal file
View file

@ -0,0 +1,14 @@
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto whitespace=trailing-space
# Explicitly declare text files you want to always be normalized and converted
# to native line endings on checkout.
*.c text
*.h text
*.java text
# Denote all files that are truly binary and should not be modified.
*.png binary
*.jpg binary
*.ihex binary
*.s37 binary

49
.gitignore vendored
View file

@ -4,6 +4,7 @@
*.png
*.log
*.elf
*.zip
*.d
*.ihex
*.pyc
@ -24,9 +25,15 @@
*.c128
*.c64
*.cc2538dk
*.zoul
*.jn516x
*.srf06-cc26xx
*.ev-aducrf101mkxz
*.report
summary
*.summary
*.runerr
*.runlog
*.faillog
*.orig
*~
@ -35,7 +42,9 @@ obj_*
symbols.*
Makefile.target
doc/html
doc/latex
patches-*
tools/tunslip
tools/tunslip6
build
tools/coffee-manager/build/
@ -52,7 +61,6 @@ tools/cooja/apps/avrora/lib/cooja_avrora.jar
tools/cooja/apps/collect-view/cooja-collect-view.jar
# sdcc build artifacts
contiki-sensinode.lib
contiki-cc2530dk.lib
*.ihx
*.hex
@ -61,7 +69,6 @@ contiki-cc2530dk.lib
*.omf
*.cdb
*.banks
*.sensinode
*.cc2530dk
# VC++ build artifacts
@ -73,18 +80,52 @@ contiki-cc2530dk.lib
*.dsc
#cc65 build artifacts
*.S
*.s
*.eth
*.dsk
*.2mg
*.po
*.atr
*.d64
*.d71
*.d81
# Cooja Build Artifacts
*.cooja
#regression tests artifacts
*.testlog
*.log.prog
regression-tests/[0-9][0-9]-*/report
regression-tests/[0-9][0-9]-*/org/
# rl78 build artifacts
*.eval-adf7xxxmb4z
*.eval-adf7xxxmb4z.srec
# cscope files
cscope.*
# vim swap files
*.swp
*.swo
# x86 UEFI files
cpu/x86/uefi/Makefile.uefi
cpu/x86/uefi/edk2
# galileo bsp files
platform/galileo/bsp/libc/Makefile.libc
platform/galileo/bsp/libc/i586-elf/
platform/galileo/bsp/libc/newlib-2.2.0-1*
platform/galileo/bsp/grub/src/
platform/galileo/bsp/grub/bin/
# galileo build and debug artefacts
*.galileo
*.galileo.dll
*.galileo.efi
LOG_OPENOCD
# nRF52 build artifacts
*.jlink
*.nrf52dk

10
.gitmodules vendored
View file

@ -4,3 +4,13 @@
[submodule "tools/cc2538-bsl"]
path = tools/cc2538-bsl
url = https://github.com/JelmerT/cc2538-bsl.git
[submodule "cpu/cc26xx-cc13xx/lib/cc26xxware"]
path = cpu/cc26xx-cc13xx/lib/cc26xxware
url = https://github.com/g-oikonomou/cc26xxware.git
[submodule "cpu/cc26xx-cc13xx/lib/cc13xxware"]
path = cpu/cc26xx-c../.gitmodulesc13xx/lib/cc13xxware
url = https://github.com/g-oikonomou/cc13xxware.git
[submodule "platform/stm32nucleo-spirit1/stm32cube-lib"]
path = platform/stm32nucleo-spirit1/stm32cube-lib
url = https://github.com/STclab/stm32nucleo-spirit1-lib

View file

@ -2,42 +2,115 @@ notifications:
email: false
language: c #NOTE: this will set CC=gcc which might cause trouble
before_script:
- "sudo apt-get -qq update"
## Install these mainline toolchains for all build types
- "sudo apt-get -qq install lib32z1 || true"
- "curl -s \
http://adamdunkels.github.io/contiki-fork/mspgcc-4.7.0-compiled.tar.bz2 \
| tar xjf - -C /tmp/ && sudo cp -f -r /tmp/msp430/* /usr/local/ && rm -rf /tmp/msp430 && msp430-gcc --version || true"
- "sudo apt-get -qq install gcc-avr avr-libc || true"
- "sudo apt-get -qq install libc6:i386 libgcc1:i386 gcc-4.6-base:i386 libstdc++5:i386 libstdc++6:i386 || true"
- WGET="travis_retry wget --continue --tries=20 --waitretry=10 --retry-connrefused --no-dns-cache --timeout 300"
- sudo apt-get -qq update
## Install toolchain for mc1233x, cc2538 and mbxxx in care-free way
- "[ ${BUILD_ARCH:-0} = arm ] && curl -s \
https://raw.github.com/wiki/malvira/libmc1322x/files/arm-2008q3-66-arm-none-eabi-i686-pc-linux-gnu.tar.bz2 \
| tar xjf - -C /tmp/ && sudo cp -f -r /tmp/arm-2008q3/* /usr/ && rm -rf /tmp/arm-2008q3 && arm-none-eabi-gcc --version || true"
## Support building a binary that is identical to the CI
- echo -n "Contiki will be compiled with RELSTR=" ; git --git-dir .git describe --tags --always
## Install RL78 GCC chain (following the instructions in platform/eval-adf7xxxmb4z/README.md)
- "sudo apt-get install git make gcc libc-dev multiarch-support libncurses5:i386 zlib1g:i386"
- "wget https://dl.dropboxusercontent.com/u/60522916/gnurl78-v13.02-elf_1-2_i386.deb"
- "sudo dpkg -i gnurl78*.deb"
## Install doxygen
- if [ ${BUILD_CATEGORY:-0} = doxygen ] ; then
sudo add-apt-repository ppa:libreoffice/libreoffice-4-4 -y && sudo apt-get -qq update &&
sudo apt-get --no-install-suggests --no-install-recommends -qq install doxygen &&
doxygen --version ;
fi
## Install msp430 toolchain
- sudo apt-get -qq install lib32z1
- $WGET http://simonduq.github.io/resources/mspgcc-4.7.2-compiled.tar.bz2 &&
tar xjf mspgcc*.tar.bz2 -C /tmp/ &&
sudo cp -f -r /tmp/msp430/* /usr/local/ &&
rm -rf /tmp/msp430 mspgcc*.tar.bz2 &&
msp430-gcc --version
## Install avr toolchain
- $WGET http://atiselsts.github.io/resources/avr-gcc-4.9.2-compiled.tar.bz2 &&
tar xjf avr-gcc*.tar.bz2 -C /tmp/ &&
sudo cp -f -r /tmp/avr-gcc/* /usr/local/ &&
rm -rf /tmp/avr-gcc avr-gcc*.tar.bz2 &&
avr-gcc --version
## Install 32-bit compatibility libraries
- sudo apt-get -qq install libc6:i386 libgcc1:i386 gcc-4.6-base:i386
libstdc++5:i386 libstdc++6:i386
## Install old APCS ARM toolchain for mc1233x and mbxxx
- if [ ${BUILD_ARCH:-0} = arm-apcs ] ; then
$WGET https://raw.githubusercontent.com/wiki/malvira/libmc1322x/files/arm-2008q3-66-arm-none-eabi-i686-pc-linux-gnu.tar.bz2 &&
tar xjf arm-2008q3*.tar.bz2 -C /tmp/ &&
sudo cp -f -r /tmp/arm-2008q3/* /usr/ &&
rm -rf /tmp/arm-2008q3 arm-2008q3*.tar.bz2 &&
sudo apt-get -qq install libconfig-dev uuid-dev libqrencode-dev &&
arm-none-eabi-gcc --version ;
fi
## Install mainline ARM toolchain and srecord.
- if [ ${BUILD_ARCH:-0} = arm-aapcs ] ; then
sudo apt-get -qq install srecord &&
$WGET https://launchpad.net/gcc-arm-embedded/5.0/5-2015-q4-major/+download/gcc-arm-none-eabi-5_2-2015q4-20151219-linux.tar.bz2 &&
tar xjf gcc-arm-none-eabi-5_2-2015q4-20151219-linux.tar.bz2 -C /tmp/ &&
sudo cp -f -r /tmp/gcc-arm-none-eabi-5_2-2015q4/* /usr/local/ &&
rm -rf /tmp/gcc-arm-none-eabi-* gcc-arm-none-eabi-*-linux.tar.bz2 &&
arm-none-eabi-gcc --version ;
fi
## Install RL78 GCC toolchain
- sudo apt-get install libncurses5:i386 zlib1g:i386
- $WGET http://adamdunkels.github.io/contiki-fork/gnurl78-v13.02-elf_1-2_i386.deb &&
sudo dpkg -i gnurl78*.deb
## Install SDCC from a purpose-built bundle
- "[ ${BUILD_ARCH:-0} = 8051 ] && curl -s \
https://raw.github.com/wiki/g-oikonomou/contiki-sensinode/files/sdcc.tar.gz \
| tar xzf - -C /tmp/ && sudo cp -f -r /tmp/sdcc/* /usr/local/ && rm -rf /tmp/sdcc && sdcc --version || true"
- "[ ${BUILD_ARCH:-0} = 8051 ] && sudo apt-get -qq install srecord || true"
- if [ ${BUILD_ARCH:-0} = 8051 ] ; then
$WGET https://raw.githubusercontent.com/wiki/g-oikonomou/contiki-sensinode/files/sdcc.tar.gz &&
tar xzf sdcc.tar.gz -C /tmp/ &&
sudo cp -f -r /tmp/sdcc/* /usr/local/ &&
rm -rf /tmp/sdcc sdcc.tar.gz &&
sdcc --version &&
sudo apt-get -qq install srecord ;
fi
## Clone and build cc65 when testing 6502 ports
- "[ ${BUILD_ARCH:-0} = 6502 ] && git clone \
https://github.com/cc65/cc65 /tmp/cc65 && \
make -C /tmp/cc65 bin apple2enh atarixl c64 c128 && sudo make -C /tmp/cc65 avail && \
export CC65_HOME=/tmp/cc65/ && cc65 --version || true"
- if [ ${BUILD_ARCH:-0} = 6502 ] ; then
git clone https://github.com/cc65/cc65 /tmp/cc65 &&
make -C /tmp/cc65 bin apple2enh atarixl c64 c128 &&
sudo make -C /tmp/cc65 avail &&
cc65 --version ;
fi
## Install NXP toolchain
- if [ ${BUILD_ARCH:-0} = jn516x ] ; then
$WGET http://simonduq.github.io/resources/ba-elf-gcc-4.7.4-part1.tar.bz2 &&
$WGET http://simonduq.github.io/resources/ba-elf-gcc-4.7.4-part2.tar.bz2 &&
$WGET http://simonduq.github.io/resources/jn516x-sdk-4163.tar.bz2 &&
mkdir /tmp/jn516x-sdk /tmp/ba-elf-gcc &&
tar xjf jn516x-sdk-*.tar.bz2 -C /tmp/jn516x-sdk &&
tar xjf ba-elf-gcc-*part1.tar.bz2 -C /tmp/ba-elf-gcc &&
tar xjf ba-elf-gcc-*part2.tar.bz2 -C /tmp/ba-elf-gcc &&
sudo cp -f -r /tmp/jn516x-sdk /usr/ &&
sudo cp -f -r /tmp/ba-elf-gcc /usr/ &&
export PATH=/usr/ba-elf-gcc/bin:$PATH &&
rm -rf /tmp/ba-elf-gcc* /tmp/jn516x-sdk* &&
ba-elf-gcc --version ;
fi
## Install mainline ARM toolchain and download nRF52 SDK
- if [ ${BUILD_ARCH:-0} = nrf52dk ] ; then
sudo add-apt-repository -y ppa:team-gcc-arm-embedded/ppa &&
sudo apt-get -qq update &&
sudo apt-get -qq install gcc-arm-embedded srecord &&
arm-none-eabi-gcc --version &&
$WGET https://developer.nordicsemi.com/nRF5_IoT_SDK/nRF5_IoT_SDK_v0.9.x/nrf5_iot_sdk_3288530.zip &&
mkdir /tmp/nrf52-sdk &&
unzip nrf5_iot_sdk_3288530.zip -d /tmp/nrf52-sdk &&
export NRF52_SDK_ROOT=/tmp/nrf52-sdk ;
fi
## Compile cooja.jar only when it's going to be needed
- "[ ${BUILD_CATEGORY:-sim} = sim ] && java -version && ant -q -f tools/cooja/build.xml jar && sudo java -Xshare:dump -version || true"
## IMPORTANT: The commands here have to end with `|| true`,
## because it would make the test fail if BUILD_TYPE test fails
- if [ ${BUILD_CATEGORY:-sim} = sim ] ; then
java -version &&
ant -q -f tools/cooja/build.xml jar &&
sudo java -Xshare:dump -version ;
fi
script:
## regression-tests/Makefile handles most of generic logic
@ -48,7 +121,7 @@ after_script:
- "[ ${BUILD_CATEGORY:-sim} = sim ] && tail regression-tests/??-$BUILD_TYPE/*.testlog"
## Print a basic summary
- "echo 'Summary:'; cat regression-tests/??-$BUILD_TYPE/summary"
- "FAILS=`grep -c -i 'fail' regression-tests/??-$BUILD_TYPE/summary`"
- "FAILS=`grep -c ' FAIL ' regression-tests/??-$BUILD_TYPE/summary`"
## This will detect whether the build should pass or fail
- "test $FAILS -eq 0; exit $?"
@ -56,12 +129,17 @@ after_script:
env:
## This magically kick-off parallel jobs for each of the for the sets
## of environment variable defined below
- BUILD_TYPE='doxygen' BUILD_CATEGORY='doxygen'
- BUILD_TYPE='compile-base' BUILD_CATEGORY='compile'
- BUILD_TYPE='compile-tools' BUILD_CATEGORY='compile'
- BUILD_TYPE='collect'
- BUILD_TYPE='collect-lossy'
- BUILD_TYPE='rpl'
- BUILD_TYPE='rpl-non-storing'
- BUILD_TYPE='large-rpl'
- BUILD_TYPE='rime'
- BUILD_TYPE='ipv6'
- BUILD_TYPE='ip64' MAKE_TARGETS='cooja'
- BUILD_TYPE='hello-world'
- BUILD_TYPE='base'
# XXX: netperf disabled b/c it's flaky
@ -72,6 +150,11 @@ env:
# - BUILD_TYPE='ipv4'
- BUILD_TYPE='ipv6-apps'
- BUILD_TYPE='compile-8051-ports' BUILD_CATEGORY='compile' BUILD_ARCH='8051'
- BUILD_TYPE='compile-arm-ports' BUILD_CATEGORY='compile' BUILD_ARCH='arm'
- BUILD_TYPE='compile-arm-apcs-ports' BUILD_CATEGORY='compile' BUILD_ARCH='arm-apcs'
- BUILD_TYPE='compile-6502-ports' BUILD_CATEGORY='compile' BUILD_ARCH='6502'
- BUILD_TYPE='compile-arm-ports' BUILD_CATEGORY='compile' BUILD_ARCH='arm-aapcs'
- BUILD_TYPE='compile-nxp-ports' BUILD_CATEGORY='compile' BUILD_ARCH='jn516x'
- BUILD_TYPE='compile-nrf52-ports' BUILD_CATEGORY='compile' BUILD_ARCH='nrf52dk'
- BUILD_TYPE='slip-radio' MAKE_TARGETS='cooja'
- BUILD_TYPE='llsec' MAKE_TARGETS='cooja'
- BUILD_TYPE='compile-avr' BUILD_CATEGORY='compile' BUILD_ARCH='avr-rss2'

278
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,278 @@
Code Contributions
==================
Do you have a new cool feature that you'd like to contribute to
Contiki? Or a fix for a bug? Great! The Contiki project loves code
contributions, improvements, and bugfixes, but we require that they
follow a set of guidelines and that they are contributed in a specific
way.
Additional rules apply for contributions of a new hardware platform.
General Advice
--------------
The chance of getting your pull request accepted increases considerably
if you adhere to the following rules in addition to the aforementioned
formatting and naming standards:
* Ensure that all contributed files have a valid copyright statement
and an open-source license.
* Do not bundle commits that are unrelated to each other -- create
separate pull requests instead.
* Adhere to ISO C99 in all C language source files. Exceptions are
allowed for those platform-dependent source files that rely on the
extensions of a specific set of compilers.
* Clean up the commit history. "git rebase -i" is useful for this purpose.
* Do not include executable binary files, because they are usually
rejected for security reasons. Instead, provide instructions for how
to compile the file, so that a trusted member of the merge team can
commit it.
* Write a descriptive pull request message. Explain the advantages and
disadvantages of your proposed changes.
* Before starting to work on a major contribution, discuss your idea
with experienced Contiki programmers (e.g., on the contiki-developers
mailing list) to avoid wasting time on things that have no chance of
getting merged into Contiki.
Source code that goes into the mainline Contiki repository must be of
interest to a large part of the Contiki community. It must be
well-tested and the merge team must have confidence that the code can
be maintained over a longer period. See below for more details
pertaining to platform contributions.
Contributions that have been made in research projects, and typically
do not get maintained thereafter, are better suited for inclusion in
the Contiki projects repository.
Structuring Commits
-------------------
* Write descriptive commit messages. They don't have to be very long,
but you should mention what the commit achieves. Commit messages
like "modified foo/bar.c" are not helpful, should not be used, and
are likely to result in you having to re-write them.
* Please do not add / remove irrelevant new line markers. Don't remove
the new line marker at the EOF.
* Please, make sure that your patch doesn't add lines with trailing
whitespaces. If you run uncrustify as discussed above, this should
get taken care of for you automatically.
* More generally speaking, make sure that each commit in your history
only includes changes necessary to implement whatever it is the
commit is trying to achieve. All changes should be mentioned in the
commit message.
Code Formatting
---------------
We require that all code contributed to the Contiki tree follows the
same code formatting as the existing Contiki code. We are very strict
on this.
Code must be formatted according to
[contiki/doc/code-style.c](https://github.com/contiki-os/contiki/blob/master/doc/code-style.c).
The Contiki source tree contains scripts to assist with correct code formatting
and we recommend [Uncrustify](http://uncrustify.sourceforge.net/) as the
preferred auto formatter. Everything is under
[tools/code-style](https://github.com/contiki-os/contiki/tree/master/tools/code-style).
If you wish, you can format all changed resources in your working tree
automatically if the
[tools/code-style/uncrustify-changed.sh](https://github.com/contiki-os/contiki/blob/master/tools/code-style/uncrustify-changed.sh)
script is added as a [Git pre-commit
hook](http://git-scm.com/book/en/Customizing-Git-Git-Hooks) to your Git
configuration.
Here are some examples of what you can do:
* To check a file's style without changing the file on disk, you can run this:
`./tools/code-style/uncrustify-check-style.sh <path-to-file>`
This script will only accept a single file as its argument.
* To auto format a file (and change it on disk) you can run this:
`./tools/code-style/uncrustify-fix-style.sh <path-to-file>`
* `uncrustify-fix-style.sh` will accept a space-delimited list of files as its argument. Thus, you can auto-format an entire directory by running something like this:
``./tools/code-style/uncrustify-fix-style.sh `find cpu/cc2538 -type f -name "*.[ch]"` ``
This is _not_ a silver bullet and developer intervention is still required. Below are some examples of code which will get misformatted by uncrustify:
* Math symbol following a cast to a typedef
```
a = (uint8_t) ~P0_1; /* Cast to a typedef. Space gets added here (incorrect) */
a = (int)~P0_1; /* Cast to a known type. Space gets removed (correct) */
a = (uint8_t)P0_1; /* Variable directly after the cast. Space gets removed (correct) */
```
* `while(<condition>);` will become `while(<condition>) ;` (space incorrectly added after closing paren)
* `asm("wfi");` becomes `asm ("wfi");`: A space gets added before the opening paren, because the `asm` keyword stops this from getting interpreted as a normal function call / macro invocation. This is only a problem with `asm`. For instance, `foo("bar");` gets formatted correctly.
Naming
------
We require that all code contributed to the Contiki tree follow the
Contiki source code naming standard:
* File names are composed of lower-case characters and dashes. Like
this: simple-udp.c
* Variable and function names are composed of lower-case characters
and underscores. Like this: simple_udp_send();
* Variable and function names that are visible outside of their module
must begin with the name of the module. Like this:
simple_udp_send(), which is in the simple-udp module, declared in
simple-udp.h, and implemented in simple-udp.c.
* C macros are composed of upper-case characters and underscores. Like
this: PROCESS_THREAD().
* Configuration definitions begin with the module name and CONF_. Like
this: PROCESS_CONF_NUMEVENTS.
How to Contribute Code
----------------------
When your code is formatted according to the Contiki code style and
follows the Contiki naming standard, it is time to send it to the
Contiki maintainers to look at!
All code contributions to Contiki are submitted as [Github pull
requests](https://help.github.com/articles/using-pull-requests). Pull
requests will be reviewed and accepted according to the guidelines
found in the next section.
The basic guidelines to to start a Pull-Request:
* Create a new branch for your modifications. This branch should be based on the latest contiki master branch.
* If you already added the commits to another branch you can [cherry-pick](http://git-scm.com/docs/git-cherry-pick) them onto your new branch.
* Push the new branch to github.
* Raise the new Pull Requests on this new branch. Raising a Pull Request for the master branch is almost always a bad idea.
* If changes are requested do not close the pull request but rewrite your history. [Details about rewriting your history](http://git-scm.com/book/en/Git-Tools-Rewriting-History)
* You now force-push the changes to github. The pull-request is automatically updated.
In Git terminology this is equivalent to:
* Make sure you have the original contiki repo as origin.
```bash
$ git remote -v
contiki-orig https://github.com/contiki-os/contiki.git
```
* If not add it
```bash
$ git remote add contiki-orig https://github.com/contiki-os/contiki.git
```
* Make sure you have the latest version of your remotes
```bash
$ git remote update
```
* Create a new branch "my_new_feature" based on the latest contiki master branch
```bash
$ git checkout contiki-orig/master -b my_new_feature
```
* Add your work. For example by cherry-picking your changes from another branch.
```bash
$ git cherry-pick <HASH OF COMMIT>
```
* Push to _your_ github repository
```bash
$ git push origin my_new_feature
```
* Make a Pull Request for that branch
* Rewrite your history if requested
```bash
$ git rebase -i contiki-orig/master
```
* As rewriting your history can break things you must force-push the changes. **Warning**: Force-pushing normally is dangerous and you might break things. Make sure you are never force-pushing branches other people are supposed to work with.
```bash
$ git push origin my_new_feature -f
```
* NOTE: To avoid all the pain of selectively picking commits, rebasing and force-pushing - begin your development with a branch OTHER THAN your master branch, and push changes to that branch after any local commits.
Pull Request Merging Policy
---------------------------
Pull requests (PRs) are reviewed by the [merge team](https://github.com/orgs/contiki-os/people).
Generally, PRs require two "+1" before they can be merged by someone on the merge team.
The since Contiki 3.0, the merging policy is the following:
* The PR receives **one "-1"** from a merge team member (along with specific feedback). The PR is closed. A "-1" must be accompanied with a clear explanation why the PR will not be considered for inclusion.
* The PR receives **two "+1"** from merge team members. The PR is merged.
* The PR was inactive for **two months**. A team member may either:
* Comment "Is there any interest for this PR? Is there any work pending on it? If not I will close it in **one month**." Back to initial state in case of activity, close otherwise.
* Comment "I approve this PR. If nobody disapproves within **one month**, I will merge it." Back to initial state in case of activity, merge otherwise.
There is an exception to the rule.
Code that requires esoteric expertise such as some applications, platforms or tools can be merged after a single "+1" from its domain expert.
Travis / Regression testing
---------------------------
[Travis](https://travis-ci.org/) is a service that runs regression
tests. If you make a pull-request for Contiki this is automatically
forwarded to Travis and regression tests are run. A box with
information about the state of you pull request should show up after a
minute or two.
If the test fails it is likely that something is wrong with your
code. Please look carefully at the log. It might also be that some
package on the testing VM was updated and causes the build to fail. If
you are sure that is is not your code causing the tests to fail start
a new issue describing the problem. Also note this in your pull
request.
You can also register at [Travis](https://travis-ci.org/) for
free. Once you activated your Contiki repository, every push will be
tested at Travis. The configuration is part of the contiki repository
and testing will therefore work out-of-the-box. At Travis you then get
an overview of the state of each of your branches.
New Platforms
-------------
A new hardware port will be considered for inclusion in mainline Contiki
if it satisfies the following rules:
* There must be at least one person willing and committed to maintain it.
They may but do not have to be the people who wrote the code. Similarly,
they may but do not have to be affiliated with the hardware manufacturer.
In the first instance, code maintenance would mean keeping the port up to
speed by submitting pull requests as Contiki moves forward. In the longer
term, people who maintain a reasonable level of commitment and who demonstrate
that they know what they're doing may be invited to become repo collaborators.
* The hardware must be commercially available and of interest to a wide audience.
In other words, ports for bespoke hardware built for e.g. a specific project /
a single customer / niche markets are more suitable for a Contiki fork.
* The code must strictly adhere to the Contiki code style, as discussed above.
* The new files must have a clear copyright notice and license header. Contiki's
preferred software license is the
[3-clause BSD](http://opensource.org/licenses/BSD-3-Clause).
Other licenses may also be considered
as long as they are compatible with the 3-clause BSD (e.g. the Apache 2.0 license).
Conversely, code distributed under GPL cannot be considered. The same applies to
bespoke licenses, such as those allowing use or redistribution only together with
certain kinds of hardware.
* The port must demonstrate a certain degree of completeness and maturity. Common sense
applies here.
* The port must be accompanied by examples demonstrating basic functionality. This could
be a set of examples under `examples/<new-hardware-port>` and/or documentation of
which existing examples are meant to work.
* The port must provide compile regression tests by extending the existing travis
integration testing framework. Again, we can't specify explicitly
what those tests should be, but something more interesting than hello-world is expected.
* The work must be documented. The documentation could be README.md files
under the platform / cpu / example dirs or wiki pages. Doxygen comments are
also encouraged. The documentation should include:
* A getting started guide, including a list of tools required to use the platform
(e.g. toolchain, software to program the device), where to get them from and brief notes
how to install them (can simply be a list of links to external guides)
* A list of things which will work off the shelf
* A list of things which are not meant to work, if any
* Additional reading resources (e.g. datasheets, hardware user guides, web resources)
* A ToDo list, if applicable.
* It must be possible to use the port using free software. We do not discourage the
use of commercial software (e.g. support for a commercial toolchain), quite the opposite.
However, we will insist on the existence of a free alternative for everything.
After the port has been accepted, things meant to work off the shelf should
keep working off the shelf as Contiki moves forward.
We appreciate that, for many people, contributing to Contiki is a spare time
activity and our expectations from port maintainers take this into
consideration. All we ask from maintainers is to comment on and address
relevant pull requests at a reasonable frequency and to make sure travis keeps
passing. In other words, we just want platforms to stay healthy over time and
to thus avoid becoming very broken / obsolete.

View file

@ -14,10 +14,6 @@ ifeq ($(TARGET),)
endif
endif
ifeq ($(UIP_CONF_IPV6),1)
CFLAGS += -DUIP_CONF_IPV6=1
endif
ifeq ($(DEFINES),)
-include Makefile.$(TARGET).defines
ifneq ($(DEFINES),)
@ -35,6 +31,13 @@ ifndef HOST_OS
endif
endif
#More debug information when running in CI
ifdef CI
ifeq ($(CI),true)
V = 1
endif
endif
usage:
@echo "make MAKETARGETS... [TARGET=(TARGET)] [savetarget] [targets]"
@ -60,9 +63,46 @@ CFLAGS += -DCONTIKI=1 -DCONTIKI_TARGET_$(TARGET_UPPERCASE)=1
MODULES += core/sys core/dev core/lib
# Include IPv6, IPv4, and/or Rime
HAS_STACK = 0
ifeq ($(CONTIKI_WITH_IPV4),1)
HAS_STACK = 1
CFLAGS += -DNETSTACK_CONF_WITH_IPV4=1
MODULES += core/net/ipv4 core/net/ip
endif
ifeq ($(CONTIKI_WITH_RIME),1)
HAS_STACK = 1
CFLAGS += -DNETSTACK_CONF_WITH_RIME=1
MODULES += core/net/rime
endif
# Make IPv6 the default stack
ifeq ($(HAS_STACK),0)
ifneq ($(CONTIKI_WITH_IPV6),0)
CONTIKI_WITH_IPV6 = 1
endif
endif
ifeq ($(CONTIKI_WITH_IPV6),1)
CFLAGS += -DNETSTACK_CONF_WITH_IPV6=1
ifneq ($(CONTIKI_WITH_RPL),0)
CONTIKI_WITH_RPL = 1
endif
MODULES += core/net/ipv6 core/net/ip
endif
ifeq ($(CONTIKI_WITH_RPL),1)
CFLAGS += -DUIP_CONF_IPV6_RPL=1
MODULES += core/net/rpl
else
CFLAGS += -DUIP_CONF_IPV6_RPL=0
endif
CONTIKI_SOURCEFILES += $(CONTIKIFILES)
CONTIKIDIRS += ${addprefix $(CONTIKI)/core/,dev lib net net/mac net/rime \
CONTIKIDIRS += ${addprefix $(CONTIKI)/core/,dev lib net net/llsec net/mac net/rime \
net/rpl sys cfs ctk lib/ctk loader . }
oname = ${patsubst %.cpp,%.o,${patsubst %.c,%.o,${patsubst %.S,%.o,$(1)}}}
@ -107,7 +147,6 @@ endif
ifdef MODULES
UNIQUEMODULES = $(call uniq,$(MODULES))
MODULESSUBST = ${subst /,-,$(UNIQUEMODULES)}
MODULEDIRS = ${wildcard ${addprefix $(CONTIKI)/, $(UNIQUEMODULES)}}
MODULES_SOURCES = ${foreach d, $(MODULEDIRS), ${subst ${d}/,,${wildcard $(d)/*.c}}}
CONTIKI_SOURCEFILES += $(MODULES_SOURCES)
@ -145,7 +184,7 @@ CONTIKI_CPU_DIRS_CONCAT = ${addprefix $(CONTIKI_CPU)/, \
$(CONTIKI_CPU_DIRS)}
SOURCEDIRS = . $(PROJECTDIRS) $(CONTIKI_TARGET_DIRS_CONCAT) \
$(CONTIKI_CPU_DIRS_CONCAT) $(CONTIKIDIRS) $(APPDS) ${dir $(target_makefile)}
$(CONTIKI_CPU_DIRS_CONCAT) $(CONTIKIDIRS) $(APPDS) $(EXTERNALDIRS) ${dir $(target_makefile)}
vpath %.c $(SOURCEDIRS)
vpath %.cpp $(SOURCEDIRS)
@ -155,8 +194,10 @@ CFLAGS += ${addprefix -I,$(SOURCEDIRS) $(CONTIKI)}
### Check for a git repo and pass version if found
### git.exe in Windows cmd shells may require no stderr redirection
#RELSTR=${shell git describe --tags}
RELSTR=${shell git --git-dir ${CONTIKI}/.git describe --tags 2>/dev/null}
ifndef RELSTR
RELSTR:=${shell git --git-dir ${CONTIKI}/.git describe --tags --always}
endif
ifneq ($(RELSTR),)
CFLAGS += -DCONTIKI_VERSION_STRING=\"Contiki-$(RELSTR)\"
endif
@ -186,7 +227,7 @@ clean:
-rm -rf $(OBJECTDIR)
distclean: clean
-rm -rf $(CONTIKI_PROJECT).$(TARGET)
-rm -f ${addsuffix .$(TARGET),$(CONTIKI_PROJECT)}
-include $(CONTIKI)/platform/$(TARGET)/Makefile.customrules-$(TARGET)

View file

@ -1,7 +1,7 @@
The Contiki Operating System
============================
[![Build Status](https://secure.travis-ci.org/contiki-os/contiki.png)](http://travis-ci.org/contiki-os/contiki)
[![Build Status](https://travis-ci.org/contiki-os/contiki.svg?branch=master)](https://travis-ci.org/contiki-os/contiki/branches)
Contiki is an open source operating system that runs on tiny low-power
microcontrollers and makes it possible to develop applications that

View file

@ -586,7 +586,6 @@ derive_relation(lvm_instance_t *p, derivation_t *local_derivations)
node_type_t type;
operand_t operand[2];
int i;
int var;
int variable_id;
operand_value_t *value;
derivation_t *derivation;
@ -640,11 +639,9 @@ derive_relation(lvm_instance_t *p, derivation_t *local_derivations)
if(operand[1].type == LVM_VARIABLE) {
return DERIVATION_ERROR;
}
var = 0;
variable_id = operand[0].value.id;
value = &operand[1].value;
} else {
var = 1;
variable_id = operand[1].value.id;
value = &operand[0].value;
}

View file

@ -49,24 +49,99 @@
*
*/
#include <stdlib.h>
#include <string.h>
#include "arduino-process.h"
#include "hw_timer.h"
#include "adc.h"
#include "hw-arduino.h"
#include "contiki.h"
#define DEBUG 0
#if DEBUG
#include <stdio.h>
#define PRINTF(...) printf(__VA_ARGS__)
#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]", (lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3], (lladdr)->addr[4], (lladdr)->addr[5])
#else
#define PRINTF(...)
#define PRINT6ADDR(addr)
#define PRINTLLADDR(addr)
#endif
extern volatile uint8_t mcusleepcycle;
#if PLATFORM_HAS_BUTTON
#include "rest-engine.h"
#include "dev/button-sensor.h"
extern resource_t res_event, res_separate;
#endif /* PLATFORM_HAS_BUTTON */
volatile uint8_t mcusleepcycleval;
/*-------------- enabled sleep mode ----------------------------------------*/
void
mcu_sleep_init(void)
{
mcusleepcycleval=mcusleepcycle;
}
void
mcu_sleep_on(void)
{
mcusleepcycle= mcusleepcycleval;
}
/*--------------- disable sleep mode ---------------------------------------*/
void
mcu_sleep_off(void)
{
mcusleepcycle=0;
}
/*---------------- set duty cycle value ------------------------------------*/
void
mcu_sleep_set(uint8_t value)
{
mcusleepcycleval= value;
mcusleepcycle = mcusleepcycleval;
}
PROCESS(arduino_sketch, "Arduino Sketch Wrapper");
#ifndef LOOP_INTERVAL
#define LOOP_INTERVAL (1 * CLOCK_SECOND)
#endif
PROCESS_THREAD(arduino_sketch, ev, data)
{
PROCESS_BEGIN();
static struct etimer loop_periodic_timer;
arduino_pwm_timer_init ();
PROCESS_BEGIN();
adc_init ();
mcu_sleep_init ();
setup ();
/* Define application-specific events here. */
etimer_set(&loop_periodic_timer, LOOP_INTERVAL);
while (1) {
loop ();
/* Give other processes a chance to run */
PROCESS_PAUSE();
PROCESS_WAIT_EVENT();
#if PLATFORM_HAS_BUTTON
if(ev == sensors_event && data == &button_sensor) {
mcu_sleep_off();
PRINTF("*******BUTTON*******\n");
/* Call the event_handler for this application-specific event. */
res_event.trigger();
/* Also call the separate response example handler. */
res_separate.resume();
mcu_sleep_on();
}
#endif /* PLATFORM_HAS_BUTTON */
if(etimer_expired(&loop_periodic_timer)) {
mcu_sleep_off();
loop ();
mcu_sleep_on();
etimer_reset(&loop_periodic_timer);
}
}
PROCESS_END();
}

View file

@ -51,6 +51,13 @@
#include "contiki.h"
/*--------------- enable sleep mode ---------------------------------------*/
void mcu_sleep_on(void);
/*--------------- disable sleep mode ---------------------------------------*/
void mcu_sleep_off(void);
/*---------------- set sleep value ------------------------------------*/
void mcu_sleep_set(uint8_t value);
extern void loop (void);
extern void setup (void);
extern void arduino_init (void);

View file

@ -0,0 +1 @@
at-master_src = at-master.c

148
apps/at-master/at-master.c Normal file
View file

@ -0,0 +1,148 @@
/*
* Copyright (c) 2015, Zolertia - http://www.zolertia.com
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
*/
#include "contiki.h"
#include "contiki-lib.h"
#include "at-master.h"
#include "cpu.h"
#include "dev/uart.h"
#include "dev/serial-line.h"
#include "dev/sys-ctrl.h"
#include "lib/list.h"
#include "sys/cc.h"
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
/*---------------------------------------------------------------------------*/
#define DEBUG 0
#if DEBUG
#define PRINTF(...) printf(__VA_ARGS__)
#else
#define PRINTF(...)
#endif
/*---------------------------------------------------------------------------*/
LIST(at_cmd_list);
process_event_t at_cmd_received_event;
/*---------------------------------------------------------------------------*/
static uint8_t at_uart = 0;
/*---------------------------------------------------------------------------*/
PROCESS(at_process, "AT process");
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(at_process, ev, data)
{
uint8_t plen;
char *pch, *buf;
struct at_cmd *a;
PROCESS_BEGIN();
while(1) {
PROCESS_WAIT_EVENT_UNTIL(ev == serial_line_event_message && data != NULL);
buf = (char *)data;
plen = strlen(buf);
for(a = list_head(at_cmd_list); a != NULL; a = list_item_next(a)) {
pch = strstr(buf, a->cmd_header);
if((plen <= a->cmd_max_len) && (pch != NULL)) {
if(strncmp(a->cmd_header, pch, a->cmd_hdr_len) == 0) {
if((a->cmd_hdr_len == plen) || (a->cmd_max_len > a->cmd_hdr_len)) {
a->event_callback(a, plen, (char *)pch);
process_post(a->app_process, at_cmd_received_event, NULL);
break;
}
}
}
}
}
PROCESS_END();
}
/*---------------------------------------------------------------------------*/
struct at_cmd *
at_list(void)
{
return list_head(at_cmd_list);
}
/*---------------------------------------------------------------------------*/
uint8_t
at_send(char *s, uint8_t len)
{
uint8_t i = 0;
while(s && *s != 0) {
if(i >= len) {
break;
}
uart_write_byte(at_uart, *s++);
i++;
}
return i;
}
/*---------------------------------------------------------------------------*/
void
at_init(uint8_t uart_sel)
{
static uint8_t inited = 0;
if(!inited) {
list_init(at_cmd_list);
at_cmd_received_event = process_alloc_event();
inited = 1;
at_uart = uart_sel;
uart_init(at_uart);
uart_set_input(at_uart, serial_line_input_byte);
serial_line_init();
process_start(&at_process, NULL);
PRINTF("AT: Started (%u)\n", at_uart);
}
}
/*---------------------------------------------------------------------------*/
at_status_t
at_register(struct at_cmd *cmd, struct process *app_process,
const char *cmd_hdr, const uint8_t hdr_len,
const uint8_t cmd_max_len, at_event_callback_t event_callback)
{
if((hdr_len < 1) || (cmd_max_len < 1) || (!strncmp(cmd_hdr, "AT", 2) == 0) ||
(event_callback == NULL)) {
PRINTF("AT: Invalid argument\n");
return AT_STATUS_INVALID_ARGS_ERROR;
}
memset(cmd, 0, sizeof(struct at_cmd));
cmd->event_callback = event_callback;
cmd->cmd_header = cmd_hdr;
cmd->cmd_hdr_len = hdr_len;
cmd->cmd_max_len = cmd_max_len;
cmd->app_process = app_process;
list_add(at_cmd_list, cmd);
PRINTF("AT: registered HDR %s LEN %u MAX %u\n", cmd->cmd_header,
cmd->cmd_hdr_len,
cmd->cmd_max_len);
return AT_STATUS_OK;
}
/*---------------------------------------------------------------------------*/

123
apps/at-master/at-master.h Normal file
View file

@ -0,0 +1,123 @@
/*
* Copyright (c) 2015, Zolertia - http://www.zolertia.com
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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 AT_MASTER_H_
#define AT_MASTER_H_
#include "contiki.h"
/*---------------------------------------------------------------------------*/
#define AT_DEFAULT_RESPONSE_OK "\r\nOK\r\n"
#define AT_DEFAULT_RESPONSE_ERROR "\r\nERROR\r\n"
/*---------------------------------------------------------------------------*/
#define AT_RESPONSE(x) at_send((x), (strlen(x)))
/*---------------------------------------------------------------------------*/
extern process_event_t at_cmd_received_event;
struct at_cmd;
/*---------------------------------------------------------------------------*/
typedef enum {
AT_STATUS_OK,
AT_STATUS_ERROR,
AT_STATUS_INVALID_ARGS_ERROR,
} at_status_t;
/*---------------------------------------------------------------------------*/
/**
* \brief AT initialization
* \param uart selects which UART to use
*
* The AT driver invokes this function upon registering a command, this will
* wait for the serial_line_event_message event
*/
void at_init(uint8_t uart);
/*---------------------------------------------------------------------------*/
/**
* \brief AT initialization
* \param uart selects which UART to use
*
* The AT driver invokes this function upon registering a command, this will
* wait for the serial_line_event_message event
*/
uint8_t at_send(char *s, uint8_t len);
/*---------------------------------------------------------------------------*/
/**
* \brief AT event callback
* \param cmd A pointer to the AT command placeholder
* \param len Lenght of the received data (including the AT command header)
* \param data A user-defined pointer
*
* The AT event callback function gets called whenever there is an
* event on an incoming AT command
*/
typedef void (*at_event_callback_t)(struct at_cmd *cmd,
uint8_t len,
char *data);
/*---------------------------------------------------------------------------*/
struct at_cmd {
struct at_cmd *next;
const char *cmd_header;
uint8_t cmd_hdr_len;
uint8_t cmd_max_len;
at_event_callback_t event_callback;
struct process *app_process;
};
/*---------------------------------------------------------------------------*/
/**
* \brief Registers the callback to return an AT command
* \param cmd A pointer to the CMD placeholder
* \param cmd_hdr String to compare when an AT command is received
* \param cmd_len Lenght of cmd_hdr
* \param event_callback Callback function to handle the AT command
* \return AT_STATUS_OK or AT_STATUS_INVALID_ARGS_ERROR
*
* Register the commands to search for when a valid AT frame has been received
*/
at_status_t at_register(struct at_cmd *cmd,
struct process *app_process,
const char *cmd_hdr,
const uint8_t cmd_hdr_len,
const uint8_t cmd_max_len,
at_event_callback_t event_callback);
/*---------------------------------------------------------------------------*/
struct at_cmd *at_list(void);
/*---------------------------------------------------------------------------*/
/**
* \brief Registers the callback to return an AT command
* \param cmd A pointer to the CMD placeholder
* \param cmd_hdr String to compare when an AT command is received
* \param cmd_len Lenght of cmd_hdr
* \param event_callback Callback function to handle the AT command
* \return AT_STATUS_OK or AT_STATUS_INVALID_ARGS_ERROR
*
* Register the commands to search for when a valid AT frame has been received
*/
at_status_t at_register(struct at_cmd *cmd,
struct process *app_process,
const char *cmd_hdr,
const uint8_t cmd_hdr_len,
const uint8_t cmd_max_len,
at_event_callback_t event_callback);
#endif /* AT_MASTER_H_ */

View file

@ -24,7 +24,7 @@ static char send_udp = 0;
static const char *prompt = "contiki> ";
/*---------------------------------------------------------------------------*/
static char * CC_FASTCALL
static char *
n(uint16_t num, char *ptr)
{
uint16_t d;

View file

@ -0,0 +1,10 @@
codeprop-tmp_src = codeprop-tmp.c
# Enable LARGE MEMORY MODEL supports for WISMOTE and EXP5438 platform
ifeq ($(TARGET),wismote)
TARGET_MEMORY_MODEL = large
endif
ifeq ($(TARGET),exp5438)
TARGET_MEMORY_MODEL = large
endif

View file

@ -30,9 +30,6 @@
*
*/
/** \addtogroup esb
* @{ */
/**
*
* \file
@ -510,4 +507,3 @@ uipcall(void *state)
}
}
/*---------------------------------------------------------------------*/
/** @} */

View file

@ -30,9 +30,6 @@
*
*/
/** \addtogroup esb
* @{ */
/**
*
* \file
@ -477,4 +474,3 @@ uipcall(void *state)
}
}
/*---------------------------------------------------------------------*/
/** @} */

View file

@ -1 +0,0 @@
deluge_src = deluge.c

View file

@ -1,698 +0,0 @@
/*
* Copyright (c) 2007, Swedish Institute of Computer Science
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* An implementation of the Deluge protocol.
* (Hui and Culler: The dynamic behavior of a data
* dissemination protocol for network programming at scale,
* ACM SenSys 2004)
* \author
* Nicolas Tsiftes <nvt@sics.se>
*/
#include "contiki.h"
#include "net/rime/rime.h"
#include "cfs/cfs.h"
#include "loader/elfloader.h"
#include "lib/crc16.h"
#include "lib/random.h"
#include "sys/node-id.h"
#include "deluge.h"
#if NETSIM
#include "ether.h"
#include <stdio.h>
#endif
#include "dev/leds.h"
#include <stdlib.h>
#include <string.h>
#define DEBUG 0
#if DEBUG
#include <stdio.h>
#define PRINTF(...) printf(__VA_ARGS__)
#else
#define PRINTF(...)
#endif
/* Implementation-specific variables. */
static struct broadcast_conn deluge_broadcast;
static struct unicast_conn deluge_uc;
static struct deluge_object current_object;
static process_event_t deluge_event;
/* Deluge variables. */
static int deluge_state;
static int old_summary;
static int neighbor_inconsistency;
static unsigned r_interval;
static unsigned recv_adv;
static int broadcast_profile;
/* Deluge timers. */
static struct ctimer rx_timer;
static struct ctimer tx_timer;
static struct ctimer summary_timer;
static struct ctimer profile_timer;
/* Deluge objects will get an ID that defaults to the current value of
the next_object_id parameter. */
static deluge_object_id_t next_object_id;
/* Rime callbacks. */
static void broadcast_recv(struct broadcast_conn *, const linkaddr_t *);
static void unicast_recv(struct unicast_conn *, const linkaddr_t *);
static const struct broadcast_callbacks broadcast_call = {broadcast_recv, NULL};
static const struct unicast_callbacks unicast_call = {unicast_recv, NULL};
/* The Deluge process manages the main Deluge timer. */
PROCESS(deluge_process, "Deluge");
static void
transition(int state)
{
if(state != deluge_state) {
switch(deluge_state) {
case DELUGE_STATE_MAINTAIN:
ctimer_stop(&summary_timer);
ctimer_stop(&profile_timer);
break;
case DELUGE_STATE_RX:
ctimer_stop(&rx_timer);
break;
case DELUGE_STATE_TX:
ctimer_stop(&tx_timer);
break;
}
deluge_state = state;
}
}
static int
write_page(struct deluge_object *obj, unsigned pagenum, unsigned char *data)
{
cfs_offset_t offset;
offset = pagenum * S_PAGE;
if(cfs_seek(obj->cfs_fd, offset, CFS_SEEK_SET) != offset) {
return -1;
}
return cfs_write(obj->cfs_fd, (char *)data, S_PAGE);
}
static int
read_page(struct deluge_object *obj, unsigned pagenum, unsigned char *buf)
{
cfs_offset_t offset;
offset = pagenum * S_PAGE;
if(cfs_seek(obj->cfs_fd, offset, CFS_SEEK_SET) != offset) {
return -1;
}
return cfs_read(obj->cfs_fd, (char *)buf, S_PAGE);
}
static void
init_page(struct deluge_object *obj, int pagenum, int have)
{
struct deluge_page *page;
unsigned char buf[S_PAGE];
page = &obj->pages[pagenum];
page->flags = 0;
page->last_request = 0;
page->last_data = 0;
if(have) {
page->version = obj->version;
page->packet_set = ALL_PACKETS;
page->flags |= PAGE_COMPLETE;
read_page(obj, pagenum, buf);
page->crc = crc16_data(buf, S_PAGE, 0);
} else {
page->version = 0;
page->packet_set = 0;
}
}
static cfs_offset_t
file_size(const char *file)
{
int fd;
cfs_offset_t size;
fd = cfs_open(file, CFS_READ);
if(fd < 0) {
return (cfs_offset_t)-1;
}
size = cfs_seek(fd, 0, CFS_SEEK_END);
cfs_close(fd);
return size;
}
static int
init_object(struct deluge_object *obj, char *filename, unsigned version)
{
static struct deluge_page *page;
int i;
obj->cfs_fd = cfs_open(filename, CFS_READ | CFS_WRITE);
if(obj->cfs_fd < 0) {
return -1;
}
obj->filename = filename;
obj->object_id = next_object_id++;
obj->size = file_size(filename);
obj->version = obj->update_version = version;
obj->current_rx_page = 0;
obj->nrequests = 0;
obj->tx_set = 0;
obj->pages = malloc(OBJECT_PAGE_COUNT(*obj) * sizeof(*obj->pages));
if(obj->pages == NULL) {
cfs_close(obj->cfs_fd);
return -1;
}
for(i = 0; i < OBJECT_PAGE_COUNT(current_object); i++) {
page = &current_object.pages[i];
init_page(&current_object, i, 1);
}
memset(obj->current_page, 0, sizeof(obj->current_page));
return 0;
}
static int
highest_available_page(struct deluge_object *obj)
{
int i;
for(i = 0; i < OBJECT_PAGE_COUNT(*obj); i++) {
if(!(obj->pages[i].flags & PAGE_COMPLETE)) {
break;
}
}
return i;
}
static void
send_request(void *arg)
{
struct deluge_object *obj;
struct deluge_msg_request request;
obj = (struct deluge_object *)arg;
request.cmd = DELUGE_CMD_REQUEST;
request.pagenum = obj->current_rx_page;
request.version = obj->pages[request.pagenum].version;
request.request_set = ~obj->pages[obj->current_rx_page].packet_set;
request.object_id = obj->object_id;
PRINTF("Sending request for page %d, version %u, request_set %u\n",
request.pagenum, request.version, request.request_set);
packetbuf_copyfrom(&request, sizeof(request));
unicast_send(&deluge_uc, &obj->summary_from);
/* Deluge R.2 */
if(++obj->nrequests == CONST_LAMBDA) {
/* XXX check rate here too. */
obj->nrequests = 0;
transition(DELUGE_STATE_MAINTAIN);
} else {
ctimer_reset(&rx_timer);
}
}
static void
advertise_summary(struct deluge_object *obj)
{
struct deluge_msg_summary summary;
if(recv_adv >= CONST_K) {
ctimer_stop(&summary_timer);
return;
}
summary.cmd = DELUGE_CMD_SUMMARY;
summary.version = obj->update_version;
summary.highest_available = highest_available_page(obj);
summary.object_id = obj->object_id;
PRINTF("Advertising summary for object id %u: version=%u, available=%u\n",
(unsigned)obj->object_id, summary.version, summary.highest_available);
packetbuf_copyfrom(&summary, sizeof(summary));
broadcast_send(&deluge_broadcast);
}
static void
handle_summary(struct deluge_msg_summary *msg, const linkaddr_t *sender)
{
int highest_available, i;
clock_time_t oldest_request, oldest_data, now;
struct deluge_page *page;
highest_available = highest_available_page(&current_object);
if(msg->version != current_object.version ||
msg->highest_available != highest_available) {
neighbor_inconsistency = 1;
} else {
recv_adv++;
}
if(msg->version < current_object.version) {
old_summary = 1;
broadcast_profile = 1;
}
/* Deluge M.5 */
if(msg->version == current_object.update_version &&
msg->highest_available > highest_available) {
if(msg->highest_available > OBJECT_PAGE_COUNT(current_object)) {
PRINTF("Error: highest available is above object page count!\n");
return;
}
oldest_request = oldest_data = now = clock_time();
for(i = 0; i < msg->highest_available; i++) {
page = &current_object.pages[i];
if(page->last_request < oldest_request) {
oldest_request = page->last_request;
}
if(page->last_request < oldest_data) {
oldest_data = page->last_data;
}
}
if(((now - oldest_request) / CLOCK_SECOND) <= 2 * r_interval ||
((now - oldest_data) / CLOCK_SECOND) <= r_interval) {
return;
}
linkaddr_copy(&current_object.summary_from, sender);
transition(DELUGE_STATE_RX);
if(ctimer_expired(&rx_timer)) {
ctimer_set(&rx_timer,
CONST_OMEGA * ESTIMATED_TX_TIME + ((unsigned)random_rand() % T_R),
send_request, &current_object);
}
}
}
static void
send_page(struct deluge_object *obj, unsigned pagenum)
{
unsigned char buf[S_PAGE];
struct deluge_msg_packet pkt;
unsigned char *cp;
pkt.cmd = DELUGE_CMD_PACKET;
pkt.pagenum = pagenum;
pkt.version = obj->pages[pagenum].version;
pkt.packetnum = 0;
pkt.object_id = obj->object_id;
pkt.crc = 0;
read_page(obj, pagenum, buf);
/* Divide the page into packets and send them one at a time. */
for(cp = buf; cp + S_PKT <= (unsigned char *)&buf[S_PAGE]; cp += S_PKT) {
if(obj->tx_set & (1 << pkt.packetnum)) {
pkt.crc = crc16_data(cp, S_PKT, 0);
memcpy(pkt.payload, cp, S_PKT);
packetbuf_copyfrom(&pkt, sizeof(pkt));
broadcast_send(&deluge_broadcast);
}
pkt.packetnum++;
}
obj->tx_set = 0;
}
static void
tx_callback(void *arg)
{
struct deluge_object *obj;
obj = (struct deluge_object *)arg;
if(obj->current_tx_page >= 0 && obj->tx_set) {
send_page(obj, obj->current_tx_page);
/* Deluge T.2. */
if(obj->tx_set) {
packetbuf_set_attr(PACKETBUF_ATTR_PACKET_TYPE,
PACKETBUF_ATTR_PACKET_TYPE_STREAM);
ctimer_reset(&tx_timer);
} else {
packetbuf_set_attr(PACKETBUF_ATTR_PACKET_TYPE,
PACKETBUF_ATTR_PACKET_TYPE_STREAM_END);
obj->current_tx_page = -1;
transition(DELUGE_STATE_MAINTAIN);
}
}
}
static void
handle_request(struct deluge_msg_request *msg)
{
int highest_available;
if(msg->pagenum >= OBJECT_PAGE_COUNT(current_object)) {
return;
}
if(msg->version != current_object.version) {
neighbor_inconsistency = 1;
}
highest_available = highest_available_page(&current_object);
/* Deluge M.6 */
if(msg->version == current_object.version &&
msg->pagenum <= highest_available) {
current_object.pages[msg->pagenum].last_request = clock_time();
/* Deluge T.1 */
if(msg->pagenum == current_object.current_tx_page) {
current_object.tx_set |= msg->request_set;
} else {
current_object.current_tx_page = msg->pagenum;
current_object.tx_set = msg->request_set;
}
transition(DELUGE_STATE_TX);
ctimer_set(&tx_timer, CLOCK_SECOND, tx_callback, &current_object);
}
}
static void
handle_packet(struct deluge_msg_packet *msg)
{
struct deluge_page *page;
uint16_t crc;
struct deluge_msg_packet packet;
memcpy(&packet, msg, sizeof(packet));
PRINTF("Incoming packet for object id %u, version %u, page %u, packet num %u!\n",
(unsigned)packet.object_id, (unsigned)packet.version,
(unsigned)packet.pagenum, (unsigned)packet.packetnum);
if(packet.pagenum != current_object.current_rx_page) {
return;
}
if(packet.version != current_object.version) {
neighbor_inconsistency = 1;
}
page = &current_object.pages[packet.pagenum];
if(packet.version == page->version && !(page->flags & PAGE_COMPLETE)) {
memcpy(&current_object.current_page[S_PKT * packet.packetnum],
packet.payload, S_PKT);
crc = crc16_data(packet.payload, S_PKT, 0);
if(packet.crc != crc) {
PRINTF("packet crc: %hu, calculated crc: %hu\n", packet.crc, crc);
return;
}
page->last_data = clock_time();
page->packet_set |= (1 << packet.packetnum);
if(page->packet_set == ALL_PACKETS) {
/* This is the last packet of the requested page; stop streaming. */
packetbuf_set_attr(PACKETBUF_ATTR_PACKET_TYPE,
PACKETBUF_ATTR_PACKET_TYPE_STREAM_END);
write_page(&current_object, packet.pagenum, current_object.current_page);
page->version = packet.version;
page->flags = PAGE_COMPLETE;
PRINTF("Page %u completed\n", packet.pagenum);
current_object.current_rx_page++;
if(packet.pagenum == OBJECT_PAGE_COUNT(current_object) - 1) {
current_object.version = current_object.update_version;
leds_on(LEDS_RED);
PRINTF("Update completed for object %u, version %u\n",
(unsigned)current_object.object_id, packet.version);
} else if(current_object.current_rx_page < OBJECT_PAGE_COUNT(current_object)) {
if(ctimer_expired(&rx_timer)) {
ctimer_set(&rx_timer,
CONST_OMEGA * ESTIMATED_TX_TIME + (random_rand() % T_R),
send_request, &current_object);
}
}
/* Deluge R.3 */
transition(DELUGE_STATE_MAINTAIN);
} else {
/* More packets to come. Put lower layers in streaming mode. */
packetbuf_set_attr(PACKETBUF_ATTR_PACKET_TYPE,
PACKETBUF_ATTR_PACKET_TYPE_STREAM);
}
}
}
static void
send_profile(struct deluge_object *obj)
{
struct deluge_msg_profile *msg;
unsigned char buf[sizeof(*msg) + OBJECT_PAGE_COUNT(*obj)];
int i;
if(broadcast_profile && recv_adv < CONST_K) {
broadcast_profile = 0;
msg = (struct deluge_msg_profile *)buf;
msg->cmd = DELUGE_CMD_PROFILE;
msg->version = obj->version;
msg->npages = OBJECT_PAGE_COUNT(*obj);
msg->object_id = obj->object_id;
for(i = 0; i < msg->npages; i++) {
msg->version_vector[i] = obj->pages[i].version;
}
packetbuf_copyfrom(buf, sizeof(buf));
broadcast_send(&deluge_broadcast);
}
}
static void
handle_profile(struct deluge_msg_profile *msg)
{
int i;
int npages;
struct deluge_object *obj;
char *p;
obj = &current_object;
if(msg->version <= current_object.update_version) {
return;
}
PRINTF("Received profile of version %u with a vector of %u pages.\n",
msg->version, msg->npages);
leds_off(LEDS_RED);
current_object.tx_set = 0;
npages = OBJECT_PAGE_COUNT(*obj);
obj->size = msg->npages * S_PAGE;
p = malloc(OBJECT_PAGE_COUNT(*obj) * sizeof(*obj->pages));
if(p == NULL) {
PRINTF("Failed to reallocate memory for pages!\n");
return;
}
memcpy(p, obj->pages, npages * sizeof(*obj->pages));
free(obj->pages);
obj->pages = (struct deluge_page *)p;
if(msg->npages < npages) {
npages = msg->npages;
}
for(i = 0; i < npages; i++) {
if(msg->version_vector[i] > obj->pages[i].version) {
obj->pages[i].packet_set = 0;
obj->pages[i].flags &= ~PAGE_COMPLETE;
obj->pages[i].version = msg->version_vector[i];
}
}
for(; i < msg->npages; i++) {
init_page(obj, i, 0);
}
obj->current_rx_page = highest_available_page(obj);
obj->update_version = msg->version;
transition(DELUGE_STATE_RX);
ctimer_set(&rx_timer,
CONST_OMEGA * ESTIMATED_TX_TIME + ((unsigned)random_rand() % T_R),
send_request, obj);
}
static void
command_dispatcher(const linkaddr_t *sender)
{
char *msg;
int len;
struct deluge_msg_profile *profile;
msg = packetbuf_dataptr();
len = packetbuf_datalen();
if(len < 1)
return;
switch(msg[0]) {
case DELUGE_CMD_SUMMARY:
if(len >= sizeof(struct deluge_msg_summary))
handle_summary((struct deluge_msg_summary *)msg, sender);
break;
case DELUGE_CMD_REQUEST:
if(len >= sizeof(struct deluge_msg_request))
handle_request((struct deluge_msg_request *)msg);
break;
case DELUGE_CMD_PACKET:
if(len >= sizeof(struct deluge_msg_packet))
handle_packet((struct deluge_msg_packet *)msg);
break;
case DELUGE_CMD_PROFILE:
profile = (struct deluge_msg_profile *)msg;
if(len >= sizeof(*profile) &&
len >= sizeof(*profile) + profile->npages * profile->version_vector[0])
handle_profile((struct deluge_msg_profile *)msg);
break;
default:
PRINTF("Incoming packet with unknown command: %d\n", msg[0]);
}
}
static void
unicast_recv(struct unicast_conn *c, const linkaddr_t *sender)
{
command_dispatcher(sender);
}
static void
broadcast_recv(struct broadcast_conn *c, const linkaddr_t *sender)
{
command_dispatcher(sender);
}
int
deluge_disseminate(char *file, unsigned version)
{
/* This implementation disseminates at most one object. */
if(next_object_id > 0 || init_object(&current_object, file, version) < 0) {
return -1;
}
process_start(&deluge_process, file);
return 0;
}
PROCESS_THREAD(deluge_process, ev, data)
{
static struct etimer et;
static unsigned time_counter;
static unsigned r_rand;
PROCESS_EXITHANDLER(goto exit);
PROCESS_BEGIN();
deluge_event = process_alloc_event();
broadcast_open(&deluge_broadcast, DELUGE_BROADCAST_CHANNEL, &broadcast_call);
unicast_open(&deluge_uc, DELUGE_UNICAST_CHANNEL, &unicast_call);
r_interval = T_LOW;
PRINTF("Maintaining state for object %s of %d pages\n",
current_object.filename, OBJECT_PAGE_COUNT(current_object));
deluge_state = DELUGE_STATE_MAINTAIN;
for(r_interval = T_LOW;;) {
if(neighbor_inconsistency) {
/* Deluge M.2 */
r_interval = T_LOW;
neighbor_inconsistency = 0;
} else {
/* Deluge M.3 */
r_interval = (2 * r_interval >= T_HIGH) ? T_HIGH : 2 * r_interval;
}
r_rand = r_interval / 2 + ((unsigned)random_rand() % (r_interval / 2));
recv_adv = 0;
old_summary = 0;
/* Deluge M.1 */
ctimer_set(&summary_timer, r_rand * CLOCK_SECOND,
(void *)(void *)advertise_summary, &current_object);
/* Deluge M.4 */
ctimer_set(&profile_timer, r_rand * CLOCK_SECOND,
(void *)(void *)send_profile, &current_object);
LONG_TIMER(et, time_counter, r_interval);
}
exit:
unicast_close(&deluge_uc);
broadcast_close(&deluge_broadcast);
if(current_object.cfs_fd >= 0) {
cfs_close(current_object.cfs_fd);
}
if(current_object.pages != NULL) {
free(current_object.pages);
}
PROCESS_END();
}

View file

@ -1,159 +0,0 @@
/*
* Copyright (c) 2007, Swedish Institute of Computer Science
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* Header for Deluge.
* \author
* Nicolas Tsiftes <nvt@sics.se>
*/
#ifndef DELUGE_H
#define DELUGE_H
#include "net/rime/rime.h"
PROCESS_NAME(deluge_process);
#define LONG_TIMER(et, counter, time) \
do { \
for (counter = 0; counter < time; counter++) { \
etimer_set(&et, CLOCK_SECOND); \
PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et)); \
} \
} while (0)
#define DELUGE_UNICAST_CHANNEL 55
#define DELUGE_BROADCAST_CHANNEL 56
/* All the packets in a page have been received. */
#define PAGE_COMPLETE 1
/* All pages up to, and including, this page are complete. */
#define PAGE_AVAILABLE 1
#define S_PKT 64 /* Deluge packet size. */
#define N_PKT 4 /* Packets per page. */
#define S_PAGE (S_PKT * N_PKT) /* Fixed page size. */
/* Bounds for the round time in seconds. */
#define T_LOW 2
#define T_HIGH 64
/* Random interval for request transmissions in jiffies. */
#define T_R (CLOCK_SECOND * 2)
/* Bound for the number of advertisements. */
#define CONST_K 1
/* The number of pages in this object. */
#define OBJECT_PAGE_COUNT(obj) (((obj).size + (S_PAGE - 1)) / S_PAGE)
#define ALL_PACKETS ((1 << N_PKT) - 1)
#define DELUGE_CMD_SUMMARY 1
#define DELUGE_CMD_REQUEST 2
#define DELUGE_CMD_PACKET 3
#define DELUGE_CMD_PROFILE 4
#define DELUGE_STATE_MAINTAIN 1
#define DELUGE_STATE_RX 2
#define DELUGE_STATE_TX 3
#define CONST_LAMBDA 2
#define CONST_ALPHA 0.5
#define CONST_OMEGA 8
#define ESTIMATED_TX_TIME (CLOCK_SECOND)
typedef uint8_t deluge_object_id_t;
struct deluge_msg_summary {
uint8_t cmd;
uint8_t version;
uint8_t highest_available;
deluge_object_id_t object_id;
};
struct deluge_msg_request {
uint8_t cmd;
uint8_t version;
uint8_t pagenum;
uint8_t request_set;
deluge_object_id_t object_id;
};
struct deluge_msg_packet {
uint8_t cmd;
uint8_t version;
uint8_t pagenum;
uint8_t packetnum;
uint16_t crc;
deluge_object_id_t object_id;
unsigned char payload[S_PKT];
};
struct deluge_msg_profile {
uint8_t cmd;
uint8_t version;
uint8_t npages;
deluge_object_id_t object_id;
uint8_t version_vector[];
};
struct deluge_object {
char *filename;
uint16_t object_id;
uint16_t size;
uint8_t version;
uint8_t update_version;
struct deluge_page *pages;
uint8_t current_rx_page;
int8_t current_tx_page;
uint8_t nrequests;
uint8_t current_page[S_PAGE];
uint8_t tx_set;
int cfs_fd;
linkaddr_t summary_from;
};
struct deluge_page {
uint32_t packet_set;
uint16_t crc;
clock_time_t last_request;
clock_time_t last_data;
uint8_t flags;
uint8_t version;
};
int deluge_disseminate(char *file, unsigned version);
#endif

View file

@ -88,7 +88,7 @@ makestrings(void)
uip_getdraddr(&addr);
makeaddr(&addr, gateway);
addrptr = resolv_getserver();
addrptr = uip_nameserver_get(0);
if(addrptr != NULL) {
makeaddr(addrptr, dnsserver);
}
@ -116,7 +116,6 @@ PROCESS_THREAD(dhcp_process, ev, data)
ctk_window_open(&window);
dhcpc_init(uip_lladdr.addr, sizeof(uip_lladdr.addr));
while(1) {
PROCESS_WAIT_EVENT();
@ -125,7 +124,7 @@ PROCESS_THREAD(dhcp_process, ev, data)
dhcpc_request();
set_statustext("Requesting...");
}
} else if(ev == tcpip_event) {
} else if(ev == tcpip_event || ev == PROCESS_EVENT_TIMER) {
dhcpc_appcall(ev, data);
} else if(ev == PROCESS_EVENT_EXIT ||
ev == ctk_signal_window_close) {
@ -147,7 +146,7 @@ dhcpc_configured(const struct dhcpc_state *s)
uip_sethostaddr(&s->ipaddr);
uip_setnetmask(&s->netmask);
uip_setdraddr(&s->default_router);
resolv_conf(&s->dnsaddr);
uip_nameserver_update(&s->dnsaddr, UIP_NAMESERVER_INFINITE_LIFETIME);
set_statustext("Configured.");
process_post(PROCESS_CURRENT(), SHOWCONFIG, NULL);
}

View file

@ -1 +0,0 @@
er-coap-03_src = er-coap-03-engine.c er-coap-03.c er-coap-03-transactions.c er-coap-03-observing.c

View file

@ -1,559 +0,0 @@
/*
* Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* CoAP implementation of the REST Engine
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "contiki.h"
#include "contiki-net.h"
#include "er-coap-03-engine.h"
#define DEBUG 0
#if DEBUG
#define PRINTF(...) printf(__VA_ARGS__)
#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
#define PRINTLLADDR(lladdr) PRINTF(" %02x:%02x:%02x:%02x:%02x:%02x ",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5])
#define PRINTBITS(buf,len) { \
int i,j=0; \
for (i=0; i<len; ++i) { \
for (j=7; j>=0; --j) { \
PRINTF("%c", (((char *)buf)[i] & 1<<j) ? '1' : '0'); \
} \
PRINTF(" "); \
} \
}
#else
#define PRINTF(...)
#define PRINT6ADDR(addr)
#define PRINTLLADDR(addr)
#define PRINTBITS(buf,len)
#endif
PROCESS(coap_receiver, "CoAP Receiver");
/*-----------------------------------------------------------------------------------*/
/*- Constants -----------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------*/
const char* error_messages[] = {
"",
/* Memory errors */
"Transaction buffer allocation failed",
"Memory boundary exceeded",
/* CoAP errors */
"Request has unknown critical option", /*FIXME which one? */
"Packet could not be serialized"
};
/*-----------------------------------------------------------------------------------*/
/*- Variables -----------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------*/
static service_callback_t service_cbk = NULL;
/*-----------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------*/
static
int
handle_incoming_data(void)
{
int error = NO_ERROR;
PRINTF("handle_incoming_data(): received uip_datalen=%u \n",(uint16_t)uip_datalen());
if (uip_newdata()) {
PRINTF("receiving UDP datagram from: ");
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
PRINTF(":%u\n Length: %u\n Data: ", uip_ntohs(UIP_UDP_BUF->srcport), uip_datalen() );
PRINTBITS(uip_appdata, uip_datalen());
PRINTF("\n");
coap_packet_t message[1];
coap_transaction_t *transaction = NULL;
error = coap_parse_message(message, uip_appdata, uip_datalen());
if (error==NO_ERROR)
{
/*TODO duplicates suppression, if required */
PRINTF(" Parsed: v %u, t %u, oc %u, c %u, tid %u\n", message->version, message->type, message->option_count, message->code, message->tid);
PRINTF(" URL: %.*s\n", message->uri_path_len, message->uri_path);
PRINTF(" Payload: %.*s\n", message->payload_len, message->payload);
/* Handle requests. */
if (message->code >= COAP_GET && message->code <= COAP_DELETE)
{
/* Use transaction buffer for response to confirmable request. */
if ( (transaction = coap_new_transaction(message->tid, &UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport)) )
{
uint32_t block_num = 0;
uint16_t block_size = REST_MAX_CHUNK_SIZE;
uint32_t block_offset = 0;
int32_t new_offset = 0;
/* prepare response */
coap_packet_t response[1]; /* This way the packet can be treated as pointer as usual. */
if (message->type==COAP_TYPE_CON)
{
/* Reliable CON requests are answered with an ACK. */
coap_init_message(response, COAP_TYPE_ACK, OK_200, message->tid);
}
else
{
/* Unreliable NON requests are answered with a NON as well. */
coap_init_message(response, COAP_TYPE_NON, OK_200, coap_get_tid());
}
/* resource handlers must take care of different handling (e.g., TOKEN_OPTION_REQUIRED_240) */
if (IS_OPTION(message, COAP_OPTION_TOKEN))
{
coap_set_header_token(response, message->token, message->token_len);
SET_OPTION(response, COAP_OPTION_TOKEN);
}
/* get offset for blockwise transfers */
if (coap_get_header_block(message, &block_num, NULL, &block_size, &block_offset))
{
PRINTF("Blockwise: block request %lu (%u/%u) @ %lu bytes\n", block_num, block_size, REST_MAX_CHUNK_SIZE, block_offset);
block_size = MIN(block_size, REST_MAX_CHUNK_SIZE);
new_offset = block_offset;
}
/*------------------------------------------*/
/* call application-specific handler */
/*------------------------------------------*/
if (service_cbk) {
service_cbk(message, response, transaction->packet+COAP_MAX_HEADER_SIZE, block_size, &new_offset);
}
/*------------------------------------------*/
/* apply blockwise transfers */
if ( IS_OPTION(message, COAP_OPTION_BLOCK) )
{
/* unchanged new_offset indicates that resource is unaware of blockwise transfer */
if (new_offset==block_offset)
{
PRINTF("Blockwise: unaware resource with payload length %u/%u\n", response->payload_len, block_size);
if (block_offset >= response->payload_len)
{
response->code = BAD_REQUEST_400;
coap_set_payload(response, (uint8_t*)"Block out of scope", 18);
}
else
{
coap_set_header_block(response, block_num, response->payload_len - block_offset > block_size, block_size);
coap_set_payload(response, response->payload+block_offset, MIN(response->payload_len - block_offset, block_size));
} /* if (valid offset) */
}
else
{
/* resource provides chunk-wise data */
PRINTF("Blockwise: blockwise resource, new offset %ld\n", new_offset);
coap_set_header_block(response, block_num, new_offset!=-1 || response->payload_len > block_size, block_size);
if (response->payload_len > block_size) coap_set_payload(response, response->payload, block_size);
} /* if (resource aware of blockwise) */
}
else if (new_offset!=0)
{
PRINTF("Blockwise: no block option for blockwise resource, using block size %u\n", REST_MAX_CHUNK_SIZE);
coap_set_header_block(response, 0, new_offset!=-1, REST_MAX_CHUNK_SIZE);
coap_set_payload(response, response->payload, MIN(response->payload_len, REST_MAX_CHUNK_SIZE));
} /* if (blockwise request) */
if ((transaction->packet_len = coap_serialize_message(response, transaction->packet))==0)
{
error = PACKET_SERIALIZATION_ERROR;
}
} else {
error = MEMORY_ALLOCATION_ERROR;
}
}
else
{
/* Responses */
coap_transaction_t *t;
if (message->type==COAP_TYPE_ACK)
{
PRINTF("Received ACK\n");
}
else if (message->type==COAP_TYPE_RST)
{
PRINTF("Received RST\n");
/* Cancel possible subscriptions. */
if (IS_OPTION(message, COAP_OPTION_TOKEN))
{
PRINTF(" Token 0x%02X%02X\n", message->token[0], message->token[1]);
coap_remove_observer_by_token(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, message->token, message->token_len);
}
}
if ( (t = coap_get_transaction_by_tid(message->tid)) )
{
/* Free transaction memory before callback, as it may create a new transaction. */
restful_response_handler callback = t->callback;
void *callback_data = t->callback_data;
coap_clear_transaction(t);
/* Check if someone registered for the response */
if (callback) {
callback(callback_data, message);
}
} /* if (transaction) */
}
} /* if (parsed correctly) */
if (error==NO_ERROR) {
if (transaction) coap_send_transaction(transaction);
}
else
{
PRINTF("ERROR %u: %s\n", error, error_messages[error]);
/* reuse input buffer */
coap_init_message(message, COAP_TYPE_ACK, INTERNAL_SERVER_ERROR_500, message->tid);
coap_set_payload(message, (uint8_t *) error_messages[error], strlen(error_messages[error]));
coap_send_message(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, uip_appdata, coap_serialize_message(message, uip_appdata));
}
} /* if (new data) */
return error;
}
/*-----------------------------------------------------------------------------------*/
void
coap_receiver_init()
{
process_start(&coap_receiver, NULL);
}
/*-----------------------------------------------------------------------------------*/
void
coap_set_service_callback(service_callback_t callback)
{
service_cbk = callback;
}
/*-----------------------------------------------------------------------------------*/
rest_resource_flags_t
coap_get_rest_method(void *packet)
{
return (rest_resource_flags_t)(1 << (((coap_packet_t *)packet)->code - 1));
}
/*-----------------------------------------------------------------------------------*/
int
coap_set_rest_status(void *packet, unsigned int code)
{
if (code <= 0xFF)
{
((coap_packet_t *)packet)->code = (uint8_t) code;
return 1;
}
else
{
return 0;
}
}
/*-----------------------------------------------------------------------------------*/
/*- Server part ---------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------*/
/* The discover resource should be included when using CoAP. */
RESOURCE(well_known_core, METHOD_GET, ".well-known/core", "");
void
well_known_core_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset)
{
/* Response might be NULL for non-confirmable requests. */
if (response)
{
size_t strpos = 0;
size_t bufpos = 0;
resource_t* resource = NULL;
for (resource = (resource_t*)list_head(rest_get_resources()); resource; resource = resource->next)
{
strpos += snprintf((char *) buffer + bufpos, REST_MAX_CHUNK_SIZE - bufpos + 1,
"</%s>%s%s%s",
resource->url,
resource->attributes[0] ? ";" : "",
resource->attributes,
resource->next ? "," : "" );
PRINTF("discover: %s\n", resource->url);
if (strpos <= *offset)
{
/* Discard output before current block */
PRINTF(" if %d <= %ld B\n", strpos, *offset);
PRINTF(" %s\n", buffer);
bufpos = 0;
}
else /* (strpos > *offset) */
{
/* output partly in block */
size_t len = MIN(strpos - *offset, preferred_size);
PRINTF(" el %d/%d @ %ld B\n", len, preferred_size, *offset);
/* Block might start in the middle of the output; align with buffer start. */
if (bufpos == 0)
{
memmove(buffer, buffer+strlen((char *)buffer)-strpos+*offset, len);
}
bufpos = len;
PRINTF(" %s\n", buffer);
if (bufpos >= preferred_size)
{
break;
}
}
}
if (bufpos>0) {
coap_set_payload(response, buffer, bufpos );
coap_set_header_content_type(response, APPLICATION_LINK_FORMAT);
}
else
{
coap_set_rest_status(response, BAD_REQUEST_400);
coap_set_payload(response, (uint8_t*)"Block out of scope", 18);
}
if (resource==NULL) {
*offset = -1;
}
else
{
*offset += bufpos;
}
}
}
/*-----------------------------------------------------------------------------------*/
PROCESS_THREAD(coap_receiver, ev, data)
{
PROCESS_BEGIN();
PRINTF("Starting CoAP-03 receiver...\n");
rest_activate_resource(&resource_well_known_core);
coap_register_as_transaction_handler();
coap_init_connection(SERVER_LISTEN_PORT);
while(1) {
PROCESS_YIELD();
if(ev == tcpip_event) {
handle_incoming_data();
} else if (ev == PROCESS_EVENT_TIMER) {
/* retransmissions are handled here */
coap_check_transactions();
}
} /* while (1) */
PROCESS_END();
}
/*-----------------------------------------------------------------------------------*/
/*- Client part ---------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------*/
void blocking_request_callback(void *callback_data, void *response) {
struct request_state_t *state = (struct request_state_t *) callback_data;
state->response = (coap_packet_t*) response;
process_poll(state->process);
}
/*-----------------------------------------------------------------------------------*/
PT_THREAD(coap_blocking_request(struct request_state_t *state, process_event_t ev,
uip_ipaddr_t *remote_ipaddr, uint16_t remote_port,
coap_packet_t *request,
blocking_response_handler request_callback)) {
PT_BEGIN(&state->pt);
static uint8_t more;
static uint32_t res_block;
static uint8_t block_error;
state->block_num = 0;
state->response = NULL;
state->process = PROCESS_CURRENT();
more = 0;
res_block = 0;
block_error = 0;
do {
request->tid = coap_get_tid();
if ((state->transaction = coap_new_transaction(request->tid, remote_ipaddr, remote_port)))
{
state->transaction->callback = blocking_request_callback;
state->transaction->callback_data = state;
if (state->block_num>0)
{
coap_set_header_block(request, state->block_num, 0, REST_MAX_CHUNK_SIZE);
}
state->transaction->packet_len = coap_serialize_message(request, state->transaction->packet);
coap_send_transaction(state->transaction);
PRINTF("Requested #%lu (TID %u)\n", state->block_num, request->tid);
PT_YIELD_UNTIL(&state->pt, ev == PROCESS_EVENT_POLL);
if (!state->response)
{
PRINTF("Server not responding\n");
PT_EXIT(&state->pt);
}
coap_get_header_block(state->response, &res_block, &more, NULL, NULL);
PRINTF("Received #%lu%s (%u bytes)\n", res_block, more ? "+" : "", state->response->payload_len);
if (res_block==state->block_num)
{
request_callback(state->response);
++(state->block_num);
}
else
{
PRINTF("WRONG BLOCK %lu/%lu\n", res_block, state->block_num);
++block_error;
}
}
else
{
PRINTF("Could not allocate transaction buffer");
PT_EXIT(&state->pt);
}
} while (more && block_error<COAP_MAX_ATTEMPTS);
PT_END(&state->pt);
}
/*-----------------------------------------------------------------------------------*/
/*- Engine Interface ----------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------*/
const struct rest_implementation coap_rest_implementation = {
"CoAP-03",
coap_receiver_init,
coap_set_service_callback,
coap_get_header_uri_path,
coap_set_header_uri_path,
coap_get_rest_method,
coap_set_rest_status,
coap_get_header_content_type,
coap_set_header_content_type,
NULL,
NULL,
NULL,
coap_get_header_max_age,
coap_set_header_max_age,
coap_set_header_etag,
NULL,
NULL,
coap_get_header_uri_host,
coap_set_header_location,
coap_get_payload,
coap_set_payload,
coap_get_header_uri_query,
coap_get_query_variable,
coap_get_post_variable,
coap_notify_observers,
(restful_post_handler) coap_observe_handler,
NULL,
NULL,
{
OK_200,
CREATED_201,
OK_200,
OK_200,
NOT_MODIFIED_304,
BAD_REQUEST_400,
METHOD_NOT_ALLOWED_405,
BAD_REQUEST_400,
METHOD_NOT_ALLOWED_405,
NOT_FOUND_404,
METHOD_NOT_ALLOWED_405,
UNSUPPORTED_MEDIA_TYPE_415,
BAD_REQUEST_400,
UNSUPPORTED_MEDIA_TYPE_415,
INTERNAL_SERVER_ERROR_500,
CRITICAL_OPTION_NOT_SUPPORTED,
BAD_GATEWAY_502,
SERVICE_UNAVAILABLE_503,
GATEWAY_TIMEOUT_504,
INTERNAL_SERVER_ERROR_500
},
{
TEXT_PLAIN,
TEXT_XML,
TEXT_CSV,
TEXT_HTML,
IMAGE_GIF,
IMAGE_JPEG,
IMAGE_PNG,
IMAGE_TIFF,
AUDIO_RAW,
VIDEO_RAW,
APPLICATION_LINK_FORMAT,
APPLICATION_XML,
APPLICATION_OCTET_STREAM,
APPLICATION_RDF_XML,
APPLICATION_SOAP_XML,
APPLICATION_ATOM_XML,
APPLICATION_XMPP_XML,
APPLICATION_EXI,
APPLICATION_FASTINFOSET,
APPLICATION_SOAP_FASTINFOSET,
APPLICATION_JSON,
APPLICATION_OCTET_STREAM
}
};

View file

@ -1,207 +0,0 @@
/*
* Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* CoAP module for observing resources
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#include <stdio.h>
#include <string.h>
#include "er-coap-03-observing.h"
#define DEBUG 0
#if DEBUG
#define PRINTF(...) printf(__VA_ARGS__)
#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5])
#else
#define PRINTF(...)
#define PRINT6ADDR(addr)
#define PRINTLLADDR(addr)
#endif
MEMB(observers_memb, coap_observer_t, COAP_MAX_OBSERVERS);
LIST(observers_list);
/*-----------------------------------------------------------------------------------*/
coap_observer_t *
coap_add_observer(uip_ipaddr_t *addr, uint16_t port, const uint8_t *token, size_t token_len, const char *url)
{
coap_observer_t *o = memb_alloc(&observers_memb);
if (o)
{
o->url = url;
uip_ipaddr_copy(&o->addr, addr);
o->port = port;
o->token_len = token_len;
memcpy(o->token, token, token_len);
stimer_set(&o->refresh_timer, COAP_OBSERVING_REFRESH_INTERVAL);
PRINTF("Adding observer for /%s [0x%02X%02X]\n", o->url, o->token[0], o->token[1]);
list_add(observers_list, o);
}
return o;
}
/*-----------------------------------------------------------------------------------*/
void
coap_remove_observer(coap_observer_t *o)
{
PRINTF("Removing observer for /%s [0x%02X%02X]\n", o->url, o->token[0], o->token[1]);
memb_free(&observers_memb, o);
list_remove(observers_list, o);
}
int
coap_remove_observer_by_client(uip_ipaddr_t *addr, uint16_t port)
{
int removed = 0;
coap_observer_t* obs = NULL;
for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next)
{
PRINTF("Remove check Port %u\n", port);
if (uip_ipaddr_cmp(&obs->addr, addr) && obs->port==port)
{
coap_remove_observer(obs);
removed++;
}
}
return removed;
}
int
coap_remove_observer_by_token(uip_ipaddr_t *addr, uint16_t port, uint8_t *token, size_t token_len)
{
int removed = 0;
coap_observer_t* obs = NULL;
for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next)
{
PRINTF("Remove check Token 0x%02X%02X\n", token[0], token[1]);
if (uip_ipaddr_cmp(&obs->addr, addr) && obs->port==port && memcmp(obs->token, token, token_len)==0)
{
coap_remove_observer(obs);
removed++;
}
}
return removed;
}
/*-----------------------------------------------------------------------------------*/
void
coap_notify_observers(resource_t *resource, int32_t obs_counter, void *notification)
{
coap_packet_t *const coap_res = (coap_packet_t *) notification;
coap_observer_t* obs = NULL;
uint8_t preferred_type = coap_res->type;
PRINTF("Observing: Notification from %s\n", resource->url);
/* Iterate over observers. */
for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next)
{
if (obs->url==resource->url) /* using RESOURCE url pointer as handle */
{
coap_transaction_t *transaction = NULL;
/*TODO implement special transaction for CON, sharing the same buffer to allow for more observers. */
if ( (transaction = coap_new_transaction(coap_get_tid(), &obs->addr, obs->port)) )
{
PRINTF(" Observer ");
PRINT6ADDR(&obs->addr);
PRINTF(":%u\n", obs->port);
/* Prepare response */
coap_res->tid = transaction->tid;
coap_set_header_observe(coap_res, obs_counter);
coap_set_header_token(coap_res, obs->token, obs->token_len);
/* Use CON to check whether client is still there/interested after COAP_OBSERVING_REFRESH_INTERVAL. */
if (stimer_expired(&obs->refresh_timer))
{
PRINTF(" Refreshing with CON\n");
coap_res->type = COAP_TYPE_CON;
stimer_restart(&obs->refresh_timer);
}
else
{
coap_res->type = preferred_type;
}
transaction->packet_len = coap_serialize_message(coap_res, transaction->packet);
coap_send_transaction(transaction);
}
}
}
}
/*-----------------------------------------------------------------------------------*/
void
coap_observe_handler(resource_t *resource, void *request, void *response)
{
static char content[26];
if (response && ((coap_packet_t *)response)->code<128) /* response without error code */
{
if (IS_OPTION((coap_packet_t *)request, COAP_OPTION_OBSERVE))
{
if (IS_OPTION((coap_packet_t *)request, COAP_OPTION_TOKEN))
{
if (coap_add_observer(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, ((coap_packet_t *)request)->token, ((coap_packet_t *)request)->token_len, resource->url))
{
coap_set_header_observe(response, 0);
coap_set_payload(response, (uint8_t *)content, snprintf(content, sizeof(content), "Added as observer %u/%u", list_length(observers_list), COAP_MAX_OBSERVERS));
}
else
{
((coap_packet_t *)response)->code = SERVICE_UNAVAILABLE_503;
coap_set_payload(response, (uint8_t *)"Too many observers", 18);
} /* if (added observer) */
}
else /* if (token) */
{
((coap_packet_t *)response)->code = TOKEN_OPTION_REQUIRED;
coap_set_payload(response, (uint8_t *)"Observing requires token", 24);
} /* if (token) */
}
else /* if (observe) */
{
/* Remove client if it is currently observing. */
coap_remove_observer_by_client(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport);
} /* if (observe) */
}
}

View file

@ -1,77 +0,0 @@
/*
* Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* CoAP module for observing resources
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#ifndef COAP_OBSERVING_H_
#define COAP_OBSERVING_H_
#include "sys/stimer.h"
#include "er-coap-03.h"
#include "er-coap-03-transactions.h"
#ifndef COAP_MAX_OBSERVERS
#define COAP_MAX_OBSERVERS 4
#endif /* COAP_MAX_OBSERVERS */
/* Interval in seconds in which NON notifies are changed to CON notifies to check client. */
#define COAP_OBSERVING_REFRESH_INTERVAL 60
#if COAP_MAX_OPEN_TRANSACTIONS<COAP_MAX_OBSERVERS
#warning "COAP_MAX_OPEN_TRANSACTIONS smaller than COAP_MAX_OBSERVERS: cannot handle CON notifications"
#endif
typedef struct coap_observer {
struct coap_observer *next; /* for LIST */
const char *url;
uip_ipaddr_t addr;
uint16_t port;
uint8_t token_len;
uint8_t token[COAP_TOKEN_LEN];
struct stimer refresh_timer;
} coap_observer_t;
list_t coap_get_observers(void);
coap_observer_t *coap_add_observer(uip_ipaddr_t *addr, uint16_t port, const uint8_t *token, size_t token_len, const char *url);
void coap_remove_observer(coap_observer_t *o);
int coap_remove_observer_by_client(uip_ipaddr_t *addr, uint16_t port);
int coap_remove_observer_by_token(uip_ipaddr_t *addr, uint16_t port, uint8_t *token, size_t token_len);
void coap_notify_observers(resource_t *resource, int32_t obs_counter, void *notification);
void coap_observe_handler(resource_t *resource, void *request, void *response);
#endif /* COAP_OBSERVING_H_ */

View file

@ -1,177 +0,0 @@
/*
* Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* CoAP module for reliable transport
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#include "contiki.h"
#include "contiki-net.h"
#include "er-coap-03-transactions.h"
#include "er-coap-03-observing.h"
#define DEBUG 0
#if DEBUG
#include <stdio.h>
#define PRINTF(...) printf(__VA_ARGS__)
#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5])
#else
#define PRINTF(...)
#define PRINT6ADDR(addr)
#define PRINTLLADDR(addr)
#endif
MEMB(transactions_memb, coap_transaction_t, COAP_MAX_OPEN_TRANSACTIONS);
LIST(transactions_list);
static struct process *transaction_handler_process = NULL;
void
coap_register_as_transaction_handler()
{
transaction_handler_process = PROCESS_CURRENT();
}
coap_transaction_t *
coap_new_transaction(uint16_t tid, uip_ipaddr_t *addr, uint16_t port)
{
coap_transaction_t *t = memb_alloc(&transactions_memb);
if (t)
{
t->tid = tid;
t->retrans_counter = 0;
/* save client address */
uip_ipaddr_copy(&t->addr, addr);
t->port = port;
}
return t;
}
void
coap_send_transaction(coap_transaction_t *t)
{
PRINTF("Sending transaction %u\n", t->tid);
coap_send_message(&t->addr, t->port, t->packet, t->packet_len);
if (COAP_TYPE_CON==((COAP_HEADER_TYPE_MASK & t->packet[0])>>COAP_HEADER_TYPE_POSITION))
{
if (t->retrans_counter<COAP_MAX_RETRANSMIT)
{
PRINTF("Keeping transaction %u\n", t->tid);
/*FIXME hack, maybe there is a better way, but avoid posting everything to the process */
struct process *process_actual = PROCESS_CURRENT();
process_current = transaction_handler_process;
etimer_set(&t->retrans_timer, CLOCK_SECOND * COAP_RESPONSE_TIMEOUT * (1<<(t->retrans_counter)));
process_current = process_actual;
list_add(transactions_list, t); /* list itself makes sure same element is not added twice */
t = NULL;
}
else
{
/* timeout */
PRINTF("Timeout\n");
restful_response_handler callback = t->callback;
void *callback_data = t->callback_data;
/* handle observers */
coap_remove_observer_by_client(&t->addr, t->port);
coap_clear_transaction(t);
if (callback) {
callback(callback_data, NULL);
}
}
}
else
{
coap_clear_transaction(t);
}
}
void
coap_clear_transaction(coap_transaction_t *t)
{
if (t)
{
PRINTF("Freeing transaction %u: %p\n", t->tid, t);
etimer_stop(&t->retrans_timer);
list_remove(transactions_list, t);
memb_free(&transactions_memb, t);
}
}
coap_transaction_t *
coap_get_transaction_by_tid(uint16_t tid)
{
coap_transaction_t *t = NULL;
for (t = (coap_transaction_t*)list_head(transactions_list); t; t = t->next)
{
if (t->tid==tid)
{
PRINTF("Found transaction for TID %u: %p\n", t->tid, t);
return t;
}
}
return NULL;
}
void
coap_check_transactions()
{
coap_transaction_t *t = NULL;
for (t = (coap_transaction_t*)list_head(transactions_list); t; t = t->next)
{
if (etimer_expired(&t->retrans_timer))
{
++(t->retrans_counter);
PRINTF("Retransmitting %u (%u)\n", t->tid, t->retrans_counter);
coap_send_transaction(t);
}
}
}

View file

@ -1,769 +0,0 @@
/*
* Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* An implementation of the Constrained Application Protocol (draft 03)
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#ifdef CONTIKI_TARGET_NETSIM
#include <stdio.h>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#else
#include "contiki.h"
#include "contiki-net.h"
#include <string.h>
#include <stdio.h>
#endif
#include "er-coap-03.h"
#include "er-coap-03-transactions.h"
#define DEBUG 0
#if DEBUG
#include <stdio.h>
#define PRINTF(...) printf(__VA_ARGS__)
#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5])
#else
#define PRINTF(...)
#define PRINT6ADDR(addr)
#define PRINTLLADDR(addr)
#endif
static struct uip_udp_conn *udp_conn = NULL;
static uint16_t current_tid = 0;
/*-----------------------------------------------------------------------------------*/
/*- LOCAL HELP FUNCTIONS ------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------*/
static
uint16_t
log_2(uint16_t value)
{
uint16_t result = 0;
do {
value = value >> 1;
result++;
} while (value);
return result ? result - 1 : result;
}
/*-----------------------------------------------------------------------------------*/
static
uint32_t
bytes_2_uint32(uint8_t *bytes, uint16_t length)
{
uint32_t var = 0;
int i = 0;
while (i<length)
{
var <<= 8;
var |= 0xFF & bytes[i++];
}
return var;
}
/*-----------------------------------------------------------------------------------*/
/* Unused in coap-03.
static
int
uint16_2_bytes(uint8_t *bytes, uint16_t var)
{
int i = 0;
if (0xFF00 & var) bytes[i++] = var>>8;
bytes[i++] = 0xFF & var;
return i;
}
*/
/*-----------------------------------------------------------------------------------*/
static
int
uint32_2_bytes(uint8_t *bytes, uint32_t var)
{
int i = 0;
if (0xFF000000 & var) bytes[i++] = (0xFF & var>>24);
if (0xFFFF0000 & var) bytes[i++] = (0xFF & var>>16);
if (0xFFFFFF00 & var) bytes[i++] = (0xFF & var>>8);
bytes[i++] = 0xFF & var;
return i;
}
/*-----------------------------------------------------------------------------------*/
static
int
coap_get_variable(const char *buffer, size_t length, const char *name, const char **output)
{
const char *start = NULL;
const char *end = NULL;
const char *value_end = NULL;
size_t name_len = 0;
/*initialize the output buffer first*/
*output = 0;
name_len = strlen(name);
end = buffer + length;
for (start = buffer; start + name_len < end; ++start){
if ((start == buffer || start[-1] == '&') && start[name_len] == '=' &&
strncmp(name, start, name_len)==0) {
/* Point start to variable value */
start += name_len + 1;
/* Point end to the end of the value */
value_end = (const char *) memchr(start, '&', end - start);
if (value_end == NULL) {
value_end = end;
}
*output = start;
return (value_end - start);
}
}
return 0;
}
/*-----------------------------------------------------------------------------------*/
/*- MEASSAGE SENDING ----------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------*/
void
coap_init_connection(uint16_t port)
{
/* new connection with remote host */
udp_conn = udp_new(NULL, 0, NULL);
udp_bind(udp_conn, port);
PRINTF("Listening on port %u\n", uip_ntohs(udp_conn->lport));
/* Initialize transaction ID. */
current_tid = random_rand();
}
/*-----------------------------------------------------------------------------------*/
uint16_t
coap_get_tid()
{
++current_tid;
PRINTF("Get TID %u\n", current_tid);
return current_tid;
}
/*-----------------------------------------------------------------------------------*/
void
coap_send_message(uip_ipaddr_t *addr, uint16_t port, const uint8_t *data, uint16_t length)
{
/*configure connection to reply to client*/
uip_ipaddr_copy(&udp_conn->ripaddr, addr);
udp_conn->rport = port;
uip_udp_packet_send(udp_conn, data, length);
PRINTF("-sent UDP datagram------\n Length: %u\n -----------------------\n", length);
/* Restore server connection to allow data from any node */
memset(&udp_conn->ripaddr, 0, sizeof(udp_conn->ripaddr));
udp_conn->rport = 0;
}
/*-----------------------------------------------------------------------------------*/
/*- MEASSAGE PROCESSING -------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------*/
void
coap_init_message(void *packet, coap_message_type_t type, uint8_t code, uint16_t tid)
{
memset(packet, 0, sizeof(coap_packet_t));
((coap_packet_t *)packet)->type = type;
((coap_packet_t *)packet)->code = code;
((coap_packet_t *)packet)->tid = tid;
}
/*-----------------------------------------------------------------------------------*/
int
coap_serialize_message(void *packet, uint8_t *buffer)
{
((coap_packet_t *)packet)->buffer = buffer;
((coap_packet_t *)packet)->version = 1;
((coap_packet_t *)packet)->option_count = 0;
/* serialize options */
uint8_t *option = ((coap_packet_t *)packet)->buffer + COAP_HEADER_LEN;
size_t option_len = 0;
int index = 0;
if (IS_OPTION((coap_packet_t *)packet, COAP_OPTION_CONTENT_TYPE)) {
((coap_header_option_t *)option)->s.delta = 1;
((coap_header_option_t *)option)->s.length = 1;
*(++option) = ((coap_packet_t *)packet)->content_type;
PRINTF("OPTION %u (type %u, len 1, delta %u): ", ((coap_packet_t *)packet)->option_count, COAP_OPTION_CONTENT_TYPE, COAP_OPTION_CONTENT_TYPE - index);
PRINTF("Content-Type [%u]\n", ((coap_packet_t *)packet)->content_type);
index = COAP_OPTION_CONTENT_TYPE;
option += 1;
++(((coap_packet_t *)packet)->option_count);
}
if (IS_OPTION((coap_packet_t *)packet, COAP_OPTION_MAX_AGE)) {
option_len = uint32_2_bytes(option+1, ((coap_packet_t *)packet)->max_age);
((coap_header_option_t *)option)->s.delta = COAP_OPTION_MAX_AGE - index;
((coap_header_option_t *)option)->s.length = option_len;
PRINTF("OPTION %u (type %u, len %u, delta %u): ", ((coap_packet_t *)packet)->option_count, COAP_OPTION_MAX_AGE, option_len, COAP_OPTION_MAX_AGE - index);
PRINTF("Max-Age [%lu]\n", ((coap_packet_t *)packet)->max_age);
index = COAP_OPTION_MAX_AGE;
option += 1 + option_len;
++(((coap_packet_t *)packet)->option_count);
}
if (IS_OPTION((coap_packet_t *)packet, COAP_OPTION_ETAG)) {
((coap_header_option_t *)option)->s.delta = COAP_OPTION_ETAG - index;
((coap_header_option_t *)option)->s.length = ((coap_packet_t *)packet)->etag_len;
memcpy(++option, ((coap_packet_t *)packet)->etag, ((coap_packet_t *)packet)->etag_len);
PRINTF("OPTION %u (type %u, len %u, delta %u): ", ((coap_packet_t *)packet)->option_count, COAP_OPTION_ETAG, ((coap_packet_t *)packet)->etag_len, COAP_OPTION_ETAG - index);
PRINTF("ETag %u [0x%02X", ((coap_packet_t *)packet)->etag_len, ((coap_packet_t *)packet)->etag[0]); /*FIXME always prints 4 bytes */
PRINTF("%02X", ((coap_packet_t *)packet)->etag[1]);
PRINTF("%02X", ((coap_packet_t *)packet)->etag[2]);
PRINTF("%02X", ((coap_packet_t *)packet)->etag[3]);
PRINTF("]\n");
index = COAP_OPTION_ETAG;
option += ((coap_packet_t *)packet)->etag_len;
++(((coap_packet_t *)packet)->option_count);
}
if (IS_OPTION((coap_packet_t *)packet, COAP_OPTION_URI_HOST)) {
if (((coap_packet_t *)packet)->uri_host_len<15) {
((coap_header_option_t *)option)->s.delta = COAP_OPTION_URI_HOST - index;
((coap_header_option_t *)option)->s.length = ((coap_packet_t *)packet)->uri_host_len;
option += 1;
} else {
((coap_header_option_t *)option)->l.delta = COAP_OPTION_URI_HOST - index;
((coap_header_option_t *)option)->s.length = 15;
((coap_header_option_t *)option)->l.length = ((coap_packet_t *)packet)->uri_host_len - 15;
option += 2;
}
memcpy(option, ((coap_packet_t *)packet)->uri_host, ((coap_packet_t *)packet)->uri_host_len);
PRINTF("OPTION %u (type %u, len %u, delta %u): ", ((coap_packet_t *)packet)->option_count, COAP_OPTION_URI_HOST, ((coap_packet_t *)packet)->uri_host_len, COAP_OPTION_URI_HOST - index);
PRINTF("Uri-Auth [%.*s]\n", ((coap_packet_t *)packet)->uri_host_len, ((coap_packet_t *)packet)->uri_host);
index = COAP_OPTION_URI_HOST;
option += ((coap_packet_t *)packet)->uri_host_len;
++(((coap_packet_t *)packet)->option_count);
}
if (IS_OPTION((coap_packet_t *)packet, COAP_OPTION_LOCATION_PATH)) {
if (((coap_packet_t *)packet)->location_path_len<15) {
((coap_header_option_t *)option)->s.delta = COAP_OPTION_LOCATION_PATH - index;
((coap_header_option_t *)option)->s.length = ((coap_packet_t *)packet)->location_path_len;
option += 1;
} else {
((coap_header_option_t *)option)->l.delta = COAP_OPTION_LOCATION_PATH - index;
((coap_header_option_t *)option)->s.length = 15;
((coap_header_option_t *)option)->l.length = ((coap_packet_t *)packet)->location_path_len - 15;
option += 2;
}
memcpy(option, ((coap_packet_t *)packet)->location_path, ((coap_packet_t *)packet)->location_path_len);
PRINTF("OPTION %u (type %u, len %u, delta %u): ", ((coap_packet_t *)packet)->option_count, COAP_OPTION_LOCATION_PATH, ((coap_packet_t *)packet)->location_path_len, COAP_OPTION_LOCATION_PATH - index);
PRINTF("Location [%.*s]\n", ((coap_packet_t *)packet)->location_path_len, ((coap_packet_t *)packet)->location_path);
index = COAP_OPTION_LOCATION_PATH;
option += ((coap_packet_t *)packet)->location_path_len;
++(((coap_packet_t *)packet)->option_count);
}
if (IS_OPTION((coap_packet_t *)packet, COAP_OPTION_URI_PATH)) {
if (((coap_packet_t *)packet)->uri_path_len<15) {
((coap_header_option_t *)option)->s.delta = COAP_OPTION_URI_PATH - index;
((coap_header_option_t *)option)->s.length = ((coap_packet_t *)packet)->uri_path_len;
option += 1;
} else {
((coap_header_option_t *)option)->l.delta = COAP_OPTION_URI_PATH - index;
((coap_header_option_t *)option)->s.length = 15;
((coap_header_option_t *)option)->l.length = ((coap_packet_t *)packet)->uri_path_len - 15;
option += 2;
}
memcpy(option, ((coap_packet_t *)packet)->uri_path, ((coap_packet_t *)packet)->uri_path_len);
PRINTF("OPTION %u (type %u, len %u, delta %u): ", ((coap_packet_t *)packet)->option_count, COAP_OPTION_URI_PATH, ((coap_packet_t *)packet)->uri_path_len, COAP_OPTION_URI_PATH - index);
PRINTF("Uri-Path [%.*s]\n", ((coap_packet_t *)packet)->uri_path_len, ((coap_packet_t *)packet)->uri_path);
index = COAP_OPTION_URI_PATH;
option += ((coap_packet_t *)packet)->uri_path_len;
++(((coap_packet_t *)packet)->option_count);
}
if (IS_OPTION((coap_packet_t *)packet, COAP_OPTION_OBSERVE)) {
option_len = uint32_2_bytes(option+1, ((coap_packet_t *)packet)->observe);
((coap_header_option_t *)option)->s.delta = COAP_OPTION_OBSERVE - index;
((coap_header_option_t *)option)->s.length = option_len;
PRINTF("OPTION %u (type %u, len %u, delta %u): ", ((coap_packet_t *)packet)->option_count, COAP_OPTION_OBSERVE, option_len, COAP_OPTION_OBSERVE - index);
PRINTF("Observe [%lu]\n", ((coap_packet_t *)packet)->observe);
index = COAP_OPTION_OBSERVE;
option += 1 + option_len;
++(((coap_packet_t *)packet)->option_count);
}
if (IS_OPTION((coap_packet_t *)packet, COAP_OPTION_TOKEN)) {
((coap_header_option_t *)option)->s.delta = COAP_OPTION_TOKEN - index;
((coap_header_option_t *)option)->s.length = ((coap_packet_t *)packet)->token_len;
memcpy(++option, ((coap_packet_t *)packet)->token, ((coap_packet_t *)packet)->token_len);
PRINTF("OPTION %u (type %u, len %u, delta %u): ", ((coap_packet_t *)packet)->option_count, COAP_OPTION_TOKEN, ((coap_packet_t *)packet)->token_len, COAP_OPTION_TOKEN - index);
PRINTF("Token %u [0x%02X%02X]\n", ((coap_packet_t *)packet)->token_len, ((coap_packet_t *)packet)->token[0], ((coap_packet_t *)packet)->token[1]); /*FIXME always prints 2 bytes */
index = COAP_OPTION_TOKEN;
option += ((coap_packet_t *)packet)->token_len;
++(((coap_packet_t *)packet)->option_count);
}
if (IS_OPTION((coap_packet_t *)packet, COAP_OPTION_BLOCK)) {
uint32_t block = ((coap_packet_t *)packet)->block_num << 4;
if (((coap_packet_t *)packet)->block_more) block |= 0x8;
block |= 0xF & log_2(((coap_packet_t *)packet)->block_size/16);
option_len = uint32_2_bytes(option+1, block);
((coap_header_option_t *)option)->s.delta = COAP_OPTION_BLOCK - index;
((coap_header_option_t *)option)->s.length = option_len;
PRINTF("OPTION %u (type %u, len %u, delta %u): ", ((coap_packet_t *)packet)->option_count, COAP_OPTION_BLOCK, option_len, COAP_OPTION_BLOCK - index);
PRINTF("Block [%lu%s (%u B/blk)]\n", ((coap_packet_t *)packet)->block_num, ((coap_packet_t *)packet)->block_more ? "+" : "", ((coap_packet_t *)packet)->block_size);
index = COAP_OPTION_BLOCK;
option += 1 + option_len;
++(((coap_packet_t *)packet)->option_count);
}
if (IS_OPTION((coap_packet_t *)packet, COAP_OPTION_URI_QUERY)) {
if (((coap_packet_t *)packet)->uri_query_len<15) {
((coap_header_option_t *)option)->s.delta = COAP_OPTION_URI_QUERY - index;
((coap_header_option_t *)option)->s.length = ((coap_packet_t *)packet)->uri_query_len;
option += 1;
} else {
((coap_header_option_t *)option)->l.delta = COAP_OPTION_URI_QUERY - index;
((coap_header_option_t *)option)->s.length = 15;
((coap_header_option_t *)option)->l.length = ((coap_packet_t *)packet)->uri_query_len - 15;
option += 2;
}
memcpy(option, ((coap_packet_t *)packet)->uri_query, ((coap_packet_t *)packet)->uri_query_len);
PRINTF("OPTION %u (type %u, len %u, delta %u): ", ((coap_packet_t *)packet)->option_count, COAP_OPTION_URI_QUERY, ((coap_packet_t *)packet)->uri_query_len, COAP_OPTION_URI_QUERY - index);
PRINTF("Uri-Query [%.*s]\n", ((coap_packet_t *)packet)->uri_query_len, ((coap_packet_t *)packet)->uri_query);
index = COAP_OPTION_URI_QUERY;
option += ((coap_packet_t *)packet)->uri_query_len;
++(((coap_packet_t *)packet)->option_count);
}
/* pack payload */
if ((option - ((coap_packet_t *)packet)->buffer)<=COAP_MAX_HEADER_SIZE)
{
memmove(option, ((coap_packet_t *)packet)->payload, ((coap_packet_t *)packet)->payload_len);
}
else
{
/* An error occured. Caller must check for !=0. */
return 0;
}
/* set header fields */
((coap_packet_t *)packet)->buffer[0] = 0x00;
((coap_packet_t *)packet)->buffer[0] |= COAP_HEADER_VERSION_MASK & (((coap_packet_t *)packet)->version)<<COAP_HEADER_VERSION_POSITION;
((coap_packet_t *)packet)->buffer[0] |= COAP_HEADER_TYPE_MASK & (((coap_packet_t *)packet)->type)<<COAP_HEADER_TYPE_POSITION;
((coap_packet_t *)packet)->buffer[0] |= COAP_HEADER_OPTION_COUNT_MASK & (((coap_packet_t *)packet)->option_count)<<COAP_HEADER_OPTION_COUNT_POSITION;
((coap_packet_t *)packet)->buffer[1] = ((coap_packet_t *)packet)->code;
((coap_packet_t *)packet)->buffer[2] = 0xFF & (((coap_packet_t *)packet)->tid)>>8;
((coap_packet_t *)packet)->buffer[3] = 0xFF & ((coap_packet_t *)packet)->tid;
PRINTF("Serialized %u options, header len %u, payload len %u\n", ((coap_packet_t *)packet)->option_count, option - ((coap_packet_t *)packet)->buffer, ((coap_packet_t *)packet)->payload_len);
return (option - ((coap_packet_t *)packet)->buffer) + ((coap_packet_t *)packet)->payload_len; /* packet length */
}
/*-----------------------------------------------------------------------------------*/
error_t
coap_parse_message(void *packet, uint8_t *data, uint16_t data_len)
{
/* Initialize packet */
memset(packet, 0, sizeof(coap_packet_t));
/* pointer to packet bytes */
((coap_packet_t *)packet)->buffer = data;
/* parse header fields */
((coap_packet_t *)packet)->version = (COAP_HEADER_VERSION_MASK & ((coap_packet_t *)packet)->buffer[0])>>COAP_HEADER_VERSION_POSITION;
((coap_packet_t *)packet)->type = (COAP_HEADER_TYPE_MASK & ((coap_packet_t *)packet)->buffer[0])>>COAP_HEADER_TYPE_POSITION;
((coap_packet_t *)packet)->option_count = (COAP_HEADER_OPTION_COUNT_MASK & ((coap_packet_t *)packet)->buffer[0])>>COAP_HEADER_OPTION_COUNT_POSITION;
((coap_packet_t *)packet)->code = ((coap_packet_t *)packet)->buffer[1];
((coap_packet_t *)packet)->tid = ((coap_packet_t *)packet)->buffer[2]<<8 | ((coap_packet_t *)packet)->buffer[3];
/* parse options */
((coap_packet_t *)packet)->options = 0x0000;
coap_header_option_t *current_option = (coap_header_option_t *) (data + COAP_HEADER_LEN);
if (((coap_packet_t *)packet)->option_count) {
uint8_t option_index = 0;
uint8_t option_type = 0;
uint16_t option_len = 0;
uint8_t *option_data = NULL;
uint8_t *last_option = NULL;
for (option_index=0; option_index < ((coap_packet_t *)packet)->option_count; ++option_index) {
option_type += current_option->s.delta;
if (current_option->s.length<15) {
option_len = current_option->s.length;
option_data = ((uint8_t *) current_option) + 1;
} else {
option_len = current_option->l.length + 15;
option_data = ((uint8_t *) current_option) + 2;
}
PRINTF("OPTION %u (type %u, len %u, delta %u): ", option_index, option_type, option_len, current_option->s.delta);
SET_OPTION((coap_packet_t *)packet, option_type);
switch (option_type) {
case COAP_OPTION_CONTENT_TYPE:
((coap_packet_t *)packet)->content_type = option_data[0];
PRINTF("Content-Type [%u]\n", ((coap_packet_t *)packet)->content_type);
break;
case COAP_OPTION_MAX_AGE:
((coap_packet_t *)packet)->max_age = bytes_2_uint32(option_data, option_len);
PRINTF("Max-Age [%lu]\n", ((coap_packet_t *)packet)->max_age);
break;
case COAP_OPTION_ETAG:
((coap_packet_t *)packet)->etag_len = MIN(COAP_ETAG_LEN, option_len);
memcpy(((coap_packet_t *)packet)->etag, option_data, ((coap_packet_t *)packet)->etag_len);
PRINTF("ETag %u [0x%02X", ((coap_packet_t *)packet)->etag_len, ((coap_packet_t *)packet)->etag[0]); /*FIXME always prints 4 bytes */
PRINTF("%02X", ((coap_packet_t *)packet)->etag[1]);
PRINTF("%02X", ((coap_packet_t *)packet)->etag[2]);
PRINTF("%02X", ((coap_packet_t *)packet)->etag[3]);
PRINTF("]\n");
break;
case COAP_OPTION_URI_HOST:
((coap_packet_t *)packet)->uri_host = (char *) option_data;
((coap_packet_t *)packet)->uri_host_len = option_len;
PRINTF("Uri-Auth [%.*s]\n", ((coap_packet_t *)packet)->uri_host_len, ((coap_packet_t *)packet)->uri_host);
break;
case COAP_OPTION_LOCATION_PATH:
((coap_packet_t *)packet)->location_path = (char *) option_data;
((coap_packet_t *)packet)->location_path_len = option_len;
PRINTF("Location [%.*s]\n", ((coap_packet_t *)packet)->location_path_len, ((coap_packet_t *)packet)->location_path);
break;
case COAP_OPTION_URI_PATH:
((coap_packet_t *)packet)->uri_path = (char *) option_data;
((coap_packet_t *)packet)->uri_path_len = option_len;
PRINTF("Uri-Path [%.*s]\n", ((coap_packet_t *)packet)->uri_path_len, ((coap_packet_t *)packet)->uri_path);
break;
case COAP_OPTION_OBSERVE:
((coap_packet_t *)packet)->observe = bytes_2_uint32(option_data, option_len);
PRINTF("Observe [%lu]\n", ((coap_packet_t *)packet)->observe);
break;
case COAP_OPTION_TOKEN:
((coap_packet_t *)packet)->token_len = MIN(COAP_TOKEN_LEN, option_len);
memcpy(((coap_packet_t *)packet)->token, option_data, ((coap_packet_t *)packet)->token_len);
PRINTF("Token %u [0x%02X%02X]\n", ((coap_packet_t *)packet)->token_len, ((coap_packet_t *)packet)->token[0], ((coap_packet_t *)packet)->token[1]); /*FIXME always prints 2 bytes */
break;
case COAP_OPTION_BLOCK:
((coap_packet_t *)packet)->block_num = bytes_2_uint32(option_data, option_len);
((coap_packet_t *)packet)->block_more = (((coap_packet_t *)packet)->block_num & 0x08)>>3;
((coap_packet_t *)packet)->block_size = 16 << (((coap_packet_t *)packet)->block_num & 0x07);
((coap_packet_t *)packet)->block_offset = (((coap_packet_t *)packet)->block_num & ~0x0F)<<(((coap_packet_t *)packet)->block_num & 0x07);
((coap_packet_t *)packet)->block_num >>= 4;
PRINTF("Block [%lu%s (%u B/blk)]\n", ((coap_packet_t *)packet)->block_num, ((coap_packet_t *)packet)->block_more ? "+" : "", ((coap_packet_t *)packet)->block_size);
break;
case COAP_OPTION_NOOP:
PRINTF("Noop-Fencepost\n");
break;
case COAP_OPTION_URI_QUERY:
((coap_packet_t *)packet)->uri_query = (char *) option_data;
((coap_packet_t *)packet)->uri_query_len = option_len;
PRINTF("Uri-Query [%.*s]\n", ((coap_packet_t *)packet)->uri_query_len, ((coap_packet_t *)packet)->uri_query);
break;
default:
PRINTF("unknown (%u)\n", option_type);
if (option_type & 1)
{
return UNKNOWN_CRITICAL_OPTION;
}
}
/* terminate strings where possible */
if (last_option) {
last_option[0] = 0x00;
}
last_option = (uint8_t *) current_option;
current_option = (coap_header_option_t *) (option_data+option_len);
} /* for () */
} /* if (oc) */
((coap_packet_t *)packet)->payload = (uint8_t *) current_option;
((coap_packet_t *)packet)->payload_len = data_len - (((coap_packet_t *)packet)->payload - data);
return NO_ERROR;
}
/*-----------------------------------------------------------------------------------*/
/*- REST FRAMEWORK FUNCTIONS --------------------------------------------------------*/
/*-----------------------------------------------------------------------------------*/
int
coap_get_query_variable(void *packet, const char *name, const char **output)
{
if (IS_OPTION((coap_packet_t *)packet, COAP_OPTION_URI_QUERY)) {
return coap_get_variable(((coap_packet_t *)packet)->uri_query, ((coap_packet_t *)packet)->uri_query_len, name, output);
}
return 0;
}
int
coap_get_post_variable(void *packet, const char *name, const char **output)
{
if (((coap_packet_t *)packet)->payload_len) {
return coap_get_variable((const char *)((coap_packet_t *)packet)->payload, ((coap_packet_t *)packet)->payload_len, name, output);
}
return 0;
}
/*-----------------------------------------------------------------------------------*/
/*- HEADER OPTION GETTERS AND SETTERS -----------------------------------------------*/
/*-----------------------------------------------------------------------------------*/
unsigned int
coap_get_header_content_type(void *packet)
{
return ((coap_packet_t *)packet)->content_type;
}
int
coap_set_header_content_type(void *packet, unsigned int content_type)
{
((coap_packet_t *)packet)->content_type = (coap_content_type_t) content_type;
SET_OPTION((coap_packet_t *)packet, COAP_OPTION_CONTENT_TYPE);
return 1;
}
/*-----------------------------------------------------------------------------------*/
int
coap_get_header_max_age(void *packet, uint32_t *age)
{
if (!IS_OPTION((coap_packet_t *)packet, COAP_OPTION_MAX_AGE)) {
*age = COAP_DEFAULT_MAX_AGE;
} else {
*age = ((coap_packet_t *)packet)->max_age;
}
return 1;
}
int
coap_set_header_max_age(void *packet, uint32_t age)
{
((coap_packet_t *)packet)->max_age = age;
SET_OPTION((coap_packet_t *)packet, COAP_OPTION_MAX_AGE);
return 1;
}
/*-----------------------------------------------------------------------------------*/
int
coap_get_header_etag(void *packet, const uint8_t **etag)
{
if (!IS_OPTION((coap_packet_t *)packet, COAP_OPTION_ETAG)) return 0;
*etag = ((coap_packet_t *)packet)->etag;
return ((coap_packet_t *)packet)->etag_len;
}
int
coap_set_header_etag(void *packet, const uint8_t *etag, size_t etag_len)
{
((coap_packet_t *)packet)->etag_len = MIN(COAP_ETAG_LEN, etag_len);
memcpy(((coap_packet_t *)packet)->etag, etag, ((coap_packet_t *)packet)->etag_len);
SET_OPTION((coap_packet_t *)packet, COAP_OPTION_ETAG);
return ((coap_packet_t *)packet)->etag_len;
}
/*-----------------------------------------------------------------------------------*/
int
coap_get_header_uri_host(void *packet, const char **host)
{
if (!IS_OPTION((coap_packet_t *)packet, COAP_OPTION_URI_HOST)) return 0;
*host = ((coap_packet_t *)packet)->uri_host;
return ((coap_packet_t *)packet)->uri_host_len;
}
int
coap_set_header_uri_host(void *packet, const char *host)
{
((coap_packet_t *)packet)->uri_host = host;
((coap_packet_t *)packet)->uri_host_len = strlen(host);
SET_OPTION((coap_packet_t *)packet, COAP_OPTION_URI_HOST);
return ((coap_packet_t *)packet)->uri_host_len;
}
/*-----------------------------------------------------------------------------------*/
int
coap_get_header_location(void *packet, const char **location)
{
if (!IS_OPTION((coap_packet_t *)packet, COAP_OPTION_LOCATION_PATH)) return 0;
*location = ((coap_packet_t *)packet)->location_path;
return ((coap_packet_t *)packet)->location_path_len;
}
int
coap_set_header_location(void *packet, const char *location)
{
while (location[0]=='/') ++location;
((coap_packet_t *)packet)->location_path = location;
((coap_packet_t *)packet)->location_path_len = strlen(location);
SET_OPTION((coap_packet_t *)packet, COAP_OPTION_LOCATION_PATH);
return ((coap_packet_t *)packet)->location_path_len;
}
/*-----------------------------------------------------------------------------------*/
int
coap_get_header_uri_path(void *packet, const char **path)
{
if (!IS_OPTION((coap_packet_t *)packet, COAP_OPTION_URI_PATH)) return 0;
*path = ((coap_packet_t *)packet)->uri_path;
return ((coap_packet_t *)packet)->uri_path_len;
}
int
coap_set_header_uri_path(void *packet, const char *path)
{
while (path[0]=='/') ++path;
((coap_packet_t *)packet)->uri_path = path;
((coap_packet_t *)packet)->uri_path_len = strlen(path);
SET_OPTION((coap_packet_t *)packet, COAP_OPTION_URI_PATH);
return ((coap_packet_t *)packet)->uri_path_len;
}
/*-----------------------------------------------------------------------------------*/
int
coap_get_header_observe(void *packet, uint32_t *observe)
{
if (!IS_OPTION((coap_packet_t *)packet, COAP_OPTION_OBSERVE)) return 0;
*observe = ((coap_packet_t *)packet)->observe;
return 1;
}
int
coap_set_header_observe(void *packet, uint32_t observe)
{
((coap_packet_t *)packet)->observe = observe;
SET_OPTION((coap_packet_t *)packet, COAP_OPTION_OBSERVE);
return 1;
}
/*-----------------------------------------------------------------------------------*/
int
coap_get_header_token(void *packet, const uint8_t **token)
{
if (!IS_OPTION((coap_packet_t *)packet, COAP_OPTION_TOKEN)) return 0;
*token = ((coap_packet_t *)packet)->token;
return ((coap_packet_t *)packet)->token_len;
}
int
coap_set_header_token(void *packet, const uint8_t *token, size_t token_len)
{
((coap_packet_t *)packet)->token_len = MIN(COAP_TOKEN_LEN, token_len);
memcpy(((coap_packet_t *)packet)->token, token, ((coap_packet_t *)packet)->token_len);
SET_OPTION((coap_packet_t *)packet, COAP_OPTION_TOKEN);
return ((coap_packet_t *)packet)->token_len;
}
/*-----------------------------------------------------------------------------------*/
int
coap_get_header_block(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset)
{
if (!IS_OPTION((coap_packet_t *)packet, COAP_OPTION_BLOCK)) return 0;
/* pointers may be NULL to get only specific block parameters */
if (num!=NULL) *num = ((coap_packet_t *)packet)->block_num;
if (more!=NULL) *more = ((coap_packet_t *)packet)->block_more;
if (size!=NULL) *size = ((coap_packet_t *)packet)->block_size;
if (offset!=NULL) *offset = ((coap_packet_t *)packet)->block_offset;
return 1;
}
int
coap_set_header_block(void *packet, uint32_t num, uint8_t more, uint16_t size)
{
if (size<16) return 0;
if (size>2048) return 0;
if (num>0x0FFFFF) return 0;
((coap_packet_t *)packet)->block_num = num;
((coap_packet_t *)packet)->block_more = more;
((coap_packet_t *)packet)->block_size = size;
SET_OPTION((coap_packet_t *)packet, COAP_OPTION_BLOCK);
return 1;
}
/*-----------------------------------------------------------------------------------*/
int
coap_get_header_uri_query(void *packet, const char **query)
{
if (!IS_OPTION((coap_packet_t *)packet, COAP_OPTION_URI_QUERY)) return 0;
*query = ((coap_packet_t *)packet)->uri_query;
return ((coap_packet_t *)packet)->uri_query_len;
}
int
coap_set_header_uri_query(void *packet, const char *query)
{
while (query[0]=='?') ++query;
((coap_packet_t *)packet)->uri_query = query;
((coap_packet_t *)packet)->uri_query_len = strlen(query);
SET_OPTION((coap_packet_t *)packet, COAP_OPTION_URI_QUERY);
return ((coap_packet_t *)packet)->uri_query_len;
}
/*-----------------------------------------------------------------------------------*/
/*- PAYLOAD -------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------*/
int
coap_get_payload(void *packet, const uint8_t **payload)
{
if (((coap_packet_t *)packet)->payload) {
*payload = ((coap_packet_t *)packet)->payload;
return ((coap_packet_t *)packet)->payload_len;
} else {
*payload = NULL;
return 0;
}
}
int
coap_set_payload(void *packet, const void *payload, size_t length)
{
PRINTF("setting payload (%u/%u)\n", length, REST_MAX_CHUNK_SIZE);
((coap_packet_t *)packet)->payload = (uint8_t *) payload;
((coap_packet_t *)packet)->payload_len = MIN(REST_MAX_CHUNK_SIZE, length);
return ((coap_packet_t *)packet)->payload_len;
}
/*-----------------------------------------------------------------------------------*/

View file

@ -1,284 +0,0 @@
/*
* Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* An implementation of the Constrained Application Protocol (draft 03)
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#ifndef COAP_03_H_
#define COAP_03_H_
#include <stddef.h> /* for size_t */
#include "contiki-net.h"
#include "erbium.h"
#define COAP_DEFAULT_PORT 61616
#ifndef COAP_SERVER_PORT
#define COAP_SERVER_PORT COAP_DEFAULT_PORT
#endif
#define COAP_DEFAULT_MAX_AGE 60
#define COAP_RESPONSE_TIMEOUT 1
#define COAP_MAX_RETRANSMIT 5
#define COAP_HEADER_LEN 4 /* | oc:0xF0 type:0x0C version:0x03 | code | tid:0x00FF | tid:0xFF00 | */
#define COAP_ETAG_LEN 4 /* The maximum number of bytes for the ETag, which is 4 for coap-03 */
#define COAP_TOKEN_LEN 2 /* The maximum number of bytes for the ETag, which is 4 for coap-03 */
#define COAP_HEADER_VERSION_MASK 0xC0
#define COAP_HEADER_VERSION_POSITION 6
#define COAP_HEADER_TYPE_MASK 0x30
#define COAP_HEADER_TYPE_POSITION 4
#define COAP_HEADER_OPTION_COUNT_MASK 0x0F
#define COAP_HEADER_OPTION_COUNT_POSITION 0
#define COAP_HEADER_OPTION_DELTA_MASK 0xF0
#define COAP_HEADER_OPTION_SHORT_LENGTH_MASK 0x0F
/*
* Conservative size limit, as not all options have to be set at the same time.
*/
/* Hdr CoT Age Tag Obs Tok Blo strings */
#define COAP_MAX_HEADER_SIZE (4 + 2 + 5 + 5 + 5 + 5 + 4 + 0)
#define COAP_MAX_PACKET_SIZE (COAP_MAX_HEADER_SIZE + REST_MAX_CHUNK_SIZE)
/* 0/14 48 for IPv6 (28 for IPv4) */
#if COAP_MAX_PACKET_SIZE > (UIP_BUFSIZE - UIP_LLH_LEN - UIP_IPUDPH_LEN)
#error "UIP_CONF_BUFFER_SIZE too small for REST_MAX_CHUNK_SIZE"
#endif
/*
* Maximum number of failed request attempts before action
*/
#ifndef COAP_MAX_ATTEMPTS
#define COAP_MAX_ATTEMPTS 4
#endif /* COAP_MAX_ATTEMPTS */
#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN])
#define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[UIP_LLH_LEN + UIP_IPH_LEN])
#define SET_OPTION(packet, opt) ((packet)->options |= 1<<opt)
#define IS_OPTION(packet, opt) ((packet)->options & 1<<opt)
#ifndef MIN
#define MIN(a, b) ((a) < (b)? (a) : (b))
#endif /* MIN */
/* CoAP message types */
typedef enum {
COAP_TYPE_CON, /* confirmables */
COAP_TYPE_NON, /* non-confirmables */
COAP_TYPE_ACK, /* acknowledgements */
COAP_TYPE_RST /* reset */
} coap_message_type_t;
/* CoAP request method codes */
typedef enum {
COAP_GET = 1,
COAP_POST,
COAP_PUT,
COAP_DELETE
} coap_method_t;
/* CoAP response codes */
typedef enum {
CONTINUE_100 = 40,
OK_200 = 80,
CREATED_201 = 81,
NOT_MODIFIED_304 = 124,
BAD_REQUEST_400 = 160,
NOT_FOUND_404 = 164,
METHOD_NOT_ALLOWED_405 = 165,
UNSUPPORTED_MEDIA_TYPE_415 = 175,
INTERNAL_SERVER_ERROR_500 = 200,
BAD_GATEWAY_502 = 202,
SERVICE_UNAVAILABLE_503 = 203,
GATEWAY_TIMEOUT_504 = 204,
TOKEN_OPTION_REQUIRED = 240,
HOST_REQUIRED = 241,
CRITICAL_OPTION_NOT_SUPPORTED = 242
} coap_status_t;
/* CoAP header options */
typedef enum {
COAP_OPTION_CONTENT_TYPE = 1, /* 1 B */
COAP_OPTION_MAX_AGE = 2, /* 1-4 B */
COAP_OPTION_ETAG = 4, /* 1-4 B */
COAP_OPTION_URI_HOST = 5, /* 1-270 B */
COAP_OPTION_LOCATION_PATH = 6, /* 1-270 B */
COAP_OPTION_URI_PATH = 9, /* 1-270 B */
COAP_OPTION_OBSERVE = 10, /* 0-4 B */
COAP_OPTION_TOKEN = 11, /* 1-2 B */
COAP_OPTION_BLOCK = 13, /* 1-3 B */
COAP_OPTION_NOOP = 14, /* 0 B */
COAP_OPTION_URI_QUERY = 15 /* 1-270 B */
} coap_option_t;
/* CoAP content-types */
typedef enum {
TEXT_PLAIN = 0,
TEXT_XML = 1,
TEXT_CSV = 2,
TEXT_HTML = 3,
IMAGE_GIF = 21,
IMAGE_JPEG = 22,
IMAGE_PNG = 23,
IMAGE_TIFF = 24,
AUDIO_RAW = 25,
VIDEO_RAW = 26,
APPLICATION_LINK_FORMAT = 40,
APPLICATION_XML = 41,
APPLICATION_OCTET_STREAM = 42,
APPLICATION_RDF_XML = 43,
APPLICATION_SOAP_XML = 44,
APPLICATION_ATOM_XML = 45,
APPLICATION_XMPP_XML = 46,
APPLICATION_EXI = 47,
APPLICATION_X_BXML = 48,
APPLICATION_FASTINFOSET = 49,
APPLICATION_SOAP_FASTINFOSET = 50,
APPLICATION_JSON = 51
} coap_content_type_t;
typedef union {
struct { /* 0--14 bytes options */
uint8_t length:4; /* option length in bytes (15 indicates long option format) */
uint8_t delta:4; /* option delta */
/* 0--14 bytes options */
} s;
struct { /* 15-270 bytes options */
uint16_t flag:4; /* must be 15 */
uint16_t delta:4; /* option delta */
uint16_t length:8; /* length - 15 */
} l;
} coap_header_option_t;
typedef struct {
uint8_t *buffer; /* pointer to CoAP header / incoming packet buffer / memory to serialize packet */
uint8_t version;
coap_message_type_t type;
uint8_t option_count;
uint8_t code;
uint16_t tid;
uint16_t options; /* Bitmap to check if option is set */
coap_content_type_t content_type; /* Parse options once and store; allows setting options in random order */
uint32_t max_age;
uint8_t etag_len;
uint8_t etag[COAP_ETAG_LEN];
uint8_t uri_host_len;
const char *uri_host;
uint8_t location_path_len;
const char *location_path;
uint8_t uri_path_len;
const char *uri_path;
uint32_t observe; /* 0-4 bytes for coap-03 */
uint8_t token_len;
uint8_t token[COAP_TOKEN_LEN];
uint32_t block_num;
uint8_t block_more;
uint16_t block_size;
uint32_t block_offset;
uint8_t uri_query_len;
const char *uri_query;
uint16_t payload_len;
uint8_t *payload;
} coap_packet_t;
/*error definitions*/
typedef enum
{
NO_ERROR,
/* Memory errors */
MEMORY_ALLOCATION_ERROR,
MEMORY_BOUNDARY_EXCEEDED,
/* CoAP errors */
UNKNOWN_CRITICAL_OPTION,
PACKET_SERIALIZATION_ERROR
} error_t;
void coap_init_connection(uint16_t port);
uint16_t coap_get_tid(void);
void coap_send_message(uip_ipaddr_t *addr, uint16_t port, const uint8_t *data, uint16_t length);
void coap_init_message(void *packet, coap_message_type_t type, uint8_t code, uint16_t tid);
int coap_serialize_message(void *packet, uint8_t *buffer);
error_t coap_parse_message(void *request, uint8_t *data, uint16_t data_len);
coap_method_t coap_get_method(void *packet);
void coap_set_status(void *packet, unsigned int code);
int coap_get_query_variable(void *packet, const char *name, const char **output);
int coap_get_post_variable(void *packet, const char *name, const char **output);
unsigned int coap_get_header_content_type(void *packet);
int coap_set_header_content_type(void *packet, unsigned int content_type);
int coap_get_header_max_age(void *packet, uint32_t *age);
int coap_set_header_max_age(void *packet, uint32_t age);
int coap_get_header_etag(void *packet, const uint8_t **etag);
int coap_set_header_etag(void *packet, const uint8_t *etag, size_t etag_len);
int coap_get_header_uri_host(void *packet, const char **host); /*CAUTION in-place string might not be 0-terminated */
int coap_set_header_uri_host(void *packet, const char *host);
int coap_get_header_location(void *packet, const char **uri); /*CAUTION in-place string might not be 0-terminated */
int coap_set_header_location(void *packet, const char *uri);
int coap_get_header_uri_path(void *packet, const char **uri); /*CAUTION in-place string might not be 0-terminated */
int coap_set_header_uri_path(void *packet, const char *uri);
int coap_get_header_observe(void *packet, uint32_t *observe);
int coap_set_header_observe(void *packet, uint32_t observe);
int coap_get_header_token(void *packet, const uint8_t **token);
int coap_set_header_token(void *packet, const uint8_t *token, size_t token_len);
int coap_get_header_block(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset);
int coap_set_header_block(void *packet, uint32_t num, uint8_t more, uint16_t size);
int coap_get_header_uri_query(void *packet, const char **query); /*CAUTION in-place string might not be 0-terminated */
int coap_set_header_uri_query(void *packet, const char *query);
int coap_get_payload(void *packet, const uint8_t **payload);
int coap_set_payload(void *packet, const void *payload, size_t length);
#endif /* COAP_03_H_ */

View file

@ -1 +0,0 @@
er-coap-07_src = er-coap-07-engine.c er-coap-07.c er-coap-07-transactions.c er-coap-07-observing.c er-coap-07-separate.c

View file

@ -1,612 +0,0 @@
/*
* Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* CoAP implementation of the REST Engine
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "contiki.h"
#include "contiki-net.h"
#include "er-coap-07-engine.h"
#define DEBUG 0
#if DEBUG
#define PRINTF(...) printf(__VA_ARGS__)
#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
#define PRINTLLADDR(lladdr) PRINTF(" %02x:%02x:%02x:%02x:%02x:%02x ",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5])
#define PRINTBITS(buf,len) { \
int i,j=0; \
for (i=0; i<len; ++i) { \
for (j=7; j>=0; --j) { \
PRINTF("%c", (((char *)buf)[i] & 1<<j) ? '1' : '0'); \
} \
PRINTF(" "); \
} \
}
#else
#define PRINTF(...)
#define PRINT6ADDR(addr)
#define PRINTLLADDR(addr)
#define PRINTBITS(buf,len)
#endif
PROCESS(coap_receiver, "CoAP Receiver");
/*----------------------------------------------------------------------------*/
/*- Variables ----------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
static service_callback_t service_cbk = NULL;
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
static
int
coap_receive(void)
{
coap_error_code = NO_ERROR;
PRINTF("handle_incoming_data(): received uip_datalen=%u \n",(uint16_t)uip_datalen());
/* Static declaration reduces stack peaks and program code size. */
static coap_packet_t message[1]; /* This way the packet can be treated as pointer as usual. */
static coap_packet_t response[1];
static coap_transaction_t *transaction = NULL;
if (uip_newdata()) {
PRINTF("receiving UDP datagram from: ");
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
PRINTF(":%u\n Length: %u\n Data: ", uip_ntohs(UIP_UDP_BUF->srcport), uip_datalen() );
PRINTBITS(uip_appdata, uip_datalen());
PRINTF("\n");
coap_error_code = coap_parse_message(message, uip_appdata, uip_datalen());
if (coap_error_code==NO_ERROR)
{
/*TODO duplicates suppression, if required by application */
PRINTF(" Parsed: v %u, t %u, oc %u, c %u, mid %u\n", message->version, message->type, message->option_count, message->code, message->mid);
PRINTF(" URL: %.*s\n", message->uri_path_len, message->uri_path);
PRINTF(" Payload: %.*s\n", message->payload_len, message->payload);
/* Handle requests. */
if (message->code >= COAP_GET && message->code <= COAP_DELETE)
{
/* Use transaction buffer for response to confirmable request. */
if ( (transaction = coap_new_transaction(message->mid, &UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport)) )
{
uint32_t block_num = 0;
uint16_t block_size = REST_MAX_CHUNK_SIZE;
uint32_t block_offset = 0;
int32_t new_offset = 0;
/* prepare response */
if (message->type==COAP_TYPE_CON)
{
/* Reliable CON requests are answered with an ACK. */
coap_init_message(response, COAP_TYPE_ACK, CONTENT_2_05, message->mid);
}
else
{
/* Unreliable NON requests are answered with a NON as well. */
coap_init_message(response, COAP_TYPE_NON, CONTENT_2_05, coap_get_mid());
}
/* resource handlers must take care of different handling (e.g., TOKEN_OPTION_REQUIRED_240) */
if (IS_OPTION(message, COAP_OPTION_TOKEN))
{
coap_set_header_token(response, message->token, message->token_len);
SET_OPTION(response, COAP_OPTION_TOKEN);
}
/* get offset for blockwise transfers */
if (coap_get_header_block2(message, &block_num, NULL, &block_size, &block_offset))
{
PRINTF("Blockwise: block request %lu (%u/%u) @ %lu bytes\n", block_num, block_size, REST_MAX_CHUNK_SIZE, block_offset);
block_size = MIN(block_size, REST_MAX_CHUNK_SIZE);
new_offset = block_offset;
}
/* Invoke resource handler. */
if (service_cbk)
{
/* Call REST framework and check if found and allowed. */
if (service_cbk(message, response, transaction->packet+COAP_MAX_HEADER_SIZE, block_size, &new_offset))
{
if (coap_error_code==NO_ERROR)
{
/* Apply blockwise transfers. */
if ( IS_OPTION(message, COAP_OPTION_BLOCK1) && response->code<BAD_REQUEST_4_00 && !IS_OPTION(response, COAP_OPTION_BLOCK1) )
{
PRINTF("Block1 NOT IMPLEMENTED\n");
coap_error_code = NOT_IMPLEMENTED_5_01;
coap_error_message = "NoBlock1Support";
}
else if ( IS_OPTION(message, COAP_OPTION_BLOCK2) )
{
/* unchanged new_offset indicates that resource is unaware of blockwise transfer */
if (new_offset==block_offset)
{
PRINTF("Blockwise: unaware resource with payload length %u/%u\n", response->payload_len, block_size);
if (block_offset >= response->payload_len)
{
PRINTF("handle_incoming_data(): block_offset >= response->payload_len\n");
response->code = BAD_OPTION_4_02;
coap_set_payload(response, "BlockOutOfScope", 15); /* a const char str[] and sizeof(str) produces larger code size */
}
else
{
coap_set_header_block2(response, block_num, response->payload_len - block_offset > block_size, block_size);
coap_set_payload(response, response->payload+block_offset, MIN(response->payload_len - block_offset, block_size));
} /* if (valid offset) */
}
else
{
/* resource provides chunk-wise data */
PRINTF("Blockwise: blockwise resource, new offset %ld\n", new_offset);
coap_set_header_block2(response, block_num, new_offset!=-1 || response->payload_len > block_size, block_size);
if (response->payload_len > block_size) coap_set_payload(response, response->payload, block_size);
} /* if (resource aware of blockwise) */
}
else if (new_offset!=0)
{
PRINTF("Blockwise: no block option for blockwise resource, using block size %u\n", REST_MAX_CHUNK_SIZE);
coap_set_header_block2(response, 0, new_offset!=-1, REST_MAX_CHUNK_SIZE);
coap_set_payload(response, response->payload, MIN(response->payload_len, REST_MAX_CHUNK_SIZE));
} /* if (blockwise request) */
} /* no errors/hooks */
} /* successful service callback */
/* Serialize response. */
if (coap_error_code==NO_ERROR)
{
if ((transaction->packet_len = coap_serialize_message(response, transaction->packet))==0)
{
coap_error_code = PACKET_SERIALIZATION_ERROR;
}
}
}
else
{
coap_error_code = NOT_IMPLEMENTED_5_01;
coap_error_message = "NoServiceCallbck"; // no a to fit 16 bytes
} /* if (service callback) */
} else {
coap_error_code = SERVICE_UNAVAILABLE_5_03;
coap_error_message = "NoFreeTraBuffer";
} /* if (transaction buffer) */
}
else
{
/* Responses */
if (message->type==COAP_TYPE_ACK)
{
PRINTF("Received ACK\n");
}
else if (message->type==COAP_TYPE_RST)
{
PRINTF("Received RST\n");
/* Cancel possible subscriptions. */
coap_remove_observer_by_mid(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, message->mid);
}
if ( (transaction = coap_get_transaction_by_mid(message->mid)) )
{
/* Free transaction memory before callback, as it may create a new transaction. */
restful_response_handler callback = transaction->callback;
void *callback_data = transaction->callback_data;
coap_clear_transaction(transaction);
/* Check if someone registered for the response */
if (callback) {
callback(callback_data, message);
}
} /* if (ACKed transaction) */
transaction = NULL;
} /* Request or Response */
} /* if (parsed correctly) */
if (coap_error_code==NO_ERROR)
{
if (transaction) coap_send_transaction(transaction);
}
else if (coap_error_code==MANUAL_RESPONSE)
{
PRINTF("Clearing transaction for manual response");
coap_clear_transaction(transaction);
}
else
{
PRINTF("ERROR %u: %s\n", coap_error_code, coap_error_message);
coap_clear_transaction(transaction);
/* Set to sendable error code. */
if (coap_error_code >= 192)
{
coap_error_code = INTERNAL_SERVER_ERROR_5_00;
}
/* Reuse input buffer for error message. */
coap_init_message(message, COAP_TYPE_ACK, coap_error_code, message->mid);
coap_set_payload(message, coap_error_message, strlen(coap_error_message));
coap_send_message(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, uip_appdata, coap_serialize_message(message, uip_appdata));
}
} /* if (new data) */
return coap_error_code;
}
/*----------------------------------------------------------------------------*/
void
coap_receiver_init()
{
process_start(&coap_receiver, NULL);
}
/*----------------------------------------------------------------------------*/
void
coap_set_service_callback(service_callback_t callback)
{
service_cbk = callback;
}
/*----------------------------------------------------------------------------*/
rest_resource_flags_t
coap_get_rest_method(void *packet)
{
return (rest_resource_flags_t)(1 << (((coap_packet_t *)packet)->code - 1));
}
/*----------------------------------------------------------------------------*/
/*- Server part --------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/* The discover resource is automatically included for CoAP. */
RESOURCE(well_known_core, METHOD_GET, ".well-known/core", "ct=40");
void
well_known_core_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset)
{
size_t strpos = 0; /* position in overall string (which is larger than the buffer) */
size_t bufpos = 0; /* position within buffer (bytes written) */
size_t tmplen = 0;
resource_t* resource = NULL;
/* For filtering. */
const char *filter = NULL;
int len = coap_get_query_variable(request, "rt", &filter);
char *rt = NULL;
for (resource = (resource_t*)list_head(rest_get_resources()); resource; resource = resource->next)
{
/* Filtering */
if (len && ((rt=strstr(resource->attributes, "rt=\""))==NULL || memcmp(rt+4, filter, len-1)!=0 || (filter[len-1]!='*' && (filter[len-1]!=rt[3+len] || rt[4+len]!='"'))))
{
continue;
}
PRINTF("res: /%s (%p)\npos: s%d, o%d, b%d\n", resource->url, resource, strpos, *offset, bufpos);
if (strpos >= *offset && bufpos < preferred_size)
{
buffer[bufpos++] = '<';
}
++strpos;
if (strpos >= *offset && bufpos < preferred_size)
{
buffer[bufpos++] = '/';
}
++strpos;
tmplen = strlen(resource->url);
if (strpos+tmplen > *offset)
{
bufpos += snprintf((char *) buffer + bufpos, preferred_size - bufpos + 1,
"%s", resource->url + ((*offset-(int32_t)strpos > 0) ? (*offset-(int32_t)strpos) : 0));
/* minimal-net requires these casts */
if (bufpos >= preferred_size)
{
break;
}
}
strpos += tmplen;
if (strpos >= *offset && bufpos < preferred_size)
{
buffer[bufpos++] = '>';
}
++strpos;
if (resource->attributes[0])
{
if (strpos >= *offset && bufpos < preferred_size)
{
buffer[bufpos++] = ';';
}
++strpos;
tmplen = strlen(resource->attributes);
if (strpos+tmplen > *offset)
{
bufpos += snprintf((char *) buffer + bufpos, preferred_size - bufpos + 1,
"%s", resource->attributes + (*offset-(int32_t)strpos > 0 ? *offset-(int32_t)strpos : 0));
if (bufpos >= preferred_size)
{
break;
}
}
strpos += tmplen;
}
if (resource->next)
{
if (strpos >= *offset && bufpos < preferred_size)
{
buffer[bufpos++] = ',';
}
++strpos;
}
/* buffer full, but resource not completed yet; or: do not break if resource exactly fills buffer. */
if (bufpos >= preferred_size && strpos-bufpos > *offset)
{
PRINTF("res: BREAK at %s (%p)\n", resource->url, resource);
break;
}
}
if (bufpos>0) {
PRINTF("BUF %d: %.*s\n", bufpos, bufpos, (char *) buffer);
coap_set_payload(response, buffer, bufpos );
coap_set_header_content_type(response, APPLICATION_LINK_FORMAT);
}
else if (strpos>0)
{
PRINTF("well_known_core_handler(): bufpos<=0\n");
coap_set_status_code(response, BAD_OPTION_4_02);
coap_set_payload(response, "BlockOutOfScope", 15);
}
if (resource==NULL) {
PRINTF("res: DONE\n");
*offset = -1;
}
else
{
PRINTF("res: MORE at %s (%p)\n", resource->url, resource);
*offset += preferred_size;
}
}
/*----------------------------------------------------------------------------*/
PROCESS_THREAD(coap_receiver, ev, data)
{
PROCESS_BEGIN();
PRINTF("Starting CoAP-07 receiver...\n");
rest_activate_resource(&resource_well_known_core);
coap_register_as_transaction_handler();
coap_init_connection(SERVER_LISTEN_PORT);
PRINTF("Listening on port %u\n", UIP_HTONS(SERVER_LISTEN_PORT));
while(1) {
PROCESS_YIELD();
if(ev == tcpip_event) {
coap_receive();
} else if (ev == PROCESS_EVENT_TIMER) {
/* retransmissions are handled here */
coap_check_transactions();
}
} /* while (1) */
PROCESS_END();
}
/*----------------------------------------------------------------------------*/
/*- Client part --------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
void coap_blocking_request_callback(void *callback_data, void *response) {
struct request_state_t *state = (struct request_state_t *) callback_data;
state->response = (coap_packet_t*) response;
process_poll(state->process);
}
/*----------------------------------------------------------------------------*/
PT_THREAD(coap_blocking_request(struct request_state_t *state, process_event_t ev,
uip_ipaddr_t *remote_ipaddr, uint16_t remote_port,
coap_packet_t *request,
blocking_response_handler request_callback)) {
PT_BEGIN(&state->pt);
static uint8_t more;
static uint32_t res_block;
static uint8_t block_error;
state->block_num = 0;
state->response = NULL;
state->process = PROCESS_CURRENT();
more = 0;
res_block = 0;
block_error = 0;
do {
request->mid = coap_get_mid();
if ((state->transaction = coap_new_transaction(request->mid, remote_ipaddr, remote_port)))
{
state->transaction->callback = coap_blocking_request_callback;
state->transaction->callback_data = state;
if (state->block_num>0)
{
coap_set_header_block2(request, state->block_num, 0, REST_MAX_CHUNK_SIZE);
}
state->transaction->packet_len = coap_serialize_message(request, state->transaction->packet);
coap_send_transaction(state->transaction);
PRINTF("Requested #%lu (MID %u)\n", state->block_num, request->mid);
PT_YIELD_UNTIL(&state->pt, ev == PROCESS_EVENT_POLL);
if (!state->response)
{
PRINTF("Server not responding\n");
PT_EXIT(&state->pt);
}
coap_get_header_block2(state->response, &res_block, &more, NULL, NULL);
PRINTF("Received #%lu%s (%u bytes)\n", res_block, more ? "+" : "", state->response->payload_len);
if (res_block==state->block_num)
{
request_callback(state->response);
++(state->block_num);
}
else
{
PRINTF("WRONG BLOCK %lu/%lu\n", res_block, state->block_num);
++block_error;
}
}
else
{
PRINTF("Could not allocate transaction buffer");
PT_EXIT(&state->pt);
}
} while (more && block_error<COAP_MAX_ATTEMPTS);
PT_END(&state->pt);
}
/*----------------------------------------------------------------------------*/
/*- Engine Interface ---------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
const struct rest_implementation coap_rest_implementation = {
"CoAP-07",
coap_receiver_init,
coap_set_service_callback,
coap_get_header_uri_path,
coap_set_header_uri_path,
coap_get_rest_method,
coap_set_status_code,
coap_get_header_content_type,
coap_set_header_content_type,
coap_get_header_accept,
NULL,
NULL,
coap_get_header_max_age,
coap_set_header_max_age,
coap_set_header_etag,
coap_get_header_if_match,
coap_get_header_if_none_match,
coap_get_header_uri_host,
coap_set_header_location_path,
coap_get_payload,
coap_set_payload,
coap_get_header_uri_query,
coap_get_query_variable,
coap_get_post_variable,
coap_notify_observers,
(restful_post_handler) coap_observe_handler,
NULL, /* default pre-handler (set separate handler after activation if needed) */
NULL, /* default post-handler for non-observable resources */
{
CONTENT_2_05,
CREATED_2_01,
CHANGED_2_04,
DELETED_2_02,
VALID_2_03,
BAD_REQUEST_4_00,
UNAUTHORIZED_4_01,
BAD_OPTION_4_02,
FORBIDDEN_4_03,
NOT_FOUND_4_04,
METHOD_NOT_ALLOWED_4_05,
NOT_ACCEPTABLE_4_06,
REQUEST_ENTITY_TOO_LARGE_4_13,
UNSUPPORTED_MEDIA_TYPE_4_15,
INTERNAL_SERVER_ERROR_5_00,
NOT_IMPLEMENTED_5_01,
BAD_GATEWAY_5_02,
SERVICE_UNAVAILABLE_5_03,
GATEWAY_TIMEOUT_5_04,
PROXYING_NOT_SUPPORTED_5_05
},
{
TEXT_PLAIN,
TEXT_XML,
TEXT_CSV,
TEXT_HTML,
IMAGE_GIF,
IMAGE_JPEG,
IMAGE_PNG,
IMAGE_TIFF,
AUDIO_RAW,
VIDEO_RAW,
APPLICATION_LINK_FORMAT,
APPLICATION_XML,
APPLICATION_OCTET_STREAM,
APPLICATION_RDF_XML,
APPLICATION_SOAP_XML,
APPLICATION_ATOM_XML,
APPLICATION_XMPP_XML,
APPLICATION_EXI,
APPLICATION_FASTINFOSET,
APPLICATION_SOAP_FASTINFOSET,
APPLICATION_JSON,
APPLICATION_X_OBIX_BINARY
}
};

View file

@ -1,94 +0,0 @@
/*
* Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* CoAP implementation of the REST Engine
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#ifndef COAP_SERVER_H_
#define COAP_SERVER_H_
#if !defined(REST)
#error "Define REST to \"coap_rest_implementation\""
#endif
#include "er-coap-07.h"
#include "er-coap-07-transactions.h"
#include "er-coap-07-observing.h"
#include "er-coap-07-separate.h"
#include "pt.h"
/* Declare server process */
PROCESS_NAME(coap_receiver);
#define SERVER_LISTEN_PORT UIP_HTONS(COAP_SERVER_PORT)
typedef coap_packet_t rest_request_t;
typedef coap_packet_t rest_response_t;
extern const struct rest_implementation coap_rest_implementation;
void coap_receiver_init(void);
/*-----------------------------------------------------------------------------------*/
/*- Client part ---------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------*/
struct request_state_t {
struct pt pt;
struct process *process;
coap_transaction_t *transaction;
coap_packet_t *response;
uint32_t block_num;
};
typedef void (*blocking_response_handler) (void* response);
PT_THREAD(coap_blocking_request(struct request_state_t *state, process_event_t ev,
uip_ipaddr_t *remote_ipaddr, uint16_t remote_port,
coap_packet_t *request,
blocking_response_handler request_callback));
#define COAP_BLOCKING_REQUEST(server_addr, server_port, request, chunk_handler) \
{ \
static struct request_state_t request_state; \
PT_SPAWN(process_pt, &request_state.pt, \
coap_blocking_request(&request_state, ev, \
server_addr, server_port, \
request, chunk_handler) \
); \
}
/*-----------------------------------------------------------------------------------*/
#endif /* COAP_SERVER_H_ */

View file

@ -1,255 +0,0 @@
/*
* Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* CoAP module for observing resources
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#include <stdio.h>
#include <string.h>
#include "er-coap-07-observing.h"
#define DEBUG 0
#if DEBUG
#define PRINTF(...) printf(__VA_ARGS__)
#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5])
#else
#define PRINTF(...)
#define PRINT6ADDR(addr)
#define PRINTLLADDR(addr)
#endif
MEMB(observers_memb, coap_observer_t, COAP_MAX_OBSERVERS);
LIST(observers_list);
/*-----------------------------------------------------------------------------------*/
coap_observer_t *
coap_add_observer(uip_ipaddr_t *addr, uint16_t port, const uint8_t *token, size_t token_len, const char *url)
{
/* Remove existing observe relationship, if any. */
coap_remove_observer_by_url(addr, port, url);
coap_observer_t *o = memb_alloc(&observers_memb);
if (o)
{
o->url = url;
uip_ipaddr_copy(&o->addr, addr);
o->port = port;
o->token_len = token_len;
memcpy(o->token, token, token_len);
o->last_mid = 0;
stimer_set(&o->refresh_timer, COAP_OBSERVING_REFRESH_INTERVAL);
PRINTF("Adding observer for /%s [0x%02X%02X]\n", o->url, o->token[0], o->token[1]);
list_add(observers_list, o);
}
return o;
}
/*-----------------------------------------------------------------------------------*/
void
coap_remove_observer(coap_observer_t *o)
{
PRINTF("Removing observer for /%s [0x%02X%02X]\n", o->url, o->token[0], o->token[1]);
memb_free(&observers_memb, o);
list_remove(observers_list, o);
}
int
coap_remove_observer_by_client(uip_ipaddr_t *addr, uint16_t port)
{
int removed = 0;
coap_observer_t* obs = NULL;
for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next)
{
PRINTF("Remove check client ");
PRINT6ADDR(addr);
PRINTF(":%u\n", port);
if (uip_ipaddr_cmp(&obs->addr, addr) && obs->port==port)
{
coap_remove_observer(obs);
removed++;
}
}
return removed;
}
int
coap_remove_observer_by_token(uip_ipaddr_t *addr, uint16_t port, uint8_t *token, size_t token_len)
{
int removed = 0;
coap_observer_t* obs = NULL;
for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next)
{
PRINTF("Remove check Token 0x%02X%02X\n", token[0], token[1]);
if (uip_ipaddr_cmp(&obs->addr, addr) && obs->port==port && obs->token_len==token_len && memcmp(obs->token, token, token_len)==0)
{
coap_remove_observer(obs);
removed++;
}
}
return removed;
}
int
coap_remove_observer_by_url(uip_ipaddr_t *addr, uint16_t port, const char *url)
{
int removed = 0;
coap_observer_t* obs = NULL;
for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next)
{
PRINTF("Remove check URL %p\n", url);
if (uip_ipaddr_cmp(&obs->addr, addr) && obs->port==port && (obs->url==url || memcmp(obs->url, url, strlen(obs->url))==0))
{
coap_remove_observer(obs);
removed++;
}
}
return removed;
}
int
coap_remove_observer_by_mid(uip_ipaddr_t *addr, uint16_t port, uint16_t mid)
{
int removed = 0;
coap_observer_t* obs = NULL;
for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next)
{
PRINTF("Remove check MID %u\n", mid);
if (uip_ipaddr_cmp(&obs->addr, addr) && obs->port==port && obs->last_mid==mid)
{
coap_remove_observer(obs);
removed++;
}
}
return removed;
}
/*-----------------------------------------------------------------------------------*/
void
coap_notify_observers(resource_t *resource, int32_t obs_counter, void *notification)
{
coap_packet_t *const coap_res = (coap_packet_t *) notification;
coap_observer_t* obs = NULL;
uint8_t preferred_type = coap_res->type;
PRINTF("Observing: Notification from %s\n", resource->url);
/* Iterate over observers. */
for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next)
{
if (obs->url==resource->url) /* using RESOURCE url pointer as handle */
{
coap_transaction_t *transaction = NULL;
/*TODO implement special transaction for CON, sharing the same buffer to allow for more observers. */
if ( (transaction = coap_new_transaction(coap_get_mid(), &obs->addr, obs->port)) )
{
PRINTF(" Observer ");
PRINT6ADDR(&obs->addr);
PRINTF(":%u\n", obs->port);
/* Update last MID for RST matching. */
obs->last_mid = transaction->mid;
/* Prepare response */
coap_res->mid = transaction->mid;
coap_set_header_observe(coap_res, obs_counter);
coap_set_header_token(coap_res, obs->token, obs->token_len);
/* Use CON to check whether client is still there/interested after COAP_OBSERVING_REFRESH_INTERVAL. */
if (stimer_expired(&obs->refresh_timer))
{
PRINTF(" Refreshing with CON\n");
coap_res->type = COAP_TYPE_CON;
stimer_restart(&obs->refresh_timer);
}
else
{
coap_res->type = preferred_type;
}
transaction->packet_len = coap_serialize_message(coap_res, transaction->packet);
coap_send_transaction(transaction);
}
}
}
}
/*-----------------------------------------------------------------------------------*/
void
coap_observe_handler(resource_t *resource, void *request, void *response)
{
coap_packet_t *const coap_req = (coap_packet_t *) request;
coap_packet_t *const coap_res = (coap_packet_t *) response;
static char content[16];
if (coap_req->code==COAP_GET && coap_res->code<128) /* GET request and response without error code */
{
if (IS_OPTION(coap_req, COAP_OPTION_OBSERVE))
{
if (coap_add_observer(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, coap_req->token, coap_req->token_len, resource->url))
{
coap_set_header_observe(coap_res, 0);
/*
* For demonstration purposes only. A subscription should return the same representation as a normal GET.
* TODO: Comment the following line for any real application.
*/
coap_set_payload(coap_res, content, snprintf(content, sizeof(content), "Added %u/%u", list_length(observers_list), COAP_MAX_OBSERVERS));
}
else
{
coap_res->code = SERVICE_UNAVAILABLE_5_03;
coap_set_payload(coap_res, "TooManyObservers", 16);
} /* if (added observer) */
}
else /* if (observe) */
{
/* Remove client if it is currently observing. */
coap_remove_observer_by_url(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, resource->url);
} /* if (observe) */
}
}

View file

@ -1,83 +0,0 @@
/*
* Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* CoAP module for observing resources
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#ifndef COAP_OBSERVING_H_
#define COAP_OBSERVING_H_
#include "sys/stimer.h"
#include "er-coap-07.h"
#include "er-coap-07-transactions.h"
#ifndef COAP_MAX_OBSERVERS
#define COAP_MAX_OBSERVERS 4
#endif /* COAP_MAX_OBSERVERS */
/* Interval in seconds in which NON notifies are changed to CON notifies to check client. */
#define COAP_OBSERVING_REFRESH_INTERVAL 60
#if COAP_MAX_OPEN_TRANSACTIONS<COAP_MAX_OBSERVERS
#warning "COAP_MAX_OPEN_TRANSACTIONS smaller than COAP_MAX_OBSERVERS: cannot handle CON notifications"
#endif
typedef struct coap_observer {
struct coap_observer *next; /* for LIST */
const char *url;
uip_ipaddr_t addr;
uint16_t port;
uint8_t token_len;
uint8_t token[COAP_TOKEN_LEN];
uint16_t last_mid;
struct stimer refresh_timer;
} coap_observer_t;
list_t coap_get_observers(void);
coap_observer_t *coap_add_observer(uip_ipaddr_t *addr, uint16_t port, const uint8_t *token, size_t token_len, const char *url);
void coap_remove_observer(coap_observer_t *o);
int coap_remove_observer_by_client(uip_ipaddr_t *addr, uint16_t port);
int coap_remove_observer_by_token(uip_ipaddr_t *addr, uint16_t port, uint8_t *token, size_t token_len);
int coap_remove_observer_by_url(uip_ipaddr_t *addr, uint16_t port, const char *url);
int coap_remove_observer_by_mid(uip_ipaddr_t *addr, uint16_t port, uint16_t mid);
void coap_notify_observers(resource_t *resource, int32_t obs_counter, void *notification);
void coap_observe_handler(resource_t *resource, void *request, void *response);
#endif /* COAP_OBSERVING_H_ */

View file

@ -1,117 +0,0 @@
/*
* Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* CoAP module for separate responses
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#include <stdio.h>
#include <string.h>
#include "er-coap-07-separate.h"
#include "er-coap-07-transactions.h"
#define DEBUG 0
#if DEBUG
#define PRINTF(...) printf(__VA_ARGS__)
#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5])
#else
#define PRINTF(...)
#define PRINT6ADDR(addr)
#define PRINTLLADDR(addr)
#endif
/*----------------------------------------------------------------------------*/
void
coap_separate_reject()
{
coap_error_code = SERVICE_UNAVAILABLE_5_03;
coap_error_message = "AlreadyInUse";
}
/*----------------------------------------------------------------------------*/
int
coap_separate_accept(void *request, coap_separate_t *separate_store)
{
coap_packet_t *const coap_req = (coap_packet_t *) request;
coap_transaction_t *const t = coap_get_transaction_by_mid(coap_req->mid);
PRINTF("Separate ACCEPT: /%.*s MID %u\n", coap_req->uri_path_len, coap_req->uri_path, coap_req->mid);
if (t)
{
/* Send separate ACK for CON. */
if (coap_req->type==COAP_TYPE_CON)
{
coap_packet_t ack[1];
/* ACK with empty code (0) */
coap_init_message(ack, COAP_TYPE_ACK, 0, coap_req->mid);
/* Serializing into IPBUF: Only overwrites header parts that are already parsed into the request struct. */
coap_send_message(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, (uip_appdata), coap_serialize_message(ack, uip_appdata));
}
/* Store remote address. */
uip_ipaddr_copy(&separate_store->addr, &t->addr);
separate_store->port = t->port;
/* Store correct response type. */
separate_store->type = coap_req->type==COAP_TYPE_CON ? COAP_TYPE_CON : COAP_TYPE_NON;
separate_store->mid = coap_get_mid(); /* if it was a NON, we burned one MID in the engine... */
memcpy(separate_store->token, coap_req->token, coap_req->token_len);
separate_store->token_len = coap_req->token_len;
separate_store->block2_num = coap_req->block2_num;
separate_store->block2_size = coap_req->block2_size;
/* Signal the engine to skip automatic response and clear transaction by engine. */
coap_error_code = MANUAL_RESPONSE;
return 1;
}
else
{
PRINTF("ERROR: Response transaction for separate request not found!\n");
return 0;
}
}
/*----------------------------------------------------------------------------*/
void
coap_separate_resume(void *response, coap_separate_t *separate_store, uint8_t code)
{
coap_init_message(response, separate_store->type, code, separate_store->mid);
if (separate_store->token_len)
{
coap_set_header_token(response, separate_store->token, separate_store->token_len);
}
}

View file

@ -1,199 +0,0 @@
/*
* Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* CoAP module for reliable transport
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#include "contiki.h"
#include "contiki-net.h"
#include "er-coap-07-transactions.h"
#include "er-coap-07-observing.h"
/*
* Modulo mask (+1 and +0.5 for rounding) for a random number to get the tick number for the random
* retransmission time between COAP_RESPONSE_TIMEOUT and COAP_RESPONSE_TIMEOUT*COAP_RESPONSE_RANDOM_FACTOR.
*/
#define COAP_RESPONSE_TIMEOUT_TICKS (CLOCK_SECOND * COAP_RESPONSE_TIMEOUT)
#define COAP_RESPONSE_TIMEOUT_BACKOFF_MASK ((CLOCK_SECOND * COAP_RESPONSE_TIMEOUT * (COAP_RESPONSE_RANDOM_FACTOR - 1)) + 1.5)
#define DEBUG 0
#if DEBUG
#include <stdio.h>
#define PRINTF(...) printf(__VA_ARGS__)
#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5])
#else
#define PRINTF(...)
#define PRINT6ADDR(addr)
#define PRINTLLADDR(addr)
#endif
MEMB(transactions_memb, coap_transaction_t, COAP_MAX_OPEN_TRANSACTIONS);
LIST(transactions_list);
static struct process *transaction_handler_process = NULL;
void
coap_register_as_transaction_handler()
{
transaction_handler_process = PROCESS_CURRENT();
}
coap_transaction_t *
coap_new_transaction(uint16_t mid, uip_ipaddr_t *addr, uint16_t port)
{
coap_transaction_t *t = memb_alloc(&transactions_memb);
if (t)
{
t->mid = mid;
t->retrans_counter = 0;
/* save client address */
uip_ipaddr_copy(&t->addr, addr);
t->port = port;
list_add(transactions_list, t); /* List itself makes sure same element is not added twice. */
}
return t;
}
void
coap_send_transaction(coap_transaction_t *t)
{
PRINTF("Sending transaction %u\n", t->mid);
coap_send_message(&t->addr, t->port, t->packet, t->packet_len);
if (COAP_TYPE_CON==((COAP_HEADER_TYPE_MASK & t->packet[0])>>COAP_HEADER_TYPE_POSITION))
{
if (t->retrans_counter<COAP_MAX_RETRANSMIT)
{
/* Not timed out yet. */
PRINTF("Keeping transaction %u\n", t->mid);
if (t->retrans_counter==0)
{
t->retrans_timer.timer.interval = COAP_RESPONSE_TIMEOUT_TICKS + (random_rand() % (clock_time_t) COAP_RESPONSE_TIMEOUT_BACKOFF_MASK);
PRINTF("Initial interval %f\n", (float)t->retrans_timer.timer.interval/CLOCK_SECOND);
}
else
{
t->retrans_timer.timer.interval <<= 1; /* double */
PRINTF("Doubled (%u) interval %f\n", t->retrans_counter, (float)t->retrans_timer.timer.interval/CLOCK_SECOND);
}
/*FIXME
* Hack: Setting timer for responsible process.
* Maybe there is a better way, but avoid posting everything to the process.
*/
struct process *process_actual = PROCESS_CURRENT();
process_current = transaction_handler_process;
etimer_restart(&t->retrans_timer); /* interval updated above */
process_current = process_actual;
t = NULL;
}
else
{
/* Timed out. */
PRINTF("Timeout\n");
restful_response_handler callback = t->callback;
void *callback_data = t->callback_data;
/* handle observers */
coap_remove_observer_by_client(&t->addr, t->port);
coap_clear_transaction(t);
if (callback) {
callback(callback_data, NULL);
}
}
}
else
{
coap_clear_transaction(t);
}
}
void
coap_clear_transaction(coap_transaction_t *t)
{
if (t)
{
PRINTF("Freeing transaction %u: %p\n", t->mid, t);
etimer_stop(&t->retrans_timer);
list_remove(transactions_list, t);
memb_free(&transactions_memb, t);
}
}
coap_transaction_t *
coap_get_transaction_by_mid(uint16_t mid)
{
coap_transaction_t *t = NULL;
for (t = (coap_transaction_t*)list_head(transactions_list); t; t = t->next)
{
if (t->mid==mid)
{
PRINTF("Found transaction for MID %u: %p\n", t->mid, t);
return t;
}
}
return NULL;
}
void
coap_check_transactions()
{
coap_transaction_t *t = NULL;
for (t = (coap_transaction_t*)list_head(transactions_list); t; t = t->next)
{
if (etimer_expired(&t->retrans_timer))
{
++(t->retrans_counter);
PRINTF("Retransmitting %u (%u)\n", t->mid, t->retrans_counter);
coap_send_transaction(t);
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,322 +0,0 @@
/*
* Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* An implementation of the Constrained Application Protocol (draft 07)
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#ifndef COAP_07_H_
#define COAP_07_H_
#include <stddef.h> /* for size_t */
#include "contiki-net.h"
#include "erbium.h"
#define COAP_DEFAULT_PORT 5683
#ifndef COAP_SERVER_PORT
#define COAP_SERVER_PORT COAP_DEFAULT_PORT
#endif
#define COAP_DEFAULT_MAX_AGE 60
#define COAP_RESPONSE_TIMEOUT 2
#define COAP_RESPONSE_RANDOM_FACTOR 1.5
#define COAP_MAX_RETRANSMIT 4
#define COAP_HEADER_LEN 4 /* | oc:0xF0 type:0x0C version:0x03 | code | mid:0x00FF | mid:0xFF00 | */
#define COAP_ETAG_LEN 8 /* The maximum number of bytes for the ETag */
#define COAP_TOKEN_LEN 8 /* The maximum number of bytes for the Token */
#define COAP_MAX_ACCEPT_NUM 2 /* The maximum number of accept preferences to parse/store */
#define COAP_HEADER_VERSION_MASK 0xC0
#define COAP_HEADER_VERSION_POSITION 6
#define COAP_HEADER_TYPE_MASK 0x30
#define COAP_HEADER_TYPE_POSITION 4
#define COAP_HEADER_OPTION_COUNT_MASK 0x0F
#define COAP_HEADER_OPTION_COUNT_POSITION 0
#define COAP_HEADER_OPTION_DELTA_MASK 0xF0
#define COAP_HEADER_OPTION_SHORT_LENGTH_MASK 0x0F
/*
* Conservative size limit, as not all options have to be set at the same time.
*/
/* Hdr CoT Age Tag Obs Tok Blo strings */
#define COAP_MAX_HEADER_SIZE (4 + 3 + 5 + 1+COAP_ETAG_LEN + 3 + 1+COAP_TOKEN_LEN + 4 + 10) /* 50 */
#define COAP_MAX_PACKET_SIZE (COAP_MAX_HEADER_SIZE + REST_MAX_CHUNK_SIZE)
/* 0/14 48 for IPv6 (28 for IPv4) */
#if COAP_MAX_PACKET_SIZE > (UIP_BUFSIZE - UIP_LLH_LEN - UIP_IPUDPH_LEN)
#error "UIP_CONF_BUFFER_SIZE too small for REST_MAX_CHUNK_SIZE"
#endif
/*
* Maximum number of failed request attempts before action
*/
#ifndef COAP_MAX_ATTEMPTS
#define COAP_MAX_ATTEMPTS 4
#endif /* COAP_MAX_ATTEMPTS */
#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN])
#define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[UIP_LLH_LEN + UIP_IPH_LEN])
#define SET_OPTION(packet, opt) ((packet)->options |= 1L<<opt)
#define IS_OPTION(packet, opt) ((packet)->options & 1L<<opt)
#ifndef MIN
#define MIN(a, b) ((a) < (b)? (a) : (b))
#endif /* MIN */
/* CoAP message types */
typedef enum {
COAP_TYPE_CON, /* confirmables */
COAP_TYPE_NON, /* non-confirmables */
COAP_TYPE_ACK, /* acknowledgements */
COAP_TYPE_RST /* reset */
} coap_message_type_t;
/* CoAP request method codes */
typedef enum {
COAP_GET = 1,
COAP_POST,
COAP_PUT,
COAP_DELETE
} coap_method_t;
/* CoAP response codes */
typedef enum {
NO_ERROR = 0,
CREATED_2_01 = 65, /* CREATED */
DELETED_2_02 = 66, /* DELETED */
VALID_2_03 = 67, /* NOT_MODIFIED */
CHANGED_2_04 = 68, /* CHANGED */
CONTENT_2_05 = 69, /* OK */
BAD_REQUEST_4_00 = 128, /* BAD_REQUEST */
UNAUTHORIZED_4_01 = 129, /* UNAUTHORIZED */
BAD_OPTION_4_02 = 130, /* BAD_OPTION */
FORBIDDEN_4_03 = 131, /* FORBIDDEN */
NOT_FOUND_4_04 = 132, /* NOT_FOUND */
METHOD_NOT_ALLOWED_4_05 = 133, /* METHOD_NOT_ALLOWED */
NOT_ACCEPTABLE_4_06 = 134, /* NOT_ACCEPTABLE */
PRECONDITION_FAILED_4_12 = 140, /* BAD_REQUEST */
REQUEST_ENTITY_TOO_LARGE_4_13 = 141, /* REQUEST_ENTITY_TOO_LARGE */
UNSUPPORTED_MEDIA_TYPE_4_15 = 143, /* UNSUPPORTED_MEDIA_TYPE */
INTERNAL_SERVER_ERROR_5_00 = 160, /* INTERNAL_SERVER_ERROR */
NOT_IMPLEMENTED_5_01 = 161, /* NOT_IMPLEMENTED */
BAD_GATEWAY_5_02 = 162, /* BAD_GATEWAY */
SERVICE_UNAVAILABLE_5_03 = 163, /* SERVICE_UNAVAILABLE */
GATEWAY_TIMEOUT_5_04 = 164, /* GATEWAY_TIMEOUT */
PROXYING_NOT_SUPPORTED_5_05 = 165, /* PROXYING_NOT_SUPPORTED */
/* Erbium errors */
MEMORY_ALLOCATION_ERROR = 192,
PACKET_SERIALIZATION_ERROR,
/* Erbium hooks */
MANUAL_RESPONSE
} coap_status_t;
/* CoAP header options */
typedef enum {
COAP_OPTION_CONTENT_TYPE = 1, /* 0-2 B */
COAP_OPTION_MAX_AGE = 2, /* 0-4 B */
COAP_OPTION_PROXY_URI = 3, /* 1-270 B */
COAP_OPTION_ETAG = 4, /* 1-8 B */
COAP_OPTION_URI_HOST = 5, /* 1-270 B */
COAP_OPTION_LOCATION_PATH = 6, /* 1-270 B */
COAP_OPTION_URI_PORT = 7, /* 0-2 B */
COAP_OPTION_LOCATION_QUERY = 8, /* 1-270 B */
COAP_OPTION_URI_PATH = 9, /* 1-270 B */
COAP_OPTION_OBSERVE = 10, /* 0-2 B */
COAP_OPTION_TOKEN = 11, /* 1-8 B */
COAP_OPTION_ACCEPT = 12, /* 0-2 B */
COAP_OPTION_IF_MATCH = 13, /* 0-8 B */
COAP_OPTION_FENCE_POST = 14, /* 0 B */
COAP_OPTION_URI_QUERY = 15, /* 1-270 B */
COAP_OPTION_BLOCK2 = 17, /* 1-3 B */
COAP_OPTION_BLOCK1 = 19, /* 1-3 B */
COAP_OPTION_IF_NONE_MATCH = 21 /* 0 B */
} coap_option_t;
/* CoAP content-types */
typedef enum {
TEXT_PLAIN = 0,
TEXT_XML = 1, /* Intented types are not in the initial registry. */
TEXT_CSV = 2,
TEXT_HTML = 3,
IMAGE_GIF = 21,
IMAGE_JPEG = 22,
IMAGE_PNG = 23,
IMAGE_TIFF = 24,
AUDIO_RAW = 25,
VIDEO_RAW = 26,
APPLICATION_LINK_FORMAT = 40, /* Actually not in registry!? */
APPLICATION_XML = 41,
APPLICATION_OCTET_STREAM = 42,
APPLICATION_RDF_XML = 43,
APPLICATION_SOAP_XML = 44,
APPLICATION_ATOM_XML = 45,
APPLICATION_XMPP_XML = 46,
APPLICATION_EXI = 47,
APPLICATION_FASTINFOSET = 48,
APPLICATION_SOAP_FASTINFOSET = 49,
APPLICATION_JSON = 50,
APPLICATION_X_OBIX_BINARY = 51
} coap_content_type_t;
typedef struct {
uint8_t *buffer; /* pointer to CoAP header / incoming packet buffer / memory to serialize packet */
uint8_t version;
coap_message_type_t type;
uint8_t option_count;
uint8_t code;
uint16_t mid;
uint32_t options; /* Bitmap to check if option is set */
coap_content_type_t content_type; /* Parse options once and store; allows setting options in random order */
uint32_t max_age;
size_t proxy_uri_len;
const char *proxy_uri;
uint8_t etag_len;
uint8_t etag[COAP_ETAG_LEN];
size_t uri_host_len;
const char *uri_host;
size_t location_path_len;
const char *location_path;
uint16_t uri_port;
size_t location_query_len;
const char *location_query;
size_t uri_path_len;
const char *uri_path;
uint16_t observe;
uint8_t token_len;
uint8_t token[COAP_TOKEN_LEN];
uint8_t accept_num;
uint16_t accept[COAP_MAX_ACCEPT_NUM];
uint8_t if_match_len;
uint8_t if_match[COAP_ETAG_LEN];
uint32_t block2_num;
uint8_t block2_more;
uint16_t block2_size;
uint32_t block2_offset;
uint32_t block1_num;
uint8_t block1_more;
uint16_t block1_size;
uint32_t block1_offset;
size_t uri_query_len;
const char *uri_query;
uint8_t if_none_match;
uint16_t payload_len;
uint8_t *payload;
} coap_packet_t;
/* To store error code and human-readable payload */
extern coap_status_t coap_error_code;
extern char *coap_error_message;
void coap_init_connection(uint16_t port);
uint16_t coap_get_mid(void);
void coap_init_message(void *packet, coap_message_type_t type, uint8_t code, uint16_t mid);
size_t coap_serialize_message(void *packet, uint8_t *buffer);
void coap_send_message(uip_ipaddr_t *addr, uint16_t port, uint8_t *data, uint16_t length);
coap_status_t coap_parse_message(void *request, uint8_t *data, uint16_t data_len);
int coap_get_query_variable(void *packet, const char *name, const char **output);
int coap_get_post_variable(void *packet, const char *name, const char **output);
/*-----------------------------------------------------------------------------------*/
int coap_set_status_code(void *packet, unsigned int code);
unsigned int coap_get_header_content_type(void *packet);
int coap_set_header_content_type(void *packet, unsigned int content_type);
int coap_get_header_accept(void *packet, const uint16_t **accept);
int coap_set_header_accept(void *packet, uint16_t accept);
int coap_get_header_max_age(void *packet, uint32_t *age);
int coap_set_header_max_age(void *packet, uint32_t age);
int coap_get_header_etag(void *packet, const uint8_t **etag);
int coap_set_header_etag(void *packet, const uint8_t *etag, size_t etag_len);
int coap_get_header_if_match(void *packet, const uint8_t **etag);
int coap_set_header_if_match(void *packet, const uint8_t *etag, size_t etag_len);
int coap_get_header_if_none_match(void *packet);
int coap_set_header_if_none_match(void *packet);
int coap_get_header_token(void *packet, const uint8_t **token);
int coap_set_header_token(void *packet, const uint8_t *token, size_t token_len);
int coap_get_header_proxy_uri(void *packet, const char **uri); /* In-place string might not be 0-terminated. */
int coap_set_header_proxy_uri(void *packet, const char *uri);
int coap_get_header_uri_host(void *packet, const char **host); /* In-place string might not be 0-terminated. */
int coap_set_header_uri_host(void *packet, const char *host);
int coap_get_header_uri_path(void *packet, const char **path); /* In-place string might not be 0-terminated. */
int coap_set_header_uri_path(void *packet, const char *path);
int coap_get_header_uri_query(void *packet, const char **query); /* In-place string might not be 0-terminated. */
int coap_set_header_uri_query(void *packet, const char *query);
int coap_get_header_location_path(void *packet, const char **path); /* In-place string might not be 0-terminated. */
int coap_set_header_location_path(void *packet, const char *path); /* Also splits optional query into Location-Query option. */
int coap_get_header_location_query(void *packet, const char **query); /* In-place string might not be 0-terminated. */
int coap_set_header_location_query(void *packet, const char *query);
int coap_get_header_observe(void *packet, uint32_t *observe);
int coap_set_header_observe(void *packet, uint32_t observe);
int coap_get_header_block2(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset);
int coap_set_header_block2(void *packet, uint32_t num, uint8_t more, uint16_t size);
int coap_get_header_block1(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset);
int coap_set_header_block1(void *packet, uint32_t num, uint8_t more, uint16_t size);
int coap_get_payload(void *packet, const uint8_t **payload);
int coap_set_payload(void *packet, const void *payload, size_t length);
#endif /* COAP_07_H_ */

View file

@ -1 +0,0 @@
er-coap-12_src = er-coap-12-engine.c er-coap-12.c er-coap-12-transactions.c er-coap-12-observing.c er-coap-12-separate.c

View file

@ -1,669 +0,0 @@
/*
* Copyright (c) 2012, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* CoAP implementation of the REST Engine
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "contiki.h"
#include "contiki-net.h"
#include "er-coap-12-engine.h"
#define DEBUG 0
#if DEBUG
#define PRINTF(...) printf(__VA_ARGS__)
#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
#define PRINTLLADDR(lladdr) PRINTF(" %02x:%02x:%02x:%02x:%02x:%02x ",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5])
#define PRINTBITS(buf,len) { \
int i,j=0; \
for (i=0; i<len; ++i) { \
for (j=7; j>=0; --j) { \
PRINTF("%c", (((char *)buf)[i] & 1<<j) ? '1' : '0'); \
} \
PRINTF(" "); \
} \
}
#else
#define PRINTF(...)
#define PRINT6ADDR(addr)
#define PRINTLLADDR(addr)
#define PRINTBITS(buf,len)
#endif
PROCESS(coap_receiver, "CoAP Receiver");
/*----------------------------------------------------------------------------*/
/*- Variables ----------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
static service_callback_t service_cbk = NULL;
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
static
int
coap_receive(void)
{
coap_error_code = NO_ERROR;
PRINTF("handle_incoming_data(): received uip_datalen=%u \n",(uint16_t)uip_datalen());
/* Static declaration reduces stack peaks and program code size. */
static coap_packet_t message[1]; /* This way the packet can be treated as pointer as usual. */
static coap_packet_t response[1];
static coap_transaction_t *transaction = NULL;
if (uip_newdata()) {
PRINTF("receiving UDP datagram from: ");
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
PRINTF(":%u\n Length: %u\n Data: ", uip_ntohs(UIP_UDP_BUF->srcport), uip_datalen() );
PRINTBITS(uip_appdata, uip_datalen());
PRINTF("\n");
coap_error_code = coap_parse_message(message, uip_appdata, uip_datalen());
if (coap_error_code==NO_ERROR)
{
/*TODO duplicates suppression, if required by application */
PRINTF(" Parsed: v %u, t %u, oc %u, c %u, mid %u\n", message->version, message->type, message->option_count, message->code, message->mid);
PRINTF(" URL: %.*s\n", message->uri_path_len, message->uri_path);
PRINTF(" Payload: %.*s\n", message->payload_len, message->payload);
/* Handle requests. */
if (message->code >= COAP_GET && message->code <= COAP_DELETE)
{
/* Use transaction buffer for response to confirmable request. */
if ( (transaction = coap_new_transaction(message->mid, &UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport)) )
{
uint32_t block_num = 0;
uint16_t block_size = REST_MAX_CHUNK_SIZE;
uint32_t block_offset = 0;
int32_t new_offset = 0;
/* prepare response */
if (message->type==COAP_TYPE_CON)
{
/* Reliable CON requests are answered with an ACK. */
coap_init_message(response, COAP_TYPE_ACK, CONTENT_2_05, message->mid);
}
else
{
/* Unreliable NON requests are answered with a NON as well. */
coap_init_message(response, COAP_TYPE_NON, CONTENT_2_05, coap_get_mid());
}
/* resource handlers must take care of different handling (e.g., TOKEN_OPTION_REQUIRED_240) */
if (IS_OPTION(message, COAP_OPTION_TOKEN))
{
coap_set_header_token(response, message->token, message->token_len);
SET_OPTION(response, COAP_OPTION_TOKEN);
}
/* get offset for blockwise transfers */
if (coap_get_header_block2(message, &block_num, NULL, &block_size, &block_offset))
{
PRINTF("Blockwise: block request %lu (%u/%u) @ %lu bytes\n", block_num, block_size, REST_MAX_CHUNK_SIZE, block_offset);
block_size = MIN(block_size, REST_MAX_CHUNK_SIZE);
new_offset = block_offset;
}
/* Invoke resource handler. */
if (service_cbk)
{
/* Call REST framework and check if found and allowed. */
if (service_cbk(message, response, transaction->packet+COAP_MAX_HEADER_SIZE, block_size, &new_offset))
{
if (coap_error_code==NO_ERROR)
{
/* Apply blockwise transfers. */
if ( IS_OPTION(message, COAP_OPTION_BLOCK1) && response->code<BAD_REQUEST_4_00 && !IS_OPTION(response, COAP_OPTION_BLOCK1) )
{
PRINTF("Block1 NOT IMPLEMENTED\n");
coap_error_code = NOT_IMPLEMENTED_5_01;
coap_error_message = "NoBlock1Support";
}
else if ( IS_OPTION(message, COAP_OPTION_BLOCK2) )
{
/* unchanged new_offset indicates that resource is unaware of blockwise transfer */
if (new_offset==block_offset)
{
PRINTF("Blockwise: unaware resource with payload length %u/%u\n", response->payload_len, block_size);
if (block_offset >= response->payload_len)
{
PRINTF("handle_incoming_data(): block_offset >= response->payload_len\n");
response->code = BAD_OPTION_4_02;
coap_set_payload(response, "BlockOutOfScope", 15); /* a const char str[] and sizeof(str) produces larger code size */
}
else
{
coap_set_header_block2(response, block_num, response->payload_len - block_offset > block_size, block_size);
coap_set_payload(response, response->payload+block_offset, MIN(response->payload_len - block_offset, block_size));
} /* if (valid offset) */
}
else
{
/* resource provides chunk-wise data */
PRINTF("Blockwise: blockwise resource, new offset %ld\n", new_offset);
coap_set_header_block2(response, block_num, new_offset!=-1 || response->payload_len > block_size, block_size);
if (response->payload_len > block_size) coap_set_payload(response, response->payload, block_size);
} /* if (resource aware of blockwise) */
}
else if (new_offset!=0)
{
PRINTF("Blockwise: no block option for blockwise resource, using block size %u\n", REST_MAX_CHUNK_SIZE);
coap_set_header_block2(response, 0, new_offset!=-1, REST_MAX_CHUNK_SIZE);
coap_set_payload(response, response->payload, MIN(response->payload_len, REST_MAX_CHUNK_SIZE));
} /* if (blockwise request) */
} /* no errors/hooks */
} /* successful service callback */
/* Serialize response. */
if (coap_error_code==NO_ERROR)
{
if ((transaction->packet_len = coap_serialize_message(response, transaction->packet))==0)
{
coap_error_code = PACKET_SERIALIZATION_ERROR;
}
}
}
else
{
coap_error_code = NOT_IMPLEMENTED_5_01;
coap_error_message = "NoServiceCallbck"; // no a to fit 16 bytes
} /* if (service callback) */
} else {
coap_error_code = SERVICE_UNAVAILABLE_5_03;
coap_error_message = "NoFreeTraBuffer";
} /* if (transaction buffer) */
}
else
{
/* Responses */
if (message->type==COAP_TYPE_ACK)
{
PRINTF("Received ACK\n");
}
else if (message->type==COAP_TYPE_RST)
{
PRINTF("Received RST\n");
/* Cancel possible subscriptions. */
coap_remove_observer_by_mid(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, message->mid);
}
if ( (transaction = coap_get_transaction_by_mid(message->mid)) )
{
/* Free transaction memory before callback, as it may create a new transaction. */
restful_response_handler callback = transaction->callback;
void *callback_data = transaction->callback_data;
coap_clear_transaction(transaction);
/* Check if someone registered for the response */
if (callback) {
callback(callback_data, message);
}
} /* if (ACKed transaction) */
transaction = NULL;
} /* Request or Response */
} /* if (parsed correctly) */
if (coap_error_code==NO_ERROR)
{
if (transaction) coap_send_transaction(transaction);
}
else if (coap_error_code==MANUAL_RESPONSE)
{
PRINTF("Clearing transaction for manual response");
coap_clear_transaction(transaction);
}
else
{
PRINTF("ERROR %u: %s\n", coap_error_code, coap_error_message);
coap_clear_transaction(transaction);
/* Set to sendable error code. */
if (coap_error_code >= 192)
{
coap_error_code = INTERNAL_SERVER_ERROR_5_00;
}
/* Reuse input buffer for error message. */
coap_init_message(message, COAP_TYPE_ACK, coap_error_code, message->mid);
coap_set_payload(message, coap_error_message, strlen(coap_error_message));
coap_send_message(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, uip_appdata, coap_serialize_message(message, uip_appdata));
}
} /* if (new data) */
return coap_error_code;
}
/*----------------------------------------------------------------------------*/
void
coap_receiver_init()
{
process_start(&coap_receiver, NULL);
}
/*----------------------------------------------------------------------------*/
void
coap_set_service_callback(service_callback_t callback)
{
service_cbk = callback;
}
/*----------------------------------------------------------------------------*/
rest_resource_flags_t
coap_get_rest_method(void *packet)
{
return (rest_resource_flags_t)(1 << (((coap_packet_t *)packet)->code - 1));
}
/*----------------------------------------------------------------------------*/
/*- Server part --------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/* The discover resource is automatically included for CoAP. */
RESOURCE(well_known_core, METHOD_GET, ".well-known/core", "ct=40");
void
well_known_core_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset)
{
size_t strpos = 0; /* position in overall string (which is larger than the buffer) */
size_t bufpos = 0; /* position within buffer (bytes written) */
size_t tmplen = 0;
resource_t* resource = NULL;
#if COAP_LINK_FORMAT_FILTERING
/* For filtering. */
const char *filter = NULL;
const char *attrib = NULL;
const char *found = NULL;
const char *end = NULL;
char *value = NULL;
char lastchar;
int len = coap_get_header_uri_query(request, &filter);
if (len)
{
value = strchr(filter, '=');
value[0] = '\0';
++value;
len -= strlen(filter)+1;
PRINTF("Filter %s = %.*s\n", filter, len, value);
if (strcmp(filter,"href")==0 && value[0]=='/')
{
++value;
--len;
}
lastchar = value[len-1];
value[len-1] = '\0';
}
#endif
for (resource = (resource_t*)list_head(rest_get_resources()); resource; resource = resource->next)
{
#if COAP_LINK_FORMAT_FILTERING
/* Filtering */
if (len)
{
if (strcmp(filter,"href")==0)
{
attrib=strstr(resource->url, value);
if (attrib==NULL || (value[-1]=='/' && attrib!=resource->url)) continue;
end = attrib + strlen(attrib);
}
else
{
attrib=strstr(resource->attributes, filter);
if (attrib==NULL || (attrib[strlen(filter)]!='=' && attrib[strlen(filter)]!='"')) continue;
attrib += strlen(filter)+2;
end = strchr(attrib, '"');
}
PRINTF("Filter: res has attrib %s (%s)\n", attrib, value);
found = attrib;
while ((found=strstr(found, value))!=NULL) {
if (found > end)
{
found = NULL;
break;
}
if (lastchar==found[len-1] || lastchar=='*')
{
break;
}
++found;
}
if (found==NULL)
{
continue;
}
PRINTF("Filter: res has prefix %s\n", found);
if (lastchar!='*' && (found[len]!='"' && found[len]!=' ' && found[len]!='\0')) continue;
PRINTF("Filter: res has match\n");
}
#endif
PRINTF("res: /%s (%p)\npos: s%d, o%d, b%d\n", resource->url, resource, strpos, *offset, bufpos);
if (strpos>0)
{
if (strpos >= *offset && bufpos < preferred_size)
{
buffer[bufpos++] = ',';
}
++strpos;
}
if (strpos >= *offset && bufpos < preferred_size)
{
buffer[bufpos++] = '<';
}
++strpos;
if (strpos >= *offset && bufpos < preferred_size)
{
buffer[bufpos++] = '/';
}
++strpos;
tmplen = strlen(resource->url);
if (strpos+tmplen > *offset)
{
bufpos += snprintf((char *) buffer + bufpos, preferred_size - bufpos + 1,
"%s", resource->url + ((*offset-(int32_t)strpos > 0) ? (*offset-(int32_t)strpos) : 0));
/* minimal-net requires these casts */
if (bufpos >= preferred_size)
{
break;
}
}
strpos += tmplen;
if (strpos >= *offset && bufpos < preferred_size)
{
buffer[bufpos++] = '>';
}
++strpos;
if (resource->attributes[0])
{
if (strpos >= *offset && bufpos < preferred_size)
{
buffer[bufpos++] = ';';
}
++strpos;
tmplen = strlen(resource->attributes);
if (strpos+tmplen > *offset)
{
bufpos += snprintf((char *) buffer + bufpos, preferred_size - bufpos + 1,
"%s", resource->attributes + (*offset-(int32_t)strpos > 0 ? *offset-(int32_t)strpos : 0));
if (bufpos >= preferred_size)
{
break;
}
}
strpos += tmplen;
}
/* buffer full, but resource not completed yet; or: do not break if resource exactly fills buffer. */
if (bufpos >= preferred_size && strpos-bufpos > *offset)
{
PRINTF("res: BREAK at %s (%p)\n", resource->url, resource);
break;
}
}
if (bufpos>0) {
PRINTF("BUF %d: %.*s\n", bufpos, bufpos, (char *) buffer);
coap_set_payload(response, buffer, bufpos );
coap_set_header_content_type(response, APPLICATION_LINK_FORMAT);
}
else if (strpos>0)
{
PRINTF("well_known_core_handler(): bufpos<=0\n");
coap_set_status_code(response, BAD_OPTION_4_02);
coap_set_payload(response, "BlockOutOfScope", 15);
}
if (resource==NULL) {
PRINTF("res: DONE\n");
*offset = -1;
}
else
{
PRINTF("res: MORE at %s (%p)\n", resource->url, resource);
*offset += preferred_size;
}
}
/*----------------------------------------------------------------------------*/
PROCESS_THREAD(coap_receiver, ev, data)
{
PROCESS_BEGIN();
PRINTF("Starting CoAP-12 receiver...\n");
rest_activate_resource(&resource_well_known_core);
coap_register_as_transaction_handler();
coap_init_connection(SERVER_LISTEN_PORT);
while(1) {
PROCESS_YIELD();
if(ev == tcpip_event) {
coap_receive();
} else if (ev == PROCESS_EVENT_TIMER) {
/* retransmissions are handled here */
coap_check_transactions();
}
} /* while (1) */
PROCESS_END();
}
/*----------------------------------------------------------------------------*/
/*- Client part --------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
void coap_blocking_request_callback(void *callback_data, void *response) {
struct request_state_t *state = (struct request_state_t *) callback_data;
state->response = (coap_packet_t*) response;
process_poll(state->process);
}
/*----------------------------------------------------------------------------*/
PT_THREAD(coap_blocking_request(struct request_state_t *state, process_event_t ev,
uip_ipaddr_t *remote_ipaddr, uint16_t remote_port,
coap_packet_t *request,
blocking_response_handler request_callback)) {
PT_BEGIN(&state->pt);
static uint8_t more;
static uint32_t res_block;
static uint8_t block_error;
state->block_num = 0;
state->response = NULL;
state->process = PROCESS_CURRENT();
more = 0;
res_block = 0;
block_error = 0;
do {
request->mid = coap_get_mid();
if ((state->transaction = coap_new_transaction(request->mid, remote_ipaddr, remote_port)))
{
state->transaction->callback = coap_blocking_request_callback;
state->transaction->callback_data = state;
if (state->block_num>0)
{
coap_set_header_block2(request, state->block_num, 0, REST_MAX_CHUNK_SIZE);
}
state->transaction->packet_len = coap_serialize_message(request, state->transaction->packet);
coap_send_transaction(state->transaction);
PRINTF("Requested #%lu (MID %u)\n", state->block_num, request->mid);
PT_YIELD_UNTIL(&state->pt, ev == PROCESS_EVENT_POLL);
if (!state->response)
{
PRINTF("Server not responding\n");
PT_EXIT(&state->pt);
}
coap_get_header_block2(state->response, &res_block, &more, NULL, NULL);
PRINTF("Received #%lu%s (%u bytes)\n", res_block, more ? "+" : "", state->response->payload_len);
if (res_block==state->block_num)
{
request_callback(state->response);
++(state->block_num);
}
else
{
PRINTF("WRONG BLOCK %lu/%lu\n", res_block, state->block_num);
++block_error;
}
}
else
{
PRINTF("Could not allocate transaction buffer");
PT_EXIT(&state->pt);
}
} while (more && block_error<COAP_MAX_ATTEMPTS);
PT_END(&state->pt);
}
/*----------------------------------------------------------------------------*/
/*- Engine Interface ---------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
const struct rest_implementation coap_rest_implementation = {
"CoAP-12",
coap_receiver_init,
coap_set_service_callback,
coap_get_header_uri_path,
coap_set_header_uri_path,
coap_get_rest_method,
coap_set_status_code,
coap_get_header_content_type,
coap_set_header_content_type,
coap_get_header_accept,
coap_get_header_size,
coap_set_header_size,
coap_get_header_max_age,
coap_set_header_max_age,
coap_set_header_etag,
coap_get_header_if_match,
coap_get_header_if_none_match,
coap_get_header_uri_host,
coap_set_header_location_path,
coap_get_payload,
coap_set_payload,
coap_get_header_uri_query,
coap_get_query_variable,
coap_get_post_variable,
coap_notify_observers,
(restful_post_handler) coap_observe_handler,
NULL, /* default pre-handler (set separate handler after activation if needed) */
NULL, /* default post-handler for non-observable resources */
{
CONTENT_2_05,
CREATED_2_01,
CHANGED_2_04,
DELETED_2_02,
VALID_2_03,
BAD_REQUEST_4_00,
UNAUTHORIZED_4_01,
BAD_OPTION_4_02,
FORBIDDEN_4_03,
NOT_FOUND_4_04,
METHOD_NOT_ALLOWED_4_05,
NOT_ACCEPTABLE_4_06,
REQUEST_ENTITY_TOO_LARGE_4_13,
UNSUPPORTED_MEDIA_TYPE_4_15,
INTERNAL_SERVER_ERROR_5_00,
NOT_IMPLEMENTED_5_01,
BAD_GATEWAY_5_02,
SERVICE_UNAVAILABLE_5_03,
GATEWAY_TIMEOUT_5_04,
PROXYING_NOT_SUPPORTED_5_05
},
{
TEXT_PLAIN,
TEXT_XML,
TEXT_CSV,
TEXT_HTML,
IMAGE_GIF,
IMAGE_JPEG,
IMAGE_PNG,
IMAGE_TIFF,
AUDIO_RAW,
VIDEO_RAW,
APPLICATION_LINK_FORMAT,
APPLICATION_XML,
APPLICATION_OCTET_STREAM,
APPLICATION_RDF_XML,
APPLICATION_SOAP_XML,
APPLICATION_ATOM_XML,
APPLICATION_XMPP_XML,
APPLICATION_EXI,
APPLICATION_FASTINFOSET,
APPLICATION_SOAP_FASTINFOSET,
APPLICATION_JSON,
APPLICATION_X_OBIX_BINARY
}
};

View file

@ -1,94 +0,0 @@
/*
* Copyright (c) 2012, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* CoAP implementation of the REST Engine
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#ifndef COAP_SERVER_H_
#define COAP_SERVER_H_
#if !defined(REST)
#error "Define REST to \"coap_rest_implementation\""
#endif
#include "er-coap-12.h"
#include "er-coap-12-transactions.h"
#include "er-coap-12-observing.h"
#include "er-coap-12-separate.h"
#include "pt.h"
/* Declare server process */
PROCESS_NAME(coap_receiver);
#define SERVER_LISTEN_PORT UIP_HTONS(COAP_SERVER_PORT)
typedef coap_packet_t rest_request_t;
typedef coap_packet_t rest_response_t;
extern const struct rest_implementation coap_rest_implementation;
void coap_receiver_init(void);
/*-----------------------------------------------------------------------------------*/
/*- Client part ---------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------*/
struct request_state_t {
struct pt pt;
struct process *process;
coap_transaction_t *transaction;
coap_packet_t *response;
uint32_t block_num;
};
typedef void (*blocking_response_handler) (void* response);
PT_THREAD(coap_blocking_request(struct request_state_t *state, process_event_t ev,
uip_ipaddr_t *remote_ipaddr, uint16_t remote_port,
coap_packet_t *request,
blocking_response_handler request_callback));
#define COAP_BLOCKING_REQUEST(server_addr, server_port, request, chunk_handler) \
{ \
static struct request_state_t request_state; \
PT_SPAWN(process_pt, &request_state.pt, \
coap_blocking_request(&request_state, ev, \
server_addr, server_port, \
request, chunk_handler) \
); \
}
/*-----------------------------------------------------------------------------------*/
#endif /* COAP_SERVER_H_ */

View file

@ -1,255 +0,0 @@
/*
* Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* CoAP module for observing resources
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#include <stdio.h>
#include <string.h>
#include "er-coap-12-observing.h"
#define DEBUG 0
#if DEBUG
#define PRINTF(...) printf(__VA_ARGS__)
#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5])
#else
#define PRINTF(...)
#define PRINT6ADDR(addr)
#define PRINTLLADDR(addr)
#endif
MEMB(observers_memb, coap_observer_t, COAP_MAX_OBSERVERS);
LIST(observers_list);
/*-----------------------------------------------------------------------------------*/
coap_observer_t *
coap_add_observer(uip_ipaddr_t *addr, uint16_t port, const uint8_t *token, size_t token_len, const char *url)
{
/* Remove existing observe relationship, if any. */
coap_remove_observer_by_url(addr, port, url);
coap_observer_t *o = memb_alloc(&observers_memb);
if (o)
{
o->url = url;
uip_ipaddr_copy(&o->addr, addr);
o->port = port;
o->token_len = token_len;
memcpy(o->token, token, token_len);
o->last_mid = 0;
stimer_set(&o->refresh_timer, COAP_OBSERVING_REFRESH_INTERVAL);
PRINTF("Adding observer for /%s [0x%02X%02X]\n", o->url, o->token[0], o->token[1]);
list_add(observers_list, o);
}
return o;
}
/*-----------------------------------------------------------------------------------*/
void
coap_remove_observer(coap_observer_t *o)
{
PRINTF("Removing observer for /%s [0x%02X%02X]\n", o->url, o->token[0], o->token[1]);
memb_free(&observers_memb, o);
list_remove(observers_list, o);
}
int
coap_remove_observer_by_client(uip_ipaddr_t *addr, uint16_t port)
{
int removed = 0;
coap_observer_t* obs = NULL;
for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next)
{
PRINTF("Remove check client ");
PRINT6ADDR(addr);
PRINTF(":%u\n", port);
if (uip_ipaddr_cmp(&obs->addr, addr) && obs->port==port)
{
coap_remove_observer(obs);
removed++;
}
}
return removed;
}
int
coap_remove_observer_by_token(uip_ipaddr_t *addr, uint16_t port, uint8_t *token, size_t token_len)
{
int removed = 0;
coap_observer_t* obs = NULL;
for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next)
{
PRINTF("Remove check Token 0x%02X%02X\n", token[0], token[1]);
if (uip_ipaddr_cmp(&obs->addr, addr) && obs->port==port && obs->token_len==token_len && memcmp(obs->token, token, token_len)==0)
{
coap_remove_observer(obs);
removed++;
}
}
return removed;
}
int
coap_remove_observer_by_url(uip_ipaddr_t *addr, uint16_t port, const char *url)
{
int removed = 0;
coap_observer_t* obs = NULL;
for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next)
{
PRINTF("Remove check URL %p\n", url);
if ((addr==NULL || (uip_ipaddr_cmp(&obs->addr, addr) && obs->port==port)) && (obs->url==url || memcmp(obs->url, url, strlen(obs->url))==0))
{
coap_remove_observer(obs);
removed++;
}
}
return removed;
}
int
coap_remove_observer_by_mid(uip_ipaddr_t *addr, uint16_t port, uint16_t mid)
{
int removed = 0;
coap_observer_t* obs = NULL;
for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next)
{
PRINTF("Remove check MID %u\n", mid);
if (uip_ipaddr_cmp(&obs->addr, addr) && obs->port==port && obs->last_mid==mid)
{
coap_remove_observer(obs);
removed++;
}
}
return removed;
}
/*-----------------------------------------------------------------------------------*/
void
coap_notify_observers(resource_t *resource, int32_t obs_counter, void *notification)
{
coap_packet_t *const coap_res = (coap_packet_t *) notification;
coap_observer_t* obs = NULL;
uint8_t preferred_type = coap_res->type;
PRINTF("Observing: Notification from %s\n", resource->url);
/* Iterate over observers. */
for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next)
{
if (obs->url==resource->url) /* using RESOURCE url pointer as handle */
{
coap_transaction_t *transaction = NULL;
/*TODO implement special transaction for CON, sharing the same buffer to allow for more observers. */
if ( (transaction = coap_new_transaction(coap_get_mid(), &obs->addr, obs->port)) )
{
PRINTF(" Observer ");
PRINT6ADDR(&obs->addr);
PRINTF(":%u\n", obs->port);
/* Update last MID for RST matching. */
obs->last_mid = transaction->mid;
/* Prepare response */
coap_res->mid = transaction->mid;
if (obs_counter>=0) coap_set_header_observe(coap_res, obs_counter);
coap_set_header_token(coap_res, obs->token, obs->token_len);
/* Use CON to check whether client is still there/interested after COAP_OBSERVING_REFRESH_INTERVAL. */
if (stimer_expired(&obs->refresh_timer))
{
PRINTF(" Refreshing with CON\n");
coap_res->type = COAP_TYPE_CON;
stimer_restart(&obs->refresh_timer);
}
else
{
coap_res->type = preferred_type;
}
transaction->packet_len = coap_serialize_message(coap_res, transaction->packet);
coap_send_transaction(transaction);
}
}
}
}
/*-----------------------------------------------------------------------------------*/
void
coap_observe_handler(resource_t *resource, void *request, void *response)
{
coap_packet_t *const coap_req = (coap_packet_t *) request;
coap_packet_t *const coap_res = (coap_packet_t *) response;
static char content[16];
if (coap_req->code==COAP_GET && coap_res->code<128) /* GET request and response without error code */
{
if (IS_OPTION(coap_req, COAP_OPTION_OBSERVE))
{
if (coap_add_observer(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, coap_req->token, coap_req->token_len, resource->url))
{
coap_set_header_observe(coap_res, 0);
/*
* For demonstration purposes only. A subscription should return the same representation as a normal GET.
* TODO: Comment the following line for any real application.
*/
coap_set_payload(coap_res, content, snprintf(content, sizeof(content), "Added %u/%u", list_length(observers_list), COAP_MAX_OBSERVERS));
}
else
{
coap_res->code = SERVICE_UNAVAILABLE_5_03;
coap_set_payload(coap_res, "TooManyObservers", 16);
} /* if (added observer) */
}
else /* if (observe) */
{
/* Remove client if it is currently observing. */
coap_remove_observer_by_url(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, resource->url);
} /* if (observe) */
}
}

View file

@ -1,83 +0,0 @@
/*
* Copyright (c) 2012, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* CoAP module for observing resources
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#ifndef COAP_OBSERVING_H_
#define COAP_OBSERVING_H_
#include "sys/stimer.h"
#include "er-coap-12.h"
#include "er-coap-12-transactions.h"
#ifndef COAP_MAX_OBSERVERS
#define COAP_MAX_OBSERVERS COAP_MAX_OPEN_TRANSACTIONS-1
#endif /* COAP_MAX_OBSERVERS */
/* Interval in seconds in which NON notifies are changed to CON notifies to check client. */
#define COAP_OBSERVING_REFRESH_INTERVAL 60
#if COAP_MAX_OPEN_TRANSACTIONS<COAP_MAX_OBSERVERS
#warning "COAP_MAX_OPEN_TRANSACTIONS smaller than COAP_MAX_OBSERVERS: cannot handle CON notifications"
#endif
typedef struct coap_observer {
struct coap_observer *next; /* for LIST */
const char *url;
uip_ipaddr_t addr;
uint16_t port;
uint8_t token_len;
uint8_t token[COAP_TOKEN_LEN];
uint16_t last_mid;
struct stimer refresh_timer;
} coap_observer_t;
list_t coap_get_observers(void);
coap_observer_t *coap_add_observer(uip_ipaddr_t *addr, uint16_t port, const uint8_t *token, size_t token_len, const char *url);
void coap_remove_observer(coap_observer_t *o);
int coap_remove_observer_by_client(uip_ipaddr_t *addr, uint16_t port);
int coap_remove_observer_by_token(uip_ipaddr_t *addr, uint16_t port, uint8_t *token, size_t token_len);
int coap_remove_observer_by_url(uip_ipaddr_t *addr, uint16_t port, const char *url);
int coap_remove_observer_by_mid(uip_ipaddr_t *addr, uint16_t port, uint16_t mid);
void coap_notify_observers(resource_t *resource, int32_t obs_counter, void *notification);
void coap_observe_handler(resource_t *resource, void *request, void *response);
#endif /* COAP_OBSERVING_H_ */

View file

@ -1,117 +0,0 @@
/*
* Copyright (c) 2012, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* CoAP module for separate responses
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#include <stdio.h>
#include <string.h>
#include "er-coap-12-separate.h"
#include "er-coap-12-transactions.h"
#define DEBUG 0
#if DEBUG
#define PRINTF(...) printf(__VA_ARGS__)
#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5])
#else
#define PRINTF(...)
#define PRINT6ADDR(addr)
#define PRINTLLADDR(addr)
#endif
/*----------------------------------------------------------------------------*/
void
coap_separate_reject()
{
coap_error_code = SERVICE_UNAVAILABLE_5_03;
coap_error_message = "AlreadyInUse";
}
/*----------------------------------------------------------------------------*/
int
coap_separate_accept(void *request, coap_separate_t *separate_store)
{
coap_packet_t *const coap_req = (coap_packet_t *) request;
coap_transaction_t *const t = coap_get_transaction_by_mid(coap_req->mid);
PRINTF("Separate ACCEPT: /%.*s MID %u\n", coap_req->uri_path_len, coap_req->uri_path, coap_req->mid);
if (t)
{
/* Send separate ACK for CON. */
if (coap_req->type==COAP_TYPE_CON)
{
coap_packet_t ack[1];
/* ACK with empty code (0) */
coap_init_message(ack, COAP_TYPE_ACK, 0, coap_req->mid);
/* Serializing into IPBUF: Only overwrites header parts that are already parsed into the request struct. */
coap_send_message(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, (uip_appdata), coap_serialize_message(ack, uip_appdata));
}
/* Store remote address. */
uip_ipaddr_copy(&separate_store->addr, &t->addr);
separate_store->port = t->port;
/* Store correct response type. */
separate_store->type = coap_req->type==COAP_TYPE_CON ? COAP_TYPE_CON : COAP_TYPE_NON;
separate_store->mid = coap_get_mid(); /* if it was a NON, we burned one MID in the engine... */
memcpy(separate_store->token, coap_req->token, coap_req->token_len);
separate_store->token_len = coap_req->token_len;
separate_store->block2_num = coap_req->block2_num;
separate_store->block2_size = coap_req->block2_size;
/* Signal the engine to skip automatic response and clear transaction by engine. */
coap_error_code = MANUAL_RESPONSE;
return 1;
}
else
{
PRINTF("ERROR: Response transaction for separate request not found!\n");
return 0;
}
}
/*----------------------------------------------------------------------------*/
void
coap_separate_resume(void *response, coap_separate_t *separate_store, uint8_t code)
{
coap_init_message(response, separate_store->type, code, separate_store->mid);
if (separate_store->token_len)
{
coap_set_header_token(response, separate_store->token, separate_store->token_len);
}
}

View file

@ -1,199 +0,0 @@
/*
* Copyright (c) 2012, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* CoAP module for reliable transport
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#include "contiki.h"
#include "contiki-net.h"
#include "er-coap-12-transactions.h"
#include "er-coap-12-observing.h"
/*
* Modulo mask (+1 and +0.5 for rounding) for a random number to get the tick number for the random
* retransmission time between COAP_RESPONSE_TIMEOUT and COAP_RESPONSE_TIMEOUT*COAP_RESPONSE_RANDOM_FACTOR.
*/
#define COAP_RESPONSE_TIMEOUT_TICKS (CLOCK_SECOND * COAP_RESPONSE_TIMEOUT)
#define COAP_RESPONSE_TIMEOUT_BACKOFF_MASK ((CLOCK_SECOND * COAP_RESPONSE_TIMEOUT * (COAP_RESPONSE_RANDOM_FACTOR - 1)) + 1.5)
#define DEBUG 0
#if DEBUG
#include <stdio.h>
#define PRINTF(...) printf(__VA_ARGS__)
#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5])
#else
#define PRINTF(...)
#define PRINT6ADDR(addr)
#define PRINTLLADDR(addr)
#endif
MEMB(transactions_memb, coap_transaction_t, COAP_MAX_OPEN_TRANSACTIONS);
LIST(transactions_list);
static struct process *transaction_handler_process = NULL;
void
coap_register_as_transaction_handler()
{
transaction_handler_process = PROCESS_CURRENT();
}
coap_transaction_t *
coap_new_transaction(uint16_t mid, uip_ipaddr_t *addr, uint16_t port)
{
coap_transaction_t *t = memb_alloc(&transactions_memb);
if (t)
{
t->mid = mid;
t->retrans_counter = 0;
/* save client address */
uip_ipaddr_copy(&t->addr, addr);
t->port = port;
list_add(transactions_list, t); /* List itself makes sure same element is not added twice. */
}
return t;
}
void
coap_send_transaction(coap_transaction_t *t)
{
PRINTF("Sending transaction %u\n", t->mid);
coap_send_message(&t->addr, t->port, t->packet, t->packet_len);
if (COAP_TYPE_CON==((COAP_HEADER_TYPE_MASK & t->packet[0])>>COAP_HEADER_TYPE_POSITION))
{
if (t->retrans_counter<COAP_MAX_RETRANSMIT)
{
/* Not timed out yet. */
PRINTF("Keeping transaction %u\n", t->mid);
if (t->retrans_counter==0)
{
t->retrans_timer.timer.interval = COAP_RESPONSE_TIMEOUT_TICKS + (random_rand() % (clock_time_t) COAP_RESPONSE_TIMEOUT_BACKOFF_MASK);
PRINTF("Initial interval %f\n", (float)t->retrans_timer.timer.interval/CLOCK_SECOND);
}
else
{
t->retrans_timer.timer.interval <<= 1; /* double */
PRINTF("Doubled (%u) interval %f\n", t->retrans_counter, (float)t->retrans_timer.timer.interval/CLOCK_SECOND);
}
/*FIXME
* Hack: Setting timer for responsible process.
* Maybe there is a better way, but avoid posting everything to the process.
*/
struct process *process_actual = PROCESS_CURRENT();
process_current = transaction_handler_process;
etimer_restart(&t->retrans_timer); /* interval updated above */
process_current = process_actual;
t = NULL;
}
else
{
/* Timed out. */
PRINTF("Timeout\n");
restful_response_handler callback = t->callback;
void *callback_data = t->callback_data;
/* handle observers */
coap_remove_observer_by_client(&t->addr, t->port);
coap_clear_transaction(t);
if (callback) {
callback(callback_data, NULL);
}
}
}
else
{
coap_clear_transaction(t);
}
}
void
coap_clear_transaction(coap_transaction_t *t)
{
if (t)
{
PRINTF("Freeing transaction %u: %p\n", t->mid, t);
etimer_stop(&t->retrans_timer);
list_remove(transactions_list, t);
memb_free(&transactions_memb, t);
}
}
coap_transaction_t *
coap_get_transaction_by_mid(uint16_t mid)
{
coap_transaction_t *t = NULL;
for (t = (coap_transaction_t*)list_head(transactions_list); t; t = t->next)
{
if (t->mid==mid)
{
PRINTF("Found transaction for MID %u: %p\n", t->mid, t);
return t;
}
}
return NULL;
}
void
coap_check_transactions()
{
coap_transaction_t *t = NULL;
for (t = (coap_transaction_t*)list_head(transactions_list); t; t = t->next)
{
if (etimer_expired(&t->retrans_timer))
{
++(t->retrans_counter);
PRINTF("Retransmitting %u (%u)\n", t->mid, t->retrans_counter);
coap_send_transaction(t);
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,390 +0,0 @@
/*
* Copyright (c) 2012, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* An implementation of the Constrained Application Protocol (draft 12)
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#ifndef COAP_12_H_
#define COAP_12_H_
#include <stddef.h> /* for size_t */
#include "contiki-net.h"
#include "erbium.h"
#define COAP_LINK_FORMAT_FILTERING 1
#define COAP_DEFAULT_PORT 5683
#ifndef COAP_SERVER_PORT
#define COAP_SERVER_PORT COAP_DEFAULT_PORT
#endif
#define COAP_DEFAULT_MAX_AGE 60
#define COAP_RESPONSE_TIMEOUT 2
#define COAP_RESPONSE_RANDOM_FACTOR 1.5
#define COAP_MAX_RETRANSMIT 4
#define COAP_HEADER_LEN 4 /* | oc:0xF0 type:0x0C version:0x03 | code | mid:0x00FF | mid:0xFF00 | */
#define COAP_ETAG_LEN 8 /* The maximum number of bytes for the ETag */
#define COAP_TOKEN_LEN 8 /* The maximum number of bytes for the Token */
#define COAP_MAX_ACCEPT_NUM 2 /* The maximum number of accept preferences to parse/store */
#define COAP_HEADER_VERSION_MASK 0xC0
#define COAP_HEADER_VERSION_POSITION 6
#define COAP_HEADER_TYPE_MASK 0x30
#define COAP_HEADER_TYPE_POSITION 4
#define COAP_HEADER_OPTION_COUNT_MASK 0x0F
#define COAP_HEADER_OPTION_COUNT_POSITION 0
#define COAP_HEADER_OPTION_DELTA_MASK 0xF0
#define COAP_HEADER_OPTION_SHORT_LENGTH_MASK 0x0F
/*
* Conservative size limit, as not all options have to be set at the same time.
*/
#ifndef COAP_MAX_HEADER_SIZE
/* Hdr CoT Age Tag Obs Tok Blo strings */
#define COAP_MAX_HEADER_SIZE (4 + 3 + 5 + 1+COAP_ETAG_LEN + 3 + 1+COAP_TOKEN_LEN + 4 + 30) /* 70 */
#endif /* COAP_MAX_HEADER_SIZE */
#define COAP_MAX_PACKET_SIZE (COAP_MAX_HEADER_SIZE + REST_MAX_CHUNK_SIZE)
/* 0/14 48 for IPv6 (28 for IPv4) */
#if COAP_MAX_PACKET_SIZE > (UIP_BUFSIZE - UIP_LLH_LEN - UIP_IPUDPH_LEN)
#error "UIP_CONF_BUFFER_SIZE too small for REST_MAX_CHUNK_SIZE"
#endif
/*
* Maximum number of failed request attempts before action
*/
#ifndef COAP_MAX_ATTEMPTS
#define COAP_MAX_ATTEMPTS 4
#endif /* COAP_MAX_ATTEMPTS */
#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN])
#define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[UIP_LLH_LEN + UIP_IPH_LEN])
/* Bitmap for set options */
enum { OPTION_MAP_SIZE = sizeof(uint8_t) * 8 };
#define SET_OPTION(packet, opt) ((packet)->options[opt / OPTION_MAP_SIZE] |= 1 << (opt % OPTION_MAP_SIZE))
#define IS_OPTION(packet, opt) ((packet)->options[opt / OPTION_MAP_SIZE] & (1 << (opt % OPTION_MAP_SIZE)))
#ifndef MIN
#define MIN(a, b) ((a) < (b)? (a) : (b))
#endif /* MIN */
/* CoAP message types */
typedef enum {
COAP_TYPE_CON, /* confirmables */
COAP_TYPE_NON, /* non-confirmables */
COAP_TYPE_ACK, /* acknowledgements */
COAP_TYPE_RST /* reset */
} coap_message_type_t;
/* CoAP request method codes */
typedef enum {
COAP_GET = 1,
COAP_POST,
COAP_PUT,
COAP_DELETE
} coap_method_t;
/* CoAP response codes */
typedef enum {
NO_ERROR = 0,
CREATED_2_01 = 65, /* CREATED */
DELETED_2_02 = 66, /* DELETED */
VALID_2_03 = 67, /* NOT_MODIFIED */
CHANGED_2_04 = 68, /* CHANGED */
CONTENT_2_05 = 69, /* OK */
BAD_REQUEST_4_00 = 128, /* BAD_REQUEST */
UNAUTHORIZED_4_01 = 129, /* UNAUTHORIZED */
BAD_OPTION_4_02 = 130, /* BAD_OPTION */
FORBIDDEN_4_03 = 131, /* FORBIDDEN */
NOT_FOUND_4_04 = 132, /* NOT_FOUND */
METHOD_NOT_ALLOWED_4_05 = 133, /* METHOD_NOT_ALLOWED */
NOT_ACCEPTABLE_4_06 = 134, /* NOT_ACCEPTABLE */
PRECONDITION_FAILED_4_12 = 140, /* BAD_REQUEST */
REQUEST_ENTITY_TOO_LARGE_4_13 = 141, /* REQUEST_ENTITY_TOO_LARGE */
UNSUPPORTED_MEDIA_TYPE_4_15 = 143, /* UNSUPPORTED_MEDIA_TYPE */
INTERNAL_SERVER_ERROR_5_00 = 160, /* INTERNAL_SERVER_ERROR */
NOT_IMPLEMENTED_5_01 = 161, /* NOT_IMPLEMENTED */
BAD_GATEWAY_5_02 = 162, /* BAD_GATEWAY */
SERVICE_UNAVAILABLE_5_03 = 163, /* SERVICE_UNAVAILABLE */
GATEWAY_TIMEOUT_5_04 = 164, /* GATEWAY_TIMEOUT */
PROXYING_NOT_SUPPORTED_5_05 = 165, /* PROXYING_NOT_SUPPORTED */
/* Erbium errors */
MEMORY_ALLOCATION_ERROR = 192,
PACKET_SERIALIZATION_ERROR,
/* Erbium hooks */
MANUAL_RESPONSE
} coap_status_t;
/* CoAP header options */
typedef enum {
COAP_OPTION_IF_MATCH = 1, /* 0-8 B */
COAP_OPTION_URI_HOST = 3, /* 1-255 B */
COAP_OPTION_ETAG = 4, /* 1-8 B */
COAP_OPTION_IF_NONE_MATCH = 5, /* 0 B */
COAP_OPTION_OBSERVE = 6, /* 0-3 B */
COAP_OPTION_URI_PORT = 7, /* 0-2 B */
COAP_OPTION_LOCATION_PATH = 8, /* 0-255 B */
COAP_OPTION_URI_PATH = 11, /* 0-255 B */
COAP_OPTION_CONTENT_TYPE = 12, /* 0-2 B */
COAP_OPTION_MAX_AGE = 14, /* 0-4 B */
COAP_OPTION_URI_QUERY = 15, /* 0-270 B */
COAP_OPTION_ACCEPT = 16, /* 0-2 B */
COAP_OPTION_TOKEN = 19, /* 1-8 B */
COAP_OPTION_LOCATION_QUERY = 20, /* 1-270 B */
COAP_OPTION_BLOCK2 = 23, /* 1-3 B */
COAP_OPTION_BLOCK1 = 27, /* 1-3 B */
COAP_OPTION_SIZE = 28, /* 0-4 B */
COAP_OPTION_PROXY_URI = 35, /* 1-270 B */
} coap_option_t;
/* CoAP Content-Types */
typedef enum {
TEXT_PLAIN = 0,
TEXT_XML = 1, /* Indented types are not in the initial registry. */
TEXT_CSV = 2,
TEXT_HTML = 3,
IMAGE_GIF = 21,
IMAGE_JPEG = 22,
IMAGE_PNG = 23,
IMAGE_TIFF = 24,
AUDIO_RAW = 25,
VIDEO_RAW = 26,
APPLICATION_LINK_FORMAT = 40,
APPLICATION_XML = 41,
APPLICATION_OCTET_STREAM = 42,
APPLICATION_RDF_XML = 43,
APPLICATION_SOAP_XML = 44,
APPLICATION_ATOM_XML = 45,
APPLICATION_XMPP_XML = 46,
APPLICATION_EXI = 47,
APPLICATION_FASTINFOSET = 48,
APPLICATION_SOAP_FASTINFOSET = 49,
APPLICATION_JSON = 50,
APPLICATION_X_OBIX_BINARY = 51
} coap_content_type_t;
/* Parsed message struct */
typedef struct {
uint8_t *buffer; /* pointer to CoAP header / incoming packet buffer / memory to serialize packet */
uint8_t version;
coap_message_type_t type;
uint8_t option_count;
uint8_t code;
uint16_t mid;
uint8_t options[COAP_OPTION_PROXY_URI / OPTION_MAP_SIZE + 1]; /* Bitmap to check if option is set */
coap_content_type_t content_type; /* Parse options once and store; allows setting options in random order */
uint32_t max_age;
size_t proxy_uri_len;
const char *proxy_uri;
uint8_t etag_len;
uint8_t etag[COAP_ETAG_LEN];
size_t uri_host_len;
const char *uri_host;
size_t location_path_len;
const char *location_path;
uint16_t uri_port;
size_t location_query_len;
const char *location_query;
size_t uri_path_len;
const char *uri_path;
uint16_t observe;
uint8_t token_len;
uint8_t token[COAP_TOKEN_LEN];
uint8_t accept_num;
uint16_t accept[COAP_MAX_ACCEPT_NUM];
uint8_t if_match_len;
uint8_t if_match[COAP_ETAG_LEN];
uint32_t block2_num;
uint8_t block2_more;
uint16_t block2_size;
uint32_t block2_offset;
uint32_t block1_num;
uint8_t block1_more;
uint16_t block1_size;
uint32_t block1_offset;
uint32_t size;
size_t uri_query_len;
const char *uri_query;
uint8_t if_none_match;
uint16_t payload_len;
uint8_t *payload;
} coap_packet_t;
/* Option format serialization*/
#define COAP_SERIALIZE_INT_OPTION(number, field, text) \
if (IS_OPTION(coap_pkt, number)) { \
PRINTF(text" [%u]\n", coap_pkt->field); \
option += coap_serialize_int_option(number, current_number, option, coap_pkt->field); \
coap_pkt->option_count += 1; \
current_number = number; \
}
#define COAP_SERIALIZE_BYTE_OPTION(number, field, text) \
if (IS_OPTION(coap_pkt, number)) { \
PRINTF(text" %u [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", coap_pkt->field##_len, \
coap_pkt->field[0], \
coap_pkt->field[1], \
coap_pkt->field[2], \
coap_pkt->field[3], \
coap_pkt->field[4], \
coap_pkt->field[5], \
coap_pkt->field[6], \
coap_pkt->field[7] \
); /*FIXME always prints 8 bytes */ \
uint8_t split_options = '\0'; \
option += coap_serialize_array_option(number, current_number, option, coap_pkt->field, coap_pkt->field##_len, &split_options); \
coap_pkt->option_count += split_options; \
current_number = number; \
}
#define COAP_SERIALIZE_STRING_OPTION(number, field, splitter, text) \
if (IS_OPTION(coap_pkt, number)) { \
PRINTF(text" [%.*s]\n", coap_pkt->field##_len, coap_pkt->field); \
uint8_t split_options = splitter; \
option += coap_serialize_array_option(number, current_number, option, (uint8_t *) coap_pkt->field, coap_pkt->field##_len, &split_options); \
coap_pkt->option_count += split_options; \
current_number = number; \
}
#define COAP_SERIALIZE_ACCEPT_OPTION(number, field, text) \
if (IS_OPTION(coap_pkt, number)) { \
int i; \
for (i=0; i<coap_pkt->field##_num; ++i) \
{ \
PRINTF(text" [%u]\n", coap_pkt->field[i]); \
option += coap_serialize_int_option(number, current_number, option, coap_pkt->field[i]); \
coap_pkt->option_count += 1; \
current_number = number; \
} \
}
#define COAP_SERIALIZE_BLOCK_OPTION(number, field, text) \
if (IS_OPTION(coap_pkt, number)) \
{ \
PRINTF(text" [%lu%s (%u B/blk)]\n", coap_pkt->field##_num, coap_pkt->field##_more ? "+" : "", coap_pkt->field##_size); \
uint32_t block = coap_pkt->field##_num << 4; \
if (coap_pkt->field##_more) block |= 0x8; \
block |= 0xF & coap_log_2(coap_pkt->field##_size/16); \
PRINTF(text" encoded: 0x%lX\n", block); \
option += coap_serialize_int_option(number, current_number, option, block); \
coap_pkt->option_count += 1; \
current_number = number; \
}
/* To store error code and human-readable payload */
extern coap_status_t coap_error_code;
extern char *coap_error_message;
void coap_init_connection(uint16_t port);
uint16_t coap_get_mid(void);
void coap_init_message(void *packet, coap_message_type_t type, uint8_t code, uint16_t mid);
size_t coap_serialize_message(void *packet, uint8_t *buffer);
void coap_send_message(uip_ipaddr_t *addr, uint16_t port, uint8_t *data, uint16_t length);
coap_status_t coap_parse_message(void *request, uint8_t *data, uint16_t data_len);
int coap_get_query_variable(void *packet, const char *name, const char **output);
int coap_get_post_variable(void *packet, const char *name, const char **output);
/*-----------------------------------------------------------------------------------*/
int coap_set_status_code(void *packet, unsigned int code);
unsigned int coap_get_header_content_type(void *packet);
int coap_set_header_content_type(void *packet, unsigned int content_type);
int coap_get_header_accept(void *packet, const uint16_t **accept);
int coap_set_header_accept(void *packet, uint16_t accept);
int coap_get_header_max_age(void *packet, uint32_t *age);
int coap_set_header_max_age(void *packet, uint32_t age);
int coap_get_header_etag(void *packet, const uint8_t **etag);
int coap_set_header_etag(void *packet, const uint8_t *etag, size_t etag_len);
int coap_get_header_if_match(void *packet, const uint8_t **etag);
int coap_set_header_if_match(void *packet, const uint8_t *etag, size_t etag_len);
int coap_get_header_if_none_match(void *packet);
int coap_set_header_if_none_match(void *packet);
int coap_get_header_token(void *packet, const uint8_t **token);
int coap_set_header_token(void *packet, const uint8_t *token, size_t token_len);
int coap_get_header_proxy_uri(void *packet, const char **uri); /* In-place string might not be 0-terminated. */
int coap_set_header_proxy_uri(void *packet, const char *uri);
int coap_get_header_uri_host(void *packet, const char **host); /* In-place string might not be 0-terminated. */
int coap_set_header_uri_host(void *packet, const char *host);
int coap_get_header_uri_path(void *packet, const char **path); /* In-place string might not be 0-terminated. */
int coap_set_header_uri_path(void *packet, const char *path);
int coap_get_header_uri_query(void *packet, const char **query); /* In-place string might not be 0-terminated. */
int coap_set_header_uri_query(void *packet, const char *query);
int coap_get_header_location_path(void *packet, const char **path); /* In-place string might not be 0-terminated. */
int coap_set_header_location_path(void *packet, const char *path); /* Also splits optional query into Location-Query option. */
int coap_get_header_location_query(void *packet, const char **query); /* In-place string might not be 0-terminated. */
int coap_set_header_location_query(void *packet, const char *query);
int coap_get_header_observe(void *packet, uint32_t *observe);
int coap_set_header_observe(void *packet, uint32_t observe);
int coap_get_header_block2(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset);
int coap_set_header_block2(void *packet, uint32_t num, uint8_t more, uint16_t size);
int coap_get_header_block1(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset);
int coap_set_header_block1(void *packet, uint32_t num, uint8_t more, uint16_t size);
int coap_get_header_size(void *packet, uint32_t *size);
int coap_set_header_size(void *packet, uint32_t size);
int coap_get_payload(void *packet, const uint8_t **payload);
int coap_set_payload(void *packet, const void *payload, size_t length);
#endif /* COAP_12_H_ */

View file

@ -1 +0,0 @@
er-coap-13_src = er-coap-13.c er-coap-13-engine.c er-coap-13-transactions.c er-coap-13-observing.c er-coap-13-separate.c

View file

@ -1,681 +0,0 @@
/*
* Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* CoAP implementation of the REST Engine
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "contiki.h"
#include "contiki-net.h"
#include "er-coap-13-engine.h"
#define DEBUG 0
#if DEBUG
#define PRINTF(...) printf(__VA_ARGS__)
#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
#define PRINTLLADDR(lladdr) PRINTF(" %02x:%02x:%02x:%02x:%02x:%02x ",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5])
#define PRINTBITS(buf,len) { \
int i,j=0; \
for (i=0; i<len; ++i) { \
for (j=7; j>=0; --j) { \
PRINTF("%c", (((char *)buf)[i] & 1<<j) ? '1' : '0'); \
} \
PRINTF(" "); \
} \
}
#else
#define PRINTF(...)
#define PRINT6ADDR(addr)
#define PRINTLLADDR(addr)
#define PRINTBITS(buf,len)
#endif
PROCESS(coap_receiver, "CoAP Receiver");
/*----------------------------------------------------------------------------*/
/*- Variables ----------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
static service_callback_t service_cbk = NULL;
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
static
int
coap_receive(void)
{
coap_error_code = NO_ERROR;
PRINTF("handle_incoming_data(): received uip_datalen=%u \n",(uint16_t)uip_datalen());
/* Static declaration reduces stack peaks and program code size. */
static coap_packet_t message[1]; /* This way the packet can be treated as pointer as usual. */
static coap_packet_t response[1];
static coap_transaction_t *transaction = NULL;
if (uip_newdata()) {
PRINTF("receiving UDP datagram from: ");
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
PRINTF(":%u\n Length: %u\n Data: ", uip_ntohs(UIP_UDP_BUF->srcport), uip_datalen() );
PRINTBITS(uip_appdata, uip_datalen());
PRINTF("\n");
coap_error_code = coap_parse_message(message, uip_appdata, uip_datalen());
if (coap_error_code==NO_ERROR)
{
/*TODO duplicates suppression, if required by application */
PRINTF(" Parsed: v %u, t %u, tkl %u, c %u, mid %u\n", message->version, message->type, message->token_len, message->code, message->mid);
PRINTF(" URL: %.*s\n", message->uri_path_len, message->uri_path);
PRINTF(" Payload: %.*s\n", message->payload_len, message->payload);
/* Handle requests. */
if (message->code >= COAP_GET && message->code <= COAP_DELETE)
{
/* Use transaction buffer for response to confirmable request. */
if ( (transaction = coap_new_transaction(message->mid, &UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport)) )
{
uint32_t block_num = 0;
uint16_t block_size = REST_MAX_CHUNK_SIZE;
uint32_t block_offset = 0;
int32_t new_offset = 0;
/* prepare response */
if (message->type==COAP_TYPE_CON)
{
/* Reliable CON requests are answered with an ACK. */
coap_init_message(response, COAP_TYPE_ACK, CONTENT_2_05, message->mid);
}
else
{
/* Unreliable NON requests are answered with a NON as well. */
coap_init_message(response, COAP_TYPE_NON, CONTENT_2_05, coap_get_mid());
}
/* mirror token */
if (message->token_len)
{
coap_set_header_token(response, message->token, message->token_len);
}
/* get offset for blockwise transfers */
if (coap_get_header_block2(message, &block_num, NULL, &block_size, &block_offset))
{
PRINTF("Blockwise: block request %lu (%u/%u) @ %lu bytes\n", block_num, block_size, REST_MAX_CHUNK_SIZE, block_offset);
block_size = MIN(block_size, REST_MAX_CHUNK_SIZE);
new_offset = block_offset;
}
/* Invoke resource handler. */
if (service_cbk)
{
/* Call REST framework and check if found and allowed. */
if (service_cbk(message, response, transaction->packet+COAP_MAX_HEADER_SIZE, block_size, &new_offset))
{
if (coap_error_code==NO_ERROR)
{
/* Apply blockwise transfers. */
if ( IS_OPTION(message, COAP_OPTION_BLOCK1) && response->code<BAD_REQUEST_4_00 && !IS_OPTION(response, COAP_OPTION_BLOCK1) )
{
PRINTF("Block1 NOT IMPLEMENTED\n");
coap_error_code = NOT_IMPLEMENTED_5_01;
coap_error_message = "NoBlock1Support";
}
else if ( IS_OPTION(message, COAP_OPTION_BLOCK2) )
{
/* unchanged new_offset indicates that resource is unaware of blockwise transfer */
if (new_offset==block_offset)
{
PRINTF("Blockwise: unaware resource with payload length %u/%u\n", response->payload_len, block_size);
if (block_offset >= response->payload_len)
{
PRINTF("handle_incoming_data(): block_offset >= response->payload_len\n");
response->code = BAD_OPTION_4_02;
coap_set_payload(response, "BlockOutOfScope", 15); /* a const char str[] and sizeof(str) produces larger code size */
}
else
{
coap_set_header_block2(response, block_num, response->payload_len - block_offset > block_size, block_size);
coap_set_payload(response, response->payload+block_offset, MIN(response->payload_len - block_offset, block_size));
} /* if (valid offset) */
}
else
{
/* resource provides chunk-wise data */
PRINTF("Blockwise: blockwise resource, new offset %ld\n", new_offset);
coap_set_header_block2(response, block_num, new_offset!=-1 || response->payload_len > block_size, block_size);
if (response->payload_len > block_size) coap_set_payload(response, response->payload, block_size);
} /* if (resource aware of blockwise) */
}
else if (new_offset!=0)
{
PRINTF("Blockwise: no block option for blockwise resource, using block size %u\n", REST_MAX_CHUNK_SIZE);
coap_set_header_block2(response, 0, new_offset!=-1, REST_MAX_CHUNK_SIZE);
coap_set_payload(response, response->payload, MIN(response->payload_len, REST_MAX_CHUNK_SIZE));
} /* if (blockwise request) */
} /* no errors/hooks */
} /* successful service callback */
/* Serialize response. */
if (coap_error_code==NO_ERROR)
{
if ((transaction->packet_len = coap_serialize_message(response, transaction->packet))==0)
{
coap_error_code = PACKET_SERIALIZATION_ERROR;
}
}
}
else
{
coap_error_code = NOT_IMPLEMENTED_5_01;
coap_error_message = "NoServiceCallbck"; // no a to fit 16 bytes
} /* if (service callback) */
} else {
coap_error_code = SERVICE_UNAVAILABLE_5_03;
coap_error_message = "NoFreeTraBuffer";
} /* if (transaction buffer) */
}
else
{
/* Responses */
if (message->type==COAP_TYPE_CON && message->code==0)
{
PRINTF("Received Ping\n");
coap_error_code = PING_RESPONSE;
}
else if (message->type==COAP_TYPE_ACK)
{
/* Transactions are closed through lookup below */
PRINTF("Received ACK\n");
}
else if (message->type==COAP_TYPE_RST)
{
PRINTF("Received RST\n");
/* Cancel possible subscriptions. */
coap_remove_observer_by_mid(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, message->mid);
}
if ( (transaction = coap_get_transaction_by_mid(message->mid)) )
{
/* Free transaction memory before callback, as it may create a new transaction. */
restful_response_handler callback = transaction->callback;
void *callback_data = transaction->callback_data;
coap_clear_transaction(transaction);
/* Check if someone registered for the response */
if (callback) {
callback(callback_data, message);
}
} /* if (ACKed transaction) */
transaction = NULL;
} /* Request or Response */
} /* if (parsed correctly) */
if (coap_error_code==NO_ERROR)
{
if (transaction) coap_send_transaction(transaction);
}
else if (coap_error_code==MANUAL_RESPONSE)
{
PRINTF("Clearing transaction for manual response");
coap_clear_transaction(transaction);
}
else
{
coap_message_type_t reply_type = COAP_TYPE_ACK;
PRINTF("ERROR %u: %s\n", coap_error_code, coap_error_message);
coap_clear_transaction(transaction);
/* Set to sendable error code. */
if (coap_error_code >= 192)
{
coap_error_code = INTERNAL_SERVER_ERROR_5_00;
}
if (coap_error_code == PING_RESPONSE)
{
coap_error_code = 0;
reply_type = COAP_TYPE_RST;
}
/* Reuse input buffer for error message. */
coap_init_message(message, reply_type, coap_error_code, message->mid);
coap_set_payload(message, coap_error_message, strlen(coap_error_message));
coap_send_message(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, uip_appdata, coap_serialize_message(message, uip_appdata));
}
} /* if (new data) */
return coap_error_code;
}
/*----------------------------------------------------------------------------*/
void
coap_receiver_init()
{
process_start(&coap_receiver, NULL);
}
/*----------------------------------------------------------------------------*/
void
coap_set_service_callback(service_callback_t callback)
{
service_cbk = callback;
}
/*----------------------------------------------------------------------------*/
rest_resource_flags_t
coap_get_rest_method(void *packet)
{
return (rest_resource_flags_t)(1 << (((coap_packet_t *)packet)->code - 1));
}
/*----------------------------------------------------------------------------*/
/*- Server part --------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/* The discover resource is automatically included for CoAP. */
RESOURCE(well_known_core, METHOD_GET, ".well-known/core", "ct=40");
void
well_known_core_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset)
{
size_t strpos = 0; /* position in overall string (which is larger than the buffer) */
size_t bufpos = 0; /* position within buffer (bytes written) */
size_t tmplen = 0;
resource_t* resource = NULL;
#if COAP_LINK_FORMAT_FILTERING
/* For filtering. */
const char *filter = NULL;
const char *attrib = NULL;
const char *found = NULL;
const char *end = NULL;
char *value = NULL;
char lastchar = '\0';
int len = coap_get_header_uri_query(request, &filter);
if (len)
{
value = strchr(filter, '=');
value[0] = '\0';
++value;
len -= strlen(filter)+1;
PRINTF("Filter %s = %.*s\n", filter, len, value);
if (strcmp(filter,"href")==0 && value[0]=='/')
{
++value;
--len;
}
lastchar = value[len-1];
value[len-1] = '\0';
}
#endif
for (resource = (resource_t*)list_head(rest_get_resources()); resource; resource = resource->next)
{
#if COAP_LINK_FORMAT_FILTERING
/* Filtering */
if (len)
{
if (strcmp(filter,"href")==0)
{
attrib=strstr(resource->url, value);
if (attrib==NULL || (value[-1]=='/' && attrib!=resource->url)) continue;
end = attrib + strlen(attrib);
}
else
{
attrib=strstr(resource->attributes, filter);
if (attrib==NULL || (attrib[strlen(filter)]!='=' && attrib[strlen(filter)]!='"')) continue;
attrib += strlen(filter)+2;
end = strchr(attrib, '"');
}
PRINTF("Filter: res has attrib %s (%s)\n", attrib, value);
found = attrib;
while ((found=strstr(found, value))!=NULL) {
if (found > end)
{
found = NULL;
break;
}
if (lastchar==found[len-1] || lastchar=='*')
{
break;
}
++found;
}
if (found==NULL)
{
continue;
}
PRINTF("Filter: res has prefix %s\n", found);
if (lastchar!='*' && (found[len]!='"' && found[len]!=' ' && found[len]!='\0')) continue;
PRINTF("Filter: res has match\n");
}
#endif
PRINTF("res: /%s (%p)\npos: s%d, o%ld, b%d\n", resource->url, resource, strpos, *offset, bufpos);
if (strpos>0)
{
if (strpos >= *offset && bufpos < preferred_size)
{
buffer[bufpos++] = ',';
}
++strpos;
}
if (strpos >= *offset && bufpos < preferred_size)
{
buffer[bufpos++] = '<';
}
++strpos;
if (strpos >= *offset && bufpos < preferred_size)
{
buffer[bufpos++] = '/';
}
++strpos;
tmplen = strlen(resource->url);
if (strpos+tmplen > *offset)
{
bufpos += snprintf((char *) buffer + bufpos, preferred_size - bufpos + 1,
"%s", resource->url + ((*offset-(int32_t)strpos > 0) ? (*offset-(int32_t)strpos) : 0));
/* minimal-net requires these casts */
if (bufpos >= preferred_size)
{
break;
}
}
strpos += tmplen;
if (strpos >= *offset && bufpos < preferred_size)
{
buffer[bufpos++] = '>';
}
++strpos;
if (resource->attributes[0])
{
if (strpos >= *offset && bufpos < preferred_size)
{
buffer[bufpos++] = ';';
}
++strpos;
tmplen = strlen(resource->attributes);
if (strpos+tmplen > *offset)
{
bufpos += snprintf((char *) buffer + bufpos, preferred_size - bufpos + 1,
"%s", resource->attributes + (*offset-(int32_t)strpos > 0 ? *offset-(int32_t)strpos : 0));
if (bufpos >= preferred_size)
{
break;
}
}
strpos += tmplen;
}
/* buffer full, but resource not completed yet; or: do not break if resource exactly fills buffer. */
if (bufpos >= preferred_size && strpos-bufpos > *offset)
{
PRINTF("res: BREAK at %s (%p)\n", resource->url, resource);
break;
}
}
if (bufpos>0) {
PRINTF("BUF %d: %.*s\n", bufpos, bufpos, (char *) buffer);
coap_set_payload(response, buffer, bufpos );
coap_set_header_content_type(response, APPLICATION_LINK_FORMAT);
}
else if (strpos>0)
{
PRINTF("well_known_core_handler(): bufpos<=0\n");
coap_set_status_code(response, BAD_OPTION_4_02);
coap_set_payload(response, "BlockOutOfScope", 15);
}
if (resource==NULL) {
PRINTF("res: DONE\n");
*offset = -1;
}
else
{
PRINTF("res: MORE at %s (%p)\n", resource->url, resource);
*offset += preferred_size;
}
}
/*----------------------------------------------------------------------------*/
PROCESS_THREAD(coap_receiver, ev, data)
{
PROCESS_BEGIN();
PRINTF("Starting CoAP-13 receiver...\n");
rest_activate_resource(&resource_well_known_core);
coap_register_as_transaction_handler();
coap_init_connection(SERVER_LISTEN_PORT);
while(1) {
PROCESS_YIELD();
if(ev == tcpip_event) {
coap_receive();
} else if (ev == PROCESS_EVENT_TIMER) {
/* retransmissions are handled here */
coap_check_transactions();
}
} /* while (1) */
PROCESS_END();
}
/*----------------------------------------------------------------------------*/
/*- Client part --------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
void coap_blocking_request_callback(void *callback_data, void *response) {
struct request_state_t *state = (struct request_state_t *) callback_data;
state->response = (coap_packet_t*) response;
process_poll(state->process);
}
/*----------------------------------------------------------------------------*/
PT_THREAD(coap_blocking_request(struct request_state_t *state, process_event_t ev,
uip_ipaddr_t *remote_ipaddr, uint16_t remote_port,
coap_packet_t *request,
blocking_response_handler request_callback)) {
PT_BEGIN(&state->pt);
static uint8_t more;
static uint32_t res_block;
static uint8_t block_error;
state->block_num = 0;
state->response = NULL;
state->process = PROCESS_CURRENT();
more = 0;
res_block = 0;
block_error = 0;
do {
request->mid = coap_get_mid();
if ((state->transaction = coap_new_transaction(request->mid, remote_ipaddr, remote_port)))
{
state->transaction->callback = coap_blocking_request_callback;
state->transaction->callback_data = state;
if (state->block_num>0)
{
coap_set_header_block2(request, state->block_num, 0, REST_MAX_CHUNK_SIZE);
}
state->transaction->packet_len = coap_serialize_message(request, state->transaction->packet);
coap_send_transaction(state->transaction);
PRINTF("Requested #%lu (MID %u)\n", state->block_num, request->mid);
PT_YIELD_UNTIL(&state->pt, ev == PROCESS_EVENT_POLL);
if (!state->response)
{
PRINTF("Server not responding\n");
PT_EXIT(&state->pt);
}
coap_get_header_block2(state->response, &res_block, &more, NULL, NULL);
PRINTF("Received #%lu%s (%u bytes)\n", res_block, more ? "+" : "", state->response->payload_len);
if (res_block==state->block_num)
{
request_callback(state->response);
++(state->block_num);
}
else
{
PRINTF("WRONG BLOCK %lu/%lu\n", res_block, state->block_num);
++block_error;
}
}
else
{
PRINTF("Could not allocate transaction buffer");
PT_EXIT(&state->pt);
}
} while (more && block_error<COAP_MAX_ATTEMPTS);
PT_END(&state->pt);
}
/*----------------------------------------------------------------------------*/
/*- Engine Interface ---------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
const struct rest_implementation coap_rest_implementation = {
"CoAP-13",
coap_receiver_init,
coap_set_service_callback,
coap_get_header_uri_path,
coap_set_header_uri_path,
coap_get_rest_method,
coap_set_status_code,
coap_get_header_content_type,
coap_set_header_content_type,
coap_get_header_accept,
coap_get_header_size,
coap_set_header_size,
coap_get_header_max_age,
coap_set_header_max_age,
coap_set_header_etag,
coap_get_header_if_match,
coap_get_header_if_none_match,
coap_get_header_uri_host,
coap_set_header_location_path,
coap_get_payload,
coap_set_payload,
coap_get_header_uri_query,
coap_get_query_variable,
coap_get_post_variable,
coap_notify_observers,
(restful_post_handler) coap_observe_handler,
NULL, /* default pre-handler (set separate handler after activation if needed) */
NULL, /* default post-handler for non-observable resources */
{
CONTENT_2_05,
CREATED_2_01,
CHANGED_2_04,
DELETED_2_02,
VALID_2_03,
BAD_REQUEST_4_00,
UNAUTHORIZED_4_01,
BAD_OPTION_4_02,
FORBIDDEN_4_03,
NOT_FOUND_4_04,
METHOD_NOT_ALLOWED_4_05,
NOT_ACCEPTABLE_4_06,
REQUEST_ENTITY_TOO_LARGE_4_13,
UNSUPPORTED_MEDIA_TYPE_4_15,
INTERNAL_SERVER_ERROR_5_00,
NOT_IMPLEMENTED_5_01,
BAD_GATEWAY_5_02,
SERVICE_UNAVAILABLE_5_03,
GATEWAY_TIMEOUT_5_04,
PROXYING_NOT_SUPPORTED_5_05
},
{
TEXT_PLAIN,
TEXT_XML,
TEXT_CSV,
TEXT_HTML,
IMAGE_GIF,
IMAGE_JPEG,
IMAGE_PNG,
IMAGE_TIFF,
AUDIO_RAW,
VIDEO_RAW,
APPLICATION_LINK_FORMAT,
APPLICATION_XML,
APPLICATION_OCTET_STREAM,
APPLICATION_RDF_XML,
APPLICATION_SOAP_XML,
APPLICATION_ATOM_XML,
APPLICATION_XMPP_XML,
APPLICATION_EXI,
APPLICATION_FASTINFOSET,
APPLICATION_SOAP_FASTINFOSET,
APPLICATION_JSON,
APPLICATION_X_OBIX_BINARY
}
};

View file

@ -1,94 +0,0 @@
/*
* Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* CoAP implementation of the REST Engine
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#ifndef COAP_SERVER_H_
#define COAP_SERVER_H_
#if !defined(REST)
#error "Define REST to \"coap_rest_implementation\""
#endif
#include "er-coap-13.h"
#include "er-coap-13-transactions.h"
#include "er-coap-13-observing.h"
#include "er-coap-13-separate.h"
#include "pt.h"
/* Declare server process */
PROCESS_NAME(coap_receiver);
#define SERVER_LISTEN_PORT UIP_HTONS(COAP_SERVER_PORT)
typedef coap_packet_t rest_request_t;
typedef coap_packet_t rest_response_t;
extern const struct rest_implementation coap_rest_implementation;
void coap_receiver_init(void);
/*-----------------------------------------------------------------------------------*/
/*- Client part ---------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------*/
struct request_state_t {
struct pt pt;
struct process *process;
coap_transaction_t *transaction;
coap_packet_t *response;
uint32_t block_num;
};
typedef void (*blocking_response_handler) (void* response);
PT_THREAD(coap_blocking_request(struct request_state_t *state, process_event_t ev,
uip_ipaddr_t *remote_ipaddr, uint16_t remote_port,
coap_packet_t *request,
blocking_response_handler request_callback));
#define COAP_BLOCKING_REQUEST(server_addr, server_port, request, chunk_handler) \
{ \
static struct request_state_t request_state; \
PT_SPAWN(process_pt, &request_state.pt, \
coap_blocking_request(&request_state, ev, \
server_addr, server_port, \
request, chunk_handler) \
); \
}
/*-----------------------------------------------------------------------------------*/
#endif /* COAP_SERVER_H_ */

View file

@ -1,255 +0,0 @@
/*
* Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* CoAP module for observing resources
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#include <stdio.h>
#include <string.h>
#include "er-coap-13-observing.h"
#define DEBUG 0
#if DEBUG
#define PRINTF(...) printf(__VA_ARGS__)
#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5])
#else
#define PRINTF(...)
#define PRINT6ADDR(addr)
#define PRINTLLADDR(addr)
#endif
MEMB(observers_memb, coap_observer_t, COAP_MAX_OBSERVERS);
LIST(observers_list);
/*-----------------------------------------------------------------------------------*/
coap_observer_t *
coap_add_observer(uip_ipaddr_t *addr, uint16_t port, const uint8_t *token, size_t token_len, const char *url)
{
/* Remove existing observe relationship, if any. */
coap_remove_observer_by_url(addr, port, url);
coap_observer_t *o = memb_alloc(&observers_memb);
if (o)
{
o->url = url;
uip_ipaddr_copy(&o->addr, addr);
o->port = port;
o->token_len = token_len;
memcpy(o->token, token, token_len);
o->last_mid = 0;
stimer_set(&o->refresh_timer, COAP_OBSERVING_REFRESH_INTERVAL);
PRINTF("Adding observer for /%s [0x%02X%02X]\n", o->url, o->token[0], o->token[1]);
list_add(observers_list, o);
}
return o;
}
/*-----------------------------------------------------------------------------------*/
void
coap_remove_observer(coap_observer_t *o)
{
PRINTF("Removing observer for /%s [0x%02X%02X]\n", o->url, o->token[0], o->token[1]);
memb_free(&observers_memb, o);
list_remove(observers_list, o);
}
int
coap_remove_observer_by_client(uip_ipaddr_t *addr, uint16_t port)
{
int removed = 0;
coap_observer_t* obs = NULL;
for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next)
{
PRINTF("Remove check client ");
PRINT6ADDR(addr);
PRINTF(":%u\n", port);
if (uip_ipaddr_cmp(&obs->addr, addr) && obs->port==port)
{
coap_remove_observer(obs);
removed++;
}
}
return removed;
}
int
coap_remove_observer_by_token(uip_ipaddr_t *addr, uint16_t port, uint8_t *token, size_t token_len)
{
int removed = 0;
coap_observer_t* obs = NULL;
for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next)
{
PRINTF("Remove check Token 0x%02X%02X\n", token[0], token[1]);
if (uip_ipaddr_cmp(&obs->addr, addr) && obs->port==port && obs->token_len==token_len && memcmp(obs->token, token, token_len)==0)
{
coap_remove_observer(obs);
removed++;
}
}
return removed;
}
int
coap_remove_observer_by_url(uip_ipaddr_t *addr, uint16_t port, const char *url)
{
int removed = 0;
coap_observer_t* obs = NULL;
for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next)
{
PRINTF("Remove check URL %p\n", url);
if ((addr==NULL || (uip_ipaddr_cmp(&obs->addr, addr) && obs->port==port)) && (obs->url==url || memcmp(obs->url, url, strlen(obs->url))==0))
{
coap_remove_observer(obs);
removed++;
}
}
return removed;
}
int
coap_remove_observer_by_mid(uip_ipaddr_t *addr, uint16_t port, uint16_t mid)
{
int removed = 0;
coap_observer_t* obs = NULL;
for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next)
{
PRINTF("Remove check MID %u\n", mid);
if (uip_ipaddr_cmp(&obs->addr, addr) && obs->port==port && obs->last_mid==mid)
{
coap_remove_observer(obs);
removed++;
}
}
return removed;
}
/*-----------------------------------------------------------------------------------*/
void
coap_notify_observers(resource_t *resource, int32_t obs_counter, void *notification)
{
coap_packet_t *const coap_res = (coap_packet_t *) notification;
coap_observer_t* obs = NULL;
uint8_t preferred_type = coap_res->type;
PRINTF("Observing: Notification from %s\n", resource->url);
/* Iterate over observers. */
for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next)
{
if (obs->url==resource->url) /* using RESOURCE url pointer as handle */
{
coap_transaction_t *transaction = NULL;
/*TODO implement special transaction for CON, sharing the same buffer to allow for more observers. */
if ( (transaction = coap_new_transaction(coap_get_mid(), &obs->addr, obs->port)) )
{
PRINTF(" Observer ");
PRINT6ADDR(&obs->addr);
PRINTF(":%u\n", obs->port);
/* Update last MID for RST matching. */
obs->last_mid = transaction->mid;
/* Prepare response */
coap_res->mid = transaction->mid;
if (obs_counter>=0) coap_set_header_observe(coap_res, obs_counter);
coap_set_header_token(coap_res, obs->token, obs->token_len);
/* Use CON to check whether client is still there/interested after COAP_OBSERVING_REFRESH_INTERVAL. */
if (stimer_expired(&obs->refresh_timer))
{
PRINTF(" Refreshing with CON\n");
coap_res->type = COAP_TYPE_CON;
stimer_restart(&obs->refresh_timer);
}
else
{
coap_res->type = preferred_type;
}
transaction->packet_len = coap_serialize_message(coap_res, transaction->packet);
coap_send_transaction(transaction);
}
}
}
}
/*-----------------------------------------------------------------------------------*/
void
coap_observe_handler(resource_t *resource, void *request, void *response)
{
coap_packet_t *const coap_req = (coap_packet_t *) request;
coap_packet_t *const coap_res = (coap_packet_t *) response;
static char content[16];
if (coap_req->code==COAP_GET && coap_res->code<128) /* GET request and response without error code */
{
if (IS_OPTION(coap_req, COAP_OPTION_OBSERVE))
{
if (coap_add_observer(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, coap_req->token, coap_req->token_len, resource->url))
{
coap_set_header_observe(coap_res, 0);
/*
* For demonstration purposes only. A subscription should return the same representation as a normal GET.
* TODO: Comment the following line for any real application.
*/
coap_set_payload(coap_res, content, snprintf(content, sizeof(content), "Added %u/%u", list_length(observers_list), COAP_MAX_OBSERVERS));
}
else
{
coap_res->code = SERVICE_UNAVAILABLE_5_03;
coap_set_payload(coap_res, "TooManyObservers", 16);
} /* if (added observer) */
}
else /* if (observe) */
{
/* Remove client if it is currently observing. */
coap_remove_observer_by_url(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, resource->url);
} /* if (observe) */
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,382 +0,0 @@
/*
* Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* An implementation of the Constrained Application Protocol (draft 12)
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#ifndef COAP_13_H_
#define COAP_13_H_
#include <stddef.h> /* for size_t */
#include "contiki-net.h"
#include "erbium.h"
#define COAP_LINK_FORMAT_FILTERING 1
#define COAP_DEFAULT_PORT 5683
#ifndef COAP_SERVER_PORT
#define COAP_SERVER_PORT COAP_DEFAULT_PORT
#endif
#define COAP_DEFAULT_MAX_AGE 60
#define COAP_RESPONSE_TIMEOUT 2
#define COAP_RESPONSE_RANDOM_FACTOR 1.5
#define COAP_MAX_RETRANSMIT 4
#define COAP_HEADER_LEN 4 /* | version:0x03 type:0x0C tkl:0xF0 | code | mid:0x00FF | mid:0xFF00 | */
#define COAP_ETAG_LEN 8 /* The maximum number of bytes for the ETag */
#define COAP_TOKEN_LEN 8 /* The maximum number of bytes for the Token */
#define COAP_MAX_ACCEPT_NUM 2 /* The maximum number of accept preferences to parse/store */
#define COAP_HEADER_VERSION_MASK 0xC0
#define COAP_HEADER_VERSION_POSITION 6
#define COAP_HEADER_TYPE_MASK 0x30
#define COAP_HEADER_TYPE_POSITION 4
#define COAP_HEADER_TOKEN_LEN_MASK 0x0F
#define COAP_HEADER_TOKEN_LEN_POSITION 0
#define COAP_HEADER_OPTION_DELTA_MASK 0xF0
#define COAP_HEADER_OPTION_SHORT_LENGTH_MASK 0x0F
/*
* Conservative size limit, as not all options have to be set at the same time.
*/
#ifndef COAP_MAX_HEADER_SIZE
/* Hdr CoT Age Tag Obs Tok Blo strings */
#define COAP_MAX_HEADER_SIZE (4 + 3 + 5 + 1+COAP_ETAG_LEN + 3 + 1+COAP_TOKEN_LEN + 4 + 30) /* 70 */
#endif /* COAP_MAX_HEADER_SIZE */
#define COAP_MAX_PACKET_SIZE (COAP_MAX_HEADER_SIZE + REST_MAX_CHUNK_SIZE)
/* 0/14 48 for IPv6 (28 for IPv4) */
#if COAP_MAX_PACKET_SIZE > (UIP_BUFSIZE - UIP_LLH_LEN - UIP_IPUDPH_LEN)
#error "UIP_CONF_BUFFER_SIZE too small for REST_MAX_CHUNK_SIZE"
#endif
/*
* Maximum number of failed request attempts before action
*/
#ifndef COAP_MAX_ATTEMPTS
#define COAP_MAX_ATTEMPTS 4
#endif /* COAP_MAX_ATTEMPTS */
#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN])
#define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[UIP_LLH_LEN + UIP_IPH_LEN])
/* Bitmap for set options */
enum { OPTION_MAP_SIZE = sizeof(uint8_t) * 8 };
#define SET_OPTION(packet, opt) ((packet)->options[opt / OPTION_MAP_SIZE] |= 1 << (opt % OPTION_MAP_SIZE))
#define IS_OPTION(packet, opt) ((packet)->options[opt / OPTION_MAP_SIZE] & (1 << (opt % OPTION_MAP_SIZE)))
#ifndef MIN
#define MIN(a, b) ((a) < (b)? (a) : (b))
#endif /* MIN */
/* CoAP message types */
typedef enum {
COAP_TYPE_CON, /* confirmables */
COAP_TYPE_NON, /* non-confirmables */
COAP_TYPE_ACK, /* acknowledgements */
COAP_TYPE_RST /* reset */
} coap_message_type_t;
/* CoAP request method codes */
typedef enum {
COAP_GET = 1,
COAP_POST,
COAP_PUT,
COAP_DELETE
} coap_method_t;
/* CoAP response codes */
typedef enum {
NO_ERROR = 0,
CREATED_2_01 = 65, /* CREATED */
DELETED_2_02 = 66, /* DELETED */
VALID_2_03 = 67, /* NOT_MODIFIED */
CHANGED_2_04 = 68, /* CHANGED */
CONTENT_2_05 = 69, /* OK */
BAD_REQUEST_4_00 = 128, /* BAD_REQUEST */
UNAUTHORIZED_4_01 = 129, /* UNAUTHORIZED */
BAD_OPTION_4_02 = 130, /* BAD_OPTION */
FORBIDDEN_4_03 = 131, /* FORBIDDEN */
NOT_FOUND_4_04 = 132, /* NOT_FOUND */
METHOD_NOT_ALLOWED_4_05 = 133, /* METHOD_NOT_ALLOWED */
NOT_ACCEPTABLE_4_06 = 134, /* NOT_ACCEPTABLE */
PRECONDITION_FAILED_4_12 = 140, /* BAD_REQUEST */
REQUEST_ENTITY_TOO_LARGE_4_13 = 141, /* REQUEST_ENTITY_TOO_LARGE */
UNSUPPORTED_MEDIA_TYPE_4_15 = 143, /* UNSUPPORTED_MEDIA_TYPE */
INTERNAL_SERVER_ERROR_5_00 = 160, /* INTERNAL_SERVER_ERROR */
NOT_IMPLEMENTED_5_01 = 161, /* NOT_IMPLEMENTED */
BAD_GATEWAY_5_02 = 162, /* BAD_GATEWAY */
SERVICE_UNAVAILABLE_5_03 = 163, /* SERVICE_UNAVAILABLE */
GATEWAY_TIMEOUT_5_04 = 164, /* GATEWAY_TIMEOUT */
PROXYING_NOT_SUPPORTED_5_05 = 165, /* PROXYING_NOT_SUPPORTED */
/* Erbium errors */
MEMORY_ALLOCATION_ERROR = 192,
PACKET_SERIALIZATION_ERROR,
/* Erbium hooks */
MANUAL_RESPONSE,
PING_RESPONSE
} coap_status_t;
/* CoAP header options */
typedef enum {
COAP_OPTION_IF_MATCH = 1, /* 0-8 B */
COAP_OPTION_URI_HOST = 3, /* 1-255 B */
COAP_OPTION_ETAG = 4, /* 1-8 B */
COAP_OPTION_IF_NONE_MATCH = 5, /* 0 B */
COAP_OPTION_OBSERVE = 6, /* 0-3 B */
COAP_OPTION_URI_PORT = 7, /* 0-2 B */
COAP_OPTION_LOCATION_PATH = 8, /* 0-255 B */
COAP_OPTION_URI_PATH = 11, /* 0-255 B */
COAP_OPTION_CONTENT_TYPE = 12, /* 0-2 B */
COAP_OPTION_MAX_AGE = 14, /* 0-4 B */
COAP_OPTION_URI_QUERY = 15, /* 0-270 B */
COAP_OPTION_ACCEPT = 16, /* 0-2 B */
COAP_OPTION_LOCATION_QUERY = 20, /* 1-270 B */
COAP_OPTION_BLOCK2 = 23, /* 1-3 B */
COAP_OPTION_BLOCK1 = 27, /* 1-3 B */
COAP_OPTION_SIZE = 28, /* 0-4 B */
COAP_OPTION_PROXY_URI = 35, /* 1-270 B */
} coap_option_t;
/* CoAP Content-Types */
typedef enum {
TEXT_PLAIN = 0,
TEXT_XML = 1, /* Indented types are not in the initial registry. */
TEXT_CSV = 2,
TEXT_HTML = 3,
IMAGE_GIF = 21,
IMAGE_JPEG = 22,
IMAGE_PNG = 23,
IMAGE_TIFF = 24,
AUDIO_RAW = 25,
VIDEO_RAW = 26,
APPLICATION_LINK_FORMAT = 40,
APPLICATION_XML = 41,
APPLICATION_OCTET_STREAM = 42,
APPLICATION_RDF_XML = 43,
APPLICATION_SOAP_XML = 44,
APPLICATION_ATOM_XML = 45,
APPLICATION_XMPP_XML = 46,
APPLICATION_EXI = 47,
APPLICATION_FASTINFOSET = 48,
APPLICATION_SOAP_FASTINFOSET = 49,
APPLICATION_JSON = 50,
APPLICATION_X_OBIX_BINARY = 51
} coap_content_type_t;
/* Parsed message struct */
typedef struct {
uint8_t *buffer; /* pointer to CoAP header / incoming packet buffer / memory to serialize packet */
uint8_t version;
coap_message_type_t type;
uint8_t code;
uint16_t mid;
uint8_t options[COAP_OPTION_PROXY_URI / OPTION_MAP_SIZE + 1]; /* Bitmap to check if option is set */
coap_content_type_t content_type; /* Parse options once and store; allows setting options in random order */
uint32_t max_age;
size_t proxy_uri_len;
const char *proxy_uri;
uint8_t etag_len;
uint8_t etag[COAP_ETAG_LEN];
size_t uri_host_len;
const char *uri_host;
size_t location_path_len;
const char *location_path;
uint16_t uri_port;
size_t location_query_len;
const char *location_query;
size_t uri_path_len;
const char *uri_path;
uint16_t observe;
uint8_t token_len;
uint8_t token[COAP_TOKEN_LEN];
uint8_t accept_num;
uint16_t accept[COAP_MAX_ACCEPT_NUM];
uint8_t if_match_len;
uint8_t if_match[COAP_ETAG_LEN];
uint32_t block2_num;
uint8_t block2_more;
uint16_t block2_size;
uint32_t block2_offset;
uint32_t block1_num;
uint8_t block1_more;
uint16_t block1_size;
uint32_t block1_offset;
uint32_t size;
size_t uri_query_len;
const char *uri_query;
uint8_t if_none_match;
uint16_t payload_len;
uint8_t *payload;
} coap_packet_t;
/* Option format serialization*/
#define COAP_SERIALIZE_INT_OPTION(number, field, text) \
if (IS_OPTION(coap_pkt, number)) { \
PRINTF(text" [%u]\n", coap_pkt->field); \
option += coap_serialize_int_option(number, current_number, option, coap_pkt->field); \
current_number = number; \
}
#define COAP_SERIALIZE_BYTE_OPTION(number, field, text) \
if (IS_OPTION(coap_pkt, number)) { \
PRINTF(text" %u [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", coap_pkt->field##_len, \
coap_pkt->field[0], \
coap_pkt->field[1], \
coap_pkt->field[2], \
coap_pkt->field[3], \
coap_pkt->field[4], \
coap_pkt->field[5], \
coap_pkt->field[6], \
coap_pkt->field[7] \
); /*FIXME always prints 8 bytes */ \
option += coap_serialize_array_option(number, current_number, option, coap_pkt->field, coap_pkt->field##_len, '\0'); \
current_number = number; \
}
#define COAP_SERIALIZE_STRING_OPTION(number, field, splitter, text) \
if (IS_OPTION(coap_pkt, number)) { \
PRINTF(text" [%.*s]\n", coap_pkt->field##_len, coap_pkt->field); \
option += coap_serialize_array_option(number, current_number, option, (uint8_t *) coap_pkt->field, coap_pkt->field##_len, splitter); \
current_number = number; \
}
#define COAP_SERIALIZE_ACCEPT_OPTION(number, field, text) \
if (IS_OPTION(coap_pkt, number)) { \
int i; \
for (i=0; i<coap_pkt->field##_num; ++i) \
{ \
PRINTF(text" [%u]\n", coap_pkt->field[i]); \
option += coap_serialize_int_option(number, current_number, option, coap_pkt->field[i]); \
current_number = number; \
} \
}
#define COAP_SERIALIZE_BLOCK_OPTION(number, field, text) \
if (IS_OPTION(coap_pkt, number)) \
{ \
PRINTF(text" [%lu%s (%u B/blk)]\n", coap_pkt->field##_num, coap_pkt->field##_more ? "+" : "", coap_pkt->field##_size); \
uint32_t block = coap_pkt->field##_num << 4; \
if (coap_pkt->field##_more) block |= 0x8; \
block |= 0xF & coap_log_2(coap_pkt->field##_size/16); \
PRINTF(text" encoded: 0x%lX\n", block); \
option += coap_serialize_int_option(number, current_number, option, block); \
current_number = number; \
}
/* To store error code and human-readable payload */
extern coap_status_t coap_error_code;
extern char *coap_error_message;
void coap_init_connection(uint16_t port);
uint16_t coap_get_mid(void);
void coap_init_message(void *packet, coap_message_type_t type, uint8_t code, uint16_t mid);
size_t coap_serialize_message(void *packet, uint8_t *buffer);
void coap_send_message(uip_ipaddr_t *addr, uint16_t port, uint8_t *data, uint16_t length);
coap_status_t coap_parse_message(void *request, uint8_t *data, uint16_t data_len);
int coap_get_query_variable(void *packet, const char *name, const char **output);
int coap_get_post_variable(void *packet, const char *name, const char **output);
/*-----------------------------------------------------------------------------------*/
int coap_set_status_code(void *packet, unsigned int code);
unsigned int coap_get_header_content_type(void *packet);
int coap_set_header_content_type(void *packet, unsigned int content_type);
int coap_get_header_accept(void *packet, const uint16_t **accept);
int coap_set_header_accept(void *packet, uint16_t accept);
int coap_get_header_max_age(void *packet, uint32_t *age);
int coap_set_header_max_age(void *packet, uint32_t age);
int coap_get_header_etag(void *packet, const uint8_t **etag);
int coap_set_header_etag(void *packet, const uint8_t *etag, size_t etag_len);
int coap_get_header_if_match(void *packet, const uint8_t **etag);
int coap_set_header_if_match(void *packet, const uint8_t *etag, size_t etag_len);
int coap_get_header_if_none_match(void *packet);
int coap_set_header_if_none_match(void *packet);
int coap_get_header_token(void *packet, const uint8_t **token);
int coap_set_header_token(void *packet, const uint8_t *token, size_t token_len);
int coap_get_header_proxy_uri(void *packet, const char **uri); /* In-place string might not be 0-terminated. */
int coap_set_header_proxy_uri(void *packet, const char *uri);
int coap_get_header_uri_host(void *packet, const char **host); /* In-place string might not be 0-terminated. */
int coap_set_header_uri_host(void *packet, const char *host);
int coap_get_header_uri_path(void *packet, const char **path); /* In-place string might not be 0-terminated. */
int coap_set_header_uri_path(void *packet, const char *path);
int coap_get_header_uri_query(void *packet, const char **query); /* In-place string might not be 0-terminated. */
int coap_set_header_uri_query(void *packet, const char *query);
int coap_get_header_location_path(void *packet, const char **path); /* In-place string might not be 0-terminated. */
int coap_set_header_location_path(void *packet, const char *path); /* Also splits optional query into Location-Query option. */
int coap_get_header_location_query(void *packet, const char **query); /* In-place string might not be 0-terminated. */
int coap_set_header_location_query(void *packet, const char *query);
int coap_get_header_observe(void *packet, uint32_t *observe);
int coap_set_header_observe(void *packet, uint32_t observe);
int coap_get_header_block2(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset);
int coap_set_header_block2(void *packet, uint32_t num, uint8_t more, uint16_t size);
int coap_get_header_block1(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset);
int coap_set_header_block1(void *packet, uint32_t num, uint8_t more, uint16_t size);
int coap_get_header_size(void *packet, uint32_t *size);
int coap_set_header_size(void *packet, uint32_t size);
int coap_get_payload(void *packet, const uint8_t **payload);
int coap_set_payload(void *packet, const void *payload, size_t length);
#endif /* COAP_13_H_ */

6
apps/er-coap/Makefile.er-coap Executable file
View file

@ -0,0 +1,6 @@
er-coap_src = er-coap.c er-coap-engine.c er-coap-transactions.c \
er-coap-observe.c er-coap-separate.c er-coap-res-well-known-core.c \
er-coap-block1.c er-coap-observe-client.c
# Erbium will implement the REST Engine
CFLAGS += -DREST=coap_rest_implementation

View file

@ -0,0 +1,119 @@
/*
* Copyright (c) 2014, Lars Schmertmann <SmallLars@t-online.de>.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* CoAP module for block 1 handling
* \author
* Lars Schmertmann <SmallLars@t-online.de>
*/
#include <stdio.h>
#include <string.h>
#include "er-coap.h"
#include "er-coap-block1.h"
#define DEBUG 0
#if DEBUG
#define PRINTF(...) printf(__VA_ARGS__)
#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]", (lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3], (lladdr)->addr[4], (lladdr)->addr[5])
#else
#define PRINTF(...)
#define PRINT6ADDR(addr)
#define PRINTLLADDR(addr)
#endif
/*----------------------------------------------------------------------------*/
/**
* \brief Block 1 support within a coap-ressource
*
* This function will help you to use block 1. If target is null
* error handling and response configuration is active. On return
* value 0, the last block was recived, while on return value 1
* more blocks will follow. With target, len and maxlen this
* function will assemble the blocks.
*
* You can find an example in:
* examples/er-rest-example/resources/res-b1-sep-b2.c
*
* \param request Request pointer from the handler
* \param response Response pointer from the handler
* \param target Pointer to the buffer where the request payload can be assembled
* \param len Pointer to the variable, where the function stores the actual length
* \param max_len Length of the "target"-Buffer
*
* \return 0 if initialisation was successful
* -1 if initialisation failed
*/
int
coap_block1_handler(void *request, void *response, uint8_t *target, size_t *len, size_t max_len)
{
const uint8_t *payload = 0;
int pay_len = REST.get_request_payload(request, &payload);
if(!pay_len || !payload) {
erbium_status_code = REST.status.BAD_REQUEST;
coap_error_message = "NoPayload";
return -1;
}
coap_packet_t *packet = (coap_packet_t *)request;
if(packet->block1_offset + pay_len > max_len) {
erbium_status_code = REST.status.REQUEST_ENTITY_TOO_LARGE;
coap_error_message = "Message to big";
return -1;
}
if(target && len) {
memcpy(target + packet->block1_offset, payload, pay_len);
*len = packet->block1_offset + pay_len;
}
if(IS_OPTION(packet, COAP_OPTION_BLOCK1)) {
PRINTF("Blockwise: block 1 request: Num: %u, More: %u, Size: %u, Offset: %u\n",
packet->block1_num,
packet->block1_more,
packet->block1_size,
packet->block1_offset);
coap_set_header_block1(response, packet->block1_num, packet->block1_more, packet->block1_size);
if(packet->block1_more) {
coap_set_status_code(response, CONTINUE_2_31);
return 1;
}
}
return 0;
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2014, Lars Schmertmann <SmallLars@t-online.de>.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* CoAP module for block 1 handling
* \author
* Lars Schmertmann <SmallLars@t-online.de>
*/
#ifndef COAP_BLOCK1_H_
#define COAP_BLOCK1_H_
#include <stddef.h>
#include <stdint.h>
int coap_block1_handler(void *request, void *response, uint8_t *target, size_t *len, size_t max_len);
#endif /* COAP_BLOCK1_H_ */

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, Institute for Pervasive Computing, ETH Zurich
* Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -31,48 +31,44 @@
/**
* \file
* CoAP module for reliable transport
* Collection of default configuration values.
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#ifndef COAP_TRANSACTIONS_H_
#define COAP_TRANSACTIONS_H_
#ifndef ER_COAP_CONF_H_
#define ER_COAP_CONF_H_
#include "er-coap-12.h"
/* Features that can be disabled to achieve smaller memory footprint */
#define COAP_LINK_FORMAT_FILTERING 0
#define COAP_PROXY_OPTION_PROCESSING 0
/*
* The number of concurrent messages that can be stored for retransmission in the transaction layer.
*/
/* Listening port for the CoAP REST Engine */
#ifndef COAP_SERVER_PORT
#define COAP_SERVER_PORT COAP_DEFAULT_PORT
#endif
/* The number of concurrent messages that can be stored for retransmission in the transaction layer. */
#ifndef COAP_MAX_OPEN_TRANSACTIONS
#define COAP_MAX_OPEN_TRANSACTIONS 4
#define COAP_MAX_OPEN_TRANSACTIONS 4
#endif /* COAP_MAX_OPEN_TRANSACTIONS */
/* container for transactions with message buffer and retransmission info */
typedef struct coap_transaction {
struct coap_transaction *next; /* for LIST */
/* Maximum number of failed request attempts before action */
#ifndef COAP_MAX_ATTEMPTS
#define COAP_MAX_ATTEMPTS 4
#endif /* COAP_MAX_ATTEMPTS */
uint16_t mid;
struct etimer retrans_timer;
uint8_t retrans_counter;
/* Conservative size limit, as not all options have to be set at the same time. Check when Proxy-Uri option is used */
#ifndef COAP_MAX_HEADER_SIZE /* Hdr CoF If-Match Obs Blo strings */
#define COAP_MAX_HEADER_SIZE (4 + COAP_TOKEN_LEN + 3 + 1 + COAP_ETAG_LEN + 4 + 4 + 30) /* 65 */
#endif /* COAP_MAX_HEADER_SIZE */
uip_ipaddr_t addr;
uint16_t port;
/* Number of observer slots (each takes abot xxx bytes) */
#ifndef COAP_MAX_OBSERVERS
#define COAP_MAX_OBSERVERS COAP_MAX_OPEN_TRANSACTIONS - 1
#endif /* COAP_MAX_OBSERVERS */
restful_response_handler callback;
void *callback_data;
/* Interval in notifies in which NON notifies are changed to CON notifies to check client. */
#define COAP_OBSERVE_REFRESH_INTERVAL 20
uint16_t packet_len;
uint8_t packet[COAP_MAX_PACKET_SIZE+1]; /* +1 for the terminating '\0' to simply and savely use snprintf(buf, len+1, "", ...) in the resource handler. */
} coap_transaction_t;
void coap_register_as_transaction_handler();
coap_transaction_t *coap_new_transaction(uint16_t mid, uip_ipaddr_t *addr, uint16_t port);
void coap_send_transaction(coap_transaction_t *t);
void coap_clear_transaction(coap_transaction_t *t);
coap_transaction_t *coap_get_transaction_by_mid(uint16_t mid);
void coap_check_transactions();
#endif /* COAP_TRANSACTIONS_H_ */
#endif /* ER_COAP_CONF_H_ */

View file

@ -0,0 +1,166 @@
/*
* Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* Collection of constants specified in the CoAP standard.
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#ifndef ER_COAP_CONSTANTS_H_
#define ER_COAP_CONSTANTS_H_
#define COAP_DEFAULT_PORT 5683
#define COAP_DEFAULT_MAX_AGE 60
#define COAP_RESPONSE_TIMEOUT 3
#define COAP_RESPONSE_RANDOM_FACTOR 1.5
#define COAP_MAX_RETRANSMIT 4
#define COAP_HEADER_LEN 4 /* | version:0x03 type:0x0C tkl:0xF0 | code | mid:0x00FF | mid:0xFF00 | */
#define COAP_TOKEN_LEN 8 /* The maximum number of bytes for the Token */
#define COAP_ETAG_LEN 8 /* The maximum number of bytes for the ETag */
#define COAP_HEADER_VERSION_MASK 0xC0
#define COAP_HEADER_VERSION_POSITION 6
#define COAP_HEADER_TYPE_MASK 0x30
#define COAP_HEADER_TYPE_POSITION 4
#define COAP_HEADER_TOKEN_LEN_MASK 0x0F
#define COAP_HEADER_TOKEN_LEN_POSITION 0
#define COAP_HEADER_OPTION_DELTA_MASK 0xF0
#define COAP_HEADER_OPTION_SHORT_LENGTH_MASK 0x0F
/* CoAP message types */
typedef enum {
COAP_TYPE_CON, /* confirmables */
COAP_TYPE_NON, /* non-confirmables */
COAP_TYPE_ACK, /* acknowledgements */
COAP_TYPE_RST /* reset */
} coap_message_type_t;
/* CoAP request method codes */
typedef enum {
COAP_GET = 1,
COAP_POST,
COAP_PUT,
COAP_DELETE
} coap_method_t;
/* CoAP response codes */
typedef enum {
NO_ERROR = 0,
CREATED_2_01 = 65, /* CREATED */
DELETED_2_02 = 66, /* DELETED */
VALID_2_03 = 67, /* NOT_MODIFIED */
CHANGED_2_04 = 68, /* CHANGED */
CONTENT_2_05 = 69, /* OK */
CONTINUE_2_31 = 95, /* CONTINUE */
BAD_REQUEST_4_00 = 128, /* BAD_REQUEST */
UNAUTHORIZED_4_01 = 129, /* UNAUTHORIZED */
BAD_OPTION_4_02 = 130, /* BAD_OPTION */
FORBIDDEN_4_03 = 131, /* FORBIDDEN */
NOT_FOUND_4_04 = 132, /* NOT_FOUND */
METHOD_NOT_ALLOWED_4_05 = 133, /* METHOD_NOT_ALLOWED */
NOT_ACCEPTABLE_4_06 = 134, /* NOT_ACCEPTABLE */
PRECONDITION_FAILED_4_12 = 140, /* BAD_REQUEST */
REQUEST_ENTITY_TOO_LARGE_4_13 = 141, /* REQUEST_ENTITY_TOO_LARGE */
UNSUPPORTED_MEDIA_TYPE_4_15 = 143, /* UNSUPPORTED_MEDIA_TYPE */
INTERNAL_SERVER_ERROR_5_00 = 160, /* INTERNAL_SERVER_ERROR */
NOT_IMPLEMENTED_5_01 = 161, /* NOT_IMPLEMENTED */
BAD_GATEWAY_5_02 = 162, /* BAD_GATEWAY */
SERVICE_UNAVAILABLE_5_03 = 163, /* SERVICE_UNAVAILABLE */
GATEWAY_TIMEOUT_5_04 = 164, /* GATEWAY_TIMEOUT */
PROXYING_NOT_SUPPORTED_5_05 = 165, /* PROXYING_NOT_SUPPORTED */
/* Erbium errors */
MEMORY_ALLOCATION_ERROR = 192,
PACKET_SERIALIZATION_ERROR,
/* Erbium hooks */
MANUAL_RESPONSE,
PING_RESPONSE
} coap_status_t;
/* CoAP header option numbers */
typedef enum {
COAP_OPTION_IF_MATCH = 1, /* 0-8 B */
COAP_OPTION_URI_HOST = 3, /* 1-255 B */
COAP_OPTION_ETAG = 4, /* 1-8 B */
COAP_OPTION_IF_NONE_MATCH = 5, /* 0 B */
COAP_OPTION_OBSERVE = 6, /* 0-3 B */
COAP_OPTION_URI_PORT = 7, /* 0-2 B */
COAP_OPTION_LOCATION_PATH = 8, /* 0-255 B */
COAP_OPTION_URI_PATH = 11, /* 0-255 B */
COAP_OPTION_CONTENT_FORMAT = 12, /* 0-2 B */
COAP_OPTION_MAX_AGE = 14, /* 0-4 B */
COAP_OPTION_URI_QUERY = 15, /* 0-255 B */
COAP_OPTION_ACCEPT = 17, /* 0-2 B */
COAP_OPTION_LOCATION_QUERY = 20, /* 0-255 B */
COAP_OPTION_BLOCK2 = 23, /* 1-3 B */
COAP_OPTION_BLOCK1 = 27, /* 1-3 B */
COAP_OPTION_SIZE2 = 28, /* 0-4 B */
COAP_OPTION_PROXY_URI = 35, /* 1-1034 B */
COAP_OPTION_PROXY_SCHEME = 39, /* 1-255 B */
COAP_OPTION_SIZE1 = 60, /* 0-4 B */
} coap_option_t;
/* CoAP Content-Formats */
typedef enum {
TEXT_PLAIN = 0,
TEXT_XML = 1,
TEXT_CSV = 2,
TEXT_HTML = 3,
IMAGE_GIF = 21,
IMAGE_JPEG = 22,
IMAGE_PNG = 23,
IMAGE_TIFF = 24,
AUDIO_RAW = 25,
VIDEO_RAW = 26,
APPLICATION_LINK_FORMAT = 40,
APPLICATION_XML = 41,
APPLICATION_OCTET_STREAM = 42,
APPLICATION_RDF_XML = 43,
APPLICATION_SOAP_XML = 44,
APPLICATION_ATOM_XML = 45,
APPLICATION_XMPP_XML = 46,
APPLICATION_EXI = 47,
APPLICATION_FASTINFOSET = 48,
APPLICATION_SOAP_FASTINFOSET = 49,
APPLICATION_JSON = 50,
APPLICATION_X_OBIX_BINARY = 51
} coap_content_format_t;
#endif /* ER_COAP_CONSTANTS_H_ */

View file

@ -0,0 +1,520 @@
/*
* Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* CoAP implementation for the REST Engine.
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#include "sys/cc.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "er-coap-engine.h"
#define DEBUG 0
#if DEBUG
#include <stdio.h>
#define PRINTF(...) printf(__VA_ARGS__)
#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]", (lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3], (lladdr)->addr[4], (lladdr)->addr[5])
#else
#define PRINTF(...)
#define PRINT6ADDR(addr)
#define PRINTLLADDR(addr)
#endif
PROCESS(coap_engine, "CoAP Engine");
/*---------------------------------------------------------------------------*/
/*- Variables ---------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
static service_callback_t service_cbk = NULL;
/*---------------------------------------------------------------------------*/
/*- Internal API ------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
static int
coap_receive(void)
{
erbium_status_code = NO_ERROR;
PRINTF("handle_incoming_data(): received uip_datalen=%u \n",
(uint16_t)uip_datalen());
/* static declaration reduces stack peaks and program code size */
static coap_packet_t message[1]; /* this way the packet can be treated as pointer as usual */
static coap_packet_t response[1];
static coap_transaction_t *transaction = NULL;
if(uip_newdata()) {
PRINTF("receiving UDP datagram from: ");
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
PRINTF(":%u\n Length: %u\n", uip_ntohs(UIP_UDP_BUF->srcport),
uip_datalen());
erbium_status_code =
coap_parse_message(message, uip_appdata, uip_datalen());
if(erbium_status_code == NO_ERROR) {
/*TODO duplicates suppression, if required by application */
PRINTF(" Parsed: v %u, t %u, tkl %u, c %u, mid %u\n", message->version,
message->type, message->token_len, message->code, message->mid);
PRINTF(" URL: %.*s\n", message->uri_path_len, message->uri_path);
PRINTF(" Payload: %.*s\n", message->payload_len, message->payload);
/* handle requests */
if(message->code >= COAP_GET && message->code <= COAP_DELETE) {
/* use transaction buffer for response to confirmable request */
if((transaction =
coap_new_transaction(message->mid, &UIP_IP_BUF->srcipaddr,
UIP_UDP_BUF->srcport))) {
uint32_t block_num = 0;
uint16_t block_size = COAP_MAX_BLOCK_SIZE;
uint32_t block_offset = 0;
int32_t new_offset = 0;
/* prepare response */
if(message->type == COAP_TYPE_CON) {
/* reliable CON requests are answered with an ACK */
coap_init_message(response, COAP_TYPE_ACK, CONTENT_2_05,
message->mid);
} else {
/* unreliable NON requests are answered with a NON as well */
coap_init_message(response, COAP_TYPE_NON, CONTENT_2_05,
coap_get_mid());
/* mirror token */
} if(message->token_len) {
coap_set_token(response, message->token, message->token_len);
/* get offset for blockwise transfers */
}
if(coap_get_header_block2
(message, &block_num, NULL, &block_size, &block_offset)) {
PRINTF("Blockwise: block request %lu (%u/%u) @ %lu bytes\n",
block_num, block_size, COAP_MAX_BLOCK_SIZE, block_offset);
block_size = MIN(block_size, COAP_MAX_BLOCK_SIZE);
new_offset = block_offset;
}
/* invoke resource handler */
if(service_cbk) {
/* call REST framework and check if found and allowed */
if(service_cbk
(message, response, transaction->packet + COAP_MAX_HEADER_SIZE,
block_size, &new_offset)) {
if(erbium_status_code == NO_ERROR) {
/* TODO coap_handle_blockwise(request, response, start_offset, end_offset); */
/* resource is unaware of Block1 */
if(IS_OPTION(message, COAP_OPTION_BLOCK1)
&& response->code < BAD_REQUEST_4_00
&& !IS_OPTION(response, COAP_OPTION_BLOCK1)) {
PRINTF("Block1 NOT IMPLEMENTED\n");
erbium_status_code = NOT_IMPLEMENTED_5_01;
coap_error_message = "NoBlock1Support";
/* client requested Block2 transfer */
} else if(IS_OPTION(message, COAP_OPTION_BLOCK2)) {
/* unchanged new_offset indicates that resource is unaware of blockwise transfer */
if(new_offset == block_offset) {
PRINTF
("Blockwise: unaware resource with payload length %u/%u\n",
response->payload_len, block_size);
if(block_offset >= response->payload_len) {
PRINTF
("handle_incoming_data(): block_offset >= response->payload_len\n");
response->code = BAD_OPTION_4_02;
coap_set_payload(response, "BlockOutOfScope", 15); /* a const char str[] and sizeof(str) produces larger code size */
} else {
coap_set_header_block2(response, block_num,
response->payload_len -
block_offset > block_size,
block_size);
coap_set_payload(response,
response->payload + block_offset,
MIN(response->payload_len -
block_offset, block_size));
} /* if(valid offset) */
/* resource provides chunk-wise data */
} else {
PRINTF("Blockwise: blockwise resource, new offset %ld\n",
new_offset);
coap_set_header_block2(response, block_num,
new_offset != -1
|| response->payload_len >
block_size, block_size);
if(response->payload_len > block_size) {
coap_set_payload(response, response->payload,
block_size);
}
} /* if(resource aware of blockwise) */
/* Resource requested Block2 transfer */
} else if(new_offset != 0) {
PRINTF
("Blockwise: no block option for blockwise resource, using block size %u\n",
COAP_MAX_BLOCK_SIZE);
coap_set_header_block2(response, 0, new_offset != -1,
COAP_MAX_BLOCK_SIZE);
coap_set_payload(response, response->payload,
MIN(response->payload_len,
COAP_MAX_BLOCK_SIZE));
} /* blockwise transfer handling */
} /* no errors/hooks */
/* successful service callback */
/* serialize response */
}
if(erbium_status_code == NO_ERROR) {
if((transaction->packet_len = coap_serialize_message(response,
transaction->
packet)) ==
0) {
erbium_status_code = PACKET_SERIALIZATION_ERROR;
}
}
} else {
erbium_status_code = NOT_IMPLEMENTED_5_01;
coap_error_message = "NoServiceCallbck"; /* no 'a' to fit into 16 bytes */
} /* if(service callback) */
} else {
erbium_status_code = SERVICE_UNAVAILABLE_5_03;
coap_error_message = "NoFreeTraBuffer";
} /* if(transaction buffer) */
/* handle responses */
} else {
if(message->type == COAP_TYPE_CON && message->code == 0) {
PRINTF("Received Ping\n");
erbium_status_code = PING_RESPONSE;
} else if(message->type == COAP_TYPE_ACK) {
/* transactions are closed through lookup below */
PRINTF("Received ACK\n");
} else if(message->type == COAP_TYPE_RST) {
PRINTF("Received RST\n");
/* cancel possible subscriptions */
coap_remove_observer_by_mid(&UIP_IP_BUF->srcipaddr,
UIP_UDP_BUF->srcport, message->mid);
}
if((transaction = coap_get_transaction_by_mid(message->mid))) {
/* free transaction memory before callback, as it may create a new transaction */
restful_response_handler callback = transaction->callback;
void *callback_data = transaction->callback_data;
coap_clear_transaction(transaction);
/* check if someone registered for the response */
if(callback) {
callback(callback_data, message);
}
}
/* if(ACKed transaction) */
transaction = NULL;
#if COAP_OBSERVE_CLIENT
/* if observe notification */
if((message->type == COAP_TYPE_CON || message->type == COAP_TYPE_NON)
&& IS_OPTION(message, COAP_OPTION_OBSERVE)) {
PRINTF("Observe [%u]\n", message->observe);
coap_handle_notification(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport,
message);
}
#endif /* COAP_OBSERVE_CLIENT */
} /* request or response */
} /* parsed correctly */
/* if(parsed correctly) */
if(erbium_status_code == NO_ERROR) {
if(transaction) {
coap_send_transaction(transaction);
}
} else if(erbium_status_code == MANUAL_RESPONSE) {
PRINTF("Clearing transaction for manual response");
coap_clear_transaction(transaction);
} else {
coap_message_type_t reply_type = COAP_TYPE_ACK;
PRINTF("ERROR %u: %s\n", erbium_status_code, coap_error_message);
coap_clear_transaction(transaction);
if(erbium_status_code == PING_RESPONSE) {
erbium_status_code = 0;
reply_type = COAP_TYPE_RST;
} else if(erbium_status_code >= 192) {
/* set to sendable error code */
erbium_status_code = INTERNAL_SERVER_ERROR_5_00;
/* reuse input buffer for error message */
}
coap_init_message(message, reply_type, erbium_status_code,
message->mid);
coap_set_payload(message, coap_error_message,
strlen(coap_error_message));
coap_send_message(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport,
uip_appdata, coap_serialize_message(message,
uip_appdata));
}
}
/* if(new data) */
return erbium_status_code;
}
/*---------------------------------------------------------------------------*/
void
coap_init_engine(void)
{
process_start(&coap_engine, NULL);
}
/*---------------------------------------------------------------------------*/
void
coap_set_service_callback(service_callback_t callback)
{
service_cbk = callback;
}
/*---------------------------------------------------------------------------*/
rest_resource_flags_t
coap_get_rest_method(void *packet)
{
return (rest_resource_flags_t)(1 <<
(((coap_packet_t *)packet)->code - 1));
}
/*---------------------------------------------------------------------------*/
/*- Server Part -------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/* the discover resource is automatically included for CoAP */
extern resource_t res_well_known_core;
#ifdef WITH_DTLS
extern resource_t res_dtls;
#endif
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(coap_engine, ev, data)
{
PROCESS_BEGIN();
PRINTF("Starting %s receiver...\n", coap_rest_implementation.name);
rest_activate_resource(&res_well_known_core, ".well-known/core");
coap_register_as_transaction_handler();
coap_init_connection(SERVER_LISTEN_PORT);
while(1) {
PROCESS_YIELD();
if(ev == tcpip_event) {
coap_receive();
} else if(ev == PROCESS_EVENT_TIMER) {
/* retransmissions are handled here */
coap_check_transactions();
}
} /* while (1) */
PROCESS_END();
}
/*---------------------------------------------------------------------------*/
/*- Client Part -------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
void
coap_blocking_request_callback(void *callback_data, void *response)
{
struct request_state_t *state = (struct request_state_t *)callback_data;
state->response = (coap_packet_t *)response;
process_poll(state->process);
}
/*---------------------------------------------------------------------------*/
PT_THREAD(coap_blocking_request
(struct request_state_t *state, process_event_t ev,
uip_ipaddr_t *remote_ipaddr, uint16_t remote_port,
coap_packet_t *request,
blocking_response_handler request_callback))
{
PT_BEGIN(&state->pt);
static uint8_t more;
static uint32_t res_block;
static uint8_t block_error;
state->block_num = 0;
state->response = NULL;
state->process = PROCESS_CURRENT();
more = 0;
res_block = 0;
block_error = 0;
do {
request->mid = coap_get_mid();
if((state->transaction = coap_new_transaction(request->mid, remote_ipaddr,
remote_port))) {
state->transaction->callback = coap_blocking_request_callback;
state->transaction->callback_data = state;
if(state->block_num > 0) {
coap_set_header_block2(request, state->block_num, 0,
REST_MAX_CHUNK_SIZE);
}
state->transaction->packet_len = coap_serialize_message(request,
state->
transaction->
packet);
coap_send_transaction(state->transaction);
PRINTF("Requested #%lu (MID %u)\n", state->block_num, request->mid);
PT_YIELD_UNTIL(&state->pt, ev == PROCESS_EVENT_POLL);
if(!state->response) {
PRINTF("Server not responding\n");
PT_EXIT(&state->pt);
}
coap_get_header_block2(state->response, &res_block, &more, NULL, NULL);
PRINTF("Received #%lu%s (%u bytes)\n", res_block, more ? "+" : "",
state->response->payload_len);
if(res_block == state->block_num) {
request_callback(state->response);
++(state->block_num);
} else {
PRINTF("WRONG BLOCK %lu/%lu\n", res_block, state->block_num);
++block_error;
}
} else {
PRINTF("Could not allocate transaction buffer");
PT_EXIT(&state->pt);
}
} while(more && block_error < COAP_MAX_ATTEMPTS);
PT_END(&state->pt);
}
/*---------------------------------------------------------------------------*/
/*- REST Engine Interface ---------------------------------------------------*/
/*---------------------------------------------------------------------------*/
const struct rest_implementation coap_rest_implementation = {
"CoAP-18",
coap_init_engine,
coap_set_service_callback,
coap_get_header_uri_path,
coap_get_rest_method,
coap_set_status_code,
coap_get_header_content_format,
coap_set_header_content_format,
coap_get_header_accept,
coap_get_header_size2,
coap_set_header_size2,
coap_get_header_max_age,
coap_set_header_max_age,
coap_set_header_etag,
coap_get_header_if_match,
coap_get_header_if_none_match,
coap_get_header_uri_host,
coap_set_header_location_path,
coap_get_payload,
coap_set_payload,
coap_get_header_uri_query,
coap_get_query_variable,
coap_get_post_variable,
coap_notify_observers,
coap_observe_handler,
{
CONTENT_2_05,
CREATED_2_01,
CHANGED_2_04,
DELETED_2_02,
VALID_2_03,
BAD_REQUEST_4_00,
UNAUTHORIZED_4_01,
BAD_OPTION_4_02,
FORBIDDEN_4_03,
NOT_FOUND_4_04,
METHOD_NOT_ALLOWED_4_05,
NOT_ACCEPTABLE_4_06,
REQUEST_ENTITY_TOO_LARGE_4_13,
UNSUPPORTED_MEDIA_TYPE_4_15,
INTERNAL_SERVER_ERROR_5_00,
NOT_IMPLEMENTED_5_01,
BAD_GATEWAY_5_02,
SERVICE_UNAVAILABLE_5_03,
GATEWAY_TIMEOUT_5_04,
PROXYING_NOT_SUPPORTED_5_05
},
{
TEXT_PLAIN,
TEXT_XML,
TEXT_CSV,
TEXT_HTML,
IMAGE_GIF,
IMAGE_JPEG,
IMAGE_PNG,
IMAGE_TIFF,
AUDIO_RAW,
VIDEO_RAW,
APPLICATION_LINK_FORMAT,
APPLICATION_XML,
APPLICATION_OCTET_STREAM,
APPLICATION_RDF_XML,
APPLICATION_SOAP_XML,
APPLICATION_ATOM_XML,
APPLICATION_XMPP_XML,
APPLICATION_EXI,
APPLICATION_FASTINFOSET,
APPLICATION_SOAP_FASTINFOSET,
APPLICATION_JSON,
APPLICATION_X_OBIX_BINARY
}
};
/*---------------------------------------------------------------------------*/

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich
* Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -31,61 +31,56 @@
/**
* \file
* CoAP implementation of the REST Engine
* CoAP implementation for the REST Engine.
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#ifndef COAP_SERVER_H_
#define COAP_SERVER_H_
#if !defined(REST)
#error "Define REST to \"coap_rest_implementation\""
#endif
#include "er-coap-03.h"
#include "er-coap-03-transactions.h"
#include "er-coap-03-observing.h"
#ifndef ER_COAP_ENGINE_H_
#define ER_COAP_ENGINE_H_
#include "pt.h"
/* Declare server process */
PROCESS_NAME(coap_receiver);
#include "er-coap.h"
#include "er-coap-transactions.h"
#include "er-coap-observe.h"
#include "er-coap-separate.h"
#include "er-coap-observe-client.h"
#define SERVER_LISTEN_PORT UIP_HTONS(COAP_SERVER_PORT)
typedef coap_packet_t rest_request_t;
typedef coap_packet_t rest_response_t;
extern const struct rest_implementation coap_rest_implementation;
void coap_init_engine(void);
void coap_receiver_init(void);
/*-----------------------------------------------------------------------------------*/
/*- Client part ---------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/*- Client Part -------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
struct request_state_t {
struct pt pt;
struct process *process;
coap_transaction_t *transaction;
coap_packet_t *response;
uint32_t block_num;
struct pt pt;
struct process *process;
coap_transaction_t *transaction;
coap_packet_t *response;
uint32_t block_num;
};
typedef void (*blocking_response_handler) (void* response);
typedef void (*blocking_response_handler)(void *response);
PT_THREAD(coap_blocking_request(struct request_state_t *state, process_event_t ev,
uip_ipaddr_t *remote_ipaddr, uint16_t remote_port,
coap_packet_t *request,
blocking_response_handler request_callback));
PT_THREAD(coap_blocking_request
(struct request_state_t *state, process_event_t ev,
uip_ipaddr_t *remote_ipaddr, uint16_t remote_port,
coap_packet_t *request,
blocking_response_handler request_callback));
#define COAP_BLOCKING_REQUEST(server_addr, server_port, request, chunk_handler) \
static struct request_state_t request_state; \
PT_SPAWN(process_pt, &request_state.pt, \
{ \
static struct request_state_t request_state; \
PT_SPAWN(process_pt, &request_state.pt, \
coap_blocking_request(&request_state, ev, \
server_addr, server_port, \
request, chunk_handler) \
);
/*-----------------------------------------------------------------------------------*/
); \
}
/*---------------------------------------------------------------------------*/
#endif /* COAP_SERVER_H_ */
#endif /* ER_COAP_ENGINE_H_ */

View file

@ -0,0 +1,342 @@
/*
* Copyright (c) 2014, Daniele Alessandrelli.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*
*/
/*
* \file
* Extension to Erbium for enabling CoAP observe clients
* \author
* Daniele Alessandrelli <daniele.alessandrelli@gmail.com>
*/
#include <stdio.h>
#include <string.h>
#include "er-coap.h"
#include "er-coap-observe-client.h"
/* Compile this code only if client-side support for CoAP Observe is required */
#if COAP_OBSERVE_CLIENT
#define DEBUG 1
#if DEBUG
#define PRINTF(...) printf(__VA_ARGS__)
#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:" \
"%02x%02x:%02x%02x:%02x%02x:%02x%02x]", \
((uint8_t *)addr)[0], ((uint8_t *)addr)[1], \
((uint8_t *)addr)[2], ((uint8_t *)addr)[3], \
((uint8_t *)addr)[4], ((uint8_t *)addr)[5], \
((uint8_t *)addr)[6], ((uint8_t *)addr)[7], \
((uint8_t *)addr)[8], ((uint8_t *)addr)[9], \
((uint8_t *)addr)[10], ((uint8_t *)addr)[11], \
((uint8_t *)addr)[12], ((uint8_t *)addr)[13], \
((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]", \
(lladdr)->addr[0], (lladdr)->addr[1], \
(lladdr)->addr[2], (lladdr)->addr[3], \
(lladdr)->addr[4], (lladdr)->addr[5])
#else
#define PRINTF(...)
#define PRINT6ADDR(addr)
#define PRINTLLADDR(addr)
#endif
MEMB(obs_subjects_memb, coap_observee_t, COAP_MAX_OBSERVEES);
LIST(obs_subjects_list);
/*----------------------------------------------------------------------------*/
static size_t
get_token(void *packet, const uint8_t **token)
{
coap_packet_t *const coap_pkt = (coap_packet_t *)packet;
*token = coap_pkt->token;
return coap_pkt->token_len;
}
/*----------------------------------------------------------------------------*/
static int
set_token(void *packet, const uint8_t *token, size_t token_len)
{
coap_packet_t *const coap_pkt = (coap_packet_t *)packet;
coap_pkt->token_len = MIN(COAP_TOKEN_LEN, token_len);
memcpy(coap_pkt->token, token, coap_pkt->token_len);
return coap_pkt->token_len;
}
/*----------------------------------------------------------------------------*/
coap_observee_t *
coap_obs_add_observee(uip_ipaddr_t *addr, uint16_t port,
const uint8_t *token, size_t token_len, const char *url,
notification_callback_t notification_callback,
void *data)
{
coap_observee_t *o;
/* Remove existing observe relationship, if any. */
coap_obs_remove_observee_by_url(addr, port, url);
o = memb_alloc(&obs_subjects_memb);
if(o) {
o->url = url;
uip_ipaddr_copy(&o->addr, addr);
o->port = port;
o->token_len = token_len;
memcpy(o->token, token, token_len);
/* o->last_mid = 0; */
o->notification_callback = notification_callback;
o->data = data;
/* stimer_set(&o->refresh_timer, COAP_OBSERVING_REFRESH_INTERVAL); */
PRINTF("Adding obs_subject for /%s [0x%02X%02X]\n", o->url, o->token[0],
o->token[1]);
list_add(obs_subjects_list, o);
}
return o;
}
/*----------------------------------------------------------------------------*/
void
coap_obs_remove_observee(coap_observee_t *o)
{
PRINTF("Removing obs_subject for /%s [0x%02X%02X]\n", o->url, o->token[0],
o->token[1]);
memb_free(&obs_subjects_memb, o);
list_remove(obs_subjects_list, o);
}
/*----------------------------------------------------------------------------*/
coap_observee_t *
coap_get_obs_subject_by_token(const uint8_t *token, size_t token_len)
{
coap_observee_t *obs = NULL;
for(obs = (coap_observee_t *)list_head(obs_subjects_list); obs;
obs = obs->next) {
PRINTF("Looking for token 0x%02X%02X\n", token[0], token[1]);
if(obs->token_len == token_len
&& memcmp(obs->token, token, token_len) == 0) {
return obs;
}
}
return NULL;
}
/*----------------------------------------------------------------------------*/
int
coap_obs_remove_observee_by_token(uip_ipaddr_t *addr, uint16_t port,
uint8_t *token, size_t token_len)
{
int removed = 0;
coap_observee_t *obs = NULL;
for(obs = (coap_observee_t *)list_head(obs_subjects_list); obs;
obs = obs->next) {
PRINTF("Remove check Token 0x%02X%02X\n", token[0], token[1]);
if(uip_ipaddr_cmp(&obs->addr, addr)
&& obs->port == port
&& obs->token_len == token_len
&& memcmp(obs->token, token, token_len) == 0) {
coap_obs_remove_observee(obs);
removed++;
}
}
return removed;
}
/*----------------------------------------------------------------------------*/
int
coap_obs_remove_observee_by_url(uip_ipaddr_t *addr, uint16_t port,
const char *url)
{
int removed = 0;
coap_observee_t *obs = NULL;
for(obs = (coap_observee_t *)list_head(obs_subjects_list); obs;
obs = obs->next) {
PRINTF("Remove check URL %s\n", url);
if(uip_ipaddr_cmp(&obs->addr, addr)
&& obs->port == port
&& (obs->url == url || memcmp(obs->url, url, strlen(obs->url)) == 0)) {
coap_obs_remove_observee(obs);
removed++;
}
}
return removed;
}
/*----------------------------------------------------------------------------*/
static void
simple_reply(coap_message_type_t type, uip_ip6addr_t *addr, uint16_t port,
coap_packet_t *notification)
{
static coap_packet_t response[1];
size_t len;
coap_init_message(response, type, NO_ERROR, notification->mid);
len = coap_serialize_message(response, uip_appdata);
coap_send_message(addr, port, uip_appdata, len);
}
/*----------------------------------------------------------------------------*/
static coap_notification_flag_t
classify_notification(void *response, int first)
{
coap_packet_t *pkt;
pkt = (coap_packet_t *)response;
if(!pkt) {
PRINTF("no response\n");
return NO_REPLY_FROM_SERVER;
}
PRINTF("server replied\n");
if(!IS_RESPONSE_CODE_2_XX(pkt)) {
PRINTF("error response code\n");
return ERROR_RESPONSE_CODE;
}
if(!IS_OPTION(pkt, COAP_OPTION_OBSERVE)) {
PRINTF("server does not support observe\n");
return OBSERVE_NOT_SUPPORTED;
}
if(first) {
return OBSERVE_OK;
}
return NOTIFICATION_OK;
}
/*----------------------------------------------------------------------------*/
void
coap_handle_notification(uip_ipaddr_t *addr, uint16_t port,
coap_packet_t *notification)
{
coap_packet_t *pkt;
const uint8_t *token;
int token_len;
coap_observee_t *obs;
coap_notification_flag_t flag;
uint32_t observe;
PRINTF("coap_handle_notification()\n");
pkt = (coap_packet_t *)notification;
token_len = get_token(pkt, &token);
PRINTF("Getting token\n");
if(0 == token_len) {
PRINTF("Error while handling coap observe notification: "
"no token in message\n");
return;
}
PRINTF("Getting observee info\n");
obs = coap_get_obs_subject_by_token(token, token_len);
if(NULL == obs) {
PRINTF("Error while handling coap observe notification: "
"no matching token found\n");
simple_reply(COAP_TYPE_RST, addr, port, notification);
return;
}
if(notification->type == COAP_TYPE_CON) {
simple_reply(COAP_TYPE_ACK, addr, port, notification);
}
if(obs->notification_callback != NULL) {
flag = classify_notification(notification, 0);
/* TODO: the following mechanism for discarding duplicates is too trivial */
/* refer to Observe RFC for a better solution */
if(flag == NOTIFICATION_OK) {
coap_get_header_observe(notification, &observe);
if(observe == obs->last_observe) {
PRINTF("Discarding duplicate\n");
return;
}
obs->last_observe = observe;
}
obs->notification_callback(obs, notification, flag);
}
}
/*----------------------------------------------------------------------------*/
static void
handle_obs_registration_response(void *data, void *response)
{
coap_observee_t *obs;
notification_callback_t notification_callback;
coap_notification_flag_t flag;
PRINTF("handle_obs_registration_response(): ");
obs = (coap_observee_t *)data;
notification_callback = obs->notification_callback;
flag = classify_notification(response, 1);
if(notification_callback) {
notification_callback(obs, response, flag);
}
if(flag != OBSERVE_OK) {
coap_obs_remove_observee(obs);
}
}
/*----------------------------------------------------------------------------*/
uint8_t
coap_generate_token(uint8_t **token_ptr)
{
static uint8_t token = 0;
token++;
/* FIXME: we should check that this token is not already used */
*token_ptr = (uint8_t *)&token;
return sizeof(token);
}
/*----------------------------------------------------------------------------*/
coap_observee_t *
coap_obs_request_registration(uip_ipaddr_t *addr, uint16_t port, char *uri,
notification_callback_t notification_callback,
void *data)
{
coap_packet_t request[1];
coap_transaction_t *t;
uint8_t *token;
uint8_t token_len;
coap_observee_t *obs;
obs = NULL;
coap_init_message(request, COAP_TYPE_CON, COAP_GET, coap_get_mid());
coap_set_header_uri_path(request, uri);
coap_set_header_observe(request, 0);
token_len = coap_generate_token(&token);
set_token(request, token, token_len);
t = coap_new_transaction(request->mid, addr, port);
if(t) {
obs = coap_obs_add_observee(addr, port, (uint8_t *)token, token_len, uri,
notification_callback, data);
if(obs) {
t->callback = handle_obs_registration_response;
t->callback_data = obs;
t->packet_len = coap_serialize_message(request, t->packet);
coap_send_transaction(t);
} else {
PRINTF("Could not allocate obs_subject resource buffer");
coap_clear_transaction(t);
}
} else {
PRINTF("Could not allocate transaction buffer");
}
return obs;
}
#endif /* COAP_OBSERVE_CLIENT */

View file

@ -0,0 +1,121 @@
/*
* Copyright (c) 2014, Daniele Alessandrelli.
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*
*/
/*
* \file
* Extension to Erbium for enabling CoAP observe clients
* \author
* Daniele Alessandrelli <daniele.alessandrelli@gmail.com>
*/
#ifndef COAP_OBSERVING_CLIENT_H_
#define COAP_OBSERVING_CLIENT_H_
#include "er-coap.h"
#include "er-coap-transactions.h"
#ifndef COAP_OBSERVE_CLIENT
#define COAP_OBSERVE_CLIENT 0
#endif
#ifdef COAP_CONF_MAX_OBSERVEES
#define COAP_MAX_OBSERVEES COAP_CONF_MAX_OBSERVEES
#else
#define COAP_MAX_OBSERVEES 4
#endif /* COAP_CONF_MAX_OBSERVEES */
#if COAP_MAX_OPEN_TRANSACTIONS < COAP_MAX_OBSERVEES
#warning "COAP_MAX_OPEN_TRANSACTIONS smaller than COAP_MAX_OBSERVEES: " \
"this may be a problem"
#endif
#define IS_RESPONSE_CODE_2_XX(message) (64 < message->code \
&& message->code < 128)
/*----------------------------------------------------------------------------*/
typedef enum {
OBSERVE_OK,
NOTIFICATION_OK,
OBSERVE_NOT_SUPPORTED,
ERROR_RESPONSE_CODE,
NO_REPLY_FROM_SERVER,
} coap_notification_flag_t;
/*----------------------------------------------------------------------------*/
typedef struct coap_observee_s coap_observee_t;
typedef void (*notification_callback_t)(coap_observee_t *subject,
void *notification,
coap_notification_flag_t);
struct coap_observee_s {
coap_observee_t *next; /* for LIST */
uip_ipaddr_t addr;
uint16_t port;
const char *url;
uint8_t token_len;
uint8_t token[COAP_TOKEN_LEN];
void *data; /* generic pointer for storing user data */
notification_callback_t notification_callback;
uint32_t last_observe;
};
/*----------------------------------------------------------------------------*/
coap_observee_t *coap_obs_add_observee(uip_ipaddr_t *addr, uint16_t port,
const uint8_t *token, size_t token_len,
const char *url,
notification_callback_t
notification_callback, void *data);
void coap_obs_remove_observee(coap_observee_t *o);
coap_observee_t *coap_obs_get_observee_by_token(const uint8_t *token,
size_t token_len);
int coap_obs_remove_observee_by_token(uip_ipaddr_t *addr, uint16_t port,
uint8_t *token, size_t token_len);
int coap_obs_remove_observee_by_url(uip_ipaddr_t *addr, uint16_t port,
const char *url);
void coap_handle_notification(uip_ipaddr_t *, uint16_t port,
coap_packet_t *notification);
coap_observee_t *coap_obs_request_registration(uip_ipaddr_t *addr,
uint16_t port, char *uri,
notification_callback_t
notification_callback,
void *data);
/* TODO: this function may be moved to er-coap.c */
uint8_t coap_generate_token(uint8_t **token_ptr);
#endif /* COAP_OBSERVING_CLIENT_H_ */

View file

@ -0,0 +1,306 @@
/*
* Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* CoAP module for observing resources (draft-ietf-core-observe-11).
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#include <stdio.h>
#include <string.h>
#include "er-coap-observe.h"
#define DEBUG 0
#if DEBUG
#include <stdio.h>
#define PRINTF(...) printf(__VA_ARGS__)
#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]", (lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3], (lladdr)->addr[4], (lladdr)->addr[5])
#else
#define PRINTF(...)
#define PRINT6ADDR(addr)
#define PRINTLLADDR(addr)
#endif
/*---------------------------------------------------------------------------*/
MEMB(observers_memb, coap_observer_t, COAP_MAX_OBSERVERS);
LIST(observers_list);
/*---------------------------------------------------------------------------*/
/*- Internal API ------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
static coap_observer_t *
add_observer(uip_ipaddr_t *addr, uint16_t port, const uint8_t *token,
size_t token_len, const char *uri, int uri_len)
{
/* Remove existing observe relationship, if any. */
coap_remove_observer_by_uri(addr, port, uri);
coap_observer_t *o = memb_alloc(&observers_memb);
if(o) {
int max = sizeof(o->url) - 1;
if(max > uri_len) {
max = uri_len;
}
memcpy(o->url, uri, max);
o->url[max] = 0;
uip_ipaddr_copy(&o->addr, addr);
o->port = port;
o->token_len = token_len;
memcpy(o->token, token, token_len);
o->last_mid = 0;
PRINTF("Adding observer (%u/%u) for /%s [0x%02X%02X]\n",
list_length(observers_list) + 1, COAP_MAX_OBSERVERS,
o->url, o->token[0], o->token[1]);
list_add(observers_list, o);
}
return o;
}
/*---------------------------------------------------------------------------*/
/*- Removal -----------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
void
coap_remove_observer(coap_observer_t *o)
{
PRINTF("Removing observer for /%s [0x%02X%02X]\n", o->url, o->token[0],
o->token[1]);
memb_free(&observers_memb, o);
list_remove(observers_list, o);
}
/*---------------------------------------------------------------------------*/
int
coap_remove_observer_by_client(uip_ipaddr_t *addr, uint16_t port)
{
int removed = 0;
coap_observer_t *obs = NULL;
for(obs = (coap_observer_t *)list_head(observers_list); obs;
obs = obs->next) {
PRINTF("Remove check client ");
PRINT6ADDR(addr);
PRINTF(":%u\n", port);
if(uip_ipaddr_cmp(&obs->addr, addr) && obs->port == port) {
coap_remove_observer(obs);
removed++;
}
}
return removed;
}
/*---------------------------------------------------------------------------*/
int
coap_remove_observer_by_token(uip_ipaddr_t *addr, uint16_t port,
uint8_t *token, size_t token_len)
{
int removed = 0;
coap_observer_t *obs = NULL;
for(obs = (coap_observer_t *)list_head(observers_list); obs;
obs = obs->next) {
PRINTF("Remove check Token 0x%02X%02X\n", token[0], token[1]);
if(uip_ipaddr_cmp(&obs->addr, addr) && obs->port == port
&& obs->token_len == token_len
&& memcmp(obs->token, token, token_len) == 0) {
coap_remove_observer(obs);
removed++;
}
}
return removed;
}
/*---------------------------------------------------------------------------*/
int
coap_remove_observer_by_uri(uip_ipaddr_t *addr, uint16_t port,
const char *uri)
{
int removed = 0;
coap_observer_t *obs = NULL;
for(obs = (coap_observer_t *)list_head(observers_list); obs;
obs = obs->next) {
PRINTF("Remove check URL %p\n", uri);
if((addr == NULL
|| (uip_ipaddr_cmp(&obs->addr, addr) && obs->port == port))
&& (obs->url == uri || memcmp(obs->url, uri, strlen(obs->url)) == 0)) {
coap_remove_observer(obs);
removed++;
}
}
return removed;
}
/*---------------------------------------------------------------------------*/
int
coap_remove_observer_by_mid(uip_ipaddr_t *addr, uint16_t port, uint16_t mid)
{
int removed = 0;
coap_observer_t *obs = NULL;
for(obs = (coap_observer_t *)list_head(observers_list); obs;
obs = obs->next) {
PRINTF("Remove check MID %u\n", mid);
if(uip_ipaddr_cmp(&obs->addr, addr) && obs->port == port
&& obs->last_mid == mid) {
coap_remove_observer(obs);
removed++;
}
}
return removed;
}
/*---------------------------------------------------------------------------*/
/*- Notification ------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
void
coap_notify_observers(resource_t *resource)
{
coap_notify_observers_sub(resource, NULL);
}
void
coap_notify_observers_sub(resource_t *resource, const char *subpath)
{
/* build notification */
coap_packet_t notification[1]; /* this way the packet can be treated as pointer as usual */
coap_packet_t request[1]; /* this way the packet can be treated as pointer as usual */
coap_observer_t *obs = NULL;
int url_len, obs_url_len;
char url[COAP_OBSERVER_URL_LEN];
url_len = strlen(resource->url);
strncpy(url, resource->url, COAP_OBSERVER_URL_LEN - 1);
if(url_len < COAP_OBSERVER_URL_LEN - 1 && subpath != NULL) {
strncpy(&url[url_len], subpath, COAP_OBSERVER_URL_LEN - url_len - 1);
}
/* Ensure url is null terminated because strncpy does not guarantee this */
url[COAP_OBSERVER_URL_LEN - 1] = '\0';
/* url now contains the notify URL that needs to match the observer */
PRINTF("Observe: Notification from %s\n", url);
coap_init_message(notification, COAP_TYPE_NON, CONTENT_2_05, 0);
/* create a "fake" request for the URI */
coap_init_message(request, COAP_TYPE_CON, COAP_GET, 0);
coap_set_header_uri_path(request, url);
/* iterate over observers */
url_len = strlen(url);
for(obs = (coap_observer_t *)list_head(observers_list); obs;
obs = obs->next) {
obs_url_len = strlen(obs->url);
/* Do a match based on the parent/sub-resource match so that it is
possible to do parent-node observe */
if((obs_url_len == url_len
|| (obs_url_len > url_len
&& (resource->flags & HAS_SUB_RESOURCES)
&& obs->url[url_len] == '/'))
&& strncmp(url, obs->url, url_len) == 0) {
coap_transaction_t *transaction = NULL;
/*TODO implement special transaction for CON, sharing the same buffer to allow for more observers */
if((transaction = coap_new_transaction(coap_get_mid(), &obs->addr, obs->port))) {
if(obs->obs_counter % COAP_OBSERVE_REFRESH_INTERVAL == 0) {
PRINTF(" Force Confirmable for\n");
notification->type = COAP_TYPE_CON;
}
PRINTF(" Observer ");
PRINT6ADDR(&obs->addr);
PRINTF(":%u\n", obs->port);
/* update last MID for RST matching */
obs->last_mid = transaction->mid;
/* prepare response */
notification->mid = transaction->mid;
resource->get_handler(request, notification,
transaction->packet + COAP_MAX_HEADER_SIZE,
REST_MAX_CHUNK_SIZE, NULL);
if(notification->code < BAD_REQUEST_4_00) {
coap_set_header_observe(notification, (obs->obs_counter)++);
}
coap_set_token(notification, obs->token, obs->token_len);
transaction->packet_len =
coap_serialize_message(notification, transaction->packet);
coap_send_transaction(transaction);
}
}
}
}
/*---------------------------------------------------------------------------*/
void
coap_observe_handler(resource_t *resource, void *request, void *response)
{
coap_packet_t *const coap_req = (coap_packet_t *)request;
coap_packet_t *const coap_res = (coap_packet_t *)response;
coap_observer_t * obs;
if(coap_req->code == COAP_GET && coap_res->code < 128) { /* GET request and response without error code */
if(IS_OPTION(coap_req, COAP_OPTION_OBSERVE)) {
if(coap_req->observe == 0) {
obs = add_observer(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport,
coap_req->token, coap_req->token_len,
coap_req->uri_path, coap_req->uri_path_len);
if(obs) {
coap_set_header_observe(coap_res, (obs->obs_counter)++);
/*
* Following payload is for demonstration purposes only.
* A subscription should return the same representation as a normal GET.
* Uncomment if you want an information about the avaiable observers.
*/
#if 0
static char content[16];
coap_set_payload(coap_res,
content,
snprintf(content, sizeof(content), "Added %u/%u",
list_length(observers_list),
COAP_MAX_OBSERVERS));
#endif
} else {
coap_res->code = SERVICE_UNAVAILABLE_5_03;
coap_set_payload(coap_res, "TooManyObservers", 16);
}
} else if(coap_req->observe == 1) {
/* remove client if it is currently observe */
coap_remove_observer_by_token(&UIP_IP_BUF->srcipaddr,
UIP_UDP_BUF->srcport, coap_req->token,
coap_req->token_len);
}
}
}
}
/*---------------------------------------------------------------------------*/

View file

@ -31,53 +31,58 @@
/**
* \file
* CoAP module for observing resources
* CoAP module for observing resources (draft-ietf-core-observe-11).
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#ifndef COAP_OBSERVING_H_
#define COAP_OBSERVING_H_
#ifndef COAP_OBSERVE_H_
#define COAP_OBSERVE_H_
#include "sys/stimer.h"
#include "er-coap-13.h"
#include "er-coap-13-transactions.h"
#include "er-coap.h"
#include "er-coap-transactions.h"
#include "stimer.h"
#ifndef COAP_MAX_OBSERVERS
#define COAP_MAX_OBSERVERS COAP_MAX_OPEN_TRANSACTIONS-1
#endif /* COAP_MAX_OBSERVERS */
#define COAP_OBSERVER_URL_LEN 20
/* Interval in seconds in which NON notifies are changed to CON notifies to check client. */
#define COAP_OBSERVING_REFRESH_INTERVAL 60
#if COAP_MAX_OPEN_TRANSACTIONS<COAP_MAX_OBSERVERS
#warning "COAP_MAX_OPEN_TRANSACTIONS smaller than COAP_MAX_OBSERVERS: cannot handle CON notifications"
#endif
typedef struct coap_observable {
uint32_t observe_clock;
struct stimer orphan_timer;
list_t observers;
coap_packet_t notification;
uint8_t buffer[COAP_MAX_PACKET_SIZE + 1];
} coap_observable_t;
typedef struct coap_observer {
struct coap_observer *next; /* for LIST */
struct coap_observer *next; /* for LIST */
const char *url;
char url[COAP_OBSERVER_URL_LEN];
uip_ipaddr_t addr;
uint16_t port;
uint8_t token_len;
uint8_t token[COAP_TOKEN_LEN];
uint16_t last_mid;
struct stimer refresh_timer;
int32_t obs_counter;
struct etimer retrans_timer;
uint8_t retrans_counter;
} coap_observer_t;
list_t coap_get_observers(void);
coap_observer_t *coap_add_observer(uip_ipaddr_t *addr, uint16_t port, const uint8_t *token, size_t token_len, const char *url);
void coap_remove_observer(coap_observer_t *o);
int coap_remove_observer_by_client(uip_ipaddr_t *addr, uint16_t port);
int coap_remove_observer_by_token(uip_ipaddr_t *addr, uint16_t port, uint8_t *token, size_t token_len);
int coap_remove_observer_by_url(uip_ipaddr_t *addr, uint16_t port, const char *url);
int coap_remove_observer_by_mid(uip_ipaddr_t *addr, uint16_t port, uint16_t mid);
int coap_remove_observer_by_token(uip_ipaddr_t *addr, uint16_t port,
uint8_t *token, size_t token_len);
int coap_remove_observer_by_uri(uip_ipaddr_t *addr, uint16_t port,
const char *uri);
int coap_remove_observer_by_mid(uip_ipaddr_t *addr, uint16_t port,
uint16_t mid);
void coap_notify_observers(resource_t *resource, int32_t obs_counter, void *notification);
void coap_notify_observers(resource_t *resource);
void coap_notify_observers_sub(resource_t *resource, const char *subpath);
void coap_observe_handler(resource_t *resource, void *request, void *response);
void coap_observe_handler(resource_t *resource, void *request,
void *response);
#endif /* COAP_OBSERVING_H_ */
#endif /* COAP_OBSERVE_H_ */

View file

@ -0,0 +1,208 @@
/*
* Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* /.well-known/core resource implementation.
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#include <string.h>
#include "er-coap-engine.h"
#define DEBUG 0
#if DEBUG
#include <stdio.h>
#define PRINTF(...) printf(__VA_ARGS__)
#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]", (lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3], (lladdr)->addr[4], (lladdr)->addr[5])
#else
#define PRINTF(...)
#define PRINT6ADDR(addr)
#define PRINTLLADDR(addr)
#endif
#define ADD_CHAR_IF_POSSIBLE(char) \
if(strpos >= *offset && bufpos < preferred_size) { \
buffer[bufpos++] = char; \
} \
++strpos
#define ADD_STRING_IF_POSSIBLE(string, op) \
tmplen = strlen(string); \
if(strpos + tmplen > *offset) { \
bufpos += snprintf((char *)buffer + bufpos, \
preferred_size - bufpos + 1, \
"%s", \
string \
+ (*offset - (int32_t)strpos > 0 ? \
*offset - (int32_t)strpos : 0)); \
if(bufpos op preferred_size) { \
PRINTF("res: BREAK at %s (%p)\n", string, resource); \
break; \
} \
} \
strpos += tmplen
/*---------------------------------------------------------------------------*/
/*- Resource Handlers -------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
void
well_known_core_get_handler(void *request, void *response, uint8_t *buffer,
uint16_t preferred_size, int32_t *offset)
{
size_t strpos = 0; /* position in overall string (which is larger than the buffer) */
size_t bufpos = 0; /* position within buffer (bytes written) */
size_t tmplen = 0;
resource_t *resource = NULL;
#if COAP_LINK_FORMAT_FILTERING
/* For filtering. */
const char *filter = NULL;
const char *attrib = NULL;
const char *found = NULL;
const char *end = NULL;
char *value = NULL;
char lastchar = '\0';
int len = coap_get_header_uri_query(request, &filter);
if(len) {
value = strchr(filter, '=');
value[0] = '\0';
++value;
len -= strlen(filter) + 1;
PRINTF("Filter %s = %.*s\n", filter, len, value);
if(strcmp(filter, "href") == 0 && value[0] == '/') {
++value;
--len;
}
lastchar = value[len - 1];
value[len - 1] = '\0';
}
#endif
for(resource = (resource_t *)list_head(rest_get_resources()); resource;
resource = resource->next) {
#if COAP_LINK_FORMAT_FILTERING
/* Filtering */
if(len) {
if(strcmp(filter, "href") == 0) {
attrib = strstr(resource->url, value);
if(attrib == NULL || (value[-1] == '/' && attrib != resource->url)) {
continue;
}
end = attrib + strlen(attrib);
} else if(resource->attributes != NULL) {
attrib = strstr(resource->attributes, filter);
if(attrib == NULL
|| (attrib[strlen(filter)] != '='
&& attrib[strlen(filter)] != '"')) {
continue;
}
attrib += strlen(filter) + 2;
end = strchr(attrib, '"');
}
PRINTF("Filter: res has attrib %s (%s)\n", attrib, value);
found = attrib;
while((found = strstr(found, value)) != NULL) {
if(found > end) {
found = NULL;
break;
}
if(lastchar == found[len - 1] || lastchar == '*') {
break;
}
++found;
}
if(found == NULL) {
continue;
}
PRINTF("Filter: res has prefix %s\n", found);
if(lastchar != '*'
&& (found[len] != '"' && found[len] != ' ' && found[len] != '\0')) {
continue;
}
PRINTF("Filter: res has match\n");
}
#endif
PRINTF("res: /%s (%p)\npos: s%zu, o%ld, b%zu\n", resource->url, resource,
strpos, (long)*offset, bufpos);
if(strpos > 0) {
ADD_CHAR_IF_POSSIBLE(',');
}
ADD_CHAR_IF_POSSIBLE('<');
ADD_CHAR_IF_POSSIBLE('/');
ADD_STRING_IF_POSSIBLE(resource->url, >=);
ADD_CHAR_IF_POSSIBLE('>');
if(resource->attributes != NULL && resource->attributes[0]) {
ADD_CHAR_IF_POSSIBLE(';');
ADD_STRING_IF_POSSIBLE(resource->attributes, >);
}
/* buffer full, but resource not completed yet; or: do not break if resource exactly fills buffer. */
if(bufpos > preferred_size && strpos - bufpos > *offset) {
PRINTF("res: BREAK at %s (%p)\n", resource->url, resource);
break;
}
}
if(bufpos > 0) {
PRINTF("BUF %zu: %.*s\n", bufpos, (int)bufpos, (char *)buffer);
coap_set_payload(response, buffer, bufpos);
coap_set_header_content_format(response, APPLICATION_LINK_FORMAT);
} else if(strpos > 0) {
PRINTF("well_known_core_handler(): bufpos<=0\n");
coap_set_status_code(response, BAD_OPTION_4_02);
coap_set_payload(response, "BlockOutOfScope", 15);
}
if(resource == NULL) {
PRINTF("res: DONE\n");
*offset = -1;
} else {
PRINTF("res: MORE at %s (%p)\n", resource->url, resource);
*offset += preferred_size;
}
}
/*---------------------------------------------------------------------------*/
RESOURCE(res_well_known_core, "ct=40", well_known_core_get_handler, NULL,
NULL, NULL);
/*---------------------------------------------------------------------------*/

View file

@ -31,87 +31,119 @@
/**
* \file
* CoAP module for separate responses
* CoAP module for separate responses.
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#include "sys/cc.h"
#include <stdio.h>
#include <string.h>
#include "er-coap-13-separate.h"
#include "er-coap-13-transactions.h"
#include "er-coap-separate.h"
#include "er-coap-transactions.h"
#define DEBUG 0
#if DEBUG
#include <stdio.h>
#define PRINTF(...) printf(__VA_ARGS__)
#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5])
#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]", (lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3], (lladdr)->addr[4], (lladdr)->addr[5])
#else
#define PRINTF(...)
#define PRINT6ADDR(addr)
#define PRINTLLADDR(addr)
#endif
/*----------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/*- Separate Response API ---------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/**
* \brief Reject a request that would require a separate response with an error message
*
* When the server does not have enough resources left to store the information
* for a separate response or otherwise cannot execute the resource handler,
* this function will respond with 5.03 Service Unavailable. The client can
* then retry later.
*/
void
coap_separate_reject()
{
coap_error_code = SERVICE_UNAVAILABLE_5_03;
/* TODO: Accept string pointer for custom error message */
erbium_status_code = SERVICE_UNAVAILABLE_5_03;
coap_error_message = "AlreadyInUse";
}
/*----------------------------------------------------------------------------*/
int
/**
* \brief Initiate a separate response with an empty ACK
* \param request The request to accept
* \param separate_store A pointer to the data structure that will store the
* relevant information for the response
*
* When the server does not have enough resources left to store the information
* for a separate response or otherwise cannot execute the resource handler,
* this function will respond with 5.03 Service Unavailable. The client can
* then retry later.
*/
void
coap_separate_accept(void *request, coap_separate_t *separate_store)
{
coap_packet_t *const coap_req = (coap_packet_t *) request;
coap_packet_t *const coap_req = (coap_packet_t *)request;
coap_transaction_t *const t = coap_get_transaction_by_mid(coap_req->mid);
PRINTF("Separate ACCEPT: /%.*s MID %u\n", coap_req->uri_path_len, coap_req->uri_path, coap_req->mid);
if (t)
{
/* Send separate ACK for CON. */
if (coap_req->type==COAP_TYPE_CON)
{
PRINTF("Separate ACCEPT: /%.*s MID %u\n", coap_req->uri_path_len,
coap_req->uri_path, coap_req->mid);
if(t) {
/* send separate ACK for CON */
if(coap_req->type == COAP_TYPE_CON) {
coap_packet_t ack[1];
/* ACK with empty code (0) */
coap_init_message(ack, COAP_TYPE_ACK, 0, coap_req->mid);
/* Serializing into IPBUF: Only overwrites header parts that are already parsed into the request struct. */
coap_send_message(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, (uip_appdata), coap_serialize_message(ack, uip_appdata));
/* serializing into IPBUF: Only overwrites header parts that are already parsed into the request struct */
coap_send_message(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport,
(uip_appdata), coap_serialize_message(ack,
uip_appdata));
}
/* Store remote address. */
/* store remote address */
uip_ipaddr_copy(&separate_store->addr, &t->addr);
separate_store->port = t->port;
/* Store correct response type. */
separate_store->type = coap_req->type==COAP_TYPE_CON ? COAP_TYPE_CON : COAP_TYPE_NON;
/* store correct response type */
separate_store->type =
coap_req->type == COAP_TYPE_CON ? COAP_TYPE_CON : COAP_TYPE_NON;
separate_store->mid = coap_get_mid(); /* if it was a NON, we burned one MID in the engine... */
memcpy(separate_store->token, coap_req->token, coap_req->token_len);
separate_store->token_len = coap_req->token_len;
separate_store->block1_num = coap_req->block1_num;
separate_store->block1_size = coap_req->block1_size;
separate_store->block2_num = coap_req->block2_num;
separate_store->block2_size = coap_req->block2_size;
separate_store->block2_size = coap_req->block2_size > 0 ? MIN(COAP_MAX_BLOCK_SIZE, coap_req->block2_size) : COAP_MAX_BLOCK_SIZE;
/* Signal the engine to skip automatic response and clear transaction by engine. */
coap_error_code = MANUAL_RESPONSE;
return 1;
}
else
{
/* signal the engine to skip automatic response and clear transaction by engine */
erbium_status_code = MANUAL_RESPONSE;
} else {
PRINTF("ERROR: Response transaction for separate request not found!\n");
return 0;
erbium_status_code = INTERNAL_SERVER_ERROR_5_00;
}
}
/*----------------------------------------------------------------------------*/
void
coap_separate_resume(void *response, coap_separate_t *separate_store, uint8_t code)
coap_separate_resume(void *response, coap_separate_t *separate_store,
uint8_t code)
{
coap_init_message(response, separate_store->type, code, separate_store->mid);
if (separate_store->token_len)
{
coap_set_header_token(response, separate_store->token, separate_store->token_len);
coap_init_message(response, separate_store->type, code,
separate_store->mid);
if(separate_store->token_len) {
coap_set_token(response, separate_store->token,
separate_store->token_len);
}
if(separate_store->block1_size) {
coap_set_header_block1(response, separate_store->block1_num,
0, separate_store->block1_size);
}
}
/*---------------------------------------------------------------------------*/

View file

@ -31,7 +31,7 @@
/**
* \file
* CoAP module for separate responses
* CoAP module for separate responses.
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
@ -39,7 +39,7 @@
#ifndef COAP_SEPARATE_H_
#define COAP_SEPARATE_H_
#include "er-coap-13.h"
#include "er-coap.h"
typedef struct coap_separate {
@ -52,15 +52,18 @@ typedef struct coap_separate {
uint8_t token_len;
uint8_t token[COAP_TOKEN_LEN];
/* separate + blockwise is untested! */
uint32_t block1_num;
uint16_t block1_size;
uint32_t block2_num;
uint16_t block2_size;
} coap_separate_t;
int coap_separate_handler(resource_t *resource, void *request, void *response);
void coap_separate_reject();
int coap_separate_accept(void *request, coap_separate_t *separate_store);
void coap_separate_resume(void *response, coap_separate_t *separate_store, uint8_t code);
int coap_separate_handler(resource_t *resource, void *request,
void *response);
void coap_separate_reject(void);
void coap_separate_accept(void *request, coap_separate_t *separate_store);
void coap_separate_resume(void *response, coap_separate_t *separate_store,
uint8_t code);
#endif /* COAP_SEPARATE_H_ */

View file

@ -38,49 +38,41 @@
#include "contiki.h"
#include "contiki-net.h"
#include "er-coap-13-transactions.h"
#include "er-coap-13-observing.h"
/*
* Modulo mask (+1 and +0.5 for rounding) for a random number to get the tick number for the random
* retransmission time between COAP_RESPONSE_TIMEOUT and COAP_RESPONSE_TIMEOUT*COAP_RESPONSE_RANDOM_FACTOR.
*/
#define COAP_RESPONSE_TIMEOUT_TICKS (CLOCK_SECOND * COAP_RESPONSE_TIMEOUT)
#define COAP_RESPONSE_TIMEOUT_BACKOFF_MASK ((CLOCK_SECOND * COAP_RESPONSE_TIMEOUT * (COAP_RESPONSE_RANDOM_FACTOR - 1)) + 1.5)
#include "er-coap-transactions.h"
#include "er-coap-observe.h"
#define DEBUG 0
#if DEBUG
#include <stdio.h>
#define PRINTF(...) printf(__VA_ARGS__)
#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5])
#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]", (lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3], (lladdr)->addr[4], (lladdr)->addr[5])
#else
#define PRINTF(...)
#define PRINT6ADDR(addr)
#define PRINTLLADDR(addr)
#endif
/*---------------------------------------------------------------------------*/
MEMB(transactions_memb, coap_transaction_t, COAP_MAX_OPEN_TRANSACTIONS);
LIST(transactions_list);
static struct process *transaction_handler_process = NULL;
/*---------------------------------------------------------------------------*/
/*- Internal API ------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
void
coap_register_as_transaction_handler()
{
transaction_handler_process = PROCESS_CURRENT();
}
coap_transaction_t *
coap_new_transaction(uint16_t mid, uip_ipaddr_t *addr, uint16_t port)
{
coap_transaction_t *t = memb_alloc(&transactions_memb);
if (t)
{
if(t) {
t->mid = mid;
t->retrans_counter = 0;
@ -88,12 +80,12 @@ coap_new_transaction(uint16_t mid, uip_ipaddr_t *addr, uint16_t port)
uip_ipaddr_copy(&t->addr, addr);
t->port = port;
list_add(transactions_list, t); /* List itself makes sure same element is not added twice. */
list_add(transactions_list, t); /* list itself makes sure same element is not added twice */
}
return t;
}
/*---------------------------------------------------------------------------*/
void
coap_send_transaction(coap_transaction_t *t)
{
@ -101,38 +93,33 @@ coap_send_transaction(coap_transaction_t *t)
coap_send_message(&t->addr, t->port, t->packet, t->packet_len);
if (COAP_TYPE_CON==((COAP_HEADER_TYPE_MASK & t->packet[0])>>COAP_HEADER_TYPE_POSITION))
{
if (t->retrans_counter<COAP_MAX_RETRANSMIT)
{
/* Not timed out yet. */
if(COAP_TYPE_CON ==
((COAP_HEADER_TYPE_MASK & t->packet[0]) >> COAP_HEADER_TYPE_POSITION)) {
if(t->retrans_counter < COAP_MAX_RETRANSMIT) {
/* not timed out yet */
PRINTF("Keeping transaction %u\n", t->mid);
if (t->retrans_counter==0)
{
t->retrans_timer.timer.interval = COAP_RESPONSE_TIMEOUT_TICKS + (random_rand() % (clock_time_t) COAP_RESPONSE_TIMEOUT_BACKOFF_MASK);
PRINTF("Initial interval %f\n", (float)t->retrans_timer.timer.interval/CLOCK_SECOND);
}
else
{
t->retrans_timer.timer.interval <<= 1; /* double */
PRINTF("Doubled (%u) interval %f\n", t->retrans_counter, (float)t->retrans_timer.timer.interval/CLOCK_SECOND);
if(t->retrans_counter == 0) {
t->retrans_timer.timer.interval =
COAP_RESPONSE_TIMEOUT_TICKS + (random_rand()
%
(clock_time_t)
COAP_RESPONSE_TIMEOUT_BACKOFF_MASK);
PRINTF("Initial interval %f\n",
(float)t->retrans_timer.timer.interval / CLOCK_SECOND);
} else {
t->retrans_timer.timer.interval <<= 1; /* double */
PRINTF("Doubled (%u) interval %f\n", t->retrans_counter,
(float)t->retrans_timer.timer.interval / CLOCK_SECOND);
}
/*FIXME
* Hack: Setting timer for responsible process.
* Maybe there is a better way, but avoid posting everything to the process.
*/
struct process *process_actual = PROCESS_CURRENT();
process_current = transaction_handler_process;
etimer_restart(&t->retrans_timer); /* interval updated above */
process_current = process_actual;
PROCESS_CONTEXT_BEGIN(transaction_handler_process);
etimer_restart(&t->retrans_timer); /* interval updated above */
PROCESS_CONTEXT_END(transaction_handler_process);
t = NULL;
}
else
{
/* Timed out. */
} else {
/* timed out */
PRINTF("Timeout\n");
restful_response_handler callback = t->callback;
void *callback_data = t->callback_data;
@ -142,22 +129,19 @@ coap_send_transaction(coap_transaction_t *t)
coap_clear_transaction(t);
if (callback) {
if(callback) {
callback(callback_data, NULL);
}
}
}
else
{
} else {
coap_clear_transaction(t);
}
}
/*---------------------------------------------------------------------------*/
void
coap_clear_transaction(coap_transaction_t *t)
{
if (t)
{
if(t) {
PRINTF("Freeing transaction %u: %p\n", t->mid, t);
etimer_stop(&t->retrans_timer);
@ -165,35 +149,31 @@ coap_clear_transaction(coap_transaction_t *t)
memb_free(&transactions_memb, t);
}
}
coap_transaction_t *
coap_get_transaction_by_mid(uint16_t mid)
{
coap_transaction_t *t = NULL;
for (t = (coap_transaction_t*)list_head(transactions_list); t; t = t->next)
{
if (t->mid==mid)
{
for(t = (coap_transaction_t *)list_head(transactions_list); t; t = t->next) {
if(t->mid == mid) {
PRINTF("Found transaction for MID %u: %p\n", t->mid, t);
return t;
}
}
return NULL;
}
/*---------------------------------------------------------------------------*/
void
coap_check_transactions()
{
coap_transaction_t *t = NULL;
for (t = (coap_transaction_t*)list_head(transactions_list); t; t = t->next)
{
if (etimer_expired(&t->retrans_timer))
{
for(t = (coap_transaction_t *)list_head(transactions_list); t; t = t->next) {
if(etimer_expired(&t->retrans_timer)) {
++(t->retrans_counter);
PRINTF("Retransmitting %u (%u)\n", t->mid, t->retrans_counter);
coap_send_transaction(t);
}
}
}
/*---------------------------------------------------------------------------*/

View file

@ -39,18 +39,18 @@
#ifndef COAP_TRANSACTIONS_H_
#define COAP_TRANSACTIONS_H_
#include "er-coap-13.h"
#include "er-coap.h"
/*
* The number of concurrent messages that can be stored for retransmission in the transaction layer.
* Modulo mask (thus +1) for a random number to get the tick number for the random
* retransmission time between COAP_RESPONSE_TIMEOUT and COAP_RESPONSE_TIMEOUT*COAP_RESPONSE_RANDOM_FACTOR.
*/
#ifndef COAP_MAX_OPEN_TRANSACTIONS
#define COAP_MAX_OPEN_TRANSACTIONS 4
#endif /* COAP_MAX_OPEN_TRANSACTIONS */
#define COAP_RESPONSE_TIMEOUT_TICKS (CLOCK_SECOND * COAP_RESPONSE_TIMEOUT)
#define COAP_RESPONSE_TIMEOUT_BACKOFF_MASK (long)((CLOCK_SECOND * COAP_RESPONSE_TIMEOUT * ((float)COAP_RESPONSE_RANDOM_FACTOR - 1.0)) + 0.5) + 1
/* container for transactions with message buffer and retransmission info */
typedef struct coap_transaction {
struct coap_transaction *next; /* for LIST */
struct coap_transaction *next; /* for LIST */
uint16_t mid;
struct etimer retrans_timer;
@ -63,16 +63,18 @@ typedef struct coap_transaction {
void *callback_data;
uint16_t packet_len;
uint8_t packet[COAP_MAX_PACKET_SIZE+1]; /* +1 for the terminating '\0' to simply and savely use snprintf(buf, len+1, "", ...) in the resource handler. */
uint8_t packet[COAP_MAX_PACKET_SIZE + 1]; /* +1 for the terminating '\0' which will not be sent
* Use snprintf(buf, len+1, "", ...) to completely fill payload */
} coap_transaction_t;
void coap_register_as_transaction_handler();
void coap_register_as_transaction_handler(void);
coap_transaction_t *coap_new_transaction(uint16_t mid, uip_ipaddr_t *addr, uint16_t port);
coap_transaction_t *coap_new_transaction(uint16_t mid, uip_ipaddr_t *addr,
uint16_t port);
void coap_send_transaction(coap_transaction_t *t);
void coap_clear_transaction(coap_transaction_t *t);
coap_transaction_t *coap_get_transaction_by_mid(uint16_t mid);
void coap_check_transactions();
void coap_check_transactions(void);
#endif /* COAP_TRANSACTIONS_H_ */

1210
apps/er-coap/er-coap.c Normal file

File diff suppressed because it is too large Load diff

263
apps/er-coap/er-coap.h Normal file
View file

@ -0,0 +1,263 @@
/*
* Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* An implementation of the Constrained Application Protocol (RFC).
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#ifndef ER_COAP_H_
#define ER_COAP_H_
#include <stddef.h> /* for size_t */
#include "contiki-net.h"
#include "er-coap-constants.h"
#include "er-coap-conf.h"
/* sanity check for configured values */
#define COAP_MAX_PACKET_SIZE (COAP_MAX_HEADER_SIZE + REST_MAX_CHUNK_SIZE)
#if COAP_MAX_PACKET_SIZE > (UIP_BUFSIZE - UIP_IPH_LEN - UIP_UDPH_LEN)
#error "UIP_CONF_BUFFER_SIZE too small for REST_MAX_CHUNK_SIZE"
#endif
/* use Erbium CoAP for the REST Engine. Must come before include of rest-engine.h. */
#define REST coap_rest_implementation
#include "rest-engine.h"
/* REST_MAX_CHUNK_SIZE can be different from 2^x so we need to get next lower 2^x for COAP_MAX_BLOCK_SIZE */
#ifndef COAP_MAX_BLOCK_SIZE
#define COAP_MAX_BLOCK_SIZE (REST_MAX_CHUNK_SIZE < 32 ? 16 : \
(REST_MAX_CHUNK_SIZE < 64 ? 32 : \
(REST_MAX_CHUNK_SIZE < 128 ? 64 : \
(REST_MAX_CHUNK_SIZE < 256 ? 128 : \
(REST_MAX_CHUNK_SIZE < 512 ? 256 : \
(REST_MAX_CHUNK_SIZE < 1024 ? 512 : \
(REST_MAX_CHUNK_SIZE < 2048 ? 1024 : 2048)))))))
#endif /* COAP_MAX_BLOCK_SIZE */
/* direct access into the buffer */
#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN])
#if NETSTACK_CONF_WITH_IPV6
#define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[uip_l2_l3_hdr_len])
#else
#define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[UIP_LLH_LEN + UIP_IPH_LEN])
#endif
/* bitmap for set options */
enum { OPTION_MAP_SIZE = sizeof(uint8_t) * 8 };
#define SET_OPTION(packet, opt) ((packet)->options[opt / OPTION_MAP_SIZE] |= 1 << (opt % OPTION_MAP_SIZE))
#define IS_OPTION(packet, opt) ((packet)->options[opt / OPTION_MAP_SIZE] & (1 << (opt % OPTION_MAP_SIZE)))
/* parsed message struct */
typedef struct {
uint8_t *buffer; /* pointer to CoAP header / incoming packet buffer / memory to serialize packet */
uint8_t version;
coap_message_type_t type;
uint8_t code;
uint16_t mid;
uint8_t token_len;
uint8_t token[COAP_TOKEN_LEN];
uint8_t options[COAP_OPTION_SIZE1 / OPTION_MAP_SIZE + 1]; /* bitmap to check if option is set */
uint16_t content_format; /* parse options once and store; allows setting options in random order */
uint32_t max_age;
uint8_t etag_len;
uint8_t etag[COAP_ETAG_LEN];
size_t proxy_uri_len;
const char *proxy_uri;
size_t proxy_scheme_len;
const char *proxy_scheme;
size_t uri_host_len;
const char *uri_host;
size_t location_path_len;
const char *location_path;
uint16_t uri_port;
size_t location_query_len;
const char *location_query;
size_t uri_path_len;
const char *uri_path;
int32_t observe;
uint16_t accept;
uint8_t if_match_len;
uint8_t if_match[COAP_ETAG_LEN];
uint32_t block2_num;
uint8_t block2_more;
uint16_t block2_size;
uint32_t block2_offset;
uint32_t block1_num;
uint8_t block1_more;
uint16_t block1_size;
uint32_t block1_offset;
uint32_t size2;
uint32_t size1;
size_t uri_query_len;
const char *uri_query;
uint8_t if_none_match;
uint16_t payload_len;
uint8_t *payload;
} coap_packet_t;
/* option format serialization */
#define COAP_SERIALIZE_INT_OPTION(number, field, text) \
if(IS_OPTION(coap_pkt, number)) { \
PRINTF(text " [%u]\n", (unsigned int)coap_pkt->field); \
option += coap_serialize_int_option(number, current_number, option, coap_pkt->field); \
current_number = number; \
}
#define COAP_SERIALIZE_BYTE_OPTION(number, field, text) \
if(IS_OPTION(coap_pkt, number)) { \
PRINTF(text " %u [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", (unsigned int)coap_pkt->field##_len, \
coap_pkt->field[0], \
coap_pkt->field[1], \
coap_pkt->field[2], \
coap_pkt->field[3], \
coap_pkt->field[4], \
coap_pkt->field[5], \
coap_pkt->field[6], \
coap_pkt->field[7] \
); /* FIXME always prints 8 bytes */ \
option += coap_serialize_array_option(number, current_number, option, coap_pkt->field, coap_pkt->field##_len, '\0'); \
current_number = number; \
}
#define COAP_SERIALIZE_STRING_OPTION(number, field, splitter, text) \
if(IS_OPTION(coap_pkt, number)) { \
PRINTF(text " [%.*s]\n", (int)coap_pkt->field##_len, coap_pkt->field); \
option += coap_serialize_array_option(number, current_number, option, (uint8_t *)coap_pkt->field, coap_pkt->field##_len, splitter); \
current_number = number; \
}
#define COAP_SERIALIZE_BLOCK_OPTION(number, field, text) \
if(IS_OPTION(coap_pkt, number)) \
{ \
PRINTF(text " [%lu%s (%u B/blk)]\n", (unsigned long)coap_pkt->field##_num, coap_pkt->field##_more ? "+" : "", coap_pkt->field##_size); \
uint32_t block = coap_pkt->field##_num << 4; \
if(coap_pkt->field##_more) { block |= 0x8; } \
block |= 0xF & coap_log_2(coap_pkt->field##_size / 16); \
PRINTF(text " encoded: 0x%lX\n", (unsigned long)block); \
option += coap_serialize_int_option(number, current_number, option, block); \
current_number = number; \
}
/* to store error code and human-readable payload */
extern coap_status_t erbium_status_code;
extern char *coap_error_message;
void coap_init_connection(uint16_t port);
uint16_t coap_get_mid(void);
void coap_init_message(void *packet, coap_message_type_t type, uint8_t code,
uint16_t mid);
size_t coap_serialize_message(void *packet, uint8_t *buffer);
void coap_send_message(uip_ipaddr_t *addr, uint16_t port, uint8_t *data,
uint16_t length);
coap_status_t coap_parse_message(void *request, uint8_t *data,
uint16_t data_len);
int coap_get_query_variable(void *packet, const char *name,
const char **output);
int coap_get_post_variable(void *packet, const char *name,
const char **output);
/*---------------------------------------------------------------------------*/
int coap_set_status_code(void *packet, unsigned int code);
int coap_set_token(void *packet, const uint8_t *token, size_t token_len);
int coap_get_header_content_format(void *packet, unsigned int *format);
int coap_set_header_content_format(void *packet, unsigned int format);
int coap_get_header_accept(void *packet, unsigned int *accept);
int coap_set_header_accept(void *packet, unsigned int accept);
int coap_get_header_max_age(void *packet, uint32_t *age);
int coap_set_header_max_age(void *packet, uint32_t age);
int coap_get_header_etag(void *packet, const uint8_t **etag);
int coap_set_header_etag(void *packet, const uint8_t *etag, size_t etag_len);
int coap_get_header_if_match(void *packet, const uint8_t **etag);
int coap_set_header_if_match(void *packet, const uint8_t *etag,
size_t etag_len);
int coap_get_header_if_none_match(void *packet);
int coap_set_header_if_none_match(void *packet);
int coap_get_header_proxy_uri(void *packet, const char **uri); /* in-place string might not be 0-terminated. */
int coap_set_header_proxy_uri(void *packet, const char *uri);
int coap_get_header_proxy_scheme(void *packet, const char **scheme); /* in-place string might not be 0-terminated. */
int coap_set_header_proxy_scheme(void *packet, const char *scheme);
int coap_get_header_uri_host(void *packet, const char **host); /* in-place string might not be 0-terminated. */
int coap_set_header_uri_host(void *packet, const char *host);
int coap_get_header_uri_path(void *packet, const char **path); /* in-place string might not be 0-terminated. */
int coap_set_header_uri_path(void *packet, const char *path);
int coap_get_header_uri_query(void *packet, const char **query); /* in-place string might not be 0-terminated. */
int coap_set_header_uri_query(void *packet, const char *query);
int coap_get_header_location_path(void *packet, const char **path); /* in-place string might not be 0-terminated. */
int coap_set_header_location_path(void *packet, const char *path); /* also splits optional query into Location-Query option. */
int coap_get_header_location_query(void *packet, const char **query); /* in-place string might not be 0-terminated. */
int coap_set_header_location_query(void *packet, const char *query);
int coap_get_header_observe(void *packet, uint32_t *observe);
int coap_set_header_observe(void *packet, uint32_t observe);
int coap_get_header_block2(void *packet, uint32_t *num, uint8_t *more,
uint16_t *size, uint32_t *offset);
int coap_set_header_block2(void *packet, uint32_t num, uint8_t more,
uint16_t size);
int coap_get_header_block1(void *packet, uint32_t *num, uint8_t *more,
uint16_t *size, uint32_t *offset);
int coap_set_header_block1(void *packet, uint32_t num, uint8_t more,
uint16_t size);
int coap_get_header_size2(void *packet, uint32_t *size);
int coap_set_header_size2(void *packet, uint32_t size);
int coap_get_header_size1(void *packet, uint32_t *size);
int coap_set_header_size1(void *packet, uint32_t size);
int coap_get_payload(void *packet, const uint8_t **payload);
int coap_set_payload(void *packet, const void *payload, size_t length);
#endif /* ER_COAP_H_ */

View file

@ -1 +0,0 @@
erbium_src = erbium.c

View file

@ -1,236 +0,0 @@
/*
* Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* An abstraction layer for RESTful Web services
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#include "contiki.h"
#include <string.h> /*for string operations in match_addresses*/
#include <stdio.h> /*for sprintf in rest_set_header_**/
#include "erbium.h"
#define DEBUG 0
#if DEBUG
#define PRINTF(...) printf(__VA_ARGS__)
#define PRINT6ADDR(addr) PRINTF(" %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x ", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
#define PRINTLLADDR(lladdr) PRINTF(" %02x:%02x:%02x:%02x:%02x:%02x ",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5])
#else
#define PRINTF(...)
#define PRINT6ADDR(addr)
#define PRINTLLADDR(addr)
#endif
PROCESS_NAME(rest_manager_process);
LIST(restful_services);
LIST(restful_periodic_services);
void
rest_init_engine(void)
{
list_init(restful_services);
REST.set_service_callback(rest_invoke_restful_service);
/* Start the RESTful server implementation. */
REST.init();
/*Start rest manager process*/
process_start(&rest_manager_process, NULL);
}
void
rest_activate_resource(resource_t* resource)
{
PRINTF("Activating: %s", resource->url);
if (!resource->pre_handler)
{
rest_set_pre_handler(resource, REST.default_pre_handler);
}
if (!resource->post_handler)
{
rest_set_post_handler(resource, REST.default_post_handler);
}
list_add(restful_services, resource);
}
void
rest_activate_periodic_resource(periodic_resource_t* periodic_resource)
{
list_add(restful_periodic_services, periodic_resource);
rest_activate_resource(periodic_resource->resource);
rest_set_post_handler(periodic_resource->resource, REST.subscription_handler);
}
void
rest_activate_event_resource(resource_t* resource)
{
rest_activate_resource(resource);
rest_set_post_handler(resource, REST.subscription_handler);
}
list_t
rest_get_resources(void)
{
return restful_services;
}
void*
rest_get_user_data(resource_t* resource)
{
return resource->user_data;
}
void
rest_set_user_data(resource_t* resource, void* user_data)
{
resource->user_data = user_data;
}
void
rest_set_pre_handler(resource_t* resource, restful_pre_handler pre_handler)
{
resource->pre_handler = pre_handler;
}
void
rest_set_post_handler(resource_t* resource, restful_post_handler post_handler)
{
resource->post_handler = post_handler;
}
void
rest_set_special_flags(resource_t* resource, rest_resource_flags_t flags)
{
resource->flags |= flags;
}
int
rest_invoke_restful_service(void* request, void* response, uint8_t *buffer, uint16_t buffer_size, int32_t *offset)
{
uint8_t found = 0;
uint8_t allowed = 0;
PRINTF("rest_invoke_restful_service url /%.*s -->\n", url_len, url);
resource_t* resource = NULL;
const char *url = NULL;
for (resource = (resource_t*)list_head(restful_services); resource; resource = resource->next)
{
/*if the web service handles that kind of requests and urls matches*/
if ((REST.get_url(request, &url)==strlen(resource->url) || (REST.get_url(request, &url)>strlen(resource->url) && (resource->flags & HAS_SUB_RESOURCES)))
&& strncmp(resource->url, url, strlen(resource->url)) == 0)
{
found = 1;
rest_resource_flags_t method = REST.get_method_type(request);
PRINTF("method %u, resource->flags %u\n", (uint16_t)method, resource->flags);
if (resource->flags & method)
{
allowed = 1;
/*call pre handler if it exists*/
if (!resource->pre_handler || resource->pre_handler(resource, request, response))
{
/* call handler function*/
resource->handler(request, response, buffer, buffer_size, offset);
/*call post handler if it exists*/
if (resource->post_handler)
{
resource->post_handler(resource, request, response);
}
}
} else {
REST.set_response_status(response, REST.status.METHOD_NOT_ALLOWED);
}
break;
}
}
if (!found) {
REST.set_response_status(response, REST.status.NOT_FOUND);
}
return found & allowed;
}
/*-----------------------------------------------------------------------------------*/
PROCESS(rest_manager_process, "Rest Process");
PROCESS_THREAD(rest_manager_process, ev, data)
{
PROCESS_BEGIN();
PROCESS_PAUSE();
/* Initialize the PERIODIC_RESOURCE timers, which will be handled by this process. */
periodic_resource_t* periodic_resource = NULL;
for (periodic_resource = (periodic_resource_t*) list_head(restful_periodic_services); periodic_resource; periodic_resource = periodic_resource->next) {
if (periodic_resource->period) {
PRINTF("Periodic: Set timer for %s to %lu\n", periodic_resource->resource->url, periodic_resource->period);
etimer_set(&periodic_resource->periodic_timer, periodic_resource->period);
}
}
while (1) {
PROCESS_WAIT_EVENT();
if (ev == PROCESS_EVENT_TIMER) {
for (periodic_resource = (periodic_resource_t*)list_head(restful_periodic_services);periodic_resource;periodic_resource = periodic_resource->next) {
if (periodic_resource->period && etimer_expired(&periodic_resource->periodic_timer)) {
PRINTF("Periodic: etimer expired for /%s (period: %lu)\n", periodic_resource->resource->url, periodic_resource->period);
/* Call the periodic_handler function if it exists. */
if (periodic_resource->periodic_handler) {
(periodic_resource->periodic_handler)(periodic_resource->resource);
}
etimer_reset(&periodic_resource->periodic_timer);
}
}
}
}
PROCESS_END();
}

View file

@ -1,345 +0,0 @@
/*
* Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich
* 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* An abstraction layer for RESTful Web services
* \author
* Matthias Kovatsch <kovatsch@inf.ethz.ch>
*/
#ifndef ERBIUM_H_
#define ERBIUM_H_
/*includes*/
#include <stdio.h>
#include "contiki.h"
#include "contiki-lib.h"
/*
* The maximum buffer size that is provided for resource responses and must be respected due to the limited IP buffer.
* Larger data must be handled by the resource and will be sent chunk-wise through a TCP stream or CoAP blocks.
*/
#ifndef REST_MAX_CHUNK_SIZE
#define REST_MAX_CHUNK_SIZE 128
#endif
#ifndef MIN
#define MIN(a, b) ((a) < (b)? (a) : (b))
#endif /* MIN */
/* REST method types */
typedef enum {
/* methods to handle */
METHOD_GET = (1 << 0),
METHOD_POST = (1 << 1),
METHOD_PUT = (1 << 2),
METHOD_DELETE = (1 << 3),
/* special flags */
HAS_SUB_RESOURCES = (1<<7)
} rest_resource_flags_t;
struct resource_s;
struct periodic_resource_s;
/* Signatures of handler functions. */
typedef void (*restful_handler) (void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset);
typedef int (*restful_pre_handler) (struct resource_s *resource, void* request, void* response);
typedef void (*restful_post_handler) (struct resource_s *resource, void* request, void* response);
typedef void (*restful_periodic_handler) (struct resource_s* resource);
typedef void (*restful_response_handler) (void *data, void* response);
/* Signature of the rest-engine service function. */
typedef int (* service_callback_t)(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset);
/**
* The structure of a MAC protocol driver in Contiki.
*/
struct rest_implementation_status
{
const unsigned int OK; /* CONTENT_2_05, OK_200 */
const unsigned int CREATED; /* CREATED_2_01, CREATED_201 */
const unsigned int CHANGED; /* CHANGED_2_04, NO_CONTENT_204 */
const unsigned int DELETED; /* DELETED_2_02, NO_CONTENT_204 */
const unsigned int NOT_MODIFIED; /* VALID_2_03, NOT_MODIFIED_304 */
const unsigned int BAD_REQUEST; /* BAD_REQUEST_4_00, BAD_REQUEST_400 */
const unsigned int UNAUTHORIZED; /* UNAUTHORIZED_4_01, UNAUTHORIZED_401 */
const unsigned int BAD_OPTION; /* BAD_OPTION_4_02, BAD_REQUEST_400 */
const unsigned int FORBIDDEN; /* FORBIDDEN_4_03, FORBIDDEN_403 */
const unsigned int NOT_FOUND; /* NOT_FOUND_4_04, NOT_FOUND_404 */
const unsigned int METHOD_NOT_ALLOWED; /* METHOD_NOT_ALLOWED_4_05, METHOD_NOT_ALLOWED_405 */
const unsigned int NOT_ACCEPTABLE; /* NOT_ACCEPTABLE_4_06, NOT_ACCEPTABLE_406 */
const unsigned int REQUEST_ENTITY_TOO_LARGE; /* REQUEST_ENTITY_TOO_LARGE_4_13, REQUEST_ENTITY_TOO_LARGE_413 */
const unsigned int UNSUPPORTED_MEDIA_TYPE; /* UNSUPPORTED_MEDIA_TYPE_4_15, UNSUPPORTED_MEDIA_TYPE_415 */
const unsigned int INTERNAL_SERVER_ERROR; /* INTERNAL_SERVER_ERROR_5_00, INTERNAL_SERVER_ERROR_500 */
const unsigned int NOT_IMPLEMENTED; /* NOT_IMPLEMENTED_5_01, NOT_IMPLEMENTED_501 */
const unsigned int BAD_GATEWAY; /* BAD_GATEWAY_5_02, BAD_GATEWAY_502 */
const unsigned int SERVICE_UNAVAILABLE; /* SERVICE_UNAVAILABLE_5_03, SERVICE_UNAVAILABLE_503 */
const unsigned int GATEWAY_TIMEOUT; /* GATEWAY_TIMEOUT_5_04, GATEWAY_TIMEOUT_504 */
const unsigned int PROXYING_NOT_SUPPORTED; /* PROXYING_NOT_SUPPORTED_5_05, INTERNAL_SERVER_ERROR_500 */
};
struct rest_implementation_type
{
unsigned int TEXT_PLAIN;
unsigned int TEXT_XML;
unsigned int TEXT_CSV;
unsigned int TEXT_HTML;
unsigned int IMAGE_GIF;
unsigned int IMAGE_JPEG;
unsigned int IMAGE_PNG;
unsigned int IMAGE_TIFF;
unsigned int AUDIO_RAW;
unsigned int VIDEO_RAW;
unsigned int APPLICATION_LINK_FORMAT;
unsigned int APPLICATION_XML;
unsigned int APPLICATION_OCTET_STREAM;
unsigned int APPLICATION_RDF_XML;
unsigned int APPLICATION_SOAP_XML;
unsigned int APPLICATION_ATOM_XML;
unsigned int APPLICATION_XMPP_XML;
unsigned int APPLICATION_EXI;
unsigned int APPLICATION_FASTINFOSET;
unsigned int APPLICATION_SOAP_FASTINFOSET;
unsigned int APPLICATION_JSON;
unsigned int APPLICATION_X_OBIX_BINARY;
};
/*
* Data structure representing a resource in REST.
*/
struct resource_s {
struct resource_s *next; /* for LIST, points to next resource defined */
rest_resource_flags_t flags; /* handled RESTful methods */
const char* url; /*handled URL*/
const char* attributes; /* link-format attributes */
restful_handler handler; /* handler function */
restful_pre_handler pre_handler; /* to be called before handler, may perform initializations */
restful_post_handler post_handler; /* to be called after handler, may perform finalizations (cleanup, etc) */
void* user_data; /* pointer to user specific data */
unsigned int benchmark; /* to benchmark resource handler, used for separate response */
};
typedef struct resource_s resource_t;
struct periodic_resource_s {
struct periodic_resource_s *next; /* for LIST, points to next resource defined */
resource_t *resource;
uint32_t period;
struct etimer periodic_timer;
restful_periodic_handler periodic_handler;
};
typedef struct periodic_resource_s periodic_resource_t;
struct rest_implementation {
char *name;
/** Initialize the REST implementation. */
void (* init)(void);
/** Register the RESTful service callback at implementation */
void (* set_service_callback)(service_callback_t callback);
/** Get request URI path */
int (* get_url)(void *request, const char **url);
int (* set_url)(void *request, const char *url);
/** Get the method of a request. */
rest_resource_flags_t (* get_method_type)(void *request);
/** Set the status code of a response. */
int (* set_response_status)(void *response, unsigned int code);
/** Get the content-type of a request. */
unsigned int (* get_header_content_type)(void *request);
/** Set the Content-Type of a response. */
int (* set_header_content_type)(void *response, unsigned int content_type);
/** Get the Accept types of a request. */
int (* get_header_accept)(void *request, const uint16_t **accept);
/** Get the Length option of a request. */
int (* get_header_length)(void *request, uint32_t *size);
/** Set the Length option of a response. */
int (* set_header_length)(void *response, uint32_t size);
/** Get the Max-Age option of a request. */
int (* get_header_max_age)(void *request, uint32_t *age);
/** Set the Max-Age option of a response. */
int (* set_header_max_age)(void *response, uint32_t age);
/** Set the ETag option of a response. */
int (* set_header_etag)(void *response, const uint8_t *etag, size_t length);
/** Get the If-Match option of a request. */
int (* get_header_if_match)(void *request, const uint8_t **etag);
/** Get the If-Match option of a request. */
int (* get_header_if_none_match)(void *request);
/** Get the Host option of a request. */
int (* get_header_host)(void *request, const char **host);
/** Set the location option of a response. */
int (* set_header_location)(void *response, const char *location);
/** Get the payload option of a request. */
int (* get_request_payload)(void *request, const uint8_t **payload);
/** Set the payload option of a response. */
int (* set_response_payload)(void *response, const void *payload, size_t length);
/** Get the query string of a request. */
int (* get_query)(void *request, const char **value);
/** Get the value of a request query key-value pair. */
int (* get_query_variable)(void *request, const char *name, const char **value);
/** Get the value of a request POST key-value pair. */
int (* get_post_variable)(void *request, const char *name, const char **value);
/** Send the payload to all subscribers of the resource at url. */
void (* notify_subscribers)(resource_t *resource, int32_t counter, void *notification);
/** The handler for resource subscriptions. */
restful_post_handler subscription_handler;
/** A default pre-handler that is assigned with the RESOURCE macro. */
restful_pre_handler default_pre_handler;
/** A default post-handler that is assigned with the RESOURCE macro. */
restful_post_handler default_post_handler;
/* REST status codes. */
const struct rest_implementation_status status;
/* REST content-types. */
const struct rest_implementation_type type;
};
/*
* Instance of REST implementation
*/
extern const struct rest_implementation REST;
/*
* Macro to define a Resource
* Resources are statically defined for the sake of efficiency and better memory management.
*/
#define RESOURCE(name, flags, url, attributes) \
void name##_handler(void *, void *, uint8_t *, uint16_t, int32_t *); \
resource_t resource_##name = {NULL, flags, url, attributes, name##_handler, NULL, NULL, NULL}
/*
* Macro to define a sub-resource
* Make sure to define its parent resource beforehand and set 'parent' to that name.
*/
#define SUB_RESOURCE(name, flags, url, attributes, parent) \
resource_t resource_##name = {NULL, flags, url, attributes, parent##_handler, NULL, NULL, NULL}
/*
* Macro to define an event resource
* Like periodic resources, event resources have a post_handler that manages a subscriber list.
* Instead of a periodic_handler, an event_callback must be provided.
*/
#define EVENT_RESOURCE(name, flags, url, attributes) \
void name##_handler(void *, void *, uint8_t *, uint16_t, int32_t *); \
void name##_event_handler(resource_t*); \
resource_t resource_##name = {NULL, flags, url, attributes, name##_handler, NULL, NULL, NULL}
/*
* Macro to define a periodic resource
* The corresponding [name]_periodic_handler() function will be called every period.
* For instance polling a sensor and publishing a changed value to subscribed clients would be done there.
* The subscriber list will be maintained by the post_handler rest_subscription_handler() (see rest-mapping header file).
*/
#define PERIODIC_RESOURCE(name, flags, url, attributes, period) \
void name##_handler(void *, void *, uint8_t *, uint16_t, int32_t *); \
resource_t resource_##name = {NULL, flags, url, attributes, name##_handler, NULL, NULL, NULL}; \
void name##_periodic_handler(resource_t*); \
periodic_resource_t periodic_resource_##name = {NULL, &resource_##name, period, {{0}}, name##_periodic_handler}
/*
* Initializes REST framework and starts HTTP or COAP process
*/
void rest_init_engine(void);
/*
* Resources wanted to be accessible should be activated with the following code.
*/
void rest_activate_resource(resource_t* resource);
void rest_activate_periodic_resource(periodic_resource_t* periodic_resource);
void rest_activate_event_resource(resource_t* resource);
/*
* To be called by HTTP/COAP server as a callback function when a new service request appears.
* This function dispatches the corresponding RESTful service.
*/
int rest_invoke_restful_service(void* request, void* response, uint8_t *buffer, uint16_t buffer_size, int32_t *offset);
/*
* Returns the resource list
*/
list_t rest_get_resources(void);
/*
* Getter and setter methods for user specific data.
*/
void* rest_get_user_data(resource_t* resource);
void rest_set_user_data(resource_t* resource, void* user_data);
/*
* Sets the pre handler function of the Resource.
* If set, this function will be called just before the original handler function.
* Can be used to setup work before resource handling.
*/
void rest_set_pre_handler(resource_t* resource, restful_pre_handler pre_handler);
/*
* Sets the post handler function of the Resource.
* If set, this function will be called just after the original handler function.
* Can be used to do cleanup (deallocate memory, etc) after resource handling.
*/
void rest_set_post_handler(resource_t* resource, restful_post_handler post_handler);
/*
* Sets resource flags for special properties, e.g., handling of sub-resources of URI-path.
*/
void rest_set_special_flags(resource_t* resource, rest_resource_flags_t flags);
#endif /*ERBIUM_H_*/

View file

@ -162,7 +162,7 @@ http_post_auth(const uint8_t *username_password, const char *msg)
PRINTF("message '%s'\n", s->message);*/
/* Spawn process to deal with TCP connection */
process_start(&http_post_auth_process, (char *)s);
process_start(&http_post_auth_process, (void *)s);
return 1;
}
/*---------------------------------------------------------------------------*/

View file

@ -0,0 +1,3 @@
ipso-objects_src = ipso-temperature.c ipso-button.c ipso-leds-control.c \
ipso-light-control.c ipso-objects.c
CFLAGS += -DWITH_IPSO=1

View file

@ -0,0 +1,163 @@
/*
* Copyright (c) 2015, Yanzi Networks AB.
* 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 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 HOLDER 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.
*/
/**
* \addtogroup ipso-objects
* @{
*/
/**
* \file
* Implementation of OMA LWM2M / IPSO button as a digital input
* \author
* Joakim Eriksson <joakime@sics.se>
* Niclas Finne <nfi@sics.se>
*/
#include "contiki.h"
#include "lwm2m-object.h"
#include "lwm2m-engine.h"
#include "er-coap-engine.h"
#define DEBUG 0
#if DEBUG
#include <stdio.h>
#define PRINTF(...) printf(__VA_ARGS__)
#else
#define PRINTF(...)
#endif
#if PLATFORM_HAS_BUTTON
#include "dev/button-sensor.h"
PROCESS(ipso_button_process, "ipso-button");
#endif /* PLATFORM_HAS_BUTTON */
static int input_state = 0;
static int polarity = 0;
static int32_t counter = 0;
static int32_t edge_selection = 3;
static int32_t debounce_time = 10;
/*---------------------------------------------------------------------------*/
static int
read_state(lwm2m_context_t *ctx, uint8_t *outbuf, size_t outsize)
{
int value;
if(polarity == 0) {
value = input_state ? 1 : 0;
} else {
value = input_state ? 0 : 1;
}
PRINTF("Read button state (polarity=%d, state=%d): %d\n",
polarity, input_state, value);
return ctx->writer->write_boolean(ctx, outbuf, outsize, value);
}
/*---------------------------------------------------------------------------*/
static int
reset_counter(lwm2m_context_t *ctx, const uint8_t *arg, size_t len,
uint8_t *outbuf, size_t outlen)
{
counter = 0;
return 0;
}
/*---------------------------------------------------------------------------*/
LWM2M_RESOURCES(button_resources,
LWM2M_RESOURCE_CALLBACK(5500, { read_state, NULL, NULL }),
LWM2M_RESOURCE_INTEGER_VAR(5501, &counter),
LWM2M_RESOURCE_BOOLEAN_VAR(5502, &polarity),
LWM2M_RESOURCE_INTEGER_VAR(5503, &debounce_time),
LWM2M_RESOURCE_INTEGER_VAR(5504, &edge_selection),
LWM2M_RESOURCE_CALLBACK(5505, { NULL, NULL, reset_counter }),
LWM2M_RESOURCE_STRING(5751, "Button")
);
LWM2M_INSTANCES(button_instances,
LWM2M_INSTANCE(0, button_resources));
LWM2M_OBJECT(button, 3200, button_instances);
/*---------------------------------------------------------------------------*/
void
ipso_button_init(void)
{
/* register this device and its handlers - the handlers automatically
sends in the object to handle */
lwm2m_engine_register_object(&button);
#if PLATFORM_HAS_BUTTON
process_start(&ipso_button_process, NULL);
#endif /* PLATFORM_HAS_BUTTON */
}
/*---------------------------------------------------------------------------*/
#if PLATFORM_HAS_BUTTON
PROCESS_THREAD(ipso_button_process, ev, data)
{
static struct etimer timer;
int32_t time;
PROCESS_BEGIN();
SENSORS_ACTIVATE(button_sensor);
while(1) {
PROCESS_WAIT_EVENT();
if(ev == sensors_event && data == &button_sensor) {
if(!input_state) {
input_state = 1;
counter++;
if((edge_selection & 2) != 0) {
lwm2m_object_notify_observers(&button, "/0/5500");
}
lwm2m_object_notify_observers(&button, "/0/5501");
time = (debounce_time * CLOCK_SECOND / 1000);
if(time < 1) {
time = 1;
}
etimer_set(&timer, (clock_time_t)time);
}
} else if(ev == PROCESS_EVENT_TIMER && data == &timer) {
if(!input_state) {
/* Button is not in pressed state */
} else if(button_sensor.value(0) != 0) {
/* Button is still pressed */
etimer_reset(&timer);
} else {
input_state = 0;
if((edge_selection & 1) != 0) {
lwm2m_object_notify_observers(&button, "/0/5500");
}
}
}
}
PROCESS_END();
}
#endif /* PLATFORM_HAS_BUTTON */
/*---------------------------------------------------------------------------*/
/** @} */

View file

@ -0,0 +1,236 @@
/*
* Copyright (c) 2015, Yanzi Networks AB.
* 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 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 HOLDER 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.
*/
/**
* \addtogroup ipso-objects
* @{
*
*/
/**
* \file
* Implementation of OMA LWM2M / IPSO Light Control for LEDs
* \author
* Joakim Eriksson <joakime@sics.se>
* Niclas Finne <nfi@sics.se>
*/
#include "lwm2m-object.h"
#include "lwm2m-engine.h"
#include "er-coap-engine.h"
#include "dev/leds.h"
#include <stdint.h>
#define DEBUG 0
#if DEBUG
#include <stdio.h>
#define PRINTF(...) printf(__VA_ARGS__)
#else
#define PRINTF(...)
#endif
#if LEDS_ALL & LEDS_BLUE || LEDS_ALL & LEDS_RED || LEDS_ALL & LEDS_BLUE
#define LEDS_CONTROL_NUMBER (((LEDS_ALL & LEDS_BLUE) ? 1 : 0) + ((LEDS_ALL & LEDS_RED) ? 1 : 0) + ((LEDS_ALL & LEDS_GREEN) ? 1 : 0))
#else
#define LEDS_CONTROL_NUMBER 1
#endif
struct led_state {
unsigned long last_on_time;
uint32_t total_on_time;
uint8_t is_on;
uint8_t led_value;
};
static struct led_state states[LEDS_CONTROL_NUMBER];
static lwm2m_instance_t leds_control_instances[LEDS_CONTROL_NUMBER];
/*---------------------------------------------------------------------------*/
static int
read_state(lwm2m_context_t *ctx, uint8_t *outbuf, size_t outsize)
{
uint8_t idx = ctx->object_instance_index;
if(idx >= LEDS_CONTROL_NUMBER) {
return 0;
}
return ctx->writer->write_boolean(ctx, outbuf, outsize,
states[idx].is_on ? 1 : 0);
}
/*---------------------------------------------------------------------------*/
static int
write_state(lwm2m_context_t *ctx, const uint8_t *inbuf, size_t insize,
uint8_t *outbuf, size_t outsize)
{
int value;
size_t len;
uint8_t idx = ctx->object_instance_index;
if(idx >= LEDS_CONTROL_NUMBER) {
return 0;
}
len = ctx->reader->read_boolean(ctx, inbuf, insize, &value);
if(len > 0) {
if(value) {
if(!states[idx].is_on) {
states[idx].is_on = 1;
states[idx].last_on_time = clock_seconds();
#if PLATFORM_HAS_LEDS
leds_on(states[idx].led_value);
#endif /* PLATFORM_HAS_LEDS */
}
} else if(states[idx].is_on) {
states[idx].total_on_time += clock_seconds() - states[idx].last_on_time;
states[idx].is_on = 0;
#if PLATFORM_HAS_LEDS
leds_off(states[idx].led_value);
#endif /* PLATFORM_HAS_LEDS */
}
} else {
PRINTF("IPSO leds control - ignored illegal write to on/off\n");
}
return len;
}
/*---------------------------------------------------------------------------*/
static char *
get_color(int value) {
switch(value) {
case LEDS_GREEN:
return "Green";
case LEDS_RED:
return "Red";
case LEDS_BLUE:
return "Blue";
}
return "None";
}
static int
read_color(lwm2m_context_t *ctx, uint8_t *outbuf, size_t outsize)
{
char *value;
uint8_t idx = ctx->object_instance_index;
if(idx >= LEDS_CONTROL_NUMBER) {
return 0;
}
value = get_color(states[idx].led_value);
return ctx->writer->write_string(ctx, outbuf, outsize,
value, strlen(value));
}
/*---------------------------------------------------------------------------*/
static int
read_on_time(lwm2m_context_t *ctx, uint8_t *outbuf, size_t outsize)
{
unsigned long now;
uint8_t idx = ctx->object_instance_index;
if(idx >= LEDS_CONTROL_NUMBER) {
return 0;
}
if(states[idx].is_on) {
/* Update the on time */
now = clock_seconds();
states[idx].total_on_time += now - states[idx].last_on_time;
states[idx].last_on_time = now;
}
return ctx->writer->write_int(ctx, outbuf, outsize,
(int32_t)states[idx].total_on_time);
}
/*---------------------------------------------------------------------------*/
static int
write_on_time(lwm2m_context_t *ctx,
const uint8_t *inbuf, size_t insize,
uint8_t *outbuf, size_t outsize)
{
int32_t value;
size_t len;
uint8_t idx = ctx->object_instance_index;
if(idx >= LEDS_CONTROL_NUMBER) {
return 0;
}
len = ctx->reader->read_int(ctx, inbuf, insize, &value);
if(len > 0 && value == 0) {
PRINTF("IPSO leds control - reset On Time\n");
states[idx].total_on_time = 0;
if(states[idx].is_on) {
states[idx].last_on_time = clock_seconds();
}
} else {
PRINTF("IPSO leds control - ignored illegal write to On Time\n");
}
return len;
}
/*---------------------------------------------------------------------------*/
LWM2M_RESOURCES(leds_control_resources,
LWM2M_RESOURCE_CALLBACK(5850, { read_state, write_state, NULL }),
LWM2M_RESOURCE_CALLBACK(5706, { read_color, NULL, NULL }),
LWM2M_RESOURCE_CALLBACK(5852, { read_on_time, write_on_time, NULL })
);
LWM2M_OBJECT(leds_control, 3311, leds_control_instances);
/*---------------------------------------------------------------------------*/
static int
bit_no(int bit)
{
int i;
for(i = 0; i < 8; i++) {
if(LEDS_ALL & (1 << i)) {
if(bit == 0) {
/* matching bit */
return 1 << i;
} else {
/* matching but used */
bit--;
}
}
}
return 0;
}
void
ipso_leds_control_init(void)
{
lwm2m_instance_t template = LWM2M_INSTANCE(0, leds_control_resources);
int i;
/* Initialize the instances */
for(i = 0; i < LEDS_CONTROL_NUMBER; i++) {
leds_control_instances[i] = template;
leds_control_instances[i].id = i;
states[i].led_value = bit_no(i);
}
/* register this device and its handlers - the handlers automatically
sends in the object to handle */
lwm2m_engine_register_object(&leds_control);
PRINTF("IPSO leds control initialized with %u instances\n",
LEDS_CONTROL_NUMBER);
}
/*---------------------------------------------------------------------------*/
/** @} */

View file

@ -0,0 +1,202 @@
/*
* Copyright (c) 2015, Yanzi Networks AB.
* 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 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 HOLDER 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.
*/
/**
* \addtogroup ipso-objects
* @{
*
*/
/**
* \file
* Implementation of OMA LWM2M / IPSO Light Control
* \author
* Joakim Eriksson <joakime@sics.se>
* Niclas Finne <nfi@sics.se>
*/
#include "ipso-objects.h"
#include "lwm2m-object.h"
#include "lwm2m-engine.h"
#ifdef IPSO_LIGHT_CONTROL
extern const struct ipso_objects_actuator IPSO_LIGHT_CONTROL;
#endif /* IPSO_LIGHT_CONTROL */
/*---------------------------------------------------------------------------*/
static unsigned long last_on_time;
static uint32_t total_on_time;
static int dim_level = 0;
static uint8_t is_on = 0;
/*---------------------------------------------------------------------------*/
static int
read_state(lwm2m_context_t *ctx, uint8_t *outbuf, size_t outsize)
{
return ctx->writer->write_boolean(ctx, outbuf, outsize, is_on ? 1 : 0);
}
/*---------------------------------------------------------------------------*/
static int
write_state(lwm2m_context_t *ctx, const uint8_t *inbuf, size_t insize,
uint8_t *outbuf, size_t outsize)
{
int value;
size_t len;
len = ctx->reader->read_boolean(ctx, inbuf, insize, &value);
if(len > 0) {
if(value) {
if(!is_on) {
is_on = 1;
last_on_time = clock_seconds();
}
} else {
if(is_on) {
total_on_time += clock_seconds() - last_on_time;
is_on = 0;
}
}
#ifdef IPSO_LIGHT_CONTROL
if(IPSO_LIGHT_CONTROL.set_on) {
IPSO_LIGHT_CONTROL.set_on(value);
} else if(IPSO_LIGHT_CONTROL.set_dim_level) {
dim_level = value ? 100 : 0;
IPSO_LIGHT_CONTROL.set_dim_level(dim_level);
}
#endif /* IPSO_LIGHT_CONTROL */
}
return len;
}
/*---------------------------------------------------------------------------*/
static int
read_dim(lwm2m_context_t *ctx, uint8_t *outbuf, size_t outsize)
{
return ctx->writer->write_int(ctx, outbuf, outsize, dim_level);
}
/*---------------------------------------------------------------------------*/
static int
write_dim(lwm2m_context_t *ctx, const uint8_t *inbuf, size_t insize,
uint8_t *outbuf, size_t outsize)
{
int32_t value;
size_t len;
len = ctx->reader->read_int(ctx, inbuf, insize, &value);
if(len > 0) {
if(value < 0) {
value = 0;
} else if(value > 100) {
value = 100;
}
dim_level = value;
if(value > 0) {
if(!is_on) {
is_on = 1;
last_on_time = clock_seconds();
}
} else {
if(is_on) {
total_on_time += clock_seconds() - last_on_time;
is_on = 0;
}
}
#ifdef IPSO_LIGHT_CONTROL
if(IPSO_LIGHT_CONTROL.set_dim_level) {
IPSO_LIGHT_CONTROL.set_dim_level(dim_level);
} else if(IPSO_LIGHT_CONTROL.set_on) {
IPSO_LIGHT_CONTROL.set_on(is_on);
}
#endif /* IPSO_LIGHT_CONTROL */
}
return len;
}
/*---------------------------------------------------------------------------*/
static int
read_on_time(lwm2m_context_t *ctx, uint8_t *outbuf, size_t outsize)
{
unsigned long now;
if(is_on) {
/* Update the on time */
now = clock_seconds();
total_on_time += now - last_on_time;
last_on_time = now;
}
return ctx->writer->write_int(ctx, outbuf, outsize, (int32_t)total_on_time);
}
/*---------------------------------------------------------------------------*/
static int
write_on_time(lwm2m_context_t *ctx,
const uint8_t *inbuf, size_t insize,
uint8_t *outbuf, size_t outsize)
{
int32_t value;
size_t len;
len = ctx->reader->read_int(ctx, inbuf, insize, &value);
if(len > 0 && value == 0) {
total_on_time = 0;
if(is_on) {
last_on_time = clock_seconds();
}
}
return len;
}
/*---------------------------------------------------------------------------*/
LWM2M_RESOURCES(light_control_resources,
LWM2M_RESOURCE_CALLBACK(5850, { read_state, write_state, NULL }),
LWM2M_RESOURCE_CALLBACK(5851, { read_dim, write_dim, NULL }),
LWM2M_RESOURCE_CALLBACK(5852, { read_on_time, write_on_time, NULL }),
);
LWM2M_INSTANCES(light_control_instances,
LWM2M_INSTANCE(0, light_control_resources));
LWM2M_OBJECT(light_control, 3311, light_control_instances);
/*---------------------------------------------------------------------------*/
void
ipso_light_control_init(void)
{
#ifdef IPSO_LIGHT_CONTROL
if(IPSO_LIGHT_CONTROL.init) {
IPSO_LIGHT_CONTROL.init();
}
if(IPSO_LIGHT_CONTROL.is_on) {
is_on = IPSO_LIGHT_CONTROL.is_on();
}
if(IPSO_LIGHT_CONTROL.get_dim_level) {
dim_level = IPSO_LIGHT_CONTROL.get_dim_level();
if(dim_level > 0 && IPSO_LIGHT_CONTROL.is_on == NULL) {
is_on = 1;
}
}
#endif /* IPSO_LIGHT_CONTROL */
last_on_time = clock_seconds();
lwm2m_engine_register_object(&light_control);
}
/*---------------------------------------------------------------------------*/
/** @} */

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2015, Yanzi Networks AB.
* 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 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 HOLDER 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.
*/
/**
* \addtogroup oma-lwm2m
* @{
*/
/**
* \file
* Implementation of the IPSO Objects
* \author
* Joakim Eriksson <joakime@sics.se>
* Niclas Finne <nfi@sics.se>
*/
#include "contiki.h"
#include "ipso-objects.h"
/*---------------------------------------------------------------------------*/
void
ipso_objects_init(void)
{
/* initialize any relevant object for the IPSO Objects */
#ifdef IPSO_TEMPERATURE
ipso_temperature_init();
#endif
#if PLATFORM_HAS_BUTTON
ipso_button_init();
#endif
#ifdef IPSO_LIGHT_CONTROL
ipso_light_control_init();
#elif PLATFORM_HAS_LEDS
ipso_leds_control_init();
#endif
}
/*---------------------------------------------------------------------------*/
/** @} */

View file

@ -0,0 +1,122 @@
/*
* Copyright (c) 2015, Yanzi Networks AB.
* 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 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 HOLDER 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.
*/
/**
* \addtogroup apps
* @{
*/
/**
* \defgroup ipso-objects An implementation of IPSO Objects
* @{
*
* This application is an implementation of IPSO Objects for
* OMA Lightweight M2M.
*/
/**
* \file
* Header file for the Contiki IPSO Objects for OMA LWM2M
* \author
* Joakim Eriksson <joakime@sics.se>
* Niclas Finne <nfi@sics.se>
*/
#ifndef IPSO_OBJECTS_H_
#define IPSO_OBJECTS_H_
#include "contiki-conf.h"
void ipso_temperature_init(void);
void ipso_button_init(void);
void ipso_light_control_init(void);
void ipso_leds_control_init(void);
/* the init function to register the IPSO objects */
void ipso_objects_init(void);
struct ipso_objects_actuator {
/**
* \brief Initialize the driver.
*/
void (* init)(void);
/**
* \brief Check if the actuator is on or off.
*
* \return Zero if the actuator is off and non-zero otherwise.
*/
int (* is_on)(void);
/**
* \brief Set the actuator to on or off.
*
* \param onoroff Zero to set the actuator to off and non-zero otherwise.
* \return Zero if ok and a non-zero error code otherwise.
*/
int (* set_on)(int onoroff);
/**
* \brief Set the actuator to on or off.
*
* \param onoroff Zero to set the actuator to off and non-zero otherwise.
* \return Zero if ok and a non-zero error code otherwise.
*/
int (* get_dim_level)(void);
/**
* \brief Set the dim level of the actuator.
*
* \param level The dim level between 0% and 100%.
* \return Zero if ok and a non-zero error code otherwise.
*/
int (* set_dim_level)(int level);
};
struct ipso_objects_sensor {
/**
* \brief Initialize the driver.
*/
void (* init)(void);
/**
* \brief Read the sensor value in 1/1000 units.
*
* \param value A pointer to the variable to hold the sensor value.
* \return Zero if ok and a non-zero error code otherwise.
*/
int (* read_value)(int32_t *value);
};
#endif /* IPSO_OBJECTS_H_ */
/**
* @}
* @}
*/

View file

@ -0,0 +1,159 @@
/*
* Copyright (c) 2015, Yanzi Networks AB.
* 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 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 HOLDER 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.
*/
/**
* \addtogroup ipso-objects
* @{
*/
/**
* \file
* Implementation of OMA LWM2M / IPSO Temperature
* \author
* Joakim Eriksson <joakime@sics.se>
* Niclas Finne <nfi@sics.se>
*/
#include <stdint.h>
#include "ipso-objects.h"
#include "lwm2m-object.h"
#include "lwm2m-engine.h"
#include "er-coap-engine.h"
#ifdef IPSO_TEMPERATURE
extern const struct ipso_objects_sensor IPSO_TEMPERATURE;
#endif /* IPSO_TEMPERATURE */
#ifndef IPSO_TEMPERATURE_MIN
#define IPSO_TEMPERATURE_MIN (-50 * LWM2M_FLOAT32_FRAC)
#endif
#ifndef IPSO_TEMPERATURE_MAX
#define IPSO_TEMPERATURE_MAX (80 * LWM2M_FLOAT32_FRAC)
#endif
static struct ctimer periodic_timer;
static int32_t min_temp;
static int32_t max_temp;
static int read_temp(int32_t *value);
/*---------------------------------------------------------------------------*/
static int
temp(lwm2m_context_t *ctx, uint8_t *outbuf, size_t outsize)
{
int32_t value;
if(read_temp(&value)) {
return ctx->writer->write_float32fix(ctx, outbuf, outsize,
value, LWM2M_FLOAT32_BITS);
}
return 0;
}
/*---------------------------------------------------------------------------*/
LWM2M_RESOURCES(temperature_resources,
/* Temperature (Current) */
LWM2M_RESOURCE_CALLBACK(5700, { temp, NULL, NULL }),
/* Units */
LWM2M_RESOURCE_STRING(5701, "Cel"),
/* Min Range Value */
LWM2M_RESOURCE_FLOATFIX(5603, IPSO_TEMPERATURE_MIN),
/* Max Range Value */
LWM2M_RESOURCE_FLOATFIX(5604, IPSO_TEMPERATURE_MAX),
/* Min Measured Value */
LWM2M_RESOURCE_FLOATFIX_VAR(5601, &min_temp),
/* Max Measured Value */
LWM2M_RESOURCE_FLOATFIX_VAR(5602, &max_temp),
);
LWM2M_INSTANCES(temperature_instances,
LWM2M_INSTANCE(0, temperature_resources));
LWM2M_OBJECT(temperature, 3303, temperature_instances);
/*---------------------------------------------------------------------------*/
static int
read_temp(int32_t *value)
{
#ifdef IPSO_TEMPERATURE
int32_t temp;
if(IPSO_TEMPERATURE.read_value == NULL ||
IPSO_TEMPERATURE.read_value(&temp) != 0) {
return 0;
}
/* Convert milliCelsius to fix float */
*value = (temp * LWM2M_FLOAT32_FRAC) / 1000;
if(*value < min_temp) {
min_temp = *value;
lwm2m_object_notify_observers(&temperature, "/0/5601");
}
if(*value > max_temp) {
max_temp = *value;
lwm2m_object_notify_observers(&temperature, "/0/5602");
}
return 1;
#else /* IPSO_TEMPERATURE */
return 0;
#endif /* IPSO_TEMPERATURE */
}
/*---------------------------------------------------------------------------*/
static void
handle_periodic_timer(void *ptr)
{
static int32_t last_value = IPSO_TEMPERATURE_MIN;
int32_t v;
/* Only notify when the value has changed since last */
if(read_temp(&v) && v != last_value) {
last_value = v;
lwm2m_object_notify_observers(&temperature, "/0/5700");
}
ctimer_reset(&periodic_timer);
}
/*---------------------------------------------------------------------------*/
void
ipso_temperature_init(void)
{
int32_t v;
min_temp = IPSO_TEMPERATURE_MAX;
max_temp = IPSO_TEMPERATURE_MIN;
#ifdef IPSO_TEMPERATURE
if(IPSO_TEMPERATURE.init) {
IPSO_TEMPERATURE.init();
}
#endif /* IPSO_TEMPERATURE */
/* register this device and its handlers - the handlers automatically
sends in the object to handle */
lwm2m_engine_register_object(&temperature);
/* update temp and min/max + notify any listeners */
read_temp(&v);
ctimer_set(&periodic_timer, CLOCK_SECOND * 10, handle_periodic_timer, NULL);
}
/*---------------------------------------------------------------------------*/
/** @} */

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, Ralf Schlatterbeck Open Source Consulting
* Copyright (c) 2014-15, Ralf Schlatterbeck Open Source Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -46,8 +46,7 @@
#include <string.h>
#include "contiki.h"
#include "jsonparse.h"
/* Only coap 13 for now */
#include "er-coap-13.h"
#include "er-coap.h"
#include "generic_resource.h"
/* Error-handling macro */
@ -106,97 +105,138 @@ json_parse_variable
return 0;
}
void generic_handler
static const char *get_uri (void *request)
{
static char buf [MAX_URI_STRING_LENGTH];
const char *uri;
size_t len = coap_get_header_uri_path (request, &uri);
if (len > sizeof (buf) - 1) {
*buf = '\0';
} else {
strncpy (buf, uri, len);
buf [len] = '\0';
}
return buf;
}
void generic_get_handler
( void *request
, void *response
, uint8_t *buffer
, uint16_t preferred_size
, int32_t *offset
, char *name
, void (*from_str)(const char *name, const char *s)
, size_t (*to_str)(const char *name, uint8_t is_json, char *buf, size_t bsize)
, int is_str
, size_t (*to_str)(const char *name, const char *uri, char *buf, size_t bsize)
)
{
int success = 1;
char temp [MAX_GET_STRING_LENGTH];
size_t len = 0;
unsigned int accept = -1;
const char *uri = get_uri (request);
REST.get_header_accept (request, &accept);
if ( accept != -1
&& accept != REST.type.TEXT_PLAIN
&& accept != REST.type.APPLICATION_JSON
)
{
success = 0;
REST.set_response_status (response, REST.status.NOT_ACCEPTABLE);
return;
}
// TEXT format
if (accept == REST.type.APPLICATION_JSON) {
len += snprintf
( temp + len
, sizeof (temp) - len
, "{\n \"%s\" : %s"
, name
, is_str ? "\"" : ""
);
if (len > sizeof (temp)) {
success = 0;
goto out;
}
len += to_str (name, uri, temp + len, sizeof (temp) - len);
if (len > sizeof (temp)) {
success = 0;
goto out;
}
len += snprintf
( temp + len
, sizeof (temp) - len
, "%s\n}\n"
, is_str ? "\"" : ""
);
if (len > sizeof (temp)) {
success = 0;
goto out;
}
} else { // TEXT Format
len += to_str (name, uri, temp + len, sizeof (temp) - len);
if (len > sizeof (temp)) {
success = 0;
goto out;
}
len += snprintf (temp + len, sizeof (temp) - len, "\n");
if (len > sizeof (temp)) {
success = 0;
goto out;
}
}
memcpy (buffer, temp, len);
REST.set_header_content_type (response, accept);
REST.set_response_payload (response, buffer, len);
out :
if (!success) {
REST.set_response_status (response, REST.status.BAD_REQUEST);
}
}
void generic_put_handler
( void *request
, void *response
, uint8_t *buffer
, uint16_t preferred_size
, int32_t *offset
, char *name
, int (*from_str)(const char *name, const char *uri, const char *s)
)
{
int success = 1;
char temp [100];
int i = 0;
size_t len = 0;
int n_acc = 0;
const uint8_t *bytes = NULL;
const uint16_t *accept = NULL;
uint16_t a_ctype = REST.type.APPLICATION_JSON;
uint16_t c_ctype = REST.get_header_content_type (request);
unsigned int c_ctype;
const char *uri = get_uri (request);
REST.get_header_content_type (request, &c_ctype);
/* Seems like accepted type is currently unsupported? */
n_acc = REST.get_header_accept (request, &accept);
for (i=0; i<n_acc; i++) {
if ( accept [i] == REST.type.TEXT_PLAIN
|| accept [i] == REST.type.APPLICATION_JSON
)
{
a_ctype = accept [i];
break;
}
}
switch(REST.get_method_type(request)) {
case METHOD_GET:
// TEXT format
if (a_ctype == REST.type.TEXT_PLAIN) {
len += to_str (name, 0, temp + len, sizeof (temp) - len);
if (len > sizeof (temp)) {
success = 0;
break;
}
len += snprintf (temp + len, sizeof (temp) - len, "\n");
if (len > sizeof (temp)) {
success = 0;
break;
}
} else { // jSON Format
len += snprintf
(temp + len, sizeof (temp) - len, "{\n \"%s\" : ", name);
if (len > sizeof (temp)) {
success = 0;
break;
}
len += to_str (name, 1, temp + len, sizeof (temp) - len);
if (len > sizeof (temp)) {
success = 0;
break;
}
len += snprintf (temp + len, sizeof (temp) - len, "\n}\n");
if (len > sizeof (temp)) {
success = 0;
break;
}
}
memcpy (buffer, temp, len);
REST.set_header_content_type (response, a_ctype);
REST.set_response_payload (response, buffer, len);
break;
case METHOD_PUT:
if (from_str && (len = coap_get_payload(request, &bytes))) {
if (c_ctype == REST.type.TEXT_PLAIN) {
temp [sizeof (temp) - 1] = 0;
strncpy (temp, (const char *)bytes, MIN (len, sizeof (temp) - 1));
} else { // jSON Format
if (json_parse_variable (bytes, len, name, temp, sizeof (temp)) < 0) {
success = 0;
break;
}
}
from_str (name, temp);
REST.set_response_status(response, REST.status.CHANGED);
} else {
if (from_str && (len = coap_get_payload (request, &bytes))) {
if (c_ctype == REST.type.TEXT_PLAIN) {
int l = MIN (len, sizeof (temp) - 1);
temp [sizeof (temp) - 1] = 0;
strncpy (temp, (const char *)bytes, l);
temp [l] = 0;
} else { // jSON Format
if (json_parse_variable (bytes, len, name, temp, sizeof (temp)) < 0) {
success = 0;
goto out;
}
break;
default:
}
if (from_str (name, uri, temp) < 0) {
success = 0;
} else {
REST.set_response_status (response, REST.status.CHANGED);
}
} else {
success = 0;
}
out:
if (!success) {
REST.set_response_status(response, REST.status.BAD_REQUEST);
REST.set_response_status (response, REST.status.BAD_REQUEST);
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, Ralf Schlatterbeck Open Source Consulting
* Copyright (c) 2014-15, Ralf Schlatterbeck Open Source Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -50,6 +50,9 @@
#define STR__(s) #s
#define STR_(s) STR__(s)
#define MAX_GET_STRING_LENGTH 100
#define MAX_URI_STRING_LENGTH 30
/*
* A macro that extends the resource definition and also sets up the
* necessary handler function that calls format/parse routines that
@ -60,8 +63,8 @@
* Yes, this *is* a hack. But I hate boilerplate code.
*/
#define GENERIC_RESOURCE(name, methods, path, title, unit, fs, ts) \
void name##_handler \
#define GENERIC_RESOURCE(name, title, unit, is_str, fs, ts) \
static void name##_get_handler \
( void *request \
, void *response \
, uint8_t *buffer \
@ -69,16 +72,34 @@
, int32_t *offset \
) \
{ \
generic_handler \
(request, response, buffer, ps, offset, STR_(name), fs, ts); \
generic_get_handler \
(request, response, buffer, ps, offset, STR_(name), is_str, ts); \
} \
static void name##_put_handler \
( void *request \
, void *response \
, uint8_t *buffer \
, uint16_t ps \
, int32_t *offset \
) \
{ \
generic_put_handler \
(request, response, buffer, ps, offset, STR_(name), fs); \
} \
\
RESOURCE ( name, methods, path \
RESOURCE ( res_##name \
, "title=\"" STR_(title) "\"" \
";rt=UCUM:\"" STR_(unit) "\"" \
";ct=\"0 5\"" \
, (ts) ? name##_get_handler : NULL \
, NULL /* POST */ \
, (fs) ? name##_put_handler : NULL \
, NULL /* DELETE */ \
)
/* Ignore constant pointer tests above */
#pragma GCC diagnostic ignored "-Waddress"
/**
* \brief Parse a resource in json format
* \param bytes: Input string received via coap
@ -94,11 +115,8 @@ extern int8_t json_parse_variable
(const uint8_t *bytes, size_t len, char *name, char *buf, size_t buflen);
/**
* \brief Generic coap resource handler
* \brief Generic coap GET resource handler
* \param name: The name of the variable in json
* \param from_str: Application method to parse value from string
* and act on it, may be NULL in which case the resource only
* supports GET not PUT
* \param to_str: Application method to format value for output;
* the function may chose to format differently for coap or text
* The other parameters are the same as a normal resource handler
@ -106,21 +124,44 @@ extern int8_t json_parse_variable
*
* The callback functions get the name of the parameter as a first
* argument, this allows to re-use the same function for different
* parameters. The from_str in addition gets the string to parse.
* parameters.
* For the to_str function the is_json flag allows to generate a
* different string depending on the content-type. In addition it gets a
* buffer and the size of the buffer. It needs to return the number of
* bytes output, similar to sprintf.
*/
extern void generic_handler
extern void generic_get_handler
( void *request
, void *response
, uint8_t *buffer
, uint16_t preferred_size
, int32_t *offset
, char *name
, void (*from_str)(const char *name, const char *s)
, size_t (*to_str)(const char *name, uint8_t is_json, char *buf, size_t bsize)
, int is_str
, size_t (*to_str)(const char *name, const char *uri, char *buf, size_t bsize)
);
/**
* \brief Generic coap PUT resource handler
* \param name: The name of the variable in json
* \param from_str: Application method to parse value from string
* and act on it, may be NULL in which case the resource only
* supports GET not PUT
* The other parameters are the same as a normal resource handler
* This helps avoid boilerplate code for request handlers
*
* The callback functions get the name of the parameter as a first
* argument, this allows to re-use the same function for different
* parameters. The from_str in addition gets the string to parse.
*/
extern void generic_put_handler
( void *request
, void *response
, uint8_t *buffer
, uint16_t preferred_size
, int32_t *offset
, char *name
, int (*from_str)(const char *name, const char *uri, const char *s)
);
/*

View file

@ -45,6 +45,7 @@
#define JSON_TYPE_PAIR ':'
#define JSON_TYPE_PAIR_NAME 'N' /* for N:V pairs */
#define JSON_TYPE_STRING '"'
#define JSON_TYPE_UINT 'U'
#define JSON_TYPE_INT 'I'
#define JSON_TYPE_NUMBER '0'
#define JSON_TYPE_ERROR 0
@ -56,12 +57,21 @@
#define JSON_TYPE_CALLBACK 'C'
/* integer pointer types */
#define JSON_TYPE_S8PTR 'b'
#define JSON_TYPE_U8PTR 'B'
#define JSON_TYPE_S16PTR 'w'
#define JSON_TYPE_U16PTR 'W'
#define JSON_TYPE_S32PTR 'd'
#define JSON_TYPE_U32PTR 'D'
enum {
JSON_ERROR_OK,
JSON_ERROR_SYNTAX,
JSON_ERROR_UNEXPECTED_ARRAY,
JSON_ERROR_UNEXPECTED_END_OF_ARRAY,
JSON_ERROR_UNEXPECTED_OBJECT,
JSON_ERROR_UNEXPECTED_END_OF_OBJECT,
JSON_ERROR_UNEXPECTED_STRING
};

View file

@ -43,6 +43,14 @@ push(struct jsonparse_state *state, char c)
return state->depth < JSONPARSE_MAX_DEPTH;
}
/*--------------------------------------------------------------------*/
static void
modify(struct jsonparse_state *state, char c)
{
if(state->depth > 0) {
state->stack[state->depth - 1] = c;
}
}
/*--------------------------------------------------------------------*/
static char
pop(struct jsonparse_state *state)
{
@ -50,25 +58,31 @@ pop(struct jsonparse_state *state)
return JSON_TYPE_ERROR;
}
state->depth--;
state->vtype = state->stack[state->depth];
return state->stack[state->depth];
}
/*--------------------------------------------------------------------*/
/* will pass by the value and store the start and length of the value for
atomic types */
/*--------------------------------------------------------------------*/
static void
static char
atomic(struct jsonparse_state *state, char type)
{
char c;
const char *str;
int len;
state->vstart = state->pos;
state->vtype = type;
if(type == JSON_TYPE_STRING || type == JSON_TYPE_PAIR_NAME) {
while((c = state->json[state->pos++]) && c != '"') {
if(c == '\\') {
state->pos++; /* skip current char */
}
}
if (c != '"') {
state->error = JSON_ERROR_SYNTAX;
return JSON_TYPE_ERROR;
}
state->vlen = state->pos - state->vstart - 1;
} else if(type == JSON_TYPE_NUMBER) {
do {
@ -82,8 +96,31 @@ atomic(struct jsonparse_state *state, char type)
/* need to back one step since first char is already gone */
state->vstart--;
state->vlen = state->pos - state->vstart;
} else if(type == JSON_TYPE_NULL || type == JSON_TYPE_TRUE || type == JSON_TYPE_FALSE) {
state->vstart--;
switch (type) {
case JSON_TYPE_NULL: str = "null"; break;
case JSON_TYPE_TRUE: str = "true"; break;
case JSON_TYPE_FALSE: str = "false"; break;
default: str = ""; break;
}
while ((c = state->json[state->pos]) && c != ' ' && c != ',' && c != ']' && c != '}') {
state->pos++;
}
state->vlen = state->pos - state->vstart;
len = strlen(str);
len = state->vlen > len ? state->vlen : len;
if (strncmp(str, &state->json[state->vstart], len) != 0) {
state->error = JSON_ERROR_SYNTAX;
return JSON_TYPE_ERROR;
}
}
/* no other types for now... */
state->vtype = type;
return state->vtype;
}
/*--------------------------------------------------------------------*/
static void
@ -97,6 +134,17 @@ skip_ws(struct jsonparse_state *state)
}
}
/*--------------------------------------------------------------------*/
static int
is_atomic(struct jsonparse_state *state)
{
char v = state->vtype;
if(v == 'N' || v == '"' || v == '0' || v == 'n' || v == 't' || v == 'f') {
return 1;
} else {
return 0;
}
}
/*--------------------------------------------------------------------*/
void
jsonparse_setup(struct jsonparse_state *state, const char *json, int len)
{
@ -105,6 +153,7 @@ jsonparse_setup(struct jsonparse_state *state, const char *json, int len)
state->pos = 0;
state->depth = 0;
state->error = 0;
state->vtype = 0;
state->stack[0] = 0;
}
/*--------------------------------------------------------------------*/
@ -113,31 +162,33 @@ jsonparse_next(struct jsonparse_state *state)
{
char c;
char s;
char v;
skip_ws(state);
c = state->json[state->pos];
s = jsonparse_get_type(state);
v = state->vtype;
state->pos++;
switch(c) {
case '{':
push(state, c);
if((s == 0 && v == 0) || s == '[' || s == ':') {
push(state, c);
} else {
state->error = JSON_ERROR_UNEXPECTED_OBJECT;
return JSON_TYPE_ERROR;
}
return c;
case '}':
if(s == ':' && state->vtype != 0) {
/* printf("Popping vtype: '%c'\n", state->vtype); */
pop(state);
s = jsonparse_get_type(state);
}
if(s == '{') {
if((s == ':' && v != ',' && v != 0 ) || (s == '{' && v == 0)) {
pop(state);
} else {
state->error = JSON_ERROR_SYNTAX;
state->error = JSON_ERROR_UNEXPECTED_END_OF_OBJECT;
return JSON_TYPE_ERROR;
}
return c;
case ']':
if(s == '[') {
if(s == '[' && v != ',') {
pop(state);
} else {
state->error = JSON_ERROR_UNEXPECTED_END_OF_ARRAY;
@ -145,41 +196,67 @@ jsonparse_next(struct jsonparse_state *state)
}
return c;
case ':':
push(state, c);
return c;
if(s == '{' && v == 'N') {
modify(state, ':');
state->vtype = 0;
} else {
state->error = JSON_ERROR_SYNTAX;
return JSON_TYPE_ERROR;
}
return jsonparse_next(state);
case ',':
/* if x:y ... , */
if(s == ':' && state->vtype != 0) {
pop(state);
if(s == ':' && v != 0) {
modify(state, '{');
state->vtype = c;
} else if(s == '[') {
/* ok! */
state->vtype = c;
} else {
state->error = JSON_ERROR_SYNTAX;
return JSON_TYPE_ERROR;
}
return c;
case '"':
if(s == '{' || s == '[' || s == ':') {
atomic(state, c = (s == '{' ? JSON_TYPE_PAIR_NAME : c));
if((s == 0 && v == 0) || s == '{' || s == '[' || s == ':') {
return atomic(state, c = (s == '{' ? JSON_TYPE_PAIR_NAME : c));
} else {
state->error = JSON_ERROR_UNEXPECTED_STRING;
return JSON_TYPE_ERROR;
}
return c;
case '[':
if(s == '{' || s == '[' || s == ':') {
if((s == 0 && v == 0) || s == '[' || s == ':') {
push(state, c);
} else {
state->error = JSON_ERROR_UNEXPECTED_ARRAY;
return JSON_TYPE_ERROR;
}
return c;
case 0:
if(v == 0 || state->depth > 0) {
state->error = JSON_ERROR_SYNTAX;
}
return JSON_TYPE_ERROR;
default:
if(s == ':' || s == '[') {
if(c <= '9' && c >= '0') {
atomic(state, JSON_TYPE_NUMBER);
return JSON_TYPE_NUMBER;
if(s == 0 || s == ':' || s == '[') {
if (v != 0 && v != ',') {
state->error = JSON_ERROR_SYNTAX;
return JSON_TYPE_ERROR;
}
if(c == '-' || (c <= '9' && c >= '0')) {
return atomic(state, JSON_TYPE_NUMBER);
} else if(c == 'n') {
return atomic(state, JSON_TYPE_NULL);
} else if(c == 't') {
return atomic(state, JSON_TYPE_TRUE);
} else if(c == 'f') {
return atomic(state, JSON_TYPE_FALSE);
} else {
state->error = JSON_ERROR_SYNTAX;
return JSON_TYPE_ERROR;
}
} else if(s == '{') {
state->error = JSON_ERROR_SYNTAX;
return JSON_TYPE_ERROR;
}
}
return 0;
@ -192,16 +269,31 @@ jsonparse_next(struct jsonparse_state *state)
int
jsonparse_copy_value(struct jsonparse_state *state, char *str, int size)
{
int i;
int i, o;
char c;
if(state->vtype == 0) {
if(!is_atomic(state)) {
return 0;
}
size = size <= state->vlen ? (size - 1) : state->vlen;
for(i = 0; i < size; i++) {
str[i] = state->json[state->vstart + i];
for(i = 0, o = 0; i < state->vlen && o < size - 1; i++) {
c = state->json[state->vstart + i];
if(c == '\\') {
i++;
switch(state->json[state->vstart + i]) {
case '"': str[o++] = '"'; break;
case '\\': str[o++] = '\\'; break;
case '/': str[o++] = '/'; break;
case 'b': str[o++] = '\b'; break;
case 'f': str[o++] = '\f'; break;
case 'n': str[o++] = '\n'; break;
case 'r': str[o++] = '\r'; break;
case 't': str[o++] = '\t'; break;
}
continue;
}
str[o++] = c;
}
str[i] = 0;
str[o] = 0;
return state->vtype;
}
/*--------------------------------------------------------------------*/
@ -228,7 +320,7 @@ jsonparse_get_value_as_long(struct jsonparse_state *state)
int
jsonparse_strcmp_value(struct jsonparse_state *state, const char *str)
{
if(state->vtype == 0) {
if(!is_atomic(state)) {
return -1;
}
return strncmp(str, &state->json[state->vstart], state->vlen);

View file

@ -79,16 +79,11 @@ jsontree_write_string(const struct jsontree_context *js_ctx, const char *text)
}
/*---------------------------------------------------------------------------*/
void
jsontree_write_int(const struct jsontree_context *js_ctx, int value)
jsontree_write_uint(const struct jsontree_context *js_ctx, unsigned int value)
{
char buf[10];
int l;
if(value < 0) {
js_ctx->putchar('-');
value = -value;
}
l = sizeof(buf) - 1;
do {
buf[l--] = '0' + (value % 10);
@ -101,6 +96,17 @@ jsontree_write_int(const struct jsontree_context *js_ctx, int value)
}
/*---------------------------------------------------------------------------*/
void
jsontree_write_int(const struct jsontree_context *js_ctx, int value)
{
if(value < 0) {
js_ctx->putchar('-');
value = -value;
}
jsontree_write_uint(js_ctx, value);
}
/*---------------------------------------------------------------------------*/
void
jsontree_setup(struct jsontree_context *js_ctx, struct jsontree_value *root,
int (* putchar)(int))
{
@ -132,6 +138,9 @@ jsontree_print_next(struct jsontree_context *js_ctx)
{
struct jsontree_value *v;
int index;
#if JSONTREE_PRETTY
int indent;
#endif
v = js_ctx->values[js_ctx->depth];
@ -145,10 +154,19 @@ jsontree_print_next(struct jsontree_context *js_ctx)
index = js_ctx->index[js_ctx->depth];
if(index == 0) {
js_ctx->putchar(v->type);
#if JSONTREE_PRETTY
js_ctx->putchar('\n');
#endif
}
if(index >= o->count) {
#if JSONTREE_PRETTY
js_ctx->putchar('\n');
indent = js_ctx->depth;
while (indent--) {
js_ctx->putchar(' ');
js_ctx->putchar(' ');
}
#endif
js_ctx->putchar(v->type + 2);
/* Default operation: back up one level! */
break;
@ -156,12 +174,26 @@ jsontree_print_next(struct jsontree_context *js_ctx)
if(index > 0) {
js_ctx->putchar(',');
#if JSONTREE_PRETTY
js_ctx->putchar('\n');
#endif
}
#if JSONTREE_PRETTY
indent = js_ctx->depth + 1;
while (indent--) {
js_ctx->putchar(' ');
js_ctx->putchar(' ');
}
#endif
if(v->type == JSON_TYPE_OBJECT) {
jsontree_write_string(js_ctx,
((struct jsontree_object *)o)->pairs[index].name);
js_ctx->putchar(':');
#if JSONTREE_PRETTY
js_ctx->putchar(' ');
#endif
ov = ((struct jsontree_object *)o)->pairs[index].value;
} else {
ov = o->values[index];
@ -177,6 +209,10 @@ jsontree_print_next(struct jsontree_context *js_ctx)
jsontree_write_string(js_ctx, ((struct jsontree_string *)v)->value);
/* Default operation: back up one level! */
break;
case JSON_TYPE_UINT:
jsontree_write_uint(js_ctx, ((struct jsontree_uint *)v)->value);
/* Default operation: back up one level! */
break;
case JSON_TYPE_INT:
jsontree_write_int(js_ctx, ((struct jsontree_int *)v)->value);
/* Default operation: back up one level! */
@ -198,6 +234,30 @@ jsontree_print_next(struct jsontree_context *js_ctx)
}
/* Default operation: back up one level! */
break;
case JSON_TYPE_S8PTR:
jsontree_write_int(js_ctx, *((int8_t *)((struct jsontree_ptr *)v)->value));
/* Default operation: back up one level! */
break;
case JSON_TYPE_U8PTR:
jsontree_write_uint(js_ctx, *((uint8_t *)((struct jsontree_ptr *)v)->value));
/* Default operation: back up one level! */
break;
case JSON_TYPE_S16PTR:
jsontree_write_int(js_ctx, *((int16_t *)((struct jsontree_ptr *)v)->value));
/* Default operation: back up one level! */
break;
case JSON_TYPE_U16PTR:
jsontree_write_uint(js_ctx, *((uint16_t *)((struct jsontree_ptr *)v)->value));
/* Default operation: back up one level! */
break;
case JSON_TYPE_S32PTR:
jsontree_write_int(js_ctx, *((int32_t *)((struct jsontree_ptr *)v)->value));
/* Default operation: back up one level! */
break;
case JSON_TYPE_U32PTR:
jsontree_write_uint(js_ctx, *((uint32_t *)((struct jsontree_ptr *)v)->value));
/* Default operation: back up one level! */
break;
}
default:
PRINTF("\nError: Illegal json type:'%c'\n", v->type);

View file

@ -49,6 +49,12 @@
#define JSONTREE_MAX_DEPTH 10
#endif /* JSONTREE_CONF_MAX_DEPTH */
#ifdef JSONTREE_CONF_PRETTY
#define JSONTREE_PRETTY JSONTREE_CONF_PRETTY
#else
#define JSONTREE_PRETTY 0
#endif /* JSONTREE_CONF_PRETTY */
struct jsontree_context {
struct jsontree_value *values[JSONTREE_MAX_DEPTH];
uint16_t index[JSONTREE_MAX_DEPTH];
@ -68,6 +74,11 @@ struct jsontree_string {
const char *value;
};
struct jsontree_uint {
uint8_t type;
unsigned int value;
};
struct jsontree_int {
uint8_t type;
int value;
@ -98,6 +109,11 @@ struct jsontree_array {
struct jsontree_value **values;
};
struct jsontree_ptr {
uint8_t type;
const void *value;
};
#define JSONTREE_STRING(text) {JSON_TYPE_STRING, (text)}
#define JSONTREE_PAIR(name, value) {(name), (struct jsontree_value *)(value)}
#define JSONTREE_CALLBACK(output, set) {JSON_TYPE_CALLBACK, (output), (set)}
@ -130,6 +146,8 @@ void jsontree_reset(struct jsontree_context *js_ctx);
const char *jsontree_path_name(const struct jsontree_context *js_ctx,
int depth);
void jsontree_write_uint(const struct jsontree_context *js_ctx,
unsigned int value);
void jsontree_write_int(const struct jsontree_context *js_ctx, int value);
void jsontree_write_atom(const struct jsontree_context *js_ctx,
const char *text);

1
apps/mqtt/Makefile.mqtt Normal file
View file

@ -0,0 +1 @@
mqtt_src = mqtt.c

1483
apps/mqtt/mqtt.c Normal file

File diff suppressed because it is too large Load diff

509
apps/mqtt/mqtt.h Normal file
View file

@ -0,0 +1,509 @@
/*
* Copyright (c) 2015, Texas Instruments Incorporated - http://www.ti.com/
* 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 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.
*/
/*---------------------------------------------------------------------------*/
/**
* \addtogroup apps
* @{
*
* \defgroup mqtt-engine An implementation of MQTT v3.1
* @{
*
* This application is an engine for MQTT v3.1. It supports QoS Levels 0 and 1.
*
* MQTT is a Client Server publish/subscribe messaging transport protocol.
* It is light weight, open, simple, and designed so as to be easy to implement.
* These characteristics make it ideal for use in many situations, including
* constrained environments such as for communication in Machine to Machine
* (M2M) and Internet of Things (IoT) contexts where a small code footprint is
* required and/or network bandwidth is at a premium.
*
* The protocol runs over TCP/IP, more specifically tcp_socket.
* Its features include:
*
* - Use of the publish/subscribe message pattern which provides
* one-to-many message distribution and decoupling of applications.
* - A messaging transport that is agnostic to the content of the payload.
* Three qualities of service for message delivery:
* -- "At most once" (0), where messages are delivered according to the best
* efforts of the operating environment. Message loss can occur.
* This level could be used, for example, with ambient sensor data where it
* does not matter if an individual reading is lost as the next one will be
* published soon after.
* --"At least once" (1), where messages are assured to arrive but duplicates
* can occur.
* -- "Exactly once" (2), where message are assured to arrive exactly once.
* This level could be used, for example, with billing systems where duplicate
* or lost messages could lead to incorrect charges being applied. This QoS
* level is currently not supported in this implementation.
*
* - A small transport overhead and protocol exchanges minimized to reduce
* network traffic.
* - A mechanism, Last Will, to notify interested parties when an abnormal
* disconnection occurs.
*
* The protocol specification and other useful information can be found
* here: http://mqtt.org
*
*/
/**
* \file
* Header file for the Contiki MQTT engine
*
* \author
* Texas Instruments
*/
/*---------------------------------------------------------------------------*/
#ifndef MQTT_H_
#define MQTT_H_
/*---------------------------------------------------------------------------*/
#include "contiki.h"
#include "contiki-net.h"
#include "contiki-lib.h"
#include "lib/random.h"
#include "sys/ctimer.h"
#include "sys/etimer.h"
#include "net/rpl/rpl.h"
#include "net/ip/uip.h"
#include "net/ipv6/uip-ds6.h"
#include "dev/leds.h"
#include "tcp-socket.h"
#include "udp-socket.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/*---------------------------------------------------------------------------*/
/* Protocol constants */
#define MQTT_CLIENT_ID_MAX_LEN 23
/* Size of the underlying TCP buffers */
#define MQTT_TCP_INPUT_BUFF_SIZE 512
#define MQTT_TCP_OUTPUT_BUFF_SIZE 512
#define MQTT_INPUT_BUFF_SIZE 512
#define MQTT_MAX_TOPIC_LENGTH 64
#define MQTT_MAX_TOPICS_PER_SUBSCRIBE 1
#define MQTT_FHDR_SIZE 1
#define MQTT_MAX_REMAINING_LENGTH_BYTES 4
#define MQTT_PROTOCOL_VERSION 3
#define MQTT_PROTOCOL_NAME "MQIsdp"
#define MQTT_TOPIC_MAX_LENGTH 128
/*---------------------------------------------------------------------------*/
/*
* Debug configuration, this is similar but not exactly like the Debugging
* System discussion at https://github.com/contiki-os/contiki/wiki.
*/
#define DEBUG_MQTT 0
#if DEBUG_MQTT == 1
#define DBG(...) printf(__VA_ARGS__)
#else
#define DBG(...)
#endif /* DEBUG */
/*---------------------------------------------------------------------------*/
extern process_event_t mqtt_update_event;
/* Forward declaration */
struct mqtt_connection;
typedef enum {
MQTT_RETAIN_OFF,
MQTT_RETAIN_ON,
} mqtt_retain_t;
/**
* \brief MQTT engine events
*/
typedef enum {
MQTT_EVENT_CONNECTED,
MQTT_EVENT_DISCONNECTED,
MQTT_EVENT_SUBACK,
MQTT_EVENT_UNSUBACK,
MQTT_EVENT_PUBLISH,
MQTT_EVENT_PUBACK,
/* Errors */
MQTT_EVENT_ERROR = 0x80,
MQTT_EVENT_PROTOCOL_ERROR,
MQTT_EVENT_CONNECTION_REFUSED_ERROR,
MQTT_EVENT_DNS_ERROR,
MQTT_EVENT_NOT_IMPLEMENTED_ERROR,
/* Add more */
} mqtt_event_t;
typedef enum {
MQTT_STATUS_OK,
MQTT_STATUS_OUT_QUEUE_FULL,
/* Errors */
MQTT_STATUS_ERROR = 0x80,
MQTT_STATUS_NOT_CONNECTED_ERROR,
MQTT_STATUS_INVALID_ARGS_ERROR,
MQTT_STATUS_DNS_ERROR,
} mqtt_status_t;
typedef enum {
MQTT_QOS_LEVEL_0,
MQTT_QOS_LEVEL_1,
MQTT_QOS_LEVEL_2,
} mqtt_qos_level_t;
typedef enum {
MQTT_QOS_STATE_NO_ACK,
MQTT_QOS_STATE_GOT_ACK,
/* Expand for QoS 2 */
} mqtt_qos_state_t;
/*---------------------------------------------------------------------------*/
/*
* This is the state of the connection itself.
*
* N.B. The order is important because of runtime checks on how far the
* connection has proceeded.
*/
typedef enum {
MQTT_CONN_STATE_ERROR,
MQTT_CONN_STATE_DNS_ERROR,
MQTT_CONN_STATE_DISCONNECTING,
MQTT_CONN_STATE_NOT_CONNECTED,
MQTT_CONN_STATE_DNS_LOOKUP,
MQTT_CONN_STATE_TCP_CONNECTING,
MQTT_CONN_STATE_TCP_CONNECTED,
MQTT_CONN_STATE_CONNECTING_TO_BROKER,
MQTT_CONN_STATE_CONNECTED_TO_BROKER,
MQTT_CONN_STATE_SENDING_MQTT_DISCONNECT,
MQTT_CONN_STATE_ABORT_IMMEDIATE,
} mqtt_conn_state_t;
/*---------------------------------------------------------------------------*/
struct mqtt_string {
char *string;
uint16_t length;
};
/*
* Note that the pairing mid <-> QoS level only applies one-to-one if we only
* allow the subscription of one topic at a time. Otherwise we will have an
* ordered list of QoS levels corresponding to the order of topics.
*
* This could be part of a union of event data structures.
*/
struct mqtt_suback_event {
uint16_t mid;
mqtt_qos_level_t qos_level;
};
/* This is the MQTT message that is exposed to the end user. */
struct mqtt_message {
uint32_t mid;
char topic[MQTT_MAX_TOPIC_LENGTH + 1]; /* +1 for string termination */
uint8_t *payload_chunk;
uint16_t payload_chunk_length;
uint8_t first_chunk;
uint16_t payload_length;
uint16_t payload_left;
};
/* This struct represents a packet received from the MQTT server. */
struct mqtt_in_packet {
/* Used by the list interface, must be first in the struct. */
struct mqtt_connection *next;
/* Total bytes read so far. Compared to the remaining length to to decide when
* we've read the payload. */
uint32_t byte_counter;
uint8_t packet_received;
uint8_t fhdr;
uint16_t remaining_length;
uint16_t mid;
/* Helper variables needed to decode the remaining_length */
uint8_t remaining_multiplier;
uint8_t has_remaining_length;
uint8_t remaining_length_bytes;
/* Not the same as payload in the MQTT sense, it also contains the variable
* header.
*/
uint8_t payload_pos;
uint8_t payload[MQTT_INPUT_BUFF_SIZE];
/* Message specific data */
uint16_t topic_len;
uint16_t topic_pos;
uint8_t topic_len_received;
uint8_t topic_received;
};
/* This struct represents a packet sent to the MQTT server. */
struct mqtt_out_packet {
uint8_t fhdr;
uint32_t remaining_length;
uint8_t remaining_length_enc[MQTT_MAX_REMAINING_LENGTH_BYTES];
uint8_t remaining_length_enc_bytes;
uint16_t mid;
char *topic;
uint16_t topic_length;
uint8_t *payload;
uint32_t payload_size;
mqtt_qos_level_t qos;
mqtt_qos_state_t qos_state;
mqtt_retain_t retain;
};
/*---------------------------------------------------------------------------*/
/**
* \brief MQTT event callback function
* \param m A pointer to a MQTT connection
* \param event The event number
* \param data A user-defined pointer
*
* The MQTT socket event callback function gets called whenever there is an
* event on a MQTT connection, such as the connection getting connected
* or closed.
*/
typedef void (*mqtt_event_callback_t)(struct mqtt_connection *m,
mqtt_event_t event,
void *data);
typedef void (*mqtt_topic_callback_t)(struct mqtt_connection *m,
struct mqtt_message *msg);
/*---------------------------------------------------------------------------*/
struct mqtt_will {
struct mqtt_string topic;
struct mqtt_string message;
mqtt_qos_level_t qos;
};
struct mqtt_credentials {
struct mqtt_string username;
struct mqtt_string password;
};
struct mqtt_connection {
/* Used by the list interface, must be first in the struct */
struct mqtt_connection *next;
struct timer t;
struct mqtt_string client_id;
uint8_t connect_vhdr_flags;
uint8_t auto_reconnect;
uint16_t keep_alive;
struct ctimer keep_alive_timer;
uint8_t waiting_for_pingresp;
struct mqtt_will will;
struct mqtt_credentials credentials;
mqtt_conn_state_t state;
mqtt_event_callback_t event_callback;
/* Internal data */
uint16_t mid_counter;
/* Used for communication between MQTT API and APP */
uint8_t out_queue_full;
struct process *app_process;
/* Outgoing data related */
uint8_t *out_buffer_ptr;
uint8_t out_buffer[MQTT_TCP_OUTPUT_BUFF_SIZE];
uint8_t out_buffer_sent;
struct mqtt_out_packet out_packet;
struct pt out_proto_thread;
uint32_t out_write_pos;
uint16_t max_segment_size;
/* Incoming data related */
uint8_t in_buffer[MQTT_TCP_INPUT_BUFF_SIZE];
struct mqtt_in_packet in_packet;
struct mqtt_message in_publish_msg;
/* TCP related information */
char *server_host;
uip_ipaddr_t server_ip;
uint16_t server_port;
struct tcp_socket socket;
};
/* This is the API exposed to the user. */
/*---------------------------------------------------------------------------*/
/**
* \brief Initializes the MQTT engine.
* \param conn A pointer to the MQTT connection.
* \param app_process A pointer to the application process handling the MQTT
* connection.
* \param client_id A pointer to the MQTT client ID.
* \param event_callback Callback function responsible for handling the
* callback from MQTT engine.
* \param max_segment_size The TCP segment size to use for this MQTT/TCP
* connection.
* \return MQTT_STATUS_OK or MQTT_STATUS_INVALID_ARGS_ERROR
*
* This function initializes the MQTT engine and shall be called before any
* other MQTT function.
*/
mqtt_status_t mqtt_register(struct mqtt_connection *conn,
struct process *app_process,
char *client_id,
mqtt_event_callback_t event_callback,
uint16_t max_segment_size);
/*---------------------------------------------------------------------------*/
/**
* \brief Connects to a MQTT broker.
* \param conn A pointer to the MQTT connection.
* \param host IP address of the broker to connect to.
* \param port Port of the broker to connect to, default is MQTT port is 1883.
* \param keep_alive Keep alive timer in seconds. Used by broker to handle
* client disc. Defines the maximum time interval between two messages
* from the client. Shall be min 1.5 x report interval.
* \return MQTT_STATUS_OK or an error status
*
* This function connects to a MQTT broker.
*/
mqtt_status_t mqtt_connect(struct mqtt_connection *conn,
char *host,
uint16_t port,
uint16_t keep_alive);
/*---------------------------------------------------------------------------*/
/**
* \brief Disconnects from a MQTT broker.
* \param conn A pointer to the MQTT connection.
*
* This function disconnects from a MQTT broker.
*/
void mqtt_disconnect(struct mqtt_connection *conn);
/*---------------------------------------------------------------------------*/
/**
* \brief Subscribes to a MQTT topic.
* \param conn A pointer to the MQTT connection.
* \param mid A pointer to message ID.
* \param topic A pointer to the topic to subscribe to.
* \param qos_level Quality Of Service level to use. Currently supports 0, 1.
* \return MQTT_STATUS_OK or some error status
*
* This function subscribes to a topic on a MQTT broker.
*/
mqtt_status_t mqtt_subscribe(struct mqtt_connection *conn,
uint16_t *mid,
char *topic,
mqtt_qos_level_t qos_level);
/*---------------------------------------------------------------------------*/
/**
* \brief Unsubscribes from a MQTT topic.
* \param conn A pointer to the MQTT connection.
* \param mid A pointer to message ID.
* \param topic A pointer to the topic to unsubscribe from.
* \return MQTT_STATUS_OK or some error status
*
* This function unsubscribes from a topic on a MQTT broker.
*/
mqtt_status_t mqtt_unsubscribe(struct mqtt_connection *conn,
uint16_t *mid,
char *topic);
/*---------------------------------------------------------------------------*/
/**
* \brief Publish to a MQTT topic.
* \param conn A pointer to the MQTT connection.
* \param mid A pointer to message ID.
* \param topic A pointer to the topic to subscribe to.
* \param payload A pointer to the topic payload.
* \param payload_size Payload size.
* \param qos_level Quality Of Service level to use. Currently supports 0, 1.
* \param retain If the RETAIN flag is set to 1, in a PUBLISH Packet sent by a
* Client to a Server, the Server MUST store the Application Message
* and its QoS, so that it can be delivered to future subscribers whose
* subscriptions match its topic name
* \return MQTT_STATUS_OK or some error status
*
* This function publishes to a topic on a MQTT broker.
*/
mqtt_status_t mqtt_publish(struct mqtt_connection *conn,
uint16_t *mid,
char *topic,
uint8_t *payload,
uint32_t payload_size,
mqtt_qos_level_t qos_level,
mqtt_retain_t retain);
/*---------------------------------------------------------------------------*/
/**
* \brief Set the user name and password for a MQTT client.
* \param conn A pointer to the MQTT connection.
* \param username A pointer to the user name.
* \param password A pointer to the password.
*
* This function sets clients user name and password to use when connecting to
* a MQTT broker.
*/
void mqtt_set_username_password(struct mqtt_connection *conn,
char *username,
char *password);
/*---------------------------------------------------------------------------*/
/**
* \brief Set the last will topic and message for a MQTT client.
* \param conn A pointer to the MQTT connection.
* \param topic A pointer to the Last Will topic.
* \param message A pointer to the Last Will message (payload).
* \param qos The desired QoS level.
*
* This function sets clients Last Will topic and message (payload).
* If the Will Flag is set to 1 (using the function) this indicates that,
* if the Connect request is accepted, a Will Message MUST be stored on the
* Server and associated with the Network Connection. The Will Message MUST
* be published when the Network Connection is subsequently closed.
*
* This functionality can be used to get notified that a device has
* disconnected from the broker.
*
*/
void mqtt_set_last_will(struct mqtt_connection *conn,
char *topic,
char *message,
mqtt_qos_level_t qos);
#define mqtt_connected(conn) \
((conn)->state == MQTT_CONN_STATE_CONNECTED_TO_BROKER ? 1 : 0)
#define mqtt_ready(conn) \
(!(conn)->out_queue_full && mqtt_connected((conn)))
/*---------------------------------------------------------------------------*/
#endif /* MQTT_H_ */
/*---------------------------------------------------------------------------*/
/**
* @}
* @}
*/

View file

@ -113,7 +113,7 @@ makestrings(void)
makeaddr(&addr, gateway);
#if UIP_UDP
addrptr = resolv_getserver();
addrptr = uip_nameserver_get(0);
if(addrptr != NULL) {
makeaddr(addrptr, dnsserver);
}
@ -152,7 +152,7 @@ apply_tcpipconfig(void)
#if UIP_UDP
nullterminate(dnsserver);
if(uiplib_ipaddrconv(dnsserver, &addr)) {
resolv_conf(&addr);
uip_nameserver_update(&addr, UIP_NAMESERVER_INFINITE_LIFETIME);
}
#endif /* UIP_UDP */
}

View file

@ -0,0 +1,13 @@
oma-lwm2m_src = \
lwm2m-object.c \
lwm2m-engine.c \
lwm2m-device.c \
lwm2m-server.c \
lwm2m-security.c \
oma-tlv.c \
oma-tlv-reader.c \
oma-tlv-writer.c \
lwm2m-plain-text.c \
lwm2m-json.c \
#
CFLAGS += -DHAVE_OMA_LWM2M=1

View file

@ -0,0 +1,152 @@
/*
* Copyright (c) 2015, Yanzi Networks AB.
* 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 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 HOLDER 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.
*/
/**
* \addtogroup oma-lwm2m
* @{
*/
/**
* \file
* Implementation of the Contiki OMA LWM2M device
* \author
* Joakim Eriksson <joakime@sics.se>
* Niclas Finne <nfi@sics.se>
*/
#include "lwm2m-object.h"
#include "lwm2m-device.h"
#include "lwm2m-engine.h"
#define DEBUG 0
#if DEBUG
#include <stdio.h>
#define PRINTF(...) printf(__VA_ARGS__)
#else
#define PRINTF(...)
#endif
static int32_t time_offset = 0;
/*---------------------------------------------------------------------------*/
static int
read_lwtime(lwm2m_context_t *ctx, uint8_t *outbuf, size_t outsize)
{
return ctx->writer->write_int(ctx, outbuf, outsize,
time_offset + clock_seconds());
}
/*---------------------------------------------------------------------------*/
static int
set_lwtime(lwm2m_context_t *ctx, const uint8_t *inbuf, size_t insize,
uint8_t *outbuf, size_t outsize)
{
/* assume that this only read one TLV value */
int32_t lw_time;
size_t len = ctx->reader->read_int(ctx, inbuf, insize, &lw_time);
if(len == 0) {
PRINTF("FAIL: could not read time '%*.s'\n", (int)insize, inbuf);
} else {
PRINTF("Got: time: %*.s => %" PRId32 "\n", (int)insize, inbuf, lw_time);
time_offset = lw_time - clock_seconds();
PRINTF("Write time...%" PRId32 " => offset = %" PRId32 "\n",
lw_time, time_offset);
}
/* return the number of bytes read */
return len;
}
/*---------------------------------------------------------------------------*/
#ifdef PLATFORM_REBOOT
static struct ctimer reboot_timer;
static void
do_the_reboot(void *ptr)
{
PLATFORM_REBOOT();
}
static int
reboot(lwm2m_context_t *ctx, const uint8_t *arg, size_t argsize,
uint8_t *outbuf, size_t outsize)
{
PRINTF("Device will reboot!\n");
ctimer_set(&reboot_timer, CLOCK_SECOND / 2, do_the_reboot, NULL);
return 0;
}
#endif /* PLATFORM_REBOOT */
/*---------------------------------------------------------------------------*/
#ifdef PLATFORM_FACTORY_DEFAULT
static int
factory_reset(lwm2m_context_t *ctx, const uint8_t *arg, size_t arg_size,
uint8_t *outbuf, size_t outsize)
{
PRINTF("Device will do factory default!\n");
PLATFORM_FACTORY_DEFAULT();
return 0;
}
#endif /* PLATFORM_FACTORY_DEFAULT */
/*---------------------------------------------------------------------------*/
LWM2M_RESOURCES(device_resources,
#ifdef LWM2M_DEVICE_MANUFACTURER
LWM2M_RESOURCE_STRING(0, LWM2M_DEVICE_MANUFACTURER),
#endif /* LWM2M_DEVICE_MANUFACTURER */
#ifdef LWM2M_DEVICE_TYPE
LWM2M_RESOURCE_STRING(17, LWM2M_DEVICE_TYPE),
#endif /* LWM2M_DEVICE_TYPE */
#ifdef LWM2M_DEVICE_MODEL_NUMBER
LWM2M_RESOURCE_STRING(1, LWM2M_DEVICE_MODEL_NUMBER),
#endif /* LWM2M_DEVICE_MODEL_NUMBER */
#ifdef LWM2M_DEVICE_SERIAL_NO
LWM2M_RESOURCE_STRING(2, LWM2M_DEVICE_SERIAL_NO),
#endif /* LWM2M_DEVICE_SERIAL_NO */
#ifdef LWM2M_DEVICE_FIRMWARE_VERSION
LWM2M_RESOURCE_STRING(3, LWM2M_DEVICE_FIRMWARE_VERSION),
#endif /* LWM2M_DEVICE_FIRMWARE_VERSION */
#ifdef PLATFORM_REBOOT
LWM2M_RESOURCE_CALLBACK(4, { NULL, NULL, reboot }),
#endif /* PLATFORM_REBOOT */
#ifdef PLATFORM_FACTORY_DEFAULT
LWM2M_RESOURCE_CALLBACK(5, { NULL, NULL, factory_reset }),
#endif /* PLATFORM_FACTORY_DEFAULT */
/* Current Time */
LWM2M_RESOURCE_CALLBACK(13, { read_lwtime, set_lwtime, NULL }),
);
LWM2M_INSTANCES(device_instances, LWM2M_INSTANCE(0, device_resources));
LWM2M_OBJECT(device, 3, device_instances);
/*---------------------------------------------------------------------------*/
void
lwm2m_device_init(void)
{
/**
* Register this device and its handlers - the handlers
* automatically sends in the object to handle.
*/
PRINTF("*** Init lwm2m-device\n");
lwm2m_engine_register_object(&device);
}
/*---------------------------------------------------------------------------*/
/** @} */

View file

@ -0,0 +1,62 @@
/*
* Copyright (c) 2015, Yanzi Networks AB.
* 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 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 HOLDER 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.
*/
/**
* \addtogroup oma-lwm2m
* @{
*/
/**
* \file
* Header file for the Contiki OMA LWM2M device
* \author
* Joakim Eriksson <joakime@sics.se>
* Niclas Finne <nfi@sics.se>
*/
#ifndef LWM2M_DEVICE_H_
#define LWM2M_DEVICE_H_
#include "contiki-conf.h"
#ifndef LWM2M_DEVICE_MODEL_NUMBER
#ifdef BOARD_STRING
#define LWM2M_DEVICE_MODEL_NUMBER BOARD_STRING
#endif /* BOARD_STRING */
#endif /* LWM2M_DEVICE_MODEL_NUMBER */
#ifndef LWM2M_DEVICE_FIRMWARE_VERSION
#define LWM2M_DEVICE_FIRMWARE_VERSION CONTIKI_VERSION_STRING
#endif /* LWM2M_DEVICE_FIRMWARE_VERSION */
void lwm2m_device_init(void);
#endif /* LWM2M_DEVICE_H_ */
/** @} */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,82 @@
/*
* Copyright (c) 2015, Yanzi Networks AB.
* 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 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 HOLDER 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.
*/
/**
* \addtogroup oma-lwm2m
* @{
*/
/**
* \file
* Header file for the Contiki OMA LWM2M engine
* \author
* Joakim Eriksson <joakime@sics.se>
* Niclas Finne <nfi@sics.se>
*/
#ifndef LWM2M_ENGINE_H
#define LWM2M_ENGINE_H
#include "lwm2m-object.h"
#define LWM2M_FLOAT32_BITS 10
#define LWM2M_FLOAT32_FRAC (1L << LWM2M_FLOAT32_BITS)
/* LWM2M / CoAP Content-Formats */
typedef enum {
LWM2M_TEXT_PLAIN = 1541,
LWM2M_TLV = 1542,
LWM2M_JSON = 1543,
LWM2M_OPAQUE = 1544
} lwm2m_content_format_t;
void lwm2m_engine_init(void);
void lwm2m_engine_register_default_objects(void);
void lwm2m_engine_use_bootstrap_server(int use);
void lwm2m_engine_use_registration_server(int use);
void lwm2m_engine_register_with_server(const uip_ipaddr_t *server, uint16_t port);
void lwm2m_engine_register_with_bootstrap_server(const uip_ipaddr_t *server, uint16_t port);
const lwm2m_object_t *lwm2m_engine_get_object(uint16_t id);
int lwm2m_engine_register_object(const lwm2m_object_t *object);
void lwm2m_engine_handler(const lwm2m_object_t *object,
void *request, void *response,
uint8_t *buffer, uint16_t preferred_size,
int32_t *offset);
void lwm2m_engine_delete_handler(const lwm2m_object_t *object,
void *request, void *response,
uint8_t *buffer, uint16_t preferred_size,
int32_t *offset);
#endif /* LWM2M_ENGINE_H */
/** @} */

162
apps/oma-lwm2m/lwm2m-json.c Normal file
View file

@ -0,0 +1,162 @@
/*
* Copyright (c) 2016, Eistec AB.
* 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 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 HOLDER 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.
*/
/**
* \addtogroup oma-lwm2m
* @{
*/
/**
* \file
* Implementation of the Contiki OMA LWM2M JSON writer
* \author
* Joakim Nohlgård <joakim.nohlgard@eistec.se>
*/
#include "lwm2m-object.h"
#include "lwm2m-json.h"
#include "lwm2m-plain-text.h"
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <inttypes.h>
#define DEBUG 0
#if DEBUG
#define PRINTF(...) printf(__VA_ARGS__)
#else
#define PRINTF(...)
#endif
/*---------------------------------------------------------------------------*/
static size_t
write_boolean(const lwm2m_context_t *ctx, uint8_t *outbuf, size_t outlen,
int value)
{
int len = snprintf((char *)outbuf, outlen, "{\"e\":[{\"n\":\"%u\",\"bv\":%s}]}\n", ctx->resource_id, value ? "true" : "false");
if((len < 0) || (len >= outlen)) {
return 0;
}
return len;
}
/*---------------------------------------------------------------------------*/
static size_t
write_int(const lwm2m_context_t *ctx, uint8_t *outbuf, size_t outlen,
int32_t value)
{
int len = snprintf((char *)outbuf, outlen, "{\"e\":[{\"n\":\"%u\",\"v\":%" PRId32 "}]}\n", ctx->resource_id, value);
if((len < 0) || (len >= outlen)) {
return 0;
}
return len;
}
/*---------------------------------------------------------------------------*/
static size_t
write_float32fix(const lwm2m_context_t *ctx, uint8_t *outbuf, size_t outlen,
int32_t value, int bits)
{
size_t len = 0;
int res;
res = snprintf((char *)outbuf, outlen, "{\"e\":[{\"n\":\"%u\",\"v\":", ctx->resource_id);
if(res <= 0 || res >= outlen) {
return 0;
}
len += res;
outlen -= res;
res = lwm2m_plain_text_write_float32fix(&outbuf[len], outlen, value, bits);
if((res <= 0) || (res >= outlen)) {
return 0;
}
len += res;
outlen -= res;
res = snprintf((char *)&outbuf[len], outlen, "}]}\n");
if((res <= 0) || (res >= outlen)) {
return 0;
}
len += res;
return len;
}
/*---------------------------------------------------------------------------*/
static size_t
write_string(const lwm2m_context_t *ctx, uint8_t *outbuf, size_t outlen,
const char *value, size_t stringlen)
{
size_t i;
size_t len = 0;
int res;
PRINTF("{\"e\":[{\"n\":\"%u\",\"sv\":\"", ctx->resource_id);
res = snprintf((char *)outbuf, outlen, "{\"e\":[{\"n\":\"%u\",\"sv\":\"", ctx->resource_id);
if(res < 0 || res >= outlen) {
return 0;
}
len += res;
for (i = 0; i < stringlen && len < outlen; ++i) {
/* Escape special characters */
/* TODO: Handle UTF-8 strings */
if(value[i] < '\x20') {
PRINTF("\\x%x", value[i]);
res = snprintf((char *)&outbuf[len], outlen - len, "\\x%x", value[i]);
if((res < 0) || (res >= (outlen - len))) {
return 0;
}
len += res;
continue;
} else if(value[i] == '"' || value[i] == '\\') {
PRINTF("\\");
outbuf[len] = '\\';
++len;
if(len >= outlen) {
return 0;
}
}
PRINTF("%c", value[i]);
outbuf[len] = value[i];
++len;
if(len >= outlen) {
return 0;
}
}
PRINTF("\"}]}\n");
res = snprintf((char *)&outbuf[len], outlen - len, "\"}]}\n");
if((res < 0) || (res >= (outlen - len))) {
return 0;
}
len += res;
return len;
}
/*---------------------------------------------------------------------------*/
const lwm2m_writer_t lwm2m_json_writer = {
write_int,
write_string,
write_float32fix,
write_boolean
};
/*---------------------------------------------------------------------------*/
/** @} */

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2016, Eistec AB.
* 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 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 HOLDER 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.
*/
/**
* \addtogroup oma-lwm2m
* @{
*/
/**
* \file
* Header file for the Contiki OMA LWM2M JSON writer
* \author
* Joakim Nohlgård <joakim.nohlgard@eistec.se>
*/
#ifndef LWM2M_JSON_H_
#define LWM2M_JSON_H_
#include "lwm2m-object.h"
extern const lwm2m_writer_t lwm2m_json_writer;
#endif /* LWM2M_JSON_H_ */
/** @} */

View file

@ -0,0 +1,336 @@
/*
* Copyright (c) 2015, Yanzi Networks AB.
* 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 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 HOLDER 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.
*/
/**
* \addtogroup oma-lwm2m
* @{
*
*/
/**
* \file
* Implementation of the Contiki OMA LWM2M object API
* \author
* Joakim Eriksson <joakime@sics.se>
* Niclas Finne <nfi@sics.se>
*/
#include "lwm2m-object.h"
#include <string.h>
/*---------------------------------------------------------------------------*/
int
lwm2m_object_is_resource_string(const lwm2m_resource_t *resource)
{
if(resource == NULL) {
return 0;
}
if(resource->type == LWM2M_RESOURCE_TYPE_STR_VALUE ||
resource->type == LWM2M_RESOURCE_TYPE_STR_VARIABLE ||
resource->type == LWM2M_RESOURCE_TYPE_STR_VARIABLE_ARRAY) {
return 1;
}
return 0;
}
/*---------------------------------------------------------------------------*/
const uint8_t *
lwm2m_object_get_resource_string(const lwm2m_resource_t *resource,
const lwm2m_context_t *context)
{
if(resource == NULL || context == NULL) {
return NULL;
}
if(resource->type == LWM2M_RESOURCE_TYPE_STR_VALUE) {
return resource->value.string.value;
}
if(resource->type == LWM2M_RESOURCE_TYPE_STR_VARIABLE) {
return *(resource->value.stringvar.var);
}
if(resource->type == LWM2M_RESOURCE_TYPE_STR_VARIABLE_ARRAY) {
if(context->object_instance_index < resource->value.stringvararr.count) {
return resource->value.stringvararr.var +
resource->value.stringvararr.size * context->object_instance_index;
}
return NULL;
}
/* Not a string */
return NULL;
}
/*---------------------------------------------------------------------------*/
uint16_t
lwm2m_object_get_resource_strlen(const lwm2m_resource_t *resource,
const lwm2m_context_t *context)
{
if(resource == NULL || context == NULL) {
return 0;
}
if(resource->type == LWM2M_RESOURCE_TYPE_STR_VALUE) {
return resource->value.string.len;
}
if(resource->type == LWM2M_RESOURCE_TYPE_STR_VARIABLE) {
return *(resource->value.stringvar.len);
}
if(resource->type == LWM2M_RESOURCE_TYPE_STR_VARIABLE_ARRAY) {
if(context->object_instance_index < resource->value.stringvararr.count) {
return resource->value.stringvararr.len[context->object_instance_index];
}
return 0;
}
/* Not a string */
return 0;
}
/*---------------------------------------------------------------------------*/
int
lwm2m_object_set_resource_string(const lwm2m_resource_t *resource,
const lwm2m_context_t *context,
uint16_t len, const uint8_t *string)
{
if(resource == NULL || context == NULL) {
return 0;
}
if(resource->type == LWM2M_RESOURCE_TYPE_STR_VARIABLE) {
if(len > resource->value.stringvar.size) {
/* Too large */
return 0;
}
memcpy(resource->value.stringvar.var, string, len);
*(resource->value.stringvar.len) = len;
return 1;
}
if(resource->type == LWM2M_RESOURCE_TYPE_STR_VARIABLE_ARRAY) {
if(context->object_instance_index < resource->value.stringvararr.count &&
len <= resource->value.stringvararr.size) {
memcpy(resource->value.stringvararr.var +
resource->value.stringvararr.size * context->object_instance_index,
string, len);
resource->value.stringvararr.len[context->object_instance_index] = len;
return 1;
}
return 0;
}
/* Not a string variable */
return 0;
}
/*---------------------------------------------------------------------------*/
int
lwm2m_object_is_resource_int(const lwm2m_resource_t *resource)
{
if(resource == NULL) {
return 0;
}
if(resource->type == LWM2M_RESOURCE_TYPE_INT_VALUE ||
resource->type == LWM2M_RESOURCE_TYPE_INT_VARIABLE ||
resource->type == LWM2M_RESOURCE_TYPE_INT_VARIABLE_ARRAY) {
return 1;
}
return 0;
}
/*---------------------------------------------------------------------------*/
int
lwm2m_object_get_resource_int(const lwm2m_resource_t *resource,
const lwm2m_context_t *context,
int32_t *value)
{
if(resource == NULL || context == NULL || value == NULL) {
return 0;
}
if(resource->type == LWM2M_RESOURCE_TYPE_INT_VALUE) {
*value = resource->value.integer.value;
return 1;
}
if(resource->type == LWM2M_RESOURCE_TYPE_INT_VARIABLE) {
*value = *(resource->value.integervar.var);
return 1;
}
if(resource->type == LWM2M_RESOURCE_TYPE_INT_VARIABLE_ARRAY) {
if(context->object_instance_index < resource->value.integervararr.count) {
*value = resource->value.integervararr.var[context->object_instance_index];
return 1;
}
return 0;
}
/* Not an integer */
return 0;
}
/*---------------------------------------------------------------------------*/
int
lwm2m_object_set_resource_int(const lwm2m_resource_t *resource,
const lwm2m_context_t *context,
int32_t value)
{
if(resource == NULL || context == NULL) {
return 0;
}
if(resource->type == LWM2M_RESOURCE_TYPE_INT_VARIABLE) {
*(resource->value.integervar.var) = value;
return 1;
}
if(resource->type == LWM2M_RESOURCE_TYPE_INT_VARIABLE_ARRAY) {
if(context->object_instance_index < resource->value.integervararr.count) {
resource->value.integervararr.var[context->object_instance_index] =
value;
return 1;
}
return 0;
}
/* Not an integer variable */
return 0;
}
/*---------------------------------------------------------------------------*/
int
lwm2m_object_is_resource_floatfix(const lwm2m_resource_t *resource)
{
if(resource == NULL) {
return 0;
}
if(resource->type == LWM2M_RESOURCE_TYPE_FLOATFIX_VALUE ||
resource->type == LWM2M_RESOURCE_TYPE_FLOATFIX_VARIABLE ||
resource->type == LWM2M_RESOURCE_TYPE_FLOATFIX_VARIABLE_ARRAY) {
return 1;
}
return 0;
}
/*---------------------------------------------------------------------------*/
int
lwm2m_object_get_resource_floatfix(const lwm2m_resource_t *resource,
const lwm2m_context_t *context,
int32_t *value)
{
if(resource == NULL || context == NULL || value == NULL) {
return 0;
}
if(resource->type == LWM2M_RESOURCE_TYPE_FLOATFIX_VALUE) {
*value = resource->value.floatfix.value;
return 1;
}
if(resource->type == LWM2M_RESOURCE_TYPE_FLOATFIX_VARIABLE) {
*value = *(resource->value.floatfixvar.var);
return 1;
}
if(resource->type == LWM2M_RESOURCE_TYPE_FLOATFIX_VARIABLE_ARRAY) {
if(context->object_instance_index < resource->value.floatfixvararr.count) {
*value = resource->value.floatfixvararr.var[context->object_instance_index];
return 1;
}
return 0;
}
/* Not an float */
return 0;
}
/*---------------------------------------------------------------------------*/
int
lwm2m_object_set_resource_floatfix(const lwm2m_resource_t *resource,
const lwm2m_context_t *context,
int32_t value)
{
if(resource == NULL || context == NULL) {
return 0;
}
if(resource->type == LWM2M_RESOURCE_TYPE_FLOATFIX_VARIABLE) {
*(resource->value.floatfixvar.var) = value;
return 1;
}
if(resource->type == LWM2M_RESOURCE_TYPE_FLOATFIX_VARIABLE_ARRAY) {
if(context->object_instance_index < resource->value.floatfixvararr.count) {
resource->value.floatfixvararr.var[context->object_instance_index] =
value;
return 1;
}
return 0;
}
/* Not an float variable */
return 0;
}
/*---------------------------------------------------------------------------*/
int
lwm2m_object_is_resource_boolean(const lwm2m_resource_t *resource)
{
if(resource == NULL) {
return 0;
}
if(resource->type == LWM2M_RESOURCE_TYPE_BOOLEAN_VALUE ||
resource->type == LWM2M_RESOURCE_TYPE_BOOLEAN_VARIABLE ||
resource->type == LWM2M_RESOURCE_TYPE_BOOLEAN_VARIABLE_ARRAY) {
return 1;
}
return 0;
}
/*---------------------------------------------------------------------------*/
int
lwm2m_object_get_resource_boolean(const lwm2m_resource_t *resource,
const lwm2m_context_t *context,
int *value)
{
if(resource == NULL || context == NULL || value == NULL) {
return 0;
}
if(resource->type == LWM2M_RESOURCE_TYPE_BOOLEAN_VALUE) {
*value = resource->value.boolean.value;
return 1;
}
if(resource->type == LWM2M_RESOURCE_TYPE_BOOLEAN_VARIABLE) {
*value = *(resource->value.booleanvar.var);
return 1;
}
if(resource->type == LWM2M_RESOURCE_TYPE_BOOLEAN_VARIABLE_ARRAY) {
if(context->object_instance_index < resource->value.booleanvararr.count) {
*value = resource->value.booleanvararr.var[context->object_instance_index];
return 1;
}
return 0;
}
/* Not a boolean */
return 0;
}
/*---------------------------------------------------------------------------*/
int
lwm2m_object_set_resource_boolean(const lwm2m_resource_t *resource,
const lwm2m_context_t *context,
int value)
{
if(resource == NULL || context == NULL) {
return 0;
}
if(resource->type == LWM2M_RESOURCE_TYPE_BOOLEAN_VARIABLE) {
*(resource->value.booleanvar.var) = value;
return 1;
}
if(resource->type == LWM2M_RESOURCE_TYPE_BOOLEAN_VARIABLE_ARRAY) {
if(context->object_instance_index < resource->value.booleanvararr.count) {
resource->value.booleanvararr.var[context->object_instance_index] =
value;
return 1;
}
return 0;
}
/* Not a boolean variable */
return 0;
}
/*---------------------------------------------------------------------------*/
/** @} */

View file

@ -0,0 +1,359 @@
/*
* Copyright (c) 2015, Yanzi Networks AB.
* 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 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 HOLDER 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.
*/
/**
* \addtogroup apps
* @{
*/
/**
* \defgroup oma-lwm2m An implementation of OMA LWM2M
* @{
*
* This application is an implementation of OMA Lightweight M2M.
*/
/**
* \file
* Header file for the Contiki OMA LWM2M object API
* \author
* Joakim Eriksson <joakime@sics.se>
* Niclas Finne <nfi@sics.se>
*/
#ifndef LWM2M_OBJECT_H_
#define LWM2M_OBJECT_H_
#include "rest-engine.h"
#include "er-coap-observe.h"
#define LWM2M_OBJECT_SECURITY_ID 0
#define LWM2M_OBJECT_SERVER_ID 1
#define LWM2M_OBJECT_ACCESS_CONTROL_ID 2
#define LWM2M_OBJECT_DEVICE_ID 3
#define LWM2M_OBJECT_CONNECTIVITY_MONITORING_ID 4
#define LWM2M_OBJECT_FIRMWARE_ID 5
#define LWM2M_OBJECT_LOCATION_ID 6
#define LWM2M_OBJECT_CONNECTIVITY_STATISTICS_ID 7
#define LWM2M_SECURITY_SERVER_URI 0
#define LWM2M_SECURITY_BOOTSTRAP_SERVER 1
#define LWM2M_SECURITY_MODE 2
#define LWM2M_SECURITY_CLIENT_PKI 3
#define LWM2M_SECURITY_SERVER_PKI 4
#define LWM2M_SECURITY_KEY 5
#define LWM2M_SECURITY_SHORT_SERVER_ID 10
/* Pre-shared key mode */
#define LWM2M_SECURITY_MODE_PSK 0
/* Raw Public Key mode */
#define LWM2M_SECURITY_MODE_RPK 1
/* Certificate mode */
#define LWM2M_SECURITY_MODE_CERTIFICATE 2
/* NoSec mode */
#define LWM2M_SECURITY_MODE_NOSEC 3
#define LWM2M_OBJECT_STR_HELPER(x) (uint8_t *) #x
#define LWM2M_OBJECT_STR(x) LWM2M_OBJECT_STR_HELPER(x)
#define LWM2M_OBJECT_PATH_STR_HELPER(x) #x
#define LWM2M_OBJECT_PATH_STR(x) LWM2M_OBJECT_PATH_STR_HELPER(x)
struct lwm2m_reader;
struct lwm2m_writer;
/* Data model for OMA LWM2M objects */
typedef struct lwm2m_context {
uint16_t object_id;
uint16_t object_instance_id;
uint16_t resource_id;
uint8_t object_instance_index;
uint8_t resource_index;
/* TODO - add uint16_t resource_instance_id */
const struct lwm2m_reader *reader;
const struct lwm2m_writer *writer;
} lwm2m_context_t;
/* LWM2M format writer for the various formats supported */
typedef struct lwm2m_writer {
size_t (* write_int)(const lwm2m_context_t *ctx, uint8_t *outbuf, size_t outlen, int32_t value);
size_t (* write_string)(const lwm2m_context_t *ctx, uint8_t *outbuf, size_t outlen, const char *value, size_t strlen);
size_t (* write_float32fix)(const lwm2m_context_t *ctx, uint8_t *outbuf, size_t outlen, int32_t value, int bits);
size_t (* write_boolean)(const lwm2m_context_t *ctx, uint8_t *outbuf, size_t outlen, int value);
} lwm2m_writer_t;
typedef struct lwm2m_reader {
size_t (* read_int)(const lwm2m_context_t *ctx, const uint8_t *inbuf, size_t len, int32_t *value);
size_t (* read_string)(const lwm2m_context_t *ctx, const uint8_t *inbuf, size_t len, uint8_t *value, size_t strlen);
size_t (* read_float32fix)(const lwm2m_context_t *ctx, const uint8_t *inbuf, size_t len, int32_t *value, int bits);
size_t (* read_boolean)(const lwm2m_context_t *ctx, const uint8_t *inbuf, size_t len, int *value);
} lwm2m_reader_t;
typedef struct lwm2m_value_callback {
int (* read)(lwm2m_context_t *ctx, uint8_t *outbuf, size_t outlen);
int (* write)(lwm2m_context_t *ctx,
const uint8_t *buffer, size_t len,
uint8_t *outbuf, size_t outlen);
int (* exec)(lwm2m_context_t *ctx, const uint8_t *arg, size_t len,
uint8_t *outbuf, size_t outlen);
} lwm2m_value_callback_t;
#define LWM2M_RESOURCE_TYPE_STR_VALUE 1
#define LWM2M_RESOURCE_TYPE_STR_VARIABLE 2
#define LWM2M_RESOURCE_TYPE_STR_VARIABLE_ARRAY 3
#define LWM2M_RESOURCE_TYPE_INT_VALUE 4
#define LWM2M_RESOURCE_TYPE_INT_VARIABLE 5
#define LWM2M_RESOURCE_TYPE_INT_VARIABLE_ARRAY 6
#define LWM2M_RESOURCE_TYPE_FLOATFIX_VALUE 7
#define LWM2M_RESOURCE_TYPE_FLOATFIX_VARIABLE 8
#define LWM2M_RESOURCE_TYPE_FLOATFIX_VARIABLE_ARRAY 9
#define LWM2M_RESOURCE_TYPE_BOOLEAN_VALUE 10
#define LWM2M_RESOURCE_TYPE_BOOLEAN_VARIABLE 11
#define LWM2M_RESOURCE_TYPE_BOOLEAN_VARIABLE_ARRAY 12
#define LWM2M_RESOURCE_TYPE_CALLBACK 16
#define LWM2M_RESOURCE_TYPE_INSTANCES 17
typedef struct lwm2m_resource {
uint16_t id;
uint8_t type; /* indicate value type and multi-instance resource */
union {
struct {
uint16_t len;
const uint8_t *value;
} string;
struct {
uint16_t size;
uint16_t *len;
uint8_t **var;
} stringvar;
struct {
uint16_t count;
uint16_t size;
/* string var array with counting entries */
uint16_t *len;
uint8_t *var;
} stringvararr;
struct {
int32_t value;
} integer;
struct {
int32_t *var;
} integervar;
struct {
/* used for multiple instances (dynamic) NOTE: this is an index into
the instance so having two instances means that there is need for
allocation of two ints here */
uint16_t count;
int32_t *var; /* used as an array? */
} integervararr;
struct {
int32_t value;
} floatfix;
struct {
int32_t *var;
} floatfixvar;
struct {
uint16_t count;
int32_t *var;
} floatfixvararr;
struct {
int value;
} boolean;
struct {
int *var;
} booleanvar;
struct {
uint16_t count;
int *var;
} booleanvararr;
lwm2m_value_callback_t callback;
/* lwm2m_resource *resources[]; TO BE ADDED LATER*/
} value;
} lwm2m_resource_t;
#define LWM2M_INSTANCE_FLAG_USED 1
typedef struct lwm2m_instance {
uint16_t id;
uint16_t count;
uint16_t flag;
const lwm2m_resource_t *resources;
} lwm2m_instance_t;
typedef struct lwm2m_object {
uint16_t id;
uint16_t count;
const char *path;
resource_t *coap_resource;
lwm2m_instance_t *instances;
} lwm2m_object_t;
#define LWM2M_RESOURCES(name, ...) \
static const lwm2m_resource_t name[] = { __VA_ARGS__ }
#define LWM2M_RESOURCE_STRING(id, s) \
{ id, LWM2M_RESOURCE_TYPE_STR_VALUE, .value.string.len = sizeof(s) - 1, .value.string.value = (uint8_t *) s }
#define LWM2M_RESOURCE_STRING_VAR(id, s, l, v) \
{ id, LWM2M_RESOURCE_TYPE_STR_VARIABLE, .value.stringvar.size = (s), .value.stringvar.len = (l), .value.stringvar.var = (v) }
#define LWM2M_RESOURCE_STRING_VAR_ARR(id, c, s, l, v) \
{ id, LWM2M_RESOURCE_TYPE_STR_VARIABLE_ARRAY, .value.stringvararr.count = c, .value.stringvararr.size = s, .value.stringvararr.len = l, .value.stringvararr.var = (uint8_t *) v }
#define LWM2M_RESOURCE_INTEGER(id, v) \
{ id, LWM2M_RESOURCE_TYPE_INT_VALUE, .value.integer.value = (v) }
#define LWM2M_RESOURCE_INTEGER_VAR(id, v) \
{ id, LWM2M_RESOURCE_TYPE_INT_VARIABLE, .value.integervar.var = (v) }
#define LWM2M_RESOURCE_INTEGER_VAR_ARR(id, c, v) \
{ id, LWM2M_RESOURCE_TYPE_INT_VARIABLE_ARRAY, .value.integervararr.count = (c), .value.integervararr.var = (v) }
#define LWM2M_RESOURCE_FLOATFIX(id, v) \
{ id, LWM2M_RESOURCE_TYPE_FLOATFIX_VALUE, .value.floatfix.value = (v) }
#define LWM2M_RESOURCE_FLOATFIX_VAR(id, v) \
{ id, LWM2M_RESOURCE_TYPE_FLOATFIX_VARIABLE, .value.floatfixvar.var = (v) }
#define LWM2M_RESOURCE_FLOATFIX_VAR_ARR(id, c, v) \
{ id, LWM2M_RESOURCE_TYPE_FLOATFIX_VARIABLE_ARRAY, .value.floatfixvararr.count = (c), .value.floatfixvararr.var = (v) }
#define LWM2M_RESOURCE_BOOLEAN(id, v) \
{ id, LWM2M_RESOURCE_TYPE_BOOLEAN_VALUE, .value.boolean.value = (v) }
#define LWM2M_RESOURCE_BOOLEAN_VAR(id, v) \
{ id, LWM2M_RESOURCE_TYPE_BOOLEAN_VARIABLE, .value.booleanvar.var = (v) }
#define LWM2M_RESOURCE_BOOLEAN_VAR_ARR(id, c, v) \
{ id, LWM2M_RESOURCE_TYPE_BOOLEAN_VARIABLE_ARRAY, .value.booleanvararr.count = (c), .value.booleanvararr.var = (v) }
#define LWM2M_RESOURCE_CALLBACK(id, ...) \
{ id, LWM2M_RESOURCE_TYPE_CALLBACK, .value.callback = __VA_ARGS__ }
#define LWM2M_INSTANCE(id, resources) \
{ id, sizeof(resources)/sizeof(lwm2m_resource_t), LWM2M_INSTANCE_FLAG_USED, resources }
#define LWM2M_INSTANCE_UNUSED(id, resources) \
{ id, sizeof(resources)/sizeof(lwm2m_resource_t), 0, resources }
#define LWM2M_INSTANCES(name, ...) \
static lwm2m_instance_t name[] = { __VA_ARGS__ }
#define LWM2M_OBJECT(name, id, instances) \
static void lwm2m_get_h_##name(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); \
static void lwm2m_put_h_##name(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); \
static void lwm2m_post_h_##name(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); \
static void lwm2m_delete_h_##name(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); \
static resource_t rest_rsc_##name = { NULL, NULL, HAS_SUB_RESOURCES | IS_OBSERVABLE, NULL, lwm2m_get_h_##name, lwm2m_post_h_##name, lwm2m_put_h_##name, lwm2m_delete_h_##name, { NULL } }; \
static const lwm2m_object_t name = { id, sizeof(instances)/sizeof(lwm2m_instance_t), LWM2M_OBJECT_PATH_STR(id), &rest_rsc_##name, instances}; \
static void lwm2m_get_h_##name(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) { \
lwm2m_engine_handler(&name, request, response, buffer, preferred_size, offset); } \
static void lwm2m_put_h_##name(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) { \
lwm2m_engine_handler(&name, request, response, buffer, preferred_size, offset); } \
static void lwm2m_post_h_##name(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) { \
lwm2m_engine_handler(&name, request, response, buffer, preferred_size, offset); } \
static void lwm2m_delete_h_##name(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) { \
lwm2m_engine_delete_handler(&name, request, response, buffer, preferred_size, offset); }
/* how do we register attributes in the above resource here ??? */
int lwm2m_object_is_resource_string(const lwm2m_resource_t *resource);
int lwm2m_object_is_resource_int(const lwm2m_resource_t *resource);
int lwm2m_object_is_resource_floatfix(const lwm2m_resource_t *resource);
int lwm2m_object_is_resource_boolean(const lwm2m_resource_t *resource);
static inline int
lwm2m_object_is_resource_callback(const lwm2m_resource_t *resource)
{
return resource != NULL && resource->type == LWM2M_RESOURCE_TYPE_CALLBACK;
}
const uint8_t *
lwm2m_object_get_resource_string(const lwm2m_resource_t *resource,
const lwm2m_context_t *context);
uint16_t
lwm2m_object_get_resource_strlen(const lwm2m_resource_t *resource,
const lwm2m_context_t *context);
int
lwm2m_object_set_resource_string(const lwm2m_resource_t *resource,
const lwm2m_context_t *context,
uint16_t len, const uint8_t *string);
int
lwm2m_object_get_resource_int(const lwm2m_resource_t *resource,
const lwm2m_context_t *context,
int32_t *value);
int
lwm2m_object_set_resource_int(const lwm2m_resource_t *resource,
const lwm2m_context_t *context,
int32_t value);
int
lwm2m_object_get_resource_floatfix(const lwm2m_resource_t *resource,
const lwm2m_context_t *context,
int32_t *value);
int
lwm2m_object_set_resource_floatfix(const lwm2m_resource_t *resource,
const lwm2m_context_t *context,
int32_t value);
int
lwm2m_object_get_resource_boolean(const lwm2m_resource_t *resource,
const lwm2m_context_t *context,
int *value);
int
lwm2m_object_set_resource_boolean(const lwm2m_resource_t *resource,
const lwm2m_context_t *context,
int value);
static inline resource_t *
lwm2m_object_get_coap_resource(const lwm2m_object_t *object)
{
return (resource_t *)object->coap_resource;
}
static inline void
lwm2m_object_notify_observers(const lwm2m_object_t *object, char *path)
{
coap_notify_observers_sub(lwm2m_object_get_coap_resource(object), path);
}
#include "lwm2m-engine.h"
#endif /* LWM2M_OBJECT_H_ */
/**
* @}
* @}
*/

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